From 60fb4ce28507f6c44a04da59687eb9b464397475 Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 18 Jan 2023 19:43:12 +0100 Subject: [PATCH] Improve support for declarative partitioning. Generated SQL for a partition is now correct (atleast for simple cases) Switched to C++ 20 so the ranges library can be used in this case to filter unwanted items. --- common.pri | 2 +- pglablib/catalog/PgClass.cpp | 122 ++++++++++++++++++++++---------- pglablib/catalog/PgClass.h | 2 + pglablib/catalog/PgConstraint.h | 4 ++ 4 files changed, 92 insertions(+), 38 deletions(-) diff --git a/common.pri b/common.pri index cf53398..dcff145 100644 --- a/common.pri +++ b/common.pri @@ -4,7 +4,7 @@ error( "Use local.pri.sample to create your own local.pri" ) LIBS += -lUser32 -lws2_32 -llibpq -CONFIG += c++latest +CONFIG += c++20 # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked as deprecated (the exact warnings diff --git a/pglablib/catalog/PgClass.cpp b/pglablib/catalog/PgClass.cpp index 90b1b71..ccb007c 100644 --- a/pglablib/catalog/PgClass.cpp +++ b/pglablib/catalog/PgClass.cpp @@ -6,7 +6,7 @@ #include "PgInheritsContainer.h" #include #include "SqlFormattingUtils.h" - +#include void operator<<(RelPersistence &s, const Pgsql::Value &v) @@ -125,52 +125,90 @@ QString PgClass::createTableSql() const sql += "TEMP "; sql += "TABLE "; sql += fullyQualifiedQuotedObjectName(); - sql += " (\n "; - auto && cols = catalog().attributes()->getColumnsForRelation(oid()); - bool first = true; - for (auto && col : cols) + if (!partitionBoundaries.isEmpty()) { - if (col.num > 0 && !col.isdropped) - { - if (first) - { - first = false; - } - else - sql += ",\n "; - if (!col.islocal) sql += "-- "; - sql += col.columnDefinition(catalog()); - } -// { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ] -// | table_constraint -// ] ) - } - auto && constraints = catalog().constraints()->getConstraintsForRelation(oid()); - for (auto && constraint: constraints) - { - if (first) - { - sql += "\n "; - first = false; - } - else - sql += ",\n "; - sql += getConstraintDefinition(catalog(), constraint, " "); - } + sql += " PARTITION OF " + getPartitionOfName(); + sql += generateBodySql(true); + } + else + sql += generateBodySql(false); - sql += "\n)" - % generateInheritsSql() - // [ PARTITION BY { RANGE | LIST } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ] - // [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ] - // [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] + 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()); @@ -189,6 +227,16 @@ QString PgClass::generateInheritsSql() const 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) diff --git a/pglablib/catalog/PgClass.h b/pglablib/catalog/PgClass.h index 524b87d..0591c37 100644 --- a/pglablib/catalog/PgClass.h +++ b/pglablib/catalog/PgClass.h @@ -62,7 +62,9 @@ private: mutable QString createSqlCache; QString createTableSql() const; + QString generateBodySql(bool isPartition) const; QString generateInheritsSql() const; + QString getPartitionOfName() const; QString generateTablespaceSql() const; QString createViewSql() const; }; diff --git a/pglablib/catalog/PgConstraint.h b/pglablib/catalog/PgConstraint.h index 8a16a6b..ae8feac 100644 --- a/pglablib/catalog/PgConstraint.h +++ b/pglablib/catalog/PgConstraint.h @@ -79,6 +79,10 @@ public: QString dropSql() const override; QString createSql() const override; + bool isInherited() const + { + return inhcount > 0; + } };