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