Skip to content

Commit 55ff859

Browse files
committed
Complete first draft for wide table format; demo results
1 parent 0775dbf commit 55ff859

4 files changed

+176
-59
lines changed

build_floataway.cmd

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
REM if dynamic linking, also need to distribute the libgcc and libstdc++ DLLs.
2-
REM g++ -g -shared -std=c++11 floataway.c -o floataway.dll
2+
g++ -g -shared -std=c++11 floataway.c -o floataway.dll
33

44
REM If static linking, no other DLLs required.
5-
g++ -g -shared -std=c++11 -static-libstdc++ -static-libgcc floataway.c -o floataway.dll
5+
REM g++ -g -shared -std=c++11 -static-libstdc++ -static-libgcc floataway.c -o floataway.dll

demo_connect_params.sql

+34-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
.load floataway
2+
.mode table
3+
24
DROP TABLE IF EXISTS temp.t1;
3-
--CREATE VIRTUAL TABLE IF NOT EXISTS temp.t1 USING float_each( 5 ,'filename=thefile.csv', filename = SELECT 'thefile' || chr(10) || '.csv' );
4-
CREATE VIRTUAL TABLE IF NOT EXISTS temp.t1 USING float_each( 5 );
5-
select a.rowid, a.key, a.value, a.json
5+
CREATE VIRTUAL TABLE temp.t1 USING float_each();
6+
select hex(a.data), a.nrow, a.col1
67
FROM temp.t1(
78
x'BA498A41'
89
|| x'C5208841'
@@ -11,14 +12,34 @@ x'BA498A41'
1112
|| x'60E58541'
1213
|| x'91ED8E41'
1314
) a
14-
WHERE key > 2 AND key < 5;
15+
;
16+
17+
DROP TABLE IF EXISTS temp.t2;
18+
CREATE VIRTUAL TABLE temp.t2 USING float_each(N=2,prefix=hr,suffix=a);
19+
select hex(a.data), a.nrow, a.hr1a, a.hr2a
20+
FROM temp.t2(
21+
x'BA498A41'
22+
|| x'C5208841'
23+
|| x'8B6C8641'
24+
|| x'6ABC8541'
25+
|| x'60E58541'
26+
|| x'91ED8E41'
27+
) a
28+
;
29+
30+
select hex(a.data), a.nrow, a.hr1a, a.hr2a
31+
FROM temp.t2(
32+
x'BA498A41'
33+
|| x'C5208841'
34+
|| x'8B6C8641'
35+
|| x'6ABC8541'
36+
|| x'60E58541'
37+
|| x'91ED8E41'
38+
) a
39+
WHERE nrow = 1
40+
;
1541

16-
/* Result
17-
argc=6
18-
argv[0]=float_each
19-
argv[1]=temp
20-
argv[2]=t1
21-
argv[3]=5
22-
argv[4]='filename=thefile.csv'
23-
argv[5]=filename = SELECT 'thefile' || chr(10) || '.csv'
24-
*/
42+
DROP TABLE IF EXISTS temp.t3;
43+
CREATE VIRTUAL TABLE temp.t3 USING float_each(N=3,prefix=hr,suffix=a,rowname=daynum);
44+
select a.* FROM temp.t3(x'BA498A41C52088418B6C86416ABC854160E5854191ED8E41') a
45+
;

