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 }