2 public import static "ecere"
3 public import static "EDA"
19 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, void * data1, int count2, void * data2)
45 if(type.type == normalClass || type.type == noHeadClass)
47 Instance inst1, inst2;
49 SerialBuffer buffer1 { size = count1, count = count1, buffer = data1 };
50 SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
52 type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst1, buffer1);
53 type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst2, buffer2);
55 result = 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 = data1 };
70 SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
72 inst1 = new0 byte[type.structSize];
73 inst2 = new0 byte[type.structSize];
74 type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst1, buffer1);
75 type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst2, buffer2);
77 result = type._vTbl[__ecereVMethodID_class_OnCompare](type, inst1, inst2);
79 buffer1.buffer = null;
80 buffer2.buffer = null;
88 return type._vTbl[__ecereVMethodID_class_OnCompare](type, data1, data2);
91 public class SQLiteStaticLink { } // Until .imp generation is fixed
93 class SQLiteDataSource : DataSourceDriver
95 class_property(name) = "SQLite";
97 OldList listDatabases;
100 String BuildLocator(DataSource ds)
102 return CopyString(ds.host);
105 uint GetDatabasesCount()
107 return databasesCount;
115 bool Connect(const String locator)
118 path = CopyString(locator);
119 // TODO, use user name and password for local security?
120 // TODO, open ds in read or write mode
124 FileListing listing { path, "sqlite" };
126 while(listing.Find())
133 bool RenameDatabase(const String name, const String rename)
135 if(name && rename && path && FileExists(path))
138 path = MakeDatabasePath(name);
143 repath = MakeDatabasePath(rename);
144 renamed = RenameFile(path, repath);
154 bool DeleteDatabase(const String name)
156 if(path && FileExists(path))
159 String path = MakeDatabasePath(name);
160 deleted = DeleteFile(path); // delete file seems to return true even if the file does not exist
168 virtual String MakeDatabasePath(const String name)
172 char build[MAX_LOCATION];
173 strcpy(build, path ? path : "");
174 PathCat(build, name);
175 ChangeExtension(build, "sqlite", build);
176 return CopyString(build);
181 Database OpenDatabase(const String name, CreateOptions createOptions, DataSource ds)
183 Database result = null;
186 String path = MakeDatabasePath(name);
189 // sqlite3_open(path, &db);
190 // sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY /*SQLITE_OPEN_READWRITE*/ /*SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE*/, null );
192 if(sqlite3_open_v2(path, &db, (createOptions == readOnly) ? SQLITE_OPEN_READONLY :
193 (SQLITE_OPEN_READWRITE | ((createOptions == create) ? SQLITE_OPEN_CREATE : 0)), null))
195 // fprintf(stderr, "%s\n", s); // interesting
196 printf($"EDASQLite: Can't open database (%s): %s\n", path, sqlite3_errmsg(db));
202 sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);");
203 sqlite3_exec(db, command, null, null, null);
205 result = SQLiteDatabase { db = db };
213 class SQLiteField : Field
218 public LinkElement<SQLiteField> link;
236 int GetLength() { return length; }
251 class SQLiteDatabase : Database
254 AVLTree<String> collations { };
261 uint ObjectsCount(ObjectType type)
267 bool RenameObject(ObjectType type, const String name, const String rename)
273 bool DeleteObject(ObjectType type, const String name)
279 Table OpenTable(const String name, OpenOptions options)
283 int nRows = 0, nCols = 0;
285 SQLiteTable table = null;
286 if(options.type == tablesList)
289 strcpy(command, "SELECT name FROM sqlite_master WHERE type='table' AND name!='eda_table_fields';");
290 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
291 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
294 table.fields.Add(field);
296 else if(options.type == fieldsList)
300 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
301 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
303 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
305 table.fields.Add(field);
306 field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
308 table.fields.Add(field);
309 field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
311 table.fields.Add(field);
313 else if(options.type == tableRows)
315 bool addFields = false;
317 sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
318 result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
322 sqlite3_free_table(t);
324 sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
325 nCols = 0, nRows = 0;
326 result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
328 if((nCols || nRows) || options.create)
330 table = SQLiteTable { db = this, name = CopyString(name) };
333 table.mustCreate = true;
339 for(r = 1; r <= nRows; r++) // There should be only 1 row here
341 char * sql = t[nCols * r];
342 char * bracket = strchr(sql, '(');
354 int sqliteType = SQLITE_BLOB;
355 Class type = class(int);
359 while((ch = bracket[c++]))
361 if(ch == ',' || ch == ')')
364 for(d = c-1; d >= 0 && bracket[d] != ' '; d--);
366 memcpy(fieldName, bracket + start, d - start);
367 fieldName[d - start] = 0;
369 memcpy(dataType, bracket + d + 1, c - d - 2);
370 dataType[c - d - 2] = 0;
372 while(ch && bracket[c] == ' ') c++;
374 if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
375 else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
376 else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
377 else if(!strcmp(dataType, "BLOB")) { sqliteType = SQLITE_BLOB; type = class(char *); } //class(byte *);
379 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
380 fieldName, type.name, 0);
381 result = sqlite3_exec(db, command, null, null, null);
384 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table.fields.count, sqliteType = sqliteType };
386 table.fields.Add(field);
389 if(!ch || ch == ')') break;
396 Table refTable = null;
397 sqlite3_stmt * statement;
399 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
400 result = sqlite3_prepare_v2(db, command, -1, &statement, null);
402 while(sqlite3_step(statement) != SQLITE_DONE)
404 char * fieldName = sqlite3_column_text(statement, 0);
405 char * typeName = sqlite3_column_text(statement, 1);
406 int length = sqlite3_column_int(statement, 2);
408 int sqliteType = SQLITE_BLOB;
410 ((Class)(&type)).OnGetDataFromString(typeName); // TODO: THIS REQUIRES A FIX SOMEWHERE ELSE
414 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
415 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
416 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
417 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
418 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
419 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
420 sqliteType = SQLITE_INTEGER;
421 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
422 sqliteType = SQLITE_FLOAT;
423 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
424 sqliteType = SQLITE_TEXT;
427 if(strcmp(type.fullName, "CIString") && !collations.Find(type.fullName))
429 collations.Add(type.fullName);
430 sqlite3_create_collation_v2(table.db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
432 sqliteType = SQLITE_BLOB;
437 Table * fTable = (Table *)eClass_GetProperty(type, "table");
438 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table.fields.count, sqliteType = sqliteType };
440 if(fTable) refTable = *fTable;
441 if(!table.primaryKey && refTable && !strcmp(refTable.name, table.name))
442 table.primaryKey = field;
444 table.fields.Add(field);
447 sqlite3_finalize(statement);
451 sqlite3_free_table(t);
460 sprintf(command, "BEGIN;");
461 result = sqlite3_exec(db, command, null, null, null);
463 PrintLn($"BEGIN FAILED!");
464 return result == SQLITE_OK;
471 sprintf(command, "COMMIT;");
472 result = sqlite3_exec(db, command, null, null, null);
474 PrintLn($"COMMIT FAILED!");
475 return result == SQLITE_OK;
478 bool CreateCustomFunction(char * name, SQLCustomFunction customFunction)
481 Class cfClass = customFunction._class;
482 customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module);
483 if(customFunction.method)
485 String typeString = CopyString(customFunction.method.dataTypeString);
487 int count = TokenizeWith(typeString, sizeof(tokens)/sizeof(tokens[0]), tokens, "(,)", false);
489 bool variadic = false;
491 for(c = 0; c < count; c++)
494 bool pointer = false;
495 String arg = tokens[c];
497 TrimLSpaces(arg, arg);
498 if(strchr(arg, '*')) pointer = true;
500 // Using String for generic pointer...
501 type = class(String);
504 if((space = strchr(arg, ' '))) *space = 0;
505 if(!strcmp(arg, "void"))
507 else if(!strcmp(arg, "..."))
511 if(cfClass.templateParams.count)
513 ClassTemplateParameter p;
515 for(p = cfClass.templateParams.first; p; p = p.next, id++)
517 if(!strcmp(p.name, arg))
520 if(p && cfClass.templateArgs)
521 arg = cfClass.templateArgs[id].dataTypeString;
523 type = eSystem_FindClass(customFunction._class.module, arg);
525 type = eSystem_FindClass(customFunction._class.module.application, arg);
529 customFunction.returnType = type;
531 customFunction.args.Add(type);
537 // Variadic args don't make sense for SQL custom functions
538 // Note that different CIF must be prepared for different set of arguments
539 // ffi_prep_cif_var(&customFunction.cif, FFI_DEFAULT_ABI, args.count-1, rType, argTypes);
543 customFunction.rType = FFIGetType(customFunction.returnType, true);
544 customFunction.argTypes.Add((void *)&ffi_type_pointer); // This pointer for SQLCustomFunction object
545 for(a : customFunction.args) customFunction.argTypes.Add((void *)FFIGetType(a, false));
546 ffi_prep_cif(&customFunction.cif, FFI_DEFAULT_ABI, customFunction.argTypes.count, customFunction.rType, (ffi_type **) customFunction.argTypes.array);
547 result = sqlite3_create_function(db, name, customFunction.args.count, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null) == SQLITE_OK;
554 static class FFITypesHolder : Map<Class, String> { ~FFITypesHolder() { Free(); } }
555 FFITypesHolder structFFITypes { };
557 public ffi_type * FFIGetType(Class type, bool structByValue)
566 MapIterator<Class, String> it { map = structFFITypes };
567 ffi_type * ffiType = null;
568 if(it.Index(type, false))
569 ffiType = (void *)it.data;
574 Array<String> memberTypes { };
575 for(member = type.membersAndProperties.first; member; member = member.next)
577 if(!member.isProperty)
579 memberTypes.Add(FFIGetType(member.dataType
583 ffiType = new0 ffi_type[1];
584 ffiType->size = type.structSize;
585 ffiType->type = FFI_TYPE_STRUCT;
586 structFFITypes[type] = (void *)ffiType;
593 return &ffi_type_pointer;
599 if(!strcmp(type.dataTypeString, "float")) return &ffi_type_float;
600 else if(!strcmp(type.dataTypeString, "double")) return &ffi_type_double;
602 switch(type.typeSize)
604 case 1: return &ffi_type_uint8;
605 case 2: return &ffi_type_uint16;
606 case 4: return &ffi_type_uint32;
607 case 8: return &ffi_type_uint64;
611 return &ffi_type_void;
615 static SerialBuffer staticBuffer { };
616 void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values)
618 SQLCustomFunction sqlFunction = sqlite3_user_data(context);
620 /* // Simple 1 pointer param returning a string
621 void * p = sqlFunction.method.function(sqlFunction, sqlite3_value_text(values[0]));
622 sqlite3_result_text(context, p, strlen(p), SQLITE_TRANSIENT);
625 void * ret = &retData;
626 Array<String> args { size = sqlFunction.args.count + 1 };
627 Iterator<String> ffiArg { sqlFunction.argTypes };
628 Iterator<String> arg { args };
631 // this * for the SQLCustomFunction
632 args[0] = (void *)&sqlFunction;
634 // Get the arguments from SQLite
635 for(a : sqlFunction.args)
637 ffi_type * type = (ffi_type *)ffiArg.data;
646 void ** data = new void *[1];
647 args[i+1] = (void *)data;
648 if(a == class(String))
650 int numBytes = sqlite3_value_bytes(values[i]);
651 char * text = sqlite3_value_text(values[i]);
652 *(char **)data = text ? new byte[numBytes+1] : null;
654 memcpy(*(char **)data, text, numBytes+1);
658 SerialBuffer buffer = staticBuffer; //{ };
660 buffer._size = sqlite3_value_bytes(values[i]);
661 buffer._buffer = sqlite3_value_text(values[i]);
662 //buffer._buffer = sqlite3_value_blob(curStatement);
663 buffer.count = buffer._size;
664 a._vTbl[__ecereVMethodID_class_OnUnserialize](a, data, buffer);
665 buffer._buffer = null;
674 if(type == &ffi_type_double || type == &ffi_type_float)
676 double d = sqlite3_value_double(values[i]);
679 double * data = new double[1];
680 args[i+1] = (void *)data;
685 float * data = new float[1];
686 args[i+1] = (void *)data;
696 int64 * data = new int64[1];
697 args[i+1] = (void *)data;
698 *data = sqlite3_value_int64(values[i]);
703 int * data = new int[1];
704 args[i+1] = (void *)data;
705 *data = sqlite3_value_int(values[i]);
710 short * data = new short[1];
712 args[i+1] = (void *)data;
713 value = sqlite3_value_int(values[i]);
715 *data = (short)value;
717 *(uint16 *)data = (uint16)value;
722 char * data = new char[1];
725 value = sqlite3_value_int(values[i]);
729 *(byte *)data = (byte)value;
739 if(sqlFunction.returnType && sqlFunction.returnType.type == structClass)
740 ret = new byte[sqlFunction.returnType.typeSize];
741 ffi_call(&sqlFunction.cif, (void *)sqlFunction.method.function, ret, args.array);
742 // Give SQLite the return value
743 if(sqlFunction.returnType)
745 ffi_type * type = sqlFunction.rType;
746 Class r = sqlFunction.returnType;
754 void * data = ret ? *(void **)ret : null;
755 if(r.type == structClass)
757 if(r == class(String))
760 sqlite3_result_text(context, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
762 sqlite3_result_text(context, null, 0, SQLITE_TRANSIENT);
766 SerialBuffer buffer { };
767 r._vTbl[__ecereVMethodID_class_OnSerialize](r, data, buffer);
768 sqlite3_result_text(context, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
771 // Avoid destroying Strings for now... (Returning memory owned by the Custom Function)
772 r._vTbl[__ecereVMethodID_class_OnFree](r, data);
775 if(r.type == structClass)
783 if(type == &ffi_type_double || type == &ffi_type_float)
786 sqlite3_result_double(context, *(double *)ret);
788 sqlite3_result_double(context, (double)*(float *)ret);
795 sqlite3_result_int64(context, (sqlite3_int64)*(int64 *)ret);
798 sqlite3_result_int(context, *(int *)ret);
804 value = (int)*(short *)ret;
806 //value = (int)*(uint16 *)ret;
807 sqlite3_result_int(context, value);
814 value = (int)*(char *)ret;
816 //value = (int)*(byte *)ret;
817 sqlite3_result_int(context, value);
828 for(type : sqlFunction.args; arg.Next())
831 void * data = *(void **)arg.data;
832 type._vTbl[__ecereVMethodID_class_OnFree](type, data);
840 class SQLiteTable : Table
845 LinkList<SQLiteField> fields { };
846 char * specialStatement;
847 SQLiteField primaryKey;
848 FieldIndex * indexFields;
849 int indexFieldsCount;
852 Field AddField(const String fieldName, Class type, int length)
859 Table refTable = null;
860 Field idField = null;
863 if(FindField(fieldName)) return null;
865 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
866 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
867 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
868 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
869 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
870 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
872 strcpy(dataType, "INTEGER");
873 sqliteType = SQLITE_INTEGER;
875 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
877 strcpy(dataType, "REAL");
878 sqliteType = SQLITE_FLOAT;
880 else if(!strcmp(type.name, "CIString"))
882 strcpy(dataType, "TEXT");
883 sqliteType = SQLITE_BLOB;
885 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
887 strcpy(dataType, "TEXT");
888 sqliteType = SQLITE_TEXT;
892 //strcpy(dataType, "BLOB");
893 strcpy(dataType, "TEXT");
894 sqliteType = SQLITE_BLOB;
896 if(!db.collations.Find(type.fullName))
898 db.collations.Add(type.fullName);
899 result = sqlite3_create_collation_v2(db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
902 if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)))
904 Table * table = (Table *)eClass_GetProperty(type, "table");
905 if(table) refTable = *table;
908 if(primaryKey || refTable != this)
910 for(idField = refTable.firstField; idField; idField = idField.next)
911 if(eClass_IsDerived(type, idField.type)) break;
914 PrintLn("WARNING: field not yet created for class ", (String)type.name);
917 idField = primaryKey;
921 PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
927 if(sqliteType == SQLITE_BLOB)
929 if(!strcmp(type.name, "CIString"))
930 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE NOCASE);", name, fieldName, dataType);
932 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE '%s');", name, fieldName, dataType, type.fullName);
936 if(!idField && refTable == this)
937 sprintf(command, "CREATE TABLE `%s`(`%s` %s PRIMARY KEY);", name, fieldName, dataType);
939 sprintf(command, "CREATE TABLE `%s`(`%s` %s REFERENCES `%s`(`%s`));", name, fieldName, dataType, refTable.name, idField.name);
942 sprintf(command, "CREATE TABLE `%s`(`%s` %s);", name, fieldName, dataType);
943 result = sqlite3_exec(db.db, command, null, null, null);
944 if(result) return null;
949 if(sqliteType == SQLITE_BLOB)
951 if(!strcmp(type.name, "CIString"))
952 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE NOCASE;", name, fieldName, dataType);
954 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE `%s`;", name, fieldName, dataType, type.fullName);
958 if(!idField && refTable == this)
960 PrintLn($"WARNING: ALTER TABLE DOESN'T WORK WITH PRIMARY KEY FOR ", (String)name);
961 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s PRIMARY KEY;", name, fieldName, dataType);
964 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s REFERENCES `%s`(`%s`);", name, fieldName, dataType, refTable.name, idField.name);
967 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s;", name, fieldName, dataType);
968 result = sqlite3_exec(db.db, command, null, null, null);
969 if(result) return null;
972 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
973 fieldName, type.name, length);
974 result = sqlite3_exec(db.db, command, null, null, null);
976 field = { name = CopyString(fieldName), type = type, num = fields.count, sqliteType = sqliteType };
979 if(!primaryKey && refTable == this)
984 Field FindField(const String name)
986 for(f : fields; !strcmp(f.name, name))
990 if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
993 Table * tablePtr = (Table *)eClass_GetProperty(f.type, "table");
994 if(tablePtr && *tablePtr == this)
1003 bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init)
1008 char indexName[4096];
1011 indexFieldsCount = count;
1012 indexFields = new FieldIndex[count];
1013 memcpy(indexFields, fieldIndexes, count * sizeof(FieldIndex));
1015 // TODO: USE CODED INDEX NAME INSTEAD?
1016 strcpy(indexName, "index_");
1017 strcat(indexName, name);
1018 strcat(indexName, "_");
1019 for(c = 0; c<count; c++)
1021 if(fieldIndexes[c].field)
1023 if(count == 1 && fieldIndexes[c].field == primaryKey)
1025 strcat(indexName, fieldIndexes[c].field.name);
1026 if(fieldIndexes[c].memberField)
1028 strcat(indexName, ".");
1029 strcat(indexName, fieldIndexes[c].memberField.name);
1031 strcat(indexName, (fieldIndexes[c].order == ascending) ? "+" : "-");
1037 sprintf(command, "CREATE INDEX IF NOT EXISTS `%s` ON `%s` (", indexName, name);
1038 for(c = 0; c<count; c++)
1040 char columnName[1024];
1041 sprintf(columnName, "`%s` %s", fieldIndexes[c].field.name, (fieldIndexes[c].order == ascending) ? "ASC" : "DESC");
1042 if(c > 0) strcat(command, ", ");
1043 strcat(command, columnName);
1045 strcat(command, ");");
1046 result = sqlite3_exec(db.db, command, null, null, null);
1048 return result == SQLITE_OK;
1056 Field GetFirstField()
1058 return fields.first;
1061 Field GetPrimaryKey()
1066 uint GetFieldsCount()
1068 return fields.count;
1078 sprintf(command, "SELECT COUNT(*) FROM `%s`;", name);
1079 result = sqlite3_get_table(db.db, command, &t, &nRows, &nCols, null);
1080 if(result == SQLITE_OK)
1082 rowCount = atoi(t[1]);
1083 sqlite3_free_table(t);
1088 // Returns true if not ordered by row ID
1089 bool GetIndexOrder(char * fullOrder, bool flip)
1091 if(!flip && (!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending)))
1093 strcpy(fullOrder, " ORDER BY ROWID");
1099 strcpy(fullOrder, " ORDER BY ");
1100 for(c = flip ? indexFieldsCount-1 : 0; flip ? (c >= 0) : (c < indexFieldsCount); flip ? c-- : c++)
1103 FieldIndex * fIndex = &indexFields[c];
1105 if(c) strcat(order, ", ");
1107 strcat(order, fIndex->field.name);
1109 if(fIndex->order == (flip ? ascending : descending)) strcat(order, " DESC");
1110 strcat(fullOrder, order);
1116 DriverRow CreateRow()
1119 sqlite3_stmt * statement;
1120 sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
1121 sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null;
1123 if(specialStatement)
1124 strcpy(command, specialStatement);
1128 /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
1129 sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
1130 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
1131 sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
1133 sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
1134 sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
1136 sprintf(command, "INSERT INTO `%s` (ROWID) VALUES(?);", name);
1137 sqlite3_prepare_v2(db.db, command, -1, &insertIDStmt, null);
1139 sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
1140 sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
1142 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
1143 sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
1145 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
1146 sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
1148 sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
1149 sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
1151 /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
1153 sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
1155 GetIndexOrder(order, false);
1156 sprintf(command, "SELECT ROWID, * FROM `%s`%s;", name, order);
1158 sqlite3_prepare_v2(db.db, command, -1, &statement, null);
1160 sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
1161 sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
1163 sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
1164 sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
1167 { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt,
1168 insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
1169 previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
1175 delete specialStatement;
1181 class SQLiteRow : DriverRow
1184 sqlite3_stmt * curStatement;
1186 sqlite3_stmt * defaultStatement;
1187 sqlite3_stmt * findStatement;
1188 sqlite3_stmt * prevFindStatement, * lastFindStatement;
1189 sqlite3_stmt * nextFindStatement;
1190 sqlite3_stmt * sysIDStatement;
1191 sqlite3_stmt * queryStatement;
1192 sqlite3_stmt * selectRowIDsStmt;
1193 sqlite3_stmt * setRowIDStmt;
1194 sqlite3_stmt * lastStatement;
1195 sqlite3_stmt * previousStatement;
1196 sqlite3_stmt * nextStatement;
1198 sqlite3_stmt * insertStatement;
1199 sqlite3_stmt * deleteStatement;
1200 sqlite3_stmt * updateStatement;
1201 sqlite3_stmt * insertIDStatement;
1205 // Because we use GoToSysID() and the sysIDStatement when searching for a primary key with Find(),
1206 // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
1217 if(defaultStatement) sqlite3_finalize(defaultStatement);
1218 if(findStatement) sqlite3_finalize(findStatement);
1219 if(prevFindStatement)sqlite3_finalize(prevFindStatement);
1220 if(lastFindStatement)sqlite3_finalize(lastFindStatement);
1221 if(nextFindStatement)sqlite3_finalize(nextFindStatement);
1222 if(sysIDStatement) sqlite3_finalize(sysIDStatement);
1223 if(insertStatement) sqlite3_finalize(insertStatement);
1224 if(deleteStatement) sqlite3_finalize(deleteStatement);
1225 if(updateStatement) sqlite3_finalize(updateStatement);
1226 if(queryStatement) sqlite3_finalize(queryStatement);
1227 if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
1228 if(setRowIDStmt) sqlite3_finalize(setRowIDStmt);
1229 if(previousStatement)sqlite3_finalize(previousStatement);
1230 if(nextStatement) sqlite3_finalize(nextStatement);
1231 if(lastStatement) sqlite3_finalize(lastStatement);
1232 if(insertIDStatement) sqlite3_finalize(insertIDStatement);
1235 bool Select(MoveOptions move)
1238 bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
1240 curStatement = defaultStatement;
1245 sqlite3_reset(curStatement);
1246 result = sqlite3_step(curStatement);
1247 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1248 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1249 rowID = sqlite3_column_int64(curStatement, 0);
1254 sqlite3_reset(curStatement);
1255 curStatement = lastStatement;
1256 result = sqlite3_step(curStatement);
1257 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1258 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1259 rowID = sqlite3_column_int64(curStatement, 0);
1267 // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
1268 if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) ||
1269 (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
1271 result = sqlite3_step(curStatement);
1272 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1273 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1274 rowID = sqlite3_column_int64(curStatement, 0);
1276 else if(curStatement == prevFindStatement || curStatement == findStatement || curStatement == nextFindStatement || curStatement == lastFindStatement)
1280 int bindId = findBindId;
1281 sqlite3_reset((move == next) ? nextFindStatement : prevFindStatement);
1282 BindCursorData((move == next) ? nextFindStatement : prevFindStatement, move,
1283 (move == next && (!tbl.indexFields || (tbl.indexFieldsCount == 1 && tbl.indexFields[0].field == tbl.primaryKey && tbl.indexFields[0].order == ascending))) ? false : true, &bindId);
1284 sqlite3_reset(curStatement);
1285 curStatement = (move == next) ? nextFindStatement : prevFindStatement;
1286 result = sqlite3_step(curStatement);
1287 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1288 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1289 rowID = sqlite3_column_int64(curStatement, 0);
1293 int bindId = findBindId;
1294 sqlite3_reset((move == next) ? findStatement : lastFindStatement);
1295 sqlite3_reset(curStatement);
1296 curStatement = (move == next) ? findStatement : lastFindStatement;
1297 result = sqlite3_step(curStatement);
1298 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1299 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1300 rowID = sqlite3_column_int64(curStatement, 0);
1305 sqlite3_reset(curStatement);
1306 curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
1307 sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
1308 result = sqlite3_step(curStatement);
1309 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1310 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1311 rowID = sqlite3_column_int64(curStatement, 0);
1316 sqlite3_reset(curStatement);
1326 bool Query(char * queryString)
1332 sqlite3_reset(curStatement);
1335 sqlite3_finalize(queryStatement);
1336 queryStatement = null;
1341 result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
1344 curStatement = queryStatement;
1345 if(!strchr(queryString, '?'))
1347 result = sqlite3_step(queryStatement);
1349 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1350 if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
1352 rowID = sqlite3_column_int64(queryStatement, 0);
1359 curStatement = null;
1363 bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
1366 Class dataType = fld.type;
1367 SerialBuffer buffer = null;
1368 switch(fld.sqliteType)
1370 case SQLITE_INTEGER:
1372 switch(dataType.typeSize)
1375 result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1378 result = sqlite3_bind_int(statement, pos, *(int *)data);
1384 value = (int)*(short *)data;
1386 value = (int)*(uint16 *)data;
1387 result = sqlite3_bind_int(statement, pos, value);
1394 value = (int)*(char *)data;
1396 value = (int)*(byte *)data;
1397 result = sqlite3_bind_int(statement, pos, value);
1405 if(dataType.typeSize == 8)
1406 result = sqlite3_bind_double(statement, pos, *(double *)data);
1408 result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1414 result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1416 result = sqlite3_bind_null(statement, pos);
1424 buffer = SerialBuffer { };
1425 dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, data, buffer);
1426 result = sqlite3_bind_text(statement, pos, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1429 result = sqlite3_bind_null(statement, pos);
1434 *bufferOut = buffer;
1440 void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1442 if(move == next || move == previous)
1444 // Where clauses for index
1448 bool gotPrimaryKey = false;
1450 strcatf(command, " AND (");
1451 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1454 FieldIndex * fIndex = &tbl.indexFields[c];
1458 strcat(where, fIndex->field.name);
1459 strcat(where, "` ");
1460 strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1461 strcat(where, " ? OR (");
1462 strcat(where, fIndex->field.name);
1463 if(fIndex->field == tbl.primaryKey)
1464 gotPrimaryKey = true;
1465 strcat(where, " = ? AND (");
1466 strcat(command, where);
1468 strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1469 for(c = 0; c < tbl.indexFieldsCount; c++)
1470 strcat(command, "))");
1473 strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1477 void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1479 if(move == next || move == previous)
1481 // The binds for the Extra ordering Where clauses
1485 /* // Code to not rely on curStatement being set up
1486 SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1487 dataRow.GoToSysID((uint)rowID);
1489 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1491 FieldIndex * fIndex = &tbl.indexFields[c];
1493 SQLiteField fld = (SQLiteField)fIndex->field;
1494 Class type = fld.type;
1496 SerialBuffer buffer;
1498 if(type.type == unitClass && !type.typeSize)
1500 Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1504 if(type.type == structClass)
1506 data = (int64)new0 byte[type.structSize];
1507 dataPtr = (void *) data;
1509 // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1510 ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)data : &data);
1511 if(type.type == normalClass || type.type == noHeadClass)
1512 dataPtr = (void *) data;
1515 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1516 // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
1517 // Reuse the buffer for Blobs...
1518 if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1520 sqlite3_bind_text(stmt, (*bindId)++, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1524 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1526 type._vTbl[__ecereVMethodID_class_OnFree](type, dataPtr);
1531 // Bind for the rowid
1532 sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1536 bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1538 char order[1024], command[2048];
1541 sqlite3_stmt * stmt = null;
1544 if(fld == tbl.primaryKey)
1546 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1547 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1548 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1549 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1550 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1551 result = GoToSysID(*(int *)data);
1557 useIndex = tbl.GetIndexOrder(order, false);
1559 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1560 AddCursorWhereClauses(command, move, useIndex);
1561 strcat(command, order);
1562 strcat(command, ";");
1563 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1564 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1565 BindCursorData(stmt, move, useIndex, &bindId);
1567 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1568 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1569 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1570 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1571 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1572 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1574 curStatement = findStatement = stmt;
1575 findBindId = bindId;
1577 // For going back to forward find
1579 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1580 AddCursorWhereClauses(command, next, useIndex);
1581 strcat(command, order);
1582 strcat(command, ";");
1583 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1584 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1585 nextFindStatement = stmt;
1588 tbl.GetIndexOrder(order, true);
1589 // For tracing back finds
1591 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1592 AddCursorWhereClauses(command, previous, true);
1593 strcat(command, order);
1594 strcat(command, ";");
1595 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1596 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1597 prevFindStatement = stmt;
1599 // For tracing back from last
1601 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1602 strcat(command, order);
1603 strcat(command, ";");
1604 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1605 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1606 lastFindStatement = stmt;
1608 result = sqlite3_step(findStatement);
1610 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1614 sqlite3_reset(findStatement);
1617 rowID = sqlite3_column_int64(findStatement, 0);
1621 bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1624 for(c = 0; c < numFields; c++) \
1626 FieldFindData * fieldFind = &findData[c]; \
1627 SQLiteField sqlFld = (SQLiteField)findData->field; \
1628 Class dataType = sqlFld.type; \
1629 BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1634 char criterias[4096], command[4096], order[1024];
1638 sqlite3_stmt * stmt = null;
1642 sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1643 for(c = 0; c < numFields; c++)
1645 FieldFindData * fieldFind = &findData[c];
1647 if(c) strcat(criterias, " AND `");
1648 strcat(criterias, fieldFind->field.name);
1649 strcat(criterias, "` = ?");
1652 useIndex = tbl.GetIndexOrder(order, false);
1653 // Basic Find (multiple)
1654 strcpy(command, criterias);
1655 AddCursorWhereClauses(command, move, useIndex);
1656 strcat(command, order);
1657 strcat(command, ";");
1658 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1660 BindCursorData(stmt, move, useIndex, &bindId);
1662 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1663 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1664 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1665 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1666 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1667 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1669 curStatement = findStatement = stmt;
1670 findBindId = bindId;
1672 // For tracing back forward finds
1674 strcpy(command, criterias);
1675 AddCursorWhereClauses(command, previous, true);
1676 strcat(command, order);
1677 strcat(command, ";");
1678 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1680 nextFindStatement = stmt;
1683 tbl.GetIndexOrder(order, true);
1684 // For tracing back finds
1686 strcpy(command, criterias);
1687 AddCursorWhereClauses(command, next, useIndex);
1688 strcat(command, order);
1689 strcat(command, ";");
1690 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1692 prevFindStatement = stmt;
1694 // For tracing back from last
1696 strcpy(command, criterias);
1697 strcat(command, order);
1698 strcat(command, ";");
1699 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1701 lastFindStatement = stmt;
1703 result = sqlite3_step(findStatement);
1704 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1708 sqlite3_reset(findStatement);
1711 rowID = sqlite3_column_int64(findStatement, 0);
1717 bool Synch(DriverRow to)
1719 SQLiteRow rowTo = (SQLiteRow)to;
1720 if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1721 return GoToSysID((uint)rowTo.rowID);
1728 //char command[1024];
1729 //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1730 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1733 sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1734 result = sqlite3_step(insertIDStatement);
1737 result = sqlite3_step(insertStatement);
1738 if(result == SQLITE_DONE) // if(result == SQLITE_OK)
1740 rowID = sqlite3_last_insert_rowid(tbl.db.db);
1741 if(rowID > MAXDWORD)
1743 int64 lastID = tbl.lastID;
1745 sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1749 result = sqlite3_step(selectRowIDsStmt);
1750 if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1751 id = sqlite3_column_int64(selectRowIDsStmt, 0);
1752 if(id - lastID > 1) break;
1755 sqlite3_reset(selectRowIDsStmt);
1757 sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1760 sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1761 result = sqlite3_step(setRowIDStmt);
1762 sqlite3_reset(setRowIDStmt);
1764 sqlite3_reset(id ? insertIDStatement : insertStatement);
1765 curStatement = sysIDStatement;
1767 sqlite3_reset(curStatement);
1768 sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1769 result = sqlite3_step(curStatement);
1770 done = false; // Make sure 'nil' is false
1773 sqlite3_reset(insertStatement);
1780 //char command[1024];
1781 //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1782 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1783 sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1784 result = sqlite3_step(deleteStatement);
1785 sqlite3_reset(deleteStatement);
1787 return result == SQLITE_OK || result == SQLITE_DONE;
1790 bool GetData(Field fld, typed_object &data)
1792 SQLiteField sqlFld = (SQLiteField)fld;
1793 int num = sqlFld.num + 1;
1794 Class dataType = sqlFld.type;
1797 switch(sqlFld.sqliteType)
1799 case SQLITE_INTEGER:
1801 switch(dataType.typeSize)
1804 if(fld == tbl.primaryKey)
1805 *(int64 *)data = rowID;
1807 *(int64 *)data = sqlite3_column_int64(curStatement, num);
1810 if(fld == tbl.primaryKey)
1811 *(int *)data = (int)(uint)rowID;
1813 *(int *)data = sqlite3_column_int(curStatement, num);
1818 if(fld == tbl.primaryKey)
1819 value = (int)(uint)rowID;
1821 value = sqlite3_column_int(curStatement, num);
1823 *(short *)data = (short)value;
1825 *(uint16 *)data = (uint16)value;
1831 if(fld == tbl.primaryKey)
1832 value = (int)(uint)rowID;
1834 value = sqlite3_column_int(curStatement, num);
1836 *(char *)data = (char)value;
1838 *(byte *)data = (byte)value;
1846 double d = sqlite3_column_double(curStatement, num);
1847 if(dataType.typeSize == 8)
1848 *(double *)data = d;
1850 *(float *)data = (float)d;
1855 int numBytes = sqlite3_column_bytes(curStatement, num);
1856 char * text = sqlite3_column_text(curStatement, num);
1857 *(char **)data = text ? new byte[numBytes+1] : null;
1859 memcpy(*(char **)data, text, numBytes+1);
1864 SerialBuffer buffer { };
1865 //buffer._buffer = sqlite3_column_blob(curStatement, num);
1866 buffer._size = sqlite3_column_bytes(curStatement, num);
1867 buffer._buffer = sqlite3_column_text(curStatement, num);
1868 buffer.count = buffer._size;
1870 dataType._vTbl[__ecereVMethodID_class_OnUnserialize](dataType, data, buffer);
1872 buffer._buffer = null;
1880 bool SetData(Field fld, typed_object data)
1882 SQLiteField sqlFld = (SQLiteField)fld;
1884 int num = sqlFld.num + 1;
1888 sqlite3_finalize(updateStatement);
1889 sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1890 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1891 sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1892 BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1893 result = sqlite3_step(updateStatement);
1894 sqlite3_reset(updateStatement);
1895 if(fld == tbl.primaryKey)
1896 rowID = *(uint *)data;
1897 return result == SQLITE_DONE;
1902 return (int)(uint)rowID;
1905 bool GoToSysID(uint id)
1907 //char command[1024];
1911 //sqlite3_finalize(statement);
1912 //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1913 //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1917 sqlite3_reset(curStatement);
1919 curStatement = sysIDStatement;
1920 sqlite3_reset(sysIDStatement);
1921 sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1922 result = sqlite3_step(curStatement);
1923 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1924 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1928 bool SetQueryParam(int paramID, int value)
1931 if(curStatement != queryStatement)
1933 if(curStatement) sqlite3_reset(curStatement);
1934 curStatement = queryStatement;
1936 sqlite3_reset(queryStatement);
1937 result = sqlite3_bind_int(queryStatement, paramID, value);
1941 bool SetQueryParam64(int paramID, int64 value)
1944 if(curStatement != queryStatement)
1946 if(curStatement) sqlite3_reset(curStatement);
1947 curStatement = queryStatement;
1949 sqlite3_reset(queryStatement);
1950 result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1954 bool SetQueryParamText(int paramID, char * data)
1957 if(curStatement != queryStatement)
1959 if(curStatement) sqlite3_reset(curStatement);
1960 curStatement = queryStatement;
1962 sqlite3_reset(queryStatement);
1964 result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1966 result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1970 bool SetQueryParamObject(int paramID, void * data, Class type)
1973 if(curStatement != queryStatement)
1975 if(curStatement) sqlite3_reset(curStatement);
1976 curStatement = queryStatement;
1978 sqlite3_reset(queryStatement);
1980 SerialBuffer buffer { };
1981 type._vTbl[__ecereVMethodID_class_OnSerialize](type, data, buffer);
1982 result = sqlite3_bind_text(queryStatement, paramID, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1988 bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1990 if(curStatement != queryStatement)
1992 if(curStatement) sqlite3_reset(curStatement);
1993 curStatement = queryStatement;
1995 sqlite3_reset(queryStatement);
1996 return BindData(queryStatement, pos, fld, data, null);
1999 /*char * GetExtraColumn(int paramID)
2001 SQLiteField lastFld = tbl.fields.last;
2002 return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
2004 char * GetColumn(int paramID)
2006 return sqlite3_column_text(curStatement, paramID);