demo_floataway_results.txt

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
+--------------------------------------------------+------+------------------+
2+
| hex(a.data) | nrow | col1 |
3+
+--------------------------------------------------+------+------------------+
4+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 1 | 17.2859992980957 |
5+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 2 | 17.0160007476807 |
6+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 3 | 16.80299949646 |
7+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 4 | 16.7169990539551 |
8+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 5 | 16.7369995117188 |
9+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 6 | 17.8659992218018 |
10+
+--------------------------------------------------+------+------------------+
11+
+--------------------------------------------------+------+------------------+------------------+
12+
| hex(a.data) | nrow | hr1a | hr2a |
13+
+--------------------------------------------------+------+------------------+------------------+
14+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 1 | 17.2859992980957 | 17.0160007476807 |
15+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 2 | 16.80299949646 | 16.7169990539551 |
16+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 3 | 16.7369995117188 | 17.8659992218018 |
17+
+--------------------------------------------------+------+------------------+------------------+
18+
+--------------------------------------------------+------+------------------+------------------+
19+
| hex(a.data) | nrow | hr1a | hr2a |
20+
+--------------------------------------------------+------+------------------+------------------+
21+
| BA498A41C52088418B6C86416ABC854160E5854191ED8E41 | 1 | 17.2859992980957 | 17.0160007476807 |
22+
+--------------------------------------------------+------+------------------+------------------+
23+
+--------+------------------+------------------+------------------+
24+
| daynum | hr1a | hr2a | hr3a |
25+
+--------+------------------+------------------+------------------+
26+
| 1 | 17.2859992980957 | 17.0160007476807 | 16.80299949646 |
27+
| 2 | 16.7169990539551 | 16.7369995117188 | 17.8659992218018 |
28+
+--------+------------------+------------------+------------------+

floataway.c

+112-44
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ SQLITE_EXTENSION_INIT1
4343
#ifdef __cplusplus
4444
#include <regex>
4545
#include <string>
46+
#include <sstream>
47+
#include <iomanip>
4648
#endif
4749

4850
/* Mark a function parameter as unused, to suppress nuisance compiler
@@ -404,7 +406,19 @@ struct JsonEachCursor {
404406
float *zJson; /* Input array */
405407
};
406408

