diff --git a/src/dbc.rs b/src/dbc.rs index ecdc599..305a9d0 100644 --- a/src/dbc.rs +++ b/src/dbc.rs @@ -1,12 +1,11 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; - use serde_json; mod mysql; -mod sqlite; mod postgres; +mod sqlite; pub type Error = Box; @@ -47,7 +46,11 @@ impl Database { Ok(serde_json::to_vec(&result)?) } - pub fn execute_query_with_params(&mut self, query: &str, params: &[&str]) -> Result { + pub fn execute_query_with_params( + &mut self, + query: &str, + params: &[&str], + ) -> Result { let mut query = query.to_string(); for param in params { query = query.replace("?", param); @@ -55,7 +58,11 @@ impl Database { self.execute_query(&query) } - pub fn execute_query_with_params_and_serialize(&mut self, query: &str, params: &[&str]) -> Result { + pub fn execute_query_with_params_and_serialize( + &mut self, + query: &str, + params: &[&str], + ) -> Result { let result = self.execute_query_with_params(query, params)?; Ok(serde_json::to_string(&result)?) } @@ -97,24 +104,114 @@ pub struct QueryResult { #[derive(Serialize, Deserialize, Clone, Debug)] pub enum ColumnType { NULL, + NUMERIC, DECIMAL, + DECIMAL_NEW, INT, + INT2, + INT4, + INT8, + INT24, TINY, SHORT, LONG, LONGLONG, FLOAT, + FLOAT4, + FLOAT8, BIT, + BOOL, DOUBLE, STRING, + VARCHAR, + TEXT, + CHAR, + BYTEA, TIMESTAMP, + TIMESTAMPTZ, DATE, TIME, + TIMETZ, + INTERVAL, YEAR, DATETIME, JSON, + JSONB, ENUM, SET, BLOB, + BLOB_TINY, + BLOB_MEDIUM, + BLOB_LONG, GEOMETRY, + UUID, + OID, + XML, + CIDR, + INET, + MACADDR, + VARBIT, + REFCURSOR, } + +macro_rules! add_postgres_types { + ($( $existing_variant:ident ),* ) => { + pub enum ColumnType { + $( + $existing_variant, + )* + Postgres(postgres::types::Type), + } + }; +} + +add_postgres_types!( + NULL, + NUMERIC, + DECIMAL, + DECIMAL_NEW, + INT, + INT2, + INT4, + INT8, + INT24, + TINY, + SHORT, + LONG, + LONGLONG, + FLOAT, + FLOAT4, + FLOAT8, + BIT, + BOOL, + DOUBLE, + STRING, + VARCHAR, + TEXT, + CHAR, + BYTEA, + TIMESTAMP, + TIMESTAMPTZ, + DATE, + TIME, + TIMETZ, + INTERVAL, + YEAR, + DATETIME, + JSON, + JSONB, + ENUM, + SET, + BLOB, + BLOB_TINY, + BLOB_MEDIUM, + BLOB_LONG, + GEOMETRY, + UUID, + OID, + XML, + CIDR, + INET, + MACADDR, + VARBIT, +); diff --git a/src/dbc/mysql.rs b/src/dbc/mysql.rs index c1fb5ef..e032dd3 100644 --- a/src/dbc/mysql.rs +++ b/src/dbc/mysql.rs @@ -1,8 +1,8 @@ -use std::sync::Arc; +use crate::dbc; use mysql; use mysql::prelude::Queryable; use mysql_common::constants::ColumnType; -use crate::dbc; +use std::sync::Arc; pub(crate) struct MySQLConnection { connection: mysql::Conn, @@ -19,37 +19,37 @@ impl MySQLConnection { impl dbc::Connection for MySQLConnection { fn execute(&mut self, query: &str) -> Result { let result = self.connection.query_iter(query)?; - let columns = result.columns().as_ref().iter().map( - |column| { - dbc::Column { - name: column.name_str().to_string(), - column_type: column.column_type().into(), - } - } - ).collect::>(); + let columns = result + .columns() + .as_ref() + .iter() + .map(|column| dbc::Column { + name: column.name_str().to_string(), + column_type: column.column_type().into(), + }) + .collect::>(); let columns = Arc::from(columns); - let mut rows: Vec = Vec::new(); for row in result { let row = row?; - let values: Vec = row.unwrap_raw().iter().map( - |value| { + let values: Vec = row + .unwrap_raw() + .iter() + .map(|value| { if value.is_none() { dbc::Value::NULL } else { value.as_ref().unwrap().into() } - } - ).collect(); + }) + .collect(); rows.push(dbc::Row { values, columns: Arc::clone(&columns), }); } - Ok(dbc::QueryResult{ - rows, - }) + Ok(dbc::QueryResult { rows }) } } @@ -62,8 +62,12 @@ impl From<&mysql::Value> for dbc::Value { mysql::Value::UInt(uint) => dbc::Value::UInt(*uint), mysql::Value::Float(float) => dbc::Value::Float(*float), mysql::Value::Double(double) => dbc::Value::Double(*double), - mysql::Value::Date(year, month, day, hour, minute, second, microsecond) => dbc::Value::Date(*year, *month, *day, *hour, *minute, *second, *microsecond), - mysql::Value::Time(negative, days, hours, minutes, seconds, microseconds) => dbc::Value::Time(*negative, *days, *hours, *minutes, *seconds, *microseconds), + mysql::Value::Date(year, month, day, hour, minute, second, microsecond) => { + dbc::Value::Date(*year, *month, *day, *hour, *minute, *second, *microsecond) + } + mysql::Value::Time(negative, days, hours, minutes, seconds, microseconds) => { + dbc::Value::Time(*negative, *days, *hours, *minutes, *seconds, *microseconds) + } } } } @@ -80,26 +84,26 @@ impl From for dbc::ColumnType { ColumnType::MYSQL_TYPE_NULL => dbc::ColumnType::NULL, ColumnType::MYSQL_TYPE_TIMESTAMP => dbc::ColumnType::TIMESTAMP, ColumnType::MYSQL_TYPE_LONGLONG => dbc::ColumnType::LONGLONG, - ColumnType::MYSQL_TYPE_INT24 => dbc::ColumnType::INT, + ColumnType::MYSQL_TYPE_INT24 => dbc::ColumnType::INT24, ColumnType::MYSQL_TYPE_DATE => dbc::ColumnType::DATE, ColumnType::MYSQL_TYPE_TIME => dbc::ColumnType::TIME, - ColumnType::MYSQL_TYPE_DATETIME => dbc::ColumnType::TIMESTAMP, - ColumnType::MYSQL_TYPE_YEAR => dbc::ColumnType::INT, + ColumnType::MYSQL_TYPE_DATETIME => dbc::ColumnType::DATETIME, + ColumnType::MYSQL_TYPE_YEAR => dbc::ColumnType::YEAR, ColumnType::MYSQL_TYPE_NEWDATE => dbc::ColumnType::DATE, // Internal? do we need this? - ColumnType::MYSQL_TYPE_VARCHAR => dbc::ColumnType::STRING, + ColumnType::MYSQL_TYPE_VARCHAR => dbc::ColumnType::VARCHAR, ColumnType::MYSQL_TYPE_BIT => dbc::ColumnType::BIT, - ColumnType::MYSQL_TYPE_TIMESTAMP2 => dbc::ColumnType::TIMESTAMP, - ColumnType::MYSQL_TYPE_DATETIME2 => dbc::ColumnType::DATETIME, - ColumnType::MYSQL_TYPE_TIME2 => dbc::ColumnType::TIME, + ColumnType::MYSQL_TYPE_TIMESTAMP2 => dbc::ColumnType::TIMESTAMP, // Internal? do we need this? + ColumnType::MYSQL_TYPE_DATETIME2 => dbc::ColumnType::DATETIME, // Internal? do we need this? + ColumnType::MYSQL_TYPE_TIME2 => dbc::ColumnType::TIME, // Internal? do we need this? ColumnType::MYSQL_TYPE_JSON => dbc::ColumnType::JSON, - ColumnType::MYSQL_TYPE_NEWDECIMAL => dbc::ColumnType::DECIMAL, + ColumnType::MYSQL_TYPE_NEWDECIMAL => dbc::ColumnType::DECIMAL_NEW, ColumnType::MYSQL_TYPE_ENUM => dbc::ColumnType::ENUM, ColumnType::MYSQL_TYPE_SET => dbc::ColumnType::SET, - ColumnType::MYSQL_TYPE_TINY_BLOB => dbc::ColumnType::BLOB, - ColumnType::MYSQL_TYPE_MEDIUM_BLOB => dbc::ColumnType::BLOB, - ColumnType::MYSQL_TYPE_LONG_BLOB => dbc::ColumnType::BLOB, + ColumnType::MYSQL_TYPE_TINY_BLOB => dbc::ColumnType::BLOB_TINY, + ColumnType::MYSQL_TYPE_MEDIUM_BLOB => dbc::ColumnType::BLOB_MEDIUM, + ColumnType::MYSQL_TYPE_LONG_BLOB => dbc::ColumnType::BLOB_LONG, ColumnType::MYSQL_TYPE_BLOB => dbc::ColumnType::BLOB, - ColumnType::MYSQL_TYPE_VAR_STRING => dbc::ColumnType::STRING, + ColumnType::MYSQL_TYPE_VAR_STRING => dbc::ColumnType::VARCHAR, ColumnType::MYSQL_TYPE_STRING => dbc::ColumnType::STRING, ColumnType::MYSQL_TYPE_GEOMETRY => dbc::ColumnType::GEOMETRY, _ => { @@ -107,4 +111,4 @@ impl From for dbc::ColumnType { } } } -} \ No newline at end of file +} diff --git a/src/dbc/postgres.rs b/src/dbc/postgres.rs index 131fd56..0e2db4d 100644 --- a/src/dbc/postgres.rs +++ b/src/dbc/postgres.rs @@ -1 +1,86 @@ -use postgres; \ No newline at end of file +use std::sync::Arc; + +use postgres; + +use crate::dbc; + +pub struct PostgresConnection { + pub(crate) connection: postgres::Client, +} + +impl PostgresConnection { + pub(crate) fn get_connection(url: &str) -> Result, dbc::Error> { + Ok(Box::new(PostgresConnection { + connection: postgres::Client::connect(url, postgres::NoTls)?, + }) as Box) + } +} + +impl dbc::Connection for PostgresConnection { + fn execute(&mut self, query: &str) -> Result { + let result = self.connection.query(query, &[])?; + let columns = result + .columns() + .iter() + .map(|column| dbc::Column { + name: column.name().to_string(), + column_type: column.type_().into(), + }) + .collect::>(); + let columns = Arc::from(columns); + + let mut rows: Vec = Vec::new(); + for row in result { + let values: Vec = row + .iter() + .map(|value| { + if value.is_none() { + dbc::Value::NULL + } else { + value.unwrap().into() + } + }) + .collect(); + rows.push(dbc::Row { + values, + columns: Arc::clone(&columns), + }); + } + Ok(dbc::QueryResult { rows }) + } +} + +impl From for dbc::ColumnType { + fn from(value: postgres::types::Type) -> Self { + match value { + postgres::types::Type::BOOL => dbc::ColumnType::BOOL, + postgres::types::Type::INT2 => dbc::ColumnType::INT2, + postgres::types::Type::INT4 => dbc::ColumnType::INT4, + postgres::types::Type::INT8 => dbc::ColumnType::INT8, + postgres::types::Type::FLOAT4 => dbc::ColumnType::FLOAT4, + postgres::types::Type::FLOAT8 => dbc::ColumnType::FLOAT8, + postgres::types::Type::NUMERIC => dbc::ColumnType::NUMERIC, + postgres::types::Type::TIMESTAMP => dbc::ColumnType::TIMESTAMP, + postgres::types::Type::TIMESTAMPTZ => dbc::ColumnType::TIMESTAMPTZ, + postgres::types::Type::DATE => dbc::ColumnType::DATE, + postgres::types::Type::TIME => dbc::ColumnType::TIME, + postgres::types::Type::TIMETZ => dbc::ColumnType::TIMETZ, + postgres::types::Type::INTERVAL => dbc::ColumnType::INTERVAL, + postgres::types::Type::TEXT => dbc::ColumnType::TEXT, + postgres::types::Type::CHAR => dbc::ColumnType::CHAR, + postgres::types::Type::VARCHAR => dbc::ColumnType::VARCHAR, + postgres::types::Type::BYTEA => dbc::ColumnType::BYTEA, + postgres::types::Type::UUID => dbc::ColumnType::UUID, + postgres::types::Type::JSON => dbc::ColumnType::JSON, + postgres::types::Type::JSONB => dbc::ColumnType::JSONB, + postgres::types::Type::XML => dbc::ColumnType::XML, + postgres::types::Type::OID => dbc::ColumnType::OID, + postgres::types::Type::CIDR => dbc::ColumnType::CIDR, + postgres::types::Type::INET => dbc::ColumnType::INET, + postgres::types::Type::MACADDR => dbc::ColumnType::MACADDR, + postgres::types::Type::BIT => dbc::ColumnType::BIT, + postgres::types::Type::VARBIT => dbc::ColumnType::VARBIT, + _ => dbc::ColumnType::UNKNOWN, + } + } +}