#include "SqlFormattingUtils.h" #include #include #include "PgKeywordList.h" #include "PgConstraint.h" #include "PgAttributeContainer.h" #include "PgClass.h" #include "PgClassContainer.h" #include "PgIndex.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; } QString getIndexDefinition(const PgDatabaseCatalog &catalog, const PgIndex &index) { PgClass table_class = catalog.classes()->getByKey(index.relid); PgClass index_class = catalog.classes()->getByKey(index.indexrelid); QString result; result = "CREATE "; if (index.isunique) result += "UNIQUE "; result += "INDEX " // % quoteIdent(getIndexDisplayString(catalog, index.indexrelid)) % quoteIdent(index_class.name) % "\n ON " % genFQTableName(catalog, table_class); // % "\n USING " % index_class.am lookup in pg_am table return result; #if 0 + wxT("\n USING ") + GetIndexType() + wxT("\n ("); if (GetProcName().IsNull()) str += GetQuotedColumns(); else { str += GetQuotedSchemaPrefix(GetProcNamespace()) + qtIdent(GetProcName()) + wxT("(") + GetQuotedColumns() + wxT(")"); if (!this->GetOperatorClasses().IsNull()) str += wxT(" ") + GetOperatorClasses(); } str += wxT(")"); if (GetConnection()->BackendMinimumVersion(8, 2) && GetFillFactor().Length() > 0) str += wxT("\n WITH (FILLFACTOR=") + GetFillFactor() + wxT(")"); if (GetConnection()->BackendMinimumVersion(8, 0) && tablespace != GetDatabase()->GetDefaultTablespace()) str += wxT("\nTABLESPACE ") + qtIdent(tablespace); AppendIfFilled(str, wxT("\n WHERE "), GetConstraint()); str += wxT(";\n"); if (GetConnection()->BackendMinimumVersion(7, 5)) if (GetIsClustered()) str += wxT("ALTER TABLE ") + GetQuotedSchemaPrefix(GetIdxSchema()) + qtIdent(GetIdxTable()) + wxT(" CLUSTER ON ") + qtIdent(GetName()) + wxT(";\n"); #endif } /* 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; */