I'd almost be sort of surprised if someone here hasn't thought of this already, but I'll drop it out here anyways.

This is a proof-of-concept program I wrote to do a query with the convenience of a "SELECT * FROM ...", but without actually pulling every column in the database.

Additionally, it generates this "Entity" type that has a minimal amount of memory allocated for holding the data. I can imagine passing this entity type into templated functions to do nontrivial calculations. Perhaps use of metaprogramming like this can be used to dodge some of the object-relational impedance mismatch problems?

(I'm pretty sure I ran into a bunch of compiler bugs while doing this. Unfortunately it is difficult for me to isolate them right now.)

Here's the code:

import std.stdio;
import std.array;

class Query(string queryText)
{
        struct Entity(string queryText)
        {
                private static size_t bufSize = 0;
                private static size_t[string] m_offsets;
                private static size_t registerField(T)(string varName)
                {
                        auto offset = bufSize;
                        m_offsets[varName] = offset;
                        bufSize += T.sizeof;
                        return offset;
                }
                
                static @property const(size_t[string]) offsets()
                {
                        return m_offsets;
                }
                
                private void[] buf;
                
                private struct registrator(T,string varName)
                {
                        static offset = 0;
                        
                        static this()
                        {
                                offset = registerField!T(varName);
                        }
                }
                
                private struct Transformationizer(T)
                {
                        union
                        {
                                ubyte[T.sizeof] generic;
                                T specific;
                        }
                }
                
                T get(T,string varName)()
                {
                        Transformationizer!(T) t;
                        registrator!(T,varName) r;
                        auto i1 = r.offset;
                        
                        t.generic = cast(ubyte[])
                                buf[i1 .. i1 + T.sizeof];
                        return t.specific;
                }
                
                
                static opCall()
                {
                        Entity e;
                        e.buf = new void[bufSize];
                        //writefln("m_offsets = %s\n", e.m_offsets);
                        return e;
                }
                
                string toString()
                {
                        return std.string.format(
                                "Entity.offsets = %s", offsets);
                }
                
                ~this()
                {
                        delete buf;
                }
        }
        
        alias Entity!queryText EntityT;
        string finalQuery;
        
        // This is very stub-like.
        int opApply(int delegate(ref EntityT) dg)
        {
                auto e = EntityT();
                dg(e);
                return 0;
        }
        
        this()
        {
                char[] columns = new char[0];
                foreach( fieldName; EntityT.offsets.keys )
                {
                        columns ~= fieldName ~ ",";
                }
                columns = columns[0..$-1];
                
                finalQuery = std.array.replace(
                        queryText,
                        "ONLY_NEEDED_COLUMNS",
                        columns);
        }
}

void someNonTrivialCalculation(T)(T contact)
{
        auto country = contact.get!(string,"country");
}

void main()
{
        scope query = new Query!(
                "SELECT ONLY_NEEDED_COLUMNS "
                "FROM contacts c "
                "WHERE c.country = 'US' "
                "AND   c.sales > 10000;")();
        
        foreach(entity; query)
        {
                writefln("%s",entity);
                auto email_addy = entity.get!(string,"email");
                auto full_name  = entity.get!(string,"name");
                someNonTrivialCalculation(entity);
                //Email.spam(email_addy, full_name,
                        "We have this great new offer for you!");
        }
        
        writefln("Final query:\n  %s", query.finalQuery);
        writeln("done.");
}

-------------------------------------------------------------
My output:

Entity.offsets = [email:0,country:16,name:8]
Final query:
SELECT email,country,name FROM contacts c WHERE c.country = 'US' AND c.sales > 10000;
done.

Reply via email to