diff --git a/.gitignore b/.gitignore index 645129f..8bbf816 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ srcdoc/ pglabAll.pro.user.4.8-pre1 *.user /pglabAll.pro.user* +.antlr/ +**/.generated/* diff --git a/common.pri b/common.pri index e95c368..5edad44 100644 --- a/common.pri +++ b/common.pri @@ -5,8 +5,6 @@ error( "Use local.pri.sample to create your own local.pri" ) LIBS += -lUser32 -lws2_32 -llibpq CONFIG += c++17 -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 diff --git a/core/SqlLexer.h b/core/SqlLexer.h index acc5089..7328100 100644 --- a/core/SqlLexer.h +++ b/core/SqlLexer.h @@ -19,7 +19,9 @@ enum class BasicTokenType { Comma, Cast, WhiteSpace, - NewLine + NewLine, + + LastLexerToken }; enum class LexerState { diff --git a/core/SqlParser.cpp b/core/SqlParser.cpp index 5b63c68..150fc58 100644 --- a/core/SqlParser.cpp +++ b/core/SqlParser.cpp @@ -26,7 +26,24 @@ Keyword isKeyword(const QString &symbol) return Keyword::NotAKeyword; } +/* +Put tokens on a stack +Every time something is put on the stack see if it matches a rule + +The stack needs to contain both tokens from the lexical analyzer as tokens for reductions done by the parser. + +Matching rules, as we need to match against the top of the stack we should match the rules end to start. +Meaning if we have on the stack A B C then we need to consider rules ending with a C + + +*/ + +class StackItem { +public: + int Token; + +}; SqlParser::SqlParser(SqlLexer &lexer) : lexer(lexer) diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h index 5e5a9c4..cef7eec 100644 --- a/pglablib/ConnectionConfig.h +++ b/pglablib/ConnectionConfig.h @@ -36,6 +36,7 @@ class ConnectionConfig; class ConnectionNode { public: virtual ~ConnectionNode() = default; + }; class ConnectionGroup: public ConnectionNode { @@ -115,7 +116,10 @@ public: bool dirty() const; void clean(); - bool operator==(QUuid id) const { return m_uuid == id; } + bool operator==(const ConnectionConfig &rhs) const + { + return m_uuid == rhs.m_uuid; + } QString makeLongDescription() const; QByteArray encodedPassword() const; diff --git a/pglablib/PgsqlLexer.g4 b/pglablib/PgsqlLexer.g4 new file mode 100644 index 0000000..91109e7 --- /dev/null +++ b/pglablib/PgsqlLexer.g4 @@ -0,0 +1,77 @@ +lexer grammar PgsqlLexer; + +@lexer::preinclude { +#include +} + + +SemiColon: ';'; +Comma: ','; +Dot: '.'; +OpenParen: '('; +CloseParen: ')'; + +fragment A : [aA]; +fragment B : [bB]; +fragment C : [cC]; +fragment D : [dD]; +fragment E : [eE]; +fragment F : [fF]; +fragment G : [gG]; +fragment H : [hH]; +fragment I : [iI]; +fragment J : [jJ]; +fragment K : [kK]; +fragment L : [lL]; +fragment M : [mM]; +fragment N : [nN]; +fragment O : [oO]; +fragment P : [pP]; +fragment Q : [qQ]; +fragment R : [rR]; +fragment S : [sS]; +fragment T : [tT]; +fragment U : [uU]; +fragment V : [vV]; +fragment W : [wW]; +fragment X : [xX]; +fragment Y : [yY]; +fragment Z : [zZ]; + +As: A S; +By: B Y; +Cross: C R O S S; +From: F R O M; +Full: F U L L; +Group: G R O U P; +Having: H A V I N G; +Inner: I N N E R; +Join: J O I N; +Left : L E F T; +Natural : N A T U R A L; +On : O N; +Order : O R D E R; +Outer : O U T E R; +Right : R I G H T; +Select : S E L E C T; +Using : U S I N G; +Where : W H E R E; + +Ident: [\p{Alpha}]~[\p{White_Space}]* + { + setText(QString::fromStdString(getText()).toLower().toStdString()); + } + | '"' ~["]+ '"' + { + { + std::string s = getText(); + s = s.substr(1, s.length() - 2); + setText(s); + } + }; +IntegerLiteral: [1-9][0-9]*; +StringLiteral: '\'' ('\'\'' | ~['])+ '\'' { setText(getText().substr(1, getText().length()-2)); }; + +Whitespace: [\p{White_Space}] -> skip ; // skip spaces, tabs, newlines + +UnexpectedSymbol: .; \ No newline at end of file diff --git a/pglablib/PgsqlParser.g4 b/pglablib/PgsqlParser.g4 new file mode 100644 index 0000000..0b2c947 --- /dev/null +++ b/pglablib/PgsqlParser.g4 @@ -0,0 +1,119 @@ +// Define a grammar called postgresql +parser grammar PgsqlParser; + +options { + tokenVocab = PgsqlLexer; +} + +@parser::preinclude { +#include "sqlast/sqlast.h" +} + +@parser::includes { +} + +@parser::members { +} + +main returns [std::unique_ptr program] + : statement_list { $program = std::move($statement_list.result); } + ; + +statement_list returns [std::unique_ptr result] + : { $result = std::make_unique(); } + (statement SemiColon { $result->Add(std::move($statement.result)); } | empty_statement)* + (statement SemiColon? { $result->Add(std::move($statement.result)); } | empty_statement ) + ; + +statement returns [std::unique_ptr result] + : select_stmt { $result = std::move($select_stmt.result); } + ; + +empty_statement + : SemiColon + ; + +select_stmt returns [std::unique_ptr result] + : Select select_list + { + $result = std::make_unique(); + $result->SetSelectList(std::move($select_list.result)); + } + (From from_item (Comma from_item)* )? + (Where condition)? + (Group By group_by)? + (Order By order_by)? + (Having having)? + ; + +from_item + : Ident Dot Ident from_alias? + | Ident from_alias? + | OpenParen select_stmt CloseParen from_alias + | from_item (Left|Right|Full) Outer? Join from_item (join_on_condition|join_using_condition) + | from_item Natural (Left|Right|Full) Outer? Join from_item + ; + +join_on_condition + : On + ; + +join_using_condition + : Using OpenParen ident_list CloseParen (As join_using_alias) + ; + +join_using_alias + : + ; + +ident_list + : Ident (Comma Ident)* + ; + +from_alias + : As? Ident (OpenParen Ident (Comma Ident)* CloseParen)? + ; + +condition + : expr + ; + +group_by + : + ; + +order_by + : + ; + +having + : + ; + + +select_list returns [std::unique_ptr result] + : select_item + { + $result = std::make_unique(); + $result->Add(std::move($select_item.result)); + } + (Comma select_item)* { $result->Add(std::move($select_item.result)); } + | + ; + +select_item returns [std::unique_ptr result] + : expr { $result = std::make_unique(std::move($expr.result)); } + (As? Ident { $result->SetAlias($Ident.text); })? + ; + +expr returns [std::unique_ptr result] + : expr Dot Ident + | Ident + | value { $result = std::move($value.result); } + ; + +value returns [std::unique_ptr result] + : IntegerLiteral + | StringLiteral { $result = std::make_unique($StringLiteral.text); } + ; + diff --git a/pglablib/build-cpp.bat b/pglablib/build-cpp.bat new file mode 100644 index 0000000..7c3466f --- /dev/null +++ b/pglablib/build-cpp.bat @@ -0,0 +1 @@ +java -Xmx500M -cp "c:\prog\antlr\antlr-4.9.2-complete.jar;%CLASSPATH%" org.antlr.v4.Tool -Dlanguage=Cpp PgsqlLexer.g4 PgsqlParser.g4 -o sqlparser/.generated -no-listener %2 %3 %4 %5 %6 %7 \ No newline at end of file diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index a835755..5ae18c3 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -4,11 +4,11 @@ # #------------------------------------------------- -QT += widgets core +QT += widgets core concurrent TARGET = pglablib TEMPLATE = lib -CONFIG += staticlib +CONFIG += staticlib no_keywords ! include( ../common.pri ) { error( "Couldn't find the common.pri file!" ) @@ -45,6 +45,25 @@ SOURCES += \ catalog/PgConstraintContainer.cpp \ ParamListJson.cpp \ ParamListModel.cpp \ + sqlast/BuildStandardItemTreeModelNodeVisitor.cpp \ + sqlast/ColumnDefinition.cpp \ + sqlast/CreateTable.cpp \ + sqlast/Expression.cpp \ + sqlast/Literal.cpp \ + sqlast/Node.cpp \ + sqlast/NodeVisitor.cpp \ + sqlast/SelectItem.cpp \ + sqlast/SelectList.cpp \ + sqlast/SelectStatement.cpp \ + sqlast/Statement.cpp \ + sqlast/StatementList.cpp \ + sqlast/StringLiteral.cpp \ + sqlast/TypeSpecification.cpp \ + sqlast/Visitor.cpp \ + sqlparser/.generated/PgsqlLexer.cpp \ + sqlparser/.generated/PgsqlParser.cpp \ + sqlparser/ErrorListener.cpp \ + sqlparser/Parser.cpp \ util.cpp \ SqlFormattingUtils.cpp \ catalog/PgKeywordList.cpp \ @@ -113,6 +132,26 @@ HEADERS += \ catalog/PgConstraintContainer.h \ ParamListJson.h \ ParamListModel.h \ + sqlast/BuildStandardItemTreeModelNodeVisitor.h \ + sqlast/ColumnDefinition.h \ + sqlast/CreateTable.h \ + sqlast/Expression.h \ + sqlast/Literal.h \ + sqlast/Node.h \ + sqlast/NodeVisitor.h \ + sqlast/SelectItem.h \ + sqlast/SelectList.h \ + sqlast/SelectStatement.h \ + sqlast/Statement.h \ + sqlast/StatementList.h \ + sqlast/StringLiteral.h \ + sqlast/TypeSpecification.h \ + sqlast/Visitor.h \ + sqlast/sqlast.h \ + sqlparser/.generated/PgsqlLexer.h \ + sqlparser/.generated/PgsqlParser.h \ + sqlparser/ErrorListener.h \ + sqlparser/Parser.h \ util.h \ SqlFormattingUtils.h \ catalog/PgCatalogTypes.h \ @@ -170,6 +209,10 @@ else:unix:!macx: LIBS += -L$$OUT_PWD/../core/ -lcore INCLUDEPATH += $$PWD/../core DEPENDPATH += $$PWD/../core +INCLUDEPATH += C:\Prog\include\antlr +win32:CONFIG(debug, debug|release): LIBS += -lantlr4-runtimed +else:win32:CONFIG(release, debug|release): LIBS += -lantlr4-runtime + win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/debug/libcore.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../core/release/core.lib diff --git a/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.cpp b/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.cpp new file mode 100644 index 0000000..6839d9d --- /dev/null +++ b/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.cpp @@ -0,0 +1,69 @@ +#include "BuildStandardItemTreeModelNodeVisitor.h" + +using namespace sqlast; + +namespace { + + template + class AutoRevert { + public: + AutoRevert(T& var, const T newValue) + : variable(var) + , previousValue(var) + { + variable = std::move(newValue); + } + + AutoRevert(T& var, const T&& newValue) + : variable(var) + , previousValue(var) + { + variable = std::move(newValue); + } + + ~AutoRevert() + { + variable = std::move(previousValue); + } + + AutoRevert(const AutoRevert&) = delete; + AutoRevert operator=(const AutoRevert&) = delete; + + private: + T& variable; + T previousValue; + }; + + template + AutoRevert MakeAutoRevert(T& var, const T newValue) + { + return AutoRevert(var, newValue); + } +} + +BuildStandardItemTreeModelNodeVisitor::BuildStandardItemTreeModelNodeVisitor() + : model(std::make_unique()) + , currentParent(model->invisibleRootItem()) +{ +} + +void BuildStandardItemTreeModelNodeVisitor::Visit(SelectItem &selectItem) +{ +} + +void BuildStandardItemTreeModelNodeVisitor::Visit(SelectList &selectList) +{ +} + +void BuildStandardItemTreeModelNodeVisitor::Visit(SelectStatement &selectStatement) +{ + auto item = new QStandardItem("SELECT"); + currentParent->appendRow(item); + auto guard = MakeAutoRevert(currentParent, item); + + +} + +void sqlast::BuildStandardItemTreeModelNodeVisitor::Visit(StatementList &statementList) +{ +} diff --git a/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.h b/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.h new file mode 100644 index 0000000..32dbe63 --- /dev/null +++ b/pglablib/sqlast/BuildStandardItemTreeModelNodeVisitor.h @@ -0,0 +1,28 @@ +#pragma once + +#include "NodeVisitor.h" + +#include +#include + +namespace sqlast { + + class BuildStandardItemTreeModelNodeVisitor : public NodeVisitor + { + public: + BuildStandardItemTreeModelNodeVisitor(); + + // NodeVisitor interface + public: + virtual void Visit(SelectItem &selectItem) override; + virtual void Visit(SelectList &selectList) override; + virtual void Visit(SelectStatement &selectStatement) override; + virtual void Visit(StatementList &statementList) override; + virtual void Visit(StringLiteral &stringLiteral) override; + private: + std::unique_ptr model; + QStandardItem *currentParent; + + }; + +} diff --git a/pglablib/sqlast/ColumnDefinition.cpp b/pglablib/sqlast/ColumnDefinition.cpp new file mode 100644 index 0000000..d300101 --- /dev/null +++ b/pglablib/sqlast/ColumnDefinition.cpp @@ -0,0 +1,9 @@ +#include "ColumnDefinition.h" +#include "TypeSpecification.h" + +using namespace sqlast; + +ColumnDefinition::ColumnDefinition() +{ + +} diff --git a/pglablib/sqlast/ColumnDefinition.h b/pglablib/sqlast/ColumnDefinition.h new file mode 100644 index 0000000..6c98311 --- /dev/null +++ b/pglablib/sqlast/ColumnDefinition.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Node.h" +#include +#include + +namespace sqlast { + +class TypeSpecification; + +/// Defines the details of a table column +/// +/// Constraints are not included here, as we handle constraints can apply to multiple columns +/// and we want to put them all in one place. The UI and SQL generator is allowed to display +/// column specific constraints with the column they belong to. +class ColumnDefinition : public Node +{ +public: + ColumnDefinition(); + +private: + QString name; + std::unique_ptr typeName; + bool notNull = true; + +}; + +} diff --git a/pglablib/sqlast/CreateTable.cpp b/pglablib/sqlast/CreateTable.cpp new file mode 100644 index 0000000..665c7ec --- /dev/null +++ b/pglablib/sqlast/CreateTable.cpp @@ -0,0 +1,8 @@ +#include "CreateTable.h" + +using namespace sqlast; + +CreateTable::CreateTable() +{ + +} diff --git a/pglablib/sqlast/CreateTable.h b/pglablib/sqlast/CreateTable.h new file mode 100644 index 0000000..01afa43 --- /dev/null +++ b/pglablib/sqlast/CreateTable.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Statement.h" +#include + +namespace sqlast { + +class ColumnDefinition; +class TableConstraint; + +class CreateTable: public Statement +{ +public: + CreateTable(); + +private: + + +}; + +} diff --git a/pglablib/sqlast/Expression.cpp b/pglablib/sqlast/Expression.cpp new file mode 100644 index 0000000..52dd30e --- /dev/null +++ b/pglablib/sqlast/Expression.cpp @@ -0,0 +1,8 @@ +#include "Expression.h" + +using namespace sqlast; + +Expression::Expression() +{ + +} diff --git a/pglablib/sqlast/Expression.h b/pglablib/sqlast/Expression.h new file mode 100644 index 0000000..e771a89 --- /dev/null +++ b/pglablib/sqlast/Expression.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Node.h" + +namespace sqlast { + + class Expression: public Node + { + public: + Expression(); + }; + +} diff --git a/pglablib/sqlast/Literal.cpp b/pglablib/sqlast/Literal.cpp new file mode 100644 index 0000000..38f975f --- /dev/null +++ b/pglablib/sqlast/Literal.cpp @@ -0,0 +1,8 @@ +#include "Literal.h" + +using namespace sqlast; + +Literal::Literal() +{ + +} diff --git a/pglablib/sqlast/Literal.h b/pglablib/sqlast/Literal.h new file mode 100644 index 0000000..832abb6 --- /dev/null +++ b/pglablib/sqlast/Literal.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Expression.h" + +namespace sqlast { + + class Literal: public Expression + { + public: + Literal(); + }; + +} diff --git a/pglablib/sqlast/Node.cpp b/pglablib/sqlast/Node.cpp new file mode 100644 index 0000000..d995fe8 --- /dev/null +++ b/pglablib/sqlast/Node.cpp @@ -0,0 +1,12 @@ +#include "Node.h" + +using namespace sqlast; + +Node::Node() +{ +} + +QString Node::ToString() const +{ + return QString::fromUtf8(typeid(*this).name()); +} diff --git a/pglablib/sqlast/Node.h b/pglablib/sqlast/Node.h new file mode 100644 index 0000000..e754d53 --- /dev/null +++ b/pglablib/sqlast/Node.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +namespace sqlast { + + class NodeVisitor; + + class Node { + public: + Node(); + virtual ~Node() = default; + + virtual void Accept(NodeVisitor &visitor) = 0; + /// Every derived class that has child nodes should override these + /// to facilitate +// virtual int ChildCount() const { return 0; } +// virtual const Node* GetChild(int index) const { throw std::out_of_range("GetChild"); } + + virtual QString ToString() const; + }; + +} +/* + +- Node + - INSERT + - UPDATE + - DELETE + - SELECT + - WITH + - CTE + + +*/ diff --git a/pglablib/sqlast/NodeVisitor.cpp b/pglablib/sqlast/NodeVisitor.cpp new file mode 100644 index 0000000..61c2374 --- /dev/null +++ b/pglablib/sqlast/NodeVisitor.cpp @@ -0,0 +1,8 @@ +#include "NodeVisitor.h" +#include "sqlast/SelectList.h" + +void sqlast::NodeVisitor::VisitSelectListItems(SelectList &selectList) +{ + for (int idx = 0; idx < selectList.Count(); ++idx) + Visit(selectList.Get(idx)); +} diff --git a/pglablib/sqlast/NodeVisitor.h b/pglablib/sqlast/NodeVisitor.h new file mode 100644 index 0000000..baa8c7e --- /dev/null +++ b/pglablib/sqlast/NodeVisitor.h @@ -0,0 +1,27 @@ +#pragma once + +namespace sqlast { + + class SelectItem; + class SelectList; + class SelectStatement; + class StatementList; + class StringLiteral; + + class NodeVisitor + { + public: + virtual ~NodeVisitor() = default; + + virtual void Visit(SelectItem &selectItem) = 0; + virtual void Visit(SelectList &selectList) = 0; + virtual void Visit(SelectStatement &selectStatement) = 0; + virtual void Visit(StatementList &statementList) = 0; + virtual void Visit(StringLiteral &stringLiteral) = 0; + + protected: + void VisitSelectListItems(SelectList &selectList); + + }; + +} diff --git a/pglablib/sqlast/SelectItem.cpp b/pglablib/sqlast/SelectItem.cpp new file mode 100644 index 0000000..5f798e0 --- /dev/null +++ b/pglablib/sqlast/SelectItem.cpp @@ -0,0 +1,19 @@ +#include "SelectItem.h" +#include "Expression.h" +#include "NodeVisitor.h" + +using namespace sqlast; + +SelectItem::SelectItem(std::unique_ptr expr) + : expression(std::move(expr)) +{} + +void SelectItem::SetAlias(const std::string &alias) +{ + this->alias = alias; +} + +void SelectItem::Accept(NodeVisitor &visitor) +{ + visitor.Visit(*this); +} diff --git a/pglablib/sqlast/SelectItem.h b/pglablib/sqlast/SelectItem.h new file mode 100644 index 0000000..eadfabc --- /dev/null +++ b/pglablib/sqlast/SelectItem.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Node.h" +#include +#include + +namespace sqlast { + +class Expression; + +class SelectItem : public Node +{ +public: + explicit SelectItem(std::unique_ptr expr); + + Expression& GetExpression() { return *expression; } + + void SetAlias(const std::string &alias); + std::string GetAlias() const { return alias; } + + void Accept(NodeVisitor &visitor) override; +private: + std::unique_ptr expression; + std::string alias; +}; + +} diff --git a/pglablib/sqlast/SelectList.cpp b/pglablib/sqlast/SelectList.cpp new file mode 100644 index 0000000..695bb05 --- /dev/null +++ b/pglablib/sqlast/SelectList.cpp @@ -0,0 +1,29 @@ +#include "SelectList.h" +#include "SelectItem.h" +#include "sqlast/NodeVisitor.h" + +using namespace sqlast; + +SelectList::SelectList() +{ +} + +void SelectList::Add(std::unique_ptr select_item) +{ + selectItems.push_back(std::move(select_item)); +} + +int SelectList::Count() const +{ + return static_cast(selectItems.size()); +} + +SelectItem &SelectList::Get(int index) +{ + return *selectItems.at(index); +} + +void SelectList::Accept(NodeVisitor &visitor) +{ + visitor.Visit(*this); +} diff --git a/pglablib/sqlast/SelectList.h b/pglablib/sqlast/SelectList.h new file mode 100644 index 0000000..defbf0b --- /dev/null +++ b/pglablib/sqlast/SelectList.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Node.h" +#include +#include + +namespace sqlast { + + + + class SelectItem; + + class SelectList : public Node + { + public: + SelectList(); + + void Add(std::unique_ptr select_item); + int Count() const; + + SelectItem& Get(int index); + + void Accept(NodeVisitor &visitor) override; + private: + using List = std::vector>; + + List selectItems; + }; + +} diff --git a/pglablib/sqlast/SelectStatement.cpp b/pglablib/sqlast/SelectStatement.cpp new file mode 100644 index 0000000..e693c06 --- /dev/null +++ b/pglablib/sqlast/SelectStatement.cpp @@ -0,0 +1,25 @@ +#include "SelectStatement.h" +#include "SelectList.h" +#include "NodeVisitor.h" + +using namespace sqlast; + +SelectStatement::SelectStatement() +{ + +} + +SelectList* SelectStatement::GetSelectList() +{ + return selectList.get(); +} + +void SelectStatement::SetSelectList(std::unique_ptr value) +{ + selectList = std::move(value); +} + +void SelectStatement::Accept(NodeVisitor &visitor) +{ + visitor.Visit(*this); +} diff --git a/pglablib/sqlast/SelectStatement.h b/pglablib/sqlast/SelectStatement.h new file mode 100644 index 0000000..6f7708d --- /dev/null +++ b/pglablib/sqlast/SelectStatement.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Statement.h" +#include + +namespace sqlast { + + class SelectList; + + class SelectStatement: public Statement + { + public: + SelectStatement(); + + SelectList* GetSelectList(); + void SetSelectList(std::unique_ptr value); + + void Accept(NodeVisitor &visitor) override; + private: + std::unique_ptr selectList; + }; + +} diff --git a/pglablib/sqlast/Statement.cpp b/pglablib/sqlast/Statement.cpp new file mode 100644 index 0000000..774502f --- /dev/null +++ b/pglablib/sqlast/Statement.cpp @@ -0,0 +1,10 @@ +#include "Statement.h" + +using namespace sqlast; + +Statement::Statement() +{ + +} + + diff --git a/pglablib/sqlast/Statement.h b/pglablib/sqlast/Statement.h new file mode 100644 index 0000000..ee445fa --- /dev/null +++ b/pglablib/sqlast/Statement.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Node.h" + +namespace sqlast { + + class Statement: public Node + { + public: + Statement(); + }; + +} diff --git a/pglablib/sqlast/StatementList.cpp b/pglablib/sqlast/StatementList.cpp new file mode 100644 index 0000000..6bfe21b --- /dev/null +++ b/pglablib/sqlast/StatementList.cpp @@ -0,0 +1,29 @@ +#include "StatementList.h" +#include "Statement.h" +#include "sqlast/NodeVisitor.h" + +using namespace sqlast; + +StatementList::StatementList() +{} + +void StatementList::Add(std::unique_ptr &&statement) +{ + statements.push_back(std::move(statement)); +} + +Statement &StatementList::Get(int index) +{ + return *statements[index]; +} + +int StatementList::Count() const +{ + return static_cast(statements.size()); +} + +void StatementList::Accept(NodeVisitor &visitor) +{ + visitor.Visit(*this); +} + diff --git a/pglablib/sqlast/StatementList.h b/pglablib/sqlast/StatementList.h new file mode 100644 index 0000000..2d994c4 --- /dev/null +++ b/pglablib/sqlast/StatementList.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Node.h" + +#include +#include + +namespace sqlast { + + class Statement; + + class StatementList: public Node + { + public: + StatementList(); + + void Add(std::unique_ptr &&statement); + Statement &Get(int index); + int Count() const; + + void Accept(NodeVisitor &visitor) override; + private: + using Statements = std::vector>; + + Statements statements; + }; + +} diff --git a/pglablib/sqlast/StringLiteral.cpp b/pglablib/sqlast/StringLiteral.cpp new file mode 100644 index 0000000..7be79c6 --- /dev/null +++ b/pglablib/sqlast/StringLiteral.cpp @@ -0,0 +1,13 @@ +#include "StringLiteral.h" +#include "NodeVisitor.h" + +using namespace sqlast; + +StringLiteral::StringLiteral(const std::string s) + : value(QString::fromStdString(s)) +{} + +void StringLiteral::Accept(NodeVisitor &visitor) +{ + visitor.Visit(*this); +} diff --git a/pglablib/sqlast/StringLiteral.h b/pglablib/sqlast/StringLiteral.h new file mode 100644 index 0000000..dc267b1 --- /dev/null +++ b/pglablib/sqlast/StringLiteral.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Literal.h" +#include +#include + +namespace sqlast { + + class StringLiteral : public Literal + { + public: + explicit StringLiteral(const std::string s); + + QString GetValue() const { return value; } + void Accept(NodeVisitor &visitor) override; + private: + QString value; + }; + +} diff --git a/pglablib/sqlast/TypeSpecification.cpp b/pglablib/sqlast/TypeSpecification.cpp new file mode 100644 index 0000000..45511dc --- /dev/null +++ b/pglablib/sqlast/TypeSpecification.cpp @@ -0,0 +1,8 @@ +#include "TypeSpecification.h" + +using namespace sqlast; + +TypeSpecification::TypeSpecification() +{ + +} diff --git a/pglablib/sqlast/TypeSpecification.h b/pglablib/sqlast/TypeSpecification.h new file mode 100644 index 0000000..b5ec86c --- /dev/null +++ b/pglablib/sqlast/TypeSpecification.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Node.h" +#include + +namespace sqlast { + +/// These object define not only the base type, but also +/// parameters used with the type +/// Think the precision of numeric, max length of char, array +class TypeSpecification : public Node +{ +public: + TypeSpecification(); + +private: + /// We do not use the PgType from the catalog here as the type used might be defined + /// inside the script and not present yet in the catalog. + QString baseType; + // is_array +}; + +} diff --git a/pglablib/sqlast/Visitor.cpp b/pglablib/sqlast/Visitor.cpp new file mode 100644 index 0000000..16090d5 --- /dev/null +++ b/pglablib/sqlast/Visitor.cpp @@ -0,0 +1,8 @@ +#include "Visitor.h" + +using namespace sqlast; + +Visitor::Visitor() +{ + +} diff --git a/pglablib/sqlast/Visitor.h b/pglablib/sqlast/Visitor.h new file mode 100644 index 0000000..4cf5e7c --- /dev/null +++ b/pglablib/sqlast/Visitor.h @@ -0,0 +1,13 @@ +#pragma once + +namespace sqlast { + +class Visitor +{ +public: + Visitor(); + + +}; + +} diff --git a/pglablib/sqlast/sqlast.h b/pglablib/sqlast/sqlast.h new file mode 100644 index 0000000..e2e04dd --- /dev/null +++ b/pglablib/sqlast/sqlast.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Node.h" +#include "SelectStatement.h" +#include "SelectItem.h" +#include "SelectList.h" +#include "Statement.h" +#include "StatementList.h" +#include "StringLiteral.h" +#undef emit + + diff --git a/pglablib/sqlparser/ErrorListener.cpp b/pglablib/sqlparser/ErrorListener.cpp new file mode 100644 index 0000000..809bce8 --- /dev/null +++ b/pglablib/sqlparser/ErrorListener.cpp @@ -0,0 +1,22 @@ +#include "ErrorListener.h" + + +void ErrorListener::syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr e) +{ + ++errors; +} + +void ErrorListener::reportAmbiguity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs) +{ + ++errors; +} + +void ErrorListener::reportAttemptingFullContext(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, antlr4::atn::ATNConfigSet *configs) +{ + ++errors; +} + +void ErrorListener::reportContextSensitivity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) +{ + ++errors; +} diff --git a/pglablib/sqlparser/ErrorListener.h b/pglablib/sqlparser/ErrorListener.h new file mode 100644 index 0000000..6da2f02 --- /dev/null +++ b/pglablib/sqlparser/ErrorListener.h @@ -0,0 +1,21 @@ +#pragma once +#include "antlr4-runtime.h" + +class ErrorListener : public antlr4::ANTLRErrorListener +{ + // ANTLRErrorListener interface +public: + virtual void syntaxError(antlr4::Recognizer *recognizer, antlr4::Token *offendingSymbol, size_t line, size_t charPositionInLine, const std::string &msg, std::exception_ptr e) override; + virtual void reportAmbiguity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, bool exact, const antlrcpp::BitSet &ambigAlts, antlr4::atn::ATNConfigSet *configs) override; + virtual void reportAttemptingFullContext(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, const antlrcpp::BitSet &conflictingAlts, antlr4::atn::ATNConfigSet *configs) override; + virtual void reportContextSensitivity(antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex, size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) override; + +public: + int errorCount() const + { + return errors; + } + +private: + int errors = 0; +}; diff --git a/pglablib/sqlparser/Parser.cpp b/pglablib/sqlparser/Parser.cpp new file mode 100644 index 0000000..a6da16f --- /dev/null +++ b/pglablib/sqlparser/Parser.cpp @@ -0,0 +1,24 @@ +#include "Parser.h" +#include "antlr4-runtime.h" + + +Parser::Parser(const std::string &input_string) + : Parser(std::make_unique(input_string)) +{} + +Parser::Parser(std::unique_ptr stream) + : InputStream(std::move(stream)) + , Lexer(InputStream.get()) + , TokenStream(&Lexer) + , AParser(&TokenStream) +{ + AParser.removeErrorListeners(); + AParser.addErrorListener(&Errors); +} + +std::unique_ptr Parser::Parse() +{ + auto context = AParser.main(); + return std::move(context->program); +} + diff --git a/pglablib/sqlparser/Parser.h b/pglablib/sqlparser/Parser.h new file mode 100644 index 0000000..173d354 --- /dev/null +++ b/pglablib/sqlparser/Parser.h @@ -0,0 +1,26 @@ +#pragma once + +#include ".generated/PgsqlLexer.h" +#include ".generated/PgsqlParser.h" +#include "ErrorListener.h" + +class Parser +{ +public: + Parser(const std::string &input_string); + Parser(std::unique_ptr stream); + + std::unique_ptr Parse(); + + int errorCount() const + { + return Errors.errorCount(); + } +private: + std::unique_ptr InputStream; + PgsqlLexer Lexer; + antlr4::CommonTokenStream TokenStream; + PgsqlParser AParser; + ErrorListener Errors; +}; + diff --git a/pglablib/util.cpp b/pglablib/util.cpp index bdd6bef..32c9e3d 100644 --- a/pglablib/util.cpp +++ b/pglablib/util.cpp @@ -17,7 +17,7 @@ QString msfloatToHumanReadableString(float ms) if (ms < 1.0f) { val = ms * 1000.f; //result = QString::asprintf("%0.3f", ms * 1000.0f); - unit = u8"μs"; + unit = QString::fromUtf8("μs"); } else if (ms >= 1000.0) { val = ms / 1000.0f; diff --git a/tests/pglabtests/pglabtests.pro b/tests/pglabtests/pglabtests.pro index 73c4847..4a6f85c 100644 --- a/tests/pglabtests/pglabtests.pro +++ b/tests/pglabtests/pglabtests.pro @@ -6,7 +6,7 @@ include(gtest_dependency.pri) TEMPLATE = app CONFIG += console CONFIG -= app_bundle -CONFIG += thread +CONFIG += thread no_keywords CONFIG += qt QT += core widgets @@ -20,6 +20,7 @@ SOURCES += main.cpp \ tst_escapeConnectionStringValue.cpp \ tst_expected.cpp \ tst_SqlLexer.cpp \ + tst_newParser.cpp \ tst_scopeguard.cpp \ tst_CsvWriter.cpp \ tst_PasswordManager.cpp \ @@ -39,6 +40,10 @@ DEPENDPATH += $$PWD/../../core win32:CONFIG(debug, debug|release): LIBS += -lbotand else:win32:CONFIG(release, debug|release): LIBS += -lbotan +INCLUDEPATH += C:\Prog\include\antlr +win32:CONFIG(debug, debug|release): LIBS += -lantlr4-runtimed +else:win32:CONFIG(release, debug|release): LIBS += -lantlr4-runtime + win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/libcore.a else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/debug/libcore.a else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../core/release/core.lib diff --git a/tests/pglabtests/tst_newParser.cpp b/tests/pglabtests/tst_newParser.cpp new file mode 100644 index 0000000..11d7b7b --- /dev/null +++ b/tests/pglabtests/tst_newParser.cpp @@ -0,0 +1,135 @@ +#include +#include +#include "PrintTo_Qt.h" +#include "sqlparser/Parser.h" + +using namespace testing; +using namespace sqlast; + +TEST(NewSqlLexer, Select) +{ + std::string source = "SELECT"; + antlr4::ANTLRInputStream input(source); + PgsqlLexer lexer(&input); + + auto token = lexer.nextToken(); + ASSERT_EQ(PgsqlLexer::Select, token->getType()); +} + +TEST(NewSqlLexer, Ident) +{ + std::string source = "Abc"; + antlr4::ANTLRInputStream input(source); + PgsqlLexer lexer(&input); + + auto token = lexer.nextToken(); + ASSERT_EQ(PgsqlLexer::Ident, token->getType()); + ASSERT_EQ("abc", token->getText()); +} + +TEST(NewSqlLexer, QuotedIdent) +{ + std::string source = "\"Abc\""; + antlr4::ANTLRInputStream input(source); + PgsqlLexer lexer(&input); + + auto token = lexer.nextToken(); + ASSERT_EQ(PgsqlLexer::Ident, token->getType()); + ASSERT_EQ("Abc", token->getText()); +} + +TEST(NewSqlLexer, AcceptNewLineInQuotedIdent) +{ + std::string source = "\"Ab\nc\""; + antlr4::ANTLRInputStream input(source); + PgsqlLexer lexer(&input); + + auto token = lexer.nextToken(); + ASSERT_EQ(PgsqlLexer::Ident, token->getType()); + ASSERT_EQ("Ab\nc", token->getText()); +} + + +TEST(NewSqlParser, statementList) +{ + std::string input_string = "SEleCT 1; Select 2;"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_TRUE(program != nullptr); + ASSERT_EQ(2, program->Count()); + ASSERT_EQ(0, parser.errorCount()); +} + +TEST(NewSqlParser, missingSemi) +{ + std::string input_string = "1"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_EQ(1, parser.errorCount()); +} + +TEST(NewSqlParser, selectList) +{ + std::string input_string = "SEleCT 1, 'Tekst'"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_TRUE(program != nullptr); + ASSERT_EQ(1, program->Count()); + ASSERT_EQ(0, parser.errorCount()); + + SelectStatement &s = dynamic_cast(program->Get(0)); + SelectList* sl = s.GetSelectList(); + ASSERT_TRUE(sl != nullptr); + ASSERT_EQ(2, sl->Count()); + + SelectItem& si = sl->Get(1); + StringLiteral& string_literal = dynamic_cast(si.GetExpression()); + ASSERT_EQ("Tekst", string_literal.GetValue()); +} + +TEST(NewSqlParser, selectAliasWithoutAs) +{ + std::string input_string = "SELECT 1 a"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_TRUE(program != nullptr); + ASSERT_EQ(1, program->Count()); + ASSERT_EQ(0, parser.errorCount()); + + SelectStatement &s = dynamic_cast(program->Get(0)); + SelectList* sl = s.GetSelectList(); + SelectItem& si = sl->Get(0); + ASSERT_EQ("a", si.GetAlias()); +} + + +TEST(NewSqlParser, selectAliasWithAs) +{ + std::string input_string = "SELECT 1 AS b"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_TRUE(program != nullptr); + ASSERT_EQ(1, program->Count()); + ASSERT_EQ(0, parser.errorCount()); + + SelectStatement &s = dynamic_cast(program->Get(0)); + SelectList* sl = s.GetSelectList(); + SelectItem& si = sl->Get(0); + ASSERT_EQ("b", si.GetAlias()); +} + +TEST(NewSqlParser, selectFrom) +{ + std::string input_string = "SELECT 1 FROM a"; + Parser parser(input_string); + std::unique_ptr program = parser.Parse(); + + ASSERT_TRUE(program != nullptr); + ASSERT_EQ(1, program->Count()); + ASSERT_EQ(0, parser.errorCount()); +}