source: GenericIOSQLite.cxx @ 00587dc

Revision 00587dc, 10.5 KB checked in by Hal Finkel <hfinkel@…>, 10 years ago (diff)

Initial Commit (gio-base-20150317)

  • Property mode set to 100644
Line 
1#include "GenericIO.h"
2#include <cstring>
3#include <vector>
4#include <string>
5#include <limits>
6#include <sstream>
7#include <sqlite3ext.h>
8SQLITE_EXTENSION_INIT1
9
10using namespace std;
11using namespace gio;
12
13class PrinterBase {
14public:
15  virtual ~PrinterBase() {}
16  virtual void print(sqlite3_context *cxt, size_t i) = 0;
17};
18
19template <class T>
20class Printer : public PrinterBase {
21public:
22  Printer(GenericIO &G, size_t MNE, const string &N)
23    : Data(MNE + G.requestedExtraSpace()/sizeof(T)) {
24    G.addVariable(N, Data, true);
25  }
26
27  virtual void print(sqlite3_context *cxt, size_t i) {
28    T d = Data[i];
29    if (!numeric_limits<T>::is_integer) {
30      sqlite3_result_double(cxt, double(d));
31    } else {
32      if (sizeof(T) > 4) {
33        sqlite3_result_int64(cxt, (sqlite3_int64) d);
34      } else {
35        sqlite3_result_int(cxt, int(d));
36      }
37    }
38  }
39
40protected:
41  vector<T> Data;
42};
43
44template <typename T>
45PrinterBase *addPrinter(GenericIO::VariableInfo &V,
46                GenericIO &GIO, size_t MNE) {
47  if (sizeof(T) != V.Size)
48    return 0;
49
50  if (V.IsFloat == numeric_limits<T>::is_integer)
51    return 0;
52  if (V.IsSigned != numeric_limits<T>::is_signed)
53    return 0;
54
55  return new Printer<T>(GIO, MNE, V.Name);
56}
57
58// See: Michael Owens. Query Anything with SQLite.
59// Dr. Dobb's Journal. Nov. 2007.
60// http://www.drdobbs.com/database/query-anything-with-sqlite/202802959
61// See: http://www.sqlite.org/c3ref/load_extension.html
62// See: http://www.sqlite.org/vtab.html
63
64struct gio_vtab {
65  gio_vtab(const string &FN) : GIO(FN), MaxNumElems(0) {
66    memset(&vtab, 0, sizeof(sqlite3_vtab));
67
68    GIO.openAndReadHeader(false);
69
70    int NR = GIO.readNRanks();
71    for (int i = 0; i < NR; ++i) {
72      size_t NElem = GIO.readNumElems(i);
73      MaxNumElems = max(MaxNumElems, NElem);
74    }
75  }
76
77  sqlite3_vtab vtab;
78  GenericIO    GIO;
79  size_t       MaxNumElems;
80};
81
82struct gio_cursor {
83  gio_cursor(const GenericIO &G, size_t MaxNElem) : GIO(G) {
84    memset(&cursor, 0, sizeof(sqlite3_vtab_cursor));
85
86    vector<GenericIO::VariableInfo> VI;
87    GIO.getVariableInfo(VI);
88
89    Vars.resize(VI.size());
90    for (size_t i = 0; i < VI.size(); ++i) {
91      PrinterBase *P = 0;
92
93#define ADD_PRINTER(T) \
94      if (!P) P = addPrinter<T>(VI[i], GIO, MaxNElem)
95      ADD_PRINTER(float);
96      ADD_PRINTER(double);
97      ADD_PRINTER(unsigned char);
98      ADD_PRINTER(signed char);
99      ADD_PRINTER(int16_t);
100      ADD_PRINTER(uint16_t);
101      ADD_PRINTER(int32_t);
102      ADD_PRINTER(uint32_t);
103      ADD_PRINTER(int64_t);
104      ADD_PRINTER(uint64_t);
105#undef ADD_PRINTER
106
107      if (P) Printers.push_back(P);
108    }
109  }
110
111  sqlite3_vtab_cursor    cursor;
112  int                    Rank;
113  size_t                 Pos;
114  uint64_t               AbsPos;
115  GenericIO              GIO;
116  vector< vector<char> > Vars;
117  vector<PrinterBase *>  Printers;
118  vector<bool>           RankMask;
119};
120
121static
122int gio_connect(sqlite3* db, void *, int argc, const char *const *argv,
123               sqlite3_vtab **ppVTab, char **pzErr) {
124  if (argc < 4) {
125    *pzErr = sqlite3_mprintf("%s", "No input file specified!");
126    return SQLITE_ERROR;
127  }
128
129  *pzErr = 0;
130  try {
131    string FileName(argv[3]);
132
133    gio_vtab *tab = new gio_vtab(FileName);
134    if (!tab)
135      return SQLITE_ERROR;
136
137    vector<GenericIO::VariableInfo> VI;
138    tab->GIO.getVariableInfo(VI);
139
140    string Schema = "CREATE TABLE x(_rank INTEGER";
141    if (VI.size())
142      Schema += ", ";
143
144    for (size_t i = 0; i < VI.size(); ++i) {
145      if (VI[i].Size > 8)
146        continue;
147
148      Schema += VI[i].Name + (VI[i].IsFloat ? " REAL" : " INTEGER");
149      if (i != VI.size() - 1)
150        Schema += ", ";
151    }
152
153    Schema += ")";
154    if (sqlite3_declare_vtab(db, Schema.c_str()) != SQLITE_OK) {
155      *pzErr = sqlite3_mprintf("Could not declare schema: %s", Schema.c_str());
156      delete tab;
157      return SQLITE_ERROR;
158    }
159
160    *ppVTab = &tab->vtab;
161  } catch(exception &e) {
162     *pzErr = sqlite3_mprintf("%s", e.what());
163     return SQLITE_ERROR;
164  }
165
166  return SQLITE_OK;
167}
168
169static
170int gio_create(sqlite3* db, void *pAux, int argc, const char *const *argv,
171               sqlite3_vtab **ppVTab, char **pzErr) {
172  return gio_connect(db, pAux, argc, argv, ppVTab, pzErr);
173}
174
175static
176int gio_disconnect(sqlite3_vtab *pVTab) {
177  gio_vtab *tab = (gio_vtab *) pVTab;
178
179  delete tab;
180  return SQLITE_OK;
181}
182
183static
184int gio_destroy(sqlite3_vtab *pVTab) {
185  return gio_disconnect(pVTab);
186}
187
188static
189int gio_best_index(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo) {
190  gio_vtab *tab = (gio_vtab *) pVTab;
191
192  int IndexIndex = 0;
193  stringstream IndexName;
194  for (int i = 0; i < pInfo->nConstraint; ++i) {
195    if (!pInfo->aConstraint[i].usable)
196      continue;
197
198    char opcode = 0;
199    switch (pInfo->aConstraint[i].op) {
200    case SQLITE_INDEX_CONSTRAINT_EQ:
201      opcode = 'E';
202      break;
203    case SQLITE_INDEX_CONSTRAINT_GT:
204      opcode = 'g';
205      break;
206    case SQLITE_INDEX_CONSTRAINT_LT:
207      opcode = 'l';
208      break;
209    case SQLITE_INDEX_CONSTRAINT_GE:
210      opcode = 'G';
211      break;
212    case SQLITE_INDEX_CONSTRAINT_LE:
213      opcode = 'L';
214      break;
215    }
216
217    if (!opcode)
218      continue;
219
220    // For now, we only support column 0 (_rank)
221    // TODO: support for x, y, z, etc.
222    if (pInfo->aConstraint[i].iColumn != 0)
223      continue;
224
225    if (!IndexName.str().empty())
226      IndexName << ";";
227    IndexName << pInfo->aConstraint[i].iColumn << ":" << opcode;
228
229    pInfo->aConstraintUsage[i].argvIndex = 1 + IndexIndex++;
230    pInfo->aConstraintUsage[i].omit = 1;
231  }
232
233  if (!IndexName.str().empty()) {
234    pInfo->idxNum = 1;
235    pInfo->idxStr = sqlite3_mprintf("%s", IndexName.str().c_str());
236    if (!pInfo->idxStr)
237      return SQLITE_NOMEM;
238
239    pInfo->needToFreeIdxStr = 1;
240
241    // This is a crude estimate, without the actual range information,
242    // this is the best that we can do.
243    if (tab->GIO.readNRanks() > 0)
244      pInfo->estimatedCost = (double) tab->GIO.readNumElems(0);
245  } else {
246    uint64_t TotalNumElems = tab->GIO.readTotalNumElems();
247    if (TotalNumElems == (uint64_t) -1)
248      TotalNumElems = ((double) tab->GIO.readNumElems(0))*
249                        tab->GIO.readNRanks();
250    pInfo->estimatedCost = (double) TotalNumElems;
251  }
252
253  if (pInfo->nOrderBy == 1 && pInfo->aOrderBy[0].iColumn == 0 &&
254      pInfo->aOrderBy[0].desc == 0 /* ASC */)
255    pInfo->orderByConsumed = 1;
256
257  return SQLITE_OK;
258}
259
260static
261int gio_open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
262  gio_vtab   *tab = (gio_vtab *) pVTab;
263  gio_cursor *cur = new gio_cursor(tab->GIO, tab->MaxNumElems);
264  if (!cur)
265    return SQLITE_ERROR;
266
267  cur->cursor.pVtab = pVTab;
268  *ppCursor = &cur->cursor;
269
270  return SQLITE_OK;
271}
272
273static
274int gio_close(sqlite3_vtab_cursor* pCursor) {
275  gio_cursor *cur = (gio_cursor *) pCursor;
276
277  delete cur;
278  return SQLITE_OK;
279}
280
281static
282int gio_filter(sqlite3_vtab_cursor* pCursor, int idxNum, const char *idxStr,
283               int argc, sqlite3_value **argv) {
284  gio_cursor *cur = (gio_cursor *) pCursor;
285  gio_vtab   *tab = (gio_vtab *) cur->cursor.pVtab;
286
287  cur->RankMask.resize(cur->GIO.readNRanks(), true);
288  if (idxNum == 1) {
289    stringstream ss(idxStr);
290    string IndexSpec;
291    for (int arg = 0; getline(ss, IndexSpec, ';'); ++arg) {
292      if (arg >= argc)
293        return SQLITE_ERROR;
294
295      stringstream tt(IndexSpec);
296      int Col; char Colon, Op;
297      tt >> Col >> Colon >> Op;
298      if (Colon != ':')
299        return SQLITE_ERROR;
300
301      // TODO: Support for other columns.
302      if (Col != 0)
303        return SQLITE_ERROR;
304
305      int Rank = sqlite3_value_int(argv[arg]);
306      for (int i = 0; i < (int) cur->RankMask.size(); ++i) {
307        bool Incl = false;
308        if (Op == 'E')
309          Incl = (i == Rank);
310        else if (Op == 'g')
311          Incl = (i > Rank);
312        else if (Op == 'G')
313          Incl = (i >= Rank);
314        else if (Op == 'l')
315          Incl = (i < Rank);
316        else if (Op == 'L')
317          Incl = (i <= Rank);
318
319        cur->RankMask[i] = cur->RankMask[i] && Incl;
320      }
321    }
322  }
323
324  cur->Pos = 0;
325  cur->AbsPos = 0;
326
327  cur->Rank = -1;
328  do {
329    try {
330      ++cur->Rank;
331      if (cur->Rank == cur->GIO.readNRanks()) {
332        break;
333      }
334
335      if (cur->RankMask[cur->Rank])
336        cur->GIO.readData(cur->Rank, false);
337      else
338        cur->AbsPos += cur->GIO.readNumElems(cur->Rank);
339    } catch (exception &e) {
340        cerr << e.what() << endl;
341        return SQLITE_IOERR;
342    }
343  } while (!cur->RankMask[cur->Rank] ||
344           cur->GIO.readNumElems(cur->Rank) == 0);
345
346  return SQLITE_OK;
347}
348
349static
350int gio_next(sqlite3_vtab_cursor* pCursor) {
351  gio_cursor *cur = (gio_cursor *) pCursor;
352
353  if (cur->Pos == cur->GIO.readNumElems(cur->Rank) - 1) {
354    do {
355      try {
356        ++cur->Rank;
357        if (cur->Rank == cur->GIO.readNRanks()) {
358          break;
359        }
360
361        if (cur->RankMask[cur->Rank])
362          cur->GIO.readData(cur->Rank, false);
363        else
364          cur->AbsPos += cur->GIO.readNumElems(cur->Rank);
365      } catch (exception &e) {
366        cerr << e.what() << endl;
367        return SQLITE_IOERR;
368      }
369    } while (!cur->RankMask[cur->Rank] ||
370             cur->GIO.readNumElems(cur->Rank) == 0);
371
372    cur->Pos = 0;
373  } else {
374    ++cur->Pos;
375  }
376
377  ++cur->AbsPos;
378
379  return SQLITE_OK;
380}
381
382static
383int gio_eof(sqlite3_vtab_cursor* pCursor) {
384  gio_cursor *cur = (gio_cursor *) pCursor;
385
386  if (cur->Rank == cur->GIO.readNRanks())
387    return 1;
388
389  if (cur->Rank == cur->GIO.readNRanks() - 1 &&
390      cur->Pos >= (cur->GIO.readNumElems(cur->Rank) - 1))
391    return 1;
392
393  return 0;
394}
395
396static
397int gio_column(sqlite3_vtab_cursor* pCursor, sqlite3_context* cxt, int n) {
398  gio_cursor *cur = (gio_cursor *) pCursor;
399
400  if (n == 0)
401    sqlite3_result_int(cxt, cur->Rank);
402  else if ((n-1) >= cur->Printers.size())
403    sqlite3_result_null(cxt);
404  else
405    cur->Printers[n-1]->print(cxt, cur->Pos);
406
407  return SQLITE_OK;
408}
409
410static
411int gio_rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid) {
412  gio_cursor *cur = (gio_cursor *) pCursor;
413
414  *pRowid = (sqlite3_int64) cur->AbsPos;
415  return SQLITE_OK;
416}
417
418extern "C"
419int sq3_register_virtual_gio(sqlite3 * db) {
420  static const struct sqlite3_module gio_module = {
421    2,                         /* iVersion */
422    gio_create,
423    gio_connect,
424    gio_best_index,
425    gio_disconnect,
426    gio_destroy,
427    gio_open,
428    gio_close,
429    gio_filter,
430    gio_next,
431    gio_eof,
432    gio_column,
433    gio_rowid,
434    0,
435    0,
436    0,
437    0,
438    0,
439    0,
440    0,
441    0,
442    0,
443    0
444  };
445
446  return sqlite3_create_module( db, "GenericIO", &gio_module, NULL );
447}
448
449extern "C"
450int sqlite3_extension_init(sqlite3 *db, char **,
451                           const sqlite3_api_routines *pApi) {
452  SQLITE_EXTENSION_INIT2(pApi);
453  sq3_register_virtual_gio(db);
454  return SQLITE_OK;
455}
456
Note: See TracBrowser for help on using the repository browser.