diff --git a/package/postgres/pg-from/Cargo.lock b/package/postgres/pg-from/Cargo.lock index 06e9cd3..9426c38 100644 --- a/package/postgres/pg-from/Cargo.lock +++ b/package/postgres/pg-from/Cargo.lock @@ -35,6 +35,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -47,6 +57,24 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -65,6 +93,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + [[package]] name = "libsqlite3-sys" version = "0.30.1" @@ -76,6 +110,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "once_cell" version = "1.20.2" @@ -87,6 +127,7 @@ name = "pg-from" version = "0.1.0" dependencies = [ "rusqlite", + "tempfile", ] [[package]] @@ -127,6 +168,19 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "shlex" version = "1.3.0" @@ -150,6 +204,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "unicode-ident" version = "1.0.16" @@ -168,6 +236,97 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/package/postgres/pg-from/Cargo.toml b/package/postgres/pg-from/Cargo.toml index e3405a1..59de91c 100644 --- a/package/postgres/pg-from/Cargo.toml +++ b/package/postgres/pg-from/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] rusqlite = { version = "0.32.0", features = ["bundled"] } +tempfile = "3.16.0" diff --git a/package/postgres/pg-from/src/main.rs b/package/postgres/pg-from/src/main.rs index bbb41b4..cdcab2d 100644 --- a/package/postgres/pg-from/src/main.rs +++ b/package/postgres/pg-from/src/main.rs @@ -1,10 +1,11 @@ use rusqlite::{Connection, Result}; use std::env; use std::error::Error; +use std::fs; use std::fs::File; use std::io::Write; +use tempfile::NamedTempFile; -/// Вывод справки по использованию утилиты. fn print_help(program: &str) { println!( "Usage: {} [--inherit=]\n\n\ @@ -14,7 +15,6 @@ fn print_help(program: &str) { ); } -/// Структура для хранения информации о столбце (результат PRAGMA table_info). #[derive(Debug)] struct ColumnInfo { cid: i32, @@ -25,9 +25,6 @@ struct ColumnInfo { pk: i32, } -/// Преобразует строку типа из SQLite в тип PostgreSQL. -/// Здесь применяется простая логика: если тип содержит "INT" – выдаётся bigint, -/// если содержит "CHAR", "TEXT" или "CLOB" – text, если "REAL", "FLOA" или "DOUB" – double precision, и т.д. fn convert_sqlite_type_to_postgres(sqlite_type: &str) -> String { let upper = sqlite_type.to_uppercase(); if upper.contains("INT") { @@ -43,9 +40,6 @@ fn convert_sqlite_type_to_postgres(sqlite_type: &str) -> String { } } -/// Генерирует DDL для создания таблицы в PostgreSQL на основе информации из PRAGMA table_info. -/// Если задан параметр наследования (inherit_clause), то после списка столбцов добавляется -/// конструкция INHERITS (). fn generate_create_table_sql( table: &str, conn: &Connection, @@ -66,12 +60,10 @@ fn generate_create_table_sql( })? .collect::, _>>()?; - // Собираем список столбцов и определяем первичные ключи. let mut column_defs = Vec::new(); let pk_columns: Vec<&ColumnInfo> = columns.iter().filter(|col| col.pk > 0).collect(); - // Если имеется ровно один первичный ключ и его тип начинается с "INTEGER", - // то для него генерируем тип SERIAL (PostgreSQL автоматически создаст sequence). + // Если ровно один PK и его тип начинается с "INTEGER", генерируем SERIAL. let single_autoinc = if pk_columns.len() == 1 { let col = pk_columns[0]; col.data_type.to_uppercase().starts_with("INTEGER") @@ -100,7 +92,6 @@ fn generate_create_table_sql( column_defs.push(col_def); } - // Если составной ключ, добавляем ограничение отдельно. if pk_columns.len() > 1 { let pk_names: Vec = pk_columns .iter() @@ -110,7 +101,6 @@ fn generate_create_table_sql( column_defs.push(pk_def); } - // Собираем итоговую инструкцию. let mut table_sql = format!( "CREATE TABLE {}.\"{}\" (\n {}\n)", schema, @@ -124,9 +114,6 @@ fn generate_create_table_sql( Ok(table_sql) } -/// Генерирует DDL для индексов таблицы. -/// Используются PRAGMA index_list и PRAGMA index_info для извлечения информации об индексах. -/// Автоиндексы (имена начинаются с "sqlite_autoindex") пропускаются. fn generate_indexes_sql(table: &str, conn: &Connection, schema: &str) -> Result, Box> { let mut indexes = Vec::new(); let mut stmt = conn.prepare(&format!("PRAGMA index_list(\"{}\")", table))?; @@ -167,7 +154,6 @@ fn generate_indexes_sql(table: &str, conn: &Connection, schema: &str) -> Result< } fn main() -> Result<(), Box> { - // Обработка аргументов командной строки. let args: Vec = env::args().collect(); if args.iter().any(|arg| arg == "--help" || arg == "-h") { print_help(&args[0]); @@ -182,7 +168,6 @@ fn main() -> Result<(), Box> { let output_file = &args[2]; let schema = &args[3]; - // Если передана опция наследования, извлекаем её значение. let mut inherit_clause: Option = None; for arg in &args[4..] { if arg.starts_with("--inherit=") { @@ -190,37 +175,32 @@ fn main() -> Result<(), Box> { } } - // Открываем SQLite БД. - let conn = Connection::open(sqlite_file)?; + let temp_file = NamedTempFile::new()?; + fs::copy(sqlite_file, temp_file.path())?; + let conn = Connection::open(temp_file.path())?; - // Открываем (или создаём) выходной файл. let mut out = File::create(output_file)?; - // Записываем заголовок. writeln!(out, "-- PostgreSQL database dump generated from SQLite")?; writeln!(out, "CREATE SCHEMA IF NOT EXISTS {};\n", schema)?; writeln!(out, "SET client_encoding = 'UTF8';\n")?; - // Получаем имена таблиц (исключая внутренние). let mut stmt = conn.prepare( "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'" )?; let table_names = stmt.query_map([], |row| row.get(0))?; for table_name_result in table_names { let table_name: String = table_name_result?; - // Генерируем DDL для таблицы, передавая также опциональный inherit_clause. let table_sql = generate_create_table_sql(&table_name, &conn, schema, inherit_clause.as_deref())?; writeln!(out, "{}\n", table_sql)?; writeln!(out, "ALTER TABLE {}.\"{}\" OWNER TO postgres;\n", schema, table_name)?; - // Генерируем DDL для индексов. let indexes = generate_indexes_sql(&table_name, &conn, schema)?; for idx in indexes { writeln!(out, "{}\n", idx)?; } } - // Обработка таблицы sqlite_sequence (для автоинкрементных значений). let sqlite_sequence_exists: bool = conn.query_row( "SELECT EXISTS(SELECT 1 FROM sqlite_master WHERE type='table' AND name='sqlite_sequence')", [],