Fri, 02 Jan 2026 15:36:13 +0100
add dense result test
| dbutils/object.c | file | annotate | diff | comparison | revisions | |
| test/database.c | file | annotate | diff | comparison | revisions | |
| test/database.h | file | annotate | diff | comparison | revisions | |
| test/main.c | file | annotate | diff | comparison | revisions |
--- a/dbutils/object.c Thu Jan 01 17:59:32 2026 +0100 +++ b/dbutils/object.c Fri Jan 02 15:36:13 2026 +0100 @@ -241,6 +241,10 @@ DBUClass *cls = type; // execute sql + if(!query) { + return 1; + } + if(query->exec(query)) { query->free(query); return 1; @@ -269,14 +273,13 @@ // list of all classes returned by the result // used when dense == true, to check, if a new result row contains // the same objects (primary keys haven't changed) - size_t result_types_capacity = 16; - size_t result_types_size = 0; CX_ARRAY(DBUResultType, result_types); cx_array_init(result_types, 16); DBUResultType mainResult = { .cls = cls }; cx_array_add(result_types, mainResult); + // analyse columns: create a column-field mapping for(int i=0;i<numcols;i++) { cxstring fieldname = cx_str(result->fieldName(result, i)); DBUField *field = NULL; @@ -342,7 +345,7 @@ CxList *fklist = cxArrayListCreate(NULL, sizeof(DBUFK), 4); fklist->collection.simple_destructor = (cx_destructor_func)dbufkelm_free; - // get result + // get result int err = 0; while(result->hasData(result)) { // create main result obj @@ -351,12 +354,15 @@ int skip_fields = 0; int cls_index = 0; if(dense) { + // if the primary key in this row is the same as in the previous + // row, we don't create a new obj for this row, but reuse the + // previous obj ptr. cxstring text = result->getText(result, main_pk_index); cxmutstr prev_pk = result_types.data[0].prev_key; - if(prev_pk.ptr && !cx_strcmp(text, cx_strcast(prev_pk))) { + if(prev_pk.ptr && !cx_strcmp(text, prev_pk)) { obj = result_types.data[0].prev_obj; addobj = false; - if(1 < result_types_size) { + if(1 < result_types.size) { skip_fields = result_types.data[1].pk_col; } } @@ -391,12 +397,12 @@ if(dense) { // check if this object was already added - if(cls_index < result_types_size) { + if(cls_index < result_types.size) { cxstring text = result->getText(result, result_types.data[cls_index].pk_col); cxmutstr prev_pk = result_types.data[cls_index].prev_key; if(prev_pk.ptr && !cx_strcmp(text, cx_strcast(prev_pk))) { //printf("already added -> skip\n"); - if(cls_index+1 < result_types_size) { + if(cls_index+1 < result_types.size) { i = result_types.data[cls_index+1].pk_col-1; // -1 because i++ by loop //printf("next col %d\n", i); continue; @@ -438,7 +444,7 @@ //printf("list field\n"); DBUField *parent_field = NULL; - for(int c=0;c<result_types_size;c++) { + for(int c=0;c<result_types.size;c++) { DBUResultType parentType = result_types.data[c]; DBUField *f = cxMapGet(parentType.cls->obj_fields, field.cls->name); if(f && f->builder.create) { @@ -514,7 +520,7 @@ cxListClear(fklist); if(dense) { - for(int i=0;i<result_types_size;i++) { + for(int i=0;i<result_types.size;i++) { cxstring text = result->getText(result, result_types.data[i].pk_col); free(result_types.data[i].prev_key.ptr); result_types.data[i].prev_key = cx_strdup(text); @@ -528,7 +534,7 @@ cxListFree(fklist); - for(int i=0;i<result_types_size;i++) { + for(int i=0;i<result_types.size;i++) { free(result_types.data[i].prev_key.ptr); } free(result_types.data);
--- a/test/database.c Thu Jan 01 17:59:32 2026 +0100 +++ b/test/database.c Fri Jan 02 15:36:13 2026 +0100 @@ -143,6 +143,8 @@ dbuClassAddObj(address, "country_id", offsetof(Address, country), country); role = dbuRegisterClass(ctx, "role", Role, role_id); + dbuClassAdd(role, Role, person_id); + dbuClassAdd(role, Role, name); person = dbuRegisterClass(ctx, "person", Person, person_id); dbuClassAdd(person, Person, name); @@ -272,7 +274,7 @@ cxMempoolFree(mp); } -CX_TEST_SUBROUTINE(verifyMultiTableResult, CxList *persons) { +CX_TEST_SUBROUTINE(verifyMultiTableResult, CxList *persons, bool verify_address) { CX_TEST_ASSERT(persons); CX_TEST_ASSERT(cxListSize(persons) == 2); @@ -291,16 +293,18 @@ CX_TEST_ASSERT(p1->iscustomer == 1); CX_TEST_ASSERT(p0->hash == 123456789); CX_TEST_ASSERT(p1->hash == 987654321); - - CX_TEST_ASSERT(p0->address != NULL); - CX_TEST_ASSERT(p1->address != NULL); + + if(verify_address) { + CX_TEST_ASSERT(p0->address != NULL); + CX_TEST_ASSERT(p1->address != NULL); - CX_TEST_ASSERT(!cx_strcmp(p0->address->street, "street 1")); - CX_TEST_ASSERT(!cx_strcmp(p1->address->street, "street 2")); - CX_TEST_ASSERT(!cx_strcmp(p0->address->zip, "12343")); - CX_TEST_ASSERT(!cx_strcmp(p1->address->zip, "23456")); - CX_TEST_ASSERT(!cx_strcmp(p0->address->city, "city 17")); - CX_TEST_ASSERT(!cx_strcmp(p1->address->city, "city 18")); + CX_TEST_ASSERT(!cx_strcmp(p0->address->street, "street 1")); + CX_TEST_ASSERT(!cx_strcmp(p1->address->street, "street 2")); + CX_TEST_ASSERT(!cx_strcmp(p0->address->zip, "12343")); + CX_TEST_ASSERT(!cx_strcmp(p1->address->zip, "23456")); + CX_TEST_ASSERT(!cx_strcmp(p0->address->city, "city 17")); + CX_TEST_ASSERT(!cx_strcmp(p1->address->city, "city 18")); + } } CX_TEST(testMultiTableQuery) { @@ -312,7 +316,7 @@ DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); CxList *persons = dbuObjectBuilderGetList(builder); - CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons); + CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons, true); dbuObjectBuilderDestroy(builder); } @@ -329,7 +333,7 @@ DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); CxList *persons = dbuObjectBuilderGetList(builder); - CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons); + CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons, true); dbuObjectBuilderDestroy(builder); } @@ -361,7 +365,7 @@ DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); CxList *persons = dbuObjectBuilderGetList(builder); - CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons); + CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons, true); CX_TEST_CALL_SUBROUTINE(verifyMultiTableResultCountry, persons); dbuObjectBuilderDestroy(builder); @@ -384,11 +388,52 @@ DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); CxList *persons = dbuObjectBuilderGetList(builder); - CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons); + CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons, true); CX_TEST_CALL_SUBROUTINE(verifyMultiTableResultCountry, persons); dbuObjectBuilderDestroy(builder); } cxMempoolFree(mp); -} \ No newline at end of file +} + +CX_TEST_SUBROUTINE(verifyPersonRoles, CxList *persons) { + Person *p0 = cxListAt(persons, 0); + Person *p1 = cxListAt(persons, 1); + CX_TEST_ASSERT(p0 && p1); + + CX_TEST_ASSERT(cxListSize(p0->roles) == 3); + CX_TEST_ASSERT(cxListSize(p1->roles) == 1); + + Role *r0 = cxListAt(p0->roles, 0); + Role *r1 = cxListAt(p0->roles, 1); + Role *r2 = cxListAt(p0->roles, 2); + + Role *p1_r0 = cxListAt(p1->roles, 0); + + CX_TEST_ASSERT(!cx_strcmp(r0->name, "finance")); + CX_TEST_ASSERT(!cx_strcmp(r1->name, "dev")); + CX_TEST_ASSERT(!cx_strcmp(r2->name, "manager")); + + CX_TEST_ASSERT(!cx_strcmp(p1_r0->name, "extern")); +} + +CX_TEST(testQuerySubListDense1) { + CxMempool *mp = cxMempoolCreateSimple(64); + + CX_TEST_DO { + const char *sql1 = + "select p.*, r.role_id as [__role__role_id], r.person_id, r.name from Person p inner join Role r on p.person_id = r.person_id order by p.person_id;"; + DBUQuery *query = dbuQueryCreate(conn, mp->allocator, sql1); + DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); + dbuObjectBuilderSetDenseResult(builder, true); + CxList *persons = dbuObjectBuilderGetList(builder); + + CX_TEST_CALL_SUBROUTINE(verifyMultiTableResult, persons, false); + CX_TEST_CALL_SUBROUTINE(verifyPersonRoles, persons); + + dbuObjectBuilderDestroy(builder); + } + + cxMempoolFree(mp); +}
--- a/test/database.h Thu Jan 01 17:59:32 2026 +0100 +++ b/test/database.h Fri Jan 02 15:36:13 2026 +0100 @@ -54,6 +54,7 @@ CX_TEST(testMultiTableQuery1); CX_TEST(testMultiTableQuery2); CX_TEST(testMultiTableQueryUnknownResult1); +CX_TEST(testQuerySubListDense1); #ifdef __cplusplus
--- a/test/main.c Thu Jan 01 17:59:32 2026 +0100 +++ b/test/main.c Fri Jan 02 15:36:13 2026 +0100 @@ -137,6 +137,7 @@ cx_test_register(suite, testMultiTableQuery1); cx_test_register(suite, testMultiTableQuery2); cx_test_register(suite, testMultiTableQueryUnknownResult1); + cx_test_register(suite, testQuerySubListDense1); #endif cx_test_register(suite, testObjectToJsonSimple); cx_test_register(suite, testObjectToJsonChildObj);