use std::io::{self, Error};
use sqlx::{ColumnIndex, Database, Decode, Encode, Executor, IntoArguments, Type};
use crate::sql::{
dialects::context::Context,
schema::{self, types::convert_sql_type},
};
pub fn sql_create_table(
context: &Context,
table: &schema::table::TableSchema,
) -> io::Result<Vec<String>> {
if table.columns.is_empty() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("表 {} 没有定义字段", table.name),
));
}
let mut create_table = "CREATE TABLE IF NOT EXISTS ".to_string();
let table_name = context.table_name_with_schema(&table.name_with_schema());
create_table.push_str(context.quote(&table_name).as_str());
create_table.push_str(" (");
let mut pks: Vec<String> = vec![];
for col in &table.columns {
if col.pk {
pks.push(col.get_column_name());
}
}
let mut cols = vec![];
for col in &table.columns {
let col_def = context.sql_column(col, pks.len() == 1, None, convert_sql_type);
cols.push(col_def);
}
create_table.push_str(cols.join(",").as_str());
if pks.len() > 1 {
create_table.push_str(", PRIMARY KEY (");
create_table.push_str(pks.join(",").as_str());
create_table.push_str(")");
}
create_table.push_str(")");
Ok(vec![create_table])
}
#[derive(Default, sqlx::FromRow, Debug)]
struct Exists {
pub exists: bool,
}
pub async fn is_table_exists<'c, C, DB: Database>(
context: &Context,
conn: &mut C,
table_name: String,
schema: String,
) -> io::Result<bool>
where
for<'e> &'e mut C: Executor<'e, Database = DB>,
for<'a> DB::Arguments<'a>: IntoArguments<'a, DB>,
for<'a> std::string::String: Encode<'a, DB> + sqlx::Type<DB>,
for<'a> bool: sqlx::Decode<'a, DB> + sqlx::Type<DB>,
for<'a> &'a str: ColumnIndex<DB::Row>,
{
let schema_name = if schema.is_empty() {
context.get_default_schema()
} else {
schema.clone()
};
let query: Result<Exists, sqlx::Error> = if schema_name.is_empty() {
sqlx::query_as::<DB, Exists>(
r#"SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = $1
)"#,
)
.bind(table_name)
.fetch_one(&mut *conn)
.await
} else {
sqlx::query_as(
r#"SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = $1
AND table_name = $2
)"#,
)
.bind(schema_name)
.bind(table_name)
.fetch_one(conn)
.await
};
query
.map_err(|err| {
tracing::error!("check table exists error: {:?}", err);
Error::new(io::ErrorKind::Other, "check table exists error")
})
.map(|exists| exists.exists)
}
#[derive(Default, sqlx::FromRow)]
pub struct Table {
pub tablename: String,
pub schemaname: String,
}
pub async fn get_tables<'c, C, DB: Database>(
context: &Context,
conn: &mut C,
) -> io::Result<Vec<(String, String)>>
where
for<'e> &'e mut C: Executor<'e, Database = DB>,
for<'a> DB::Arguments<'a>: IntoArguments<'a, DB>,
for<'a> String: Decode<'a, DB> + Type<DB>,
for<'a> &'a str: ColumnIndex<DB::Row>,
for<'a> std::string::String: Encode<'a, DB>,
{
let query: Result<Vec<Table>, sqlx::Error> = if context.get_default_schema().is_empty() {
sqlx::query_as(r#"select tablename, schemaname from pg_tables"#)
.fetch_all(conn)
.await
} else {
sqlx::query_as(r#"select tablename, schemaname from pg_tables where schemaname=$1"#)
.bind(context.get_default_schema())
.fetch_all(conn)
.await
};
query
.map(|tables| {
tables
.iter()
.map(|t| (t.tablename.to_owned(), t.schemaname.to_owned()))
.collect()
})
.map_err(|err| {
tracing::error!("query tables error: {:?}", err);
Error::new(io::ErrorKind::Other, "query tables error")
})
}