diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..8aa06f0b --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,109 @@ +Important changes in sqlpp11 +============================ + +Breaking changes in 0.36: +------------------------- +__Abstract__: + +One of the main motivations of sqlpp11 is to prevent SQL programming mistakes at compile time. The following changes prevent reported mishaps. + * `from(a,b)` not allowed anymore, please use explicit joins + * `where(true)` not allowed anymore, please use `.unconditionally()` or sqlpp::value(true) + * `some_sql_expression and true` not allowed anymore, please use `tab.col == sqlpp::value(true)` if you really want to express this. + * `having(expression)` requires `expression` to be made of aggregates, e.g. columns named in `group_by()` or aggregate functions like `count()` or constant values. + * `where()` and `having` accept only one parameter + +__Explicit joins required__: + +Up until sqlpp11-0.35 you could write something like + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a,b) + .where(true)); +``` + +Using this syntax in `from()`, it was expected to have the join condition implicitly in the `where()` call. +But there is no reliable way to tell whether or not that condition is there. Or, if there definitely is none, whether +it was omitted on purpose or by accident. +In one case, an accidentally omitted join condition in the `where()` brought a production system to a screeching halt. + +In order to prevent this in the future, sqlpp11 now requires you to join table explicitly, including an explicit join condition, e.g. + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a.join(b).on(a.b == b.id)) + .where(true)); +``` + +Most joins, (`join`/`inner_join`, `left_outer_join`, `right_outer_join` and `outer_join`) require a join condition to given via `on()`. +The join condition has to be some sqlpp11 boolean expression. + +In those rare cases, when you really need a cross join, you can also use `cross_join()` which has no join condition, of course. + +``` +auto result = db(select(all_of(a), all_of(b)) + .from(a.cross_join(b)) + .where(true)); +``` + +__Use `.unconditionally()`__ + +If you want to select/update/remove all rows, earlier versions of sqlpp11 required the use of `where(true)`. Since version 0.36, use `unconditionally()`, for instance: +``` +auto result = db(select(all_of(t)).from(t).unconditionally()); +``` + +__Use `sqlpp::value()` to wrap bool values in boolean expressions__ + +Lets say you had + +``` +struct X +{ + int a; + int b; +}; +auto x = X{}; +``` + +Then earlier versions of sqlpp11 would compile the following expression: + +``` +select(all_of(t)).from(t).where(x.a == x.a or t.b == t.b); +``` + +What you probably meant was: + +``` +select(all_of(t)).from(t).where(t.a == x.a and t.b == x.b); +``` + +In order to prevent this kind of mistake, boolean operators in sql expressions require sqlpp boolean expressions as operators. +The library also requires the types of the left/right hand side operands of a comparison to be different, so that `t.a < t.a` does not compile any more. + +In the rare case you really have a bool value that you want to use a boolean sql expression, you have to wrap it in sqlpp::value(x), e.g. + +``` +select(all_of(t)).from(t).where(sqlpp::value(x.a == 17) and t.b == x.b); +``` + +__`having()` requires aggregate expressions__ + +In older versions, the following code was allowed: + +``` +select(all_of(t)).from(t).where(t.a > 7).having(t.b != ""); +``` + +As of sqlpp11-0.36, the having argument must be made of aggregate columns or functions, e.g. + +``` +select(all_of(t)).from(t).unconditionally().group_by(t.b).having(t.b != "", avg(t.c) < 42); +``` + +__`where()` and `having` accept only one expression__ + +In older versions, `where()` and `having()` would accept more than one argument and combine those arguments with `and`. +I am not sure this was ever used. So it just made life harder for the compiler. + +As of version 0.36, `where()` and `having()` accept only one parameter. diff --git a/README.md b/README.md index 90928ad3..ed9c3a23 100644 --- a/README.md +++ b/README.md @@ -145,11 +145,22 @@ Basic usage: __Linux install:__ git clone date library, needed connectors, cmake and make install them. -create DDL files (like mysql: 'show create table MyDatabase.MyTable', but remove backticks), create headers for them with provided python script: + +__Create DDL files__: +``` +mysql: 'show create table MyDatabase.MyTable' #or +mysqldump --no-data MyDatabase > MyDatabase.sql + +``` +Create headers for them with provided python script: ``` %sqlpp11_dir%/scripts/ddl2cpp ~/temp/MyTable.ddl ~/temp/MyTable %DatabaseNamespaceForExample% ``` -include generated header (MyTable.h), that's all +(In case you're getting notes about unsupported column type take a look at the other datatypes in sqlpp11/data_types. They are not hard to implement.) + +Include generated header (MyTable.h), that's all + + License: diff --git a/scripts/ddl2cpp b/scripts/ddl2cpp index 2b422078..c4d41584 100755 --- a/scripts/ddl2cpp +++ b/scripts/ddl2cpp @@ -128,6 +128,8 @@ types = { 'date' : 'day_point', 'datetime' : 'time_point', 'timestamp' : 'time_point', + 'enum' : 'text', # MYSQL + 'set' : 'text', # MYSQL } @@ -145,7 +147,7 @@ print('#include <' + INCLUDE + '/char_sequence.h>', file=header) print('', file=header) print('namespace ' + namespace, file=header) print('{', file=header) - +DataTypeError = False; for create in tableCreations: sqlTableName = create.tableName tableClass = toClassName(sqlTableName) @@ -181,7 +183,11 @@ for create in tableCreations: print(' const T& operator()() const { return ' + columnMember + '; }', file=header) print(' };', file=header) print(' };', file=header) - traitslist = [NAMESPACE + '::' + types[sqlColumnType]]; + try: + traitslist = [NAMESPACE + '::' + types[sqlColumnType]]; + except KeyError as e: + print ('Error: datatype "' + sqlColumnType + '"" is not supported.') + DataTypeError = True requireInsert = True if column.hasAutoValue: traitslist.append(NAMESPACE + '::tag::must_not_insert'); @@ -217,4 +223,10 @@ for create in tableCreations: print('}', file=header) print('#endif', file=header) - +if (DataTypeError): + print("Error: unsupported datatypes." ) + print("Possible solutions:") + print("A) Implement this datatype (examples: sqlpp11/data_types)" ) + print("B) Extend/upgrade ddl2cpp (edit types map)" ) + print("C) Raise an issue on github" ) + sys.exit(10) #return non-zero error code, we might need it for automation