diff --git a/src/cli/tests/cmd_export_test.rs b/src/cli/tests/cmd_export_test.rs index f9fe364..e115b28 100644 --- a/src/cli/tests/cmd_export_test.rs +++ b/src/cli/tests/cmd_export_test.rs @@ -230,6 +230,75 @@ async fn test_export_to_stdout() { assert!(output.contains("users:")); } +/// INTEGER PRIMARY KEY(AUTOINCREMENTキーワードなし)で auto_increment が検出されるテスト +/// +/// SQLiteでは INTEGER PRIMARY KEY は暗黙的に ROWID のエイリアスとなり自動増分する。 +/// AUTOINCREMENT キーワードがなくても auto_increment: true としてエクスポートされるべき。 +/// Regression test for #28 +#[tokio::test] +#[ignore] // 統合テスト - 実際のデータベースが必要 +async fn test_export_sqlite_integer_primary_key_auto_increment() { + install_default_drivers(); + let (_temp_dir, project_path) = + common::setup_test_project(Dialect::SQLite, None, false).unwrap(); + + // データベースファイルのパス + let db_path = project_path.join("test.db"); + fs::File::create(&db_path).unwrap(); + + // 設定ファイルにデータベース接続情報を追加 + let config = common::create_test_config(Dialect::SQLite, Some(&db_path.to_string_lossy())); + let config_path = project_path.join(strata::core::config::Config::DEFAULT_CONFIG_PATH); + let config_yaml = ConfigSerializer::to_yaml(&config).unwrap(); + fs::write(&config_path, config_yaml).unwrap(); + + use strata::adapters::database::DatabaseConnectionService; + + let db_service = DatabaseConnectionService::new(); + let db_config = config.get_database_config("development").unwrap(); + let pool = db_service + .create_pool(Dialect::SQLite, &db_config) + .await + .unwrap(); + + // INTEGER PRIMARY KEY のみ(AUTOINCREMENT キーワードなし)でテーブルを作成 + sqlx::query( + r#" + CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT NOT NULL + ) + "#, + ) + .execute(&pool) + .await + .unwrap(); + + // 標準出力へのエクスポート + let handler = ExportCommandHandler::new(); + let command = ExportCommand { + project_path, + config_path: None, + env: "development".to_string(), + output_dir: None, + force: false, + format: strata::cli::OutputFormat::Text, + split: false, + tables: vec![], + exclude_tables: vec![], + }; + + let result = handler.execute(&command).await; + assert!(result.is_ok(), "Export failed: {:?}", result); + + let output = result.unwrap(); + assert!( + output.contains("auto_increment: true"), + "Expected auto_increment: true in output, got:\n{}", + output + ); +} + #[test] fn test_format_export_summary() { let handler = ExportCommandHandler::new(); diff --git a/src/db/src/adapters/database_introspector.rs b/src/db/src/adapters/database_introspector.rs index 3f3270e..74e6865 100644 --- a/src/db/src/adapters/database_introspector.rs +++ b/src/db/src/adapters/database_introspector.rs @@ -1143,17 +1143,6 @@ impl DatabaseIntrospector for SqliteIntrospector { use crate::adapters::sql_quote::quote_identifier_sqlite; use sqlx::Row; - // CREATE TABLE SQL を取得して AUTOINCREMENT を検出 - let create_sql_query = "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?"; - let create_sql_row = sqlx::query(create_sql_query) - .bind(table_name) - .fetch_optional(pool) - .await?; - let has_autoincrement = create_sql_row - .and_then(|row| row.try_get::, _>(0).ok().flatten()) - .map(|sql| sql.to_uppercase().contains("AUTOINCREMENT")) - .unwrap_or(false); - let quoted_name = quote_identifier_sqlite(table_name); let sql = format!("PRAGMA table_info({})", quoted_name); let rows = sqlx::query(&sql).fetch_all(pool).await?; @@ -1164,13 +1153,15 @@ impl DatabaseIntrospector for SqliteIntrospector { let not_null: i32 = row.get(3); let is_pk: i32 = row.get(5); let data_type: String = row.get(2); - // SQLite の INTEGER PRIMARY KEY AUTOINCREMENT を検出 - let auto_increment = - if has_autoincrement && is_pk > 0 && data_type.to_uppercase() == "INTEGER" { - Some(true) - } else { - None - }; + // SQLite の INTEGER PRIMARY KEY を検出 + // SQLite では INTEGER PRIMARY KEY は暗黙的に ROWID のエイリアスとなり + // 自動増分する。明示的な AUTOINCREMENT キーワードの有無に関わらず、 + // INTEGER 型かつ PRIMARY KEY であれば auto_increment: true とする。 + let auto_increment = if is_pk > 0 && data_type.to_uppercase() == "INTEGER" { + Some(true) + } else { + None + }; RawColumnInfo { name: row.get(1), data_type,