Gen code for query #78

Merged
eelke merged 11 commits from gen-code-for-query into master 2018-09-23 08:38:07 +00:00
69 changed files with 1650 additions and 323 deletions

12
common.pri Normal file
View file

@ -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

View file

@ -8,23 +8,14 @@ QT -= gui
TARGET = core TARGET = core
TEMPLATE = lib 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 \ INCLUDEPATH += C:\Prog\include\pgsql
C:\Prog\include\pgsql \
C:\VSproj\boost32\include\boost-1_65_1
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. # You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line. # In order to do so, uncomment the following line.

View file

@ -6,14 +6,14 @@ namespace boost
void assertion_failed(char const * expr, char const * function, char const * file, long line) void assertion_failed(char const * expr, char const * function, char const * file, long line)
{ {
std::ostringstream out; 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()); throw std::runtime_error(out.str());
} }
void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line)
{ {
std::ostringstream out; 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()); throw std::runtime_error(out.str());
} }
} }

View file

@ -14,7 +14,7 @@ QMAKE_CXXFLAGS += /std:c++17
INCLUDEPATH += C:\prog\include \ INCLUDEPATH += C:\prog\include \
C:\Prog\include\pgsql \ C:\Prog\include\pgsql \
C:\VSproj\boost32\include\boost-1_65_1 C:\VSproj\boost32\include
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX

View file

@ -1,6 +1,7 @@
#include "BaseTableModel.h" #include "BaseTableModel.h"
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include <QBrush> #include <QBrush>
#include "Pgsql_oids.h"
using namespace Pgsql; using namespace Pgsql;
@ -10,7 +11,7 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const
Oid oid = getType(index.column()); Oid oid = getType(index.column());
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
v = getData(index); v = getData(index);
if (oid == BOOLOID) { if (oid == bool_oid) {
v = FormatBoolForDisplay(v.toBool()); v = FormatBoolForDisplay(v.toBool());
} }
} }
@ -18,7 +19,7 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const
v = (int)GetDefaultAlignmentForType(oid); v = (int)GetDefaultAlignmentForType(oid);
} }
else if (role == Qt::ForegroundRole) { else if (role == Qt::ForegroundRole) {
if (oid == BOOLOID) { if (oid == bool_oid) {
QVariant d = getData(index); QVariant d = getData(index);
if (d.type() == QVariant::Bool) { if (d.type() == QVariant::Bool) {
v = QBrush(GetDefaultBoolColor(d.toBool())); v = QBrush(GetDefaultBoolColor(d.toBool()));

View file

@ -1,8 +0,0 @@
#include "CodeBuilderConfiguration.h"
#include "Pgsql_Result.h"
QString CodeBuilder::GenClassDefinition(const Pgsql::Result &/*result*/) const
{
return QString();
}

View file

@ -1,120 +0,0 @@
#pragma once
#include <QString>
#include <QStringBuilder>
#include <libpq-fe.h>
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;
};

View file

@ -4,6 +4,9 @@
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <set> #include <set>
/** This class adds some capabilities to QPlainTextEdit that are useful
* in code editor scenarios.
*/
class CodeEditor : public QPlainTextEdit class CodeEditor : public QPlainTextEdit
{ {
Q_OBJECT Q_OBJECT

51
pglab/CodeGenerator.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "CodeGenerator.h"
#include "ui_CodeGenerator.h"
#include "codebuilder/CodeBuilder.h"
#include "codebuilder/DefaultConfigs.h"
#include "UserConfiguration.h"
#include <QTextStream>
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<const Pgsql::Result> 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);
}

33
pglab/CodeGenerator.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef CODEGENERATOR_H
#define CODEGENERATOR_H
#include <QWidget>
#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<const Pgsql::Result> dbres);
private slots:
void on_updateCodeButton_clicked();
private:
Ui::CodeGenerator *ui;
QString m_query;
std::shared_ptr<const Pgsql::Result> m_dbres;
void generateCode();
};
#endif // CODEGENERATOR_H

80
pglab/CodeGenerator.ui Normal file
View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CodeGenerator</class>
<widget class="QWidget" name="CodeGenerator">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1127</width>
<height>762</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="structNameLabel">
<property name="text">
<string>Struct name:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="structNameEdit"/>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="updateCodeButton">
<property name="text">
<string>Update code</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Configuration</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="comboBox"/>
</item>
</layout>
</widget>
<widget class="CodeEditor" name="generatedCodeEditor">
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CodeEditor</class>
<extends>QPlainTextEdit</extends>
<header>CodeEditor.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -140,7 +140,7 @@ int ColumnTableModel::columnCount(const QModelIndex &/*parent*/) const
Oid ColumnTableModel::getType(int /*column*/) const Oid ColumnTableModel::getType(int /*column*/) const
{ {
Oid oid = Pgsql::VARCHAROID; Oid oid = Pgsql::varchar_oid;
// switch (column) { // switch (column) {
// case TypeCol: // case TypeCol:
// case NameCol: // case NameCol:

View file

@ -79,12 +79,12 @@ QVariant ConstraintModel::headerData(int section, Qt::Orientation orientation, i
return v; return v;
} }
int ConstraintModel::rowCount(const QModelIndex &parent) const int ConstraintModel::rowCount(const QModelIndex &) const
{ {
return m_constraints.size(); return m_constraints.size();
} }
int ConstraintModel::columnCount(const QModelIndex &parent) const int ConstraintModel::columnCount(const QModelIndex &) const
{ {
return colCount; return colCount;
} }
@ -99,7 +99,7 @@ int ConstraintModel::columnCount(const QModelIndex &parent) const
// return v; // return v;
//} //}
Oid ConstraintModel::getType(int column) const Oid ConstraintModel::getType(int ) const
{ {
Oid oid = Pgsql::varchar_oid; Oid oid = Pgsql::varchar_oid;

View file

@ -84,10 +84,10 @@ Oid DatabasesTableModel::getType(int column) const
switch (column) { switch (column) {
case AllowConnCol: case AllowConnCol:
case IsTemplateCol: case IsTemplateCol:
oid = BOOLOID; oid = bool_oid;
break; break;
case ConnLimitCol: case ConnLimitCol:
oid = INT4OID; oid = int4_oid;
break; break;
case AclCol: case AclCol:
case CollateCol: case CollateCol:
@ -96,7 +96,7 @@ Oid DatabasesTableModel::getType(int column) const
case DbaCol: case DbaCol:
case NameCol: case NameCol:
case TablespaceCol: case TablespaceCol:
oid = VARCHAROID; oid = varchar_oid;
break; break;
default: default:
oid = InvalidOid; oid = InvalidOid;

View file

@ -32,8 +32,8 @@ void IconColumnDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
} }
} }
QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &option, QSize IconColumnDelegate::sizeHint(const QStyleOptionViewItem &,
const QModelIndex &index) const const QModelIndex &) const
{ {
return QSize(16, 16); return QSize(16, 16);
} }

View file

@ -14,6 +14,7 @@
#include "QueryTab.h" #include "QueryTab.h"
#include "util.h" #include "util.h"
#include "PlgPage.h" #include "PlgPage.h"
#include "CodeGenerator.h"
#include "MasterController.h" #include "MasterController.h"
#include "CrudTab.h" #include "CrudTab.h"
#include "WorkManager.h" #include "WorkManager.h"
@ -58,6 +59,14 @@ void MainWindow::newCrudPage(const PgClass &table)
addPage(ct, table.name); addPage(ct, table.name);
} }
void MainWindow::newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres)
{
auto cgtab = new CodeGenerator(this);
cgtab->Init(query, dbres);
addPage(cgtab, "Codegen");
}
QueryTab *MainWindow::GetActiveQueryTab() QueryTab *MainWindow::GetActiveQueryTab()
{ {
QWidget *widget = ui->tabWidget->currentWidget(); QWidget *widget = ui->tabWidget->currentWidget();
@ -344,3 +353,12 @@ void MainWindow::on_tabWidget_currentChanged(int index)
} }
m_previousPage = page; m_previousPage = page;
} }
void MainWindow::on_actionGenerate_code_triggered()
{
QueryTab *tab = GetActiveQueryTab();
if (tab) {
tab->generateCode();
}
}

