implement dense results

Fri, 03 Jan 2025 21:40:57 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 03 Jan 2025 21:40:57 +0100
changeset 10
80f9d007cb52
parent 9
5785a693834c
child 11
0aa8cbd7912e

implement dense results

dbutils/dbutils/db.h file | annotate | diff | comparison | revisions
dbutils/dbutils/dbutils.h file | annotate | diff | comparison | revisions
dbutils/object.c file | annotate | diff | comparison | revisions
dbutils/object.h file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
--- a/dbutils/dbutils/db.h	Mon Dec 16 18:38:11 2024 +0100
+++ b/dbutils/dbutils/db.h	Fri Jan 03 21:40:57 2025 +0100
@@ -112,6 +112,8 @@
 
 DBUObjectBuilder* dbuObjectBuilder(DBUClass *type, DBUQuery *query, const CxAllocator *a);
 void dbuObjectBuilderSetDenseResult(DBUObjectBuilder *builder, bool dense);
+
+// TODO: add variants for specifying the field or field name
 int dbuObjectBuilderAddAdditionalQuery(DBUObjectBuilder *builder, DBUClass *type, DBUQuery *query);
 CxList* dbuObjectBuilderGetList(DBUObjectBuilder *builder);
 CxList* dbuObjectBuilderGetValueList(DBUObjectBuilder *builder);
--- a/dbutils/dbutils/dbutils.h	Mon Dec 16 18:38:11 2024 +0100
+++ b/dbutils/dbutils/dbutils.h	Fri Jan 03 21:40:57 2025 +0100
@@ -127,7 +127,7 @@
     int int1;
     int int2;
     DBUObject (*create)(DBUObjectResult *result, DBUClass *type, const CxAllocator *a);
-    int (*add)(DBUObjectResult *result, DBUObject parent, DBUClass *tye, void *obj, CxList *fk, const CxAllocator *a);
+    int (*add)(DBUObjectResult *result, DBUObject parent, DBUClass *type, void *obj, CxList *fk, const CxAllocator *a);
     void (*free)(DBUObjectResult *result);
 };
 
--- a/dbutils/object.c	Mon Dec 16 18:38:11 2024 +0100
+++ b/dbutils/object.c	Fri Jan 03 21:40:57 2025 +0100
@@ -78,7 +78,7 @@
 }
 
 int dbuObjectBuilderAddAdditionalQuery(DBUObjectBuilder *builder, DBUClass *type, DBUQuery *query) {
-    return cxListAdd(builder->additionalQueries, &(DBUBuilderQuery){ type, query });
+    return cxListAdd(builder->additionalQueries, &(DBUBuilderQuery){ type, NULL, query });
 }
 
 
