diff options
-rw-r--r-- | libdbpp/Jamfile.jam | 11 | ||||
-rw-r--r-- | libdbpp/connection.cpp | 8 | ||||
-rw-r--r-- | libdbpp/connection.h | 2 | ||||
-rw-r--r-- | libdbpp/sqlParse.h | 36 | ||||
-rw-r--r-- | libdbpp/sqlParse.ll | 153 | ||||
-rw-r--r-- | libdbpp/unittests/Jamfile.jam | 2 | ||||
-rw-r--r-- | libdbpp/unittests/parseTest.sql | 12 | ||||
-rw-r--r-- | libdbpp/unittests/testConnection.cpp | 22 |
8 files changed, 242 insertions, 4 deletions
diff --git a/libdbpp/Jamfile.jam b/libdbpp/Jamfile.jam index e935937..3eedb10 100644 --- a/libdbpp/Jamfile.jam +++ b/libdbpp/Jamfile.jam @@ -1,21 +1,30 @@ import package ; +import lex ; + alias glibmm : : : : <cflags>"`pkg-config --cflags glibmm-2.4`" <linkflags>"`pkg-config --libs glibmm-2.4`" ; lib boost_date_time : : <name>boost_date_time ; +lib boost_filesystem ; +lib boost_system ; lib adhocutil : : : : <include>/usr/include/adhocutil ; lib dbppcore : - [ glob *.cpp ] : + [ glob *.cpp *.ll ] : <library>glibmm <library>adhocutil + <library>boost_system + <library>boost_filesystem <cflags>-fvisibility=hidden <variant>release:<cflags>-flto + <include>. : : <include>. <library>glibmm <library>boost_date_time + <library>boost_system + <library>boost_filesystem ; build-project unittests ; diff --git a/libdbpp/connection.cpp b/libdbpp/connection.cpp index 2d93ce3..2640148 100644 --- a/libdbpp/connection.cpp +++ b/libdbpp/connection.cpp @@ -2,6 +2,7 @@ #include "modifycommand.h" #include <factory.impl.h> #include <buffer.h> +#include <sqlParse.h> DB::Connection::~Connection() { @@ -22,6 +23,13 @@ DB::Connection::execute(const std::string & sql) const } void +DB::Connection::executeScript(std::istream & f, const boost::filesystem::path & s) const +{ + DB::SqlParse p(f, s, this); + while (p.yylex()) ; +} + +void DB::Connection::savepoint(const std::string & sp) const { execute("SAVEPOINT " + sp); diff --git a/libdbpp/connection.h b/libdbpp/connection.h index 9c6b46e..2e17490 100644 --- a/libdbpp/connection.h +++ b/libdbpp/connection.h @@ -4,6 +4,7 @@ #include <string> #include <factory.h> #include <visibility.h> +#include <boost/filesystem/path.hpp> namespace DB { class SelectCommand; @@ -35,6 +36,7 @@ namespace DB { virtual BulkUpdateStyle bulkUpdateStyle() const = 0; virtual void execute(const std::string & sql) const; + virtual void executeScript(std::istream & f, const boost::filesystem::path & s) const; virtual SelectCommand * newSelectCommand(const std::string & sql) const = 0; virtual ModifyCommand * newModifyCommand(const std::string & sql) const = 0; diff --git a/libdbpp/sqlParse.h b/libdbpp/sqlParse.h new file mode 100644 index 0000000..ad001bb --- /dev/null +++ b/libdbpp/sqlParse.h @@ -0,0 +1,36 @@ +#ifndef DB_SQLPARSE_H +#define DB_SQLPARSE_H + +#include <istream> +#include <string> +#include "connection.h" +#include <boost/filesystem/path.hpp> +#ifndef yyFlexLexer +#define yyFlexLexer sqlBaseFlexLexer +#include <FlexLexer.h> +#endif + +namespace DB { + +class SqlParse : public yyFlexLexer { + public: + SqlParse(std::istream &, const boost::filesystem::path &, const Connection *); + int yylex() override; + + void Comment(const std::string &) const; + void Statement(const std::string &) const; + + protected: + void LexerError(const char *) override; + + private: + const DB::Connection * conn; + const boost::filesystem::path scriptDir; + std::string comment; + std::string statement; +}; + +} + +#endif + diff --git a/libdbpp/sqlParse.ll b/libdbpp/sqlParse.ll new file mode 100644 index 0000000..253b91f --- /dev/null +++ b/libdbpp/sqlParse.ll @@ -0,0 +1,153 @@ +%option batch +%option c++ +%option noyywrap +%option 8bit +%option stack +%option yylineno +%option yyclass="DB::SqlParse" +%option prefix="sqlBase" + +%{ +#include <stdexcept> +#include "sqlParse.h" +#pragma GCC diagnostic ignored "-Wsign-compare" +%} + +space [ \t\n\r\f] +non_newline [^\r\n] +mcomment_start "/*" +mcomment_stop "*/" +comment ("--"{non_newline}*) +other . +term ; +any ({other}|{space}) +quote ' +quote_apos '' +dolq_start [A-Za-z\200-\377_] +dolq_cont [A-Za-z\200-\377_0-9] +dollarquote \$({dolq_start}{dolq_cont}*)?\$ +scriptdir "$SCRIPTDIR" + +%x COMMENT +%x STATEMENT +%x QUOTE +%x DOLLARQUOTE + +%% + +{mcomment_start} { + comment += YYText(); + yy_push_state(COMMENT); +} + +<COMMENT>{mcomment_stop} { + comment += YYText(); + Comment(comment); + comment.clear(); + yy_pop_state(); +} + +<COMMENT>{any} { + comment += YYText(); +} + +<COMMENT><<EOF>> { + throw std::runtime_error("Unterminated comment"); +} + +{comment} { + Comment(YYText()); +} + +{other} { + statement += YYText(); + yy_push_state(STATEMENT); +} + +<STATEMENT>{quote} { + statement += YYText(); + yy_push_state(QUOTE); +} + +<STATEMENT>{dollarquote} { + statement += YYText(); + yy_push_state(DOLLARQUOTE); +} + +<QUOTE>{quote} { + statement += YYText(); + yy_pop_state(); +} + +<QUOTE>{scriptdir} { + statement += scriptDir.string(); +} + +<QUOTE>{quote_apos} { + statement += YYText(); +} + +<DOLLARQUOTE>{any} { + statement += YYText(); +} + +<DOLLARQUOTE>{dollarquote} { + statement += YYText(); + yy_pop_state(); +} + +<DOLLARQUOTE><<EOF>> { + throw std::runtime_error("Unterminated dollar quoted string"); +} + +<QUOTE>{any} { + statement += YYText(); +} + +<QUOTE><<EOF>> { + throw std::runtime_error("Unterminated quoted string"); +} + +<STATEMENT>{term} { + Statement(statement); + statement.clear(); + yy_pop_state(); +} + +<STATEMENT>{any} { + statement += YYText(); +} + +<*>[ \t\r\n\f] { +} + +%% + +namespace DB { + + SqlParse::SqlParse(std::istream & f, const boost::filesystem::path & s, const Connection * c) : + yyFlexLexer(&f, NULL), + conn(c), + scriptDir(s) + { + } + + void + SqlParse::LexerError(const char * msg) + { + throw std::runtime_error(msg); + } + + void + SqlParse::Comment(const std::string &) const + { + } + + void + SqlParse::Statement(const std::string & text) const + { + conn->execute(text); + } + +} + diff --git a/libdbpp/unittests/Jamfile.jam b/libdbpp/unittests/Jamfile.jam index ef19211..cb44781 100644 --- a/libdbpp/unittests/Jamfile.jam +++ b/libdbpp/unittests/Jamfile.jam @@ -3,8 +3,6 @@ import testing ; path-constant me : . ; lib boost_utf : : <name>boost_unit_test_framework ; -lib boost_filesystem ; -lib boost_system ; run testConnection.cpp diff --git a/libdbpp/unittests/parseTest.sql b/libdbpp/unittests/parseTest.sql new file mode 100644 index 0000000..56eac57 --- /dev/null +++ b/libdbpp/unittests/parseTest.sql @@ -0,0 +1,12 @@ +CREATE TABLE name ( + t text, + i int, + primary key(i) + ); +-- Single line comment +INSERT INTO name(t, i) VALUES('string', 3); +/* + Multi line + comment + */ + diff --git a/libdbpp/unittests/testConnection.cpp b/libdbpp/unittests/testConnection.cpp index 3472dde..a707f2f 100644 --- a/libdbpp/unittests/testConnection.cpp +++ b/libdbpp/unittests/testConnection.cpp @@ -3,6 +3,9 @@ #include <factory.h> #include <connection.h> +#include <definedDirs.h> +#include <fstream> +#include <vector> class MockDb : public DB::Connection { public: @@ -20,13 +23,17 @@ class MockDb : public DB::Connection { DB::BulkDeleteStyle bulkDeleteStyle() const { return DB::BulkDeleteUsingUsing; } DB::BulkUpdateStyle bulkUpdateStyle() const { return DB::BulkUpdateUsingJoin; } - void execute(const std::string &) const {} + void execute(const std::string & sql) const { + executed.push_back(sql); + } DB::SelectCommand * newSelectCommand(const std::string &) const { return nullptr; } DB::ModifyCommand * newModifyCommand(const std::string &) const { return nullptr; } void beginBulkUpload(const char *, const char *) const {} void endBulkUpload(const char *) const {} size_t bulkUploadData(const char *, size_t) const {return 0;} + + mutable std::vector<std::string> executed; }; FACTORY(MockDb, DB::ConnectionFactory); @@ -55,3 +62,16 @@ BOOST_AUTO_TEST_CASE( resolve ) BOOST_REQUIRE_THROW(DB::ConnectionFactory::create("otherdb", "doesn't matter"), AdHoc::LoadLibraryException); } +BOOST_AUTO_TEST_CASE( parse ) +{ + auto mock = DB::ConnectionFactory::create("MockDb", "doesn't matter"); + std::fstream s((rootDir / "parseTest.sql").string()); + BOOST_REQUIRE(s.good()); + mock->executeScript(s, rootDir); + MockDb * mockdb = dynamic_cast<MockDb *>(mock); + BOOST_REQUIRE(mockdb); + BOOST_REQUIRE_EQUAL(2, mockdb->executed.size()); + BOOST_REQUIRE_EQUAL("INSERT INTO name(t, i) VALUES('string', 3)", mockdb->executed[1]); + delete mock; +} + |