diff --git a/scripts/ddl2cpp b/scripts/ddl2cpp new file mode 100755 index 00000000..cc1d78b2 --- /dev/null +++ b/scripts/ddl2cpp @@ -0,0 +1,192 @@ +#!/usr/bin/env python + +## + # Copyright (c) 2013 - 2014, Roland Bock + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without modification, + # are permitted provided that the following conditions are met: + # + # * Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright notice, + # this list of conditions and the following disclaimer in the documentation + # and/or other materials provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + ## + +from __future__ import print_function +import sys +import re +import os + +from pyparsing import CaselessLiteral, SkipTo, restOfLine, oneOf, ZeroOrMore, Optional, \ + WordStart, WordEnd, Word, alphas, alphanums, nums, QuotedString, nestedExpr, MatchFirst, OneOrMore, delimitedList, Or, Group + +INCLUDE = 'sqlpp11' +NAMESPACE = 'sqlpp' + +# HELPERS +def get_include_guard_name(namespace, inputfile): + val = re.sub("[^A-Za-z]+", "_", namespace + '_' + os.path.basename(inputfile)) + return val.upper() + +def repl_func(m): + if (m.group(1) == '_'): + return m.group(2).upper() + else: + return m.group(1) + m.group(2).upper() + +def toClassName(s): + return re.sub("(^|\s|[_0-9])(\S)", repl_func, s) + +def toMemberName(s): + return re.sub("(\s|_|[0-9])(\S)", repl_func, s) + + +# PARSER +def ddlWord(string): + return WordStart(alphanums + "_") + CaselessLiteral(string) + WordEnd(alphanums + "_") + +ddlString = Or([QuotedString("'"), QuotedString("\"", escQuote='""'), QuotedString("`")]) +ddlNum = Word(nums + ".") +ddlTerm = Word(alphas, alphanums + "_$") +ddlArguments = "(" + delimitedList(Or([ddlString, ddlTerm, ddlNum])) + ")" +ddlNotNull = Group(ddlWord("NOT") + ddlWord("NULL")).setResultsName("notNull") +ddlDefaultValue = ddlWord("DEFAULT").setResultsName("hasDefaultValue"); +ddlAutoValue = ddlWord("AUTO_INCREMENT").setResultsName("hasAutoValue"); +ddlColumnComment = Group(ddlWord("COMMENT") + ddlString).setResultsName("comment") +ddlConstraint = Or([ + ddlWord("CONSTRAINT"), + ddlWord("PRIMARY"), + ddlWord("FOREIGN"), + ddlWord("KEY"), + ddlWord("INDEX"), + ddlWord("UNIQUE"), + ]) +ddlColumn = Group(Optional(ddlConstraint).setResultsName("isConstraint") + OneOrMore(MatchFirst([ddlNotNull, ddlAutoValue, ddlDefaultValue, ddlTerm, ddlNum, ddlColumnComment, ddlString, ddlArguments]))) +createTable = Group(ddlWord("CREATE") + ddlWord("TABLE") + ddlTerm.setResultsName("tableName") + "(" + Group(delimitedList(ddlColumn)).setResultsName("columns") + ")").setResultsName("create") + + +ddl = ZeroOrMore(SkipTo(createTable, True)) + +ddlComment = oneOf(["--", "#"]) + restOfLine +ddl.ignore(ddlComment) + +# MAP SQL TYPES +types = { + 'tinyint': 'tinyint', + 'smallint': 'smallint', + 'integer': 'integer', + 'int': 'integer', + 'bigint': 'bigint', + 'char': 'char_', + 'varchar': 'varchar', + 'text': 'text', + 'bool': 'boolean', + 'double': 'floating_point', + 'float': 'floating_point', + } + +# PROCESS DDL +if (len(sys.argv) != 4): + print('Usage: ddl2cpp ') + sys.exit(1) + +pathToDdl = sys.argv[1] +pathToHeader = sys.argv[2] + '.h' +namespace = sys.argv[3] +ddlFile = open(pathToDdl, 'r') +header = open(pathToHeader, 'w') + +print('#ifndef '+get_include_guard_name(namespace, pathToHeader), file=header) +print('#define '+get_include_guard_name(namespace, pathToHeader), file=header) +print('', file=header) +print('#include <' + INCLUDE + '/table.h>', file=header) +print('#include <' + INCLUDE + '/column_types.h>', file=header) +print('', file=header) +print('namespace ' + namespace, file=header) +print('{', file=header) + +tableCreations = ddl.parseFile(pathToDdl) + +for tableCreation in tableCreations: + sqlTableName = tableCreation.create.tableName + tableClass = toClassName(sqlTableName) + tableMember = toMemberName(sqlTableName) + tableNamespace = tableClass + '_' + tableTemplateParameters = tableClass + print(' namespace ' + tableNamespace, file=header) + print(' {', file=header) + for column in tableCreation.create.columns: + if column.isConstraint: + continue + sqlColumnName = column[0] + columnClass = toClassName(sqlColumnName) + tableTemplateParameters += ',\n ' + tableNamespace + '::' + columnClass + columnMember = toMemberName(sqlColumnName) + sqlColumnType = column[1].lower() + columnCanBeNull = not column.notNull + print(' struct ' + columnClass, file=header) + print(' {', file=header) + print(' struct _name_t', file=header) + print(' {', file=header) + print(' static constexpr const char* _get_name() { return "' + sqlColumnName + '"; }', file=header) + print(' template', file=header) + print(' struct _member_t', file=header) + print(' {', file=header) + print(' T ' + columnMember + ';', file=header) + print(' T& operator()() { return ' + columnMember + '; }', file=header) + print(' const T& operator()() const { return ' + columnMember + '; }', file=header) + print(' };', file=header) + print(' };', file=header) + #print(sqlColumnType) + print(' using _value_type = ' + NAMESPACE + '::' + types[sqlColumnType] + ';', file=header) + print(' struct _column_type', file=header) + print(' {', file=header) + requireInsert = True + if column.hasAutoValue: + print(' using _must_not_insert = std::true_type;', file=header) + print(' using _must_not_update = std::true_type;', file=header) + requireInsert = False + if not column.notNull: + print(' using _can_be_null = std::true_type;', file=header) + requireInsert = False + if column.hasDefaultValue: + requireInsert = False + if requireInsert: + print(' using _require_insert = std::true_type;', file=header) + print(' };', file=header) + print(' };', file=header) + print(' }', file=header) + print('', file=header) + + print(' struct ' + tableClass + ': ' + NAMESPACE + '::table_t<' + tableTemplateParameters + '>', file=header) + print(' {', file=header) + print(' using _value_type = ' + NAMESPACE + '::no_value_t;', file=header) + print(' struct _name_t', file=header) + print(' {', file=header) + print(' static constexpr const char* _get_name() { return "' + sqlTableName + '"; }', file=header) + print(' template', file=header) + print(' struct _member_t', file=header) + print(' {', file=header) + print(' T ' + tableMember + ';', file=header) + print(' T& operator()() { return ' + tableMember + '; }', file=header) + print(' const T& operator()() const { return ' + tableMember + '; }', file=header) + print(' };', file=header) + print(' };', file=header) + print(' };', file=header) + +print('}', file=header) +print('#endif', file=header) + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f037b83c..b9798885 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,8 @@ + macro (build_and_run arg) # Add headers to sources to enable file browsing in IDEs - add_executable(${arg} ${arg}.cpp ${sqlpp_headers}) + include_directories(${CMAKE_BINARY_DIR}/tests) + add_executable(${arg} ${arg}.cpp ${sqlpp_headers} ${CMAKE_CURRENT_LIST_DIR}/Sample.h) add_test(${arg} ${arg}) endmacro () @@ -12,3 +14,8 @@ build_and_run(SelectTest) build_and_run(FunctionTest) build_and_run(PreparedTest) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_LIST_DIR}/Sample.h + COMMAND ${CMAKE_SOURCE_DIR}/scripts/ddl2cpp ${CMAKE_CURRENT_LIST_DIR}/sample.sql Sample test + ) + diff --git a/tests/FunctionTest.cpp b/tests/FunctionTest.cpp index ce16ed37..632875b0 100644 --- a/tests/FunctionTest.cpp +++ b/tests/FunctionTest.cpp @@ -23,7 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include #include @@ -37,8 +37,8 @@ SQLPP_ALIAS_PROVIDER(kaesekuchen); int main() { - TabSample t; - TabFoo f; + test::TabFoo f; + test::TabBar t; // MEMBER FUNCTIONS // ---------------- diff --git a/tests/InsertTest.cpp b/tests/InsertTest.cpp index 80760af4..a116d93c 100644 --- a/tests/InsertTest.cpp +++ b/tests/InsertTest.cpp @@ -23,7 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include "is_regular.h" #include @@ -34,7 +34,7 @@ DbMock::_context_t printer(std::cerr); int main() { - TabSample t; + test::TabBar t; auto x = t.alpha = 7; auto y = t.beta = "kaesekuchen"; diff --git a/tests/InterpretTest.cpp b/tests/InterpretTest.cpp index 1a81b9ba..5d940868 100644 --- a/tests/InterpretTest.cpp +++ b/tests/InterpretTest.cpp @@ -23,7 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include #include @@ -40,8 +40,8 @@ SQLPP_ALIAS_PROVIDER(kaesekuchen); int main() { - TabSample t; - TabFoo f; + test::TabFoo f; + test::TabBar t; interpret(insert_into(t).columns(t.gamma, t.beta), printer).flush(); interpret(insert_into(t).columns(t.gamma, t.beta).add_values(t.gamma = true, t.beta = "cheesecake"), printer).flush(); diff --git a/tests/PreparedTest.cpp b/tests/PreparedTest.cpp index f3c36cc1..2dc53876 100644 --- a/tests/PreparedTest.cpp +++ b/tests/PreparedTest.cpp @@ -23,7 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include "is_regular.h" #include @@ -35,8 +35,8 @@ DbMock db = {}; int main() { - TabSample t; - TabFoo f; + test::TabFoo f; + test::TabBar t; // empty parameter lists { diff --git a/tests/RemoveTest.cpp b/tests/RemoveTest.cpp index 20785ddf..616a2eaf 100644 --- a/tests/RemoveTest.cpp +++ b/tests/RemoveTest.cpp @@ -26,7 +26,7 @@ #include #include #include -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include "is_regular.h" @@ -36,7 +36,7 @@ DbMock::_context_t printer(std::cerr); int main() { - TabSample t; + test::TabBar t; auto x = t.alpha = 7; auto y = t.beta = "kaesekuchen"; diff --git a/tests/SelectTest.cpp b/tests/SelectTest.cpp index 1a32b1f5..94b2b790 100644 --- a/tests/SelectTest.cpp +++ b/tests/SelectTest.cpp @@ -23,7 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include "is_regular.h" #include @@ -46,8 +46,8 @@ namespace alias int main() { - TabSample t; - TabFoo f; + test::TabFoo f; + test::TabBar t; // Test a table { @@ -342,7 +342,7 @@ int main() static_assert(T::_is_expression, "T has to be an expression"); static_assert(std::is_same::value, "T has to be a numeric"); static_assert(sqlpp::is_numeric_t::value, "T has to be a numeric"); - static_assert(sqlpp::is_numeric_t::value, "TabSample.alpha has to be a numeric"); + static_assert(sqlpp::is_numeric_t::value, "TabBar.alpha has to be a numeric"); ((t.alpha + 7) + 4).asc(); static_assert(sqlpp::is_boolean_t::value, "Comparison expression have to be boolean"); auto x = (t.gamma == true) and (t.alpha == 7); @@ -352,7 +352,7 @@ int main() interpret(t.beta + "hallenhalma", printer).flush(); static_assert(sqlpp::must_not_insert_t::value, "alpha must not be inserted"); interpret(t.alpha, printer).flush(); - std::cerr << "\n" << sizeof(TabSample) << std::endl; + std::cerr << "\n" << sizeof(test::TabBar) << std::endl; static_assert(std::is_same::value, "alpha should be a named expression"); static_assert(sqlpp::is_named_expression_t::value, "alpha should be a named expression"); static_assert(sqlpp::is_named_expression_t::value, "an alias of alpha should be a named expression"); diff --git a/tests/TabSample.h b/tests/TabSample.h deleted file mode 100644 index c962675d..00000000 --- a/tests/TabSample.h +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2013, Roland Bock - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SQLPP_TAB_SAMPLE_H -#define SQLPP_TAB_SAMPLE_H - -#include -#include - - -namespace TabFoo_ -{ - struct Epsilon - { - struct _name_t - { - static constexpr const char* _get_name() { return "epsilon"; } - template - struct _member_t - { - T epsilon; - T& operator()() { return epsilon; } - const T& operator()() const { return epsilon; } - }; - }; - using _value_type = sqlpp::bigint; - struct _column_type - { - }; - }; - - struct Omega - { - struct _name_t - { - static constexpr const char* _get_name() { return "omega"; } - template - struct _member_t - { - T omega; - T& operator()() { return omega; } - const T& operator()() const { return omega; } - }; - }; - using _value_type = sqlpp::floating_point; - struct _column_type - { - }; - }; -} - -struct TabFoo: sqlpp::table_t< - TabFoo, - TabFoo_::Epsilon, - TabFoo_::Omega - > -{ - using _value_type = sqlpp::no_value_t; - struct _name_t - { - static constexpr const char* _get_name() { return "tab_foo"; } - }; - template - struct _member_t - { - T tabFoo; - T& operator()() { return tabFoo; } - const T& operator()() const { return tabFoo; } - }; - template - void serialize_impl(std::ostream& os, Db& db) const - { - os << _name_t::_get_name(); - } -}; - -namespace TabSample_ -{ - struct Alpha - { - struct _name_t - { - static constexpr const char* _get_name() { return "alpha"; } - template - struct _member_t - { - T alpha; - T& operator()() { return alpha; } - const T& operator()() const { return alpha; } - }; - }; - using _value_type = sqlpp::bigint; - struct _column_type - { - using _must_not_insert = std::true_type; - using _must_not_update = std::true_type; - using _can_be_null = std::true_type; - using _trivial_value_is_null = std::true_type; - using _foreign_key = decltype(TabFoo::omega); - }; - }; - - struct Beta - { - struct _name_t - { - static constexpr const char* _get_name() { return "beta"; } - template - struct _member_t - { - T beta; - T& operator()() { return beta; } - const T& operator()() const { return beta; } - }; - }; - using _value_type = sqlpp::varchar; - struct _column_type - { - using _can_be_null = std::true_type; - using _trivial_value_is_null = std::true_type; - using _must_not_update = std::true_type; - }; - }; - - struct Gamma - { - struct _name_t - { - static constexpr const char* _get_name() { return "gamma"; } - template - struct _member_t - { - T gamma; - T& operator()() { return gamma; } - const T& operator()() const { return gamma; } - }; - }; - using _value_type = sqlpp::boolean; - struct _column_type - { - using _require_insert = std::true_type; - }; - }; -} - -struct TabSample: sqlpp::table_t< - TabSample, - TabSample_::Alpha, - TabSample_::Beta, - TabSample_::Gamma - > -{ - using _value_type = sqlpp::no_value_t; - struct _name_t - { - static constexpr const char* _get_name() { return "tab_sample"; } - template - struct _member_t - { - T tabSample; - T& operator()() { return tabSample; } - const T& operator()() const { return tabSample; } - }; - }; - template - void serialize_impl(std::ostream& os, Db& db) const - { - os << _name_t::_get_name(); - } -}; - -#endif diff --git a/tests/UpdateTest.cpp b/tests/UpdateTest.cpp index c3f76f0b..a46c3dbd 100644 --- a/tests/UpdateTest.cpp +++ b/tests/UpdateTest.cpp @@ -25,7 +25,7 @@ #include #include -#include "TabSample.h" +#include "Sample.h" #include "MockDb.h" #include "is_regular.h" @@ -34,7 +34,7 @@ DbMock::_context_t printer(std::cerr); int main() { - TabSample t; + test::TabBar t; auto x = t.alpha = 7; auto y = t.beta = "kaesekuchen"; diff --git a/tests/sample.sql b/tests/sample.sql new file mode 100644 index 00000000..87e26c94 --- /dev/null +++ b/tests/sample.sql @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013, Roland Bock + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +CREATE TABLE tab_foo +( + epsilon bigint, + omega double +); + +CREATE TABLE tab_bar +( + alpha bigint AUTO_INCREMENT, + beta varchar(255) NULL DEFAULT "", + gamma bool NOT NULL +); +