diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index 00a4155b1..919e607cc 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -196,6 +196,11 @@ pub fn emit_query<'a>( plan: &'a mut SelectPlan, t_ctx: &'a mut TranslateCtx<'a>, ) -> Result { + 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)?; diff --git a/core/translate/select.rs b/core/translate/select.rs index 8eccea51b..208e260d3 100644 --- a/core/translate/select.rs +++ b/core/translate/select.rs @@ -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, diff --git a/core/translate/values.rs b/core/translate/values.rs index 32acd4274..db008254f 100644 --- a/core/translate/values.rs +++ b/core/translate/values.rs @@ -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>, + plan: &SelectPlan, resolver: &Resolver, -) -> crate::Result<()> { - if values.is_empty() { - return Ok(()); - } - +) -> Result { + 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(()) + } + } } diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index c67a5e5f5..8830a788f 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -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( diff --git a/testing/all.test b/testing/all.test index dc5b3264f..474ad1c73 100755 --- a/testing/all.test +++ b/testing/all.test @@ -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 diff --git a/testing/values.test b/testing/values.test new file mode 100755 index 000000000..dcd163fc1 --- /dev/null +++ b/testing/values.test @@ -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}; \ No newline at end of file