--- 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); +}