From da45929b1208cb70384b73acebcb91fce5f6b071 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 9 Sep 2018 16:20:30 +0200 Subject: [PATCH 01/11] Fix SqlLexer test --- tests/pglabtests/tst_SqlLexer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pglabtests/tst_SqlLexer.cpp b/tests/pglabtests/tst_SqlLexer.cpp index 421933a..bf7149a 100644 --- a/tests/pglabtests/tst_SqlLexer.cpp +++ b/tests/pglabtests/tst_SqlLexer.cpp @@ -53,7 +53,7 @@ TEST(SqlLexer, lexer_comma_handling) lexer.nextBasicToken(startpos, length, tokentype, out); ASSERT_THAT(startpos, Eq(3)); ASSERT_THAT(length, Eq(1)); - ASSERT_THAT(tokentype, Eq(BasicTokenType::Self)); + ASSERT_THAT(tokentype, Eq(BasicTokenType::Comma)); ASSERT_THAT(out, Eq(QString(","))); lexer.nextBasicToken(startpos, length, tokentype, out); -- 2.47.2 From 8f4845d4d20dbafcaba48e684244aee19f9322e2 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 9 Sep 2018 18:52:32 +0200 Subject: [PATCH 02/11] Lot of code for generating code. Working on unit tests. --- pglab/BaseTableModel.cpp | 5 +- pglab/CodeBuilderConfiguration.cpp | 8 - pglab/CodeBuilderConfiguration.h | 120 ----------- pglab/ColumnTableModel.cpp | 2 +- pglab/ConstraintModel.cpp | 6 +- pglab/DatabasesTableModel.cpp | 6 +- pglab/IconColumnDelegate.cpp | 4 +- pglab/RolesTableModel.cpp | 8 +- pglab/TablesTableModel.cpp | 2 +- pglab/pglab.pro | 5 +- pglablib/PgContainer.h | 9 + pglablib/codebuilder/CodeBuilder.cpp | 119 +++++++++++ pglablib/codebuilder/CodeBuilder.h | 28 +++ pglablib/codebuilder/DefaultConfigs.cpp | 50 +++++ pglablib/codebuilder/DefaultConfigs.h | 11 + pglablib/codebuilder/IndentationConfig.cpp | 27 +++ pglablib/codebuilder/IndentationConfig.h | 23 ++ pglablib/codebuilder/LanguageConfig.cpp | 15 ++ pglablib/codebuilder/LanguageConfig.h | 54 +++++ pglablib/codebuilder/NameManglingRules.cpp | 32 +++ pglablib/codebuilder/NameManglingRules.h | 50 +++++ pglablib/codebuilder/StructureTemplate.cpp | 2 + pglablib/codebuilder/StructureTemplate.h | 16 ++ pglablib/codebuilder/TypeMappings.cpp | 133 ++++++++++++ pglablib/codebuilder/TypeMappings.h | 46 ++++ pglablib/pglablib.pro | 18 +- pgsql/Pgsql_Params.h | 2 +- pgsql/Pgsql_Value.cpp | 6 +- pgsql/Pgsql_declare.h | 178 ++++++++-------- pgsql/Pgsql_oids.cpp | 9 +- pgsql/Pgsql_oids.h | 16 +- tests/PgsqlTests/tst_Value.cpp | 30 +-- tests/pglabtests/pglabtests.pro | 24 ++- tests/pglabtests/tst_CodeBuilder.cpp | 231 +++++++++++++++++++++ tests/pglabtests/tst_CsvWriter.cpp | 1 + tests/pglabtests/tst_ExplainJsonParser.cpp | 1 + tests/pglabtests/tst_NameManglingRules.cpp | 54 +++++ tests/pglabtests/tst_ParamJson.cpp | 1 + tests/pglabtests/tst_PasswordManager.cpp | 1 + tests/pglabtests/tst_SqlLexer.cpp | 1 + tests/pglabtests/tst_expected.cpp | 1 + tests/pglabtests/tst_scopeguard.cpp | 1 + 42 files changed, 1089 insertions(+), 267 deletions(-) delete mode 100644 pglab/CodeBuilderConfiguration.cpp delete mode 100644 pglab/CodeBuilderConfiguration.h create mode 100644 pglablib/codebuilder/CodeBuilder.cpp create mode 100644 pglablib/codebuilder/CodeBuilder.h create mode 100644 pglablib/codebuilder/DefaultConfigs.cpp create mode 100644 pglablib/codebuilder/DefaultConfigs.h create mode 100644 pglablib/codebuilder/IndentationConfig.cpp create mode 100644 pglablib/codebuilder/IndentationConfig.h create mode 100644 pglablib/codebuilder/LanguageConfig.cpp create mode 100644 pglablib/codebuilder/LanguageConfig.h create mode 100644 pglablib/codebuilder/NameManglingRules.cpp create mode 100644 pglablib/codebuilder/NameManglingRules.h create mode 100644 pglablib/codebuilder/StructureTemplate.cpp create mode 100644 pglablib/codebuilder/StructureTemplate.h create mode 100644 pglablib/codebuilder/TypeMappings.cpp create mode 100644 pglablib/codebuilder/TypeMappings.h create mode 100644 tests/pglabtests/tst_CodeBuilder.cpp create mode 100644 tests/pglabtests/tst_NameManglingRules.cpp diff --git a/pglab/BaseTableModel.cpp b/pglab/BaseTableModel.cpp index 0869d0f..023cd30 100644 --- a/pglab/BaseTableModel.cpp +++ b/pglab/BaseTableModel.cpp @@ -1,6 +1,7 @@ #include "BaseTableModel.h" #include "ResultTableModelUtil.h" #include +#include "Pgsql_oids.h" using namespace Pgsql; @@ -10,7 +11,7 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const Oid oid = getType(index.column()); if (role == Qt::DisplayRole) { v = getData(index); - if (oid == BOOLOID) { + if (oid == bool_oid) { v = FormatBoolForDisplay(v.toBool()); } } @@ -18,7 +19,7 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const v = (int)GetDefaultAlignmentForType(oid); } else if (role == Qt::ForegroundRole) { - if (oid == BOOLOID) { + if (oid == bool_oid) { QVariant d = getData(index); if (d.type() == QVariant::Bool) { v = QBrush(GetDefaultBoolColor(d.toBool())); diff --git a/pglab/CodeBuilderConfiguration.cpp b/pglab/CodeBuilderConfiguration.cpp deleted file mode 100644 index 61b4a2f..0000000 --- a/pglab/CodeBuilderConfiguration.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "CodeBuilderConfiguration.h" -#include "Pgsql_Result.h" - - -QString CodeBuilder::GenClassDefinition(const Pgsql::Result &/*result*/) const -{ - return QString(); -} diff --git a/pglab/CodeBuilderConfiguration.h b/pglab/CodeBuilderConfiguration.h deleted file mode 100644 index f247769..0000000 --- a/pglab/CodeBuilderConfiguration.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Pgsql { class Result; } - -/* -class PgAuthId { -public: - PgAuthId(); - - Oid oid = InvalidOid; - QString name; - bool super; - bool inherit; - bool createRole; - bool createDB; - bool canlogin; - bool replication; - bool bypassRls; - int connLimit; - QDateTime validUntil;*/ - -/** Defines how a database result fieldname should be converted into a variable - * name in the target language. - * - */ -class VarNameManglingRules { -public: - enum class CollisionHandling { - Restrict, ///< An error will be reported and no code generated - Fqn, ///< Turn into fully qualified name (table_column) - Number ///< A number will be appended to fields that have the same name - }; - - enum class CaseConversion { - AsIs, - Upper, - Lower - }; - - QString replaceSpaceWith; ///< default is empty string which means remove spaces - CollisionHandling CollisionHandling = CollisionHandling::Restrict; - CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule - CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char - bool underscoreToCamel = false; ///< remove underscores and make first char after underscore uppercase -}; -/** - * - */ -class LanguageConfig { -public: - /** Default template for declaring a variable of the correct type. - * exmaple: "{$type} {$varname};" - */ - QString varDeclTemplate; - VarNameManglingRules varNaming; - - enum class VariableStrategy { - UseLocalVariables, - DeclareClass - }; - - QString classStartTemplate; - QString classEndTemplate; - QString classFieldTemplate; -}; - -/** - * - * There are some default fallbacks in place - * - smallint > integer - * - integer > bigint - * - int2 > smallint - * - int4 > integer - * - int8 > bigint - * - * - float > double - * - text > varchar - */ -class TypeConfig { -public: - /** The following template allows you to derive the variable name from the result fieldname. - * - * {$} in the suplied value will be replaced with the name of the result field. - * - * example: {$} - */ - QString varNameTemplate; - QString typeIdent; - QString varDeclTemplate; ///< Overrules the default template in the language config - -// Mapping() = default; -// Mapping(Oid oid, QString lang_type_ident) -// : db_oid(oid), lang_type(lang_type_ident) -// {} -// -// bool operator < (const Mapping &rhs) const -// { -// return db_oid < rhs.db_oid; -// } - -}; - -class TypeMappings { -public: - -private: -}; - -class CodeBuilderConfiguration { -public: -}; - -class CodeBuilder { -public: - QString GenClassDefinition(const Pgsql::Result &result) const; -}; diff --git a/pglab/ColumnTableModel.cpp b/pglab/ColumnTableModel.cpp index 5f46ff2..5a63f95 100644 --- a/pglab/ColumnTableModel.cpp +++ b/pglab/ColumnTableModel.cpp @@ -140,7 +140,7 @@ int ColumnTableModel::columnCount(const QModelIndex &/*parent*/) const Oid ColumnTableModel::getType(int /*column*/) const { - Oid oid = Pgsql::VARCHAROID; + Oid oid = Pgsql::varchar_oid; // switch (column) { // case TypeCol: // case NameCol: diff --git a/pglab/ConstraintModel.cpp b/pglab/ConstraintModel.cpp index c11a019..acf682d 100644 --- a/pglab/ConstraintModel.cpp +++ b/pglab/ConstraintModel.cpp @@ -79,12 +79,12 @@ QVariant ConstraintModel::headerData(int section, Qt::Orientation orientation, i return v; } -int ConstraintModel::rowCount(const QModelIndex &parent) const +int ConstraintModel::rowCount(const QModelIndex &) const { return m_constraints.size(); } -int ConstraintModel::columnCount(const QModelIndex &parent) const +int ConstraintModel::columnCount(const QModelIndex &) const { return colCount; } @@ -99,7 +99,7 @@ int ConstraintModel::columnCount(const QModelIndex &parent) const // return v; //} -Oid ConstraintModel::getType(int column) const +Oid ConstraintModel::getType(int ) const { Oid oid = Pgsql::varchar_oid; diff --git a/pglab/DatabasesTableModel.cpp b/pglab/DatabasesTableModel.cpp index 2cc3e5b..1966484 100644 --- a/pglab/DatabasesTableModel.cpp +++ b/pglab/DatabasesTableModel.cpp @@ -84,10 +84,10 @@ Oid DatabasesTableModel::getType(int column) const switch (column) { case AllowConnCol: case IsTemplateCol: - oid = BOOLOID; + oid = bool_oid; break; case ConnLimitCol: - oid = INT4OID; + oid = int4_oid; break; case AclCol: case CollateCol: @@ -96,7 +96,7 @@ Oid DatabasesTableModel::getType(int column) const case DbaCol: case NameCol: case TablespaceCol: - oid = VARCHAROID; + oid = varchar_oid; break; default: oid = InvalidOid; diff --git a/pglab/IconColumnDelegate.cpp b/pglab/IconColumnDelegate.cpp index 7788c33..c73adb5 100644 --- a/pglab/IconColumnDelegate.cpp +++ b/pglab/IconColumnDelegate.cpp @@ -32,8 +32,8 @@ void IconColumnDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op } } -QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const +QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &, + const QModelIndex &) const { return QSize(16, 16); } diff --git a/pglab/RolesTableModel.cpp b/pglab/RolesTableModel.cpp index a0893bb..4f51b83 100644 --- a/pglab/RolesTableModel.cpp +++ b/pglab/RolesTableModel.cpp @@ -81,7 +81,7 @@ Oid RolesTableModel::getType(int column) const Oid oid; switch (column) { case NameCol: - oid = VARCHAROID; + oid = varchar_oid; break; case ReplicationCol: @@ -91,15 +91,15 @@ Oid RolesTableModel::getType(int column) const case CreateRoleCol: case InheritCol: case SuperCol: - oid = BOOLOID; + oid = bool_oid; break; case ConnlimitCol: - oid = INT4OID; + oid = int4_oid; break; case ValidUntilCol: - oid = TIMESTAMPOID; + oid = timestamp_oid; break; default: oid = InvalidOid; diff --git a/pglab/TablesTableModel.cpp b/pglab/TablesTableModel.cpp index cf38f0a..e534f79 100644 --- a/pglab/TablesTableModel.cpp +++ b/pglab/TablesTableModel.cpp @@ -142,7 +142,7 @@ Oid TablesTableModel::getType(int column) const case OptionsCol: // case AclCol: default: - oid = Pgsql::VARCHAROID; + oid = Pgsql::varchar_oid; } return oid; } diff --git a/pglab/pglab.pro b/pglab/pglab.pro index aac271b..8aaaa8a 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -54,7 +54,6 @@ SOURCES += main.cpp\ ConnectionList.cpp \ ProcessStdioWidget.cpp \ GlobalIoService.cpp \ - CodeBuilderConfiguration.cpp \ ResultTableModelUtil.cpp \ BaseTableModel.cpp \ QueryParamListController.cpp \ @@ -74,7 +73,7 @@ SOURCES += main.cpp\ EditorGutter.cpp \ CodeEditor.cpp \ PlgPage.cpp \ - PropertyProxyModel.cpp +PropertyProxyModel.cpp HEADERS += \ QueryResultModel.h \ @@ -122,7 +121,7 @@ HEADERS += \ PlgPage.h \ AbstractCommand.h \ PropertyProxyModel.h \ - CustomDataRole.h +CustomDataRole.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ diff --git a/pglablib/PgContainer.h b/pglablib/PgContainer.h index 5c0d6a7..f520fec 100644 --- a/pglablib/PgContainer.h +++ b/pglablib/PgContainer.h @@ -24,6 +24,8 @@ class PgContainer: public IPgContainter { public: using t_Container = std::vector; ///< Do not assume it will stay a vector only expect bidirectional access + PgContainer() = default; + explicit PgContainer(std::weak_ptr cat) : m_catalogue(cat) {} @@ -87,6 +89,13 @@ public: std::sort(m_container.begin(), m_container.end()); } + + // Meant for mocking during testing + void add(const T &elem) + { + m_container.push_back(elem); + std::sort(m_container.begin(), m_container.end()); + } protected: std::weak_ptr m_catalogue; t_Container m_container; diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp new file mode 100644 index 0000000..baed498 --- /dev/null +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -0,0 +1,119 @@ +#include "CodeBuilder.h" +#include "Pgsql_Result.h" +#include "IndentationConfig.h" +#include "LanguageConfig.h" +#include "StructureTemplate.h" +#include "util.h" +#include + +void FormatToStream(QTextStream &stream, QString format, std::function field_callback) +{ + // Use static to optimize only once + static QRegularExpression cached_find_var_re("(?:[^\\\\]|^)(\\/%([a-zA-Z0-9_-]+)%\\/)", QRegularExpression::OptimizeOnFirstUsageOption); + + int from = 0; + QRegularExpressionMatch match; + while (format.indexOf(cached_find_var_re, from, &match) >= 0) { + if (from > 0) { + // Because the regex has to check for backslash in front we have the from position + // one position before where we actually should continue for the second match and later ie when from > 0 + // Therefor increase from by 1 to make the substring (midRef) calculation work + ++from; + } + // copy code before the var to the stream + stream << format.midRef(from, match.capturedStart(1) - from); + field_callback(stream, match.captured(2)); + from = match.capturedEnd()-1; // -1 because it wants to match one character before or start of line to make sure there is no backslash + } + if (from > 0) { + // same reason as at the start of the loop + ++from; + } + stream << format.midRef(from); +} + +void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result) +{ +// %1 query string +// %2 struct_name +// %3 list of field assignments + QString exec_query_template = "Pgsql::Result result = conn.query(%1);"; + + QString loop_start_template = + "Pgsql::Result result = conn.query(/%query_literal%/);" + "for (auto row: result) {\n" + " Pgsql::Col col(row);\n" + " /%struct_type%/ v;\n" + "/%assign_fields%/" + "}"; + + // %field_name% + // %column_name% + // %column_index% + QString assign_result_field_template = "col >> v./%field_name%/"; + QString query_string_literal = ConvertToMultiLineCString(query); + + // [optional] Gen declaration of result structure + GenReturnStructDefinition(q, result); + + // assume we have connection? What name??? + + // gen code for creating and executing statement + // - bind parameters + + // Gen code for iterating through result and filling the result struct/or local variables + + +} + +void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result) const +{ + std::shared_ptr templ = m_configuration->structureTemplate(); + + QString struct_name = "TODO"; + QString field_format = templ->m_fieldTemplate; + int field_indent_levels = templ->m_fieldIndentation; + QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); + // Make struct start + q << QString(templ->m_startTemplate).arg(struct_name); + // Process fields + for (int column = 0; column < result.cols(); ++column) { + QString res_col_name = result.getColName(column); + Oid type_oid = result.type(column); + + q << field_indent_string; + genFieldDeclaration(q, field_format, res_col_name, type_oid); + q << "\n"; + // Unfortunatly there is no easy reliable way to determine if a column can contain null values + // For simple result columns we can have a look at the original column and if that has a NOT NULL constraint + // then we can assume NOT NULL for the result if there are no LEFT/RIGHT JOINS or other constructs that can introduce NULL + // values + + // Any way at generation time we might want to be able to specify the null handle + // - exception/error return + // - magic value + // - boost::optional + // - boolean flags + // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) + } + // Finish struct + q << QString(templ->m_endTemplate).arg(struct_name); +} + +QString CodeBuilder::columnNameToVariableName(QString column_name) const +{ + return m_configuration->columnNameToFieldName(column_name); +} + +QString CodeBuilder::getTypeName(Oid dbtype) const +{ + return m_configuration->getTypeName(dbtype); +} + +void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const +{ + QString field_name = columnNameToVariableName(column_name); + QString type_name = getTypeName(column_type); + q << QString(format).arg(field_name).arg(type_name); +} + diff --git a/pglablib/codebuilder/CodeBuilder.h b/pglablib/codebuilder/CodeBuilder.h new file mode 100644 index 0000000..c52e070 --- /dev/null +++ b/pglablib/codebuilder/CodeBuilder.h @@ -0,0 +1,28 @@ +#ifndef CODEBUILDER_H +#define CODEBUILDER_H + +#include "Pgsql_declare.h" +#include + +class LanguageConfig; +class QTextStream; + +class CodeBuilder { +public: + void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result); + void GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result) const; + +// Generating code for performing query and going through the result +// - Code for executing the query +// - Code for looping the result +// - Code for processing a single row +// - Declaration of struct for holding single row result + QString columnNameToVariableName(QString column_name) const; + QString getTypeName(Oid dbtype) const; +private: + std::shared_ptr m_configuration; + + void genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const; +}; + +#endif // CODEBUILDER_H diff --git a/pglablib/codebuilder/DefaultConfigs.cpp b/pglablib/codebuilder/DefaultConfigs.cpp new file mode 100644 index 0000000..ca3554d --- /dev/null +++ b/pglablib/codebuilder/DefaultConfigs.cpp @@ -0,0 +1,50 @@ +#include "DefaultConfigs.h" +#include "LanguageConfig.h" +#include "TypeMappings.h" +#include "Pgsql_oids.h" + +using namespace Pgsql; + +TypeMappings GetDefaultCppTypeMappings() +{ + TypeMappings result = { + { bool_oid, "bool" }, + { char_oid, "char" }, + { name_oid, "std::string" }, + { int8_oid, "int64_t" }, + { int2_oid, "int16_t" }, + { int4_oid, "int32_t" }, + { text_oid, "std::string" }, + { oid_oid, "Oid" }, + { float4_oid, "float" }, + { float8_oid, "double" } + }; + return result; +} + + +std::shared_ptr buildDefaultCppLanguageConfig() +{ + return std::make_shared(); +} + +std::shared_ptr getDefaultCppLanguageConfig() +{ + static auto config = buildDefaultCppLanguageConfig(); + return config; +} + +std::shared_ptr buildPgLabCppLanguageConfig() +{ + auto cfg = buildDefaultCppLanguageConfig(); + + return cfg; +} + +std::shared_ptr getPgLabCppLanguageConfig() +{ + static auto config = buildPgLabCppLanguageConfig(); + return config; +} + + diff --git a/pglablib/codebuilder/DefaultConfigs.h b/pglablib/codebuilder/DefaultConfigs.h new file mode 100644 index 0000000..d446d22 --- /dev/null +++ b/pglablib/codebuilder/DefaultConfigs.h @@ -0,0 +1,11 @@ +#ifndef DEFAULTCONFIGS_H +#define DEFAULTCONFIGS_H + +#include + +class LanguageConfig; + +std::shared_ptr getDefaultCppLanguageConfig(); +std::shared_ptr getPgLabCppLanguageConfig(); + +#endif // DEFAULTCONFIGS_H diff --git a/pglablib/codebuilder/IndentationConfig.cpp b/pglablib/codebuilder/IndentationConfig.cpp new file mode 100644 index 0000000..37bf93a --- /dev/null +++ b/pglablib/codebuilder/IndentationConfig.cpp @@ -0,0 +1,27 @@ +#include "IndentationConfig.h" + +IndentationConfig::IndentationConfig() = default; + +IndentationConfig::IndentationConfig(int tab_size, int indentation_size, bool use_tabs) + : m_tabSize(tab_size) + , m_indentationSize(indentation_size) + , m_useTabs(use_tabs) +{} + +/** Returns a string with the right amount of tabs and spaces for the + * requested indentation level. + */ +QString IndentationConfig::getIndentString(int level) const +{ + int spaces = level * m_indentationSize; + int tabs = 0; + if (m_useTabs) { + tabs = spaces / m_tabSize; + spaces -= tabs * m_tabSize; + } + + if (tabs > 0) + return QString(tabs, '\t') + QString(spaces, ' '); + else + return QString(spaces, ' '); +} diff --git a/pglablib/codebuilder/IndentationConfig.h b/pglablib/codebuilder/IndentationConfig.h new file mode 100644 index 0000000..8546c76 --- /dev/null +++ b/pglablib/codebuilder/IndentationConfig.h @@ -0,0 +1,23 @@ +#ifndef INDENTATIONCONFIG_H +#define INDENTATIONCONFIG_H + +#include + +class IndentationConfig { +public: + IndentationConfig(); + IndentationConfig(int tab_size, int indentation_size, bool use_tabs); + + /** Returns a string with the right amount of tabs and spaces for the + * requested indentation level. + */ + QString getIndentString(int level) const; + +private: + int m_tabSize = 8; ///< the size of a tab + int m_indentationSize = 4; ///< Number of positions per level to indent + bool m_useTabs = true; ///< Use tabs as much as possible instead of spaces when indenting +}; + + +#endif // INDENTATIONCONFIG_H diff --git a/pglablib/codebuilder/LanguageConfig.cpp b/pglablib/codebuilder/LanguageConfig.cpp new file mode 100644 index 0000000..3d22eed --- /dev/null +++ b/pglablib/codebuilder/LanguageConfig.cpp @@ -0,0 +1,15 @@ +#include "LanguageConfig.h" +#include "NameManglingRules.h" +#include "TypeMappings.h" + +LanguageConfig::LanguageConfig() = default; + +QString LanguageConfig::columnNameToFieldName(const QString& column_name) const +{ + return m_varNaming->transform(column_name); +} + +QString LanguageConfig::getTypeName(Oid dbtype) const +{ + return m_typeMappings->getTypeForOid(dbtype); +} diff --git a/pglablib/codebuilder/LanguageConfig.h b/pglablib/codebuilder/LanguageConfig.h new file mode 100644 index 0000000..a850b5c --- /dev/null +++ b/pglablib/codebuilder/LanguageConfig.h @@ -0,0 +1,54 @@ +#ifndef LANGUAGECONFIG_H +#define LANGUAGECONFIG_H + +#include +#include "Pgsql_oids.h" + +class NameManglingRules; +class TypeMappings; +class StructureTemplate; +class IndentationConfig; +/** + * + */ +class LanguageConfig { +public: + LanguageConfig(); + + QString columnNameToFieldName(const QString& column_name) const; + QString getTypeName(Oid dbtype) const; + + std::shared_ptr typeMappings() const { return m_typeMappings; } + void setTypeMappings(std::shared_ptr type_mappings) + { + m_typeMappings = type_mappings; + } + + std::shared_ptr structureTemplate() const + { + return m_structureTemplate; + } + + std::shared_ptr indentationConfig() const + { + return m_indentationConfig; + } +private: + /** Default template for declaring a variable of the correct type. + * exmaple: "{$type} {$varname};" + */ + //QString varDeclTemplate; + std::shared_ptr m_varNaming; + std::shared_ptr m_typeMappings; + std::shared_ptr m_structureTemplate; + std::shared_ptr m_indentationConfig; + + enum class VariableStrategy { + UseLocalVariables, + DeclareClass + }; + + +}; + +#endif // LANGUAGECONFIG_H diff --git a/pglablib/codebuilder/NameManglingRules.cpp b/pglablib/codebuilder/NameManglingRules.cpp new file mode 100644 index 0000000..9e43a21 --- /dev/null +++ b/pglablib/codebuilder/NameManglingRules.cpp @@ -0,0 +1,32 @@ +#include "NameManglingRules.h" + +void NameManglingRules::apply(const ReplaceRule &rule, QString &in) const +{ + int from = 0; + int pos; + QRegularExpressionMatch match; + while ((pos = in.indexOf(rule.pattern, from, &match)) >= 0) { + int len = match.capturedLength(); + in.replace(pos, len, rule.replace); + from = pos + rule.replace.size(); + if (rule.nextToUpper) + in[from] = in[from].toUpper(); + } +} + +QString NameManglingRules::transform(const QString &input) const +{ + QString result; + if (caseConversion == CaseConversion::Lower) + result = input.toLower(); + else if (caseConversion == CaseConversion::Upper) + result = input.toUpper(); + else + result = input; + + for (auto rule : replaceRules) { + apply(rule, result); + } + + return result; +} diff --git a/pglablib/codebuilder/NameManglingRules.h b/pglablib/codebuilder/NameManglingRules.h new file mode 100644 index 0000000..5857d79 --- /dev/null +++ b/pglablib/codebuilder/NameManglingRules.h @@ -0,0 +1,50 @@ +#ifndef NAMEMANGLINGRULES_H +#define NAMEMANGLINGRULES_H + +#include +#include +#include + +/** Defines how a database result fieldname should be converted into a variable + * name in the target language. + * + */ +class NameManglingRules { +public: + enum class CollisionHandling { + Restrict, ///< An error will be reported and no code generated + Fqn, ///< Turn into fully qualified name (table_column) + Number ///< A number will be appended to fields that have the same name + }; + + enum class CaseConversion { + AsIs, + Upper, + Lower + }; + + class ReplaceRule { + public: + QRegularExpression pattern; + QString replace; + bool nextToUpper = false; + }; + using ReplaceRules = std::vector; + + ReplaceRules replaceRules; +// { {"[ -_]", QRegularExpression::OptimizeOnFirstUsageOption }, "", true } + + QString replaceSpaceWith; ///< default is empty string which means remove spaces + //CollisionHandling CollisionHandling = CollisionHandling::Restrict; + CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule + //CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char + bool camelCase = false; ///< removes underscores and make first char after underscore uppercase + + void apply(const ReplaceRule &rule, QString &in) const; + + QString transform(const QString &input) const; + + +}; + +#endif // NAMEMANGLINGRULES_H diff --git a/pglablib/codebuilder/StructureTemplate.cpp b/pglablib/codebuilder/StructureTemplate.cpp new file mode 100644 index 0000000..abc1ec5 --- /dev/null +++ b/pglablib/codebuilder/StructureTemplate.cpp @@ -0,0 +1,2 @@ +#include "StructureTemplate.h" + diff --git a/pglablib/codebuilder/StructureTemplate.h b/pglablib/codebuilder/StructureTemplate.h new file mode 100644 index 0000000..716031a --- /dev/null +++ b/pglablib/codebuilder/StructureTemplate.h @@ -0,0 +1,16 @@ +#ifndef STRUCTURETEMPLATE_H +#define STRUCTURETEMPLATE_H + +#include + +class StructureTemplate { +public: + + QString m_startTemplate; // class /$structname/ {\npublic:\n + QString m_endTemplate; // }; + QString m_fieldTemplate; // /$typename/ /$varname/; + QString m_fieldSeparator; // use when you need something between fields but not after the last field + int m_fieldIndentation = 1; +}; + +#endif // STRUCTURETEMPLATE_H diff --git a/pglablib/codebuilder/TypeMappings.cpp b/pglablib/codebuilder/TypeMappings.cpp new file mode 100644 index 0000000..b90a9e4 --- /dev/null +++ b/pglablib/codebuilder/TypeMappings.cpp @@ -0,0 +1,133 @@ +#include "TypeMappings.h" +#include "PgTypeContainer.h" + +//namespace { +// using Fallbacks = std::unordered_map; + +// Fallbacks fallbacks = { +// { oid_text, oid_varchar }, +// { } +// }; +//} + +TypeMappings::TypeMappings() = default; + +TypeMappings::TypeMappings(std::initializer_list mappings) +{ + m_typeMap.insert(mappings.begin(), mappings.end()); +} + +QString TypeMappings::getTypeForOid(Oid oid) const +{ + // TODO use the catalog to determine if someting is an array or vector type. + // If it is lookup its element type and use the std container + auto res = m_typeMap.find(oid); + if (res != m_typeMap.end()) { + return res->second; + } + + if (m_types) { + PgType type = m_types->getByKey(oid); + if (type.oid != InvalidOid && type.elem != InvalidOid) { + res = m_typeMap.find(type.elem); + QString type_string; + if (res == m_typeMap.end()) { + type_string = m_defaultStringType; + } + else { + type_string = res->second; + } + return QString(m_defaultContainerType).arg(type_string); + } + } + return m_defaultStringType; +} + +void TypeMappings::setTypes(std::shared_ptr types) +{ + m_types = types; +} + +void TypeMappings::setDefaultStringType(QString str) +{ + m_defaultStringType = str; +} + +void TypeMappings::setDefaultContainerType(QString str) +{ + m_defaultContainerType = str; +} + +void TypeMappings::set(Oid oid, QString type) +{ + m_typeMap.insert_or_assign(oid, type); +} + + + +//constexpr Oid bool_oid = 16; +//constexpr Oid bytea_oid = 17; +//constexpr Oid char_oid = 18; +//constexpr Oid name_oid = 19; +//constexpr Oid int8_oid = 20; +//constexpr Oid int2_oid = 21; +//constexpr Oid int4_oid = 23; +//constexpr Oid regproc_oid = 24; +//constexpr Oid text_oid = 25; +//constexpr Oid oid_oid = 26; +//constexpr Oid tid_oid = 27; +//constexpr Oid xid_oid = 28; +//constexpr Oid cid_oid = 29; +//constexpr Oid json_oid = 114; +//constexpr Oid xml_oid = 142; +//constexpr Oid point_oid = 600; +//constexpr Oid lseg_oid = 601; +//constexpr Oid path_oid = 602; +//constexpr Oid box_oid = 603; +//constexpr Oid polygon_oid = 604; +//constexpr Oid line_oid = 628; +//constexpr Oid cidr_oid = 650; +//constexpr Oid float4_oid = 700; +//constexpr Oid float8_oid = 701; +//constexpr Oid abstime_oid = 702; +//constexpr Oid reltime_oid = 703; +//constexpr Oid tinterval_oid = 704; +//constexpr Oid circle_oid = 718; +//constexpr Oid money_oid = 790; +//constexpr Oid macaddr_oid = 829; +//constexpr Oid inet_oid = 869; +//constexpr Oid aclitem_oid = 1033; +//constexpr Oid bpchar_oid = 1042; +//constexpr Oid varchar_oid = 1043; +//constexpr Oid date_oid = 1082; +//constexpr Oid time_oid = 1083; +//constexpr Oid timestamp_oid = 1114; +//constexpr Oid timestamptz_oid = 1184; +//constexpr Oid interval_oid = 1186; +//constexpr Oid timetz_oid = 1266; +//constexpr Oid bit_oid = 1560; +//constexpr Oid varbit_oid = 1562; +//constexpr Oid numeric_oid = 1700; +//constexpr Oid refcursor_oid = 1790; +//constexpr Oid regprocedure_oid = 2202; +//constexpr Oid regoper_oid = 2203; +//constexpr Oid regoperator_oid = 2204; +//constexpr Oid regclass_oid = 2205; +//constexpr Oid regtype_oid = 2206; +//constexpr Oid uuid_oid = 2950; +//constexpr Oid txid_snapshot_oid = 2970; +//constexpr Oid pg_lsn_oid = 3220; +//constexpr Oid tsvector_oid = 3614; +//constexpr Oid tsquery_oid = 3615; +//constexpr Oid gtsvector_oid = 3642; +//constexpr Oid regconfig_oid = 3734; +//constexpr Oid regdictionary_oid = 3769; +//constexpr Oid jsonb_oid = 3802; +//constexpr Oid int4range_oid = 3904; +//constexpr Oid numrange_oid = 3906; +//constexpr Oid tsrange_oid = 3908; +//constexpr Oid tstzrange_oid = 3910; +//constexpr Oid daterange_oid = 3912; +//constexpr Oid int8range_oid = 3926; +//constexpr Oid regnamespace_oid = 4089; +//constexpr Oid regrole_oid = 4096; diff --git a/pglablib/codebuilder/TypeMappings.h b/pglablib/codebuilder/TypeMappings.h new file mode 100644 index 0000000..38e0c74 --- /dev/null +++ b/pglablib/codebuilder/TypeMappings.h @@ -0,0 +1,46 @@ +#ifndef TYPEMAPPINGS_H +#define TYPEMAPPINGS_H + +#include +#include +#include +#include + +class PgTypeContainer; + +class TypeMappings { +public: + using TypeMap = std::unordered_map; + using Mapping = std::pair; + + TypeMappings(); + + TypeMappings(std::initializer_list mappings); + + QString getTypeForOid(Oid oid) const; + + const TypeMap& typeMap() const { return m_typeMap; } + + QString defaultStringType() const { return m_defaultStringType; } + + void setTypes(std::shared_ptr types); + + void setDefaultStringType(QString str); + + void setDefaultContainerType(QString str); + + void set(Oid oid, QString type); + /** Removing a type from the mapping will reeastablish its default mapping + * which in most cases is the default string type for the language. + */ + void remove(Oid oid); +private: + + TypeMap m_typeMap; + QString m_defaultStringType; + QString m_defaultContainerType; ///< This string should contain a format variable where the element type should go + + std::shared_ptr m_types; +}; + +#endif // TYPEMAPPINGS_H diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 8c4047c..29b5c40 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -57,7 +57,14 @@ SOURCES += \ PgAmContainer.cpp \ PgObject.cpp \ PgTablespace.cpp \ - PgTablespaceContainer.cpp +PgTablespaceContainer.cpp \ +codebuilder/LanguageConfig.cpp \ +codebuilder/CodeBuilder.cpp \ +codebuilder/NameManglingRules.cpp \ +codebuilder/DefaultConfigs.cpp \ +codebuilder/TypeMappings.cpp \ +codebuilder/IndentationConfig.cpp \ +codebuilder/StructureTemplate.cpp HEADERS += \ Pglablib.h \ @@ -92,7 +99,14 @@ HEADERS += \ PgAmContainer.h \ PgObject.h \ PgTablespace.h \ - PgTablespaceContainer.h +PgTablespaceContainer.h \ +codebuilder/LanguageConfig.h \ +codebuilder/CodeBuilder.h \ +codebuilder/NameManglingRules.h \ +codebuilder/DefaultConfigs.h \ +codebuilder/TypeMappings.h \ +codebuilder/IndentationConfig.h \ +codebuilder/StructureTemplate.h unix { target.path = /usr/lib diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index 4e9fa20..0bb5a69 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -73,7 +73,7 @@ namespace Pgsql { * * The class takes ownership of data and will try to delete[] it. */ - Param addText(const char *data, Oid oid=VARCHAROID); + Param addText(const char *data, Oid oid=varchar_oid); }; } // end namespace Pgsql diff --git a/pgsql/Pgsql_Value.cpp b/pgsql/Pgsql_Value.cpp index ac9bc54..4219887 100644 --- a/pgsql/Pgsql_Value.cpp +++ b/pgsql/Pgsql_Value.cpp @@ -103,9 +103,9 @@ Value::operator double() const bool Value::isString() const { - return m_typ == CHAROID - || m_typ == VARCHAROID - || m_typ == TEXTOID + return m_typ == char_oid + || m_typ == varchar_oid + || m_typ == text_oid ; } diff --git a/pgsql/Pgsql_declare.h b/pgsql/Pgsql_declare.h index 47f76eb..8e34976 100644 --- a/pgsql/Pgsql_declare.h +++ b/pgsql/Pgsql_declare.h @@ -5,95 +5,95 @@ namespace Pgsql { - const Oid BOOLOID = 16; - const Oid BYTEAOID = 17; - const Oid CHAROID = 18; - const Oid NAMEOID = 19; - const Oid INT8OID = 20; - const Oid INT2OID = 21; - const Oid INT2VECTOROID = 22; - const Oid INT4OID = 23; - const Oid REGPROCOID = 24; - const Oid TEXTOID = 25; - const Oid OIDOID = 26; - const Oid TIDOID = 27; - const Oid XIDOID = 28; - const Oid CIDOID = 29; - const Oid OIDVECTOROID = 30; - const Oid JSONOID = 114; - const Oid XMLOID = 142; - const Oid PGNODETREEOID = 194; - const Oid PGDDLCOMMANDOID = 32; - const Oid POINTOID = 600; - const Oid LSEGOID = 601; - const Oid PATHOID = 602; - const Oid BOXOID = 603; - const Oid POLYGONOID = 604; - const Oid LINEOID = 628; - const Oid FLOAT4OID = 700; - const Oid FLOAT8OID = 701; - const Oid ABSTIMEOID = 702; - const Oid RELTIMEOID = 703; - const Oid TINTERVALOID = 704; - const Oid UNKNOWNOID = 705; - const Oid CIRCLEOID = 718; - const Oid CASHOID = 790; - const Oid MACADDROID = 829; - const Oid INETOID = 869; - const Oid CIDROID = 650; - const Oid INT2ARRAYOID = 1005; - const Oid INT4ARRAYOID = 1007; - const Oid TEXTARRAYOID = 1009; - const Oid OIDARRAYOID = 1028; - const Oid FLOAT4ARRAYOID = 1021; - const Oid ACLITEMOID = 1033; - const Oid CSTRINGARRAYOID = 1263; - const Oid BPCHAROID = 1042; - const Oid VARCHAROID = 1043; - const Oid DATEOID = 1082; - const Oid TIMEOID = 1083; - const Oid TIMESTAMPOID = 1114; - const Oid TIMESTAMPTZOID = 1184; - const Oid INTERVALOID = 1186; - const Oid TIMETZOID = 1266; - const Oid BITOID = 1560; - const Oid VARBITOID = 1562; - const Oid NUMERICOID = 1700; - const Oid REFCURSOROID = 1790; - const Oid REGPROCEDUREOID = 2202; - const Oid REGOPEROID = 2203; - const Oid REGOPERATOROID = 2204; - const Oid REGCLASSOID = 2205; - const Oid REGTYPEOID = 2206; - const Oid REGROLEOID = 4096; - const Oid REGNAMESPACEOID = 4089; - const Oid REGTYPEARRAYOID = 2211; - const Oid UUIDOID = 2950; - const Oid LSNOID = 3220; - const Oid TSVECTOROID = 3614; - const Oid GTSVECTOROID = 3642; - const Oid TSQUERYOID = 3615; - const Oid REGCONFIGOID = 3734; - const Oid REGDICTIONARYOID = 3769; - const Oid JSONBOID = 3802; - const Oid INT4RANGEOID = 3904; - const Oid RECORDOID = 2249; - const Oid RECORDARRAYOID = 2287; - const Oid CSTRINGOID = 2275; - const Oid ANYOID = 2276; - const Oid ANYARRAYOID = 2277; - const Oid VOIDOID = 2278; - const Oid TRIGGEROID = 2279; - const Oid EVTTRIGGEROID = 3838; - const Oid LANGUAGE_HANDLEROID = 2280; - const Oid INTERNALOID = 2281; - const Oid OPAQUEOID = 2282; - const Oid ANYELEMENTOID = 2283; - const Oid ANYNONARRAYOID = 2776; - const Oid ANYENUMOID = 3500; - const Oid FDW_HANDLEROID = 3115; - const Oid TSM_HANDLEROID = 3310; - const Oid ANYRANGEOID = 3831; +// const Oid BOOLOID = 16; +// const Oid BYTEAOID = 17; +// const Oid CHAROID = 18; +// const Oid NAMEOID = 19; +// const Oid INT8OID = 20; +// const Oid INT2OID = 21; +// const Oid INT2VECTOROID = 22; +// const Oid INT4OID = 23; +// const Oid REGPROCOID = 24; +// const Oid TEXTOID = 25; +// const Oid OIDOID = 26; +// const Oid TIDOID = 27; +// const Oid XIDOID = 28; +// const Oid CIDOID = 29; +// const Oid OIDVECTOROID = 30; +// const Oid JSONOID = 114; +// const Oid XMLOID = 142; +// const Oid PGNODETREEOID = 194; +// const Oid PGDDLCOMMANDOID = 32; +// const Oid POINTOID = 600; +// const Oid LSEGOID = 601; +// const Oid PATHOID = 602; +// const Oid BOXOID = 603; +// const Oid POLYGONOID = 604; +// const Oid LINEOID = 628; +// const Oid FLOAT4OID = 700; +// const Oid FLOAT8OID = 701; +// const Oid ABSTIMEOID = 702; +// const Oid RELTIMEOID = 703; +// const Oid TINTERVALOID = 704; +// const Oid UNKNOWNOID = 705; +// const Oid CIRCLEOID = 718; +// const Oid CASHOID = 790; +// const Oid MACADDROID = 829; +// const Oid INETOID = 869; +// const Oid CIDROID = 650; +// const Oid INT2ARRAYOID = 1005; +// const Oid INT4ARRAYOID = 1007; +// const Oid TEXTARRAYOID = 1009; +// const Oid OIDARRAYOID = 1028; +// const Oid FLOAT4ARRAYOID = 1021; +// const Oid ACLITEMOID = 1033; +// const Oid CSTRINGARRAYOID = 1263; +// const Oid BPCHAROID = 1042; +// const Oid VARCHAROID = 1043; +// const Oid DATEOID = 1082; +// const Oid TIMEOID = 1083; +// const Oid TIMESTAMPOID = 1114; +// const Oid TIMESTAMPTZOID = 1184; +// const Oid INTERVALOID = 1186; +// const Oid TIMETZOID = 1266; +// const Oid BITOID = 1560; +// const Oid VARBITOID = 1562; +// const Oid NUMERICOID = 1700; +// const Oid REFCURSOROID = 1790; +// const Oid REGPROCEDUREOID = 2202; +// const Oid REGOPEROID = 2203; +// const Oid REGOPERATOROID = 2204; +// const Oid REGCLASSOID = 2205; +// const Oid REGTYPEOID = 2206; +// const Oid REGROLEOID = 4096; +// const Oid REGNAMESPACEOID = 4089; +// const Oid REGTYPEARRAYOID = 2211; +// const Oid UUIDOID = 2950; +// const Oid LSNOID = 3220; +// const Oid TSVECTOROID = 3614; +// const Oid GTSVECTOROID = 3642; +// const Oid TSQUERYOID = 3615; +// const Oid REGCONFIGOID = 3734; +// const Oid REGDICTIONARYOID = 3769; +// const Oid JSONBOID = 3802; +// const Oid INT4RANGEOID = 3904; +// const Oid RECORDOID = 2249; +// const Oid RECORDARRAYOID = 2287; +// const Oid CSTRINGOID = 2275; +// const Oid ANYOID = 2276; +// const Oid ANYARRAYOID = 2277; +// const Oid VOIDOID = 2278; +// const Oid TRIGGEROID = 2279; +// const Oid EVTTRIGGEROID = 3838; +// const Oid LANGUAGE_HANDLEROID = 2280; +// const Oid INTERNALOID = 2281; +// const Oid OPAQUEOID = 2282; +// const Oid ANYELEMENTOID = 2283; +// const Oid ANYNONARRAYOID = 2776; +// const Oid ANYENUMOID = 3500; +// const Oid FDW_HANDLEROID = 3115; +// const Oid TSM_HANDLEROID = 3310; +// const Oid ANYRANGEOID = 3831; class Params; diff --git a/pgsql/Pgsql_oids.cpp b/pgsql/Pgsql_oids.cpp index 6f6f9b2..a643e04 100644 --- a/pgsql/Pgsql_oids.cpp +++ b/pgsql/Pgsql_oids.cpp @@ -163,7 +163,7 @@ namespace { } -Oid Pgsql::ElemOidFromArrayOid(Oid oid) +Oid Pgsql::ElemOidFromArrayOid(Oid oid) noexcept { auto iter = std::lower_bound(g_ArrayToElem.begin(), g_ArrayToElem.end(), @@ -173,11 +173,11 @@ Oid Pgsql::ElemOidFromArrayOid(Oid oid) return l.array < r.array; }); if (iter == g_ArrayToElem.end()) - throw std::runtime_error("ElemOidFromArrayOid Oid not found"); + return InvalidOid; return iter->elem; } -Oid Pgsql::ArrayOidFromElemOid(Oid oid) +Oid Pgsql::ArrayOidFromElemOid(Oid oid) noexcept { auto iter = std::lower_bound(g_ElemToArray.begin(), g_ElemToArray.end(), ElemArray{oid, oid}, @@ -186,7 +186,6 @@ Oid Pgsql::ArrayOidFromElemOid(Oid oid) return l.elem < r.elem; }); if (iter == g_ElemToArray.end()) - throw std::runtime_error("ElemOidFromArrayOid Oid not found"); + return InvalidOid; return iter->array; - } diff --git a/pgsql/Pgsql_oids.h b/pgsql/Pgsql_oids.h index 4be5687..e4d502f 100644 --- a/pgsql/Pgsql_oids.h +++ b/pgsql/Pgsql_oids.h @@ -55,6 +55,7 @@ namespace Pgsql { constexpr Oid regoperator_oid = 2204; constexpr Oid regclass_oid = 2205; constexpr Oid regtype_oid = 2206; + constexpr Oid any_oid = 2276; constexpr Oid uuid_oid = 2950; constexpr Oid txid_snapshot_oid = 2970; constexpr Oid pg_lsn_oid = 3220; @@ -140,8 +141,19 @@ namespace Pgsql { constexpr Oid regnamespace_array_oid = 4090; constexpr Oid regrole_array_oid = 4097; - Oid ElemOidFromArrayOid(Oid oid); - Oid ArrayOidFromElemOid(Oid oid); + /** If oid is an array oid then it returns the corresponding element oid. + * It oid is not an array the functions returns InvalidOid. + * This function only works for the predefined postgresql types. + * If you need it to work for user defined types then use the system catalogue. + */ + Oid ElemOidFromArrayOid(Oid oid) noexcept; + /** If oid is a non array oid then it returns the corresponding array oid. + * It oid is a array oid or oid is unknown it returns InvalidOid. + * This function only works for the predefined postgresql types. + * If you need it to work for user defined types then use the system catalogue. + * + */ + Oid ArrayOidFromElemOid(Oid oid) noexcept; template class OidFor { diff --git a/tests/PgsqlTests/tst_Value.cpp b/tests/PgsqlTests/tst_Value.cpp index 2f4385b..fa7b8ed 100644 --- a/tests/PgsqlTests/tst_Value.cpp +++ b/tests/PgsqlTests/tst_Value.cpp @@ -11,14 +11,14 @@ using namespace Pgsql; TEST(Pgsql_Value, test_int4) { - Pgsql::Value v("1", INT4OID); + Pgsql::Value v("1", int4_oid); int i = (int)v; ASSERT_EQ(i, 1); } TEST(Pgsql_Value, test_QDateTime) { - Pgsql::Value v("2017-10-22 12:34:56", TIMESTAMPTZOID); + Pgsql::Value v("2017-10-22 12:34:56", timestamptz_oid); QDateTime dt = (QDateTime)v; ASSERT_EQ(dt, QDateTime(QDate(2017, 10, 22), QTime(12, 34, 56))); } @@ -26,7 +26,7 @@ TEST(Pgsql_Value, test_QDateTime) TEST(Pgsql_Value, test_QDateTime_2) { - Pgsql::Value v("2017-12-01 09:38:17.339817+02", TIMESTAMPTZOID); + Pgsql::Value v("2017-12-01 09:38:17.339817+02", timestamptz_oid); QDateTime dt = (QDateTime)v; QDateTime exp(QDate(2017, 12, 1), QTime(9, 38, 17, 340), Qt::OffsetFromUTC, 2*3600); @@ -35,21 +35,21 @@ TEST(Pgsql_Value, test_QDateTime_2) TEST(Pgsql_Value, test_QDate) { - Pgsql::Value v("2017-10-22", DATEOID); + Pgsql::Value v("2017-10-22", date_oid); QDate d = v; ASSERT_EQ(d, QDate(2017, 10, 22)); } TEST(Pgsql_Value, test_QTime) { - Pgsql::Value v("12:34:56", TIMEOID); + Pgsql::Value v("12:34:56", time_oid); QTime t = v; ASSERT_EQ(t, QTime(12, 34, 56)); } TEST(Pgsql_Value, test_QTimeMS) { - Pgsql::Value v("09:38:17.339817+02", TIMETZOID); + Pgsql::Value v("09:38:17.339817+02", timetz_oid); QTime t = v; ASSERT_EQ(t, QTime(9, 38, 17, 340)); } @@ -58,19 +58,19 @@ TEST(Pgsql_Value, test_QTimeMS) TEST(Pgsql_Value, isString_int4) { - Pgsql::Value v("1", INT4OID); + Pgsql::Value v("1", int4_oid); ASSERT_EQ(v.isString(), false); } TEST(Pgsql_Value, isString_varchar) { - Pgsql::Value v("1", VARCHAROID); + Pgsql::Value v("1", varchar_oid); ASSERT_EQ(v.isString(), true); } TEST(Pgsql_Value, getAsVector_Ints) { - Pgsql::Value v("1 2", ANYOID); + Pgsql::Value v("1 2", any_oid); std::vector r; v.getAsVector(std::back_inserter(r)); @@ -83,7 +83,7 @@ TEST(Pgsql_Value, getAsVector_Ints) TEST(Pgsql_Value, getAsArray_Ints) { - Pgsql::Value v("{1,2}", TEXTARRAYOID); + Pgsql::Value v("{1,2}", text_array_oid); std::vector r; v.getAsArray(std::back_inserter(r)); @@ -94,7 +94,7 @@ TEST(Pgsql_Value, getAsArray_Ints) TEST(Pgsql_Value, getAsArray_QDateTime) { - Pgsql::Value v("{\"2017-12-11 10:11:22\",\"2017-12-13 12:00:11\"}", TEXTARRAYOID); + Pgsql::Value v("{\"2017-12-11 10:11:22\",\"2017-12-13 12:00:11\"}", text_array_oid); std::set r; v.getAsArray(std::inserter(r, r.end())); @@ -103,7 +103,7 @@ TEST(Pgsql_Value, getAsArray_QDateTime) TEST(Pgsql_Value, getAsArray_throws_on_NULL) { - Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + Pgsql::Value v("{1,NULL,2}", text_array_oid); std::vector r; try { v.getAsArray(std::back_inserter(r)); @@ -117,7 +117,7 @@ TEST(Pgsql_Value, getAsArray_throws_on_NULL) TEST(Pgsql_Value, getAsArray_default_on_NULL) { - Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + Pgsql::Value v("{1,NULL,2}", text_array_oid); std::vector r; try { v.getAsArray(std::back_inserter(r), -1); @@ -132,7 +132,7 @@ TEST(Pgsql_Value, getAsArray_default_on_NULL) TEST(Pgsql_Value, getAsArray_ignore_NULL) { - Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + Pgsql::Value v("{1,NULL,2}", text_array_oid); std::vector r; try { v.getAsArray(std::back_inserter(r), NullHandling::Ignore); @@ -146,7 +146,7 @@ TEST(Pgsql_Value, getAsArray_ignore_NULL) TEST(Pgsql_Value, getAsArrayOptional) { - Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); + Pgsql::Value v("{1,NULL,2}", text_array_oid); std::vector> r; try { v.getAsArrayOfOptional(std::back_inserter(r)); diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index 0899186..0d2005e 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -1,12 +1,15 @@ include(gtest_dependency.pri) TEMPLATE = app -CONFIG += console c++14 +CONFIG += console c++17 CONFIG -= app_bundle CONFIG += thread CONFIG += qt -QT += core +QT += core widgets + +QMAKE_CXXFLAGS += /std:c++17 +LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq HEADERS += @@ -17,7 +20,9 @@ SOURCES += main.cpp \ tst_scopeguard.cpp \ tst_CsvWriter.cpp \ tst_PasswordManager.cpp \ - tst_ParamJson.cpp + tst_ParamJson.cpp \ + tst_CodeBuilder.cpp \ + tst_NameManglingRules.cpp win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../core/release/ -lcore else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../core/debug/ -lcore @@ -60,3 +65,16 @@ else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../p else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pglablib/release/pglablib.lib else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../pglablib/debug/pglablib.lib else:unix:!macx: PRE_TARGETDEPS += $$OUT_PWD/../../pglablib/libpglablib.a + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../mygtestutils/release/ -lmygtestutils +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../mygtestutils/debug/ -lmygtestutils +else:unix:!macx: LIBS += -L$$OUT_PWD/../mygtestutils/ -lmygtestutils + +INCLUDEPATH += $$PWD/../mygtestutils +DEPENDPATH += $$PWD/../mygtestutils + +win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/release/libmygtestutils.a +else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/debug/libmygtestutils.a +else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/release/mygtestutils.lib +else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/debug/mygtestutils.lib +else:unix:!macx: PRE_TARGETDEPS += $$OUT_PWD/../mygtestutils/libmygtestutils.a diff --git a/tests/pglabtests/tst_CodeBuilder.cpp b/tests/pglabtests/tst_CodeBuilder.cpp new file mode 100644 index 0000000..a9b2fba --- /dev/null +++ b/tests/pglabtests/tst_CodeBuilder.cpp @@ -0,0 +1,231 @@ +#include +#include +#include +#include "PgType.h" +#include "PgTypeContainer.h" +#include "PrintTo_Qt.h" +#include "codebuilder/IndentationConfig.h" +#include "codebuilder/TypeMappings.h" +#include "Pgsql_oids.h" + +using namespace testing; + + + +// Test to verify my understanding of the documentation +TEST(QRegularExpression, doIUnderstandItCorrectly) +{ + QString text("abcdefghi"); + QRegularExpression re("c(def)g"); + QRegularExpressionMatch match; + int pos = text.indexOf(re, 0, &match); + + ASSERT_THAT(pos, Eq(2)); + ASSERT_THAT(match.capturedStart(), Eq(2)); + ASSERT_THAT(match.capturedEnd(), Eq(7)); + ASSERT_THAT(match.capturedStart(1), Eq(3)); + ASSERT_THAT(match.capturedEnd(1), Eq(6)); +} + +TEST(QRegularExpression, doIUnderstandItCorrectly2) +{ + QString text("abc/%def%/ghi"); + QRegularExpression re("(?:[^\\\\]|^)(\\/%([a-zA-Z0-9_-]+)%\\/)"); + QRegularExpressionMatch match; + int pos = text.indexOf(re, 0, &match); + + ASSERT_THAT(pos, Eq(2)); + ASSERT_THAT(match.capturedStart(1), Eq(3)); + ASSERT_THAT(match.capturedLength(1), Eq(7)); + ASSERT_THAT(match.capturedStart(2), Eq(5)); + ASSERT_THAT(match.capturedLength(2), Eq(3)); +} + +#include + +void FormatToStream(QTextStream &stream, QString format, std::function field_callback); + +TEST(FormatToStream, EmptyFormat) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "", nullptr); + stream.flush(); + + ASSERT_THAT(result, Eq(QString())); +} + +TEST(FormatToStream, OnlyVar) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "/%var%/", + [](QTextStream &stream, QString varname) + { + if (varname == "var") + stream << "abc"; + }); + stream.flush(); + + ASSERT_EQ(result, QString("abc")); +} + +TEST(FormatToStream, StartWithVar) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "/%var%/def", + [](QTextStream &stream, QString varname) + { + if (varname == "var") + stream << "abc"; + }); + stream.flush(); + + ASSERT_EQ(result, QString("abcdef")); +} + +TEST(FormatToStream, EndWithVar) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "def/%var%/", + [](QTextStream &stream, QString varname) + { + if (varname == "var") + stream << "abc"; + }); + stream.flush(); + + ASSERT_EQ(result, QString("defabc")); +} + +TEST(FormatToStream, TwoVarsTogether) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "/%var1%//%var2%/", + [](QTextStream &stream, QString varname) + { + if (varname == "var1") + stream << "abc"; + else if (varname == "var2") + stream << "def"; + }); + stream.flush(); + + ASSERT_EQ(result, QString("abcdef")); +} + +TEST(FormatToStream, TwoVarsWithTextInBetween) +{ + QString result; + QTextStream stream(&result); + FormatToStream(stream, "1/%var1%/2/%var2%/3", + [](QTextStream &stream, QString varname) + { + if (varname == "var1") + stream << "abc"; + else if (varname == "var2") + stream << "def"; + }); + stream.flush(); + + ASSERT_EQ(result, QString("1abc2def3")); +} + + +TEST(IndentationConfig, tab8indent4true_level1) +{ + IndentationConfig icfg(8, 4, true); + QString result = icfg.getIndentString(1); + ASSERT_EQ(result, " "); +} + +TEST(IndentationConfig, tab8indent4true_level2) +{ + IndentationConfig icfg(8, 4, true); + QString result = icfg.getIndentString(2); + ASSERT_EQ(result, "\t"); +} + +TEST(IndentationConfig, tab8indent4true_level3) +{ + IndentationConfig icfg(8, 4, true); + QString result = icfg.getIndentString(3); + ASSERT_EQ(result, "\t "); +} + +TEST(IndentationConfig, tab8indent4false_level1) +{ + IndentationConfig icfg(8, 4, false); + QString result = icfg.getIndentString(1); + ASSERT_EQ(result, " "); +} + +TEST(IndentationConfig, tab8indent4false_level2) +{ + IndentationConfig icfg(8, 4, false); + QString result = icfg.getIndentString(2); + ASSERT_EQ(result, " "); +} + + +class TypeMappingsTest : public ::testing::Test { +protected: + void SetUp() override + { + def_str_type = "std::string"; + int4str = "int"; + int8str = "long long"; + tm = TypeMappings( { + { Pgsql::int4_oid, int4str }, + { Pgsql::int8_oid, int8str } + }); + tm.setDefaultStringType(def_str_type); + tm.setDefaultContainerType("std::vector<%1>"); + } + + // void TearDown() override {} + + QString def_str_type; + QString int4str; + QString int8str; + TypeMappings tm; +}; + +TEST_F(TypeMappingsTest, defStringType) +{ + QString result = tm.getTypeForOid(Pgsql::float4_oid); + ASSERT_EQ(result, def_str_type); +} + +TEST_F(TypeMappingsTest, int4Type) +{ + QString result = tm.getTypeForOid(Pgsql::int4_oid); + ASSERT_EQ(result, int4str); +} + +TEST_F(TypeMappingsTest, int4overideType) +{ + tm.set(Pgsql::int4_oid, "QString"); + QString result = tm.getTypeForOid(Pgsql::int4_oid); + ASSERT_EQ(result, "QString"); +} + +// Need catalogue for the next test +// Maybe we should mock this !? +TEST_F(TypeMappingsTest, int4arrayType) +{ + auto types= std::make_shared(); + PgType int4arr; + int4arr.oid = Pgsql::int4_array_oid; + int4arr.elem = Pgsql::int4_oid; + types->add(int4arr); + + tm.setTypes(types); + + QString result = tm.getTypeForOid(Pgsql::int4_array_oid); + ASSERT_EQ(result, "std::vector"); +} + diff --git a/tests/pglabtests/tst_CsvWriter.cpp b/tests/pglabtests/tst_CsvWriter.cpp index ac48b9d..b54d2c2 100644 --- a/tests/pglabtests/tst_CsvWriter.cpp +++ b/tests/pglabtests/tst_CsvWriter.cpp @@ -3,6 +3,7 @@ #include "CsvWriter.h" #include #include +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_ExplainJsonParser.cpp b/tests/pglabtests/tst_ExplainJsonParser.cpp index e5074eb..7da88b1 100644 --- a/tests/pglabtests/tst_ExplainJsonParser.cpp +++ b/tests/pglabtests/tst_ExplainJsonParser.cpp @@ -2,6 +2,7 @@ #include #include "ExplainTreeModelItem.h" #include "json/json.h" +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_NameManglingRules.cpp b/tests/pglabtests/tst_NameManglingRules.cpp new file mode 100644 index 0000000..2163a09 --- /dev/null +++ b/tests/pglabtests/tst_NameManglingRules.cpp @@ -0,0 +1,54 @@ +#include +#include +#include "PrintTo_Qt.h" +#include "codebuilder/NameManglingRules.h" +#include "Pgsql_oids.h" + +using namespace testing; + +class NameManglingRulesTest : public ::testing::Test, public ::testing::WithParamInterface< + std::tuple > { +protected: + + virtual void SetUp() override + { + NameManglingRules r; + + auto p = GetParam(); + //ReplaceRules replaceRules; + // { {"[ -_]", QRegularExpression::OptimizeOnFirstUsageOption }, "", true } + r.replaceRules; + + //QString replaceSpaceWith; ///< default is empty string which means remove spaces + r.replaceSpaceWith = std::get<0>(p); + + //CollisionHandling CollisionHandling = CollisionHandling::Restrict; + //CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule + r.caseConversion = std::get<1>(p); + //CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char + r.camelCase = std::get<2>(p); ///< removes underscores and make first char after underscore uppercase + rules = r; + } + NameManglingRules rules; +}; + + +TEST_P(NameManglingRulesTest, test1) +{ + ASSERT_EQ(rules.replaceSpaceWith, std::get<0>(GetParam())); +} + +TEST_P(NameManglingRulesTest, test2) +{ + ASSERT_EQ(rules.camelCase, std::get<2>(GetParam())); +} + +INSTANTIATE_TEST_CASE_P(InstantiationName1, + NameManglingRulesTest, + ::testing::Combine( + ::testing::Values("", "_", "+"), + ::testing::Values(NameManglingRules::CaseConversion::AsIs, + NameManglingRules::CaseConversion::Lower, + NameManglingRules::CaseConversion::Upper), + ::testing::Bool() + )); diff --git a/tests/pglabtests/tst_ParamJson.cpp b/tests/pglabtests/tst_ParamJson.cpp index b660670..4ba4783 100644 --- a/tests/pglabtests/tst_ParamJson.cpp +++ b/tests/pglabtests/tst_ParamJson.cpp @@ -2,6 +2,7 @@ #include #include "ParamListModel.h" #include "ParamListJson.h" +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_PasswordManager.cpp b/tests/pglabtests/tst_PasswordManager.cpp index f7ca8a2..566dbf0 100644 --- a/tests/pglabtests/tst_PasswordManager.cpp +++ b/tests/pglabtests/tst_PasswordManager.cpp @@ -1,6 +1,7 @@ #include #include #include "PasswordManager.h" +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_SqlLexer.cpp b/tests/pglabtests/tst_SqlLexer.cpp index bf7149a..d466d62 100644 --- a/tests/pglabtests/tst_SqlLexer.cpp +++ b/tests/pglabtests/tst_SqlLexer.cpp @@ -1,6 +1,7 @@ #include #include #include "SqlLexer.h" +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_expected.cpp b/tests/pglabtests/tst_expected.cpp index 8632ff8..1b90e60 100644 --- a/tests/pglabtests/tst_expected.cpp +++ b/tests/pglabtests/tst_expected.cpp @@ -1,6 +1,7 @@ #include #include #include "Expected.h" +#include "PrintTo_Qt.h" using namespace testing; diff --git a/tests/pglabtests/tst_scopeguard.cpp b/tests/pglabtests/tst_scopeguard.cpp index 2b14280..cab3621 100644 --- a/tests/pglabtests/tst_scopeguard.cpp +++ b/tests/pglabtests/tst_scopeguard.cpp @@ -1,6 +1,7 @@ #include #include #include "ScopeGuard.h" +#include "PrintTo_Qt.h" using namespace testing; -- 2.47.2 From 22bad8fb2224f054ab25f5967d8c22cddcfd3de4 Mon Sep 17 00:00:00 2001 From: eelke Date: Mon, 17 Sep 2018 15:41:47 +0200 Subject: [PATCH 03/11] Added name mangling tests. --- pglablib/codebuilder/NameManglingRules.h | 3 +- tests/pglabtests/tst_NameManglingRules.cpp | 79 +++++++++++++++------- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/pglablib/codebuilder/NameManglingRules.h b/pglablib/codebuilder/NameManglingRules.h index 5857d79..610c64b 100644 --- a/pglablib/codebuilder/NameManglingRules.h +++ b/pglablib/codebuilder/NameManglingRules.h @@ -34,11 +34,10 @@ public: ReplaceRules replaceRules; // { {"[ -_]", QRegularExpression::OptimizeOnFirstUsageOption }, "", true } - QString replaceSpaceWith; ///< default is empty string which means remove spaces //CollisionHandling CollisionHandling = CollisionHandling::Restrict; CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule //CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char - bool camelCase = false; ///< removes underscores and make first char after underscore uppercase +// bool camelCase = false; ///< removes underscores and make first char after underscore uppercase void apply(const ReplaceRule &rule, QString &in) const; diff --git a/tests/pglabtests/tst_NameManglingRules.cpp b/tests/pglabtests/tst_NameManglingRules.cpp index 2163a09..f09ee82 100644 --- a/tests/pglabtests/tst_NameManglingRules.cpp +++ b/tests/pglabtests/tst_NameManglingRules.cpp @@ -14,41 +14,68 @@ protected: { NameManglingRules r; - auto p = GetParam(); - //ReplaceRules replaceRules; - // { {"[ -_]", QRegularExpression::OptimizeOnFirstUsageOption }, "", true } - r.replaceRules; +// auto p = GetParam(); +// //ReplaceRules replaceRules; +// // { {"[ -_]", QRegularExpression::OptimizeOnFirstUsageOption }, "", true } +// // r.replaceRules; - //QString replaceSpaceWith; ///< default is empty string which means remove spaces - r.replaceSpaceWith = std::get<0>(p); - - //CollisionHandling CollisionHandling = CollisionHandling::Restrict; - //CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule - r.caseConversion = std::get<1>(p); - //CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char - r.camelCase = std::get<2>(p); ///< removes underscores and make first char after underscore uppercase - rules = r; +// //CaseConversion caseConversion = CaseConversion::AsIs; ///< overall case conversion rule +// r.caseConversion = std::get<1>(p); +// //CaseConversion caseFirstChar = CaseConversion::AsIs; ///< case of the first char +// //r.camelCase = std::get<2>(p); ///< removes underscores and make first char after underscore uppercase +// rules = r; } NameManglingRules rules; }; +// First a few test to test some basic things -TEST_P(NameManglingRulesTest, test1) + +TEST_F(NameManglingRulesTest, caseConversionAsIs) { - ASSERT_EQ(rules.replaceSpaceWith, std::get<0>(GetParam())); + QString input = "Ab"; + QString expected = input; + + NameManglingRules rules; + rules.caseConversion = NameManglingRules::CaseConversion::AsIs; + QString result = rules.transform(input); + + ASSERT_EQ(result, expected); } -TEST_P(NameManglingRulesTest, test2) +TEST_F(NameManglingRulesTest, caseConversionLower) { - ASSERT_EQ(rules.camelCase, std::get<2>(GetParam())); + QString input = "Ab"; + QString expected = "ab"; + + NameManglingRules rules; + rules.caseConversion = NameManglingRules::CaseConversion::Lower; + QString result = rules.transform(input); + + ASSERT_EQ(result, expected); +} + +TEST_F(NameManglingRulesTest, caseConversionUpper) +{ + QString input = "Ab"; + QString expected = "AB"; + + NameManglingRules rules; + rules.caseConversion = NameManglingRules::CaseConversion::Upper; + QString result = rules.transform(input); + + ASSERT_EQ(result, expected); +} + +TEST_F(NameManglingRulesTest, replace) +{ + QString input = "abc-def ghi"; + QString expected = "abc_Def_Ghi"; + + NameManglingRules rules; + rules.replaceRules.push_back({QRegularExpression("[- ]"), "_", true}); + QString result = rules.transform(input); + + ASSERT_EQ(result, expected); } -INSTANTIATE_TEST_CASE_P(InstantiationName1, - NameManglingRulesTest, - ::testing::Combine( - ::testing::Values("", "_", "+"), - ::testing::Values(NameManglingRules::CaseConversion::AsIs, - NameManglingRules::CaseConversion::Lower, - NameManglingRules::CaseConversion::Upper), - ::testing::Bool() - )); -- 2.47.2 From c13bbde2e48dc2a3137b6f50281d054c95bacdeb Mon Sep 17 00:00:00 2001 From: eelke Date: Mon, 17 Sep 2018 15:42:55 +0200 Subject: [PATCH 04/11] Put the FormatToStream function in its own compilation unit as it might be useful for other things to. --- pglablib/FormatToStream.cpp | 29 ++++++++++++++++++++++++++++ pglablib/FormatToStream.h | 17 ++++++++++++++++ pglablib/codebuilder/CodeBuilder.cpp | 26 ------------------------- pglablib/pglablib.pro | 6 ++++-- 4 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 pglablib/FormatToStream.cpp create mode 100644 pglablib/FormatToStream.h diff --git a/pglablib/FormatToStream.cpp b/pglablib/FormatToStream.cpp new file mode 100644 index 0000000..f8615bc --- /dev/null +++ b/pglablib/FormatToStream.cpp @@ -0,0 +1,29 @@ +#include "FormatToStream.h" +#include +#include + +void FormatToStream(QTextStream &stream, QString format, std::function field_callback) +{ + // Use static to optimize only once + static QRegularExpression cached_find_var_re("(?:[^\\\\]|^)(\\/%([a-zA-Z0-9_-]+)%\\/)", QRegularExpression::OptimizeOnFirstUsageOption); + + int from = 0; + QRegularExpressionMatch match; + while (format.indexOf(cached_find_var_re, from, &match) >= 0) { + if (from > 0) { + // Because the regex has to check for backslash in front we have the from position + // one position before where we actually should continue for the second match and later ie when from > 0 + // Therefor increase from by 1 to make the substring (midRef) calculation work + ++from; + } + // copy code before the var to the stream + stream << format.midRef(from, match.capturedStart(1) - from); + field_callback(stream, match.captured(2)); + from = match.capturedEnd()-1; // -1 because it wants to match one character before or start of line to make sure there is no backslash + } + if (from > 0) { + // same reason as at the start of the loop + ++from; + } + stream << format.midRef(from); +} diff --git a/pglablib/FormatToStream.h b/pglablib/FormatToStream.h new file mode 100644 index 0000000..aabc1f3 --- /dev/null +++ b/pglablib/FormatToStream.h @@ -0,0 +1,17 @@ +#ifndef FORMATTOSTREAM_H +#define FORMATTOSTREAM_H + +#include +#include + +class QTextStream; + +/** + * @brief FormatToStream replaces /%var%/ variables in a string with the values returned by the callback + * @param stream The stream to which to write the result of the formatting operation + * @param format The format string containing the vars. + * @param field_callback A callable which returns the values for var. + */ +void FormatToStream(QTextStream &stream, QString format, std::function field_callback); + +#endif // FORMATTOSTREAM_H diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp index baed498..9671311 100644 --- a/pglablib/codebuilder/CodeBuilder.cpp +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -6,32 +6,6 @@ #include "util.h" #include -void FormatToStream(QTextStream &stream, QString format, std::function field_callback) -{ - // Use static to optimize only once - static QRegularExpression cached_find_var_re("(?:[^\\\\]|^)(\\/%([a-zA-Z0-9_-]+)%\\/)", QRegularExpression::OptimizeOnFirstUsageOption); - - int from = 0; - QRegularExpressionMatch match; - while (format.indexOf(cached_find_var_re, from, &match) >= 0) { - if (from > 0) { - // Because the regex has to check for backslash in front we have the from position - // one position before where we actually should continue for the second match and later ie when from > 0 - // Therefor increase from by 1 to make the substring (midRef) calculation work - ++from; - } - // copy code before the var to the stream - stream << format.midRef(from, match.capturedStart(1) - from); - field_callback(stream, match.captured(2)); - from = match.capturedEnd()-1; // -1 because it wants to match one character before or start of line to make sure there is no backslash - } - if (from > 0) { - // same reason as at the start of the loop - ++from; - } - stream << format.midRef(from); -} - void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result) { // %1 query string diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 29b5c40..4441836 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -64,7 +64,8 @@ codebuilder/NameManglingRules.cpp \ codebuilder/DefaultConfigs.cpp \ codebuilder/TypeMappings.cpp \ codebuilder/IndentationConfig.cpp \ -codebuilder/StructureTemplate.cpp +codebuilder/StructureTemplate.cpp \ + FormatToStream.cpp HEADERS += \ Pglablib.h \ @@ -106,7 +107,8 @@ codebuilder/NameManglingRules.h \ codebuilder/DefaultConfigs.h \ codebuilder/TypeMappings.h \ codebuilder/IndentationConfig.h \ -codebuilder/StructureTemplate.h +codebuilder/StructureTemplate.h \ + FormatToStream.h unix { target.path = /usr/lib -- 2.47.2 From daf9536bed2aa05e31e944dd98ecd7125bf60f77 Mon Sep 17 00:00:00 2001 From: eelke Date: Tue, 18 Sep 2018 07:45:49 +0200 Subject: [PATCH 05/11] Moved typemapping test code to its own unit so it is clearer that the tests exist --- tests/pglabtests/tst_CodeBuilder.cpp | 59 ---------------------- tests/pglabtests/tst_TypeMappings.cpp | 71 +++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 59 deletions(-) create mode 100644 tests/pglabtests/tst_TypeMappings.cpp diff --git a/tests/pglabtests/tst_CodeBuilder.cpp b/tests/pglabtests/tst_CodeBuilder.cpp index a9b2fba..206d1fe 100644 --- a/tests/pglabtests/tst_CodeBuilder.cpp +++ b/tests/pglabtests/tst_CodeBuilder.cpp @@ -170,62 +170,3 @@ TEST(IndentationConfig, tab8indent4false_level2) ASSERT_EQ(result, " "); } - -class TypeMappingsTest : public ::testing::Test { -protected: - void SetUp() override - { - def_str_type = "std::string"; - int4str = "int"; - int8str = "long long"; - tm = TypeMappings( { - { Pgsql::int4_oid, int4str }, - { Pgsql::int8_oid, int8str } - }); - tm.setDefaultStringType(def_str_type); - tm.setDefaultContainerType("std::vector<%1>"); - } - - // void TearDown() override {} - - QString def_str_type; - QString int4str; - QString int8str; - TypeMappings tm; -}; - -TEST_F(TypeMappingsTest, defStringType) -{ - QString result = tm.getTypeForOid(Pgsql::float4_oid); - ASSERT_EQ(result, def_str_type); -} - -TEST_F(TypeMappingsTest, int4Type) -{ - QString result = tm.getTypeForOid(Pgsql::int4_oid); - ASSERT_EQ(result, int4str); -} - -TEST_F(TypeMappingsTest, int4overideType) -{ - tm.set(Pgsql::int4_oid, "QString"); - QString result = tm.getTypeForOid(Pgsql::int4_oid); - ASSERT_EQ(result, "QString"); -} - -// Need catalogue for the next test -// Maybe we should mock this !? -TEST_F(TypeMappingsTest, int4arrayType) -{ - auto types= std::make_shared(); - PgType int4arr; - int4arr.oid = Pgsql::int4_array_oid; - int4arr.elem = Pgsql::int4_oid; - types->add(int4arr); - - tm.setTypes(types); - - QString result = tm.getTypeForOid(Pgsql::int4_array_oid); - ASSERT_EQ(result, "std::vector"); -} - diff --git a/tests/pglabtests/tst_TypeMappings.cpp b/tests/pglabtests/tst_TypeMappings.cpp new file mode 100644 index 0000000..505778f --- /dev/null +++ b/tests/pglabtests/tst_TypeMappings.cpp @@ -0,0 +1,71 @@ +#include +#include +//#include +#include "PgType.h" +#include "PgTypeContainer.h" +//#include "PrintTo_Qt.h" +//#include "codebuilder/IndentationConfig.h" +#include "codebuilder/TypeMappings.h" +#include "Pgsql_oids.h" + +using namespace testing; + + +class TypeMappingsTest : public ::testing::Test { +protected: + void SetUp() override + { + def_str_type = "std::string"; + int4str = "int"; + int8str = "long long"; + tm = TypeMappings( { + { Pgsql::int4_oid, int4str }, + { Pgsql::int8_oid, int8str } + }); + tm.setDefaultStringType(def_str_type); + tm.setDefaultContainerType("std::vector<%1>"); + } + + // void TearDown() override {} + + QString def_str_type; + QString int4str; + QString int8str; + TypeMappings tm; +}; + +TEST_F(TypeMappingsTest, defStringType) +{ + QString result = tm.getTypeForOid(Pgsql::float4_oid); + ASSERT_EQ(result, def_str_type); +} + +TEST_F(TypeMappingsTest, int4Type) +{ + QString result = tm.getTypeForOid(Pgsql::int4_oid); + ASSERT_EQ(result, int4str); +} + +TEST_F(TypeMappingsTest, int4overideType) +{ + tm.set(Pgsql::int4_oid, "QString"); + QString result = tm.getTypeForOid(Pgsql::int4_oid); + ASSERT_EQ(result, "QString"); +} + +// Need catalogue for the next test +// Maybe we should mock this !? +TEST_F(TypeMappingsTest, int4arrayType) +{ + auto types= std::make_shared(); + PgType int4arr; + int4arr.oid = Pgsql::int4_array_oid; + int4arr.elem = Pgsql::int4_oid; + types->add(int4arr); + + tm.setTypes(types); + + QString result = tm.getTypeForOid(Pgsql::int4_array_oid); + ASSERT_EQ(result, "std::vector"); +} + -- 2.47.2 From f5145f36ed4aa120fe7b05cacb6b2d0b9da09f6f Mon Sep 17 00:00:00 2001 From: eelke Date: Tue, 18 Sep 2018 11:53:19 +0200 Subject: [PATCH 06/11] wip: codegenerator, basic widget present for showing the generated code and specifying parameters. Some code is also generated but it is not complete yet. minimum still required - field assignments - properly format and escape the query string --- pglab/CodeEditor.h | 3 + pglab/CodeGenerator.cpp | 51 ++++++++++++++++ pglab/CodeGenerator.h | 33 ++++++++++ pglab/CodeGenerator.ui | 80 +++++++++++++++++++++++++ pglab/MainWindow.cpp | 18 ++++++ pglab/MainWindow.h | 2 + pglab/MainWindow.ui | 8 ++- pglab/QueryResultModel.h | 1 + pglab/QueryTab.cpp | 25 ++++++-- pglab/QueryTab.h | 1 + pglab/UserConfiguration.cpp | 25 ++++++++ pglab/UserConfiguration.h | 24 ++++++++ pglab/pglab.pro | 11 +++- pglab/tuplesresultwidget.h | 2 + pglablib/codebuilder/CodeBuilder.cpp | 63 ++++++++++++++----- pglablib/codebuilder/CodeBuilder.h | 21 ++++++- pglablib/codebuilder/DefaultConfigs.cpp | 26 ++++++-- pglablib/codebuilder/LanguageConfig.h | 15 +++++ tests/pglabtests/pglabtests.pro | 3 +- 19 files changed, 380 insertions(+), 32 deletions(-) create mode 100644 pglab/CodeGenerator.cpp create mode 100644 pglab/CodeGenerator.h create mode 100644 pglab/CodeGenerator.ui create mode 100644 pglab/UserConfiguration.cpp create mode 100644 pglab/UserConfiguration.h diff --git a/pglab/CodeEditor.h b/pglab/CodeEditor.h index 62b97bb..ec6910a 100644 --- a/pglab/CodeEditor.h +++ b/pglab/CodeEditor.h @@ -4,6 +4,9 @@ #include #include +/** This class adds some capabilities to QPlainTextEdit that are useful + * in code editor scenarios. + */ class CodeEditor : public QPlainTextEdit { Q_OBJECT diff --git a/pglab/CodeGenerator.cpp b/pglab/CodeGenerator.cpp new file mode 100644 index 0000000..92c4c2f --- /dev/null +++ b/pglab/CodeGenerator.cpp @@ -0,0 +1,51 @@ +#include "CodeGenerator.h" +#include "ui_CodeGenerator.h" +#include "codebuilder/CodeBuilder.h" +#include "codebuilder/DefaultConfigs.h" +#include "UserConfiguration.h" +#include + +CodeGenerator::CodeGenerator(QWidget *parent) : + PlgPage(parent), + ui(new Ui::CodeGenerator) +{ + ui->setupUi(this); + + ui->generatedCodeEditor->setFont(UserConfiguration::instance()->codeFont()); + ui->generatedCodeEditor->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); +} + +CodeGenerator::~CodeGenerator() +{ + delete ui; +} + +void CodeGenerator::Init(QString query, std::shared_ptr dbres) +{ + m_query = query; + m_dbres = dbres; + generateCode(); + + +} + + +void CodeGenerator::on_updateCodeButton_clicked() +{ + generateCode(); +} + +void CodeGenerator::generateCode() +{ + QString struct_name = ui->structNameEdit->text(); + + QString buffer; + QTextStream stream(&buffer); + CodeBuilder builder; + builder.setLanguageConfig(getDefaultCppLanguageConfig()); + builder.GenCodeForExecutingQuery(stream, m_query, *m_dbres, struct_name); + stream.flush(); + ui->generatedCodeEditor->setPlainText(buffer); +// QApplication::clipboard()->setText(buffer); + +} diff --git a/pglab/CodeGenerator.h b/pglab/CodeGenerator.h new file mode 100644 index 0000000..c2dfac9 --- /dev/null +++ b/pglab/CodeGenerator.h @@ -0,0 +1,33 @@ +#ifndef CODEGENERATOR_H +#define CODEGENERATOR_H + +#include +#include "PlgPage.h" +#include "Pgsql_declare.h" + +namespace Ui { +class CodeGenerator; +} + +class CodeGenerator : public PlgPage +{ + Q_OBJECT + +public: + explicit CodeGenerator(QWidget *parent = nullptr); + ~CodeGenerator(); + + void Init(QString query, std::shared_ptr dbres); +private slots: + void on_updateCodeButton_clicked(); + +private: + Ui::CodeGenerator *ui; + + QString m_query; + std::shared_ptr m_dbres; + + void generateCode(); +}; + +#endif // CODEGENERATOR_H diff --git a/pglab/CodeGenerator.ui b/pglab/CodeGenerator.ui new file mode 100644 index 0000000..8d91f1b --- /dev/null +++ b/pglab/CodeGenerator.ui @@ -0,0 +1,80 @@ + + + CodeGenerator + + + + 0 + 0 + 1127 + 762 + + + + Form + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + Struct name: + + + + + + + + + + Update code + + + + + + + Configuration + + + + + + + + + + + true + + + false + + + + + + + + + CodeEditor + QPlainTextEdit +
CodeEditor.h
+
+
+ + +
diff --git a/pglab/MainWindow.cpp b/pglab/MainWindow.cpp index 256ab72..064649f 100644 --- a/pglab/MainWindow.cpp +++ b/pglab/MainWindow.cpp @@ -14,6 +14,7 @@ #include "QueryTab.h" #include "util.h" #include "PlgPage.h" +#include "CodeGenerator.h" #include "MasterController.h" #include "CrudTab.h" #include "WorkManager.h" @@ -58,6 +59,14 @@ void MainWindow::newCrudPage(const PgClass &table) addPage(ct, table.name); } +void MainWindow::newCodeGenPage(QString query, std::shared_ptr dbres) +{ + auto cgtab = new CodeGenerator(this); + cgtab->Init(query, dbres); + addPage(cgtab, "Codegen"); +} + + QueryTab *MainWindow::GetActiveQueryTab() { QWidget *widget = ui->tabWidget->currentWidget(); @@ -344,3 +353,12 @@ void MainWindow::on_tabWidget_currentChanged(int index) } m_previousPage = page; } + +void MainWindow::on_actionGenerate_code_triggered() +{ + QueryTab *tab = GetActiveQueryTab(); + if (tab) { + tab->generateCode(); + } + +} diff --git a/pglab/MainWindow.h b/pglab/MainWindow.h index 8fd7cc6..4fcfa60 100644 --- a/pglab/MainWindow.h +++ b/pglab/MainWindow.h @@ -46,6 +46,7 @@ public: std::shared_ptr getDatabase() { return m_database; } void newCrudPage(const PgClass &table); + void newCodeGenPage(QString query, std::shared_ptr dbres); private: Ui::MainWindow *ui; @@ -116,6 +117,7 @@ private slots: void on_actionCopy_as_C_string_triggered(); void on_actionCopy_as_raw_Cpp_string_triggered(); void on_tabWidget_currentChanged(int index); + void on_actionGenerate_code_triggered(); }; #endif // MAINWINDOW_H diff --git a/pglab/MainWindow.ui b/pglab/MainWindow.ui index 7599ceb..98fe391 100644 --- a/pglab/MainWindow.ui +++ b/pglab/MainWindow.ui @@ -45,7 +45,7 @@ 0 0 993 - 20 + 25 @@ -86,6 +86,7 @@ + @@ -233,6 +234,11 @@ Copy as raw C++-string + + + Generate code + + diff --git a/pglab/QueryResultModel.h b/pglab/QueryResultModel.h index 111ab7a..68169c8 100644 --- a/pglab/QueryResultModel.h +++ b/pglab/QueryResultModel.h @@ -19,6 +19,7 @@ public: virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + std::shared_ptr GetPgsqlResult() const { return result; } protected: // virtual Oid getType(int column) const override; // virtual QVariant getData(const QModelIndex &index) const override; diff --git a/pglab/QueryTab.cpp b/pglab/QueryTab.cpp index f55fb35..69f98d1 100644 --- a/pglab/QueryTab.cpp +++ b/pglab/QueryTab.cpp @@ -22,6 +22,7 @@ #include "QueryParamListController.h" #include "util.h" #include "GlobalIoService.h" +#include "UserConfiguration.h" QueryTab::QueryTab(MainWindow *win, QWidget *parent) : PlgPage(parent), @@ -34,11 +35,7 @@ QueryTab::QueryTab(MainWindow *win, QWidget *parent) : connect(&m_dbConnection, &ASyncDBConnection::onStateChanged, this, &QueryTab::connectionStateChanged); connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTab::receiveNotice); - QFont font; - font.setFamily("Source Code Pro"); - font.setFixedPitch(true); - font.setPointSize(10); - ui->queryEdit->setFont(font); + ui->queryEdit->setFont(UserConfiguration::instance()->codeFont()); highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document()); auto open_database = m_win->getDatabase(); @@ -583,14 +580,30 @@ void QueryTab::copyQueryAsCString() QApplication::clipboard()->setText(cs); } +#include +#include + void QueryTab::copyQueryAsRawCppString() { - //auto sql = getAllOrSelectedSql(); QString command = getCommand(); + //auto sql = getAllOrSelectedSql(); QString cs = ConvertToMultiLineRawCppString(command); QApplication::clipboard()->setText(cs); } +void QueryTab::generateCode() +{ + QString command = getCommand(); + + if (resultList.empty()) { + QMessageBox::question(this, "pglab", tr("Please execute the query first"), QMessageBox::Ok); + } + if (resultList.size() == 1) { + std::shared_ptr dbres = resultList[0]->GetPgsqlResult(); + m_win->newCodeGenPage(command, dbres); + } +} + void QueryTab::exportData(const QString &file_name) { auto widget = ui->tabWidget->currentWidget(); diff --git a/pglab/QueryTab.h b/pglab/QueryTab.h index 0d86c0b..d31faed 100644 --- a/pglab/QueryTab.h +++ b/pglab/QueryTab.h @@ -55,6 +55,7 @@ public: void copyQueryAsCString(); void copyQueryAsRawCppString(); + void generateCode(); void exportData(const QString &filename); QString fileName() const { return m_fileName; } diff --git a/pglab/UserConfiguration.cpp b/pglab/UserConfiguration.cpp new file mode 100644 index 0000000..3b0ef95 --- /dev/null +++ b/pglab/UserConfiguration.cpp @@ -0,0 +1,25 @@ +#include "UserConfiguration.h" +#include + +UserConfiguration* UserConfiguration::instance() +{ + static UserConfiguration config; // in C++ 11 static ensures thread safe initialization at first call of function + return &config; +} + +UserConfiguration::UserConfiguration() + : m_settings(nullptr) +{ + m_settings.setIniCodec("UTF-8"); +} + +QFont UserConfiguration::codeFont() const +{ + QString family = m_settings.value("fonts/code/family", QString("Source Code Pro")).toString(); + int size = m_settings.value("fonts/code/size", 10).toInt(); + QFont font; + font.setFamily(family); + font.setFixedPitch(true); + font.setPointSize(size); + return font; +} diff --git a/pglab/UserConfiguration.h b/pglab/UserConfiguration.h new file mode 100644 index 0000000..897f1ce --- /dev/null +++ b/pglab/UserConfiguration.h @@ -0,0 +1,24 @@ +#ifndef USERCONFIGURATION_H +#define USERCONFIGURATION_H + +#include +/** Class for most of the program configuration + * + * The settings represented by this class are stored in a settings file + * that is user specific. Depending on OS setup it might be shared with + * other machines in the network because for instance on windows it is + * part of the roaming profile. + */ +class UserConfiguration +{ +public: + static UserConfiguration* instance(); + + UserConfiguration(); + + QFont codeFont() const; +private: + QSettings m_settings; +}; + +#endif // USERCONFIGURATION_H diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 8aaaa8a..18b7a69 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -73,7 +73,9 @@ SOURCES += main.cpp\ EditorGutter.cpp \ CodeEditor.cpp \ PlgPage.cpp \ -PropertyProxyModel.cpp +PropertyProxyModel.cpp \ + CodeGenerator.cpp \ + UserConfiguration.cpp HEADERS += \ QueryResultModel.h \ @@ -121,7 +123,9 @@ HEADERS += \ PlgPage.h \ AbstractCommand.h \ PropertyProxyModel.h \ -CustomDataRole.h +CustomDataRole.h \ + CodeGenerator.h \ + UserConfiguration.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ @@ -134,7 +138,8 @@ FORMS += mainwindow.ui \ TablesPage.ui \ NamespaceFilterWidget.ui \ ApplicationWindow.ui \ - CrudTab.ui + CrudTab.ui \ + CodeGenerator.ui RESOURCES += \ resources.qrc diff --git a/pglab/tuplesresultwidget.h b/pglab/tuplesresultwidget.h index 7511404..b2605ba 100644 --- a/pglab/tuplesresultwidget.h +++ b/pglab/tuplesresultwidget.h @@ -18,6 +18,8 @@ public: void setResult(std::shared_ptr res, float ms); void exportData(const QString &file_name) const; + + std::shared_ptr GetPgsqlResult() const { return resultModel->GetPgsqlResult(); } private: Ui::TuplesResultWidget *ui; diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp index 9671311..dd782c0 100644 --- a/pglablib/codebuilder/CodeBuilder.cpp +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -1,4 +1,5 @@ #include "CodeBuilder.h" +#include "FormatToStream.h" #include "Pgsql_Result.h" #include "IndentationConfig.h" #include "LanguageConfig.h" @@ -6,31 +7,41 @@ #include "util.h" #include -void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result) +void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname) { // %1 query string // %2 struct_name // %3 list of field assignments QString exec_query_template = "Pgsql::Result result = conn.query(%1);"; - QString loop_start_template = - "Pgsql::Result result = conn.query(/%query_literal%/);" - "for (auto row: result) {\n" - " Pgsql::Col col(row);\n" - " /%struct_type%/ v;\n" - "/%assign_fields%/" - "}"; + QString loop_start_template = R"__(Pgsql::Result result = conn.query(/%queryliteral%/); +for (auto row: result) { + Pgsql::Col col(row); + /%structname%/ v; + /%assignfields%/ +})__"; // %field_name% // %column_name% // %column_index% - QString assign_result_field_template = "col >> v./%field_name%/"; + QString assign_result_field_template = "col >> v./%fieldname%/"; QString query_string_literal = ConvertToMultiLineCString(query); + ColumnDataList columnData = createColumnDataList(result); + + auto get_var_func = [&] (QTextStream &out, QString var) { + if (var == "structname") out << structname; + //else if (var == "assign_fields") GenFieldAssignments(out); + else if (var == "queryliteral") out << query; + + }; + + // [optional] Gen declaration of result structure - GenReturnStructDefinition(q, result); + GenReturnStructDefinition(q, result, structname); // assume we have connection? What name??? + FormatToStream(q, loop_start_template, get_var_func); // gen code for creating and executing statement // - bind parameters @@ -40,16 +51,20 @@ void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, } -void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result) const +void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const { std::shared_ptr templ = m_configuration->structureTemplate(); - QString struct_name = "TODO"; QString field_format = templ->m_fieldTemplate; int field_indent_levels = templ->m_fieldIndentation; QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); + + auto struct_callback = [&] (QTextStream &out, QString var) { + if (var == "structname") out << structname; + }; // Make struct start - q << QString(templ->m_startTemplate).arg(struct_name); + //q << QString(templ->m_startTemplate).arg(struct_name); + FormatToStream(q, templ->m_startTemplate, struct_callback); // Process fields for (int column = 0; column < result.cols(); ++column) { QString res_col_name = result.getColName(column); @@ -71,7 +86,9 @@ void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } // Finish struct - q << QString(templ->m_endTemplate).arg(struct_name); + + //q << QString(templ->m_endTemplate).arg(struct_name); + FormatToStream(q, templ->m_endTemplate, struct_callback); } QString CodeBuilder::columnNameToVariableName(QString column_name) const @@ -88,6 +105,22 @@ void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, con { QString field_name = columnNameToVariableName(column_name); QString type_name = getTypeName(column_type); - q << QString(format).arg(field_name).arg(type_name); + //q << QString(format).arg(field_name).arg(type_name); + FormatToStream(q, format, [&] (QTextStream &out, QString var) { + if (var == "typename") out << type_name; + else if (var == "varname") out << field_name; + }); } +CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) +{ + ColumnDataList list; + list.reserve(static_cast(result.cols())); + for (int column = 0; column < result.cols(); ++column) { + Oid type_oid = result.type(column); + QString column_name = result.getColName(column); + QString field_name = columnNameToVariableName(column_name); + list.push_back({type_oid, column_name, field_name}); + } + return list; +} diff --git a/pglablib/codebuilder/CodeBuilder.h b/pglablib/codebuilder/CodeBuilder.h index c52e070..aad070d 100644 --- a/pglablib/codebuilder/CodeBuilder.h +++ b/pglablib/codebuilder/CodeBuilder.h @@ -3,14 +3,19 @@ #include "Pgsql_declare.h" #include +#include class LanguageConfig; class QTextStream; class CodeBuilder { public: - void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result); - void GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result) const; + void setLanguageConfig(std::shared_ptr config){ + m_configuration = config; + } + + void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname); + void GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const; // Generating code for performing query and going through the result // - Code for executing the query @@ -20,9 +25,21 @@ public: QString columnNameToVariableName(QString column_name) const; QString getTypeName(Oid dbtype) const; private: + class ColumnData { + public: + Oid oid; + QString columnName; + QString varName; ///< either field of the struct or in loop local var name + }; + using ColumnDataList = std::vector; + std::shared_ptr m_configuration; + + void GenFieldAssignments(QTextStream &q, const Pgsql::Result &result); void genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const; + + ColumnDataList createColumnDataList(const Pgsql::Result &result); }; #endif // CODEBUILDER_H diff --git a/pglablib/codebuilder/DefaultConfigs.cpp b/pglablib/codebuilder/DefaultConfigs.cpp index ca3554d..d9f31c3 100644 --- a/pglablib/codebuilder/DefaultConfigs.cpp +++ b/pglablib/codebuilder/DefaultConfigs.cpp @@ -1,13 +1,17 @@ #include "DefaultConfigs.h" +#include "IndentationConfig.h" #include "LanguageConfig.h" +#include "NameManglingRules.h" +#include "StructureTemplate.h" #include "TypeMappings.h" #include "Pgsql_oids.h" using namespace Pgsql; -TypeMappings GetDefaultCppTypeMappings() +std::shared_ptr GetDefaultCppTypeMappings() { - TypeMappings result = { + auto tm = std::make_shared(); + *tm = { { bool_oid, "bool" }, { char_oid, "char" }, { name_oid, "std::string" }, @@ -19,13 +23,27 @@ TypeMappings GetDefaultCppTypeMappings() { float4_oid, "float" }, { float8_oid, "double" } }; - return result; + tm->setDefaultStringType("std::string"); + tm->setDefaultContainerType("std::vector<%1>"); + return tm; } std::shared_ptr buildDefaultCppLanguageConfig() { - return std::make_shared(); + auto config = std::make_shared(); + config->setTypeMappings(GetDefaultCppTypeMappings()); + config->setNameManglingRules(std::make_shared()); + + auto st_templ = std::make_shared(); + st_templ->m_startTemplate = "class /%structname%/ {\npublic:\n"; + st_templ->m_endTemplate = "};\n"; // }; + st_templ->m_fieldTemplate = "/%typename%/ /%varname%/;"; + //st_templ->m_fieldSeparator; + config->setStructureTemplate(st_templ); + config->setIndentationConfig(std::make_shared()); + + return config; } std::shared_ptr getDefaultCppLanguageConfig() diff --git a/pglablib/codebuilder/LanguageConfig.h b/pglablib/codebuilder/LanguageConfig.h index a850b5c..e583a28 100644 --- a/pglablib/codebuilder/LanguageConfig.h +++ b/pglablib/codebuilder/LanguageConfig.h @@ -18,6 +18,11 @@ public: QString columnNameToFieldName(const QString& column_name) const; QString getTypeName(Oid dbtype) const; + void setNameManglingRules(std::shared_ptr name_mangling_rules) + { + m_varNaming = name_mangling_rules; + } + std::shared_ptr typeMappings() const { return m_typeMappings; } void setTypeMappings(std::shared_ptr type_mappings) { @@ -29,10 +34,20 @@ public: return m_structureTemplate; } + void setStructureTemplate(std::shared_ptr structure_template) + { + m_structureTemplate = structure_template; + } + std::shared_ptr indentationConfig() const { return m_indentationConfig; } + + void setIndentationConfig(std::shared_ptr indentation_config) + { + m_indentationConfig = indentation_config; + } private: /** Default template for declaring a variable of the correct type. * exmaple: "{$type} {$varname};" diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index 0d2005e..e2da178 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -22,7 +22,8 @@ SOURCES += main.cpp \ tst_PasswordManager.cpp \ tst_ParamJson.cpp \ tst_CodeBuilder.cpp \ - tst_NameManglingRules.cpp + tst_NameManglingRules.cpp \ + tst_TypeMappings.cpp win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../core/release/ -lcore else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../core/debug/ -lcore -- 2.47.2 From 780d912cd195fc8792d62eee3e9189a9b0f26767 Mon Sep 17 00:00:00 2001 From: eelke Date: Tue, 18 Sep 2018 20:24:54 +0200 Subject: [PATCH 07/11] Adjusted project files for new boost version. This should be handled differently the location of boost should not be baked into the project files. --- common.pri | 12 ++++++++++++ core/core.pro | 19 +++++-------------- core/my_boost_assert_handler.cpp | 4 ++-- ctk/ctk.pro | 2 +- pglab/UserConfiguration.cpp | 1 + pglab/UserConfiguration.h | 4 ++++ pglab/pglab.pro | 11 +++++++---- pglabAll.pro | 2 -- pglablib/pglablib.pro | 12 ++++-------- pgsql/pgsql.pro | 9 +++------ tests/PgsqlTests/PgsqlTests.pro | 9 ++++++--- tests/pglabtests/pglabtests.pro | 8 ++++++-- tests/pglabtests/tst_CodeBuilder.cpp | 2 +- 13 files changed, 52 insertions(+), 43 deletions(-) create mode 100644 common.pri diff --git a/common.pri b/common.pri new file mode 100644 index 0000000..39e9269 --- /dev/null +++ b/common.pri @@ -0,0 +1,12 @@ +INCLUDEPATH += C:\prog\include C:\prog\include\pgsql C:\VSproj\boost\include\boost-1_68 + +LIBS += -LC:\VSproj\boost\lib -LC:/PROG/LIB -lws2_32 -llibpq + +QMAKE_CXXFLAGS += /std:c++17 + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX diff --git a/core/core.pro b/core/core.pro index 7b165a1..f603227 100644 --- a/core/core.pro +++ b/core/core.pro @@ -8,23 +8,14 @@ QT -= gui TARGET = core TEMPLATE = lib -CONFIG += staticlib c++14 +CONFIG += staticlib c++17 -QMAKE_CXXFLAGS += /std:c++17 +! include( ../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} -INCLUDEPATH += C:\prog\include \ -C:\Prog\include\pgsql \ -C:\VSproj\boost32\include\boost-1_65_1 +INCLUDEPATH += C:\Prog\include\pgsql -DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX -#LIBS += /LIBPATH:C:\VSproj\boost_1_63_0\stage\lib /LIBPATH:c:\prog\lib\ libpq.lib fmt.lib User32.lib ws2_32.lib - - -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked as deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. diff --git a/core/my_boost_assert_handler.cpp b/core/my_boost_assert_handler.cpp index fda7bba..d3739fa 100644 --- a/core/my_boost_assert_handler.cpp +++ b/core/my_boost_assert_handler.cpp @@ -6,14 +6,14 @@ namespace boost void assertion_failed(char const * expr, char const * function, char const * file, long line) { std::ostringstream out; - out << "Assertion failure int " << function << " " << file << ":" << line; + out << "Assertion failure int " << function << " " << file << ":" << line << "\n" << expr; throw std::runtime_error(out.str()); } void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { std::ostringstream out; - out << "Assertion failure int " << function << " " << file << ":" << line << "\n"<< msg; + out << "Assertion failure int " << function << " " << file << ":" << line << "\n" << msg << "\n" << expr; throw std::runtime_error(out.str()); } } diff --git a/ctk/ctk.pro b/ctk/ctk.pro index cf4d827..6e42fbb 100644 --- a/ctk/ctk.pro +++ b/ctk/ctk.pro @@ -14,7 +14,7 @@ QMAKE_CXXFLAGS += /std:c++17 INCLUDEPATH += C:\prog\include \ C:\Prog\include\pgsql \ -C:\VSproj\boost32\include\boost-1_65_1 +C:\VSproj\boost32\include DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX diff --git a/pglab/UserConfiguration.cpp b/pglab/UserConfiguration.cpp index 3b0ef95..2cecd67 100644 --- a/pglab/UserConfiguration.cpp +++ b/pglab/UserConfiguration.cpp @@ -23,3 +23,4 @@ QFont UserConfiguration::codeFont() const font.setPointSize(size); return font; } + diff --git a/pglab/UserConfiguration.h b/pglab/UserConfiguration.h index 897f1ce..2eb1a5f 100644 --- a/pglab/UserConfiguration.h +++ b/pglab/UserConfiguration.h @@ -2,6 +2,8 @@ #define USERCONFIGURATION_H #include + + /** Class for most of the program configuration * * The settings represented by this class are stored in a settings file @@ -17,6 +19,8 @@ public: UserConfiguration(); QFont codeFont() const; + + private: QSettings m_settings; }; diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 18b7a69..be8e656 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -9,15 +9,18 @@ QT += core gui concurrent greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql -QMAKE_CXXFLAGS += /std:c++17 TARGET = pglab TEMPLATE = app -INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql C:\VSproj\boost32\include\boost-1_65_1 -DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501 +! include( ../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} + +INCLUDEPATH += C:\Prog\include\pgsql +DEFINES += _WIN32_WINNT=0x0501 #LIBS += -LC:/prog/boost/lib -Lc:/prog/lib libpq.lib fmt.lib User32.lib ws2_32.lib -LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq +#LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq #debug { LIBS += c:/prog/lib/botand_imp.lib diff --git a/pglabAll.pro b/pglabAll.pro index ae61f1d..c6dfff3 100644 --- a/pglabAll.pro +++ b/pglabAll.pro @@ -2,7 +2,6 @@ TEMPLATE = subdirs DEFINES += BOOST_ENABLE_ASSERT_HANDLER - SUBDIRS += core \ ctk \ pgsql \ @@ -15,7 +14,6 @@ pglablib.depends = core pgsql tests.depends = core pgsql pglablib - CONFIG(debug, debug|release) { SUBDIRS += tests } diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 4441836..1d0acf9 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -10,15 +10,11 @@ TARGET = pglablib TEMPLATE = lib CONFIG += staticlib -QMAKE_CXXFLAGS += /std:c++17 +! include( ../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} -# The following define makes your compiler emit warnings if you use -# any feature of Qt which has been marked as deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql C:\VSproj\boost32\include\boost-1_65_1 +INCLUDEPATH += C:\Prog\include\pgsql # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. diff --git a/pgsql/pgsql.pro b/pgsql/pgsql.pro index da88da9..6999c72 100644 --- a/pgsql/pgsql.pro +++ b/pgsql/pgsql.pro @@ -12,13 +12,10 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql TARGET = pgsql TEMPLATE = lib -INCLUDEPATH += C:\prog\include \ -C:\Prog\include\pgsql \ -C:\VSproj\boost32\include\boost-1_65_1 +! include( ../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} -QMAKE_CXXFLAGS += /std:c++17 - -DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX #LIBS += -LC:/prog/boost/lib -Lc:/prog/lib libpq.lib fmt.lib User32.lib ws2_32.lib LIBS += -LC:/PROG/LIB -lws2_32 -llibpq diff --git a/tests/PgsqlTests/PgsqlTests.pro b/tests/PgsqlTests/PgsqlTests.pro index 18845c9..3136f67 100644 --- a/tests/PgsqlTests/PgsqlTests.pro +++ b/tests/PgsqlTests/PgsqlTests.pro @@ -1,16 +1,19 @@ +! include( ../../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} include(gtest_dependency.pri) TEMPLATE = app -CONFIG += console c++11 +CONFIG += console c++17 CONFIG -= app_bundle CONFIG += thread CONFIG += qt QT += core -QMAKE_CXXFLAGS += /std:c++17 +INCLUDEPATH += C:\Prog\include\pgsql + -INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql HEADERS += diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index e2da178..ed131bf 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -9,7 +9,11 @@ CONFIG += qt QT += core widgets QMAKE_CXXFLAGS += /std:c++17 -LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq + +! include( ../../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} + HEADERS += @@ -30,7 +34,7 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../core/debug/ - INCLUDEPATH += C:\prog\include \ C:\Prog\include\pgsql \ -C:\VSproj\boost32\include\boost-1_65_1 +C:\VSproj\boost32\include INCLUDEPATH += $$PWD/../../core DEPENDPATH += $$PWD/../../core diff --git a/tests/pglabtests/tst_CodeBuilder.cpp b/tests/pglabtests/tst_CodeBuilder.cpp index 206d1fe..cf7d349 100644 --- a/tests/pglabtests/tst_CodeBuilder.cpp +++ b/tests/pglabtests/tst_CodeBuilder.cpp @@ -1,9 +1,9 @@ #include #include +#include "PrintTo_Qt.h" #include #include "PgType.h" #include "PgTypeContainer.h" -#include "PrintTo_Qt.h" #include "codebuilder/IndentationConfig.h" #include "codebuilder/TypeMappings.h" #include "Pgsql_oids.h" -- 2.47.2 From e4ccd93b09450b71e6f60aecd0907e3c90f1250a Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Sep 2018 08:25:23 +0200 Subject: [PATCH 08/11] Better support for boost::optional in database layer. --- pglablib/PgClassContainer.cpp | 4 ++-- pgsql/Pgsql_Col.h | 10 ++++++++-- pgsql/Pgsql_Value.h | 17 ++++++++++++++++- tests/PgsqlTests/tst_Value.cpp | 1 + 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pglablib/PgClassContainer.cpp b/pglablib/PgClassContainer.cpp index 701dc68..94d10b4 100644 --- a/pglablib/PgClassContainer.cpp +++ b/pglablib/PgClassContainer.cpp @@ -24,8 +24,8 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row) >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid; - col.getAsArray(std::back_inserter(v.acl), Pgsql::NullHandling::Ignore); - col.getAsArray(std::back_inserter(v.options), Pgsql::NullHandling::Ignore); + col.getAsArray(std::back_inserter(v.acl), QString()); + col.getAsArray(std::back_inserter(v.options), QString()); auto cat = m_catalogue.lock(); auto ns = cat->namespaces()->getByKey(v.relnamespace); diff --git a/pgsql/Pgsql_Col.h b/pgsql/Pgsql_Col.h index 8136eb7..0c0cd36 100644 --- a/pgsql/Pgsql_Col.h +++ b/pgsql/Pgsql_Col.h @@ -26,14 +26,14 @@ namespace Pgsql { } template - void getAsArray(I insert_iter, const E &value_for_nulls) const + Col& getAsArray(I insert_iter, const E &value_for_nulls) { nextValue().getAsArray(insert_iter, value_for_nulls); return *this; } template - void getAsArrayOfOptional(I insert_iter) const + Col& getAsArrayOfOptional(I insert_iter) { nextValue().getAsArrayOfOptional(insert_iter); return *this; @@ -50,6 +50,12 @@ namespace Pgsql { int col = -1; }; + template + Col& operator>>(Col &c, std::vector &s) + { + return c.getAsArray(std::back_inserter(s)); + } + template Col& operator>>(Col &c, T &s) { diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index 2c28e2b..b27f7ca 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace Pgsql { @@ -149,10 +150,24 @@ namespace Pgsql { Oid m_typ; }; + template + void operator<<(boost::optional &s, const Value &v) + { + if (v.null()) + s = boost::optional(); + else + *s << v; + } + + template + void operator<<(std::vector &s, const Value &v) + { + v.getAsArray(std::back_inserter(s)); + } + template void operator<<(T &s, const Value &v) { - //s = static_cast(v); s = v.operator T(); } diff --git a/tests/PgsqlTests/tst_Value.cpp b/tests/PgsqlTests/tst_Value.cpp index fa7b8ed..dd66495 100644 --- a/tests/PgsqlTests/tst_Value.cpp +++ b/tests/PgsqlTests/tst_Value.cpp @@ -92,6 +92,7 @@ TEST(Pgsql_Value, getAsArray_Ints) ASSERT_EQ(r[1], 2); } + TEST(Pgsql_Value, getAsArray_QDateTime) { Pgsql::Value v("{\"2017-12-11 10:11:22\",\"2017-12-13 12:00:11\"}", text_array_oid); -- 2.47.2 From 8d3bddfa1eb7bfc2322879156c9ee1ee23c449d3 Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Sep 2018 08:25:58 +0200 Subject: [PATCH 09/11] Moved implementations from header to cpp. --- pglablib/codebuilder/LanguageConfig.cpp | 35 +++++++++++++++++++++++ pglablib/codebuilder/LanguageConfig.h | 37 +++++-------------------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/pglablib/codebuilder/LanguageConfig.cpp b/pglablib/codebuilder/LanguageConfig.cpp index 3d22eed..33100f8 100644 --- a/pglablib/codebuilder/LanguageConfig.cpp +++ b/pglablib/codebuilder/LanguageConfig.cpp @@ -13,3 +13,38 @@ QString LanguageConfig::getTypeName(Oid dbtype) const { return m_typeMappings->getTypeForOid(dbtype); } + +void LanguageConfig::setNameManglingRules(std::shared_ptr name_mangling_rules) +{ + m_varNaming = name_mangling_rules; +} + +std::shared_ptr LanguageConfig::typeMappings() const +{ + return m_typeMappings; +} + +void LanguageConfig::setTypeMappings(std::shared_ptr type_mappings) +{ + m_typeMappings = type_mappings; +} + +std::shared_ptr LanguageConfig::structureTemplate() const +{ + return m_structureTemplate; +} + +void LanguageConfig::setStructureTemplate(std::shared_ptr structure_template) +{ + m_structureTemplate = structure_template; +} + +std::shared_ptr LanguageConfig::indentationConfig() const +{ + return m_indentationConfig; +} + +void LanguageConfig::setIndentationConfig(std::shared_ptr indentation_config) +{ + m_indentationConfig = indentation_config; +} diff --git a/pglablib/codebuilder/LanguageConfig.h b/pglablib/codebuilder/LanguageConfig.h index e583a28..3f9ac46 100644 --- a/pglablib/codebuilder/LanguageConfig.h +++ b/pglablib/codebuilder/LanguageConfig.h @@ -18,36 +18,13 @@ public: QString columnNameToFieldName(const QString& column_name) const; QString getTypeName(Oid dbtype) const; - void setNameManglingRules(std::shared_ptr name_mangling_rules) - { - m_varNaming = name_mangling_rules; - } - - std::shared_ptr typeMappings() const { return m_typeMappings; } - void setTypeMappings(std::shared_ptr type_mappings) - { - m_typeMappings = type_mappings; - } - - std::shared_ptr structureTemplate() const - { - return m_structureTemplate; - } - - void setStructureTemplate(std::shared_ptr structure_template) - { - m_structureTemplate = structure_template; - } - - std::shared_ptr indentationConfig() const - { - return m_indentationConfig; - } - - void setIndentationConfig(std::shared_ptr indentation_config) - { - m_indentationConfig = indentation_config; - } + void setNameManglingRules(std::shared_ptr name_mangling_rules); + std::shared_ptr typeMappings() const; + void setTypeMappings(std::shared_ptr type_mappings); + std::shared_ptr structureTemplate() const; + void setStructureTemplate(std::shared_ptr structure_template); + std::shared_ptr indentationConfig() const; + void setIndentationConfig(std::shared_ptr indentation_config); private: /** Default template for declaring a variable of the correct type. * exmaple: "{$type} {$varname};" -- 2.47.2 From 0ba632afd1ac39797b7a78b0cbcac695c9da6a34 Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Sep 2018 08:45:01 +0200 Subject: [PATCH 10/11] The getAsArray variation that uses a default value for NULL elements crashed when the array value itself was NULL. Choosen to return without error adding zero elements to the list. If you really need strict NULL handling do this using the other functions possibly checking for null before hand to choose other code path. --- pgsql/Pgsql_Value.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index b27f7ca..31a7e61 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -48,7 +48,7 @@ namespace Pgsql { { if (m_val == nullptr) { if (nullhandling == NullHandling::Throw) - throw std::runtime_error("Unexpected NULL value in array"); + throw std::runtime_error("Unexpected NULL value for array"); } else { using value_type = E; @@ -77,6 +77,8 @@ namespace Pgsql { template void getAsArray(I insert_iter, const E &value_for_nulls) const { + if (m_val == nullptr) return; + using value_type = E; ArrayParser parser(m_val); for (;;) { -- 2.47.2 From 0c3bb27e585fad25607f075a305c1e0345af7213 Mon Sep 17 00:00:00 2001 From: eelke Date: Wed, 19 Sep 2018 09:55:43 +0200 Subject: [PATCH 11/11] Further improvements to codegeneration the defaultcpp config is now called the PgLab config as it is very specific to the PgLab codebase. More hard programmed templates moved out of codebuilder to the language config. --- pglab/CodeGenerator.cpp | 2 +- pglablib/codebuilder/CodeBuilder.cpp | 74 ++++++++++----------- pglablib/codebuilder/CodeBuilder.h | 26 ++++---- pglablib/codebuilder/DefaultConfigs.cpp | 78 ++++++++++++++--------- pglablib/codebuilder/DefaultConfigs.h | 4 +- pglablib/codebuilder/LanguageConfig.cpp | 10 +++ pglablib/codebuilder/LanguageConfig.h | 6 +- pglablib/codebuilder/ResultLoopTemplate.h | 26 ++++++++ pglablib/codebuilder/StructureTemplate.h | 5 +- pglablib/pglablib.pro | 3 +- 10 files changed, 143 insertions(+), 91 deletions(-) create mode 100644 pglablib/codebuilder/ResultLoopTemplate.h diff --git a/pglab/CodeGenerator.cpp b/pglab/CodeGenerator.cpp index 92c4c2f..0cf97aa 100644 --- a/pglab/CodeGenerator.cpp +++ b/pglab/CodeGenerator.cpp @@ -42,7 +42,7 @@ void CodeGenerator::generateCode() QString buffer; QTextStream stream(&buffer); CodeBuilder builder; - builder.setLanguageConfig(getDefaultCppLanguageConfig()); + builder.setLanguageConfig(getPglabCppLanguageConfig()); builder.GenCodeForExecutingQuery(stream, m_query, *m_dbres, struct_name); stream.flush(); ui->generatedCodeEditor->setPlainText(buffer); diff --git a/pglablib/codebuilder/CodeBuilder.cpp b/pglablib/codebuilder/CodeBuilder.cpp index dd782c0..763c567 100644 --- a/pglablib/codebuilder/CodeBuilder.cpp +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -3,45 +3,30 @@ #include "Pgsql_Result.h" #include "IndentationConfig.h" #include "LanguageConfig.h" +#include "ResultLoopTemplate.h" #include "StructureTemplate.h" #include "util.h" #include void CodeBuilder::GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname) { -// %1 query string -// %2 struct_name -// %3 list of field assignments - QString exec_query_template = "Pgsql::Result result = conn.query(%1);"; - - QString loop_start_template = R"__(Pgsql::Result result = conn.query(/%queryliteral%/); -for (auto row: result) { - Pgsql::Col col(row); - /%structname%/ v; - /%assignfields%/ -})__"; - - // %field_name% - // %column_name% - // %column_index% - QString assign_result_field_template = "col >> v./%fieldname%/"; + QString loop_template = m_configuration->resultLoopTemplate()->m_loopTemplate;; QString query_string_literal = ConvertToMultiLineCString(query); ColumnDataList columnData = createColumnDataList(result); auto get_var_func = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; - //else if (var == "assign_fields") GenFieldAssignments(out); else if (var == "queryliteral") out << query; - + else if (var == "assignfields") genFieldRetrieval(out, columnData); }; // [optional] Gen declaration of result structure - GenReturnStructDefinition(q, result, structname); + GenReturnStructDefinition(q, columnData, structname); // assume we have connection? What name??? - FormatToStream(q, loop_start_template, get_var_func); + FormatToStream(q, loop_template, get_var_func); // gen code for creating and executing statement // - bind parameters @@ -51,27 +36,30 @@ for (auto row: result) { } -void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const +void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const ColumnDataList &columns, QString structname) const { std::shared_ptr templ = m_configuration->structureTemplate(); - QString field_format = templ->m_fieldTemplate; - int field_indent_levels = templ->m_fieldIndentation; - QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); - auto struct_callback = [&] (QTextStream &out, QString var) { if (var == "structname") out << structname; + else if (var == "structfields") genStructFields(out, columns); }; - // Make struct start - //q << QString(templ->m_startTemplate).arg(struct_name); - FormatToStream(q, templ->m_startTemplate, struct_callback); - // Process fields - for (int column = 0; column < result.cols(); ++column) { - QString res_col_name = result.getColName(column); - Oid type_oid = result.type(column); + FormatToStream(q, templ->m_structTemplate, struct_callback); +} + +void CodeBuilder::genStructFields(QTextStream &q, const ColumnDataList &columns) const +{ + std::shared_ptr templ = m_configuration->structureTemplate(); + QString field_format = templ->m_fieldTemplate; + const int field_indent_levels = templ->m_fieldIndentation; + QString field_indent_string = m_configuration->indentationConfig()->getIndentString(field_indent_levels); + + for (auto col : columns) { + QString field_name = col.varName; + Oid type_oid = col.oid; q << field_indent_string; - genFieldDeclaration(q, field_format, res_col_name, type_oid); + genFieldDeclaration(q, field_format, field_name, type_oid); q << "\n"; // Unfortunatly there is no easy reliable way to determine if a column can contain null values // For simple result columns we can have a look at the original column and if that has a NOT NULL constraint @@ -85,10 +73,6 @@ void CodeBuilder::GenReturnStructDefinition(QTextStream &q, const Pgsql::Result // - boolean flags // - null pointer (useful for languages where this has no cost, other cases boolean flags will be more performant) } - // Finish struct - - //q << QString(templ->m_endTemplate).arg(struct_name); - FormatToStream(q, templ->m_endTemplate, struct_callback); } QString CodeBuilder::columnNameToVariableName(QString column_name) const @@ -101,18 +85,26 @@ QString CodeBuilder::getTypeName(Oid dbtype) const return m_configuration->getTypeName(dbtype); } -void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const +void CodeBuilder::genFieldRetrieval(QTextStream &q, const ColumnDataList &columns) const +{ + QString assign_result_field_template = m_configuration->resultLoopTemplate()->m_retrieveValueTemplate; + for (auto col : columns) { + FormatToStream(q, assign_result_field_template, [&col] (QTextStream &out, QString var) { + if (var == "varname") out << col.varName; + }); + } +} + +void CodeBuilder::genFieldDeclaration(QTextStream &q, const QString &format, const QString &field_name, Oid column_type) const { - QString field_name = columnNameToVariableName(column_name); QString type_name = getTypeName(column_type); - //q << QString(format).arg(field_name).arg(type_name); FormatToStream(q, format, [&] (QTextStream &out, QString var) { if (var == "typename") out << type_name; else if (var == "varname") out << field_name; }); } -CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) +CodeBuilder::ColumnDataList CodeBuilder::createColumnDataList(const Pgsql::Result &result) const { ColumnDataList list; list.reserve(static_cast(result.cols())); diff --git a/pglablib/codebuilder/CodeBuilder.h b/pglablib/codebuilder/CodeBuilder.h index aad070d..01694e7 100644 --- a/pglablib/codebuilder/CodeBuilder.h +++ b/pglablib/codebuilder/CodeBuilder.h @@ -10,12 +10,22 @@ class QTextStream; class CodeBuilder { public: + class ColumnData { + public: + Oid oid; + QString columnName; + QString varName; ///< either field of the struct or in loop local var name + }; + using ColumnDataList = std::vector; + + ColumnDataList createColumnDataList(const Pgsql::Result &result) const; + void setLanguageConfig(std::shared_ptr config){ m_configuration = config; } void GenCodeForExecutingQuery(QTextStream &q, const QString &query, const Pgsql::Result &result, QString structname); - void GenReturnStructDefinition(QTextStream &q, const Pgsql::Result &result, QString structname) const; + void GenReturnStructDefinition(QTextStream &q, const ColumnDataList &columns, QString structname) const; // Generating code for performing query and going through the result // - Code for executing the query @@ -25,21 +35,13 @@ public: QString columnNameToVariableName(QString column_name) const; QString getTypeName(Oid dbtype) const; private: - class ColumnData { - public: - Oid oid; - QString columnName; - QString varName; ///< either field of the struct or in loop local var name - }; - using ColumnDataList = std::vector; std::shared_ptr m_configuration; + void genStructFields(QTextStream &q, const ColumnDataList &columns) const; + void genFieldRetrieval(QTextStream &q, const ColumnDataList &columns) const; + void genFieldDeclaration(QTextStream &q, const QString &format, const QString &field_name, Oid column_type) const; - void GenFieldAssignments(QTextStream &q, const Pgsql::Result &result); - void genFieldDeclaration(QTextStream &q, const QString &format, const QString &column_name, Oid column_type) const; - - ColumnDataList createColumnDataList(const Pgsql::Result &result); }; #endif // CODEBUILDER_H diff --git a/pglablib/codebuilder/DefaultConfigs.cpp b/pglablib/codebuilder/DefaultConfigs.cpp index d9f31c3..da1eba2 100644 --- a/pglablib/codebuilder/DefaultConfigs.cpp +++ b/pglablib/codebuilder/DefaultConfigs.cpp @@ -2,13 +2,14 @@ #include "IndentationConfig.h" #include "LanguageConfig.h" #include "NameManglingRules.h" +#include "ResultLoopTemplate.h" #include "StructureTemplate.h" #include "TypeMappings.h" #include "Pgsql_oids.h" using namespace Pgsql; -std::shared_ptr GetDefaultCppTypeMappings() +std::shared_ptr GetPglabCppTypeMappings() { auto tm = std::make_shared(); *tm = { @@ -28,40 +29,57 @@ std::shared_ptr GetDefaultCppTypeMappings() return tm; } +std::shared_ptr buildPglabStructureTemplate() +{ + auto t = std::make_shared(); + t->m_structTemplate = +R"__(class /%structname%/ { +public: +/%structfields%/ +}; +using /%structname%/Lst = std::vector; +)__"; -std::shared_ptr buildDefaultCppLanguageConfig() + t->m_fieldTemplate = "/%typename%/ /%varname%/;"; + //st_templ->m_fieldSeparator; + + return t; +} + +std::shared_ptr buildPglabResultLoopTemplate() +{ + auto t = std::make_shared(); + t->m_loopTemplate = +R"__(Pgsql::Result result = conn.query(/%queryliteral%/); +/%structname%/Lst lst; +for (auto row: result) { + Pgsql::Col col(row); + /%structname%/ v; + col + /%assignfields%/; + lst.push_back(v); +} +)__"; + t->m_retrieveValueTemplate = " >> v./%varname%/"; + + return t; +} + +std::shared_ptr buildPglabCppLanguageConfig() { auto config = std::make_shared(); - config->setTypeMappings(GetDefaultCppTypeMappings()); + config->setTypeMappings(GetPglabCppTypeMappings()); config->setNameManglingRules(std::make_shared()); - - auto st_templ = std::make_shared(); - st_templ->m_startTemplate = "class /%structname%/ {\npublic:\n"; - st_templ->m_endTemplate = "};\n"; // }; - st_templ->m_fieldTemplate = "/%typename%/ /%varname%/;"; - //st_templ->m_fieldSeparator; - config->setStructureTemplate(st_templ); + config->setStructureTemplate(buildPglabStructureTemplate()); config->setIndentationConfig(std::make_shared()); - - return config; -} - -std::shared_ptr getDefaultCppLanguageConfig() -{ - static auto config = buildDefaultCppLanguageConfig(); - return config; -} - -std::shared_ptr buildPgLabCppLanguageConfig() -{ - auto cfg = buildDefaultCppLanguageConfig(); - - return cfg; -} - -std::shared_ptr getPgLabCppLanguageConfig() -{ - static auto config = buildPgLabCppLanguageConfig(); + config->setResultLoopTemplate(buildPglabResultLoopTemplate()); + return config; +} + + +std::shared_ptr getPglabCppLanguageConfig() +{ + static auto config = buildPglabCppLanguageConfig(); return config; } diff --git a/pglablib/codebuilder/DefaultConfigs.h b/pglablib/codebuilder/DefaultConfigs.h index d446d22..bb04da9 100644 --- a/pglablib/codebuilder/DefaultConfigs.h +++ b/pglablib/codebuilder/DefaultConfigs.h @@ -5,7 +5,7 @@ class LanguageConfig; -std::shared_ptr getDefaultCppLanguageConfig(); -std::shared_ptr getPgLabCppLanguageConfig(); +//std::shared_ptr getDefaultCppLanguageConfig(); +std::shared_ptr getPglabCppLanguageConfig(); #endif // DEFAULTCONFIGS_H diff --git a/pglablib/codebuilder/LanguageConfig.cpp b/pglablib/codebuilder/LanguageConfig.cpp index 33100f8..2fa4b74 100644 --- a/pglablib/codebuilder/LanguageConfig.cpp +++ b/pglablib/codebuilder/LanguageConfig.cpp @@ -48,3 +48,13 @@ void LanguageConfig::setIndentationConfig(std::shared_ptr LanguageConfig::resultLoopTemplate() const +{ + return m_resultLoopTemplate; +} + +void LanguageConfig::setResultLoopTemplate(std::shared_ptr result_loop_template) +{ + m_resultLoopTemplate = result_loop_template; +} diff --git a/pglablib/codebuilder/LanguageConfig.h b/pglablib/codebuilder/LanguageConfig.h index 3f9ac46..8631a99 100644 --- a/pglablib/codebuilder/LanguageConfig.h +++ b/pglablib/codebuilder/LanguageConfig.h @@ -8,6 +8,7 @@ class NameManglingRules; class TypeMappings; class StructureTemplate; class IndentationConfig; +class ResultLoopTemplate; /** * */ @@ -25,6 +26,8 @@ public: void setStructureTemplate(std::shared_ptr structure_template); std::shared_ptr indentationConfig() const; void setIndentationConfig(std::shared_ptr indentation_config); + std::shared_ptr resultLoopTemplate() const; + void setResultLoopTemplate(std::shared_ptr result_loop_template); private: /** Default template for declaring a variable of the correct type. * exmaple: "{$type} {$varname};" @@ -34,13 +37,12 @@ private: std::shared_ptr m_typeMappings; std::shared_ptr m_structureTemplate; std::shared_ptr m_indentationConfig; + std::shared_ptr m_resultLoopTemplate; enum class VariableStrategy { UseLocalVariables, DeclareClass }; - - }; #endif // LANGUAGECONFIG_H diff --git a/pglablib/codebuilder/ResultLoopTemplate.h b/pglablib/codebuilder/ResultLoopTemplate.h new file mode 100644 index 0000000..660d437 --- /dev/null +++ b/pglablib/codebuilder/ResultLoopTemplate.h @@ -0,0 +1,26 @@ +#ifndef RESULTLOOPTEMPLATE_H +#define RESULTLOOPTEMPLATE_H + +#include + +class ResultLoopTemplate { +public: + /** + * @brief m_loopTemplate + * + * Excepted template fields + * - structname: The typename of the struct/class + * - queryliteral: The query + * - assignfields: Replaced with the field assignment + */ + QString m_loopTemplate; + /** + * @brief m_retrieveValueTemplate + * + * Excepted template fields + * - varname: the name of the field of the struct or normal variable + */ + QString m_retrieveValueTemplate; +}; + +#endif // RESULTLOOPTEMPLATE_H diff --git a/pglablib/codebuilder/StructureTemplate.h b/pglablib/codebuilder/StructureTemplate.h index 716031a..f93a1ef 100644 --- a/pglablib/codebuilder/StructureTemplate.h +++ b/pglablib/codebuilder/StructureTemplate.h @@ -6,8 +6,9 @@ class StructureTemplate { public: - QString m_startTemplate; // class /$structname/ {\npublic:\n - QString m_endTemplate; // }; +// QString m_startTemplate; // class /$structname/ {\npublic:\n +// QString m_endTemplate; // }; + QString m_structTemplate; QString m_fieldTemplate; // /$typename/ /$varname/; QString m_fieldSeparator; // use when you need something between fields but not after the last field int m_fieldIndentation = 1; diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 1d0acf9..b355769 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -104,7 +104,8 @@ codebuilder/DefaultConfigs.h \ codebuilder/TypeMappings.h \ codebuilder/IndentationConfig.h \ codebuilder/StructureTemplate.h \ - FormatToStream.h + FormatToStream.h \ + codebuilder/ResultLoopTemplate.h unix { target.path = /usr/lib -- 2.47.2