add support for child objects in the object builder

Wed, 11 Dec 2024 20:57:19 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 11 Dec 2024 20:57:19 +0100
changeset 5
f0c66b2139ea
parent 4
1908c8b1599f
child 6
d6981b56ab30

add support for child objects in the object builder

dbutils/dbutils.c file | annotate | diff | comparison | revisions
dbutils/object.c file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
--- a/dbutils/dbutils.c	Mon Dec 09 23:47:52 2024 +0100
+++ b/dbutils/dbutils.c	Wed Dec 11 20:57:19 2024 +0100
@@ -53,6 +53,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyInt32(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
@@ -67,6 +68,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyUInt32(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
@@ -81,6 +83,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyInt64(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
@@ -95,6 +98,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyUInt64(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
@@ -109,6 +113,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyString(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
@@ -123,6 +128,7 @@
         off_t primary_key_offset)
 {
     DBUClass *cls = dbuClassCreate(name);
+    cls->context = context;
     cls->obj_size = obj_size;
     dbuClassSetPrimaryKeyCxMutStr(cls, primary_key_column, primary_key_offset);
     cxMapPut(context->classes, name, cls);
--- a/dbutils/object.c	Mon Dec 09 23:47:52 2024 +0100
+++ b/dbutils/object.c	Wed Dec 11 20:57:19 2024 +0100
@@ -195,18 +195,23 @@
             // after a column is marked as the start of the new table
             // all following columns will be treated as columns of this table
             cxstring tabname = cx_strsubs(fieldname, 2);
-            DBUClass *fcls = cxMapGet(builder->ctx->classes, tabname);
+            cxstring remaining = cx_strstr(tabname, CX_STR("__"));
+            DBUClass *fcls = NULL;
+            if(remaining.length > 2) {
+                tabname.length = remaining.ptr - tabname.ptr;
+                
+            }
+            fcls = cxMapGet(builder->ctx->classes, tabname);
+            
             if(fcls) {
-                field_class = fcls;
-                cxstring remaining = cx_strstr(tabname, CX_STR("__"));
                 if(remaining.length > 2) {
-                    field = cxMapGet(cls->fields, cx_strsubs(remaining, 2));
-                } else {
-                    field_mapping[i].cls = fcls;
+                    field = cxMapGet(fcls->fields, cx_strsubs(remaining, 2));
                 }
+                field_mapping[i].cls = fcls;
+                field_class = fcls;
             }
         } else {
-            field = cxMapGet(cls->fields, fieldname);
+            field = cxMapGet(field_class->fields, fieldname);
         }
         
         if(field) {
@@ -236,27 +241,31 @@
         
         void *current_obj = obj;
         void *child_obj = NULL;
-        DBUClass *child_cls = NULL;
         DBUClass *current_cls = cls;
         for(int i=0;i<numcols;i++) {
             DBUFieldMapping field = field_mapping[i];
             int isnull = result->isNull(result, i);
             
-            if(field.cls != current_cls) {
-                // check if an object of this class can be added to the main obj
-                DBUField *obj_field = cxMapGet(cls->obj_fields, field.cls->name);
-                if(obj_field && obj_field->initObjValue) {
-                    child_obj = objresult->create(objresult, field.cls, a);
-                    if(!child_obj) {
-                        err = 1;
-                        break;
+            if(field.cls && field.cls != current_cls) {
+                // following columns are for this new class
+                current_cls = field.cls;
+                if(field.field) {
+                    // check if an object of this class can be added to the main obj
+                    DBUField *obj_field = cxMapGet(cls->obj_fields, field.cls->name);
+                    if(obj_field && obj_field->initObjValue) {
+                        child_obj = objresult->create(objresult, field.cls, a);
+                        current_obj = child_obj;
+                        if(!child_obj) {
+                            err = 1;
+                            break;
+                        }
+
+                        if(field.cls->init) {
+                            field.cls->init(child_obj, a); // TODO: check return
+                        }
+
+                        obj_field->initObjValue(obj_field, a, obj, child_obj);
                     }
-
-                    if(field.cls->init) {
-                        field.cls->init(child_obj, a); // TODO: check return
-                    }
-                    
-                    obj_field->initObjValue(obj_field, a, obj, child_obj);
                 }
             }
             
--- a/test/main.c	Mon Dec 09 23:47:52 2024 +0100
+++ b/test/main.c	Wed Dec 11 20:57:19 2024 +0100
@@ -40,16 +40,41 @@
 "email text, "
 "age integer, " 
 "iscustomer integer , "
-"hash integer);";
+"hash integer, "
+"address_id integer);";
+
+const char *sql_create_table_address =
+"create table if not exists Address ("
+"address_id integer primary key autoincrement, "
+"street text, "
+"zip text, "
+"city text);";
+
 
 const char *sql_check_table =
 "select person_id from Person limit 1;";
 
-const char *sql_create_test_data = 
-"insert into person (name, email, age, iscustomer, hash) "
+
+
+const char *sql_create_test_data1 = 
+"insert into address (street, zip, city) "
+"values "
+"('street 1', '12343', 'city 17'), "
+"('street 2', '23456', 'city 18');";
+
+const char *sql_create_test_data2 = 
+"insert into person (name, email, age, iscustomer, hash, address_id) "
 "values "
-"('alice', 'alice@example.com', 30, 1, 123456789), "
-"('bob', 'bob@example.com', 25, 0, 987654321);";
+"('alice', 'alice@example.com', 30, 1, 123456789, (select address_id from address where street = 'street 1')), "
+"('bob', 'bob@example.com', 25, 0, 987654321,  (select address_id from address where street = 'street 2'));";
+
+typedef struct Address {
+    int64_t address_id;
+    
+    cxmutstr street;
+    cxmutstr zip;
+    cxmutstr city;
+} Address;
 
 typedef struct Person {
     int64_t person_id;
@@ -58,9 +83,13 @@
     cxmutstr email;
     int      age;
     bool     iscustomer;
-    uint64_t hash;  
+    uint64_t hash;
+    
+    Address *address;
 } Person;
 
+
+
 static int create_test_data(sqlite3 *db);
 
 int main(int argc, char **argv) {
@@ -68,13 +97,18 @@
     
     DBUContext *ctx = dbuContextCreate();
     
+    DBUClass *address = dbuRegisterClass(ctx, "address", Address, address_id);
+    dbuClassAdd(address, Address, street);
+    dbuClassAdd(address, Address, zip);
+    dbuClassAdd(address, Address, city);
+    
     DBUClass *person = dbuRegisterClass(ctx, "person", Person, person_id);
     dbuClassAdd(person, Person, name);
     dbuClassAdd(person, Person, email);
     dbuClassAdd(person, Person, age);
     dbuClassAdd(person, Person, iscustomer);
     dbuClassAdd(person, Person, hash);
-
+    dbuClassAddObj(person, "address_id", offsetof(Person, address), address);
     
     
     // Open or create the database
@@ -92,7 +126,7 @@
     
     DBUConnection *conn = dbuSQLiteConnectionFromDB(db, true);
     DBUQuery *query = conn->createQuery(conn, NULL);
-    dbuQuerySetSQL(query, "select * from Person;");
+    dbuQuerySetSQL(query, "select p.*, a.address_id as [__address__address_id], a.street, a.zip, a.city from Person p inner join Address a on p.address_id = a.address_id;");
     
     DBUObjectBuilder *builder = dbuObjectBuilder(person, query, cxDefaultAllocator);
     CxList *persons = dbuObjectBuilderGetList(builder);
@@ -121,6 +155,14 @@
         return 1;
     }
     
+    rc = sqlite3_exec(db, sql_create_table_address, 0, 0, &err_msg);
+    if(rc != SQLITE_OK) {
+        fprintf(stderr, "SQLite error: %s\n", err_msg);
+        sqlite3_free(err_msg);
+        sqlite3_close(db);
+        return 1;
+    }
+    
     
     sqlite3_stmt *stmt;
     rc = sqlite3_prepare_v2(db, sql_check_table, -1, &stmt, 0);
@@ -140,7 +182,15 @@
         return 0;
     }
     
-    rc = sqlite3_exec(db, sql_create_test_data, 0, 0, &err_msg);
+    rc = sqlite3_exec(db, sql_create_test_data1, 0, 0, &err_msg);
+    if(rc != SQLITE_OK) {
+        fprintf(stderr, "SQLite error: %s\n", err_msg);
+        sqlite3_free(err_msg);
+        sqlite3_close(db);
+        return 1;
+    }
+    
+    rc = sqlite3_exec(db, sql_create_test_data2, 0, 0, &err_msg);
     if(rc != SQLITE_OK) {
         fprintf(stderr, "SQLite error: %s\n", err_msg);
         sqlite3_free(err_msg);

mercurial