# HG changeset patch # User Olaf Wintermann # Date 1733947039 -3600 # Node ID f0c66b2139eace13283de4b520feee57b9b6de9c # Parent 1908c8b1599f4766637190a0461ff332f1abafc6 add support for child objects in the object builder diff -r 1908c8b1599f -r f0c66b2139ea dbutils/dbutils.c --- a/dbutils/dbutils.c Mon Dec 09 23:47:52 2024 +0100 +++ b/dbutils/dbutils.c Wed Dec 11 20:57:19 2024 +0100 @@ -53,6 +53,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyInt32(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); @@ -67,6 +68,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyUInt32(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); @@ -81,6 +83,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyInt64(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); @@ -95,6 +98,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyUInt64(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); @@ -109,6 +113,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyString(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); @@ -123,6 +128,7 @@ off_t primary_key_offset) { DBUClass *cls = dbuClassCreate(name); + cls->context = context; cls->obj_size = obj_size; dbuClassSetPrimaryKeyCxMutStr(cls, primary_key_column, primary_key_offset); cxMapPut(context->classes, name, cls); diff -r 1908c8b1599f -r f0c66b2139ea dbutils/object.c --- a/dbutils/object.c Mon Dec 09 23:47:52 2024 +0100 +++ b/dbutils/object.c Wed Dec 11 20:57:19 2024 +0100 @@ -195,18 +195,23 @@ // after a column is marked as the start of the new table // all following columns will be treated as columns of this table cxstring tabname = cx_strsubs(fieldname, 2); - DBUClass *fcls = cxMapGet(builder->ctx->classes, tabname); + cxstring remaining = cx_strstr(tabname, CX_STR("__")); + DBUClass *fcls = NULL; + if(remaining.length > 2) { + tabname.length = remaining.ptr - tabname.ptr; + + } + fcls = cxMapGet(builder->ctx->classes, tabname); + if(fcls) { - field_class = fcls; - cxstring remaining = cx_strstr(tabname, CX_STR("__")); if(remaining.length > 2) { - field = cxMapGet(cls->fields, cx_strsubs(remaining, 2)); - } else { - field_mapping[i].cls = fcls; + field = cxMapGet(fcls->fields, cx_strsubs(remaining, 2)); } + field_mapping[i].cls = fcls; + field_class = fcls; } } else { - field = cxMapGet(cls->fields, fieldname); + field = cxMapGet(field_class->fields, fieldname); } if(field) { @@ -236,27 +241,31 @@ void *current_obj = obj; void *child_obj = NULL; - DBUClass *child_cls = NULL; DBUClass *current_cls = cls; for(int i=0;iisNull(result, i); - if(field.cls != current_cls) { - // 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) { - child_obj = objresult->create(objresult, field.cls, a); - if(!child_obj) { - err = 1; - break; + if(field.cls && field.cls != current_cls) { + // following columns are for this new class + 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) { + child_obj = objresult->create(objresult, field.cls, a); + current_obj = child_obj; + if(!child_obj) { + err = 1; + break; + } + + if(field.cls->init) { + field.cls->init(child_obj, a); // TODO: check return + } + + obj_field->initObjValue(obj_field, a, obj, child_obj); } - - if(field.cls->init) { - field.cls->init(child_obj, a); // TODO: check return - } - - obj_field->initObjValue(obj_field, a, obj, child_obj); } } diff -r 1908c8b1599f -r f0c66b2139ea test/main.c --- a/test/main.c Mon Dec 09 23:47:52 2024 +0100 +++ b/test/main.c Wed Dec 11 20:57:19 2024 +0100 @@ -40,16 +40,41 @@ "email text, " "age integer, " "iscustomer integer , " -"hash integer);"; +"hash integer, " +"address_id integer);"; + +const char *sql_create_table_address = +"create table if not exists Address (" +"address_id integer primary key autoincrement, " +"street text, " +"zip text, " +"city text);"; + const char *sql_check_table = "select person_id from Person limit 1;"; -const char *sql_create_test_data = -"insert into person (name, email, age, iscustomer, hash) " + + +const char *sql_create_test_data1 = +"insert into address (street, zip, city) " +"values " +"('street 1', '12343', 'city 17'), " +"('street 2', '23456', 'city 18');"; + +const char *sql_create_test_data2 = +"insert into person (name, email, age, iscustomer, hash, address_id) " "values " -"('alice', 'alice@example.com', 30, 1, 123456789), " -"('bob', 'bob@example.com', 25, 0, 987654321);"; +"('alice', 'alice@example.com', 30, 1, 123456789, (select address_id from address where street = 'street 1')), " +"('bob', 'bob@example.com', 25, 0, 987654321, (select address_id from address where street = 'street 2'));"; + +typedef struct Address { + int64_t address_id; + + cxmutstr street; + cxmutstr zip; + cxmutstr city; +} Address; typedef struct Person { int64_t person_id; @@ -58,9 +83,13 @@ cxmutstr email; int age; bool iscustomer; - uint64_t hash; + uint64_t hash; + + Address *address; } Person; + + static int create_test_data(sqlite3 *db); int main(int argc, char **argv) { @@ -68,13 +97,18 @@ DBUContext *ctx = dbuContextCreate(); + DBUClass *address = dbuRegisterClass(ctx, "address", Address, address_id); + dbuClassAdd(address, Address, street); + dbuClassAdd(address, Address, zip); + dbuClassAdd(address, Address, city); + DBUClass *person = dbuRegisterClass(ctx, "person", Person, person_id); dbuClassAdd(person, Person, name); dbuClassAdd(person, Person, email); dbuClassAdd(person, Person, age); dbuClassAdd(person, Person, iscustomer); dbuClassAdd(person, Person, hash); - + dbuClassAddObj(person, "address_id", offsetof(Person, address), address); // Open or create the database @@ -92,7 +126,7 @@ DBUConnection *conn = dbuSQLiteConnectionFromDB(db, true); DBUQuery *query = conn->createQuery(conn, NULL); - dbuQuerySetSQL(query, "select * from Person;"); + dbuQuerySetSQL(query, "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;"); DBUObjectBuilder *builder = dbuObjectBuilder(person, query, cxDefaultAllocator); CxList *persons = dbuObjectBuilderGetList(builder); @@ -121,6 +155,14 @@ return 1; } + rc = sqlite3_exec(db, sql_create_table_address, 0, 0, &err_msg); + if(rc != SQLITE_OK) { + fprintf(stderr, "SQLite error: %s\n", err_msg); + sqlite3_free(err_msg); + sqlite3_close(db); + return 1; + } + sqlite3_stmt *stmt; rc = sqlite3_prepare_v2(db, sql_check_table, -1, &stmt, 0); @@ -140,7 +182,15 @@ return 0; } - rc = sqlite3_exec(db, sql_create_test_data, 0, 0, &err_msg); + rc = sqlite3_exec(db, sql_create_test_data1, 0, 0, &err_msg); + if(rc != SQLITE_OK) { + fprintf(stderr, "SQLite error: %s\n", err_msg); + sqlite3_free(err_msg); + sqlite3_close(db); + return 1; + } + + rc = sqlite3_exec(db, sql_create_test_data2, 0, 0, &err_msg); if(rc != SQLITE_OK) { fprintf(stderr, "SQLite error: %s\n", err_msg); sqlite3_free(err_msg);