keep type information as a string in column metadata

SQLite holds on to it deeply, for example:

sqlite> create table a(a int);
sqlite> create table b(b integer);
sqlite> create table c(c glauber);

sqlite> pragma table_info=a;
0|a|INT|0||0
sqlite> pragma table_info=b;
0|b|INTEGER|0||0
sqlite> pragma table_info=c;
0|c|glauber|0||0

So we'll keep it as well so we can produce the same responses.
This commit is contained in:
Glauber Costa
2025-01-30 19:51:37 -05:00
parent f1df43633a
commit 249a8cf8d2
4 changed files with 86 additions and 12 deletions

View File

@@ -176,6 +176,7 @@ impl PseudoTable {
self.columns.push(Column {
name: normalize_ident(name),
ty,
ty_str: ty.to_string(),
primary_key,
is_rowid_alias: false,
notnull: false,
@@ -245,30 +246,42 @@ fn create_table(
// and the value of this column are the same.
// https://www.sqlite.org/lang_createtable.html#rowids_and_the_integer_primary_key
let mut typename_exactly_integer = false;
let ty = match col_def.col_type {
let (ty, ty_str) = match col_def.col_type {
Some(data_type) => {
let s = data_type.name.as_str();
let ty_str = if matches!(
s.to_uppercase().as_str(),
"TEXT" | "INT" | "INTEGER" | "BLOB" | "REAL"
) {
s.to_uppercase().to_string()
} else {
s.to_string()
};
// https://www.sqlite.org/datatype3.html
let type_name = data_type.name.as_str().to_uppercase();
let type_name = ty_str.to_uppercase();
if type_name.contains("INT") {
typename_exactly_integer = type_name == "INTEGER";
Type::Integer
(Type::Integer, ty_str)
} else if type_name.contains("CHAR")
|| type_name.contains("CLOB")
|| type_name.contains("TEXT")
{
Type::Text
} else if type_name.contains("BLOB") || type_name.is_empty() {
Type::Blob
(Type::Text, ty_str)
} else if type_name.contains("BLOB") {
(Type::Blob, ty_str)
} else if type_name.is_empty() {
(Type::Blob, "".to_string())
} else if type_name.contains("REAL")
|| type_name.contains("FLOA")
|| type_name.contains("DOUB")
{
Type::Real
(Type::Real, ty_str)
} else {
Type::Numeric
(Type::Numeric, ty_str)
}
}
None => Type::Null,
None => (Type::Null, "".to_string()),
};
let mut default = None;
@@ -298,6 +311,7 @@ fn create_table(
cols.push(Column {
name: normalize_ident(&name),
ty,
ty_str,
primary_key,
is_rowid_alias: typename_exactly_integer && primary_key,
notnull,
@@ -348,6 +362,8 @@ pub fn _build_pseudo_table(columns: &[ResultColumn]) -> PseudoTable {
pub struct Column {
pub name: 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,
@@ -388,6 +404,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
Column {
name: "type".to_string(),
ty: Type::Text,
ty_str: "TEXT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,
@@ -396,6 +413,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
Column {
name: "name".to_string(),
ty: Type::Text,
ty_str: "TEXT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,
@@ -404,6 +422,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
Column {
name: "tbl_name".to_string(),
ty: Type::Text,
ty_str: "TEXT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,
@@ -412,6 +431,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
Column {
name: "rootpage".to_string(),
ty: Type::Integer,
ty_str: "INT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,
@@ -420,6 +440,7 @@ pub fn sqlite_schema_table() -> BTreeTable {
Column {
name: "sql".to_string(),
ty: Type::Text,
ty_str: "TEXT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,
@@ -769,6 +790,51 @@ mod tests {
Ok(())
}
#[test]
pub fn test_col_type_string_integer() -> Result<()> {
let sql = r#"CREATE TABLE t1 (a InTeGeR);"#;
let table = BTreeTable::from_sql(sql, 0)?;
let column = table.get_column("a").unwrap().1;
assert_eq!(column.ty_str, "INTEGER");
Ok(())
}
#[test]
pub fn test_col_type_string_int() -> Result<()> {
let sql = r#"CREATE TABLE t1 (a InT);"#;
let table = BTreeTable::from_sql(sql, 0)?;
let column = table.get_column("a").unwrap().1;
assert_eq!(column.ty_str, "INT");
Ok(())
}
#[test]
pub fn test_col_type_string_blob() -> Result<()> {
let sql = r#"CREATE TABLE t1 (a bLoB);"#;
let table = BTreeTable::from_sql(sql, 0)?;
let column = table.get_column("a").unwrap().1;
assert_eq!(column.ty_str, "BLOB");
Ok(())
}
#[test]
pub fn test_col_type_string_empty() -> Result<()> {
let sql = r#"CREATE TABLE t1 (a);"#;
let table = BTreeTable::from_sql(sql, 0)?;
let column = table.get_column("a").unwrap().1;
assert_eq!(column.ty_str, "");
Ok(())
}
#[test]
pub fn test_col_type_string_some_nonsense() -> Result<()> {
let sql = r#"CREATE TABLE t1 (a someNonsenseName);"#;
let table = BTreeTable::from_sql(sql, 0)?;
let column = table.get_column("a").unwrap().1;
assert_eq!(column.ty_str, "someNonsenseName");
Ok(())
}
#[test]
pub fn test_sqlite_schema() {
let expected = r#"CREATE TABLE sqlite_schema (
@@ -841,6 +907,7 @@ mod tests {
columns: vec![Column {
name: "a".to_string(),
ty: Type::Integer,
ty_str: "INT".to_string(),
primary_key: false,
is_rowid_alias: false,
notnull: false,

View File

@@ -165,11 +165,13 @@ pub fn emit_group_by<'a>(
.map(|agg| agg.args.len())
.sum::<usize>();
// sorter column names do not matter
let ty = crate::schema::Type::Null;
let pseudo_columns = (0..sorter_column_count)
.map(|i| Column {
name: i.to_string(),
primary_key: false,
ty: crate::schema::Type::Null,
ty,
ty_str: ty.to_string(),
is_rowid_alias: false,
notnull: false,
default: None,

View File

@@ -67,11 +67,13 @@ pub fn emit_order_by(
let sort_loop_end_label = program.allocate_label();
let mut pseudo_columns = vec![];
for (i, _) in order_by.iter().enumerate() {
let ty = crate::schema::Type::Null;
pseudo_columns.push(Column {
// Names don't matter. We are tracking which result column is in which position in the ORDER BY clause in m.result_column_indexes_in_orderby_sorter.
name: format!("sort_key_{}", i),
primary_key: false,
ty: crate::schema::Type::Null,
ty,
ty_str: ty.to_string(),
is_rowid_alias: false,
notnull: false,
default: None,
@@ -84,10 +86,12 @@ pub fn emit_order_by(
continue;
}
}
let ty = crate::schema::Type::Null;
pseudo_columns.push(Column {
name: rc.expr.to_string(),
primary_key: false,
ty: crate::schema::Type::Null,
ty,
ty_str: ty.to_string(),
is_rowid_alias: false,
notnull: false,
default: None,

View File

@@ -273,6 +273,7 @@ impl TableReference {
.map(|rc| Column {
name: rc.name.clone(),
ty: Type::Text, // FIXME: infer proper type
ty_str: "TEXT".to_string(),
is_rowid_alias: false,
primary_key: false,
notnull: false,