2018-01-06 21:22:22 +01:00
|
|
|
|
#include "SqlFormattingUtils.h"
|
|
|
|
|
|
#include <QStringBuilder>
|
|
|
|
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
#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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2018-02-05 21:42:54 +01:00
|
|
|
|
QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const SmallAttNumVec<5> &attnums)
|
2018-01-06 21:22:22 +01:00
|
|
|
|
{
|
|
|
|
|
|
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;
|
|
|
|
|
|
*/
|
|
|
|
|
|
|