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