diff --git a/.changepacks/changepack_log_WoRHR0j9DpzxX5QVCT3xp.json b/.changepacks/changepack_log_WoRHR0j9DpzxX5QVCT3xp.json new file mode 100644 index 0000000..828944a --- /dev/null +++ b/.changepacks/changepack_log_WoRHR0j9DpzxX5QVCT3xp.json @@ -0,0 +1 @@ +{"changes":{"crates/vespertide-naming/Cargo.toml":"Patch","crates/vespertide-query/Cargo.toml":"Patch","crates/vespertide-loader/Cargo.toml":"Patch","crates/vespertide-config/Cargo.toml":"Patch","crates/vespertide-core/Cargo.toml":"Patch","crates/vespertide-exporter/Cargo.toml":"Patch","crates/vespertide-cli/Cargo.toml":"Patch","crates/vespertide-planner/Cargo.toml":"Patch","crates/vespertide-macro/Cargo.toml":"Patch","crates/vespertide/Cargo.toml":"Patch"},"note":"Fix upper issue","date":"2026-01-15T13:48:47.690192600Z"} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3c65f8e..60a0526 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2995,7 +2995,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vespertide" -version = "0.1.28" +version = "0.1.29" dependencies = [ "vespertide-core", "vespertide-macro", @@ -3003,7 +3003,7 @@ dependencies = [ [[package]] name = "vespertide-cli" -version = "0.1.28" +version = "0.1.29" dependencies = [ "anyhow", "assert_cmd", @@ -3028,7 +3028,7 @@ dependencies = [ [[package]] name = "vespertide-config" -version = "0.1.28" +version = "0.1.29" dependencies = [ "clap", "schemars", @@ -3038,7 +3038,7 @@ dependencies = [ [[package]] name = "vespertide-core" -version = "0.1.28" +version = "0.1.29" dependencies = [ "rstest", "schemars", @@ -3050,7 +3050,7 @@ dependencies = [ [[package]] name = "vespertide-exporter" -version = "0.1.28" +version = "0.1.29" dependencies = [ "insta", "rstest", @@ -3061,7 +3061,7 @@ dependencies = [ [[package]] name = "vespertide-loader" -version = "0.1.28" +version = "0.1.29" dependencies = [ "anyhow", "rstest", @@ -3076,7 +3076,7 @@ dependencies = [ [[package]] name = "vespertide-macro" -version = "0.1.28" +version = "0.1.29" dependencies = [ "proc-macro2", "quote", @@ -3093,11 +3093,11 @@ dependencies = [ [[package]] name = "vespertide-naming" -version = "0.1.28" +version = "0.1.29" [[package]] name = "vespertide-planner" -version = "0.1.28" +version = "0.1.29" dependencies = [ "insta", "rstest", @@ -3108,7 +3108,7 @@ dependencies = [ [[package]] name = "vespertide-query" -version = "0.1.28" +version = "0.1.29" dependencies = [ "insta", "rstest", diff --git a/crates/vespertide-query/src/sql/create_table.rs b/crates/vespertide-query/src/sql/create_table.rs index e52f5ba..15270ce 100644 --- a/crates/vespertide-query/src/sql/create_table.rs +++ b/crates/vespertide-query/src/sql/create_table.rs @@ -657,4 +657,64 @@ mod tests { assert_snapshot!(sql); }); } + + /// Test creating a table with timestamp column and NOW() default + /// SQLite should convert NOW() to CURRENT_TIMESTAMP + #[rstest] + #[case::timestamp_now_default_postgres(DatabaseBackend::Postgres)] + #[case::timestamp_now_default_mysql(DatabaseBackend::MySql)] + #[case::timestamp_now_default_sqlite(DatabaseBackend::Sqlite)] + fn test_create_table_with_timestamp_now_default(#[case] backend: DatabaseBackend) { + let columns = vec![ + ColumnDef { + name: "id".into(), + r#type: ColumnType::Simple(SimpleColumnType::BigInt), + nullable: false, + default: None, + comment: None, + primary_key: None, + unique: None, + index: None, + foreign_key: None, + }, + ColumnDef { + name: "created_at".into(), + r#type: ColumnType::Simple(SimpleColumnType::Timestamptz), + nullable: false, + default: Some("NOW()".into()), // uppercase NOW() + comment: None, + primary_key: None, + unique: None, + index: None, + foreign_key: None, + }, + ]; + + let result = build_create_table(&backend, "events", &columns, &[]); + assert!(result.is_ok(), "build_create_table failed: {:?}", result); + let queries = result.unwrap(); + let sql = queries + .iter() + .map(|q| q.build(backend)) + .collect::>() + .join("\n"); + + // SQLite should NOT have NOW() - it should be converted to CURRENT_TIMESTAMP + if matches!(backend, DatabaseBackend::Sqlite) { + assert!( + !sql.contains("NOW()"), + "SQLite should not contain NOW(), got: {}", + sql + ); + assert!( + sql.contains("CURRENT_TIMESTAMP"), + "SQLite should use CURRENT_TIMESTAMP, got: {}", + sql + ); + } + + with_settings!({ snapshot_suffix => format!("create_table_with_timestamp_now_default_{:?}", backend) }, { + assert_snapshot!(sql); + }); + } } diff --git a/crates/vespertide-query/src/sql/helpers.rs b/crates/vespertide-query/src/sql/helpers.rs index 880c069..1de8b4f 100644 --- a/crates/vespertide-query/src/sql/helpers.rs +++ b/crates/vespertide-query/src/sql/helpers.rs @@ -167,19 +167,31 @@ pub fn reference_action_sql(action: &ReferenceAction) -> &'static str { /// Convert a default value string to the appropriate backend-specific expression pub fn convert_default_for_backend(default: &str, backend: &DatabaseBackend) -> String { - match default { - "gen_random_uuid()" | "UUID()" | "lower(hex(randomblob(16)))" => match backend { + let lower = default.to_lowercase(); + + // UUID generation functions + if lower == "gen_random_uuid()" || lower == "uuid()" || lower == "lower(hex(randomblob(16)))" { + return match backend { DatabaseBackend::Postgres => "gen_random_uuid()".to_string(), DatabaseBackend::MySql => "(UUID())".to_string(), DatabaseBackend::Sqlite => "lower(hex(randomblob(16)))".to_string(), - }, - "current_timestamp()" | "now()" | "CURRENT_TIMESTAMP" => match backend { + }; + } + + // Timestamp functions (case-insensitive) + if lower == "current_timestamp()" + || lower == "now()" + || lower == "current_timestamp" + || lower == "getdate()" + { + return match backend { DatabaseBackend::Postgres => "CURRENT_TIMESTAMP".to_string(), DatabaseBackend::MySql => "CURRENT_TIMESTAMP".to_string(), DatabaseBackend::Sqlite => "CURRENT_TIMESTAMP".to_string(), - }, - other => other.to_string(), + }; } + + default.to_string() } /// Check if the column type is an enum type @@ -492,6 +504,9 @@ mod tests { #[case::now_postgres("now()", DatabaseBackend::Postgres, "CURRENT_TIMESTAMP")] #[case::now_mysql("now()", DatabaseBackend::MySql, "CURRENT_TIMESTAMP")] #[case::now_sqlite("now()", DatabaseBackend::Sqlite, "CURRENT_TIMESTAMP")] + #[case::now_upper_postgres("NOW()", DatabaseBackend::Postgres, "CURRENT_TIMESTAMP")] + #[case::now_upper_mysql("NOW()", DatabaseBackend::MySql, "CURRENT_TIMESTAMP")] + #[case::now_upper_sqlite("NOW()", DatabaseBackend::Sqlite, "CURRENT_TIMESTAMP")] #[case::current_timestamp_upper_postgres( "CURRENT_TIMESTAMP", DatabaseBackend::Postgres, diff --git a/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_MySql.snap b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_MySql.snap new file mode 100644 index 0000000..d4a8e94 --- /dev/null +++ b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_MySql.snap @@ -0,0 +1,5 @@ +--- +source: crates/vespertide-query/src/sql/create_table.rs +expression: sql +--- +CREATE TABLE `events` ( `id` bigint NOT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ) diff --git a/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Postgres.snap b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Postgres.snap new file mode 100644 index 0000000..265c844 --- /dev/null +++ b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Postgres.snap @@ -0,0 +1,5 @@ +--- +source: crates/vespertide-query/src/sql/create_table.rs +expression: sql +--- +CREATE TABLE "events" ( "id" bigint NOT NULL, "created_at" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP ) diff --git a/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Sqlite.snap b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Sqlite.snap new file mode 100644 index 0000000..cbf5b7e --- /dev/null +++ b/crates/vespertide-query/src/sql/snapshots/vespertide_query__sql__create_table__tests__create_table_with_timestamp_now_default@create_table_with_timestamp_now_default_Sqlite.snap @@ -0,0 +1,5 @@ +--- +source: crates/vespertide-query/src/sql/create_table.rs +expression: sql +--- +CREATE TABLE "events" ( "id" bigint NOT NULL, "created_at" timestamp_with_timezone_text NOT NULL DEFAULT CURRENT_TIMESTAMP )