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