#include "SqlFormattingUtils.h" #include #include #include "PgKeywordList.h" #include "PgConstraint.h" #include "PgAttributeContainer.h" #include "PgClass.h" #include "PgClassContainer.h" #include "PgNamespace.h" #include "PgNamespaceContainer.h" #include "PgDatabaseCatalog.h" //inline QString u16(char16_t *utf16) bool identNeedsQuotes(QString ident) { if (ident[0].isDigit()) return true; for (auto c : ident) if (c < 'a' && c > 'z' && c != '_') return true; auto kw = getPgsqlKeyword(ident); if (kw == nullptr) return false; else if (kw->getCategory() == UNRESERVED_KEYWORD) return false; // if (forTypes && sk->category == COL_NAME_KEYWORD) // return false; return true; } QString quoteIdent(QString ident) { assert(ident.length() > 0); static const wchar_t dquote = L'"'; if (identNeedsQuotes(ident)) { QString out; out += dquote; out += ident.replace("\"", "\"\""); out += dquote; return out; } else return ident; } QString genSchemaPrefix(const PgNamespace &ns) { QString str; if (!ns.name.isEmpty()) { str = quoteIdent(ns.name) % QString::fromUtf16(u"."); } return str; } QString genFQTableName(const PgDatabaseCatalog &catalog, const PgClass &cls) { auto ns = catalog.namespaces()->getByKey(cls.relnamespace); return genSchemaPrefix(ns) % quoteIdent(cls.name); } QString genAlterTable(const PgDatabaseCatalog &catalog, const PgClass &cls) { return "ALTER TABLE " % genFQTableName(catalog, cls); } QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) { PgClass cls = catalog.classes()->getByKey(constraint.relid); return genAlterTable(catalog, cls) % " DROP CONSTRAINT " % quoteIdent(constraint.name) % ";"; } /// Helper class that builds comma seperated identifier list class IdentListString { public: void add(QString ident) { if (!m_str.isEmpty()) m_str += ","; m_str += quoteIdent(ident); } const QString& str() const { return m_str; } private: QString m_str; }; QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const SmallAttNumVec<5> &attnums) { IdentListString result; const auto ac = catalog.attributes(); for (auto an : attnums) { result.add(ac->getByKey({ relid, an }).name); } return result.str(); } QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) { //PgClass cls = catalog.classes()->getByKey(constraint.relid); PgClass fcls = catalog.classes()->getByKey(constraint.frelid); QString deferrable; QString validated; if (!constraint.validated) validated += " NOT VALID"; if (constraint.deferrable) { deferrable = QLatin1String(" DEFERRABLE INITIALLY ") % (constraint.deferred ? "DEFERRED" : "IMMEDIATE"); } return "\n FOREIGN KEY (" % getColumnNameList(catalog, constraint.relid, constraint.key) % ")\n REFERENCES " % genFQTableName(catalog, fcls) % " (" % getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")\n MATCH " % ForeignKeyMatchToString(constraint.fmatchtype) % " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype) % " ON DELETE " % ForeignKeyActionToString(constraint.fdeltype) % deferrable % validated % ";"; } QString getPrimaryKeyConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) { QString ddl = " PRIMARY KEY (" % getColumnNameList(catalog, constraint.relid, constraint.key) % ");"; return ddl; } QString getUniqueConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) { QString ddl = " UNIQUE (" % getColumnNameList(catalog, constraint.relid, constraint.key) % ");"; return ddl; } QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint) { PgClass cls = catalog.classes()->getByKey(constraint.relid); // return genAlterTable(catalog, cls) % " ADD CONSTRAINT " // % quoteIdent(constraint.name) % " " % constraint.definition % ";"; QString result = genAlterTable(catalog, cls) % "\n ADD CONSTRAINT " % quoteIdent(constraint.name); switch (constraint.type) { case ConstraintType::ForeignKey: result += getForeignKeyConstraintDefinition(catalog, constraint); break; case ConstraintType::PrimaryKey: result += getPrimaryKeyConstraintDefinition(catalog, constraint); break; case ConstraintType::Unique: result += getUniqueConstraintDefinition(catalog, constraint); break; case ConstraintType::Check: default: result = result % " " % constraint.definition; break; } return result; } /* wxString sql; sql = wxT("(") + GetQuotedFkColumns() + wxT(")\n REFERENCES ") + GetQuotedSchemaPrefix(GetRefSchema()) + qtIdent(GetReferences()) + wxT(" (") + GetQuotedRefColumns() + wxT(")"); if (GetDatabase()->BackendMinimumVersion(7, 4) || GetMatch() == wxT("FULL")) sql += wxT(" MATCH ") + GetMatch(); sql += wxT("\n ON UPDATE ") + GetOnUpdate() + wxT(" ON DELETE ") + GetOnDelete(); if (GetDeferrable()) { sql += wxT(" DEFERRABLE INITIALLY "); if (GetDeferred()) sql += wxT("DEFERRED"); else sql += wxT("IMMEDIATE"); } if (GetDatabase()->BackendMinimumVersion(9, 1) && !GetValid()) sql += wxT("\n NOT VALID"); return sql; */