2 public import static "ecere"
3 public import static "EDA"
19 __attribute__((unused)) static void UnusedFunction()
27 a.OnEdit(null,null,0,0,0,0,0);
28 a.OnDisplay(null,0,0,0,0,0,0);
29 a.OnGetDataFromString(null);
30 a.OnUnserialize(null);
35 extern int __ecereVMethodID_class_OnGetString;
36 extern int __ecereVMethodID_class_OnGetDataFromString;
37 extern int __ecereVMethodID_class_OnCompare;
38 extern int __ecereVMethodID_class_OnSerialize;
39 extern int __ecereVMethodID_class_OnUnserialize;
40 extern int __ecereVMethodID_class_OnFree;
43 int CollationCompare(Class type, int count1, const void * data1, int count2, const void * data2)
45 if(type.type == normalClass || type.type == noHeadClass)
47 Instance inst1, inst2;
49 SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
50 SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
52 ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst1, buffer1);
53 ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst2, buffer2);
55 result = ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
57 buffer1.buffer = null;
58 buffer2.buffer = null;
65 else if(type.type == structClass)
67 void * inst1, * inst2;
69 SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
70 SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
72 inst1 = new0 byte[type.structSize];
73 inst2 = new0 byte[type.structSize];
74 ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst1, buffer1);
75 ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst2, buffer2);
77 result = ((int (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
79 buffer1.buffer = null;
80 buffer2.buffer = null;
88 return ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, data1, data2);
91 public class SQLiteStaticLink { } // Until .imp generation is fixed
93 class SQLiteDataSource : DirFilesDataSourceDriver
95 class_property(name) = "SQLite";
96 class_property(databaseFileExtension) = "sqlite";
98 Database OpenDatabase(const String name, CreateOptions createOptions, DataSource ds)
100 Database result = null;
103 String path = MakeDatabasePath(name);
106 // sqlite3_open(path, &db);
107 // sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY /*SQLITE_OPEN_READWRITE*/ /*SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE*/, null );
109 if(sqlite3_open_v2(path, &db, (createOptions == readOnly) ? SQLITE_OPEN_READONLY :
110 (SQLITE_OPEN_READWRITE | ((createOptions == create) ? SQLITE_OPEN_CREATE : 0)), null))
112 // fprintf(stderr, "%s\n", s); // interesting
113 printf($"EDASQLite: Can't open database (%s): %s\n", path, sqlite3_errmsg(db));
120 sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);");
121 sqlite3_exec(db, command, null, null, null);
123 if(createOptions != readOnly)
125 sqlite3_exec(db, "PRAGMA locking_mode=exclusive", null, null, null);
126 sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
127 if(sqlite3_exec(db, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('lockDummy', 'lockDummy', 'lockDummy', 'lockDummy')", null, null, null))
130 sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
134 result = SQLiteDatabase { db = db };
142 class SQLiteField : Field
147 public LinkElement<SQLiteField> link;
157 const String GetName()
165 int GetLength() { return length; }
180 class SQLiteDatabase : Database
183 AVLTree<const String> collations { };
187 sqlite3_exec(db, "PRAGMA locking_mode=normal", null, null, null);
188 // "Simply setting the locking-mode to NORMAL is not enough - locks are not released until the next time the database file is accessed."
189 sqlite3_exec(db, "SELECT COUNT(*) from eda_table_fields", null, null, null);
193 uint ObjectsCount(ObjectType type)
199 bool RenameObject(ObjectType type, const String name, const String rename)
205 bool DeleteObject(ObjectType type, const String name)
211 Table OpenTable(const String name, OpenOptions options)
215 int nRows = 0, nCols = 0;
217 SQLiteTable table = null;
218 if(options.type == tablesList)
221 strcpy(command, "SELECT name FROM sqlite_master WHERE type='table' AND name!='eda_table_fields';");
222 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
223 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
226 table._fields.Add(field);
228 else if(options.type == fieldsList)
232 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
233 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
235 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
237 table._fields.Add(field);
238 field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
240 table._fields.Add(field);
241 field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
243 table._fields.Add(field);
245 else if(options.type == tableRows)
247 bool addFields = false;
249 sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
250 /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
254 sqlite3_free_table(t);
256 sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
257 nCols = 0, nRows = 0;
258 /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
260 if((nCols || nRows) || options.create)
262 table = SQLiteTable { db = this, name = CopyString(name) };
265 table.mustCreate = true;
271 for(r = 1; r <= nRows; r++) // There should be only 1 row here
273 char * sql = t[nCols * r];
274 char * bracket = strchr(sql, '(');
286 int sqliteType = SQLITE_BLOB;
287 Class type = class(int);
291 while((ch = bracket[c++]))
293 if(ch == ',' || ch == ')')
296 for(d = c-1; d >= 0 && bracket[d] != ' '; d--);
298 memcpy(fieldName, bracket + start, d - start);
299 fieldName[d - start] = 0;
301 memcpy(dataType, bracket + d + 1, c - d - 2);
302 dataType[c - d - 2] = 0;
304 while(ch && bracket[c] == ' ') c++;
306 if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
307 else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
308 else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
309 else if(!strcmp(dataType, "BLOB")) { sqliteType = SQLITE_BLOB; type = class(char *); } //class(byte *);
311 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
312 fieldName, type.name, 0);
313 /*result = */sqlite3_exec(db, command, null, null, null);
316 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table._fields.count, sqliteType = sqliteType };
318 table._fields.Add(field);
321 if(!ch || ch == ')') break;
328 Table refTable = null;
329 sqlite3_stmt * statement;
331 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
332 /*result = */sqlite3_prepare_v2(db, command, -1, &statement, null);
334 while(sqlite3_step(statement) != SQLITE_DONE)
336 const char * fieldName = (const char *)sqlite3_column_text(statement, 0);
337 const char * typeName = (const char *)sqlite3_column_text(statement, 1);
338 int length = sqlite3_column_int(statement, 2);
340 int sqliteType = SQLITE_BLOB;
342 type.OnGetDataFromString(typeName);
346 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
347 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
348 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
349 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
350 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
351 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
352 sqliteType = SQLITE_INTEGER;
353 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
354 sqliteType = SQLITE_FLOAT;
355 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
356 sqliteType = SQLITE_TEXT;
359 if(strcmp(type.fullName, "CIString") && !collations.Find(type.fullName))
361 collations.Add(type.fullName);
362 sqlite3_create_collation_v2(table.db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
364 sqliteType = SQLITE_BLOB;
369 Table * fTable = (Table *)(intptr)eClass_GetProperty(type, "table");
370 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table._fields.count, sqliteType = sqliteType };
372 if(fTable) refTable = *fTable;
373 if(!table.primaryKey && refTable && !strcmp(refTable.name, table.name))
374 table.primaryKey = field;
376 table._fields.Add(field);
379 sqlite3_finalize(statement);
383 sqlite3_free_table(t);
392 sprintf(command, "BEGIN;");
393 result = sqlite3_exec(db, command, null, null, null);
395 PrintLn($"BEGIN FAILED!");
396 return result == SQLITE_OK;
403 sprintf(command, "COMMIT;");
404 result = sqlite3_exec(db, command, null, null, null);
406 PrintLn($"COMMIT FAILED!");
407 return result == SQLITE_OK;
410 bool CreateCustomFunction(const char * name, SQLCustomFunction customFunction)
413 Class cfClass = customFunction._class;
414 customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module);
415 if(customFunction.method)
417 String typeString = CopyString(customFunction.method.dataTypeString);
419 int count = TokenizeWith(typeString, sizeof(tokens)/sizeof(tokens[0]), tokens, "(,)", false);
421 bool variadic = false;
423 for(c = 0; c < count; c++)
426 bool pointer = false;
427 const String arg = tokens[c];
429 TrimLSpaces(tokens[c], tokens[c]);
430 if(strchr(arg, '*')) pointer = true;
432 // Using String for generic pointer...
433 type = class(String);
436 if((space = strchr(arg, ' '))) *space = 0;
437 if(!strcmp(arg, "void"))
439 else if(!strcmp(arg, "..."))
443 if(cfClass.templateParams.count)
445 ClassTemplateParameter p;
447 for(p = cfClass.templateParams.first; p; p = p.next, id++)
449 if(!strcmp(p.name, arg))
452 if(p && cfClass.templateArgs)
453 arg = cfClass.templateArgs[id].dataTypeString;
455 type = eSystem_FindClass(customFunction._class.module, arg);
457 type = eSystem_FindClass(customFunction._class.module.application, arg);
461 customFunction.returnType = type;
463 customFunction.args.Add(type);
469 // Variadic args don't make sense for SQL custom functions
470 // Note that different CIF must be prepared for different set of arguments
471 // ffi_prep_cif_var(&customFunction.cif, FFI_DEFAULT_ABI, args.count-1, rType, argTypes);
475 customFunction.rType = FFIGetType(customFunction.returnType, true);
476 customFunction.argTypes.Add((void *)&ffi_type_pointer); // This pointer for SQLCustomFunction object
477 for(a : customFunction.args) customFunction.argTypes.Add((void *)FFIGetType(a, false));
478 ffi_prep_cif(&customFunction.cif, FFI_DEFAULT_ABI, customFunction.argTypes.count, customFunction.rType, (ffi_type **) customFunction.argTypes.array);
479 result = sqlite3_create_function(db, name, customFunction.args.count, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null) == SQLITE_OK;
486 static class FFITypesHolder : Map<Class, String> { ~FFITypesHolder() { Free(); } }
487 FFITypesHolder structFFITypes { };
488 __attribute__((unused)) static Iterator dummy; // TOFIX: forward struct declaration issues on Clang
490 public ffi_type * FFIGetType(Class type, bool structByValue)
499 MapIterator<Class, String> it { map = structFFITypes };
500 ffi_type * ffiType = null;
501 if(it.Index(type, false))
502 ffiType = (void *)it.data;
507 Array<String> memberTypes { };
508 for(member = type.membersAndProperties.first; member; member = member.next)
510 if(!member.isProperty)
512 memberTypes.Add(FFIGetType(member.dataType
516 ffiType = new0 ffi_type[1];
517 ffiType->size = type.structSize;
518 ffiType->type = FFI_TYPE_STRUCT;
519 structFFITypes[type] = (void *)ffiType;
526 return &ffi_type_pointer;
532 if(!strcmp(type.dataTypeString, "float")) return &ffi_type_float;
533 else if(!strcmp(type.dataTypeString, "double")) return &ffi_type_double;
535 switch(type.typeSize)
537 case 1: return &ffi_type_uint8;
538 case 2: return &ffi_type_uint16;
539 case 4: return &ffi_type_uint32;
540 case 8: return &ffi_type_uint64;
544 return &ffi_type_void;
548 static SerialBuffer staticBuffer { };
549 void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values)
551 SQLCustomFunction sqlFunction = sqlite3_user_data(context);
553 /* // Simple 1 pointer param returning a string
554 void * p = sqlFunction.method.function(sqlFunction, sqlite3_value_text(values[0]));
555 sqlite3_result_text(context, p, strlen(p), SQLITE_TRANSIENT);
558 void * ret = &retData;
559 Array<String> args { size = sqlFunction.args.count + 1 };
560 Iterator<String> ffiArg { sqlFunction.argTypes };
561 Iterator<String> arg { args };
564 // this * for the SQLCustomFunction
565 args[0] = (void *)&sqlFunction;
567 // Get the arguments from SQLite
568 for(a : sqlFunction.args)
570 ffi_type * type = (ffi_type *)sqlFunction.argTypes[i+1];
579 void ** data = new void *[1];
580 args[i+1] = (void *)data;
581 if(a == class(String))
583 int numBytes = sqlite3_value_bytes(values[i]);
584 const char * text = (const char *)sqlite3_value_text(values[i]);
585 *(char **)data = text ? new byte[numBytes+1] : null;
587 memcpy(*(char **)data, text, numBytes+1);
591 SerialBuffer buffer = staticBuffer; //{ };
593 buffer._size = sqlite3_value_bytes(values[i]);
594 buffer._buffer = (byte *)sqlite3_value_text(values[i]);
595 //buffer._buffer = sqlite3_value_blob(curStatement);
596 buffer.count = buffer._size;
597 if(a.type == structClass)
598 *data = new byte[a.structSize];
599 ((void (*)(void *, void *, void *))(void *)a._vTbl[__ecereVMethodID_class_OnUnserialize])(a, (a.type == structClass) ? *data : data, buffer);
600 buffer._buffer = null;
609 if(type == &ffi_type_double || type == &ffi_type_float)
611 double d = sqlite3_value_double(values[i]);
614 double * data = new double[1];
615 args[i+1] = (void *)data;
620 float * data = new float[1];
621 args[i+1] = (void *)data;
631 int64 * data = new int64[1];
632 args[i+1] = (void *)data;
633 *data = sqlite3_value_int64(values[i]);
638 int * data = new int[1];
639 args[i+1] = (void *)data;
640 *data = sqlite3_value_int(values[i]);
645 short * data = new short[1];
647 args[i+1] = (void *)data;
648 value = sqlite3_value_int(values[i]);
650 *data = (short)value;
652 *(uint16 *)data = (uint16)value;
657 char * data = new char[1];
660 value = sqlite3_value_int(values[i]);
664 *(byte *)data = (byte)value;
674 if(sqlFunction.returnType && sqlFunction.returnType.type == structClass)
675 ret = new byte[sqlFunction.returnType.typeSize];
676 ffi_call(&sqlFunction.cif, (void *)sqlFunction.method.function, ret, args.array);
677 // Give SQLite the return value
678 if(sqlFunction.returnType)
680 ffi_type * type = sqlFunction.rType;
681 Class r = sqlFunction.returnType;
689 void * data = ret ? *(void **)ret : null;
690 if(r.type == structClass)
692 if(r == class(String))
695 sqlite3_result_text(context, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
697 sqlite3_result_text(context, null, 0, SQLITE_TRANSIENT);
701 SerialBuffer buffer { };
702 ((void (*)(void *, void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnSerialize])(r, data, buffer);
703 sqlite3_result_text(context, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
706 // Avoid destroying Strings for now... (Returning memory owned by the Custom Function)
707 ((void (*)(void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnFree])(r, data);
710 if(r.type == structClass)
718 if(type == &ffi_type_double || type == &ffi_type_float)
721 sqlite3_result_double(context, *(double *)ret);
723 sqlite3_result_double(context, (double)*(float *)ret);
730 sqlite3_result_int64(context, (sqlite3_int64)*(int64 *)ret);
733 sqlite3_result_int(context, *(int *)ret);
739 value = (int)*(short *)ret;
741 //value = (int)*(uint16 *)ret;
742 sqlite3_result_int(context, value);
749 value = (int)*(char *)ret;
751 //value = (int)*(byte *)ret;
752 sqlite3_result_int(context, value);
763 for(type : sqlFunction.args; arg.Next())
766 void * data = *(void **)arg.data;
767 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, data);
768 if(type.type == structClass)
777 class SQLiteTable : Table
782 LinkList<SQLiteField> _fields { };
783 char * specialStatement;
784 SQLiteField primaryKey;
785 FieldIndex * indexFields;
786 int indexFieldsCount;
789 Field AddField(const String fieldName, Class type, int length)
796 Table refTable = null;
797 Field idField = null;
800 if(FindField(fieldName)) return null;
802 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
803 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
804 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
805 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
806 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
807 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
809 strcpy(dataType, "INTEGER");
810 sqliteType = SQLITE_INTEGER;
812 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
814 strcpy(dataType, "REAL");
815 sqliteType = SQLITE_FLOAT;
817 else if(!strcmp(type.name, "CIString"))
819 strcpy(dataType, "TEXT");
820 sqliteType = SQLITE_BLOB;
822 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
824 strcpy(dataType, "TEXT");
825 sqliteType = SQLITE_TEXT;
829 //strcpy(dataType, "BLOB");
830 strcpy(dataType, "TEXT");
831 sqliteType = SQLITE_BLOB;
833 if(!db.collations.Find(type.fullName))
835 db.collations.Add(type.fullName);
836 result = sqlite3_create_collation_v2(db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
839 if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)) && type != class(eda::Id))
841 Table * table = (Table *)(intptr)eClass_GetProperty(type, "table");
842 if(table) refTable = *table;
845 if(primaryKey || refTable != this)
847 for(idField = refTable.firstField; idField; idField = idField.next)
848 if(eClass_IsDerived(type, idField.type)) break;
851 PrintLn("WARNING: field not yet created for class ", (String)type.name);
854 idField = primaryKey;
858 PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
864 if(sqliteType == SQLITE_BLOB)
866 if(!strcmp(type.name, "CIString"))
867 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE NOCASE);", name, fieldName, dataType);
869 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE '%s');", name, fieldName, dataType, type.fullName);
873 if(!idField && refTable == this)
874 sprintf(command, "CREATE TABLE `%s`(`%s` %s PRIMARY KEY);", name, fieldName, dataType);
876 sprintf(command, "CREATE TABLE `%s`(`%s` %s REFERENCES `%s`(`%s`));", name, fieldName, dataType, refTable.name, idField.name);
879 sprintf(command, "CREATE TABLE `%s`(`%s` %s);", name, fieldName, dataType);
880 result = sqlite3_exec(db.db, command, null, null, null);
881 if(result) return null;
886 if(sqliteType == SQLITE_BLOB)
888 if(!strcmp(type.name, "CIString"))
889 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE NOCASE;", name, fieldName, dataType);
891 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE `%s`;", name, fieldName, dataType, type.fullName);
895 if(!idField && refTable == this)
897 PrintLn($"WARNING: ALTER TABLE DOESN'T WORK WITH PRIMARY KEY FOR ", (String)name);
898 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s PRIMARY KEY;", name, fieldName, dataType);
901 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s REFERENCES `%s`(`%s`);", name, fieldName, dataType, refTable.name, idField.name);
904 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s;", name, fieldName, dataType);
905 result = sqlite3_exec(db.db, command, null, null, null);
906 if(result) return null;
909 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
910 fieldName, type.name, length);
911 result = sqlite3_exec(db.db, command, null, null, null);
913 field = { name = CopyString(fieldName), type = type, num = _fields.count, sqliteType = sqliteType };
916 if(!primaryKey && refTable == this)
921 Field FindField(const String name)
923 for(f : _fields; !strcmp(f.name, name))
927 if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
930 Table * tablePtr = (Table *)(intptr)eClass_GetProperty(f.type, "table");
931 if(tablePtr && *tablePtr == this)
940 bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init)
945 char indexName[4096];
948 indexFieldsCount = count;
949 indexFields = new FieldIndex[count];
950 memcpy(indexFields, fieldIndexes, count * sizeof(FieldIndex));
952 // TODO: USE CODED INDEX NAME INSTEAD?
953 strcpy(indexName, "index_");
954 strcat(indexName, name);
955 strcat(indexName, "_");
956 for(c = 0; c<count; c++)
958 if(fieldIndexes[c].field)
960 if(count == 1 && fieldIndexes[c].field == primaryKey)
962 strcat(indexName, fieldIndexes[c].field.name);
963 if(fieldIndexes[c].memberField)
965 strcat(indexName, ".");
966 strcat(indexName, fieldIndexes[c].memberField.name);
968 strcat(indexName, (fieldIndexes[c].order == ascending) ? "+" : "-");
974 sprintf(command, "CREATE INDEX IF NOT EXISTS `%s` ON `%s` (", indexName, name);
975 for(c = 0; c<count; c++)
977 char columnName[1024];
978 sprintf(columnName, "`%s` %s", fieldIndexes[c].field.name, (fieldIndexes[c].order == ascending) ? "ASC" : "DESC");
979 if(c > 0) strcat(command, ", ");
980 strcat(command, columnName);
982 strcat(command, ");");
983 result = sqlite3_exec(db.db, command, null, null, null);
985 return result == SQLITE_OK;
988 const String GetName()
993 Field GetFirstField()
995 return _fields.first;
998 Field GetPrimaryKey()
1003 uint GetFieldsCount()
1005 return _fields.count;
1015 sprintf(command, "SELECT COUNT(*) FROM `%s`;", name);
1016 result = sqlite3_get_table(db.db, command, &t, &nRows, &nCols, null);
1017 if(result == SQLITE_OK)
1019 rowCount = atoi(t[1]);
1020 sqlite3_free_table(t);
1025 // Returns true if not ordered by row ID
1026 bool GetIndexOrder(char * fullOrder, bool flip)
1028 if(!flip && (!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending)))
1030 strcpy(fullOrder, " ORDER BY ROWID");
1036 strcpy(fullOrder, " ORDER BY ");
1037 for(c = flip ? indexFieldsCount-1 : 0; flip ? (c >= 0) : (c < indexFieldsCount); flip ? c-- : c++)
1040 FieldIndex * fIndex = &indexFields[c];
1042 if(c) strcat(order, ", ");
1044 strcat(order, fIndex->field.name);
1046 if(fIndex->order == (flip ? ascending : descending)) strcat(order, " DESC");
1047 strcat(fullOrder, order);
1053 Container<Field> GetFields()
1055 return (Container<Field>)_fields;
1058 DriverRow CreateRow()
1061 sqlite3_stmt * statement;
1062 sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
1063 sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null;
1065 if(specialStatement)
1066 strcpy(command, specialStatement);
1070 /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
1071 sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
1072 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
1073 sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
1075 sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
1076 sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
1078 sprintf(command, "INSERT INTO `%s` (ROWID) VALUES(?);", name);
1079 sqlite3_prepare_v2(db.db, command, -1, &insertIDStmt, null);
1081 sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
1082 sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
1084 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
1085 sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
1087 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
1088 sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
1090 sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
1091 sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
1093 /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
1095 sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
1097 GetIndexOrder(order, false);
1098 sprintf(command, "SELECT ROWID, * FROM `%s`%s;", name, order);
1100 sqlite3_prepare_v2(db.db, command, -1, &statement, null);
1102 sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
1103 sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
1105 sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
1106 sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
1109 { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt,
1110 insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
1111 previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
1117 delete specialStatement;
1123 class SQLiteRow : DriverRow
1126 sqlite3_stmt * curStatement;
1128 sqlite3_stmt * defaultStatement;
1129 sqlite3_stmt * findStatement;
1130 sqlite3_stmt * prevFindStatement, * lastFindStatement;
1131 sqlite3_stmt * nextFindStatement;
1132 sqlite3_stmt * sysIDStatement;
1133 sqlite3_stmt * queryStatement;
1134 sqlite3_stmt * selectRowIDsStmt;
1135 sqlite3_stmt * setRowIDStmt;
1136 sqlite3_stmt * lastStatement;
1137 sqlite3_stmt * previousStatement;
1138 sqlite3_stmt * nextStatement;
1140 sqlite3_stmt * insertStatement;
1141 sqlite3_stmt * deleteStatement;
1142 sqlite3_stmt * updateStatement;
1143 sqlite3_stmt * insertIDStatement;
1147 // Because we use GoToSysID() and the sysIDStatement when searching for a primary key with Find(),
1148 // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
1159 if(defaultStatement) sqlite3_finalize(defaultStatement);
1160 if(findStatement) sqlite3_finalize(findStatement);
1161 if(prevFindStatement)sqlite3_finalize(prevFindStatement);
1162 if(lastFindStatement)sqlite3_finalize(lastFindStatement);
1163 if(nextFindStatement)sqlite3_finalize(nextFindStatement);
1164 if(sysIDStatement) sqlite3_finalize(sysIDStatement);
1165 if(insertStatement) sqlite3_finalize(insertStatement);
1166 if(deleteStatement) sqlite3_finalize(deleteStatement);
1167 if(updateStatement) sqlite3_finalize(updateStatement);
1168 if(queryStatement) sqlite3_finalize(queryStatement);
1169 if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
1170 if(setRowIDStmt) sqlite3_finalize(setRowIDStmt);
1171 if(previousStatement)sqlite3_finalize(previousStatement);
1172 if(nextStatement) sqlite3_finalize(nextStatement);
1173 if(lastStatement) sqlite3_finalize(lastStatement);
1174 if(insertIDStatement) sqlite3_finalize(insertIDStatement);
1177 bool Select(MoveOptions move)
1180 bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
1182 curStatement = defaultStatement;
1187 sqlite3_reset(curStatement);
1188 result = sqlite3_step(curStatement);
1189 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1190 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1191 rowID = sqlite3_column_int64(curStatement, 0);
1196 sqlite3_reset(curStatement);
1197 curStatement = lastStatement;
1198 result = sqlite3_step(curStatement);
1199 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1200 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1201 rowID = sqlite3_column_int64(curStatement, 0);
1209 // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
1210 if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) ||
1211 (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
1213 result = sqlite3_step(curStatement);
1214 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1215 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1216 rowID = sqlite3_column_int64(curStatement, 0);
1218 else if(curStatement == prevFindStatement || curStatement == findStatement || curStatement == nextFindStatement || curStatement == lastFindStatement)
1222 int bindId = findBindId;
1223 sqlite3_reset((move == next) ? nextFindStatement : prevFindStatement);
1224 BindCursorData((move == next) ? nextFindStatement : prevFindStatement, move,
1225 (move == next && (!tbl.indexFields || (tbl.indexFieldsCount == 1 && tbl.indexFields[0].field == tbl.primaryKey && tbl.indexFields[0].order == ascending))) ? false : true, &bindId);
1226 sqlite3_reset(curStatement);
1227 curStatement = (move == next) ? nextFindStatement : prevFindStatement;
1228 result = sqlite3_step(curStatement);
1229 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1230 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1231 rowID = sqlite3_column_int64(curStatement, 0);
1235 sqlite3_reset((move == next) ? findStatement : lastFindStatement);
1236 sqlite3_reset(curStatement);
1237 curStatement = (move == next) ? findStatement : lastFindStatement;
1238 result = sqlite3_step(curStatement);
1239 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1240 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1241 rowID = sqlite3_column_int64(curStatement, 0);
1246 sqlite3_reset(curStatement);
1247 curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
1248 sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
1249 result = sqlite3_step(curStatement);
1250 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1251 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1252 rowID = sqlite3_column_int64(curStatement, 0);
1257 sqlite3_reset(curStatement);
1267 bool Query(const char * queryString)
1273 sqlite3_reset(curStatement);
1276 sqlite3_finalize(queryStatement);
1277 queryStatement = null;
1282 result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
1285 curStatement = queryStatement;
1286 if(!strchr(queryString, '?'))
1288 result = sqlite3_step(queryStatement);
1290 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1291 if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
1293 rowID = sqlite3_column_int64(queryStatement, 0);
1298 printf("SQLite Query Error: %s\n", queryString);
1303 curStatement = null;
1307 bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
1310 Class dataType = fld.type;
1311 SerialBuffer buffer = null;
1312 switch(fld.sqliteType)
1314 case SQLITE_INTEGER:
1316 switch(dataType.typeSize)
1319 result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1322 result = sqlite3_bind_int(statement, pos, *(int *)data);
1328 value = (int)*(short *)data;
1330 value = (int)*(uint16 *)data;
1331 result = sqlite3_bind_int(statement, pos, value);
1338 value = (int)*(char *)data;
1340 value = (int)*(byte *)data;
1341 result = sqlite3_bind_int(statement, pos, value);
1349 if(dataType.typeSize == 8)
1350 result = sqlite3_bind_double(statement, pos, *(double *)data);
1352 result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1358 result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1360 result = sqlite3_bind_null(statement, pos);
1368 buffer = SerialBuffer { };
1369 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnSerialize])(dataType, data, buffer);
1370 result = sqlite3_bind_text(statement, pos, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1373 result = sqlite3_bind_null(statement, pos);
1378 *bufferOut = buffer;
1384 void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1386 if(move == next || move == previous)
1388 // Where clauses for index
1392 bool gotPrimaryKey = false;
1394 strcatf(command, " AND (");
1395 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1398 FieldIndex * fIndex = &tbl.indexFields[c];
1402 strcat(where, fIndex->field.name);
1403 strcat(where, "` ");
1404 strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1405 strcat(where, " ? OR (");
1406 strcat(where, fIndex->field.name);
1407 if(fIndex->field == tbl.primaryKey)
1408 gotPrimaryKey = true;
1409 strcat(where, " = ? AND (");
1410 strcat(command, where);
1412 strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1413 for(c = 0; c < tbl.indexFieldsCount; c++)
1414 strcat(command, "))");
1417 strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1421 void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1423 if(move == next || move == previous)
1425 // The binds for the Extra ordering Where clauses
1429 /* // Code to not rely on curStatement being set up
1430 SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1431 dataRow.GoToSysID((uint)rowID);
1433 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1435 FieldIndex * fIndex = &tbl.indexFields[c];
1437 SQLiteField fld = (SQLiteField)fIndex->field;
1438 Class type = fld.type;
1440 SerialBuffer buffer;
1442 if(type.type == unitClass && !type.typeSize)
1444 Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1448 if(type.type == structClass)
1450 data = (int64)(intptr)new0 byte[type.structSize];
1451 dataPtr = (void *)(intptr)data;
1453 // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1454 ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)(intptr)data : &data);
1455 if(type.type == normalClass || type.type == noHeadClass)
1456 dataPtr = (void *)(intptr)data;
1459 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1460 // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
1461 // Reuse the buffer for Blobs...
1462 if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1464 sqlite3_bind_text(stmt, (*bindId)++, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1468 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1470 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, dataPtr);
1475 // Bind for the rowid
1476 sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1480 bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1482 char order[1024], command[2048];
1485 sqlite3_stmt * stmt = null;
1488 if(fld == tbl.primaryKey)
1490 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1491 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1492 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1493 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1494 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1495 result = GoToSysID(*(int *)data);
1501 useIndex = tbl.GetIndexOrder(order, false);
1503 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1504 AddCursorWhereClauses(command, move, useIndex);
1505 strcat(command, order);
1506 strcat(command, ";");
1507 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1508 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1509 BindCursorData(stmt, move, useIndex, &bindId);
1511 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1512 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1513 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1514 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1515 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1516 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1518 curStatement = findStatement = stmt;
1519 findBindId = bindId;
1521 // For going back to forward find
1523 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1524 AddCursorWhereClauses(command, next, useIndex);
1525 strcat(command, order);
1526 strcat(command, ";");
1527 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1528 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1529 nextFindStatement = stmt;
1532 tbl.GetIndexOrder(order, true);
1533 // For tracing back finds
1535 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1536 AddCursorWhereClauses(command, previous, true);
1537 strcat(command, order);
1538 strcat(command, ";");
1539 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1540 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1541 prevFindStatement = stmt;
1543 // For tracing back from last
1545 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1546 strcat(command, order);
1547 strcat(command, ";");
1548 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1549 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1550 lastFindStatement = stmt;
1552 result = sqlite3_step(findStatement);
1554 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1558 sqlite3_reset(findStatement);
1561 rowID = sqlite3_column_int64(findStatement, 0);
1565 bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1568 for(c = 0; c < numFields; c++) \
1570 FieldFindData * fieldFind = &findData[c]; \
1571 SQLiteField sqlFld = (SQLiteField)findData->field; \
1572 Class dataType = sqlFld.type; \
1573 BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1578 char criterias[4096], command[4096], order[1024];
1582 sqlite3_stmt * stmt = null;
1586 sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1587 for(c = 0; c < numFields; c++)
1589 FieldFindData * fieldFind = &findData[c];
1591 if(c) strcat(criterias, " AND `");
1592 strcat(criterias, fieldFind->field.name);
1593 strcat(criterias, "` = ?");
1596 useIndex = tbl.GetIndexOrder(order, false);
1597 // Basic Find (multiple)
1598 strcpy(command, criterias);
1599 AddCursorWhereClauses(command, move, useIndex);
1600 strcat(command, order);
1601 strcat(command, ";");
1602 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1604 BindCursorData(stmt, move, useIndex, &bindId);
1606 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1607 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1608 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1609 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1610 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1611 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1613 curStatement = findStatement = stmt;
1614 findBindId = bindId;
1616 // For tracing back forward finds
1618 strcpy(command, criterias);
1619 AddCursorWhereClauses(command, previous, true);
1620 strcat(command, order);
1621 strcat(command, ";");
1622 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1624 nextFindStatement = stmt;
1627 tbl.GetIndexOrder(order, true);
1628 // For tracing back finds
1630 strcpy(command, criterias);
1631 AddCursorWhereClauses(command, next, useIndex);
1632 strcat(command, order);
1633 strcat(command, ";");
1634 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1636 prevFindStatement = stmt;
1638 // For tracing back from last
1640 strcpy(command, criterias);
1641 strcat(command, order);
1642 strcat(command, ";");
1643 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1645 lastFindStatement = stmt;
1647 result = sqlite3_step(findStatement);
1648 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1652 sqlite3_reset(findStatement);
1655 rowID = sqlite3_column_int64(findStatement, 0);
1661 bool Synch(DriverRow to)
1663 SQLiteRow rowTo = (SQLiteRow)to;
1664 if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1665 return GoToSysID((uint)rowTo.rowID);
1672 //char command[1024];
1673 //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1674 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1677 sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1678 result = sqlite3_step(insertIDStatement);
1681 result = sqlite3_step(insertStatement);
1682 if(result == SQLITE_DONE) // if(result == SQLITE_OK)
1684 rowID = sqlite3_last_insert_rowid(tbl.db.db);
1685 if(rowID > MAXDWORD)
1687 int64 lastID = tbl.lastID;
1689 sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1693 result = sqlite3_step(selectRowIDsStmt);
1694 if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1695 id = sqlite3_column_int64(selectRowIDsStmt, 0);
1696 if(id - lastID > 1) break;
1699 sqlite3_reset(selectRowIDsStmt);
1701 sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1704 sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1705 result = sqlite3_step(setRowIDStmt);
1706 sqlite3_reset(setRowIDStmt);
1708 sqlite3_reset(id ? insertIDStatement : insertStatement);
1709 curStatement = sysIDStatement;
1711 sqlite3_reset(curStatement);
1712 sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1713 result = sqlite3_step(curStatement);
1714 done = false; // Make sure 'nil' is false
1717 sqlite3_reset(insertStatement);
1724 //char command[1024];
1725 //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1726 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1727 sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1728 result = sqlite3_step(deleteStatement);
1729 sqlite3_reset(deleteStatement);
1731 return result == SQLITE_OK || result == SQLITE_DONE;
1734 bool GetData(Field fld, typed_object &data)
1736 SQLiteField sqlFld = (SQLiteField)fld;
1737 int num = sqlFld.num + 1;
1738 Class dataType = sqlFld.type;
1741 switch(sqlFld.sqliteType)
1743 case SQLITE_INTEGER:
1745 switch(dataType.typeSize)
1748 if(fld == tbl.primaryKey)
1749 *(int64 *)data = rowID;
1751 *(int64 *)data = sqlite3_column_int64(curStatement, num);
1754 if(fld == tbl.primaryKey)
1755 *(int *)data = (int)(uint)rowID;
1757 *(int *)data = sqlite3_column_int(curStatement, num);
1762 if(fld == tbl.primaryKey)
1763 value = (int)(uint)rowID;
1765 value = sqlite3_column_int(curStatement, num);
1767 *(short *)data = (short)value;
1769 *(uint16 *)data = (uint16)value;
1775 if(fld == tbl.primaryKey)
1776 value = (int)(uint)rowID;
1778 value = sqlite3_column_int(curStatement, num);
1780 *(char *)data = (char)value;
1782 *(byte *)data = (byte)value;
1790 double d = sqlite3_column_double(curStatement, num);
1791 if(dataType.typeSize == 8)
1792 *(double *)data = d;
1794 *(float *)data = (float)d;
1799 int numBytes = sqlite3_column_bytes(curStatement, num);
1800 const char * text = (const char *)sqlite3_column_text(curStatement, num);
1801 *(char **)data = text ? new byte[numBytes+1] : null;
1803 memcpy(*(char **)data, text, numBytes+1);
1808 SerialBuffer buffer { };
1809 //buffer._buffer = sqlite3_column_blob(curStatement, num);
1810 buffer._size = sqlite3_column_bytes(curStatement, num);
1811 buffer._buffer = (byte *)sqlite3_column_text(curStatement, num);
1812 buffer.count = buffer._size;
1814 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnUnserialize])(dataType, data, buffer);
1816 buffer._buffer = null;
1824 bool SetData(Field fld, typed_object data)
1826 SQLiteField sqlFld = (SQLiteField)fld;
1831 sqlite3_finalize(updateStatement);
1832 sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1833 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1834 sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1835 BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1836 result = sqlite3_step(updateStatement);
1837 sqlite3_reset(updateStatement);
1838 if(fld == tbl.primaryKey)
1839 rowID = *(uint *)data;
1840 return result == SQLITE_DONE;
1845 return (int)(uint)rowID;
1848 bool GoToSysID(uint id)
1850 //char command[1024];
1854 //sqlite3_finalize(statement);
1855 //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1856 //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1860 sqlite3_reset(curStatement);
1862 curStatement = sysIDStatement;
1863 sqlite3_reset(sysIDStatement);
1864 sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1865 result = sqlite3_step(curStatement);
1866 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1867 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1871 bool SetQueryParam(int paramID, int value)
1874 if(curStatement != queryStatement)
1876 if(curStatement) sqlite3_reset(curStatement);
1877 curStatement = queryStatement;
1879 sqlite3_reset(queryStatement);
1880 result = sqlite3_bind_int(queryStatement, paramID, value);
1884 bool SetQueryParam64(int paramID, int64 value)
1887 if(curStatement != queryStatement)
1889 if(curStatement) sqlite3_reset(curStatement);
1890 curStatement = queryStatement;
1892 sqlite3_reset(queryStatement);
1893 result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1897 bool SetQueryParamText(int paramID, const char * data)
1900 if(curStatement != queryStatement)
1902 if(curStatement) sqlite3_reset(curStatement);
1903 curStatement = queryStatement;
1905 sqlite3_reset(queryStatement);
1907 result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1909 result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1913 bool SetQueryParamObject(int paramID, const void * data, Class type)
1916 if(curStatement != queryStatement)
1918 if(curStatement) sqlite3_reset(curStatement);
1919 curStatement = queryStatement;
1921 sqlite3_reset(queryStatement);
1923 SerialBuffer buffer { };
1924 ((void (*)(void *, const void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnSerialize])(type, data, buffer);
1925 result = sqlite3_bind_text(queryStatement, paramID, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1931 bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1933 if(curStatement != queryStatement)
1935 if(curStatement) sqlite3_reset(curStatement);
1936 curStatement = queryStatement;
1938 sqlite3_reset(queryStatement);
1939 return BindData(queryStatement, pos, fld, data, null);
1942 /*char * GetExtraColumn(int paramID)
1944 SQLiteField lastFld = tbl._fields.last;
1945 return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1947 const char * GetColumn(int paramID)
1949 return (const char *)sqlite3_column_text(curStatement, paramID);