generated from alecodes/base-template
wip: add sqlx setup
This commit is contained in:
parent
57074a89ff
commit
7350923ee1
8 changed files with 2129 additions and 15 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ secring.*
|
|||
# Added by cargo
|
||||
|
||||
/target
|
||||
.env
|
||||
|
|
|
|||
4
.justfile
Normal file
4
.justfile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
set dotenv-load := true
|
||||
|
||||
dev:
|
||||
cargo run --bin cli -- query "SELECT * FROM sources;"
|
||||
2008
Cargo.lock
generated
2008
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,3 +5,12 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.26", features = ["derive", "env"] }
|
||||
futures = "0.3.31"
|
||||
sqlx = { version = "0.8", features = [
|
||||
"runtime-tokio",
|
||||
"tls-native-tls",
|
||||
"postgres",
|
||||
"sqlite",
|
||||
"chrono",
|
||||
] }
|
||||
tokio = {version = "1.43.0", features = ["full"]}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
use clap::{Parser, Subcommand};
|
||||
use futures::TryStreamExt;
|
||||
use sqlx::postgres::PgPool;
|
||||
use sqlx::Column;
|
||||
use sqlx::Row;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
|
|
@ -21,14 +25,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(());
|
||||
}
|
||||
|
|
|
|||
18
src/lib.rs
18
src/lib.rs
|
|
@ -0,0 +1,18 @@
|
|||
pub mod sql;
|
||||
|
||||
struct Column<T> {
|
||||
sql_type: String,
|
||||
value: T,
|
||||
}
|
||||
|
||||
enum ColumnType {
|
||||
I8(Column<i8>),
|
||||
String(Column<String>),
|
||||
Bool(Column<bool>),
|
||||
I16(Column<i16>),
|
||||
I32(Column<i32>),
|
||||
I64(Column<i64>),
|
||||
F32(Column<f32>),
|
||||
F64(Column<f64>),
|
||||
DateTime(Column<String>),
|
||||
}
|
||||
1
src/sql.rs
Normal file
1
src/sql.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod parser;
|
||||
72
src/sql/parser.rs
Normal file
72
src/sql/parser.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use crate::{Column, ColumnType};
|
||||
use sqlx::Column as _;
|
||||
use sqlx::Row as _;
|
||||
|
||||
/// 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: &impl sqlx::Row, index: usize) -> Option<ColumnType> {
|
||||
let type_info = row.columns().get(index)?.type_info();
|
||||
let column_type = type_info.name();
|
||||
// see https://docs.rs/sqlx/0.4.0-beta.1/sqlx/postgres/types/index.html
|
||||
|
||||
match column_type {
|
||||
"bool" => {
|
||||
let v: bool = row.try_get(index)?;
|
||||
Some(ColumnType::Bool(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"varchar" | "char(n)" | "text" | "name" => row.get(index),
|
||||
"char" => {
|
||||
let v = row.get::<_, i8>(index);
|
||||
Some(ColumnType::I8(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"smallserial" | "smallint" => {
|
||||
let v = row.get::<_, Option<i16>>(index);
|
||||
Some(ColumnType::I16(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"int" | "int4" | "serial" => {
|
||||
let v = row.get::<_, Option<i32>>(index);
|
||||
Some(ColumnType::I32(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"int8" | "bigserial" | "bigint" => {
|
||||
let v = row.get::<_, Option<i64>>(index);
|
||||
Some(ColumnType::I64(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"float4" | "real" => {
|
||||
let v = row.get::<_, Option<f32>>(index);
|
||||
Some(ColumnType::F32(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
"float8" | "double precision" => {
|
||||
let v = row.get::<_, Option<f64>>(index);
|
||||
Some(ColumnType::F64(Column {
|
||||
sql_type: String::from(column_type),
|
||||
value: v,
|
||||
}))
|
||||
}
|
||||
// "timestamp" | "timestamptz" => {
|
||||
// // with-chrono feature is needed for this
|
||||
// let v: Option<SystemTime> = row.get(index);
|
||||
// let v = DateTime::<chrono::Utc>::from(v.unwrap());
|
||||
// Some(v.to_string())
|
||||
// }
|
||||
&_ => None,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue