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 enum string structName = t.name ~ "SyncPkParamRow"; 277 static if( t.pkCols.length >0 ){ 278 enum pkCols = t.pkCols; 279 enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(pkCols, "key") ~ asStruct_!(SyncCols) ~ "}"; 280 } 281 else { 282 enum cols = t.decoratedCols; 283 enum string def = "struct " ~ structName ~ " {" ~ asStruct_!(cols, "key") ~ "}"; 284 } 285 mixin(def ~"; alias asSyncPkParamStruct = " ~ structName ~ ";"); 286 } 287 static assert(is(asPkParamStruct!(Table("t", [immutable(Col)("c1", Type.integer), immutable(Col)("c2", Type.text)], [0], [])) == struct )); 288 289 290 291 /** 292 * returns the values of the primary keys of this table row. */ 293 auto pkValues(alias table)(const asStruct!table fixture) 294 { 295 asPkStruct!table keys; 296 assignCommonFields(keys, fixture); 297 return keys; 298 } 299 unittest { 300 static assert(luke.pkValues!People.name == "Luke"); 301 } 302 303 /** 304 * returns the values of the primary keys of this table row. */ 305 auto pkParamValues(alias table)(const asStruct!table fixture) 306 { 307 asPkStruct!table keys; 308 asPkParamStruct!table paramKeys; 309 assignCommonFields(keys, fixture); 310 assignFields(paramKeys, keys); 311 return paramKeys; 312 } 313 unittest { 314 static assert(luke.pkParamValues!People.keyname == "Luke"); 315 } 316 317 void assignCommonFields(R, V, size_t i = 0)(ref R r, V v) 318 { 319 alias NamesR = FieldNameTuple!R; 320 alias NamesV = FieldNameTuple!V; 321 322 enum string Name = NamesV[i]; 323 324 325 enum j = staticIndexOf!(Name, NamesR); 326 static if( j != -1 ){ 327 r.tupleof[j] = v.tupleof[i]; 328 } 329 static if(i+1 < NamesV.length){ 330 assignCommonFields!(R, V, i+1)(r, v); 331 } 332 } 333 unittest 334 { 335 struct V { string a; string b; string c; } 336 struct R { string b; string a; } 337 struct X { int b; string a; } 338 339 V v = V("a", "b", "c"); 340 R r; 341 X x; 342 343 static assert(!__traits(compiles, assignCommonFields!(X, V)(x, v) )); 344 345 assignCommonFields!(R, V)(r, v); 346 assert( r == R("b", "a")) ; 347 348 } 349 350 void assignFields(R, V, size_t i = 0)(ref R r, V v) 351 { 352 r.tupleof[i] = v.tupleof[i]; 353 static if(i+1 < v.tupleof.length) assignFields!(R, V, i+1)(r, v); 354 } 355 unittest 356 { 357 struct V { string a; int b;} 358 struct R { string c; int d;} 359 auto v = V("hello", 42); 360 R r; 361 assignFields(r, v); 362 assert( r == R("hello", 42) ); 363 }