Skip to content

Commit e7b164a

Browse files
committed
Support select <columns> for order by operation
1 parent 3f09488 commit e7b164a

File tree

9 files changed

+99
-66
lines changed

9 files changed

+99
-66
lines changed

core/btree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::types::{Cursor, CursorResult, OwnedRecord};
44

55
use anyhow::Result;
66

7-
use std::cell::{Ref, RefCell};
7+
use std::cell::RefCell;
88
use std::rc::Rc;
99

1010
pub struct MemPage {

core/expr.rs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use sqlite3_parser::ast::{self, Expr, UnaryOperator};
33

44
use crate::{
55
function::{Func, SingleRowFunc},
6-
schema::{Column, Schema, Table},
6+
schema::{Schema, Table, Type},
77
select::{ColumnInfo, Select, SrcTable},
88
util::normalize_ident,
99
vdbe::{BranchOffset, Insn, ProgramBuilder},
@@ -286,9 +286,8 @@ pub fn translate_expr(
286286
for arg in args {
287287
let reg = program.alloc_register();
288288
let _ = translate_expr(program, select, arg, reg, cursor_hint)?;
289-
match arg {
290-
ast::Expr::Literal(_) => program.mark_last_insn_constant(),
291-
_ => {}
289+
if let ast::Expr::Literal(_) = arg {
290+
program.mark_last_insn_constant()
292291
}
293292
}
294293
program.emit_insn(Insn::Function {
@@ -374,9 +373,9 @@ pub fn translate_expr(
374373
ast::Expr::FunctionCallStar { .. } => todo!(),
375374
ast::Expr::Id(ident) => {
376375
// let (idx, col) = table.unwrap().get_column(&ident.0).unwrap();
377-
let (idx, col, cursor_id) =
376+
let (idx, col_type, cursor_id, is_primary_key) =
378377
resolve_ident_table(program, &ident.0, select, cursor_hint)?;
379-
if col.primary_key {
378+
if is_primary_key {
380379
program.emit_insn(Insn::RowId {
381380
cursor_id,
382381
dest: target_register,
@@ -388,7 +387,7 @@ pub fn translate_expr(
388387
cursor_id,
389388
});
390389
}
391-
maybe_apply_affinity(col, target_register, program);
390+
maybe_apply_affinity(col_type, target_register, program);
392391
Ok(target_register)
393392
}
394393
ast::Expr::InList { .. } => todo!(),
@@ -436,9 +435,9 @@ pub fn translate_expr(
436435
ast::Expr::NotNull(_) => todo!(),
437436
ast::Expr::Parenthesized(_) => todo!(),
438437
ast::Expr::Qualified(tbl, ident) => {
439-
let (idx, col, cursor_id) =
438+
let (idx, col_type, cursor_id, is_primary_key) =
440439
resolve_ident_qualified(program, &tbl.0, &ident.0, select, cursor_hint)?;
441-
if col.primary_key {
440+
if is_primary_key {
442441
program.emit_insn(Insn::RowId {
443442
cursor_id,
444443
dest: target_register,
@@ -450,7 +449,7 @@ pub fn translate_expr(
450449
cursor_id,
451450
});
452451
}
453-
maybe_apply_affinity(col, target_register, program);
452+
maybe_apply_affinity(col_type, target_register, program);
454453
Ok(target_register)
455454
}
456455
ast::Expr::Raise(_, _) => todo!(),
@@ -562,7 +561,7 @@ pub fn resolve_ident_qualified<'a>(
562561
ident: &String,
563562
select: &'a Select,
564563
cursor_hint: Option<usize>,
565-
) -> Result<(usize, &'a Column, usize)> {
564+
) -> Result<(usize, Type, usize, bool)> {
566565
for join in &select.src_tables {
567566
match join.table {
568567
Table::BTree(ref table) => {
@@ -579,7 +578,7 @@ pub fn resolve_ident_qualified<'a>(
579578
if res.is_some() {
580579
let (idx, col) = res.unwrap();
581580
let cursor_id = program.resolve_cursor_id(&table_identifier, cursor_hint);
582-
return Ok((idx, col, cursor_id));
581+
return Ok((idx, col.ty, cursor_id, col.primary_key));
583582
}
584583
}
585584
}
@@ -598,7 +597,7 @@ pub fn resolve_ident_table<'a>(
598597
ident: &String,
599598
select: &'a Select,
600599
cursor_hint: Option<usize>,
601-
) -> Result<(usize, &'a Column, usize)> {
600+
) -> Result<(usize, Type, usize, bool)> {
602601
let mut found = Vec::new();
603602
for join in &select.src_tables {
604603
match join.table {
@@ -611,11 +610,29 @@ pub fn resolve_ident_table<'a>(
611610
.columns
612611
.iter()
613612
.enumerate()
614-
.find(|(_, col)| col.name == *ident);
613+
.find(|(_, col)| col.name == *ident)
614+
.map(|(idx, col)| (idx, col.ty, col.primary_key));
615+
let mut idx;
616+
let mut col_type;
617+
let mut is_primary_key;
615618
if res.is_some() {
616-
let (idx, col) = res.unwrap();
619+
(idx, col_type, is_primary_key) = res.unwrap();
620+
// overwrite if cursor hint is provided
621+
if let Some(cursor_hint) = cursor_hint {
622+
let cols = &program.cursor_ref[cursor_hint].1;
623+
if let Some(res) = cols.as_ref().and_then(|res| {
624+
res.columns()
625+
.iter()
626+
.enumerate()
627+
.find(|x| x.1.name == *ident)
628+
}) {
629+
idx = res.0;
630+
col_type = res.1.ty;
631+
is_primary_key = res.1.primary_key;
632+
}
633+
}
617634
let cursor_id = program.resolve_cursor_id(&table_identifier, cursor_hint);
618-
found.push((idx, col, cursor_id));
635+
found.push((idx, col_type, cursor_id, is_primary_key));
619636
}
620637
}
621638
Table::Pseudo(_) => todo!(),
@@ -631,8 +648,8 @@ pub fn resolve_ident_table<'a>(
631648
anyhow::bail!("Parse error: ambiguous column name {}", ident.as_str());
632649
}
633650

634-
pub fn maybe_apply_affinity(col: &Column, target_register: usize, program: &mut ProgramBuilder) {
635-
if col.ty == crate::schema::Type::Real {
651+
pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mut ProgramBuilder) {
652+
if col_type == crate::schema::Type::Real {
636653
program.emit_insn(Insn::RealAffinity {
637654
register: target_register,
638655
})

core/pseudo.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::Result;
2-
use std::cell::{Ref, RefCell};
2+
use std::cell::RefCell;
33

44
use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue};
55

@@ -35,13 +35,14 @@ impl Cursor for PseudoCursor {
3535
}
3636

3737
fn rowid(&self) -> Result<Option<u64>> {
38-
let x = self.current.borrow().as_ref().map(|record| {
39-
let rowid = match record.values[0] {
38+
let x = self
39+
.current
40+
.borrow()
41+
.as_ref()
42+
.map(|record| match record.values[0] {
4043
OwnedValue::Integer(rowid) => rowid as u64,
4144
_ => panic!("Expected integer value"),
42-
};
43-
rowid
44-
});
45+
});
4546
Ok(x)
4647
}
4748

core/schema.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::types::OwnedRecord;
21
use crate::util::normalize_ident;
32
use anyhow::Result;
43
use core::fmt;
@@ -55,7 +54,7 @@ impl Table {
5554
pub fn get_name(&self) -> &str {
5655
match self {
5756
Table::BTree(table) => &table.name,
58-
Table::Pseudo(table) => "pseudo",
57+
Table::Pseudo(_) => "",
5958
}
6059
}
6160

@@ -155,15 +154,11 @@ impl BTreeTable {
155154
#[derive(Debug)]
156155
pub struct PseudoTable {
157156
pub columns: Vec<Column>,
158-
pub row: Option<OwnedRecord>,
159157
}
160158

161159
impl PseudoTable {
162160
pub fn new() -> Self {
163-
Self {
164-
columns: vec![],
165-
row: None,
166-
}
161+
Self { columns: vec![] }
167162
}
168163

169164
pub fn add_column(&mut self, name: &str, ty: Type, primary_key: bool) {
@@ -302,7 +297,7 @@ pub fn _build_pseudo_table(columns: &[ResultColumn]) -> PseudoTable {
302297
table
303298
}
304299

305-
#[derive(Debug)]
300+
#[derive(Debug, Clone)]
306301
pub struct Column {
307302
pub name: String,
308303
pub ty: Type,

core/sorter.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue};
1+
use crate::types::{Cursor, CursorResult, OwnedRecord};
22
use anyhow::Result;
3-
use log::trace;
43
use std::{
54
cell::RefCell,
65
collections::{BTreeMap, VecDeque},
@@ -75,7 +74,6 @@ impl Cursor for Sorter {
7574
fn insert(&mut self, record: &OwnedRecord) -> Result<()> {
7675
let key_fields = self.order.len();
7776
let key = OwnedRecord::new(record.values[0..key_fields].to_vec());
78-
trace!("Inserting record with key: {:?}", key);
7977
self.insert(key, OwnedRecord::new(record.values[key_fields..].to_vec()));
8078
Ok(())
8179
}

core/translate.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use std::rc::Rc;
44
use crate::expr::{build_select, maybe_apply_affinity, translate_expr};
55
use crate::function::{AggFunc, Func};
66
use crate::pager::Pager;
7-
use crate::schema::{Schema, Table};
7+
use crate::schema::{Column, PseudoTable, Schema, Table};
88
use crate::select::{ColumnInfo, LoopInfo, Select, SrcTable};
99
use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE};
1010
use crate::types::{OwnedRecord, OwnedValue};
11+
use crate::util::normalize_ident;
1112
use crate::vdbe::{BranchOffset, Insn, Program, ProgramBuilder};
1213
use crate::where_clause::{
1314
evaluate_conditions, translate_conditions, translate_where, Inner, Left, QueryConstraint,
@@ -178,7 +179,7 @@ fn translate_select(mut select: Select) -> Result<Program> {
178179
}
179180
program.emit_insn(Insn::ResultRow {
180181
start_reg: register_start,
181-
count: count,
182+
count,
182183
});
183184
emit_limit_insn(&limit_info, &mut program);
184185
};
@@ -228,8 +229,31 @@ fn translate_sorter(
228229
limit_info: &Option<LimitInfo>,
229230
) -> Result<()> {
230231
assert!(sort_info.count > 0);
231-
232-
let pseudo_cursor = program.alloc_cursor_id(None, None);
232+
let mut pseudo_columns = Vec::new();
233+
for col in select.columns.iter() {
234+
match col {
235+
ast::ResultColumn::Expr(expr, _) => match expr {
236+
ast::Expr::Id(ident) => {
237+
pseudo_columns.push(Column {
238+
name: normalize_ident(&ident.0),
239+
primary_key: false,
240+
ty: crate::schema::Type::Null,
241+
});
242+
}
243+
_ => {
244+
todo!();
245+
}
246+
},
247+
ast::ResultColumn::Star => {}
248+
ast::ResultColumn::TableStar(_) => {}
249+
}
250+
}
251+
let pseudo_cursor = program.alloc_cursor_id(
252+
None,
253+
Some(Table::Pseudo(Rc::new(PseudoTable {
254+
columns: pseudo_columns,
255+
}))),
256+
);
233257
let pseudo_content_reg = program.alloc_register();
234258
program.emit_insn(Insn::OpenPseudo {
235259
cursor_id: pseudo_cursor,
@@ -530,7 +554,7 @@ fn translate_table_star(
530554
dest: col_target_register,
531555
cursor_id: table_cursor,
532556
});
533-
maybe_apply_affinity(col, col_target_register, program);
557+
maybe_apply_affinity(col.ty, col_target_register, program);
534558
}
535559
}
536560
}

core/vdbe.rs

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ pub struct ProgramBuilder {
271271
unresolved_labels: Vec<Vec<InsnReference>>,
272272
next_insn_label: Option<BranchOffset>,
273273
// Cursors that are referenced by the program. Indexed by CursorID.
274-
cursor_ref: Vec<(Option<String>, Option<Table>)>,
274+
pub cursor_ref: Vec<(Option<String>, Option<Table>)>,
275275
// List of deferred label resolutions. Each entry is a pair of (label, insn_reference).
276276
deferred_label_resolutions: Vec<(BranchOffset, InsnReference)>,
277277
}
@@ -303,14 +303,6 @@ impl ProgramBuilder {
303303
reg
304304
}
305305

306-
pub fn drop_register(&mut self) {
307-
self.next_free_register -= 1;
308-
}
309-
310-
pub fn drop_registers(&mut self, amount: usize) {
311-
self.next_free_register -= amount;
312-
}
313-
314306
pub fn next_free_register(&self) -> usize {
315307
self.next_free_register
316308
}
@@ -335,17 +327,6 @@ impl ProgramBuilder {
335327
}
336328
}
337329

338-
pub fn last_insn(&self) -> Option<&Insn> {
339-
self.insns.last()
340-
}
341-
342-
pub fn last_of_type(&self, typ: std::mem::Discriminant<Insn>) -> Option<&Insn> {
343-
self.insns
344-
.iter()
345-
.rev()
346-
.find(|v| std::mem::discriminant(*v) == typ)
347-
}
348-
349330
// Emit an instruction that will be put at the end of the program (after Transaction statement).
350331
// This is useful for instructions that otherwise will be unnecessarily repeated in a loop.
351332
// Example: In `SELECT * from users where name='John'`, it is unnecessary to set r[1]='John' as we SCAN users table.
@@ -1629,7 +1610,7 @@ fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: Stri
16291610
dest,
16301611
table
16311612
.as_ref()
1632-
.and_then(|x| Some(x.get_name()))
1613+
.map(|x| x.get_name())
16331614
.unwrap_or(format!("cursor {}", cursor_id).as_str()),
16341615
table
16351616
.as_ref()
@@ -1766,7 +1747,7 @@ fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: Stri
17661747
&program.cursor_ref[*cursor_id]
17671748
.1
17681749
.as_ref()
1769-
.and_then(|x| Some(x.get_name()))
1750+
.map(|x| x.get_name())
17701751
.unwrap_or(format!("cursor {}", cursor_id).as_str())
17711752
),
17721753
),

core/where_clause.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,11 @@ fn introspect_expression_for_cursors(
518518
)?);
519519
}
520520
ast::Expr::Id(ident) => {
521-
let (_, _, cursor_id) = resolve_ident_table(program, &ident.0, select, cursor_hint)?;
521+
let (_, _, cursor_id, _) = resolve_ident_table(program, &ident.0, select, cursor_hint)?;
522522
cursors.push(cursor_id);
523523
}
524524
ast::Expr::Qualified(tbl, ident) => {
525-
let (_, _, cursor_id) =
525+
let (_, _, cursor_id, _) =
526526
resolve_ident_qualified(program, &tbl.0, &ident.0, select, cursor_hint)?;
527527
cursors.push(cursor_id);
528528
}

testing/orderby.test

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,21 @@ do_execsql_test basic-order-by-and-limit {
2323
9|boots|1.0
2424
2|cap|82.0
2525
10|coat|33.0
26-
1|hat|79.0}
26+
1|hat|79.0}
27+
28+
do_execsql_test basic-order-by-and-limit-2 {
29+
select id, name from products order by name limit 5;
30+
} {11|accessories
31+
9|boots
32+
2|cap
33+
10|coat
34+
1|hat}
35+
36+
do_execsql_test basic-order-by-and-limit-3 {
37+
select price, name from products where price > 70 order by name;
38+
} {81.0|accessories
39+
82.0|cap
40+
79.0|hat
41+
78.0|jeans
42+
82.0|sneakers
43+
74.0|sweatshirt}

0 commit comments

Comments
 (0)