1 /**
2 DDBC - D DataBase Connector - abstraction layer for RDBMS access, with interface similar to JDBC. 
3 
4 Source file ddbc/drivers/pgsqldstruct.ddbc.d.
5  DDBC library attempts to provide implementation independent interface to different databases.
6  
7  Set of supported RDBMSs can be extended by writing Drivers for particular DBs.
8  
9  JDBC documentation can be found here:
10  $(LINK http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/package-summary.html)$(BR)
11 
12  This module contains implementation POD utilities.
13 ----
14 import dstruct.ddbc;
15 import std.stdio;
16 
17 // prepare database connectivity
18 auto conn = createConnection("sqlite:ddbctest.sqlite");
19 scope(exit) conn.close();
20 Statement stmt = conn.createStatement();
21 scope(exit) stmt.close();
22 // fill database with test data
23 stmt.executeUpdate("DROP TABLE IF EXISTS user");
24 stmt.executeUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, flags int null)");
25 stmt.executeUpdate(`INSERT INTO user (id, name, flags) VALUES (1, "John", 5), (2, "Andrei", 2), (3, "Walter", 2), (4, "Rikki", 3), (5, "Iain", 0), (6, "Robert", 1)`);
26 
27 // our POD object
28 struct User {
29     long id;
30     string name;
31     int flags;
32 }
33 
34 writeln("reading all user table rows");
35 foreach(e; stmt.select!User) {
36     writeln("id:", e.id, " name:", e.name, " flags:", e.flags);
37 }
38 
39 writeln("reading user table rows with where and order by");
40 foreach(e; stmt.select!User.where("id < 6").orderBy("name desc")) {
41     writeln("id:", e.id, " name:", e.name, " flags:", e.flags);
42 }
43 ----
44 
45  Copyright: Copyright 2013
46  License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
47  Author:   Vadim Lopatin
48 */
49 module dstruct.ddbc.pods;
50 
51 import std.algorithm;
52 import std.traits;
53 import std.typecons;
54 import std.conv;
55 import std.datetime;
56 import std..string;
57 import std.variant;
58 
59 static import std.ascii;
60 
61 import dstruct.ddbc.core;
62 
63 alias Nullable!byte Byte;
64 alias Nullable!ubyte Ubyte;
65 alias Nullable!short Short;
66 alias Nullable!ushort Ushort;
67 alias Nullable!int Int;
68 alias Nullable!uint Uint;
69 alias Nullable!long Long;
70 alias Nullable!ulong Ulong;
71 alias Nullable!float Float;
72 alias Nullable!double Double;
73 alias Nullable!SysTime NullableSysTime;
74 alias Nullable!DateTime NullableDateTime;
75 alias Nullable!Date NullableDate;
76 alias Nullable!TimeOfDay NullableTimeOfDay;
77 
78 enum PropertyMemberType : int {
79     BOOL_TYPE,    // bool
80     BYTE_TYPE,    // byte
81     SHORT_TYPE,   // short
82     INT_TYPE,     // int
83     LONG_TYPE,    // long
84     UBYTE_TYPE,   // ubyte
85     USHORT_TYPE,  // ushort
86     UINT_TYPE,    // uint
87     ULONG_TYPE,   // ulong
88     NULLABLE_BYTE_TYPE,  // Nullable!byte
89     NULLABLE_SHORT_TYPE, // Nullable!short
90     NULLABLE_INT_TYPE,   // Nullable!int
91     NULLABLE_LONG_TYPE,  // Nullable!long
92     NULLABLE_UBYTE_TYPE, // Nullable!ubyte
93     NULLABLE_USHORT_TYPE,// Nullable!ushort
94     NULLABLE_UINT_TYPE,  // Nullable!uint
95     NULLABLE_ULONG_TYPE, // Nullable!ulong
96     FLOAT_TYPE,   // float
97     DOUBLE_TYPE,   // double
98     NULLABLE_FLOAT_TYPE, // Nullable!float
99     NULLABLE_DOUBLE_TYPE,// Nullable!double
100     STRING_TYPE,   // string
101     SYSTIME_TYPE,
102     DATETIME_TYPE, // std.datetime.DateTime
103     DATE_TYPE, // std.datetime.Date
104     TIME_TYPE, // std.datetime.TimeOfDay
105     NULLABLE_SYSTIME_TYPE,
106     NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
107     NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
108     NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
109     BYTE_ARRAY_TYPE, // byte[]
110     UBYTE_ARRAY_TYPE, // ubyte[]
111 }
112 
113 /// converts camel case MyEntityName to my_entity_name
114 string camelCaseToUnderscoreDelimited(immutable string s) {
115     string res;
116     bool lastLower = false;
117     static import std.ascii;
118 
119     foreach(ch; s) {
120         if (ch >= 'A' && ch <= 'Z') {
121             if (lastLower) {
122                 lastLower = false;
123                 res ~= "_";
124             }
125             res ~= std.ascii.toLower(ch);
126         } else if (ch >= 'a' && ch <= 'z') {
127             lastLower = true;
128             res ~= ch;
129         } else {
130             res ~= ch;
131         }
132     }
133     return res;
134 }
135 
136 unittest {
137     static assert(camelCaseToUnderscoreDelimited("User") == "user");
138     static assert(camelCaseToUnderscoreDelimited("MegaTableName") == "mega_table_name");
139 }
140 
141 
142 template isSupportedSimpleType(T, string m) {
143     alias typeof(__traits(getMember, T, m)) ti;
144     static if (is(ti == function)) {
145         static if (is(ReturnType!(ti) == bool)) {
146             enum bool isSupportedSimpleType = true;
147         } else static if (is(ReturnType!(ti) == byte)) {
148             enum bool isSupportedSimpleType = true;
149         } else static if (is(ReturnType!(ti) == short)) {
150             enum bool isSupportedSimpleType = true;
151         } else static if (is(ReturnType!(ti) == int)) {
152             enum bool isSupportedSimpleType = true;
153         } else static if (is(ReturnType!(ti) == long)) {
154             enum bool isSupportedSimpleType = true;
155         } else static if (is(ReturnType!(ti) == ubyte)) {
156             enum bool isSupportedSimpleType = true;
157         } else static if (is(ReturnType!(ti) == ushort)) {
158             enum bool isSupportedSimpleType = true;
159         } else static if (is(ReturnType!(ti) == uint)) {
160             enum bool isSupportedSimpleType = true;
161         } else static if (is(ReturnType!(ti) == ulong)) {
162             enum bool isSupportedSimpleType = true;
163         } else static if (is(ReturnType!(ti) == float)) {
164             enum bool isSupportedSimpleType = true;
165         } else static if (is(ReturnType!(ti) == double)) {
166             enum bool isSupportedSimpleType = true;
167         } else static if (is(ReturnType!(ti) == Nullable!byte)) {
168             enum bool isSupportedSimpleType = true;
169         } else static if (is(ReturnType!(ti) == Nullable!short)) {
170             enum bool isSupportedSimpleType = true;
171         } else static if (is(ReturnType!(ti) == Nullable!int)) {
172             enum bool isSupportedSimpleType = true;
173         } else static if (is(ReturnType!(ti) == Nullable!long)) {
174             enum bool isSupportedSimpleType = true;
175         } else static if (is(ReturnType!(ti) == Nullable!ubyte)) {
176             enum bool isSupportedSimpleType = true;
177         } else static if (is(ReturnType!(ti) == Nullable!ushort)) {
178             enum bool isSupportedSimpleType = true;
179         } else static if (is(ReturnType!(ti) == Nullable!uint)) {
180             enum bool isSupportedSimpleType = true;
181         } else static if (is(ReturnType!(ti) == Nullable!ulong)) {
182             enum bool isSupportedSimpleType = true;
183         } else static if (is(ReturnType!(ti) == Nullable!float)) {
184             enum bool isSupportedSimpleType = true;
185         } else static if (is(ReturnType!(ti) == Nullable!double)) {
186             enum bool isSupportedSimpleType = true;
187         } else static if (is(ReturnType!(ti) == string)) {
188             enum bool isSupportedSimpleType = true;
189         } else static if (is(ReturnType!(ti) == SysTime)) {
190             enum bool isSupportedSimpleType = true;
191         } else static if (is(ReturnType!(ti) == DateTime)) {
192             enum bool isSupportedSimpleType = true;
193         } else static if (is(ReturnType!(ti) == Date)) {
194             enum bool isSupportedSimpleType = true;
195         } else static if (is(ReturnType!(ti) == TimeOfDay)) {
196             enum bool isSupportedSimpleType = true;
197         } else static if (is(ReturnType!(ti) == Nullable!SysTime)) {
198             enum bool isSupportedSimpleType = true;
199         } else static if (is(ReturnType!(ti) == Nullable!DateTime)) {
200             enum bool isSupportedSimpleType = true;
201         } else static if (is(ReturnType!(ti) == Nullable!Date)) {
202             enum bool isSupportedSimpleType = true;
203         } else static if (is(ReturnType!(ti) == Nullable!TimeOfDay)) {
204             enum bool isSupportedSimpleType = true;
205         } else static if (is(ReturnType!(ti) == byte[])) {
206             enum bool isSupportedSimpleType = true;
207         } else static if (is(ReturnType!(ti) == ubyte[])) {
208             enum bool isSupportedSimpleType = true;
209         } else static if (true) {
210             enum bool isSupportedSimpleType = false;
211         }
212     } else static if (is(ti == bool)) {
213         enum bool isSupportedSimpleType = true;
214     } else static if (is(ti == byte)) {
215         enum bool isSupportedSimpleType = true;
216     } else static if (is(ti == short)) {
217         enum bool isSupportedSimpleType = true;
218     } else static if (is(ti == int)) {
219         enum bool isSupportedSimpleType = true;
220     } else static if (is(ti == long)) {
221         enum bool isSupportedSimpleType = true;
222     } else static if (is(ti == ubyte)) {
223         enum bool isSupportedSimpleType = true;
224     } else static if (is(ti == ushort)) {
225         enum bool isSupportedSimpleType = true;
226     } else static if (is(ti == uint)) {
227         enum bool isSupportedSimpleType = true;
228     } else static if (is(ti == ulong)) {
229         enum bool isSupportedSimpleType = true;
230     } else static if (is(ti == float)) {
231         enum bool isSupportedSimpleType = true;
232     } else static if (is(ti == double)) {
233         enum bool isSupportedSimpleType = true;
234     } else static if (is(ti == Nullable!byte)) {
235         enum bool isSupportedSimpleType = true;
236     } else static if (is(ti == Nullable!short)) {
237         enum bool isSupportedSimpleType = true;
238     } else static if (is(ti == Nullable!int)) {
239         enum bool isSupportedSimpleType = true;
240     } else static if (is(ti == Nullable!long)) {
241         enum bool isSupportedSimpleType = true;
242     } else static if (is(ti == Nullable!ubyte)) {
243         enum bool isSupportedSimpleType = true;
244     } else static if (is(ti == Nullable!ushort)) {
245         enum bool isSupportedSimpleType = true;
246     } else static if (is(ti == Nullable!uint)) {
247         enum bool isSupportedSimpleType = true;
248     } else static if (is(ti == Nullable!ulong)) {
249         enum bool isSupportedSimpleType = true;
250     } else static if (is(ti == Nullable!float)) {
251         enum bool isSupportedSimpleType = true;
252     } else static if (is(ti == Nullable!double)) {
253         enum bool isSupportedSimpleType = true;
254     } else static if (is(ti == string)) {
255         enum bool isSupportedSimpleType = true;
256     } else static if (is(ti == SysTime)) {
257         enum bool isSupportedSimpleType = true;
258     } else static if (is(ti == DateTime)) {
259         enum bool isSupportedSimpleType = true;
260     } else static if (is(ti == Date)) {
261         enum bool isSupportedSimpleType = true;
262     } else static if (is(ti == TimeOfDay)) {
263         enum bool isSupportedSimpleType = true;
264     } else static if (is(ti == Nullable!SysTime)) {
265         enum bool isSupportedSimpleType = true;
266     } else static if (is(ti == Nullable!DateTime)) {
267         enum bool isSupportedSimpleType = true;
268     } else static if (is(ti == Nullable!Date)) {
269         enum bool isSupportedSimpleType = true;
270     } else static if (is(ti == Nullable!TimeOfDay)) {
271         enum bool isSupportedSimpleType = true;
272     } else static if (is(ti == byte[])) {
273         enum bool isSupportedSimpleType = true;
274     } else static if (is(ti == ubyte[])) {
275         enum bool isSupportedSimpleType = true;
276     } else static if (true) {
277         enum bool isSupportedSimpleType = false;
278     }
279 }
280 
281 PropertyMemberType getPropertyType(ti)() {
282     //pragma(msg, T.stringof);
283     //alias typeof(T) ti;
284 	static if (is(ti == bool)) {
285 		return PropertyMemberType.BOOL_TYPE;
286     } else static if (is(ti == byte)) {
287         return PropertyMemberType.BYTE_TYPE;
288     } else static if (is(ti == short)) {
289         return PropertyMemberType.SHORT_TYPE;
290     } else static if (is(ti == int)) {
291         return PropertyMemberType.INT_TYPE;
292     } else static if (is(ti == long)) {
293         return PropertyMemberType.LONG_TYPE;
294     } else static if (is(ti == ubyte)) {
295         return PropertyMemberType.UBYTE_TYPE;
296     } else static if (is(ti == ushort)) {
297         return PropertyMemberType.USHORT_TYPE;
298     } else static if (is(ti == uint)) {
299         return PropertyMemberType.UINT_TYPE;
300     } else static if (is(ti == ulong)) {
301         return PropertyMemberType.ULONG_TYPE;
302     } else static if (is(ti == float)) {
303         return PropertyMemberType.FLOAT_TYPE;
304     } else static if (is(ti == double)) {
305         return PropertyMemberType.DOUBLE_TYPE;
306     } else static if (is(ti == Nullable!byte)) {
307         return PropertyMemberType.NULLABLE_BYTE_TYPE;
308     } else static if (is(ti == Nullable!short)) {
309         return PropertyMemberType.NULLABLE_SHORT_TYPE;
310     } else static if (is(ti == Nullable!int)) {
311         return PropertyMemberType.NULLABLE_INT_TYPE;
312     } else static if (is(ti == Nullable!long)) {
313         return PropertyMemberType.NULLABLE_LONG_TYPE;
314     } else static if (is(ti == Nullable!ubyte)) {
315         return PropertyMemberType.NULLABLE_UBYTE_TYPE;
316     } else static if (is(ti == Nullable!ushort)) {
317         return PropertyMemberType.NULLABLE_USHORT_TYPE;
318     } else static if (is(ti == Nullable!uint)) {
319         return PropertyMemberType.NULLABLE_UINT_TYPE;
320     } else static if (is(ti == Nullable!ulong)) {
321         return PropertyMemberType.NULLABLE_ULONG_TYPE;
322     } else static if (is(ti == Nullable!float)) {
323         return PropertyMemberType.NULLABLE_FLOAT_TYPE;
324     } else static if (is(ti == Nullable!double)) {
325         return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
326     } else static if (is(ti == string)) {
327         return PropertyMemberType.STRING_TYPE;
328     } else static if (is(ti == SysTime)) {
329         return PropertyMemberType.SYSTIME_TYPE;
330     } else static if (is(ti == DateTime)) {
331         return PropertyMemberType.DATETIME_TYPE;
332     } else static if (is(ti == Date)) {
333         return PropertyMemberType.DATE_TYPE;
334     } else static if (is(ti == TimeOfDay)) {
335         return PropertyMemberType.TIME_TYPE;
336     } else static if (is(ti == Nullable!SysTime)) {
337         return PropertyMemberType.NULLABLE_SYSTIME_TYPE;
338     } else static if (is(ti == Nullable!DateTime)) {
339         return PropertyMemberType.NULLABLE_DATETIME_TYPE;
340     } else static if (is(ti == Nullable!Date)) {
341         return PropertyMemberType.NULLABLE_DATE_TYPE;
342     } else static if (is(ti == Nullable!TimeOfDay)) {
343         return PropertyMemberType.NULLABLE_TIME_TYPE;
344     } else static if (is(ti == byte[])) {
345         return PropertyMemberType.BYTE_ARRAY_TYPE;
346     } else static if (is(ti == ubyte[])) {
347         return PropertyMemberType.UBYTE_ARRAY_TYPE;
348     } else static if (true) {
349         static assert (false, "has unsupported type " ~ ti.stringof);
350     }
351 }
352 
353 PropertyMemberType getPropertyMemberType(T, string m)() {
354     alias typeof(__traits(getMember, T, m)) ti;
355     static if (is(ti == bool)) {
356         return PropertyMemberType.BOOL_TYPE;
357     } else static if (is(ti == byte)) {
358         return PropertyMemberType.BYTE_TYPE;
359     } else static if (is(ti == short)) {
360         return PropertyMemberType.SHORT_TYPE;
361     } else static if (is(ti == int)) {
362         return PropertyMemberType.INT_TYPE;
363     } else static if (is(ti == long)) {
364         return PropertyMemberType.LONG_TYPE;
365     } else static if (is(ti == ubyte)) {
366         return PropertyMemberType.UBYTE_TYPE;
367     } else static if (is(ti == ushort)) {
368         return PropertyMemberType.USHORT_TYPE;
369     } else static if (is(ti == uint)) {
370         return PropertyMemberType.UINT_TYPE;
371     } else static if (is(ti == ulong)) {
372         return PropertyMemberType.ULONG_TYPE;
373     } else static if (is(ti == float)) {
374         return PropertyMemberType.FLOAT_TYPE;
375     } else static if (is(ti == double)) {
376         return PropertyMemberType.DOUBLE_TYPE;
377     } else static if (is(ti == Nullable!byte)) {
378         return PropertyMemberType.NULLABLE_BYTE_TYPE;
379     } else static if (is(ti == Nullable!short)) {
380         return PropertyMemberType.NULLABLE_SHORT_TYPE;
381     } else static if (is(ti == Nullable!int)) {
382         return PropertyMemberType.NULLABLE_INT_TYPE;
383     } else static if (is(ti == Nullable!long)) {
384         return PropertyMemberType.NULLABLE_LONG_TYPE;
385     } else static if (is(ti == Nullable!ubyte)) {
386         return PropertyMemberType.NULLABLE_UBYTE_TYPE;
387     } else static if (is(ti == Nullable!ushort)) {
388         return PropertyMemberType.NULLABLE_USHORT_TYPE;
389     } else static if (is(ti == Nullable!uint)) {
390         return PropertyMemberType.NULLABLE_UINT_TYPE;
391     } else static if (is(ti == Nullable!ulong)) {
392         return PropertyMemberType.NULLABLE_ULONG_TYPE;
393     } else static if (is(ti == Nullable!float)) {
394         return PropertyMemberType.NULLABLE_FLOAT_TYPE;
395     } else static if (is(ti == Nullable!double)) {
396         return PropertyMemberType.NULLABLE_DOUBLE_TYPE;
397     } else static if (is(ti == string)) {
398         return PropertyMemberType.STRING_TYPE;
399     } else static if (is(ti == SysTime)) {
400         return PropertyMemberType.SYSTIME_TYPE;
401     } else static if (is(ti == DateTime)) {
402         return PropertyMemberType.DATETIME_TYPE;
403     } else static if (is(ti == Date)) {
404         return PropertyMemberType.DATE_TYPE;
405     } else static if (is(ti == TimeOfDay)) {
406         return PropertyMemberType.TIME_TYPE;
407     } else static if (is(ti == Nullable!SysTime)) {
408         return PropertyMemberType.NULLABLE_SYSTIME_TYPE;
409     } else static if (is(ti == Nullable!DateTime)) {
410         return PropertyMemberType.NULLABLE_DATETIME_TYPE;
411     } else static if (is(ti == Nullable!Date)) {
412         return PropertyMemberType.NULLABLE_DATE_TYPE;
413     } else static if (is(ti == Nullable!TimeOfDay)) {
414         return PropertyMemberType.NULLABLE_TIME_TYPE;
415     } else static if (is(ti == byte[])) {
416         return PropertyMemberType.BYTE_ARRAY_TYPE;
417     } else static if (is(ti == ubyte[])) {
418         return PropertyMemberType.UBYTE_ARRAY_TYPE;
419     } else static if (true) {
420         static assert (false, "Member " ~ m ~ " of class " ~ T.stringof ~ " has unsupported type " ~ ti.stringof);
421     }
422 }
423 
424 string getPropertyReadCode(T, string m)() {
425     return "entity." ~ m;
426 }
427 
428 string getPropertyReadCode(alias T)() {
429     return "entity." ~ T.stringof;
430 }
431 
432 static immutable bool[] ColumnTypeCanHoldNulls = 
433 [
434     false, //BOOL_TYPE     // bool
435     false, //BYTE_TYPE,    // byte
436     false, //SHORT_TYPE,   // short
437     false, //INT_TYPE,     // int
438     false, //LONG_TYPE,    // long
439     false, //UBYTE_TYPE,   // ubyte
440     false, //USHORT_TYPE,  // ushort
441     false, //UINT_TYPE,    // uint
442     false, //ULONG_TYPE,   // ulong
443     true, //NULLABLE_BYTE_TYPE,  // Nullable!byte
444     true, //NULLABLE_SHORT_TYPE, // Nullable!short
445     true, //NULLABLE_INT_TYPE,   // Nullable!int
446     true, //NULLABLE_LONG_TYPE,  // Nullable!long
447     true, //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
448     true, //NULLABLE_USHORT_TYPE,// Nullable!ushort
449     true, //NULLABLE_UINT_TYPE,  // Nullable!uint
450     true, //NULLABLE_ULONG_TYPE, // Nullable!ulong
451     false,//FLOAT_TYPE,   // float
452     false,//DOUBLE_TYPE,   // double
453     true, //NULLABLE_FLOAT_TYPE, // Nullable!float
454     true, //NULLABLE_DOUBLE_TYPE,// Nullable!double
455     false, //STRING_TYPE   // string  -- treat as @NotNull by default
456     false, //SYSTIME_TYPE
457     false, //DATETIME_TYPE, // std.datetime.DateTime
458     false, //DATE_TYPE, // std.datetime.Date
459     false, //TIME_TYPE, // std.datetime.TimeOfDay
460     true, //NULLABLE_SYSTIME_TYPE
461     true, //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
462     true, //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
463     true, //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
464     true, //BYTE_ARRAY_TYPE, // byte[]
465     true, //UBYTE_ARRAY_TYPE, // ubyte[]
466 ];
467 
468 bool isColumnTypeNullableByDefault(T, string m)() {
469     return ColumnTypeCanHoldNulls[getPropertyMemberType!(T,m)];
470 }
471 
472 static immutable string[] ColumnTypeKeyIsSetCode = 
473 [
474     "(%s != 0)", //BOOL_TYPE     // bool
475     "(%s != 0)", //BYTE_TYPE,    // byte
476     "(%s != 0)", //SHORT_TYPE,   // short
477     "(%s != 0)", //INT_TYPE,     // int
478     "(%s != 0)", //LONG_TYPE,    // long
479     "(%s != 0)", //UBYTE_TYPE,   // ubyte
480     "(%s != 0)", //USHORT_TYPE,  // ushort
481     "(%s != 0)", //UINT_TYPE,    // uint
482     "(%s != 0)", //ULONG_TYPE,   // ulong
483     "(!%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
484     "(!%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
485     "(!%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
486     "(!%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
487     "(!%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
488     "(!%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
489     "(!%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
490     "(!%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
491     "(%s != 0)",//FLOAT_TYPE,   // float
492     "(%s != 0)",//DOUBLE_TYPE,   // double
493     "(!%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
494     "(!%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
495     "(%s !is null)", //STRING_TYPE   // string
496     "(%s != SysTime())", //SYSTIME_TYPE, // std.datetime.systime : SysTime
497     "(%s != DateTime())", //DATETIME_TYPE, // std.datetime.DateTime
498     "(%s != Date())", //DATE_TYPE, // std.datetime.Date
499     "(%s != TimeOfDay())", //TIME_TYPE, // std.datetime.TimeOfDay
500     "(!%s.isNull)", //NULLABLE_SYSTIME_TYPE, // Nullable!std.datetime.systime.SysTime
501     "(!%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
502     "(!%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
503     "(!%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
504     "(%s !is null)", //BYTE_ARRAY_TYPE, // byte[]
505     "(%s !is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
506 ];
507 
508 string getColumnTypeKeyIsSetCode(T, string m)() {
509     return substituteParam(ColumnTypeKeyIsSetCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
510 }
511 
512 static immutable string[] ColumnTypeIsNullCode = 
513 [
514     "(false)", //BOOL_TYPE     // bool
515     "(false)", //BYTE_TYPE,    // byte
516     "(false)", //SHORT_TYPE,   // short
517     "(false)", //INT_TYPE,     // int
518     "(false)", //LONG_TYPE,    // long
519     "(false)", //UBYTE_TYPE,   // ubyte
520     "(false)", //USHORT_TYPE,  // ushort
521     "(false)", //UINT_TYPE,    // uint
522     "(false)", //ULONG_TYPE,   // ulong
523     "(%s.isNull)", //NULLABLE_BYTE_TYPE,  // Nullable!byte
524     "(%s.isNull)", //NULLABLE_SHORT_TYPE, // Nullable!short
525     "(%s.isNull)", //NULLABLE_INT_TYPE,   // Nullable!int
526     "(%s.isNull)", //NULLABLE_LONG_TYPE,  // Nullable!long
527     "(%s.isNull)", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
528     "(%s.isNull)", //NULLABLE_USHORT_TYPE,// Nullable!ushort
529     "(%s.isNull)", //NULLABLE_UINT_TYPE,  // Nullable!uint
530     "(%s.isNull)", //NULLABLE_ULONG_TYPE, // Nullable!ulong
531     "(false)",//FLOAT_TYPE,   // float
532     "(false)",//DOUBLE_TYPE,   // double
533     "(%s.isNull)", //NULLABLE_FLOAT_TYPE, // Nullable!float
534     "(%s.isNull)", //NULLABLE_DOUBLE_TYPE,// Nullable!double
535     "(%s is null)", //STRING_TYPE   // string
536     "(false)", //SYSTIME_TYPE
537     "(false)", //DATETIME_TYPE, // std.datetime.DateTime
538     "(false)", //DATE_TYPE, // std.datetime.Date
539     "(false)", //TIME_TYPE, // std.datetime.TimeOfDay
540     "(%s.isNull)", //NULLABLE_SYSTIME_TYPE
541     "(%s.isNull)", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
542     "(%s.isNull)", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
543     "(%s.isNull)", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
544     "(%s is null)", //BYTE_ARRAY_TYPE, // byte[]
545     "(%s is null)", //UBYTE_ARRAY_TYPE, // ubyte[]
546 ];
547 
548 string getColumnTypeIsNullCode(T, string m)() {
549     return substituteParam(ColumnTypeIsNullCode[getPropertyMemberType!(T,m)()], getPropertyReadCode!(T,m)());
550 }
551 
552 static immutable string[] ColumnTypeSetNullCode = 
553 [
554     "bool nv;", // BOOL_TYPE   // bool
555     "byte nv = 0;", //BYTE_TYPE,    // byte
556     "short nv = 0;", //SHORT_TYPE,   // short
557     "int nv = 0;", //INT_TYPE,     // int
558     "long nv = 0;", //LONG_TYPE,    // long
559     "ubyte nv = 0;", //UBYTE_TYPE,   // ubyte
560     "ushort nv = 0;", //USHORT_TYPE,  // ushort
561     "uint nv = 0;", //UINT_TYPE,    // uint
562     "ulong nv = 0;", //ULONG_TYPE,   // ulong
563     "Nullable!byte nv;", //NULLABLE_BYTE_TYPE,  // Nullable!byte
564     "Nullable!short nv;", //NULLABLE_SHORT_TYPE, // Nullable!short
565     "Nullable!int nv;", //NULLABLE_INT_TYPE,   // Nullable!int
566     "Nullable!long nv;", //NULLABLE_LONG_TYPE,  // Nullable!long
567     "Nullable!ubyte nv;", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
568     "Nullable!ushort nv;", //NULLABLE_USHORT_TYPE,// Nullable!ushort
569     "Nullable!uint nv;", //NULLABLE_UINT_TYPE,  // Nullable!uint
570     "Nullable!ulong nv;", //NULLABLE_ULONG_TYPE, // Nullable!ulong
571     "float nv = 0;",//FLOAT_TYPE,   // float
572     "double nv = 0;",//DOUBLE_TYPE,   // double
573     "Nullable!float nv;", //NULLABLE_FLOAT_TYPE, // Nullable!float
574     "Nullable!double nv;", //NULLABLE_DOUBLE_TYPE,// Nullable!double
575     "string nv;", //STRING_TYPE   // string
576     "SysTime nv;", //SYSTIME_TYPE
577     "DateTime nv;", //DATETIME_TYPE, // std.datetime.DateTime
578     "Date nv;", //DATE_TYPE, // std.datetime.Date
579     "TimeOfDay nv;", //TIME_TYPE, // std.datetime.TimeOfDay
580     "Nullable!SysTime nv;", //NULLABLE_SYSTIME_TYPE
581     "Nullable!DateTime nv;", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
582     "Nullable!Date nv;", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
583     "Nullable!TimeOfDay nv;", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
584     "byte[] nv = null;", //BYTE_ARRAY_TYPE, // byte[]
585     "ubyte[] nv = null;", //UBYTE_ARRAY_TYPE, // ubyte[]
586 ];
587 
588 static immutable string[] ColumnTypePropertyToVariant = 
589 [
590     "Variant(%s)", //BOOL_TYPE     // bool
591     "Variant(%s)", //BYTE_TYPE,    // byte
592     "Variant(%s)", //SHORT_TYPE,   // short
593     "Variant(%s)", //INT_TYPE,     // int
594     "Variant(%s)", //LONG_TYPE,    // long
595     "Variant(%s)", //UBYTE_TYPE,   // ubyte
596     "Variant(%s)", //USHORT_TYPE,  // ushort
597     "Variant(%s)", //UINT_TYPE,    // uint
598     "Variant(%s)", //ULONG_TYPE,   // ulong
599     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
600     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_SHORT_TYPE, // Nullable!short
601     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_INT_TYPE,   // Nullable!int
602     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_LONG_TYPE,  // Nullable!long
603     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
604     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
605     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_UINT_TYPE,  // Nullable!uint
606     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
607     "Variant(%s)",//FLOAT_TYPE,   // float
608     "Variant(%s)",//DOUBLE_TYPE,   // double
609     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_FLOAT_TYPE, // Nullable!float
610     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
611     "Variant(%s)", //STRING_TYPE   // string
612     "Variant(%s)", //SYSTIME_TYPE
613     "Variant(%s)", //DATETIME_TYPE, // std.datetime.DateTime
614     "Variant(%s)", //DATE_TYPE, // std.datetime.Date
615     "Variant(%s)", //TIME_TYPE, // std.datetime.TimeOfDay
616     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_SYSTIME_TYPE
617     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
618     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
619     "(%s.isNull ? Variant(null) : Variant(%s.get()))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
620     "Variant(%s)", //BYTE_ARRAY_TYPE, // byte[]
621     "Variant(%s)", //UBYTE_ARRAY_TYPE, // ubyte[]
622 ];
623 
624 static immutable string[] ColumnTypeDatasetReaderCode = 
625 [
626     "r.getBoolean(index)", //BOOL_TYPE,    // bool
627     "r.getByte(index)", //BYTE_TYPE,    // byte
628     "r.getShort(index)", //SHORT_TYPE,   // short
629     "r.getInt(index)", //INT_TYPE,     // int
630     "r.getLong(index)", //LONG_TYPE,    // long
631     "r.getUbyte(index)", //UBYTE_TYPE,   // ubyte
632     "r.getUshort(index)", //USHORT_TYPE,  // ushort
633     "r.getUint(index)", //UINT_TYPE,    // uint
634     "r.getUlong(index)", //ULONG_TYPE,   // ulong
635     "Nullable!byte(r.getByte(index))", //NULLABLE_BYTE_TYPE,  // Nullable!byte
636     "Nullable!short(r.getShort(index))", //NULLABLE_SHORT_TYPE, // Nullable!short
637     "Nullable!int(r.getInt(index))", //NULLABLE_INT_TYPE,   // Nullable!int
638     "Nullable!long(r.getLong(index))", //NULLABLE_LONG_TYPE,  // Nullable!long
639     "Nullable!ubyte(r.getUbyte(index))", //NULLABLE_UBYTE_TYPE, // Nullable!ubyte
640     "Nullable!ushort(r.getUshort(index))", //NULLABLE_USHORT_TYPE,// Nullable!ushort
641     "Nullable!uint(r.getUint(index))", //NULLABLE_UINT_TYPE,  // Nullable!uint
642     "Nullable!ulong(r.getUlong(index))", //NULLABLE_ULONG_TYPE, // Nullable!ulong
643     "r.getFloat(index)",//FLOAT_TYPE,   // float
644     "r.getDouble(index)",//DOUBLE_TYPE,   // double
645     "Nullable!float(r.getFloat(index))", //NULLABLE_FLOAT_TYPE, // Nullable!float
646     "Nullable!double(r.getDouble(index))", //NULLABLE_DOUBLE_TYPE,// Nullable!double
647     "r.getString(index)", //STRING_TYPE   // string
648     "r.getSysTime(index)", //SYSTIME_TYPE
649     "r.getDateTime(index)", //DATETIME_TYPE, // std.datetime.DateTime
650     "r.getDate(index)", //DATE_TYPE, // std.datetime.Date
651     "r.getTime(index)", //TIME_TYPE, // std.datetime.TimeOfDay
652     "Nullable!SysTime(r.getSysTime(index))", //NULLABLE_SYSTIME_TYPE
653     "Nullable!DateTime(r.getDateTime(index))", //NULLABLE_DATETIME_TYPE, // Nullable!std.datetime.DateTime
654     "Nullable!Date(r.getDate(index))", //NULLABLE_DATE_TYPE, // Nullable!std.datetime.Date
655     "Nullable!TimeOfDay(r.getTime(index))", //NULLABLE_TIME_TYPE, // Nullable!std.datetime.TimeOfDay
656     "r.getBytes(index)", //BYTE_ARRAY_TYPE, // byte[]
657     "r.getUbytes(index)", //UBYTE_ARRAY_TYPE, // ubyte[]
658 ];
659 
660 string getColumnTypeDatasetReadCode(T, string m)() {
661     return ColumnTypeDatasetReaderCode[getPropertyMemberType!(T,m)()];
662 }
663 
664 string getVarTypeDatasetReadCode(T)() {
665     return ColumnTypeDatasetReaderCode[getPropertyType!T];
666 }
667 
668 string getPropertyWriteCode(T, string m)() {
669     //immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
670     immutable string nullValueCode = ColumnTypeSetNullCode[getPropertyMemberType!(T,m)()];
671     immutable string datasetReader = "(!r.isNull(index) ? " ~ getColumnTypeDatasetReadCode!(T, m)() ~ " : nv)";
672     return nullValueCode ~ "entity." ~ m ~ " = " ~ datasetReader ~ ";";
673 }
674 
675 string getPropertyWriteCode(T)() {
676     //immutable PropertyMemberKind kind = getPropertyMemberKind!(T, m)();
677     immutable string nullValueCode = ColumnTypeSetNullCode[getPropertyType!T];
678     immutable string datasetReader = "(!r.isNull(index) ? " ~ getVarTypeDatasetReadCode!T ~ " : nv)";
679     return nullValueCode ~ "a = " ~ datasetReader ~ ";";
680 }
681 
682 /// returns array of field names
683 string[] getColumnNamesForType(T)()  if (__traits(isPOD, T)) {
684     string[] res;
685     foreach(m; FieldNameTuple!T) {
686         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
687             // skip non-public members
688             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
689                 static if (isSupportedSimpleType!(T, m)) {
690                     res ~= m;
691                 }
692             }
693         }
694     }
695     return res;
696 }
697 
698 string getColumnReadCode(T, string m)() {
699     return "{" ~ getPropertyWriteCode!(T,m)() ~ "index++;}\n";
700 }
701 
702 string getAllColumnsReadCode(T)() {
703     string res = "int index = 1;\n";
704     foreach(m; FieldNameTuple!T) {
705         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
706             // skip non-public members
707             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
708                 static if (isSupportedSimpleType!(T, m)) {
709                     res ~= getColumnReadCode!(T, m);
710                 }
711             }
712         }
713     }
714     return res;
715 }
716 
717 string getAllColumnsReadCode(T, fieldList...)() {
718     string res = "int index = 1;\n";
719     foreach(m; fieldList) {
720         res ~= getColumnReadCode!(T, m);
721     }
722     return res;
723 }
724 
725 unittest {
726     struct User1 {
727         long id;
728         string name;
729         int flags;
730     }
731     //pragma(msg, "nullValueCode = " ~ ColumnTypeSetNullCode[getPropertyMemberType!(User, "id")()]);
732     //pragma(msg, "datasetReader = " ~ getColumnTypeDatasetReadCode!(User, "id")());
733     //pragma(msg, "getPropertyWriteCode: " ~ getPropertyWriteCode!(User, "id"));
734     //pragma(msg, "getAllColumnsReadCode:\n" ~ getAllColumnsReadCode!(User));
735     //static assert(getPropertyWriteCode!(User, "id") == "long nv = 0;entity.id = (!r.isNull(index) ? r.getLong(index) : nv);");
736 }
737 
738 unittest {
739     struct User1 {
740         long id;
741         string name;
742         int flags;
743     }
744     static assert(getPropertyMemberType!(User1, "id")() == PropertyMemberType.LONG_TYPE);
745     static assert(getPropertyMemberType!(User1, "name")() == PropertyMemberType.STRING_TYPE);
746     //pragma(msg, "getPropertyMemberType unit test passed");
747 }
748 
749 
750 
751 /// returns table name for struct type
752 string getTableNameForType(T)() if (__traits(isPOD, T)) {
753     return camelCaseToUnderscoreDelimited(T.stringof);
754 }
755 
756 unittest {
757     struct User1 {
758         long id;
759         string name;
760         int flags;
761     }
762     static assert(getTableNameForType!User1() == "user1");
763 }
764 
765 /// returns "SELECT <field list> FROM <table name>"
766 string generateSelectSQL(T)() {
767     return "SELECT " ~ join(getColumnNamesForType!(T)(), ",") ~ " FROM " ~ getTableNameForType!(T)();
768 }
769 
770 unittest {
771     struct User1 {
772         long id;
773         string name;
774         int flags;
775     }
776     static assert(generateSelectSQL!User1() == "SELECT id,name,flags FROM user1");
777 }
778 
779 string joinFieldList(fieldList...)() {
780     string res;
781     foreach(f; fieldList) {
782         if (res.length)
783             res ~= ",";
784         res ~= f;
785     }
786     return res;
787 }
788 
789 /// returns "SELECT <field list> FROM <table name>"
790 string generateSelectSQL(T, fieldList...)() {
791     string res = "SELECT ";
792     res ~= joinFieldList!fieldList;
793     res ~= " FROM " ~ getTableNameForType!(T)();
794     return res;
795 }
796 
797 unittest {
798     //pragma(msg, "column names: " ~ join(getColumnNamesForType!(User)(), ","));
799     //pragma(msg, "select SQL: " ~ generateSelectSQL!(User)());
800 }
801 
802 /// returns "SELECT <field list> FROM <table name>"
803 string generateSelectForGetSQL(T)() {
804     string res = generateSelectSQL!T();
805     res ~= " WHERE id=";
806     return res;
807 }
808 
809 string generateSelectForGetSQLWithFilter(T)() {
810   string res = generateSelectSQL!T();
811   res ~= " WHERE ";
812   return res;
813 }
814 
815 T get(T)(Statement stmt, long id) {
816   T entity;
817   static immutable getSQL = generateSelectForGetSQL!T();
818   ResultSet r;
819   r = stmt.executeQuery(getSQL ~ to!string(id));
820   r.next();
821   mixin(getAllColumnsReadCode!T());
822   return entity;
823 }
824 
825 T get(T)(Statement stmt, string filter) {
826   T entity;
827   static immutable getSQL = generateSelectForGetSQLWithFilter!T();
828   ResultSet r;
829   r = stmt.executeQuery(getSQL ~ filter);
830   r.next();
831   mixin(getAllColumnsReadCode!T());
832   return entity;
833 }
834 
835 string getColumnTypeDatasetReadCodeByName(T, string m)() {
836     PropertyMemberType pmt = getPropertyMemberType!(T,m)();
837     final switch(pmt) with (PropertyMemberType) {
838         case BOOL_TYPE:
839             return `r.getBoolean("` ~ m ~ `")`;
840         case BYTE_TYPE:
841             return `r.getByte("` ~ m ~ `")`;
842         case SHORT_TYPE:
843             return `r.getShort("` ~ m ~ `")`;
844         case INT_TYPE:
845             return `r.getInt("` ~ m ~ `")`;
846         case LONG_TYPE:
847             return `r.getLong("` ~ m ~ `")`;
848         case UBYTE_TYPE:
849             return `r.getUbyte("` ~ m ~ `")`;
850         case USHORT_TYPE:
851             return `r.getUshort("` ~ m ~ `")`;
852         case UINT_TYPE:
853             return `r.getUint("` ~ m ~ `")`;
854         case ULONG_TYPE:
855             return `r.getUlong("` ~ m ~ `")`;
856         case FLOAT_TYPE:
857             return `r.getFloat("` ~ m ~ `")`;
858         case DOUBLE_TYPE:
859             return `r.getDouble("` ~ m ~ `")`;
860         case STRING_TYPE:
861             return `r.getString("` ~ m ~ `")`;
862         case DATE_TYPE:
863             return `r.getDate("` ~ m ~ `")`;
864         case TIME_TYPE:
865             return `r.getTime("` ~ m ~ `")`;
866         case SYSTIME_TYPE:
867             return `r.getSysTime("` ~ m ~ `")`;
868         case DATETIME_TYPE:
869             return `r.getDateTime("` ~ m ~ `")`;
870         case BYTE_ARRAY_TYPE:
871             return `r.getBytes("` ~ m ~ `")`;
872         case UBYTE_ARRAY_TYPE:
873             return `r.getUbytes("` ~ m ~ `")`;
874         case NULLABLE_BYTE_TYPE:
875             return `Nullable!byte(r.getByte("` ~ m ~ `"))`;
876         case NULLABLE_SHORT_TYPE:
877             return `Nullable!short(r.getShort("` ~ m ~ `"))`;
878         case NULLABLE_INT_TYPE:
879             return `Nullable!int(r.getInt("` ~ m ~ `"))`;
880         case NULLABLE_LONG_TYPE:
881             return `Nullable!long(r.getLong("` ~ m ~ `"))`;
882         case NULLABLE_UBYTE_TYPE:
883             return `Nullable!ubyte(r.getUbyte("` ~ m ~ `"))`;
884         case NULLABLE_USHORT_TYPE:
885             return `Nullable!ushort(r.getUshort("` ~ m ~ `"))`;
886         case NULLABLE_UINT_TYPE:
887             return `Nullable!uint(r.getUint("` ~ m ~ `"))`;
888         case NULLABLE_ULONG_TYPE:
889             return `Nullable!ulong(r.getUlong("` ~ m ~ `"))`;
890         case NULLABLE_FLOAT_TYPE:
891             return `Nullable!float(r.getFloat("` ~ m ~ `"))`;
892         case NULLABLE_DOUBLE_TYPE:
893             return `Nullable!double(r.getDouble("` ~ m ~ `"))`;
894         case NULLABLE_STRING_TYPE:
895             return `r.getString("` ~ m ~ `")`;
896         case NULLABLE_DATE_TYPE:
897             return `Nullable!Date(r.getDate("` ~ m ~ `"))`;
898         case NULLABLE_TIME_TYPE:
899             return `Nullable!Time(r.getTime("` ~ m ~ `"))`;
900         case NULLABLE_SYSTIME_TYPE:
901             return `Nullable!SysTime(r.getSysTime("` ~ m ~ `"))`;
902         case NULLABLE_DATETIME_TYPE:
903             return `Nullable!DateTime(r.getDateTime("` ~ m ~ `"))`;
904     }
905 }
906 
907 string getPropertyWriteCodeByName(T, string m)() {
908     immutable string nullValueCode = ColumnTypeSetNullCode[getPropertyMemberType!(T,m)()];
909     immutable string propertyWriter = nullValueCode ~ "entity." ~ m ~ " = " ~ getColumnTypeDatasetReadCodeByName!(T, m)() ~ ";\n";
910     return propertyWriter ~ "if (r.wasNull) entity." ~ m ~ " = nv;";
911 }
912 
913 string getColumnReadCodeByName(T, string m)() {
914     return "{" ~ getPropertyWriteCodeByName!(T,m)() ~ "}\n";
915 }
916 
917 string getAllColumnsReadCodeByName(T)() {
918     string res;
919     foreach(m; FieldNameTuple!T) {
920         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
921             // skip non-public members
922             static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
923                 static if (isSupportedSimpleType!(T, m)) {
924                     res ~= getColumnReadCodeByName!(T, m);
925                 }
926             }
927         }
928     }
929     return res;
930 }
931 
932 /**
933  * Extract a row from the result set as the specified type.
934  * Requires that next has already been checked.
935  * Can be used for example to extract rows from executing a PreparedStatement.
936  */
937 T get(T)(ResultSet r) {
938     T entity;
939     mixin(getAllColumnsReadCodeByName!T());
940     return entity;
941 }
942 
943 /// range for select query
944 struct select(T, fieldList...) if (__traits(isPOD, T)) {
945   T entity;
946   private Statement stmt;
947   private ResultSet r;
948   static immutable selectSQL = generateSelectSQL!(T, fieldList)();
949   string whereCondSQL;
950   string orderBySQL;
951   this(Statement stmt) {
952     this.stmt = stmt;
953   }
954   ref select where(string whereCond) {
955     whereCondSQL = " WHERE " ~ whereCond;
956     return this;
957   }
958   ref select orderBy(string order) {
959     orderBySQL = " ORDER BY " ~ order;
960     return this;
961   }
962   ref T front() {
963     return entity;
964   }
965   void popFront() {
966   }
967   @property bool empty() {
968     if (!r)
969       r = stmt.executeQuery(selectSQL ~ whereCondSQL ~ orderBySQL);
970     if (!r.next())
971       return true;
972     mixin(getAllColumnsReadCode!(T, fieldList));
973     return false;
974   }
975   ~this() {
976     if (r)
977       r.close();
978   }
979 }
980 
981 /// returns "INSERT INTO <table name> (<field list>) VALUES (value list)
982 string generateInsertSQL(T)() {
983     string res = "INSERT INTO " ~ getTableNameForType!(T)();
984     string []values;
985     foreach(m; FieldNameTuple!T) {
986       if (m != "id") {
987         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
988           // skip non-public members
989           static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
990             values ~= m;
991           }
992         }
993       }
994     }
995     res ~= "(" ~ join(values, ",") ~ ")";
996     res ~= " VALUES ";
997     return res;
998 }
999 
1000 string addFieldValue(T)(string m) {
1001   string tmp = `{Variant v = o.`~m~`;`;
1002   tmp ~=  `static if (isColumnTypeNullableByDefault!(T, "`~m~`")()) {`;
1003   tmp ~= `	if(o.`~m~`.isNull) {`;
1004   tmp ~= `		values ~= "NULL";`;
1005   tmp ~= `	} else {`;
1006   tmp ~= `		values ~= "'" ~ to!string(o.` ~ m ~ `) ~ "'";`;
1007   tmp ~= `}} else {`;
1008   tmp ~= `		values ~= "'" ~ to!string(o.` ~ m ~ `) ~ "'";`;
1009   tmp ~= `}}`;
1010   return tmp;
1011   // return `values ~= "'" ~ to!string(o.` ~ m ~ `) ~ "'";`;
1012 }
1013 
1014 bool insert(T)(Statement stmt, ref T o) if (__traits(isPOD, T)) {
1015     auto insertSQL = generateInsertSQL!(T)();
1016     string []values;
1017     foreach(m; FieldNameTuple!T) {
1018       if (m != "id") {
1019         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
1020           // skip non-public members
1021           static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
1022             // pragma(msg,addFieldValue!(T)(m));
1023             mixin(addFieldValue!(T)(m));
1024           }
1025         }
1026       }
1027     }
1028     insertSQL ~= "(" ~ join(values, ",") ~ ")";
1029     Variant insertId;
1030     stmt.executeUpdate(insertSQL, insertId);
1031     o.id = insertId.get!long;
1032     return true;
1033 }
1034 
1035 /// returns "UPDATE <table name> SET field1=value1 WHERE id=id
1036 string generateUpdateSQL(T)() {
1037   string res = "UPDATE " ~ getTableNameForType!(T)();
1038   string []values;
1039   foreach(m; FieldNameTuple!T) {
1040     if (m != "id") {
1041       static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
1042         // skip non-public members
1043         static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
1044           values ~= m;
1045         }
1046       }
1047     }
1048   }
1049   res ~= " SET ";
1050   return res;
1051 }
1052 
1053 string addUpdateValue(T)(string m) {
1054   return `values ~= "` ~ m ~ `=\"" ~ to!string(o.` ~ m ~ `) ~ "\"";`;
1055 }
1056 
1057 bool update(T)(Statement stmt, ref T o) if (__traits(isPOD, T)) {
1058     auto updateSQL = generateUpdateSQL!(T)();
1059     string []values;
1060     foreach(m; FieldNameTuple!T) {
1061       if (m != "id") {
1062         static if (__traits(compiles, (typeof(__traits(getMember, T, m))))){
1063           // skip non-public members
1064           static if (__traits(getProtection, __traits(getMember, T, m)) == "public") {
1065 
1066             // static if(typeof(__traits(getMember, T, m)) == function) {
1067             //     pragma(msg, "Ignoring function: "~m~"()");
1068             // }
1069 
1070             // static if(is(__traits(getMember, T, m) == function)) {
1071             //     pragma(msg, "Ignoring function: "~m~"()");
1072             // } else {
1073             //     pragma(msg, addUpdateValue!(T)(m));
1074             //     //mixin(addUpdateValue!(T)(m));
1075             // }
1076 
1077             static if (__traits(getOverloads, T, m).length > 0) {
1078                 // even if the struct/class doesn't have and override (such as opAssign) the compiler
1079                 // can potentially add one. See: https://dlang.org/library/std/traits/has_elaborate_assign.html
1080                 pragma(msg, "Ignoring 'override "~m~"()'");
1081             } else {
1082                 pragma(msg, addUpdateValue!(T)(m));
1083                 mixin(addUpdateValue!(T)(m));
1084             }
1085           }
1086         }
1087       }
1088     }
1089     updateSQL ~= join(values, ",");
1090     updateSQL ~= mixin(`" WHERE id="~ to!string(o.id) ~ ";"`);
1091     Variant updateId;
1092     stmt.executeUpdate(updateSQL, updateId);
1093     return true;
1094 }
1095 
1096 /// returns "DELETE FROM <table name> WHERE id=id
1097 string generateDeleteSQL(T)() {
1098   string res = "DELETE FROM " ~ getTableNameForType!(T)();
1099   return res;
1100 }
1101 
1102 bool remove(T)(Statement stmt, ref T o) if (__traits(isPOD, T)) {
1103   auto deleteSQL = generateDeleteSQL!(T)();
1104   deleteSQL ~= mixin(`" WHERE id="~ to!string(o.id) ~ ";"`);
1105   Variant deleteId;
1106   stmt.executeUpdate(deleteSQL, deleteId);
1107   return true;
1108 }
1109 
1110 template isSupportedSimpleTypeRef(M) {
1111     alias typeof(M) ti;
1112     static if (!__traits(isRef, M)) {
1113         enum bool isSupportedSimpleTypeRef = false;
1114     } else static if (is(ti == bool)) {
1115         enum bool isSupportedSimpleType = true;
1116     } else static if (is(ti == byte)) {
1117         enum bool isSupportedSimpleType = true;
1118     } else static if (is(ti == short)) {
1119         enum bool isSupportedSimpleType = true;
1120     } else static if (is(ti == int)) {
1121         enum bool isSupportedSimpleType = true;
1122     } else static if (is(ti == long)) {
1123         enum bool isSupportedSimpleType = true;
1124     } else static if (is(ti == ubyte)) {
1125         enum bool isSupportedSimpleType = true;
1126     } else static if (is(ti == ushort)) {
1127         enum bool isSupportedSimpleType = true;
1128     } else static if (is(ti == uint)) {
1129         enum bool isSupportedSimpleType = true;
1130     } else static if (is(ti == ulong)) {
1131         enum bool isSupportedSimpleType = true;
1132     } else static if (is(ti == float)) {
1133         enum bool isSupportedSimpleType = true;
1134     } else static if (is(ti == double)) {
1135         enum bool isSupportedSimpleType = true;
1136     } else static if (is(ti == Nullable!byte)) {
1137         enum bool isSupportedSimpleType = true;
1138     } else static if (is(ti == Nullable!short)) {
1139         enum bool isSupportedSimpleType = true;
1140     } else static if (is(ti == Nullable!int)) {
1141         enum bool isSupportedSimpleType = true;
1142     } else static if (is(ti == Nullable!long)) {
1143         enum bool isSupportedSimpleType = true;
1144     } else static if (is(ti == Nullable!ubyte)) {
1145         enum bool isSupportedSimpleType = true;
1146     } else static if (is(ti == Nullable!ushort)) {
1147         enum bool isSupportedSimpleType = true;
1148     } else static if (is(ti == Nullable!uint)) {
1149         enum bool isSupportedSimpleType = true;
1150     } else static if (is(ti == Nullable!ulong)) {
1151         enum bool isSupportedSimpleType = true;
1152     } else static if (is(ti == Nullable!float)) {
1153         enum bool isSupportedSimpleType = true;
1154     } else static if (is(ti == Nullable!double)) {
1155         enum bool isSupportedSimpleType = true;
1156     } else static if (is(ti == string)) {
1157         enum bool isSupportedSimpleType = true;
1158     } else static if (is(ti == SysTime)) {
1159         enum bool isSupportedSimpleType = true;
1160     } else static if (is(ti == DateTime)) {
1161         enum bool isSupportedSimpleType = true;
1162     } else static if (is(ti == Date)) {
1163         enum bool isSupportedSimpleType = true;
1164     } else static if (is(ti == TimeOfDay)) {
1165         enum bool isSupportedSimpleType = true;
1166     } else static if (is(ti == Nullable!SysTime)) {
1167         enum bool isSupportedSimpleType = true;
1168     } else static if (is(ti == Nullable!DateTime)) {
1169         enum bool isSupportedSimpleType = true;
1170     } else static if (is(ti == Nullable!Date)) {
1171         enum bool isSupportedSimpleType = true;
1172     } else static if (is(ti == Nullable!TimeOfDay)) {
1173         enum bool isSupportedSimpleType = true;
1174     } else static if (is(ti == byte[])) {
1175         enum bool isSupportedSimpleType = true;
1176     } else static if (is(ti == ubyte[])) {
1177         enum bool isSupportedSimpleType = true;
1178     } else static if (true) {
1179         enum bool isSupportedSimpleType = false;
1180     }
1181 }
1182 
1183 // TODO: use better way to count parameters
1184 int paramCount(destList...)() {
1185     int res = 0;
1186     foreach(p; destList) {
1187         res++;
1188     }
1189     return res;
1190 }
1191 
1192 bool isSupportedSimpleTypeRefList(destList...)() {
1193     foreach(p; destList) {
1194         static if (!isSupportedSimpleTypeRef!p) {
1195             return false;
1196         }
1197     }
1198     return true;
1199 }
1200 
1201 struct select(Args...)  {//if (isSupportedSimpleTypeRefList!Args())
1202     private Statement stmt;
1203     private ResultSet r;
1204     private void delegate() _copyFunction;
1205     private int rowIndex;
1206     
1207     this(Args...)(Statement stmt, string sql, ref Args args) {
1208         this.stmt = stmt;
1209         selectSQL = sql;
1210         _copyFunction = delegate() {
1211             foreach(i, ref a; args) {
1212                 int index = i + 1;
1213                 mixin(getPropertyWriteCode!(typeof(a)));
1214             }
1215         };
1216     }
1217 
1218     string selectSQL;
1219     string whereCondSQL;
1220     string orderBySQL;
1221     ref select where(string whereCond) {
1222         whereCondSQL = " WHERE " ~ whereCond;
1223         return this;
1224     }
1225     ref select orderBy(string order) {
1226         orderBySQL = " ORDER BY " ~ order;
1227         return this;
1228     }
1229     int front() {
1230         return rowIndex;
1231     }
1232     void popFront() {
1233         rowIndex++;
1234     }
1235     @property bool empty() {
1236         if (!r)
1237             r = stmt.executeQuery(selectSQL ~ whereCondSQL ~ orderBySQL);
1238         if (!r.next())
1239             return true;
1240         _copyFunction();
1241         return false;
1242     }
1243     ~this() {
1244         if (r)
1245             r.close();
1246     }
1247 
1248 }