View file

@ -46,6 +46,7 @@ public:
std::shared_ptr<OpenDatabase> getDatabase() { return m_database; } std::shared_ptr<OpenDatabase> getDatabase() { return m_database; }
void newCrudPage(const PgClass &table); void newCrudPage(const PgClass &table);
void newCodeGenPage(QString query, std::shared_ptr<const Pgsql::Result> dbres);
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
@ -116,6 +117,7 @@ private slots:
void on_actionCopy_as_C_string_triggered(); void on_actionCopy_as_C_string_triggered();
void on_actionCopy_as_raw_Cpp_string_triggered(); void on_actionCopy_as_raw_Cpp_string_triggered();
void on_tabWidget_currentChanged(int index); void on_tabWidget_currentChanged(int index);
void on_actionGenerate_code_triggered();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View file

@ -45,7 +45,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>993</width> <width>993</width>
<height>20</height> <height>25</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuTest"> <widget class="QMenu" name="menuTest">
@ -86,6 +86,7 @@
<addaction name="actionCopy"/> <addaction name="actionCopy"/>
<addaction name="actionCopy_as_C_string"/> <addaction name="actionCopy_as_C_string"/>
<addaction name="actionCopy_as_raw_Cpp_string"/> <addaction name="actionCopy_as_raw_Cpp_string"/>
<addaction name="actionGenerate_code"/>
</widget> </widget>
<addaction name="menuTest"/> <addaction name="menuTest"/>
<addaction name="menuEdit"/> <addaction name="menuEdit"/>
@ -233,6 +234,11 @@
<string>Copy as raw C++-string</string> <string>Copy as raw C++-string</string>
</property> </property>
</action> </action>
<action name="actionGenerate_code">
<property name="text">
<string>Generate code</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View file

@ -19,6 +19,7 @@ public:
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// virtual Qt::ItemFlags flags(const QModelIndex &index) const override; // virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
std::shared_ptr<const Pgsql::Result> GetPgsqlResult() const { return result; }
protected: protected:
// virtual Oid getType(int column) const override; // virtual Oid getType(int column) const override;
// virtual QVariant getData(const QModelIndex &index) const override; // virtual QVariant getData(const QModelIndex &index) const override;

View file

@ -22,6 +22,7 @@
#include "QueryParamListController.h" #include "QueryParamListController.h"
#include "util.h" #include "util.h"
#include "GlobalIoService.h" #include "GlobalIoService.h"
#include "UserConfiguration.h"
QueryTab::QueryTab(MainWindow *win, QWidget *parent) : QueryTab::QueryTab(MainWindow *win, QWidget *parent) :
PlgPage(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::onStateChanged, this, &QueryTab::connectionStateChanged);
connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTab::receiveNotice); connect(&m_dbConnection, &ASyncDBConnection::onNotice, this, &QueryTab::receiveNotice);
QFont font; ui->queryEdit->setFont(UserConfiguration::instance()->codeFont());
font.setFamily("Source Code Pro");
font.setFixedPitch(true);
font.setPointSize(10);
ui->queryEdit->setFont(font);
highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document()); highlighter = new SqlSyntaxHighlighter(ui->queryEdit->document());
auto open_database = m_win->getDatabase(); auto open_database = m_win->getDatabase();
@ -583,14 +580,30 @@ void QueryTab::copyQueryAsCString()
QApplication::clipboard()->setText(cs); QApplication::clipboard()->setText(cs);
} }
#include <codebuilder/CodeBuilder.h>
#include <codebuilder/DefaultConfigs.h>
void QueryTab::copyQueryAsRawCppString() void QueryTab::copyQueryAsRawCppString()
{ {
//auto sql = getAllOrSelectedSql();
QString command = getCommand(); QString command = getCommand();
//auto sql = getAllOrSelectedSql();
QString cs = ConvertToMultiLineRawCppString(command); QString cs = ConvertToMultiLineRawCppString(command);
QApplication::clipboard()->setText(cs); 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<const Pgsql::Result> dbres = resultList[0]->GetPgsqlResult();
m_win->newCodeGenPage(command, dbres);
}
}
void QueryTab::exportData(const QString &file_name) void QueryTab::exportData(const QString &file_name)
{ {
auto widget = ui->tabWidget->currentWidget(); auto widget = ui->tabWidget->currentWidget();

View file

@ -55,6 +55,7 @@ public:
void copyQueryAsCString(); void copyQueryAsCString();
void copyQueryAsRawCppString(); void copyQueryAsRawCppString();
void generateCode();
void exportData(const QString &filename); void exportData(const QString &filename);
QString fileName() const { return m_fileName; } QString fileName() const { return m_fileName; }

View file

@ -81,7 +81,7 @@ Oid RolesTableModel::getType(int column) const
Oid oid; Oid oid;
switch (column) { switch (column) {
case NameCol: case NameCol:
oid = VARCHAROID; oid = varchar_oid;
break; break;
case ReplicationCol: case ReplicationCol:
@ -91,15 +91,15 @@ Oid RolesTableModel::getType(int column) const
case CreateRoleCol: case CreateRoleCol:
case InheritCol: case InheritCol:
case SuperCol: case SuperCol:
oid = BOOLOID; oid = bool_oid;
break; break;
case ConnlimitCol: case ConnlimitCol:
oid = INT4OID; oid = int4_oid;
break; break;
case ValidUntilCol: case ValidUntilCol:
oid = TIMESTAMPOID; oid = timestamp_oid;
break; break;
default: default:
oid = InvalidOid; oid = InvalidOid;

View file

@ -142,7 +142,7 @@ Oid TablesTableModel::getType(int column) const
case OptionsCol: case OptionsCol:
// case AclCol: // case AclCol:
default: default:
oid = Pgsql::VARCHAROID; oid = Pgsql::varchar_oid;
} }
return oid; return oid;
} }

