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