Skip to content

Commit

Permalink
Use BigInt for integer primary key in sqlite for diesel-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
stormshield-kg committed Feb 19, 2024
1 parent 7c4ba73 commit 1242ebd
Show file tree
Hide file tree
Showing 41 changed files with 945 additions and 28 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,16 @@ jobs:
shell: bash
run: cargo +${{ matrix.rust }} test --manifest-path diesel_cli/Cargo.toml --no-default-features --features "${{ matrix.backend }}"

- name: Test diesel-cli (integer_primary_key_i64_for_sqlite_3_37)
shell: bash
run: cargo +${{ matrix.rust }} test --manifest-path diesel_cli/Cargo.toml --no-default-features --features "sqlite integer_primary_key_i64_for_sqlite_3_37"

- name: Test diesel examples
shell: bash
env:
BACKEND: ${{ matrix.backend }}
run: |
(cd examples/${{ matrix.backend }} && rustup run ${{ matrix.rust }} bash test_all)
(cd examples/${{ matrix.backend }} && rustup run ${{ matrix.rust }} bash test_all)
- name: Test migrations-internals
shell: bash
Expand Down
3 changes: 2 additions & 1 deletion diesel_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ libsqlite3-sys = { version = ">=0.17.2, <0.29.0", optional = true }
diffy = "0.3.0"
regex = "1.0.6"
serde_regex = "1.1"
diesel_table_macro_syntax = {version = "0.1", path = "../diesel_table_macro_syntax"}
diesel_table_macro_syntax = { version = "0.1", path = "../diesel_table_macro_syntax" }
syn = { version = "2", features = ["visit"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3.10", features = ["env-filter"] }
Expand All @@ -61,6 +61,7 @@ postgres = ["diesel/postgres", "uses_information_schema"]
sqlite = ["diesel/sqlite"]
mysql = ["diesel/mysql", "uses_information_schema"]
sqlite-bundled = ["sqlite", "libsqlite3-sys/bundled"]
integer_primary_key_i64_for_sqlite_3_37 = []
uses_information_schema = []

[[test]]
Expand Down
9 changes: 6 additions & 3 deletions diesel_cli/src/infer_schema_internals/inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,13 @@ fn get_column_information(
fn determine_column_type(
attr: &ColumnInformation,
conn: &mut InferConnection,
#[allow(unused_variables)] table: &TableName,
) -> Result<ColumnType, crate::errors::Error> {
match *conn {
#[cfg(feature = "sqlite")]
InferConnection::Sqlite(_) => super::sqlite::determine_column_type(attr),
InferConnection::Sqlite(ref mut conn) => {
super::sqlite::determine_column_type(conn, attr, table)
}
#[cfg(feature = "postgres")]
InferConnection::Pg(ref mut conn) => {
use crate::infer_schema_internals::information_schema::DefaultSchema;
Expand Down Expand Up @@ -275,12 +278,12 @@ pub fn load_table_data(
let primary_key = primary_key
.iter()
.map(|k| rust_name_for_sql_name(k))
.collect();
.collect::<Vec<_>>();

let column_data = get_column_information(connection, &name, column_sorting)?
.into_iter()
.map(|c| {
let ty = determine_column_type(&c, connection)?;
let ty = determine_column_type(&c, connection, &name)?;

let ColumnInformation {
column_name,
Expand Down
131 changes: 128 additions & 3 deletions diesel_cli/src/infer_schema_internals/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,67 @@ impl QueryableByName<Sqlite> for PrimaryKeyInformation {
}
}

#[cfg(feature = "integer_primary_key_i64_for_sqlite_3_37")]
struct WithoutRowIdInformation {
name: String,
without_row_id: bool,
}

#[cfg(feature = "integer_primary_key_i64_for_sqlite_3_37")]
impl QueryableByName<Sqlite> for WithoutRowIdInformation {
fn build<'a>(row: &impl NamedRow<'a, Sqlite>) -> deserialize::Result<Self> {
Ok(Self {
name: NamedRow::get::<Text, String>(row, "name")?,
without_row_id: NamedRow::get::<Bool, bool>(row, "wr")?,
})
}
}

#[cfg(feature = "integer_primary_key_i64_for_sqlite_3_37")]
pub fn column_is_row_id(
conn: &mut SqliteConnection,
table: &TableName,
column_name: &str,
type_name: &str,
) -> QueryResult<bool> {
let sqlite_version = get_sqlite_version(conn)?;
if sqlite_version < SqliteVersion::new(3, 37, 0) {
return Ok(false);
}

if type_name != "integer" {
return Ok(false);
}

let table_xinfo_query = format!("PRAGMA TABLE_XINFO('{}')", &table.sql_name);
let table_xinfo_results = sql_query(table_xinfo_query).load::<PrimaryKeyInformation>(conn)?;

let primary_keys = table_xinfo_results
.iter()
.filter(|pk_info| pk_info.primary_key)
.collect::<Vec<_>>();

if primary_keys.len() != 1 {
return Ok(false);
}

if primary_keys[0].name != column_name {
return Ok(false);
}

let table_list_query = format!("PRAGMA TABLE_LIST('{}')", &table.sql_name);
let table_list_results = sql_query(table_list_query).load::<WithoutRowIdInformation>(conn)?;

match table_list_results
.iter()
.find(|wr_info| wr_info.name == table.sql_name)
.map(|wr_info| wr_info.without_row_id)
{
Some(false) => Ok(true),
_ => Ok(false),
}
}

#[derive(Queryable)]
struct ForeignKeyListRow {
_id: i32,
Expand Down Expand Up @@ -232,8 +293,12 @@ pub fn get_primary_keys(
Ok(collected)
}

#[tracing::instrument]
pub fn determine_column_type(attr: &ColumnInformation) -> Result<ColumnType, crate::errors::Error> {
#[tracing::instrument(skip(conn))]
pub fn determine_column_type(
#[allow(unused_variables)] conn: &mut SqliteConnection,
attr: &ColumnInformation,
table: &TableName,
) -> Result<ColumnType, crate::errors::Error> {
let mut type_name = attr.type_name.to_lowercase();
if type_name == "generated always" {
type_name.clear();
Expand All @@ -246,7 +311,16 @@ pub fn determine_column_type(attr: &ColumnInformation) -> Result<ColumnType, cra
} else if is_bigint(&type_name) {
String::from("BigInt")
} else if type_name.contains("int") {
String::from("Integer")
#[cfg(not(feature = "integer_primary_key_i64_for_sqlite_3_37"))]
let is_row_id = false;
#[cfg(feature = "integer_primary_key_i64_for_sqlite_3_37")]
let is_row_id = column_is_row_id(conn, table, &attr.column_name, &type_name)?;

if is_row_id {
String::from("BigInt")
} else {
String::from("Integer")
}
} else if is_text(&type_name) {
String::from("Text")
} else if is_binary(&type_name) {
Expand Down Expand Up @@ -447,3 +521,54 @@ fn all_rowid_aliases_used_empty_result() {
assert!(res.is_ok());
assert!(res.unwrap().is_empty());
}

#[test]
#[cfg(feature = "integer_primary_key_i64_for_sqlite_3_37")]
fn integer_primary_key_sqlite_3_37() {
let mut conn = SqliteConnection::establish(":memory:").unwrap();

let sqlite_version = get_sqlite_version(&mut conn).unwrap();
if sqlite_version < SqliteVersion::new(3, 37, 0) {
return;
}

diesel::sql_query("CREATE TABLE table_1 (id INTEGER PRIMARY KEY)")
.execute(&mut conn)
.unwrap();

diesel::sql_query("CREATE TABLE table_2 (id INTEGER)")
.execute(&mut conn)
.unwrap();

diesel::sql_query("CREATE TABLE table_3 (id INT PRIMARY KEY)")
.execute(&mut conn)
.unwrap();

diesel::sql_query("CREATE TABLE table_4 (id INTEGER PRIMARY KEY) WITHOUT ROWID")
.execute(&mut conn)
.unwrap();

let tables = [
TableName::from_name("table_1"),
TableName::from_name("table_2"),
TableName::from_name("table_3"),
TableName::from_name("table_4"),
];

let column_infos = tables
.iter()
.map(|table| get_table_data(&mut conn, &table, &Default::default()).unwrap()[0].clone())
.collect::<Vec<_>>();

let column_types = std::iter::zip(&tables, &column_infos)
.map(|(table, column_info)| {
determine_column_type(&mut conn, &column_info, &table)
.unwrap()
.sql_name
})
.collect::<Vec<_>>();

let expected_column_types = vec!["BigInt", "Integer", "Integer", "Integer"];

assert_eq!(column_types, expected_column_types);
}
25 changes: 23 additions & 2 deletions diesel_cli/src/migrations/diff_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,11 +514,32 @@ where
query_builder.push_sql(",\n");
}
query_builder.push_sql("\t");

let is_only_primary_key =
primary_keys.contains(&column.rust_name) && primary_keys.len() == 1;

query_builder.push_identifier(&column.sql_name)?;
generate_column_type_name(query_builder, &column.ty);
if primary_keys.contains(&column.rust_name) && primary_keys.len() == 1 {

if cfg!(all(
feature = "sqlite",
feature = "integer_primary_key_i64_for_sqlite_3_37"
)) && is_only_primary_key
&& column.ty.sql_name.eq_ignore_ascii_case("BigInt")
{
let ty = ColumnType {
rust_name: "Integer".into(),
sql_name: "Integer".into(),
..column.ty.clone()
};
generate_column_type_name(query_builder, &ty);
} else {
generate_column_type_name(query_builder, &column.ty);
}

if is_only_primary_key {
query_builder.push_sql(" PRIMARY KEY");
}

if let Some((table, _, pk)) = foreign_keys.iter().find(|(_, k, _)| k == &column.rust_name) {
foreign_key_list.push((column, table, pk));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: diesel_cli/tests/migration_generate.rs
description: "Test: diff_add_table"
---
// @generated automatically by Diesel CLI.

diesel::table! {
users (id) {
id -> BigInt,
name -> Text,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
source: diesel_cli/tests/migration_generate.rs
assertion_line: 354
description: "Test: diff_add_table_all_the_types"
---
// @generated automatically by Diesel CLI.

diesel::table! {
all_the_types (id) {
id -> BigInt,
bool_column -> Bool,
integer_column -> Integer,
small_int_col -> SmallInt,
big_int_col -> BigInt,
binary_col -> Binary,
text_col -> Text,
double_col -> Double,
float_col -> Float,
numeric_col -> Double,
date_col -> Date,
timestamp_col -> Timestamp,
time_col -> Time,
float4_col -> Float,
small_int2_col -> SmallInt,
int2_col -> SmallInt,
int4_col -> Integer,
int8_col -> BigInt,
big_int2_col -> BigInt,
float8_col -> Float,
decimal_col -> Double,
varchar_col -> Text,
varchar2_col -> Text,
char_col -> Text,
tinytext_col -> Text,
mediumtext_col -> Text,
tinyblob_col -> Binary,
blob_col -> Binary,
longblob_col -> Binary,
mediumblob_col -> Binary,
varbinary_col -> Binary,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
diesel::table! {
users {
id -> BigInt,
name -> Text,
}
}

diesel::table! {
posts {
id -> BigInt,
title -> Text,
body -> Nullable<Text>,
user_id -> Integer,
}
}

diesel::joinable!(posts -> users (user_id));
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
source: diesel_cli/tests/migration_generate.rs
assertion_line: 332
description: "Test: diff_add_table_with_fk"
---
// @generated automatically by Diesel CLI.

diesel::table! {
posts (id) {
id -> BigInt,
title -> Text,
body -> Nullable<Text>,
user_id -> Integer,
}
}

diesel::table! {
users (id) {
id -> BigInt,
name -> Text,
}
}

diesel::joinable!(posts -> users (user_id));

diesel::allow_tables_to_appear_in_same_query!(
posts,
users,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
table! {
users {
id -> BigInt,
name -> Text,
hair_color -> Nullable<Text>,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: diesel_cli/tests/migration_generate.rs
assertion_line: 327
description: "Test: diff_alter_table_add_column"
---
// @generated automatically by Diesel CLI.

diesel::table! {
users (id) {
id -> BigInt,
name -> Text,
hair_color -> Nullable<Text>,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
table! {
users {
id -> BigInt,
}
}
Loading

0 comments on commit 1242ebd

Please sign in to comment.