eda: Added BindQueryData typed_object interface to Row
[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 static void UnusedFunction()
16 {
17    int a;
18    a.OnGetString(0,0,0);
19    a.OnFree();
20    a.OnCopy(null);
21    a.OnCompare(null);
22    a.OnSaveEdit(null,0);
23    a.OnEdit(null,null,0,0,0,0,0);
24    a.OnDisplay(null,0,0,0,0,0,0);
25    a.OnGetDataFromString(null);
26    a.OnUnserialize(null);
27    a.OnSerialize(null);
28 }
29
30 default:
31 extern int __ecereVMethodID_class_OnGetString;
32 extern int __ecereVMethodID_class_OnGetDataFromString;
33 extern int __ecereVMethodID_class_OnCompare;
34 extern int __ecereVMethodID_class_OnSerialize;
35 extern int __ecereVMethodID_class_OnUnserialize;
36 extern int __ecereVMethodID_class_OnFree;
37 private:
38
39 int CollationCompare(Class type, int count1, void * data1, int count2, void * data2)
40 {
41    if(type.type == normalClass || type.type ==  noHeadClass)
42    {
43       Instance inst1, inst2;
44       int result;
45       SerialBuffer buffer1 { size = count1, count = count1, buffer = data1 };
46       SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
47
48       type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst1, buffer1);
49       type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst2, buffer2);
50
51       result = type._vTbl[__ecereVMethodID_class_OnCompare](type, inst1, inst2);
52      
53       buffer1.buffer = null;
54       buffer2.buffer = null;
55       delete buffer1;
56       delete buffer2;
57       inst1.OnFree();
58       inst2.OnFree();
59       return result;
60    }
61    else if(type.type == structClass)
62    {
63       void * inst1, * inst2;
64       int result;
65       SerialBuffer buffer1 { size = count1, count = count1, buffer = data1 };
66       SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
67
68       inst1 = new0 byte[type.structSize];
69       inst2 = new0 byte[type.structSize];
70       type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst1, buffer1);
71       type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst2, buffer2);
72
73       result = type._vTbl[__ecereVMethodID_class_OnCompare](type, inst1, inst2);
74      
75       buffer1.buffer = null;
76       buffer2.buffer = null;
77       delete buffer1;
78       delete buffer2;
79       delete inst1;
80       delete inst2;
81       return result;
82    }
83    else
84       return type._vTbl[__ecereVMethodID_class_OnCompare](type, data1, data2);
85 }
86
87 public class SQLiteStaticLink { }   // Until .imp generation is fixed
88
89 class SQLiteDataSource : DataSourceDriver
90 {
91    class_property(name) = "SQLite";
92    String path;
93    OldList listDatabases;
94    uint databasesCount;
95
96    String BuildLocator(DataSource ds)
97    {
98       return CopyString(ds.host);
99    }
100
101    uint GetDatabasesCount()
102    {
103       return databasesCount;
104    }
105
106    ~SQLiteDataSource()
107    {
108       delete path;
109    }
110
111    bool Connect(const String locator)
112    {
113       delete path;
114       path = CopyString(locator);
115       // TODO, use user name and password for local security?
116       // TODO, open ds in read or write mode
117       if(FileExists(path))
118       {
119          int n = 0;
120          FileListing listing { path, "sqlite" };
121          databasesCount = 0;
122          while(listing.Find())
123             databasesCount++;
124          return true;
125       }
126       return false;
127    }
128
129    bool RenameDatabase(const String name, const String rename)
130    {
131       if(name && rename && path && FileExists(path))
132       {
133          String path;
134          path = MakeDatabasePath(name);
135          if(FileExists(path))
136          {
137             bool renamed;
138             String repath;
139             repath = MakeDatabasePath(rename);
140             renamed = RenameFile(path, repath);
141             delete path;
142             delete repath;
143             return renamed;
144          }
145          delete path;
146       }
147       return false;
148    }
149
150    bool DeleteDatabase(const String name)
151    {
152       if(path && FileExists(path))
153       {
154          bool deleted;
155          String path = MakeDatabasePath(name);
156          deleted = DeleteFile(path);  // delete file seems to return true even if the file does not exist
157          databasesCount--;
158          delete path;
159          return deleted;
160       }
161       return false;
162    }
163
164    virtual String MakeDatabasePath(const String name)
165    {
166       if(name)
167       {
168          char build[MAX_LOCATION];
169          strcpy(build, path ? path : "");
170          PathCat(build, name);
171          ChangeExtension(build, "sqlite", build);
172          return CopyString(build);
173       }
174       return null;
175    }
176
177    Database OpenDatabase(const String name, CreateOptions createOptions, DataSource ds)
178    {
179       Database result = null;
180       if(name && name[0])
181       {
182          String path = MakeDatabasePath(name);
183          sqlite3 * db;
184
185          // sqlite3_open(path, &db);
186          // sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY /*SQLITE_OPEN_READWRITE*/ /*SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE*/, null );
187          
188          if(sqlite3_open_v2(path, &db, (createOptions == readOnly) ? SQLITE_OPEN_READONLY :
189             (SQLITE_OPEN_READWRITE | ((createOptions == create) ? SQLITE_OPEN_CREATE : 0)), null))
190          {
191             // fprintf(stderr, "%s\n", s); // interesting
192             printf($"EDASQLite: Can't open database (%s): %s\n", path, sqlite3_errmsg(db));
193             sqlite3_close(db);
194          }
195          else
196          {
197             char command[1024];
198             sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);");
199             sqlite3_exec(db, command, null, null, null);
200
201             result = SQLiteDatabase { db = db };
202          }
203          delete path;
204       }
205       return result;
206    }
207 }
208
209 class SQLiteField : Field
210 {
211    char * name;
212    Class type;
213    int length;
214    public LinkElement<SQLiteField> link;
215    int num;
216    int sqliteType;
217    SQLiteTable tbl;
218
219    ~SQLiteField()
220    {
221       delete name;
222    }
223
224    String GetName()
225    {
226       return name;
227    }
228    Class GetType()
229    {
230       return type;
231    }
232    int GetLength() { return length; }
233    Field GetPrev()
234    {
235       return link.prev;
236    }
237    Field GetNext()
238    {
239       return link.next;
240    }
241    Table GetTable()
242    {
243       return tbl;
244    }
245 }
246
247 class SQLiteDatabase : Database
248 {
249    sqlite3 * db;
250    AVLTree<String> collations { };
251    
252    ~SQLiteDatabase()
253    {
254       sqlite3_close(db);
255    }
256
257    uint ObjectsCount(ObjectType type)
258    {
259       // TODO
260       return 0;
261    }
262
263    bool RenameObject(ObjectType type, const String name, const String rename)
264    {
265       // TODO
266       return false;
267    }
268
269    bool DeleteObject(ObjectType type, const String name)
270    {
271       // TODO
272       return false;
273    }
274
275    Table OpenTable(const String name, OpenOptions options)
276    {
277       char command[1024];
278       int result;
279       int nRows = 0, nCols = 0;
280       char ** t;
281       SQLiteTable table = null;
282       if(options.type == tablesList)
283       {
284          SQLiteField field;
285          strcpy(command, "SELECT name FROM sqlite_master WHERE type='table' AND name!='eda_table_fields';");
286          table = SQLiteTable { db = this, specialStatement = CopyString(command) };
287          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
288          LinkTable(table);
289          incref field;
290          table.fields.Add(field);
291       }
292       else if(options.type == fieldsList)
293       {
294          SQLiteField field;
295
296          sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
297          table = SQLiteTable { db = this, specialStatement = CopyString(command) };
298          LinkTable(table);
299          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
300          incref field;
301          table.fields.Add(field);
302          field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
303          incref field;
304          table.fields.Add(field);
305          field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
306          incref field;
307          table.fields.Add(field);
308       }
309       else if(options.type == tableRows)
310       {
311          bool addFields = false;
312
313          sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
314          result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
315          if(!nRows && !nCols)
316             addFields = true;
317
318          sqlite3_free_table(t);
319
320          sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
321          nCols = 0, nRows = 0;
322          result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
323          
324          if((nCols || nRows) || options.create)
325          {
326             table = SQLiteTable { db = this, name = CopyString(name) };
327             LinkTable(table);
328             if(!nCols && !nRows)
329                table.mustCreate = true;
330             else
331             {
332                if(addFields)
333                {
334                   int r;
335                   for(r = 1; r <= nRows; r++)      // There should be only 1 row here
336                   {
337                      char * sql = t[nCols * r];
338                      char * bracket = strchr(sql, '(');
339                      if(bracket) 
340                      {
341                         int c = 0;
342                         bracket++;
343                         while(true)
344                         {
345                            char ch;
346                            char fieldName[256];
347                            char dataType[256];
348                            int d;
349                            int start = c;
350                            int sqliteType = SQLITE_BLOB;
351                            Class type = class(int);
352                            fieldName[0] = 0;
353                            dataType[0] = 0;
354
355                            while((ch = bracket[c++]))
356                            {
357                               if(ch == ',' || ch == ')')
358                                  break;
359                            }
360                            for(d = c-1; d >= 0 && bracket[d] != ' '; d--);
361
362                            memcpy(fieldName, bracket + start, d - start);
363                            fieldName[d - start] = 0;
364
365                            memcpy(dataType, bracket + d + 1, c - d - 2);
366                            dataType[c - d - 2] = 0;
367
368                            while(ch && bracket[c] == ' ') c++;
369                            
370                            if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
371                            else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
372                            else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
373                            else if(!strcmp(dataType, "BLOB")) { sqliteType = SQLITE_BLOB; type = class(char *); } //class(byte *);
374
375                            sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
376                               fieldName, type.name, 0);
377                            result = sqlite3_exec(db, command, null, null, null);
378
379                            {
380                               SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table.fields.count, sqliteType = sqliteType };
381                               incref field;
382                               table.fields.Add(field);
383                            }
384
385                            if(!ch || ch == ')') break;
386                         }
387                      }
388                   }
389                }
390                else
391                {
392                   sqlite3_stmt * statement;
393                   
394                   sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
395                   result = sqlite3_prepare_v2(db, command, -1, &statement, null);
396
397                   while(sqlite3_step(statement) != SQLITE_DONE)
398                   {
399                      char * fieldName = sqlite3_column_text(statement, 0);
400                      char * typeName = sqlite3_column_text(statement, 1);
401                      int length = sqlite3_column_int(statement, 2);
402                      Class type = null;
403                      int sqliteType = SQLITE_BLOB;
404
405                      ((Class)(&type)).OnGetDataFromString(typeName);    // TODO: THIS REQUIRES A FIX SOMEWHERE ELSE
406
407                      if(type)
408                      {
409                         if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || 
410                            !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") || 
411                            !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") || 
412                            !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || 
413                            !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
414                            !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
415                            sqliteType = SQLITE_INTEGER;
416                         else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
417                            sqliteType = SQLITE_FLOAT;
418                         else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
419                            sqliteType = SQLITE_TEXT;
420                         else
421                         {
422                            if(strcmp(type.fullName, "CIString") && !collations.Find(type.fullName))
423                            {
424                               collations.Add(type.fullName);
425                               sqlite3_create_collation_v2(table.db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
426                            }
427                            sqliteType = SQLITE_BLOB;
428                         }
429                      }
430
431                      {
432                         SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table.fields.count, sqliteType = sqliteType };
433                         incref field;
434                         table.fields.Add(field);
435                      }
436                   }
437                   sqlite3_finalize(statement);
438                }
439             }
440          }
441          sqlite3_free_table(t);
442       }
443       return (Table)table;
444    }
445
446    bool Begin()
447    {
448       char command[1024];
449       int result;
450       sprintf(command, "BEGIN;");
451       result = sqlite3_exec(db, command, null, null, null);
452       if(result)
453          PrintLn($"BEGIN FAILED!");
454       return result == SQLITE_OK;
455    }
456
457    bool Commit()
458    {
459       char command[1024];
460       int result;
461       sprintf(command, "COMMIT;");
462       result = sqlite3_exec(db, command, null, null, null);
463       if(result)
464          PrintLn($"COMMIT FAILED!");
465       return result == SQLITE_OK;
466    }
467
468    bool CreateCustomFunction(char * name, SQLCustomFunction customFunction)
469    {
470       int result = sqlite3_create_function(db, name, 1, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null);
471       return result == SQLITE_OK;
472    }
473 }
474
475 void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** value)
476 {
477    SQLCustomFunction sqlFunction = sqlite3_user_data(context);
478    char * text = sqlite3_value_text(*value);
479    sqlFunction.array.size = 1;
480    sqlFunction.array[0] = 0;
481    sqlFunction.Process(text);
482    sqlite3_result_text(context, sqlFunction.array.array, sqlFunction.array.count ? sqlFunction.array.count - 1 : 0, SQLITE_TRANSIENT);
483 }
484
485 class SQLiteTable : Table
486 {
487    char * name;
488    bool mustCreate;
489    SQLiteDatabase db;
490    LinkList<SQLiteField> fields { };
491    char * specialStatement;
492    SQLiteField primaryKey;
493    FieldIndex * indexFields;
494    int indexFieldsCount;
495    int64 lastID;
496
497    Field AddField(const String fieldName, Class type, int length)
498    {
499       SQLiteField field;
500       char command[1024];
501       char dataType[256];
502       int sqliteType;
503       int result;
504       Table refTable = null;
505       Field idField = null;
506       command[0] = 0;
507       
508       if(FindField(fieldName)) return null;
509
510       if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || 
511          !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") || 
512          !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") || 
513          !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || 
514          !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
515          !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
516       {
517          strcpy(dataType, "INTEGER");
518          sqliteType = SQLITE_INTEGER;
519       }
520       else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
521       {
522          strcpy(dataType, "REAL");
523          sqliteType = SQLITE_FLOAT;
524       }
525       else if(!strcmp(type.name, "CIString"))
526       {
527          strcpy(dataType, "TEXT");
528          sqliteType = SQLITE_BLOB;
529       }
530       else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
531       {
532          strcpy(dataType, "TEXT");
533          sqliteType = SQLITE_TEXT;
534       }
535       else
536       {
537          //strcpy(dataType, "BLOB");
538          strcpy(dataType, "TEXT");
539          sqliteType = SQLITE_BLOB;
540
541          if(!db.collations.Find(type.fullName))
542          {
543             db.collations.Add(type.fullName);
544             result = sqlite3_create_collation_v2(db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
545          }
546       }
547       if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)))
548       {
549          Table * table = (Table *)eClass_GetProperty(type, "table");
550          if(table) refTable = *table;
551          if(refTable)
552          {
553             if(primaryKey || refTable != this)
554             {
555                for(idField = refTable.firstField; idField; idField = idField.next)
556                   if(eClass_IsDerived(type, idField.type)) break;
557
558                if(!idField)
559                   PrintLn("WARNING: field not yet created for class ", (String)type.name);
560             }
561             else
562                idField = primaryKey;
563          }
564          else
565          {
566             PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
567          }
568       }
569       
570       if(mustCreate)
571       {
572          if(sqliteType == SQLITE_BLOB)
573          {
574             if(!strcmp(type.name, "CIString"))
575                sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE NOCASE);", name, fieldName, dataType);
576             else
577                sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE '%s');", name, fieldName, dataType, type.fullName);
578          }
579          else if(refTable)
580          {
581             if(!idField && refTable == this)
582                sprintf(command, "CREATE TABLE `%s`(`%s` %s PRIMARY KEY);", name, fieldName, dataType);
583             else if(idField)
584                sprintf(command, "CREATE TABLE `%s`(`%s` %s REFERENCES `%s`(`%s`));", name, fieldName, dataType, refTable.name, idField.name);
585          }
586          else
587             sprintf(command, "CREATE TABLE `%s`(`%s` %s);", name, fieldName, dataType);
588          result = sqlite3_exec(db.db, command, null, null, null);
589          if(result) return null;
590          mustCreate = false;
591       }
592       else
593       {
594          if(sqliteType == SQLITE_BLOB)
595          {
596             if(!strcmp(type.name, "CIString"))
597                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE NOCASE;", name, fieldName, dataType);
598             else
599                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE `%s`;", name, fieldName, dataType, type.fullName);
600          }
601          else if(refTable)
602          {
603             if(!idField && refTable == this)
604             {
605                PrintLn($"WARNING: ALTER TABLE DOESN'T WORK WITH PRIMARY KEY FOR ", (String)name);
606                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s PRIMARY KEY;", name, fieldName, dataType);
607             }
608             else if(idField)
609                sprintf(command, "ALTER TABLE `%s` ADD `%s` %s REFERENCES `%s`(`%s`);", name, fieldName, dataType, refTable.name, idField.name);
610          }
611          else
612             sprintf(command, "ALTER TABLE `%s` ADD `%s` %s;", name, fieldName, dataType);
613          result = sqlite3_exec(db.db, command, null, null, null);
614          if(result) return null;
615       }
616
617       sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
618          fieldName, type.name, length);
619       result = sqlite3_exec(db.db, command, null, null, null);
620
621       field = { name = CopyString(fieldName), type = type, num = fields.count, sqliteType = sqliteType };
622       incref field;
623       fields.Add(field);
624       if(!primaryKey && refTable == this)
625          primaryKey = field;
626       return (Field)field;
627    }
628
629    Field FindField(const String name)
630    {
631       for(f : fields; !strcmp(f.name, name))
632       {
633          if(!primaryKey)
634          {
635             if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
636             {
637
638                Table * tablePtr = (Table *)eClass_GetProperty(f.type, "table");
639                if(tablePtr && *tablePtr == this)
640                   primaryKey = f;
641             }
642          }
643          return (Field)f;
644       }
645       return null;
646    }
647
648    bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init)
649    {
650       char command[1024];
651       int c;
652       int result;
653       char indexName[4096];
654
655       delete indexFields;
656       indexFieldsCount = count;
657       indexFields = new FieldIndex[count];
658       memcpy(indexFields, fieldIndexes, count * sizeof(FieldIndex));
659
660       // TODO: USE CODED INDEX NAME INSTEAD?
661       strcpy(indexName, "index_");
662       strcat(indexName, name);
663       strcat(indexName, "_");
664       for(c = 0; c<count; c++)
665       {
666          if(fieldIndexes[c].field)
667          {
668             if(count == 1 && fieldIndexes[c].field == primaryKey)
669                return true;
670             strcat(indexName, fieldIndexes[c].field.name);
671             if(fieldIndexes[c].memberField)
672             {
673                strcat(indexName, ".");
674                strcat(indexName, fieldIndexes[c].memberField.name);
675             }
676             strcat(indexName, (fieldIndexes[c].order == ascending) ? "+" : "-");
677          }
678          else
679             return false;
680       }
681
682       sprintf(command, "CREATE INDEX IF NOT EXISTS `%s` ON `%s` (", indexName, name);
683       for(c = 0; c<count; c++)
684       {
685          char columnName[1024];
686          sprintf(columnName, "`%s` %s", fieldIndexes[c].field.name, (fieldIndexes[c].order == ascending) ? "ASC" : "DESC");
687          if(c > 0) strcat(command, ", ");
688          strcat(command, columnName);
689       }
690       strcat(command, ");");
691       result = sqlite3_exec(db.db, command, null, null, null);
692
693       return result == SQLITE_OK;
694    }
695
696    String GetName()
697    {
698       return name;
699    }
700
701    Field GetFirstField()
702    {
703       return fields.first;
704    }
705
706    uint GetFieldsCount()
707    {
708       return fields.count;
709    }
710
711    uint GetRowsCount()
712    {
713       char command[1024];
714       char **t;
715       int nCols, nRows;
716       int result;
717       uint rowCount = 0;
718       sprintf(command, "SELECT COUNT(*) FROM `%s`;", name);
719       result = sqlite3_get_table(db.db, command, &t, &nRows, &nCols, null);
720       if(result == SQLITE_OK)
721       {
722          rowCount = atoi(t[1]);
723          sqlite3_free_table(t);
724       }
725       return rowCount;
726    }
727
728    // Returns true if not ordered by row ID
729    bool GetIndexOrder(char * fullOrder, bool flip)
730    {
731       if(!flip && (!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending)))
732       {
733          strcpy(fullOrder, " ORDER BY ROWID");
734          return false;
735       }
736       else
737       {
738          int c;
739          strcpy(fullOrder, " ORDER BY ");
740          for(c = flip ? indexFieldsCount-1 : 0; flip ? (c >= 0) : (c < indexFieldsCount); flip ? c-- : c++)
741          {
742             char order[1024];
743             FieldIndex * fIndex = &indexFields[c];
744             order[0] = 0;
745             if(c) strcat(order, ", ");
746             strcat(order, "`");
747             strcat(order, fIndex->field.name);
748             strcat(order, "`");
749             if(fIndex->order == (flip ? ascending : descending)) strcat(order, " DESC");
750             strcat(fullOrder, order);
751          }
752          return true;
753       }
754    }
755
756    DriverRow CreateRow()
757    {
758       char command[1024];
759       sqlite3_stmt * statement;
760       sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
761       sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null;
762
763       if(specialStatement)
764          strcpy(command, specialStatement);
765       else
766       {
767          char order[1024];
768          /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
769          sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
770          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
771          sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
772
773          sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
774          sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
775
776          sprintf(command, "INSERT INTO `%s` (ROWID) VALUES(?);", name);
777          sqlite3_prepare_v2(db.db, command, -1, &insertIDStmt, null);
778
779          sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
780          sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
781
782          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
783          sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
784
785          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
786          sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
787
788          sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
789          sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
790
791          /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
792
793          sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
794
795          GetIndexOrder(order, false);
796          sprintf(command, "SELECT ROWID, * FROM `%s`%s;", name, order);
797       }
798       sqlite3_prepare_v2(db.db, command, -1, &statement, null);
799
800       sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
801       sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
802
803       sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
804       sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
805
806       return SQLiteRow
807          { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt, 
808            insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
809            previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
810    }
811
812    ~SQLiteTable()
813    {
814       delete name;
815       delete specialStatement;
816       delete indexFields;
817       fields.Free();
818    }
819 }
820
821 class SQLiteRow : DriverRow
822 {
823    SQLiteTable tbl;
824    sqlite3_stmt * curStatement;
825
826    sqlite3_stmt * defaultStatement;
827    sqlite3_stmt * findStatement;
828    sqlite3_stmt * prevFindStatement, * lastFindStatement;
829    sqlite3_stmt * nextFindStatement;
830    sqlite3_stmt * sysIDStatement;
831    sqlite3_stmt * queryStatement;
832    sqlite3_stmt * selectRowIDsStmt;
833    sqlite3_stmt * setRowIDStmt;
834    sqlite3_stmt * lastStatement;
835    sqlite3_stmt * previousStatement;
836    sqlite3_stmt * nextStatement;
837
838    sqlite3_stmt * insertStatement;
839    sqlite3_stmt * deleteStatement;
840    sqlite3_stmt * updateStatement;
841    sqlite3_stmt * insertIDStatement;
842    bool done;
843    done = true;
844    int64 rowID;
845    // Because we use GoToSysID() and the sysIDStatement when searching for a primary key with Find(),
846    // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
847    bool findSysID;
848    int findBindId;
849    
850    bool Nil()
851    {
852       return done;
853    }
854
855    ~SQLiteRow()
856    {
857       if(defaultStatement) sqlite3_finalize(defaultStatement);
858       if(findStatement)    sqlite3_finalize(findStatement);
859       if(prevFindStatement)sqlite3_finalize(prevFindStatement);
860       if(lastFindStatement)sqlite3_finalize(lastFindStatement);
861       if(nextFindStatement)sqlite3_finalize(nextFindStatement);
862       if(sysIDStatement)   sqlite3_finalize(sysIDStatement);
863       if(insertStatement)  sqlite3_finalize(insertStatement);
864       if(deleteStatement)  sqlite3_finalize(deleteStatement);
865       if(updateStatement)  sqlite3_finalize(updateStatement);
866       if(queryStatement)   sqlite3_finalize(queryStatement);
867       if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
868       if(setRowIDStmt)     sqlite3_finalize(setRowIDStmt);
869       if(previousStatement)sqlite3_finalize(previousStatement);
870       if(nextStatement)    sqlite3_finalize(nextStatement);
871       if(lastStatement)    sqlite3_finalize(lastStatement);
872       if(insertIDStatement)    sqlite3_finalize(insertIDStatement);
873    }
874
875    bool Select(MoveOptions move)
876    {
877       int result;
878       bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
879       if(!curStatement)
880          curStatement = defaultStatement;
881       switch(move)
882       {
883          case first:
884          {
885             sqlite3_reset(curStatement);
886             result = sqlite3_step(curStatement);
887             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
888             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
889             rowID = sqlite3_column_int64(curStatement, 0);
890             break;
891          }
892          case last:
893          {
894             sqlite3_reset(curStatement);
895             curStatement = lastStatement;
896             result = sqlite3_step(curStatement);
897             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
898             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
899             rowID = sqlite3_column_int64(curStatement, 0);
900             break;
901          }
902          case middle:
903             break;
904          case next:
905          case previous:
906          {
907             // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
908             if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) || 
909                (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
910             {
911                result = sqlite3_step(curStatement);
912                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
913                if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
914                rowID = sqlite3_column_int64(curStatement, 0);
915             }
916             else if(curStatement == prevFindStatement || curStatement == findStatement || curStatement == nextFindStatement || curStatement == lastFindStatement)
917             {
918                if(rowID)
919                {
920                   int bindId = findBindId;
921                   sqlite3_reset((move == next) ? nextFindStatement : prevFindStatement);
922                   BindCursorData((move == next) ? nextFindStatement : prevFindStatement, move,
923                      (move == next && (!tbl.indexFields || (tbl.indexFieldsCount == 1 && tbl.indexFields[0].field == tbl.primaryKey && tbl.indexFields[0].order == ascending))) ? false : true, &bindId);
924                   sqlite3_reset(curStatement);
925                   curStatement = (move == next) ? nextFindStatement : prevFindStatement;
926                   result = sqlite3_step(curStatement);
927                   done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
928                   if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
929                   rowID = sqlite3_column_int64(curStatement, 0);
930                }
931                else
932                {
933                   int bindId = findBindId;
934                   sqlite3_reset((move == next) ? findStatement : lastFindStatement);
935                   sqlite3_reset(curStatement);
936                   curStatement = (move == next) ? findStatement : lastFindStatement;
937                   result = sqlite3_step(curStatement);
938                   done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
939                   if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
940                   rowID = sqlite3_column_int64(curStatement, 0);
941                }
942             }
943             else
944             {
945                sqlite3_reset(curStatement);
946                curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
947                sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
948                result = sqlite3_step(curStatement);
949                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
950                if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
951                rowID = sqlite3_column_int64(curStatement, 0);
952             }
953             break;
954          }
955          case nil:
956             sqlite3_reset(curStatement);
957             rowID = 0;
958             done = true;
959             break;
960          case here:
961             break;
962       }
963       return true;
964    }
965
966    bool Query(char * queryString)
967    {
968       bool status = true;
969       int result;
970
971       if(curStatement)
972          sqlite3_reset(curStatement);
973       if(queryStatement)
974       {
975          sqlite3_finalize(queryStatement);
976          queryStatement = null;
977       }
978
979       if(queryString)
980       {
981          result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
982          curStatement = queryStatement;
983          if(!strchr(queryString, '?'))
984          {
985             result = sqlite3_step(queryStatement);
986
987             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
988             if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
989
990             rowID = sqlite3_column_int64(queryStatement, 0);
991          }
992       }
993       else
994          curStatement = null;
995       return status;
996    }
997
998    bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
999    {
1000       int result = 1;
1001       Class dataType = fld.type;
1002       SerialBuffer buffer = null;
1003       switch(fld.sqliteType)
1004       {
1005          case SQLITE_INTEGER: 
1006          {
1007             switch(dataType.typeSize)
1008             {
1009                case 8:
1010                   result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1011                   break;
1012                case 4:
1013                   result = sqlite3_bind_int(statement, pos, *(int *)data);
1014                   break;
1015                case 2:
1016                {
1017                   int value;
1018                   if((int)data < 0)
1019                      value = (int)*(short *)data;
1020                   else
1021                      value = (int)*(uint16 *)data;
1022                   result = sqlite3_bind_int(statement, pos, value);
1023                   break;
1024                }
1025                case 1:
1026                {
1027                   int value;
1028                   if((int)data < 0)
1029                      value = (int)*(char *)data;
1030                   else
1031                      value = (int)*(byte *)data;
1032                   result = sqlite3_bind_int(statement, pos, value);
1033                   break;
1034                }
1035             }
1036             break;
1037          }
1038          case SQLITE_FLOAT:
1039          {
1040             if(dataType.typeSize == 8)
1041                result = sqlite3_bind_double(statement, pos, *(double *)data);
1042             else
1043                result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1044             break;
1045          }
1046          case SQLITE_TEXT:
1047          {
1048             if((char *)data)
1049                result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1050             else
1051                result = sqlite3_bind_text(statement, pos, null, 0, SQLITE_TRANSIENT);
1052             break;
1053          }
1054          case SQLITE_BLOB:
1055          case SQLITE_NULL:
1056          {
1057             buffer = SerialBuffer { };
1058             dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, data, buffer);
1059             result = sqlite3_bind_text(statement, pos, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1060             break;
1061          }
1062       }
1063       if(bufferOut)
1064          *bufferOut = buffer;
1065       else
1066          delete buffer;
1067       return !result;
1068    }
1069
1070    void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1071    {
1072       if(move == next || move == previous)
1073       {
1074          // Where clauses for index
1075          if(useIndex)
1076          {
1077             int c;
1078             bool gotPrimaryKey = false;
1079
1080             strcatf(command, " AND (");
1081             for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1082             {
1083                char where[1024];
1084                FieldIndex * fIndex = &tbl.indexFields[c];
1085                where[0] = 0;
1086
1087                strcat(where, "`");
1088                strcat(where, fIndex->field.name);
1089                strcat(where, "` ");
1090                strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1091                strcat(where, " ? OR (");
1092                strcat(where, fIndex->field.name);
1093                if(fIndex->field == tbl.primaryKey)
1094                   gotPrimaryKey = true;
1095                strcat(where, " = ? AND (");
1096                strcat(command, where);
1097             }
1098             strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1099             for(c = 0; c < tbl.indexFieldsCount; c++)
1100                strcat(command, "))");
1101          }
1102          else
1103             strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1104       }
1105    }
1106
1107    void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1108    {
1109       if(move == next || move == previous)
1110       {
1111          // The binds for the Extra ordering Where clauses
1112          if(useIndex)
1113          {
1114             int c;
1115             /* // Code to not rely on curStatement being set up
1116             SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1117             dataRow.GoToSysID((uint)rowID);
1118             */
1119             for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1120             {
1121                FieldIndex * fIndex = &tbl.indexFields[c];
1122                int64 data;
1123                SQLiteField fld = (SQLiteField)fIndex->field;
1124                Class type = fld.type;
1125                void * dataPtr;
1126                SerialBuffer buffer;
1127
1128                if(type.type == unitClass && !type.typeSize)
1129                {
1130                   Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1131                   if(dataType)
1132                      type = dataType;
1133                }
1134                if(type.type == structClass)
1135                {
1136                   data = (int64)new0 byte[type.structSize];
1137                   dataPtr = (void *) data;
1138                }
1139                // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1140                ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)data : &data);
1141                if(type.type == normalClass || type.type == noHeadClass)
1142                   dataPtr = (void *) data;
1143                else
1144                   dataPtr = &data;
1145                ((void (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1146                // Reuse the buffer for Blobs...
1147                if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1148                {
1149                   sqlite3_bind_text(stmt, (*bindId)++, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1150                   delete buffer;
1151                }
1152                else
1153                   ((void (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1154
1155                type._vTbl[__ecereVMethodID_class_OnFree](type, dataPtr);
1156             }
1157             // delete dataRow;
1158          }
1159
1160          // Bind for the rowid
1161          sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1162       }
1163    }
1164
1165    bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1166    {
1167       char order[1024], command[2048];
1168       int result;
1169       bool useIndex;
1170       sqlite3_stmt * stmt = null;
1171       int bindId = 1;
1172
1173       if(fld == tbl.primaryKey)
1174       {
1175          if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1176          if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1177          if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1178          if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1179          if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1180          result = GoToSysID(*(int *)data);
1181          if(result)
1182             findSysID = true;
1183          return result;
1184       }
1185
1186       useIndex = tbl.GetIndexOrder(order, false);
1187       // Basic Find
1188       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1189       AddCursorWhereClauses(command, move, useIndex);
1190       strcat(command, order);
1191       strcat(command, ";");
1192       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1193       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1194       BindCursorData(stmt, move, useIndex, &bindId);
1195
1196       // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1197       if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1198       if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1199       if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1200       if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1201       if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1202
1203       curStatement = findStatement = stmt;
1204       findBindId = bindId;
1205
1206       // For going back to forward find
1207       bindId = 1;
1208       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1209       AddCursorWhereClauses(command, next, useIndex);
1210       strcat(command, order);
1211       strcat(command, ";");
1212       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1213       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1214       nextFindStatement = stmt;
1215
1216       // Backwards
1217       tbl.GetIndexOrder(order, true);
1218       // For tracing back finds
1219       bindId = 1;
1220       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1221       AddCursorWhereClauses(command, previous, true);
1222       strcat(command, order);
1223       strcat(command, ";");
1224       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1225       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1226       prevFindStatement = stmt;
1227
1228       // For tracing back from last
1229       bindId = 1;
1230       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1231       strcat(command, order);
1232       strcat(command, ";");
1233       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1234       BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1235       lastFindStatement = stmt;
1236
1237       result = sqlite3_step(findStatement);
1238
1239       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1240       if(done)
1241       {
1242          rowID = 0;
1243          sqlite3_reset(findStatement);
1244       }
1245       else
1246          rowID = sqlite3_column_int64(findStatement, 0);
1247       return !done;
1248    }
1249
1250    bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1251    {
1252 #define BINDDATA \
1253          for(c = 0; c < numFields; c++) \
1254          { \
1255             FieldFindData * fieldFind = &findData[c]; \
1256             SQLiteField sqlFld = (SQLiteField)findData->field; \
1257             Class dataType = sqlFld.type; \
1258             BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1259          }
1260
1261       if(numFields)
1262       {
1263          char criterias[4096], command[4096], order[1024];
1264          int result;
1265          int c;
1266          bool useIndex;
1267          sqlite3_stmt * stmt = null;
1268          int bindId = 1;
1269
1270          // Criterias
1271          sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1272          for(c = 0; c < numFields; c++)
1273          {
1274             FieldFindData * fieldFind = &findData[c];
1275
1276             if(c) strcat(criterias, " AND `");
1277             strcat(criterias, fieldFind->field.name);
1278             strcat(criterias, "` = ?");
1279          }
1280
1281          useIndex = tbl.GetIndexOrder(order, false);
1282          // Basic Find (multiple)
1283          strcpy(command, criterias);
1284          AddCursorWhereClauses(command, move, useIndex);
1285          strcat(command, order);
1286          strcat(command, ";");
1287          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1288          BINDDATA;
1289          BindCursorData(stmt, move, useIndex, &bindId);
1290
1291          // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1292          if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1293          if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1294          if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1295          if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1296          if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1297
1298          curStatement = findStatement = stmt;
1299          findBindId = bindId;
1300
1301          // For tracing back forward finds
1302          bindId = 1;
1303          strcpy(command, criterias);
1304          AddCursorWhereClauses(command, previous, true);
1305          strcat(command, order);
1306          strcat(command, ";");
1307          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1308          BINDDATA;
1309          nextFindStatement = stmt;
1310
1311          // Backwards
1312          tbl.GetIndexOrder(order, true);
1313          // For tracing back finds
1314          bindId = 1;
1315          strcpy(command, criterias);
1316          AddCursorWhereClauses(command, next, useIndex);
1317          strcat(command, order);
1318          strcat(command, ";");
1319          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1320          BINDDATA;
1321          prevFindStatement = stmt;
1322
1323          // For tracing back from last
1324          bindId = 1;
1325          strcpy(command, criterias);
1326          strcat(command, order);
1327          strcat(command, ";");
1328          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1329          BINDDATA;
1330          lastFindStatement = stmt;
1331
1332          result = sqlite3_step(findStatement);
1333          done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1334          if(done)
1335          {
1336             rowID = 0;
1337             sqlite3_reset(findStatement);
1338          }
1339          else
1340             rowID = sqlite3_column_int64(findStatement, 0);
1341          return !done;
1342       }
1343       return false;
1344    }
1345
1346    bool Synch(DriverRow to)
1347    {
1348       SQLiteRow rowTo = (SQLiteRow)to;
1349       if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1350          return GoToSysID((uint)rowTo.rowID);
1351       return false;
1352    }
1353
1354    bool Add(uint64 id)
1355    {
1356       int result;
1357       //char command[1024];
1358       //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1359       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1360       if(id)
1361       {
1362          sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1363          result = sqlite3_step(insertIDStatement);
1364       }
1365       else
1366          result = sqlite3_step(insertStatement);
1367       if(result == SQLITE_DONE)     // if(result == SQLITE_OK)
1368       {
1369          rowID = sqlite3_last_insert_rowid(tbl.db.db);
1370          if(rowID > MAXDWORD)
1371          {
1372             int64 lastID = tbl.lastID;
1373
1374             sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1375             while(true)
1376             {
1377                int64 id;
1378                result = sqlite3_step(selectRowIDsStmt);
1379                if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1380                id = sqlite3_column_int64(selectRowIDsStmt, 0);
1381                if(id - lastID > 1) break;
1382                lastID = id;
1383             }
1384             sqlite3_reset(selectRowIDsStmt);
1385
1386             sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1387             rowID = lastID + 1;
1388             tbl.lastID = rowID;
1389             sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1390             result = sqlite3_step(setRowIDStmt);
1391             sqlite3_reset(setRowIDStmt);
1392          }
1393          sqlite3_reset(id ? insertIDStatement : insertStatement);
1394          curStatement = sysIDStatement;
1395          findSysID = false;
1396          sqlite3_reset(curStatement);
1397          sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1398          result = sqlite3_step(curStatement);
1399          return true;
1400       }
1401       sqlite3_reset(insertStatement);
1402       return false;
1403    }
1404
1405    bool Delete()
1406    {
1407       int result;
1408       //char command[1024];
1409       //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1410       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1411       sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1412       result = sqlite3_step(deleteStatement);
1413       sqlite3_reset(deleteStatement);
1414       rowID = 0;
1415       return result == SQLITE_OK || result == SQLITE_DONE;
1416    }
1417
1418    bool GetData(Field fld, typed_object &data)
1419    {
1420       SQLiteField sqlFld = (SQLiteField)fld;
1421       int num = sqlFld.num + 1;
1422       Class dataType = sqlFld.type;
1423
1424
1425       switch(sqlFld.sqliteType)
1426       {
1427          case SQLITE_INTEGER: 
1428          {
1429             switch(dataType.typeSize)
1430             {
1431                case 8:
1432                   if(fld == tbl.primaryKey)
1433                      *(int64 *)data = rowID;
1434                   else
1435                      *(int64 *)data = sqlite3_column_int64(curStatement, num);
1436                   break;
1437                case 4:
1438                   if(fld == tbl.primaryKey)
1439                      *(int *)data = (int)(uint)rowID;
1440                   else
1441                      *(int *)data = sqlite3_column_int(curStatement, num);
1442                   break;
1443                case 2:
1444                {
1445                   int value;
1446                   if(fld == tbl.primaryKey)
1447                      value = (int)(uint)rowID;
1448                   else
1449                      value = sqlite3_column_int(curStatement, num);
1450                   if(value < 0)
1451                      *(short *)data = (short)value;
1452                   else
1453                      *(uint16 *)data = (uint16)value;
1454                   break;
1455                }
1456                case 1:
1457                {
1458                   int value;
1459                   if(fld == tbl.primaryKey)
1460                      value = (int)(uint)rowID;
1461                   else
1462                      value = sqlite3_column_int(curStatement, num);
1463                   if(value < 0)
1464                      *(char *)data = (char)value;
1465                   else
1466                      *(byte *)data = (byte)value;
1467                   break;
1468                }
1469             }
1470             break;
1471          }
1472          case SQLITE_FLOAT:
1473          {
1474             double d = sqlite3_column_double(curStatement, num);
1475             if(dataType.typeSize == 8)
1476                *(double *)data = d;
1477             else
1478                *(float *)data = (float)d;
1479             break;
1480          }
1481          case SQLITE_TEXT:
1482          {
1483             int numBytes = sqlite3_column_bytes(curStatement, num);
1484             char * text = sqlite3_column_text(curStatement, num);
1485             *(char **)data = text ? new byte[numBytes+1] : null;
1486             if(text)
1487                memcpy(*(char **)data, text, numBytes+1);
1488             break;
1489          }
1490          case SQLITE_BLOB:
1491          {
1492             SerialBuffer buffer { };
1493             //buffer._buffer = sqlite3_column_blob(curStatement, num);
1494             buffer._size = sqlite3_column_bytes(curStatement, num);
1495             buffer._buffer = sqlite3_column_text(curStatement, num);
1496             buffer.count = buffer._size;
1497
1498             dataType._vTbl[__ecereVMethodID_class_OnUnserialize](dataType, data, buffer);
1499            
1500             buffer._buffer = null;
1501             delete buffer;
1502             break;
1503          }
1504       }
1505       return true;
1506    }
1507
1508    bool SetData(Field fld, typed_object data)
1509    {
1510       SQLiteField sqlFld = (SQLiteField)fld;
1511       int result;
1512       int num = sqlFld.num + 1;
1513       char command[1024];
1514
1515       if(updateStatement)
1516          sqlite3_finalize(updateStatement);
1517       sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1518       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1519       sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1520       BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1521       result = sqlite3_step(updateStatement);
1522       sqlite3_reset(updateStatement);
1523       if(fld == tbl.primaryKey)
1524          rowID = *(uint *)data;
1525       return result == SQLITE_DONE;
1526    }
1527
1528    int GetSysID()
1529    {
1530       return (int)(uint)rowID;
1531    }
1532
1533    bool GoToSysID(uint id)
1534    {
1535       //char command[1024];
1536       int result;
1537       rowID = (uint)id;
1538       //if(statement)
1539          //sqlite3_finalize(statement);
1540       //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1541       //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1542
1543       findSysID = false;
1544       if(curStatement)
1545          sqlite3_reset(curStatement);
1546
1547       curStatement = sysIDStatement;
1548       sqlite3_reset(sysIDStatement);
1549       sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1550       result = sqlite3_step(curStatement);
1551       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1552       if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1553       return !done;
1554    }
1555
1556    bool SetQueryParam(int paramID, int value)
1557    {
1558       int result;
1559       if(curStatement != queryStatement)
1560       {
1561          if(curStatement) sqlite3_reset(curStatement);
1562          curStatement = queryStatement;
1563       }
1564       sqlite3_reset(queryStatement);
1565       result = sqlite3_bind_int(queryStatement, paramID, value);
1566       return !result;
1567    }
1568
1569    bool SetQueryParam64(int paramID, int64 value)
1570    {
1571       int result;
1572       if(curStatement != queryStatement)
1573       {
1574          if(curStatement) sqlite3_reset(curStatement);
1575          curStatement = queryStatement;
1576       }
1577       sqlite3_reset(queryStatement);
1578       result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1579       return !result;
1580    }
1581
1582    bool SetQueryParamText(int paramID, char * data)
1583    {
1584       int result;
1585       if(curStatement != queryStatement)
1586       {
1587          if(curStatement) sqlite3_reset(curStatement);
1588          curStatement = queryStatement;
1589       }
1590       sqlite3_reset(queryStatement);
1591       if(data)
1592          result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1593       else
1594          result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1595       return !result;
1596    }
1597
1598    bool SetQueryParamObject(int paramID, void * data, Class type)
1599    {
1600       int result;
1601       if(curStatement != queryStatement)
1602       {
1603          if(curStatement) sqlite3_reset(curStatement);
1604          curStatement = queryStatement;
1605       }
1606       sqlite3_reset(queryStatement);
1607       {
1608          SerialBuffer buffer { };
1609          type._vTbl[__ecereVMethodID_class_OnSerialize](type, data, buffer);
1610          result = sqlite3_bind_text(queryStatement, paramID, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1611          delete buffer;
1612       }
1613       return !result;
1614    }
1615
1616    bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1617    {
1618       if(curStatement != queryStatement)
1619       {
1620          if(curStatement) sqlite3_reset(curStatement);
1621          curStatement = queryStatement;
1622       }
1623       sqlite3_reset(queryStatement);
1624       return BindData(queryStatement, pos, fld, data, null);
1625    }
1626
1627    /*char * GetExtraColumn(int paramID)
1628    {
1629       SQLiteField lastFld = tbl.fields.last;
1630       return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1631    }*/
1632    char * GetColumn(int paramID)
1633    {
1634       return sqlite3_column_text(curStatement, paramID);
1635    }
1636 }