Automatic minimally sized SELECT clauses in SQL using D templates.

Chad J chadjoan at __spam.is.bad__gmail.com
Sun Mar 4 10:58:55 PST 2012


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.



More information about the Digitalmars-d mailing list