mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 17:05:36 +01:00
core/vdbe: Fix MakeRecord affinity handling
The MakeRecord instruction now accepts an optional affinity_str parameter that applies column-specific type conversions before creating records. When provided, the affinity string is applied character-by-character to each register using the existing apply_affinity_char() function, matching SQLite's behavior. Fixes #2040 Fixes #2041
This commit is contained in:
@@ -115,6 +115,7 @@ pub fn handle_distinct(program: &mut ProgramBuilder, agg: &Aggregate, agg_arg_re
|
||||
count: num_regs,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(distinct_ctx.ephemeral_index_name.to_string()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: distinct_ctx.cursor_id,
|
||||
|
||||
@@ -141,11 +141,18 @@ pub fn translate_alter_table(
|
||||
|
||||
let record = program.alloc_register();
|
||||
|
||||
let affinity_str = btree
|
||||
.columns
|
||||
.iter()
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: first_column,
|
||||
count: column_count,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
@@ -301,6 +308,7 @@ pub fn translate_alter_table(
|
||||
count: sqlite_schema_column_len,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
@@ -442,6 +450,7 @@ pub fn translate_alter_table(
|
||||
count: sqlite_schema_column_len,
|
||||
dest_reg: record,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
|
||||
@@ -212,6 +212,7 @@ pub fn translate_analyze(
|
||||
count: 3,
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::NewRowid {
|
||||
cursor: stat_cursor,
|
||||
|
||||
@@ -524,6 +524,7 @@ fn read_intersect_rows(
|
||||
count: column_count,
|
||||
dest_reg: row_content_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: target_cursor_id,
|
||||
|
||||
@@ -1038,6 +1038,7 @@ fn emit_update_insns(
|
||||
count: num_cols + 1,
|
||||
dest_reg: *record_reg,
|
||||
index_name: Some(index.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
if !index.unique {
|
||||
@@ -1138,11 +1139,19 @@ fn emit_update_insns(
|
||||
}
|
||||
|
||||
let record_reg = program.alloc_register();
|
||||
|
||||
let affinity_str = table_ref
|
||||
.columns()
|
||||
.iter()
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: start,
|
||||
count: table_ref.columns().len(),
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
|
||||
if has_user_provided_rowid {
|
||||
@@ -1282,6 +1291,7 @@ fn emit_update_insns(
|
||||
count: 2 * table_ref.columns().len(),
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
Some(record_reg)
|
||||
} else {
|
||||
@@ -1398,11 +1408,18 @@ pub fn emit_cdc_patch_record(
|
||||
dst_reg: columns_reg + rowid_alias_position,
|
||||
extra_amount: 0,
|
||||
});
|
||||
let affinity_str = table
|
||||
.columns()
|
||||
.iter()
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: columns_reg,
|
||||
count: table.columns().len(),
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
record_reg
|
||||
} else {
|
||||
@@ -1428,11 +1445,17 @@ pub fn emit_cdc_full_record(
|
||||
program.emit_column_or_rowid(table_cursor_id, i, columns_reg + 1 + i);
|
||||
}
|
||||
}
|
||||
let affinity_str = columns
|
||||
.iter()
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: columns_reg + 1,
|
||||
count: columns.len(),
|
||||
dest_reg: columns_reg,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
columns_reg
|
||||
}
|
||||
@@ -1535,6 +1558,7 @@ pub fn emit_cdc_insns(
|
||||
count: 8,
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
|
||||
@@ -174,6 +174,7 @@ pub fn translate_create_index(
|
||||
count: columns.len() + 1,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(idx_name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::SorterInsert {
|
||||
cursor_id: sorter_cursor_id,
|
||||
|
||||
@@ -253,11 +253,35 @@ pub fn translate_insert(
|
||||
end_offset: yield_label,
|
||||
});
|
||||
let record_reg = program.alloc_register();
|
||||
|
||||
let affinity_str = if columns.is_empty() {
|
||||
btree_table
|
||||
.columns
|
||||
.iter()
|
||||
.filter(|col| !col.hidden)
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>()
|
||||
} else {
|
||||
columns
|
||||
.iter()
|
||||
.map(|col_name| {
|
||||
let column_name = normalize_ident(col_name.as_str());
|
||||
table
|
||||
.get_column_by_name(&column_name)
|
||||
.unwrap()
|
||||
.1
|
||||
.affinity()
|
||||
.aff_mask()
|
||||
})
|
||||
.collect::<String>()
|
||||
};
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: yield_reg + 1,
|
||||
count: result.num_result_cols,
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
|
||||
let rowid_reg = program.alloc_register();
|
||||
@@ -507,6 +531,7 @@ pub fn translate_insert(
|
||||
count: num_cols + 1,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(index.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
if index.unique {
|
||||
@@ -627,11 +652,18 @@ pub fn translate_insert(
|
||||
});
|
||||
}
|
||||
// Create and insert the record
|
||||
let affinity_str = insertion
|
||||
.col_mappings
|
||||
.iter()
|
||||
.map(|col_mapping| col_mapping.column.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: insertion.first_col_register(),
|
||||
count: insertion.col_mappings.len(),
|
||||
dest_reg: insertion.record_register(),
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: cursor_id,
|
||||
|
||||
@@ -1429,6 +1429,7 @@ fn emit_autoindex(
|
||||
count: num_regs_to_reserve,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(index.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: index_cursor_id,
|
||||
|
||||
@@ -307,6 +307,7 @@ pub fn sorter_insert(
|
||||
count: column_count,
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::SorterInsert {
|
||||
cursor_id,
|
||||
|
||||
@@ -264,6 +264,7 @@ impl DistinctCtx {
|
||||
count: num_regs,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(self.ephemeral_index_name.to_string()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: self.cursor_id,
|
||||
|
||||
@@ -103,6 +103,7 @@ pub fn emit_result_row_and_limit(
|
||||
count: plan.result_columns.len(),
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(dedupe_index.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: *index_cursor_id,
|
||||
@@ -124,6 +125,7 @@ pub fn emit_result_row_and_limit(
|
||||
count: plan.result_columns.len() - 1,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(table.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
}
|
||||
program.emit_insn(Insn::Insert {
|
||||
|
||||
@@ -257,6 +257,7 @@ pub fn emit_schema_entry(
|
||||
count: 5,
|
||||
dest_reg: record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
|
||||
program.emit_insn(Insn::Insert {
|
||||
@@ -614,6 +615,7 @@ pub fn translate_create_virtual_table(
|
||||
count: args_vec.len(),
|
||||
dest_reg: args_record_reg,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
Some(args_record_reg)
|
||||
} else {
|
||||
@@ -998,6 +1000,7 @@ pub fn translate_drop_table(
|
||||
count: 5,
|
||||
dest_reg: new_record_register,
|
||||
index_name: None,
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::Delete {
|
||||
cursor_id: sqlite_schema_cursor_id_1,
|
||||
|
||||
@@ -379,6 +379,7 @@ pub fn emit_upsert(
|
||||
count: k + 1,
|
||||
dest_reg: rec,
|
||||
index_name: Some((*idx_name).clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: *idx_cid,
|
||||
@@ -392,11 +393,19 @@ pub fn emit_upsert(
|
||||
|
||||
// Write table row (same rowid, new payload)
|
||||
let rec = program.alloc_register();
|
||||
|
||||
let affinity_str = table
|
||||
.columns()
|
||||
.iter()
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>();
|
||||
|
||||
program.emit_insn(Insn::MakeRecord {
|
||||
start_reg: new_start,
|
||||
count: num_cols,
|
||||
dest_reg: rec,
|
||||
index_name: None,
|
||||
affinity_str: Some(affinity_str),
|
||||
});
|
||||
program.emit_insn(Insn::Insert {
|
||||
cursor: tbl_cursor_id,
|
||||
|
||||
@@ -199,6 +199,7 @@ fn emit_values_to_index(
|
||||
count: row_len,
|
||||
dest_reg: record_reg,
|
||||
index_name: Some(index.name.clone()),
|
||||
affinity_str: None,
|
||||
});
|
||||
program.emit_insn(Insn::IdxInsert {
|
||||
cursor_id: *cursor_id,
|
||||
|
||||
@@ -1864,10 +1864,27 @@ pub fn op_make_record(
|
||||
start_reg,
|
||||
count,
|
||||
dest_reg,
|
||||
affinity_str,
|
||||
..
|
||||
},
|
||||
insn
|
||||
);
|
||||
|
||||
if let Some(affinity_str) = affinity_str {
|
||||
if affinity_str.len() != *count {
|
||||
return Err(LimboError::InternalError(format!(
|
||||
"MakeRecord: the length of affinity string ({}) does not match the count ({})",
|
||||
affinity_str.len(),
|
||||
*count
|
||||
)));
|
||||
}
|
||||
for (i, affinity_ch) in affinity_str.chars().enumerate().take(*count) {
|
||||
let reg_index = *start_reg + i;
|
||||
let affinity = Affinity::from_char(affinity_ch);
|
||||
apply_affinity_char(&mut state.registers[reg_index], affinity);
|
||||
}
|
||||
}
|
||||
|
||||
let record = make_record(&state.registers, start_reg, count);
|
||||
state.registers[*dest_reg] = Register::Record(record);
|
||||
state.pc += 1;
|
||||
|
||||
@@ -584,6 +584,7 @@ pub fn insn_to_str(
|
||||
count,
|
||||
dest_reg,
|
||||
index_name,
|
||||
affinity_str: _,
|
||||
} => {
|
||||
let for_index = index_name.as_ref().map(|name| format!("; for {name}"));
|
||||
(
|
||||
|
||||
@@ -430,6 +430,7 @@ pub enum Insn {
|
||||
count: usize, // P2
|
||||
dest_reg: usize, // P3
|
||||
index_name: Option<String>,
|
||||
affinity_str: Option<String>,
|
||||
},
|
||||
|
||||
/// Emit a row of results.
|
||||
|
||||
Reference in New Issue
Block a user