View file

@ -0,0 +1,26 @@
#include "UserConfiguration.h"
#include <QFont>
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;
}

28
pglab/UserConfiguration.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef USERCONFIGURATION_H
#define USERCONFIGURATION_H
#include <QSettings>
/** 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

View file

@ -9,15 +9,18 @@ QT += core gui concurrent
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
QMAKE_CXXFLAGS += /std:c++17
TARGET = pglab TARGET = pglab
TEMPLATE = app TEMPLATE = app
INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql C:\VSproj\boost32\include\boost-1_65_1 ! include( ../common.pri ) {
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501 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:/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 { #debug {
LIBS += c:/prog/lib/botand_imp.lib LIBS += c:/prog/lib/botand_imp.lib
@ -54,7 +57,6 @@ SOURCES += main.cpp\
ConnectionList.cpp \ ConnectionList.cpp \
ProcessStdioWidget.cpp \ ProcessStdioWidget.cpp \
GlobalIoService.cpp \ GlobalIoService.cpp \
CodeBuilderConfiguration.cpp \
ResultTableModelUtil.cpp \ ResultTableModelUtil.cpp \
BaseTableModel.cpp \ BaseTableModel.cpp \
QueryParamListController.cpp \ QueryParamListController.cpp \
@ -74,7 +76,9 @@ SOURCES += main.cpp\
EditorGutter.cpp \ EditorGutter.cpp \
CodeEditor.cpp \ CodeEditor.cpp \
PlgPage.cpp \ PlgPage.cpp \
PropertyProxyModel.cpp PropertyProxyModel.cpp \
CodeGenerator.cpp \
UserConfiguration.cpp
HEADERS += \ HEADERS += \
QueryResultModel.h \ QueryResultModel.h \
@ -122,7 +126,9 @@ HEADERS += \
PlgPage.h \ PlgPage.h \
AbstractCommand.h \ AbstractCommand.h \
PropertyProxyModel.h \ PropertyProxyModel.h \
CustomDataRole.h CustomDataRole.h \
CodeGenerator.h \
UserConfiguration.h
FORMS += mainwindow.ui \ FORMS += mainwindow.ui \
ConnectionManagerWindow.ui \ ConnectionManagerWindow.ui \
@ -135,7 +141,8 @@ FORMS += mainwindow.ui \
TablesPage.ui \ TablesPage.ui \
NamespaceFilterWidget.ui \ NamespaceFilterWidget.ui \
ApplicationWindow.ui \ ApplicationWindow.ui \
CrudTab.ui CrudTab.ui \
CodeGenerator.ui
RESOURCES += \ RESOURCES += \
resources.qrc resources.qrc

View file

@ -18,6 +18,8 @@ public:
void setResult(std::shared_ptr<QueryResultModel> res, float ms); void setResult(std::shared_ptr<QueryResultModel> res, float ms);
void exportData(const QString &file_name) const; void exportData(const QString &file_name) const;
std::shared_ptr<const Pgsql::Result> GetPgsqlResult() const { return resultModel->GetPgsqlResult(); }
private: private:
Ui::TuplesResultWidget *ui; Ui::TuplesResultWidget *ui;

View file

@ -2,7 +2,6 @@ TEMPLATE = subdirs
DEFINES += BOOST_ENABLE_ASSERT_HANDLER DEFINES += BOOST_ENABLE_ASSERT_HANDLER
SUBDIRS += core \ SUBDIRS += core \
ctk \ ctk \
pgsql \ pgsql \
@ -15,7 +14,6 @@ pglablib.depends = core pgsql
tests.depends = core pgsql pglablib tests.depends = core pgsql pglablib
CONFIG(debug, debug|release) { CONFIG(debug, debug|release) {
SUBDIRS += tests SUBDIRS += tests
} }

View file

@ -0,0 +1,29 @@
#include "FormatToStream.h"
#include <QRegularExpression>
#include <QTextStream>
void FormatToStream(QTextStream &stream, QString format, std::function<void(QTextStream &, QString)> 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);
}

17
pglablib/FormatToStream.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef FORMATTOSTREAM_H
#define FORMATTOSTREAM_H
#include <QString>
#include <functional>
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<void(QTextStream &, QString)> field_callback);
#endif // FORMATTOSTREAM_H

View file

@ -24,8 +24,8 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
>> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence >> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence
>> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid; >> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid;
col.getAsArray<QString>(std::back_inserter(v.acl), Pgsql::NullHandling::Ignore); col.getAsArray<QString>(std::back_inserter(v.acl), QString());
col.getAsArray<QString>(std::back_inserter(v.options), Pgsql::NullHandling::Ignore); col.getAsArray<QString>(std::back_inserter(v.options), QString());
auto cat = m_catalogue.lock(); auto cat = m_catalogue.lock();
auto ns = cat->namespaces()->getByKey(v.relnamespace); auto ns = cat->namespaces()->getByKey(v.relnamespace);

View file

@ -24,6 +24,8 @@ class PgContainer: public IPgContainter {
public: public:
using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access using t_Container = std::vector<T>; ///< Do not assume it will stay a vector only expect bidirectional access
PgContainer() = default;
explicit PgContainer(std::weak_ptr<PgDatabaseCatalog> cat) explicit PgContainer(std::weak_ptr<PgDatabaseCatalog> cat)
: m_catalogue(cat) : m_catalogue(cat)
{} {}
@ -87,6 +89,13 @@ public:
std::sort(m_container.begin(), m_container.end()); 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: protected:
std::weak_ptr<PgDatabaseCatalog> m_catalogue; std::weak_ptr<PgDatabaseCatalog> m_catalogue;
t_Container m_container; t_Container m_container;

View file

@ -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 <QTextStream>
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<const StructureTemplate> 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<const StructureTemplate> 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<size_t>(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;
}

View file

@ -0,0 +1,47 @@
#ifndef CODEBUILDER_H
#define CODEBUILDER_H
#include "Pgsql_declare.h"
#include <QString>
#include <vector>
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<ColumnData>;
ColumnDataList createColumnDataList(const Pgsql::Result &result) const;
void setLanguageConfig(std::shared_ptr<const LanguageConfig> 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<const LanguageConfig> 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

View file

@ -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<const TypeMappings> GetPglabCppTypeMappings()
{
auto tm = std::make_shared<TypeMappings>();
*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<StructureTemplate> buildPglabStructureTemplate()
{
auto t = std::make_shared<StructureTemplate>();
t->m_structTemplate =
R"__(class /%structname%/ {
public:
/%structfields%/
};
using /%structname%/Lst = std::vector</%structname%/>;
)__";
t->m_fieldTemplate = "/%typename%/ /%varname%/;";
//st_templ->m_fieldSeparator;
return t;
}
std::shared_ptr<ResultLoopTemplate> buildPglabResultLoopTemplate()
{
auto t = std::make_shared<ResultLoopTemplate>();
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<LanguageConfig> buildPglabCppLanguageConfig()
{
auto config = std::make_shared<LanguageConfig>();
config->setTypeMappings(GetPglabCppTypeMappings());
config->setNameManglingRules(std::make_shared<NameManglingRules>());
config->setStructureTemplate(buildPglabStructureTemplate());
config->setIndentationConfig(std::make_shared<IndentationConfig>());
config->setResultLoopTemplate(buildPglabResultLoopTemplate());
return config;
}
std::shared_ptr<const LanguageConfig> getPglabCppLanguageConfig()
{
static auto config = buildPglabCppLanguageConfig();
return config;
}

View file

@ -0,0 +1,11 @@
#ifndef DEFAULTCONFIGS_H
#define DEFAULTCONFIGS_H
#include <memory>
class LanguageConfig;
//std::shared_ptr<const LanguageConfig> getDefaultCppLanguageConfig();
std::shared_ptr<const LanguageConfig> getPglabCppLanguageConfig();
#endif // DEFAULTCONFIGS_H

View file

@ -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, ' ');
}

View file

@ -0,0 +1,23 @@
#ifndef INDENTATIONCONFIG_H
#define INDENTATIONCONFIG_H
#include <QString>
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

View file

@ -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<const NameManglingRules> name_mangling_rules)
{
m_varNaming = name_mangling_rules;
}
std::shared_ptr<const TypeMappings> LanguageConfig::typeMappings() const
{
return m_typeMappings;
}
void LanguageConfig::setTypeMappings(std::shared_ptr<const TypeMappings> type_mappings)
{
m_typeMappings = type_mappings;
}
std::shared_ptr<const StructureTemplate> LanguageConfig::structureTemplate() const
{
return m_structureTemplate;
}
void LanguageConfig::setStructureTemplate(std::shared_ptr<const StructureTemplate> structure_template)
{
m_structureTemplate = structure_template;
}
std::shared_ptr<const IndentationConfig> LanguageConfig::indentationConfig() const
{
return m_indentationConfig;
}
void LanguageConfig::setIndentationConfig(std::shared_ptr<const IndentationConfig> indentation_config)
{
m_indentationConfig = indentation_config;
}
std::shared_ptr<const ResultLoopTemplate> LanguageConfig::resultLoopTemplate() const
{
return m_resultLoopTemplate;
}
void LanguageConfig::setResultLoopTemplate(std::shared_ptr<const ResultLoopTemplate> result_loop_template)
{
m_resultLoopTemplate = result_loop_template;
}

View file

@ -0,0 +1,48 @@
#ifndef LANGUAGECONFIG_H
#define LANGUAGECONFIG_H
#include <QString>
#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<const NameManglingRules> name_mangling_rules);
std::shared_ptr<const TypeMappings> typeMappings() const;
void setTypeMappings(std::shared_ptr<const TypeMappings> type_mappings);
std::shared_ptr<const StructureTemplate> structureTemplate() const;
void setStructureTemplate(std::shared_ptr<const StructureTemplate> structure_template);
std::shared_ptr<const IndentationConfig> indentationConfig() const;
void setIndentationConfig(std::shared_ptr<const IndentationConfig> indentation_config);
std::shared_ptr<const ResultLoopTemplate> resultLoopTemplate() const;
void setResultLoopTemplate(std::shared_ptr<const ResultLoopTemplate> result_loop_template);
private:
/** Default template for declaring a variable of the correct type.
* exmaple: "{$type} {$varname};"
*/
//QString varDeclTemplate;
std::shared_ptr<const NameManglingRules> m_varNaming;
std::shared_ptr<const TypeMappings> m_typeMappings;
std::shared_ptr<const StructureTemplate> m_structureTemplate;
std::shared_ptr<const IndentationConfig> m_indentationConfig;
std::shared_ptr<const ResultLoopTemplate> m_resultLoopTemplate;
enum class VariableStrategy {
UseLocalVariables,
DeclareClass
};
};
#endif // LANGUAGECONFIG_H

