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