From 8f4845d4d20dbafcaba48e684244aee19f9322e2 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 9 Sep 2018 18:52:32 +0200 Subject: [PATCH] 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;