# HG changeset patch # User Olaf Wintermann # Date 1770231654 -3600 # Node ID 8503be5eea491c653cfbc5d5644a9f6b3e642f52 # Parent e3163dc41a80c0be53ad0367724f3db3c8cd3e59 add testMultiTableQuery3 diff -r e3163dc41a80 -r 8503be5eea49 dbutils/dbutils/dbutils.h --- a/dbutils/dbutils/dbutils.h Wed Jan 21 18:19:31 2026 +0100 +++ b/dbutils/dbutils/dbutils.h Wed Feb 04 20:00:54 2026 +0100 @@ -97,7 +97,7 @@ * value: DBUField* */ CxMap *fields; - + /* * object fields * diff -r e3163dc41a80 -r 8503be5eea49 test/database.c --- a/test/database.c Wed Jan 21 18:19:31 2026 +0100 +++ b/test/database.c Wed Feb 04 20:00:54 2026 +0100 @@ -94,6 +94,8 @@ static DBUClass *address; static DBUClass *person; static DBUClass *role; +static DBUClass *resource; +static DBUClass *note; typedef struct Country { int64_t country_id; @@ -130,6 +132,23 @@ cxmutstr name; } Role; +typedef struct Resource { + int64_t resource_id; + int64_t parent_id; + char *nodename; + char *content; + bool iscollection; +} Resource; + +typedef struct Note { + int64_t note_id; + int64_t resource_id; + char *tags; + int type; + + Resource *resource; +} Note; + int init_db_tests(void) { ctx = dbuContextCreate(); @@ -140,7 +159,7 @@ dbuClassAdd(address, Address, street); dbuClassAdd(address, Address, zip); dbuClassAdd(address, Address, city); - dbuClassAddObj(address, "country_id", offsetof(Address, country), country); + dbuClassAddObj(address, "country", offsetof(Address, country), country); role = dbuRegisterClass(ctx, "role", Role, role_id); dbuClassAdd(role, Role, person_id); @@ -152,12 +171,25 @@ dbuClassAdd(person, Person, age); dbuClassAdd(person, Person, iscustomer); dbuClassAdd(person, Person, hash); - dbuClassAddObj(person, "address_id", offsetof(Person, address), address); + dbuClassAddObj(person, "address", offsetof(Person, address), address); dbuClassAddCxLinkedList(person, "person_id", offsetof(Person, roles), role); dbuClassAddForeignKey(role, Role, person_id, person); dbuClassAdd(role, Role, name); + resource = dbuRegisterClass(ctx, "resource", Resource, resource_id); + dbuClassAdd(resource, Resource, parent_id); + dbuClassAdd(resource, Resource, nodename); + dbuClassAdd(resource, Resource, content); + dbuClassAdd(resource, Resource, iscollection); + + note = dbuRegisterClass(ctx, "note", Note, note_id); + dbuClassAdd(note, Note, resource_id); + dbuClassAdd(note, Note, tags); + dbuClassAdd(note, Note, type); + dbuClassAddObj(note, "resource", offsetof(Note, resource), resource); + + return 0; } @@ -409,6 +441,55 @@ cxMempoolFree(mp); } +CX_TEST(testMultiTableQuery3) { + CxMempool *mp = cxMempoolCreateSimple(64); + + CX_TEST_DO { + const char *sql1 = + "select n.*, r.resource_id as [__resource__resource_id], r.parent_id, " \ + "r.nodename, r.content, r.iscollection " \ + "from resource r inner join note n on r.resource_id = n.resource_id " \ + "where parent_id = (select resource_id from resource where nodename = 'Collection1') ;"; + DBUQuery *q = dbuQueryCreate(conn, mp->allocator, sql1); + DBUObjectBuilder *builder = dbuObjectBuilder(note, q, mp->allocator); + CxList *notes = dbuObjectBuilderGetList(builder); + dbuObjectBuilderDestroy(builder); + + CX_TEST_ASSERT(notes); + CX_TEST_ASSERT(cxListSize(notes) == 2); + + Note *n0 = cxListAt(notes, 0); + Note *n1 = cxListAt(notes, 1); + + CX_TEST_ASSERT(n0); + CX_TEST_ASSERT(n1); + + CX_TEST_ASSERT(n0->note_id > 0); + CX_TEST_ASSERT(n0->resource_id > 0); + CX_TEST_ASSERT(!cx_strcmp(n0->tags, "todo, test")); + CX_TEST_ASSERT(n0->type == 1); + CX_TEST_ASSERT(n0->resource); + CX_TEST_ASSERT(n0->resource_id == n0->resource->resource_id); + CX_TEST_ASSERT(!cx_strcmp(n0->resource->nodename, "note1")); + CX_TEST_ASSERT(!n0->resource->iscollection); + CX_TEST_ASSERT(n0->resource->parent_id > 0); + CX_TEST_ASSERT(!cx_strcmp(n0->resource->content, "Hello World!")); + + CX_TEST_ASSERT(n1->note_id > 0); + CX_TEST_ASSERT(n1->resource_id > 0); + CX_TEST_ASSERT(!cx_strcmp(n1->tags, "work, project2501, ai")); + CX_TEST_ASSERT(n1->type == 2); + CX_TEST_ASSERT(n1->resource); + CX_TEST_ASSERT(n1->resource_id == n1->resource->resource_id); + CX_TEST_ASSERT(!cx_strcmp(n1->resource->nodename, "note2")); + CX_TEST_ASSERT(!n1->resource->iscollection); + CX_TEST_ASSERT(n1->resource->parent_id > 0); + CX_TEST_ASSERT(!cx_strcmp(n1->resource->content, "Test String")); + } + + cxMempoolFree(mp); +} + CX_TEST(testMultiTableQueryUnknownResult1) { CxMempool *mp = cxMempoolCreateSimple(64); diff -r e3163dc41a80 -r 8503be5eea49 test/database.h --- a/test/database.h Wed Jan 21 18:19:31 2026 +0100 +++ b/test/database.h Wed Feb 04 20:00:54 2026 +0100 @@ -53,6 +53,7 @@ CX_TEST(testMultiTableQuery); CX_TEST(testMultiTableQuery1); CX_TEST(testMultiTableQuery2); +CX_TEST(testMultiTableQuery3); CX_TEST(testMultiTableQueryUnknownResult1); CX_TEST(testQuerySubListDense1); CX_TEST(testQuerySubListDenseWithMultiTable1); diff -r e3163dc41a80 -r 8503be5eea49 test/main.c --- a/test/main.c Wed Jan 21 18:19:31 2026 +0100 +++ b/test/main.c Wed Feb 04 20:00:54 2026 +0100 @@ -136,6 +136,7 @@ cx_test_register(suite, testMultiTableQuery); cx_test_register(suite, testMultiTableQuery1); cx_test_register(suite, testMultiTableQuery2); + cx_test_register(suite, testMultiTableQuery3); cx_test_register(suite, testMultiTableQueryUnknownResult1); cx_test_register(suite, testQuerySubListDense1); cx_test_register(suite, testQuerySubListDenseWithMultiTable1); diff -r e3163dc41a80 -r 8503be5eea49 testdata.sql --- a/testdata.sql Wed Jan 21 18:19:31 2026 +0100 +++ b/testdata.sql Wed Feb 04 20:00:54 2026 +0100 @@ -28,6 +28,21 @@ name text ); +create table Resource( + resource_id integer primary key autoincrement, + parent_id integer references Resource(resource_id), + nodename text, + content text, + iscollection integer default 0 +); + +create table Note( + note_id integer primary key autoincrement, + resource_id integer references Resource(resource_id), + tags text, + type int +); + insert into country (name) values ('Germany'); insert into address (street, zip, city, country_id) @@ -46,3 +61,39 @@ (1, 'manager'), (2, 'extern'); +insert into Resource(nodename, iscollection) +values +('root', 1); + +insert into Resource(parent_id, nodename, iscollection) +values +((select resource_id from Resource where nodename = 'root'), 'Collection1', 1); + +insert into Resource(parent_id, nodename, iscollection) +values +((select resource_id from Resource where nodename = 'root'), 'Collection2', 1); + +insert into Resource(parent_id, nodename, content) +values +((select resource_id from Resource where nodename = 'Collection1'), 'note1', 'Hello World!'); + +insert into Resource(parent_id, nodename, content) +values +((select resource_id from Resource where nodename = 'Collection1'), 'note2', 'Test String'); + +insert into Resource(parent_id, nodename, content) +values +((select resource_id from Resource where nodename = 'Collection2'), 'note3', 'Content Text'); + +insert into Note(resource_id, tags, type) +values +((select resource_id from Resource where nodename = 'note1'), 'todo, test', 1); + +insert into Note(resource_id, tags, type) +values +((select resource_id from Resource where nodename = 'note2'), 'work, project2501, ai', 2); + +insert into Note(resource_id, tags, type) +values +((select resource_id from Resource where nodename = 'note3'), 'finance', 3); +