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/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/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..0cf97aa --- /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(getPglabCppLanguageConfig()); + 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/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/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/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/UserConfiguration.cpp b/pglab/UserConfiguration.cpp new file mode 100644 index 0000000..2cecd67 --- /dev/null +++ b/pglab/UserConfiguration.cpp @@ -0,0 +1,26 @@ +#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..2eb1a5f --- /dev/null +++ b/pglab/UserConfiguration.h @@ -0,0 +1,28 @@ +#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 aac271b..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 @@ -54,7 +57,6 @@ SOURCES += main.cpp\ ConnectionList.cpp \ ProcessStdioWidget.cpp \ GlobalIoService.cpp \ - CodeBuilderConfiguration.cpp \ ResultTableModelUtil.cpp \ BaseTableModel.cpp \ QueryParamListController.cpp \ @@ -74,7 +76,9 @@ SOURCES += main.cpp\ EditorGutter.cpp \ CodeEditor.cpp \ PlgPage.cpp \ - PropertyProxyModel.cpp +PropertyProxyModel.cpp \ + CodeGenerator.cpp \ + UserConfiguration.cpp HEADERS += \ QueryResultModel.h \ @@ -122,7 +126,9 @@ HEADERS += \ PlgPage.h \ AbstractCommand.h \ PropertyProxyModel.h \ - CustomDataRole.h +CustomDataRole.h \ + CodeGenerator.h \ + UserConfiguration.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ @@ -135,7 +141,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/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/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/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/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..763c567 --- /dev/null +++ b/pglablib/codebuilder/CodeBuilder.cpp @@ -0,0 +1,118 @@ +#include "CodeBuilder.h" +#include "FormatToStream.h" +#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) +{ + 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 == "queryliteral") out << query; + else if (var == "assignfields") genFieldRetrieval(out, columnData); + }; + + + // [optional] Gen declaration of result structure + GenReturnStructDefinition(q, columnData, structname); + + // assume we have connection? What name??? + FormatToStream(q, loop_template, get_var_func); + + // 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 ColumnDataList &columns, QString structname) const +{ + std::shared_ptr templ = m_configuration->structureTemplate(); + + auto struct_callback = [&] (QTextStream &out, QString var) { + if (var == "structname") out << structname; + else if (var == "structfields") genStructFields(out, columns); + }; + + 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, 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 + // 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) + } +} + +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::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 type_name = getTypeName(column_type); + 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) const +{ + 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 new file mode 100644 index 0000000..01694e7 --- /dev/null +++ b/pglablib/codebuilder/CodeBuilder.h @@ -0,0 +1,47 @@ +#ifndef CODEBUILDER_H +#define CODEBUILDER_H + +#include "Pgsql_declare.h" +#include +#include + +class LanguageConfig; +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 ColumnDataList &columns, QString structname) 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 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; + +}; + +#endif // CODEBUILDER_H diff --git a/pglablib/codebuilder/DefaultConfigs.cpp b/pglablib/codebuilder/DefaultConfigs.cpp new file mode 100644 index 0000000..da1eba2 --- /dev/null +++ b/pglablib/codebuilder/DefaultConfigs.cpp @@ -0,0 +1,86 @@ +#include "DefaultConfigs.h" +#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 GetPglabCppTypeMappings() +{ + auto tm = std::make_shared(); + *tm = { + { 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" } + }; + tm->setDefaultStringType("std::string"); + tm->setDefaultContainerType("std::vector<%1>"); + return tm; +} + +std::shared_ptr buildPglabStructureTemplate() +{ + auto t = std::make_shared(); + t->m_structTemplate = +R"__(class /%structname%/ { +public: +/%structfields%/ +}; +using /%structname%/Lst = std::vector; +)__"; + + 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(GetPglabCppTypeMappings()); + config->setNameManglingRules(std::make_shared()); + config->setStructureTemplate(buildPglabStructureTemplate()); + config->setIndentationConfig(std::make_shared()); + 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 new file mode 100644 index 0000000..bb04da9 --- /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..2fa4b74 --- /dev/null +++ b/pglablib/codebuilder/LanguageConfig.cpp @@ -0,0 +1,60 @@ +#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); +} + +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; +} + +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 new file mode 100644 index 0000000..8631a99 --- /dev/null +++ b/pglablib/codebuilder/LanguageConfig.h @@ -0,0 +1,48 @@ +#ifndef LANGUAGECONFIG_H +#define LANGUAGECONFIG_H + +#include +#include "Pgsql_oids.h" + +class NameManglingRules; +class TypeMappings; +class StructureTemplate; +class IndentationConfig; +class ResultLoopTemplate; +/** + * + */ +class LanguageConfig { +public: + LanguageConfig(); + + QString columnNameToFieldName(const QString& column_name) const; + QString getTypeName(Oid dbtype) const; + + 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); + 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};" + */ + //QString varDeclTemplate; + std::shared_ptr m_varNaming; + 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/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..610c64b --- /dev/null +++ b/pglablib/codebuilder/NameManglingRules.h @@ -0,0 +1,49 @@ +#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 } + + //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/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.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..f93a1ef --- /dev/null +++ b/pglablib/codebuilder/StructureTemplate.h @@ -0,0 +1,17 @@ +#ifndef STRUCTURETEMPLATE_H +#define STRUCTURETEMPLATE_H + +#include + +class StructureTemplate { +public: + +// 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; +}; + +#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..b355769 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. @@ -57,7 +53,15 @@ 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 \ + FormatToStream.cpp HEADERS += \ Pglablib.h \ @@ -92,7 +96,16 @@ 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 \ + FormatToStream.h \ + codebuilder/ResultLoopTemplate.h unix { target.path = /usr/lib 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_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_Value.h b/pgsql/Pgsql_Value.h index 2c28e2b..31a7e61 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace Pgsql { @@ -47,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; @@ -76,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 (;;) { @@ -149,10 +152,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/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/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/PgsqlTests/tst_Value.cpp b/tests/PgsqlTests/tst_Value.cpp index 2f4385b..dd66495 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)); @@ -92,9 +92,10 @@ 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\"}", 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 +104,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 +118,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 +133,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 +147,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..ed131bf 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -1,12 +1,19 @@ 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 + +! include( ../../common.pri ) { +error( "Couldn't find the common.pri file!" ) +} + HEADERS += @@ -17,14 +24,17 @@ SOURCES += main.cpp \ tst_scopeguard.cpp \ tst_CsvWriter.cpp \ tst_PasswordManager.cpp \ - tst_ParamJson.cpp + tst_ParamJson.cpp \ + tst_CodeBuilder.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 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 @@ -60,3 +70,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..cf7d349 --- /dev/null +++ b/tests/pglabtests/tst_CodeBuilder.cpp @@ -0,0 +1,172 @@ +#include +#include +#include "PrintTo_Qt.h" +#include +#include "PgType.h" +#include "PgTypeContainer.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, " "); +} + 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..f09ee82 --- /dev/null +++ b/tests/pglabtests/tst_NameManglingRules.cpp @@ -0,0 +1,81 @@ +#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; + +// //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_F(NameManglingRulesTest, caseConversionAsIs) +{ + QString input = "Ab"; + QString expected = input; + + NameManglingRules rules; + rules.caseConversion = NameManglingRules::CaseConversion::AsIs; + QString result = rules.transform(input); + + ASSERT_EQ(result, expected); +} + +TEST_F(NameManglingRulesTest, caseConversionLower) +{ + 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); +} + 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 421933a..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; @@ -53,7 +54,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); 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"); +} + 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;