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