19f9c1d00d0903c4f69eb8ca7d62e553fb5c797b
[sdk] / eda / drivers / sqlite / EDASQLite.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 public import static "EDA"
4 #else
5 public import "ecere"
6 public import "EDA"
7 #endif
8
9 #ifdef __linux__
10 #include <sqlite3.h>
11 #else
12 #include "sqlite3.h"
13 #endif
14
15 #define uint _uint
16 #include "ffi.h"
17 #undef uint
18
19 __attribute__((unused)) static void UnusedFunction()
20 {
21    int a;
22    a.OnGetString(0,0,0);
23    a.OnFree();
24    a.OnCopy(null);
25    a.OnCompare(null);
26    a.OnSaveEdit(null,0);
27    a.OnEdit(null,null,0,0,0,0,0);
28    a.OnDisplay(null,0,0,0,0,0,0);
29    a.OnGetDataFromString(null);
30    a.OnUnserialize(null);
31    a.OnSerialize(null);
32 }
33
34 default:
35 extern int __ecereVMethodID_class_OnGetString;
36 extern int __ecereVMethodID_class_OnGetDataFromString;
37 extern int __ecereVMethodID_class_OnCompare;
38 extern int __ecereVMethodID_class_OnSerialize;
39 extern int __ecereVMethodID_class_OnUnserialize;
40 extern int __ecereVMethodID_class_OnFree;
41 private:
42
43 static SerialBuffer collationBuffer1 { };
44 static SerialBuffer collationBuffer2 { };
45 static char storage1[512];
46 static char storage2[512];
47
48 int CollationCompare(Class type, int count1, const void * data1, int count2, const void * data2)
49 {
50    if(type.type == normalClass || type.type ==  noHeadClass)
51    {
52       Instance inst1, inst2;
53       int result;
54       SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
55       SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
56
57       ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst1, buffer1);
58       ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst2, buffer2);
59
60       result = ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
61
62       buffer1.buffer = null;
63       buffer2.buffer = null;
64       delete buffer1;
65       delete buffer2;
66       inst1.OnFree();
67       inst2.OnFree();
68       return result;
69    }
70    else if(type.type == structClass)
71    {
72       void * inst1, * inst2;
73       int result;
74       //SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
75       //SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
76
77       SerialBuffer buffer1 = collationBuffer1;
78       SerialBuffer buffer2 = collationBuffer2;
79       buffer1.buffer = (byte*)data1;
80       buffer1.size = count1;
81       buffer1.count = count1;
82       buffer1.pos = 0;
83       buffer2.buffer = (byte*)data2;
84       buffer2.size = count2;
85       buffer2.count = count2;
86       buffer2.pos = 0;
87
88       if(type.structSize > 512)
89       {
90          inst1 = new0 byte[type.structSize];
91          inst2 = new0 byte[type.structSize];
92       }
93       else
94       {
95          inst1 = storage1;
96          inst2 = storage2;
97       }
98       ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst1, buffer1);
99       ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst2, buffer2);
100
101       result = ((int (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
102
103       buffer1.buffer = null;
104       buffer2.buffer = null;
105       //delete buffer1;
106       //delete buffer2;
107       if(type.structSize > 512)
108       {
109          delete inst1;
110          delete inst2;
111       }
112       return result;
113    }
114    else
115       return ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, data1, data2);
116 }
117
118 public class SQLiteStaticLink { }   // Until .imp generation is fixed
119
120 class SQLiteDataSource : DirFilesDataSourceDriver
121 {
122    class_property(name) = "SQLite";
123    class_property(databaseFileExtension) = "sqlite";
124
125    Database OpenDatabase(const String name, CreateOptions createOptions, DataSource ds)
126    {
127       Database result = null;
128       if(name && name[0])
129       {
130          String path = MakeDatabasePath(name);
131          sqlite3 * db;
132
133          // sqlite3_open(path, &db);
134          // sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY /*SQLITE_OPEN_READWRITE*/ /*SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE*/, null );
135
136          if(sqlite3_open_v2(path, &db, (createOptions == readOnly) ? SQLITE_OPEN_READONLY :
137             (SQLITE_OPEN_READWRITE | ((createOptions == create) ? SQLITE_OPEN_CREATE : 0)), null))
138          {
139             // fprintf(stderr, "%s\n", s); // interesting
140             printf($"EDASQLite: Can't open database (%s): %s\n", path, sqlite3_errmsg(db));
141             sqlite3_close(db);
142          }
143          else
144          {
145             bool success = true;
146             char command[1024];
147             sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);");
148             sqlite3_exec(db, command, null, null, null);
149
150             if(createOptions != readOnly)
151             {
152                sqlite3_exec(db, "PRAGMA locking_mode=exclusive", null, null, null);
153                sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
154                if(sqlite3_exec(db, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('lockDummy', 'lockDummy', 'lockDummy', 'lockDummy')", null, null, null))
155                   success = false;
156                else
157                   sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
158             }
159
160             if(success)
161                result = SQLiteDatabase { db = db };
162          }
163          delete path;
164       }
165       return result;
166    }
167 }
168
169 class SQLiteField : Field
170 {
171    char * name;
172    Class type;
173    int length;
174    public LinkElement<SQLiteField> link;
175    int num;
176    int sqliteType;
177    SQLiteTable tbl;
178
179    ~SQLiteField()
180    {
181       delete name;
182    }
183
184    const String GetName()
185    {
186       return name;
187    }
188    Class GetType()
189    {
190       return type;
191    }
192    int GetLength() { return length; }
193    Field GetPrev()
194    {
195       return link.prev;
196    }
197    Field GetNext()
198    {
199       return link.next;
200    }
201    Table GetTable()
202    {
203       return tbl;
204    }
205 }
206
207 class SQLiteDatabase : Database
208 {
209    sqlite3 * db;
210    AVLTree<const String> collations { };
211
212    ~SQLiteDatabase()
213    {
214       sqlite3_exec(db, "PRAGMA locking_mode=normal", null, null, null);
215       // "Simply setting the locking-mode to NORMAL is not enough - locks are not released until the next time the database file is accessed."
216       sqlite3_exec(db, "SELECT COUNT(*) from eda_table_fields", null, null, null);
217       sqlite3_close(db);
218    }
219
220    uint ObjectsCount(ObjectType type)
221    {
222       // TODO
223       return 0;
224    }
225
226    bool RenameObject(ObjectType type, const String name, const String rename)
227    {
228       // TODO
229       return false;
230    }
231
232    bool DeleteObject(ObjectType type, const String name)
233    {
234       // TODO
235       return false;
236    }
237
238    Table OpenTable(const String name, OpenOptions options)
239    {
240       char command[1024];
241       //int result;
242       int nRows = 0, nCols = 0;
243       char ** t;
244       SQLiteTable table = null;
245       if(options.type == tablesList)
246       {
247          SQLiteField field;
248          strcpy(command, "SELECT name FROM sqlite_master WHERE type='table' AND name!='eda_table_fields';");
249          table = SQLiteTable { db = this, specialStatement = CopyString(command) };
250          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
251          LinkTable(table);
252          incref field;
253          table._fields.Add(field);
254       }
255       else if(options.type == fieldsList)
256       {
257          SQLiteField field;
258
259          sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
260          table = SQLiteTable { db = this, specialStatement = CopyString(command) };
261          LinkTable(table);
262          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
263          incref field;
264          table._fields.Add(field);
265          field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
266          incref field;
267          table._fields.Add(field);
268          field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
269          incref field;
270          table._fields.Add(field);
271       }
272       else if(options.type == tableRows)
273       {
274          bool addFields = false;
275
276          sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
277          /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
278          if(!nRows && !nCols)
279             addFields = true;
280
281          sqlite3_free_table(t);
282
283          sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
284          nCols = 0, nRows = 0;
285          /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
286
287          if((nCols || nRows) || options.create)
288          {
289             table = SQLiteTable { db = this, name = CopyString(name) };
290             LinkTable(table);
291             if(!nCols && !nRows)
292                table.mustCreate = true;
293             else
294             {
295                if(addFields)
296                {
297                   int r;
298                   for(r = 1; r <= nRows; r++)      // There should be only 1 row here
299                   {
300                      char * sql = t[nCols * r];
301                      char * bracket = strchr(sql, '(');
302                      if(bracket)
303                      {
304                         int c = 0;
305                         bracket++;
306                         while(true)
307                         {
308                            char ch;
309                            char fieldName[256];
310                            char dataType[256];
311                            int d;
312                            int start = c;
313                            int sqliteType = SQLITE_BLOB;
314                            Class type = class(int);
315                            fieldName[0] = 0;
316                            dataType[0] = 0;
317
318                            while((ch = bracket[c++]))
319                            {
320                               if(ch == ',' || ch == ')')
321                                  break;
322                            }
323                            for(d = c-1; d >= 0 && bracket[d] != ' '; d--);
324
325                            memcpy(fieldName, bracket + start, d - start);
326                            fieldName[d - start] = 0;
327
328                            memcpy(dataType, bracket + d + 1, c - d - 2);
329                            dataType[c - d - 2] = 0;
330
331                            while(ch && bracket[c] == ' ') c++;
332
333                            if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
334                            else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
335                            else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
336                            else if(!strcmp(dataType, "BLOB")) { sqliteType = SQLITE_BLOB; type = class(char *); } //class(byte *);
337
338                            sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
339                               fieldName, type.name, 0);
340                            /*result = */sqlite3_exec(db, command, null, null, null);
341
342                            {
343                               SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table._fields.count, sqliteType = sqliteType };
344                               incref field;
345                               table._fields.Add(field);
346                            }
347
348                            if(!ch || ch == ')') break;
349                         }
350                      }
351                   }
352                }
353                else
354                {
355                   Table refTable = null;
356                   sqlite3_stmt * statement;
357
358                   sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
359                   /*result = */sqlite3_prepare_v2(db, command, -1, &statement, null);
360
361                   while(sqlite3_step(statement) != SQLITE_DONE)
362                   {
363                      const char * fieldName = (const char *)sqlite3_column_text(statement, 0);
364                      const char * typeName = (const char *)sqlite3_column_text(statement, 1);
365                      int length = sqlite3_column_int(statement, 2);
366                      Class type = null;
367                      int sqliteType = SQLITE_BLOB;
368
369                      type.OnGetDataFromString(typeName);
370
371                      if(type)
372                      {
373                         if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
374                            !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
375                            !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
376                            !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
377                            !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
378                            !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
379                            sqliteType = SQLITE_INTEGER;
380                         else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
381                            sqliteType = SQLITE_FLOAT;
382                         else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
383                            sqliteType = SQLITE_TEXT;
384                         else
385                         {
386                            if(strcmp(type.fullName, "CIString") && !collations.Find(type.fullName))
387                            {
388                               collations.Add(type.fullName);
389                               sqlite3_create_collation_v2(table.db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
390                            }
391                            sqliteType = SQLITE_BLOB;
392                         }
393                      }
394
395                      {
396                         Table * fTable = (Table *)(intptr)eClass_GetProperty(type, "table");
397                         SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table._fields.count, sqliteType = sqliteType };
398                         incref field;
399                         if(fTable) refTable = *fTable;
400                         if(!table.primaryKey && refTable && !strcmp(refTable.name, table.name))
401                            table.primaryKey = field;
402
403                         table._fields.Add(field);
404                      }
405                   }
406                   sqlite3_finalize(statement);
407                }
408             }
409          }
410          sqlite3_free_table(t);
411       }
412       return (Table)table;
413    }
414
415    bool Begin()
416    {
417       char command[1024];
418       int result;
419       sprintf(command, "BEGIN;");
420       result = sqlite3_exec(db, command, null, null, null);
421       if(result)
422          PrintLn($"BEGIN FAILED!");
423       return result == SQLITE_OK;
424    }
425
426    bool Commit()
427    {
428       char command[1024];
429       int result;
430       sprintf(command, "COMMIT;");
431       result = sqlite3_exec(db, command, null, null, null);
432       if(result)
433          PrintLn($"COMMIT FAILED!");
434       return result == SQLITE_OK;
435    }
436
437    bool CreateCustomFunction(const char * name, SQLCustomFunction customFunction)
438    {
439       bool result = false;
440       Class cfClass = customFunction._class;
441       customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module);
442       if(customFunction.method)
443       {
444          String typeString = CopyString(customFunction.method.dataTypeString);
445          char * tokens[256];
446          int count = TokenizeWith(typeString, sizeof(tokens)/sizeof(tokens[0]), tokens, "(,)", false);
447          int c;
448          bool variadic = false;
449
450          for(c = 0; c < count; c++)
451          {
452             Class type = null;
453             bool pointer = false;
454             const String arg = tokens[c];
455             char * space;
456             TrimLSpaces(tokens[c], tokens[c]);
457             if(strchr(arg, '*')) pointer = true;
458             if(pointer)
459                // Using String for generic pointer...
460                type = class(String);
461             else
462             {
463                if((space = strchr(arg, ' '))) *space = 0;
464                if(!strcmp(arg, "void"))
465                   type = null;
466                else if(!strcmp(arg, "..."))
467                   variadic = true;
468                else
469                {
470                   if(cfClass.templateParams.count)
471                   {
472                      ClassTemplateParameter p;
473                      int id = 0;
474                      for(p = cfClass.templateParams.first; p; p = p.next, id++)
475                      {
476                         if(!strcmp(p.name, arg))
477                            break;
478                      }
479                      if(p && cfClass.templateArgs)
480                         arg = cfClass.templateArgs[id].dataTypeString;
481                   }
482                   type = eSystem_FindClass(customFunction._class.module, arg);
483                   if(!type)
484                      type = eSystem_FindClass(customFunction._class.module.application, arg);
485                }
486             }
487             if(c == 0)
488                customFunction.returnType = type;
489             else
490                customFunction.args.Add(type);
491          }
492          delete typeString;
493          if(variadic)
494          {
495             result = false;
496             // Variadic args don't make sense for SQL custom functions
497             // Note that different CIF must be prepared for different set of arguments
498             // ffi_prep_cif_var(&customFunction.cif, FFI_DEFAULT_ABI, args.count-1, rType, argTypes);
499          }
500          else
501          {
502             customFunction.rType = FFIGetType(customFunction.returnType, true);
503             customFunction.argTypes.Add((void *)&ffi_type_pointer);    // This pointer for SQLCustomFunction object
504             for(a : customFunction.args) customFunction.argTypes.Add((void *)FFIGetType(a, false));
505             ffi_prep_cif(&customFunction.cif, FFI_DEFAULT_ABI, customFunction.argTypes.count, customFunction.rType, (ffi_type **) customFunction.argTypes.array);
506             result = sqlite3_create_function(db, name, customFunction.args.count, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null) == SQLITE_OK;
507          }
508       }
509       return result;
510    }
511 }
512
513 static class FFITypesHolder : Map<Class, String> { ~FFITypesHolder() { Free(); } }
514 FFITypesHolder structFFITypes { };
515 __attribute__((unused)) static Iterator dummy; // TOFIX: forward struct declaration issues on Clang
516
517 public ffi_type * FFIGetType(Class type, bool structByValue)
518 {
519    if(type)
520       switch(type.type)
521       {
522          // Pointer Types
523          case structClass:
524             if(structByValue)
525             {
526                MapIterator<Class, String> it { map = structFFITypes };
527                ffi_type * ffiType = null;
528                if(it.Index(type, false))
529                   ffiType = (void *)it.data;
530                else
531                {
532                   /*
533                   DataMember member;
534                   Array<String> memberTypes { };
535                   for(member = type.membersAndProperties.first; member; member = member.next)
536                   {
537                      if(!member.isProperty)
538                      {
539                         memberTypes.Add(FFIGetType(member.dataType
540                      }
541                   }
542                   */
543                   ffiType = new0 ffi_type[1];
544                   ffiType->size = type.structSize;
545                   ffiType->type = FFI_TYPE_STRUCT;
546                   structFFITypes[type] = (void *)ffiType;
547                }
548                return ffiType;
549             }
550          case normalClass:
551          case noHeadClass:
552          case unionClass:
553             return &ffi_type_pointer;
554          // Scalar Types
555          case bitClass:
556          case enumClass:
557          case systemClass:
558          case unitClass:
559                  if(!strcmp(type.dataTypeString, "float"))  return &ffi_type_float;
560             else if(!strcmp(type.dataTypeString, "double")) return &ffi_type_double;
561             else
562                switch(type.typeSize)
563                {
564                   case 1: return &ffi_type_uint8;
565                   case 2: return &ffi_type_uint16;
566                   case 4: return &ffi_type_uint32;
567                   case 8: return &ffi_type_uint64;
568                }
569       }
570    else
571       return &ffi_type_void;
572    return null;
573 }
574
575 static SerialBuffer staticBuffer { };
576 void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values)
577 {
578    SQLCustomFunction sqlFunction = sqlite3_user_data(context);
579
580    /*  // Simple 1 pointer param returning a string
581    void * p = sqlFunction.method.function(sqlFunction, sqlite3_value_text(values[0]));
582    sqlite3_result_text(context, p, strlen(p), SQLITE_TRANSIENT);
583    */
584    int64 retData = 0;
585    void * ret = &retData;
586    Array<String> args { size = sqlFunction.args.count + 1 };
587    Iterator<String> ffiArg { sqlFunction.argTypes };
588    Iterator<String> arg { args };
589    int i = 0;
590
591    // this * for the SQLCustomFunction
592    args[0] = (void *)&sqlFunction;
593    ffiArg.Next();
594    // Get the arguments from SQLite
595    for(a : sqlFunction.args)
596    {
597       ffi_type * type = (ffi_type *)sqlFunction.argTypes[i+1];
598       if(i >= n) break;
599       switch(a.type)
600       {
601          case normalClass:
602          case noHeadClass:
603          case structClass:
604          case unionClass:
605          {
606             void ** data = new void *[1];
607             args[i+1] = (void *)data;
608             if(a == class(String))
609             {
610                int numBytes = sqlite3_value_bytes(values[i]);
611                const char * text = (const char *)sqlite3_value_text(values[i]);
612                *(char **)data = text ? new byte[numBytes+1] : null;
613                if(text)
614                   memcpy(*(char **)data, text, numBytes+1);
615             }
616             else
617             {
618                SerialBuffer buffer = staticBuffer; //{ };
619                buffer.pos = 0;
620                buffer._size = sqlite3_value_bytes(values[i]);
621                buffer._buffer = (byte *)sqlite3_value_text(values[i]);
622                //buffer._buffer = sqlite3_value_blob(curStatement);
623                buffer.count = buffer._size;
624                if(a.type == structClass)
625                   *data = new byte[a.structSize];
626                ((void (*)(void *, void *, void *))(void *)a._vTbl[__ecereVMethodID_class_OnUnserialize])(a, (a.type == structClass) ? *data : data, buffer);
627                buffer._buffer = null;
628                //delete buffer;
629             }
630             break;
631          }
632          case bitClass:
633          case enumClass:
634          case systemClass:
635          case unitClass:
636             if(type == &ffi_type_double || type == &ffi_type_float)
637             {
638                double d = sqlite3_value_double(values[i]);
639                if(a.typeSize == 8)
640                {
641                   double * data = new double[1];
642                   args[i+1] = (void *)data;
643                   *data = d;
644                }
645                else
646                {
647                   float * data = new float[1];
648                   args[i+1] = (void *)data;
649                   *data = (float)d;
650                }
651             }
652             else
653             {
654                switch(a.typeSize)
655                {
656                   case 8:
657                   {
658                      int64 * data = new int64[1];
659                      args[i+1] = (void *)data;
660                      *data = sqlite3_value_int64(values[i]);
661                      break;
662                   }
663                   case 4:
664                   {
665                      int * data = new int[1];
666                      args[i+1] = (void *)data;
667                      *data = sqlite3_value_int(values[i]);
668                      break;
669                   }
670                   case 2:
671                   {
672                      short * data = new short[1];
673                      int value;
674                      args[i+1] = (void *)data;
675                      value = sqlite3_value_int(values[i]);
676                      if(value < 0)
677                         *data = (short)value;
678                      else
679                         *(uint16 *)data = (uint16)value;
680                      break;
681                   }
682                   case 1:
683                   {
684                      char * data = new char[1];
685                      int value;
686                      args[i+1] = data;
687                      value = sqlite3_value_int(values[i]);
688                      if(value < 0)
689                         *data = (char)value;
690                      else
691                         *(byte *)data = (byte)value;
692                      break;
693                   }
694                }
695             }
696             break;
697       }
698       i++;
699       ffiArg.Next();
700    }
701    if(sqlFunction.returnType && sqlFunction.returnType.type == structClass)
702       ret = new byte[sqlFunction.returnType.typeSize];
703    ffi_call(&sqlFunction.cif, (void *)sqlFunction.method.function, ret, args.array);
704    // Give SQLite the return value
705    if(sqlFunction.returnType)
706    {
707       ffi_type * type = sqlFunction.rType;
708       Class r = sqlFunction.returnType;
709       switch(r.type)
710       {
711          case normalClass:
712          case noHeadClass:
713          case structClass:
714          case unionClass:
715          {
716             void * data = ret ? *(void **)ret : null;
717             if(r.type == structClass)
718                data = ret;
719             if(r == class(String))
720             {
721                if(data)
722                   sqlite3_result_text(context, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
723                else
724                   sqlite3_result_text(context, null, 0, SQLITE_TRANSIENT);
725             }
726             else
727             {
728                SerialBuffer buffer { };
729                ((void (*)(void *, void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnSerialize])(r, data, buffer);
730                sqlite3_result_text(context, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
731                delete buffer;
732
733                // Avoid destroying Strings for now... (Returning memory owned by the Custom Function)
734                ((void (*)(void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnFree])(r, data);
735             }
736
737             if(r.type == structClass)
738                delete ret;
739             break;
740          }
741          case bitClass:
742          case enumClass:
743          case systemClass:
744          case unitClass:
745             if(type == &ffi_type_double || type == &ffi_type_float)
746             {
747                if(r.typeSize == 8)
748                   sqlite3_result_double(context, *(double *)ret);
749                else
750                   sqlite3_result_double(context, (double)*(float *)ret);
751             }
752             else
753             {
754                switch(r.typeSize)
755                {
756                   case 8:
757                      sqlite3_result_int64(context, (sqlite3_int64)*(int64 *)ret);
758                      break;
759                   case 4:
760                      sqlite3_result_int(context, *(int *)ret);
761                      break;
762                   case 2:
763                   {
764                      int value;
765                      //if((int)data < 0)
766                         value = (int)*(short *)ret;
767                      //else
768                         //value = (int)*(uint16 *)ret;
769                      sqlite3_result_int(context, value);
770                      break;
771                   }
772                   case 1:
773                   {
774                      int value;
775                      //if((int)data < 0)
776                         value = (int)*(char *)ret;
777                      //else
778                         //value = (int)*(byte *)ret;
779                      sqlite3_result_int(context, value);
780                      break;
781                   }
782                }
783             }
784             break;
785       }
786    }
787
788    // Free Stuff up
789    arg.Next();
790    for(type : sqlFunction.args; arg.Next())
791    {
792       // Free instance
793       void * data = *(void **)arg.data;
794       ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, data);
795       if(type.type == structClass)
796          delete data;
797       // Free arg holder
798       data = arg.data;
799       delete data;
800    }
801    delete args;
802 }
803
804 class SQLiteTable : Table
805 {
806    char * name;
807    bool mustCreate;
808    SQLiteDatabase db;
809    LinkList<SQLiteField> _fields { };
810    char * specialStatement;
811    SQLiteField primaryKey;
812    FieldIndex * indexFields;
813    int indexFieldsCount;
814    int64 lastID;
815
816    Field AddField(const String fieldName, Class type, int length)
817    {
818       SQLiteField field;
819       char command[1024];
820       char dataType[256];
821       int sqliteType;
822       int result;
823       Table refTable = null;
824       Field idField = null;
825       command[0] = 0;
826
827       if(FindField(fieldName)) return null;
828
829       if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
830          !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
831          !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
832          !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
833          !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
834          !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
835       {
836          strcpy(dataType, "INTEGER");
837          sqliteType = SQLITE_INTEGER;
838       }
839       else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
840       {
841          strcpy(dataType, "REAL");
842          sqliteType = SQLITE_FLOAT;
843       }
844       else if(!strcmp(type.name, "CIString"))
845       {
846          strcpy(dataType, "TEXT");
847          sqliteType = SQLITE_BLOB;
848       }
849       else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
850       {
851          strcpy(dataType, "TEXT");
852          sqliteType = SQLITE_TEXT;
853       }
854       else
855       {
856          //strcpy(dataType, "BLOB");
857          strcpy(dataType, "TEXT");
858          sqliteType = SQLITE_BLOB;
859
860          if(!db.collations.Find(type.fullName))
861          {
862             db.collations.Add(type.fullName);
863             result = sqlite3_create_collation_v2(db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
864          }
865       }
866       if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)) && type != class(eda::Id))
867       {
868          Table * table = (Table *)(intptr)eClass_GetProperty(type, "table");
869          if(table) refTable = *table;
870          if(refTable)
871          {
872             if(primaryKey || refTable != this)
873             {
874                for(idField = refTable.firstField; idField; idField = idField.next)
875                   if(eClass_IsDerived(type, idField.type)) break;
876
877                if(!idField)
878                   PrintLn("WARNING: field not yet created for class ", (String)type.name);
879             }
880             else
881                idField = primaryKey;
882          }
883          else
884          {
885             PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
886          }
887       }
888
889       if(mustCreate)
890       {
891          if(sqliteType == SQLITE_BLOB)
892          {
893             if(!strcmp(type.name, "CIString"))
894                sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE NOCASE);", name, fieldName, dataType);
895             else
896                sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE '%s');", name, fieldName, dataType, type.fullName);
897          }
898          else if(refTable)
899          {
900             if(!idField && refTable == this)
901                sprintf(command, "CREATE TABLE `%s`(`%s` %s PRIMARY KEY);", name, fieldName, dataType);
902             else if(idField)
903                sprintf(command, "CREATE TABLE `%s`(`%s` %s REFERENCES `%s`(`%s`));", name, fieldName, dataType, refTable.name, idField.name);
904          }
905          else
906             sprintf(command, "CREATE TABLE `%s`(`%s` %s);", name, fieldName, dataType);
907          result = sqlite3_exec(db.db, command, null, null, null);
908          if(result) return null;
909          mustCreate = false;
910       }
911       else
912       {
913          if(sqliteType == SQLITE_BLOB)
914          {
915             if(!strcmp(type.name, "CIString"))
916                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE NOCASE;", name, fieldName, dataType);
917             else
918                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE `%s`;", name, fieldName, dataType, type.fullName);
919          }
920          else if(refTable)
921          {
922             if(!idField && refTable == this)
923             {
924                PrintLn($"WARNING: ALTER TABLE DOESN'T WORK WITH PRIMARY KEY FOR ", (String)name);
925                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s PRIMARY KEY;", name, fieldName, dataType);
926             }
927             else if(idField)
928                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s REFERENCES `%s`(`%s`);", name, fieldName, dataType, refTable.name, idField.name);
929          }
930          else
931             sprintf(command, "ALTER TABLE `%s` ADD `%s` %s;", name, fieldName, dataType);
932          result = sqlite3_exec(db.db, command, null, null, null);
933          if(result) return null;
934       }
935
936       sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
937          fieldName, type.name, length);
938       result = sqlite3_exec(db.db, command, null, null, null);
939
940       field = { name = CopyString(fieldName), type = type, num = _fields.count, sqliteType = sqliteType };
941       incref field;
942       _fields.Add(field);
943       if(!primaryKey && refTable == this)
944          primaryKey = field;
945       return (Field)field;
946    }
947
948    Field FindField(const String name)
949    {
950       for(f : _fields; !strcmp(f.name, name))
951       {
952          if(!primaryKey)
953          {
954             if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
955             {
956
957                Table * tablePtr = (Table *)(intptr)eClass_GetProperty(f.type, "table");
958                if(tablePtr && *tablePtr == this)
959                   primaryKey = f;
960             }
961          }
962          return (Field)f;
963       }
964       return null;
965    }
966
967    bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init)
968    {
969       char command[1024];
970       int c;
971       int result;
972       char indexName[4096];
973
974       delete indexFields;
975       indexFieldsCount = count;
976       indexFields = new FieldIndex[count];
977       memcpy(indexFields, fieldIndexes, count * sizeof(FieldIndex));
978
979       // TODO: USE CODED INDEX NAME INSTEAD?
980       strcpy(indexName, "index_");
981       strcat(indexName, name);
982       strcat(indexName, "_");
983       for(c = 0; c<count; c++)
984       {
985          if(fieldIndexes[c].field)
986          {
987             if(count == 1 && fieldIndexes[c].field == primaryKey)
988                return true;
989             strcat(indexName, fieldIndexes[c].field.name);
990             if(fieldIndexes[c].memberField)
991             {
992                strcat(indexName, ".");
993                strcat(indexName, fieldIndexes[c].memberField.name);
994             }
995             strcat(indexName, (fieldIndexes[c].order == ascending) ? "+" : "-");
996          }
997          else
998             return false;
999       }
1000
1001       sprintf(command, "CREATE INDEX IF NOT EXISTS `%s` ON `%s` (", indexName, name);
1002       for(c = 0; c<count; c++)
1003       {
1004          char columnName[1024];
1005          sprintf(columnName, "`%s` %s", fieldIndexes[c].field.name, (fieldIndexes[c].order == ascending) ? "ASC" : "DESC");
1006          if(c > 0) strcat(command, ", ");
1007          strcat(command, columnName);
1008       }
1009       strcat(command, ");");
1010       result = sqlite3_exec(db.db, command, null, null, null);
1011
1012       return result == SQLITE_OK;
1013    }
1014
1015    const String GetName()
1016    {
1017       return name;
1018    }
1019
1020    Field GetFirstField()
1021    {
1022       return _fields.first;
1023    }
1024
1025    Field GetPrimaryKey()
1026    {
1027       return primaryKey;
1028    }
1029
1030    uint GetFieldsCount()
1031    {
1032       return _fields.count;
1033    }
1034
1035    uint GetRowsCount()
1036    {
1037       char command[1024];
1038       char **t;
1039       int nCols, nRows;
1040       int result;
1041       uint rowCount = 0;
1042       sprintf(command, "SELECT COUNT(*) FROM `%s`;", name);
1043       result = sqlite3_get_table(db.db, command, &t, &nRows, &nCols, null);
1044       if(result == SQLITE_OK)
1045       {
1046          rowCount = atoi(t[1]);
1047          sqlite3_free_table(t);
1048       }
1049       return rowCount;
1050    }
1051
1052    // Returns true if not ordered by row ID
1053    bool GetIndexOrder(char * fullOrder, bool flip)
1054    {
1055       if(!flip && (!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending)))
1056       {
1057          strcpy(fullOrder, " ORDER BY ROWID");
1058          return false;
1059       }
1060       else
1061       {
1062          int c;
1063          strcpy(fullOrder, " ORDER BY ");
1064          for(c = flip ? indexFieldsCount-1 : 0; flip ? (c >= 0) : (c < indexFieldsCount); flip ? c-- : c++)
1065          {
1066             char order[1024];
1067             FieldIndex * fIndex = &indexFields[c];
1068             order[0] = 0;
1069             if(c) strcat(order, ", ");
1070             strcat(order, "`");
1071             strcat(order, fIndex->field.name);
1072             strcat(order, "`");
1073             if(fIndex->order == (flip ? ascending : descending)) strcat(order, " DESC");
1074             strcat(fullOrder, order);
1075          }
1076          return true;
1077       }
1078    }
1079
1080    Container<Field> GetFields()
1081    {
1082       return (Container<Field>)_fields;
1083    }
1084
1085    DriverRow CreateRow()
1086    {
1087       char command[1024];
1088       sqlite3_stmt * statement;
1089       sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
1090       sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null;
1091
1092       if(specialStatement)
1093          strcpy(command, specialStatement);
1094       else
1095       {
1096          char order[1024];
1097          /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
1098          sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
1099          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
1100          sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
1101
1102          sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
1103          sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
1104
1105          sprintf(command, "INSERT INTO `%s` (ROWID) VALUES(?);", name);
1106          sqlite3_prepare_v2(db.db, command, -1, &insertIDStmt, null);
1107
1108          sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
1109          sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
1110
1111          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
1112          sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
1113
1114          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
1115          sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
1116
1117          sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
1118          sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
1119
1120          /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
1121
1122          sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
1123
1124          GetIndexOrder(order, false);
1125          sprintf(command, "SELECT ROWID, * FROM `%s`%s;", name, order);
1126       }
1127       sqlite3_prepare_v2(db.db, command, -1, &statement, null);
1128
1129       sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
1130       sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
1131
1132       sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
1133       sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
1134
1135       return SQLiteRow
1136          { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt,
1137            insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
1138            previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
1139    }
1140
1141    ~SQLiteTable()
1142    {
1143       delete name;
1144       delete specialStatement;
1145       delete indexFields;
1146       _fields.Free();
1147    }
1148 }
1149
1150 class SQLiteRow : DriverRow
1151 {
1152    SQLiteTable tbl;
1153    sqlite3_stmt * curStatement;
1154
1155    sqlite3_stmt * defaultStatement;
1156    sqlite3_stmt * findStatement;
1157    sqlite3_stmt * prevFindStatement, * lastFindStatement;
1158    sqlite3_stmt * nextFindStatement;
1159    sqlite3_stmt * sysIDStatement;
1160    sqlite3_stmt * queryStatement;
1161    sqlite3_stmt * selectRowIDsStmt;
1162    sqlite3_stmt * setRowIDStmt;
1163    sqlite3_stmt * lastStatement;
1164    sqlite3_stmt * previousStatement;
1165    sqlite3_stmt * nextStatement;
1166
1167    sqlite3_stmt * insertStatement;
1168    sqlite3_stmt * deleteStatement;
1169    sqlite3_stmt * updateStatement;
1170    sqlite3_stmt * insertIDStatement;
1171    bool done;
1172    done = true;
1173    int64 rowID;
1174    // Because we use GoToSysID() and the sysIDStatement when searching for a primary key with Find(),
1175    // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
1176    bool findSysID;
1177    int findBindId;
1178
1179    bool Nil()
1180    {
1181       return done;
1182    }
1183
1184    ~SQLiteRow()
1185    {
1186       if(defaultStatement) sqlite3_finalize(defaultStatement);
1187       if(findStatement)    sqlite3_finalize(findStatement);
1188       if(prevFindStatement)sqlite3_finalize(prevFindStatement);
1189       if(lastFindStatement)sqlite3_finalize(lastFindStatement);
1190       if(nextFindStatement)sqlite3_finalize(nextFindStatement);
1191       if(sysIDStatement)   sqlite3_finalize(sysIDStatement);
1192       if(insertStatement)  sqlite3_finalize(insertStatement);
1193       if(deleteStatement)  sqlite3_finalize(deleteStatement);
1194       if(updateStatement)  sqlite3_finalize(updateStatement);
1195       if(queryStatement)   sqlite3_finalize(queryStatement);
1196       if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
1197       if(setRowIDStmt)     sqlite3_finalize(setRowIDStmt);
1198       if(previousStatement)sqlite3_finalize(previousStatement);
1199       if(nextStatement)    sqlite3_finalize(nextStatement);
1200       if(lastStatement)    sqlite3_finalize(lastStatement);
1201       if(insertIDStatement)    sqlite3_finalize(insertIDStatement);
1202    }
1203
1204    bool Select(MoveOptions move)
1205    {
1206       int result;
1207       bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
1208       if(!curStatement)
1209          curStatement = defaultStatement;
1210       switch(move)
1211       {
1212          case first:
1213          {
1214             sqlite3_reset(curStatement);
1215             result = sqlite3_step(curStatement);
1216             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1217             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1218             rowID = sqlite3_column_int64(curStatement, 0);
1219             break;
1220          }
1221          case last:
1222          {
1223             sqlite3_reset(curStatement);
1224             curStatement = lastStatement;
1225             result = sqlite3_step(curStatement);
1226             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1227             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1228             rowID = sqlite3_column_int64(curStatement, 0);
1229             break;
1230          }
1231          case middle:
1232             break;
1233          case next:
1234          case previous:
1235          {
1236             // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
1237             if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) ||
1238                (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
1239             {
1240                result = sqlite3_step(curStatement);
1241                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1242                if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1243                rowID = sqlite3_column_int64(curStatement, 0);
1244             }
1245             else if(curStatement == prevFindStatement || curStatement == findStatement || curStatement == nextFindStatement || curStatement == lastFindStatement)
1246             {
1247                if(rowID)
1248                {
1249                   int bindId = findBindId;
1250                   sqlite3_reset((move == next) ? nextFindStatement : prevFindStatement);
1251                   BindCursorData((move == next) ? nextFindStatement : prevFindStatement, move,
1252                      (move == next && (!tbl.indexFields || (tbl.indexFieldsCount == 1 && tbl.indexFields[0].field == tbl.primaryKey && tbl.indexFields[0].order == ascending))) ? false : true, &bindId);
1253                   sqlite3_reset(curStatement);
1254                   curStatement = (move == next) ? nextFindStatement : prevFindStatement;
1255                   result = sqlite3_step(curStatement);
1256                   done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1257                   if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1258                   rowID = sqlite3_column_int64(curStatement, 0);
1259                }
1260                else
1261                {
1262                   sqlite3_reset((move == next) ? findStatement : lastFindStatement);
1263                   sqlite3_reset(curStatement);
1264                   curStatement = (move == next) ? findStatement : lastFindStatement;
1265                   result = sqlite3_step(curStatement);
1266                   done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1267                   if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1268                   rowID = sqlite3_column_int64(curStatement, 0);
1269                }
1270             }
1271             else
1272             {
1273                sqlite3_reset(curStatement);
1274                curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
1275                sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
1276                result = sqlite3_step(curStatement);
1277                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1278                if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1279                rowID = sqlite3_column_int64(curStatement, 0);
1280             }
1281             break;
1282          }
1283          case nil:
1284             sqlite3_reset(curStatement);
1285             rowID = 0;
1286             done = true;
1287             break;
1288          case here:
1289             break;
1290       }
1291       return true;
1292    }
1293
1294    bool Query(const char * queryString)
1295    {
1296       bool status = true;
1297       int result;
1298
1299       if(curStatement)
1300          sqlite3_reset(curStatement);
1301       if(queryStatement)
1302       {
1303          sqlite3_finalize(queryStatement);
1304          queryStatement = null;
1305       }
1306
1307       if(queryString)
1308       {
1309          result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
1310          if(!result)
1311          {
1312             curStatement = queryStatement;
1313             if(!strchr(queryString, '?'))
1314             {
1315                result = sqlite3_step(queryStatement);
1316
1317                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1318                if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
1319
1320                rowID = sqlite3_column_int64(queryStatement, 0);
1321             }
1322          }
1323          else
1324          {
1325             printf("SQLite Query Error: %s\n", queryString);
1326             status = false;
1327          }
1328       }
1329       else
1330          curStatement = null;
1331       return status;
1332    }
1333
1334    bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
1335    {
1336       int result = 1;
1337       Class dataType = fld.type;
1338       SerialBuffer buffer = null;
1339       switch(fld.sqliteType)
1340       {
1341          case SQLITE_INTEGER:
1342          {
1343             switch(dataType.typeSize)
1344             {
1345                case 8:
1346                   result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1347                   break;
1348                case 4:
1349                   result = sqlite3_bind_int(statement, pos, *(int *)data);
1350                   break;
1351                case 2:
1352                {
1353                   int value;
1354                   if((int)data < 0)
1355                      value = (int)*(short *)data;
1356                   else
1357                      value = (int)*(uint16 *)data;
1358                   result = sqlite3_bind_int(statement, pos, value);
1359                   break;
1360                }
1361                case 1:
1362                {
1363                   int value;
1364                   if((int)data < 0)
1365                      value = (int)*(char *)data;
1366                   else
1367                      value = (int)*(byte *)data;
1368                   result = sqlite3_bind_int(statement, pos, value);
1369                   break;
1370                }
1371             }
1372             break;
1373          }
1374          case SQLITE_FLOAT:
1375          {
1376             if(dataType.typeSize == 8)
1377                result = sqlite3_bind_double(statement, pos, *(double *)data);
1378             else
1379                result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1380             break;
1381          }
1382          case SQLITE_TEXT:
1383          {
1384             if((char *)data)
1385                result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1386             else
1387                result = sqlite3_bind_null(statement, pos);
1388             break;
1389          }
1390          case SQLITE_BLOB:
1391          case SQLITE_NULL:
1392          {
1393             if((void *)data)
1394             {
1395                buffer = SerialBuffer { };
1396                ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnSerialize])(dataType, data, buffer);
1397                result = sqlite3_bind_text(statement, pos, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1398             }
1399             else
1400                result = sqlite3_bind_null(statement, pos);
1401             break;
1402          }
1403       }
1404       if(bufferOut)
1405          *bufferOut = buffer;
1406       else
1407          delete buffer;
1408       return !result;
1409    }
1410
1411    void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1412    {
1413       if(move == next || move == previous)
1414       {
1415          // Where clauses for index
1416          if(useIndex)
1417          {
1418             int c;
1419             bool gotPrimaryKey = false;
1420
1421             strcatf(command, " AND (");
1422             for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1423             {
1424                char where[1024];
1425                FieldIndex * fIndex = &tbl.indexFields[c];
1426                where[0] = 0;
1427
1428                strcat(where, "`");
1429                strcat(where, fIndex->field.name);
1430                strcat(where, "` ");
1431                strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1432                strcat(where, " ? OR (");
1433                strcat(where, fIndex->field.name);
1434                if(fIndex->field == tbl.primaryKey)
1435                   gotPrimaryKey = true;
1436                strcat(where, " = ? AND (");
1437                strcat(command, where);
1438             }
1439             strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1440             for(c = 0; c < tbl.indexFieldsCount; c++)
1441                strcat(command, "))");
1442          }
1443          else
1444             strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1445       }
1446    }
1447
1448    void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1449    {
1450       if(move == next || move == previous)
1451       {
1452          // The binds for the Extra ordering Where clauses
1453          if(useIndex)
1454          {
1455             int c;
1456             /* // Code to not rely on curStatement being set up
1457             SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1458             dataRow.GoToSysID((uint)rowID);
1459             */
1460             for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1461             {
1462                FieldIndex * fIndex = &tbl.indexFields[c];
1463                int64 data;
1464                SQLiteField fld = (SQLiteField)fIndex->field;
1465                Class type = fld.type;
1466                void * dataPtr;
1467                SerialBuffer buffer;
1468
1469                if(type.type == unitClass && !type.typeSize)
1470                {
1471                   Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1472                   if(dataType)
1473                      type = dataType;
1474                }
1475                if(type.type == structClass)
1476                {
1477                   data = (int64)(intptr)new0 byte[type.structSize];
1478                   dataPtr = (void *)(intptr)data;
1479                }
1480                // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1481                ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)(intptr)data : &data);
1482                if(type.type == normalClass || type.type == noHeadClass)
1483                   dataPtr = (void *)(intptr)data;
1484                else
1485                   dataPtr = &data;
1486                ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1487                // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
1488                // Reuse the buffer for Blobs...
1489                if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1490                {
1491                   sqlite3_bind_text(stmt, (*bindId)++, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1492                   delete buffer;
1493                }
1494                else
1495                   ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1496
1497                ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, dataPtr);
1498             }
1499             // delete dataRow;
1500          }
1501
1502          // Bind for the rowid
1503          sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1504       }
1505    }
1506
1507    bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1508    {
1509       char order[1024], command[2048];
1510       int result;
1511       bool useIndex;
1512       sqlite3_stmt * stmt = null;
1513       int bindId = 1;
1514
1515       if(fld == tbl.primaryKey)
1516       {
1517          if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1518          if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1519          if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1520          if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1521          if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1522          result = GoToSysID(*(int *)data);
1523          if(result)
1524             findSysID = true;
1525          return result != 0;
1526       }
1527
1528       useIndex = tbl.GetIndexOrder(order, false);
1529       // Basic Find
1530       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1531       AddCursorWhereClauses(command, move, useIndex);
1532       strcat(command, order);
1533       strcat(command, ";");
1534       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1535       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1536       BindCursorData(stmt, move, useIndex, &bindId);
1537
1538       // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1539       if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1540       if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1541       if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1542       if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1543       if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1544
1545       curStatement = findStatement = stmt;
1546       findBindId = bindId;
1547
1548       // For going back to forward find
1549       bindId = 1;
1550       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1551       AddCursorWhereClauses(command, next, useIndex);
1552       strcat(command, order);
1553       strcat(command, ";");
1554       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1555       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1556       nextFindStatement = stmt;
1557
1558       // Backwards
1559       tbl.GetIndexOrder(order, true);
1560       // For tracing back finds
1561       bindId = 1;
1562       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1563       AddCursorWhereClauses(command, previous, true);
1564       strcat(command, order);
1565       strcat(command, ";");
1566       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1567       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1568       prevFindStatement = stmt;
1569
1570       // For tracing back from last
1571       bindId = 1;
1572       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1573       strcat(command, order);
1574       strcat(command, ";");
1575       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1576       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1577       lastFindStatement = stmt;
1578
1579       result = sqlite3_step(findStatement);
1580
1581       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1582       if(done)
1583       {
1584          rowID = 0;
1585          sqlite3_reset(findStatement);
1586       }
1587       else
1588          rowID = sqlite3_column_int64(findStatement, 0);
1589       return !done;
1590    }
1591
1592    bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1593    {
1594 #define BINDDATA \
1595          for(c = 0; c < numFields; c++) \
1596          { \
1597             FieldFindData * fieldFind = &findData[c]; \
1598             SQLiteField sqlFld = (SQLiteField)findData->field; \
1599             Class dataType = sqlFld.type; \
1600             BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1601          }
1602
1603       if(numFields)
1604       {
1605          char criterias[4096], command[4096], order[1024];
1606          int result;
1607          int c;
1608          bool useIndex;
1609          sqlite3_stmt * stmt = null;
1610          int bindId = 1;
1611
1612          // Criterias
1613          sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1614          for(c = 0; c < numFields; c++)
1615          {
1616             FieldFindData * fieldFind = &findData[c];
1617
1618             if(c) strcat(criterias, " AND `");
1619             strcat(criterias, fieldFind->field.name);
1620             strcat(criterias, "` = ?");
1621          }
1622
1623          useIndex = tbl.GetIndexOrder(order, false);
1624          // Basic Find (multiple)
1625          strcpy(command, criterias);
1626          AddCursorWhereClauses(command, move, useIndex);
1627          strcat(command, order);
1628          strcat(command, ";");
1629          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1630          BINDDATA;
1631          BindCursorData(stmt, move, useIndex, &bindId);
1632
1633          // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1634          if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1635          if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1636          if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1637          if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1638          if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1639
1640          curStatement = findStatement = stmt;
1641          findBindId = bindId;
1642
1643          // For tracing back forward finds
1644          bindId = 1;
1645          strcpy(command, criterias);
1646          AddCursorWhereClauses(command, previous, true);
1647          strcat(command, order);
1648          strcat(command, ";");
1649          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1650          BINDDATA;
1651          nextFindStatement = stmt;
1652
1653          // Backwards
1654          tbl.GetIndexOrder(order, true);
1655          // For tracing back finds
1656          bindId = 1;
1657          strcpy(command, criterias);
1658          AddCursorWhereClauses(command, next, useIndex);
1659          strcat(command, order);
1660          strcat(command, ";");
1661          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1662          BINDDATA;
1663          prevFindStatement = stmt;
1664
1665          // For tracing back from last
1666          bindId = 1;
1667          strcpy(command, criterias);
1668          strcat(command, order);
1669          strcat(command, ";");
1670          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1671          BINDDATA;
1672          lastFindStatement = stmt;
1673
1674          result = sqlite3_step(findStatement);
1675          done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1676          if(done)
1677          {
1678             rowID = 0;
1679             sqlite3_reset(findStatement);
1680          }
1681          else
1682             rowID = sqlite3_column_int64(findStatement, 0);
1683          return !done;
1684       }
1685       return false;
1686    }
1687
1688    bool Synch(DriverRow to)
1689    {
1690       SQLiteRow rowTo = (SQLiteRow)to;
1691       if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1692          return GoToSysID((uint)rowTo.rowID);
1693       return false;
1694    }
1695
1696    bool Add(uint64 id)
1697    {
1698       int result;
1699       //char command[1024];
1700       //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1701       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1702       if(id)
1703       {
1704          sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1705          result = sqlite3_step(insertIDStatement);
1706       }
1707       else
1708          result = sqlite3_step(insertStatement);
1709       if(result == SQLITE_DONE)     // if(result == SQLITE_OK)
1710       {
1711          rowID = sqlite3_last_insert_rowid(tbl.db.db);
1712          if(rowID > MAXDWORD)
1713          {
1714             int64 lastID = tbl.lastID;
1715
1716             sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1717             while(true)
1718             {
1719                int64 id;
1720                result = sqlite3_step(selectRowIDsStmt);
1721                if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1722                id = sqlite3_column_int64(selectRowIDsStmt, 0);
1723                if(id - lastID > 1) break;
1724                lastID = id;
1725             }
1726             sqlite3_reset(selectRowIDsStmt);
1727
1728             sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1729             rowID = lastID + 1;
1730             tbl.lastID = rowID;
1731             sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1732             result = sqlite3_step(setRowIDStmt);
1733             sqlite3_reset(setRowIDStmt);
1734          }
1735          sqlite3_reset(id ? insertIDStatement : insertStatement);
1736          curStatement = sysIDStatement;
1737          findSysID = false;
1738          sqlite3_reset(curStatement);
1739          sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1740          result = sqlite3_step(curStatement);
1741          done = false; // Make sure 'nil' is false
1742          return true;
1743       }
1744       sqlite3_reset(insertStatement);
1745       return false;
1746    }
1747
1748    bool Delete()
1749    {
1750       int result;
1751       //char command[1024];
1752       //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1753       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1754       sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1755       result = sqlite3_step(deleteStatement);
1756       sqlite3_reset(deleteStatement);
1757       rowID = 0;
1758       return result == SQLITE_OK || result == SQLITE_DONE;
1759    }
1760
1761    bool GetData(Field fld, typed_object &data)
1762    {
1763       SQLiteField sqlFld = (SQLiteField)fld;
1764       int num = sqlFld.num + 1;
1765       Class dataType = sqlFld.type;
1766
1767
1768       switch(sqlFld.sqliteType)
1769       {
1770          case SQLITE_INTEGER:
1771          {
1772             switch(dataType.typeSize)
1773             {
1774                case 8:
1775                   if(fld == tbl.primaryKey)
1776                      *(int64 *)data = rowID;
1777                   else
1778                      *(int64 *)data = sqlite3_column_int64(curStatement, num);
1779                   break;
1780                case 4:
1781                   if(fld == tbl.primaryKey)
1782                      *(int *)data = (int)(uint)rowID;
1783                   else
1784                      *(int *)data = sqlite3_column_int(curStatement, num);
1785                   break;
1786                case 2:
1787                {
1788                   int value;
1789                   if(fld == tbl.primaryKey)
1790                      value = (int)(uint)rowID;
1791                   else
1792                      value = sqlite3_column_int(curStatement, num);
1793                   if(value < 0)
1794                      *(short *)data = (short)value;
1795                   else
1796                      *(uint16 *)data = (uint16)value;
1797                   break;
1798                }
1799                case 1:
1800                {
1801                   int value;
1802                   if(fld == tbl.primaryKey)
1803                      value = (int)(uint)rowID;
1804                   else
1805                      value = sqlite3_column_int(curStatement, num);
1806                   if(value < 0)
1807                      *(char *)data = (char)value;
1808                   else
1809                      *(byte *)data = (byte)value;
1810                   break;
1811                }
1812             }
1813             break;
1814          }
1815          case SQLITE_FLOAT:
1816          {
1817             double d = sqlite3_column_double(curStatement, num);
1818             if(dataType.typeSize == 8)
1819                *(double *)data = d;
1820             else
1821                *(float *)data = (float)d;
1822             break;
1823          }
1824          case SQLITE_TEXT:
1825          {
1826             int numBytes = sqlite3_column_bytes(curStatement, num);
1827             const char * text = (const char *)sqlite3_column_text(curStatement, num);
1828             *(char **)data = text ? new byte[numBytes+1] : null;
1829             if(text)
1830                memcpy(*(char **)data, text, numBytes+1);
1831             break;
1832          }
1833          case SQLITE_BLOB:
1834          {
1835             SerialBuffer buffer { };
1836             //buffer._buffer = sqlite3_column_blob(curStatement, num);
1837             buffer._size = sqlite3_column_bytes(curStatement, num);
1838             buffer._buffer = (byte *)sqlite3_column_text(curStatement, num);
1839             buffer.count = buffer._size;
1840
1841             ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnUnserialize])(dataType, data, buffer);
1842
1843             buffer._buffer = null;
1844             delete buffer;
1845             break;
1846          }
1847       }
1848       return true;
1849    }
1850
1851    bool SetData(Field fld, typed_object data)
1852    {
1853       SQLiteField sqlFld = (SQLiteField)fld;
1854       int result;
1855       char command[1024];
1856
1857       if(updateStatement)
1858          sqlite3_finalize(updateStatement);
1859       sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1860       // TODO: Shouldn't we cache those update statements per field?
1861       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1862       sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1863       BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1864       result = sqlite3_step(updateStatement);
1865       sqlite3_reset(updateStatement);
1866       if(fld == tbl.primaryKey)
1867          rowID = *(uint *)data;
1868       return result == SQLITE_DONE;
1869    }
1870
1871    int GetSysID()
1872    {
1873       return (int)(uint)rowID;
1874    }
1875
1876    bool GoToSysID(uint id)
1877    {
1878       //char command[1024];
1879       int result;
1880       rowID = (uint)id;
1881       //if(statement)
1882          //sqlite3_finalize(statement);
1883       //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1884       //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1885
1886       findSysID = false;
1887       if(curStatement)
1888          sqlite3_reset(curStatement);
1889
1890       curStatement = sysIDStatement;
1891       sqlite3_reset(sysIDStatement);
1892       sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1893       result = sqlite3_step(curStatement);
1894       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1895       if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1896       return !done;
1897    }
1898
1899    bool SetQueryParam(int paramID, int value)
1900    {
1901       int result;
1902       if(curStatement != queryStatement)
1903       {
1904          if(curStatement) sqlite3_reset(curStatement);
1905          curStatement = queryStatement;
1906       }
1907       sqlite3_reset(queryStatement);
1908       result = sqlite3_bind_int(queryStatement, paramID, value);
1909       return !result;
1910    }
1911
1912    bool SetQueryParam64(int paramID, int64 value)
1913    {
1914       int result;
1915       if(curStatement != queryStatement)
1916       {
1917          if(curStatement) sqlite3_reset(curStatement);
1918          curStatement = queryStatement;
1919       }
1920       sqlite3_reset(queryStatement);
1921       result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1922       return !result;
1923    }
1924
1925    bool SetQueryParamText(int paramID, const char * data)
1926    {
1927       int result;
1928       if(curStatement != queryStatement)
1929       {
1930          if(curStatement) sqlite3_reset(curStatement);
1931          curStatement = queryStatement;
1932       }
1933       sqlite3_reset(queryStatement);
1934       if(data)
1935          result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1936       else
1937          result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1938       return !result;
1939    }
1940
1941    bool SetQueryParamObject(int paramID, const void * data, Class type)
1942    {
1943       int result;
1944       if(curStatement != queryStatement)
1945       {
1946          if(curStatement) sqlite3_reset(curStatement);
1947          curStatement = queryStatement;
1948       }
1949       sqlite3_reset(queryStatement);
1950       {
1951          SerialBuffer buffer { };
1952          ((void (*)(void *, const void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnSerialize])(type, data, buffer);
1953          result = sqlite3_bind_text(queryStatement, paramID, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1954          delete buffer;
1955       }
1956       return !result;
1957    }
1958
1959    bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1960    {
1961       if(curStatement != queryStatement)
1962       {
1963          if(curStatement) sqlite3_reset(curStatement);
1964          curStatement = queryStatement;
1965       }
1966       sqlite3_reset(queryStatement);
1967       return BindData(queryStatement, pos, fld, data, null);
1968    }
1969
1970    /*char * GetExtraColumn(int paramID)
1971    {
1972       SQLiteField lastFld = tbl._fields.last;
1973       return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1974    }*/
1975    const char * GetColumn(int paramID)
1976    {
1977       return (const char *)sqlite3_column_text(curStatement, paramID);
1978    }
1979 }