eda/ecere: Prevent assigning itself as a parent to a DataRow; EDA GUI Data loading...
[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    DriverRow CreateRow()
717    {
718       char command[1024];
719       sqlite3_stmt * statement;
720       sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
721       sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null;
722
723       if(specialStatement)
724          strcpy(command, specialStatement);
725       else
726       {
727          /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
728          sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
729          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
730          sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
731
732          sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
733          sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
734          sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
735          sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
736
737          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
738          sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
739
740          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
741          sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
742
743          sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
744          sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
745
746          /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
747
748          sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
749
750          if(!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending))
751             sprintf(command, "SELECT ROWID, * FROM `%s`;", name);
752          else
753          {
754             int c;
755             sprintf(command, "SELECT ROWID, * FROM `%s` ORDER BY ", name);
756             for(c = 0; c < indexFieldsCount; c++)
757             {
758                char order[1024];
759                FieldIndex * fIndex = &indexFields[c];
760                order[0] = 0;
761                if(c) strcat(order, ", ");
762                strcat(order, "`");
763                strcat(order, fIndex->field.name);
764                strcat(order, "`");
765                if(fIndex->order == descending) strcat(order, " DESC");
766                strcat(command, order);
767             }
768             strcat(command, ";");
769          }
770       }
771       sqlite3_prepare_v2(db.db, command, -1, &statement, null);
772
773       sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
774       sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
775
776       sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
777       sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
778
779       return SQLiteRow
780          { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt, 
781            insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
782            previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt };
783    }
784
785    ~SQLiteTable()
786    {
787       delete name;
788       delete specialStatement;
789       delete indexFields;
790       fields.Free();
791    }
792 }
793
794 class SQLiteRow : DriverRow
795 {
796    SQLiteTable tbl;
797    sqlite3_stmt * curStatement;
798
799    sqlite3_stmt * defaultStatement;
800    sqlite3_stmt * findStatement;
801    sqlite3_stmt * sysIDStatement;
802    sqlite3_stmt * queryStatement;
803    sqlite3_stmt * findMultipleStatement;
804    sqlite3_stmt * selectRowIDsStmt;
805    sqlite3_stmt * setRowIDStmt;
806    sqlite3_stmt * lastStatement;
807    sqlite3_stmt * previousStatement;
808    sqlite3_stmt * nextStatement;
809
810    sqlite3_stmt * insertStatement;
811    sqlite3_stmt * deleteStatement;
812    sqlite3_stmt * updateStatement;
813    bool done;
814    done = true;
815    int64 rowID;
816    
817    bool Nil()
818    {
819       return done;
820    }
821
822    ~SQLiteRow()
823    {
824       if(defaultStatement) sqlite3_finalize(defaultStatement);
825       if(findStatement)    sqlite3_finalize(findStatement);
826       if(findMultipleStatement)    sqlite3_finalize(findMultipleStatement);
827       if(sysIDStatement)   sqlite3_finalize(sysIDStatement);
828       if(insertStatement)  sqlite3_finalize(insertStatement);
829       if(deleteStatement)  sqlite3_finalize(deleteStatement);
830       if(updateStatement)  sqlite3_finalize(updateStatement);
831       if(queryStatement)   sqlite3_finalize(queryStatement);
832       if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
833       if(setRowIDStmt)     sqlite3_finalize(setRowIDStmt);
834       if(previousStatement)sqlite3_finalize(previousStatement);
835       if(nextStatement)    sqlite3_finalize(nextStatement);
836       if(lastStatement)    sqlite3_finalize(lastStatement);
837    }
838
839    bool Select(MoveOptions move)
840    {
841       int result;
842       bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
843       if(!curStatement)
844          curStatement = defaultStatement;
845       switch(move)
846       {
847          case first:
848          {
849             sqlite3_reset(curStatement);
850             result = sqlite3_step(curStatement);
851             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
852             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
853             rowID = sqlite3_column_int64(curStatement, 0);
854             break;
855          }
856          case last:
857          {
858             sqlite3_reset(curStatement);
859             curStatement = lastStatement;
860             result = sqlite3_step(curStatement);
861             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
862             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
863             rowID = sqlite3_column_int64(curStatement, 0);
864             break;
865          }
866          case middle:
867             break;
868          case next:
869             if(!stepping)
870             {
871                result = sqlite3_step(curStatement);
872                done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
873                if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
874                rowID = sqlite3_column_int64(curStatement, 0);
875                break;
876             }
877          case previous:
878          {
879             sqlite3_reset(curStatement);
880             curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
881             sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
882             result = sqlite3_step(curStatement);
883             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
884             if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
885             rowID = sqlite3_column_int64(curStatement, 0);
886             break;
887          }
888          case nil:
889             sqlite3_reset(curStatement);
890             rowID = 0;
891             done = true;
892             break;
893          case here:
894             break;
895       }
896       return true;
897    }
898
899    bool Query(char * queryString)
900    {
901       bool status = true;
902       int result;
903
904       if(curStatement)
905          sqlite3_reset(curStatement);
906       if(queryStatement)
907       {
908          sqlite3_finalize(queryStatement);
909          queryStatement = null;
910       }
911
912       if(queryString)
913       {
914          result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
915          curStatement = queryStatement;
916          if(!strchr(queryString, '?'))
917          {
918             result = sqlite3_step(queryStatement);
919
920             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
921             if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
922
923             rowID = sqlite3_column_int64(queryStatement, 0);
924          }
925       }
926       else
927          curStatement = null;
928       return status;
929    }
930
931    bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
932    {
933       char command[1024];
934       int result;
935       SQLiteField sqlFld = (SQLiteField)fld;
936       Class dataType = sqlFld.type;
937
938       if(fld == tbl.primaryKey)
939       {
940          return GoToSysID(*(int *)data);
941       }
942       
943       if(curStatement)
944          sqlite3_reset(curStatement);
945       if(findStatement)
946          sqlite3_finalize(findStatement);
947
948       sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?;", tbl.name, fld.name);
949       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &findStatement, null);
950
951       // result = sqlite3_bind_text(findStatement, 1, fld.name, strlen(fld.name), SQLITE_STATIC);
952
953       curStatement = findStatement;
954       switch(sqlFld.sqliteType)
955       {
956          case SQLITE_INTEGER: 
957          {
958             switch(dataType.typeSize)
959             {
960                case 8:
961                   sqlite3_bind_int64(findStatement, 1, (sqlite3_int64)*(int64 *)data);
962                   break;
963                case 4:
964                   sqlite3_bind_int(findStatement, 1, *(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(findStatement, 1, 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(findStatement, 1, value);
984                   break;
985                }
986             }
987             break;
988          }
989          case SQLITE_FLOAT:
990          {
991             if(dataType.typeSize == 8)
992                sqlite3_bind_double(findStatement, 1, *(double *)data);
993             else
994                sqlite3_bind_double(findStatement, 1, (double)*(float *)data);
995             break;
996          }
997          case SQLITE_TEXT:
998          {
999             if((char *)data)
1000                sqlite3_bind_text(findStatement, 1, (char *)data, strlen((char *)data), SQLITE_STATIC);
1001             else
1002                sqlite3_bind_text(findStatement, 1, null, 0, SQLITE_STATIC);
1003             break;
1004          }
1005          case SQLITE_BLOB:
1006          case SQLITE_NULL:
1007          {
1008             SerialBuffer buffer { };
1009
1010             dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, data, buffer);
1011             //sqlite3_bind_blob(findStatement, 1, buffer._buffer, buffer.count, SQLITE_STATIC);
1012             sqlite3_bind_text(findStatement, 1, buffer._buffer, buffer.count, SQLITE_STATIC);
1013             result = sqlite3_step(findStatement);
1014
1015             done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1016             if(done) { rowID = 0; sqlite3_reset(findStatement); delete buffer; return false; }
1017
1018             rowID = sqlite3_column_int64(findStatement, 0);
1019
1020             delete buffer;
1021             return true;
1022             break;
1023          } 
1024       }
1025       result = sqlite3_step(findStatement);
1026
1027       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1028       if(done) { rowID = 0; sqlite3_reset(findStatement); return false; }
1029
1030       rowID = sqlite3_column_int64(findStatement, 0);
1031       return true;
1032    }
1033
1034    bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1035    {
1036       if(numFields)
1037       {
1038          char command[4096];
1039          int result;
1040          int c;
1041          Array<SerialBuffer> serialBuffers { };
1042
1043          if(curStatement)
1044             sqlite3_reset(curStatement);
1045          if(findMultipleStatement)
1046             sqlite3_finalize(findMultipleStatement);
1047
1048          sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1049          for(c = 0; c < numFields; c++)
1050          {
1051             FieldFindData * fieldFind = &findData[c];
1052
1053             if(c) strcat(command, " AND `");
1054             strcat(command, fieldFind->field.name);
1055             strcat(command, "` = ?");
1056          }
1057          strcat(command, ";");
1058
1059          result = sqlite3_prepare_v2(tbl.db.db, command, -1, &findMultipleStatement, null);
1060          curStatement = findMultipleStatement;
1061
1062          for(c = 0; c < numFields; c++)
1063          {
1064             FieldFindData * fieldFind = &findData[c];
1065             SQLiteField sqlFld = (SQLiteField)fieldFind->field;
1066             Class dataType = sqlFld.type;
1067
1068             switch(sqlFld.sqliteType)
1069             {
1070                case SQLITE_INTEGER: 
1071                {
1072                   switch(dataType.typeSize)
1073                   {
1074                      case 8:
1075                         sqlite3_bind_int64(findMultipleStatement, 1 + c, (sqlite_int64)fieldFind->value.i64);
1076                         break;
1077                      case 4:
1078                         sqlite3_bind_int(findMultipleStatement, 1 + c, fieldFind->value.i);
1079                         break;
1080                      case 2:
1081                      {
1082                         int value;
1083                         if(value < 0)
1084                            value = (int)fieldFind->value.s;
1085                         else
1086                            value = (int)fieldFind->value.us;
1087                         sqlite3_bind_int(findMultipleStatement, 1 + c, value);
1088                         break;
1089                      }
1090                      case 1:
1091                      {
1092                         int value;
1093                         if(value < 0)
1094                            value = (int)fieldFind->value.c;
1095                         else
1096                            value = (int)fieldFind->value.uc;
1097                         sqlite3_bind_int(findMultipleStatement, 1 + c, value);
1098                         break;
1099                      }
1100                   }
1101                   break;
1102                }
1103                case SQLITE_FLOAT:
1104                {
1105                   if(dataType.typeSize == 8)
1106                      sqlite3_bind_double(findMultipleStatement, 1 + c, fieldFind->value.d);
1107                   else
1108                      sqlite3_bind_double(findMultipleStatement, 1 + c, fieldFind->value.f);
1109                   break;
1110                }
1111                case SQLITE_TEXT:
1112                {
1113                   if(fieldFind->value.p)
1114                      sqlite3_bind_text(findMultipleStatement, 1 + c, (char *)fieldFind->value.p, strlen(fieldFind->value.p), SQLITE_STATIC);
1115                   else
1116                      sqlite3_bind_text(findMultipleStatement, 1 + c, null, 0, SQLITE_STATIC);
1117                   break;
1118                }
1119                case SQLITE_BLOB:
1120                case SQLITE_NULL:
1121                {
1122                   SerialBuffer buffer { };
1123
1124                   dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, fieldFind->value.p, buffer);
1125                   //sqlite3_bind_blob(findMultipleStatement, 1 + c, buffer._buffer, buffer.count, SQLITE_STATIC);
1126                   sqlite3_bind_text(findMultipleStatement, 1 + c, buffer._buffer, buffer.count, SQLITE_STATIC);
1127
1128                   serialBuffers.Add(buffer);
1129                   break;
1130                } 
1131             }
1132          }
1133
1134          result = sqlite3_step(findMultipleStatement);
1135
1136          done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1137          if(done)
1138          {
1139             rowID = 0;
1140             sqlite3_reset(findMultipleStatement);
1141
1142             serialBuffers.Free();
1143             delete serialBuffers;
1144             return false;
1145          }
1146          else
1147          {
1148             rowID = sqlite3_column_int64(findMultipleStatement, 0);
1149
1150             serialBuffers.Free();
1151             delete serialBuffers;
1152             return true;
1153          }
1154       }
1155       return false;
1156    }
1157
1158    bool Synch(DriverRow to)
1159    {
1160       return true;
1161    }
1162
1163    bool Add()
1164    {
1165       int result;
1166       //char command[1024];
1167       //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1168       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1169       result = sqlite3_step(insertStatement);
1170       if(result == SQLITE_DONE)     // if(result == SQLITE_OK)
1171       {
1172          rowID = sqlite3_last_insert_rowid(tbl.db.db);
1173          if(rowID > MAXDWORD)
1174          {
1175             int64 lastID = tbl.lastID;
1176
1177             sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1178             while(true)
1179             {
1180                int64 id;
1181                result = sqlite3_step(selectRowIDsStmt);
1182                if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1183                id = sqlite3_column_int64(selectRowIDsStmt, 0);
1184                if(id - lastID > 1) break;
1185                lastID = id;
1186             }
1187             sqlite3_reset(selectRowIDsStmt);
1188
1189             sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1190             rowID = lastID + 1;
1191             tbl.lastID = rowID;
1192             sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1193             result = sqlite3_step(setRowIDStmt);
1194             sqlite3_reset(setRowIDStmt);
1195          }
1196          sqlite3_reset(insertStatement);
1197          curStatement = sysIDStatement;
1198          sqlite3_reset(curStatement);
1199          return true;
1200       }
1201       sqlite3_reset(insertStatement);
1202       return false;
1203    }
1204
1205    bool Delete()
1206    {
1207       int result;
1208       //char command[1024];
1209       //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1210       //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1211       sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1212       result = sqlite3_step(deleteStatement);
1213       sqlite3_reset(deleteStatement);
1214       rowID = 0;
1215       return result == SQLITE_OK || result == SQLITE_DONE;
1216    }
1217
1218    bool GetData(Field fld, typed_object &data)
1219    {
1220       SQLiteField sqlFld = (SQLiteField)fld;
1221       int num = sqlFld.num + 1;
1222       Class dataType = sqlFld.type;
1223       switch(sqlFld.sqliteType)
1224       {
1225          case SQLITE_INTEGER: 
1226          {
1227             switch(dataType.typeSize)
1228             {
1229                case 8:
1230                   if(fld == tbl.primaryKey)
1231                      *(int64 *)data = rowID;
1232                   else
1233                      *(int64 *)data = sqlite3_column_int64(curStatement, num);
1234                   break;
1235                case 4:
1236                   if(fld == tbl.primaryKey)
1237                      *(int *)data = (int)(uint)rowID;
1238                   else
1239                      *(int *)data = sqlite3_column_int(curStatement, num);
1240                   break;
1241                case 2:
1242                {
1243                   int value;
1244                   if(fld == tbl.primaryKey)
1245                      value = (int)(uint)rowID;
1246                   else
1247                      value = sqlite3_column_int(curStatement, num);
1248                   if(value < 0)
1249                      *(short *)data = (short)value;
1250                   else
1251                      *(uint16 *)data = (uint16)value;
1252                   break;
1253                }
1254                case 1:
1255                {
1256                   int value;
1257                   if(fld == tbl.primaryKey)
1258                      value = (int)(uint)rowID;
1259                   else
1260                      value = sqlite3_column_int(curStatement, num);
1261                   if(value < 0)
1262                      *(char *)data = (char)value;
1263                   else
1264                      *(byte *)data = (byte)value;
1265                   break;
1266                }
1267             }
1268             break;
1269          }
1270          case SQLITE_FLOAT:
1271          {
1272             double d = sqlite3_column_double(curStatement, num);
1273             if(dataType.typeSize == 8)
1274                *(double *)data = d;
1275             else
1276                *(float *)data = (float)d;
1277             break;
1278          }
1279          case SQLITE_TEXT:
1280          {
1281             int numBytes = sqlite3_column_bytes(curStatement, num);
1282             char * text = sqlite3_column_text(curStatement, num);
1283             *(char **)data = text ? new byte[numBytes+1] : null;
1284             if(text)
1285                memcpy(*(char **)data, text, numBytes+1);
1286             break;
1287          }
1288          case SQLITE_BLOB:
1289          {
1290             SerialBuffer buffer { };
1291             //buffer._buffer = sqlite3_column_blob(curStatement, num);
1292             buffer._size = sqlite3_column_bytes(curStatement, num);
1293             buffer._buffer = sqlite3_column_text(curStatement, num);
1294             buffer.count = buffer._size;
1295
1296             dataType._vTbl[__ecereVMethodID_class_OnUnserialize](dataType, data, buffer);
1297            
1298             buffer._buffer = null;
1299             delete buffer;
1300             break;
1301          } 
1302       }
1303       return true;
1304    }
1305
1306    bool SetData(Field fld, typed_object data)
1307    {
1308       SQLiteField sqlFld = (SQLiteField)fld;
1309       int result;
1310
1311       int num = sqlFld.num + 1;
1312       Class dataType = sqlFld.type;
1313       char command[1024];
1314       //sqlite3_stmt * setStatement;
1315
1316       if(updateStatement)
1317          sqlite3_finalize(updateStatement);
1318
1319       // sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = %d;", tbl.name, sqlFld.name, rowID);
1320       sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1321
1322       result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1323
1324       //sqlite3_bind_text(updateStatement, 1, sqlFld.name, strlen(sqlFld.name), SQLITE_STATIC);
1325       //sqlite3_bind_int64(updateStatement, 3, (sqlite3_int64)rowID);
1326
1327       sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1328       switch(sqlFld.sqliteType)
1329       {
1330          case SQLITE_INTEGER: 
1331          {
1332             switch(dataType.typeSize)
1333             {
1334                case 8:
1335                   sqlite3_bind_int64(updateStatement, 1, (sqlite3_int64)*(int64 *)data);
1336                   break;
1337                case 4:
1338                   sqlite3_bind_int(updateStatement, 1, *(int *)data);
1339                   break;
1340                case 2:
1341                {
1342                   int value;
1343                   if((int)data < 0)
1344                      value = (int)*(short *)data;
1345                   else
1346                      value = (int)*(uint16 *)data;
1347                   sqlite3_bind_int(updateStatement, 1, value);
1348                   break;
1349                }
1350                case 1:
1351                {
1352                   int value;
1353                   if((int)data < 0)
1354                      value = (int)*(char *)data;
1355                   else
1356                      value = (int)*(byte *)data;
1357                   sqlite3_bind_int(updateStatement, 1, value);
1358                   break;
1359                }
1360             }
1361             break;
1362          }
1363          case SQLITE_FLOAT:
1364          {
1365             if(dataType.typeSize == 8)
1366                sqlite3_bind_double(updateStatement, 1, *(double *)data);
1367             else
1368                sqlite3_bind_double(updateStatement, 1, (double)*(float *)data);
1369             break;
1370          }
1371          case SQLITE_TEXT:
1372          {
1373             // TOFIX: Checking a not casted typed_object for null should generate a compiler error (crashes!)
1374             if((char *)data)
1375                sqlite3_bind_text(updateStatement, 1, (char *)data, strlen((char *)data), SQLITE_STATIC);
1376             else
1377                sqlite3_bind_text(updateStatement, 1, null, 0, SQLITE_STATIC);
1378             break;
1379          }
1380          case SQLITE_BLOB:
1381          case SQLITE_NULL:
1382          {
1383             SerialBuffer buffer { };
1384
1385             dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, data, buffer);
1386             //sqlite3_bind_blob(updateStatement, 1, buffer._buffer, buffer.count, SQLITE_STATIC);
1387             sqlite3_bind_text(updateStatement, 1, buffer._buffer, buffer.count, SQLITE_STATIC);
1388             sqlite3_step(updateStatement);
1389             sqlite3_reset(updateStatement);
1390             delete buffer;
1391             return true;
1392             break;
1393          }
1394       }
1395       result = sqlite3_step(updateStatement);
1396       sqlite3_reset(updateStatement);
1397       if(fld == tbl.primaryKey)
1398       {
1399          rowID = *(uint *)data;
1400       }
1401       return result == SQLITE_DONE;
1402    }
1403
1404    int GetSysID()
1405    {
1406       return (int)(uint)rowID;
1407    }
1408
1409    bool GoToSysID(int id)
1410    {
1411       //char command[1024];
1412       int result;
1413       rowID = (uint)id;
1414       //if(statement)
1415          //sqlite3_finalize(statement);
1416       //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1417       //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1418
1419       if(curStatement)
1420          sqlite3_reset(curStatement);
1421
1422       curStatement = sysIDStatement;
1423       sqlite3_reset(sysIDStatement);
1424       sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1425       result = sqlite3_step(curStatement);
1426       done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1427       if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1428       return !done;
1429    }
1430
1431    bool SetQueryParam(int paramID, int value)
1432    {
1433       int result;
1434       if(curStatement != queryStatement)
1435       {
1436          if(curStatement) sqlite3_reset(curStatement);
1437          curStatement = queryStatement;
1438       }
1439       sqlite3_reset(queryStatement);
1440       result = sqlite3_bind_int(queryStatement, paramID, value);
1441       return !result;
1442    }
1443
1444    bool SetQueryParam64(int paramID, int64 value)
1445    {
1446       int result;
1447       if(curStatement != queryStatement)
1448       {
1449          if(curStatement) sqlite3_reset(curStatement);
1450          curStatement = queryStatement;
1451       }
1452       sqlite3_reset(queryStatement);
1453       result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1454       return !result;
1455    }
1456
1457    bool SetQueryParamText(int paramID, char * data)
1458    {
1459       int result;
1460       if(curStatement != queryStatement)
1461       {
1462          if(curStatement) sqlite3_reset(curStatement);
1463          curStatement = queryStatement;
1464       }
1465       sqlite3_reset(queryStatement);
1466       if(data)
1467          result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_STATIC);
1468       else
1469          result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_STATIC);
1470       return !result;
1471    }
1472
1473    bool SetQueryParamObject(int paramID, void * data, Class type)
1474    {
1475       int result;
1476       if(curStatement != queryStatement)
1477       {
1478          if(curStatement) sqlite3_reset(curStatement);
1479          curStatement = queryStatement;
1480       }
1481       sqlite3_reset(queryStatement);
1482       {
1483          SerialBuffer buffer { };
1484          type._vTbl[__ecereVMethodID_class_OnSerialize](type, data, buffer);
1485          result = sqlite3_bind_text(queryStatement, paramID, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1486          delete buffer;
1487       }
1488       return !result;
1489    }
1490
1491    /*char * GetExtraColumn(int paramID)
1492    {
1493       SQLiteField lastFld = tbl.fields.last;
1494       return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1495    }*/
1496    char * GetColumn(int paramID)
1497    {
1498       return sqlite3_column_text(curStatement, paramID);
1499    }
1500 }