mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 17:14:20 +01:00
Optimize and refactor schema::Column type
This commit is contained in:
@@ -2272,42 +2272,28 @@ mod tests {
|
||||
root_page: 2,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("age".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(
|
||||
Some("name".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("age".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2328,42 +2314,28 @@ mod tests {
|
||||
turso_parser::ast::SortOrder::Asc,
|
||||
)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("product_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("product_name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("price".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("product_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(
|
||||
Some("product_name".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("price".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2384,54 +2356,33 @@ mod tests {
|
||||
turso_parser::ast::SortOrder::Asc,
|
||||
)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("order_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("user_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("product_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("quantity".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("order_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("user_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("product_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("quantity".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
has_autoincrement: false,
|
||||
@@ -2449,30 +2400,23 @@ mod tests {
|
||||
root_page: 6,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(
|
||||
Some("name".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2490,54 +2434,33 @@ mod tests {
|
||||
root_page: 7,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("customer_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("vendor_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("quantity".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("customer_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("vendor_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("quantity".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2555,42 +2478,28 @@ mod tests {
|
||||
root_page: 8,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("price".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(
|
||||
Some("name".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("price".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2607,30 +2516,16 @@ mod tests {
|
||||
root_page: 2,
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("product_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("amount".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("product_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("amount".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
|
||||
@@ -685,7 +685,7 @@ impl IncrementalView {
|
||||
|
||||
for table in referenced_tables {
|
||||
// Check if the table has a rowid alias (INTEGER PRIMARY KEY column)
|
||||
let has_rowid_alias = table.columns.iter().any(|col| col.is_rowid_alias);
|
||||
let has_rowid_alias = table.columns.iter().any(|col| col.is_rowid_alias());
|
||||
|
||||
// Select all columns. The circuit will handle filtering and projection
|
||||
// If there's a rowid alias, we don't need to select rowid separately
|
||||
@@ -1398,30 +1398,19 @@ mod tests {
|
||||
root_page: 2,
|
||||
primary_key_columns: vec![("id".to_string(), ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(Some("name".to_string()), "TEXT".to_string(), None),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -1436,42 +1425,35 @@ mod tests {
|
||||
root_page: 3,
|
||||
primary_key_columns: vec![("id".to_string(), ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("customer_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("total".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new(
|
||||
Some("customer_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("total".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -1486,42 +1468,31 @@ mod tests {
|
||||
root_page: 4,
|
||||
primary_key_columns: vec![("id".to_string(), ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("price".to_string()),
|
||||
ty: Type::Real,
|
||||
ty_str: "REAL".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(Some("name".to_string()), "TEXT".to_string(), None),
|
||||
SchemaColumn::new(
|
||||
Some("price".to_string()),
|
||||
"REAL".to_string(),
|
||||
None,
|
||||
Type::Real,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -1536,42 +1507,28 @@ mod tests {
|
||||
root_page: 5,
|
||||
primary_key_columns: vec![], // No primary key, so no rowid alias
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("message".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("level".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("timestamp".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("message".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
Type::Text,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("level".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("timestamp".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true, // Has implicit rowid but no alias
|
||||
is_strict: false,
|
||||
|
||||
@@ -2838,7 +2838,7 @@ impl Statement {
|
||||
.table_references
|
||||
.find_table_by_internal_id(*table)?;
|
||||
let table_column = table_ref.get_column_at(*column_idx)?;
|
||||
match &table_column.ty {
|
||||
match &table_column.ty() {
|
||||
crate::schema::Type::Integer => Some("INTEGER".to_string()),
|
||||
crate::schema::Type::Real => Some("REAL".to_string()),
|
||||
crate::schema::Type::Text => Some("TEXT".to_string()),
|
||||
|
||||
473
core/schema.rs
473
core/schema.rs
@@ -7,8 +7,9 @@ use crate::translate::expr::{
|
||||
use crate::translate::index::{resolve_index_method_parameters, resolve_sorted_columns};
|
||||
use crate::translate::planner::ROWID_STRS;
|
||||
use parking_lot::RwLock;
|
||||
use turso_macros::AtomicEnum;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, AtomicEnum)]
|
||||
pub enum ViewState {
|
||||
Ready,
|
||||
InProgress,
|
||||
@@ -21,7 +22,7 @@ pub struct View {
|
||||
pub sql: String,
|
||||
pub select_stmt: ast::Select,
|
||||
pub columns: Vec<Column>,
|
||||
pub state: Mutex<ViewState>,
|
||||
pub state: AtomicViewState,
|
||||
}
|
||||
|
||||
impl View {
|
||||
@@ -31,28 +32,28 @@ impl View {
|
||||
sql,
|
||||
select_stmt,
|
||||
columns,
|
||||
state: Mutex::new(ViewState::Ready),
|
||||
state: AtomicViewState::new(ViewState::Ready),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&self) -> Result<()> {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
match *state {
|
||||
let state = self.state.get();
|
||||
match state {
|
||||
ViewState::InProgress => {
|
||||
bail_parse_error!("view {} is circularly defined", self.name)
|
||||
}
|
||||
ViewState::Ready => {
|
||||
*state = ViewState::InProgress;
|
||||
self.state.set(ViewState::InProgress);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn done(&self) {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
match *state {
|
||||
let state = self.state.get();
|
||||
match state {
|
||||
ViewState::InProgress => {
|
||||
*state = ViewState::Ready;
|
||||
self.state.set(ViewState::Ready);
|
||||
}
|
||||
ViewState::Ready => {}
|
||||
}
|
||||
@@ -66,7 +67,7 @@ impl Clone for View {
|
||||
sql: self.sql.clone(),
|
||||
select_stmt: self.select_stmt.clone(),
|
||||
columns: self.columns.clone(),
|
||||
state: Mutex::new(ViewState::Ready),
|
||||
state: AtomicViewState::new(ViewState::Ready),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,8 +546,8 @@ impl Schema {
|
||||
table.name
|
||||
)));
|
||||
};
|
||||
if column.primary_key && unique_set.is_primary_key {
|
||||
if column.is_rowid_alias {
|
||||
if column.primary_key() && unique_set.is_primary_key {
|
||||
if column.is_rowid_alias() {
|
||||
// rowid alias, no index needed
|
||||
continue;
|
||||
}
|
||||
@@ -950,7 +951,7 @@ impl Schema {
|
||||
let pk_name = &parent_tbl.primary_key_columns[0].0;
|
||||
// rowid or alias INTEGER PRIMARY KEY; either is ok implicitly
|
||||
parent_tbl.columns.iter().any(|c| {
|
||||
c.is_rowid_alias
|
||||
c.is_rowid_alias()
|
||||
&& c.name
|
||||
.as_deref()
|
||||
.is_some_and(|n| n.eq_ignore_ascii_case(pk_name))
|
||||
@@ -1056,7 +1057,7 @@ impl Schema {
|
||||
let c = parent_cols[0].as_str();
|
||||
ROWID_STRS.iter().any(|&r| r.eq_ignore_ascii_case(c))
|
||||
|| parent_tbl.columns.iter().any(|col| {
|
||||
col.is_rowid_alias
|
||||
col.is_rowid_alias()
|
||||
&& col
|
||||
.name
|
||||
.as_deref()
|
||||
@@ -1325,7 +1326,7 @@ impl BTreeTable {
|
||||
pub fn get_rowid_alias_column(&self) -> Option<(usize, &Column)> {
|
||||
if self.primary_key_columns.len() == 1 {
|
||||
let (idx, col) = self.get_column(&self.primary_key_columns[0].0)?;
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
return Some((idx, col));
|
||||
}
|
||||
}
|
||||
@@ -1384,14 +1385,14 @@ impl BTreeTable {
|
||||
sql.push(' ');
|
||||
sql.push_str(&column.ty_str);
|
||||
}
|
||||
if column.notnull {
|
||||
if column.notnull() {
|
||||
sql.push_str(" NOT NULL");
|
||||
}
|
||||
|
||||
if column.unique {
|
||||
if column.unique() {
|
||||
sql.push_str(" UNIQUE");
|
||||
}
|
||||
if needs_pk_inline && column.primary_key {
|
||||
if needs_pk_inline && column.primary_key() {
|
||||
sql.push_str(" PRIMARY KEY");
|
||||
}
|
||||
|
||||
@@ -1462,8 +1463,11 @@ impl BTreeTable {
|
||||
sql
|
||||
}
|
||||
|
||||
pub fn column_collations(&self) -> Vec<Option<CollationSeq>> {
|
||||
self.columns.iter().map(|column| column.collation).collect()
|
||||
pub fn column_collations(&self) -> Vec<CollationSeq> {
|
||||
self.columns
|
||||
.iter()
|
||||
.map(|column| column.collation())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1826,20 +1830,18 @@ pub fn create_table(tbl_name: &str, body: &CreateTableBody, root_page: i64) -> R
|
||||
primary_key = true;
|
||||
}
|
||||
|
||||
cols.push(Column {
|
||||
name: Some(normalize_ident(&name)),
|
||||
ty,
|
||||
cols.push(Column::new(
|
||||
Some(normalize_ident(&name)),
|
||||
ty_str,
|
||||
primary_key,
|
||||
is_rowid_alias: typename_exactly_integer
|
||||
&& primary_key
|
||||
&& !primary_key_desc_columns_constraint,
|
||||
notnull,
|
||||
default,
|
||||
unique,
|
||||
ty,
|
||||
collation,
|
||||
hidden: false,
|
||||
});
|
||||
primary_key,
|
||||
typename_exactly_integer && primary_key && !primary_key_desc_columns_constraint,
|
||||
notnull,
|
||||
unique,
|
||||
false,
|
||||
));
|
||||
}
|
||||
if options.contains(TableOptions::WITHOUT_ROWID) {
|
||||
has_rowid = false;
|
||||
@@ -1852,7 +1854,7 @@ pub fn create_table(tbl_name: &str, body: &CreateTableBody, root_page: i64) -> R
|
||||
// or if the table has no rowid
|
||||
if !has_rowid || primary_key_columns.len() > 1 {
|
||||
for col in cols.iter_mut() {
|
||||
col.is_rowid_alias = false;
|
||||
col.set_rowid_alias(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1865,14 +1867,14 @@ pub fn create_table(tbl_name: &str, body: &CreateTableBody, root_page: i64) -> R
|
||||
let pk_col = cols.iter().find(|c| c.name.as_deref() == Some(pk_col_name));
|
||||
|
||||
if let Some(col) = pk_col {
|
||||
if col.ty != Type::Integer {
|
||||
if col.ty() != Type::Integer {
|
||||
crate::bail_parse_error!("AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for col in cols.iter() {
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
// Unique sets are used for creating automatic indexes. An index is not created for a rowid alias PRIMARY KEY.
|
||||
// However, an index IS created for a rowid alias UNIQUE, e.g. CREATE TABLE t(x INTEGER PRIMARY KEY, UNIQUE(x))
|
||||
let unique_set_w_only_rowid_alias = unique_sets.iter().position(|us| {
|
||||
@@ -2025,7 +2027,7 @@ impl ResolvedFkRef {
|
||||
.columns
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, c)| c.is_rowid_alias)
|
||||
.find(|(_, c)| c.is_rowid_alias())
|
||||
{
|
||||
return updated_parent_positions.contains(&idx);
|
||||
}
|
||||
@@ -2053,7 +2055,7 @@ impl ResolvedFkRef {
|
||||
// special case: if FK uses a rowid alias on child, and rowid changed
|
||||
if self.child_cols.len() == 1 {
|
||||
let (i, col) = child_tbl.get_column(&self.child_cols[0]).unwrap();
|
||||
if col.is_rowid_alias && updated_child_positions.contains(&i) {
|
||||
if col.is_rowid_alias() && updated_child_positions.contains(&i) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2064,22 +2066,194 @@ impl ResolvedFkRef {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Column {
|
||||
pub name: Option<String>,
|
||||
pub ty: Type,
|
||||
// many sqlite operations like table_info retain the original string
|
||||
pub ty_str: String,
|
||||
pub primary_key: bool,
|
||||
pub is_rowid_alias: bool,
|
||||
pub notnull: bool,
|
||||
pub default: Option<Box<Expr>>,
|
||||
pub unique: bool,
|
||||
pub collation: Option<CollationSeq>,
|
||||
pub hidden: bool,
|
||||
raw: u16,
|
||||
}
|
||||
|
||||
// flags
|
||||
const F_PRIMARY_KEY: u16 = 1;
|
||||
const F_ROWID_ALIAS: u16 = 2;
|
||||
const F_NOTNULL: u16 = 4;
|
||||
const F_UNIQUE: u16 = 8;
|
||||
const F_HIDDEN: u16 = 16;
|
||||
|
||||
// pack Type and Collation in the remaining bits
|
||||
const TYPE_SHIFT: u16 = 5;
|
||||
const TYPE_MASK: u16 = 0b111 << TYPE_SHIFT;
|
||||
const COLL_SHIFT: u16 = TYPE_SHIFT + 3;
|
||||
const COLL_MASK: u16 = 0b11 << COLL_SHIFT;
|
||||
|
||||
impl Column {
|
||||
pub fn affinity(&self) -> Affinity {
|
||||
affinity(&self.ty_str)
|
||||
}
|
||||
pub const fn new_default_text(
|
||||
name: Option<String>,
|
||||
ty_str: String,
|
||||
default: Option<Box<Expr>>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
name,
|
||||
ty_str,
|
||||
default,
|
||||
Type::Text,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
}
|
||||
pub const fn new_default_integer(
|
||||
name: Option<String>,
|
||||
ty_str: String,
|
||||
default: Option<Box<Expr>>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
name,
|
||||
ty_str,
|
||||
default,
|
||||
Type::Integer,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
}
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub const fn new(
|
||||
name: Option<String>,
|
||||
ty_str: String,
|
||||
default: Option<Box<Expr>>,
|
||||
ty: Type,
|
||||
col: Option<CollationSeq>,
|
||||
primary_key: bool,
|
||||
rowid_alias: bool,
|
||||
notnull: bool,
|
||||
unique: bool,
|
||||
hidden: bool,
|
||||
) -> Self {
|
||||
let mut raw = 0u16;
|
||||
raw |= (ty as u16) << TYPE_SHIFT;
|
||||
if let Some(c) = col {
|
||||
raw |= (c as u16) << COLL_SHIFT;
|
||||
}
|
||||
if primary_key {
|
||||
raw |= F_PRIMARY_KEY
|
||||
}
|
||||
if rowid_alias {
|
||||
raw |= F_ROWID_ALIAS
|
||||
}
|
||||
if notnull {
|
||||
raw |= F_NOTNULL
|
||||
}
|
||||
if unique {
|
||||
raw |= F_UNIQUE
|
||||
}
|
||||
if hidden {
|
||||
raw |= F_HIDDEN
|
||||
}
|
||||
Self {
|
||||
name,
|
||||
ty_str,
|
||||
default,
|
||||
raw,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub const fn ty(&self) -> Type {
|
||||
let v = ((self.raw & TYPE_MASK) >> TYPE_SHIFT) as u8;
|
||||
Type::from_bits(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn set_ty(&mut self, ty: Type) {
|
||||
self.raw = (self.raw & !TYPE_MASK) | (((ty as u16) << TYPE_SHIFT) & TYPE_MASK);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn collation_opt(&self) -> Option<CollationSeq> {
|
||||
if self.has_explicit_collation() {
|
||||
Some(self.collation())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn collation(&self) -> CollationSeq {
|
||||
let v = ((self.raw & COLL_MASK) >> COLL_SHIFT) as u8;
|
||||
CollationSeq::from_bits(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn has_explicit_collation(&self) -> bool {
|
||||
let v = ((self.raw & COLL_MASK) >> COLL_SHIFT) as u8;
|
||||
v != CollationSeq::Unset as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn set_collation(&mut self, c: Option<CollationSeq>) {
|
||||
if let Some(c) = c {
|
||||
self.raw = (self.raw & !COLL_MASK) | (((c as u16) << COLL_SHIFT) & COLL_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_key(&self) -> bool {
|
||||
self.raw & F_PRIMARY_KEY != 0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn is_rowid_alias(&self) -> bool {
|
||||
self.raw & F_ROWID_ALIAS != 0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn notnull(&self) -> bool {
|
||||
self.raw & F_NOTNULL != 0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn unique(&self) -> bool {
|
||||
self.raw & F_UNIQUE != 0
|
||||
}
|
||||
#[inline]
|
||||
pub const fn hidden(&self) -> bool {
|
||||
self.raw & F_HIDDEN != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn set_primary_key(&mut self, v: bool) {
|
||||
self.set_flag(F_PRIMARY_KEY, v);
|
||||
}
|
||||
#[inline]
|
||||
pub const fn set_rowid_alias(&mut self, v: bool) {
|
||||
self.set_flag(F_ROWID_ALIAS, v);
|
||||
}
|
||||
#[inline]
|
||||
pub const fn set_notnull(&mut self, v: bool) {
|
||||
self.set_flag(F_NOTNULL, v);
|
||||
}
|
||||
#[inline]
|
||||
pub const fn set_unique(&mut self, v: bool) {
|
||||
self.set_flag(F_UNIQUE, v);
|
||||
}
|
||||
#[inline]
|
||||
pub const fn set_hidden(&mut self, v: bool) {
|
||||
self.set_flag(F_HIDDEN, v);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn set_flag(&mut self, mask: u16, val: bool) {
|
||||
if val {
|
||||
self.raw |= mask
|
||||
} else {
|
||||
self.raw &= !mask
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This might replace some of util::columns_from_create_table_body
|
||||
@@ -2125,18 +2299,18 @@ impl From<&ColumnDefinition> for Column {
|
||||
|
||||
let hidden = ty_str.contains("HIDDEN");
|
||||
|
||||
Column {
|
||||
name: Some(normalize_ident(name)),
|
||||
ty,
|
||||
default,
|
||||
notnull,
|
||||
Column::new(
|
||||
Some(normalize_ident(name)),
|
||||
ty_str,
|
||||
primary_key,
|
||||
is_rowid_alias: primary_key && matches!(ty, Type::Integer),
|
||||
unique,
|
||||
default,
|
||||
ty,
|
||||
collation,
|
||||
primary_key,
|
||||
primary_key && matches!(ty, Type::Integer),
|
||||
notnull,
|
||||
unique,
|
||||
hidden,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2181,14 +2355,30 @@ pub fn affinity(datatype: &str) -> Affinity {
|
||||
Affinity::Numeric
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Type {
|
||||
Null,
|
||||
Text,
|
||||
Numeric,
|
||||
Integer,
|
||||
Real,
|
||||
Blob,
|
||||
Null = 0,
|
||||
Text = 1,
|
||||
Numeric = 2,
|
||||
Integer = 3,
|
||||
Real = 4,
|
||||
Blob = 5,
|
||||
}
|
||||
|
||||
impl Type {
|
||||
#[inline]
|
||||
const fn from_bits(bits: u8) -> Self {
|
||||
match bits {
|
||||
0 => Type::Null,
|
||||
1 => Type::Text,
|
||||
2 => Type::Numeric,
|
||||
3 => Type::Integer,
|
||||
4 => Type::Real,
|
||||
5 => Type::Blob,
|
||||
_ => Type::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # SQLite Column Type Affinities
|
||||
@@ -2341,66 +2531,11 @@ pub fn sqlite_schema_table() -> BTreeTable {
|
||||
has_autoincrement: false,
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![
|
||||
Column {
|
||||
name: Some("type".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
Column {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
Column {
|
||||
name: Some("tbl_name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
Column {
|
||||
name: Some("rootpage".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
Column {
|
||||
name: Some("sql".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
Column::new_default_text(Some("type".to_string()), "TEXT".to_string(), None),
|
||||
Column::new_default_text(Some("name".to_string()), "TEXT".to_string(), None),
|
||||
Column::new_default_text(Some("tbl_name".to_string()), "TEXT".to_string(), None),
|
||||
Column::new_default_integer(Some("rootpage".to_string()), "INT".to_string(), None),
|
||||
Column::new_default_text(Some("sql".to_string()), "TEXT".to_string(), None),
|
||||
],
|
||||
foreign_keys: vec![],
|
||||
unique_sets: vec![],
|
||||
@@ -2540,7 +2675,7 @@ impl Index {
|
||||
name: normalize_ident(col_name),
|
||||
order: *order,
|
||||
pos_in_table,
|
||||
collation: column.collation,
|
||||
collation: column.collation_opt(),
|
||||
default: column.default.clone(),
|
||||
});
|
||||
}
|
||||
@@ -2579,7 +2714,7 @@ impl Index {
|
||||
name: normalize_ident(col.name.as_ref().unwrap()),
|
||||
order: *sort_order,
|
||||
pos_in_table: *pos_in_table,
|
||||
collation: col.collation,
|
||||
collation: col.collation_opt(),
|
||||
default: col.default.clone(),
|
||||
})
|
||||
})
|
||||
@@ -2741,7 +2876,7 @@ mod tests {
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!column.is_rowid_alias,
|
||||
!column.is_rowid_alias(),
|
||||
"column 'a´ has type different than INTEGER so can't be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
@@ -2752,7 +2887,10 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.is_rowid_alias, "column 'a´ should be a rowid alias");
|
||||
assert!(
|
||||
column.is_rowid_alias(),
|
||||
"column 'a´ should be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2762,7 +2900,10 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, PRIMARY KEY(a));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.is_rowid_alias, "column 'a´ should be a rowid alias");
|
||||
assert!(
|
||||
column.is_rowid_alias(),
|
||||
"column 'a´ should be a rowid alias"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2773,7 +2914,7 @@ mod tests {
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!column.is_rowid_alias,
|
||||
!column.is_rowid_alias(),
|
||||
"column 'a´ shouldn't be a rowid alias because table has no rowid"
|
||||
);
|
||||
Ok(())
|
||||
@@ -2785,7 +2926,7 @@ mod tests {
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!column.is_rowid_alias,
|
||||
!column.is_rowid_alias(),
|
||||
"column 'a´ shouldn't be a rowid alias because table has no rowid"
|
||||
);
|
||||
Ok(())
|
||||
@@ -2808,7 +2949,7 @@ mod tests {
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(
|
||||
!column.is_rowid_alias,
|
||||
!column.is_rowid_alias(),
|
||||
"column 'a´ shouldn't be a rowid alias because table has composite primary key"
|
||||
);
|
||||
Ok(())
|
||||
@@ -2819,11 +2960,17 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER PRIMARY KEY, b TEXT, c REAL);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.primary_key, "column 'a' should be a primary key");
|
||||
assert!(column.primary_key(), "column 'a' should be a primary key");
|
||||
let column = table.get_column("b").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'b' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'b' shouldn't be a primary key"
|
||||
);
|
||||
let column = table.get_column("c").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'c' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'c' shouldn't be a primary key"
|
||||
);
|
||||
assert_eq!(
|
||||
vec![("a".to_string(), SortOrder::Asc)],
|
||||
table.primary_key_columns,
|
||||
@@ -2848,11 +2995,17 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, c REAL, PRIMARY KEY(a desc));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.primary_key, "column 'a' should be a primary key");
|
||||
assert!(column.primary_key(), "column 'a' should be a primary key");
|
||||
let column = table.get_column("b").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'b' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'b' shouldn't be a primary key"
|
||||
);
|
||||
let column = table.get_column("c").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'c' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'c' shouldn't be a primary key"
|
||||
);
|
||||
assert_eq!(
|
||||
vec![("a".to_string(), SortOrder::Desc)],
|
||||
table.primary_key_columns,
|
||||
@@ -2866,11 +3019,14 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, c REAL, PRIMARY KEY(a, b desc));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.primary_key, "column 'a' should be a primary key");
|
||||
assert!(column.primary_key(), "column 'a' should be a primary key");
|
||||
let column = table.get_column("b").unwrap().1;
|
||||
assert!(column.primary_key, "column 'b' shouldn be a primary key");
|
||||
assert!(column.primary_key(), "column 'b' shouldn be a primary key");
|
||||
let column = table.get_column("c").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'c' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'c' shouldn't be a primary key"
|
||||
);
|
||||
assert_eq!(
|
||||
vec![
|
||||
("a".to_string(), SortOrder::Asc),
|
||||
@@ -2887,11 +3043,17 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, c REAL, PRIMARY KEY('a'));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.primary_key, "column 'a' should be a primary key");
|
||||
assert!(column.primary_key(), "column 'a' should be a primary key");
|
||||
let column = table.get_column("b").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'b' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'b' shouldn't be a primary key"
|
||||
);
|
||||
let column = table.get_column("c").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'c' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'c' shouldn't be a primary key"
|
||||
);
|
||||
assert_eq!(
|
||||
vec![("a".to_string(), SortOrder::Asc)],
|
||||
table.primary_key_columns,
|
||||
@@ -2904,11 +3066,17 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER, b TEXT, c REAL, PRIMARY KEY("a"));"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.primary_key, "column 'a' should be a primary key");
|
||||
assert!(column.primary_key(), "column 'a' should be a primary key");
|
||||
let column = table.get_column("b").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'b' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'b' shouldn't be a primary key"
|
||||
);
|
||||
let column = table.get_column("c").unwrap().1;
|
||||
assert!(!column.primary_key, "column 'c' shouldn't be a primary key");
|
||||
assert!(
|
||||
!column.primary_key(),
|
||||
"column 'c' shouldn't be a primary key"
|
||||
);
|
||||
assert_eq!(
|
||||
vec![("a".to_string(), SortOrder::Asc)],
|
||||
table.primary_key_columns,
|
||||
@@ -2932,7 +3100,7 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER NOT NULL);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(column.notnull);
|
||||
assert!(column.notnull());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2941,7 +3109,7 @@ mod tests {
|
||||
let sql = r#"CREATE TABLE t1 (a INTEGER);"#;
|
||||
let table = BTreeTable::from_sql(sql, 0)?;
|
||||
let column = table.get_column("a").unwrap().1;
|
||||
assert!(!column.notnull);
|
||||
assert!(!column.notnull());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3029,18 +3197,11 @@ mod tests {
|
||||
is_strict: false,
|
||||
has_autoincrement: false,
|
||||
primary_key_columns: vec![("nonexistent".to_string(), SortOrder::Asc)],
|
||||
columns: vec![Column {
|
||||
name: Some("a".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
}],
|
||||
columns: vec![Column::new_default_integer(
|
||||
Some("a".to_string()),
|
||||
"INT".to_string(),
|
||||
None,
|
||||
)],
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
};
|
||||
|
||||
@@ -109,10 +109,10 @@ fn emit_collseq_if_needed(
|
||||
if let ast::Expr::Column { table, column, .. } = expr {
|
||||
if let Some((_, table_ref)) = referenced_tables.find_table_by_internal_id(*table) {
|
||||
if let Some(table_column) = table_ref.get_column_at(*column) {
|
||||
if let Some(collation) = &table_column.collation {
|
||||
if let Some(c) = table_column.collation_opt() {
|
||||
program.emit_insn(Insn::CollSeq {
|
||||
reg: None,
|
||||
collation: *collation,
|
||||
collation: c,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,13 +110,13 @@ pub fn translate_alter_table(
|
||||
// The column is used in the expression of a generated column.
|
||||
// The column appears in a trigger or view.
|
||||
|
||||
if column.primary_key {
|
||||
if column.primary_key() {
|
||||
return Err(LimboError::ParseError(format!(
|
||||
"cannot drop column \"{column_name}\": PRIMARY KEY"
|
||||
)));
|
||||
}
|
||||
|
||||
if column.unique
|
||||
if column.unique()
|
||||
|| btree.unique_sets.iter().any(|set| {
|
||||
set.columns
|
||||
.iter()
|
||||
|
||||
@@ -161,7 +161,7 @@ pub fn translate_analyze(
|
||||
});
|
||||
};
|
||||
|
||||
if target_schema.columns().iter().any(|c| c.primary_key) {
|
||||
if target_schema.columns().iter().any(|c| c.primary_key()) {
|
||||
bail_parse_error!("ANALYZE on tables with primary key is not supported");
|
||||
}
|
||||
if !target_btree.has_rowid {
|
||||
|
||||
@@ -19,14 +19,13 @@ use crate::{
|
||||
/// **Pre defined collation sequences**\
|
||||
/// Collating functions only matter when comparing string values.
|
||||
/// Numeric values are always compared numerically, and BLOBs are always compared byte-by-byte using memcmp().
|
||||
#[repr(u8)]
|
||||
pub enum CollationSeq {
|
||||
/// Standard String compare
|
||||
Unset = 0,
|
||||
#[default]
|
||||
Binary,
|
||||
/// Ascii case insensitive
|
||||
NoCase,
|
||||
/// Same as Binary but with trimmed whitespace
|
||||
Rtrim,
|
||||
Binary = 1,
|
||||
NoCase = 2,
|
||||
Rtrim = 3,
|
||||
}
|
||||
|
||||
impl CollationSeq {
|
||||
@@ -35,11 +34,20 @@ impl CollationSeq {
|
||||
crate::LimboError::ParseError(format!("no such collation sequence: {collation}"))
|
||||
})
|
||||
}
|
||||
#[inline]
|
||||
/// Returns the collation, defaulting to BINARY if unset
|
||||
pub const fn from_bits(bits: u8) -> Self {
|
||||
match bits {
|
||||
2 => CollationSeq::NoCase,
|
||||
3 => CollationSeq::Rtrim,
|
||||
_ => CollationSeq::Binary,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn compare_strings(&self, lhs: &str, rhs: &str) -> Ordering {
|
||||
match self {
|
||||
CollationSeq::Binary => Self::binary_cmp(lhs, rhs),
|
||||
CollationSeq::Unset | CollationSeq::Binary => Self::binary_cmp(lhs, rhs),
|
||||
CollationSeq::NoCase => Self::nocase_cmp(lhs, rhs),
|
||||
CollationSeq::Rtrim => Self::rtrim_cmp(lhs, rhs),
|
||||
}
|
||||
@@ -112,7 +120,7 @@ pub fn get_collseq_from_expr(
|
||||
.get_column_at(*column)
|
||||
.ok_or_else(|| crate::LimboError::ParseError("column not found".to_string()))?;
|
||||
if maybe_column_collseq.is_none() {
|
||||
maybe_column_collseq = column.collation;
|
||||
maybe_column_collseq = column.collation_opt();
|
||||
}
|
||||
return Ok(WalkControl::Continue);
|
||||
}
|
||||
@@ -123,7 +131,7 @@ pub fn get_collseq_from_expr(
|
||||
if let Some(btree) = table_ref.btree() {
|
||||
if let Some((_, rowid_alias_col)) = btree.get_rowid_alias_column() {
|
||||
if maybe_column_collseq.is_none() {
|
||||
maybe_column_collseq = rowid_alias_col.collation;
|
||||
maybe_column_collseq = rowid_alias_col.collation_opt();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,18 +368,18 @@ mod tests {
|
||||
is_strict: false,
|
||||
name: "foo".to_string(),
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![Column {
|
||||
name: Some("foo".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "text".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
columns: vec![Column::new(
|
||||
Some("foo".to_string()),
|
||||
"text".to_string(),
|
||||
None,
|
||||
Type::Text,
|
||||
collation,
|
||||
hidden: false,
|
||||
}],
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
})),
|
||||
@@ -403,18 +411,18 @@ mod tests {
|
||||
is_strict: false,
|
||||
name: "t1".to_string(),
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![Column {
|
||||
name: Some("a".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "text".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: left,
|
||||
hidden: false,
|
||||
}],
|
||||
columns: vec![Column::new(
|
||||
Some("a".to_string()),
|
||||
"text".to_string(),
|
||||
None,
|
||||
Type::Text,
|
||||
left,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
})),
|
||||
@@ -437,18 +445,18 @@ mod tests {
|
||||
is_strict: false,
|
||||
name: "t2".to_string(),
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![Column {
|
||||
name: Some("b".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "text".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: right,
|
||||
hidden: false,
|
||||
}],
|
||||
columns: vec![Column::new(
|
||||
Some("b".to_string()),
|
||||
"text".to_string(),
|
||||
None,
|
||||
Type::Text,
|
||||
right,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
})),
|
||||
@@ -478,18 +486,18 @@ mod tests {
|
||||
is_strict: false,
|
||||
name: "bar".to_string(),
|
||||
primary_key_columns: vec![("id".to_string(), SortOrder::Asc)],
|
||||
columns: vec![Column {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: true,
|
||||
columns: vec![Column::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
collation,
|
||||
hidden: false,
|
||||
}],
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
)],
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
})),
|
||||
|
||||
@@ -544,7 +544,7 @@ pub fn emit_fk_child_decrement_on_delete(
|
||||
let null_skip = program.allocate_label();
|
||||
for cname in &fk_ref.child_cols {
|
||||
let (pos, col) = child_tbl.get_column(cname).unwrap();
|
||||
let src = if col.is_rowid_alias {
|
||||
let src = if col.is_rowid_alias() {
|
||||
child_rowid_reg
|
||||
} else {
|
||||
let tmp = program.alloc_register();
|
||||
@@ -571,7 +571,7 @@ pub fn emit_fk_child_decrement_on_delete(
|
||||
let pcur = open_read_table(program, &parent_tbl);
|
||||
|
||||
let (pos, col) = child_tbl.get_column(&fk_ref.child_cols[0]).unwrap();
|
||||
let val = if col.is_rowid_alias {
|
||||
let val = if col.is_rowid_alias() {
|
||||
child_rowid_reg
|
||||
} else {
|
||||
let tmp = program.alloc_register();
|
||||
@@ -624,7 +624,7 @@ pub fn emit_fk_child_decrement_on_delete(
|
||||
let probe = program.alloc_registers(n);
|
||||
for (i, cname) in fk_ref.child_cols.iter().enumerate() {
|
||||
let (pos, col) = child_tbl.get_column(cname).unwrap();
|
||||
let src = if col.is_rowid_alias {
|
||||
let src = if col.is_rowid_alias() {
|
||||
child_rowid_reg
|
||||
} else {
|
||||
let r = program.alloc_register();
|
||||
@@ -1136,7 +1136,7 @@ fn emit_update_insns(
|
||||
.table
|
||||
.columns()
|
||||
.iter()
|
||||
.position(|c| c.is_rowid_alias);
|
||||
.position(|c| c.is_rowid_alias());
|
||||
|
||||
let has_direct_rowid_update = plan
|
||||
.set_clauses
|
||||
@@ -1232,7 +1232,7 @@ fn emit_update_insns(
|
||||
continue;
|
||||
}
|
||||
if has_user_provided_rowid
|
||||
&& (table_column.primary_key || table_column.is_rowid_alias)
|
||||
&& (table_column.primary_key() || table_column.is_rowid_alias())
|
||||
&& !is_virtual
|
||||
{
|
||||
let rowid_set_clause_reg = rowid_set_clause_reg.unwrap();
|
||||
@@ -1257,7 +1257,7 @@ fn emit_update_insns(
|
||||
target_reg,
|
||||
&t_ctx.resolver,
|
||||
)?;
|
||||
if table_column.notnull {
|
||||
if table_column.notnull() {
|
||||
use crate::error::SQLITE_CONSTRAINT_NOTNULL;
|
||||
program.emit_insn(Insn::HaltIfNull {
|
||||
target_reg,
|
||||
@@ -1303,7 +1303,7 @@ fn emit_update_insns(
|
||||
|
||||
// don't emit null for pkey of virtual tables. they require first two args
|
||||
// before the 'record' to be explicitly non-null
|
||||
if table_column.is_rowid_alias && !is_virtual {
|
||||
if table_column.is_rowid_alias() && !is_virtual {
|
||||
program.emit_null(target_reg, None);
|
||||
} else if is_virtual {
|
||||
program.emit_insn(Insn::VColumn {
|
||||
@@ -1511,7 +1511,7 @@ fn emit_update_insns(
|
||||
.get(col.pos_in_table)
|
||||
.expect("column index out of bounds");
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: if col_in_table.is_rowid_alias {
|
||||
src_reg: if col_in_table.is_rowid_alias() {
|
||||
rowid_reg
|
||||
} else {
|
||||
start + col.pos_in_table
|
||||
@@ -1892,7 +1892,7 @@ pub fn emit_cdc_patch_record(
|
||||
rowid_reg: usize,
|
||||
) -> usize {
|
||||
let columns = table.columns();
|
||||
let rowid_alias_position = columns.iter().position(|x| x.is_rowid_alias);
|
||||
let rowid_alias_position = columns.iter().position(|x| x.is_rowid_alias());
|
||||
if let Some(rowid_alias_position) = rowid_alias_position {
|
||||
let record_reg = program.alloc_register();
|
||||
program.emit_insn(Insn::Copy {
|
||||
@@ -1927,7 +1927,7 @@ pub fn emit_cdc_full_record(
|
||||
) -> usize {
|
||||
let columns_reg = program.alloc_registers(columns.len() + 1);
|
||||
for (i, column) in columns.iter().enumerate() {
|
||||
if column.is_rowid_alias {
|
||||
if column.is_rowid_alias() {
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: rowid_reg,
|
||||
dst_reg: columns_reg + 1 + i,
|
||||
@@ -2181,7 +2181,7 @@ fn rewrite_where_for_update_registers(
|
||||
.as_ref()
|
||||
.is_some_and(|n| n.eq_ignore_ascii_case(&normalized))
|
||||
}) {
|
||||
if c.is_rowid_alias {
|
||||
if c.is_rowid_alias() {
|
||||
*e = Expr::Register(rowid_reg);
|
||||
} else {
|
||||
*e = Expr::Register(columns_start_reg + idx);
|
||||
@@ -2200,7 +2200,7 @@ fn rewrite_where_for_update_registers(
|
||||
.as_ref()
|
||||
.is_some_and(|n| n.eq_ignore_ascii_case(&normalized))
|
||||
}) {
|
||||
if c.is_rowid_alias {
|
||||
if c.is_rowid_alias() {
|
||||
*e = Expr::Register(rowid_reg);
|
||||
} else {
|
||||
*e = Expr::Register(columns_start_reg + idx);
|
||||
|
||||
@@ -2133,7 +2133,7 @@ pub fn translate_expr(
|
||||
crate::bail_parse_error!("column index out of bounds");
|
||||
};
|
||||
// Counter intuitive but a column always needs to have a collation
|
||||
program.set_collation(Some((table_column.collation.unwrap_or_default(), false)));
|
||||
program.set_collation(Some((table_column.collation(), false)));
|
||||
}
|
||||
|
||||
// If we are reading a column from a table, we find the cursor that corresponds to
|
||||
@@ -2222,7 +2222,7 @@ pub fn translate_expr(
|
||||
let Some(column) = table.get_column_at(*column) else {
|
||||
crate::bail_parse_error!("column index out of bounds");
|
||||
};
|
||||
maybe_apply_affinity(column.ty, target_register, program);
|
||||
maybe_apply_affinity(column.ty(), target_register, program);
|
||||
}
|
||||
Ok(target_register)
|
||||
}
|
||||
@@ -3701,7 +3701,7 @@ pub fn bind_and_rewrite_expr<'a>(
|
||||
match_result = Some((
|
||||
joined_table.internal_id,
|
||||
col_idx.unwrap(),
|
||||
col.is_rowid_alias,
|
||||
col.is_rowid_alias(),
|
||||
));
|
||||
}
|
||||
// only if we haven't found a match, check for explicit rowid reference
|
||||
@@ -3743,7 +3743,7 @@ pub fn bind_and_rewrite_expr<'a>(
|
||||
match_result = Some((
|
||||
outer_ref.internal_id,
|
||||
col_idx.unwrap(),
|
||||
col.is_rowid_alias,
|
||||
col.is_rowid_alias(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -3822,7 +3822,7 @@ pub fn bind_and_rewrite_expr<'a>(
|
||||
database: None, // TODO: support different databases
|
||||
table: tbl_id,
|
||||
column: col_idx,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
is_rowid_alias: col.is_rowid_alias(),
|
||||
};
|
||||
tracing::debug!("rewritten to column");
|
||||
referenced_tables.mark_column_used(tbl_id, col_idx);
|
||||
@@ -3882,7 +3882,7 @@ pub fn bind_and_rewrite_expr<'a>(
|
||||
let col = table.columns().get(col_idx).unwrap();
|
||||
|
||||
// Check if this is a rowid alias
|
||||
let is_rowid_alias = col.is_rowid_alias;
|
||||
let is_rowid_alias = col.is_rowid_alias();
|
||||
|
||||
// Convert to Column expression - since this is a cross-database reference,
|
||||
// we need to create a synthetic table reference for it
|
||||
|
||||
@@ -245,7 +245,7 @@ pub fn stabilize_new_row_for_fk(
|
||||
.get_column(pk_name)
|
||||
.ok_or_else(|| crate::LimboError::InternalError(format!("pk col {pk_name} missing")))?;
|
||||
if !set_cols.contains(&pos) {
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: rowid_new_reg,
|
||||
dst_reg: start + pos,
|
||||
@@ -388,7 +388,7 @@ pub fn emit_parent_index_key_change_checks(
|
||||
for (i, index_col) in index.columns.iter().enumerate() {
|
||||
let pos_in_table = index_col.pos_in_table;
|
||||
let column = &table_btree.columns[pos_in_table];
|
||||
if column.is_rowid_alias {
|
||||
if column.is_rowid_alias() {
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: old_rowid_reg,
|
||||
dst_reg: old_key + i,
|
||||
@@ -407,7 +407,7 @@ pub fn emit_parent_index_key_change_checks(
|
||||
for (i, index_col) in index.columns.iter().enumerate() {
|
||||
let pos_in_table = index_col.pos_in_table;
|
||||
let column = &table_btree.columns[pos_in_table];
|
||||
let src = if column.is_rowid_alias {
|
||||
let src = if column.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_values_start + pos_in_table
|
||||
@@ -565,7 +565,7 @@ fn build_parent_key(
|
||||
let (pos, col) = parent_bt
|
||||
.get_column(pcol)
|
||||
.ok_or_else(|| crate::LimboError::InternalError(format!("col {pcol} missing")))?;
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
parent_rowid_reg
|
||||
} else {
|
||||
program.emit_insn(Insn::Column {
|
||||
@@ -716,7 +716,7 @@ pub fn emit_fk_child_update_counters(
|
||||
let fk_ok = program.allocate_label();
|
||||
for cname in &fk_ref.fk.child_columns {
|
||||
let (i, col) = child_tbl.get_column(cname).unwrap();
|
||||
let src = if col.is_rowid_alias {
|
||||
let src = if col.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i
|
||||
@@ -736,7 +736,7 @@ pub fn emit_fk_child_update_counters(
|
||||
|
||||
// Take the first child column value from NEW image
|
||||
let (i_child, col_child) = child_tbl.get_column(&fk_ref.child_cols[0]).unwrap();
|
||||
let val_reg = if col_child.is_rowid_alias {
|
||||
let val_reg = if col_child.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i_child
|
||||
@@ -781,7 +781,7 @@ pub fn emit_fk_child_update_counters(
|
||||
for (k, cname) in fk_ref.child_cols.iter().enumerate() {
|
||||
let (i, col) = child_tbl.get_column(cname).unwrap();
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: if col.is_rowid_alias {
|
||||
src_reg: if col.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i
|
||||
|
||||
@@ -522,7 +522,7 @@ pub fn resolve_sorted_columns(
|
||||
name: col.1.name.as_ref().unwrap().clone(),
|
||||
order: sc.order.unwrap_or(SortOrder::Asc),
|
||||
pos_in_table: col.0,
|
||||
collation: col.1.collation,
|
||||
collation: col.1.collation_opt(),
|
||||
default: col.1.default.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -858,10 +858,10 @@ fn emit_notnulls(program: &mut ProgramBuilder, ctx: &InsertEmitCtx, insertion: &
|
||||
for column_mapping in insertion
|
||||
.col_mappings
|
||||
.iter()
|
||||
.filter(|column_mapping| column_mapping.column.notnull)
|
||||
.filter(|column_mapping| column_mapping.column.notnull())
|
||||
{
|
||||
// if this is rowid alias - turso-db will emit NULL as a column value and always use rowid for the row as a column value
|
||||
if column_mapping.column.is_rowid_alias {
|
||||
if column_mapping.column.is_rowid_alias() {
|
||||
continue;
|
||||
}
|
||||
program.emit_insn(Insn::HaltIfNull {
|
||||
@@ -918,7 +918,7 @@ fn bind_insert(
|
||||
values = table
|
||||
.columns()
|
||||
.iter()
|
||||
.filter(|c| !c.hidden)
|
||||
.filter(|c| !c.hidden())
|
||||
.map(|c| {
|
||||
c.default
|
||||
.clone()
|
||||
@@ -1133,7 +1133,7 @@ fn init_source_emission<'a>(
|
||||
ctx.table
|
||||
.columns
|
||||
.iter()
|
||||
.filter(|col| !col.hidden)
|
||||
.filter(|col| !col.hidden())
|
||||
.map(|col| col.affinity().aff_mask())
|
||||
.collect::<String>()
|
||||
} else {
|
||||
@@ -1237,18 +1237,18 @@ pub struct AutoincMeta {
|
||||
table_name_reg: usize,
|
||||
}
|
||||
|
||||
pub const ROWID_COLUMN: &Column = &Column {
|
||||
name: None,
|
||||
ty: schema::Type::Integer,
|
||||
ty_str: String::new(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
};
|
||||
pub const ROWID_COLUMN: Column = Column::new(
|
||||
None, // name
|
||||
String::new(), // type string
|
||||
None, // default
|
||||
schema::Type::Integer,
|
||||
None,
|
||||
true, // primary key
|
||||
true, // rowid alias
|
||||
true, // notnull
|
||||
false, // unique
|
||||
false, // hidden
|
||||
);
|
||||
|
||||
/// Represents how a table should be populated during an INSERT.
|
||||
#[derive(Debug)]
|
||||
@@ -1389,7 +1389,7 @@ fn build_insertion<'a>(
|
||||
|
||||
if columns.is_empty() {
|
||||
// Case 1: No columns specified - map values to columns in order
|
||||
if num_values != table_columns.iter().filter(|c| !c.hidden).count() {
|
||||
if num_values != table_columns.iter().filter(|c| !c.hidden()).count() {
|
||||
crate::bail_parse_error!(
|
||||
"table {} has {} columns but {} values were supplied",
|
||||
&table.get_name(),
|
||||
@@ -1399,11 +1399,11 @@ fn build_insertion<'a>(
|
||||
}
|
||||
let mut value_idx = 0;
|
||||
for (i, col) in table_columns.iter().enumerate() {
|
||||
if col.hidden {
|
||||
if col.hidden() {
|
||||
// Hidden columns are not taken into account.
|
||||
continue;
|
||||
}
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
insertion_key = InsertionKey::RowidAlias(ColMapping {
|
||||
column: col,
|
||||
value_index: Some(value_idx),
|
||||
@@ -1421,7 +1421,7 @@ fn build_insertion<'a>(
|
||||
let column_name = normalize_ident(column_name.as_str());
|
||||
if let Some((idx_in_table, col_in_table)) = table.get_column_by_name(&column_name) {
|
||||
// Named column
|
||||
if col_in_table.is_rowid_alias {
|
||||
if col_in_table.is_rowid_alias() {
|
||||
insertion_key = InsertionKey::RowidAlias(ColMapping {
|
||||
column: col_in_table,
|
||||
value_index: Some(value_index),
|
||||
@@ -1435,7 +1435,7 @@ fn build_insertion<'a>(
|
||||
.any(|s| s.eq_ignore_ascii_case(&column_name))
|
||||
{
|
||||
// Explicit use of the 'rowid' keyword
|
||||
if let Some(col_in_table) = table.columns().iter().find(|c| c.is_rowid_alias) {
|
||||
if let Some(col_in_table) = table.columns().iter().find(|c| c.is_rowid_alias()) {
|
||||
insertion_key = InsertionKey::RowidAlias(ColMapping {
|
||||
column: col_in_table,
|
||||
value_index: Some(value_index),
|
||||
@@ -1574,7 +1574,7 @@ fn translate_key(
|
||||
register,
|
||||
} => translate_column(
|
||||
program,
|
||||
ROWID_COLUMN,
|
||||
&ROWID_COLUMN,
|
||||
*register,
|
||||
*value_index,
|
||||
&mut translate_value_fn,
|
||||
@@ -1594,13 +1594,13 @@ fn translate_column(
|
||||
) -> Result<()> {
|
||||
if let Some(value_index) = value_index {
|
||||
translate_value_fn(program, value_index, column_register)?;
|
||||
} else if column.is_rowid_alias {
|
||||
} else if column.is_rowid_alias() {
|
||||
// Although a non-NULL integer key is used for the insertion key,
|
||||
// the rowid alias column is emitted as NULL.
|
||||
program.emit_insn(Insn::SoftNull {
|
||||
reg: column_register,
|
||||
});
|
||||
} else if column.hidden {
|
||||
} else if column.hidden() {
|
||||
// Emit NULL for not-explicitly-mentioned hidden columns, even ignoring DEFAULT.
|
||||
program.emit_insn(Insn::Null {
|
||||
dest: column_register,
|
||||
@@ -1609,7 +1609,7 @@ fn translate_column(
|
||||
} else if let Some(default_expr) = column.default.as_ref() {
|
||||
translate_expr(program, None, default_expr, column_register, resolver)?;
|
||||
} else {
|
||||
let nullable = !column.notnull && !column.primary_key && !column.unique;
|
||||
let nullable = !column.notnull() && !column.primary_key() && !column.unique();
|
||||
if !nullable {
|
||||
crate::bail_parse_error!(
|
||||
"column {} is not nullable",
|
||||
@@ -2063,7 +2063,7 @@ pub fn rewrite_partial_index_where(
|
||||
if ROWID_STRS.iter().any(|s| s.eq_ignore_ascii_case(name)) {
|
||||
Some(insertion.key_register())
|
||||
} else if let Some(c) = insertion.get_col_mapping_by_name(name) {
|
||||
if c.column.is_rowid_alias {
|
||||
if c.column.is_rowid_alias() {
|
||||
Some(insertion.key_register())
|
||||
} else {
|
||||
Some(c.register)
|
||||
@@ -2231,7 +2231,7 @@ pub fn emit_fk_child_insert_checks(
|
||||
let fk_ok = program.allocate_label();
|
||||
for cname in &fk_ref.child_cols {
|
||||
let (i, col) = child_tbl.get_column(cname).unwrap();
|
||||
let src = if col.is_rowid_alias {
|
||||
let src = if col.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i
|
||||
@@ -2250,7 +2250,7 @@ pub fn emit_fk_child_insert_checks(
|
||||
|
||||
// first child col carries rowid
|
||||
let (i_child, col_child) = child_tbl.get_column(&fk_ref.child_cols[0]).unwrap();
|
||||
let val_reg = if col_child.is_rowid_alias {
|
||||
let val_reg = if col_child.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i_child
|
||||
@@ -2305,7 +2305,7 @@ pub fn emit_fk_child_insert_checks(
|
||||
for (k, cname) in fk_ref.child_cols.iter().enumerate() {
|
||||
let (i, col) = child_tbl.get_column(cname).unwrap();
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: if col.is_rowid_alias {
|
||||
src_reg: if col.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + i
|
||||
@@ -2333,7 +2333,7 @@ pub fn emit_fk_child_insert_checks(
|
||||
for (i, pname) in parent_cols.iter().enumerate() {
|
||||
let (pos, col) = child_tbl.get_column(pname).unwrap();
|
||||
program.emit_insn(Insn::Copy {
|
||||
src_reg: if col.is_rowid_alias {
|
||||
src_reg: if col.is_rowid_alias() {
|
||||
new_rowid_reg
|
||||
} else {
|
||||
new_start_reg + pos
|
||||
|
||||
@@ -2273,7 +2273,7 @@ impl<'a> LogicalPlanBuilder<'a> {
|
||||
if let Some(ref name) = col.name {
|
||||
columns.push(ColumnInfo {
|
||||
name: name.clone(),
|
||||
ty: col.ty,
|
||||
ty: col.ty(),
|
||||
database: database.clone(),
|
||||
table: Some(actual_table.clone()),
|
||||
table_alias: alias.map(|s| s.to_string()),
|
||||
@@ -2391,54 +2391,25 @@ mod tests {
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
foreign_keys: vec![],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("age".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("email".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(Some("name".to_string()), "TEXT".to_string(), None),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("age".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_text(Some("email".to_string()), "TEXT".to_string(), None),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2455,54 +2426,40 @@ mod tests {
|
||||
root_page: 3,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("user_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("product".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("amount".to_string()),
|
||||
ty: Type::Real,
|
||||
ty_str: "REAL".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("user_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new_default_text(
|
||||
Some("product".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
SchemaColumn::new(
|
||||
Some("amount".to_string()),
|
||||
"REAL".to_string(),
|
||||
None,
|
||||
Type::Real,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
@@ -2520,54 +2477,36 @@ mod tests {
|
||||
root_page: 4,
|
||||
primary_key_columns: vec![("id".to_string(), turso_parser::ast::SortOrder::Asc)],
|
||||
columns: vec![
|
||||
SchemaColumn {
|
||||
name: Some("id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: true,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("name".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("price".to_string()),
|
||||
ty: Type::Real,
|
||||
ty_str: "REAL".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn {
|
||||
name: Some("product_id".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
SchemaColumn::new(
|
||||
Some("id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_text(Some("name".to_string()), "TEXT".to_string(), None),
|
||||
SchemaColumn::new(
|
||||
Some("price".to_string()),
|
||||
"REAL".to_string(),
|
||||
None,
|
||||
Type::Real,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
SchemaColumn::new_default_integer(
|
||||
Some("product_id".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
),
|
||||
],
|
||||
has_rowid: true,
|
||||
is_strict: false,
|
||||
|
||||
@@ -1488,9 +1488,10 @@ fn emit_seek_termination(
|
||||
affinity = if let Some(table_ref) = tables
|
||||
.joined_tables()
|
||||
.iter()
|
||||
.find(|t| t.columns().iter().any(|c| c.is_rowid_alias))
|
||||
.find(|t| t.columns().iter().any(|c| c.is_rowid_alias()))
|
||||
{
|
||||
if let Some(rowid_col_idx) = table_ref.columns().iter().position(|c| c.is_rowid_alias())
|
||||
{
|
||||
if let Some(rowid_col_idx) = table_ref.columns().iter().position(|c| c.is_rowid_alias) {
|
||||
Some(table_ref.columns()[rowid_col_idx].affinity())
|
||||
} else {
|
||||
Some(Affinity::Numeric)
|
||||
|
||||
@@ -101,7 +101,7 @@ fn find_best_access_method_for_btree(
|
||||
index: None,
|
||||
constraint_refs: vec![],
|
||||
};
|
||||
let rowid_column_idx = rhs_table.columns().iter().position(|c| c.is_rowid_alias);
|
||||
let rowid_column_idx = rhs_table.columns().iter().position(|c| c.is_rowid_alias());
|
||||
|
||||
// Estimate cost for each candidate index (including the rowid index) and replace best_access_method if the cost is lower.
|
||||
for candidate in rhs_constraints.candidates.iter() {
|
||||
|
||||
@@ -165,7 +165,7 @@ const SELECTIVITY_UNIQUE_EQUALITY: f64 = 1.0 / ESTIMATED_HARDCODED_ROWS_PER_TABL
|
||||
fn estimate_selectivity(column: &Column, op: ast::Operator) -> f64 {
|
||||
match op {
|
||||
ast::Operator::Equals => {
|
||||
if column.is_rowid_alias || column.primary_key {
|
||||
if column.is_rowid_alias() || column.primary_key() {
|
||||
SELECTIVITY_UNIQUE_EQUALITY
|
||||
} else {
|
||||
SELECTIVITY_EQ
|
||||
@@ -197,7 +197,7 @@ pub fn constraints_from_where_clause(
|
||||
let rowid_alias_column = table_reference
|
||||
.columns()
|
||||
.iter()
|
||||
.position(|c| c.is_rowid_alias);
|
||||
.position(|c| c.is_rowid_alias());
|
||||
|
||||
let mut cs = TableConstraints {
|
||||
table_id: table_reference.internal_id,
|
||||
@@ -313,7 +313,7 @@ pub fn constraints_from_where_clause(
|
||||
// For each constraint we found, add a reference to it for each index that may be able to use it.
|
||||
for (i, constraint) in cs.constraints.iter_mut().enumerate() {
|
||||
let constrained_column = &table_reference.table.columns()[constraint.table_col_pos];
|
||||
let column_collation = constrained_column.collation.unwrap_or_default();
|
||||
let column_collation = constrained_column.collation();
|
||||
let constraining_expr = constraint.get_constraining_expr_ref(where_clause);
|
||||
// Index seek keys must use the same collation as the constrained column.
|
||||
match get_collseq_from_expr(constraining_expr, table_references)? {
|
||||
|
||||
@@ -1670,18 +1670,18 @@ mod tests {
|
||||
}
|
||||
|
||||
fn _create_column(c: &TestColumn) -> Column {
|
||||
Column {
|
||||
name: Some(c.name.clone()),
|
||||
ty: c.ty,
|
||||
ty_str: c.ty.to_string(),
|
||||
is_rowid_alias: c.is_rowid_alias,
|
||||
primary_key: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
}
|
||||
Column::new(
|
||||
Some(c.name.clone()),
|
||||
c.ty.to_string(),
|
||||
None,
|
||||
c.ty,
|
||||
None,
|
||||
c.is_rowid_alias,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
}
|
||||
fn _create_column_of_type(name: &str, ty: Type) -> Column {
|
||||
_create_column(&TestColumn {
|
||||
|
||||
@@ -16,8 +16,9 @@ use turso_ext::{ConstraintInfo, ConstraintUsage};
|
||||
use turso_parser::ast::{self, Expr, SortOrder};
|
||||
|
||||
use crate::{
|
||||
schema::{BTreeTable, Column, Index, IndexColumn, Schema, Table, Type, ROWID_SENTINEL},
|
||||
schema::{BTreeTable, Index, IndexColumn, Schema, Table, ROWID_SENTINEL},
|
||||
translate::{
|
||||
insert::ROWID_COLUMN,
|
||||
optimizer::{
|
||||
access_method::AccessMethodParams,
|
||||
constraints::{RangeConstraintRef, SeekRangeConstraint, TableConstraints},
|
||||
@@ -167,7 +168,7 @@ fn optimize_update_plan(
|
||||
};
|
||||
let Some(index) = table_ref.op.index() else {
|
||||
let rowid_alias_used = plan.set_clauses.iter().fold(false, |accum, (idx, _)| {
|
||||
accum || (*idx != ROWID_SENTINEL && btree_table.columns[*idx].is_rowid_alias)
|
||||
accum || (*idx != ROWID_SENTINEL && btree_table.columns[*idx].is_rowid_alias())
|
||||
});
|
||||
if rowid_alias_used {
|
||||
break 'requires true;
|
||||
@@ -217,18 +218,7 @@ fn add_ephemeral_table_to_update_plan(
|
||||
has_rowid: true,
|
||||
has_autoincrement: false,
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![Column {
|
||||
name: Some("rowid".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: true,
|
||||
is_rowid_alias: false,
|
||||
notnull: true,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
}],
|
||||
columns: vec![ROWID_COLUMN],
|
||||
is_strict: false,
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
@@ -1034,7 +1024,7 @@ impl Optimizable for ast::Expr {
|
||||
.expect("table not found");
|
||||
let columns = table_ref.columns();
|
||||
let column = &columns[*column];
|
||||
column.primary_key || column.notnull
|
||||
column.primary_key() || column.notnull()
|
||||
}
|
||||
Expr::RowId { .. } => true,
|
||||
Expr::InList { lhs, rhs, .. } => {
|
||||
@@ -1265,7 +1255,7 @@ fn ephemeral_index_build(
|
||||
name: c.name.clone().unwrap(),
|
||||
order: SortOrder::Asc,
|
||||
pos_in_table: i,
|
||||
collation: c.collation,
|
||||
collation: c.collation_opt(),
|
||||
default: c.default.clone(),
|
||||
})
|
||||
// only include columns that are used in the query
|
||||
|
||||
@@ -184,7 +184,7 @@ pub fn plan_satisfies_order_target(
|
||||
.table
|
||||
.columns()
|
||||
.iter()
|
||||
.position(|c| c.is_rowid_alias);
|
||||
.position(|c| c.is_rowid_alias());
|
||||
let Some(rowid_alias_col) = rowid_alias_col else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ pub fn init_order_by(
|
||||
name: pos_in_table.to_string(),
|
||||
order: SortOrder::Asc,
|
||||
pos_in_table,
|
||||
collation: Some(CollationSeq::Binary),
|
||||
collation: None,
|
||||
default: None,
|
||||
});
|
||||
for _ in remappings.iter().filter(|r| !r.deduplicated) {
|
||||
|
||||
@@ -512,7 +512,7 @@ pub fn select_star(tables: &[JoinedTable], out_columns: &mut Vec<ResultSetColumn
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, col)| !col.hidden)
|
||||
.filter(|(_, col)| !col.hidden())
|
||||
.filter(|(_, col)| {
|
||||
// If we are joining with USING, we need to deduplicate the columns from the right table
|
||||
// that are also present in the USING clause.
|
||||
@@ -532,7 +532,7 @@ pub fn select_star(tables: &[JoinedTable], out_columns: &mut Vec<ResultSetColumn
|
||||
database: None,
|
||||
table: table.internal_id,
|
||||
column: i,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
is_rowid_alias: col.is_rowid_alias(),
|
||||
},
|
||||
contains_aggregates: false,
|
||||
}),
|
||||
@@ -913,23 +913,27 @@ impl JoinedTable {
|
||||
let mut columns = plan
|
||||
.result_columns
|
||||
.iter()
|
||||
.map(|rc| Column {
|
||||
name: rc.name(&plan.table_references).map(String::from),
|
||||
ty: Type::Blob, // FIXME: infer proper type
|
||||
ty_str: "BLOB".to_string(),
|
||||
is_rowid_alias: false,
|
||||
primary_key: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
.map(|rc| {
|
||||
Column::new(
|
||||
rc.name(&plan.table_references).map(String::from),
|
||||
"BLOB".to_string(),
|
||||
None,
|
||||
Type::Blob, // FIXME: infer proper type
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (i, column) in columns.iter_mut().enumerate() {
|
||||
column.collation =
|
||||
get_collseq_from_expr(&plan.result_columns[i].expr, &plan.table_references)?;
|
||||
column.set_collation(get_collseq_from_expr(
|
||||
&plan.result_columns[i].expr,
|
||||
&plan.table_references,
|
||||
)?);
|
||||
}
|
||||
|
||||
let table = Table::FromClauseSubquery(FromClauseSubquery {
|
||||
|
||||
@@ -593,7 +593,7 @@ fn transform_args_into_where_terms(
|
||||
let mut args_iter = args.iter();
|
||||
let mut hidden_count = 0;
|
||||
for (i, col) in table.columns().iter().enumerate() {
|
||||
if !col.hidden {
|
||||
if !col.hidden() {
|
||||
continue;
|
||||
}
|
||||
hidden_count += 1;
|
||||
@@ -603,7 +603,7 @@ fn transform_args_into_where_terms(
|
||||
database: None,
|
||||
table: internal_id,
|
||||
column: i,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
is_rowid_alias: col.is_rowid_alias(),
|
||||
};
|
||||
let expr = match arg_expr.as_ref() {
|
||||
Expr::Literal(Null) => Expr::IsNull(Box::new(column_expr)),
|
||||
@@ -1087,14 +1087,14 @@ fn parse_join(
|
||||
let mut distinct_names: Vec<ast::Name> = vec![];
|
||||
// TODO: O(n^2) maybe not great for large tables or big multiway joins
|
||||
// SQLite doesn't use HIDDEN columns for NATURAL joins: https://www3.sqlite.org/src/info/ab09ef427181130b
|
||||
for right_col in rightmost_table.columns().iter().filter(|col| !col.hidden) {
|
||||
for right_col in rightmost_table.columns().iter().filter(|col| !col.hidden()) {
|
||||
let mut found_match = false;
|
||||
for left_table in table_references
|
||||
.joined_tables()
|
||||
.iter()
|
||||
.take(table_references.joined_tables().len() - 1)
|
||||
{
|
||||
for left_col in left_table.columns().iter().filter(|col| !col.hidden) {
|
||||
for left_col in left_table.columns().iter().filter(|col| !col.hidden()) {
|
||||
if left_col.name == right_col.name {
|
||||
distinct_names.push(ast::Name::exact(
|
||||
left_col.name.clone().expect("column name is None"),
|
||||
@@ -1154,7 +1154,7 @@ fn parse_join(
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, col)| !natural || !col.hidden)
|
||||
.filter(|(_, col)| !natural || !col.hidden())
|
||||
.find(|(_, col)| {
|
||||
col.name
|
||||
.as_ref()
|
||||
@@ -1189,14 +1189,14 @@ fn parse_join(
|
||||
database: None,
|
||||
table: left_table_id,
|
||||
column: left_col_idx,
|
||||
is_rowid_alias: left_col.is_rowid_alias,
|
||||
is_rowid_alias: left_col.is_rowid_alias(),
|
||||
}),
|
||||
ast::Operator::Equals,
|
||||
Box::new(Expr::Column {
|
||||
database: None,
|
||||
table: right_table.internal_id,
|
||||
column: right_col_idx,
|
||||
is_rowid_alias: right_col.is_rowid_alias,
|
||||
is_rowid_alias: right_col.is_rowid_alias(),
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -743,7 +743,7 @@ fn emit_columns_for_table_info(
|
||||
// According to the SQLite documentation: "The 'cid' column should not be taken to
|
||||
// mean more than 'rank within the current result set'."
|
||||
// Therefore, we enumerate only after filtering out hidden columns.
|
||||
for (i, column) in columns.iter().filter(|col| !col.hidden).enumerate() {
|
||||
for (i, column) in columns.iter().filter(|col| !col.hidden()).enumerate() {
|
||||
// cid
|
||||
program.emit_int(i as i64, base_reg);
|
||||
// name
|
||||
@@ -753,7 +753,7 @@ fn emit_columns_for_table_info(
|
||||
program.emit_string8(column.ty_str.clone(), base_reg + 2);
|
||||
|
||||
// notnull
|
||||
program.emit_bool(column.notnull, base_reg + 3);
|
||||
program.emit_bool(column.notnull(), base_reg + 3);
|
||||
|
||||
// dflt_value
|
||||
match &column.default {
|
||||
@@ -766,7 +766,7 @@ fn emit_columns_for_table_info(
|
||||
}
|
||||
|
||||
// pk
|
||||
program.emit_bool(column.primary_key, base_reg + 5);
|
||||
program.emit_bool(column.primary_key(), base_reg + 5);
|
||||
|
||||
program.emit_result_row(base_reg, 6);
|
||||
}
|
||||
|
||||
@@ -2,25 +2,16 @@ use std::sync::Arc;
|
||||
|
||||
use crate::ast;
|
||||
use crate::ext::VTabImpl;
|
||||
use crate::schema::create_table;
|
||||
use crate::schema::BTreeTable;
|
||||
use crate::schema::Column;
|
||||
use crate::schema::Table;
|
||||
use crate::schema::Type;
|
||||
use crate::schema::RESERVED_TABLE_PREFIXES;
|
||||
use crate::schema::{create_table, BTreeTable, Column, Table, Type, RESERVED_TABLE_PREFIXES};
|
||||
use crate::storage::pager::CreateBTreeFlags;
|
||||
use crate::translate::emitter::emit_cdc_full_record;
|
||||
use crate::translate::emitter::emit_cdc_insns;
|
||||
use crate::translate::emitter::prepare_cdc_if_necessary;
|
||||
use crate::translate::emitter::OperationMode;
|
||||
use crate::translate::emitter::Resolver;
|
||||
use crate::translate::ProgramBuilder;
|
||||
use crate::translate::ProgramBuilderOpts;
|
||||
use crate::translate::emitter::{
|
||||
emit_cdc_full_record, emit_cdc_insns, prepare_cdc_if_necessary, OperationMode, Resolver,
|
||||
};
|
||||
use crate::translate::{ProgramBuilder, ProgramBuilderOpts};
|
||||
use crate::util::normalize_ident;
|
||||
use crate::util::PRIMARY_KEY_AUTOMATIC_INDEX_NAME_PREFIX;
|
||||
use crate::vdbe::builder::CursorType;
|
||||
use crate::vdbe::insn::Cookie;
|
||||
use crate::vdbe::insn::{CmpInsFlags, InsertFlags, Insn};
|
||||
use crate::vdbe::insn::{CmpInsFlags, Cookie, InsertFlags, Insn};
|
||||
use crate::Connection;
|
||||
use crate::{bail_parse_error, Result};
|
||||
|
||||
@@ -435,7 +426,7 @@ fn collect_autoindexes(
|
||||
};
|
||||
|
||||
let needs_index = if us.is_primary_key {
|
||||
!(col.primary_key && col.is_rowid_alias)
|
||||
!(col.primary_key() && col.is_rowid_alias())
|
||||
} else {
|
||||
// UNIQUE single needs an index
|
||||
true
|
||||
@@ -841,18 +832,18 @@ pub fn translate_drop_table(
|
||||
has_rowid: true,
|
||||
has_autoincrement: false,
|
||||
primary_key_columns: vec![],
|
||||
columns: vec![Column {
|
||||
name: Some("rowid".to_string()),
|
||||
ty: Type::Integer,
|
||||
ty_str: "INTEGER".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
}],
|
||||
columns: vec![Column::new(
|
||||
Some("rowid".to_string()),
|
||||
"INTEGER".to_string(),
|
||||
None,
|
||||
Type::Integer,
|
||||
None,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
)],
|
||||
is_strict: false,
|
||||
unique_sets: vec![],
|
||||
foreign_keys: vec![],
|
||||
|
||||
@@ -236,14 +236,14 @@ fn prepare_one_select_plan(
|
||||
ResultColumn::Star => table_references
|
||||
.joined_tables()
|
||||
.iter()
|
||||
.map(|t| t.columns().iter().filter(|col| !col.hidden).count())
|
||||
.map(|t| t.columns().iter().filter(|col| !col.hidden()).count())
|
||||
.sum(),
|
||||
// Guess 5 columns if we can't find the table using the identifier (maybe it's in [brackets] or `tick_quotes`, or miXeDcAse)
|
||||
ResultColumn::TableStar(n) => table_references
|
||||
.joined_tables()
|
||||
.iter()
|
||||
.find(|t| t.identifier == n.as_str())
|
||||
.map(|t| t.columns().iter().filter(|col| !col.hidden).count())
|
||||
.map(|t| t.columns().iter().filter(|col| !col.hidden()).count())
|
||||
.unwrap_or(5),
|
||||
// Otherwise allocate space for 1 column
|
||||
ResultColumn::Expr(_, _) => 1,
|
||||
@@ -317,7 +317,7 @@ fn prepare_one_select_plan(
|
||||
for table in plan.table_references.joined_tables_mut() {
|
||||
for idx in 0..table.columns().len() {
|
||||
let column = &table.columns()[idx];
|
||||
if column.hidden {
|
||||
if column.hidden() {
|
||||
continue;
|
||||
}
|
||||
table.mark_column_used(idx);
|
||||
@@ -339,7 +339,7 @@ fn prepare_one_select_plan(
|
||||
let num_columns = table.columns().len();
|
||||
for idx in 0..num_columns {
|
||||
let column = &table.columns()[idx];
|
||||
if column.hidden {
|
||||
if column.hidden() {
|
||||
continue;
|
||||
}
|
||||
plan.result_columns.push(ResultSetColumn {
|
||||
@@ -347,7 +347,7 @@ fn prepare_one_select_plan(
|
||||
database: None, // TODO: support different databases
|
||||
table: table.internal_id,
|
||||
column: idx,
|
||||
is_rowid_alias: column.is_rowid_alias,
|
||||
is_rowid_alias: column.is_rowid_alias(),
|
||||
},
|
||||
alias: None,
|
||||
contains_aggregates: false,
|
||||
|
||||
@@ -241,7 +241,7 @@ pub fn prepare_update_plan(
|
||||
.columns()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_i, c)| c.is_rowid_alias)
|
||||
.find(|(_i, c)| c.is_rowid_alias())
|
||||
{
|
||||
// Use the rowid alias column index
|
||||
match set_clauses.iter_mut().find(|(i, _)| i == &idx) {
|
||||
@@ -320,7 +320,7 @@ pub fn prepare_update_plan(
|
||||
let updated_cols: HashSet<usize> = set_clauses.iter().map(|(i, _)| *i).collect();
|
||||
let rowid_alias_used = set_clauses
|
||||
.iter()
|
||||
.any(|(idx, _)| *idx == ROWID_SENTINEL || columns[*idx].is_rowid_alias);
|
||||
.any(|(idx, _)| *idx == ROWID_SENTINEL || columns[*idx].is_rowid_alias());
|
||||
let indexes_to_update = if rowid_alias_used {
|
||||
// If the rowid alias is used in the SET clause, we need to update all indexes
|
||||
indexes.cloned().collect()
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||
use turso_parser::ast::{self, Upsert};
|
||||
|
||||
use crate::error::SQLITE_CONSTRAINT_PRIMARYKEY;
|
||||
use crate::schema::ROWID_SENTINEL;
|
||||
use crate::schema::{IndexColumn, ROWID_SENTINEL};
|
||||
use crate::translate::emitter::UpdateRowSource;
|
||||
use crate::translate::expr::{walk_expr, WalkControl};
|
||||
use crate::translate::fkeys::{emit_fk_child_update_counters, emit_parent_key_change_checks};
|
||||
@@ -16,7 +16,7 @@ use crate::Connection;
|
||||
use crate::{
|
||||
bail_parse_error,
|
||||
error::SQLITE_CONSTRAINT_NOTNULL,
|
||||
schema::{Index, IndexColumn, Schema, Table},
|
||||
schema::{Index, Schema, Table},
|
||||
translate::{
|
||||
emitter::{
|
||||
emit_cdc_full_record, emit_cdc_insns, emit_cdc_patch_record, OperationMode, Resolver,
|
||||
@@ -114,11 +114,7 @@ fn effective_collation_for_index_col(idx_col: &IndexColumn, table: &Table) -> St
|
||||
// Otherwise use the table default, or default to BINARY
|
||||
table
|
||||
.get_column_by_name(&idx_col.name)
|
||||
.map(|s| {
|
||||
s.1.collation
|
||||
.map(|c| c.to_string().to_ascii_lowercase())
|
||||
.unwrap_or_else(|| "binary".to_string())
|
||||
})
|
||||
.map(|s| s.1.collation().to_string())
|
||||
.unwrap_or_else(|| "binary".to_string())
|
||||
}
|
||||
|
||||
@@ -132,7 +128,7 @@ pub fn upsert_matches_rowid_alias(upsert: &Upsert, table: &Table) -> bool {
|
||||
return false;
|
||||
}
|
||||
// Only treat as PK if the PK is the rowid alias (INTEGER PRIMARY KEY)
|
||||
let pk = table.columns().iter().find(|c| c.is_rowid_alias);
|
||||
let pk = table.columns().iter().find(|c| c.is_rowid_alias());
|
||||
if let Some(pkcol) = pk {
|
||||
extract_target_key(&t.targets[0].expr).is_some_and(|tk| {
|
||||
tk.col_name
|
||||
@@ -152,7 +148,7 @@ fn collect_changed_cols(
|
||||
let mut rowid_changed = false;
|
||||
for (col_idx, _) in set_pairs {
|
||||
if let Some(c) = table.columns().get(*col_idx) {
|
||||
if c.is_rowid_alias {
|
||||
if c.is_rowid_alias() {
|
||||
rowid_changed = true;
|
||||
} else {
|
||||
cols_changed.insert(*col_idx);
|
||||
@@ -356,7 +352,7 @@ pub fn emit_upsert(
|
||||
let num_cols = ctx.table.columns.len();
|
||||
let current_start = program.alloc_registers(num_cols);
|
||||
for (i, col) in ctx.table.columns.iter().enumerate() {
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
program.emit_insn(Insn::RowId {
|
||||
cursor_id: ctx.cursor_id,
|
||||
dest: current_start + i,
|
||||
@@ -433,14 +429,14 @@ pub fn emit_upsert(
|
||||
NoConstantOptReason::RegisterReuse,
|
||||
)?;
|
||||
let col = &table.columns()[*col_idx];
|
||||
if col.notnull && !col.is_rowid_alias {
|
||||
if col.notnull() && !col.is_rowid_alias() {
|
||||
program.emit_insn(Insn::HaltIfNull {
|
||||
target_reg: new_start + *col_idx,
|
||||
err_code: SQLITE_CONSTRAINT_NOTNULL,
|
||||
description: String::from(table.get_name()) + col.name.as_ref().unwrap(),
|
||||
});
|
||||
}
|
||||
if col.is_rowid_alias {
|
||||
if col.is_rowid_alias() {
|
||||
// Must be integer; remember the NEW rowid value
|
||||
let r = program.alloc_register();
|
||||
program.emit_insn(Insn::Copy {
|
||||
@@ -465,7 +461,7 @@ pub fn emit_upsert(
|
||||
}
|
||||
|
||||
let (changed_cols, rowid_changed) = collect_changed_cols(table, set_pairs);
|
||||
let rowid_alias_idx = table.columns().iter().position(|c| c.is_rowid_alias);
|
||||
let rowid_alias_idx = table.columns().iter().position(|c| c.is_rowid_alias());
|
||||
let has_direct_rowid_update = set_pairs
|
||||
.iter()
|
||||
.any(|(idx, _)| *idx == rowid_alias_idx.unwrap_or(ROWID_SENTINEL));
|
||||
@@ -713,7 +709,7 @@ pub fn emit_upsert(
|
||||
table
|
||||
.columns()
|
||||
.iter()
|
||||
.find(|c| c.is_rowid_alias)
|
||||
.find(|c| c.is_rowid_alias())
|
||||
.and_then(|c| c.name.as_ref())
|
||||
.unwrap_or(&"rowid".to_string())
|
||||
),
|
||||
@@ -943,7 +939,7 @@ fn rewrite_expr_to_registers(
|
||||
return Some(rowid_reg);
|
||||
}
|
||||
let (idx, c) = table.get_column_by_name(name)?;
|
||||
if c.is_rowid_alias {
|
||||
if c.is_rowid_alias() {
|
||||
Some(rowid_reg)
|
||||
} else {
|
||||
Some(base_start + idx)
|
||||
|
||||
97
core/util.rs
97
core/util.rs
@@ -354,7 +354,7 @@ pub fn simple_bind_expr(
|
||||
database: None,
|
||||
table: internal_id,
|
||||
column: col_idx,
|
||||
is_rowid_alias: col.is_rowid_alias,
|
||||
is_rowid_alias: col.is_rowid_alias(),
|
||||
};
|
||||
} else {
|
||||
// only if we haven't found a match, check for explicit rowid reference
|
||||
@@ -1358,18 +1358,7 @@ pub fn extract_view_columns(
|
||||
|
||||
columns.push(ViewColumn {
|
||||
table_index: table_index.unwrap_or(usize::MAX),
|
||||
column: Column {
|
||||
name: Some(col_name),
|
||||
ty: Type::Text, // Default to TEXT, could be refined with type analysis
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
column: Column::new_default_text(Some(col_name), "TEXT".to_string(), None),
|
||||
});
|
||||
}
|
||||
ast::ResultColumn::Star => {
|
||||
@@ -1392,18 +1381,18 @@ pub fn extract_view_columns(
|
||||
|
||||
columns.push(ViewColumn {
|
||||
table_index: table_idx,
|
||||
column: Column {
|
||||
name: Some(final_name),
|
||||
ty: table_column.ty,
|
||||
ty_str: table_column.ty_str.clone(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: table_column.collation,
|
||||
hidden: false,
|
||||
},
|
||||
column: Column::new(
|
||||
Some(final_name),
|
||||
table_column.ty_str.clone(),
|
||||
None,
|
||||
table_column.ty(),
|
||||
table_column.collation_opt(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1413,18 +1402,11 @@ pub fn extract_view_columns(
|
||||
if tables.is_empty() {
|
||||
columns.push(ViewColumn {
|
||||
table_index: usize::MAX,
|
||||
column: Column {
|
||||
name: Some("*".to_string()),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
column: Column::new_default_text(
|
||||
Some("*".to_string()),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1449,36 +1431,29 @@ pub fn extract_view_columns(
|
||||
|
||||
columns.push(ViewColumn {
|
||||
table_index: table_idx,
|
||||
column: Column {
|
||||
name: Some(final_name),
|
||||
ty: table_column.ty,
|
||||
ty_str: table_column.ty_str.clone(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: table_column.collation,
|
||||
hidden: false,
|
||||
},
|
||||
column: Column::new(
|
||||
Some(final_name),
|
||||
table_column.ty_str.clone(),
|
||||
None,
|
||||
table_column.ty(),
|
||||
table_column.collation_opt(),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Table not found, create placeholder
|
||||
columns.push(ViewColumn {
|
||||
table_index: usize::MAX,
|
||||
column: Column {
|
||||
name: Some(format!("{table_name_str}.*")),
|
||||
ty: Type::Text,
|
||||
ty_str: "TEXT".to_string(),
|
||||
primary_key: false,
|
||||
is_rowid_alias: false,
|
||||
notnull: false,
|
||||
default: None,
|
||||
unique: false,
|
||||
collation: None,
|
||||
hidden: false,
|
||||
},
|
||||
column: Column::new_default_text(
|
||||
Some(format!("{table_name_str}.*")),
|
||||
"TEXT".to_string(),
|
||||
None,
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,7 +1028,7 @@ impl ProgramBuilder {
|
||||
.columns
|
||||
.get(column)
|
||||
.expect("column index out of bounds");
|
||||
if column_def.is_rowid_alias {
|
||||
if column_def.is_rowid_alias() {
|
||||
self.emit_insn(Insn::RowId {
|
||||
cursor_id,
|
||||
dest: out,
|
||||
|
||||
@@ -1920,14 +1920,15 @@ pub fn op_type_check(
|
||||
.zip(table_reference.columns.iter())
|
||||
.try_for_each(|(reg, col)| {
|
||||
// INT PRIMARY KEY is not row_id_alias so we throw error if this col is NULL
|
||||
if !col.is_rowid_alias && col.primary_key && matches!(reg.get_value(), Value::Null) {
|
||||
if !col.is_rowid_alias() && col.primary_key() && matches!(reg.get_value(), Value::Null)
|
||||
{
|
||||
bail_constraint_error!(
|
||||
"NOT NULL constraint failed: {}.{} ({})",
|
||||
&table_reference.name,
|
||||
col.name.as_deref().unwrap_or(""),
|
||||
SQLITE_CONSTRAINT
|
||||
)
|
||||
} else if col.is_rowid_alias && matches!(reg.get_value(), Value::Null) {
|
||||
} else if col.is_rowid_alias() && matches!(reg.get_value(), Value::Null) {
|
||||
// Handle INTEGER PRIMARY KEY for null as usual (Rowid will be auto-assigned)
|
||||
return Ok(());
|
||||
}
|
||||
@@ -6011,7 +6012,7 @@ pub fn op_insert(
|
||||
// Fix rowid alias columns: replace Null with actual rowid value
|
||||
if let Some(table) = schema.get_table(table_name) {
|
||||
for (i, col) in table.columns().iter().enumerate() {
|
||||
if col.is_rowid_alias && i < values.len() {
|
||||
if col.is_rowid_alias() && i < values.len() {
|
||||
values[i] = Value::Integer(key);
|
||||
}
|
||||
}
|
||||
@@ -6162,7 +6163,7 @@ pub fn op_insert(
|
||||
let schema = program.connection.schema.read();
|
||||
if let Some(table) = schema.get_table(table_name) {
|
||||
for (i, col) in table.columns().iter().enumerate() {
|
||||
if col.is_rowid_alias && i < new_values.len() {
|
||||
if col.is_rowid_alias() && i < new_values.len() {
|
||||
new_values[i] = Value::Integer(key);
|
||||
}
|
||||
}
|
||||
@@ -6280,7 +6281,7 @@ pub fn op_delete(
|
||||
// Fix rowid alias columns: replace Null with actual rowid value
|
||||
if let Some(table) = schema.get_table(table_name) {
|
||||
for (i, col) in table.columns().iter().enumerate() {
|
||||
if col.is_rowid_alias && i < values.len() {
|
||||
if col.is_rowid_alias() && i < values.len() {
|
||||
values[i] = Value::Integer(key);
|
||||
}
|
||||
}
|
||||
@@ -8666,7 +8667,7 @@ pub fn op_alter_column(
|
||||
// Maintain rowid-alias bit after change/rename (INTEGER PRIMARY KEY)
|
||||
if !*rename {
|
||||
// recompute alias from `new_column`
|
||||
btree.columns[*column_index].is_rowid_alias = new_column.is_rowid_alias;
|
||||
btree.columns[*column_index].set_rowid_alias(new_column.is_rowid_alias());
|
||||
}
|
||||
|
||||
// Update this table’s OWN foreign keys
|
||||
|
||||
Reference in New Issue
Block a user