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 }