Add support for Insn::Once

This commit is contained in:
Jussi Saurio 2025-04-14 10:33:42 +03:00
parent f79da7194f
commit 9dadc58194
5 changed files with 49 additions and 1 deletions

View file

@ -501,7 +501,7 @@ Modifiers:
| NotNull | Yes | |
| Null | Yes | |
| NullRow | Yes | |
| Once | No | |
| Once | Yes | |
| OpenAutoindex | No | |
| OpenEphemeral | No | |
| OpenPseudo | Yes | |

View file

@ -4432,6 +4432,34 @@ pub fn op_open_ephemeral(
Ok(InsnFunctionStepResult::Step)
}
/// Execute the [Insn::Once] instruction.
///
/// This instruction is used to execute a block of code only once.
/// If the instruction is executed again, it will jump to the target program counter.
pub fn op_once(
program: &Program,
state: &mut ProgramState,
insn: &Insn,
pager: &Rc<Pager>,
mv_store: Option<&Rc<MvStore>>,
) -> Result<InsnFunctionStepResult> {
let Insn::Once {
target_pc_when_reentered,
} = insn
else {
unreachable!("unexpected Insn: {:?}", insn)
};
assert!(target_pc_when_reentered.is_offset());
let offset = state.pc;
if state.once.iter().any(|o| o == offset) {
state.pc = target_pc_when_reentered.to_offset_int();
return Ok(InsnFunctionStepResult::Step);
}
state.once.push(offset);
state.pc += 1;
Ok(InsnFunctionStepResult::Step)
}
fn exec_lower(reg: &OwnedValue) -> Option<OwnedValue> {
match reg {
OwnedValue::Text(t) => Some(OwnedValue::build_text(&t.as_str().to_lowercase())),

View file

@ -1321,6 +1321,17 @@ pub fn insn_to_str(
if *is_table { "true" } else { "false" }
),
),
Insn::Once {
target_pc_when_reentered,
} => (
"Once",
target_pc_when_reentered.to_debug_int(),
0,
0,
OwnedValue::build_text(""),
0,
format!("goto {}", target_pc_when_reentered.to_debug_int()),
),
};
format!(
"{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}",

View file

@ -780,6 +780,10 @@ pub enum Insn {
cursor_id: usize,
is_table: bool,
},
/// Fall through to the next instruction on the first invocation, otherwise jump to target_pc
Once {
target_pc_when_reentered: BranchOffset,
},
}
impl Insn {
@ -889,6 +893,7 @@ impl Insn {
Insn::PageCount { .. } => execute::op_page_count,
Insn::ReadCookie { .. } => execute::op_read_cookie,
Insn::OpenEphemeral { .. } => execute::op_open_ephemeral,
Insn::Once { .. } => execute::op_once,
}
}
}

View file

@ -28,6 +28,7 @@ use crate::{
error::LimboError,
fast_lock::SpinLock,
function::{AggFunc, FuncCtx},
storage::sqlite3_ondisk::SmallVec,
};
use crate::{
@ -232,6 +233,8 @@ pub struct ProgramState {
last_compare: Option<std::cmp::Ordering>,
deferred_seek: Option<(CursorID, CursorID)>,
ended_coroutine: Bitfield<4>, // flag to indicate that a coroutine has ended (key is the yield register. currently we assume that the yield register is always between 0-255, YOLO)
/// Indicate whether an [Insn::Once] instruction at a given program counter position has already been executed, well, once.
once: SmallVec<u32, 4>,
regex_cache: RegexCache,
pub(crate) mv_tx_id: Option<crate::mvcc::database::TxID>,
interrupted: bool,
@ -254,6 +257,7 @@ impl ProgramState {
last_compare: None,
deferred_seek: None,
ended_coroutine: Bitfield::new(),
once: SmallVec::<u32, 4>::new(),
regex_cache: RegexCache::new(),
mv_tx_id: None,
interrupted: false,