eda/drivers/SQLite: Printing query errors
[sdk] / eda / drivers / sqlite / EDASQLite.ec
index 61e11e4..68d3ab2 100644 (file)
@@ -12,7 +12,11 @@ public import "EDA"
 #include "sqlite3.h"
 #endif
 
-static void UnusedFunction()
+#define uint _uint
+#include "ffi.h"
+#undef uint
+
+__attribute__((unused)) static void UnusedFunction()
 {
    int a;
    a.OnGetString(0,0,0);
@@ -36,20 +40,20 @@ extern int __ecereVMethodID_class_OnUnserialize;
 extern int __ecereVMethodID_class_OnFree;
 private:
 
-int CollationCompare(Class type, int count1, void * data1, int count2, void * data2)
+int CollationCompare(Class type, int count1, const void * data1, int count2, const void * data2)
 {
    if(type.type == normalClass || type.type ==  noHeadClass)
    {
       Instance inst1, inst2;
       int result;
-      SerialBuffer buffer1 { size = count1, count = count1, buffer = data1 };
-      SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
+      SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
+      SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
+
+      ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst1, buffer1);
+      ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, &inst2, buffer2);
 
-      type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst1, buffer1);
-      type._vTbl[__ecereVMethodID_class_OnUnserialize](type, &inst2, buffer2);
+      result = ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
 
-      result = type._vTbl[__ecereVMethodID_class_OnCompare](type, inst1, inst2);
-     
       buffer1.buffer = null;
       buffer2.buffer = null;
       delete buffer1;
