use anyhow::Result;
use pgevolve_core::catalog::CatalogFilter;
use pgevolve_core::catalog::read_catalog;
use pgevolve_core::diff::diff;
use crate::cli::{DiffArgs, OutputFormat};
use crate::config::PgevolveConfig;
use crate::connection::{connect, resolve_db};
use crate::pg_querier::PgCatalogQuerier;
pub async fn run(args: DiffArgs, cfg: &PgevolveConfig, format: OutputFormat) -> Result<i32> {
let opts = resolve_db(cfg, &args.db, args.url.as_deref())?;
let source = pgevolve_core::parse::parse_directory(&cfg.project.schema_dir, &[])
.map_err(|e| anyhow::anyhow!("parse error: {e}"))?;
let client = connect(&opts).await?;
let querier = PgCatalogQuerier::new(client)?;
let filter = CatalogFilter::new(opts.managed_schemas.clone(), opts.ignore_objects.clone())?;
let (target, drift) = tokio::task::spawn_blocking(move || read_catalog(&querier, &filter))
.await
.map_err(|e| anyhow::anyhow!("join error: {e}"))??;
let changes = diff(&target, &source, &drift);
match format {
OutputFormat::Human => print_human(&changes),
OutputFormat::Json => print_json(&changes)?,
OutputFormat::Sql => print_sql(&changes),
}
if args.shadow_validate {
let shadow_cfg = cfg.shadow.as_ref().ok_or_else(|| {
anyhow::anyhow!("--shadow-validate requires a [shadow] section in pgevolve.toml")
})?;
let backend = crate::shadow::resolve(shadow_cfg)?;
let major = shadow_cfg
.postgres_version
.as_deref()
.and_then(|v| v.trim().parse::<u32>().ok())
.unwrap_or(17);
let report = crate::shadow::validate::cross_check(
backend.as_ref(),
&source,
major,
args.shadow_strict,
)
.await?;
let mismatch_count = report.canonical_mismatches.len()
+ report.extra_ast_edges.len()
+ report.missing_ast_edges.len();
if mismatch_count > 0 {
for m in &report.canonical_mismatches {
eprintln!(
" canonical mismatch {}: source={:?} catalog={:?}",
m.view_qname, m.source_canonical, m.catalog_canonical
);
}
for e in &report.extra_ast_edges {
eprintln!(" extra AST edge {}: {}", e.view_qname, e.dep_node);
}
for m in &report.missing_ast_edges {
eprintln!(
" missing AST edge {}: {}.{}",
m.view_qname, m.ref_schema, m.ref_name
);
}
if args.shadow_strict {
anyhow::bail!("shadow-validate --strict: {mismatch_count} mismatch(es)");
}
}
let n_edges = report.structural_edges_checked;
eprintln!(
"shadow-validate: ok ({n_edges} structural edge(s), {mismatch_count} canonical mismatch(es))"
);
}
Ok(0)
}
const fn scope_name(s: &pgevolve_core::ir::publication::PublicationScope) -> &'static str {
match s {
pgevolve_core::ir::publication::PublicationScope::AllTables => "AllTables",
pgevolve_core::ir::publication::PublicationScope::Selective { .. } => "Selective",
}
}
#[allow(clippy::too_many_lines)] fn print_human(changes: &pgevolve_core::diff::ChangeSet) {
if changes.is_empty() {
println!("No changes.");
return;
}
println!("{} change(s):", changes.len());
for e in changes.iter() {
let kind = std::mem::discriminant(&e.change);
let destructive = if e.destructiveness.requires_approval() {
" [destructive]"
} else {
""
};
println!(" - {kind:?}{destructive}");
match &e.change {
pgevolve_core::diff::change::Change::CreateSchema(s) => {
println!(" create schema {}", s.name);
}
pgevolve_core::diff::change::Change::DropSchema(n) => {
println!(" drop schema {n}");
}
pgevolve_core::diff::change::Change::CreateTable(t) => {
println!(" create table {}", t.qname);
}
pgevolve_core::diff::change::Change::DropTable { qname, .. } => {
println!(" drop table {qname}");
}
pgevolve_core::diff::change::Change::AlterTable { qname, ops } => {
println!(" alter table {} ({} op(s))", qname, ops.len());
}
pgevolve_core::diff::change::Change::CreateIndex(i) => {
println!(" create index {}", i.qname);
}
pgevolve_core::diff::change::Change::DropIndex(q) => {
println!(" drop index {q}");
}
pgevolve_core::diff::change::Change::ReplaceIndex { to, .. } => {
println!(" replace index {}", to.qname);
}
pgevolve_core::diff::change::Change::CreateSequence(s) => {
println!(" create sequence {}", s.qname);
}
pgevolve_core::diff::change::Change::DropSequence(q) => {
println!(" drop sequence {q}");
}
pgevolve_core::diff::change::Change::AlterSequence { qname, ops } => {
println!(" alter sequence {} ({} op(s))", qname, ops.len());
}
pgevolve_core::diff::change::Change::AlterSchema { name, .. } => {
println!(" alter schema {name}");
}
pgevolve_core::diff::change::Change::ValidateConstraint { table, constraint } => {
println!(" validate constraint {constraint} on {table}");
}
pgevolve_core::diff::change::Change::RecreateIndex { qname } => {
println!(" recreate invalid index {qname}");
}
pgevolve_core::diff::change::Change::View(vc) => {
use pgevolve_core::diff::change::ViewChange;
match vc {
ViewChange::Create(v) => println!(" create view {}", v.qname),
ViewChange::Drop(q) => println!(" drop view {q}"),
ViewChange::ReplaceBody {
source, strategy, ..
} => {
use pgevolve_core::diff::change::BodyReplaceStrategy;
let compat = match strategy {
BodyReplaceStrategy::InPlace => "compatible",
BodyReplaceStrategy::Recreate => "incompatible",
};
println!(" replace view body {} ({compat})", source.qname);
}
ViewChange::SetReloption { qname, .. } => {
println!(" set view reloption {qname}");
}
ViewChange::SetComment { qname, .. } => {
println!(" set view comment {qname}");
}
ViewChange::SetColumnComment { qname, column, .. } => {
println!(" set column comment {qname}.{column}");
}
}
}
pgevolve_core::diff::change::Change::AlterViewSetCheckOption { qname, new_value } => {
let label = match new_value {
Some(pgevolve_core::ir::view::CheckOption::Local) => "LOCAL",
Some(pgevolve_core::ir::view::CheckOption::Cascaded) => "CASCADED",
None => "none",
};
println!(" ~ ALTER VIEW {qname} SET CHECK OPTION ({label})");
}
pgevolve_core::diff::change::Change::Mv(mc) => {
use pgevolve_core::diff::change::MvChange;
match mc {
MvChange::Create(mv) => println!(" create materialized view {}", mv.qname),
MvChange::Drop(q) => println!(" drop materialized view {q}"),
MvChange::ReplaceBody { source, .. } => {
println!(" replace mv body {}", source.qname);
}
MvChange::SetComment { qname, .. } => {
println!(" set mv comment {qname}");
}
MvChange::SetColumnComment { qname, column, .. } => {
println!(" set mv column comment {qname}.{column}");
}
}
}
pgevolve_core::diff::change::Change::UserType(utc) => {
use pgevolve_core::diff::change::UserTypeChange;
match utc {
UserTypeChange::Create(t) => println!(" create type {}", t.qname),
UserTypeChange::Drop(q) => println!(" drop type {q}"),
UserTypeChange::EnumAddValue { qname, value, .. } => {
println!(" enum {qname}: add value {value}");
}
UserTypeChange::EnumRenameValue { qname, from, to } => {
println!(" enum {qname}: rename {from} -> {to}");
}
UserTypeChange::DomainAddCheck { qname, .. } => {
println!(" domain {qname}: add check");
}
UserTypeChange::DomainDropCheck { qname, name } => {
println!(" domain {qname}: drop check {name}");
}
UserTypeChange::DomainSetDefault { qname, .. } => {
println!(" domain {qname}: set default");
}
UserTypeChange::DomainSetNotNull { qname, not_null } => {
println!(" domain {qname}: set not null = {not_null}");
}
UserTypeChange::CompositeAddAttribute { qname, attribute } => {
println!(" composite {qname}: add attribute {}", attribute.name);
}
UserTypeChange::CompositeDropAttribute { qname, name } => {
println!(" composite {qname}: drop attribute {name}");
}
UserTypeChange::CompositeAlterAttributeType {
qname, attribute, ..
} => {
println!(" composite {qname}: alter attribute type {attribute}");
}
UserTypeChange::SetComment { qname, .. } => {
println!(" set type comment {qname}");
}
UserTypeChange::ReplaceWithCascade { source, .. } => {
println!(" replace type {} with cascade", source.qname);
}
}
}
pgevolve_core::diff::change::Change::Function(fc) => {
use pgevolve_core::diff::change::FunctionChange;
match fc {
FunctionChange::Create(f) => {
println!(" function {}: create", f.qname);
}
FunctionChange::Drop { qname, args } => {
let arg_sig = args
.types
.iter()
.map(pgevolve_core::ir::column_type::ColumnType::render_sql)
.collect::<Vec<_>>()
.join(", ");
println!(" function {qname}({arg_sig}): drop");
}
FunctionChange::CreateOrReplace(f) => {
println!(" function {}: create or replace", f.qname);
}
FunctionChange::ReplaceViaDropCreate { source, .. } => {
println!(" function {}: drop + recreate", source.qname);
}
FunctionChange::SetComment { qname, comment, .. } => {
if comment.is_some() {
println!(" function {qname}: set comment");
} else {
println!(" function {qname}: clear comment");
}
}
}
}
pgevolve_core::diff::change::Change::Procedure(pc) => {
use pgevolve_core::diff::change::ProcedureChange;
match pc {
ProcedureChange::Create(p) => {
println!(" procedure {}: create", p.qname);
}
ProcedureChange::Drop(q) => {
println!(" procedure {q}: drop");
}
ProcedureChange::CreateOrReplace(p) => {
println!(" procedure {}: create or replace", p.qname);
}
ProcedureChange::SetComment { qname, comment } => {
if comment.is_some() {
println!(" procedure {qname}: set comment");
} else {
println!(" procedure {qname}: clear comment");
}
}
}
}
pgevolve_core::diff::change::Change::Extension(ec) => {
use pgevolve_core::diff::change::ExtensionChange;
match ec {
ExtensionChange::Create(e) => {
println!(" create extension {}", e.name);
}
ExtensionChange::Drop(n) => {
println!(" drop extension {n}");
}
ExtensionChange::AlterUpdate { name, to_version } => {
println!(" alter extension {name} update to {to_version}");
}
ExtensionChange::ReplaceWithCascade(e) => {
println!(" replace extension {} with cascade", e.name);
}
ExtensionChange::CommentOn { name, comment } => {
if comment.is_some() {
println!(" extension {name}: set comment");
} else {
println!(" extension {name}: clear comment");
}
}
}
}
pgevolve_core::diff::change::Change::Trigger(tc) => {
use pgevolve_core::diff::change::TriggerChange;
match tc {
TriggerChange::Create(t) => {
println!(" create trigger {} on {}", t.qname, t.table);
}
TriggerChange::Drop { qname, table } => {
println!(" drop trigger {qname} on {table}");
}
TriggerChange::Replace(t) => {
println!(
" replace trigger {} on {} (drop + recreate)",
t.qname, t.table
);
}
TriggerChange::CommentOn {
qname,
table,
comment,
} => {
if comment.is_some() {
println!(" trigger {qname} on {table}: set comment");
} else {
println!(" trigger {qname} on {table}: clear comment");
}
}
}
}
pgevolve_core::diff::change::Change::Table(tc) => {
use pgevolve_core::diff::change::TableChange;
match tc {
TableChange::AttachPartition { parent, child, .. } => {
println!(" attach partition {child} to {parent}");
}
TableChange::DetachPartition { parent, child } => {
println!(" detach partition {child} from {parent}");
}
}
}
pgevolve_core::diff::change::Change::GrantObjectPrivilege { object, grant } => {
println!(
" grant {} on {} {} to {:?}",
grant.privilege.sql_keyword(),
object.sql_keyword(),
object.render_target(),
grant.grantee
);
}
pgevolve_core::diff::change::Change::RevokeObjectPrivilege { object, grant } => {
println!(
" revoke {} on {} {} from {:?}",
grant.privilege.sql_keyword(),
object.sql_keyword(),
object.render_target(),
grant.grantee
);
}
pgevolve_core::diff::change::Change::GrantColumnPrivilege { qname, grant } => {
println!(
" grant column privilege on {qname} to {:?}",
grant.grantee
);
}
pgevolve_core::diff::change::Change::RevokeColumnPrivilege { qname, grant } => {
println!(
" revoke column privilege on {qname} from {:?}",
grant.grantee
);
}
pgevolve_core::diff::change::Change::AlterObjectOwner(op) => {
let from = op
.from
.as_ref()
.map_or_else(|| "<unknown>".to_string(), ToString::to_string);
println!(
" alter owner of {} {} from {from} to {}",
op.object.sql_keyword(),
op.object.render_target(),
op.to
);
}
pgevolve_core::diff::change::Change::AlterDefaultPrivileges {
target_role,
schema,
object_type,
direction,
grant,
} => {
use pgevolve_core::diff::change::GrantDirection;
let action = match direction {
GrantDirection::Grant => "grant",
GrantDirection::Revoke => "revoke",
};
let in_schema = schema
.as_ref()
.map_or_else(String::new, |s| format!(" in schema {s}"));
println!(
" alter default privileges for role {target_role}{in_schema}: {action} {:?} {:?} to {:?}",
object_type, grant.privilege, grant.grantee
);
}
pgevolve_core::diff::change::Change::CreatePolicy { table, policy } => {
println!(" create policy {} on {table}", policy.name);
}
pgevolve_core::diff::change::Change::DropPolicy { table, name } => {
println!(" drop policy {name} on {table}");
}
pgevolve_core::diff::change::Change::AlterPolicy { table, policy } => {
println!(" alter policy {} on {table}", policy.name);
}
pgevolve_core::diff::change::Change::SetTableRowSecurity { qname, security } => {
use pgevolve_core::diff::change::RowSecurity;
let verb = match security {
RowSecurity::Enable => "enable",
RowSecurity::Disable => "disable",
};
println!(" {verb} row level security on {qname}");
}
pgevolve_core::diff::change::Change::SetTableForceRowSecurity { qname, force } => {
use pgevolve_core::diff::change::ForceRowSecurity;
let verb = match force {
ForceRowSecurity::Force => "force",
ForceRowSecurity::NoForce => "no force",
};
println!(" {verb} row level security on {qname}");
}
pgevolve_core::diff::change::Change::SetTableStorage { qname, .. } => {
println!(" ~ ALTER TABLE {qname} SET (...)");
}
pgevolve_core::diff::change::Change::SetIndexStorage { qname, .. } => {
println!(" ~ ALTER INDEX {qname} SET (...)");
}
pgevolve_core::diff::change::Change::SetMaterializedViewStorage { qname, .. } => {
println!(" ~ ALTER MATERIALIZED VIEW {qname} SET (...)");
}
pgevolve_core::diff::change::Change::UnsupportedDiff { reason } => {
println!(" unsupported diff: {reason}");
}
pgevolve_core::diff::change::Change::Publication(pc) => {
use pgevolve_core::diff::change::PublicationChange;
match pc {
PublicationChange::Create(p) => {
println!(" + CREATE PUBLICATION {}", p.name);
}
PublicationChange::Drop { name } => {
println!(" - DROP PUBLICATION {name}");
}
PublicationChange::Replace { from, to } => {
println!(
" ~ REPLACE PUBLICATION {} (mode {} -> {})",
from.name,
scope_name(&from.scope),
scope_name(&to.scope),
);
}
PublicationChange::AddTable { publication, table } => {
println!(
" ~ ALTER PUBLICATION {publication} ADD TABLE {}",
table.qname
);
}
PublicationChange::DropTable { publication, qname } => {
println!(" ~ ALTER PUBLICATION {publication} DROP TABLE {qname}");
}
PublicationChange::SetTable { publication, table } => {
println!(
" ~ ALTER PUBLICATION {publication} SET TABLE {}",
table.qname
);
}
PublicationChange::AddSchema {
publication,
schema,
} => {
println!(
" ~ ALTER PUBLICATION {publication} ADD TABLES IN SCHEMA {schema}"
);
}
PublicationChange::DropSchema {
publication,
schema,
} => {
println!(
" ~ ALTER PUBLICATION {publication} DROP TABLES IN SCHEMA {schema}"
);
}
PublicationChange::SetPublish { publication, .. } => {
println!(" ~ ALTER PUBLICATION {publication} SET (publish = ...)");
}
PublicationChange::SetViaRoot { publication, value } => {
println!(
" ~ ALTER PUBLICATION {publication} SET (publish_via_partition_root = {value})"
);
}
PublicationChange::CommentOn { name, .. } => {
println!(" ~ COMMENT ON PUBLICATION {name}");
}
}
}
pgevolve_core::diff::change::Change::Statistic(sc) => {
use pgevolve_core::diff::change::StatisticChange;
match sc {
StatisticChange::Create(s) => {
println!(" + CREATE STATISTICS {}", s.qname);
}
StatisticChange::Drop { qname } => {
println!(" - DROP STATISTICS {qname}");
}
StatisticChange::Replace { from, .. } => {
println!(" ~ REPLACE STATISTICS {} (DROP + CREATE)", from.qname);
}
StatisticChange::AlterSetTarget { qname, value } => {
println!(" ~ ALTER STATISTICS {qname} SET STATISTICS {value}");
}
StatisticChange::CommentOn { qname, comment } => {
if comment.is_some() {
println!(" ~ COMMENT ON STATISTICS {qname}");
} else {
println!(" ~ COMMENT ON STATISTICS {qname} IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::Collation(cc) => {
use pgevolve_core::diff::change::CollationChange;
match cc {
CollationChange::Create(c) => {
println!(" + CREATE COLLATION {}", c.qname);
}
CollationChange::Drop { qname } => {
println!(" - DROP COLLATION {qname}");
}
CollationChange::Rename { from, to } => {
println!(" ~ ALTER COLLATION {from} RENAME TO {to}");
}
CollationChange::Replace { from, .. } => {
println!(" ~ REPLACE COLLATION {} (DROP + CREATE)", from.qname);
}
CollationChange::CommentOn { qname, comment } => {
if comment.is_some() {
println!(" ~ COMMENT ON COLLATION {qname}");
} else {
println!(" ~ COMMENT ON COLLATION {qname} IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::Subscription(sc) => {
use pgevolve_core::diff::change::SubscriptionChange;
match sc {
SubscriptionChange::Create(s) => {
println!(" + CREATE SUBSCRIPTION {}", s.name);
}
SubscriptionChange::Drop { name } => {
println!(" - DROP SUBSCRIPTION {name}");
}
SubscriptionChange::AlterConnection { name, .. } => {
println!(" ~ ALTER SUBSCRIPTION {name} CONNECTION ...");
}
SubscriptionChange::AddPublication { name, publication } => {
println!(" ~ ALTER SUBSCRIPTION {name} ADD PUBLICATION {publication}");
}
SubscriptionChange::DropPublication { name, publication } => {
println!(
" ~ ALTER SUBSCRIPTION {name} DROP PUBLICATION {publication}"
);
}
SubscriptionChange::SetOptions { name, .. } => {
println!(" ~ ALTER SUBSCRIPTION {name} SET (...)");
}
SubscriptionChange::CommentOn { name, .. } => {
println!(" ~ COMMENT ON SUBSCRIPTION {name}");
}
}
}
pgevolve_core::diff::change::Change::EventTrigger(ec) => {
use pgevolve_core::diff::change::EventTriggerChange;
match ec {
EventTriggerChange::Create(et) => {
println!(" + CREATE EVENT TRIGGER {}", et.name);
}
EventTriggerChange::Replace { to, .. } => {
println!(" ~ REPLACE EVENT TRIGGER {} (DROP + CREATE)", to.name);
}
EventTriggerChange::Drop { name } => {
println!(" - DROP EVENT TRIGGER {name}");
}
EventTriggerChange::AlterEnable { name, enabled } => {
println!(
" ~ ALTER EVENT TRIGGER {name} {}",
enabled.alter_clause()
);
}
EventTriggerChange::AlterOwner { name, owner } => {
println!(" ~ ALTER EVENT TRIGGER {name} OWNER TO {owner}");
}
EventTriggerChange::CommentOn { name, comment } => {
if comment.is_some() {
println!(" ~ COMMENT ON EVENT TRIGGER {name}");
} else {
println!(" ~ COMMENT ON EVENT TRIGGER {name} IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::Aggregate(ac) => {
use pgevolve_core::diff::change::AggregateChange;
match ac {
AggregateChange::Create(agg) => {
println!(" + CREATE AGGREGATE {}", agg.qname);
}
AggregateChange::Replace { to, .. } => {
println!(" ~ REPLACE AGGREGATE {} (DROP + CREATE)", to.qname);
}
AggregateChange::Drop { qname, .. } => {
println!(" - DROP AGGREGATE {qname}");
}
AggregateChange::AlterOwner { qname, owner, .. } => {
println!(" ~ ALTER AGGREGATE {qname} OWNER TO {owner}");
}
AggregateChange::CommentOn { qname, comment, .. } => {
if comment.is_some() {
println!(" ~ COMMENT ON AGGREGATE {qname}");
} else {
println!(" ~ COMMENT ON AGGREGATE {qname} IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::Cast(cc) => {
use pgevolve_core::diff::change::CastChange;
match cc {
CastChange::Create(c) => {
println!(" + CREATE CAST ({} AS {})", c.source, c.target);
}
CastChange::Replace { to, .. } => {
println!(
" ~ REPLACE CAST ({} AS {}) (DROP + CREATE)",
to.source, to.target
);
}
CastChange::Drop { source, target } => {
println!(" - DROP CAST ({source} AS {target})");
}
CastChange::CommentOn {
source,
target,
comment,
} => {
if comment.is_some() {
println!(" ~ COMMENT ON CAST ({source} AS {target})");
} else {
println!(" ~ COMMENT ON CAST ({source} AS {target}) IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::TsDictionary(tsd) => {
use pgevolve_core::diff::change::TsDictionaryChange;
match tsd {
TsDictionaryChange::Create(d) => {
println!(" + CREATE TEXT SEARCH DICTIONARY {}", d.qname);
}
TsDictionaryChange::Replace { to, .. } => {
println!(
" ~ REPLACE TEXT SEARCH DICTIONARY {} (DROP + CREATE)",
to.qname
);
}
TsDictionaryChange::Drop { qname } => {
println!(" - DROP TEXT SEARCH DICTIONARY {qname}");
}
TsDictionaryChange::AlterOptions { qname, .. } => {
println!(" ~ ALTER TEXT SEARCH DICTIONARY {qname} (options)");
}
TsDictionaryChange::AlterOwner { qname, owner } => {
println!(" ~ ALTER TEXT SEARCH DICTIONARY {qname} OWNER TO {owner}");
}
TsDictionaryChange::CommentOn { qname, comment } => {
if comment.is_some() {
println!(" ~ COMMENT ON TEXT SEARCH DICTIONARY {qname}");
} else {
println!(" ~ COMMENT ON TEXT SEARCH DICTIONARY {qname} IS NULL");
}
}
}
}
pgevolve_core::diff::change::Change::TsConfiguration(tsc) => {
use pgevolve_core::diff::change::TsConfigurationChange;
match tsc {
TsConfigurationChange::Create(c) => {
println!(" + CREATE TEXT SEARCH CONFIGURATION {}", c.qname);
}
TsConfigurationChange::Replace { to, .. } => {
println!(
" ~ REPLACE TEXT SEARCH CONFIGURATION {} (DROP + CREATE)",
to.qname
);
}
TsConfigurationChange::Drop { qname } => {
println!(" - DROP TEXT SEARCH CONFIGURATION {qname}");
}
TsConfigurationChange::AddMapping {
qname, token_type, ..
} => {
println!(
" ~ ALTER TEXT SEARCH CONFIGURATION {qname} ADD MAPPING FOR {token_type}"
);
}
TsConfigurationChange::AlterMapping {
qname, token_type, ..
} => {
println!(
" ~ ALTER TEXT SEARCH CONFIGURATION {qname} ALTER MAPPING FOR {token_type}"
);
}
TsConfigurationChange::DropMapping { qname, token_type } => {
println!(
" ~ ALTER TEXT SEARCH CONFIGURATION {qname} DROP MAPPING FOR {token_type}"
);
}
TsConfigurationChange::AlterOwner { qname, owner } => {
println!(
" ~ ALTER TEXT SEARCH CONFIGURATION {qname} OWNER TO {owner}"
);
}
TsConfigurationChange::CommentOn { qname, comment } => {
if comment.is_some() {
println!(" ~ COMMENT ON TEXT SEARCH CONFIGURATION {qname}");
} else {
println!(
" ~ COMMENT ON TEXT SEARCH CONFIGURATION {qname} IS NULL"
);
}
}
}
}
}
}
}
fn print_json(changes: &pgevolve_core::diff::ChangeSet) -> Result<()> {
let s = serde_json::to_string_pretty(changes)?;
println!("{s}");
Ok(())
}
fn print_sql(changes: &pgevolve_core::diff::ChangeSet) {
if changes.is_empty() {
println!("-- no changes");
return;
}
println!("-- pgevolve diff --format=sql (no online rewrites)");
println!("-- run `pgevolve plan` for the real applyable form\n");
for e in changes.iter() {
println!("-- {} change", change_kind_name(&e.change));
match &e.change {
pgevolve_core::diff::change::Change::CreateSchema(s) => {
println!("{}", pgevolve_core::plan::rewrite::sql::create_schema(s));
}
pgevolve_core::diff::change::Change::DropSchema(n) => {
println!("{}", pgevolve_core::plan::rewrite::sql::drop_schema(n));
}
pgevolve_core::diff::change::Change::CreateTable(t) => {
println!("{}", pgevolve_core::plan::rewrite::sql::create_table(t));
}
pgevolve_core::diff::change::Change::DropTable { qname, .. } => {
println!("{}", pgevolve_core::plan::rewrite::sql::drop_table(qname));
}
pgevolve_core::diff::change::Change::CreateIndex(i) => {
println!(
"{}",
pgevolve_core::plan::rewrite::sql::create_index(i, false)
);
}
pgevolve_core::diff::change::Change::DropIndex(q) => {
println!(
"{}",
pgevolve_core::plan::rewrite::sql::drop_index(q, false)
);
}
pgevolve_core::diff::change::Change::CreateSequence(s) => {
println!("{}", pgevolve_core::plan::rewrite::sql::create_sequence(s));
}
pgevolve_core::diff::change::Change::DropSequence(q) => {
println!("{}", pgevolve_core::plan::rewrite::sql::drop_sequence(q));
}
other => println!("-- (alter/replace not rendered as standalone SQL): {other:?}"),
}
println!();
}
}
#[allow(clippy::too_many_lines)] const fn change_kind_name(c: &pgevolve_core::diff::change::Change) -> &'static str {
use pgevolve_core::diff::change::{
Change, CollationChange, MvChange, PublicationChange, StatisticChange, SubscriptionChange,
ViewChange,
};
match c {
Change::CreateSchema(_) => "CreateSchema",
Change::DropSchema(_) => "DropSchema",
Change::AlterSchema { .. } => "AlterSchema",
Change::CreateTable(_) => "CreateTable",
Change::DropTable { .. } => "DropTable",
Change::AlterTable { .. } => "AlterTable",
Change::CreateIndex(_) => "CreateIndex",
Change::DropIndex(_) => "DropIndex",
Change::ReplaceIndex { .. } => "ReplaceIndex",
Change::CreateSequence(_) => "CreateSequence",
Change::DropSequence(_) => "DropSequence",
Change::AlterSequence { .. } => "AlterSequence",
Change::ValidateConstraint { .. } => "ValidateConstraint",
Change::RecreateIndex { .. } => "RecreateIndex",
Change::View(ViewChange::Create(_)) => "CreateView",
Change::View(ViewChange::Drop(_)) => "DropView",
Change::View(ViewChange::ReplaceBody { .. }) => "ReplaceViewBody",
Change::View(ViewChange::SetReloption { .. }) => "SetViewReloption",
Change::View(ViewChange::SetComment { .. }) => "SetViewComment",
Change::View(ViewChange::SetColumnComment { .. }) => "SetViewColumnComment",
Change::AlterViewSetCheckOption { .. } => "AlterViewSetCheckOption",
Change::Mv(MvChange::Create(_)) => "CreateMv",
Change::Mv(MvChange::Drop(_)) => "DropMv",
Change::Mv(MvChange::ReplaceBody { .. }) => "ReplaceMvBody",
Change::Mv(MvChange::SetComment { .. }) => "SetMvComment",
Change::Mv(MvChange::SetColumnComment { .. }) => "SetMvColumnComment",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::Create(_)) => "CreateType",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::Drop(_)) => "DropType",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::EnumAddValue { .. }) => {
"EnumAddValue"
}
Change::UserType(pgevolve_core::diff::change::UserTypeChange::EnumRenameValue {
..
}) => "EnumRenameValue",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::DomainAddCheck {
..
}) => "DomainAddCheck",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::DomainDropCheck {
..
}) => "DomainDropCheck",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::DomainSetDefault {
..
}) => "DomainSetDefault",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::DomainSetNotNull {
..
}) => "DomainSetNotNull",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::CompositeAddAttribute {
..
}) => "CompositeAddAttribute",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::CompositeDropAttribute {
..
}) => "CompositeDropAttribute",
Change::UserType(
pgevolve_core::diff::change::UserTypeChange::CompositeAlterAttributeType { .. },
) => "CompositeAlterAttributeType",
Change::UserType(pgevolve_core::diff::change::UserTypeChange::SetComment { .. }) => {
"SetTypeComment"
}
Change::UserType(pgevolve_core::diff::change::UserTypeChange::ReplaceWithCascade {
..
}) => "ReplaceTypeWithCascade",
Change::Function(pgevolve_core::diff::change::FunctionChange::Create(_)) => {
"CreateFunction"
}
Change::Function(pgevolve_core::diff::change::FunctionChange::Drop { .. }) => {
"DropFunction"
}
Change::Function(pgevolve_core::diff::change::FunctionChange::CreateOrReplace(_)) => {
"CreateOrReplaceFunction"
}
Change::Function(pgevolve_core::diff::change::FunctionChange::ReplaceViaDropCreate {
..
}) => "ReplaceFunctionViaDropCreate",
Change::Function(pgevolve_core::diff::change::FunctionChange::SetComment { .. }) => {
"SetFunctionComment"
}
Change::Procedure(pgevolve_core::diff::change::ProcedureChange::Create(_)) => {
"CreateProcedure"
}
Change::Procedure(pgevolve_core::diff::change::ProcedureChange::Drop(_)) => "DropProcedure",
Change::Procedure(pgevolve_core::diff::change::ProcedureChange::CreateOrReplace(_)) => {
"CreateOrReplaceProcedure"
}
Change::Procedure(pgevolve_core::diff::change::ProcedureChange::SetComment { .. }) => {
"SetProcedureComment"
}
Change::Extension(pgevolve_core::diff::change::ExtensionChange::Create(_)) => {
"CreateExtension"
}
Change::Extension(pgevolve_core::diff::change::ExtensionChange::Drop(_)) => "DropExtension",
Change::Extension(pgevolve_core::diff::change::ExtensionChange::AlterUpdate { .. }) => {
"AlterExtensionUpdate"
}
Change::Extension(pgevolve_core::diff::change::ExtensionChange::ReplaceWithCascade(_)) => {
"ReplaceExtensionWithCascade"
}
Change::Extension(pgevolve_core::diff::change::ExtensionChange::CommentOn { .. }) => {
"CommentOnExtension"
}
Change::Trigger(pgevolve_core::diff::change::TriggerChange::Create(_)) => "CreateTrigger",
Change::Trigger(pgevolve_core::diff::change::TriggerChange::Drop { .. }) => "DropTrigger",
Change::Trigger(pgevolve_core::diff::change::TriggerChange::Replace(_)) => "ReplaceTrigger",
Change::Trigger(pgevolve_core::diff::change::TriggerChange::CommentOn { .. }) => {
"CommentOnTrigger"
}
Change::Table(pgevolve_core::diff::change::TableChange::AttachPartition { .. }) => {
"AttachPartition"
}
Change::Table(pgevolve_core::diff::change::TableChange::DetachPartition { .. }) => {
"DetachPartition"
}
Change::GrantObjectPrivilege { .. } => "GrantObjectPrivilege",
Change::RevokeObjectPrivilege { .. } => "RevokeObjectPrivilege",
Change::GrantColumnPrivilege { .. } => "GrantColumnPrivilege",
Change::RevokeColumnPrivilege { .. } => "RevokeColumnPrivilege",
Change::AlterObjectOwner(_) => "AlterObjectOwner",
Change::AlterDefaultPrivileges { .. } => "AlterDefaultPrivileges",
Change::CreatePolicy { .. } => "CreatePolicy",
Change::DropPolicy { .. } => "DropPolicy",
Change::AlterPolicy { .. } => "AlterPolicy",
Change::SetTableRowSecurity { .. } => "SetTableRowSecurity",
Change::SetTableForceRowSecurity { .. } => "SetTableForceRowSecurity",
Change::SetTableStorage { .. } => "SetTableStorage",
Change::SetIndexStorage { .. } => "SetIndexStorage",
Change::SetMaterializedViewStorage { .. } => "SetMaterializedViewStorage",
Change::UnsupportedDiff { .. } => "UnsupportedDiff",
Change::Publication(PublicationChange::Create(_)) => "create_publication",
Change::Publication(PublicationChange::Drop { .. }) => "drop_publication",
Change::Publication(PublicationChange::Replace { .. }) => "replace_publication",
Change::Publication(PublicationChange::AddTable { .. }) => "alter_publication_add_table",
Change::Publication(PublicationChange::DropTable { .. }) => "alter_publication_drop_table",
Change::Publication(PublicationChange::SetTable { .. }) => "alter_publication_set_table",
Change::Publication(PublicationChange::AddSchema { .. }) => "alter_publication_add_schema",
Change::Publication(PublicationChange::DropSchema { .. }) => {
"alter_publication_drop_schema"
}
Change::Publication(PublicationChange::SetPublish { .. }) => {
"alter_publication_set_publish"
}
Change::Publication(PublicationChange::SetViaRoot { .. }) => {
"alter_publication_set_via_root"
}
Change::Publication(PublicationChange::CommentOn { .. }) => "comment_on_publication",
Change::Statistic(StatisticChange::Create(_)) => "create_statistic",
Change::Statistic(StatisticChange::Drop { .. }) => "drop_statistic",
Change::Statistic(StatisticChange::Replace { .. }) => "replace_statistic",
Change::Statistic(StatisticChange::AlterSetTarget { .. }) => "alter_statistic_set_target",
Change::Statistic(StatisticChange::CommentOn { .. }) => "comment_on_statistic",
Change::Subscription(SubscriptionChange::Create(_)) => "create_subscription",
Change::Subscription(SubscriptionChange::Drop { .. }) => "drop_subscription",
Change::Subscription(SubscriptionChange::AlterConnection { .. }) => {
"alter_subscription_connection"
}
Change::Subscription(SubscriptionChange::AddPublication { .. }) => {
"alter_subscription_add_publication"
}
Change::Subscription(SubscriptionChange::DropPublication { .. }) => {
"alter_subscription_drop_publication"
}
Change::Subscription(SubscriptionChange::SetOptions { .. }) => {
"alter_subscription_set_options"
}
Change::Subscription(SubscriptionChange::CommentOn { .. }) => "comment_on_subscription",
Change::Collation(CollationChange::Create(_)) => "create_collation",
Change::Collation(CollationChange::Drop { .. }) => "drop_collation",
Change::Collation(CollationChange::Rename { .. }) => "rename_collation",
Change::Collation(CollationChange::Replace { .. }) => "replace_collation",
Change::Collation(CollationChange::CommentOn { .. }) => "comment_on_collation",
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::Create(_)) => {
"create_event_trigger"
}
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::Replace {
..
}) => "replace_event_trigger",
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::Drop { .. }) => {
"drop_event_trigger"
}
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::AlterEnable {
..
}) => "alter_event_trigger_enable",
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::AlterOwner {
..
}) => "alter_event_trigger_owner",
Change::EventTrigger(pgevolve_core::diff::change::EventTriggerChange::CommentOn {
..
}) => "comment_on_event_trigger",
Change::Aggregate(pgevolve_core::diff::change::AggregateChange::Create(_)) => {
"create_aggregate"
}
Change::Aggregate(pgevolve_core::diff::change::AggregateChange::Replace { .. }) => {
"replace_aggregate"
}
Change::Aggregate(pgevolve_core::diff::change::AggregateChange::Drop { .. }) => {
"drop_aggregate"
}
Change::Aggregate(pgevolve_core::diff::change::AggregateChange::AlterOwner { .. }) => {
"alter_aggregate_owner"
}
Change::Aggregate(pgevolve_core::diff::change::AggregateChange::CommentOn { .. }) => {
"comment_on_aggregate"
}
Change::Cast(pgevolve_core::diff::change::CastChange::Create(_)) => "create_cast",
Change::Cast(pgevolve_core::diff::change::CastChange::Replace { .. }) => "replace_cast",
Change::Cast(pgevolve_core::diff::change::CastChange::Drop { .. }) => "drop_cast",
Change::Cast(pgevolve_core::diff::change::CastChange::CommentOn { .. }) => {
"comment_on_cast"
}
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::Create(_)) => {
"create_ts_dictionary"
}
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::Replace {
..
}) => "replace_ts_dictionary",
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::Drop { .. }) => {
"drop_ts_dictionary"
}
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::AlterOptions {
..
}) => "alter_ts_dictionary_options",
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::AlterOwner {
..
}) => "alter_ts_dictionary_owner",
Change::TsDictionary(pgevolve_core::diff::change::TsDictionaryChange::CommentOn {
..
}) => "comment_on_ts_dictionary",
Change::TsConfiguration(pgevolve_core::diff::change::TsConfigurationChange::Create(_)) => {
"create_ts_configuration"
}
Change::TsConfiguration(pgevolve_core::diff::change::TsConfigurationChange::Replace {
..
}) => "replace_ts_configuration",
Change::TsConfiguration(pgevolve_core::diff::change::TsConfigurationChange::Drop {
..
}) => "drop_ts_configuration",
Change::TsConfiguration(
pgevolve_core::diff::change::TsConfigurationChange::AddMapping { .. },
) => "alter_ts_configuration_add_mapping",
Change::TsConfiguration(
pgevolve_core::diff::change::TsConfigurationChange::AlterMapping { .. },
) => "alter_ts_configuration_alter_mapping",
Change::TsConfiguration(
pgevolve_core::diff::change::TsConfigurationChange::DropMapping { .. },
) => "alter_ts_configuration_drop_mapping",
Change::TsConfiguration(
pgevolve_core::diff::change::TsConfigurationChange::AlterOwner { .. },
) => "alter_ts_configuration_owner",
Change::TsConfiguration(
pgevolve_core::diff::change::TsConfigurationChange::CommentOn { .. },
) => "comment_on_ts_configuration",
}
}