use super::*;
use crate::write_int;
impl TableBuilder for PostgresQueryBuilder {
fn prepare_column_def(&self, column_def: &ColumnDef, sql: &mut impl SqlWriter) {
fn f(this: &PostgresQueryBuilder, column_def: &ColumnDef, sql: &mut impl SqlWriter) {
this.prepare_column_type_check_auto_increment(column_def, sql);
}
self.prepare_column_def_common(column_def, sql, |column_def, sql| f(self, column_def, sql));
}
fn prepare_column_type(&self, column_type: &ColumnType, sql: &mut impl SqlWriter) {
match column_type {
ColumnType::Char(length) => match length {
Some(length) => {
sql.write_str("char(").unwrap();
write_int(sql, *length);
sql.write_char(')')
}
None => sql.write_str("char"),
},
ColumnType::String(length) => match length {
StringLen::N(length) => {
sql.write_str("varchar(").unwrap();
write_int(sql, *length);
sql.write_char(')')
}
_ => sql.write_str("varchar"),
},
ColumnType::Text => sql.write_str("text"),
ColumnType::TinyInteger | ColumnType::TinyUnsigned => sql.write_str("smallint"),
ColumnType::SmallInteger => sql.write_str("smallint"),
ColumnType::Integer | ColumnType::SmallUnsigned => sql.write_str("integer"),
ColumnType::BigInteger | ColumnType::Unsigned | ColumnType::BigUnsigned => {
sql.write_str("bigint")
}
ColumnType::Float => sql.write_str("real"),
ColumnType::Double => sql.write_str("double precision"),
ColumnType::Decimal(precision) => match precision {
Some((precision, scale)) => {
sql.write_str("decimal(").unwrap();
write_int(sql, *precision);
sql.write_str(", ").unwrap();
write_int(sql, *scale);
sql.write_char(')')
}
None => sql.write_str("decimal"),
},
ColumnType::DateTime => sql.write_str("timestamp without time zone"),
ColumnType::Timestamp => sql.write_str("timestamp"),
ColumnType::TimestampWithTimeZone => sql.write_str("timestamp with time zone"),
ColumnType::Time => sql.write_str("time"),
ColumnType::Date => sql.write_str("date"),
ColumnType::Interval(fields, precision) => {
sql.write_str("interval").unwrap();
if let Some(fields) = fields {
write!(sql, " {fields}").unwrap();
}
if let Some(precision) = precision {
sql.write_char('(').unwrap();
write_int(sql, *precision);
sql.write_char(')').unwrap();
}
Ok(())
}
ColumnType::Binary(_) | ColumnType::VarBinary(_) | ColumnType::Blob => {
sql.write_str("bytea")
}
ColumnType::Bit(length) => match length {
Some(length) => {
sql.write_str("bit(").unwrap();
write_int(sql, *length);
sql.write_char(')')
}
None => sql.write_str("bit"),
},
ColumnType::VarBit(length) => {
sql.write_str("varbit(").unwrap();
write_int(sql, *length);
sql.write_char(')')
}
ColumnType::Boolean => sql.write_str("bool"),
ColumnType::Money(precision) => match precision {
Some((precision, scale)) => {
sql.write_str("money(").unwrap();
write_int(sql, *precision);
sql.write_str(", ").unwrap();
write_int(sql, *scale);
sql.write_char(')')
}
None => sql.write_str("money"),
},
ColumnType::Json => sql.write_str("json"),
ColumnType::JsonBinary => sql.write_str("jsonb"),
ColumnType::Uuid => sql.write_str("uuid"),
ColumnType::Array(elem_type) => {
self.prepare_column_type(elem_type, sql);
sql.write_str("[]")
}
ColumnType::Vector(size) => match size {
Some(size) => {
sql.write_str("vector(").unwrap();
write_int(sql, *size);
sql.write_str(")")
}
None => sql.write_str("vector"),
},
ColumnType::Custom(iden) => sql.write_str(&iden.0),
ColumnType::Enum { name, .. } => sql.write_str(&name.0),
ColumnType::Cidr => sql.write_str("cidr"),
ColumnType::Inet => sql.write_str("inet"),
ColumnType::MacAddr => sql.write_str("macaddr"),
ColumnType::Year => unimplemented!("Year is not available in Postgres."),
ColumnType::LTree => sql.write_str("ltree"),
}
.unwrap()
}
fn column_spec_auto_increment_keyword(&self) -> &str {
""
}
fn prepare_table_alter_statement(&self, alter: &TableAlterStatement, sql: &mut impl SqlWriter) {
if alter.options.is_empty() {
panic!("No alter option found")
};
sql.write_str("ALTER TABLE ").unwrap();
if let Some(table) = &alter.table {
self.prepare_table_ref_table_stmt(table, sql);
sql.write_str(" ").unwrap();
}
let mut opts = alter.options.iter();
join_io!(
opts,
opt,
join {
sql.write_str(", ").unwrap();
},
do {
match opt {
TableAlterOption::AddColumn(AddColumnOption {
column,
if_not_exists,
}) => {
sql.write_str("ADD COLUMN ").unwrap();
if *if_not_exists {
sql.write_str("IF NOT EXISTS ").unwrap();
}
self.prepare_column_def_common(column, sql, |column_def, sql| {
if let Some(column_type) = &column_def.types {
write!(sql, " ").unwrap();
if column_def.spec.auto_increment {
self.prepare_column_auto_increment(column_type, sql);
} else {
self.prepare_column_type(column_type, sql);
}
}
});
}
TableAlterOption::ModifyColumn(column_def) => {
self.prepare_modify_column(sql, column_def);
}
TableAlterOption::RenameColumn(from_name, to_name) => {
sql.write_str("RENAME COLUMN ").unwrap();
self.prepare_iden(from_name, sql);
sql.write_str(" TO ").unwrap();
self.prepare_iden(to_name, sql);
}
TableAlterOption::DropColumn(DropColumnOption{
column_name,
if_exists
}) => {
sql.write_str("DROP COLUMN ").unwrap();
if *if_exists {
sql.write_str("IF EXISTS ").unwrap();
}
self.prepare_iden(column_name, sql);
}
TableAlterOption::DropForeignKey(name) => {
let mut foreign_key = TableForeignKey::new();
foreign_key.name(name.to_string());
let drop = ForeignKeyDropStatement {
foreign_key,
table: None,
};
self.prepare_foreign_key_drop_statement_internal(
&drop,
sql,
Mode::TableAlter,
);
}
TableAlterOption::AddForeignKey(foreign_key) => {
let create = ForeignKeyCreateStatement {
foreign_key: foreign_key.to_owned(),
};
self.prepare_foreign_key_create_statement_internal(
&create,
sql,
Mode::TableAlter,
);
}
TableAlterOption::DropConstraint(name) => {
sql.write_str("DROP CONSTRAINT ").unwrap();
self.prepare_iden(name, sql);
}
}
}
);
}
fn prepare_table_rename_statement(
&self,
rename: &TableRenameStatement,
sql: &mut impl SqlWriter,
) {
sql.write_str("ALTER TABLE ").unwrap();
if let Some(from_name) = &rename.from_name {
self.prepare_table_ref_table_stmt(from_name, sql);
}
sql.write_str(" RENAME TO ").unwrap();
if let Some(to_name) = &rename.to_name {
self.prepare_table_ref_table_stmt(to_name, sql);
}
}
fn prepare_partition_by(&self, partition_by: &PartitionBy, sql: &mut impl SqlWriter) {
match partition_by {
PartitionBy::Range(cols) => {
sql.write_str("RANGE (").unwrap();
self.prepare_partition_cols(cols, sql);
sql.write_char(')').unwrap();
}
PartitionBy::List(cols) => {
sql.write_str("LIST (").unwrap();
self.prepare_partition_cols(cols, sql);
sql.write_char(')').unwrap();
}
PartitionBy::Hash(cols) => {
sql.write_str("HASH (").unwrap();
self.prepare_partition_cols(cols, sql);
sql.write_char(')').unwrap();
}
PartitionBy::Key(_) => panic!("Postgres does not support PARTITION BY KEY"),
}
}
fn prepare_partition_values(
&self,
partition_values: &PartitionValues,
sql: &mut impl SqlWriter,
) {
sql.write_str("FOR VALUES ").unwrap();
match partition_values {
PartitionValues::In(values) => {
sql.write_str("IN (").unwrap();
self.prepare_partition_exprs(values, sql);
sql.write_char(')').unwrap();
}
PartitionValues::FromTo(from, to) => {
sql.write_str("FROM (").unwrap();
self.prepare_partition_exprs(from, sql);
sql.write_str(") TO (").unwrap();
self.prepare_partition_exprs(to, sql);
sql.write_char(')').unwrap();
}
PartitionValues::LessThan(_) => panic!("Postgres does not support VALUES LESS THAN"),
PartitionValues::With(modulus, remainder) => {
write!(sql, "WITH (MODULUS {modulus}, REMAINDER {remainder})").unwrap();
}
}
}
}
impl PostgresQueryBuilder {
fn prepare_partition_cols(&self, cols: &[DynIden], sql: &mut impl SqlWriter) {
let mut first = true;
for col in cols {
if !first {
sql.write_str(", ").unwrap();
}
self.prepare_iden(col, sql);
first = false;
}
}
fn prepare_partition_exprs(&self, exprs: &[Expr], sql: &mut impl SqlWriter) {
let mut first = true;
for expr in exprs {
if !first {
sql.write_str(", ").unwrap();
}
self.prepare_expr(expr, sql);
first = false;
}
}
}
impl PostgresQueryBuilder {
fn prepare_column_auto_increment(&self, column_type: &ColumnType, sql: &mut impl SqlWriter) {
let num_ty = if cfg!(feature = "option-postgres-use-serial") {
match column_type {
ColumnType::SmallInteger => "smallserial",
ColumnType::Integer => "serial",
ColumnType::BigInteger => "bigserial",
_ => unimplemented!("{:?} doesn't support auto increment", column_type),
}
} else {
match column_type {
ColumnType::SmallInteger => "smallint GENERATED BY DEFAULT AS IDENTITY",
ColumnType::Integer => "integer GENERATED BY DEFAULT AS IDENTITY",
ColumnType::BigInteger => "bigint GENERATED BY DEFAULT AS IDENTITY",
_ => unimplemented!("{:?} doesn't support auto increment", column_type),
}
};
sql.write_str(num_ty).unwrap();
}
fn prepare_column_type_check_auto_increment(
&self,
column_def: &ColumnDef,
sql: &mut impl SqlWriter,
) {
if let Some(column_type) = &column_def.types {
let is_auto_increment = column_def.spec.auto_increment;
sql.write_str(" ").unwrap();
if is_auto_increment {
self.prepare_column_auto_increment(column_type, sql);
} else {
self.prepare_column_type(column_type, sql);
}
}
}
fn prepare_column_def_common<F, W>(&self, column_def: &ColumnDef, sql: &mut W, f: F)
where
F: Fn(&ColumnDef, &mut W),
W: SqlWriter,
{
self.prepare_iden(&column_def.name, sql);
f(column_def, sql);
self.prepare_column_spec(&column_def.spec, sql);
}
fn prepare_modify_column(&self, sql: &mut impl SqlWriter, column_def: &ColumnDef) {
let mut is_first = true;
macro_rules! write_comma_if_not_first {
() => {
if !is_first {
write!(sql, ", ").unwrap();
} else {
is_first = false
}
};
}
if let Some(column_type) = &column_def.types {
write!(sql, "ALTER COLUMN ").unwrap();
self.prepare_iden(&column_def.name, sql);
write!(sql, " TYPE ").unwrap();
self.prepare_column_type(column_type, sql);
is_first = false;
}
if column_def.spec.auto_increment {
}
if let Some(nullable) = column_def.spec.nullable {
write_comma_if_not_first!();
write!(sql, "ALTER COLUMN ").unwrap();
self.prepare_iden(&column_def.name, sql);
if nullable {
write!(sql, " DROP NOT NULL").unwrap();
} else {
write!(sql, " SET NOT NULL").unwrap();
}
}
if let Some(default) = &column_def.spec.default {
write_comma_if_not_first!();
write!(sql, "ALTER COLUMN ").unwrap();
self.prepare_iden(&column_def.name, sql);
write!(sql, " SET DEFAULT ").unwrap();
QueryBuilder::prepare_expr(self, default, sql);
}
if column_def.spec.unique {
write_comma_if_not_first!();
write!(sql, "ADD UNIQUE (").unwrap();
self.prepare_iden(&column_def.name, sql);
write!(sql, ")").unwrap();
}
if column_def.spec.primary_key {
write_comma_if_not_first!();
write!(sql, "ADD PRIMARY KEY (").unwrap();
self.prepare_iden(&column_def.name, sql);
write!(sql, ")").unwrap();
}
if let Some(check) = &column_def.spec.check {
write_comma_if_not_first!();
self.prepare_check_constraint(check, sql);
}
if let Some(x) = &column_def.spec.generated {
let _ = x;
}
if let Some(x) = &column_def.spec.comment {
let _ = x;
}
if let Some(expr) = &column_def.spec.using {
write!(sql, " USING ").unwrap();
QueryBuilder::prepare_expr(self, expr, sql);
}
if let Some(extra) = &column_def.spec.extra {
write!(sql, "{extra}").unwrap()
}
let _ = is_first;
}
}