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);
191 uint ObjectsCount(ObjectType type)
197 bool RenameObject(ObjectType type, const String name, const String rename)
203 bool DeleteObject(ObjectType type, const String name)
209 Table OpenTable(const String name, OpenOptions options)
213 int nRows = 0, nCols = 0;
215 SQLiteTable table = null;
216 if(options.type == tablesList)
219 strcpy(command, "SELECT name FROM sqlite_master WHERE type='table' AND name!='eda_table_fields';");
220 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
221 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
224 table._fields.Add(field);
226 else if(options.type == fieldsList)
230 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
231 table = SQLiteTable { db = this, specialStatement = CopyString(command) };
233 field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
235 table._fields.Add(field);
236 field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
238 table._fields.Add(field);
239 field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
241 table._fields.Add(field);
243 else if(options.type == tableRows)
245 bool addFields = false;
247 sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
248 /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
252 sqlite3_free_table(t);
254 sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
255 nCols = 0, nRows = 0;
256 /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
258 if((nCols || nRows) || options.create)
260 table = SQLiteTable { db = this, name = CopyString(name) };
263 table.mustCreate = true;
269 for(r = 1; r <= nRows; r++) // There should be only 1 row here
271 char * sql = t[nCols * r];
272 char * bracket = strchr(sql, '(');
284 int sqliteType = SQLITE_BLOB;
285 Class type = class(int);
289 while((ch = bracket[c++]))
291 if(ch == ',' || ch == ')')
294 for(d = c-1; d >= 0 && bracket[d] != ' '; d--);
296 memcpy(fieldName, bracket + start, d - start);
297 fieldName[d - start] = 0;
299 memcpy(dataType, bracket + d + 1, c - d - 2);
300 dataType[c - d - 2] = 0;
302 while(ch && bracket[c] == ' ') c++;
304 if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
305 else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
306 else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
307 else if(!strcmp(dataType, "BLOB")) { sqliteType = SQLITE_BLOB; type = class(char *); } //class(byte *);
309 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
310 fieldName, type.name, 0);
311 /*result = */sqlite3_exec(db, command, null, null, null);
314 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table._fields.count, sqliteType = sqliteType };
316 table._fields.Add(field);
319 if(!ch || ch == ')') break;
326 Table refTable = null;
327 sqlite3_stmt * statement;
329 sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
330 /*result = */sqlite3_prepare_v2(db, command, -1, &statement, null);
332 while(sqlite3_step(statement) != SQLITE_DONE)
334 const char * fieldName = (const char *)sqlite3_column_text(statement, 0);
335 const char * typeName = (const char *)sqlite3_column_text(statement, 1);
336 int length = sqlite3_column_int(statement, 2);
338 int sqliteType = SQLITE_BLOB;
340 ((Class)(&type)).OnGetDataFromString(typeName); // TODO: THIS REQUIRES A FIX SOMEWHERE ELSE
344 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
345 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
346 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
347 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
348 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
349 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
350 sqliteType = SQLITE_INTEGER;
351 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
352 sqliteType = SQLITE_FLOAT;
353 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
354 sqliteType = SQLITE_TEXT;
357 if(strcmp(type.fullName, "CIString") && !collations.Find(type.fullName))
359 collations.Add(type.fullName);
360 sqlite3_create_collation_v2(table.db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
362 sqliteType = SQLITE_BLOB;
367 Table * fTable = (Table *)eClass_GetProperty(type, "table");
368 SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table._fields.count, sqliteType = sqliteType };
370 if(fTable) refTable = *fTable;
371 if(!table.primaryKey && refTable && !strcmp(refTable.name, table.name))
372 table.primaryKey = field;
374 table._fields.Add(field);
377 sqlite3_finalize(statement);
381 sqlite3_free_table(t);
390 sprintf(command, "BEGIN;");
391 result = sqlite3_exec(db, command, null, null, null);
393 PrintLn($"BEGIN FAILED!");
394 return result == SQLITE_OK;
401 sprintf(command, "COMMIT;");
402 result = sqlite3_exec(db, command, null, null, null);
404 PrintLn($"COMMIT FAILED!");
405 return result == SQLITE_OK;
408 bool CreateCustomFunction(const char * name, SQLCustomFunction customFunction)
411 Class cfClass = customFunction._class;
412 customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module);
413 if(customFunction.method)
415 String typeString = CopyString(customFunction.method.dataTypeString);
417 int count = TokenizeWith(typeString, sizeof(tokens)/sizeof(tokens[0]), tokens, "(,)", false);
419 bool variadic = false;
421 for(c = 0; c < count; c++)
424 bool pointer = false;
425 const String arg = tokens[c];
427 TrimLSpaces(tokens[c], tokens[c]);
428 if(strchr(arg, '*')) pointer = true;
430 // Using String for generic pointer...
431 type = class(String);
434 if((space = strchr(arg, ' '))) *space = 0;
435 if(!strcmp(arg, "void"))
437 else if(!strcmp(arg, "..."))
441 if(cfClass.templateParams.count)
443 ClassTemplateParameter p;
445 for(p = cfClass.templateParams.first; p; p = p.next, id++)
447 if(!strcmp(p.name, arg))
450 if(p && cfClass.templateArgs)
451 arg = cfClass.templateArgs[id].dataTypeString;
453 type = eSystem_FindClass(customFunction._class.module, arg);
455 type = eSystem_FindClass(customFunction._class.module.application, arg);
459 customFunction.returnType = type;
461 customFunction.args.Add(type);
467 // Variadic args don't make sense for SQL custom functions
468 // Note that different CIF must be prepared for different set of arguments
469 // ffi_prep_cif_var(&customFunction.cif, FFI_DEFAULT_ABI, args.count-1, rType, argTypes);
473 customFunction.rType = FFIGetType(customFunction.returnType, true);
474 customFunction.argTypes.Add((void *)&ffi_type_pointer); // This pointer for SQLCustomFunction object
475 for(a : customFunction.args) customFunction.argTypes.Add((void *)FFIGetType(a, false));
476 ffi_prep_cif(&customFunction.cif, FFI_DEFAULT_ABI, customFunction.argTypes.count, customFunction.rType, (ffi_type **) customFunction.argTypes.array);
477 result = sqlite3_create_function(db, name, customFunction.args.count, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null) == SQLITE_OK;
484 static class FFITypesHolder : Map<Class, String> { ~FFITypesHolder() { Free(); } }
485 FFITypesHolder structFFITypes { };
486 __attribute__((unused)) static Iterator dummy; // TOFIX: forward struct declaration issues on Clang
488 public ffi_type * FFIGetType(Class type, bool structByValue)
497 MapIterator<Class, String> it { map = structFFITypes };
498 ffi_type * ffiType = null;
499 if(it.Index(type, false))
500 ffiType = (void *)it.data;
505 Array<String> memberTypes { };
506 for(member = type.membersAndProperties.first; member; member = member.next)
508 if(!member.isProperty)
510 memberTypes.Add(FFIGetType(member.dataType
514 ffiType = new0 ffi_type[1];
515 ffiType->size = type.structSize;
516 ffiType->type = FFI_TYPE_STRUCT;
517 structFFITypes[type] = (void *)ffiType;
524 return &ffi_type_pointer;
530 if(!strcmp(type.dataTypeString, "float")) return &ffi_type_float;
531 else if(!strcmp(type.dataTypeString, "double")) return &ffi_type_double;
533 switch(type.typeSize)
535 case 1: return &ffi_type_uint8;
536 case 2: return &ffi_type_uint16;
537 case 4: return &ffi_type_uint32;
538 case 8: return &ffi_type_uint64;
542 return &ffi_type_void;
546 static SerialBuffer staticBuffer { };
547 void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values)
549 SQLCustomFunction sqlFunction = sqlite3_user_data(context);
551 /* // Simple 1 pointer param returning a string
552 void * p = sqlFunction.method.function(sqlFunction, sqlite3_value_text(values[0]));
553 sqlite3_result_text(context, p, strlen(p), SQLITE_TRANSIENT);
556 void * ret = &retData;
557 Array<String> args { size = sqlFunction.args.count + 1 };
558 Iterator<String> ffiArg { sqlFunction.argTypes };
559 Iterator<String> arg { args };
562 // this * for the SQLCustomFunction
563 args[0] = (void *)&sqlFunction;
565 // Get the arguments from SQLite
566 for(a : sqlFunction.args)
568 ffi_type * type = (ffi_type *)sqlFunction.argTypes[i+1];
577 void ** data = new void *[1];
578 args[i+1] = (void *)data;
579 if(a == class(String))
581 int numBytes = sqlite3_value_bytes(values[i]);
582 const char * text = (const char *)sqlite3_value_text(values[i]);
583 *(char **)data = text ? new byte[numBytes+1] : null;
585 memcpy(*(char **)data, text, numBytes+1);
589 SerialBuffer buffer = staticBuffer; //{ };
591 buffer._size = sqlite3_value_bytes(values[i]);
592 buffer._buffer = (byte *)sqlite3_value_text(values[i]);
593 //buffer._buffer = sqlite3_value_blob(curStatement);
594 buffer.count = buffer._size;
595 if(a.type == structClass)
596 *data = new byte[a.structSize];
597 ((void (*)(void *, void *, void *))(void *)a._vTbl[__ecereVMethodID_class_OnUnserialize])(a, (a.type == structClass) ? *data : data, buffer);
598 buffer._buffer = null;
607 if(type == &ffi_type_double || type == &ffi_type_float)
609 double d = sqlite3_value_double(values[i]);
612 double * data = new double[1];
613 args[i+1] = (void *)data;
618 float * data = new float[1];
619 args[i+1] = (void *)data;
629 int64 * data = new int64[1];
630 args[i+1] = (void *)data;
631 *data = sqlite3_value_int64(values[i]);
636 int * data = new int[1];
637 args[i+1] = (void *)data;
638 *data = sqlite3_value_int(values[i]);
643 short * data = new short[1];
645 args[i+1] = (void *)data;
646 value = sqlite3_value_int(values[i]);
648 *data = (short)value;
650 *(uint16 *)data = (uint16)value;
655 char * data = new char[1];
658 value = sqlite3_value_int(values[i]);
662 *(byte *)data = (byte)value;
672 if(sqlFunction.returnType && sqlFunction.returnType.type == structClass)
673 ret = new byte[sqlFunction.returnType.typeSize];
674 ffi_call(&sqlFunction.cif, (void *)sqlFunction.method.function, ret, args.array);
675 // Give SQLite the return value
676 if(sqlFunction.returnType)
678 ffi_type * type = sqlFunction.rType;
679 Class r = sqlFunction.returnType;
687 void * data = ret ? *(void **)ret : null;
688 if(r.type == structClass)
690 if(r == class(String))
693 sqlite3_result_text(context, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
695 sqlite3_result_text(context, null, 0, SQLITE_TRANSIENT);
699 SerialBuffer buffer { };
700 ((void (*)(void *, void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnSerialize])(r, data, buffer);
701 sqlite3_result_text(context, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
704 // Avoid destroying Strings for now... (Returning memory owned by the Custom Function)
705 ((void (*)(void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnFree])(r, data);
708 if(r.type == structClass)
716 if(type == &ffi_type_double || type == &ffi_type_float)
719 sqlite3_result_double(context, *(double *)ret);
721 sqlite3_result_double(context, (double)*(float *)ret);
728 sqlite3_result_int64(context, (sqlite3_int64)*(int64 *)ret);
731 sqlite3_result_int(context, *(int *)ret);
737 value = (int)*(short *)ret;
739 //value = (int)*(uint16 *)ret;
740 sqlite3_result_int(context, value);
747 value = (int)*(char *)ret;
749 //value = (int)*(byte *)ret;
750 sqlite3_result_int(context, value);
761 for(type : sqlFunction.args; arg.Next())
764 void * data = *(void **)arg.data;
765 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, data);
766 if(type.type == structClass)
775 class SQLiteTable : Table
780 LinkList<SQLiteField> _fields { };
781 char * specialStatement;
782 SQLiteField primaryKey;
783 FieldIndex * indexFields;
784 int indexFieldsCount;
787 Field AddField(const String fieldName, Class type, int length)
794 Table refTable = null;
795 Field idField = null;
798 if(FindField(fieldName)) return null;
800 if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
801 !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
802 !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
803 !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
804 !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
805 !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
807 strcpy(dataType, "INTEGER");
808 sqliteType = SQLITE_INTEGER;
810 else if(!strcmp(type.dataTypeString, "double") || !strcmp(type.dataTypeString, "float"))
812 strcpy(dataType, "REAL");
813 sqliteType = SQLITE_FLOAT;
815 else if(!strcmp(type.name, "CIString"))
817 strcpy(dataType, "TEXT");
818 sqliteType = SQLITE_BLOB;
820 else if(!strcmp(type.dataTypeString, "String") || !strcmp(type.dataTypeString, "char *"))
822 strcpy(dataType, "TEXT");
823 sqliteType = SQLITE_TEXT;
827 //strcpy(dataType, "BLOB");
828 strcpy(dataType, "TEXT");
829 sqliteType = SQLITE_BLOB;
831 if(!db.collations.Find(type.fullName))
833 db.collations.Add(type.fullName);
834 result = sqlite3_create_collation_v2(db.db, type.fullName, SQLITE_UTF8, type, CollationCompare, null);
837 if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)))
839 Table * table = (Table *)eClass_GetProperty(type, "table");
840 if(table) refTable = *table;
843 if(primaryKey || refTable != this)
845 for(idField = refTable.firstField; idField; idField = idField.next)
846 if(eClass_IsDerived(type, idField.type)) break;
849 PrintLn("WARNING: field not yet created for class ", (String)type.name);
852 idField = primaryKey;
856 PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
862 if(sqliteType == SQLITE_BLOB)
864 if(!strcmp(type.name, "CIString"))
865 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE NOCASE);", name, fieldName, dataType);
867 sprintf(command, "CREATE TABLE `%s`(%s %s COLLATE '%s');", name, fieldName, dataType, type.fullName);
871 if(!idField && refTable == this)
872 sprintf(command, "CREATE TABLE `%s`(`%s` %s PRIMARY KEY);", name, fieldName, dataType);
874 sprintf(command, "CREATE TABLE `%s`(`%s` %s REFERENCES `%s`(`%s`));", name, fieldName, dataType, refTable.name, idField.name);
877 sprintf(command, "CREATE TABLE `%s`(`%s` %s);", name, fieldName, dataType);
878 result = sqlite3_exec(db.db, command, null, null, null);
879 if(result) return null;
884 if(sqliteType == SQLITE_BLOB)
886 if(!strcmp(type.name, "CIString"))
887 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE NOCASE;", name, fieldName, dataType);
889 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s COLLATE `%s`;", name, fieldName, dataType, type.fullName);
893 if(!idField && refTable == this)
895 PrintLn($"WARNING: ALTER TABLE DOESN'T WORK WITH PRIMARY KEY FOR ", (String)name);
896 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s PRIMARY KEY;", name, fieldName, dataType);
899 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s REFERENCES `%s`(`%s`);", name, fieldName, dataType, refTable.name, idField.name);
902 sprintf(command, "ALTER TABLE `%s` ADD `%s` %s;", name, fieldName, dataType);
903 result = sqlite3_exec(db.db, command, null, null, null);
904 if(result) return null;
907 sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
908 fieldName, type.name, length);
909 result = sqlite3_exec(db.db, command, null, null, null);
911 field = { name = CopyString(fieldName), type = type, num = _fields.count, sqliteType = sqliteType };
914 if(!primaryKey && refTable == this)
919 Field FindField(const String name)
921 for(f : _fields; !strcmp(f.name, name))
925 if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
928 Table * tablePtr = (Table *)eClass_GetProperty(f.type, "table");
929 if(tablePtr && *tablePtr == this)
938 bool GenerateIndex(int count, FieldIndex * fieldIndexes, bool init)
943 char indexName[4096];
946 indexFieldsCount = count;
947 indexFields = new FieldIndex[count];
948 memcpy(indexFields, fieldIndexes, count * sizeof(FieldIndex));
950 // TODO: USE CODED INDEX NAME INSTEAD?
951 strcpy(indexName, "index_");
952 strcat(indexName, name);
953 strcat(indexName, "_");
954 for(c = 0; c<count; c++)
956 if(fieldIndexes[c].field)
958 if(count == 1 && fieldIndexes[c].field == primaryKey)
960 strcat(indexName, fieldIndexes[c].field.name);
961 if(fieldIndexes[c].memberField)
963 strcat(indexName, ".");
964 strcat(indexName, fieldIndexes[c].memberField.name);
966 strcat(indexName, (fieldIndexes[c].order == ascending) ? "+" : "-");
972 sprintf(command, "CREATE INDEX IF NOT EXISTS `%s` ON `%s` (", indexName, name);
973 for(c = 0; c<count; c++)
975 char columnName[1024];
976 sprintf(columnName, "`%s` %s", fieldIndexes[c].field.name, (fieldIndexes[c].order == ascending) ? "ASC" : "DESC");
977 if(c > 0) strcat(command, ", ");
978 strcat(command, columnName);
980 strcat(command, ");");
981 result = sqlite3_exec(db.db, command, null, null, null);
983 return result == SQLITE_OK;
986 const String GetName()
991 Field GetFirstField()
993 return _fields.first;
996 Field GetPrimaryKey()
1001 uint GetFieldsCount()
1003 return _fields.count;
1013 sprintf(command, "SELECT COUNT(*) FROM `%s`;", name);
1014 result = sqlite3_get_table(db.db, command, &t, &nRows, &nCols, null);
1015 if(result == SQLITE_OK)
1017 rowCount = atoi(t[1]);
1018 sqlite3_free_table(t);
1023 // Returns true if not ordered by row ID
1024 bool GetIndexOrder(char * fullOrder, bool flip)
1026 if(!flip && (!indexFields || (indexFieldsCount == 1 && indexFields[0].field == primaryKey && indexFields[0].order == ascending)))
1028 strcpy(fullOrder, " ORDER BY ROWID");
1034 strcpy(fullOrder, " ORDER BY ");
1035 for(c = flip ? indexFieldsCount-1 : 0; flip ? (c >= 0) : (c < indexFieldsCount); flip ? c-- : c++)
1038 FieldIndex * fIndex = &indexFields[c];
1040 if(c) strcat(order, ", ");
1042 strcat(order, fIndex->field.name);
1044 if(fIndex->order == (flip ? ascending : descending)) strcat(order, " DESC");
1045 strcat(fullOrder, order);
1051 Container<Field> GetFields()
1053 return (Container<Field>)_fields;
1056 DriverRow CreateRow()
1059 sqlite3_stmt * statement;
1060 sqlite3_stmt * sysIDStmt = null, * insertStmt = null, * deleteStmt = null, * selectRowIDsStmt = null, * setRowIDStmt = null;
1061 sqlite3_stmt * prevStmt = null, * nextStmt = null, * lastStmt = null, * insertIDStmt = null;
1063 if(specialStatement)
1064 strcpy(command, specialStatement);
1068 /*sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ? = ?;", name);
1069 sqlite3_prepare_v2(db.db, command, -1, &findStmt, null);*/
1070 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", name);
1071 sqlite3_prepare_v2(db.db, command, -1, &sysIDStmt, null);
1073 sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", name);
1074 sqlite3_prepare_v2(db.db, command, -1, &insertStmt, null);
1076 sprintf(command, "INSERT INTO `%s` (ROWID) VALUES(?);", name);
1077 sqlite3_prepare_v2(db.db, command, -1, &insertIDStmt, null);
1079 sprintf(command, "DELETE FROM `%s` WHERE ROWID = ?;", name);
1080 sqlite3_prepare_v2(db.db, command, -1, &deleteStmt, null);
1082 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID < ? ORDER BY ROWID DESC LIMIT 1;", name);
1083 sqlite3_prepare_v2(db.db, command, -1, &prevStmt, null);
1085 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID > ? ORDER BY ROWID LIMIT 1;", name);
1086 sqlite3_prepare_v2(db.db, command, -1, &nextStmt, null);
1088 sprintf(command, "SELECT MAX(ROWID), * FROM `%s`", name);
1089 sqlite3_prepare_v2(db.db, command, -1, &lastStmt, null);
1091 /*sprintf(command, "UPDATE `%s` SET ? = ? WHERE ROWID = ?;", name);
1093 sqlite3_prepare_v2(db.db, command, -1, &updateStmt, null);*/
1095 GetIndexOrder(order, false);
1096 sprintf(command, "SELECT ROWID, * FROM `%s`%s;", name, order);
1098 sqlite3_prepare_v2(db.db, command, -1, &statement, null);
1100 sprintf(command, "SELECT ROWID FROM `%s` WHERE ROWID > ?", name);
1101 sqlite3_prepare_v2(db.db, command, -1, &selectRowIDsStmt, null);
1103 sprintf(command, "UPDATE `%s` SET ROWID = ? WHERE ROWID = ?", name);
1104 sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
1107 { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt,
1108 insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
1109 previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
1115 delete specialStatement;
1121 class SQLiteRow : DriverRow
1124 sqlite3_stmt * curStatement;
1126 sqlite3_stmt * defaultStatement;
1127 sqlite3_stmt * findStatement;
1128 sqlite3_stmt * prevFindStatement, * lastFindStatement;
1129 sqlite3_stmt * nextFindStatement;
1130 sqlite3_stmt * sysIDStatement;
1131 sqlite3_stmt * queryStatement;
1132 sqlite3_stmt * selectRowIDsStmt;
1133 sqlite3_stmt * setRowIDStmt;
1134 sqlite3_stmt * lastStatement;
1135 sqlite3_stmt * previousStatement;
1136 sqlite3_stmt * nextStatement;
1138 sqlite3_stmt * insertStatement;
1139 sqlite3_stmt * deleteStatement;
1140 sqlite3_stmt * updateStatement;
1141 sqlite3_stmt * insertIDStatement;
1145 // Because we use GoToSysID() and the sysIDStatement when searching for a primary key with Find(),
1146 // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
1157 if(defaultStatement) sqlite3_finalize(defaultStatement);
1158 if(findStatement) sqlite3_finalize(findStatement);
1159 if(prevFindStatement)sqlite3_finalize(prevFindStatement);
1160 if(lastFindStatement)sqlite3_finalize(lastFindStatement);
1161 if(nextFindStatement)sqlite3_finalize(nextFindStatement);
1162 if(sysIDStatement) sqlite3_finalize(sysIDStatement);
1163 if(insertStatement) sqlite3_finalize(insertStatement);
1164 if(deleteStatement) sqlite3_finalize(deleteStatement);
1165 if(updateStatement) sqlite3_finalize(updateStatement);
1166 if(queryStatement) sqlite3_finalize(queryStatement);
1167 if(selectRowIDsStmt) sqlite3_finalize(selectRowIDsStmt);
1168 if(setRowIDStmt) sqlite3_finalize(setRowIDStmt);
1169 if(previousStatement)sqlite3_finalize(previousStatement);
1170 if(nextStatement) sqlite3_finalize(nextStatement);
1171 if(lastStatement) sqlite3_finalize(lastStatement);
1172 if(insertIDStatement) sqlite3_finalize(insertIDStatement);
1175 bool Select(MoveOptions move)
1178 bool stepping = curStatement == previousStatement || curStatement == nextStatement || curStatement == lastStatement;
1180 curStatement = defaultStatement;
1185 sqlite3_reset(curStatement);
1186 result = sqlite3_step(curStatement);
1187 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1188 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1189 rowID = sqlite3_column_int64(curStatement, 0);
1194 sqlite3_reset(curStatement);
1195 curStatement = lastStatement;
1196 result = sqlite3_step(curStatement);
1197 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1198 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1199 rowID = sqlite3_column_int64(curStatement, 0);
1207 // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
1208 if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) ||
1209 (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
1211 result = sqlite3_step(curStatement);
1212 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1213 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1214 rowID = sqlite3_column_int64(curStatement, 0);
1216 else if(curStatement == prevFindStatement || curStatement == findStatement || curStatement == nextFindStatement || curStatement == lastFindStatement)
1220 int bindId = findBindId;
1221 sqlite3_reset((move == next) ? nextFindStatement : prevFindStatement);
1222 BindCursorData((move == next) ? nextFindStatement : prevFindStatement, move,
1223 (move == next && (!tbl.indexFields || (tbl.indexFieldsCount == 1 && tbl.indexFields[0].field == tbl.primaryKey && tbl.indexFields[0].order == ascending))) ? false : true, &bindId);
1224 sqlite3_reset(curStatement);
1225 curStatement = (move == next) ? nextFindStatement : prevFindStatement;
1226 result = sqlite3_step(curStatement);
1227 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1228 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1229 rowID = sqlite3_column_int64(curStatement, 0);
1233 sqlite3_reset((move == next) ? findStatement : lastFindStatement);
1234 sqlite3_reset(curStatement);
1235 curStatement = (move == next) ? findStatement : lastFindStatement;
1236 result = sqlite3_step(curStatement);
1237 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1238 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1239 rowID = sqlite3_column_int64(curStatement, 0);
1244 sqlite3_reset(curStatement);
1245 curStatement = (move == previous) ? (rowID ? previousStatement : lastStatement) : (rowID ? nextStatement : defaultStatement);
1246 sqlite3_bind_int64(curStatement, 1, (sqlite3_int64)rowID);
1247 result = sqlite3_step(curStatement);
1248 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1249 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1250 rowID = sqlite3_column_int64(curStatement, 0);
1255 sqlite3_reset(curStatement);
1265 bool Query(const char * queryString)
1271 sqlite3_reset(curStatement);
1274 sqlite3_finalize(queryStatement);
1275 queryStatement = null;
1280 result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
1283 curStatement = queryStatement;
1284 if(!strchr(queryString, '?'))
1286 result = sqlite3_step(queryStatement);
1288 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1289 if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
1291 rowID = sqlite3_column_int64(queryStatement, 0);
1298 curStatement = null;
1302 bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
1305 Class dataType = fld.type;
1306 SerialBuffer buffer = null;
1307 switch(fld.sqliteType)
1309 case SQLITE_INTEGER:
1311 switch(dataType.typeSize)
1314 result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
1317 result = sqlite3_bind_int(statement, pos, *(int *)data);
1323 value = (int)*(short *)data;
1325 value = (int)*(uint16 *)data;
1326 result = sqlite3_bind_int(statement, pos, value);
1333 value = (int)*(char *)data;
1335 value = (int)*(byte *)data;
1336 result = sqlite3_bind_int(statement, pos, value);
1344 if(dataType.typeSize == 8)
1345 result = sqlite3_bind_double(statement, pos, *(double *)data);
1347 result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
1353 result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1355 result = sqlite3_bind_null(statement, pos);
1363 buffer = SerialBuffer { };
1364 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnSerialize])(dataType, data, buffer);
1365 result = sqlite3_bind_text(statement, pos, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1368 result = sqlite3_bind_null(statement, pos);
1373 *bufferOut = buffer;
1379 void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
1381 if(move == next || move == previous)
1383 // Where clauses for index
1387 bool gotPrimaryKey = false;
1389 strcatf(command, " AND (");
1390 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1393 FieldIndex * fIndex = &tbl.indexFields[c];
1397 strcat(where, fIndex->field.name);
1398 strcat(where, "` ");
1399 strcat(where, (fIndex->order == ((move == next) ? descending : ascending)) ? "<" : ">");
1400 strcat(where, " ? OR (");
1401 strcat(where, fIndex->field.name);
1402 if(fIndex->field == tbl.primaryKey)
1403 gotPrimaryKey = true;
1404 strcat(where, " = ? AND (");
1405 strcat(command, where);
1407 strcat(command, gotPrimaryKey ? "1)" : ((move == next) ? "ROWID > ?)" : "ROWID < ?)"));
1408 for(c = 0; c < tbl.indexFieldsCount; c++)
1409 strcat(command, "))");
1412 strcatf(command, (move == next) ? " AND ROWID > ?" : " AND ROWID < ?");
1416 void BindCursorData(sqlite3_stmt * stmt, MoveOptions move, bool useIndex, int * bindId)
1418 if(move == next || move == previous)
1420 // The binds for the Extra ordering Where clauses
1424 /* // Code to not rely on curStatement being set up
1425 SQLiteRow dataRow = (SQLiteRow)tbl.CreateRow();
1426 dataRow.GoToSysID((uint)rowID);
1428 for(c = ((move == next) ? 0 : tbl.indexFieldsCount-1); (move == next) ? c < tbl.indexFieldsCount : c >= 0; (move == next) ? c++ : c--)
1430 FieldIndex * fIndex = &tbl.indexFields[c];
1432 SQLiteField fld = (SQLiteField)fIndex->field;
1433 Class type = fld.type;
1435 SerialBuffer buffer;
1437 if(type.type == unitClass && !type.typeSize)
1439 Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
1443 if(type.type == structClass)
1445 data = (int64)new0 byte[type.structSize];
1446 dataPtr = (void *) data;
1448 // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
1449 ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)data : &data);
1450 if(type.type == normalClass || type.type == noHeadClass)
1451 dataPtr = (void *) data;
1454 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
1455 // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
1456 // Reuse the buffer for Blobs...
1457 if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
1459 sqlite3_bind_text(stmt, (*bindId)++, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1463 ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
1465 ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, dataPtr);
1470 // Bind for the rowid
1471 sqlite3_bind_int64(stmt, (*bindId)++, (sqlite3_int64)rowID);
1475 bool Find(Field fld, MoveOptions move, MatchOptions match, typed_object data)
1477 char order[1024], command[2048];
1480 sqlite3_stmt * stmt = null;
1483 if(fld == tbl.primaryKey)
1485 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1486 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1487 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1488 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1489 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1490 result = GoToSysID(*(int *)data);
1496 useIndex = tbl.GetIndexOrder(order, false);
1498 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1499 AddCursorWhereClauses(command, move, useIndex);
1500 strcat(command, order);
1501 strcat(command, ";");
1502 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1503 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1504 BindCursorData(stmt, move, useIndex, &bindId);
1506 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1507 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1508 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1509 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1510 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1511 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1513 curStatement = findStatement = stmt;
1514 findBindId = bindId;
1516 // For going back to forward find
1518 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1519 AddCursorWhereClauses(command, next, useIndex);
1520 strcat(command, order);
1521 strcat(command, ";");
1522 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1523 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1524 nextFindStatement = stmt;
1527 tbl.GetIndexOrder(order, true);
1528 // For tracing back finds
1530 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1531 AddCursorWhereClauses(command, previous, true);
1532 strcat(command, order);
1533 strcat(command, ";");
1534 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1535 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1536 prevFindStatement = stmt;
1538 // For tracing back from last
1540 sprintf(command, "SELECT ROWID, * FROM `%s` WHERE `%s` = ?", tbl.name, fld.name);
1541 strcat(command, order);
1542 strcat(command, ";");
1543 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1544 BindData(stmt, bindId++, (SQLiteField)fld, data, null);
1545 lastFindStatement = stmt;
1547 result = sqlite3_step(findStatement);
1549 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1553 sqlite3_reset(findStatement);
1556 rowID = sqlite3_column_int64(findStatement, 0);
1560 bool FindMultiple(FieldFindData * findData, MoveOptions move, int numFields)
1563 for(c = 0; c < numFields; c++) \
1565 FieldFindData * fieldFind = &findData[c]; \
1566 SQLiteField sqlFld = (SQLiteField)findData->field; \
1567 Class dataType = sqlFld.type; \
1568 BindData(stmt, bindId++, sqlFld, (dataType.type == structClass || dataType.type == noHeadClass || dataType.type == normalClass) ? fieldFind->value.p : &fieldFind->value.i, null); \
1573 char criterias[4096], command[4096], order[1024];
1577 sqlite3_stmt * stmt = null;
1581 sprintf(criterias, "SELECT ROWID, * FROM `%s` WHERE `", tbl.name);
1582 for(c = 0; c < numFields; c++)
1584 FieldFindData * fieldFind = &findData[c];
1586 if(c) strcat(criterias, " AND `");
1587 strcat(criterias, fieldFind->field.name);
1588 strcat(criterias, "` = ?");
1591 useIndex = tbl.GetIndexOrder(order, false);
1592 // Basic Find (multiple)
1593 strcpy(command, criterias);
1594 AddCursorWhereClauses(command, move, useIndex);
1595 strcat(command, order);
1596 strcat(command, ";");
1597 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1599 BindCursorData(stmt, move, useIndex, &bindId);
1601 // Currently, we can't reset curStatement until after BindCursorData, as current data is read from it
1602 if(curStatement) { sqlite3_reset(curStatement); curStatement = null; }
1603 if(findStatement) { sqlite3_finalize(findStatement); findStatement = null; }
1604 if(nextFindStatement) { sqlite3_finalize(nextFindStatement); nextFindStatement = null; }
1605 if(prevFindStatement) { sqlite3_finalize(prevFindStatement); prevFindStatement = null; }
1606 if(lastFindStatement) { sqlite3_finalize(lastFindStatement); lastFindStatement = null; }
1608 curStatement = findStatement = stmt;
1609 findBindId = bindId;
1611 // For tracing back forward finds
1613 strcpy(command, criterias);
1614 AddCursorWhereClauses(command, previous, true);
1615 strcat(command, order);
1616 strcat(command, ";");
1617 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1619 nextFindStatement = stmt;
1622 tbl.GetIndexOrder(order, true);
1623 // For tracing back finds
1625 strcpy(command, criterias);
1626 AddCursorWhereClauses(command, next, useIndex);
1627 strcat(command, order);
1628 strcat(command, ";");
1629 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1631 prevFindStatement = stmt;
1633 // For tracing back from last
1635 strcpy(command, criterias);
1636 strcat(command, order);
1637 strcat(command, ";");
1638 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &stmt, null);
1640 lastFindStatement = stmt;
1642 result = sqlite3_step(findStatement);
1643 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1647 sqlite3_reset(findStatement);
1650 rowID = sqlite3_column_int64(findStatement, 0);
1656 bool Synch(DriverRow to)
1658 SQLiteRow rowTo = (SQLiteRow)to;
1659 if(tbl && rowTo.tbl && !strcmp(tbl.name, rowTo.tbl.name))
1660 return GoToSysID((uint)rowTo.rowID);
1667 //char command[1024];
1668 //sprintf(command, "INSERT INTO `%s` DEFAULT VALUES;", tbl.name);
1669 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1672 sqlite3_bind_int64(insertIDStatement, 1, (sqlite3_int64)id);
1673 result = sqlite3_step(insertIDStatement);
1676 result = sqlite3_step(insertStatement);
1677 if(result == SQLITE_DONE) // if(result == SQLITE_OK)
1679 rowID = sqlite3_last_insert_rowid(tbl.db.db);
1680 if(rowID > MAXDWORD)
1682 int64 lastID = tbl.lastID;
1684 sqlite3_bind_int64(selectRowIDsStmt, 1, (sqlite3_int64)lastID);
1688 result = sqlite3_step(selectRowIDsStmt);
1689 if(result == SQLITE_DONE || result != SQLITE_ROW) break;
1690 id = sqlite3_column_int64(selectRowIDsStmt, 0);
1691 if(id - lastID > 1) break;
1694 sqlite3_reset(selectRowIDsStmt);
1696 sqlite3_bind_int64(setRowIDStmt, 2, (sqlite3_int64)rowID);
1699 sqlite3_bind_int64(setRowIDStmt, 1, (sqlite3_int64)rowID);
1700 result = sqlite3_step(setRowIDStmt);
1701 sqlite3_reset(setRowIDStmt);
1703 sqlite3_reset(id ? insertIDStatement : insertStatement);
1704 curStatement = sysIDStatement;
1706 sqlite3_reset(curStatement);
1707 sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
1708 result = sqlite3_step(curStatement);
1709 done = false; // Make sure 'nil' is false
1712 sqlite3_reset(insertStatement);
1719 //char command[1024];
1720 //sprintf(command, "DELETE FROM `%s` WHERE ROWID = %d;", tbl.name, rowID);
1721 //result = sqlite3_exec(tbl.db.db, command, null, null, null);
1722 sqlite3_bind_int64(deleteStatement, 1, (sqlite3_int64)rowID);
1723 result = sqlite3_step(deleteStatement);
1724 sqlite3_reset(deleteStatement);
1726 return result == SQLITE_OK || result == SQLITE_DONE;
1729 bool GetData(Field fld, typed_object &data)
1731 SQLiteField sqlFld = (SQLiteField)fld;
1732 int num = sqlFld.num + 1;
1733 Class dataType = sqlFld.type;
1736 switch(sqlFld.sqliteType)
1738 case SQLITE_INTEGER:
1740 switch(dataType.typeSize)
1743 if(fld == tbl.primaryKey)
1744 *(int64 *)data = rowID;
1746 *(int64 *)data = sqlite3_column_int64(curStatement, num);
1749 if(fld == tbl.primaryKey)
1750 *(int *)data = (int)(uint)rowID;
1752 *(int *)data = sqlite3_column_int(curStatement, num);
1757 if(fld == tbl.primaryKey)
1758 value = (int)(uint)rowID;
1760 value = sqlite3_column_int(curStatement, num);
1762 *(short *)data = (short)value;
1764 *(uint16 *)data = (uint16)value;
1770 if(fld == tbl.primaryKey)
1771 value = (int)(uint)rowID;
1773 value = sqlite3_column_int(curStatement, num);
1775 *(char *)data = (char)value;
1777 *(byte *)data = (byte)value;
1785 double d = sqlite3_column_double(curStatement, num);
1786 if(dataType.typeSize == 8)
1787 *(double *)data = d;
1789 *(float *)data = (float)d;
1794 int numBytes = sqlite3_column_bytes(curStatement, num);
1795 const char * text = (const char *)sqlite3_column_text(curStatement, num);
1796 *(char **)data = text ? new byte[numBytes+1] : null;
1798 memcpy(*(char **)data, text, numBytes+1);
1803 SerialBuffer buffer { };
1804 //buffer._buffer = sqlite3_column_blob(curStatement, num);
1805 buffer._size = sqlite3_column_bytes(curStatement, num);
1806 buffer._buffer = (byte *)sqlite3_column_text(curStatement, num);
1807 buffer.count = buffer._size;
1809 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnUnserialize])(dataType, data, buffer);
1811 buffer._buffer = null;
1819 bool SetData(Field fld, typed_object data)
1821 SQLiteField sqlFld = (SQLiteField)fld;
1826 sqlite3_finalize(updateStatement);
1827 sprintf(command, "UPDATE `%s` SET `%s` = ? WHERE ROWID = ?;", tbl.name, sqlFld.name);
1828 result = sqlite3_prepare_v2(tbl.db.db, command, -1, &updateStatement, null);
1829 sqlite3_bind_int64(updateStatement, 2, (sqlite3_int64)rowID);
1830 BindData(updateStatement, 1, (SQLiteField)fld, data, null);
1831 result = sqlite3_step(updateStatement);
1832 sqlite3_reset(updateStatement);
1833 if(fld == tbl.primaryKey)
1834 rowID = *(uint *)data;
1835 return result == SQLITE_DONE;
1840 return (int)(uint)rowID;
1843 bool GoToSysID(uint id)
1845 //char command[1024];
1849 //sqlite3_finalize(statement);
1850 //sprintf(command, "SELECT ROWID, * FROM `%s` WHERE ROWID = ?;", tbl.name);
1851 //result = sqlite3_prepare_v2(tbl.db.db, command, -1, &statement, null);
1855 sqlite3_reset(curStatement);
1857 curStatement = sysIDStatement;
1858 sqlite3_reset(sysIDStatement);
1859 sqlite3_bind_int64(curStatement, 1, (sqlite_int64)rowID);
1860 result = sqlite3_step(curStatement);
1861 done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
1862 if(done) { rowID = 0; sqlite3_reset(curStatement); return false; }
1866 bool SetQueryParam(int paramID, int value)
1869 if(curStatement != queryStatement)
1871 if(curStatement) sqlite3_reset(curStatement);
1872 curStatement = queryStatement;
1874 sqlite3_reset(queryStatement);
1875 result = sqlite3_bind_int(queryStatement, paramID, value);
1879 bool SetQueryParam64(int paramID, int64 value)
1882 if(curStatement != queryStatement)
1884 if(curStatement) sqlite3_reset(curStatement);
1885 curStatement = queryStatement;
1887 sqlite3_reset(queryStatement);
1888 result = sqlite3_bind_int64(queryStatement, paramID, (sqlite_int64)value);
1892 bool SetQueryParamText(int paramID, const char * data)
1895 if(curStatement != queryStatement)
1897 if(curStatement) sqlite3_reset(curStatement);
1898 curStatement = queryStatement;
1900 sqlite3_reset(queryStatement);
1902 result = sqlite3_bind_text(queryStatement, paramID, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
1904 result = sqlite3_bind_text(queryStatement, paramID, null, 0, SQLITE_TRANSIENT);
1908 bool SetQueryParamObject(int paramID, const void * data, Class type)
1911 if(curStatement != queryStatement)
1913 if(curStatement) sqlite3_reset(curStatement);
1914 curStatement = queryStatement;
1916 sqlite3_reset(queryStatement);
1918 SerialBuffer buffer { };
1919 ((void (*)(void *, const void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnSerialize])(type, data, buffer);
1920 result = sqlite3_bind_text(queryStatement, paramID, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
1926 bool BindQueryData(int pos, SQLiteField fld, typed_object data)
1928 if(curStatement != queryStatement)
1930 if(curStatement) sqlite3_reset(curStatement);
1931 curStatement = queryStatement;
1933 sqlite3_reset(queryStatement);
1934 return BindData(queryStatement, pos, fld, data, null);
1937 /*char * GetExtraColumn(int paramID)
1939 SQLiteField lastFld = tbl._fields.last;
1940 return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
1942 const char * GetColumn(int paramID)
1944 return (const char *)sqlite3_column_text(curStatement, paramID);