diff --git a/Cargo.toml b/Cargo.toml index 671c03ed6..4c1e1268a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,15 +50,26 @@ proc-macro2 = { version = "1", optional = true } quote = { version = "^1", optional = true } time = { version = "^0.2", optional = true } +[dependencies.r2d2-oracle] # override +git = "https://github.com/Niedzwiedzw/r2d2-oracle" +features = ["chrono"] +optional = true + [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } pretty_assertions = { version = "^1" } + + [features] backend-mysql = [] backend-postgres = [] backend-sqlite = [] -default = ["derive", "backend-mysql", "backend-postgres", "backend-sqlite"] +backend-oracle = ["oracle-overrides"] + +oracle-overrides = ["r2d2-oracle", "with-chrono"] + +default = ["derive", "backend-mysql", "backend-postgres", "backend-sqlite", "backend-oracle"] derive = ["sea-query-derive"] postgres = ["bytes", "postgres-types"] postgres-chrono = ["with-chrono", "postgres-types/with-chrono-0_4"] @@ -106,6 +117,11 @@ name = "test-sqlite" path = "tests/sqlite/mod.rs" required-features = ["backend-sqlite"] +[[test]] +name = "test-oracle" +path = "tests/oracle/mod.rs" +required-features = ["backend-oracle"] + [[bench]] name = "basic" harness = false diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 64b856e2e..83ba15591 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -5,6 +5,9 @@ use crate::*; #[cfg(feature = "backend-mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "backend-mysql")))] mod mysql; +#[cfg(feature = "backend-oracle")] +#[cfg_attr(docsrs, doc(cfg(feature = "backend-oracle")))] +mod oracle; #[cfg(feature = "backend-postgres")] #[cfg_attr(docsrs, doc(cfg(feature = "backend-postgres")))] mod postgres; @@ -14,6 +17,8 @@ mod sqlite; #[cfg(feature = "backend-mysql")] pub use mysql::*; +#[cfg(feature = "backend-oracle")] +pub use oracle::*; #[cfg(feature = "backend-postgres")] pub use postgres::*; #[cfg(feature = "backend-sqlite")] diff --git a/src/backend/oracle/foreign_key.rs b/src/backend/oracle/foreign_key.rs new file mode 100644 index 000000000..ee38a717d --- /dev/null +++ b/src/backend/oracle/foreign_key.rs @@ -0,0 +1,79 @@ +use super::*; + +impl ForeignKeyBuilder for OracleQueryBuilder { + fn prepare_foreign_key_drop_statement( + &self, + drop: &ForeignKeyDropStatement, + sql: &mut SqlWriter, + ) { + write!(sql, "ALTER TABLE ").unwrap(); + if let Some(table) = &drop.table { + table.prepare(sql, self.quote()); + } + + write!(sql, " DROP CONSTRAINT ").unwrap(); + if let Some(name) = &drop.foreign_key.name { + write!(sql, "\"{}\"", name).unwrap(); + } + } + + fn prepare_foreign_key_create_statement_internal( + &self, + create: &ForeignKeyCreateStatement, + sql: &mut SqlWriter, + inside_table_creation: bool, + ) { + if !inside_table_creation { + write!(sql, "ALTER TABLE ").unwrap(); + if let Some(table) = &create.foreign_key.table { + table.prepare(sql, self.quote()); + } + write!(sql, " ADD ").unwrap(); + } + + if let Some(name) = &create.foreign_key.name { + write!(sql, "CONSTRAINT ").unwrap(); + write!(sql, "\"{}\" ", name).unwrap(); + } + + write!(sql, "FOREIGN KEY (").unwrap(); + create.foreign_key.columns.iter().fold(true, |first, col| { + if !first { + write!(sql, ", ").unwrap(); + } + col.prepare(sql, self.quote()); + false + }); + write!(sql, ")").unwrap(); + + write!(sql, " REFERENCES ").unwrap(); + if let Some(ref_table) = &create.foreign_key.ref_table { + ref_table.prepare(sql, self.quote()); + } + write!(sql, " ").unwrap(); + + write!(sql, "(").unwrap(); + create + .foreign_key + .ref_columns + .iter() + .fold(true, |first, col| { + if !first { + write!(sql, ", ").unwrap(); + } + col.prepare(sql, self.quote()); + false + }); + write!(sql, ")").unwrap(); + + if let Some(foreign_key_action) = &create.foreign_key.on_delete { + write!(sql, " ON DELETE ").unwrap(); + self.prepare_foreign_key_action(foreign_key_action, sql); + } + + if let Some(foreign_key_action) = &create.foreign_key.on_update { + write!(sql, " ON UPDATE ").unwrap(); + self.prepare_foreign_key_action(foreign_key_action, sql); + } + } +} diff --git a/src/backend/oracle/index.rs b/src/backend/oracle/index.rs new file mode 100644 index 000000000..11fb88d3a --- /dev/null +++ b/src/backend/oracle/index.rs @@ -0,0 +1,64 @@ +use super::*; + +impl IndexBuilder for OracleQueryBuilder { + fn prepare_table_index_expression(&self, create: &IndexCreateStatement, sql: &mut SqlWriter) { + if create.index.name.is_some() { + write!(sql, "CONSTRAINT ").unwrap(); + self.prepare_index_name(&create.index.name, sql); + write!(sql, " ").unwrap(); + } + + self.prepare_index_prefix(create, sql); + + self.prepare_index_columns(&create.index.columns, sql); + } + + fn prepare_index_create_statement(&self, create: &IndexCreateStatement, sql: &mut SqlWriter) { + write!(sql, "CREATE ").unwrap(); + self.prepare_index_prefix(create, sql); + write!(sql, "INDEX ").unwrap(); + + self.prepare_index_name(&create.index.name, sql); + + write!(sql, " ON ").unwrap(); + if let Some(table) = &create.table { + table.prepare(sql, self.quote()); + } + + self.prepare_index_type(&create.index_type, sql); + + self.prepare_index_columns(&create.index.columns, sql); + } + + fn prepare_index_drop_statement(&self, drop: &IndexDropStatement, sql: &mut SqlWriter) { + write!(sql, "DROP INDEX ").unwrap(); + if let Some(name) = &drop.index.name { + write!(sql, "\"{}\"", name).unwrap(); + } + } + + fn prepare_index_type(&self, col_index_type: &Option, sql: &mut SqlWriter) { + if let Some(index_type) = col_index_type { + write!( + sql, + " USING {}", + match index_type { + IndexType::BTree => "BTREE".to_owned(), + IndexType::FullText => "GIN".to_owned(), + IndexType::Hash => "HASH".to_owned(), + IndexType::Custom(custom) => custom.to_string(), + } + ) + .unwrap(); + } + } + + fn prepare_index_prefix(&self, create: &IndexCreateStatement, sql: &mut SqlWriter) { + if create.primary { + write!(sql, "PRIMARY KEY ").unwrap(); + } + if create.unique { + write!(sql, "UNIQUE ").unwrap(); + } + } +} diff --git a/src/backend/oracle/mod.rs b/src/backend/oracle/mod.rs new file mode 100644 index 000000000..a9a4baf4e --- /dev/null +++ b/src/backend/oracle/mod.rs @@ -0,0 +1,27 @@ +pub(crate) mod foreign_key; +pub(crate) mod index; +pub(crate) mod query; +pub(crate) mod table; +pub(crate) mod types; + +use super::*; + +/// Postgres query builder. +#[derive(Debug)] +pub struct OracleQueryBuilder; + +impl Default for OracleQueryBuilder { + fn default() -> Self { + Self + } +} + +impl GenericBuilder for OracleQueryBuilder {} + +impl SchemaBuilder for OracleQueryBuilder {} + +impl QuotedBuilder for OracleQueryBuilder { + fn quote(&self) -> char { + '"' + } +} diff --git a/src/backend/oracle/query.rs b/src/backend/oracle/query.rs new file mode 100644 index 000000000..f61157f94 --- /dev/null +++ b/src/backend/oracle/query.rs @@ -0,0 +1,320 @@ +use super::*; +// use crate::extension::postgres::*; + +impl QueryBuilder for OracleQueryBuilder { + fn placeholder(&self) -> (&str, bool) { + (":", true) + } + + fn if_null_function(&self) -> &str { + "COALESCE" + } + + fn write_string_quoted(&self, string: &str, buffer: &mut String) { + let escaped = escape_string(string); + let string = if escaped.find('\\').is_some() { + "E'".to_owned() + &escaped + "'" + } else { + "'".to_owned() + &escaped + "'" + }; + write!(buffer, "{}", string).unwrap() + } + + fn prepare_bin_oper( + &self, + bin_oper: &BinOper, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + match bin_oper { + // BinOper::Matches => write!(sql, "@@").unwrap(), + // BinOper::Contains => write!(sql, "@>").unwrap(), + // BinOper::Contained => write!(sql, "<@").unwrap(), + // BinOper::Concatenate => write!(sql, "||").unwrap(), + _ => self.prepare_bin_oper_common(bin_oper, sql, collector), + } + } + + fn prepare_function( + &self, + function: &Function, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + match function { + // Function::PgFunction(function) => write!( + // sql, + // "{}", + // match function { + // PgFunction::ToTsquery => "TO_TSQUERY", + // PgFunction::ToTsvector => "TO_TSVECTOR", + // PgFunction::PhrasetoTsquery => "PHRASETO_TSQUERY", + // PgFunction::PlaintoTsquery => "PLAINTO_TSQUERY", + // PgFunction::WebsearchToTsquery => "WEBSEARCH_TO_TSQUERY", + // PgFunction::TsRank => "TS_RANK", + // PgFunction::TsRankCd => "TS_RANK_CD", + // #[cfg(feature = "postgres-array")] + // PgFunction::Any => "ANY", + // #[cfg(feature = "postgres-array")] + // PgFunction::Some => "SOME", + // #[cfg(feature = "postgres-array")] + // PgFunction::All => "ALL", + // } + // ) + // .unwrap(), + _ => self.prepare_function_common(function, sql, collector), + } + } + + fn prepare_simple_expr( + &self, + simple_expr: &SimpleExpr, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + match simple_expr { + SimpleExpr::AsEnum(type_name, expr) => { + let simple_expr = expr.clone().cast_as(SeaRc::clone(type_name)); + self.prepare_simple_expr_common(&simple_expr, sql, collector); + } + _ => QueryBuilder::prepare_simple_expr_common(self, simple_expr, sql, collector), + } + } + + fn prepare_order_expr( + &self, + order_expr: &OrderExpr, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + if !matches!(order_expr.order, Order::Field(_)) { + self.prepare_simple_expr(&order_expr.expr, sql, collector); + } + write!(sql, " ").unwrap(); + self.prepare_order(order_expr, sql, collector); + match order_expr.nulls { + None => (), + Some(NullOrdering::Last) => write!(sql, " NULLS LAST").unwrap(), + Some(NullOrdering::First) => write!(sql, " NULLS FIRST").unwrap(), + } + } + + fn prepare_query_statement( + &self, + query: &SubQueryStatement, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + query.prepare_statement(self, sql, collector); + } + + /// Translate [`SelectExpr`] into oracle SQL statement. + fn prepare_select_expr( + &self, + select_expr: &SelectExpr, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + self.prepare_simple_expr(&select_expr.expr, sql, collector); + match &select_expr.window { + Some(WindowSelectType::Name(name)) => { + write!(sql, " OVER ").unwrap(); + name.prepare(sql, self.quote()) + } + Some(WindowSelectType::Query(window)) => { + write!(sql, " OVER ").unwrap(); + write!(sql, "( ").unwrap(); + self.prepare_window_statement(window, sql, collector); + write!(sql, " ) ").unwrap(); + } + None => {} + }; + // oracle override + match &select_expr.alias { + Some(alias) => { + write!(sql, " ").unwrap(); + alias.prepare(sql, self.quote()); + } + None => {} + }; + } + + /// Translate [`SelectStatement`] into oracle SQL statement. + fn prepare_select_statement( + &self, + select: &SelectStatement, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + write!(sql, "SELECT ").unwrap(); + + if let Some(distinct) = &select.distinct { + write!(sql, " ").unwrap(); + self.prepare_select_distinct(distinct, sql, collector); + write!(sql, " ").unwrap(); + } + + select.selects.iter().fold(true, |first, expr| { + if !first { + write!(sql, ", ").unwrap() + } + self.prepare_select_expr(expr, sql, collector); + false + }); + + if !select.from.is_empty() { + write!(sql, " FROM ").unwrap(); + select.from.iter().fold(true, |first, table_ref| { + if !first { + write!(sql, ", ").unwrap() + } + backend::query_builder::QueryBuilder::prepare_table_ref( + self, table_ref, sql, collector, + ); + false + }); + } + + if !select.join.is_empty() { + for expr in select.join.iter() { + write!(sql, " ").unwrap(); + self.prepare_join_expr(expr, sql, collector); + } + } + + self.prepare_condition(&select.r#where, "WHERE", sql, collector); + + if !select.groups.is_empty() { + write!(sql, " GROUP BY ").unwrap(); + select.groups.iter().fold(true, |first, expr| { + if !first { + write!(sql, ", ").unwrap() + } + self.prepare_simple_expr(expr, sql, collector); + false + }); + } + + self.prepare_condition(&select.having, "HAVING", sql, collector); + + if !select.unions.is_empty() { + select.unions.iter().for_each(|(union_type, query)| { + match union_type { + UnionType::Distinct => write!(sql, " UNION ").unwrap(), + UnionType::All => write!(sql, " UNION ALL ").unwrap(), + } + self.prepare_select_statement(query, sql, collector); + }); + } + + if !select.orders.is_empty() { + write!(sql, " ORDER BY ").unwrap(); + select.orders.iter().fold(true, |first, expr| { + if !first { + write!(sql, ", ").unwrap() + } + self.prepare_order_expr(expr, sql, collector); + false + }); + } + + // oracle override - requires oracle 11c or later! + if let Some(offset) = &select.offset { + write!(sql, " OFFSET ").unwrap(); + self.prepare_value(offset, sql, collector); + write!(sql, " ROWS ").unwrap(); + } + + if let Some(limit) = &select.limit { + write!(sql, " FETCH NEXT ").unwrap(); + self.prepare_value(limit, sql, collector); + write!(sql, " ROWS ONLY ").unwrap(); + } + + if let Some(lock) = &select.lock { + write!(sql, " ").unwrap(); + self.prepare_select_lock(lock, sql, collector); + } + + if let Some((name, query)) = &select.window { + write!(sql, " WINDOW ").unwrap(); + name.prepare(sql, self.quote()); + write!(sql, " AS ").unwrap(); + self.prepare_window_statement(query, sql, collector); + } + } + /// Oracle override. + fn prepare_table_ref_common( + &self, + table_ref: &TableRef, + sql: &mut SqlWriter, + collector: &mut dyn FnMut(Value), + ) { + match table_ref { + TableRef::Table(iden) => { + iden.prepare(sql, self.quote()); + } + TableRef::SchemaTable(schema, table) => { + schema.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + table.prepare(sql, self.quote()); + } + TableRef::DatabaseSchemaTable(database, schema, table) => { + database.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + schema.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + table.prepare(sql, self.quote()); + } + TableRef::TableAlias(iden, alias) => { + iden.prepare(sql, self.quote()); + write!(sql, " AS ").unwrap(); + alias.prepare(sql, self.quote()); + } + TableRef::SchemaTableAlias(schema, table, alias) => { + schema.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + table.prepare(sql, self.quote()); + write!(sql, " AS ").unwrap(); + alias.prepare(sql, self.quote()); + } + TableRef::DatabaseSchemaTableAlias(database, schema, table, alias) => { + database.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + schema.prepare(sql, self.quote()); + write!(sql, ".").unwrap(); + table.prepare(sql, self.quote()); + write!(sql, " AS ").unwrap(); + alias.prepare(sql, self.quote()); + } + TableRef::SubQuery(query, alias) => { + // oracle override + write!(sql, "(").unwrap(); + self.prepare_select_statement(query, sql, collector); + write!(sql, ")").unwrap(); + write!(sql, " ").unwrap(); + alias.prepare(sql, self.quote()); + } + } + } + + /// Translate [`JoinType`] into oracle SQL statement. + fn prepare_join_type( + &self, + join_type: &JoinType, + sql: &mut SqlWriter, + _collector: &mut dyn FnMut(Value), + ) { + write!( + sql, + "{}", + match join_type { + JoinType::Join => "JOIN", + JoinType::InnerJoin => "JOIN", + JoinType::LeftJoin => "LEFT OUTER JOIN", + JoinType::RightJoin => "RIGHT JOIN", + } + ) + .unwrap() + } +} diff --git a/src/backend/oracle/table.rs b/src/backend/oracle/table.rs new file mode 100644 index 000000000..30ae3f3f0 --- /dev/null +++ b/src/backend/oracle/table.rs @@ -0,0 +1,214 @@ +use super::*; + +impl TableBuilder for OracleQueryBuilder { + fn prepare_column_def(&self, column_def: &ColumnDef, sql: &mut SqlWriter) { + column_def.name.prepare(sql, self.quote()); + + self.prepare_column_type_check_auto_increment(column_def, sql); + + for column_spec in column_def.spec.iter() { + if let ColumnSpec::AutoIncrement = column_spec { + continue; + } + write!(sql, " ").unwrap(); + self.prepare_column_spec(column_spec, sql); + } + } + + fn prepare_column_type(&self, column_type: &ColumnType, sql: &mut SqlWriter) { + write!( + sql, + "{}", + match column_type { + ColumnType::Char(length) => match length { + Some(length) => format!("char({})", length), + None => "char".into(), + }, + ColumnType::String(length) => match length { + Some(length) => format!("varchar({})", length), + None => "varchar".into(), + }, + ColumnType::Text => "text".into(), + ColumnType::TinyInteger(length) | ColumnType::TinyUnsigned(length) => + match length { + Some(length) => format!("smallint({})", length), + None => "smallint".into(), + }, + ColumnType::SmallInteger(length) | ColumnType::SmallUnsigned(length) => + match length { + Some(length) => format!("smallint({})", length), + None => "smallint".into(), + }, + ColumnType::Integer(length) | ColumnType::Unsigned(length) => match length { + Some(length) => format!("integer({})", length), + None => "integer".into(), + }, + ColumnType::BigInteger(length) | ColumnType::BigUnsigned(length) => match length { + Some(length) => format!("bigint({})", length), + None => "bigint".into(), + }, + ColumnType::Float(precision) => match precision { + Some(precision) => format!("real({})", precision), + None => "real".into(), + }, + ColumnType::Double(precision) => match precision { + Some(precision) => format!("double precision({})", precision), + None => "double precision".into(), + }, + ColumnType::Decimal(precision) => match precision { + Some((precision, scale)) => format!("decimal({}, {})", precision, scale), + None => "decimal".into(), + }, + ColumnType::DateTime(precision) => match precision { + Some(precision) => format!("timestamp({}) without time zone", precision), + None => "timestamp without time zone".into(), + }, + ColumnType::Timestamp(precision) => match precision { + Some(precision) => format!("timestamp({})", precision), + None => "timestamp".into(), + }, + ColumnType::TimestampWithTimeZone(precision) => match precision { + Some(precision) => format!("timestamp({}) with time zone", precision), + None => "timestamp with time zone".into(), + }, + ColumnType::Time(precision) => match precision { + Some(precision) => format!("time({})", precision), + None => "time".into(), + }, + ColumnType::Date => "date".into(), + ColumnType::Binary(length) => match length { + Some(_) | None => "bytea".into(), + }, + ColumnType::Boolean => "bool".into(), + ColumnType::Money(precision) => match precision { + Some((precision, scale)) => format!("money({}, {})", precision, scale), + None => "money".into(), + }, + ColumnType::Json => "json".into(), + ColumnType::JsonBinary => "jsonb".into(), + ColumnType::Uuid => "uuid".into(), + ColumnType::Array(elem_type) => format!("{}[]", elem_type.as_ref().unwrap()), + ColumnType::Custom(iden) => iden.to_string(), + ColumnType::Enum(name, _) => name.into(), + _ => unimplemented!(), + // ColumnType::Interval(fields, precision) => { + // let mut typ = "interval".to_string(); + // if let Some(fields) = fields { + // typ.push_str(&format!(" {}", fields)); + // } + // if let Some(precision) = precision { + // typ.push_str(&format!("({})", precision)); + // } + // typ + // } + } + ) + .unwrap() + } + + fn prepare_column_spec(&self, column_spec: &ColumnSpec, sql: &mut SqlWriter) { + match column_spec { + ColumnSpec::Null => write!(sql, "NULL"), + ColumnSpec::NotNull => write!(sql, "NOT NULL"), + ColumnSpec::Default(value) => write!(sql, "DEFAULT {}", self.value_to_string(value)), + ColumnSpec::AutoIncrement => write!(sql, ""), + ColumnSpec::UniqueKey => write!(sql, "UNIQUE"), + ColumnSpec::PrimaryKey => write!(sql, "PRIMARY KEY"), + ColumnSpec::Extra(string) => write!(sql, "{}", string), + } + .unwrap() + } + + fn prepare_table_alter_statement(&self, alter: &TableAlterStatement, sql: &mut SqlWriter) { + if alter.options.is_empty() { + panic!("No alter option found") + }; + write!(sql, "ALTER TABLE ").unwrap(); + if let Some(table) = &alter.table { + table.prepare(sql, self.quote()); + write!(sql, " ").unwrap(); + } + + alter.options.iter().fold(true, |first, option| { + if !first { + write!(sql, ", ").unwrap(); + }; + match option { + TableAlterOption::AddColumn(AddColumnOption { + column, + if_not_exists, + }) => { + write!(sql, "ADD COLUMN ").unwrap(); + if *if_not_exists { + write!(sql, "IF NOT EXISTS ").unwrap(); + } + self.prepare_column_def(column, sql); + } + TableAlterOption::ModifyColumn(column_def) => { + write!(sql, "ALTER COLUMN ").unwrap(); + column_def.name.prepare(sql, self.quote()); + write!(sql, " TYPE").unwrap(); + self.prepare_column_type_check_auto_increment(column_def, sql); + for column_spec in column_def.spec.iter() { + if let ColumnSpec::AutoIncrement = column_spec { + continue; + } + write!(sql, ", ").unwrap(); + write!(sql, "ALTER COLUMN ").unwrap(); + column_def.name.prepare(sql, self.quote()); + write!(sql, " SET ").unwrap(); + self.prepare_column_spec(column_spec, sql); + } + } + TableAlterOption::RenameColumn(from_name, to_name) => { + write!(sql, "RENAME COLUMN ").unwrap(); + from_name.prepare(sql, self.quote()); + write!(sql, " TO ").unwrap(); + to_name.prepare(sql, self.quote()); + } + TableAlterOption::DropColumn(column_name) => { + write!(sql, "DROP COLUMN ").unwrap(); + column_name.prepare(sql, self.quote()); + } + } + false + }); + } + + fn prepare_table_rename_statement(&self, rename: &TableRenameStatement, sql: &mut SqlWriter) { + write!(sql, "ALTER TABLE ").unwrap(); + if let Some(from_name) = &rename.from_name { + from_name.prepare(sql, self.quote()); + } + write!(sql, " RENAME TO ").unwrap(); + if let Some(to_name) = &rename.to_name { + to_name.prepare(sql, self.quote()); + } + } +} + +impl OracleQueryBuilder { + fn prepare_column_type_check_auto_increment( + &self, + column_def: &ColumnDef, + sql: &mut SqlWriter, + ) { + if let Some(column_type) = &column_def.types { + write!(sql, " ").unwrap(); + let is_auto_increment = column_def + .spec + .iter() + .position(|s| matches!(s, ColumnSpec::AutoIncrement)); + if is_auto_increment.is_some() { + match &column_type { + ColumnType::SmallInteger(_) => write!(sql, "smallserial").unwrap(), + ColumnType::Integer(_) => write!(sql, "serial").unwrap(), + ColumnType::BigInteger(_) => write!(sql, "bigserial").unwrap(), + _ => unimplemented!(), + } + } else { + self.prepare_column_type(column_type, sql); + } + } + } +} diff --git a/src/backend/oracle/types.rs b/src/backend/oracle/types.rs new file mode 100644 index 000000000..afa18fd12 --- /dev/null +++ b/src/backend/oracle/types.rs @@ -0,0 +1,137 @@ +use super::*; +// use crate::extension::postgres::*; + +// impl TypeBuilder for OracleQueryBuilder { +// fn prepare_type_create_statement( +// &self, +// create: &TypeCreateStatement, +// sql: &mut SqlWriter, +// collector: &mut dyn FnMut(Value), +// ) { +// write!(sql, "CREATE TYPE ").unwrap(); + +// if let Some(name) = &create.name { +// name.prepare(sql, self.quote()); +// } + +// if let Some(as_type) = &create.as_type { +// write!(sql, " AS ").unwrap(); +// self.prepare_create_as_type(as_type, sql); +// } + +// if !create.values.is_empty() { +// write!(sql, " (").unwrap(); + +// for (count, val) in create.values.iter().enumerate() { +// if count > 0 { +// write!(sql, ", ").unwrap(); +// } +// self.prepare_value(&val.to_string().into(), sql, collector); +// } + +// write!(sql, ")").unwrap(); +// } +// } + +// fn prepare_type_drop_statement( +// &self, +// drop: &TypeDropStatement, +// sql: &mut SqlWriter, +// _collector: &mut dyn FnMut(Value), +// ) { +// write!(sql, "DROP TYPE ").unwrap(); + +// if drop.if_exists { +// write!(sql, "IF EXISTS ").unwrap(); +// } + +// for name in drop.names.iter() { +// name.prepare(sql, self.quote()); +// } + +// if let Some(option) = &drop.option { +// write!(sql, " ").unwrap(); +// self.prepare_drop_type_opt(option, sql); +// } +// } + +// fn prepare_type_alter_statement( +// &self, +// alter: &TypeAlterStatement, +// sql: &mut SqlWriter, +// collector: &mut dyn FnMut(Value), +// ) { +// write!(sql, "ALTER TYPE ").unwrap(); + +// if let Some(name) = &alter.name { +// name.prepare(sql, self.quote()); +// } + +// if let Some(option) = &alter.option { +// self.prepare_alter_type_opt(option, sql, collector) +// } +// } +// } + +// impl OracleQueryBuilder { +// fn prepare_create_as_type(&self, as_type: &TypeAs, sql: &mut SqlWriter) { +// write!( +// sql, +// "{}", +// match as_type { +// TypeAs::Enum => "ENUM", +// } +// ) +// .unwrap() +// } + +// fn prepare_drop_type_opt(&self, opt: &TypeDropOpt, sql: &mut SqlWriter) { +// write!( +// sql, +// "{}", +// match opt { +// TypeDropOpt::Cascade => "CASCADE", +// TypeDropOpt::Restrict => "RESTRICT", +// } +// ) +// .unwrap() +// } + +// fn prepare_alter_type_opt( +// &self, +// opt: &TypeAlterOpt, +// sql: &mut SqlWriter, +// collector: &mut dyn FnMut(Value), +// ) { +// match opt { +// TypeAlterOpt::Add(value, placement) => { +// write!(sql, " ADD VALUE ").unwrap(); +// match placement { +// Some(add_option) => match add_option { +// TypeAlterAddOpt::Before(before_value) => { +// self.prepare_value(&value.to_string().into(), sql, collector); +// write!(sql, " BEFORE ").unwrap(); +// self.prepare_value(&before_value.to_string().into(), sql, collector); +// } +// TypeAlterAddOpt::After(after_value) => { +// self.prepare_value(&value.to_string().into(), sql, collector); +// write!(sql, " AFTER ").unwrap(); +// self.prepare_value(&after_value.to_string().into(), sql, collector); +// } +// }, +// None => self.prepare_value(&value.to_string().into(), sql, collector), +// } +// } +// TypeAlterOpt::Rename(new_name) => { +// write!(sql, " RENAME TO ").unwrap(); +// self.prepare_value(&new_name.to_string().into(), sql, collector); +// } +// TypeAlterOpt::RenameValue(existing, new_name) => { +// write!(sql, " RENAME VALUE ").unwrap(); +// self.prepare_value(&existing.to_string().into(), sql, collector); +// write!(sql, " TO ").unwrap(); +// self.prepare_value(&new_name.to_string().into(), sql, collector); +// } +// } +// } +// } diff --git a/src/types.rs b/src/types.rs index e049d7870..44805f302 100644 --- a/src/types.rs +++ b/src/types.rs @@ -392,7 +392,7 @@ mod tests { ); #[cfg(feature = "backend-postgres")] assert_eq!( - query.to_string(PostgresQueryBuilder), + query.to_string(OracleQueryBuilder), r#"SELECT "hello-World_""# ); #[cfg(feature = "backend-sqlite")] @@ -414,7 +414,7 @@ mod tests { let query = Query::select().column(Alias::new("hel\"lo")).to_owned(); #[cfg(feature = "backend-postgres")] - assert_eq!(query.to_string(PostgresQueryBuilder), r#"SELECT "hel""lo""#); + assert_eq!(query.to_string(OracleQueryBuilder), r#"SELECT "hel""lo""#); } #[test] @@ -429,9 +429,6 @@ mod tests { let query = Query::select().column(Alias::new("hel\"\"lo")).to_owned(); #[cfg(feature = "backend-postgres")] - assert_eq!( - query.to_string(PostgresQueryBuilder), - r#"SELECT "hel""""lo""# - ); + assert_eq!(query.to_string(OracleQueryBuilder), r#"SELECT "hel""""lo""#); } } diff --git a/src/value.rs b/src/value.rs index 0cab56eeb..00ea755b3 100644 --- a/src/value.rs +++ b/src/value.rs @@ -105,6 +105,115 @@ pub enum Value { Array(Option>>), } +#[cfg(feature = "oracle-overrides")] +pub mod oracle_overrides { + use super::*; + use r2d2_oracle::oracle::sql_type::ToSql; + + // /// 2021-07-14T23:59:59 + // this works in my project, no idea if this helps anyone + // const CLIENT_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; + // const DB_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S"; + + // pub fn to_oracle_format(time: chrono::NaiveDateTime) -> String { + // format!( + // r#"to_date('{time}', 'YYYY-MM-DD HH24:MI:SS')"#, + // time = time.format(DB_DATETIME_FORMAT), + // ) + // } + + impl ToSql for Value { + /// I've commented out values I didn't know how to handle + fn oratype( + &self, + conn: &r2d2_oracle::oracle::Connection, + ) -> r2d2_oracle::oracle::Result { + match self { + Value::Bool(v) => v.oratype(conn), + Value::TinyInt(v) => v.oratype(conn), + Value::SmallInt(v) => v.oratype(conn), + Value::Int(v) => v.oratype(conn), + Value::BigInt(v) => v.oratype(conn), + Value::TinyUnsigned(v) => v.oratype(conn), + Value::SmallUnsigned(v) => v.oratype(conn), + Value::Unsigned(v) => v.oratype(conn), + Value::BigUnsigned(v) => v.oratype(conn), + Value::Float(v) => v.oratype(conn), + Value::Double(v) => v.oratype(conn), + Value::String(v) => v.as_ref().map(|v| v.as_ref().clone()).oratype(conn), + Value::Bytes(v) => v.as_ref().map(|v| v.as_ref().clone()).oratype(conn), + // #[cfg(feature = "with-json")] + // Value::Json(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + Value::ChronoDate(v) => v.as_ref().map(|v| *v.as_ref()).oratype(conn), + // Value::ChronoTime(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + Value::ChronoDateTime(v) => v.as_ref().map(|v| *v.as_ref()).oratype(conn), + Value::ChronoDateTimeUtc(v) => v.as_ref().map(|v| *v.as_ref()).oratype(conn), + Value::ChronoDateTimeLocal(v) => v.as_ref().map(|v| *v.as_ref()).oratype(conn), + Value::ChronoDateTimeWithTimeZone(v) => { + v.as_ref().map(|v| *v.as_ref()).oratype(conn) + } + // Value::TimeDate(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + // Value::TimeTime(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + // Value::TimeDateTime(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + // Value::TimeDateTimeWithTimeZone(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + // Value::Uuid(v) => v.map(|v| v.as_ref().clone()).oratype(conn), + // #[cfg(feature = "with-rust_decimal")] + // Value::Decimal(v) => v.map(|v| v.as_ref().clone().into()).oratype(conn), + #[cfg(feature = "with-bigdecimal")] + Value::BigDecimal(v) => v + .as_ref() + .map(|v| v.as_ref().clone().to_string()) // TODO: this is probably wrong + .oratype(conn), + v => unimplemented!("{v:?}"), + } + } + + fn to_sql( + &self, + conn: &mut r2d2_oracle::oracle::SqlValue, + ) -> r2d2_oracle::oracle::Result<()> { + match self { + Value::Bool(v) => v.to_sql(conn), + Value::TinyInt(v) => v.to_sql(conn), + Value::SmallInt(v) => v.to_sql(conn), + Value::Int(v) => v.to_sql(conn), + Value::BigInt(v) => v.to_sql(conn), + Value::TinyUnsigned(v) => v.to_sql(conn), + Value::SmallUnsigned(v) => v.to_sql(conn), + Value::Unsigned(v) => v.to_sql(conn), + Value::BigUnsigned(v) => v.to_sql(conn), + Value::Float(v) => v.to_sql(conn), + Value::Double(v) => v.to_sql(conn), + Value::String(v) => v.as_ref().map(|v| v.as_ref().clone()).to_sql(conn), + Value::Bytes(v) => v.as_ref().map(|v| v.as_ref().clone()).to_sql(conn), + // #[cfg(feature = "with-json")] + // Value::Json(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + Value::ChronoDate(v) => v.as_ref().map(|v| *v.as_ref()).to_sql(conn), + // Value::ChronoTime(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + Value::ChronoDateTime(v) => v.as_ref().map(|v| *v.as_ref()).to_sql(conn), + Value::ChronoDateTimeUtc(v) => v.as_ref().map(|v| *v.as_ref()).to_sql(conn), + Value::ChronoDateTimeLocal(v) => v.as_ref().map(|v| *v.as_ref()).to_sql(conn), + Value::ChronoDateTimeWithTimeZone(v) => { + v.as_ref().map(|v| *v.as_ref()).to_sql(conn) + } + // Value::TimeDate(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + // Value::TimeTime(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + // Value::TimeDateTime(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + // Value::TimeDateTimeWithTimeZone(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + // Value::Uuid(v) => v.map(|v| v.as_ref().clone()).to_sql(conn), + // #[cfg(feature = "with-rust_decimal")] + // Value::Decimal(v) => v.map(|v| v.as_ref().clone().into()).to_sql(conn), + #[cfg(feature = "with-bigdecimal")] + Value::BigDecimal(v) => v + .as_ref() + .map(|v| v.as_ref().clone().to_string()) // TODO: this is probably wrong + .to_sql(conn), + v => unimplemented!("{v:?}"), + } + } + } +} + pub trait ValueType: Sized { fn try_from(v: Value) -> Result; diff --git a/tests/oracle/foreign_key.rs b/tests/oracle/foreign_key.rs new file mode 100644 index 000000000..d0f523302 --- /dev/null +++ b/tests/oracle/foreign_key.rs @@ -0,0 +1,31 @@ +use super::*; + +#[test] +fn create_1() { + assert_eq!( + ForeignKey::create() + .name("FK_2e303c3a712662f1fc2a4d0aad6") + .from(Char::Table, Char::FontId) + .to(Font::Table, Font::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) + .to_string(OracleQueryBuilder), + vec![ + r#"ALTER TABLE "character" ADD CONSTRAINT "FK_2e303c3a712662f1fc2a4d0aad6""#, + r#"FOREIGN KEY ("font_id") REFERENCES "font" ("id")"#, + r#"ON DELETE CASCADE ON UPDATE CASCADE"#, + ] + .join(" ") + ); +} + +#[test] +fn drop_1() { + assert_eq!( + ForeignKey::drop() + .name("FK_2e303c3a712662f1fc2a4d0aad6") + .table(Char::Table) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "character" DROP CONSTRAINT "FK_2e303c3a712662f1fc2a4d0aad6""# + ); +} diff --git a/tests/oracle/index.rs b/tests/oracle/index.rs new file mode 100644 index 000000000..fd48ae1b0 --- /dev/null +++ b/tests/oracle/index.rs @@ -0,0 +1,50 @@ +use super::*; + +#[test] +fn create_1() { + assert_eq!( + Index::create() + .name("idx-glyph-aspect") + .table(Glyph::Table) + .col(Glyph::Aspect) + .to_string(OracleQueryBuilder), + r#"CREATE INDEX "idx-glyph-aspect" ON "glyph" ("aspect")"# + ); +} + +#[test] +fn create_2() { + assert_eq!( + Index::create() + .unique() + .name("idx-glyph-aspect-image") + .table(Glyph::Table) + .col(Glyph::Aspect) + .col(Glyph::Image) + .to_string(OracleQueryBuilder), + r#"CREATE UNIQUE INDEX "idx-glyph-aspect-image" ON "glyph" ("aspect", "image")"# + ); +} + +#[test] +fn create_3() { + assert_eq!( + Index::create() + .full_text() + .name("idx-glyph-image") + .table(Glyph::Table) + .col(Glyph::Image) + .to_string(OracleQueryBuilder), + r#"CREATE INDEX "idx-glyph-image" ON "glyph" USING GIN ("image")"# + ); +} + +#[test] +fn drop_1() { + assert_eq!( + Index::drop() + .name("idx-glyph-aspect") + .to_string(OracleQueryBuilder), + r#"DROP INDEX "idx-glyph-aspect""# + ); +} diff --git a/tests/oracle/interval.rs b/tests/oracle/interval.rs new file mode 100644 index 000000000..20d01c121 --- /dev/null +++ b/tests/oracle/interval.rs @@ -0,0 +1,35 @@ +use quote::{quote, ToTokens}; +use sea_query::*; + +// #[test] +// fn with_quote_1() { +// for (interval_field, token_stream) in [ +// (PgInterval::Year, quote! { PgInterval::Year }), +// (PgInterval::Month, quote! { PgInterval::Month }), +// (PgInterval::Day, quote! { PgInterval::Day }), +// (PgInterval::Hour, quote! { PgInterval::Hour }), +// (PgInterval::Minute, quote! { PgInterval::Minute }), +// (PgInterval::Second, quote! { PgInterval::Second }), +// (PgInterval::YearToMonth, quote! { PgInterval::YearToMonth }), +// (PgInterval::DayToHour, quote! { PgInterval::DayToHour }), +// (PgInterval::DayToMinute, quote! { PgInterval::DayToMinute }), +// (PgInterval::DayToSecond, quote! { PgInterval::DayToSecond }), +// ( +// PgInterval::HourToMinute, +// quote! { PgInterval::HourToMinute }, +// ), +// ( +// PgInterval::HourToSecond, +// quote! { PgInterval::HourToSecond }, +// ), +// ( +// PgInterval::MinuteToSecond, +// quote! { PgInterval::MinuteToSecond }, +// ), +// ] { +// assert_eq!( +// interval_field.into_token_stream().to_string(), +// token_stream.to_string() +// ); +// } +// } diff --git a/tests/oracle/mod.rs b/tests/oracle/mod.rs new file mode 100644 index 000000000..ee1e12d1b --- /dev/null +++ b/tests/oracle/mod.rs @@ -0,0 +1,10 @@ +use sea_query::{tests_cfg::*, *}; + +mod foreign_key; +mod index; +#[cfg(feature = "postgres-interval")] +mod interval; +#[allow(deprecated)] +mod query; +mod table; +mod types; diff --git a/tests/oracle/query.rs b/tests/oracle/query.rs new file mode 100644 index 000000000..4992704fb --- /dev/null +++ b/tests/oracle/query.rs @@ -0,0 +1,1246 @@ +use super::*; +use pretty_assertions::assert_eq; + +#[test] +fn select_1() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .limit(10) + .offset(100) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" OFFSET 100 ROWS FETCH NEXT 10 ROWS ONLY "# + ); +} + +#[test] +fn select_2() { + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .and_where(Expr::col(Char::SizeW).eq(3)) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "size_w" = 3"# + ); +} + +#[test] +fn select_3() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .and_where(Expr::col(Char::SizeW).eq(3)) + .and_where(Expr::col(Char::SizeH).eq(4)) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE ("size_w" = 3 AND "size_h" = 4)"# + ); +} + +#[test] +fn select_4() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect]) + .from_subquery( + Query::select() + .columns(vec![Glyph::Image, Glyph::Aspect]) + .from(Glyph::Table) + .take(), + Alias::new("subglyph") + ) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM (SELECT "image", "aspect" FROM "glyph") "subglyph""# + ); +} + +#[test] +fn select_5() { + assert_eq!( + Query::select() + .column((Glyph::Table, Glyph::Image)) + .from(Glyph::Table) + .and_where(Expr::tbl(Glyph::Table, Glyph::Aspect).is_in(vec![3, 4])) + .to_string(OracleQueryBuilder), + r#"SELECT "glyph"."image" FROM "glyph" WHERE "glyph"."aspect" IN (3, 4)"# + ); +} + +#[test] +fn select_6() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .exprs(vec![Expr::col(Glyph::Image).max(),]) + .from(Glyph::Table) + .group_by_columns(vec![Glyph::Aspect,]) + .and_having(Expr::col(Glyph::Aspect).gt(2)) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect", MAX("image") FROM "glyph" GROUP BY "aspect" HAVING "aspect" > 2"# + ); +} + +#[test] +fn select_7() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2"# + ); +} + +#[test] +fn select_8() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character,]) + .from(Char::Table) + .left_join( + Font::Table, + Expr::tbl(Char::Table, Char::FontId).equals(Font::Table, Font::Id) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" LEFT OUTER JOIN "font" ON "character"."font_id" = "font"."id""# + ); +} + +#[test] +fn select_9() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character,]) + .from(Char::Table) + .left_join( + Font::Table, + Expr::tbl(Char::Table, Char::FontId).equals(Font::Table, Font::Id) + ) + .inner_join( + Glyph::Table, + Expr::tbl(Char::Table, Char::Character).equals(Glyph::Table, Glyph::Image) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" LEFT OUTER JOIN "font" ON "character"."font_id" = "font"."id" JOIN "glyph" ON "character"."character" = "glyph"."image""# + ); +} + +#[test] +fn select_10() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character,]) + .from(Char::Table) + .left_join( + Font::Table, + Expr::tbl(Char::Table, Char::FontId) + .equals(Font::Table, Font::Id) + .and(Expr::tbl(Char::Table, Char::FontId).equals(Font::Table, Font::Id)) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" LEFT OUTER JOIN "font" ON ("character"."font_id" = "font"."id" AND "character"."font_id" = "font"."id")"# + ); +} + +#[test] +fn select_11() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by(Glyph::Image, Order::Desc) + .order_by((Glyph::Table, Glyph::Aspect), Order::Asc) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "image" DESC, "glyph"."aspect" ASC"# + ); +} + +#[test] +fn select_12() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by_columns(vec![(Glyph::Id, Order::Asc), (Glyph::Aspect, Order::Desc),]) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "id" ASC, "aspect" DESC"# + ); +} + +#[test] +fn select_13() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by_columns(vec![ + ((Glyph::Table, Glyph::Id), Order::Asc), + ((Glyph::Table, Glyph::Aspect), Order::Desc), + ]) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "glyph"."id" ASC, "glyph"."aspect" DESC"# + ); +} + +#[test] +fn select_14() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Id, Glyph::Aspect,]) + .expr(Expr::col(Glyph::Image).max()) + .from(Glyph::Table) + .group_by_columns(vec![ + (Glyph::Table, Glyph::Id), + (Glyph::Table, Glyph::Aspect), + ]) + .and_having(Expr::col(Glyph::Aspect).gt(2)) + .to_string(OracleQueryBuilder), + r#"SELECT "id", "aspect", MAX("image") FROM "glyph" GROUP BY "glyph"."id", "glyph"."aspect" HAVING "aspect" > 2"# + ); +} + +#[test] +fn select_15() { + assert_eq!( + Query::select() + .columns(vec![Char::Character]) + .from(Char::Table) + .and_where(Expr::col(Char::FontId).is_null()) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE "font_id" IS NULL"# + ); +} + +#[test] +fn select_16() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character]) + .from(Char::Table) + .and_where(Expr::col(Char::FontId).is_null()) + .and_where(Expr::col(Char::Character).is_not_null()) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE ("font_id" IS NULL AND "character" IS NOT NULL)"# + ); +} + +#[test] +fn select_17() { + assert_eq!( + Query::select() + .columns(vec![(Glyph::Table, Glyph::Image),]) + .from(Glyph::Table) + .and_where(Expr::tbl(Glyph::Table, Glyph::Aspect).between(3, 5)) + .to_string(OracleQueryBuilder), + r#"SELECT "glyph"."image" FROM "glyph" WHERE "glyph"."aspect" BETWEEN 3 AND 5"# + ); +} + +#[test] +fn select_18() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::col(Glyph::Aspect).between(3, 5)) + .and_where(Expr::col(Glyph::Aspect).not_between(8, 10)) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE ("aspect" BETWEEN 3 AND 5 AND "aspect" NOT BETWEEN 8 AND 10)"# + ); +} + +#[test] +fn select_19() { + assert_eq!( + Query::select() + .columns(vec![Char::Character]) + .from(Char::Table) + .and_where(Expr::col(Char::Character).eq("A")) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE "character" = 'A'"# + ); +} + +#[test] +fn select_20() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where(Expr::col(Char::Character).like("A")) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE "character" LIKE 'A'"# + ); +} + +#[test] +fn select_21() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character]) + .from(Char::Table) + .or_where(Expr::col(Char::Character).like("A%")) + .or_where(Expr::col(Char::Character).like("%B")) + .or_where(Expr::col(Char::Character).like("%C%")) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE ("character" LIKE 'A%' OR "character" LIKE '%B' OR "character" LIKE '%C%')"# + ); +} + +#[test] +fn select_22() { + // FIXME + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .cond_where( + Cond::all() + .add( + Cond::any().add(Expr::col(Char::Character).like("C")).add( + Expr::col(Char::Character) + .like("D") + .and(Expr::col(Char::Character).like("E")) + ) + ) + .add( + Expr::col(Char::Character) + .like("F") + .or(Expr::col(Char::Character).like("G")) + ) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE (("character" LIKE 'C' OR ("character" LIKE 'D' AND "character" LIKE 'E')) AND ("character" LIKE 'F' OR "character" LIKE 'G'))"# + ); +} + +#[test] +fn select_23() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where_option(None) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character""# + ); +} + +#[test] +fn select_24() { + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .conditions( + true, + |x| { + x.and_where(Expr::col(Char::FontId).eq(5)); + }, + |_| () + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE "font_id" = 5"# + ); +} + +#[test] +fn select_25() { + // FIXME + assert_eq!( + Query::select() + .column(Char::Character) + .from(Char::Table) + .and_where( + Expr::col(Char::SizeW) + .mul(2) + .equals(Expr::col(Char::SizeH).div(2)) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "character" FROM "character" WHERE ("size_w" * 2) = ("size_h" / 2)"# + ); +} + +// #[test] +// fn select_26() { +// assert_eq!( +// Query::select() +// .column(Char::Character) +// .from(Char::Table) +// .and_where( +// Expr::expr(Expr::col(Char::SizeW).add(1)) +// .mul(2) +// .equals(Expr::expr(Expr::col(Char::SizeH).div(2)).sub(1)) +// ) +// .to_string(OracleQueryBuilder), +// r#"SELECT "character" FROM "character" WHERE ("size_w" + 1) * 2 = ("size_h" / 2) - 1"# +// ); +// } + +#[test] +fn select_27() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .and_where(Expr::col(Char::SizeW).eq(3)) + .and_where(Expr::col(Char::SizeH).eq(4)) + .and_where(Expr::col(Char::SizeH).eq(5)) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE ("size_w" = 3 AND "size_h" = 4 AND "size_h" = 5)"# + ); +} + +#[test] +fn select_28() { + // FIXME + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .or_where(Expr::col(Char::SizeW).eq(3)) + .or_where(Expr::col(Char::SizeH).eq(4)) + .or_where(Expr::col(Char::SizeH).eq(5)) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE ("size_w" = 3 OR "size_h" = 4 OR "size_h" = 5)"# + ); +} + +#[test] +#[should_panic] +fn select_29() { + assert_eq!( + Query::select() + .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) + .from(Char::Table) + .and_where(Expr::col(Char::SizeW).eq(3)) + .or_where(Expr::col(Char::SizeH).eq(4)) + .and_where(Expr::col(Char::SizeH).eq(5)) + .to_string(OracleQueryBuilder), + r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE ("size_w" = 3 OR ("size_h" = 4 AND "size_h" = 5))"# + ); +} + +// #[test] +// fn select_30() { +// assert_eq!( +// Query::select() +// .columns(vec![Char::Character, Char::SizeW, Char::SizeH]) +// .from(Char::Table) +// .and_where( +// Expr::col(Char::SizeW) +// .mul(2) +// .add(Expr::col(Char::SizeH).div(3)) +// .equals(Expr::value(4)) +// ) +// .to_string(OracleQueryBuilder), +// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE ("size_w" * 2) + ("size_h" / 3) = 4"# +// ); +// } + +// #[test] +// fn select_31() { +// assert_eq!( +// Query::select() +// .expr((1..10_i32).fold(Expr::value(0), |expr, i| { expr.add(Expr::value(i)) })) +// .to_string(OracleQueryBuilder), +// r#"SELECT 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9"# +// ); +// } + +#[test] +fn select_32() { + // FIXME + assert_eq!( + Query::select() + .expr_as(Expr::col(Char::Character), Alias::new("C")) + .from(Char::Table) + .to_string(OracleQueryBuilder), + r#"SELECT "character" "C" FROM "character""# + ); +} + +// #[test] +// fn select_33() { +// assert_eq!( +// Query::select() +// .column(Glyph::Image) +// .from(Glyph::Table) +// .and_where( +// Expr::col(Glyph::Aspect) +// .in_subquery(Query::select().expr(Expr::cust("3 + 2 * 2")).take()) +// ) +// .to_string(OracleQueryBuilder), +// r#"SELECT "image" FROM "glyph" WHERE "aspect" IN (SELECT 3 + 2 * 2)"# +// ); +// } + +#[test] +fn select_34a() { + // FIXME + assert_eq!( + Query::select() + .column(Glyph::Aspect) + .expr(Expr::col(Glyph::Image).max()) + .from(Glyph::Table) + .group_by_columns(vec![Glyph::Aspect,]) + .or_having( + Expr::col(Glyph::Aspect) + .gt(2) + .or(Expr::col(Glyph::Aspect).lt(8)) + ) + .or_having( + Expr::col(Glyph::Aspect) + .gt(12) + .and(Expr::col(Glyph::Aspect).lt(18)) + ) + .or_having(Expr::col(Glyph::Aspect).gt(32)) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect", MAX("image") FROM "glyph" GROUP BY "aspect" HAVING ("aspect" > 2 OR "aspect" < 8 OR ("aspect" > 12 AND "aspect" < 18) OR "aspect" > 32)"#, + ); +} + +#[test] +#[should_panic] +fn select_34b() { + assert_eq!( + Query::select() + .column(Glyph::Aspect) + .expr(Expr::col(Glyph::Image).max()) + .from(Glyph::Table) + .group_by_columns(vec![Glyph::Aspect,]) + .or_having( + Expr::col(Glyph::Aspect) + .gt(2) + .or(Expr::col(Glyph::Aspect).lt(8)) + ) + .and_having( + Expr::col(Glyph::Aspect) + .gt(22) + .or(Expr::col(Glyph::Aspect).lt(28)) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect", MAX("image") FROM "glyph" GROUP BY "aspect" HAVING (("aspect" > 2 OR "aspect" < 8) AND ("aspect" > 22 OR "aspect" < 28))"#, + ); +} + +#[test] +fn select_35() { + let (statement, values) = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .and_where(Expr::col(Glyph::Aspect).is_null()) + .build(sea_query::OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE "aspect" IS NULL"# + ); + assert_eq!(values.0, vec![]); +} + +#[test] +fn select_36() { + let (statement, values) = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where(Cond::any().add(Expr::col(Glyph::Aspect).is_null())) + .build(sea_query::OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE "aspect" IS NULL"# + ); + assert_eq!(values.0, vec![]); +} + +#[test] +fn select_37() { + let (statement, values) = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where(Cond::any().add(Cond::all()).add(Cond::any())) + .build(sea_query::OracleQueryBuilder); + + assert_eq!(statement, r#"SELECT "id" FROM "glyph""#); + assert_eq!(values.0, vec![]); +} + +#[test] +fn select_38() { + // FIXME + let (statement, values) = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::any() + .add(Expr::col(Glyph::Aspect).is_null()) + .add(Expr::col(Glyph::Aspect).is_not_null()), + ) + .build(sea_query::OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE ("aspect" IS NULL OR "aspect" IS NOT NULL)"# + ); + assert_eq!(values.0, vec![]); +} + +#[test] +fn select_39() { + // FIXME + let (statement, values) = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::all() + .add(Expr::col(Glyph::Aspect).is_null()) + .add(Expr::col(Glyph::Aspect).is_not_null()), + ) + .build(sea_query::OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE ("aspect" IS NULL AND "aspect" IS NOT NULL)"# + ); + assert_eq!(values.0, vec![]); +} + +#[test] +fn select_40() { + // FIXME + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where(any![ + Expr::col(Glyph::Aspect).is_null(), + all![ + Expr::col(Glyph::Aspect).is_not_null(), + Expr::col(Glyph::Aspect).lt(8) + ] + ]) + .to_string(sea_query::OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE ("aspect" IS NULL OR ("aspect" IS NOT NULL AND "aspect" < 8))"# + ); +} + +#[test] +fn select_41() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect]) + .exprs(vec![Expr::col(Glyph::Image).max()]) + .from(Glyph::Table) + .group_by_columns(vec![Glyph::Aspect]) + .cond_having(any![Expr::col(Glyph::Aspect).gt(2)]) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect", MAX("image") FROM "glyph" GROUP BY "aspect" HAVING "aspect" > 2"# + ); +} + +#[test] +fn select_42() { + // FIXME + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::all() + .add_option(Some(Expr::col(Glyph::Aspect).lt(8))) + .add(Expr::col(Glyph::Aspect).is_not_null()), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE ("aspect" < 8 AND "aspect" IS NOT NULL)"# + ); +} + +#[test] +fn select_43() { + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where(Cond::all().add_option::(None)) + .to_string(OracleQueryBuilder); + + assert_eq!(statement, r#"SELECT "id" FROM "glyph""#); +} + +#[test] +fn select_44() { + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::any() + .not() + .add_option(Some(Expr::col(Glyph::Aspect).lt(8))), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE NOT ("aspect" < 8)"# + ); +} + +#[test] +fn select_45() { + // FIXME + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::any() + .not() + .add_option(Some(Expr::col(Glyph::Aspect).lt(8))) + .add(Expr::col(Glyph::Aspect).is_not_null()), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE NOT (("aspect" < 8 OR "aspect" IS NOT NULL))"# + ); +} + +#[test] +fn select_46() { + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::all() + .not() + .add_option(Some(Expr::col(Glyph::Aspect).lt(8))), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE NOT ("aspect" < 8)"# + ); +} + +#[test] +fn select_47() { + // FIXME + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::all() + .not() + .add_option(Some(Expr::col(Glyph::Aspect).lt(8))) + .add(Expr::col(Glyph::Aspect).is_not_null()), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE NOT (("aspect" < 8 AND "aspect" IS NOT NULL))"# + ); +} + +#[test] +fn select_48() { + // FIXME + let statement = sea_query::Query::select() + .column(Glyph::Id) + .from(Glyph::Table) + .cond_where( + Cond::all().add_option(Some(ConditionExpression::SimpleExpr( + Expr::tuple([ + Expr::col(Glyph::Aspect).into_simple_expr(), + Expr::value(100), + ]) + .less_than(Expr::tuple([Expr::value(8), Expr::value(100)])), + ))), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "id" FROM "glyph" WHERE ("aspect" <= 8 AND ("aspect" < 8 OR ("aspect" = 8 AND 100 < 100)))"# + ); +} + +#[test] +fn select_49() { + let statement = sea_query::Query::select() + .expr(Expr::asterisk()) + .from(Char::Table) + .to_string(OracleQueryBuilder); + + assert_eq!(statement, r#"SELECT * FROM "character""#); +} + +#[test] +fn select_50() { + // FIXME + let statement = sea_query::Query::select() + .expr(Expr::table_asterisk(Char::Table)) + .column((Font::Table, Font::Name)) + .from(Char::Table) + .inner_join( + Font::Table, + Expr::tbl(Char::Table, Char::FontId).equals(Font::Table, Font::Id), + ) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT "character".*, "font"."name" FROM "character" JOIN "font" ON "character"."font_id" = "font"."id""# + ) +} + +#[test] +fn select_51() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by_with_nulls(Glyph::Image, Order::Desc, NullOrdering::First) + .order_by_with_nulls( + (Glyph::Table, Glyph::Aspect), + Order::Asc, + NullOrdering::Last + ) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "image" DESC NULLS FIRST, "glyph"."aspect" ASC NULLS LAST"#, + ); +} + +#[test] +fn select_52() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by_columns_with_nulls(vec![ + (Glyph::Id, Order::Asc, NullOrdering::First), + (Glyph::Aspect, Order::Desc, NullOrdering::Last), + ]) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "id" ASC NULLS FIRST, "aspect" DESC NULLS LAST"#, + ); +} + +#[test] +fn select_53() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by_columns_with_nulls(vec![ + ((Glyph::Table, Glyph::Id), Order::Asc, NullOrdering::First), + ( + (Glyph::Table, Glyph::Aspect), + Order::Desc, + NullOrdering::Last + ), + ]) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "glyph"."id" ASC NULLS FIRST, "glyph"."aspect" DESC NULLS LAST"#, + ); +} + +#[test] +fn select_54() { + let statement = sea_query::Query::select() + .expr(Expr::asterisk()) + .from(Char::Table) + .from(Font::Table) + .and_where(Expr::tbl(Font::Table, Font::Id).equals(Char::Table, Char::FontId)) + .to_string(OracleQueryBuilder); + + assert_eq!( + statement, + r#"SELECT * FROM "character", "font" WHERE "font"."id" = "character"."font_id""# + ); +} + +#[test] +fn select_55() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by( + Glyph::Id, + Order::Field(Values(vec![ + Value::Int(Some(4)), + Value::Int(Some(5)), + Value::Int(Some(1)), + Value::Int(Some(3)) + ])) + ) + .order_by((Glyph::Table, Glyph::Aspect), Order::Asc) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY CASE WHEN "id"=4 THEN 0 WHEN "id"=5 THEN 1 WHEN "id"=1 THEN 2 WHEN "id"=3 THEN 3 ELSE 4 END, "glyph"."aspect" ASC"# + ); +} + +#[test] +fn select_56() { + assert_eq!( + Query::select() + .columns(vec![Glyph::Aspect,]) + .from(Glyph::Table) + .and_where(Expr::expr(Expr::col(Glyph::Aspect).if_null(0)).gt(2)) + .order_by((Glyph::Table, Glyph::Aspect), Order::Asc) + .order_by( + Glyph::Id, + Order::Field(Values(vec![ + Value::Int(Some(4)), + Value::Int(Some(5)), + Value::Int(Some(1)), + Value::Int(Some(3)) + ])) + ) + .to_string(OracleQueryBuilder), + r#"SELECT "aspect" FROM "glyph" WHERE COALESCE("aspect", 0) > 2 ORDER BY "glyph"."aspect" ASC, CASE WHEN "id"=4 THEN 0 WHEN "id"=5 THEN 1 WHEN "id"=1 THEN 2 WHEN "id"=3 THEN 3 ELSE 4 END"#, + ); +} + +#[test] +fn select_57() { + let select = SelectStatement::new() + .columns([Glyph::Id, Glyph::Image, Glyph::Aspect]) + .from(Glyph::Table) + .to_owned(); + let cte = CommonTableExpression::new() + .query(select) + .table_name(Alias::new("cte")) + .to_owned(); + let with_clause = WithClause::new().cte(cte).to_owned(); + let select = SelectStatement::new() + .columns([Glyph::Id, Glyph::Image, Glyph::Aspect]) + .from(Alias::new("cte")) + .to_owned(); + assert_eq!( + select.with(with_clause).to_string(OracleQueryBuilder), + r#"WITH "cte" AS (SELECT "id", "image", "aspect" FROM "glyph") SELECT "id", "image", "aspect" FROM "cte""#, + ); +} + +#[test] +fn select_58() { + // FIXME + let query = Query::select() + .expr_as( + CaseStatement::new() + .case( + Expr::tbl(Glyph::Table, Glyph::Aspect).gt(0), + Expr::val("positive"), + ) + .case( + Expr::tbl(Glyph::Table, Glyph::Aspect).lt(0), + Expr::val("negative"), + ) + .finally(Expr::val("zero")), + Alias::new("polarity"), + ) + .from(Glyph::Table) + .to_owned(); + + assert_eq!( + query.to_string(OracleQueryBuilder), + r#"SELECT CASE WHEN "glyph"."aspect" > 0 THEN 'positive' WHEN "glyph"."aspect" < 0 THEN 'negative' ELSE 'zero' END "polarity" FROM "glyph""# + ); +} + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_2() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Image, Glyph::Aspect,]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("image", "aspect") VALUES ('04108048005887010020060000204E0180400400', 3.1415)"# +// ); +// } + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_3() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Image, Glyph::Aspect,]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .values_panic(vec![Value::String(None), 2.1345.into(),]) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("image", "aspect") VALUES ('04108048005887010020060000204E0180400400', 3.1415), (NULL, 2.1345)"# +// ); +// } + +// #[test] +// #[cfg(feature = "with-chrono")] +// fn insert_4() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Image]) +// .values_panic(vec![chrono::NaiveDateTime::from_timestamp(0, 0).into()]) +// .to_string(OracleQueryBuilder), +// "INSERT INTO \"glyph\" (\"image\") VALUES ('1970-01-01 00:00:00')" +// ); +// } + +// #[test] +// #[cfg(feature = "with-time")] +// fn insert_9() { +// use time::{date, time}; +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Image]) +// .values_panic(vec![date!(1970 - 01 - 01) +// .with_time(time!(00:00:00)) +// .into()]) +// .to_string(OracleQueryBuilder), +// "INSERT INTO \"glyph\" (\"image\") VALUES ('1970-01-01 00:00:00')" +// ); +// } + +// #[test] +// #[cfg(feature = "with-uuid")] +// fn insert_5() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Image]) +// .values_panic(vec![uuid::Uuid::nil().into()]) +// .to_string(OracleQueryBuilder), +// "INSERT INTO \"glyph\" (\"image\") VALUES ('00000000-0000-0000-0000-000000000000')" +// ); +// } + +// #[test] +// fn insert_from_select() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Aspect, Glyph::Image]) +// .select_from( +// Query::select() +// .column(Glyph::Aspect) +// .column(Glyph::Image) +// .from(Glyph::Table) +// .conditions( +// true, +// |x| { +// x.and_where(Expr::col(Glyph::Image).like("%")); +// }, +// |x| { +// x.and_where(Expr::col(Glyph::Id).eq(6)); +// }, +// ) +// .to_owned() +// ) +// .unwrap() +// .to_owned() +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("aspect", "image") SELECT "aspect", "image" FROM "glyph" WHERE "image" LIKE '%'"# +// ); +// } + +// #[test] +// fn insert_6() -> sea_query::error::Result<()> { +// let select = SelectStatement::new() +// .columns([Glyph::Id, Glyph::Image, Glyph::Aspect]) +// .from(Glyph::Table) +// .to_owned(); +// let cte = CommonTableExpression::new() +// .query(select) +// .column(Glyph::Id) +// .column(Glyph::Image) +// .column(Glyph::Aspect) +// .table_name(Alias::new("cte")) +// .to_owned(); +// let with_clause = WithClause::new().cte(cte).to_owned(); +// let select = SelectStatement::new() +// .columns([Glyph::Id, Glyph::Image, Glyph::Aspect]) +// .from(Alias::new("cte")) +// .to_owned(); +// let mut insert = Query::insert(); +// insert +// .into_table(Glyph::Table) +// .columns([Glyph::Id, Glyph::Image, Glyph::Aspect]) +// .select_from(select)?; +// let sql = insert.with(with_clause).to_string(OracleQueryBuilder); +// assert_eq!( +// sql.as_str(), +// r#"WITH "cte" ("id", "image", "aspect") AS (SELECT "id", "image", "aspect" FROM "glyph") INSERT INTO "glyph" ("id", "image", "aspect") SELECT "id", "image", "aspect" FROM "cte""#, +// ); +// Ok(()) +// } + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_on_conflict_1() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Aspect, Glyph::Image]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .on_conflict( +// OnConflict::column(Glyph::Id) +// .update_column(Glyph::Aspect) +// .to_owned() +// ) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("aspect", "image") VALUES ('04108048005887010020060000204E0180400400', 3.1415) ON CONFLICT ("id") DO UPDATE SET "aspect" = "excluded"."aspect""#, +// ); +// } + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_on_conflict_2() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Aspect, Glyph::Image]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .on_conflict( +// OnConflict::columns([Glyph::Id, Glyph::Aspect]) +// .update_columns([Glyph::Aspect, Glyph::Image]) +// .to_owned() +// ) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("aspect", "image") VALUES ('04108048005887010020060000204E0180400400', 3.1415) ON CONFLICT ("id", "aspect") DO UPDATE SET "aspect" = "excluded"."aspect", "image" = "excluded"."image""#, +// ); +// } + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_on_conflict_3() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Aspect, Glyph::Image]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .on_conflict( +// OnConflict::columns([Glyph::Id, Glyph::Aspect]) +// .update_values([ +// ( +// Glyph::Aspect, +// "04108048005887010020060000204E0180400400".into() +// ), +// (Glyph::Image, 3.1415.into()), +// ]) +// .to_owned() +// ) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("aspect", "image") VALUES ('04108048005887010020060000204E0180400400', 3.1415) ON CONFLICT ("id", "aspect") DO UPDATE SET "aspect" = '04108048005887010020060000204E0180400400', "image" = 3.1415"#, +// ); +// } + +// #[test] +// #[allow(clippy::approx_constant)] +// fn insert_on_conflict_4() { +// assert_eq!( +// Query::insert() +// .into_table(Glyph::Table) +// .columns(vec![Glyph::Aspect, Glyph::Image]) +// .values_panic(vec![ +// "04108048005887010020060000204E0180400400".into(), +// 3.1415.into(), +// ]) +// .on_conflict( +// OnConflict::columns([Glyph::Id, Glyph::Aspect]) +// .update_expr((Glyph::Image, Expr::val(1).add(2))) +// .to_owned() +// ) +// .to_string(OracleQueryBuilder), +// r#"INSERT INTO "glyph" ("aspect", "image") VALUES ('04108048005887010020060000204E0180400400', 3.1415) ON CONFLICT ("id", "aspect") DO UPDATE SET "image" = 1 + 2"#, +// ); +// } + +// #[test] +// fn update_1() { +// assert_eq!( +// Query::update() +// .table(Glyph::Table) +// .values(vec![ +// (Glyph::Aspect, 2.1345.into()), +// ( +// Glyph::Image, +// "24B0E11951B03B07F8300FD003983F03F0780060".into() +// ), +// ]) +// .and_where(Expr::col(Glyph::Id).eq(1)) +// .to_string(OracleQueryBuilder), +// r#"UPDATE "glyph" SET "aspect" = 2.1345, "image" = '24B0E11951B03B07F8300FD003983F03F0780060' WHERE "id" = 1"# +// ); +// } + +// #[test] +// fn update_3() { +// assert_eq!( +// Query::update() +// .table(Glyph::Table) +// .value_expr(Glyph::Aspect, Expr::cust("60 * 24 * 24")) +// .values(vec![( +// Glyph::Image, +// "24B0E11951B03B07F8300FD003983F03F0780060".into() +// ),]) +// .and_where(Expr::col(Glyph::Id).eq(1)) +// .to_string(OracleQueryBuilder), +// r#"UPDATE "glyph" SET "aspect" = 60 * 24 * 24, "image" = '24B0E11951B03B07F8300FD003983F03F0780060' WHERE "id" = 1"# +// ); +// } + +// #[test] +// fn delete_1() { +// assert_eq!( +// Query::delete() +// .from_table(Glyph::Table) +// .and_where(Expr::col(Glyph::Id).eq(1)) +// .to_string(OracleQueryBuilder), +// r#"DELETE FROM "glyph" WHERE "id" = 1"# +// ); +// } diff --git a/tests/oracle/table.rs b/tests/oracle/table.rs new file mode 100644 index 000000000..bf07f95aa --- /dev/null +++ b/tests/oracle/table.rs @@ -0,0 +1,361 @@ +use super::*; + +#[test] +fn create_1() { + assert_eq!( + Table::create() + .table(Glyph::Table) + .col( + ColumnDef::new(Glyph::Id) + .integer() + .not_null() + .auto_increment() + .primary_key() + ) + .col(ColumnDef::new(Glyph::Aspect).double().not_null()) + .col(ColumnDef::new(Glyph::Image).text()) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE "glyph" ("#, + r#""id" serial NOT NULL PRIMARY KEY,"#, + r#""aspect" double precision NOT NULL,"#, + r#""image" text"#, + r#")"#, + ] + .join(" ") + ); +} + +#[test] +fn create_2() { + assert_eq!( + Table::create() + .table(Font::Table) + .col( + ColumnDef::new(Font::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Font::Name).string().not_null()) + .col(ColumnDef::new(Font::Variant).string_len(255).not_null()) + .col(ColumnDef::new(Font::Language).string_len(255).not_null()) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE "font" ("#, + r#""id" serial NOT NULL PRIMARY KEY,"#, + r#""name" varchar NOT NULL,"#, + r#""variant" varchar(255) NOT NULL,"#, + r#""language" varchar(255) NOT NULL"#, + r#")"#, + ] + .join(" ") + ); +} + +#[test] +fn create_3() { + assert_eq!( + Table::create() + .table(Char::Table) + .if_not_exists() + .col( + ColumnDef::new(Char::Id) + .integer() + .not_null() + .primary_key() + .auto_increment() + ) + .col(ColumnDef::new(Char::FontSize).integer().not_null()) + .col(ColumnDef::new(Char::Character).string_len(255).not_null()) + .col(ColumnDef::new(Char::SizeW).unsigned().not_null()) + .col(ColumnDef::new(Char::SizeH).unsigned().not_null()) + .col( + ColumnDef::new(Char::FontId) + .integer() + .default(Value::Int(None)) + ) + .foreign_key( + ForeignKey::create() + .name("FK_2e303c3a712662f1fc2a4d0aad6") + .from(Char::Table, Char::FontId) + .to(Font::Table, Font::Id) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade) + ) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE IF NOT EXISTS "character" ("#, + r#""id" serial NOT NULL PRIMARY KEY,"#, + r#""font_size" integer NOT NULL,"#, + r#""character" varchar(255) NOT NULL,"#, + r#""size_w" integer NOT NULL,"#, + r#""size_h" integer NOT NULL,"#, + r#""font_id" integer DEFAULT NULL,"#, + r#"CONSTRAINT "FK_2e303c3a712662f1fc2a4d0aad6""#, + r#"FOREIGN KEY ("font_id") REFERENCES "font" ("id")"#, + r#"ON DELETE CASCADE ON UPDATE CASCADE"#, + r#")"#, + ] + .join(" ") + ); +} + +#[test] +fn create_4() { + assert_eq!( + Table::create() + .table(Glyph::Table) + .col(ColumnDef::new(Glyph::Image).custom(Glyph::Aspect)) + .to_string(OracleQueryBuilder), + vec![r#"CREATE TABLE "glyph" ("#, r#""image" aspect"#, r#")"#,].join(" ") + ); +} + +#[test] +fn create_5() { + assert_eq!( + Table::create() + .table(Glyph::Table) + .col(ColumnDef::new(Glyph::Image).json()) + .col(ColumnDef::new(Glyph::Aspect).json_binary()) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE "glyph" ("#, + r#""image" json,"#, + r#""aspect" jsonb"#, + r#")"#, + ] + .join(" ") + ); +} + +#[test] +fn create_6() { + assert_eq!( + Table::create() + .table(Glyph::Table) + .col( + ColumnDef::new(Glyph::Id) + .integer() + .not_null() + .extra("ANYTHING I WANT TO SAY".to_owned()) + ) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE "glyph" ("#, + r#""id" integer NOT NULL ANYTHING I WANT TO SAY"#, + r#")"#, + ] + .join(" ") + ); +} + +// #[test] +// fn create_7() { +// assert_eq!( +// Table::create() +// .table(Glyph::Table) +// .col( +// ColumnDef::new(Glyph::Aspect) +// .interval(None, None) +// .not_null() +// ) +// .to_string(OracleQueryBuilder), +// vec![ +// r#"CREATE TABLE "glyph" ("#, +// r#""aspect" interval NOT NULL"#, +// r#")"#, +// ] +// .join(" ") +// ); +// } + +// #[test] +// fn create_8() { +// assert_eq!( +// Table::create() +// .table(Glyph::Table) +// .col( +// ColumnDef::new(Glyph::Aspect) +// .interval(Some(PgInterval::YearToMonth), None) +// .not_null() +// ) +// .to_string(OracleQueryBuilder), +// vec![ +// r#"CREATE TABLE "glyph" ("#, +// r#""aspect" interval YEAR TO MONTH NOT NULL"#, +// r#")"#, +// ] +// .join(" ") +// ); +// } + +// #[test] +// fn create_9() { +// assert_eq!( +// Table::create() +// .table(Glyph::Table) +// .col( +// ColumnDef::new(Glyph::Aspect) +// .interval(None, Some(42)) +// .not_null() +// ) +// .to_string(OracleQueryBuilder), +// vec![ +// r#"CREATE TABLE "glyph" ("#, +// r#""aspect" interval(42) NOT NULL"#, +// r#")"#, +// ] +// .join(" ") +// ); +// } + +// #[test] +// fn create_10() { +// assert_eq!( +// Table::create() +// .table(Glyph::Table) +// .col( +// ColumnDef::new(Glyph::Aspect) +// .interval(Some(PgInterval::Hour), Some(43)) +// .not_null() +// ) +// .to_string(OracleQueryBuilder), +// vec![ +// r#"CREATE TABLE "glyph" ("#, +// r#""aspect" interval HOUR(43) NOT NULL"#, +// r#")"#, +// ] +// .join(" ") +// ); +// } + +#[test] +fn create_11() { + assert_eq!( + Table::create() + .table(Char::Table) + .col( + ColumnDef::new(Char::CreatedAt) + .timestamp_with_time_zone_len(0) + .not_null() + ) + .to_string(OracleQueryBuilder), + vec![ + r#"CREATE TABLE "character" ("#, + r#""created_at" timestamp(0) with time zone NOT NULL"#, + r#")"#, + ] + .join(" ") + ); +} + +#[test] +fn drop_1() { + assert_eq!( + Table::drop() + .table(Glyph::Table) + .table(Char::Table) + .cascade() + .to_string(OracleQueryBuilder), + r#"DROP TABLE "glyph", "character" CASCADE"# + ); +} + +#[test] +fn truncate_1() { + assert_eq!( + Table::truncate() + .table(Font::Table) + .to_string(OracleQueryBuilder), + r#"TRUNCATE TABLE "font""# + ); +} + +#[test] +fn alter_1() { + assert_eq!( + Table::alter() + .table(Font::Table) + .add_column( + ColumnDef::new(Alias::new("new_col")) + .integer() + .not_null() + .default(100) + ) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "font" ADD COLUMN "new_col" integer NOT NULL DEFAULT 100"# + ); +} + +#[test] +fn alter_2() { + assert_eq!( + Table::alter() + .table(Font::Table) + .modify_column( + ColumnDef::new(Alias::new("new_col")) + .big_integer() + .default(999) + ) + .to_string(OracleQueryBuilder), + vec![ + r#"ALTER TABLE "font""#, + r#"ALTER COLUMN "new_col" TYPE bigint,"#, + r#"ALTER COLUMN "new_col" SET DEFAULT 999"#, + ] + .join(" ") + ); +} + +#[test] +fn alter_3() { + assert_eq!( + Table::alter() + .table(Font::Table) + .rename_column(Alias::new("new_col"), Alias::new("new_column")) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "font" RENAME COLUMN "new_col" TO "new_column""# + ); +} + +#[test] +fn alter_4() { + assert_eq!( + Table::alter() + .table(Font::Table) + .drop_column(Alias::new("new_column")) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "font" DROP COLUMN "new_column""# + ); +} + +#[test] +fn alter_5() { + assert_eq!( + Table::rename() + .table(Font::Table, Alias::new("font_new")) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "font" RENAME TO "font_new""# + ); +} + +#[test] +#[should_panic(expected = "No alter option found")] +fn alter_6() { + Table::alter().to_string(OracleQueryBuilder); +} + +#[test] +fn alter_7() { + assert_eq!( + Table::alter() + .table(Font::Table) + .add_column(ColumnDef::new(Alias::new("new_col")).integer()) + .rename_column(Font::Name, Alias::new("name_new")) + .to_string(OracleQueryBuilder), + r#"ALTER TABLE "font" ADD COLUMN "new_col" integer, RENAME COLUMN "name" TO "name_new""# + ); +} diff --git a/tests/oracle/types.rs b/tests/oracle/types.rs new file mode 100644 index 000000000..ea73604f3 --- /dev/null +++ b/tests/oracle/types.rs @@ -0,0 +1,102 @@ +// use sea_query::{extension::postgres::Type, Alias, OracleQueryBuilder}; + +// use super::*; + +// #[test] +// fn create_1() { +// assert_eq!( +// Type::create() +// .as_enum(Font::Table) +// .values(vec![Font::Name, Font::Variant, Font::Language]) +// .to_string(OracleQueryBuilder), +// r#"CREATE TYPE "font" AS ENUM ('name', 'variant', 'language')"# +// ); +// } + +// #[test] +// fn drop_1() { +// assert_eq!( +// Type::drop() +// .if_exists() +// .name(Font::Table) +// .restrict() +// .to_string(OracleQueryBuilder), +// r#"DROP TYPE IF EXISTS "font" RESTRICT"# +// ) +// } + +// #[test] +// fn drop_2() { +// assert_eq!( +// Type::drop().name(Font::Table).to_string(OracleQueryBuilder), +// r#"DROP TYPE "font""# +// ); +// } + +// #[test] +// fn drop_3() { +// assert_eq!( +// Type::drop() +// .if_exists() +// .name(Font::Table) +// .cascade() +// .to_string(OracleQueryBuilder), +// r#"DROP TYPE IF EXISTS "font" CASCADE"# +// ); +// } + +// #[test] +// fn alter_1() { +// assert_eq!( +// Type::alter() +// .name(Font::Table) +// .add_value(Alias::new("weight")) +// .to_string(OracleQueryBuilder), +// r#"ALTER TYPE "font" ADD VALUE 'weight'"# +// ) +// } +// #[test] +// fn alter_2() { +// assert_eq!( +// Type::alter() +// .name(Font::Table) +// .add_value(Alias::new("weight")) +// .before(Font::Variant) +// .to_string(OracleQueryBuilder), +// r#"ALTER TYPE "font" ADD VALUE 'weight' BEFORE 'variant'"# +// ) +// } + +// #[test] +// fn alter_3() { +// assert_eq!( +// Type::alter() +// .name(Font::Table) +// .add_value(Alias::new("weight")) +// .after(Font::Variant) +// .to_string(OracleQueryBuilder), +// r#"ALTER TYPE "font" ADD VALUE 'weight' AFTER 'variant'"# +// ) +// } + +// #[test] +// fn alter_4() { +// assert_eq!( +// Type::alter() +// .name(Font::Table) +// .rename_to(Alias::new("typeface")) +// .to_string(OracleQueryBuilder), +// r#"ALTER TYPE "font" RENAME TO 'typeface'"# +// ) +// } + +// #[test] +// fn alter_5() { +// assert_eq!( +// Type::alter() +// .name(Font::Table) +// .rename_value(Font::Variant, Font::Language) +// .to_string(OracleQueryBuilder), +// r#"ALTER TYPE "font" RENAME VALUE 'variant' TO 'language'"# +// ) +// }