mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 00:45:37 +01:00
Implement basic ORDER BY
- Only SELECT * is supported - Only ASC is supported
This commit is contained in:
@@ -152,12 +152,12 @@ impl Cursor for BTreeCursor {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rowid(&self) -> Result<Ref<Option<u64>>> {
|
||||
Ok(self.rowid.borrow())
|
||||
fn rowid(&self) -> Result<Option<u64>> {
|
||||
Ok(*self.rowid.borrow())
|
||||
}
|
||||
|
||||
fn record(&self) -> Result<Ref<Option<OwnedRecord>>> {
|
||||
Ok(self.record.borrow())
|
||||
fn record(&self) -> Result<Option<OwnedRecord>> {
|
||||
Ok(self.record.borrow().to_owned())
|
||||
}
|
||||
|
||||
fn insert(&mut self, _record: &OwnedRecord) -> Result<()> {
|
||||
|
||||
@@ -4,6 +4,7 @@ mod expr;
|
||||
mod function;
|
||||
mod io;
|
||||
mod pager;
|
||||
mod pseudo;
|
||||
mod schema;
|
||||
mod select;
|
||||
mod sorter;
|
||||
|
||||
64
core/pseudo.rs
Normal file
64
core/pseudo.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use anyhow::Result;
|
||||
use std::cell::{Ref, RefCell};
|
||||
|
||||
use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue};
|
||||
|
||||
pub struct PseudoCursor {
|
||||
current: RefCell<Option<OwnedRecord>>,
|
||||
}
|
||||
|
||||
impl PseudoCursor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cursor for PseudoCursor {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.current.borrow().is_none()
|
||||
}
|
||||
|
||||
fn rewind(&mut self) -> Result<CursorResult<()>> {
|
||||
*self.current.borrow_mut() = None;
|
||||
Ok(CursorResult::Ok(()))
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Result<CursorResult<()>> {
|
||||
*self.current.borrow_mut() = None;
|
||||
Ok(CursorResult::Ok(()))
|
||||
}
|
||||
|
||||
fn wait_for_completion(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rowid(&self) -> Result<Option<u64>> {
|
||||
let x = self.current.borrow().as_ref().map(|record| {
|
||||
let rowid = match record.values[0] {
|
||||
OwnedValue::Integer(rowid) => rowid as u64,
|
||||
_ => panic!("Expected integer value"),
|
||||
};
|
||||
rowid
|
||||
});
|
||||
Ok(x)
|
||||
}
|
||||
|
||||
fn record(&self) -> Result<Option<OwnedRecord>> {
|
||||
Ok(self.current.borrow().to_owned())
|
||||
}
|
||||
|
||||
fn insert(&mut self, record: &OwnedRecord) -> Result<()> {
|
||||
*self.current.borrow_mut() = Some(record.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_null_flag(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_null_flag(&mut self, _null_flag: bool) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
@@ -155,11 +155,15 @@ impl BTreeTable {
|
||||
#[derive(Debug)]
|
||||
pub struct PseudoTable {
|
||||
pub columns: Vec<Column>,
|
||||
pub row: Option<OwnedRecord>,
|
||||
}
|
||||
|
||||
impl PseudoTable {
|
||||
pub fn new() -> Self {
|
||||
Self { columns: vec![] }
|
||||
Self {
|
||||
columns: vec![],
|
||||
row: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_column(&mut self, name: &str, ty: Type, primary_key: bool) {
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
use crate::types::{Cursor, CursorResult, OwnedRecord, OwnedValue};
|
||||
use anyhow::Result;
|
||||
use log::trace;
|
||||
use ordered_multimap::ListOrderedMultimap;
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{BTreeMap, VecDeque},
|
||||
};
|
||||
|
||||
pub struct Sorter {
|
||||
records: ListOrderedMultimap<String, OwnedRecord>,
|
||||
current: RefCell<Option<OwnedRecord>>,
|
||||
records: BTreeMap<OwnedRecord, VecDeque<OwnedRecord>>,
|
||||
current: RefCell<Option<VecDeque<OwnedRecord>>>,
|
||||
order: Vec<bool>,
|
||||
}
|
||||
|
||||
impl Sorter {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(order: Vec<bool>) -> Self {
|
||||
Self {
|
||||
records: ListOrderedMultimap::new(),
|
||||
records: BTreeMap::new(),
|
||||
current: RefCell::new(None),
|
||||
order,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: String, record: OwnedRecord) {
|
||||
self.records.insert(key, record);
|
||||
pub fn insert(&mut self, key: OwnedRecord, record: OwnedRecord) {
|
||||
if let Some(vec) = self.records.get_mut(&key) {
|
||||
vec.push_back(record);
|
||||
} else {
|
||||
self.records.insert(key, VecDeque::from(vec![record]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,28 +36,26 @@ impl Cursor for Sorter {
|
||||
}
|
||||
|
||||
fn rewind(&mut self) -> Result<CursorResult<()>> {
|
||||
let current = self.records.pop_front();
|
||||
match current {
|
||||
Some((_, record)) => {
|
||||
*self.current.borrow_mut() = Some(record);
|
||||
}
|
||||
None => {
|
||||
*self.current.borrow_mut() = None;
|
||||
}
|
||||
let empty = {
|
||||
let current = self.current.borrow();
|
||||
current.as_ref().map(|r| r.is_empty()).unwrap_or(true)
|
||||
};
|
||||
if empty {
|
||||
let mut c = self.current.borrow_mut();
|
||||
*c = self.records.pop_first().map(|(_, record)| record);
|
||||
}
|
||||
Ok(CursorResult::Ok(()))
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Result<CursorResult<()>> {
|
||||
let current = self.records.pop_front();
|
||||
match current {
|
||||
Some((_, record)) => {
|
||||
*self.current.borrow_mut() = Some(record);
|
||||
}
|
||||
None => {
|
||||
*self.current.borrow_mut() = None;
|
||||
}
|
||||
let empty = {
|
||||
let current = self.current.borrow();
|
||||
current.as_ref().map(|r| r.is_empty()).unwrap_or(true)
|
||||
};
|
||||
if empty {
|
||||
let mut c = self.current.borrow_mut();
|
||||
*c = self.records.pop_first().map(|(_, record)| record);
|
||||
}
|
||||
Ok(CursorResult::Ok(()))
|
||||
}
|
||||
|
||||
@@ -57,22 +63,20 @@ impl Cursor for Sorter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rowid(&self) -> Result<Ref<Option<u64>>> {
|
||||
fn rowid(&self) -> Result<Option<u64>> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn record(&self) -> Result<Ref<Option<OwnedRecord>>> {
|
||||
Ok(self.current.borrow())
|
||||
fn record(&self) -> Result<Option<OwnedRecord>> {
|
||||
let mut current = self.current.borrow_mut();
|
||||
Ok(current.as_mut().map(|r| r.pop_front().unwrap()))
|
||||
}
|
||||
|
||||
fn insert(&mut self, record: &OwnedRecord) -> Result<()> {
|
||||
let key = match record.values[0] {
|
||||
OwnedValue::Integer(i) => i.to_string(),
|
||||
OwnedValue::Text(ref s) => s.to_string(),
|
||||
_ => todo!(),
|
||||
};
|
||||
trace!("Inserting record with key: {}", key);
|
||||
self.insert(key, record.clone());
|
||||
let key_fields = self.order.len();
|
||||
let key = OwnedRecord::new(record.values[0..key_fields].to_vec());
|
||||
trace!("Inserting record with key: {:?}", key);
|
||||
self.insert(key, OwnedRecord::new(record.values[key_fields..].to_vec()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::pager::Pager;
|
||||
use crate::schema::{Schema, Table};
|
||||
use crate::select::{ColumnInfo, LoopInfo, Select, SrcTable};
|
||||
use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE};
|
||||
use crate::types::{OwnedRecord, OwnedValue};
|
||||
use crate::vdbe::{BranchOffset, Insn, Program, ProgramBuilder};
|
||||
use crate::where_clause::{
|
||||
evaluate_conditions, translate_conditions, translate_where, Inner, Left, QueryConstraint,
|
||||
@@ -56,10 +57,20 @@ fn translate_select(mut select: Select) -> Result<Program> {
|
||||
);
|
||||
let start_offset = program.offset();
|
||||
|
||||
let mut sort_info = if let Some(_) = select.order_by {
|
||||
let mut sort_info = if let Some(order_by) = select.order_by {
|
||||
let sorter_cursor = program.alloc_cursor_id(None, None);
|
||||
let mut order = Vec::new();
|
||||
for col in order_by {
|
||||
order.push(OwnedValue::Integer(if let Some(ord) = col.order {
|
||||
ord as i64
|
||||
} else {
|
||||
0
|
||||
}));
|
||||
}
|
||||
program.emit_insn(Insn::SorterOpen {
|
||||
cursor_id: sorter_cursor,
|
||||
order: OwnedRecord::new(order),
|
||||
columns: select.column_info.len() + 1, // +1 for the key
|
||||
});
|
||||
Some(SortInfo {
|
||||
sorter_cursor,
|
||||
@@ -174,7 +185,7 @@ fn translate_select(mut select: Select) -> Result<Program> {
|
||||
|
||||
// now do the sort for ORDER BY
|
||||
if select.order_by.is_some() {
|
||||
let _ = translate_sorter(&select, &mut program, &sort_info.unwrap());
|
||||
let _ = translate_sorter(&select, &mut program, &sort_info.unwrap(), &limit_info);
|
||||
}
|
||||
|
||||
program.emit_insn(Insn::Halt);
|
||||
@@ -214,6 +225,7 @@ fn translate_sorter(
|
||||
select: &Select,
|
||||
program: &mut ProgramBuilder,
|
||||
sort_info: &SortInfo,
|
||||
limit_info: &Option<LimitInfo>,
|
||||
) -> Result<()> {
|
||||
assert!(sort_info.count > 0);
|
||||
|
||||
@@ -236,12 +248,14 @@ fn translate_sorter(
|
||||
program.emit_insn(Insn::SorterData {
|
||||
cursor_id: sort_info.sorter_cursor,
|
||||
dest_reg: pseudo_content_reg,
|
||||
pseudo_cursor,
|
||||
});
|
||||
let (register_start, count) = translate_columns(program, select, Some(pseudo_cursor))?;
|
||||
program.emit_insn(Insn::ResultRow {
|
||||
start_reg: register_start,
|
||||
count,
|
||||
});
|
||||
emit_limit_insn(&limit_info, program);
|
||||
program.emit_insn(Insn::SorterNext {
|
||||
cursor_id: sort_info.sorter_cursor,
|
||||
pc_if_next: sorter_data_offset,
|
||||
|
||||
@@ -93,6 +93,14 @@ impl std::cmp::PartialOrd<OwnedValue> for OwnedValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::Eq for OwnedValue {}
|
||||
|
||||
impl std::cmp::Ord for OwnedValue {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.partial_cmp(other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<OwnedValue> for OwnedValue {
|
||||
type Output = OwnedValue;
|
||||
|
||||
@@ -267,7 +275,7 @@ impl<'a> Record<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct OwnedRecord {
|
||||
pub values: Vec<OwnedValue>,
|
||||
}
|
||||
@@ -288,8 +296,8 @@ pub trait Cursor {
|
||||
fn rewind(&mut self) -> Result<CursorResult<()>>;
|
||||
fn next(&mut self) -> Result<CursorResult<()>>;
|
||||
fn wait_for_completion(&mut self) -> Result<()>;
|
||||
fn rowid(&self) -> Result<Ref<Option<u64>>>;
|
||||
fn record(&self) -> Result<Ref<Option<OwnedRecord>>>;
|
||||
fn rowid(&self) -> Result<Option<u64>>;
|
||||
fn record(&self) -> Result<Option<OwnedRecord>>;
|
||||
fn insert(&mut self, record: &OwnedRecord) -> Result<()>;
|
||||
fn set_null_flag(&mut self, flag: bool);
|
||||
fn get_null_flag(&self) -> bool;
|
||||
|
||||
98
core/vdbe.rs
98
core/vdbe.rs
@@ -1,6 +1,7 @@
|
||||
use crate::btree::BTreeCursor;
|
||||
use crate::function::{AggFunc, SingleRowFunc};
|
||||
use crate::pager::Pager;
|
||||
use crate::pseudo::PseudoCursor;
|
||||
use crate::schema::Table;
|
||||
use crate::types::{AggContext, Cursor, CursorResult, OwnedRecord, OwnedValue, Record};
|
||||
|
||||
@@ -216,7 +217,9 @@ pub enum Insn {
|
||||
|
||||
// Open a sorter.
|
||||
SorterOpen {
|
||||
cursor_id: CursorID,
|
||||
cursor_id: CursorID, // P1
|
||||
columns: usize, // P2
|
||||
order: OwnedRecord, // P4. 0 if ASC and 1 if DESC
|
||||
},
|
||||
|
||||
// Insert a row into the sorter.
|
||||
@@ -233,8 +236,9 @@ pub enum Insn {
|
||||
|
||||
// Retrieve the next row from the sorter.
|
||||
SorterData {
|
||||
cursor_id: CursorID, // P1
|
||||
dest_reg: usize, // P2
|
||||
cursor_id: CursorID, // P1
|
||||
dest_reg: usize, // P2
|
||||
pseudo_cursor: usize, // P3
|
||||
},
|
||||
|
||||
// Advance to the next row in the sorter.
|
||||
@@ -871,13 +875,12 @@ impl Program {
|
||||
}
|
||||
Insn::OpenPseudo {
|
||||
cursor_id,
|
||||
content_reg,
|
||||
num_fields,
|
||||
content_reg: _,
|
||||
num_fields: _,
|
||||
} => {
|
||||
let _ = cursor_id;
|
||||
let _ = content_reg;
|
||||
let _ = num_fields;
|
||||
todo!();
|
||||
let cursor = Box::new(PseudoCursor::new());
|
||||
cursors.insert(*cursor_id, cursor);
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::RewindAsync { cursor_id } => {
|
||||
let cursor = cursors.get_mut(cursor_id).unwrap();
|
||||
@@ -908,7 +911,7 @@ impl Program {
|
||||
dest,
|
||||
} => {
|
||||
let cursor = cursors.get_mut(cursor_id).unwrap();
|
||||
if let Some(ref record) = *cursor.record()? {
|
||||
if let Some(ref record) = cursor.record()? {
|
||||
let null_flag = cursor.get_null_flag();
|
||||
state.registers[*dest] = if null_flag {
|
||||
OwnedValue::Null
|
||||
@@ -989,7 +992,7 @@ impl Program {
|
||||
}
|
||||
Insn::RowId { cursor_id, dest } => {
|
||||
let cursor = cursors.get_mut(cursor_id).unwrap();
|
||||
if let Some(ref rowid) = *cursor.rowid()? {
|
||||
if let Some(ref rowid) = cursor.rowid()? {
|
||||
state.registers[*dest] = OwnedValue::Integer(*rowid as i64);
|
||||
} else {
|
||||
todo!();
|
||||
@@ -1221,21 +1224,38 @@ impl Program {
|
||||
};
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::SorterOpen { cursor_id } => {
|
||||
let cursor = Box::new(crate::sorter::Sorter::new());
|
||||
Insn::SorterOpen {
|
||||
cursor_id,
|
||||
columns,
|
||||
order,
|
||||
} => {
|
||||
let order = order
|
||||
.values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
OwnedValue::Integer(i) => *i == 0,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
let cursor = Box::new(crate::sorter::Sorter::new(order));
|
||||
cursors.insert(*cursor_id, cursor);
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::SorterData {
|
||||
cursor_id,
|
||||
dest_reg,
|
||||
pseudo_cursor: sorter_cursor,
|
||||
} => {
|
||||
let cursor = cursors.get_mut(cursor_id).unwrap();
|
||||
if let Some(ref record) = *cursor.record()? {
|
||||
state.registers[*dest_reg] = OwnedValue::Record(record.clone());
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
let record = match cursor.record()? {
|
||||
Some(ref record) => record.clone(),
|
||||
None => {
|
||||
todo!();
|
||||
}
|
||||
};
|
||||
state.registers[*dest_reg] = OwnedValue::Record(record.clone());
|
||||
let sorter_cursor = cursors.get_mut(sorter_cursor).unwrap();
|
||||
sorter_cursor.insert(&record)?;
|
||||
state.pc += 1;
|
||||
}
|
||||
Insn::SorterInsert {
|
||||
@@ -1809,23 +1829,45 @@ fn insn_to_str(program: &Program, addr: InsnReference, insn: &Insn, indent: Stri
|
||||
0,
|
||||
format!("accum=r[{}]", *register),
|
||||
),
|
||||
Insn::SorterOpen { cursor_id } => (
|
||||
"SorterOpen",
|
||||
*cursor_id as i32,
|
||||
0,
|
||||
0,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("cursor={}", cursor_id),
|
||||
),
|
||||
Insn::SorterOpen {
|
||||
cursor_id,
|
||||
columns,
|
||||
order,
|
||||
} => {
|
||||
let p4 = String::new();
|
||||
let to_print: Vec<String> = order
|
||||
.values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
OwnedValue::Integer(i) => {
|
||||
if *i == 0 {
|
||||
"B".to_string()
|
||||
} else {
|
||||
"-B".to_string()
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
(
|
||||
"SorterOpen",
|
||||
*cursor_id as i32,
|
||||
*columns as i32,
|
||||
0,
|
||||
OwnedValue::Text(Rc::new(format!("k({},{})", columns, to_print.join(",")))),
|
||||
0,
|
||||
format!("cursor={}", cursor_id),
|
||||
)
|
||||
}
|
||||
Insn::SorterData {
|
||||
cursor_id,
|
||||
dest_reg,
|
||||
pseudo_cursor,
|
||||
} => (
|
||||
"SorterData",
|
||||
*cursor_id as i32,
|
||||
*dest_reg as i32,
|
||||
0,
|
||||
*pseudo_cursor as i32,
|
||||
OwnedValue::Text(Rc::new("".to_string())),
|
||||
0,
|
||||
format!("r[{}]=data", dest_reg),
|
||||
|
||||
@@ -10,3 +10,4 @@ source $testdir/select.test
|
||||
source $testdir/where.test
|
||||
source $testdir/like.test
|
||||
source $testdir/scalar-functions.test
|
||||
source $testdir/orderby.test
|
||||
|
||||
26
testing/orderby.test
Normal file
26
testing/orderby.test
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env tclsh
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
do_execsql_test basic-order-by {
|
||||
select * from products order by price;
|
||||
} {9|boots|1.0
|
||||
3|shirt|18.0
|
||||
4|sweater|25.0
|
||||
10|coat|33.0
|
||||
6|shorts|70.0
|
||||
5|sweatshirt|74.0
|
||||
7|jeans|78.0
|
||||
1|hat|79.0
|
||||
11|accessories|81.0
|
||||
2|cap|82.0
|
||||
8|sneakers|82.0}
|
||||
|
||||
do_execsql_test basic-order-by-and-limit {
|
||||
select * from products order by name limit 5;
|
||||
} {11|accessories|81.0
|
||||
9|boots|1.0
|
||||
2|cap|82.0
|
||||
10|coat|33.0
|
||||
1|hat|79.0}
|
||||
Reference in New Issue
Block a user