407-
/* Constructor for the json_each virtual table */
409+
/* Constructor for the json_each virtual table.
410+
** Usage:
411+
** CREATE VIRTUAL TABLE temp.t1 USING float_each(
412+
** N=1, prefix=col, suffix=, rowname=nrow);
413+
**
414+
** Arguments:
415+
** N = number of columns.
416+
** prefix and suffix: e.g.,
417+
** Given (9,col), columns will be named nrow,col1 ... col9.
418+
** Given (10,hr,a), columns will be named nrow,hr01a ... hr10a.
419+
** All arguments are optional but must appear in order,
420+
** with or without argument names. Do not quote strings.
421+
*/
408422
static int jsonEachConnect(
409423
sqlite3 *db,
410424
void *pAux,
@@ -415,22 +429,25 @@ static int jsonEachConnect(
415429
CsvTable *pNew = NULL; /* The CsvTable object to construct */
416430
int rc;
417431

418-
/* Column numbers */
419-
#define JEACH_KEY 0
420-
#define JEACH_VALUE 1
421-
/* The xBestIndex method assumes that the JSON and ROOT columns are
422-
** the last two columns in the table. Should this ever changes, be
423-
** sure to update the xBestIndex method. */
424-
#define JEACH_JSON 2
432+
/* Column numbers
433+
** The xBestIndex method assumes that the JSON is the first column.
434+
** Should this ever changes, be sure to update the xBestIndex method.
435+
*/
436+
#define JEACH_JSON 0
437+
#define JEACH_KEY 1
438+
#define JEACH_VALUE 2
425439

426440
UNUSED_PARAM(pzErr);
427441
UNUSED_PARAM(pAux);
428442

429-
/* Determine the parameter N.
443+
int nCol = 1; int nColDigits = 1;
444+
std::string zColPrefix("col");
445+
std::string zColSuffix("");
446+
std::string zRowName("nrow");
447+
/* Determine the parameter N = number of columns.
430448
** Take the first user argument if it exists, e.g. argv[3].
431449
** Otherwise default to N = 1.
432450
*/
433-
int nCol = 1;
434451
if (argc > 3) {
435452
std::string target(argv[3]);
436453
// Pattern should match the number in 'N = 123' or simply '123'.
@@ -442,13 +459,61 @@ static int jsonEachConnect(
442459
int nColFound = std::stoi(match.str(1));
443460
if (nColFound >= 1) {
444461
nCol = nColFound;
462+
} else {
463+
//silently ignore the error
445464
}
465+
} else {
466+
//silently ignore the error
467+
}
468+
}
469+
if (argc > 4) {
470+
std::string target(argv[4]);
471+
// prefix=col
472+
std::regex rx(R"""((?:\s*prefix\s*=)?\s*(\w+)\s*)""");
473+
std::smatch match;
474+
if (regex_match(target.cbegin(), target.cend(), match, rx)) {
475+
// Regex found
476+
zColPrefix = match.str(1);
477+
} else {
478+
//silently ignore the error
446479
}
447480
}
481+
if (argc > 5) {
482+
std::string target(argv[5]);
483+
// suffix=a
484+
std::regex rx(R"""((?:\s*suffix\s*=)?\s*(\w+)\s*)""");
485+
std::smatch match;
486+
if (regex_match(target.cbegin(), target.cend(), match, rx)) {
487+
// Regex found
488+
zColSuffix = match.str(1);
489+
} else {
490+
//silently ignore the error
491+
}
492+
}
493+
if (argc > 6) {
494+
std::string target(argv[6]);
495+
// rowname=a
496+
std::regex rx(R"""((?:\s*rowname\s*=)?\s*(\w+)\s*)""");
497+
std::smatch match;
498+
if (regex_match(target.cbegin(), target.cend(), match, rx)) {
499+
// Regex found
500+
zRowName = match.str(1);
501+
} else {
502+
//silently ignore the error
503+
}
504+
}
505+
nColDigits = 1+(int)log10(nCol); // e.g. 1 -> 1, 9 -> 1, 10 -> 2, 99 -> 2, etc.
506+
std::ostringstream schema;
507+
schema << "CREATE TABLE x(data HIDDEN";
508+
schema << ",\"" << zRowName << "\"";
509+
for (int i = 1; i <= nCol; i++) {
510+
schema << ",\"" << zColPrefix
511+
<< std::setw(nColDigits) << std::setfill('0') << i
512+
<< zColSuffix << "\"";
513+
}
514+
schema << ")";
448515

449-
rc = sqlite3_declare_vtab(db,
450-
"CREATE TABLE x(key,value,"
451-
"json HIDDEN)");
516+
rc = sqlite3_declare_vtab(db,schema.str().data());
452517
if( rc==SQLITE_OK ){
453518
pNew = (CsvTable *)sqlite3_malloc( sizeof(*pNew) );
454519
*ppVtab = (sqlite3_vtab*)pNew;
@@ -544,22 +609,22 @@ static int jsonEachColumn(
544609
int i /* Which column to return */
545610
){
546611
JsonEachCursor *p = (JsonEachCursor*)cur;
612+
CsvTable *pTab = (CsvTable*)cur->pVtab;
547613
assert(p->iRowid < p->iEnd);
548614
switch( i ){
549-
case JEACH_KEY: {
550-
sqlite3_result_int64(ctx, (sqlite3_int64)p->iRowid);
615+
case JEACH_JSON: {
616+
sqlite3_result_blob(ctx, p->zJson, p->iNbytes, SQLITE_TRANSIENT);
551617
break;
552618
}
553-
case JEACH_VALUE: {
554-
sqlite3_result_double(ctx, (double)p->zJson[p->iRowid]);
619+
case JEACH_KEY: {
620+
sqlite3_result_int64(ctx, (sqlite3_int64)(1 + p->iRowid));
555621
break;
556622
}
557-
case JEACH_JSON: {
558-
assert( i==JEACH_JSON );
559-
//sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
560-
//sqlite3_result_blob(ctx, p->zJson, p->iNbytes, SQLITE_TRANSIENT);
561-
CsvTable *pTab = (CsvTable*)cur->pVtab;
562-
sqlite3_result_int(ctx, pTab->nCol);
623+
default: {
624+
int iCol = i - JEACH_VALUE;
625+
assert( i >= JEACH_VALUE );
626+
assert( i < pTab->nCol );
627+
sqlite3_result_double(ctx, (double)p->zJson[pTab->nCol * p->iRowid + iCol]);
563628
break;
564629
}
565630
}
@@ -593,23 +658,24 @@ static int jsonEachBestIndex(
593658
sqlite3_vtab *tab,
594659
sqlite3_index_info *pIdxInfo
595660
){
661+
UNUSED_PARAM(tab);
596662
int i; /* Loop counter or computed array index */
597-
int aIdx[2]; /* Index of constraints for JSON and ROOT */
598-
int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */
599-
int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */
663+
int aIdx[2]; /* Index of constraints for JSON and NROW */
664+
int unusableMask = 0; /* Mask of unusable JSON and NROW constraints */
665+
int idxMask = 0; /* Mask of usable == constraints JSON and NROW */
600666
const struct sqlite3_index_constraint *pConstraint;
601667

602-
/* This implementation assumes that JSON is the last
668+
/* This implementation assumes that JSON is the first
603669
** columns in the table */
604-
UNUSED_PARAM(tab);
605670
aIdx[0] = aIdx[1] = -1;
606671
pConstraint = (const struct sqlite3_index_constraint *)pIdxInfo->aConstraint;
607672
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
608673
int iCol;
609674
int iMask;
610-
if( pConstraint->iColumn < JEACH_JSON ) continue;
611-
iCol = pConstraint->iColumn - JEACH_JSON;
612-
assert( iCol==0 );
675+
if( pConstraint->iColumn < 0 ) continue; // -1 for ROWID
676+
if( pConstraint->iColumn > JEACH_JSON ) continue;
677+
iCol = pConstraint->iColumn;
678+
assert( iCol==0 || iCol==1 );
613679
testcase( iCol==0 );
614680
iMask = 1 << iCol;
615681
if( pConstraint->usable==0 ){
@@ -620,7 +686,7 @@ static int jsonEachBestIndex(
620686
}
621687
}
622688
if( (unusableMask & ~idxMask)!=0 ){
623-
/* If there are any unusable constraints on JSON or ROOT, then reject
689+
/* If there are any unusable constraints on JSON or NROW, then reject
624690
** this entire plan */
625691
return SQLITE_CONSTRAINT;
626692
}
@@ -647,32 +713,34 @@ static int jsonEachFilter(
647713
int idxNum, const char *idxStr,
648714
int argc, sqlite3_value **argv
649715
){
650-
JsonEachCursor *p = (JsonEachCursor*)cur;
716+
JsonEachCursor *pCur = (JsonEachCursor*)cur;
717+
CsvTable *pTab = (CsvTable*)cur->pVtab;
718+
int bytesPerRow = pTab->nCol * sizeof(float);
651719
const float *z;
652720
const char *zRoot = 0;
653721
sqlite3_int64 n;
654-
655722
UNUSED_PARAM(idxStr);
656-
UNUSED_PARAM(argc);
657-
jsonEachCursorReset(p);
723+
assert( argc == 1 );
724+
jsonEachCursorReset(pCur);
658725
if( idxNum==0 ) return SQLITE_OK;
726+
659727
n = sqlite3_value_bytes(argv[0]);
660-
p->iNbytes = n;
661-
p->iEnd = (u32) (n / sizeof(float));
728+
pCur->iNbytes = n;
729+
pCur->iEnd = (u32) (n / bytesPerRow);
662730
z = (const float*)sqlite3_value_blob(argv[0]);
663731
if( z==0 ) return SQLITE_OK; // If NUL in, then NUL out.
664-
p->zJson = (float *)sqlite3_malloc64( n );
665-
if( p->zJson==0 ) return SQLITE_NOMEM;
666-
memcpy(p->zJson, z, (size_t)n);
667-
if( (n % sizeof(float)) != 0 ){
732+
if( (n % bytesPerRow) != 0 ){
668733
int rc = SQLITE_NOMEM;
669734
sqlite3_free(cur->pVtab->zErrMsg);
670735
cur->pVtab->zErrMsg = sqlite3_mprintf("malformed array data");
671736
if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
672-
jsonEachCursorReset(p);
737+
jsonEachCursorReset(pCur);
673738
return rc;
674739
}else{
675-
p->iRowid = 0;
740+
pCur->zJson = (float *)sqlite3_malloc64( n );
741+
if( pCur->zJson==0 ) return SQLITE_NOMEM;
742+
memcpy(pCur->zJson, z, (size_t)n);
743+
pCur->iRowid = 0;
676744
}
677745
return SQLITE_OK;
678746
}

0 commit comments

Comments
 (0)