diff --git a/diesel_cli/src/infer_schema_internals/data_structures.rs b/diesel_cli/src/infer_schema_internals/data_structures.rs index 735f141edb23..67cee7bc1e04 100644 --- a/diesel_cli/src/infer_schema_internals/data_structures.rs +++ b/diesel_cli/src/infer_schema_internals/data_structures.rs @@ -97,9 +97,9 @@ where #[cfg(feature = "sqlite")] impl Queryable for ColumnInformation where - (i32, String, String, bool, Option, bool): FromStaticSqlRow, + (i32, String, String, bool, Option, bool, i32): FromStaticSqlRow, { - type Row = (i32, String, String, bool, Option, bool); + type Row = (i32, String, String, bool, Option, bool, i32); fn build(row: Self::Row) -> deserialize::Result { Ok(ColumnInformation::new(row.1, row.2, None, !row.3)) diff --git a/diesel_cli/src/infer_schema_internals/sqlite.rs b/diesel_cli/src/infer_schema_internals/sqlite.rs index 1f2009d71a1d..9aaad0f22ac4 100644 --- a/diesel_cli/src/infer_schema_internals/sqlite.rs +++ b/diesel_cli/src/infer_schema_internals/sqlite.rs @@ -21,6 +21,7 @@ table! { notnull -> Bool, dflt_value -> Nullable, pk -> Bool, + hidden -> Integer, } } @@ -89,12 +90,50 @@ pub fn load_foreign_key_constraints( Ok(rows.into_iter().flatten().collect()) } +#[derive(PartialEq, Eq, PartialOrd, Ord)] +struct SqliteVersion { + major: u32, + minor: u32, + patch: u32, +} + +impl SqliteVersion { + pub fn new(major: u32, minor: u32, patch: u32) -> SqliteVersion { + SqliteVersion { + major, + minor, + patch, + } + } +} + +fn get_sqlite_version(conn: &mut SqliteConnection) -> SqliteVersion { + let query = "SELECT sqlite_version()"; + let result = sql::(&query).load::(conn).unwrap(); + let parts = result[0] + .split('.') + .map(|part| part.parse().unwrap()) + .collect::>(); + assert_eq!(parts.len(), 3); + SqliteVersion::new(parts[0], parts[1], parts[2]) +} + pub fn get_table_data( conn: &mut SqliteConnection, table: &TableName, column_sorting: &ColumnSorting, ) -> QueryResult> { - let query = format!("PRAGMA TABLE_INFO('{}')", &table.sql_name); + let sqlite_version = get_sqlite_version(conn); + let query = if sqlite_version >= SqliteVersion::new(3, 26, 0) { + /* + * To get generated columns we need to use TABLE_XINFO + * This would return hidden columns as well, but those would need to be created at runtime + * therefore they aren't an issue. + */ + format!("PRAGMA TABLE_XINFO('{}')", &table.sql_name) + } else { + format!("PRAGMA TABLE_INFO('{}')", &table.sql_name) + }; let mut result = sql::(&query).load(conn)?; match column_sorting { ColumnSorting::OrdinalPosition => {} @@ -115,6 +154,7 @@ struct FullTableInfo { _not_null: bool, _dflt_value: Option, primary_key: bool, + _hidden: i32, } #[derive(Queryable)] @@ -133,7 +173,12 @@ pub fn get_primary_keys( conn: &mut SqliteConnection, table: &TableName, ) -> QueryResult> { - let query = format!("PRAGMA TABLE_INFO('{}')", &table.sql_name); + let sqlite_version = get_sqlite_version(conn); + let query = if sqlite_version >= SqliteVersion::new(3, 26, 0) { + format!("PRAGMA TABLE_XINFO('{}')", &table.sql_name) + } else { + format!("PRAGMA TABLE_INFO('{}')", &table.sql_name) + }; let results = sql::(&query).load::(conn)?; Ok(results .into_iter() @@ -144,7 +189,11 @@ pub fn get_primary_keys( pub fn determine_column_type( attr: &ColumnInformation, ) -> Result> { - let type_name = attr.type_name.to_lowercase(); + let mut type_name = attr.type_name.to_lowercase(); + if type_name == "generated always" { + type_name.clear(); + } + let path = if is_bool(&type_name) { String::from("Bool") } else if is_smallint(&type_name) { diff --git a/diesel_cli/tests/print_schema.rs b/diesel_cli/tests/print_schema.rs index dc5dacf285a3..bafdb2db03dd 100644 --- a/diesel_cli/tests/print_schema.rs +++ b/diesel_cli/tests/print_schema.rs @@ -198,6 +198,18 @@ fn print_schema_specifying_schema_name_with_custom_type() { ) } +#[test] +#[cfg(feature = "sqlite")] +fn print_schema_generated_columns() { + test_print_schema("print_schema_generated_columns", vec![]) +} + +#[test] +#[cfg(feature = "sqlite")] +fn print_schema_generated_columns_with_generated_always() { + test_print_schema("print_schema_generated_columns_generated_always", vec![]) +} + #[cfg(feature = "sqlite")] const BACKEND: &str = "sqlite"; #[cfg(feature = "postgres")] diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns/diesel.toml b/diesel_cli/tests/print_schema/print_schema_generated_columns/diesel.toml new file mode 100644 index 000000000000..3985231b860a --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns/diesel.toml @@ -0,0 +1,3 @@ +[print_schema] +file = "src/schema.rs" +with_docs = false diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/expected.rs b/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/expected.rs new file mode 100644 index 000000000000..f355db8f2b88 --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/expected.rs @@ -0,0 +1,8 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + generated (id) { + id -> Nullable, + generated -> Nullable, + } +} diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/schema.sql b/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/schema.sql new file mode 100644 index 000000000000..5df2ed8274c7 --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns/sqlite/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE generated ( + id integer primary key, + generated integer as (id * 3) +); diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/diesel.toml b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/diesel.toml new file mode 100644 index 000000000000..3985231b860a --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/diesel.toml @@ -0,0 +1,3 @@ +[print_schema] +file = "src/schema.rs" +with_docs = false diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/expected.rs b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/expected.rs new file mode 100644 index 000000000000..f355db8f2b88 --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/expected.rs @@ -0,0 +1,8 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + generated (id) { + id -> Nullable, + generated -> Nullable, + } +} diff --git a/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/schema.sql b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/schema.sql new file mode 100644 index 000000000000..512ca845e045 --- /dev/null +++ b/diesel_cli/tests/print_schema/print_schema_generated_columns_generated_always/sqlite/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE generated ( + id integer primary key, + generated integer generated always as (id * 3) +);