@@ -62,16 +66,16 @@ int CollationCompare(Class type, int count1, void * data1, int count2, void * da
    {
       void * inst1, * inst2;
       int result;
-      SerialBuffer buffer1 { size = count1, count = count1, buffer = data1 };
-      SerialBuffer buffer2 { size = count2, count = count2, buffer = data2 };
+      SerialBuffer buffer1 { size = count1, count = count1, buffer = (byte *)data1 };
+      SerialBuffer buffer2 { size = count2, count = count2, buffer = (byte *)data2 };
 
       inst1 = new0 byte[type.structSize];
       inst2 = new0 byte[type.structSize];
-      type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst1, buffer1);
-      type._vTbl[__ecereVMethodID_class_OnUnserialize](type, inst2, buffer2);
+      ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst1, buffer1);
+      ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnUnserialize])(type, inst2, buffer2);
+
+      result = ((int (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, inst1, inst2);
 
-      result = type._vTbl[__ecereVMethodID_class_OnCompare](type, inst1, inst2);
-     
       buffer1.buffer = null;
       buffer2.buffer = null;
       delete buffer1;
@@ -81,98 +85,15 @@ int CollationCompare(Class type, int count1, void * data1, int count2, void * da
       return result;
    }
    else
-      return type._vTbl[__ecereVMethodID_class_OnCompare](type, data1, data2);
+      return ((int (*)(void *, const void *, const void *))(void *)type._vTbl[__ecereVMethodID_class_OnCompare])(type, data1, data2);
 }
 
 public class SQLiteStaticLink { }   // Until .imp generation is fixed
 
-class SQLiteDataSource : DataSourceDriver
+class SQLiteDataSource : DirFilesDataSourceDriver
 {
    class_property(name) = "SQLite";
-   String path;
-   OldList listDatabases;
-   uint databasesCount;
-
-   String BuildLocator(DataSource ds)
-   {
-      return CopyString(ds.host);
-   }
-
-   uint GetDatabasesCount()
-   {
-      return databasesCount;
-   }
-
-   ~SQLiteDataSource()
-   {
-      delete path;
-   }
-
-   bool Connect(const String locator)
-   {
-      delete path;
-      path = CopyString(locator);
-      // TODO, use user name and password for local security?
-      // TODO, open ds in read or write mode
-      if(FileExists(path))
-      {
-         int n = 0;
-         FileListing listing { path, "sqlite" };
-         databasesCount = 0;
-         while(listing.Find())
-            databasesCount++;
-         return true;
-      }
-      return false;
-   }
-
-   bool RenameDatabase(const String name, const String rename)
-   {
-      if(name && rename && path && FileExists(path))
-      {
-         String path;
-         path = MakeDatabasePath(name);
-         if(FileExists(path))
-         {
-            bool renamed;
-            String repath;
-            repath = MakeDatabasePath(rename);
-            renamed = RenameFile(path, repath);
-            delete path;
-            delete repath;
-            return renamed;
-         }
-         delete path;
-      }
-      return false;
-   }
-
-   bool DeleteDatabase(const String name)
-   {
-      if(path && FileExists(path))
-      {
-         bool deleted;
-         String path = MakeDatabasePath(name);
-         deleted = DeleteFile(path);  // delete file seems to return true even if the file does not exist
-         databasesCount--;
-         delete path;
-         return deleted;
-      }
-      return false;
-   }
-
-   virtual String MakeDatabasePath(const String name)
-   {
-      if(name)
-      {
-         char build[MAX_LOCATION];
-         strcpy(build, path ? path : "");
-         PathCat(build, name);
-         ChangeExtension(build, "sqlite", build);
-         return CopyString(build);
-      }
-      return null;
-   }
+   class_property(databaseFileExtension) = "sqlite";
 
    Database OpenDatabase(const String name, CreateOptions createOptions, DataSource ds)
    {
@@ -184,7 +105,7 @@ class SQLiteDataSource : DataSourceDriver
 
          // sqlite3_open(path, &db);
          // sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY /*SQLITE_OPEN_READWRITE*/ /*SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE*/, null );
-         
+
          if(sqlite3_open_v2(path, &db, (createOptions == readOnly) ? SQLITE_OPEN_READONLY :
             (SQLITE_OPEN_READWRITE | ((createOptions == create) ? SQLITE_OPEN_CREATE : 0)), null))
          {
@@ -194,11 +115,23 @@ class SQLiteDataSource : DataSourceDriver
          }
          else
          {
+            bool success = true;
             char command[1024];
             sprintf(command, "CREATE TABLE eda_table_fields(Table_Name TEXT, Name TEXT, Type TEXT, Length INT);");
             sqlite3_exec(db, command, null, null, null);
 
-            result = SQLiteDatabase { db = db };
+            if(createOptions != readOnly)
+            {
+               sqlite3_exec(db, "PRAGMA locking_mode=exclusive", null, null, null);
+               sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
+               if(sqlite3_exec(db, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('lockDummy', 'lockDummy', 'lockDummy', 'lockDummy')", null, null, null))
+                  success = false;
+               else
+                  sqlite3_exec(db, "DELETE FROM eda_table_fields WHERE Name = 'lockDummy'", null, null, null);
+            }
+
+            if(success)
+               result = SQLiteDatabase { db = db };
          }
          delete path;
       }
@@ -221,7 +154,7 @@ class SQLiteField : Field
       delete name;
    }
 
-   String GetName()
+   const String GetName()
    {
       return name;
    }
@@ -247,10 +180,13 @@ class SQLiteField : Field
 class SQLiteDatabase : Database
 {
    sqlite3 * db;
-   AVLTree<String> collations { };
-   
+   AVLTree<const String> collations { };
+
    ~SQLiteDatabase()
    {
+      sqlite3_exec(db, "PRAGMA locking_mode=normal", null, null, null);
+      // "Simply setting the locking-mode to NORMAL is not enough - locks are not released until the next time the database file is accessed."
+      sqlite3_exec(db, "SELECT COUNT(*) from eda_table_fields", null, null, null);
       sqlite3_close(db);
    }
 
@@ -275,7 +211,7 @@ class SQLiteDatabase : Database
    Table OpenTable(const String name, OpenOptions options)
    {
       char command[1024];
-      int result;
+      //int result;
       int nRows = 0, nCols = 0;
       char ** t;
       SQLiteTable table = null;
@@ -287,7 +223,7 @@ class SQLiteDatabase : Database
          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
          LinkTable(table);
          incref field;
-         table.fields.Add(field);
+         table._fields.Add(field);
       }
       else if(options.type == fieldsList)
       {
@@ -298,20 +234,20 @@ class SQLiteDatabase : Database
          LinkTable(table);
          field = { tbl = table, name = CopyString("Name"), type = class(String), num = -1, sqliteType = SQLITE_TEXT };
          incref field;
-         table.fields.Add(field);
+         table._fields.Add(field);
          field = { tbl = table, name = CopyString("Type"), type = class(Class), num = 0, sqliteType = SQLITE_TEXT };
          incref field;
-         table.fields.Add(field);
+         table._fields.Add(field);
          field = { tbl = table, name = CopyString("Length"), type = class(int), num = 1, sqliteType = SQLITE_INTEGER };
          incref field;
-         table.fields.Add(field);
+         table._fields.Add(field);
       }
       else if(options.type == tableRows)
       {
          bool addFields = false;
 
          sprintf(command, "SELECT Name FROM eda_table_fields WHERE Table_Name='%s';", name);
-         result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
+         /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
          if(!nRows && !nCols)
             addFields = true;
 
@@ -319,8 +255,8 @@ class SQLiteDatabase : Database
 
          sprintf(command, "SELECT sql FROM sqlite_master WHERE type='table' AND name='%s';", name);
          nCols = 0, nRows = 0;
-         result = sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
-         
+         /*result = */sqlite3_get_table(db, command, &t, &nRows, &nCols, null);
+
          if((nCols || nRows) || options.create)
          {
             table = SQLiteTable { db = this, name = CopyString(name) };
@@ -336,7 +272,7 @@ class SQLiteDatabase : Database
                   {
                      char * sql = t[nCols * r];
                      char * bracket = strchr(sql, '(');
-                     if(bracket) 
+                     if(bracket)
                      {
                         int c = 0;
                         bracket++;
@@ -366,7 +302,7 @@ class SQLiteDatabase : Database
                            dataType[c - d - 2] = 0;
 
                            while(ch && bracket[c] == ' ') c++;
-                           
+
                            if(!strcmp(dataType, "REAL")) { sqliteType = SQLITE_FLOAT; type = class(double); }
                            else if(!strcmp(dataType, "TEXT")) { sqliteType = SQLITE_TEXT; type = class(String); }
                            else if(!strcmp(dataType, "INTEGER")) { sqliteType = SQLITE_INTEGER; type = class(int); }
@@ -374,12 +310,12 @@ class SQLiteDatabase : Database
 
                            sprintf(command, "INSERT INTO eda_table_fields (Table_Name, Name, Type, Length) VALUES ('%s', '%s', '%s', %d);", name,
                               fieldName, type.name, 0);
-                           result = sqlite3_exec(db, command, null, null, null);
+                           /*result = */sqlite3_exec(db, command, null, null, null);
 
                            {
-                              SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table.fields.count, sqliteType = sqliteType };
+                              SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, num = table._fields.count, sqliteType = sqliteType };
                               incref field;
-                              table.fields.Add(field);
+                              table._fields.Add(field);
                            }
 
                            if(!ch || ch == ')') break;
@@ -389,27 +325,28 @@ class SQLiteDatabase : Database
                }
                else
                {
+                  Table refTable = null;
                   sqlite3_stmt * statement;
-                  
+
                   sprintf(command, "SELECT Name, Type, Length FROM eda_table_fields WHERE Table_Name='%s';", name);
-                  result = sqlite3_prepare_v2(db, command, -1, &statement, null);
+                  /*result = */sqlite3_prepare_v2(db, command, -1, &statement, null);
 
                   while(sqlite3_step(statement) != SQLITE_DONE)
                   {
-                     char * fieldName = sqlite3_column_text(statement, 0);
-                     char * typeName = sqlite3_column_text(statement, 1);
+                     const char * fieldName = (const char *)sqlite3_column_text(statement, 0);
+                     const char * typeName = (const char *)sqlite3_column_text(statement, 1);
                      int length = sqlite3_column_int(statement, 2);
                      Class type = null;
                      int sqliteType = SQLITE_BLOB;
 
-                     ((Class)(&type)).OnGetDataFromString(typeName);    // TODO: THIS REQUIRES A FIX SOMEWHERE ELSE
+                     type.OnGetDataFromString(typeName);
 
                      if(type)
                      {
-                        if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || 
-                           !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") || 
-                           !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") || 
-                           !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || 
+                        if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
+                           !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
+                           !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
+                           !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
                            !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
                            !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
                            sqliteType = SQLITE_INTEGER;
@@ -429,9 +366,14 @@ class SQLiteDatabase : Database
                      }
 
                      {
-                        SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table.fields.count, sqliteType = sqliteType };
+                        Table * fTable = (Table *)(intptr)eClass_GetProperty(type, "table");
+                        SQLiteField field { tbl = table, name = CopyString(fieldName), type = type, length = length, num = table._fields.count, sqliteType = sqliteType };
                         incref field;
-                        table.fields.Add(field);
+                        if(fTable) refTable = *fTable;
+                        if(!table.primaryKey && refTable && !strcmp(refTable.name, table.name))
+                           table.primaryKey = field;
+
+                        table._fields.Add(field);
                      }
                   }
                   sqlite3_finalize(statement);
@@ -465,21 +407,371 @@ class SQLiteDatabase : Database
       return result == SQLITE_OK;
    }
 
-   bool CreateCustomFunction(char * name, SQLCustomFunction customFunction)
+   bool CreateCustomFunction(const char * name, SQLCustomFunction customFunction)
    {
-      int result = sqlite3_create_function(db, name, 1, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null);
-      return result == SQLITE_OK;
+      bool result = false;
+      Class cfClass = customFunction._class;
+      customFunction.method = eClass_FindMethod(cfClass, "function", cfClass.module);
+      if(customFunction.method)
+      {
+         String typeString = CopyString(customFunction.method.dataTypeString);
+         char * tokens[256];
+         int count = TokenizeWith(typeString, sizeof(tokens)/sizeof(tokens[0]), tokens, "(,)", false);
+         int c;
+         bool variadic = false;
+
+         for(c = 0; c < count; c++)
+         {
+            Class type = null;
+            bool pointer = false;
+            const String arg = tokens[c];
+            char * space;
+            TrimLSpaces(tokens[c], tokens[c]);
+            if(strchr(arg, '*')) pointer = true;
+            if(pointer)
+               // Using String for generic pointer...
+               type = class(String);
+            else
+            {
+               if((space = strchr(arg, ' '))) *space = 0;
+               if(!strcmp(arg, "void"))
+                  type = null;
+               else if(!strcmp(arg, "..."))
+                  variadic = true;
+               else
+               {
+                  if(cfClass.templateParams.count)
+                  {
+                     ClassTemplateParameter p;
+                     int id = 0;
+                     for(p = cfClass.templateParams.first; p; p = p.next, id++)
+                     {
+                        if(!strcmp(p.name, arg))
+                           break;
+                     }
+                     if(p && cfClass.templateArgs)
+                        arg = cfClass.templateArgs[id].dataTypeString;
+                  }
+                  type = eSystem_FindClass(customFunction._class.module, arg);
+                  if(!type)
+                     type = eSystem_FindClass(customFunction._class.module.application, arg);
+               }
+            }
+            if(c == 0)
+               customFunction.returnType = type;
+            else
+               customFunction.args.Add(type);
+         }
+         delete typeString;
+         if(variadic)
+         {
+            result = false;
+            // Variadic args don't make sense for SQL custom functions
+            // Note that different CIF must be prepared for different set of arguments
+            // ffi_prep_cif_var(&customFunction.cif, FFI_DEFAULT_ABI, args.count-1, rType, argTypes);
+         }
+         else
+         {
+            customFunction.rType = FFIGetType(customFunction.returnType, true);
+            customFunction.argTypes.Add((void *)&ffi_type_pointer);    // This pointer for SQLCustomFunction object
+            for(a : customFunction.args) customFunction.argTypes.Add((void *)FFIGetType(a, false));
+            ffi_prep_cif(&customFunction.cif, FFI_DEFAULT_ABI, customFunction.argTypes.count, customFunction.rType, (ffi_type **) customFunction.argTypes.array);
+            result = sqlite3_create_function(db, name, customFunction.args.count, SQLITE_UTF8, customFunction, SQLiteFunctionProcessor, null, null) == SQLITE_OK;
+         }
+      }
+      return result;
    }
 }
 
-void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** value)
+static class FFITypesHolder : Map<Class, String> { ~FFITypesHolder() { Free(); } }
+FFITypesHolder structFFITypes { };
+__attribute__((unused)) static Iterator dummy; // TOFIX: forward struct declaration issues on Clang
+
+public ffi_type * FFIGetType(Class type, bool structByValue)
+{
+   if(type)
+      switch(type.type)
+      {
+         // Pointer Types
+         case structClass:
+            if(structByValue)
+            {
+               MapIterator<Class, String> it { map = structFFITypes };
+               ffi_type * ffiType = null;
+               if(it.Index(type, false))
+                  ffiType = (void *)it.data;
+               else
+               {
+                  /*
+                  DataMember member;
+                  Array<String> memberTypes { };
+                  for(member = type.membersAndProperties.first; member; member = member.next)
+                  {
+                     if(!member.isProperty)
+                     {
+                        memberTypes.Add(FFIGetType(member.dataType
+                     }
+                  }
+                  */
+                  ffiType = new0 ffi_type[1];
+                  ffiType->size = type.structSize;
+                  ffiType->type = FFI_TYPE_STRUCT;
+                  structFFITypes[type] = (void *)ffiType;
+               }
+               return ffiType;
+            }
+         case normalClass:
+         case noHeadClass:
+         case unionClass:
+            return &ffi_type_pointer;
+         // Scalar Types
+         case bitClass:
+         case enumClass:
+         case systemClass:
+         case unitClass:
+                 if(!strcmp(type.dataTypeString, "float"))  return &ffi_type_float;
+            else if(!strcmp(type.dataTypeString, "double")) return &ffi_type_double;
+            else
+               switch(type.typeSize)
+               {
+                  case 1: return &ffi_type_uint8;
+                  case 2: return &ffi_type_uint16;
+                  case 4: return &ffi_type_uint32;
+                  case 8: return &ffi_type_uint64;
+               }
+      }
+   else
+      return &ffi_type_void;
+   return null;
+}
+
+static SerialBuffer staticBuffer { };
+void SQLiteFunctionProcessor(sqlite3_context* context, int n, sqlite3_value** values)
 {
    SQLCustomFunction sqlFunction = sqlite3_user_data(context);
-   char * text = sqlite3_value_text(*value);
-   sqlFunction.array.size = 1;
-   sqlFunction.array[0] = 0;
-   sqlFunction.Process(text);
-   sqlite3_result_text(context, sqlFunction.array.array, sqlFunction.array.count ? sqlFunction.array.count - 1 : 0, SQLITE_TRANSIENT);
+
+   /*  // Simple 1 pointer param returning a string
+   void * p = sqlFunction.method.function(sqlFunction, sqlite3_value_text(values[0]));
+   sqlite3_result_text(context, p, strlen(p), SQLITE_TRANSIENT);
+   */
+   int64 retData = 0;
+   void * ret = &retData;
+   Array<String> args { size = sqlFunction.args.count + 1 };
+   Iterator<String> ffiArg { sqlFunction.argTypes };
+   Iterator<String> arg { args };
+   int i = 0;
+
+   // this * for the SQLCustomFunction
+   args[0] = (void *)&sqlFunction;
+   ffiArg.Next();
+   // Get the arguments from SQLite
+   for(a : sqlFunction.args)
+   {
+      ffi_type * type = (ffi_type *)sqlFunction.argTypes[i+1];
+      if(i >= n) break;
+      switch(a.type)
+      {
+         case normalClass:
+         case noHeadClass:
+         case structClass:
+         case unionClass:
+         {
+            void ** data = new void *[1];
+            args[i+1] = (void *)data;
+            if(a == class(String))
+            {
+               int numBytes = sqlite3_value_bytes(values[i]);
+               const char * text = (const char *)sqlite3_value_text(values[i]);
+               *(char **)data = text ? new byte[numBytes+1] : null;
+               if(text)
+                  memcpy(*(char **)data, text, numBytes+1);
+            }
+            else
+            {
+               SerialBuffer buffer = staticBuffer; //{ };
+               buffer.pos = 0;
+               buffer._size = sqlite3_value_bytes(values[i]);
+               buffer._buffer = (byte *)sqlite3_value_text(values[i]);
+               //buffer._buffer = sqlite3_value_blob(curStatement);
+               buffer.count = buffer._size;
+               if(a.type == structClass)
+                  *data = new byte[a.structSize];
+               ((void (*)(void *, void *, void *))(void *)a._vTbl[__ecereVMethodID_class_OnUnserialize])(a, (a.type == structClass) ? *data : data, buffer);
+               buffer._buffer = null;
+               //delete buffer;
+            }
+            break;
+         }
+         case bitClass:
+         case enumClass:
+         case systemClass:
+         case unitClass:
+            if(type == &ffi_type_double || type == &ffi_type_float)
+            {
+               double d = sqlite3_value_double(values[i]);
+               if(a.typeSize == 8)
+               {
+                  double * data = new double[1];
+                  args[i+1] = (void *)data;
+                  *data = d;
+               }
+               else
+               {
+                  float * data = new float[1];
+                  args[i+1] = (void *)data;
+                  *data = (float)d;
+               }
+            }
+            else
+            {
+               switch(a.typeSize)
+               {
+                  case 8:
+                  {
+                     int64 * data = new int64[1];
+                     args[i+1] = (void *)data;
+                     *data = sqlite3_value_int64(values[i]);
+                     break;
+                  }
+                  case 4:
+                  {
+                     int * data = new int[1];
+                     args[i+1] = (void *)data;
+                     *data = sqlite3_value_int(values[i]);
+                     break;
+                  }
+                  case 2:
+                  {
+                     short * data = new short[1];
+                     int value;
+                     args[i+1] = (void *)data;
+                     value = sqlite3_value_int(values[i]);
+                     if(value < 0)
+                        *data = (short)value;
+                     else
+                        *(uint16 *)data = (uint16)value;
+                     break;
+                  }
+                  case 1:
+                  {
+                     char * data = new char[1];
+                     int value;
+                     args[i+1] = data;
+                     value = sqlite3_value_int(values[i]);
+                     if(value < 0)
+                        *data = (char)value;
+                     else
+                        *(byte *)data = (byte)value;
+                     break;
+                  }
+               }
+            }
+            break;
+      }
+      i++;
+      ffiArg.Next();
+   }
+   if(sqlFunction.returnType && sqlFunction.returnType.type == structClass)
+      ret = new byte[sqlFunction.returnType.typeSize];
+   ffi_call(&sqlFunction.cif, (void *)sqlFunction.method.function, ret, args.array);
+   // Give SQLite the return value
+   if(sqlFunction.returnType)
+   {
+      ffi_type * type = sqlFunction.rType;
+      Class r = sqlFunction.returnType;
+      switch(r.type)
+      {
+         case normalClass:
+         case noHeadClass:
+         case structClass:
+         case unionClass:
+         {
+            void * data = ret ? *(void **)ret : null;
+            if(r.type == structClass)
+               data = ret;
+            if(r == class(String))
+            {
+               if(data)
+                  sqlite3_result_text(context, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
+               else
+                  sqlite3_result_text(context, null, 0, SQLITE_TRANSIENT);
+            }
+            else
+            {
+               SerialBuffer buffer { };
+               ((void (*)(void *, void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnSerialize])(r, data, buffer);
+               sqlite3_result_text(context, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
+               delete buffer;
+
+               // Avoid destroying Strings for now... (Returning memory owned by the Custom Function)
+               ((void (*)(void *, void *))(void *)r._vTbl[__ecereVMethodID_class_OnFree])(r, data);
+            }
+
+            if(r.type == structClass)
+               delete ret;
+            break;
+         }
+         case bitClass:
+         case enumClass:
+         case systemClass:
+         case unitClass:
+            if(type == &ffi_type_double || type == &ffi_type_float)
+            {
+               if(r.typeSize == 8)
+                  sqlite3_result_double(context, *(double *)ret);
+               else
+                  sqlite3_result_double(context, (double)*(float *)ret);
+            }
+            else
+            {
+               switch(r.typeSize)
+               {
+                  case 8:
+                     sqlite3_result_int64(context, (sqlite3_int64)*(int64 *)ret);
+                     break;
+                  case 4:
+                     sqlite3_result_int(context, *(int *)ret);
+                     break;
+                  case 2:
+                  {
+                     int value;
+                     //if((int)data < 0)
+                        value = (int)*(short *)ret;
+                     //else
+                        //value = (int)*(uint16 *)ret;
+                     sqlite3_result_int(context, value);
+                     break;
+                  }
+                  case 1:
+                  {
+                     int value;
+                     //if((int)data < 0)
+                        value = (int)*(char *)ret;
+                     //else
+                        //value = (int)*(byte *)ret;
+                     sqlite3_result_int(context, value);
+                     break;
+                  }
+               }
+            }
+            break;
+      }
+   }
+
+   // Free Stuff up
+   arg.Next();
+   for(type : sqlFunction.args; arg.Next())
+   {
+      // Free instance
+      void * data = *(void **)arg.data;
+      ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, data);
+      if(type.type == structClass)
+         delete data;
+      // Free arg holder
+      data = arg.data;
+      delete data;
+   }
+   delete args;
 }
 
 class SQLiteTable : Table
@@ -487,7 +779,7 @@ class SQLiteTable : Table
    char * name;
    bool mustCreate;
    SQLiteDatabase db;
-   LinkList<SQLiteField> fields { };
+   LinkList<SQLiteField> _fields { };
    char * specialStatement;
    SQLiteField primaryKey;
    FieldIndex * indexFields;
@@ -504,13 +796,13 @@ class SQLiteTable : Table
       Table refTable = null;
       Field idField = null;
       command[0] = 0;
-      
+
       if(FindField(fieldName)) return null;
 
-      if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || 
-         !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") || 
-         !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") || 
-         !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || 
+      if(!strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") ||
+         !strcmp(type.dataTypeString, "long") || !strcmp(type.dataTypeString, "long int") ||
+         !strcmp(type.dataTypeString, "uint") || !strcmp(type.dataTypeString, "uint32") ||
+         !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") ||
          !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
          !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
       {
@@ -546,7 +838,7 @@ class SQLiteTable : Table
       }
       if(sqliteType != SQLITE_BLOB && eClass_IsDerived(type, class(eda::Id)))
       {
-         Table * table = (Table *)eClass_GetProperty(type, "table");
+         Table * table = (Table *)(intptr)eClass_GetProperty(type, "table");
          if(table) refTable = *table;
          if(refTable)
          {
@@ -566,7 +858,7 @@ class SQLiteTable : Table
             PrintLn($"WARNING: Table not yet created for class ", (String)type.name);
          }
       }
-      
+
       if(mustCreate)
       {
          if(sqliteType == SQLITE_BLOB)
@@ -618,9 +910,9 @@ class SQLiteTable : Table
          fieldName, type.name, length);
       result = sqlite3_exec(db.db, command, null, null, null);
 
-      field = { name = CopyString(fieldName), type = type, num = fields.count, sqliteType = sqliteType };
+      field = { name = CopyString(fieldName), type = type, num = _fields.count, sqliteType = sqliteType };
       incref field;
-      fields.Add(field);
+      _fields.Add(field);
       if(!primaryKey && refTable == this)
          primaryKey = field;
       return (Field)field;
@@ -628,14 +920,14 @@ class SQLiteTable : Table
 
    Field FindField(const String name)
    {
-      for(f : fields; !strcmp(f.name, name))
+      for(f : _fields; !strcmp(f.name, name))
       {
          if(!primaryKey)
          {
             if(f.sqliteType != SQLITE_BLOB && eClass_IsDerived(f.type, class(eda::Id)))
             {
 
-               Table * tablePtr = (Table *)eClass_GetProperty(f.type, "table");
+               Table * tablePtr = (Table *)(intptr)eClass_GetProperty(f.type, "table");
                if(tablePtr && *tablePtr == this)
                   primaryKey = f;
             }
@@ -693,19 +985,24 @@ class SQLiteTable : Table
       return result == SQLITE_OK;
    }
 
-   String GetName()
+   const String GetName()
    {
       return name;
    }
 
    Field GetFirstField()
    {
-      return fields.first;
+      return _fields.first;
+   }
+
+   Field GetPrimaryKey()
+   {
+      return primaryKey;
    }
 
    uint GetFieldsCount()
    {
-      return fields.count;
+      return _fields.count;
    }
 
    uint GetRowsCount()
@@ -753,6 +1050,11 @@ class SQLiteTable : Table
       }
    }
 
+   Container<Field> GetFields()
+   {
+      return (Container<Field>)_fields;
+   }
+
    DriverRow CreateRow()
    {
       char command[1024];
@@ -804,7 +1106,7 @@ class SQLiteTable : Table
       sqlite3_prepare_v2(db.db, command, -1, &setRowIDStmt, null);
 
       return SQLiteRow
-         { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt, 
+         { tbl = this, defaultStatement = statement, curStatement = statement, sysIDStatement = sysIDStmt,
            insertStatement = insertStmt, deleteStatement = deleteStmt, selectRowIDsStmt = selectRowIDsStmt, setRowIDStmt = setRowIDStmt,
            previousStatement = prevStmt, nextStatement = nextStmt, lastStatement = lastStmt, insertIDStatement = insertIDStmt };
    }
@@ -814,7 +1116,7 @@ class SQLiteTable : Table
       delete name;
       delete specialStatement;
       delete indexFields;
-      fields.Free();
+      _fields.Free();
    }
 }
 
@@ -846,7 +1148,7 @@ class SQLiteRow : DriverRow
    // this flag is used to distinguish between a Find() and a GoToSysID() for Select(next) purposes:
    bool findSysID;
    int findBindId;
-   
+
    bool Nil()
    {
       return done;
@@ -905,7 +1207,7 @@ class SQLiteRow : DriverRow
          case previous:
          {
             // For sysID statement, for a Find() we want to go through next/previous in order, otherwise we just go to nil
-            if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) || 
+            if((move == next && curStatement != prevFindStatement && curStatement != lastFindStatement && !stepping && (curStatement != sysIDStatement || findSysID)) ||
                (move == previous && (curStatement == prevFindStatement || curStatement == lastFindStatement)))
             {
                result = sqlite3_step(curStatement);
@@ -930,7 +1232,6 @@ class SQLiteRow : DriverRow
                }
                else
                {
-                  int bindId = findBindId;
                   sqlite3_reset((move == next) ? findStatement : lastFindStatement);
                   sqlite3_reset(curStatement);
                   curStatement = (move == next) ? findStatement : lastFindStatement;
@@ -963,7 +1264,7 @@ class SQLiteRow : DriverRow
       return true;
    }
 
-   bool Query(char * queryString)
+   bool Query(const char * queryString)
    {
       bool status = true;
       int result;
@@ -979,15 +1280,23 @@ class SQLiteRow : DriverRow
       if(queryString)
       {
          result = sqlite3_prepare_v2(tbl.db.db, queryString, -1, &queryStatement, null);
-         curStatement = queryStatement;
-         if(!strchr(queryString, '?'))
+         if(!result)
          {
-            result = sqlite3_step(queryStatement);
+            curStatement = queryStatement;
+            if(!strchr(queryString, '?'))
+            {
+               result = sqlite3_step(queryStatement);
 
-            done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
-            if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
+               done = result == SQLITE_DONE || (result && result != SQLITE_ROW);
+               if(done) { rowID = 0; sqlite3_reset(queryStatement); return false; }
 
-            rowID = sqlite3_column_int64(queryStatement, 0);
+               rowID = sqlite3_column_int64(queryStatement, 0);
+            }
+         }
+         else
+         {
+            printf("SQLite Query Error: %s\n", queryString);
+            status = false;
          }
       }
       else
@@ -995,21 +1304,22 @@ class SQLiteRow : DriverRow
       return status;
    }
 
-   void BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
+   bool BindData(sqlite3_stmt * statement, int pos, SQLiteField fld, typed_object data, SerialBuffer * bufferOut)
    {
+      int result = 1;
       Class dataType = fld.type;
       SerialBuffer buffer = null;
       switch(fld.sqliteType)
       {
-         case SQLITE_INTEGER: 
+         case SQLITE_INTEGER:
          {
             switch(dataType.typeSize)
             {
                case 8:
-                  sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
+                  result = sqlite3_bind_int64(statement, pos, (sqlite3_int64)*(int64 *)data);
                   break;
                case 4:
-                  sqlite3_bind_int(statement, pos, *(int *)data);
+                  result = sqlite3_bind_int(statement, pos, *(int *)data);
                   break;
                case 2:
                {
@@ -1018,7 +1328,7 @@ class SQLiteRow : DriverRow
                      value = (int)*(short *)data;
                   else
                      value = (int)*(uint16 *)data;
-                  sqlite3_bind_int(statement, pos, value);
+                  result = sqlite3_bind_int(statement, pos, value);
                   break;
                }
                case 1:
@@ -1028,7 +1338,7 @@ class SQLiteRow : DriverRow
                      value = (int)*(char *)data;
                   else
                      value = (int)*(byte *)data;
-                  sqlite3_bind_int(statement, pos, value);
+                  result = sqlite3_bind_int(statement, pos, value);
                   break;
                }
             }
@@ -1037,25 +1347,30 @@ class SQLiteRow : DriverRow
          case SQLITE_FLOAT:
          {
             if(dataType.typeSize == 8)
-               sqlite3_bind_double(statement, pos, *(double *)data);
+               result = sqlite3_bind_double(statement, pos, *(double *)data);
             else
-               sqlite3_bind_double(statement, pos, (double)*(float *)data);
+               result = sqlite3_bind_double(statement, pos, (double)*(float *)data);
             break;
          }
          case SQLITE_TEXT:
          {
             if((char *)data)
-               sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
+               result = sqlite3_bind_text(statement, pos, (char *)data, strlen((char *)data), SQLITE_TRANSIENT);
             else
-               sqlite3_bind_text(statement, pos, null, 0, SQLITE_TRANSIENT);
+               result = sqlite3_bind_null(statement, pos);
             break;
          }
          case SQLITE_BLOB:
          case SQLITE_NULL:
          {
-            buffer = SerialBuffer { };
-            dataType._vTbl[__ecereVMethodID_class_OnSerialize](dataType, data, buffer);
-            sqlite3_bind_text(statement, pos, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
+            if((void *)data)
+            {
+               buffer = SerialBuffer { };
+               ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnSerialize])(dataType, data, buffer);
+               result = sqlite3_bind_text(statement, pos, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
+            }
+            else
+               result = sqlite3_bind_null(statement, pos);
             break;
          }
       }
@@ -1063,6 +1378,7 @@ class SQLiteRow : DriverRow
          *bufferOut = buffer;
       else
          delete buffer;
+      return !result;
    }
 
    void AddCursorWhereClauses(char * command, MoveOptions move, bool useIndex)
@@ -1131,26 +1447,27 @@ class SQLiteRow : DriverRow
                }
                if(type.type == structClass)
                {
-                  data = (int64)new0 byte[type.structSize];
-                  dataPtr = (void *) data;
+                  data = (int64)(intptr)new0 byte[type.structSize];
+                  dataPtr = (void *)(intptr)data;
                }
                // ((bool (*)())(void *)dataRow.GetData)(dataRow, fld, type, (type.type == structClass) ? (void *)data : &data);
-               ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)data : &data);
+               ((bool (*)())(void *)this.GetData)(this, fld, type, (type.type == structClass) ? (void *)(intptr)data : &data);
                if(type.type == normalClass || type.type == noHeadClass)
-                  dataPtr = (void *) data;
+                  dataPtr = (void *)(intptr)data;
                else
                   dataPtr = &data;
-               ((void (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
+               ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, &buffer);
+               // NOTE: The data is bound twice, for there are 2x '?' in the query from AddCursorWhereClauses
                // Reuse the buffer for Blobs...
                if(fld.sqliteType == SQLITE_BLOB || fld.sqliteType == SQLITE_NULL)
                {
-                  sqlite3_bind_text(stmt, (*bindId)++, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
+                  sqlite3_bind_text(stmt, (*bindId)++, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
                   delete buffer;
                }
                else
-                  ((void (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
+                  ((bool (*)())(void *)this.BindData)(this, stmt, (*bindId)++, fld, type, dataPtr, null);
 
-               type._vTbl[__ecereVMethodID_class_OnFree](type, dataPtr);
+               ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, dataPtr);
             }
             // delete dataRow;
          }
@@ -1178,7 +1495,7 @@ class SQLiteRow : DriverRow
          result = GoToSysID(*(int *)data);
          if(result)
             findSysID = true;
-         return result;
+         return result != 0;
       }
 
       useIndex = tbl.GetIndexOrder(order, false);
@@ -1394,6 +1711,7 @@ class SQLiteRow : DriverRow
          sqlite3_reset(curStatement);
          sqlite3_bind_int64(sysIDStatement, 1, (sqlite3_int64)rowID);
          result = sqlite3_step(curStatement);
+         done = false; // Make sure 'nil' is false
          return true;
       }
       sqlite3_reset(insertStatement);
@@ -1422,7 +1740,7 @@ class SQLiteRow : DriverRow
 
       switch(sqlFld.sqliteType)
       {
-         case SQLITE_INTEGER: 
+         case SQLITE_INTEGER:
          {
             switch(dataType.typeSize)
             {
@@ -1479,7 +1797,7 @@ class SQLiteRow : DriverRow
          case SQLITE_TEXT:
          {
             int numBytes = sqlite3_column_bytes(curStatement, num);
-            char * text = sqlite3_column_text(curStatement, num);
+            const char * text = (const char *)sqlite3_column_text(curStatement, num);
             *(char **)data = text ? new byte[numBytes+1] : null;
             if(text)
                memcpy(*(char **)data, text, numBytes+1);
@@ -1490,11 +1808,11 @@ class SQLiteRow : DriverRow
             SerialBuffer buffer { };
             //buffer._buffer = sqlite3_column_blob(curStatement, num);
             buffer._size = sqlite3_column_bytes(curStatement, num);
-            buffer._buffer = sqlite3_column_text(curStatement, num);
+            buffer._buffer = (byte *)sqlite3_column_text(curStatement, num);
             buffer.count = buffer._size;
 
-            dataType._vTbl[__ecereVMethodID_class_OnUnserialize](dataType, data, buffer);
-           
+            ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnUnserialize])(dataType, data, buffer);
+
             buffer._buffer = null;
             delete buffer;
             break;
@@ -1507,7 +1825,6 @@ class SQLiteRow : DriverRow
    {
       SQLiteField sqlFld = (SQLiteField)fld;
       int result;
-      int num = sqlFld.num + 1;
       char command[1024];
 
       if(updateStatement)
@@ -1577,7 +1894,7 @@ class SQLiteRow : DriverRow
       return !result;
    }
 
-   bool SetQueryParamText(int paramID, char * data)
+   bool SetQueryParamText(int paramID, const char * data)
    {
       int result;
       if(curStatement != queryStatement)
@@ -1593,7 +1910,7 @@ class SQLiteRow : DriverRow
       return !result;
    }
 
-   bool SetQueryParamObject(int paramID, void * data, Class type)
+   bool SetQueryParamObject(int paramID, const void * data, Class type)
    {
       int result;
       if(curStatement != queryStatement)
@@ -1604,20 +1921,31 @@ class SQLiteRow : DriverRow
       sqlite3_reset(queryStatement);
       {
          SerialBuffer buffer { };
-         type._vTbl[__ecereVMethodID_class_OnSerialize](type, data, buffer);
-         result = sqlite3_bind_text(queryStatement, paramID, buffer._buffer, buffer.count, SQLITE_TRANSIENT);
+         ((void (*)(void *, const void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnSerialize])(type, data, buffer);
+         result = sqlite3_bind_text(queryStatement, paramID, (char *)buffer._buffer, buffer.count, SQLITE_TRANSIENT);
          delete buffer;
       }
       return !result;
    }
 
+   bool BindQueryData(int pos, SQLiteField fld, typed_object data)
+   {
+      if(curStatement != queryStatement)
+      {
+         if(curStatement) sqlite3_reset(curStatement);
+         curStatement = queryStatement;
+      }
+      sqlite3_reset(queryStatement);
+      return BindData(queryStatement, pos, fld, data, null);
+   }
+
    /*char * GetExtraColumn(int paramID)
    {
-      SQLiteField lastFld = tbl.fields.last;
+      SQLiteField lastFld = tbl._fields.last;
       return sqlite3_column_text(curStatement, lastFld.num + 1 + paramID);
    }*/
-   char * GetColumn(int paramID)
+   const char * GetColumn(int paramID)
    {
-      return sqlite3_column_text(curStatement, paramID);
+      return (const char *)sqlite3_column_text(curStatement, paramID);
    }
 }