From d86bc412fc94158c3e478bbe645e3bb405902d94 Mon Sep 17 00:00:00 2001 From: MoonlightSentinel Date: Fri, 7 May 2021 21:33:40 +0200 Subject: [PATCH 1/2] database_generation: Don't assign to functions, ... in populateFromDbRow Using `tupleof` instead of `__traits(allMembers, ...)` skips over all nested declarations that are not actual fields. --- database.d | 52 +++++++++++++++++++++++++++++++++++++++++++ database_generation.d | 28 ++++++++++++++++++----- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/database.d b/database.d index 99d0257..51e49ab 100644 --- a/database.d +++ b/database.d @@ -1468,5 +1468,57 @@ struct varchar(size_t max) { alias asString this; } +version (unittest) +{ + /// Unittest utility that returns a predefined set of values + package (arsd) final class PredefinedResultSet : ResultSet + { + string[] fields; + Row[] rows; + size_t current; + this(string[] fields, Row[] rows) + { + this.fields = fields; + this.rows = rows; + foreach (ref row; rows) + row.resultSet = this; + } + int getFieldIndex(const string field) const + { + foreach (const idx, const val; fields) + if (val == field) + return cast(int) idx; + + assert(false, "No field with name: " ~ field); + } + + string[] fieldNames() + { + return fields; + } + + @property bool empty() const + { + return current == rows.length; + } + + Row front() @property + { + assert(!empty); + return rows[current]; + } + + void popFront() + { + assert(!empty); + current++; + } + + size_t length() @property + { + return rows.length - current; + } + } +} diff --git a/database_generation.d b/database_generation.d index 30b8abf..6c4470e 100644 --- a/database_generation.d +++ b/database_generation.d @@ -378,11 +378,9 @@ auto find(alias T)(Database db, int id) { private void populateFromDbRow(T)(ref T t, Row record) { foreach(field, value; record) { sw: switch(field) { - static foreach(memberName; __traits(allMembers, T)) { - case memberName: - static if(is(typeof(__traits(getMember, T, memberName)))) { - populateFromDbVal(__traits(getMember, t, memberName), value); - } + static foreach(const idx, alias mem; T.tupleof) { + case __traits(identifier, mem): + populateFromDbVal(t.tupleof[idx], value); break sw; } default: @@ -414,6 +412,26 @@ private void populateFromDbVal(V)(ref V val, string value) { } } +unittest +{ + static struct SomeStruct + { + int a; + void foo() {} + int b; + } + + auto rs = new PredefinedResultSet( + [ "a", "b" ], + [ Row([ "1", "2" ]) ] + ); + + SomeStruct s; + populateFromDbRow(s, rs.front); + + assert(s.a == 1); + assert(s.b == 2); +} /++ Gets all the children of that type. Specifically, it looks in T for a ForeignKey referencing B and queries on that. From d8c45fe1bedcc15e292c735b8f2ee942505a8e55 Mon Sep 17 00:00:00 2001 From: MoonlightSentinel Date: Fri, 7 May 2021 22:12:52 +0200 Subject: [PATCH 2/2] database_generation: Don't require an id attribute for insert --- database_generation.d | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/database_generation.d b/database_generation.d index 6c4470e..0c80c65 100644 --- a/database_generation.d +++ b/database_generation.d @@ -341,11 +341,28 @@ void insert(O)(ref O t, Database db) { foreach(row; db.query("SELECT max(id) FROM " ~ toTableName(O.stringof))) t.id.value = to!int(row[0]); } else { - foreach(row; builder.execute(db, "RETURNING id")) // FIXME: postgres-ism - t.id.value = to!int(row[0]); + static if (__traits(hasMember, O, "id")) + { + foreach(row; builder.execute(db, "RETURNING id")) // FIXME: postgres-ism + t.id.value = to!int(row[0]); + } + else + { + builder.execute(db); + } } } +// Check that insert doesn't require an `id` +unittest +{ + static struct NoPK + { + int a; + } + + alias test = insert!NoPK; +} /// class RecordNotFoundException : Exception { this() { super("RecordNotFoundException"); }