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