@@ -198,17 +198,24 @@
     
     int ret = dbuObjectExecuteQuery(builder, builder->mainQuery, builder->resultType, objresult, builder->denseResult);
     if(!ret) {
+        // execute additional queries
         CxIterator i = cxListIterator(builder->additionalQueries);
         cx_foreach(DBUBuilderQuery *, q, i) {
-            DBUObjectResult result = { 
+            // default result builder for additional queries
+            // uses malloc to allocate objects and adds checks all foreign keys
+            DBUObjectResult result = {
                 .userdata1 = q,
                 .userdata2 = builder,
                 .int1 = CX_STORE_POINTERS,
                 .create = result_create_obj,
                 .add = add_to_parent
             };
+            DBUObjectResult *objresult = &result;
+            if(q->field && q->field->builder.add) {
+                objresult = &q->field->builder;
+            }
             
-            ret = dbuObjectExecuteQuery(builder, q->query, q->type, &result, false);
+            ret = dbuObjectExecuteQuery(builder, q->query, q->type, objresult, false);
             if(ret) {
                 break;
             }
@@ -226,11 +233,13 @@
     free(elm->cache_key);
 }
 
+static void* create_or_get_obj(CxAllocator *a, DBUObjectResult *objresult, DBUClass *type, int pk_index, DBUResult *result) {
+    return NULL;
+}
+
 int dbuObjectExecuteQuery(DBUObjectBuilder *builder, DBUQuery *query, DBUClass *type, DBUObjectResult *objresult, bool dense) {
     DBUClass *cls = type;
     
-    // TODO: execute additional queries
-    
     // execute sql
     if(query->exec(query)) {
         query->free(query);
@@ -257,6 +266,12 @@
     int main_pk_index = -1;
     int end_main_fields = numcols;
     
+    CxList *list_classes = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+    CxList *list_indices = cxArrayListCreateSimple(sizeof(int), 4);
+    CxList *list_keys = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
+    list_keys->collection.simple_destructor = free;
+    cxListAdd(list_classes, cls);
+    
     for(int i=0;i<numcols;i++) {
         cxstring fieldname = cx_str(result->fieldName(result, i));
         DBUField *field = NULL;
@@ -277,6 +292,9 @@
             fcls = cxMapGet(builder->ctx->classes, tabname);
             
             if(fcls) {
+                if(fcls != field_class) {
+                    cxListAdd(list_classes, fcls);
+                }
                 if(remaining.length > 2) {
                     field = cxMapGet(fcls->fields, cx_strsubs(remaining, 2));
                 }
@@ -298,49 +316,59 @@
             if(field == cls->primary_key) {
                 main_pk_index = i;
             }
+            if(field == field_class->primary_key) {
+                //int xi = cxListSize(list_classes)-1;
+                //printf("index[%d]: %d\n", xi, i);
+                cxListAdd(list_indices, &i);
+            }
             
             if(end_main_fields == numcols && field_class != cls) {
                 end_main_fields = i;
             }
         }
     }
-    
+      
     if(main_pk_index < 0) {
         dense = false;
     }
     
     const CxAllocator *a = builder->allocator;
     
+    void **list_obj = calloc(cxListSize(list_classes), sizeof(void*));
+    
     CxList *fklist = cxArrayListCreateSimple(sizeof(DBUFK), 4);
     fklist->collection.simple_destructor = (cx_destructor_func)dbufkelm_free;
     
+    int *indices = cxListAt(list_indices, 0);
+    size_t numindices = cxListSize(list_indices);
+    
      // get result
     int err = 0;
-    while(result->hasData(result)) {
-        
+    while(result->hasData(result)) {       
         // create main result obj
         bool addobj = true;
         void *obj = NULL;
         int skip_fields = 0;
+        int cls_index = 0;
         if(dense) {
             cxstring text = result->getText(result, main_pk_index);
-            cxmutstr cache_key = cx_asprintf("%.*s::%.*s",
-                    (int)cls->name.length, cls->name.ptr,
-                    (int)text.length, text.ptr);
-            if(!cache_key.ptr) {
-                err = 1;
-                break;
-            }
-            DBUBuilderObjCache *cached_obj = cxMapGet(builder->cache, cache_key);
-            free(cache_key.ptr);
-            if(cached_obj && cached_obj->class == cls) {
-                obj = cached_obj->obj;
+            char *prev_pk = cxListAt(list_keys, 0);
+            if(prev_pk && !strcmp(text.ptr, prev_pk)) {
+                obj = list_obj[0];
                 addobj = false;
-                skip_fields = end_main_fields;
+                int *next_index = cxListAt(list_indices, 1);
+                if(next_index) {
+                    skip_fields = *next_index;
+                }
             }
         }
+        
+        if(!obj) {
+            printf("create obj [%s]\n", cls->name.ptr);
+            obj = objresult->create(objresult, cls, a);
+        }
+        list_obj[0] = obj;
             
-        obj = objresult->create(objresult, cls, a);
         if(!obj) {
             break;
         }
@@ -358,11 +386,35 @@
             
             if(field.cls && field.cls != current_cls) {
                 // following columns are for this new class
+                cls_index++;
+                printf("new class[%d]: %s\n", cls_index, field.cls->name.ptr);
+                
+                if(dense) {
+                    // check if this object was already added
+                    int *pk_index = cxListAt(list_indices, cls_index);
+                    if(pk_index) {
+                        cxstring text = result->getText(result, *pk_index);
+                        char *prev_pk = cxListAt(list_keys, cls_index);
+                        if(text.ptr && prev_pk) {
+                            if(!strcmp(text.ptr, prev_pk)) {
+                                printf("already added -> skip\n");
+                                int *next_index = cxListAt(list_indices, cls_index+1);
+                                if(next_index) {
+                                    i = *next_index;
+                                    printf("next col %d\n", i);
+                                    continue;
+                                }
+                            }
+                        }
+                    }
+                }
+                
                 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) {
+                        printf("create child obj [%s]\n", field.cls->name.ptr);
                         child_obj = objresult->create(objresult, field.cls, a);
                         current_obj = child_obj;
                         if(!child_obj) {
@@ -375,12 +427,32 @@
                         }
 
                         obj_field->initObjValue(obj_field, a, obj, child_obj);
+                    } else {
+                        printf("list field\n");
+                        
+                        DBUField *parent_field = NULL;
+                        CxIterator iter = cxListIterator(list_classes);
+                        current_obj = NULL;
+                        cx_foreach(DBUClass *, parent_cls, iter) {
+                            DBUField *f = cxMapGet(parent_cls->obj_fields, field.cls->name);
+                            if(f && f->builder.create) {
+                                current_obj = f->builder.create(&f->builder, field.cls, a);
+                                void *parent_obj = list_obj[iter.index];
+                                f->builder.add(&f->builder, parent_obj, field.cls, current_obj, fklist, a);
+                                break;
+                            }
+                        }
+                        if(!current_obj) {
+                            printf("ignore\n");
+                        }
                     }
                 }
+                
+                list_obj[cls_index] = current_obj;
             }
             
             DBUField *type_field = field.field;
-            if(type_field) {
+            if(type_field && current_obj) {
                 if(isnull) {
                     field.field->initDefaultValue(field.field, a, current_obj);
                 } else {
@@ -431,10 +503,24 @@
         
         cxListClear(fklist);
         
+        if(dense) {
+            cxListClear(list_keys);
+            for(int i=0;i<numindices;i++) {
+                cxstring text = result->getText(result, indices[i]);
+                cxListAdd(list_keys, cx_strdup(text).ptr);
+            }
+        }
+        
         // load next row
         result->nextRow(result);
+        printf("next row\n");
     }
     
+    cxListDestroy(list_classes);
+    cxListDestroy(list_indices);
+    cxListDestroy(list_keys);
+    free(list_obj);
+    
     cxListDestroy(fklist);
     
     result->free(result);
--- a/dbutils/object.h	Mon Dec 16 18:38:11 2024 +0100
+++ b/dbutils/object.h	Fri Jan 03 21:40:57 2025 +0100
@@ -46,6 +46,7 @@
 typedef struct DBUBuilderQuery DBUBuilderQuery;
 struct DBUBuilderQuery {
     DBUClass *type;
+    DBUField *field;
     DBUQuery *query;
 };
 
@@ -77,7 +78,7 @@
      * Before a subquery is executed, the first parameter is set to the
      * primary key of the main table.
      */
-    CxMap *subQueries;
+    CxMap *subQueries; // TODO: implement
     
     /*
      * value: DBUBuilderQuery
--- a/test/main.c	Mon Dec 16 18:38:11 2024 +0100
+++ b/test/main.c	Fri Jan 03 21:40:57 2025 +0100
@@ -158,8 +158,12 @@
     DBUQuery *roleQuery = conn->createQuery(conn, NULL);
     dbuQuerySetSQL(roleQuery, "select * from role;");
     
-    DBUObjectBuilder *builder = dbuObjectBuilder(person, query, cxDefaultAllocator);
-    dbuObjectBuilderAddAdditionalQuery(builder, role, roleQuery);
+    DBUQuery *query2 = conn->createQuery(conn, NULL);
+    dbuQuerySetSQL(query2, "select p.*, a.address_id as [__address__address_id], a.street, a.zip, a.city, r.role_id as [__role__role_id], r.person_id, r.name from Person p inner join Address a on p.address_id = a.address_id left join Role r on p.person_id = r.person_id;");
+    
+    DBUObjectBuilder *builder = dbuObjectBuilder(person, query2, cxDefaultAllocator);
+    dbuObjectBuilderSetDenseResult(builder, true);
+    //dbuObjectBuilderAddAdditionalQuery(builder, role, roleQuery);
     CxList *persons = dbuObjectBuilderGetList(builder);
     if(persons) {
         CxIterator i = cxListIterator(persons);

mercurial