summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2015-09-21 00:49:14 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2015-09-21 00:49:14 +0100
commit3a151fce45acd0a1f7ec077b8320b90b5fb34968 (patch)
treeb483d832158f95f888ab62243c90717b918382e9
parentAdd install rules (diff)
downloadlibdbpp-3a151fce45acd0a1f7ec077b8320b90b5fb34968.tar.bz2
libdbpp-3a151fce45acd0a1f7ec077b8320b90b5fb34968.tar.xz
libdbpp-3a151fce45acd0a1f7ec077b8320b90b5fb34968.zip
Migrate SQL script parser and add support for executing a script on a connection
-rw-r--r--libdbpp/Jamfile.jam11
-rw-r--r--libdbpp/connection.cpp8
-rw-r--r--libdbpp/connection.h2
-rw-r--r--libdbpp/sqlParse.h36
-rw-r--r--libdbpp/sqlParse.ll153
-rw-r--r--libdbpp/unittests/Jamfile.jam2
-rw-r--r--libdbpp/unittests/parseTest.sql12
-rw-r--r--libdbpp/unittests/testConnection.cpp22
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;
+}
+