diff --git a/.gitignore b/.gitignore index 24cb7a2..e024f7b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ secring.* # Added by cargo /target +.env diff --git a/.justfile b/.justfile new file mode 100644 index 0000000..232e670 --- /dev/null +++ b/.justfile @@ -0,0 +1,4 @@ +set dotenv-load := true + +dev: + cargo run --bin cli -- query "SELECT * FROM sources;" diff --git a/Cargo.lock b/Cargo.lock index d8489aa..4cc4da1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,21 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "anstream" @@ -38,7 +53,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -49,9 +64,48 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", - "windows-sys", + "windows-sys 0.59.0", ] +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "clap" version = "4.5.26" @@ -98,6 +152,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "heck" version = "0.5.0" @@ -110,12 +170,92 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + [[package]] name = "proc-macro2" version = "1.0.93" @@ -134,11 +274,58 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simple-crud" version = "0.1.0" dependencies = [ "clap", + "tokio", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -158,6 +345,35 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.14" @@ -170,6 +386,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index c4c692c..04da768 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] clap = { version = "4.5.26", features = ["derive", "env"] } +tokio = {version = "1.43.0", features = ["full"]} diff --git a/src/bin/cli/main.rs b/src/bin/cli/main.rs index 17e1dc4..d0da4c2 100644 --- a/src/bin/cli/main.rs +++ b/src/bin/cli/main.rs @@ -21,14 +21,33 @@ enum Commands { }, } -fn main() { +#[tokio::main] +async fn main() { let cli = Cli::parse(); + let url = cli.db_url.unwrap(); + // You can check for the existence of subcommands, and if found use their // matches just as you would the top level cmd match &cli.command { - Commands::Query { sql } => { - println!("Provided query: {sql:?}"); - } + Commands::Query { sql } => handle_query(url, sql).await.unwrap(), } } + +async fn handle_query(url: String, query: &String) -> Result<(), sqlx::Error> { + let pool = PgPool::connect(url.as_str()).await?; + + let mut rows = sqlx::query(query.as_str()).fetch(&pool); + + while let Some(row) = rows.try_next().await? { + // map the row into a user-defined domain type + let columns = row.columns(); + + for col in columns { + let value = String::from(row.get(col.ordinal())); + println!("Column {} value: {}", col.name(), value); + } + } + + return Ok(()); +} diff --git a/src/lib.rs b/src/lib.rs index e69de29..2752f63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1 @@ +pub mod sql; diff --git a/src/sql.rs b/src/sql.rs new file mode 100644 index 0000000..67c567f --- /dev/null +++ b/src/sql.rs @@ -0,0 +1 @@ +pub mod parser; diff --git a/src/sql/parser.rs b/src/sql/parser.rs new file mode 100644 index 0000000..b6b3ffe --- /dev/null +++ b/src/sql/parser.rs @@ -0,0 +1,47 @@ +/// The postgres-crate does not provide a default mapping to fallback to String for all +/// types: row.get is generic and without a type assignment the FromSql-Trait cannot be inferred. +/// This function matches over the current column-type and does a manual conversion +pub fn reflective_get(row: &Row, index: usize) -> String { + let column_type = row.columns().get(index).map(|c| c.type_().name()).unwrap(); + // see https://docs.rs/sqlx/0.4.0-beta.1/sqlx/postgres/types/index.html + + let value = match column_type { + "bool" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "varchar" | "char(n)" | "text" | "name" => row.get(index), + "char" => { + let v = row.get::<_, i8>(index); + Some(v.to_string()) + } + "smallserial" | "smallint" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "int" | "int4" | "serial" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "int8" | "bigserial" | "bigint" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "float4" | "real" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "float8" | "double precision" => { + let v = row.get::<_, Option>(index); + v.map(|v| v.to_string()) + } + "timestamp" | "timestamptz" => { + // with-chrono feature is needed for this + let v: Option = row.get(index); + let v = DateTime::::from(v.unwrap()); + Some(v.to_string()) + } + &_ => Some("CANNOT PARSE".to_string()), + }; + value.unwrap_or("null".to_string()) +}