mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 17:05:36 +01:00
feat: support keyword rowid
This commit is contained in:
@@ -1545,6 +1545,15 @@ pub fn translate_expr(
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::RowId { database: _, table } => {
|
||||
let tbl_ref = referenced_tables.as_ref().unwrap().get(*table).unwrap();
|
||||
let cursor_id = program.resolve_cursor_id(&tbl_ref.table_identifier);
|
||||
program.emit_insn(Insn::RowId {
|
||||
cursor_id,
|
||||
dest: target_register,
|
||||
});
|
||||
Ok(target_register)
|
||||
}
|
||||
ast::Expr::InList { .. } => todo!(),
|
||||
ast::Expr::InSelect { .. } => todo!(),
|
||||
ast::Expr::InTable { .. } => todo!(),
|
||||
|
||||
@@ -12,6 +12,8 @@ use crate::{
|
||||
};
|
||||
use sqlite3_parser::ast::{self, Expr, FromClause, JoinType, Limit};
|
||||
|
||||
pub const ROWID: &'static str = "rowid";
|
||||
|
||||
pub struct OperatorIdCounter {
|
||||
id: usize,
|
||||
}
|
||||
@@ -102,8 +104,18 @@ pub fn bind_column_references(
|
||||
if id.0.eq_ignore_ascii_case("true") || id.0.eq_ignore_ascii_case("false") {
|
||||
return Ok(());
|
||||
}
|
||||
let mut match_result = None;
|
||||
let normalized_id = normalize_ident(id.0.as_str());
|
||||
|
||||
if referenced_tables.len() > 0 {
|
||||
if let Some(row_id_expr) =
|
||||
parse_row_id(&normalized_id, 0, || referenced_tables.len() != 1)?
|
||||
{
|
||||
*expr = row_id_expr;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let mut match_result = None;
|
||||
for (tbl_idx, table) in referenced_tables.iter().enumerate() {
|
||||
let col_idx = table
|
||||
.columns()
|
||||
@@ -140,6 +152,12 @@ pub fn bind_column_references(
|
||||
}
|
||||
let tbl_idx = matching_tbl_idx.unwrap();
|
||||
let normalized_id = normalize_ident(id.0.as_str());
|
||||
|
||||
if let Some(row_id_expr) = parse_row_id(&normalized_id, tbl_idx, || false)? {
|
||||
*expr = row_id_expr;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
let col_idx = referenced_tables[tbl_idx]
|
||||
.columns()
|
||||
.iter()
|
||||
@@ -209,7 +227,7 @@ pub fn bind_column_references(
|
||||
Ok(())
|
||||
}
|
||||
// Already bound earlier
|
||||
ast::Expr::Column { .. } => Ok(()),
|
||||
ast::Expr::Column { .. } | ast::Expr::RowId { .. } => Ok(()),
|
||||
ast::Expr::DoublyQualified(_, _, _) => todo!(),
|
||||
ast::Expr::Exists(_) => todo!(),
|
||||
ast::Expr::FunctionCallStar { .. } => Ok(()),
|
||||
@@ -491,17 +509,23 @@ fn parse_join(
|
||||
let left_tables = &tables[..table_index];
|
||||
assert!(!left_tables.is_empty());
|
||||
let right_table = &tables[table_index];
|
||||
let mut left_col = None;
|
||||
let mut left_col =
|
||||
parse_row_id(&name_normalized, 0, || left_tables.len() != 1)?;
|
||||
for (left_table_idx, left_table) in left_tables.iter().enumerate() {
|
||||
if left_col.is_some() {
|
||||
break;
|
||||
}
|
||||
left_col = left_table
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, col)| col.name == name_normalized)
|
||||
.map(|(idx, col)| (left_table_idx, idx, col));
|
||||
if left_col.is_some() {
|
||||
break;
|
||||
}
|
||||
.map(|(idx, col)| ast::Expr::Column {
|
||||
database: None,
|
||||
table: left_table_idx,
|
||||
column: idx,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
});
|
||||
}
|
||||
if left_col.is_none() {
|
||||
crate::bail_parse_error!(
|
||||
@@ -509,33 +533,33 @@ fn parse_join(
|
||||
distinct_name.0
|
||||
);
|
||||
}
|
||||
let right_col = right_table
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, col)| col.name == name_normalized);
|
||||
let right_col =
|
||||
parse_row_id(&name_normalized, right_table.table_index, || false)?.or_else(
|
||||
|| {
|
||||
right_table
|
||||
.table
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, col)| col.name == name_normalized)
|
||||
.map(|(i, col)| ast::Expr::Column {
|
||||
database: None,
|
||||
table: right_table.table_index,
|
||||
column: i,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
})
|
||||
},
|
||||
);
|
||||
if right_col.is_none() {
|
||||
crate::bail_parse_error!(
|
||||
"cannot join using column {} - column not present in all tables",
|
||||
distinct_name.0
|
||||
);
|
||||
}
|
||||
let (left_table_idx, left_col_idx, left_col) = left_col.unwrap();
|
||||
let (right_col_idx, right_col) = right_col.unwrap();
|
||||
using_predicates.push(ast::Expr::Binary(
|
||||
Box::new(ast::Expr::Column {
|
||||
database: None,
|
||||
table: left_table_idx,
|
||||
column: left_col_idx,
|
||||
is_rowid_alias: left_col.is_rowid_alias,
|
||||
}),
|
||||
Box::new(left_col.unwrap()),
|
||||
ast::Operator::Equals,
|
||||
Box::new(ast::Expr::Column {
|
||||
database: None,
|
||||
table: right_table.table_index,
|
||||
column: right_col_idx,
|
||||
is_rowid_alias: right_col.is_rowid_alias,
|
||||
}),
|
||||
Box::new(right_col.unwrap()),
|
||||
));
|
||||
}
|
||||
predicates = Some(using_predicates);
|
||||
@@ -582,3 +606,20 @@ pub fn break_predicate_at_and_boundaries(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_row_id<F>(column_name: &str, table_id: usize, fn_check: F) -> Result<Option<ast::Expr>>
|
||||
where
|
||||
F: FnOnce() -> bool,
|
||||
{
|
||||
if column_name.eq_ignore_ascii_case(ROWID) {
|
||||
if fn_check() {
|
||||
crate::bail_parse_error!("ROWID is ambiguous");
|
||||
}
|
||||
|
||||
return Ok(Some(ast::Expr::RowId {
|
||||
database: None, // TODO: support different databases
|
||||
table: table_id,
|
||||
}));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
@@ -106,6 +106,22 @@ Jamie|coat
|
||||
Jamie|accessories
|
||||
Cindy|}
|
||||
|
||||
do_execsql_test left-join-row-id {
|
||||
select u.rowid, p.rowid from users u left join products as p on u.rowid = p.rowid where u.rowid >= 10 limit 5;
|
||||
} {10|10
|
||||
11|11
|
||||
12|
|
||||
13|
|
||||
14|}
|
||||
|
||||
do_execsql_test left-join-row-id-2 {
|
||||
select u.rowid, p.rowid from users u left join products as p using(rowid) where u.rowid >= 10 limit 5;
|
||||
} {10|10
|
||||
11|11
|
||||
12|
|
||||
13|
|
||||
14|}
|
||||
|
||||
do_execsql_test left-join-constant-condition-true {
|
||||
select u.first_name, p.name from users u left join products as p on true limit 1;
|
||||
} {Jamie|hat}
|
||||
|
||||
@@ -80,6 +80,14 @@ do_execsql_test select_with_quoting_2 {
|
||||
select "users".`id` from users where `users`.[id] = 5;
|
||||
} {5}
|
||||
|
||||
do_execsql_test select-rowid {
|
||||
select rowid, first_name from users u where rowid = 5;
|
||||
} {5|Edward}
|
||||
|
||||
do_execsql_test select-rowid-2 {
|
||||
select users.rowid, first_name from users u where rowid = 5;
|
||||
} {5|Edward}
|
||||
|
||||
do_execsql_test seekrowid {
|
||||
select * from users u where u.id = 5;
|
||||
} {"5|Edward|Miller|christiankramer@example.com|725-281-1033|08522 English Plain|Lake Keith|ID|23283|15"}
|
||||
|
||||
@@ -194,6 +194,9 @@ impl CreateTableBody {
|
||||
{
|
||||
let mut generated_count = 0;
|
||||
for c in columns.values() {
|
||||
if c.col_name == "rowid" {
|
||||
return Err(custom_err!("cannot use reserved word: ROWID"));
|
||||
}
|
||||
for cs in &c.constraints {
|
||||
if let ColumnConstraint::Generated { .. } = cs.constraint {
|
||||
generated_count += 1;
|
||||
|
||||
@@ -728,6 +728,7 @@ impl ToTokens for Expr {
|
||||
}
|
||||
s.append(TK_RP, None)
|
||||
}
|
||||
Self::RowId { .. } => Ok(()),
|
||||
Self::Subquery(query) => {
|
||||
s.append(TK_LP, None)?;
|
||||
query.to_tokens(s)?;
|
||||
|
||||
@@ -338,6 +338,13 @@ pub enum Expr {
|
||||
/// is the column a rowid alias
|
||||
is_rowid_alias: bool,
|
||||
},
|
||||
/// `ROWID`
|
||||
RowId {
|
||||
/// the x in `x.y.z`. index of the db in catalog.
|
||||
database: Option<usize>,
|
||||
/// the y in `x.y.z`. index of the table in catalog.
|
||||
table: usize,
|
||||
},
|
||||
/// `IN`
|
||||
InList {
|
||||
/// expression
|
||||
|
||||
Reference in New Issue
Block a user