@@ -43,6 +43,8 @@ SQLITE_EXTENSION_INIT1
43
43
#ifdef __cplusplus
44
44
#include <regex>
45
45
#include <string>
46
+ #include <sstream>
47
+ #include <iomanip>
46
48
#endif
47
49
48
50
/* Mark a function parameter as unused, to suppress nuisance compiler
@@ -404,7 +406,19 @@ struct JsonEachCursor {
404
406
float * zJson ; /* Input array */
405
407
};
406
408
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
+ */
408
422
static int jsonEachConnect (
409
423
sqlite3 * db ,
410
424
void * pAux ,
@@ -415,22 +429,25 @@ static int jsonEachConnect(
415
429
CsvTable * pNew = NULL ; /* The CsvTable object to construct */
416
430
int rc ;
417
431
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
425
439
426
440
UNUSED_PARAM (pzErr );
427
441
UNUSED_PARAM (pAux );
428
442
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.
430
448
** Take the first user argument if it exists, e.g. argv[3].
431
449
** Otherwise default to N = 1.
432
450
*/
433
- int nCol = 1 ;
434
451
if (argc > 3 ) {
435
452
std ::string target (argv [3 ]);
436
453
// Pattern should match the number in 'N = 123' or simply '123'.
@@ -442,13 +459,61 @@ static int jsonEachConnect(
442
459
int nColFound = std ::stoi (match .str (1 ));
443
460
if (nColFound >= 1 ) {
444
461
nCol = nColFound ;
462
+ } else {
463
+ //silently ignore the error
445
464
}
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
446
479
}
447
480
}
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 << ")" ;
448
515
449
- rc = sqlite3_declare_vtab (db ,
450
- "CREATE TABLE x(key,value,"
451
- "json HIDDEN)" );
516
+ rc = sqlite3_declare_vtab (db ,schema .str ().data ());
452
517
if ( rc == SQLITE_OK ){
453
518
pNew = (CsvTable * )sqlite3_malloc ( sizeof (* pNew ) );
454
519
* ppVtab = (sqlite3_vtab * )pNew ;
@@ -544,22 +609,22 @@ static int jsonEachColumn(
544
609
int i /* Which column to return */
545
610
){
546
611
JsonEachCursor * p = (JsonEachCursor * )cur ;
612
+ CsvTable * pTab = (CsvTable * )cur -> pVtab ;
547
613
assert (p -> iRowid < p -> iEnd );
548
614
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 );
551
617
break ;
552
618
}
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 ) );
555
621
break ;
556
622
}
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 ]);
563
628
break ;
564
629
}
565
630
}
@@ -593,23 +658,24 @@ static int jsonEachBestIndex(
593
658
sqlite3_vtab * tab ,
594
659
sqlite3_index_info * pIdxInfo
595
660
){
661
+ UNUSED_PARAM (tab );
596
662
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 */
600
666
const struct sqlite3_index_constraint * pConstraint ;
601
667
602
- /* This implementation assumes that JSON is the last
668
+ /* This implementation assumes that JSON is the first
603
669
** columns in the table */
604
- UNUSED_PARAM (tab );
605
670
aIdx [0 ] = aIdx [1 ] = -1 ;
606
671
pConstraint = (const struct sqlite3_index_constraint * )pIdxInfo -> aConstraint ;
607
672
for (i = 0 ; i < pIdxInfo -> nConstraint ; i ++ , pConstraint ++ ){
608
673
int iCol ;
609
674
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 );
613
679
testcase ( iCol == 0 );
614
680
iMask = 1 << iCol ;
615
681
if ( pConstraint -> usable == 0 ){
@@ -620,7 +686,7 @@ static int jsonEachBestIndex(
620
686
}
621
687
}
622
688
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
624
690
** this entire plan */
625
691
return SQLITE_CONSTRAINT ;
626
692
}
@@ -647,32 +713,34 @@ static int jsonEachFilter(
647
713
int idxNum , const char * idxStr ,
648
714
int argc , sqlite3_value * * argv
649
715
){
650
- JsonEachCursor * p = (JsonEachCursor * )cur ;
716
+ JsonEachCursor * pCur = (JsonEachCursor * )cur ;
717
+ CsvTable * pTab = (CsvTable * )cur -> pVtab ;
718
+ int bytesPerRow = pTab -> nCol * sizeof (float );
651
719
const float * z ;
652
720
const char * zRoot = 0 ;
653
721
sqlite3_int64 n ;
654
-
655
722
UNUSED_PARAM (idxStr );
656
- UNUSED_PARAM ( argc );
657
- jsonEachCursorReset (p );
723
+ assert ( argc == 1 );
724
+ jsonEachCursorReset (pCur );
658
725
if ( idxNum == 0 ) return SQLITE_OK ;
726
+
659
727
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 );
662
730
z = (const float * )sqlite3_value_blob (argv [0 ]);
663
731
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 ){
668
733
int rc = SQLITE_NOMEM ;
669
734
sqlite3_free (cur -> pVtab -> zErrMsg );
670
735
cur -> pVtab -> zErrMsg = sqlite3_mprintf ("malformed array data" );
671
736
if ( cur -> pVtab -> zErrMsg ) rc = SQLITE_ERROR ;
672
- jsonEachCursorReset (p );
737
+ jsonEachCursorReset (pCur );
673
738
return rc ;
674
739
}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 ;
676
744
}
677
745
return SQLITE_OK ;
678
746
}
0 commit comments