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