make values work in subquery

This commit is contained in:
meteorgan
2025-05-23 00:24:00 +08:00
parent 0467d7e11b
commit 34e05ef974
6 changed files with 79 additions and 34 deletions

View File

@@ -196,6 +196,11 @@ pub fn emit_query<'a>(
plan: &'a mut SelectPlan,
t_ctx: &'a mut TranslateCtx<'a>,
) -> Result<usize> {
if !plan.values.is_empty() {
let reg_result_cols_start = emit_values(program, &plan, &t_ctx.resolver)?;
return Ok(reg_result_cols_start);
}
// Emit subqueries first so the results can be read in the main query loop.
emit_subqueries(program, t_ctx, &mut plan.table_references)?;
@@ -291,12 +296,8 @@ pub fn emit_query<'a>(
&mut plan.where_clause,
)?;
if !plan.values.is_empty() {
emit_values(program, &plan.values, &t_ctx.resolver)?;
} else {
// Process result columns and expressions in the inner loop
emit_loop(program, t_ctx, plan)?;
}
// Process result columns and expressions in the inner loop
emit_loop(program, t_ctx, plan)?;
// Clean up and close the main execution loop
close_loop(program, t_ctx, &plan.table_references, &plan.join_order)?;

View File

@@ -404,10 +404,19 @@ pub fn prepare_select_plan<'a>(
Ok(Plan::Select(plan))
}
ast::OneSelect::Values(values) => {
let len = values[0].len();
let mut result_columns = Vec::with_capacity(len);
for i in 0..len {
result_columns.push(ResultSetColumn {
expr: ast::Expr::Literal(ast::Literal::Numeric(i.to_string())),
alias: None,
contains_aggregates: false,
});
}
let plan = SelectPlan {
join_order: vec![],
table_references: vec![],
result_columns: vec![],
result_columns,
where_clause: vec![],
group_by: None,
order_by: None,

View File

@@ -1,24 +1,22 @@
use crate::translate::emitter::Resolver;
use crate::translate::expr::{translate_expr_no_constant_opt, NoConstantOptReason};
use crate::translate::plan::{SelectPlan, SelectQueryType};
use crate::vdbe::builder::ProgramBuilder;
use crate::vdbe::insn::Insn;
use crate::vdbe::BranchOffset;
use limbo_sqlite3_parser::ast::Expr;
use crate::Result;
pub fn emit_values(
program: &mut ProgramBuilder,
values: &Vec<Vec<Expr>>,
plan: &SelectPlan,
resolver: &Resolver,
) -> crate::Result<()> {
if values.is_empty() {
return Ok(());
}
) -> Result<usize> {
let values = &plan.values;
if values.len() == 1 {
let value = &values[0];
let value_size = value.len();
let start_reg = program.alloc_registers(value_size);
for (i, v) in value.iter().enumerate() {
let first_row = &values[0];
let row_len = first_row.len();
let start_reg = program.alloc_registers(row_len);
for (i, v) in first_row.iter().enumerate() {
translate_expr_no_constant_opt(
program,
None,
@@ -28,11 +26,8 @@ pub fn emit_values(
NoConstantOptReason::RegisterReuse,
)?;
}
program.emit_insn(Insn::ResultRow {
start_reg,
count: value_size,
});
return Ok(());
emit_result_row(program, plan, start_reg, row_len)?;
return Ok(start_reg);
}
let yield_reg = program.alloc_register();
@@ -45,8 +40,8 @@ pub fn emit_values(
});
program.preassign_label_to_next_insn(start_offset_label);
let value_size = values[0].len();
let start_reg = program.alloc_registers(value_size);
let row_len = values[0].len();
let start_reg = program.alloc_registers(row_len);
for value in values {
for (i, v) in value.iter().enumerate() {
translate_expr_no_constant_opt(
@@ -78,20 +73,40 @@ pub fn emit_values(
yield_reg,
end_offset: end_label,
});
let copy_reg = program.alloc_registers(value_size);
for i in 0..value_size {
let copy_start_reg = program.alloc_registers(row_len);
for i in 0..row_len {
program.emit_insn(Insn::Copy {
src_reg: start_reg + i,
dst_reg: copy_reg + i,
dst_reg: copy_start_reg + i,
amount: 0,
});
}
program.emit_insn(Insn::ResultRow {
start_reg: copy_reg,
count: value_size,
emit_result_row(program, plan, copy_start_reg, row_len)?;
program.emit_insn(Insn::Goto {
target_pc: goto_label,
});
program.emit_goto(goto_label);
program.preassign_label_to_next_insn(end_label);
Ok(())
Ok(copy_start_reg)
}
fn emit_result_row(
program: &mut ProgramBuilder,
plan: &SelectPlan,
start_reg: usize,
count: usize,
) -> Result<()> {
match plan.query_type {
SelectQueryType::TopLevel => {
program.emit_insn(Insn::ResultRow { start_reg, count });
Ok(())
}
SelectQueryType::Subquery { yield_reg, .. } => {
program.emit_insn(Insn::Yield {
yield_reg,
end_offset: BranchOffset::Offset(0),
});
Ok(())
}
}
}

View File

@@ -1557,7 +1557,7 @@ pub fn op_result_row(
};
state.result_row = Some(row);
state.pc += 1;
return Ok(InsnFunctionStepResult::Row);
Ok(InsnFunctionStepResult::Row)
}
pub fn op_next(

View File

@@ -35,3 +35,4 @@ source $testdir/literal.test
source $testdir/null.test
source $testdir/create_table.test
source $testdir/collate.test
source $testdir/values.test

19
testing/values.test Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env tclsh
set testdir [file dirname $argv0]
source $testdir/tester.tcl
do_execsql_test values-1 {
values(1, 2);
} {1|2};
do_execsql_test values-2 {
values(1, 2), (3, 4);
} {1|2
3|4};
do_execsql_test values-in-from {
select * from (values(3, 4, 5), (5, 6, 7), (8, 9, 10));
} {3|4|5
5|6|7
8|9|10};