Fri, 03 Jan 2025 21:40:57 +0100
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);