1 
2 
3 module mars.defs;
4 
5 import std.meta;
6 import std.traits;
7 import std.algorithm;
8 import std.array;
9 import std.typecons;
10 
11 /+
12 struct Schema2 
13 {
14     this(string name, string[] tt) {
15         this.name = name;
16         foreach(t; tt){
17             this.tables ~= Table2(&this, t);
18         }
19     }
20 
21     string name;
22     Table2[] tables;
23 }
24 struct Table2 {
25     Schema2* schema;
26     string name;
27 }
28 enum s1 = Schema2("s1", ["t1", "t2"]);
29 static assert(s1.tables.length == 2);
30 static assert(s1.tables[0].schema is s1);
31 
32 auto schema(string name){ return Schema2(name, []); }
33 auto table(Schema2 schema, string name){
34     string[] tt; foreach(t; schema.tables){ tt ~= t.name; }
35     return Schema2(schema.name, schema.tables.map!"a.name"().array ~ name);
36 }
37 
38 enum ss = schema("foo");
39 enum s2 = schema("foo").table("bar");
40 +/
41 
42 struct Schema {
43     string name;
44     immutable(Table)[] tables;
45 
46     auto tableNamed(string name) const {
47         return tables.find!((t) => t.name == name)[0];
48     }
49 }
50 /+
51 Schema table(immutable(Schema) schema, string name, immutable(Col)[] columns, immutable(size_t)[] primaryKey, immutable(Reference)[] references, size_t index){
52     auto s = Schema(schema.name, schema.tables ~ immutable(Table)(name, columns, primaryKey, references, index));
53     return s;
54 }
55 unittest {
56     enum s = Schema("foo").table("bar", [immutable(Col)("c1", Type.integer)], [], [], 0);
57     static assert( s.tables.length == 1 );
58     enum s2 = Schema("foo")
59         .table("bar", [immutable(Col)("c1", Type.integer)], [], [], 0)
60         .table("bar", [immutable(Col)("c1", Type.integer)], [], [], 1)
61         ;
62     static assert( s.tables.length == 2 );
63 }
64 +/
65 struct Table { 
66     string name;
67     immutable(Col)[] columns;
68     size_t[] primaryKey;
69     Reference[] references;
70     size_t index; /// the unique index of the table in the system.
71     bool durable = true;
72 
73     immutable(Schema)* schema;
74 
75     /*
76     If the table primary key is set by the terver, we need to return it to the client. */
77     const(Col)[] returningCols() const
78     {
79         import std.algorithm : canFind, filter;
80         import std.array : array;
81         import std.range : indexed;
82         
83         return pkCols
84             .filter!( (c) => [Type.smallserial, Type.serial].canFind(c.type) )
85             .array;
86     }
87     
88     /**
89      * returns the primary keys columns of a table */
90     immutable(Col)[] pkCols() const
91     {
92         import std.array : array;
93         import std.range : indexed;
94 
95         return columns.indexed(primaryKey).array;
96     }
97 
98 }
99 unittest
100 {
101     immutable static t1 = immutable(Table)("t", [
102             Col("c1", Type.integer), Col("c2", Type.text), Col("c3", Type.integer)
103     ], [0,2], []);
104 
105     static assert( t1.pkCols() == [t1.columns[0], t1.columns[2]] );
106     
107     enum columns = t1.columns;
108     void f1(asD!columns cols){ int i = cols[0]; string s = cols[1]; int j = cols[2]; } 
109     f1(1, "foo", 2);
110 }
111 
112 struct Col {
113     string name;
114     Type type;
115     bool null_;
116 }
117 
118 struct Reference {
119     size_t[] referenceCols;
120     string referencedTable;
121     size_t[] referencedCols;
122 }
123 
124 enum Type { 
125     unknown, 
126     boolean,
127     date, 
128     real_, doublePrecision, 
129     smallint, integer, bigint, // 16, 32, 64 bit, postgresql
130     smallserial, serial, 
131     text, varchar,
132     bytea 
133 }
134 
135 /**
136  * returns the D type for the passed SQL type. */
137 template asD(alias t) if( is(Unqual!(typeof(t)) == Type) )
138 {
139 
140     static if( t == Type.integer )              alias asD = int;
141     else static if( t == Type.bigint )          alias asD = long;
142     else static if( t == Type.text )            alias asD = string;
143     else static if( t == Type.serial )          alias asD = int;
144     else static if( t == Type.boolean )         alias asD = bool;
145     else static if( t == Type.smallint )        alias asD = short;
146     else static if( t == Type.smallserial )     alias asD = short;
147     else static if( t == Type.real_ )           alias asD = float;
148     else static if( t == Type.doublePrecision ) alias asD = double;
149     else static if( t == Type.date )            alias asD = Date;
150     else static if( t == Type.bytea )           alias asD = ubyte[];
151     else static assert(false);
152 }
153 
154 /**
155  * returns the D type for a column. */
156 template asD(alias c) if( is(Unqual!(typeof(c)) == Col) )
157 {
158     enum t = c.type;
159     alias asD = .asD!(t);
160 }
161 static assert( is(asD!( Col("c", Type.integer) ) == int) );
162 static assert( is(asD!( Col("c", Type.real_) ) == float ) );
163 
164 /**
165  * returns the D type for a sequence of columns. */
166 template asD(alias c) if( is(Unqual!(typeof(c)) : immutable(Col)[]) || is(Unqual!(typeof(c)) : Col[]) )
167 {
168 
169     enum cols = c;
170     static if(cols.length == 1){
171         alias asD = AliasSeq!(asD!(cols[0]));
172     }
173     else static if(cols.length >1){
174         alias asD = AliasSeq!(asD!(cols[0]), asD!(cols[1 .. $]));
175     }
176     else static assert(false);
177 }
178 static assert( is(asD!( [Col("c", Type.integer), Col("d", Type.text)] ) == AliasSeq!(int, string)) );
179 
180 /** 
181  * returns the D type and the name of the column. */
182 private template asStruct_(alias c, string prefix ="") if( is(Unqual!(typeof(c)) : immutable(Col)[]) || is(Unqual!(typeof(c)) : Col[]) )
183 {
184 
185     enum cols = c;
186     static if(cols.length == 1){
187         alias t = asD!(cols[0]);
188         enum string n = cols[0].name;
189         enum string asStruct_ = t.stringof ~ " " ~ prefix ~ n ~ ";"; //AliasSeq!(asD!(cols[0])););
190     }
191     else static if(cols.length >1){
192         enum string asStruct_ = (asD!(cols[0])).stringof ~ " " ~ prefix ~ cols[0].name ~ "; " ~ asStruct_!(cols[1 .. $]);
193     }
194     else static assert(false, cols.length);
195 }
196 
197 
198 
199 template asStruct(alias t)
200 {
201     enum cols = t.columns;
202     enum string structName = t.name ~ "Row";
203     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols) ~ "}";
204     mixin(def ~"; alias asStruct = " ~ structName ~ ";");
205 }
206 static assert(is( asStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [], [])) == struct )); 
207 
208 template asPkStruct(alias t)
209 {
210     static if( t.pkCols.length >0 ){
211         enum cols = t.pkCols;
212     }
213     else {
214         enum cols = t.columns;
215     }
216     enum string structName = t.name ~ "PkRow";
217     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols) ~ "}";
218     mixin(def ~"; alias asPkStruct = " ~ structName ~ ";");
219 }
220 static assert(is(asPkStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct ));
221 
222 template asPkParamStruct(alias t)
223 {
224     static if( t.pkCols.length >0 ){
225         enum cols = t.pkCols;
226     }
227     else {
228         enum cols = t.columns;
229     }
230     enum string structName = t.name ~ "PkRow";
231     enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols, "key") ~ "}";
232     mixin(def ~"; alias asPkParamStruct = " ~ structName ~ ";");
233 }
234 static assert(is(asPkStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct ));
235 
236 
237 /**
238  * returns the values of the primary keys of this table row. */
239 auto pkValues(alias table)(asStruct!table fixture)
240 {
241     asPkStruct!table keys;
242     // XXX fix with recursion
243     static if(table.primaryKey.length == 0){
244         static assert(keys.tupleof.length <=9, keys.tupleof.length);
245         static if(keys.tupleof.length == 1){ keys.tupleof[0] = fixture.tupleof[0]; }
246         static if(keys.tupleof.length == 2){ keys.tupleof[1] = fixture.tupleof[1]; }
247         static if(keys.tupleof.length == 3){ keys.tupleof[2] = fixture.tupleof[2]; }
248         static if(keys.tupleof.length == 4){ keys.tupleof[3] = fixture.tupleof[3]; }
249         static if(keys.tupleof.length == 5){ keys.tupleof[4] = fixture.tupleof[4]; }
250         static if(keys.tupleof.length == 6){ keys.tupleof[5] = fixture.tupleof[5]; }
251         static if(keys.tupleof.length == 7){ keys.tupleof[6] = fixture.tupleof[6]; }
252         static if(keys.tupleof.length == 8){ keys.tupleof[7] = fixture.tupleof[7]; }
253         static if(keys.tupleof.length == 9){ keys.tupleof[8] = fixture.tupleof[8]; }
254     }
255     else static if(table.primaryKey.length == 1){
256         keys.tupleof[0] = fixture.tupleof[table.primaryKey[0]];
257     }
258     else static if(table.primaryKey.length == 2){
259         keys.tupleof[0] = fixture.tupleof[0];
260         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
261     }
262     else static if(table.primaryKey.length == 3){
263         keys.tupleof[0] = fixture.tupleof[0];
264         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
265         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
266     }
267     else static if(table.primaryKey.length == 4){
268         keys.tupleof[0] = fixture.tupleof[0];
269         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
270         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
271         keys.tupleof[3] = fixture.tupleof[table.primaryKey[3]];
272     }
273     else static if(table.primaryKey.length == 5){
274         keys.tupleof[0] = fixture.tupleof[0];
275         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
276         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
277         keys.tupleof[3] = fixture.tupleof[table.primaryKey[3]];
278         keys.tupleof[4] = fixture.tupleof[table.primaryKey[4]];
279     }
280     else static assert(false);
281     return keys;
282 }
283 
284 /**
285  * returns the values of the primary keys of this table row. */
286 auto pkParamValues(alias table)(asStruct!table fixture)
287 {
288     asPkParamStruct!table keys;
289     // XXX fix with recursion
290     static if(table.primaryKey.length == 0){
291         static assert(keys.tupleof.length <=9, keys.tupleof.length);
292         static if(keys.tupleof.length == 1){ keys.tupleof[0] = fixture.tupleof[0]; }
293         static if(keys.tupleof.length == 2){ keys.tupleof[1] = fixture.tupleof[1]; }
294         static if(keys.tupleof.length == 3){ keys.tupleof[2] = fixture.tupleof[2]; }
295         static if(keys.tupleof.length == 4){ keys.tupleof[3] = fixture.tupleof[3]; }
296         static if(keys.tupleof.length == 5){ keys.tupleof[4] = fixture.tupleof[4]; }
297         static if(keys.tupleof.length == 6){ keys.tupleof[5] = fixture.tupleof[5]; }
298         static if(keys.tupleof.length == 7){ keys.tupleof[6] = fixture.tupleof[6]; }
299         static if(keys.tupleof.length == 8){ keys.tupleof[7] = fixture.tupleof[7]; }
300         static if(keys.tupleof.length == 9){ keys.tupleof[8] = fixture.tupleof[8]; }
301     }
302     else static if(table.primaryKey.length == 1){
303         keys.tupleof[0] = fixture.tupleof[table.primaryKey[0]];
304     }
305     else static if(table.primaryKey.length == 2){
306         keys.tupleof[0] = fixture.tupleof[0];
307         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
308     }
309     else static if(table.primaryKey.length == 3){
310         keys.tupleof[0] = fixture.tupleof[0];
311         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
312         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
313     }
314     else static if(table.primaryKey.length == 4){
315         keys.tupleof[0] = fixture.tupleof[0];
316         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
317         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
318         keys.tupleof[3] = fixture.tupleof[table.primaryKey[3]];
319     }
320     else static if(table.primaryKey.length == 5){
321         keys.tupleof[0] = fixture.tupleof[0];
322         keys.tupleof[1] = fixture.tupleof[table.primaryKey[1]];
323         keys.tupleof[2] = fixture.tupleof[table.primaryKey[2]];
324         keys.tupleof[3] = fixture.tupleof[table.primaryKey[3]];
325         keys.tupleof[4] = fixture.tupleof[table.primaryKey[4]];
326     }
327     else static assert(false);
328     return keys;
329 }