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