1 module misc; 2 // Copyright Stefan Koch 2015 - 2018. 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.md or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 import sqlited; 8 import utils; 9 import std.traits; 10 11 struct_type deserialize(struct_type)(Database.Row r) if (is(struct_type == struct)) { 12 struct_type instance; 13 uint ctr; 14 foreach (member; __traits(derivedMembers, struct_type)) { 15 alias type = typeof(__traits(getMember, instance, member)); 16 static if (!is(type == function)) { 17 __traits(getMember, instance, member) = r.getAs!(type)(ctr++); 18 } 19 } 20 return instance; 21 } 22 23 import std.typecons; 24 25 struct Table { 26 const Database.PageRange pages; 27 const uint rootPage; 28 29 int opApply(scope int delegate(const Database.Row r) dg) { 30 readRowDg!dg(this); 31 return 0; 32 } 33 34 struct_type[] deserialize(struct_type)() if (is(struct_type == struct)) { 35 struct_type[] result; 36 foreach(row;this) { 37 result ~= row.deserialize!struct_type; 38 } 39 return result; 40 } 41 42 43 } 44 45 Table table(const Database db, in string tableName) pure { 46 uint rootPage; 47 48 readRows!( 49 (r) { 50 if (r.column(0).getAs!string == "table" && 51 r.column(1).getAs!string == tableName) { 52 rootPage = r.column(3).getAs!uint - 1; 53 } 54 //assert(rootPage, "table not found"); 55 })(db.pages[0], db.pages); 56 57 58 return Table(db.pages, rootPage); 59 } 60 61 62 /// usage : table.select("name","surname").where!("age","sex", (age, sex) => sex.as!Sex == Sex.female, age.as!uint < 40)) 63 /// or table.select("name").where!((type) => type.as!string == "table")("type").as!string; 64 /// or join().select() 65 /+ 66 auto where(S,T...)(S selectResult, T ) 67 68 auto where(S,T...)(S selectResult) { 69 foreach(i,Element;T) { 70 static if (i == 0) { 71 static assert(is(Element == delegeate) || is(Element == function), 72 "first template argument to where has to be a delegate or a function"); 73 static assert() 74 } 75 } 76 } 77 78 auto SQL(SQLElements...)(Database db) { 79 // static assert (allStatiesfy(isSQLElement!SQLElements)) 80 foreach(elem;SQLElements) { 81 static if (isSelect!elem) { 82 //assert that there is just one select 83 } else static if (isWhere!elem) { 84 85 } 86 } 87 } 88 +/ 89 /// handlePage is used to itterate over interiorPages transparently 90 /+ 91 void* handlePageF(Database.BTreePage page, 92 Database.PageRange pages, 93 void* function(Database.BTreePage, Database.PageRange, void*) pageHandlerF, 94 void* initialState = null) { 95 handleRow!( 96 (page, pages) => initialState = pageHandlerF(page, pages, initialState) 97 )(page, pages); 98 99 return initialState; 100 } 101 +/ 102 template pageHandlerTypeP(alias pageHandler) { 103 alias pageHandlerTypeP = typeof((cast(const)Database.BTreePage.init)); 104 } 105 106 template pageHandlerTypePP(alias pageHandler) { 107 alias pageHandlerTypePP = typeof(pageHandler(cast(const)Database.BTreePage.init, cast(const)Database.PageRange.init)); 108 } 109 110 template rowHandlerTypeR(alias rowHandler) { 111 alias rowHandlerTypeR = typeof(rowHandler(cast(const)Database.BTreePage.Row.init)); 112 } 113 114 template rowHandlerTypeRP(alias rowHandler) { 115 alias rowHandlerTypeRP = typeof(rowHandler(cast(const)Database.BTreePage.Row.init, cast(const)Database.PageRange.init)); 116 } 117 118 template rowHandlerReturnType(alias rowHandler) { 119 alias typeR = rowHandlerTypeR!rowHandler; 120 121 static if (is(typeR)) { 122 alias rowHandlerReturnType = typeR; 123 } else { 124 static assert(0, "pageHandler has to be callable with (Row)" ~ typeof(rowHandler).stringof); 125 } 126 } 127 128 template pageHandlerRetrunType(alias pageHandler) { 129 alias typePP = pageHandlerTypePP!pageHandler; 130 alias typeP = pageHandlerTypeP!pageHandler; 131 132 static if (is(typePP)) { 133 alias pageHandlerRetrunType = typePP; 134 } else static if (is(typeP)) { 135 alias pageHandlerRetrunType = typeP; 136 } else { 137 static assert(0, "pageHandler has to be callable with (BTreePage) or (BTreePage, PageRange)" ~ typeof(pageHandler).stringof); 138 } 139 } 140 static assert (is(pageHandlerRetrunType!((page, pages) => page))); 141 142 auto readRows(alias RowHandler)(const Table table) { 143 return readRows!(RowHandler)(table.pages[cast(uint)table.rootPage], table.pages); 144 } 145 146 int readRowDg(alias dg)(const Table table) { 147 readRows!((r) {dg(r);})(table); 148 return 0; 149 } 150 151 RR readRows(alias rowHandler, RR = rowHandlerReturnType!(rowHandler)[])(const Database.BTreePage page, 152 const Database.PageRange pages) { 153 RR returnArray; 154 enum noReturn = is(RR == void[]); 155 enum isPure = is(typeof((){void _() pure {rowHandler(cast(const)Database.Row.init);}}())); 156 157 // pragma(msg, isPure); 158 // static assert(isPure); 159 readRows!rowHandler(page, pages, returnArray); 160 return returnArray; 161 } 162 163 164 /// handlePage is used to itterate over interiorPages transparently 165 void readRows(alias rowHandler,bool writable = false, RR)(const Database.BTreePage page, 166 const Database.PageRange pages, ref RR returnRange) { 167 alias hrt = rowHandlerReturnType!(rowHandler); 168 alias defaultReturnRangeType = hrt[]; 169 enum isPure = is(typeof((){void _() pure {rowHandler(cast(const)Database.Row.init);}}())); 170 enum noReturn = is(hrt == void) || is(hrt == typeof(null)); 171 172 static assert(is(hrt), "rowHandler has to be callable with (Row)" ~ typeof(rowHandler).stringof); 173 174 auto cpa = page.getCellPointerArray(); 175 176 switch (page.pageType) with (Database.BTreePage.BTreePageType) { 177 178 case tableLeafPage: { 179 foreach(cp;cpa) { 180 static if (noReturn) { 181 rowHandler(page.getRow(cp, pages, page.pageType)); 182 } else { 183 static if (is (RR == defaultReturnRangeType)) { 184 returnRange ~= rowHandler(page.getRow(cp, pages, page.pageType)); 185 } else { 186 returnRange.put(rowHandler(page.getRow(cp, pages, page.pageType))); 187 } 188 } 189 } 190 } break; 191 192 193 case tableInteriorPage: { 194 foreach(cp;cpa) { 195 readRows!rowHandler(pages[BigEndian!uint(page.page[cp .. cp + uint.sizeof]) - 1], pages, returnRange); 196 //TODO Read The Key! 197 } 198 199 readRows!rowHandler(pages[page.header._rightmostPointer - 1], pages, returnRange); 200 201 } break; 202 203 case indexInteriorPage: { 204 205 foreach(cp_;cpa) { 206 ushort cp = cast(ushort)(cp_ + uint.sizeof); 207 208 static if (noReturn) { 209 rowHandler(page.getRow(cp, pages, page.pageType)); 210 } else { 211 static if (is (RR == defaultReturnRangeType)) { 212 returnRange ~= rowHandler(page.getRow(cp, pages, page.pageType)); 213 } else { 214 returnRange.put(rowHandler(page.getRow(cp, pages, page.pageType))); 215 } 216 } 217 218 readRows!rowHandler(pages[BigEndian!uint(page.page[cp_ .. cp]) - 1], pages, returnRange); 219 } 220 221 readRows!rowHandler(pages[page.header._rightmostPointer - 1], pages, returnRange); 222 223 224 } break ; 225 226 case indexLeafPage: { 227 228 foreach(cp;cpa) { 229 static if (noReturn) { 230 rowHandler(page.getRow(cp, pages, page.pageType)); 231 } else { 232 static if (is (RR == defaultReturnRangeType)) { 233 returnRange ~= rowHandler(page.getRow(cp, pages, page.pageType)); 234 } else { 235 returnRange.put(rowHandler(page.getRow(cp, pages, page.pageType))); 236 } 237 } 238 } 239 } break ; 240 241 default: 242 assert(0, "empty pages are not handled by readRows!"); 243 } 244 245 return ; 246 247 }