From 97d4e2a1a4163cc1e8e22e6c4c1959d031370516 Mon Sep 17 00:00:00 2001 From: eelke Date: Sat, 6 Jan 2018 21:22:22 +0100 Subject: [PATCH] Created IndexModel for displaying the indexes on a table. Constraints can now show the SQL to drop and create them. The keyword list is now directly based of the official keyword list from postgresql. --- pglab/ConstraintModel.cpp | 17 ++- pglab/ConstraintModel.h | 3 +- pglab/IndexModel.cpp | 85 ++++++++++++++ pglab/IndexModel.h | 45 +++++++ pglab/SqlSyntaxHighlighter.cpp | 112 ++---------------- pglab/SqlSyntaxHighlighter.h | 4 +- pglab/TablesPage.cpp | 61 +++++++++- pglab/TablesPage.h | 5 + pglab/TablesPage.ui | 14 ++- pglab/TablesTableModel.cpp | 14 +-- pglab/TablesTableModel.h | 2 +- pglab/pglab.pro | 6 +- pglabAll.pro | 1 + pglablib/PgCatalogTypes.h | 10 ++ pglablib/PgConstraint.cpp | 40 +++++++ pglablib/PgConstraint.h | 18 ++- pglablib/PgDatabaseCatalog.cpp | 6 +- pglablib/PgKeywordList.cpp | 116 ++++++++++++++++++ pglablib/PgKeywordList.h | 51 ++++++++ pglablib/SqlFormattingUtils.cpp | 201 ++++++++++++++++++++++++++++++++ pglablib/SqlFormattingUtils.h | 17 +++ pglablib/pglablib.pro | 9 +- pgsql/Pgsql_Connection.cpp | 47 ++++++++ pgsql/Pgsql_Connection.h | 98 ++-------------- 24 files changed, 754 insertions(+), 228 deletions(-) create mode 100644 pglab/IndexModel.cpp create mode 100644 pglab/IndexModel.h create mode 100644 pglablib/PgCatalogTypes.h create mode 100644 pglablib/PgKeywordList.cpp create mode 100644 pglablib/PgKeywordList.h create mode 100644 pglablib/SqlFormattingUtils.cpp create mode 100644 pglablib/SqlFormattingUtils.h diff --git a/pglab/ConstraintModel.cpp b/pglab/ConstraintModel.cpp index 933a158..c11a019 100644 --- a/pglab/ConstraintModel.cpp +++ b/pglab/ConstraintModel.cpp @@ -69,9 +69,9 @@ QVariant ConstraintModel::headerData(int section, Qt::Orientation orientation, i case SupportingIndexCol: c = tr("Supporting index"); break; - case DefinitionCol: - c = tr("Definition"); - break; +// case DefinitionCol: +// c = tr("Definition"); +// break; } v = c; } @@ -158,11 +158,16 @@ QVariant ConstraintModel::getData(const QModelIndex &index) const case SupportingIndexCol: s = getIndexDisplayString(*m_catalog, t.indid); break; - case DefinitionCol: - s = t.definition; - break; +// case DefinitionCol: +// s = t.definition; +// break; } v = s; return v; } + +const PgConstraint& ConstraintModel::constraint(int row) +{ + return m_constraints[row]; +} diff --git a/pglab/ConstraintModel.h b/pglab/ConstraintModel.h index 10e6029..ae4fa16 100644 --- a/pglab/ConstraintModel.h +++ b/pglab/ConstraintModel.h @@ -18,7 +18,7 @@ public: NameCol, /// NsCol, /// SupportingIndexCol, - DefinitionCol, +// DefinitionCol, colCount }; explicit ConstraintModel(QObject *parent = nullptr); @@ -35,6 +35,7 @@ public: //QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + const PgConstraint& constraint(int row); protected: virtual Oid getType(int column) const override; diff --git a/pglab/IndexModel.cpp b/pglab/IndexModel.cpp new file mode 100644 index 0000000..4667c4a --- /dev/null +++ b/pglab/IndexModel.cpp @@ -0,0 +1,85 @@ +#include "IndexModel.h" +#include "PgDatabaseCatalog.h" +#include "PgIndexContainer.h" +#include "Pgsql_oids.h" +#include "ScopeGuard.h" + +void IndexModel::setData(std::shared_ptr cat, const PgClass &table) +{ + beginResetModel(); + SCOPE_EXIT { endResetModel(); }; + + m_catalog = cat; + m_table = table; + + m_indexes = cat->indexes()->getIndexesForTable(table.oid); +} + +int IndexModel::rowCount(const QModelIndex &parent) const +{ + return m_indexes.size(); +} + +int IndexModel::columnCount(const QModelIndex &parent) const +{ + return colCount; +} + +Oid IndexModel::getType(int column) const +{ + return Pgsql::varchar_oid; +} + +QVariant IndexModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant v; + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + QString c; + switch (section) { + case TypeCol: + c = tr("Type"); + break; + case NameCol: + c = tr("Name"); + break; + case ColumnsCol: + c = tr("On"); + break; + case ConditionCol: + c = tr("Condition"); + break; +// case DefinitionCol: +// c = tr("Definition"); +// break; + } + v = c; + } + } + return v; + +} + +QVariant IndexModel::getData(const QModelIndex &index) const +{ + QVariant v; + int rij = index.row(); + const auto &dat = m_indexes[rij]; + switch (index.column()) { + case TypeCol: + if (dat.isprimary) v = ":/icons/constraints/primarykey.png"; + else if (dat.isunique) v = ":/icons/constraints/unique.png"; + break; + + case NameCol: + v = getIndexDisplayString(*m_catalog, dat.indexrelid); + break; + + case ColumnsCol: + break; + + case ConditionCol: + break; + } + return v; +} diff --git a/pglab/IndexModel.h b/pglab/IndexModel.h new file mode 100644 index 0000000..d312413 --- /dev/null +++ b/pglab/IndexModel.h @@ -0,0 +1,45 @@ +#ifndef INDEXMODEL_H +#define INDEXMODEL_H + +#include "BaseTableModel.h" +#include "PgClass.h" +#include "PgIndex.h" +#include +#include + + +class PgDatabaseCatalog; + +class IndexModel: public BaseTableModel { + Q_OBJECT +public: + using BaseTableModel::BaseTableModel; + + enum e_Columns : int { + TypeCol, /// primary/unique/normal + NameCol, /// + ColumnsCol, /// + ConditionCol, + colCount }; + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void setData(std::shared_ptr cat, const PgClass &table); + + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + +protected: + virtual Oid getType(int column) const override; + virtual QVariant getData(const QModelIndex &index) const override; + +private: + std::shared_ptr m_catalog; + PgClass m_table; + + using t_Indexes = std::vector; + t_Indexes m_indexes; +}; + +#endif // INDEXMODEL_H diff --git a/pglab/SqlSyntaxHighlighter.cpp b/pglab/SqlSyntaxHighlighter.cpp index 6e3af2f..128b7a6 100644 --- a/pglab/SqlSyntaxHighlighter.cpp +++ b/pglab/SqlSyntaxHighlighter.cpp @@ -3,104 +3,6 @@ #include "PgTypeContainer.h" #include "SqlLexer.h" -namespace { - - - t_SymbolSet g_Keywords = { - "a", "abort", "abs", "absent", "absolute", "access", "according", "action", "ada", "add", - "admin", "after", "aggregate", "all", "allocate", "also", "alter", "analyse", "analyze", "and", - "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asc", "asensitive", - "assetion", "assignment", "asymmetric", "at", "atomic", "attribute", "attributes", "authorization", "avg", - "backward", "base64", "before", "begin", "begin_frame", "begin_partition", "bernoulli", "between", "bigint", "binary", - "bit", "bit_length", "blob", "blocked", "bom", "boolean", "both", "breadth", "buffer", "by", - "c", "cache", "call", "called", "cardinality", "cascade", "cascaded", "case", "cast", - "catalog", "catalog_name", "ceil", "ceiling", "chain", "char", "character", "characteristics", - "characters", "character_length", "character_set_catalog", "character_set_name", "character_set_schema", - "char_length", "check", "checkpoint", "class", "class_origin", "clob", "close", "cluster", - "coalesce", "cobol", "collate", "collation", "collation_catalog", "collation_name", "collation_schema", - "collect", "column", "columns", "column_name", "command_function", "command_function_code", - "comment", "comments", "commit", "committed", "concurrently", "condition", "condition_number", - "configuration", "conflict", "connect", "connection", "connection_name", "constraint", "constraints", - "constraint_catalog", "constraint_name", "constraint_schema", "constructor", "contains", "content", - "continue", "control", "conversion", "convert", "copy", "corr", "corresponding", "cost", "count", - "covar_pop", "covar_samp", "create", "cross", "csv", "cube", "cume_dist", "current", "current_catalog", - "current_date", "current_default_transform_group", "current_path", "current_role", "current_row", - "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type", - "current_user", "cursor", "cursor_name", "cycle", - "data", "database", "datalink", "date", "datetime_interval_code", "datetime_interval_precision", - "day", "db", "deallocate", "dec", "decimal", "declare", "default", "defaults", "deferrable", "deferred", - "defined", "definer", "degree", "delete", "delimiter", "delimiters", "dense_rank", "depends", "depth", - "deref", "derived", "desc", "describe", "descriptor", "deterministic", "diagnostics", "dictionary", - "disable", "discard", "disconnect", "dispatch", "distinct", "dlnewcopy", "dlpreviouscopy", "dlurlcomplete", - "dlurlcompleteonly", "dlurlcompletewrite", "dlurlpatch", "dlurlpathonly", "dlurlpathwrite", "dlurlscheme", - "dlurlserver", "dlvalue", "do", "document", "domain", "double", "drop", "dynamic", "dynamic_function", - "dynamic_function_code", - "each", "element", "else", "empty", "enable", "encodign", "encrypted", "end", "end-exec", "end_frame", - "end_partition", "enforced", "enum", "equals", "escape", "event", "every", "except", "exception", "exclude", - "excluding", "exclusive", "exec", "execute", "exists", "exp", "explain", "expression", "extenstion", - "external", "extract", "false", "family", "fetch", "file", "filter", "final", "first", "first_value", - "flag", "float", "floor", "following", "for", "force", "foreign", "fortran", "forward", "found", - "frame_row", "free", "freeze", "from", "fs", "full", "function", "functions", "fusion", - "g", "general", "generated", "get", "global", "go" "goto", "grant", "granted", "greatest", "group", - "grouping", "groups", "handler", "having", "header", "hex", "hierarchy", "hold", "hour", "id", "identity", - "if", "ignore", "ilike", "immediate", "immediatly", "immutable", "implementation", "implicit", "import", "in", - "including", "increment", "indent", "index", "indexes", "indicator", "inherit", "inherits", "initially", "inline", - "inner", "inout", "input", "insensitive", "insert", "instance", "instantiable", "instead", "int", "integer", - "integrity", "intersect", "intersection", "interval", "into", "invoker", "is", "isnull", "isolation", - "join", - "k", "key", "key_member", "key_type", - "label", "lag", "language", "large", "last", "last_value", "lateral", "lead", "leading", "leakproof", - "least", "left", "length", "level", "library", "like", "like_regex", "limit", "link", "listen", "ln", "load", "local", - "localtime", "localtimestamp", "location", "locator", "lock", "locked", "logged", "lower", - "m", "map", "mapping", "match", "matched", "materialized", "max", "maxvalue", "max_cardinality", "member", - "merge", "message_length", "message_octet_length", "message_text", "method", "min", "minute", "minvalue", - "mod", "mode", "modifies", "module", "month", "more", "move", "multiset", "mumps", - "name", "namespace", "national", "natural", "nchar", "nclob", "nesting", "new", "next", "nfc", "nfd", "nfkc", "nkfd", - "nil", "no", "none", "normalize", "normalize", "not", "nothing", "notify", "notnull", "nowait", "nth_value", "ntile", - "null", "nullable", "nullif", "nulls", "number", "numeric", - "object", "occurrences_regex", "octets", "octet_length", "of", "off", "offset", "oids", "old", "on", "only", "open", - "operator", "option", "options", "or", "order", "ordering", "ordinality", "others", "out", "outer", "output", "over", - "overlaps", "overlay", "overriding", "owned", "owner", - "p", "pad", "parallel", "parameter", "parameter_mode", "parameter_name", "parameter_specific_catalog", - "parameter_specific_name", "parameter_specific_schema", "parser", - "partial", "partition", "pascal", "passing", "passthrough", "password", "path", "percent", "percentile_cont", - "percentile_disc", "percent_rank", "period", "permission", "placing", "plans", "pli", "policy", "portion", - "position", "position_regex", "power", "precedes", "preceding", "precision", "prepare", "prepared", "preserve", - "primary", "prior", "privileges", "procedural", "procedure", "program", "public", - "quote", "range", "rank", "read", "reads", "real", "reassign", "recheck", "recovery", "recursive", "ref", - "references", "referencing", "refresh", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", - "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "relative", "release", "rename", "repeatable", - "replace", "replica", "requiring", "reset", "respect", "restart", "restore", "restrict", "result", "return", - "returned_cardinality", "returned_length", "returned_octet_length", "returned_sqlstate", "returning", "returns", - "revoke", "right", "role", "rollback", "rollup", "routine", "routine_catalog", "routine_name", "routine_schema", - "row", "rows", "row_count", "row_number", "rule", - "savepoint", "scale", "schema", "schema_name", "scope", "scope_catalog", "scope_name", "scope_schema", "scroll", - "search", "second", "section", "security", "select", "selective", "self", "sensitive", "sequence", "sequences", - "serializable", "server", "server_name", "session", "session_user", "set", "setof", "sets", "share", "show", - "similar", "simple", "size", "skip", "smallint", "snapshot", "some", "source", "space", "specific", "specifictype", - "specific_name", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "stable", - "standalone", "start", "state", "statement", "static", "statistics", "stddev_pop", "stddev_samp", "stdin", "stdout", - "storage", "strict", "strip", "structure", "style", "subclass_origin", "submultiset", "substring", "substring_regex", - "succeeds", "sum", "symmetric", "sysid", "system", "system_time", "system_user", - "t", "table", "tables", "tablesample", "tablespace", "table_name", "temp", "template", "temporary", "text", "then", - "ties", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "token", "top_level_count", "trailing", - "transaction", "transaction_committed", "transaction_rolled_back", "transaction_active", "transform", "transforms", - "translate", "translate_regex", "translation", "treat", "trigger", "trigger_catalog", "trigger_name", "trigger_schema", - "trim", "trim_array", "true", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "under", - "unencrypted", "union", "unique", "unknown", "unlink", "unlisten", "unlogged", "unnamed", "unnest", "until", "untyped", - "update", "upper", "uri", "usage", "user", "user_defined_type_catalog", "user_defined_type_code", - "user_defined_type_name", "user_defined_type_schema", "using", - "vacuum", "valid", "validate", "validator", "value", "values", "value_of", "varbinary", "varchar", "variadic", - "varying", "var_pop", "var_samp", "verbose", "version", "versioning", "view", "views", "volatile", - "when", "whenever", "where", "whitespace", "width_bucket", "window", "with", "within", "without", "work", "wrapper", - "write", "xml", "xmlagg", "xmlattributes", "xmlbinary", "xmlcast", "xmlcomment", "xmlconcat", "xmldeclaration", - "xmldocument", "xmlelement", "xmlexists", "xmlforest", "xmliterate", "xmlnamespaces", "xmlparse", "xmlpi", - "xmlquery", "xmlroot", "xmlschema", "xmlserialize", "xmltable", "xmltext", "xmlvalidate", "year", "yes", "zone" - }; - -//"bigint", - -} SqlSyntaxHighlighter::SqlSyntaxHighlighter(QTextDocument *parent) @@ -149,12 +51,14 @@ void SqlSyntaxHighlighter::highlightBlock(const QString &text) case BasicTokenType::End: // End of input case BasicTokenType::DollarQuote: break; - case BasicTokenType::Symbol: // can be many things, keyword, object name, operator, .. - if (g_Keywords.count(s.toLower()) > 0) { - setFormat(startpos, length, m_keywordFormat); - } - else if (m_typeNames.count(s.toLower()) > 0) { - setFormat(startpos, length, m_typeFormat); + case BasicTokenType::Symbol: { // can be many things, keyword, object name, operator, .. + auto kw = getPgsqlKeyword(s); + if (kw != nullptr) { + setFormat(startpos, length, m_keywordFormat); + } + else if (m_typeNames.count(s.toLower()) > 0) { + setFormat(startpos, length, m_typeFormat); + } } break; case BasicTokenType::OpenBlockComment: diff --git a/pglab/SqlSyntaxHighlighter.h b/pglab/SqlSyntaxHighlighter.h index cf21e4c..9f43e52 100644 --- a/pglab/SqlSyntaxHighlighter.h +++ b/pglab/SqlSyntaxHighlighter.h @@ -3,11 +3,9 @@ #include #include -#include +#include "PgKeywordList.h" #include "util.h" -using t_SymbolSet = std::unordered_set; - class PgTypeContainer; class SqlSyntaxHighlighter : public QSyntaxHighlighter diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index 77e1ca5..296c561 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -9,6 +9,11 @@ #include "ConstraintModel.h" #include "NamespaceFilterWidget.h" #include "IconColumnDelegate.h" +#include "IndexModel.h" +#include "SqlFormattingUtils.h" +#include "SqlSyntaxHighlighter.h" +#include +#include TablesPage::TablesPage(QWidget *parent) : QWidget(parent), @@ -27,16 +32,32 @@ TablesPage::TablesPage(QWidget *parent) : SetTableViewDefault(ui->constraintsTable); m_constraintModel = new ConstraintModel(this); auto delegate = new IconColumnDelegate(this); - ui->constraintsTable->setModel(m_constraintModel); ui->constraintsTable->setItemDelegateForColumn(0, delegate); + QFont font; + font.setFamily("Source Code Pro"); + font.setFixedPitch(true); + font.setPointSize(10); + ui->constraintSqlEdit->setFont(font); + + SetTableViewDefault(ui->indexesTable); + m_indexModel = new IndexModel(this); + ui->indexesTable->setModel(m_indexModel); + ui->indexesTable->setItemDelegateForColumn(0, delegate); + m_namespaceFilterWidget = new NamespaceFilterWidget(this); ui->verticalLayoutTableView->addWidget(m_namespaceFilterWidget); connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, - &TablesPage::on_tableListTable_currentRowChanged); + &TablesPage::on_tableListTable_currentRowChanged); + +// connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, +// &TablesPage::on_constraintsTable_currentRowChanged); + + connect(ui->constraintsTable->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &TablesPage::on_constraintsTable_selectionChanged); } @@ -51,6 +72,10 @@ void TablesPage::setCatalog(std::shared_ptr cat) m_tablesModel->setCatalog(cat); ui->tableListTable->resizeColumnsToContents(); m_namespaceFilterWidget->init(cat->namespaces()); + + auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document()); + highlighter->setTypes(*cat->types()); + } void TablesPage::on_tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) @@ -62,5 +87,37 @@ void TablesPage::on_tableListTable_currentRowChanged(const QModelIndex ¤t, m_constraintModel->setData(m_catalog, table); ui->constraintsTable->resizeColumnsToContents(); + ui->constraintsTable->selectionModel()->reset(); + + m_indexModel->setData(m_catalog, table); + ui->indexesTable->resizeColumnsToContents(); } } + +void TablesPage::on_constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.row() != previous.row()) { +// QString drop_definition = m_constraintModel->dropDefinition(current.row()); +// QString create_definition = m_constraintModel->createDefinition(current.row()); + const PgConstraint& constraint = m_constraintModel->constraint(current.row()); + QString drop = getDropConstraintDefinition(*m_catalog, constraint); + QString add = getConstraintDefinition(*m_catalog, constraint); + + ui->constraintSqlEdit->setPlainText(drop % QString::fromUtf16(u"\n") % add); + } +} + +void TablesPage::on_constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + const auto indexes = ui->constraintsTable->selectionModel()->selectedIndexes(); + boost::container::flat_set rijen; + for (const auto e : indexes) rijen.insert(e.row()); + QString drops; + QString creates; + for (auto rij : rijen) { + const PgConstraint constraint = m_constraintModel->constraint(rij); + drops += getDropConstraintDefinition(*m_catalog, constraint) % "\n"; + creates += getConstraintDefinition(*m_catalog, constraint) % "\n"; + } + ui->constraintSqlEdit->setPlainText(drops % "\n" % creates); +} diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index d999c8b..6d29497 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -3,6 +3,7 @@ #include #include +#include namespace Ui { class TablesPage; @@ -13,6 +14,7 @@ class ColumnTableModel; class ConstraintModel; class PgDatabaseCatalog; class NamespaceFilterWidget; +class IndexModel; class TablesPage : public QWidget { @@ -29,11 +31,14 @@ private: TablesTableModel* m_tablesModel = nullptr; ColumnTableModel* m_columnsModel = nullptr; ConstraintModel* m_constraintModel = nullptr; + IndexModel* m_indexModel = nullptr; NamespaceFilterWidget* m_namespaceFilterWidget; private slots: void on_tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); + void on_constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); + void on_constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); }; #endif // TABLESPAGE_H diff --git a/pglab/TablesPage.ui b/pglab/TablesPage.ui index 4253595..248fb56 100644 --- a/pglab/TablesPage.ui +++ b/pglab/TablesPage.ui @@ -38,7 +38,7 @@ - 2 + 1 @@ -56,7 +56,17 @@ - + + + Qt::Vertical + + + + QAbstractItemView::SelectRows + + + + diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index 8376f36..f15a10c 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -59,9 +59,9 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, case OptionsCol: v = tr("Options"); break; - case AclCol: - v = tr("ACL"); - break; +// case AclCol: +// v = tr("ACL"); +// break; } } } @@ -88,7 +88,7 @@ Oid TablesTableModel::getType(int column) const case NameCol: case NamespaceCol: case OptionsCol: - case AclCol: +// case AclCol: default: oid = Pgsql::VARCHAROID; } @@ -115,9 +115,9 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const case OptionsCol: v = t.options; break; - case AclCol: - v = t.acl; - break; +// case AclCol: +// v = t.acl; +// break; } return v; diff --git a/pglab/TablesTableModel.h b/pglab/TablesTableModel.h index 78ddda6..b0ddb41 100644 --- a/pglab/TablesTableModel.h +++ b/pglab/TablesTableModel.h @@ -18,7 +18,7 @@ public: OwnerCol, TablespaceCol, OptionsCol, - AclCol, + //AclCol, colCount }; TablesTableModel(QObject *parent); diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 3170aac..e1d3213 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -65,7 +65,8 @@ SOURCES += main.cpp\ NamespaceItemModel.cpp \ ApplicationWindow.cpp \ ConstraintModel.cpp \ - IconColumnDelegate.cpp + IconColumnDelegate.cpp \ + IndexModel.cpp HEADERS += \ QueryResultModel.h \ @@ -102,7 +103,8 @@ HEADERS += \ NamespaceItemModel.h \ ApplicationWindow.h \ ConstraintModel.h \ - IconColumnDelegate.h + IconColumnDelegate.h \ + IndexModel.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ diff --git a/pglabAll.pro b/pglabAll.pro index 4371034..7ccf6de 100644 --- a/pglabAll.pro +++ b/pglabAll.pro @@ -12,6 +12,7 @@ SUBDIRS += core \ pglab.depends = core ctk pgsql pglablib pgsql.depends = core pglablib.depends = core pgsql +tests.depends = core pgsql pglablib CONFIG(debug, debug|release) { diff --git a/pglablib/PgCatalogTypes.h b/pglablib/PgCatalogTypes.h new file mode 100644 index 0000000..13171ad --- /dev/null +++ b/pglablib/PgCatalogTypes.h @@ -0,0 +1,10 @@ +#ifndef PGCATALOGTYPES_H +#define PGCATALOGTYPES_H + +#include +#include + +using AttNumVec = std::vector; +using OidVec = std::vector; + +#endif // PGCATALOGTYPES_H diff --git a/pglablib/PgConstraint.cpp b/pglablib/PgConstraint.cpp index e212aa7..a675de3 100644 --- a/pglablib/PgConstraint.cpp +++ b/pglablib/PgConstraint.cpp @@ -105,6 +105,30 @@ void operator<<(ForeignKeyAction &s, const Pgsql::Value &v) } } +QString ForeignKeyActionToString(ForeignKeyAction fka) +{ + QString result; + switch (fka) { + case ForeignKeyAction::NoAction: + result = "NO ACTION"; + break; + case ForeignKeyAction::Restrict: + result = "RESTRICT"; + break; + case ForeignKeyAction::Cascade: + result = "CASCADE"; + break; + case ForeignKeyAction::SetNull: + result = "SET NULL"; + break; + case ForeignKeyAction::SetDefault: + result = "SET DEFAULT"; + break; + } + + return result; +} + void operator<<(ForeignKeyMatch &s, const Pgsql::Value &v) { const char *c = v.c_str(); @@ -121,6 +145,22 @@ void operator<<(ForeignKeyMatch &s, const Pgsql::Value &v) } } +QString ForeignKeyMatchToString(ForeignKeyMatch fkm) +{ + QString result; + switch (fkm) { + case ForeignKeyMatch::Full : + result = "FULL"; + break; + case ForeignKeyMatch::Partial: + result = "PARTIAL"; + break; + case ForeignKeyMatch::Simple: + result = "SIMPLE"; + break; + } + return result; +} PgConstraint::PgConstraint() diff --git a/pglablib/PgConstraint.h b/pglablib/PgConstraint.h index 603adac..9174736 100644 --- a/pglablib/PgConstraint.h +++ b/pglablib/PgConstraint.h @@ -2,6 +2,7 @@ #define PGCONSTRAINT_H #include "Pgsql_Value.h" +#include "PgCatalogTypes.h" #include #include @@ -33,6 +34,8 @@ enum class ForeignKeyAction { void operator<<(ForeignKeyAction &s, const Pgsql::Value &v); +QString ForeignKeyActionToString(ForeignKeyAction fka); + enum class ForeignKeyMatch { Full, // f Partial, // p @@ -41,6 +44,8 @@ enum class ForeignKeyMatch { void operator<<(ForeignKeyMatch &s, const Pgsql::Value &v); +QString ForeignKeyMatchToString(ForeignKeyMatch fkm); + class PgConstraint { public: Oid oid = InvalidOid; @@ -60,12 +65,12 @@ public: bool islocal; int32_t inhcount; bool noinherit; - std::vector key; // list of constraint columns attnum - std::vector fkey; // fkey list of referenced columns - std::vector pfeqop; - std::vector ppeqop; - std::vector ffeqop; - std::vector exclop; + AttNumVec key; // list of constraint columns attnum + AttNumVec fkey; // fkey list of referenced columns + OidVec pfeqop; + OidVec ppeqop; + OidVec ffeqop; + OidVec exclop; QString bin; QString src; @@ -78,4 +83,5 @@ public: bool operator<(const PgConstraint &rhs) const { return oid < rhs.oid; } }; + #endif // PGCONSTRAINT_H diff --git a/pglablib/PgDatabaseCatalog.cpp b/pglablib/PgDatabaseCatalog.cpp index 8f12e99..3eb01f4 100644 --- a/pglablib/PgDatabaseCatalog.cpp +++ b/pglablib/PgDatabaseCatalog.cpp @@ -13,6 +13,8 @@ #include "Pgsql_oids.h" #include +#include +#include using namespace Pgsql; @@ -164,8 +166,10 @@ void load(Pgsql::Connection &conn, IPgContainter &pg_cont) //QThread::msleep(400); std::string q = pg_cont.getLoadQuery(); Pgsql::Result result = conn.query(q.c_str()); - if (result && result.resultStatus() == PGRES_TUPLES_OK) + if (result && result.resultStatus() == PGRES_TUPLES_OK) { + boost::timer::auto_cpu_timer t; pg_cont.load(result); + } else { auto details = result.diagDetails(); diff --git a/pglablib/PgKeywordList.cpp b/pglablib/PgKeywordList.cpp new file mode 100644 index 0000000..8d38a68 --- /dev/null +++ b/pglablib/PgKeywordList.cpp @@ -0,0 +1,116 @@ +#include "PgKeywordList.h" +#include + +namespace { + + using KeywordHT = boost::container::flat_set; + + #define PG_KEYWORD(a,b,c) {a,c}, + const KeywordHT _ScanKeywords = { + #include + }; + +} + +const Keyword* getPgsqlKeyword(QString s) +{ + const Keyword *result = nullptr; + auto fr = _ScanKeywords.find(Keyword(s.toLower(), 0)); + if (fr != _ScanKeywords.end()) + result = &*fr; + return result; +} + + +//t_SymbolSet g_Keywords = { +// "a", "abort", "abs", "absent", "absolute", "access", "according", "action", "ada", "add", +// "admin", "after", "aggregate", "all", "allocate", "also", "alter", "analyse", "analyze", "and", +// "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asc", "asensitive", +// "assetion", "assignment", "asymmetric", "at", "atomic", "attribute", "attributes", "authorization", "avg", +// "backward", "base64", "before", "begin", "begin_frame", "begin_partition", "bernoulli", "between", "bigint", "binary", +// "bit", "bit_length", "blob", "blocked", "bom", "boolean", "both", "breadth", "buffer", "by", +// "c", "cache", "call", "called", "cardinality", "cascade", "cascaded", "case", "cast", +// "catalog", "catalog_name", "ceil", "ceiling", "chain", "char", "character", "characteristics", +// "characters", "character_length", "character_set_catalog", "character_set_name", "character_set_schema", +// "char_length", "check", "checkpoint", "class", "class_origin", "clob", "close", "cluster", +// "coalesce", "cobol", "collate", "collation", "collation_catalog", "collation_name", "collation_schema", +// "collect", "column", "columns", "column_name", "command_function", "command_function_code", +// "comment", "comments", "commit", "committed", "concurrently", "condition", "condition_number", +// "configuration", "conflict", "connect", "connection", "connection_name", "constraint", "constraints", +// "constraint_catalog", "constraint_name", "constraint_schema", "constructor", "contains", "content", +// "continue", "control", "conversion", "convert", "copy", "corr", "corresponding", "cost", "count", +// "covar_pop", "covar_samp", "create", "cross", "csv", "cube", "cume_dist", "current", "current_catalog", +// "current_date", "current_default_transform_group", "current_path", "current_role", "current_row", +// "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type", +// "current_user", "cursor", "cursor_name", "cycle", +// "data", "database", "datalink", "date", "datetime_interval_code", "datetime_interval_precision", +// "day", "db", "deallocate", "dec", "decimal", "declare", "default", "defaults", "deferrable", "deferred", +// "defined", "definer", "degree", "delete", "delimiter", "delimiters", "dense_rank", "depends", "depth", +// "deref", "derived", "desc", "describe", "descriptor", "deterministic", "diagnostics", "dictionary", +// "disable", "discard", "disconnect", "dispatch", "distinct", "dlnewcopy", "dlpreviouscopy", "dlurlcomplete", +// "dlurlcompleteonly", "dlurlcompletewrite", "dlurlpatch", "dlurlpathonly", "dlurlpathwrite", "dlurlscheme", +// "dlurlserver", "dlvalue", "do", "document", "domain", "double", "drop", "dynamic", "dynamic_function", +// "dynamic_function_code", +// "each", "element", "else", "empty", "enable", "encodign", "encrypted", "end", "end-exec", "end_frame", +// "end_partition", "enforced", "enum", "equals", "escape", "event", "every", "except", "exception", "exclude", +// "excluding", "exclusive", "exec", "execute", "exists", "exp", "explain", "expression", "extenstion", +// "external", "extract", "false", "family", "fetch", "file", "filter", "final", "first", "first_value", +// "flag", "float", "floor", "following", "for", "force", "foreign", "fortran", "forward", "found", +// "frame_row", "free", "freeze", "from", "fs", "full", "function", "functions", "fusion", +// "g", "general", "generated", "get", "global", "go" "goto", "grant", "granted", "greatest", "group", +// "grouping", "groups", "handler", "having", "header", "hex", "hierarchy", "hold", "hour", "id", "identity", +// "if", "ignore", "ilike", "immediate", "immediatly", "immutable", "implementation", "implicit", "import", "in", +// "including", "increment", "indent", "index", "indexes", "indicator", "inherit", "inherits", "initially", "inline", +// "inner", "inout", "input", "insensitive", "insert", "instance", "instantiable", "instead", "int", "integer", +// "integrity", "intersect", "intersection", "interval", "into", "invoker", "is", "isnull", "isolation", +// "join", +// "k", "key", "key_member", "key_type", +// "label", "lag", "language", "large", "last", "last_value", "lateral", "lead", "leading", "leakproof", +// "least", "left", "length", "level", "library", "like", "like_regex", "limit", "link", "listen", "ln", "load", "local", +// "localtime", "localtimestamp", "location", "locator", "lock", "locked", "logged", "lower", +// "m", "map", "mapping", "match", "matched", "materialized", "max", "maxvalue", "max_cardinality", "member", +// "merge", "message_length", "message_octet_length", "message_text", "method", "min", "minute", "minvalue", +// "mod", "mode", "modifies", "module", "month", "more", "move", "multiset", "mumps", +// "name", "namespace", "national", "natural", "nchar", "nclob", "nesting", "new", "next", "nfc", "nfd", "nfkc", "nkfd", +// "nil", "no", "none", "normalize", "normalize", "not", "nothing", "notify", "notnull", "nowait", "nth_value", "ntile", +// "null", "nullable", "nullif", "nulls", "number", "numeric", +// "object", "occurrences_regex", "octets", "octet_length", "of", "off", "offset", "oids", "old", "on", "only", "open", +// "operator", "option", "options", "or", "order", "ordering", "ordinality", "others", "out", "outer", "output", "over", +// "overlaps", "overlay", "overriding", "owned", "owner", +// "p", "pad", "parallel", "parameter", "parameter_mode", "parameter_name", "parameter_specific_catalog", +// "parameter_specific_name", "parameter_specific_schema", "parser", +// "partial", "partition", "pascal", "passing", "passthrough", "password", "path", "percent", "percentile_cont", +// "percentile_disc", "percent_rank", "period", "permission", "placing", "plans", "pli", "policy", "portion", +// "position", "position_regex", "power", "precedes", "preceding", "precision", "prepare", "prepared", "preserve", +// "primary", "prior", "privileges", "procedural", "procedure", "program", "public", +// "quote", "range", "rank", "read", "reads", "real", "reassign", "recheck", "recovery", "recursive", "ref", +// "references", "referencing", "refresh", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", +// "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "relative", "release", "rename", "repeatable", +// "replace", "replica", "requiring", "reset", "respect", "restart", "restore", "restrict", "result", "return", +// "returned_cardinality", "returned_length", "returned_octet_length", "returned_sqlstate", "returning", "returns", +// "revoke", "right", "role", "rollback", "rollup", "routine", "routine_catalog", "routine_name", "routine_schema", +// "row", "rows", "row_count", "row_number", "rule", +// "savepoint", "scale", "schema", "schema_name", "scope", "scope_catalog", "scope_name", "scope_schema", "scroll", +// "search", "second", "section", "security", "select", "selective", "self", "sensitive", "sequence", "sequences", +// "serializable", "server", "server_name", "session", "session_user", "set", "setof", "sets", "share", "show", +// "similar", "simple", "size", "skip", "smallint", "snapshot", "some", "source", "space", "specific", "specifictype", +// "specific_name", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "stable", +// "standalone", "start", "state", "statement", "static", "statistics", "stddev_pop", "stddev_samp", "stdin", "stdout", +// "storage", "strict", "strip", "structure", "style", "subclass_origin", "submultiset", "substring", "substring_regex", +// "succeeds", "sum", "symmetric", "sysid", "system", "system_time", "system_user", +// "t", "table", "tables", "tablesample", "tablespace", "table_name", "temp", "template", "temporary", "text", "then", +// "ties", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "token", "top_level_count", "trailing", +// "transaction", "transaction_committed", "transaction_rolled_back", "transaction_active", "transform", "transforms", +// "translate", "translate_regex", "translation", "treat", "trigger", "trigger_catalog", "trigger_name", "trigger_schema", +// "trim", "trim_array", "true", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "under", +// "unencrypted", "union", "unique", "unknown", "unlink", "unlisten", "unlogged", "unnamed", "unnest", "until", "untyped", +// "update", "upper", "uri", "usage", "user", "user_defined_type_catalog", "user_defined_type_code", +// "user_defined_type_name", "user_defined_type_schema", "using", +// "vacuum", "valid", "validate", "validator", "value", "values", "value_of", "varbinary", "varchar", "variadic", +// "varying", "var_pop", "var_samp", "verbose", "version", "versioning", "view", "views", "volatile", +// "when", "whenever", "where", "whitespace", "width_bucket", "window", "with", "within", "without", "work", "wrapper", +// "write", "xml", "xmlagg", "xmlattributes", "xmlbinary", "xmlcast", "xmlcomment", "xmlconcat", "xmldeclaration", +// "xmldocument", "xmlelement", "xmlexists", "xmlforest", "xmliterate", "xmlnamespaces", "xmlparse", "xmlpi", +// "xmlquery", "xmlroot", "xmlschema", "xmlserialize", "xmltable", "xmltext", "xmlvalidate", "year", "yes", "zone" +//}; + diff --git a/pglablib/PgKeywordList.h b/pglablib/PgKeywordList.h new file mode 100644 index 0000000..f3ecd42 --- /dev/null +++ b/pglablib/PgKeywordList.h @@ -0,0 +1,51 @@ +#ifndef PGKEYWORDLIST_H +#define PGKEYWORDLIST_H + +#include +#include +#include +#include "util.h" + +#define UNRESERVED_KEYWORD 0 +#define COL_NAME_KEYWORD 1 +#define TYPE_FUNC_NAME_KEYWORD 2 +#define RESERVED_KEYWORD 3 + + + +using t_SymbolSet = std::unordered_set; + + +class Keyword { +public: + Keyword(QString kw, int16_t cat) + : m_keyword(kw), m_category(cat) + {} + + const QString& getKeyword() const { return m_keyword; } + int16_t getCategory() const { return m_category; } + + //bool operator<(const QString &s) const { return m_keyword < s; } + bool operator<(const Keyword &kw) const { return m_keyword < kw.m_keyword; } +private: + QString m_keyword; + int16_t m_category; +}; + +namespace std { + + template <> + struct hash + { + std::size_t operator()(const Keyword& s) const + { + return qHash(s.getKeyword()); + } + }; + +} + +const Keyword* getPgsqlKeyword(QString s); + + +#endif // PGKEYWORDLIST_H diff --git a/pglablib/SqlFormattingUtils.cpp b/pglablib/SqlFormattingUtils.cpp new file mode 100644 index 0000000..96dc529 --- /dev/null +++ b/pglablib/SqlFormattingUtils.cpp @@ -0,0 +1,201 @@ +#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 AttNumVec &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; +*/ + diff --git a/pglablib/SqlFormattingUtils.h b/pglablib/SqlFormattingUtils.h new file mode 100644 index 0000000..a5d303e --- /dev/null +++ b/pglablib/SqlFormattingUtils.h @@ -0,0 +1,17 @@ +#ifndef SQLFORMATTINGUTILS_H +#define SQLFORMATTINGUTILS_H + +#include + + +class PgConstraint; +class PgDatabaseCatalog; + +bool identNeedsQuotes(QString ident); + +QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint); +QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint); + +QString getForeignKeyConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint); + +#endif // SQLFORMATTINGUTILS_H diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index ff12b07..394b68a 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -49,7 +49,9 @@ SOURCES += \ PgConstraintContainer.cpp \ ParamListJson.cpp \ ParamListModel.cpp \ - util.cpp + util.cpp \ + SqlFormattingUtils.cpp \ + PgKeywordList.cpp HEADERS += \ Pglablib.h \ @@ -75,7 +77,10 @@ HEADERS += \ PgConstraintContainer.h \ ParamListJson.h \ ParamListModel.h \ - util.h + util.h \ + SqlFormattingUtils.h \ + PgCatalogTypes.h \ + PgKeywordList.h unix { target.path = /usr/lib diff --git a/pgsql/Pgsql_Connection.cpp b/pgsql/Pgsql_Connection.cpp index 9555434..7911c84 100644 --- a/pgsql/Pgsql_Connection.cpp +++ b/pgsql/Pgsql_Connection.cpp @@ -1,6 +1,7 @@ #include "Pgsql_Connection.h" #include "Pgsql_declare.h" #include "Pgsql_Params.h" +#include #include @@ -199,6 +200,52 @@ void Connection::setNoticeReceiver(std::function callbac , reinterpret_cast(this)); } +std::string Connection::escapeLiteral(const std::string_view &literal) +{ + std::unique_ptr result( + PQescapeLiteral(conn, literal.data(), literal.length()), + [](char*p) { if (p) PQfreemem(p); }); + if (result) { + return std::string(result.get()); + } + throw std::runtime_error("escapeLiteral(string_view) failed"); +} + +QString Connection::escapeLiteral(const QString &literal) +{ + auto u8 = literal.toUtf8(); + std::unique_ptr result( + PQescapeLiteral(conn, u8.data(), u8.length()), + [](char*p) { if (p) PQfreemem(p); }); + if (result) { + return QString::fromUtf8(result.get()); + } + throw std::runtime_error("escapeLiteral(QString) failed"); +} + +std::string Connection::escapeIdentifier(const std::string_view &ident) +{ + std::unique_ptr result( + PQescapeIdentifier(conn, ident.data(), ident.length()), + [](char*p) { if (p) PQfreemem(p); }); + if (result) { + return std::string(result.get()); + } + throw std::runtime_error("escapeIdentifier failed"); +} + +QString Connection::escapeIdentifier(const QString &ident) +{ + auto u8 = ident.toUtf8(); + std::unique_ptr result( + PQescapeIdentifier(conn, u8.data(), u8.length()), + [](char*p) { if (p) PQfreemem(p); }); + if (result) { + return QString::fromUtf8(result.get()); + } + throw std::runtime_error("escapeIdentifier(QString) failed"); +} + void Connection::notifyReceiveFunc(void *arg, const PGresult *result) { Connection *c = reinterpret_cast(arg); diff --git a/pgsql/Pgsql_Connection.h b/pgsql/Pgsql_Connection.h index 74dbf1c..6207653 100644 --- a/pgsql/Pgsql_Connection.h +++ b/pgsql/Pgsql_Connection.h @@ -25,28 +25,9 @@ namespace Pgsql { */ -// class ConnectionParams { -// public: -// std::string host; -// std::string hostaddr; -// unsigned short port = 5432; -// std::string dbname; -// std::string user; -// std::string password; -// int connect_timeout = -1; ///< -1 omit (ie uses default) -// std::string application_name; - -// }; - - - class Result; class Params; - - - - /** \brief Wrapper for a cancel object from libpq. */ class Canceller { @@ -80,16 +61,14 @@ namespace Pgsql { Connection(Connection &&rhs); Connection& operator=(Connection &&rhs); -// void connect(const ConnectionParams ¶ms); bool connect(const char *params); bool connect(const QString ¶ms) { return connect(params.toUtf8().data()); } + bool connect(const char *const * keywords, const char* const * values, int expand_dbname); - bool connectStart(const char *params); - bool connectStart(const std::string ¶ms) { return connectStart(params.c_str()); @@ -99,6 +78,7 @@ namespace Pgsql { { return connectStart(params.toUtf8().data()); } + bool connectStart(const char * const *keywords, const char * const *values); @@ -135,6 +115,11 @@ namespace Pgsql { bool isBusy(); void setNoticeReceiver(std::function callback); + + std::string escapeLiteral(const std::string_view &literal); + QString escapeLiteral(const QString &literal); + std::string escapeIdentifier(const std::string_view &ident); + QString escapeIdentifier(const QString &ident); private: PGconn *conn = nullptr; std::function notifyReceiver; @@ -142,75 +127,6 @@ namespace Pgsql { static void notifyReceiveFunc(void *arg, const PGresult *result); }; - - - -// class Field { -// public: - -// std::string getName() const; - -// private: -// //Tuples tuples; -// int field; -// // - -// }; - -// class Tuples { -// public: -// int getRowCount() const; -// int getColCount() const; - -// Field getField(const std::string &fname); -// Field getField(const int idx); - -// class const_iterator { -// public: -// const_iterator& operator++(); -// }; - -// void rewind(); - - -// }; - -// class OkResult: public Result { -// public: -// /** If the result is a data result then returns an interface for processing this data. - -// The returned interface remains valid as long as this OkResult exists. -// */ -// Tuples* hasTuples(); -// }; - -// class ErrorResult: public Result { -// -// }; - -// class ITransaction { -// public: -// -// Canceller Query(std::string query, -// std::function on_success, -// std::function on_cancelled, -// std::function on_error); -// -// Canceller Query(std::string query, -// std::function on_success, -// std::function on_cancelled, -// std::function on_error); -// -// -// void Rollback( -// std::function on_success, -// std::function on_error); -// void Commit( -// std::function on_success, -// std::function on_cancelled, -// std::function on_error); -// -// }; }