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