Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/cli/tests/cmd_export_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
27 changes: 9 additions & 18 deletions src/db/src/adapters/database_introspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Option<String>, _>(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?;
Expand All @@ -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,
Expand Down