#include "PgClass.h" #include "PgAttributeContainer.h" #include "PgClassContainer.h" #include "PgDatabaseCatalog.h" #include "PgConstraintContainer.h" #include "PgInheritsContainer.h" #include #include "SqlFormattingUtils.h" #include void operator<<(RelPersistence &s, const Pgsql::Value &v) { //s = static_cast(v); const char *c = v.c_str(); switch (*c) { case 'p': s = RelPersistence::Permanent; break; case 'u': s = RelPersistence::Unlogged; break; case 't': s = RelPersistence::Temporary; break; } } void operator<<(RelKind &s, const Pgsql::Value &v) { //s = static_cast(v); const char *c = v.c_str(); switch (*c) { case 'r': s = RelKind::Table; break; case 'i': s = RelKind::Index; break; case 'S': s = RelKind::Sequence; break; case 'v': s = RelKind::View; break; case 'm': s = RelKind::MaterializedView; break; case 'c': s = RelKind::Composite; break; case 't': s = RelKind::Toast; break; case 'f': s = RelKind::ForeignTable; break; case 'p': s = RelKind::PartitionedTable; break; case 'I': s = RelKind::PartitionedIndex; break; default: throw std::runtime_error("Unknown RelKind"); } } QString PgClass::createSql() const { if (createSqlCache.isEmpty()) { switch (kind) { case RelKind::Table: createSqlCache = createTableSql(); break; case RelKind::View: createSqlCache = createViewSql(); break; } } return createSqlCache; } QString PgClass::typeName() const { switch (kind) { case RelKind::Table: return "TABLE"; case RelKind::Index: return "INDEX"; case RelKind::Sequence: return "SEQUENCE"; case RelKind::View: return "VIEW"; case RelKind::MaterializedView: return "MATERIALIZED VIEW"; case RelKind::Composite: return "COMPOSITE"; case RelKind::Toast: return "TOAST"; case RelKind::ForeignTable: return "FOREIGN TABLE"; case RelKind::PartitionedTable: return "PARTITIONED TABLE"; case RelKind::PartitionedIndex: return "PARTITIONED INDEX"; } throw std::runtime_error("Unexpected value in PgClass::typeName()"); } QString PgClass::aclAllPattern() const { switch (kind) { case RelKind::Table: return "arwdDxt"; default: break; } return {}; } QString PgClass::createTableSql() const { QString sql; // CREATE [ TEMP | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [ sql += "CREATE "; if (persistence == RelPersistence::Unlogged) sql += "UNLOGGED "; else if (persistence == RelPersistence::Temporary) sql += "TEMP "; sql += "TABLE "; sql += fullyQualifiedQuotedObjectName(); if (!partitionBoundaries.isEmpty()) { sql += " PARTITION OF " + getPartitionOfName(); sql += generateBodySql(true); } else sql += generateBodySql(false); sql += generateInheritsSql() % generateTablespaceSql() % ";\n"; return sql; } QString PgClass::generateBodySql(bool isPartition) const { // - also remove commented inherited column list? They are listed in the column view no need for them in sql // - mark them in the view as inherited? // - detect when body empty and leave it out completely (only for partitions, is leaving it out always legal?) // - need to detect "inherited" constraint because these should not be listed either auto colsFilter = isPartition ? [] (const PgAttribute &c) { return c.num > 0 // ignore system columns && !c.isdropped // ignore dropped columns && c.islocal; } : [] (const PgAttribute &c) { return c.num > 0 // ignore system columns && !c.isdropped; // ignore dropped columns } ; auto && cols = catalog().attributes()->getColumnsForRelation(oid()) | std::views::filter(colsFilter); auto && constraints = catalog().constraints()->getConstraintsForRelation(oid()) | std::views::filter([] (const auto &c) { return c.islocal; }); if (cols.empty() && constraints.empty()) return {}; QString sql = " (\n "; bool first = true; for (auto && col : cols) { if (first) { first = false; } else sql += ",\n "; if (!col.islocal) sql += "-- "; sql += col.columnDefinition(catalog()); } for (auto && constraint: constraints) { if (first) { sql += "\n "; first = false; } else sql += ",\n "; sql += getConstraintDefinition(catalog(), constraint, " "); } sql += "\n)"; return sql; } QString PgClass::generateInheritsSql() const { if (!partitionBoundaries.isEmpty()) return "\n" + partitionBoundaries; QString sql; // [ INHERITS ( parent_table [, ... ] ) ] auto parents = catalog().inherits()->getParentsOf(oid()); if (!parents.empty()) { sql += "\nINHERITS ("; bool first = true; for (auto parent_oid : parents) { if (first) first = false; else sql += ", "; sql += catalog().classes()->getByKey(parent_oid)->fullyQualifiedQuotedObjectName(); } sql += ")"; } return sql; } QString PgClass::getPartitionOfName() const { auto parents = catalog().inherits()->getParentsOf(oid()); if (!parents.empty()) { return catalog().classes()->getByKey(parents.front())->fullyQualifiedQuotedObjectName(); } throw std::logic_error("Should only be called if there is a parent table"); } QString PgClass::generateTablespaceSql() const { if (tablespace != 0) { auto ns = getTablespaceDisplayString(catalog(), tablespace); return "\n TABLESPACE " % quoteIdent(ns); } return {}; } QString PgClass::createViewSql() const { QString sql; sql += "CREATE OR REPLACE VIEW " + fullyQualifiedQuotedObjectName(); // todo security_barrier // todo check_option sql += " AS \n"; sql += viewdef; // todo owner return sql; }