--- a/test/database.c Fri Jan 02 15:36:13 2026 +0100 +++ b/test/database.c Sat Jan 03 17:21:52 2026 +0100 @@ -311,6 +311,7 @@ CxMempool *mp = cxMempoolCreateSimple(64); CX_TEST_DO { + // select persons + addresses, a.address_id as __address__address_id (table separator) const char *sql1 = "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 order by p.person_id;"; DBUQuery *query = dbuQueryCreate(conn, mp->allocator, sql1); DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); @@ -328,6 +329,7 @@ CxMempool *mp = cxMempoolCreateSimple(64); CX_TEST_DO { + // select persons + addresses, NULL as __address (table seprator) const char *sql1 = "select p.*, NULL as [__address], a.* from Person p inner join Address a on p.address_id = a.address_id order by p.person_id;"; DBUQuery *query = dbuQueryCreate(conn, mp->allocator, sql1); DBUObjectBuilder *builder = dbuObjectBuilder(person, query, mp->allocator); @@ -356,6 +358,7 @@ CxMempool *mp = cxMempoolCreateSimple(64); CX_TEST_DO { + // select persons, addresses and countries const char *sql2 = "select p.*, " "a.address_id as [__address__address_id], a.street, a.zip, a.city, " "c.country_id as [__country__country_id], c.name " @@ -378,6 +381,7 @@ CxMempool *mp = cxMempoolCreateSimple(64); CX_TEST_DO { + // select persons, addresses and countries const char *sql2 = "select p.*, 'unknown1' as unknown1, " "a.address_id as [__address__address_id], a.street, 'unknown2' as unknown2, a.zip, 123 as unknown3, a.city, " "NULL as [__unknowntab], 456 as unknown_4, " @@ -422,6 +426,7 @@ CxMempool *mp = cxMempoolCreateSimple(64); CX_TEST_DO { + // select persons + a list of roles per person 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); @@ -437,3 +442,50 @@ cxMempoolFree(mp); } + +CX_TEST(testQuerySubListDenseWithMultiTable1) { + CxMempool *mp = cxMempoolCreateSimple(64); + + CX_TEST_DO { + const char *sql1 = + "select p.*, NULL as [__address], a.*, 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 " + "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, true); // also verify address + CX_TEST_CALL_SUBROUTINE(verifyPersonRoles, persons); + + dbuObjectBuilderDestroy(builder); + } + + cxMempoolFree(mp); +} + +CX_TEST(testQuerySubListDenseWithMultiTable2) { + CxMempool *mp = cxMempoolCreateSimple(64); + + // Same as testQuerySubListDenseWithMultiTable1, but with a different column order + // person, role, address + + CX_TEST_DO { + const char *sql1 = + "select p.*, r.role_id as [__role__role_id], r.person_id, r.name, NULL as [__address], a.* " + "from Person p inner join Address a on p.address_id = a.address_id " + "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, true); // also verify address + CX_TEST_CALL_SUBROUTINE(verifyPersonRoles, persons); + + dbuObjectBuilderDestroy(builder); + } + + cxMempoolFree(mp); +}