View file

@ -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;
}

View file

@ -0,0 +1,49 @@
#ifndef NAMEMANGLINGRULES_H
#define NAMEMANGLINGRULES_H
#include <QString>
#include <QRegularExpression>
#include <vector>
/** 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<ReplaceRule>;
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

View file

@ -0,0 +1,26 @@
#ifndef RESULTLOOPTEMPLATE_H
#define RESULTLOOPTEMPLATE_H
#include <QString>
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

View file

@ -0,0 +1,2 @@
#include "StructureTemplate.h"

View file

@ -0,0 +1,17 @@
#ifndef STRUCTURETEMPLATE_H
#define STRUCTURETEMPLATE_H
#include <QString>
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

View file

@ -0,0 +1,133 @@
#include "TypeMappings.h"
#include "PgTypeContainer.h"
//namespace {
// using Fallbacks = std::unordered_map<Oid, Oid>;
// Fallbacks fallbacks = {
// { oid_text, oid_varchar },
// { }
// };
//}
TypeMappings::TypeMappings() = default;
TypeMappings::TypeMappings(std::initializer_list<Mapping> 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<const PgTypeContainer> 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;

View file

@ -0,0 +1,46 @@
#ifndef TYPEMAPPINGS_H
#define TYPEMAPPINGS_H
#include <Pgsql_declare.h>
#include <QString>
#include <initializer_list>
#include <unordered_map>
class PgTypeContainer;
class TypeMappings {
public:
using TypeMap = std::unordered_map<Oid, QString>;
using Mapping = std::pair<Oid, QString>;
TypeMappings();
TypeMappings(std::initializer_list<Mapping> mappings);
QString getTypeForOid(Oid oid) const;
const TypeMap& typeMap() const { return m_typeMap; }
QString defaultStringType() const { return m_defaultStringType; }
void setTypes(std::shared_ptr<const PgTypeContainer> 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<const PgTypeContainer> m_types;
};
#endif // TYPEMAPPINGS_H

View file

@ -10,15 +10,11 @@ TARGET = pglablib
TEMPLATE = lib TEMPLATE = lib
CONFIG += staticlib 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 INCLUDEPATH += C:\Prog\include\pgsql
# 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
# You can also make your code fail to compile if you use deprecated APIs. # You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line. # In order to do so, uncomment the following line.
@ -57,7 +53,15 @@ SOURCES += \
PgAmContainer.cpp \ PgAmContainer.cpp \
PgObject.cpp \ PgObject.cpp \
PgTablespace.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 += \ HEADERS += \
Pglablib.h \ Pglablib.h \
@ -92,7 +96,16 @@ HEADERS += \
PgAmContainer.h \ PgAmContainer.h \
PgObject.h \ PgObject.h \
PgTablespace.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 { unix {
target.path = /usr/lib target.path = /usr/lib

View file

@ -26,14 +26,14 @@ namespace Pgsql {
} }
template <typename E, typename I> template <typename E, typename I>
void getAsArray(I insert_iter, const E &value_for_nulls) const Col& getAsArray(I insert_iter, const E &value_for_nulls)
{ {
nextValue().getAsArray<E, I>(insert_iter, value_for_nulls); nextValue().getAsArray<E, I>(insert_iter, value_for_nulls);
return *this; return *this;
} }
template <typename E, typename I> template <typename E, typename I>
void getAsArrayOfOptional(I insert_iter) const Col& getAsArrayOfOptional(I insert_iter)
{ {
nextValue().getAsArrayOfOptional<E, I>(insert_iter); nextValue().getAsArrayOfOptional<E, I>(insert_iter);
return *this; return *this;
@ -50,6 +50,12 @@ namespace Pgsql {
int col = -1; int col = -1;
}; };
template <typename T>
Col& operator>>(Col &c, std::vector<T> &s)
{
return c.getAsArray<T>(std::back_inserter(s));
}
template <typename T> template <typename T>
Col& operator>>(Col &c, T &s) Col& operator>>(Col &c, T &s)
{ {

View file

@ -73,7 +73,7 @@ namespace Pgsql {
* *
* The class takes ownership of data and will try to delete[] it. * 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 } // end namespace Pgsql

View file

@ -103,9 +103,9 @@ Value::operator double() const
bool Value::isString() const bool Value::isString() const
{ {
return m_typ == CHAROID return m_typ == char_oid
|| m_typ == VARCHAROID || m_typ == varchar_oid
|| m_typ == TEXTOID || m_typ == text_oid
; ;
} }

View file

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include <boost/optional.hpp>
namespace Pgsql { namespace Pgsql {
@ -47,7 +48,7 @@ namespace Pgsql {
{ {
if (m_val == nullptr) { if (m_val == nullptr) {
if (nullhandling == NullHandling::Throw) if (nullhandling == NullHandling::Throw)
throw std::runtime_error("Unexpected NULL value in array"); throw std::runtime_error("Unexpected NULL value for array");
} }
else { else {
using value_type = E; using value_type = E;
@ -76,6 +77,8 @@ namespace Pgsql {
template <typename E, typename I> template <typename E, typename I>
void getAsArray(I insert_iter, const E &value_for_nulls) const void getAsArray(I insert_iter, const E &value_for_nulls) const
{ {
if (m_val == nullptr) return;
using value_type = E; using value_type = E;
ArrayParser parser(m_val); ArrayParser parser(m_val);
for (;;) { for (;;) {
@ -149,10 +152,24 @@ namespace Pgsql {
Oid m_typ; Oid m_typ;
}; };
template <typename T>
void operator<<(boost::optional<T> &s, const Value &v)
{
if (v.null())
s = boost::optional<T>();
else
*s << v;
}
template <typename T>
void operator<<(std::vector<T> &s, const Value &v)
{
v.getAsArray(std::back_inserter(s));
}
template <typename T> template <typename T>
void operator<<(T &s, const Value &v) void operator<<(T &s, const Value &v)
{ {
//s = static_cast<T>(v);
s = v.operator T(); s = v.operator T();
} }

View file

@ -5,95 +5,95 @@
namespace Pgsql { namespace Pgsql {
const Oid BOOLOID = 16; // const Oid BOOLOID = 16;
const Oid BYTEAOID = 17; // const Oid BYTEAOID = 17;
const Oid CHAROID = 18; // const Oid CHAROID = 18;
const Oid NAMEOID = 19; // const Oid NAMEOID = 19;
const Oid INT8OID = 20; // const Oid INT8OID = 20;
const Oid INT2OID = 21; // const Oid INT2OID = 21;
const Oid INT2VECTOROID = 22; // const Oid INT2VECTOROID = 22;
const Oid INT4OID = 23; // const Oid INT4OID = 23;
const Oid REGPROCOID = 24; // const Oid REGPROCOID = 24;
const Oid TEXTOID = 25; // const Oid TEXTOID = 25;
const Oid OIDOID = 26; // const Oid OIDOID = 26;
const Oid TIDOID = 27; // const Oid TIDOID = 27;
const Oid XIDOID = 28; // const Oid XIDOID = 28;
const Oid CIDOID = 29; // const Oid CIDOID = 29;
const Oid OIDVECTOROID = 30; // const Oid OIDVECTOROID = 30;
const Oid JSONOID = 114; // const Oid JSONOID = 114;
const Oid XMLOID = 142; // const Oid XMLOID = 142;
const Oid PGNODETREEOID = 194; // const Oid PGNODETREEOID = 194;
const Oid PGDDLCOMMANDOID = 32; // const Oid PGDDLCOMMANDOID = 32;
const Oid POINTOID = 600; // const Oid POINTOID = 600;
const Oid LSEGOID = 601; // const Oid LSEGOID = 601;
const Oid PATHOID = 602; // const Oid PATHOID = 602;
const Oid BOXOID = 603; // const Oid BOXOID = 603;
const Oid POLYGONOID = 604; // const Oid POLYGONOID = 604;
const Oid LINEOID = 628; // const Oid LINEOID = 628;
const Oid FLOAT4OID = 700; // const Oid FLOAT4OID = 700;
const Oid FLOAT8OID = 701; // const Oid FLOAT8OID = 701;
const Oid ABSTIMEOID = 702; // const Oid ABSTIMEOID = 702;
const Oid RELTIMEOID = 703; // const Oid RELTIMEOID = 703;
const Oid TINTERVALOID = 704; // const Oid TINTERVALOID = 704;
const Oid UNKNOWNOID = 705; // const Oid UNKNOWNOID = 705;
const Oid CIRCLEOID = 718; // const Oid CIRCLEOID = 718;
const Oid CASHOID = 790; // const Oid CASHOID = 790;
const Oid MACADDROID = 829; // const Oid MACADDROID = 829;
const Oid INETOID = 869; // const Oid INETOID = 869;
const Oid CIDROID = 650; // const Oid CIDROID = 650;
const Oid INT2ARRAYOID = 1005; // const Oid INT2ARRAYOID = 1005;
const Oid INT4ARRAYOID = 1007; // const Oid INT4ARRAYOID = 1007;
const Oid TEXTARRAYOID = 1009; // const Oid TEXTARRAYOID = 1009;
const Oid OIDARRAYOID = 1028; // const Oid OIDARRAYOID = 1028;
const Oid FLOAT4ARRAYOID = 1021; // const Oid FLOAT4ARRAYOID = 1021;
const Oid ACLITEMOID = 1033; // const Oid ACLITEMOID = 1033;
const Oid CSTRINGARRAYOID = 1263; // const Oid CSTRINGARRAYOID = 1263;
const Oid BPCHAROID = 1042; // const Oid BPCHAROID = 1042;
const Oid VARCHAROID = 1043; // const Oid VARCHAROID = 1043;
const Oid DATEOID = 1082; // const Oid DATEOID = 1082;
const Oid TIMEOID = 1083; // const Oid TIMEOID = 1083;
const Oid TIMESTAMPOID = 1114; // const Oid TIMESTAMPOID = 1114;
const Oid TIMESTAMPTZOID = 1184; // const Oid TIMESTAMPTZOID = 1184;
const Oid INTERVALOID = 1186; // const Oid INTERVALOID = 1186;
const Oid TIMETZOID = 1266; // const Oid TIMETZOID = 1266;
const Oid BITOID = 1560; // const Oid BITOID = 1560;
const Oid VARBITOID = 1562; // const Oid VARBITOID = 1562;
const Oid NUMERICOID = 1700; // const Oid NUMERICOID = 1700;
const Oid REFCURSOROID = 1790; // const Oid REFCURSOROID = 1790;
const Oid REGPROCEDUREOID = 2202; // const Oid REGPROCEDUREOID = 2202;
const Oid REGOPEROID = 2203; // const Oid REGOPEROID = 2203;
const Oid REGOPERATOROID = 2204; // const Oid REGOPERATOROID = 2204;
const Oid REGCLASSOID = 2205; // const Oid REGCLASSOID = 2205;
const Oid REGTYPEOID = 2206; // const Oid REGTYPEOID = 2206;
const Oid REGROLEOID = 4096; // const Oid REGROLEOID = 4096;
const Oid REGNAMESPACEOID = 4089; // const Oid REGNAMESPACEOID = 4089;
const Oid REGTYPEARRAYOID = 2211; // const Oid REGTYPEARRAYOID = 2211;
const Oid UUIDOID = 2950; // const Oid UUIDOID = 2950;
const Oid LSNOID = 3220; // const Oid LSNOID = 3220;
const Oid TSVECTOROID = 3614; // const Oid TSVECTOROID = 3614;
const Oid GTSVECTOROID = 3642; // const Oid GTSVECTOROID = 3642;
const Oid TSQUERYOID = 3615; // const Oid TSQUERYOID = 3615;
const Oid REGCONFIGOID = 3734; // const Oid REGCONFIGOID = 3734;
const Oid REGDICTIONARYOID = 3769; // const Oid REGDICTIONARYOID = 3769;
const Oid JSONBOID = 3802; // const Oid JSONBOID = 3802;
const Oid INT4RANGEOID = 3904; // const Oid INT4RANGEOID = 3904;
const Oid RECORDOID = 2249; // const Oid RECORDOID = 2249;
const Oid RECORDARRAYOID = 2287; // const Oid RECORDARRAYOID = 2287;
const Oid CSTRINGOID = 2275; // const Oid CSTRINGOID = 2275;
const Oid ANYOID = 2276; // const Oid ANYOID = 2276;
const Oid ANYARRAYOID = 2277; // const Oid ANYARRAYOID = 2277;
const Oid VOIDOID = 2278; // const Oid VOIDOID = 2278;
const Oid TRIGGEROID = 2279; // const Oid TRIGGEROID = 2279;
const Oid EVTTRIGGEROID = 3838; // const Oid EVTTRIGGEROID = 3838;
const Oid LANGUAGE_HANDLEROID = 2280; // const Oid LANGUAGE_HANDLEROID = 2280;
const Oid INTERNALOID = 2281; // const Oid INTERNALOID = 2281;
const Oid OPAQUEOID = 2282; // const Oid OPAQUEOID = 2282;
const Oid ANYELEMENTOID = 2283; // const Oid ANYELEMENTOID = 2283;
const Oid ANYNONARRAYOID = 2776; // const Oid ANYNONARRAYOID = 2776;
const Oid ANYENUMOID = 3500; // const Oid ANYENUMOID = 3500;
const Oid FDW_HANDLEROID = 3115; // const Oid FDW_HANDLEROID = 3115;
const Oid TSM_HANDLEROID = 3310; // const Oid TSM_HANDLEROID = 3310;
const Oid ANYRANGEOID = 3831; // const Oid ANYRANGEOID = 3831;
class Params; class Params;

View file

@ -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(), 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; return l.array < r.array;
}); });
if (iter == g_ArrayToElem.end()) if (iter == g_ArrayToElem.end())
throw std::runtime_error("ElemOidFromArrayOid Oid not found"); return InvalidOid;
return iter->elem; 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(), auto iter = std::lower_bound(g_ElemToArray.begin(), g_ElemToArray.end(),
ElemArray{oid, oid}, ElemArray{oid, oid},
@ -186,7 +186,6 @@ Oid Pgsql::ArrayOidFromElemOid(Oid oid)
return l.elem < r.elem; return l.elem < r.elem;
}); });
if (iter == g_ElemToArray.end()) if (iter == g_ElemToArray.end())
throw std::runtime_error("ElemOidFromArrayOid Oid not found"); return InvalidOid;
return iter->array; return iter->array;
} }

View file

@ -55,6 +55,7 @@ namespace Pgsql {
constexpr Oid regoperator_oid = 2204; constexpr Oid regoperator_oid = 2204;
constexpr Oid regclass_oid = 2205; constexpr Oid regclass_oid = 2205;
constexpr Oid regtype_oid = 2206; constexpr Oid regtype_oid = 2206;
constexpr Oid any_oid = 2276;
constexpr Oid uuid_oid = 2950; constexpr Oid uuid_oid = 2950;
constexpr Oid txid_snapshot_oid = 2970; constexpr Oid txid_snapshot_oid = 2970;
constexpr Oid pg_lsn_oid = 3220; constexpr Oid pg_lsn_oid = 3220;
@ -140,8 +141,19 @@ namespace Pgsql {
constexpr Oid regnamespace_array_oid = 4090; constexpr Oid regnamespace_array_oid = 4090;
constexpr Oid regrole_array_oid = 4097; constexpr Oid regrole_array_oid = 4097;
Oid ElemOidFromArrayOid(Oid oid); /** If oid is an array oid then it returns the corresponding element oid.
Oid ArrayOidFromElemOid(Oid 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 <typename T> template <typename T>
class OidFor { class OidFor {

View file

@ -12,13 +12,10 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql
TARGET = pgsql TARGET = pgsql
TEMPLATE = lib TEMPLATE = lib
INCLUDEPATH += C:\prog\include \ ! include( ../common.pri ) {
C:\Prog\include\pgsql \ error( "Couldn't find the common.pri file!" )
C:\VSproj\boost32\include\boost-1_65_1 }
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/boost/lib -Lc:/prog/lib libpq.lib fmt.lib User32.lib ws2_32.lib
LIBS += -LC:/PROG/LIB -lws2_32 -llibpq LIBS += -LC:/PROG/LIB -lws2_32 -llibpq

View file

@ -1,16 +1,19 @@
! include( ../../common.pri ) {
error( "Couldn't find the common.pri file!" )
}
include(gtest_dependency.pri) include(gtest_dependency.pri)
TEMPLATE = app TEMPLATE = app
CONFIG += console c++11 CONFIG += console c++17
CONFIG -= app_bundle CONFIG -= app_bundle
CONFIG += thread CONFIG += thread
CONFIG += qt CONFIG += qt
QT += core QT += core
QMAKE_CXXFLAGS += /std:c++17 INCLUDEPATH += C:\Prog\include\pgsql
INCLUDEPATH += C:\prog\include C:\Prog\include\pgsql
HEADERS += HEADERS +=

View file

@ -11,14 +11,14 @@ using namespace Pgsql;
TEST(Pgsql_Value, test_int4) TEST(Pgsql_Value, test_int4)
{ {
Pgsql::Value v("1", INT4OID); Pgsql::Value v("1", int4_oid);
int i = (int)v; int i = (int)v;
ASSERT_EQ(i, 1); ASSERT_EQ(i, 1);
} }
TEST(Pgsql_Value, test_QDateTime) 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; QDateTime dt = (QDateTime)v;
ASSERT_EQ(dt, QDateTime(QDate(2017, 10, 22), QTime(12, 34, 56))); 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) 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 dt = (QDateTime)v;
QDateTime exp(QDate(2017, 12, 1), QTime(9, 38, 17, 340), QDateTime exp(QDate(2017, 12, 1), QTime(9, 38, 17, 340),
Qt::OffsetFromUTC, 2*3600); Qt::OffsetFromUTC, 2*3600);
@ -35,21 +35,21 @@ TEST(Pgsql_Value, test_QDateTime_2)
TEST(Pgsql_Value, test_QDate) TEST(Pgsql_Value, test_QDate)
{ {
Pgsql::Value v("2017-10-22", DATEOID); Pgsql::Value v("2017-10-22", date_oid);
QDate d = v; QDate d = v;
ASSERT_EQ(d, QDate(2017, 10, 22)); ASSERT_EQ(d, QDate(2017, 10, 22));
} }
TEST(Pgsql_Value, test_QTime) TEST(Pgsql_Value, test_QTime)
{ {
Pgsql::Value v("12:34:56", TIMEOID); Pgsql::Value v("12:34:56", time_oid);
QTime t = v; QTime t = v;
ASSERT_EQ(t, QTime(12, 34, 56)); ASSERT_EQ(t, QTime(12, 34, 56));
} }
TEST(Pgsql_Value, test_QTimeMS) 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; QTime t = v;
ASSERT_EQ(t, QTime(9, 38, 17, 340)); ASSERT_EQ(t, QTime(9, 38, 17, 340));
} }
@ -58,19 +58,19 @@ TEST(Pgsql_Value, test_QTimeMS)
TEST(Pgsql_Value, isString_int4) TEST(Pgsql_Value, isString_int4)
{ {
Pgsql::Value v("1", INT4OID); Pgsql::Value v("1", int4_oid);
ASSERT_EQ(v.isString(), false); ASSERT_EQ(v.isString(), false);
} }
TEST(Pgsql_Value, isString_varchar) TEST(Pgsql_Value, isString_varchar)
{ {
Pgsql::Value v("1", VARCHAROID); Pgsql::Value v("1", varchar_oid);
ASSERT_EQ(v.isString(), true); ASSERT_EQ(v.isString(), true);
} }
TEST(Pgsql_Value, getAsVector_Ints) TEST(Pgsql_Value, getAsVector_Ints)
{ {
Pgsql::Value v("1 2", ANYOID); Pgsql::Value v("1 2", any_oid);
std::vector<int> r; std::vector<int> r;
v.getAsVector<int>(std::back_inserter(r)); v.getAsVector<int>(std::back_inserter(r));
@ -83,7 +83,7 @@ TEST(Pgsql_Value, getAsVector_Ints)
TEST(Pgsql_Value, getAsArray_Ints) TEST(Pgsql_Value, getAsArray_Ints)
{ {
Pgsql::Value v("{1,2}", TEXTARRAYOID); Pgsql::Value v("{1,2}", text_array_oid);
std::vector<int> r; std::vector<int> r;
v.getAsArray<int>(std::back_inserter(r)); v.getAsArray<int>(std::back_inserter(r));
@ -92,9 +92,10 @@ TEST(Pgsql_Value, getAsArray_Ints)
ASSERT_EQ(r[1], 2); ASSERT_EQ(r[1], 2);
} }
TEST(Pgsql_Value, getAsArray_QDateTime) 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<QDateTime> r; std::set<QDateTime> r;
v.getAsArray<QDateTime>(std::inserter(r, r.end())); v.getAsArray<QDateTime>(std::inserter(r, r.end()));
@ -103,7 +104,7 @@ TEST(Pgsql_Value, getAsArray_QDateTime)
TEST(Pgsql_Value, getAsArray_throws_on_NULL) 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<int> r; std::vector<int> r;
try { try {
v.getAsArray<int>(std::back_inserter(r)); v.getAsArray<int>(std::back_inserter(r));
@ -117,7 +118,7 @@ TEST(Pgsql_Value, getAsArray_throws_on_NULL)
TEST(Pgsql_Value, getAsArray_default_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<int> r; std::vector<int> r;
try { try {
v.getAsArray<int>(std::back_inserter(r), -1); v.getAsArray<int>(std::back_inserter(r), -1);
@ -132,7 +133,7 @@ TEST(Pgsql_Value, getAsArray_default_on_NULL)
TEST(Pgsql_Value, getAsArray_ignore_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<int> r; std::vector<int> r;
try { try {
v.getAsArray<int>(std::back_inserter(r), NullHandling::Ignore); v.getAsArray<int>(std::back_inserter(r), NullHandling::Ignore);
@ -146,7 +147,7 @@ TEST(Pgsql_Value, getAsArray_ignore_NULL)
TEST(Pgsql_Value, getAsArrayOptional) TEST(Pgsql_Value, getAsArrayOptional)
{ {
Pgsql::Value v("{1,NULL,2}", TEXTARRAYOID); Pgsql::Value v("{1,NULL,2}", text_array_oid);
std::vector<std::optional<int>> r; std::vector<std::optional<int>> r;
try { try {
v.getAsArrayOfOptional<int>(std::back_inserter(r)); v.getAsArrayOfOptional<int>(std::back_inserter(r));

View file

@ -1,12 +1,19 @@
include(gtest_dependency.pri) include(gtest_dependency.pri)
TEMPLATE = app TEMPLATE = app
CONFIG += console c++14 CONFIG += console c++17
CONFIG -= app_bundle CONFIG -= app_bundle
CONFIG += thread CONFIG += thread
CONFIG += qt CONFIG += qt
QT += core QT += core widgets
QMAKE_CXXFLAGS += /std:c++17
! include( ../../common.pri ) {
error( "Couldn't find the common.pri file!" )
}
HEADERS += HEADERS +=
@ -17,14 +24,17 @@ SOURCES += main.cpp \
tst_scopeguard.cpp \ tst_scopeguard.cpp \
tst_CsvWriter.cpp \ tst_CsvWriter.cpp \
tst_PasswordManager.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 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 else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../core/debug/ -lcore
INCLUDEPATH += C:\prog\include \ INCLUDEPATH += C:\prog\include \
C:\Prog\include\pgsql \ C:\Prog\include\pgsql \
C:\VSproj\boost32\include\boost-1_65_1 C:\VSproj\boost32\include
INCLUDEPATH += $$PWD/../../core INCLUDEPATH += $$PWD/../../core
DEPENDPATH += $$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(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: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 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

View file

@ -0,0 +1,172 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "PrintTo_Qt.h"
#include <QRegularExpression>
#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 <QTextStream>
void FormatToStream(QTextStream &stream, QString format, std::function<void(QTextStream &, QString)> 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, " ");
}

View file

@ -3,6 +3,7 @@
#include "CsvWriter.h" #include "CsvWriter.h"
#include <QTextStream> #include <QTextStream>
#include <QByteArray> #include <QByteArray>
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;

View file

@ -2,6 +2,7 @@
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "ExplainTreeModelItem.h" #include "ExplainTreeModelItem.h"
#include "json/json.h" #include "json/json.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;

View file

@ -0,0 +1,81 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#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<QString, NameManglingRules::CaseConversion, bool> > {
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);
}

View file

@ -2,6 +2,7 @@
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "ParamListModel.h" #include "ParamListModel.h"
#include "ParamListJson.h" #include "ParamListJson.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "PasswordManager.h" #include "PasswordManager.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "SqlLexer.h" #include "SqlLexer.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;
@ -53,7 +54,7 @@ TEST(SqlLexer, lexer_comma_handling)
lexer.nextBasicToken(startpos, length, tokentype, out); lexer.nextBasicToken(startpos, length, tokentype, out);
ASSERT_THAT(startpos, Eq(3)); ASSERT_THAT(startpos, Eq(3));
ASSERT_THAT(length, Eq(1)); ASSERT_THAT(length, Eq(1));
ASSERT_THAT(tokentype, Eq(BasicTokenType::Self)); ASSERT_THAT(tokentype, Eq(BasicTokenType::Comma));
ASSERT_THAT(out, Eq(QString(","))); ASSERT_THAT(out, Eq(QString(",")));
lexer.nextBasicToken(startpos, length, tokentype, out); lexer.nextBasicToken(startpos, length, tokentype, out);

View file

@ -0,0 +1,71 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
//#include <QRegularExpression>
#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<PgTypeContainer>();
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<int>");
}

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "Expected.h" #include "Expected.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock-matchers.h> #include <gmock/gmock-matchers.h>
#include "ScopeGuard.h" #include "ScopeGuard.h"
#include "PrintTo_Qt.h"
using namespace testing; using namespace testing;