pgLab/pglablib/SqlFormattingUtils.cpp

312 lines
9 KiB
C++
Raw Normal View History

#include "SqlFormattingUtils.h"
#include <QStringBuilder>
#include <cassert>
#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 getForeignKeyConstraintReferences(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{
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 "REFERENCES "
% genFQTableName(catalog, fcls) % " ("
% getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ") MATCH "
% ForeignKeyMatchToString(constraint.fmatchtype)
% " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype)
% " ON DELETE " % ForeignKeyActionToString(constraint.fdeltype)
% deferrable % validated;
}
QString getForeignKeyConstraintReferencesShort(const PgDatabaseCatalog &catalog, const PgConstraint &constraint)
{
PgClass fcls = catalog.classes()->getByKey(constraint.frelid);
QString deferrable;
QString validated;
if (!constraint.validated)
validated += " NOT VALID";
if (constraint.deferrable) {
deferrable = QLatin1String(" DEFERRABLE") % (constraint.deferred ? " INITIALLY DEFERRED" : "");
}
QString on_update = constraint.fupdtype == ForeignKeyAction::NoAction ? QString() : " ON UPDATE " % ForeignKeyActionToString(constraint.fupdtype);
QString on_delete = constraint.fdeltype == ForeignKeyAction::NoAction ? QString() : " ON DELETE " % ForeignKeyActionToString(constraint.fdeltype);
QString match_type = constraint.fmatchtype == ForeignKeyMatch::Simple ? QString() : " MATCH " % ForeignKeyMatchToString(constraint.fmatchtype);
return genFQTableName(catalog, fcls) % " ("
% getColumnNameList(catalog, constraint.frelid, constraint.fkey) % ")"
% match_type
% on_update
% on_delete
% 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);
return index.definition + ";";
// 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
}
QString getDropIndexDefinition(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 = "DROP 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;
}
/*
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;
*/