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