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)))
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);
1300 curStatement = null;
1304 bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
1307 Class dataType = fld.type;
1308 SerialBuffer buffer = null;
1309 switch(fld.sqliteType)
1311 case SQLITE_INTEGER:
1313 switch(dataType.typeSize)
1316 result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1319 result = sqlite3_bind_int(statement, pos, *(int *)data);
1325 value = (int)*(short *)data;
1327 value = (int)*(uint16 *)data;
1328 result = sqlite3_bind_int(statement, pos, value);
1335 value = (int)*(char *)data;
1337 value = (int)*(byte *)data;
1338 result = sqlite3_bind_int(statement, pos, value);
1346 if(dataType.typeSize == 8)
1347 result = sqlite3_bind_double(statement, pos, *(double *)data);
1349 result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1355 result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1357 result = sqlite3_bind_null(statement, pos);
1365 buffer = SerialBuffer { };
1366 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnSerialize])(dataType, data, buffer);
1367 result = sqlite3_bind_text(statement, pos, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1370 result = sqlite3_bind_null(statement, pos);
1375 *bufferOut = buffer;
1381 void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1383 if(move == next || move == previous)
1385 // Where clauses for index
1389 bool gotPrimaryKey = false;
1391 strcatf(command, " AND (");
1392 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1395 FieldIndex * fIndex = &tbl.indexFields[c];
1399 strcat(where, fIndex->field.name);
1400 strcat(where, "` ");
1401 strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1402 strcat(where, " ? OR (");
1403 strcat(where, fIndex->field.name);
1404 if(fIndex->field == tbl.primaryKey)
1405 gotPrimaryKey = true;
1406 strcat(where, " = ? AND (");
1407 strcat(command, where);
1409 strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1410 for(c = 0; c < tbl.indexFieldsCount; c++)
1411 strcat(command, "))");
1414 strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1418 void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1420 if(move == next || move == previous)
1422 // The binds for the Extra ordering Where clauses
1426 /* // Code to not rely on curStatement being set up
1427 SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1428 dataRow.GoToSysID((uint)rowID);
1430 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1432 FieldIndex * fIndex = &tbl.indexFields[c];
1434 SQLiteField fld = (SQLiteField)fIndex->field;
1435 Class type = fld.type;
1437 SerialBuffer buffer;
1439 if(type.type == unitClass && !type.typeSize)
1441 Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1445 if(type.type == structClass)
1447 data = (int64)(intptr)new0 byte[type.structSize];
1448 dataPtr = (void *)(intptr)data;
1450 // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1451 ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)(intptr)data : &data);
1452 if(type.type == normalClass || type.type == noHeadClass)
1453 dataPtr = (void *)(intptr)data;
1456 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1457 // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
1458 // Reuse the buffer for Blobs...
1459 if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1461 sqlite3_bind_text(stmt, (*bindId)++, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1465 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1467 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, dataPtr);
1472 // Bind for the rowid
1473 sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1477 bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1479 char order[1024], command[2048];
1482 sqlite3_stmt * stmt = null;
1485 if(fld == tbl.primaryKey)
1487 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1488 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1489 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1490 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1491 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1492 result = GoToSysID(*(int *)data);
1498 useIndex = tbl.GetIndexOrder(order, false);
1500 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1501 AddCursorWhereClauses(command, move, useIndex);
1502 strcat(command, order);
1503 strcat(command, ";");
1504 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1505 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1506 BindCursorData(stmt, move, useIndex, &bindId);
1508 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1509 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1510 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1511 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1512 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1513 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1515 curStatement = findStatement = stmt;
1516 findBindId = bindId;
1518 // For going back to forward find
1520 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1521 AddCursorWhereClauses(command, next, useIndex);
1522 strcat(command, order);
1523 strcat(command, ";");
1524 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1525 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1526 nextFindStatement = stmt;
1529 tbl.GetIndexOrder(order, true);
1530 // For tracing back finds
1532 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1533 AddCursorWhereClauses(command, previous, true);
1534 strcat(command, order);
1535 strcat(command, ";");
1536 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1537 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1538 prevFindStatement = stmt;
1540 // For tracing back from last
1542 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1543 strcat(command, order);
1544 strcat(command, ";");
1545 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1546 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1547 lastFindStatement = stmt;
1549 result = sqlite3_step(findStatement);
1551 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1555 sqlite3_reset(findStatement);
1558 rowID = sqlite3_column_int64(findStatement, 0);
1562 bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1565 for(c = 0; c < numFields; c++) \
1567 FieldFindData * fieldFind = &findData[c]; \
1568 SQLiteField sqlFld = (SQLiteField)findData->field; \
1569 Class dataType = sqlFld.type; \
1570 BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1575 char criterias[4096], command[4096], order[1024];
1579 sqlite3_stmt * stmt = null;
1583 sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1584 for(c = 0; c < numFields; c++)
1586 FieldFindData * fieldFind = &findData[c];
1588 if(c) strcat(criterias, " AND `");
1589 strcat(criterias, fieldFind->field.name);
1590 strcat(criterias, "` = ?");
1593 useIndex = tbl.GetIndexOrder(order, false);
1594 // Basic Find (multiple)
1595 strcpy(command, criterias);
1596 AddCursorWhereClauses(command, move, useIndex);
1597 strcat(command, order);
1598 strcat(command, ";");
1599 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1601 BindCursorData(stmt, move, useIndex, &bindId);
1603 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1604 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1605 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1606 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1607 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1608 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1610 curStatement = findStatement = stmt;
1611 findBindId = bindId;
1613 // For tracing back forward finds
1615 strcpy(command, criterias);
1616 AddCursorWhereClauses(command, previous, true);
1617 strcat(command, order);
1618 strcat(command, ";");
1619 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1621 nextFindStatement = stmt;
1624 tbl.GetIndexOrder(order, true);
1625 // For tracing back finds
1627 strcpy(command, criterias);
1628 AddCursorWhereClauses(command, next, useIndex);
1629 strcat(command, order);
1630 strcat(command, ";");
1631 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1633 prevFindStatement = stmt;
1635 // For tracing back from last
1637 strcpy(command, criterias);
1638 strcat(command, order);
1639 strcat(command, ";");
1640 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1642 lastFindStatement = stmt;
1644 result = sqlite3_step(findStatement);
1645 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1649 sqlite3_reset(findStatement);
1652 rowID = sqlite3_column_int64(findStatement, 0);
1658 bool Synch(DriverRow to)
1660 SQLiteRow rowTo = (SQLiteRow)to;
1661 if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1662 return GoToSysID((uint)rowTo.rowID);
1669 //char command[1024];
1670 //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1671 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1674 sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1675 result = sqlite3_step(insertIDStatement);
1678 result = sqlite3_step(insertStatement);
1679 if(result == SQLITE_DONE) // if(result == SQLITE_OK)
1681 rowID = sqlite3_last_insert_rowid(tbl.db.db);
1682 if(rowID > MAXDWORD)
1684 int64 lastID = tbl.lastID;
1686 sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1690 result = sqlite3_step(selectRowIDsStmt);
1691 if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1692 id = sqlite3_column_int64(selectRowIDsStmt, 0);
1693 if(id - lastID > 1) break;
1696 sqlite3_reset(selectRowIDsStmt);
1698 sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1701 sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1702 result = sqlite3_step(setRowIDStmt);
1703 sqlite3_reset(setRowIDStmt);
1705 sqlite3_reset(id ? insertIDStatement : insertStatement);
1706 curStatement = sysIDStatement;
1708 sqlite3_reset(curStatement);
1709 sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1710 result = sqlite3_step(curStatement);
1711 done = false; // Make sure 'nil' is false
1714 sqlite3_reset(insertStatement);
1721 //char command[1024];
1722 //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1723 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1724 sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1725 result = sqlite3_step(deleteStatement);
1726 sqlite3_reset(deleteStatement);
1728 return result == SQLITE_OK || result == SQLITE_DONE;
1731 bool GetData(Field fld, typed_object &data)
1733 SQLiteField sqlFld = (SQLiteField)fld;
1734 int num = sqlFld.num + 1;
1735 Class dataType = sqlFld.type;
1738 switch(sqlFld.sqliteType)
1740 case SQLITE_INTEGER:
1742 switch(dataType.typeSize)
1745 if(fld == tbl.primaryKey)
1746 *(int64 *)data = rowID;
1748 *(int64 *)data = sqlite3_column_int64(curStatement, num);
1751 if(fld == tbl.primaryKey)
1752 *(int *)data = (int)(uint)rowID;
1754 *(int *)data = sqlite3_column_int(curStatement, num);
1759 if(fld == tbl.primaryKey)
1760 value = (int)(uint)rowID;
1762 value = sqlite3_column_int(curStatement, num);
1764 *(short *)data = (short)value;
1766 *(uint16 *)data = (uint16)value;
1772 if(fld == tbl.primaryKey)
1773 value = (int)(uint)rowID;
1775 value = sqlite3_column_int(curStatement, num);
1777 *(char *)data = (char)value;
1779 *(byte *)data = (byte)value;
1787 double d = sqlite3_column_double(curStatement, num);
1788 if(dataType.typeSize == 8)
1789 *(double *)data = d;
1791 *(float *)data = (float)d;
1796 int numBytes = sqlite3_column_bytes(curStatement, num);
1797 const char * text = (const char *)sqlite3_column_text(curStatement, num);
1798 *(char **)data = text ? new byte[numBytes+1] : null;
1800 memcpy(*(char **)data, text, numBytes+1);
1805 SerialBuffer buffer { };
1806 //buffer._buffer = sqlite3_column_blob(curStatement, num);
1807 buffer._size = sqlite3_column_bytes(curStatement, num);
1808 buffer._buffer = (byte *)sqlite3_column_text(curStatement, num);
1809 buffer.count = buffer._size;
1811 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnUnserialize])(dataType, data, buffer);
1813 buffer._buffer = null;
1821 bool SetData(Field fld, typed_object data)
1823 SQLiteField sqlFld = (SQLiteField)fld;
1828 sqlite3_finalize(updateStatement);
1829 sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1830 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1831 sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1832 BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1833 result = sqlite3_step(updateStatement);
1834 sqlite3_reset(updateStatement);
1835 if(fld == tbl.primaryKey)
1836 rowID = *(uint *)data;
1837 return result == SQLITE_DONE;
1842 return (int)(uint)rowID;
1845 bool GoToSysID(uint id)
1847 //char command[1024];
1851 //sqlite3_finalize(statement);
1852 //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1853 //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1857 sqlite3_reset(curStatement);
1859 curStatement = sysIDStatement;
1860 sqlite3_reset(sysIDStatement);
1861 sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1862 result = sqlite3_step(curStatement);
1863 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1864 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1868 bool SetQueryParam(int paramID, int value)
1871 if(curStatement != queryStatement)
1873 if(curStatement) sqlite3_reset(curStatement);
1874 curStatement = queryStatement;
1876 sqlite3_reset(queryStatement);
1877 result = sqlite3_bind_int(queryStatement, paramID, value);
1881 bool SetQueryParam64(int paramID, int64 value)
1884 if(curStatement != queryStatement)
1886 if(curStatement) sqlite3_reset(curStatement);
1887 curStatement = queryStatement;
1889 sqlite3_reset(queryStatement);
1890 result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1894 bool SetQueryParamText(int paramID, const char * data)
1897 if(curStatement != queryStatement)
1899 if(curStatement) sqlite3_reset(curStatement);
1900 curStatement = queryStatement;
1902 sqlite3_reset(queryStatement);
1904 result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1906 result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1910 bool SetQueryParamObject(int paramID, const void * data, Class type)
1913 if(curStatement != queryStatement)
1915 if(curStatement) sqlite3_reset(curStatement);
1916 curStatement = queryStatement;
1918 sqlite3_reset(queryStatement);
1920 SerialBuffer buffer { };
1921 ((void (*)(void *, const void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnSerialize])(type, data, buffer);
1922 result = sqlite3_bind_text(queryStatement, paramID, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1928 bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1930 if(curStatement != queryStatement)
1932 if(curStatement) sqlite3_reset(curStatement);
1933 curStatement = queryStatement;
1935 sqlite3_reset(queryStatement);
1936 return BindData(queryStatement, pos, fld, data, null);
1939 /*char * GetExtraColumn(int paramID)
1941 SQLiteField lastFld = tbl._fields.last;
1942 return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1944 const char * GetColumn(int paramID)
1946 return (const char *)sqlite3_column_text(curStatement, paramID);