Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Update this branch with latest trunk changes. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | server-process-edition |
Files: | files | file ages | folders |
SHA3-256: |
bc2498d60fa22b587ad463ec697abe82 |
User & Date: | dan 2017-08-14 06:55:22 |
2017-08-14
| ||
07:16 | Remove code for PLL support in wal mode from this branch. check-in: 8e1b28ed user: dan tags: server-process-edition | |
06:55 | Update this branch with latest trunk changes. check-in: bc2498d6 user: dan tags: server-process-edition | |
01:33 | Properly dequote column names in tables constructed by an aggregate SELECT. check-in: 7e0d3e9c user: drh tags: trunk | |
2017-08-07
| ||
14:15 | Update this branch with latest trunk changes. check-in: 17bc7ded user: dan tags: server-process-edition | |
Changes to ext/fts5/fts5_vocab.c.
25 25 ** row: 26 26 ** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term)); 27 27 ** 28 28 ** One row for each term in the database. The value of $doc is set to 29 29 ** the number of fts5 rows that contain at least one instance of term 30 30 ** $term. Field $cnt is set to the total number of instances of term 31 31 ** $term in the database. 32 +** 33 +** instance: 34 +** CREATE TABLE vocab(term, doc, col, offset, PRIMARY KEY(<all-fields>)); 35 +** 36 +** One row for each term instance in the database. 32 37 */ 33 38 34 39 35 40 #include "fts5Int.h" 36 41 37 42 38 43 typedef struct Fts5VocabTable Fts5VocabTable; ................................................................................ 40 45 41 46 struct Fts5VocabTable { 42 47 sqlite3_vtab base; 43 48 char *zFts5Tbl; /* Name of fts5 table */ 44 49 char *zFts5Db; /* Db containing fts5 table */ 45 50 sqlite3 *db; /* Database handle */ 46 51 Fts5Global *pGlobal; /* FTS5 global object for this database */ 47 - int eType; /* FTS5_VOCAB_COL or ROW */ 52 + int eType; /* FTS5_VOCAB_COL, ROW or INSTANCE */ 48 53 }; 49 54 50 55 struct Fts5VocabCursor { 51 56 sqlite3_vtab_cursor base; 52 57 sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */ 53 58 Fts5Index *pIndex; /* Associated FTS5 index */ 54 59 ................................................................................ 60 65 61 66 /* These are used by 'col' tables only */ 62 67 Fts5Config *pConfig; /* Fts5 table configuration */ 63 68 int iCol; 64 69 i64 *aCnt; 65 70 i64 *aDoc; 66 71 67 - /* Output values used by 'row' and 'col' tables */ 72 + /* Output values used by all tables. */ 68 73 i64 rowid; /* This table's current rowid value */ 69 74 Fts5Buffer term; /* Current value of 'term' column */ 75 + 76 + /* Output values Used by 'instance' tables only */ 77 + i64 iInstPos; 78 + int iInstOff; 70 79 }; 71 80 72 -#define FTS5_VOCAB_COL 0 73 -#define FTS5_VOCAB_ROW 1 81 +#define FTS5_VOCAB_COL 0 82 +#define FTS5_VOCAB_ROW 1 83 +#define FTS5_VOCAB_INSTANCE 2 74 84 75 85 #define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt" 76 86 #define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt" 87 +#define FTS5_VOCAB_INST_SCHEMA "term, doc, col, offset" 77 88 78 89 /* 79 90 ** Bits for the mask used as the idxNum value by xBestIndex/xFilter. 80 91 */ 81 92 #define FTS5_VOCAB_TERM_EQ 0x01 82 93 #define FTS5_VOCAB_TERM_GE 0x02 83 94 #define FTS5_VOCAB_TERM_LE 0x04 ................................................................................ 96 107 sqlite3Fts5Dequote(zCopy); 97 108 if( sqlite3_stricmp(zCopy, "col")==0 ){ 98 109 *peType = FTS5_VOCAB_COL; 99 110 }else 100 111 101 112 if( sqlite3_stricmp(zCopy, "row")==0 ){ 102 113 *peType = FTS5_VOCAB_ROW; 114 + }else 115 + if( sqlite3_stricmp(zCopy, "instance")==0 ){ 116 + *peType = FTS5_VOCAB_INSTANCE; 103 117 }else 104 118 { 105 119 *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy); 106 120 rc = SQLITE_ERROR; 107 121 } 108 122 sqlite3_free(zCopy); 109 123 } ................................................................................ 157 171 int argc, /* Number of elements in argv array */ 158 172 const char * const *argv, /* xCreate/xConnect argument array */ 159 173 sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ 160 174 char **pzErr /* Write any error message here */ 161 175 ){ 162 176 const char *azSchema[] = { 163 177 "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")", 164 - "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")" 178 + "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")", 179 + "CREATE TABlE vocab(" FTS5_VOCAB_INST_SCHEMA ")" 165 180 }; 166 181 167 182 Fts5VocabTable *pRet = 0; 168 183 int rc = SQLITE_OK; /* Return code */ 169 184 int bDb; 170 185 171 186 bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0); ................................................................................ 231 246 char **pzErr /* OUT: sqlite3_malloc'd error message */ 232 247 ){ 233 248 return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr); 234 249 } 235 250 236 251 /* 237 252 ** Implementation of the xBestIndex method. 253 +** 254 +** Only constraints of the form: 255 +** 256 +** term <= ? 257 +** term == ? 258 +** term >= ? 259 +** 260 +** are interpreted. Less-than and less-than-or-equal are treated 261 +** identically, as are greater-than and greater-than-or-equal. 238 262 */ 239 263 static int fts5VocabBestIndexMethod( 240 264 sqlite3_vtab *pUnused, 241 265 sqlite3_index_info *pInfo 242 266 ){ 243 267 int i; 244 268 int iTermEq = -1; ................................................................................ 374 398 fts5VocabResetCursor(pCsr); 375 399 sqlite3Fts5BufferFree(&pCsr->term); 376 400 sqlite3_finalize(pCsr->pStmt); 377 401 sqlite3_free(pCsr); 378 402 return SQLITE_OK; 379 403 } 380 404 405 +static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ 406 + int rc = SQLITE_OK; 407 + 408 + if( sqlite3Fts5IterEof(pCsr->pIter) ){ 409 + pCsr->bEof = 1; 410 + }else{ 411 + const char *zTerm; 412 + int nTerm; 413 + zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); 414 + if( pCsr->nLeTerm>=0 ){ 415 + int nCmp = MIN(nTerm, pCsr->nLeTerm); 416 + int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); 417 + if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){ 418 + pCsr->bEof = 1; 419 + } 420 + } 421 + 422 + sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); 423 + } 424 + return rc; 425 +} 426 + 427 +static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ 428 + int eDetail = pCsr->pConfig->eDetail; 429 + int rc = SQLITE_OK; 430 + Fts5IndexIter *pIter = pCsr->pIter; 431 + i64 *pp = &pCsr->iInstPos; 432 + int *po = &pCsr->iInstOff; 433 + 434 + while( eDetail==FTS5_DETAIL_NONE 435 + || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) 436 + ){ 437 + pCsr->iInstPos = 0; 438 + pCsr->iInstOff = 0; 439 + 440 + rc = sqlite3Fts5IterNextScan(pCsr->pIter); 441 + if( rc==SQLITE_OK ){ 442 + rc = fts5VocabInstanceNewTerm(pCsr); 443 + if( eDetail==FTS5_DETAIL_NONE ) break; 444 + } 445 + if( rc ){ 446 + pCsr->bEof = 1; 447 + break; 448 + } 449 + } 450 + 451 + return rc; 452 +} 381 453 382 454 /* 383 455 ** Advance the cursor to the next row in the table. 384 456 */ 385 457 static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ 386 458 Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; 387 459 Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; 388 460 int rc = SQLITE_OK; 389 461 int nCol = pCsr->pConfig->nCol; 390 462 391 463 pCsr->rowid++; 464 + 465 + if( pTab->eType==FTS5_VOCAB_INSTANCE ){ 466 + return fts5VocabInstanceNext(pCsr); 467 + } 392 468 393 469 if( pTab->eType==FTS5_VOCAB_COL ){ 394 470 for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){ 395 471 if( pCsr->aDoc[pCsr->iCol] ) break; 396 472 } 397 473 } 398 474 399 - if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){ 475 + if( pTab->eType!=FTS5_VOCAB_COL || pCsr->iCol>=nCol ){ 400 476 if( sqlite3Fts5IterEof(pCsr->pIter) ){ 401 477 pCsr->bEof = 1; 402 478 }else{ 403 479 const char *zTerm; 404 480 int nTerm; 405 481 406 482 zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); ................................................................................ 416 492 sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm); 417 493 memset(pCsr->aCnt, 0, nCol * sizeof(i64)); 418 494 memset(pCsr->aDoc, 0, nCol * sizeof(i64)); 419 495 pCsr->iCol = 0; 420 496 421 497 assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); 422 498 while( rc==SQLITE_OK ){ 499 + int eDetail = pCsr->pConfig->eDetail; 423 500 const u8 *pPos; int nPos; /* Position list */ 424 501 i64 iPos = 0; /* 64-bit position read from poslist */ 425 502 int iOff = 0; /* Current offset within position list */ 426 503 427 504 pPos = pCsr->pIter->pData; 428 505 nPos = pCsr->pIter->nData; 429 - switch( pCsr->pConfig->eDetail ){ 430 - case FTS5_DETAIL_FULL: 431 - pPos = pCsr->pIter->pData; 432 - nPos = pCsr->pIter->nData; 433 - if( pTab->eType==FTS5_VOCAB_ROW ){ 506 + 507 + switch( pTab->eType ){ 508 + case FTS5_VOCAB_ROW: 509 + if( eDetail==FTS5_DETAIL_FULL ){ 434 510 while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ 435 511 pCsr->aCnt[0]++; 436 512 } 437 - pCsr->aDoc[0]++; 438 - }else{ 513 + } 514 + pCsr->aDoc[0]++; 515 + break; 516 + 517 + case FTS5_VOCAB_COL: 518 + if( eDetail==FTS5_DETAIL_FULL ){ 439 519 int iCol = -1; 440 520 while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ 441 521 int ii = FTS5_POS2COLUMN(iPos); 442 522 pCsr->aCnt[ii]++; 443 523 if( iCol!=ii ){ 444 524 if( ii>=nCol ){ 445 525 rc = FTS5_CORRUPT; 446 526 break; 447 527 } 448 528 pCsr->aDoc[ii]++; 449 529 iCol = ii; 450 530 } 451 531 } 452 - } 453 - break; 454 - 455 - case FTS5_DETAIL_COLUMNS: 456 - if( pTab->eType==FTS5_VOCAB_ROW ){ 457 - pCsr->aDoc[0]++; 458 - }else{ 532 + }else if( eDetail==FTS5_DETAIL_COLUMNS ){ 459 533 while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ 460 534 assert_nc( iPos>=0 && iPos<nCol ); 461 535 if( iPos>=nCol ){ 462 536 rc = FTS5_CORRUPT; 463 537 break; 464 538 } 465 539 pCsr->aDoc[iPos]++; 466 540 } 541 + }else{ 542 + assert( eDetail==FTS5_DETAIL_NONE ); 543 + pCsr->aDoc[0]++; 467 544 } 468 545 break; 469 546 470 - default: 471 - assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE ); 472 - pCsr->aDoc[0]++; 547 + default: 548 + assert( pTab->eType==FTS5_VOCAB_INSTANCE ); 473 549 break; 474 550 } 475 551 476 552 if( rc==SQLITE_OK ){ 477 553 rc = sqlite3Fts5IterNextScan(pCsr->pIter); 478 554 } 555 + if( pTab->eType==FTS5_VOCAB_INSTANCE ) break; 479 556 480 557 if( rc==SQLITE_OK ){ 481 558 zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); 482 559 if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){ 483 560 break; 484 561 } 485 562 if( sqlite3Fts5IterEof(pCsr->pIter) ) break; ................................................................................ 501 578 static int fts5VocabFilterMethod( 502 579 sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ 503 580 int idxNum, /* Strategy index */ 504 581 const char *zUnused, /* Unused */ 505 582 int nUnused, /* Number of elements in apVal */ 506 583 sqlite3_value **apVal /* Arguments for the indexing scheme */ 507 584 ){ 585 + Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; 508 586 Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; 587 + int eType = pTab->eType; 509 588 int rc = SQLITE_OK; 510 589 511 590 int iVal = 0; 512 591 int f = FTS5INDEX_QUERY_SCAN; 513 592 const char *zTerm = 0; 514 593 int nTerm = 0; 515 594 ................................................................................ 541 620 rc = SQLITE_NOMEM; 542 621 }else{ 543 622 memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1); 544 623 } 545 624 } 546 625 } 547 626 548 - 549 627 if( rc==SQLITE_OK ){ 550 628 rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); 551 629 } 552 - if( rc==SQLITE_OK ){ 630 + if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ 631 + rc = fts5VocabInstanceNewTerm(pCsr); 632 + } 633 + if( rc==SQLITE_OK 634 + && !pCsr->bEof 635 + && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE) 636 + ){ 553 637 rc = fts5VocabNextMethod(pCursor); 554 638 } 555 639 556 640 return rc; 557 641 } 558 642 559 643 /* ................................................................................ 587 671 sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); 588 672 } 589 673 }else if( iCol==2 ){ 590 674 iVal = pCsr->aDoc[pCsr->iCol]; 591 675 }else{ 592 676 iVal = pCsr->aCnt[pCsr->iCol]; 593 677 } 594 - }else{ 678 + }else if( eType==FTS5_VOCAB_ROW ){ 595 679 assert( iCol==1 || iCol==2 ); 596 680 if( iCol==1 ){ 597 681 iVal = pCsr->aDoc[0]; 598 682 }else{ 599 683 iVal = pCsr->aCnt[0]; 684 + } 685 + }else{ 686 + int eDetail = pCsr->pConfig->eDetail; 687 + assert( eType==FTS5_VOCAB_INSTANCE ); 688 + switch( iCol ){ 689 + case 1: 690 + sqlite3_result_int64(pCtx, pCsr->pIter->iRowid); 691 + break; 692 + case 2: { 693 + int ii = -1; 694 + if( eDetail==FTS5_DETAIL_FULL ){ 695 + ii = FTS5_POS2COLUMN(pCsr->iInstPos); 696 + }else if( eDetail==FTS5_DETAIL_COLUMNS ){ 697 + ii = pCsr->iInstPos; 698 + } 699 + if( ii>=0 && ii<pCsr->pConfig->nCol ){ 700 + const char *z = pCsr->pConfig->azCol[ii]; 701 + sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); 702 + } 703 + break; 704 + } 705 + default: { 706 + assert( iCol==3 ); 707 + if( eDetail==FTS5_DETAIL_FULL ){ 708 + int ii = FTS5_POS2OFFSET(pCsr->iInstPos); 709 + sqlite3_result_int(pCtx, ii); 710 + } 711 + break; 712 + } 600 713 } 601 714 } 602 715 603 716 if( iVal>0 ) sqlite3_result_int64(pCtx, iVal); 604 717 return SQLITE_OK; 605 718 } 606 719
Added ext/fts5/test/fts5vocab2.test.
1 +# 2017 August 10 2 +# 3 +# The author disclaims copyright to this source code. In place of 4 +# a legal notice, here is a blessing: 5 +# 6 +# May you do good and not evil. 7 +# May you find forgiveness for yourself and forgive others. 8 +# May you share freely, never taking more than you give. 9 +# 10 +#*********************************************************************** 11 +# 12 +# The tests in this file focus on testing the fts5vocab module. 13 +# 14 + 15 +source [file join [file dirname [info script]] fts5_common.tcl] 16 +set testprefix fts5vocab 17 + 18 +# If SQLITE_ENABLE_FTS5 is defined, omit this file. 19 +ifcapable !fts5 { 20 + finish_test 21 + return 22 +} 23 + 24 +do_execsql_test 1.0 { 25 + CREATE VIRTUAL TABLE t1 USING fts5(a, b); 26 + CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); 27 + 28 + INSERT INTO t1 VALUES('one two', 'two three'); 29 + INSERT INTO t1 VALUES('three four', 'four five five five'); 30 +} 31 + 32 +do_execsql_test 1.1 { 33 + SELECT * FROM v1; 34 +} { 35 + five 2 b 1 36 + five 2 b 2 37 + five 2 b 3 38 + four 2 a 1 39 + four 2 b 0 40 + one 1 a 0 41 + three 1 b 1 42 + three 2 a 0 43 + two 1 a 1 44 + two 1 b 0 45 +} 46 + 47 +do_execsql_test 1.2 { 48 + SELECT * FROM v1 WHERE term='three'; 49 +} { 50 + three 1 b 1 51 + three 2 a 0 52 +} 53 + 54 +do_execsql_test 1.3 { 55 + BEGIN; 56 + DELETE FROM t1 WHERE rowid=2; 57 + SELECT * FROM v1; 58 + ROLLBACK; 59 +} { 60 + one 1 a 0 61 + three 1 b 1 62 + two 1 a 1 63 + two 1 b 0 64 +} 65 + 66 +do_execsql_test 1.4 { 67 + BEGIN; 68 + DELETE FROM t1 WHERE rowid=1; 69 + SELECT * FROM v1; 70 + ROLLBACK; 71 +} { 72 + five 2 b 1 73 + five 2 b 2 74 + five 2 b 3 75 + four 2 a 1 76 + four 2 b 0 77 + three 2 a 0 78 +} 79 + 80 +do_execsql_test 1.5 { 81 + DELETE FROM t1; 82 + SELECT * FROM v1; 83 +} { 84 +} 85 + 86 +#------------------------------------------------------------------------- 87 +# 88 +do_execsql_test 2.0 { 89 + DROP TABLE IF EXISTS t1; 90 + DROP TABLE IF EXISTS v1; 91 + 92 + CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=column); 93 + CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); 94 + 95 + INSERT INTO t1 VALUES('one two', 'two three'); 96 + INSERT INTO t1 VALUES('three four', 'four five five five'); 97 +} 98 + 99 +do_execsql_test 2.1 { 100 + SELECT * FROM v1; 101 +} { 102 + five 2 b {} 103 + four 2 a {} 104 + four 2 b {} 105 + one 1 a {} 106 + three 1 b {} 107 + three 2 a {} 108 + two 1 a {} 109 + two 1 b {} 110 +} 111 + 112 +do_execsql_test 2.2 { 113 + SELECT * FROM v1 WHERE term='three'; 114 +} { 115 + three 1 b {} 116 + three 2 a {} 117 +} 118 + 119 +do_execsql_test 2.3 { 120 + BEGIN; 121 + DELETE FROM t1 WHERE rowid=2; 122 + SELECT * FROM v1; 123 + ROLLBACK; 124 +} { 125 + one 1 a {} 126 + three 1 b {} 127 + two 1 a {} 128 + two 1 b {} 129 +} 130 + 131 +do_execsql_test 2.4 { 132 + BEGIN; 133 + DELETE FROM t1 WHERE rowid=1; 134 + SELECT * FROM v1; 135 + ROLLBACK; 136 +} { 137 + five 2 b {} 138 + four 2 a {} 139 + four 2 b {} 140 + three 2 a {} 141 +} 142 + 143 +do_execsql_test 2.5 { 144 + DELETE FROM t1; 145 + SELECT * FROM v1; 146 +} { 147 +} 148 + 149 +#------------------------------------------------------------------------- 150 +# 151 +do_execsql_test 3.0 { 152 + DROP TABLE IF EXISTS t1; 153 + DROP TABLE IF EXISTS v1; 154 + 155 + CREATE VIRTUAL TABLE t1 USING fts5(a, b, detail=none); 156 + CREATE VIRTUAL TABLE v1 USING fts5vocab(t1, instance); 157 + 158 + INSERT INTO t1 VALUES('one two', 'two three'); 159 + INSERT INTO t1 VALUES('three four', 'four five five five'); 160 +} 161 + 162 +do_execsql_test 3.1 { 163 + SELECT * FROM v1; 164 +} { 165 + five 2 {} {} 166 + four 2 {} {} 167 + one 1 {} {} 168 + three 1 {} {} 169 + three 2 {} {} 170 + two 1 {} {} 171 +} 172 + 173 +do_execsql_test 3.2 { 174 + SELECT * FROM v1 WHERE term='three'; 175 +} { 176 + three 1 {} {} 177 + three 2 {} {} 178 +} 179 + 180 +do_execsql_test 3.3 { 181 + BEGIN; 182 + DELETE FROM t1 WHERE rowid=2; 183 + SELECT * FROM v1; 184 + ROLLBACK; 185 +} { 186 + one 1 {} {} 187 + three 1 {} {} 188 + two 1 {} {} 189 +} 190 + 191 +do_execsql_test 3.4 { 192 + BEGIN; 193 + DELETE FROM t1 WHERE rowid=1; 194 + SELECT * FROM v1; 195 + ROLLBACK; 196 +} { 197 + five 2 {} {} 198 + four 2 {} {} 199 + three 2 {} {} 200 +} 201 + 202 +do_execsql_test 3.5 { 203 + DELETE FROM t1; 204 + SELECT * FROM v1; 205 +} { 206 +} 207 + 208 +finish_test 209 +
Changes to ext/lsm1/lsm_vtab.c.
6 6 ** 7 7 ** May you do good and not evil. 8 8 ** May you find forgiveness for yourself and forgive others. 9 9 ** May you share freely, never taking more than you give. 10 10 ** 11 11 ************************************************************************* 12 12 ** 13 -** This file implements a simple virtual table wrapper around the LSM 13 +** This file implements a virtual table for SQLite3 around the LSM 14 14 ** storage engine from SQLite4. 15 +** 16 +** USAGE 17 +** 18 +** CREATE VIRTUAL TABLE demo USING lsm1(filename,key,keytype,value1,...); 19 +** 20 +** The filename parameter is the name of the LSM database file, which is 21 +** separate and distinct from the SQLite3 database file. 22 +** 23 +** The keytype must be one of: UINT, TEXT, BLOB. All keys must be of that 24 +** one type. "UINT" means unsigned integer. The values may be of any 25 +** SQLite datatype: BLOB, TEXT, INTEGER, FLOAT, or NULL. 26 +** 27 +** The virtual table contains read-only hidden columns: 28 +** 29 +** lsm1_key A BLOB which is the raw LSM key. If the "keytype" 30 +** is BLOB or TEXT then this column is exactly the 31 +** same as the key. For the UINT keytype, this column 32 +** will be a variable-length integer encoding of the key. 33 +** 34 +** lsm1_value A BLOB which is the raw LSM value. All of the value 35 +** columns are packed into this BLOB using the encoding 36 +** described below. 37 +** 38 +** Attempts to write values into the lsm1_key and lsm1_value columns are 39 +** silently ignored. 40 +** 41 +** EXAMPLE 42 +** 43 +** The virtual table declared this way: 44 +** 45 +** CREATE VIRTUAL TABLE demo2 USING lsm1('x.lsm',id,UINT,a,b,c,d); 46 +** 47 +** Results in a new virtual table named "demo2" that acts as if it has 48 +** the following schema: 49 +** 50 +** CREATE TABLE demo2( 51 +** id UINT PRIMARY KEY ON CONFLICT REPLACE, 52 +** a ANY, 53 +** b ANY, 54 +** c ANY, 55 +** d ANY, 56 +** lsm1_key BLOB HIDDEN, 57 +** lsm1_value BLOB HIDDEN 58 +** ) WITHOUT ROWID; 59 +** 60 +** 61 +** 62 +** INTERNALS 63 +** 64 +** The key encoding for BLOB and TEXT is just a copy of the blob or text. 65 +** UTF-8 is used for text. The key encoding for UINT is the variable-length 66 +** integer format at https://sqlite.org/src4/doc/trunk/www/varint.wiki. 67 +** 68 +** The values are encoded as a single blob (since that is what lsm stores as 69 +** its content). There is a "type integer" followed by "content" for each 70 +** value, alternating back and forth. The content might be empty. 71 +** 72 +** TYPE1 CONTENT1 TYPE2 CONTENT2 TYPE3 CONTENT3 .... 73 +** 74 +** Each "type integer" is encoded as a variable-length integer in the 75 +** format of the link above. Let the type integer be T. The actual 76 +** datatype is an integer 0-5 equal to T%6. Values 1 through 5 correspond 77 +** to SQLITE_INTEGER through SQLITE_NULL. The size of the content in bytes 78 +** is T/6. Type value 0 means that the value is an integer whose actual 79 +** values is T/6 and there is no content. The type-value-0 integer format 80 +** only works for integers in the range of 0 through 40. 81 +** 82 +** There is no content for NULL or type-0 integers. For BLOB and TEXT 83 +** values, the content is the blob data or the UTF-8 text data. For 84 +** non-negative integers X, the content is a variable-length integer X*2. 85 +** For negative integers Y, the content is varaible-length integer (1-Y)*2+1. 86 +** For FLOAT values, the content is the IEEE754 floating point value in 87 +** native byte-order. This means that FLOAT values will be corrupted when 88 +** database file is moved between big-endian and little-endian machines. 15 89 */ 16 90 #include "sqlite3ext.h" 17 91 SQLITE_EXTENSION_INIT1 18 92 #include "lsm.h" 19 93 #include <assert.h> 20 94 #include <string.h> 21 95 22 96 /* Forward declaration of subclasses of virtual table objects */ 23 97 typedef struct lsm1_vtab lsm1_vtab; 24 98 typedef struct lsm1_cursor lsm1_cursor; 99 +typedef struct lsm1_vblob lsm1_vblob; 25 100 26 101 /* Primitive types */ 27 102 typedef unsigned char u8; 103 +typedef unsigned int u32; 104 +typedef sqlite3_uint64 u64; 28 105 29 106 /* An open connection to an LSM table */ 30 107 struct lsm1_vtab { 31 108 sqlite3_vtab base; /* Base class - must be first */ 32 109 lsm_db *pDb; /* Open connection to the LSM table */ 110 + u8 keyType; /* SQLITE_BLOB, _TEXT, or _INTEGER */ 111 + u32 nVal; /* Number of value columns */ 33 112 }; 34 113 35 114 36 115 /* lsm1_cursor is a subclass of sqlite3_vtab_cursor which will 37 116 ** serve as the underlying representation of a cursor that scans 38 117 ** over rows of the result 39 118 */ 40 119 struct lsm1_cursor { 41 120 sqlite3_vtab_cursor base; /* Base class - must be first */ 42 121 lsm_cursor *pLsmCur; /* The LSM cursor */ 43 122 u8 isDesc; /* 0: scan forward. 1: scan reverse */ 44 123 u8 atEof; /* True if the scan is complete */ 45 124 u8 bUnique; /* True if no more than one row of output */ 125 + u8 *zData; /* Content of the current row */ 126 + u32 nData; /* Number of bytes in the current row */ 127 + u8 *aeType; /* Types for all column values */ 128 + u32 *aiOfst; /* Offsets to the various fields */ 129 + u32 *aiLen; /* Length of each field */ 130 + u8 *pKey2; /* Loop termination key, or NULL */ 131 + u32 nKey2; /* Length of the loop termination key */ 132 +}; 133 + 134 +/* An extensible buffer object. 135 +** 136 +** Content can be appended. Space to hold new content is automatically 137 +** allocated. 138 +*/ 139 +struct lsm1_vblob { 140 + u8 *a; /* Space to hold content, from sqlite3_malloc64() */ 141 + u64 n; /* Bytes of space used */ 142 + u64 nAlloc; /* Bytes of space allocated */ 143 + u8 errNoMem; /* True if a memory allocation error has been seen */ 46 144 }; 145 + 146 +#if defined(__GNUC__) 147 +# define LSM1_NOINLINE __attribute__((noinline)) 148 +#elif defined(_MSC_VER) && _MSC_VER>=1310 149 +# define LSM1_NOINLINE __declspec(noinline) 150 +#else 151 +# define LSM1_NOINLINE 152 +#endif 153 + 154 + 155 +/* Increase the available space in the vblob object so that it can hold 156 +** at least N more bytes. Return the number of errors. 157 +*/ 158 +static int lsm1VblobEnlarge(lsm1_vblob *p, u32 N){ 159 + if( p->n+N>p->nAlloc ){ 160 + if( p->errNoMem ) return 1; 161 + p->nAlloc += N + (p->nAlloc ? p->nAlloc : N); 162 + p->a = sqlite3_realloc64(p->a, p->nAlloc); 163 + if( p->a==0 ){ 164 + p->n = 0; 165 + p->nAlloc = 0; 166 + p->errNoMem = 1; 167 + return 1; 168 + } 169 + p->nAlloc = sqlite3_msize(p->a); 170 + } 171 + return 0; 172 +} 173 + 174 +/* Append N bytes to a vblob after first enlarging it */ 175 +static LSM1_NOINLINE void lsm1VblobEnlargeAndAppend( 176 + lsm1_vblob *p, 177 + const u8 *pData, 178 + u32 N 179 +){ 180 + if( p->n+N>p->nAlloc && lsm1VblobEnlarge(p, N) ) return; 181 + memcpy(p->a+p->n, pData, N); 182 + p->n += N; 183 +} 184 + 185 +/* Append N bytes to a vblob */ 186 +static void lsm1VblobAppend(lsm1_vblob *p, const u8 *pData, u32 N){ 187 + sqlite3_int64 n = p->n; 188 + if( n+N>p->nAlloc ){ 189 + lsm1VblobEnlargeAndAppend(p, pData, N); 190 + }else{ 191 + p->n += N; 192 + memcpy(p->a+n, pData, N); 193 + } 194 +} 195 + 196 +/* append text to a vblob */ 197 +static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){ 198 + lsm1VblobAppend(p, (u8*)z, (u32)strlen(z)); 199 +} 47 200 48 201 /* Dequote the string */ 49 202 static void lsm1Dequote(char *z){ 50 203 int j; 51 204 char cQuote = z[0]; 52 205 size_t i, n; 53 206 ................................................................................ 72 225 int argc, const char *const*argv, 73 226 sqlite3_vtab **ppVtab, 74 227 char **pzErr 75 228 ){ 76 229 lsm1_vtab *pNew; 77 230 int rc; 78 231 char *zFilename; 232 + u8 keyType = 0; 233 + int i; 234 + lsm1_vblob sql; 235 + static const char *azTypes[] = { "UINT", "TEXT", "BLOB" }; 236 + static const u8 aeTypes[] = { SQLITE_INTEGER, SQLITE_TEXT, SQLITE_BLOB }; 237 + static const char *azArgName[] = {"filename", "key", "key type", "value1" }; 79 238 80 - if( argc!=4 || argv[3]==0 || argv[3][0]==0 ){ 81 - *pzErr = sqlite3_mprintf("filename argument missing"); 239 + for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){ 240 + if( argc<i+4 || argv[i+3]==0 || argv[i+3][0]==0 ){ 241 + *pzErr = sqlite3_mprintf("%s (%r) argument missing", 242 + azArgName[i], i+1); 243 + return SQLITE_ERROR; 244 + } 245 + } 246 + for(i=0; i<sizeof(azTypes)/sizeof(azTypes[0]); i++){ 247 + if( sqlite3_stricmp(azTypes[i],argv[5])==0 ){ 248 + keyType = aeTypes[i]; 249 + break; 250 + } 251 + } 252 + if( keyType==0 ){ 253 + *pzErr = sqlite3_mprintf("key type should be INT, TEXT, or BLOB"); 82 254 return SQLITE_ERROR; 83 255 } 84 256 *ppVtab = sqlite3_malloc( sizeof(*pNew) ); 85 257 pNew = (lsm1_vtab*)*ppVtab; 86 258 if( pNew==0 ){ 87 259 return SQLITE_NOMEM; 88 260 } 89 261 memset(pNew, 0, sizeof(*pNew)); 262 + pNew->keyType = keyType; 90 263 rc = lsm_new(0, &pNew->pDb); 91 264 if( rc ){ 92 265 *pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc); 93 266 rc = SQLITE_ERROR; 94 267 goto connect_failed; 95 268 } 96 269 zFilename = sqlite3_mprintf("%s", argv[3]); ................................................................................ 99 272 sqlite3_free(zFilename); 100 273 if( rc ){ 101 274 *pzErr = sqlite3_mprintf("lsm_open failed with %d", rc); 102 275 rc = SQLITE_ERROR; 103 276 goto connect_failed; 104 277 } 105 278 106 -/* Column numbers */ 107 -#define LSM1_COLUMN_KEY 0 108 -#define LSM1_COLUMN_BLOBKEY 1 109 -#define LSM1_COLUMN_VALUE 2 110 -#define LSM1_COLUMN_BLOBVALUE 3 111 -#define LSM1_COLUMN_COMMAND 4 279 + memset(&sql, 0, sizeof(sql)); 280 + lsm1VblobAppendText(&sql, "CREATE TABLE x("); 281 + lsm1VblobAppendText(&sql, argv[4]); 282 + lsm1VblobAppendText(&sql, " "); 283 + lsm1VblobAppendText(&sql, argv[5]); 284 + lsm1VblobAppendText(&sql, " PRIMARY KEY"); 285 + for(i=6; i<argc; i++){ 286 + lsm1VblobAppendText(&sql, ", "); 287 + lsm1VblobAppendText(&sql, argv[i]); 288 + pNew->nVal++; 289 + } 290 + lsm1VblobAppendText(&sql, 291 + ", lsm1_command HIDDEN" 292 + ", lsm1_key HIDDEN" 293 + ", lsm1_value HIDDEN) WITHOUT ROWID"); 294 + lsm1VblobAppend(&sql, (u8*)"", 1); 295 + if( sql.errNoMem ){ 296 + rc = SQLITE_NOMEM; 297 + goto connect_failed; 298 + } 299 + rc = sqlite3_declare_vtab(db, (const char*)sql.a); 300 + sqlite3_free(sql.a); 112 301 113 - rc = sqlite3_declare_vtab(db, 114 - "CREATE TABLE x(" 115 - " key," /* The primary key. Any non-NULL */ 116 - " blobkey," /* Pure BLOB primary key */ 117 - " value," /* The value associated with key. Any non-NULL */ 118 - " blobvalue," /* Pure BLOB value */ 119 - " command hidden" /* Insert here for control operations */ 120 - ");" 121 - ); 122 302 connect_failed: 123 303 if( rc!=SQLITE_OK ){ 124 304 if( pNew ){ 125 305 if( pNew->pDb ) lsm_close(pNew->pDb); 126 306 sqlite3_free(pNew); 127 307 } 128 308 *ppVtab = 0; ................................................................................ 143 323 /* 144 324 ** Constructor for a new lsm1_cursor object. 145 325 */ 146 326 static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ 147 327 lsm1_vtab *p = (lsm1_vtab*)pVtab; 148 328 lsm1_cursor *pCur; 149 329 int rc; 150 - pCur = sqlite3_malloc( sizeof(*pCur) ); 330 + pCur = sqlite3_malloc64( sizeof(*pCur) 331 + + p->nVal*(sizeof(pCur->aiOfst)+sizeof(pCur->aiLen)+1) ); 151 332 if( pCur==0 ) return SQLITE_NOMEM; 152 333 memset(pCur, 0, sizeof(*pCur)); 334 + pCur->aiOfst = (u32*)&pCur[1]; 335 + pCur->aiLen = &pCur->aiOfst[p->nVal]; 336 + pCur->aeType = (u8*)&pCur->aiLen[p->nVal]; 153 337 *ppCursor = &pCur->base; 154 338 rc = lsm_csr_open(p->pDb, &pCur->pLsmCur); 155 339 if( rc==LSM_OK ){ 156 340 rc = SQLITE_OK; 157 341 }else{ 158 342 sqlite3_free(pCur); 159 343 *ppCursor = 0; ................................................................................ 163 347 } 164 348 165 349 /* 166 350 ** Destructor for a lsm1_cursor. 167 351 */ 168 352 static int lsm1Close(sqlite3_vtab_cursor *cur){ 169 353 lsm1_cursor *pCur = (lsm1_cursor*)cur; 354 + sqlite3_free(pCur->pKey2); 170 355 lsm_csr_close(pCur->pLsmCur); 171 356 sqlite3_free(pCur); 172 357 return SQLITE_OK; 173 358 } 174 359 175 360 176 361 /* ................................................................................ 186 371 rc = lsm_csr_prev(pCur->pLsmCur); 187 372 }else{ 188 373 rc = lsm_csr_next(pCur->pLsmCur); 189 374 } 190 375 if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){ 191 376 pCur->atEof = 1; 192 377 } 378 + if( pCur->pKey2 && pCur->atEof==0 ){ 379 + const u8 *pVal; 380 + u32 nVal; 381 + assert( pCur->isDesc==0 ); 382 + rc = lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, (int*)&nVal); 383 + if( rc==LSM_OK ){ 384 + u32 len = pCur->nKey2; 385 + int c; 386 + if( len>nVal ) len = nVal; 387 + c = memcmp(pVal, pCur->pKey2, len); 388 + if( c==0 ) c = nVal - pCur->nKey2; 389 + if( c>0 ) pCur->atEof = 1; 390 + } 391 + } 392 + pCur->zData = 0; 193 393 } 194 394 return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; 195 395 } 196 396 197 397 /* 198 398 ** Return TRUE if the cursor has been moved off of the last 199 399 ** row of output. ................................................................................ 290 490 return 8; 291 491 } 292 492 z[0] = 255; 293 493 varintWrite32(z+1, w); 294 494 varintWrite32(z+5, y); 295 495 return 9; 296 496 } 497 + 498 +/* Append non-negative integer x as a variable-length integer. 499 +*/ 500 +static void lsm1VblobAppendVarint(lsm1_vblob *p, sqlite3_uint64 x){ 501 + sqlite3_int64 n = p->n; 502 + if( n+9>p->nAlloc && lsm1VblobEnlarge(p, 9) ) return; 503 + p->n += lsm1PutVarint64(p->a+p->n, x); 504 +} 297 505 298 506 /* 299 507 ** Decode the varint in the first n bytes z[]. Write the integer value 300 508 ** into *pResult and return the number of bytes in the varint. 301 509 ** 302 510 ** If the decode fails because there are not enough bytes in z[] then 303 511 ** return 0; ................................................................................ 345 553 return 8; 346 554 } 347 555 *pResult = (((sqlite3_uint64)x)<<32) + 348 556 (0xffffffff & ((z[5]<<24) + (z[6]<<16) + (z[7]<<8) + z[8])); 349 557 return 9; 350 558 } 351 559 352 -/* 353 -** Generate a key encoding for pValue such that all keys compare in 354 -** lexicographical order. Return an SQLite error code or SQLITE_OK. 560 +/* Encoded a signed integer as a varint. Numbers close to zero uses fewer 561 +** bytes than numbers far away from zero. However, the result is not in 562 +** lexicographical order. 355 563 ** 356 -** The key encoding is *pnKey bytes in length written into *ppKey. 357 -** Space to hold the key is taken from pSpace if sufficient, or else 358 -** from sqlite3_malloc(). The caller is responsible for freeing malloced 359 -** space. 564 +** Encoding: Non-negative integer X is encoding as an unsigned 565 +** varint X*2. Negative integer Y is encoding as an unsigned 566 +** varint (1-Y)*2 + 1. 360 567 */ 361 -static int lsm1EncodeKey( 362 - sqlite3_value *pValue, /* Value to be encoded */ 363 - unsigned char **ppKey, /* Write the encoding here */ 364 - int *pnKey, /* Write the size of the encoding here */ 365 - unsigned char *pSpace, /* Use this space if it is large enough */ 366 - int nSpace /* Size of pSpace[] */ 568 +static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){ 569 + sqlite3_uint64 u; 570 + if( v>=0 ){ 571 + u = (sqlite3_uint64)v; 572 + return lsm1PutVarint64(z, u*2); 573 + }else{ 574 + u = (sqlite3_uint64)(-1-v); 575 + return lsm1PutVarint64(z, u*2+1); 576 + } 577 +} 578 + 579 +/* Decoded a signed varint. */ 580 +static int lsm1GetSignedVarint64( 581 + const unsigned char *z, 582 + int n, 583 + sqlite3_int64 *pResult 367 584 ){ 368 - int eType = sqlite3_value_type(pValue); 369 - *ppKey = 0; 370 - *pnKey = 0; 371 - assert( nSpace>=32 ); 372 - switch( eType ){ 373 - default: { 374 - return SQLITE_ERROR; /* We cannot handle NULL keys */ 375 - } 376 - case SQLITE_BLOB: 377 - case SQLITE_TEXT: { 378 - int nVal = sqlite3_value_bytes(pValue); 379 - const void *pVal; 380 - if( eType==SQLITE_BLOB ){ 381 - eType = LSM1_TYPE_BLOB; 382 - pVal = sqlite3_value_blob(pValue); 383 - }else{ 384 - eType = LSM1_TYPE_TEXT; 385 - pVal = (const void*)sqlite3_value_text(pValue); 386 - if( pVal==0 ) return SQLITE_NOMEM; 387 - } 388 - if( nVal+1>nSpace ){ 389 - pSpace = sqlite3_malloc( nVal+1 ); 390 - if( pSpace==0 ) return SQLITE_NOMEM; 391 - } 392 - pSpace[0] = (unsigned char)eType; 393 - memcpy(&pSpace[1], pVal, nVal); 394 - *ppKey = pSpace; 395 - *pnKey = nVal+1; 396 - break; 585 + sqlite3_uint64 u = 0; 586 + n = lsm1GetVarint64(z, n, &u); 587 + if( u&1 ){ 588 + *pResult = -1 - (sqlite3_int64)(u>>1); 589 + }else{ 590 + *pResult = (sqlite3_int64)(u>>1); 591 + } 592 + return n; 593 +} 594 + 595 + 596 +/* 597 +** Read the value part of the key-value pair and decode it into columns. 598 +*/ 599 +static int lsm1DecodeValues(lsm1_cursor *pCur){ 600 + lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab); 601 + int i, n; 602 + int rc; 603 + u8 eType; 604 + sqlite3_uint64 v; 605 + 606 + if( pCur->zData ) return 1; 607 + rc = lsm_csr_value(pCur->pLsmCur, (const void**)&pCur->zData, 608 + (int*)&pCur->nData); 609 + if( rc ) return 0; 610 + for(i=n=0; i<pTab->nVal; i++){ 611 + v = 0; 612 + n += lsm1GetVarint64(pCur->zData+n, pCur->nData-n, &v); 613 + pCur->aeType[i] = eType = (u8)(v%6); 614 + if( eType==0 ){ 615 + pCur->aiOfst[i] = (u32)(v/6); 616 + pCur->aiLen[i] = 0; 617 + }else{ 618 + pCur->aiOfst[i] = n; 619 + n += (pCur->aiLen[i] = (u32)(v/6)); 397 620 } 398 - case SQLITE_INTEGER: { 399 - sqlite3_int64 iVal = sqlite3_value_int64(pValue); 400 - sqlite3_uint64 uVal; 401 - if( iVal<0 ){ 402 - if( iVal==0xffffffffffffffffLL ) return SQLITE_ERROR; 403 - uVal = *(sqlite3_uint64*)&iVal; 404 - eType = LSM1_TYPE_NEGATIVE; 405 - }else{ 406 - uVal = iVal; 407 - eType = LSM1_TYPE_POSITIVE; 408 - } 409 - pSpace[0] = (unsigned char)eType; 410 - *ppKey = pSpace; 411 - *pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal); 412 - } 621 + if( n>pCur->nData ) break; 413 622 } 414 - return SQLITE_OK; 623 + if( i<pTab->nVal ){ 624 + pCur->zData = 0; 625 + return 0; 626 + } 627 + return 1; 415 628 } 416 629 417 630 /* 418 631 ** Return values of columns for the row at which the lsm1_cursor 419 632 ** is currently pointing. 420 633 */ 421 634 static int lsm1Column( 422 635 sqlite3_vtab_cursor *cur, /* The cursor */ 423 636 sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ 424 637 int i /* Which column to return */ 425 638 ){ 426 639 lsm1_cursor *pCur = (lsm1_cursor*)cur; 427 - switch( i ){ 428 - case LSM1_COLUMN_BLOBKEY: { 640 + lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab); 641 + if( i==0 ){ 642 + /* The key column */ 643 + const void *pVal; 644 + int nVal; 645 + if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ 646 + if( pTab->keyType==SQLITE_BLOB ){ 647 + sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); 648 + }else if( pTab->keyType==SQLITE_TEXT ){ 649 + sqlite3_result_text(ctx,(const char*)pVal, nVal, SQLITE_TRANSIENT); 650 + }else{ 651 + const unsigned char *z = (const unsigned char*)pVal; 652 + sqlite3_uint64 v1; 653 + lsm1GetVarint64(z, nVal, &v1); 654 + sqlite3_result_int64(ctx, (sqlite3_int64)v1); 655 + } 656 + } 657 + }else if( i>pTab->nVal ){ 658 + if( i==pTab->nVal+2 ){ /* lsm1_key */ 429 659 const void *pVal; 430 660 int nVal; 431 661 if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ 432 662 sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); 433 663 } 434 - break; 435 - } 436 - case LSM1_COLUMN_KEY: { 437 - const unsigned char *pVal; 438 - int nVal; 439 - if( lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK 440 - && nVal>=1 441 - ){ 442 - if( pVal[0]==LSM1_TYPE_BLOB ){ 443 - sqlite3_result_blob(ctx, (const void*)&pVal[1],nVal-1, 444 - SQLITE_TRANSIENT); 445 - }else if( pVal[0]==LSM1_TYPE_TEXT ){ 446 - sqlite3_result_text(ctx, (const char*)&pVal[1],nVal-1, 447 - SQLITE_TRANSIENT); 448 - }else if( nVal>=2 && nVal<=10 && 449 - (pVal[0]==LSM1_TYPE_POSITIVE || pVal[0]==LSM1_TYPE_NEGATIVE) 450 - ){ 451 - sqlite3_int64 iVal; 452 - lsm1GetVarint64(pVal+1, nVal-1, (sqlite3_uint64*)&iVal); 453 - sqlite3_result_int64(ctx, iVal); 454 - } 455 - } 456 - break; 457 - } 458 - case LSM1_COLUMN_BLOBVALUE: { 664 + }else if( i==pTab->nVal+3 ){ /* lsm1_value */ 459 665 const void *pVal; 460 666 int nVal; 461 - if( lsm_csr_value(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK ){ 667 + if( lsm_csr_value(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ 462 668 sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); 463 669 } 464 - break; 465 670 } 466 - case LSM1_COLUMN_VALUE: { 467 - const unsigned char *aVal; 468 - int nVal; 469 - if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK 470 - && nVal>=1 471 - ){ 472 - switch( aVal[0] ){ 473 - case SQLITE_FLOAT: 474 - case SQLITE_INTEGER: { 475 - sqlite3_uint64 x = 0; 476 - int j; 477 - for(j=1; j<nVal; j++){ 478 - x = (x<<8) | aVal[j]; 479 - } 480 - if( aVal[0]==SQLITE_INTEGER ){ 481 - sqlite3_result_int64(ctx, *(sqlite3_int64*)&x); 482 - }else{ 483 - double r; 484 - assert( sizeof(r)==sizeof(x) ); 485 - memcpy(&r, &x, sizeof(r)); 486 - sqlite3_result_double(ctx, r); 487 - } 488 - break; 489 - } 490 - case SQLITE_TEXT: { 491 - sqlite3_result_text(ctx, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT); 492 - break; 493 - } 494 - case SQLITE_BLOB: { 495 - sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT); 496 - break; 497 - } 671 + }else if( lsm1DecodeValues(pCur) ){ 672 + /* The i-th value column (where leftmost is 1) */ 673 + const u8 *zData; 674 + u32 nData; 675 + i--; 676 + zData = pCur->zData + pCur->aiOfst[i]; 677 + nData = pCur->aiLen[i]; 678 + switch( pCur->aeType[i] ){ 679 + case 0: { /* in-line integer */ 680 + sqlite3_result_int(ctx, pCur->aiOfst[i]); 681 + break; 682 + } 683 + case SQLITE_INTEGER: { 684 + sqlite3_int64 v; 685 + lsm1GetSignedVarint64(zData, nData, &v); 686 + sqlite3_result_int64(ctx, v); 687 + break; 688 + } 689 + case SQLITE_FLOAT: { 690 + double v; 691 + if( nData==sizeof(v) ){ 692 + memcpy(&v, zData, sizeof(v)); 693 + sqlite3_result_double(ctx, v); 498 694 } 695 + break; 696 + } 697 + case SQLITE_TEXT: { 698 + sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT); 699 + break; 700 + } 701 + case SQLITE_BLOB: { 702 + sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT); 703 + break; 499 704 } 500 - break; 501 - } 502 - default: { 503 - break; 705 + default: { 706 + /* A NULL. Do nothing */ 707 + } 504 708 } 505 709 } 506 710 return SQLITE_OK; 507 711 } 712 + 713 +/* Parameter "pValue" contains an SQL value that is to be used as 714 +** a key in an LSM table. The type of the key is determined by 715 +** "keyType". Extract the raw bytes used for the key in LSM1. 716 +*/ 717 +static void lsm1KeyFromValue( 718 + int keyType, /* The key type */ 719 + sqlite3_value *pValue, /* The key value */ 720 + u8 *pBuf, /* Storage space for a generated key */ 721 + const u8 **ppKey, /* OUT: the bytes of the key */ 722 + int *pnKey /* OUT: size of the key */ 723 +){ 724 + if( keyType==SQLITE_BLOB ){ 725 + *ppKey = (const u8*)sqlite3_value_blob(pValue); 726 + *pnKey = sqlite3_value_bytes(pValue); 727 + }else if( keyType==SQLITE_TEXT ){ 728 + *ppKey = (const u8*)sqlite3_value_text(pValue); 729 + *pnKey = sqlite3_value_bytes(pValue); 730 + }else{ 731 + sqlite3_int64 v = sqlite3_value_int64(pValue); 732 + if( v<0 ) v = 0; 733 + *pnKey = lsm1PutVarint64(pBuf, v); 734 + *ppKey = pBuf; 735 + } 736 +} 508 737 509 738 /* Move to the first row to return. 510 739 */ 511 740 static int lsm1Filter( 512 741 sqlite3_vtab_cursor *pVtabCursor, 513 742 int idxNum, const char *idxStr, 514 743 int argc, sqlite3_value **argv 515 744 ){ 516 745 lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor; 746 + lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab); 517 747 int rc = LSM_OK; 748 + int seekType = -1; 749 + const u8 *pVal = 0; 750 + int nVal; 751 + u8 keyType = pTab->keyType; 752 + u8 aKey1[16]; 753 + 518 754 pCur->atEof = 1; 519 - if( idxNum==1 ){ 520 - assert( argc==1 ); 521 - pCur->isDesc = 0; 522 - pCur->bUnique = 1; 523 - if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ 524 - const void *pVal = sqlite3_value_blob(argv[0]); 525 - int nVal = sqlite3_value_bytes(argv[0]); 526 - rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, LSM_SEEK_EQ); 755 + sqlite3_free(pCur->pKey2); 756 + pCur->pKey2 = 0; 757 + if( idxNum<99 ){ 758 + lsm1KeyFromValue(keyType, argv[0], aKey1, &pVal, &nVal); 759 + } 760 + switch( idxNum ){ 761 + case 0: { /* key==argv[0] */ 762 + assert( argc==1 ); 763 + seekType = LSM_SEEK_EQ; 764 + pCur->isDesc = 0; 765 + pCur->bUnique = 1; 766 + break; 767 + } 768 + case 1: { /* key>=argv[0] AND key<=argv[1] */ 769 + u8 aKey[12]; 770 + seekType = LSM_SEEK_GE; 771 + pCur->isDesc = 0; 772 + pCur->bUnique = 0; 773 + if( keyType==SQLITE_INTEGER ){ 774 + sqlite3_int64 v = sqlite3_value_int64(argv[1]); 775 + if( v<0 ) v = 0; 776 + pCur->nKey2 = lsm1PutVarint64(aKey, (sqlite3_uint64)v); 777 + pCur->pKey2 = sqlite3_malloc( pCur->nKey2 ); 778 + if( pCur->pKey2==0 ) return SQLITE_NOMEM; 779 + memcpy(pCur->pKey2, aKey, pCur->nKey2); 780 + }else{ 781 + pCur->nKey2 = sqlite3_value_bytes(argv[1]); 782 + pCur->pKey2 = sqlite3_malloc( pCur->nKey2 ); 783 + if( pCur->pKey2==0 ) return SQLITE_NOMEM; 784 + if( keyType==SQLITE_BLOB ){ 785 + memcpy(pCur->pKey2, sqlite3_value_blob(argv[1]), pCur->nKey2); 786 + }else{ 787 + memcpy(pCur->pKey2, sqlite3_value_text(argv[1]), pCur->nKey2); 788 + } 789 + } 790 + break; 791 + } 792 + case 2: { /* key>=argv[0] */ 793 + seekType = LSM_SEEK_GE; 794 + pCur->isDesc = 0; 795 + pCur->bUnique = 0; 796 + break; 797 + } 798 + case 3: { /* key<=argv[0] */ 799 + seekType = LSM_SEEK_LE; 800 + pCur->isDesc = 1; 801 + pCur->bUnique = 0; 802 + break; 803 + } 804 + default: { /* full table scan */ 805 + pCur->isDesc = 0; 806 + pCur->bUnique = 0; 807 + break; 527 808 } 809 + } 810 + if( pVal ){ 811 + rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, seekType); 528 812 }else{ 529 813 rc = lsm_csr_first(pCur->pLsmCur); 530 - pCur->isDesc = 0; 531 - pCur->bUnique = 0; 532 814 } 533 815 if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){ 534 816 pCur->atEof = 0; 535 817 } 536 818 return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; 537 819 } 538 820 539 821 /* 540 822 ** Only comparisons against the key are allowed. The idxNum defines 541 823 ** which comparisons are available: 542 824 ** 543 -** 0 Full table scan only 544 -** bit 1 key==?1 single argument for ?1 545 -** bit 2 key>?1 546 -** bit 3 key>=?1 547 -** bit 4 key<?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set) 548 -** bit 5 key<=?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set) 549 -** bit 6 Use blobkey instead of key 550 -** 551 -** To put it another way: 552 -** 553 -** 0 Full table scan. 554 -** 1 key==?1 555 -** 2 key>?1 556 -** 4 key>=?1 557 -** 8 key<?1 558 -** 10 key>?1 AND key<?2 559 -** 12 key>=?1 AND key<?2 560 -** 16 key<=?1 561 -** 18 key>?1 AND key<=?2 562 -** 20 key>=?1 AND key<=?2 563 -** 33..52 Use blobkey in place of key... 825 +** 0 key==?1 826 +** 1 key>=?1 AND key<=?2 827 +** 2 key>?1 or key>=?1 828 +** 3 key<?1 or key<=?1 829 +** 99 Full table scan only 564 830 */ 565 831 static int lsm1BestIndex( 566 832 sqlite3_vtab *tab, 567 833 sqlite3_index_info *pIdxInfo 568 834 ){ 569 835 int i; /* Loop over constraints */ 570 - int idxNum = 0; /* The query plan bitmask */ 836 + int idxNum = 99; /* The query plan */ 571 837 int nArg = 0; /* Number of arguments to xFilter */ 572 - int eqIdx = -1; /* Index of the key== constraint, or -1 if none */ 838 + int argIdx = -1; /* Index of the key== constraint, or -1 if none */ 839 + int iIdx2 = -1; /* The index of the second key */ 840 + int omit1 = 0; 841 + int omit2 = 0; 573 842 574 843 const struct sqlite3_index_constraint *pConstraint; 575 844 pConstraint = pIdxInfo->aConstraint; 576 845 for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){ 577 846 if( pConstraint->usable==0 ) continue; 578 - if( pConstraint->iColumn!=LSM1_COLUMN_KEY ) continue; 579 - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; 847 + if( pConstraint->iColumn!=0 ) continue; 580 848 switch( pConstraint->op ){ 581 849 case SQLITE_INDEX_CONSTRAINT_EQ: { 582 - eqIdx = i; 583 - idxNum = 1; 850 + if( idxNum>0 ){ 851 + argIdx = i; 852 + iIdx2 = -1; 853 + idxNum = 0; 854 + omit1 = 1; 855 + } 856 + break; 857 + } 858 + case SQLITE_INDEX_CONSTRAINT_GE: 859 + case SQLITE_INDEX_CONSTRAINT_GT: { 860 + if( idxNum==99 ){ 861 + argIdx = i; 862 + idxNum = 2; 863 + omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE; 864 + }else if( idxNum==3 ){ 865 + iIdx2 = idxNum; 866 + omit2 = omit1; 867 + argIdx = i; 868 + idxNum = 1; 869 + omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE; 870 + } 871 + break; 872 + } 873 + case SQLITE_INDEX_CONSTRAINT_LE: 874 + case SQLITE_INDEX_CONSTRAINT_LT: { 875 + if( idxNum==99 ){ 876 + argIdx = i; 877 + idxNum = 3; 878 + omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE; 879 + }else if( idxNum==2 ){ 880 + iIdx2 = i; 881 + idxNum = 1; 882 + omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE; 883 + } 584 884 break; 585 885 } 586 886 } 587 887 } 588 - if( eqIdx>=0 ){ 589 - pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg; 590 - pIdxInfo->aConstraintUsage[eqIdx].omit = 1; 888 + if( argIdx>=0 ){ 889 + pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg; 890 + pIdxInfo->aConstraintUsage[argIdx].omit = omit1; 891 + } 892 + if( iIdx2>=0 ){ 893 + pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg; 894 + pIdxInfo->aConstraintUsage[iIdx2].omit = omit2; 591 895 } 592 - if( idxNum==1 ){ 896 + if( idxNum==0 ){ 593 897 pIdxInfo->estimatedCost = (double)1; 594 898 pIdxInfo->estimatedRows = 1; 595 899 pIdxInfo->orderByConsumed = 1; 900 + }else if( idxNum==1 ){ 901 + pIdxInfo->estimatedCost = (double)100; 902 + pIdxInfo->estimatedRows = 100; 903 + }else if( idxNum<99 ){ 904 + pIdxInfo->estimatedCost = (double)5000; 905 + pIdxInfo->estimatedRows = 5000; 596 906 }else{ 597 907 /* Full table scan */ 598 908 pIdxInfo->estimatedCost = (double)2147483647; 599 909 pIdxInfo->estimatedRows = 2147483647; 600 910 } 601 911 pIdxInfo->idxNum = idxNum; 602 912 return SQLITE_OK; ................................................................................ 611 921 int lsm1Update( 612 922 sqlite3_vtab *pVTab, 613 923 int argc, 614 924 sqlite3_value **argv, 615 925 sqlite_int64 *pRowid 616 926 ){ 617 927 lsm1_vtab *p = (lsm1_vtab*)pVTab; 618 - const void *pKey; 619 - void *pFree = 0; 620 - int nKey; 621 - int eType; 928 + int nKey, nKey2; 929 + int i; 622 930 int rc = LSM_OK; 623 - sqlite3_value *pValue; 624 - const unsigned char *pVal; 625 - unsigned char *pData; 626 - int nVal; 627 - unsigned char pSpace[100]; 931 + const u8 *pKey, *pKey2; 932 + unsigned char aKey[16]; 933 + unsigned char pSpace[16]; 934 + lsm1_vblob val; 628 935 629 936 if( argc==1 ){ 630 - pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE"); 631 - return SQLITE_ERROR; 937 + /* DELETE the record whose key is argv[0] */ 938 + lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey); 939 + lsm_delete(p->pDb, pKey, nKey); 940 + return SQLITE_OK; 632 941 } 942 + 633 943 if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ 634 - pVTab->zErrMsg = sqlite3_mprintf("cannot UPDATE"); 635 - return SQLITE_ERROR; 944 + /* An UPDATE */ 945 + lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey); 946 + lsm1KeyFromValue(p->keyType, argv[1], pSpace, &pKey2, &nKey2); 947 + if( nKey!=nKey2 || memcmp(pKey, pKey2, nKey)!=0 ){ 948 + /* The UPDATE changes the PRIMARY KEY value. DELETE the old key */ 949 + lsm_delete(p->pDb, pKey, nKey); 950 + } 951 + /* Fall through into the INSERT case to complete the UPDATE */ 636 952 } 637 953 638 - /* "INSERT INTO tab(command) VALUES('....')" is used to implement 954 + /* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement 639 955 ** special commands. 640 956 */ 641 - if( sqlite3_value_type(argv[2+LSM1_COLUMN_COMMAND])!=SQLITE_NULL ){ 957 + if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){ 642 958 return SQLITE_OK; 643 959 } 644 - if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBKEY])==SQLITE_BLOB ){ 645 - /* Use the blob key exactly as supplied */ 646 - pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBKEY]); 647 - nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBKEY]); 648 - }else{ 649 - /* Use a key encoding that sorts in lexicographical order */ 650 - rc = lsm1EncodeKey(argv[2+LSM1_COLUMN_KEY], 651 - (unsigned char**)&pKey,&nKey, 652 - pSpace,sizeof(pSpace)); 653 - if( rc ) return rc; 654 - if( pKey!=(const void*)pSpace ) pFree = (void*)pKey; 655 - } 656 - if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBVALUE])==SQLITE_BLOB ){ 657 - pVal = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBVALUE]); 658 - nVal = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBVALUE]); 659 - rc = lsm_insert(p->pDb, pKey, nKey, pVal, nVal); 660 - }else{ 661 - pValue = argv[2+LSM1_COLUMN_VALUE]; 662 - eType = sqlite3_value_type(pValue); 960 + lsm1KeyFromValue(p->keyType, argv[2], aKey, &pKey, &nKey); 961 + memset(&val, 0, sizeof(val)); 962 + for(i=0; i<p->nVal; i++){ 963 + sqlite3_value *pArg = argv[3+i]; 964 + u8 eType = sqlite3_value_type(pArg); 663 965 switch( eType ){ 664 966 case SQLITE_NULL: { 665 - rc = lsm_delete(p->pDb, pKey, nKey); 967 + lsm1VblobAppendVarint(&val, SQLITE_NULL); 666 968 break; 667 969 } 668 - case SQLITE_BLOB: 669 - case SQLITE_TEXT: { 670 - if( eType==SQLITE_TEXT ){ 671 - pVal = sqlite3_value_text(pValue); 970 + case SQLITE_INTEGER: { 971 + sqlite3_int64 v = sqlite3_value_int64(pArg); 972 + if( v>=0 && v<=240/6 ){ 973 + lsm1VblobAppendVarint(&val, v*6); 672 974 }else{ 673 - pVal = (unsigned char*)sqlite3_value_blob(pValue); 674 - } 675 - nVal = sqlite3_value_bytes(pValue); 676 - pData = sqlite3_malloc( nVal+1 ); 677 - if( pData==0 ){ 678 - rc = SQLITE_NOMEM; 679 - }else{ 680 - pData[0] = (unsigned char)eType; 681 - memcpy(&pData[1], pVal, nVal); 682 - rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1); 683 - sqlite3_free(pData); 975 + int n = lsm1PutSignedVarint64(pSpace, v); 976 + lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6); 977 + lsm1VblobAppend(&val, pSpace, n); 684 978 } 685 979 break; 686 980 } 687 - case SQLITE_INTEGER: 688 981 case SQLITE_FLOAT: { 689 - sqlite3_uint64 x; 690 - unsigned char aVal[9]; 691 - int i; 692 - if( eType==SQLITE_INTEGER ){ 693 - *(sqlite3_int64*)&x = sqlite3_value_int64(pValue); 694 - }else{ 695 - double r = sqlite3_value_double(pValue); 696 - assert( sizeof(r)==sizeof(x) ); 697 - memcpy(&x, &r, sizeof(r)); 698 - } 699 - for(i=8; x>0 && i>=1; i--){ 700 - aVal[i] = x & 0xff; 701 - x >>= 8; 702 - } 703 - aVal[i] = (unsigned char)eType; 704 - rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i); 982 + double r = sqlite3_value_double(pArg); 983 + lsm1VblobAppendVarint(&val, SQLITE_FLOAT + 8*6); 984 + lsm1VblobAppend(&val, (u8*)&r, sizeof(r)); 985 + break; 986 + } 987 + case SQLITE_BLOB: { 988 + int n = sqlite3_value_bytes(pArg); 989 + lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB); 990 + lsm1VblobAppend(&val, sqlite3_value_blob(pArg), n); 991 + break; 992 + } 993 + case SQLITE_TEXT: { 994 + int n = sqlite3_value_bytes(pArg); 995 + lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT); 996 + lsm1VblobAppend(&val, sqlite3_value_text(pArg), n); 705 997 break; 706 998 } 707 999 } 708 1000 } 709 - sqlite3_free(pFree); 1001 + if( val.errNoMem ){ 1002 + return SQLITE_NOMEM; 1003 + } 1004 + rc = lsm_insert(p->pDb, pKey, nKey, val.a, val.n); 1005 + sqlite3_free(val.a); 710 1006 return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; 711 1007 } 712 1008 713 1009 /* Begin a transaction 714 1010 */ 715 1011 static int lsm1Begin(sqlite3_vtab *pVtab){ 716 1012 lsm1_vtab *p = (lsm1_vtab*)pVtab;
Changes to ext/lsm1/test/lsm1_simple.test.
15 15 source [file join [file dirname [info script]] lsm1_common.tcl] 16 16 set testprefix lsm1_simple 17 17 return_if_no_lsm1 18 18 load_lsm1_vtab db 19 19 20 20 forcedelete testlsm.db 21 21 22 -do_execsql_test 1.0 { 23 - CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db); 22 +do_execsql_test 100 { 23 + CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,UINT,b,c,d); 24 24 PRAGMA table_info(x1); 25 25 } { 26 - 0 key {} 0 {} 0 27 - 1 blobkey {} 0 {} 0 28 - 2 value {} 0 {} 0 29 - 3 blobvalue {} 0 {} 0 26 + 0 a UINT 1 {} 1 27 + 1 b {} 0 {} 0 28 + 2 c {} 0 {} 0 29 + 3 d {} 0 {} 0 30 30 } 31 31 32 -do_execsql_test 1.1 { 33 - INSERT INTO x1(blobkey, blobvalue) VALUES(x'abcd', x'1234'); 34 - SELECT quote(blobkey), quote(blobvalue) FROM x1; 35 -} {X'ABCD' X'1234'} 32 +do_execsql_test 110 { 33 + INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL), 34 + (12,NULL,3.25,-559281390); 35 + SELECT a, quote(b), quote(c), quote(d) FROM x1; 36 +} {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 33} 37 +do_execsql_test 111 { 38 + SELECT a, quote(lsm1_key), quote(lsm1_value) FROM x1; 39 +} {8 X'08' X'2162616E6A6F1633323105' 12 X'0C' X'05320000000000000A401FFB42ABE9DB' 15 X'0F' X'4284C6'} 40 + 41 +do_execsql_test 120 { 42 + UPDATE x1 SET d = d+1.0 WHERE a=15; 43 + SELECT a, quote(b), quote(c), quote(d) FROM x1; 44 +} {8 'banjo' X'333231' NULL 12 NULL 3.25 -559281390 15 11 22 34.0} 45 + 46 +do_execsql_test 130 { 47 + UPDATE x1 SET a=123456789 WHERE a=12; 48 + SELECT a, quote(b), quote(c), quote(d) FROM x1; 49 +} {8 'banjo' X'333231' NULL 15 11 22 34.0 123456789 NULL 3.25 -559281390} 50 +do_execsql_test 131 { 51 + SELECT quote(lsm1_key), printf('0x%x',a) FROM x1 WHERE a > 100000000; 52 +} {X'FB075BCD15' 0x75bcd15} 36 53 37 -do_catchsql_test 1.2 { 38 - UPDATE x1 SET blobvalue = x'7890' WHERE blobkey = x'abcd'; 39 -} {1 {cannot UPDATE}} 54 +do_execsql_test 140 { 55 + DELETE FROM x1 WHERE a=15; 56 + SELECT a, quote(b), quote(c), quote(d) FROM x1; 57 +} {8 'banjo' X'333231' NULL 123456789 NULL 3.25 -559281390} 40 58 41 -do_catchsql_test 1.3 { 42 - DELETE FROM x1 WHERE blobkey = x'abcd' 43 -} {1 {cannot DELETE}} 44 - 45 -do_test 1.4 { 59 +do_test 150 { 46 60 lsort [glob testlsm.db*] 47 61 } {testlsm.db testlsm.db-log testlsm.db-shm} 48 62 49 63 db close 50 -do_test 1.5 { 64 +do_test 160 { 51 65 lsort [glob testlsm.db*] 52 66 } {testlsm.db} 53 67 54 -finish_test 68 +forcedelete testlsm.db 69 +forcedelete test.db 70 +sqlite3 db test.db 71 +load_lsm1_vtab db 72 + 73 + 74 +do_execsql_test 200 { 75 + CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,TEXT,b,c,d); 76 + PRAGMA table_info(x1); 77 +} { 78 + 0 a TEXT 1 {} 1 79 + 1 b {} 0 {} 0 80 + 2 c {} 0 {} 0 81 + 3 d {} 0 {} 0 82 +} 83 +do_execsql_test 210 { 84 + INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33),(8,'banjo',x'333231',NULL), 85 + (12,NULL,3.25,-559281390); 86 + SELECT quote(a), quote(b), quote(c), quote(d), '|' FROM x1; 87 +} {'12' NULL 3.25 -559281390 | '15' 11 22 33 | '8' 'banjo' X'333231' NULL |} 88 +do_execsql_test 211 { 89 + SELECT quote(a), quote(lsm1_key), quote(lsm1_value), '|' FROM x1; 90 +} {'12' X'3132' X'05320000000000000A401FFB42ABE9DB' | '15' X'3135' X'4284C6' | '8' X'38' X'2162616E6A6F1633323105' |} 55 91 56 92 93 +finish_test
Changes to ext/misc/csv.c.
676 676 pCur->azVal[i] = zNew; 677 677 pCur->aLen[i] = pCur->rdr.n+1; 678 678 } 679 679 memcpy(pCur->azVal[i], z, pCur->rdr.n+1); 680 680 i++; 681 681 } 682 682 }while( pCur->rdr.cTerm==',' ); 683 - while( i<pTab->nCol ){ 684 - sqlite3_free(pCur->azVal[i]); 685 - pCur->azVal[i] = 0; 686 - pCur->aLen[i] = 0; 687 - i++; 688 - } 689 - if( z==0 || pCur->rdr.cTerm==EOF ){ 683 + if( z==0 || (pCur->rdr.cTerm==EOF && i<pTab->nCol) ){ 690 684 pCur->iRowid = -1; 691 685 }else{ 692 686 pCur->iRowid++; 687 + while( i<pTab->nCol ){ 688 + sqlite3_free(pCur->azVal[i]); 689 + pCur->azVal[i] = 0; 690 + pCur->aLen[i] = 0; 691 + i++; 692 + } 693 693 } 694 694 return SQLITE_OK; 695 695 } 696 696 697 697 /* 698 698 ** Return values of columns for the row at which the CsvCursor 699 699 ** is currently pointing.
Added ext/misc/vtablog.c.
1 +/* 2 +** 2017-08-10 3 +** 4 +** The author disclaims copyright to this source code. In place of 5 +** a legal notice, here is a blessing: 6 +** 7 +** May you do good and not evil. 8 +** May you find forgiveness for yourself and forgive others. 9 +** May you share freely, never taking more than you give. 10 +** 11 +************************************************************************* 12 +** 13 +** This file implements a virtual table that prints diagnostic information 14 +** on stdout when its key interfaces are called. This is intended for 15 +** interactive analysis and debugging of virtual table interfaces. 16 +** 17 +** Usage example: 18 +** 19 +** .load ./vtablog 20 +** CREATE VIRTUAL TABLE temp.log USING vtablog( 21 +** schema='CREATE TABLE x(a,b,c)', 22 +** rows=25 23 +** ); 24 +** SELECT * FROM log; 25 +*/ 26 +#include "sqlite3ext.h" 27 +SQLITE_EXTENSION_INIT1 28 +#include <stdio.h> 29 +#include <stdlib.h> 30 +#include <assert.h> 31 +#include <string.h> 32 +#include <ctype.h> 33 + 34 + 35 +/* vtablog_vtab is a subclass of sqlite3_vtab which will 36 +** serve as the underlying representation of a vtablog virtual table 37 +*/ 38 +typedef struct vtablog_vtab vtablog_vtab; 39 +struct vtablog_vtab { 40 + sqlite3_vtab base; /* Base class - must be first */ 41 + int nRow; /* Number of rows in the table */ 42 + int iInst; /* Instance number for this vtablog table */ 43 + int nCursor; /* Number of cursors created */ 44 +}; 45 + 46 +/* vtablog_cursor is a subclass of sqlite3_vtab_cursor which will 47 +** serve as the underlying representation of a cursor that scans 48 +** over rows of the result 49 +*/ 50 +typedef struct vtablog_cursor vtablog_cursor; 51 +struct vtablog_cursor { 52 + sqlite3_vtab_cursor base; /* Base class - must be first */ 53 + int iCursor; /* Cursor number */ 54 + sqlite3_int64 iRowid; /* The rowid */ 55 +}; 56 + 57 +/* Skip leading whitespace. Return a pointer to the first non-whitespace 58 +** character, or to the zero terminator if the string has only whitespace */ 59 +static const char *vtablog_skip_whitespace(const char *z){ 60 + while( isspace((unsigned char)z[0]) ) z++; 61 + return z; 62 +} 63 + 64 +/* Remove trailing whitespace from the end of string z[] */ 65 +static void vtablog_trim_whitespace(char *z){ 66 + size_t n = strlen(z); 67 + while( n>0 && isspace((unsigned char)z[n]) ) n--; 68 + z[n] = 0; 69 +} 70 + 71 +/* Dequote the string */ 72 +static void vtablog_dequote(char *z){ 73 + int j; 74 + char cQuote = z[0]; 75 + size_t i, n; 76 + 77 + if( cQuote!='\'' && cQuote!='"' ) return; 78 + n = strlen(z); 79 + if( n<2 || z[n-1]!=z[0] ) return; 80 + for(i=1, j=0; i<n-1; i++){ 81 + if( z[i]==cQuote && z[i+1]==cQuote ) i++; 82 + z[j++] = z[i]; 83 + } 84 + z[j] = 0; 85 +} 86 + 87 +/* Check to see if the string is of the form: "TAG = VALUE" with optional 88 +** whitespace before and around tokens. If it is, return a pointer to the 89 +** first character of VALUE. If it is not, return NULL. 90 +*/ 91 +static const char *vtablog_parameter(const char *zTag, int nTag, const char *z){ 92 + z = vtablog_skip_whitespace(z); 93 + if( strncmp(zTag, z, nTag)!=0 ) return 0; 94 + z = vtablog_skip_whitespace(z+nTag); 95 + if( z[0]!='=' ) return 0; 96 + return vtablog_skip_whitespace(z+1); 97 +} 98 + 99 +/* Decode a parameter that requires a dequoted string. 100 +** 101 +** Return non-zero on an error. 102 +*/ 103 +static int vtablog_string_parameter( 104 + char **pzErr, /* Leave the error message here, if there is one */ 105 + const char *zParam, /* Parameter we are checking for */ 106 + const char *zArg, /* Raw text of the virtual table argment */ 107 + char **pzVal /* Write the dequoted string value here */ 108 +){ 109 + const char *zValue; 110 + zValue = vtablog_parameter(zParam,(int)strlen(zParam),zArg); 111 + if( zValue==0 ) return 0; 112 + if( *pzVal ){ 113 + *pzErr = sqlite3_mprintf("more than one '%s' parameter", zParam); 114 + return 1; 115 + } 116 + *pzVal = sqlite3_mprintf("%s", zValue); 117 + if( *pzVal==0 ){ 118 + *pzErr = sqlite3_mprintf("out of memory"); 119 + return 1; 120 + } 121 + vtablog_trim_whitespace(*pzVal); 122 + vtablog_dequote(*pzVal); 123 + return 0; 124 +} 125 + 126 +#if 0 /* not used - yet */ 127 +/* Return 0 if the argument is false and 1 if it is true. Return -1 if 128 +** we cannot really tell. 129 +*/ 130 +static int vtablog_boolean(const char *z){ 131 + if( sqlite3_stricmp("yes",z)==0 132 + || sqlite3_stricmp("on",z)==0 133 + || sqlite3_stricmp("true",z)==0 134 + || (z[0]=='1' && z[1]==0) 135 + ){ 136 + return 1; 137 + } 138 + if( sqlite3_stricmp("no",z)==0 139 + || sqlite3_stricmp("off",z)==0 140 + || sqlite3_stricmp("false",z)==0 141 + || (z[0]=='0' && z[1]==0) 142 + ){ 143 + return 0; 144 + } 145 + return -1; 146 +} 147 +#endif 148 + 149 +/* 150 +** The vtablogConnect() method is invoked to create a new 151 +** vtablog_vtab that describes the vtablog virtual table. 152 +** 153 +** Think of this routine as the constructor for vtablog_vtab objects. 154 +** 155 +** All this routine needs to do is: 156 +** 157 +** (1) Allocate the vtablog_vtab object and initialize all fields. 158 +** 159 +** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the 160 +** result set of queries against vtablog will look like. 161 +*/ 162 +static int vtablogConnectCreate( 163 + sqlite3 *db, 164 + void *pAux, 165 + int argc, const char *const*argv, 166 + sqlite3_vtab **ppVtab, 167 + char **pzErr, 168 + int isCreate 169 +){ 170 + static int nInst = 0; 171 + vtablog_vtab *pNew; 172 + int i; 173 + int rc; 174 + int iInst = ++nInst; 175 + char *zSchema = 0; 176 + char *zNRow = 0; 177 + 178 + printf("vtablog%s(tab=%d):\n", isCreate ? "Create" : "Connect", iInst); 179 + printf(" argc=%d\n", argc); 180 + for(i=0; i<argc; i++){ 181 + printf(" argv[%d] = ", i); 182 + if( argv[i] ){ 183 + printf("[%s]\n", argv[i]); 184 + }else{ 185 + printf("NULL\n"); 186 + } 187 + } 188 + 189 + for(i=3; i<argc; i++){ 190 + const char *z = argv[i]; 191 + if( vtablog_string_parameter(pzErr, "schema", z, &zSchema) ){ 192 + return SQLITE_ERROR; 193 + } 194 + if( vtablog_string_parameter(pzErr, "rows", z, &zNRow) ){ 195 + return SQLITE_ERROR; 196 + } 197 + } 198 + 199 + if( zSchema==0 ){ 200 + *pzErr = sqlite3_mprintf("no schema defined"); 201 + return SQLITE_ERROR; 202 + } 203 + rc = sqlite3_declare_vtab(db, zSchema); 204 + if( rc==SQLITE_OK ){ 205 + pNew = sqlite3_malloc( sizeof(*pNew) ); 206 + *ppVtab = (sqlite3_vtab*)pNew; 207 + if( pNew==0 ) return SQLITE_NOMEM; 208 + memset(pNew, 0, sizeof(*pNew)); 209 + pNew->nRow = 10; 210 + if( zNRow ) pNew->nRow = atoi(zNRow); 211 + pNew->iInst = iInst; 212 + } 213 + return rc; 214 +} 215 +static int vtablogCreate( 216 + sqlite3 *db, 217 + void *pAux, 218 + int argc, const char *const*argv, 219 + sqlite3_vtab **ppVtab, 220 + char **pzErr 221 +){ 222 + return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,1); 223 +} 224 +static int vtablogConnect( 225 + sqlite3 *db, 226 + void *pAux, 227 + int argc, const char *const*argv, 228 + sqlite3_vtab **ppVtab, 229 + char **pzErr 230 +){ 231 + return vtablogConnectCreate(db,pAux,argc,argv,ppVtab,pzErr,0); 232 +} 233 + 234 + 235 +/* 236 +** This method is the destructor for vtablog_cursor objects. 237 +*/ 238 +static int vtablogDisconnect(sqlite3_vtab *pVtab){ 239 + vtablog_vtab *pTab = (vtablog_vtab*)pVtab; 240 + printf("vtablogDisconnect(%d)\n", pTab->iInst); 241 + sqlite3_free(pVtab); 242 + return SQLITE_OK; 243 +} 244 + 245 +/* 246 +** This method is the destructor for vtablog_cursor objects. 247 +*/ 248 +static int vtablogDestroy(sqlite3_vtab *pVtab){ 249 + vtablog_vtab *pTab = (vtablog_vtab*)pVtab; 250 + printf("vtablogDestroy(%d)\n", pTab->iInst); 251 + sqlite3_free(pVtab); 252 + return SQLITE_OK; 253 +} 254 + 255 +/* 256 +** Constructor for a new vtablog_cursor object. 257 +*/ 258 +static int vtablogOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ 259 + vtablog_vtab *pTab = (vtablog_vtab*)p; 260 + vtablog_cursor *pCur; 261 + printf("vtablogOpen(tab=%d, cursor=%d)\n", pTab->iInst, ++pTab->nCursor); 262 + pCur = sqlite3_malloc( sizeof(*pCur) ); 263 + if( pCur==0 ) return SQLITE_NOMEM; 264 + memset(pCur, 0, sizeof(*pCur)); 265 + pCur->iCursor = pTab->nCursor; 266 + *ppCursor = &pCur->base; 267 + return SQLITE_OK; 268 +} 269 + 270 +/* 271 +** Destructor for a vtablog_cursor. 272 +*/ 273 +static int vtablogClose(sqlite3_vtab_cursor *cur){ 274 + vtablog_cursor *pCur = (vtablog_cursor*)cur; 275 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 276 + printf("vtablogClose(tab=%d, cursor=%d)\n", pTab->iInst, pCur->iCursor); 277 + sqlite3_free(cur); 278 + return SQLITE_OK; 279 +} 280 + 281 + 282 +/* 283 +** Advance a vtablog_cursor to its next row of output. 284 +*/ 285 +static int vtablogNext(sqlite3_vtab_cursor *cur){ 286 + vtablog_cursor *pCur = (vtablog_cursor*)cur; 287 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 288 + printf("vtablogNext(tab=%d, cursor=%d) rowid %d -> %d\n", 289 + pTab->iInst, pCur->iCursor, (int)pCur->iRowid, (int)pCur->iRowid+1); 290 + pCur->iRowid++; 291 + return SQLITE_OK; 292 +} 293 + 294 +/* 295 +** Return values of columns for the row at which the vtablog_cursor 296 +** is currently pointing. 297 +*/ 298 +static int vtablogColumn( 299 + sqlite3_vtab_cursor *cur, /* The cursor */ 300 + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ 301 + int i /* Which column to return */ 302 +){ 303 + vtablog_cursor *pCur = (vtablog_cursor*)cur; 304 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 305 + char zVal[50]; 306 + 307 + if( i<26 ){ 308 + sqlite3_snprintf(sizeof(zVal),zVal,"%c%d", 309 + "abcdefghijklmnopqrstuvwyz"[i], pCur->iRowid); 310 + }else{ 311 + sqlite3_snprintf(sizeof(zVal),zVal,"{%d}%d", i, pCur->iRowid); 312 + } 313 + printf("vtablogColumn(tab=%d, cursor=%d, i=%d): [%s]\n", 314 + pTab->iInst, pCur->iCursor, i, zVal); 315 + sqlite3_result_text(ctx, zVal, -1, SQLITE_TRANSIENT); 316 + return SQLITE_OK; 317 +} 318 + 319 +/* 320 +** Return the rowid for the current row. In this implementation, the 321 +** rowid is the same as the output value. 322 +*/ 323 +static int vtablogRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ 324 + vtablog_cursor *pCur = (vtablog_cursor*)cur; 325 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 326 + printf("vtablogRowid(tab=%d, cursor=%d): %d\n", 327 + pTab->iInst, pCur->iCursor, (int)pCur->iRowid); 328 + *pRowid = pCur->iRowid; 329 + return SQLITE_OK; 330 +} 331 + 332 +/* 333 +** Return TRUE if the cursor has been moved off of the last 334 +** row of output. 335 +*/ 336 +static int vtablogEof(sqlite3_vtab_cursor *cur){ 337 + vtablog_cursor *pCur = (vtablog_cursor*)cur; 338 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 339 + int rc = pCur->iRowid >= pTab->nRow; 340 + printf("vtablogEof(tab=%d, cursor=%d): %d\n", 341 + pTab->iInst, pCur->iCursor, rc); 342 + return rc; 343 +} 344 + 345 +/* 346 +** Output an sqlite3_value object's value as an SQL literal. 347 +*/ 348 +static void vtablogQuote(sqlite3_value *p){ 349 + char z[50]; 350 + switch( sqlite3_value_type(p) ){ 351 + case SQLITE_NULL: { 352 + printf("NULL"); 353 + break; 354 + } 355 + case SQLITE_INTEGER: { 356 + sqlite3_snprintf(50,z,"%lld", sqlite3_value_int64(p)); 357 + printf("%s", z); 358 + break; 359 + } 360 + case SQLITE_FLOAT: { 361 + sqlite3_snprintf(50,z,"%!.20g", sqlite3_value_double(p)); 362 + printf("%s", z); 363 + break; 364 + } 365 + case SQLITE_BLOB: { 366 + int n = sqlite3_value_bytes(p); 367 + const unsigned char *z = (const unsigned char*)sqlite3_value_blob(p); 368 + int i; 369 + printf("x'"); 370 + for(i=0; i<n; i++) printf("%02x", z[i]); 371 + printf("'"); 372 + break; 373 + } 374 + case SQLITE_TEXT: { 375 + const char *z = (const char*)sqlite3_value_text(p); 376 + int i; 377 + char c; 378 + for(i=0; (c = z[i])!=0 && c!='\''; i++){} 379 + if( c==0 ){ 380 + printf("'%s'",z); 381 + }else{ 382 + printf("'"); 383 + while( *z ){ 384 + for(i=0; (c = z[i])!=0 && c!='\''; i++){} 385 + if( c=='\'' ) i++; 386 + if( i ){ 387 + printf("%.*s", i, z); 388 + z += i; 389 + } 390 + if( c=='\'' ){ 391 + printf("'"); 392 + continue; 393 + } 394 + if( c==0 ){ 395 + break; 396 + } 397 + z++; 398 + } 399 + printf("'"); 400 + } 401 + break; 402 + } 403 + } 404 +} 405 + 406 + 407 +/* 408 +** This method is called to "rewind" the vtablog_cursor object back 409 +** to the first row of output. This method is always called at least 410 +** once prior to any call to vtablogColumn() or vtablogRowid() or 411 +** vtablogEof(). 412 +*/ 413 +static int vtablogFilter( 414 + sqlite3_vtab_cursor *cur, 415 + int idxNum, const char *idxStr, 416 + int argc, sqlite3_value **argv 417 +){ 418 + vtablog_cursor *pCur = (vtablog_cursor *)cur; 419 + vtablog_vtab *pTab = (vtablog_vtab*)cur->pVtab; 420 + printf("vtablogFilter(tab=%d, cursor=%d):\n", pTab->iInst, pCur->iCursor); 421 + pCur->iRowid = 0; 422 + return SQLITE_OK; 423 +} 424 + 425 +/* 426 +** SQLite will invoke this method one or more times while planning a query 427 +** that uses the vtablog virtual table. This routine needs to create 428 +** a query plan for each invocation and compute an estimated cost for that 429 +** plan. 430 +*/ 431 +static int vtablogBestIndex( 432 + sqlite3_vtab *tab, 433 + sqlite3_index_info *pIdxInfo 434 +){ 435 + vtablog_vtab *pTab = (vtablog_vtab*)tab; 436 + printf("vtablogBestIndex(tab=%d):\n", pTab->iInst); 437 + pIdxInfo->estimatedCost = (double)500; 438 + pIdxInfo->estimatedRows = 500; 439 + return SQLITE_OK; 440 +} 441 + 442 +/* 443 +** SQLite invokes this method to INSERT, UPDATE, or DELETE content from 444 +** the table. 445 +** 446 +** This implementation does not actually make any changes to the table 447 +** content. It merely logs the fact that the method was invoked 448 +*/ 449 +static int vtablogUpdate( 450 + sqlite3_vtab *tab, 451 + int argc, 452 + sqlite3_value **argv, 453 + sqlite_int64 *pRowid 454 +){ 455 + vtablog_vtab *pTab = (vtablog_vtab*)tab; 456 + int i; 457 + printf("vtablogUpdate(tab=%d):\n", pTab->iInst); 458 + printf(" argc=%d\n", argc); 459 + for(i=0; i<argc; i++){ 460 + printf(" argv[%d]=", i); 461 + vtablogQuote(argv[i]); 462 + printf("\n"); 463 + } 464 + return SQLITE_OK; 465 +} 466 + 467 +/* 468 +** This following structure defines all the methods for the 469 +** vtablog virtual table. 470 +*/ 471 +static sqlite3_module vtablogModule = { 472 + 0, /* iVersion */ 473 + vtablogCreate, /* xCreate */ 474 + vtablogConnect, /* xConnect */ 475 + vtablogBestIndex, /* xBestIndex */ 476 + vtablogDisconnect, /* xDisconnect */ 477 + vtablogDestroy, /* xDestroy */ 478 + vtablogOpen, /* xOpen - open a cursor */ 479 + vtablogClose, /* xClose - close a cursor */ 480 + vtablogFilter, /* xFilter - configure scan constraints */ 481 + vtablogNext, /* xNext - advance a cursor */ 482 + vtablogEof, /* xEof - check for end of scan */ 483 + vtablogColumn, /* xColumn - read data */ 484 + vtablogRowid, /* xRowid - read data */ 485 + vtablogUpdate, /* xUpdate */ 486 + 0, /* xBegin */ 487 + 0, /* xSync */ 488 + 0, /* xCommit */ 489 + 0, /* xRollback */ 490 + 0, /* xFindMethod */ 491 + 0, /* xRename */ 492 + 0, /* xSavepoint */ 493 + 0, /* xRelease */ 494 + 0, /* xRollbackTo */ 495 +}; 496 + 497 +#ifdef _WIN32 498 +__declspec(dllexport) 499 +#endif 500 +int sqlite3_vtablog_init( 501 + sqlite3 *db, 502 + char **pzErrMsg, 503 + const sqlite3_api_routines *pApi 504 +){ 505 + int rc; 506 + SQLITE_EXTENSION_INIT2(pApi); 507 + rc = sqlite3_create_module(db, "vtablog", &vtablogModule, 0); 508 + return rc; 509 +}
Changes to ext/rtree/rtreeA.test.
224 224 sqlite3 db test.db 225 225 do_execsql_test rtreeA-7.100 { 226 226 UPDATE t1_node SET data=x'' WHERE rowid=1; 227 227 } {} 228 228 do_catchsql_test rtreeA-7.110 { 229 229 SELECT * FROM t1 WHERE x1>0 AND x1<100 AND x2>0 AND x2<100; 230 230 } {1 {undersize RTree blobs in "t1_node"}} 231 +do_test rtreeA-7.120 { 232 + sqlite3_extended_errcode db 233 +} {SQLITE_CORRUPT} 234 + 231 235 232 236 233 237 finish_test
Changes to src/build.c.
3879 3879 3880 3880 /* 3881 3881 ** Add an INDEXED BY or NOT INDEXED clause to the most recently added 3882 3882 ** element of the source-list passed as the second argument. 3883 3883 */ 3884 3884 void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ 3885 3885 assert( pIndexedBy!=0 ); 3886 - if( p && ALWAYS(p->nSrc>0) ){ 3887 - struct SrcList_item *pItem = &p->a[p->nSrc-1]; 3886 + if( p && pIndexedBy->n>0 ){ 3887 + struct SrcList_item *pItem; 3888 + assert( p->nSrc>0 ); 3889 + pItem = &p->a[p->nSrc-1]; 3888 3890 assert( pItem->fg.notIndexed==0 ); 3889 3891 assert( pItem->fg.isIndexedBy==0 ); 3890 3892 assert( pItem->fg.isTabFunc==0 ); 3891 3893 if( pIndexedBy->n==1 && !pIndexedBy->z ){ 3892 3894 /* A "NOT INDEXED" clause was supplied. See parse.y 3893 3895 ** construct "indexed_opt" for details. */ 3894 3896 pItem->fg.notIndexed = 1; 3895 3897 }else{ 3896 3898 pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); 3897 - pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0); 3899 + pItem->fg.isIndexedBy = 1; 3898 3900 } 3899 3901 } 3900 3902 } 3901 3903 3902 3904 /* 3903 3905 ** Add the list of function arguments to the SrcList entry for a 3904 3906 ** table-valued-function.
Changes to src/delete.c.
498 498 if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){ 499 499 assert( pPk!=0 || pTab->pSelect!=0 ); 500 500 sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); 501 501 VdbeCoverage(v); 502 502 } 503 503 }else if( pPk ){ 504 504 addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v); 505 - sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey); 505 + if( IsVirtual(pTab) ){ 506 + sqlite3VdbeAddOp3(v, OP_Column, iEphCur, 0, iKey); 507 + }else{ 508 + sqlite3VdbeAddOp2(v, OP_RowData, iEphCur, iKey); 509 + } 506 510 assert( nKey==0 ); /* OP_Found will use a composite key */ 507 511 }else{ 508 512 addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); 509 513 VdbeCoverage(v); 510 514 assert( nKey==1 ); 511 515 } 512 516
Changes to src/os_unix.c.
3880 3880 #if SQLITE_MAX_MMAP_SIZE>0 3881 3881 case SQLITE_FCNTL_MMAP_SIZE: { 3882 3882 i64 newLimit = *(i64*)pArg; 3883 3883 int rc = SQLITE_OK; 3884 3884 if( newLimit>sqlite3GlobalConfig.mxMmap ){ 3885 3885 newLimit = sqlite3GlobalConfig.mxMmap; 3886 3886 } 3887 + 3888 + /* The value of newLimit may be eventually cast to (size_t) and passed 3889 + ** to mmap(). Restrict its value to 2GB if (size_t) is not at least a 3890 + ** 64-bit type. */ 3891 + if( newLimit>0 && sizeof(size_t)<8 ){ 3892 + newLimit = (newLimit & 0x7FFFFFFF); 3893 + } 3894 + 3887 3895 *(i64*)pArg = pFile->mmapSizeMax; 3888 3896 if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ 3889 3897 pFile->mmapSizeMax = newLimit; 3890 3898 if( pFile->mmapSize>0 ){ 3891 3899 unixUnmapfile(pFile); 3892 3900 rc = unixMapfile(pFile, -1); 3893 3901 }
Changes to src/os_win.c.
3555 3555 #if SQLITE_MAX_MMAP_SIZE>0 3556 3556 case SQLITE_FCNTL_MMAP_SIZE: { 3557 3557 i64 newLimit = *(i64*)pArg; 3558 3558 int rc = SQLITE_OK; 3559 3559 if( newLimit>sqlite3GlobalConfig.mxMmap ){ 3560 3560 newLimit = sqlite3GlobalConfig.mxMmap; 3561 3561 } 3562 + 3563 + /* The value of newLimit may be eventually cast to (SIZE_T) and passed 3564 + ** to MapViewOfFile(). Restrict its value to 2GB if (SIZE_T) is not at 3565 + ** least a 64-bit type. */ 3566 + if( newLimit>0 && sizeof(SIZE_T)<8 ){ 3567 + newLimit = (newLimit & 0x7FFFFFFF); 3568 + } 3569 + 3562 3570 *(i64*)pArg = pFile->mmapSizeMax; 3563 3571 if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ 3564 3572 pFile->mmapSizeMax = newLimit; 3565 3573 if( pFile->mmapSize>0 ){ 3566 3574 winUnmapfile(pFile); 3567 3575 rc = winMapfile(pFile, -1); 3568 3576 }
Changes to src/pager.c.
124 124 ** The following two macros are used within the PAGERTRACE() macros above 125 125 ** to print out file-descriptors. 126 126 ** 127 127 ** PAGERID() takes a pointer to a Pager struct as its argument. The 128 128 ** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file 129 129 ** struct as its argument. 130 130 */ 131 -#define PAGERID(p) ((int)(p->fd)) 132 -#define FILEHANDLEID(fd) ((int)fd) 131 +#define PAGERID(p) (SQLITE_PTR_TO_INT(p->fd)) 132 +#define FILEHANDLEID(fd) (SQLITE_PTR_TO_INT(fd)) 133 133 134 134 /* 135 135 ** The Pager.eState variable stores the current 'state' of a pager. A 136 136 ** pager may be in any one of the seven states shown in the following 137 137 ** state diagram. 138 138 ** 139 139 ** OPEN <------+------+
Changes to src/printf.c.
778 778 if( p->mxAlloc==0 ){ 779 779 N = p->nAlloc - p->nChar - 1; 780 780 setStrAccumError(p, STRACCUM_TOOBIG); 781 781 return N; 782 782 }else{ 783 783 char *zOld = isMalloced(p) ? p->zText : 0; 784 784 i64 szNew = p->nChar; 785 - assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) ); 786 785 szNew += N + 1; 787 786 if( szNew+p->nChar<=p->mxAlloc ){ 788 787 /* Force exponential buffer size growth as long as it does not overflow, 789 788 ** to avoid having to call this routine too often */ 790 789 szNew += p->nChar; 791 790 } 792 791 if( szNew > p->mxAlloc ){ ................................................................................ 820 819 ** Append N copies of character c to the given string buffer. 821 820 */ 822 821 void sqlite3AppendChar(StrAccum *p, int N, char c){ 823 822 testcase( p->nChar + (i64)N > 0x7fffffff ); 824 823 if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ 825 824 return; 826 825 } 827 - assert( (p->zText==p->zBase)==!isMalloced(p) ); 828 826 while( (N--)>0 ) p->zText[p->nChar++] = c; 829 827 } 830 828 831 829 /* 832 830 ** The StrAccum "p" is not large enough to accept N new bytes of z[]. 833 831 ** So enlarge if first, then do the append. 834 832 ** ................................................................................ 838 836 */ 839 837 static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ 840 838 N = sqlite3StrAccumEnlarge(p, N); 841 839 if( N>0 ){ 842 840 memcpy(&p->zText[p->nChar], z, N); 843 841 p->nChar += N; 844 842 } 845 - assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) ); 846 843 } 847 844 848 845 /* 849 846 ** Append N bytes of text from z to the StrAccum object. Increase the 850 847 ** size of the memory allocation for StrAccum if necessary. 851 848 */ 852 849 void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ ................................................................................ 873 870 874 871 /* 875 872 ** Finish off a string by making sure it is zero-terminated. 876 873 ** Return a pointer to the resulting string. Return a NULL 877 874 ** pointer if any kind of error was encountered. 878 875 */ 879 876 static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){ 877 + char *zText; 880 878 assert( p->mxAlloc>0 && !isMalloced(p) ); 881 - p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); 882 - if( p->zText ){ 883 - memcpy(p->zText, p->zBase, p->nChar+1); 879 + zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); 880 + if( zText ){ 881 + memcpy(zText, p->zText, p->nChar+1); 884 882 p->printfFlags |= SQLITE_PRINTF_MALLOCED; 885 883 }else{ 886 884 setStrAccumError(p, STRACCUM_NOMEM); 887 885 } 888 - return p->zText; 886 + p->zText = zText; 887 + return zText; 889 888 } 890 889 char *sqlite3StrAccumFinish(StrAccum *p){ 891 890 if( p->zText ){ 892 - assert( (p->zText==p->zBase)==!isMalloced(p) ); 893 891 p->zText[p->nChar] = 0; 894 892 if( p->mxAlloc>0 && !isMalloced(p) ){ 895 893 return strAccumFinishRealloc(p); 896 894 } 897 895 } 898 896 return p->zText; 899 897 } 900 898 901 899 /* 902 900 ** Reset an StrAccum string. Reclaim all malloced memory. 903 901 */ 904 902 void sqlite3StrAccumReset(StrAccum *p){ 905 - assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) ); 906 903 if( isMalloced(p) ){ 907 904 sqlite3DbFree(p->db, p->zText); 908 905 p->printfFlags &= ~SQLITE_PRINTF_MALLOCED; 909 906 } 910 907 p->zText = 0; 911 908 } 912 909 ................................................................................ 921 918 ** is malloced. 922 919 ** n: Size of zBase in bytes. If total space requirements never exceed 923 920 ** n then no memory allocations ever occur. 924 921 ** mx: Maximum number of bytes to accumulate. If mx==0 then no memory 925 922 ** allocations will ever occur. 926 923 */ 927 924 void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ 928 - p->zText = p->zBase = zBase; 925 + p->zText = zBase; 929 926 p->db = db; 930 - p->nChar = 0; 931 927 p->nAlloc = n; 932 928 p->mxAlloc = mx; 929 + p->nChar = 0; 933 930 p->accError = 0; 934 931 p->printfFlags = 0; 935 932 } 936 933 937 934 /* 938 935 ** Print into memory obtained from sqliteMalloc(). Use the internal 939 936 ** %-conversion extensions.
Changes to src/select.c.
1706 1706 /* If the column contains an "AS <name>" phrase, use <name> as the name */ 1707 1707 }else{ 1708 1708 Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr); 1709 1709 while( pColExpr->op==TK_DOT ){ 1710 1710 pColExpr = pColExpr->pRight; 1711 1711 assert( pColExpr!=0 ); 1712 1712 } 1713 - if( pColExpr->op==TK_COLUMN && pColExpr->pTab!=0 ){ 1713 + if( (pColExpr->op==TK_COLUMN || pColExpr->op==TK_AGG_COLUMN) 1714 + && pColExpr->pTab!=0 1715 + ){ 1714 1716 /* For columns use the column name name */ 1715 1717 int iCol = pColExpr->iColumn; 1716 1718 Table *pTab = pColExpr->pTab; 1717 1719 if( iCol<0 ) iCol = pTab->iPKey; 1718 1720 zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid"; 1719 1721 }else if( pColExpr->op==TK_ID ){ 1720 1722 assert( !ExprHasProperty(pColExpr, EP_IntValue) );
Changes to src/sqlite3ext.h.
242 242 const char *(*errstr)(int); 243 243 int (*stmt_busy)(sqlite3_stmt*); 244 244 int (*stmt_readonly)(sqlite3_stmt*); 245 245 int (*stricmp)(const char*,const char*); 246 246 int (*uri_boolean)(const char*,const char*,int); 247 247 sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64); 248 248 const char *(*uri_parameter)(const char*,const char*); 249 - char *(*vsnprintf)(int,char*,const char*,va_list); 249 + char *(*xvsnprintf)(int,char*,const char*,va_list); 250 250 int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*); 251 251 /* Version 3.8.7 and later */ 252 252 int (*auto_extension)(void(*)(void)); 253 253 int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64, 254 254 void(*)(void*)); 255 255 int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64, 256 256 void(*)(void*),unsigned char); ................................................................................ 438 438 #define sqlite3_value_numeric_type sqlite3_api->value_numeric_type 439 439 #define sqlite3_value_text sqlite3_api->value_text 440 440 #define sqlite3_value_text16 sqlite3_api->value_text16 441 441 #define sqlite3_value_text16be sqlite3_api->value_text16be 442 442 #define sqlite3_value_text16le sqlite3_api->value_text16le 443 443 #define sqlite3_value_type sqlite3_api->value_type 444 444 #define sqlite3_vmprintf sqlite3_api->vmprintf 445 -#define sqlite3_vsnprintf sqlite3_api->vsnprintf 445 +#define sqlite3_vsnprintf sqlite3_api->xvsnprintf 446 446 #define sqlite3_overload_function sqlite3_api->overload_function 447 447 #define sqlite3_prepare_v2 sqlite3_api->prepare_v2 448 448 #define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 449 449 #define sqlite3_clear_bindings sqlite3_api->clear_bindings 450 450 #define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob 451 451 #define sqlite3_blob_bytes sqlite3_api->blob_bytes 452 452 #define sqlite3_blob_close sqlite3_api->blob_close ................................................................................ 514 514 #define sqlite3_errstr sqlite3_api->errstr 515 515 #define sqlite3_stmt_busy sqlite3_api->stmt_busy 516 516 #define sqlite3_stmt_readonly sqlite3_api->stmt_readonly 517 517 #define sqlite3_stricmp sqlite3_api->stricmp 518 518 #define sqlite3_uri_boolean sqlite3_api->uri_boolean 519 519 #define sqlite3_uri_int64 sqlite3_api->uri_int64 520 520 #define sqlite3_uri_parameter sqlite3_api->uri_parameter 521 -#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf 521 +#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf 522 522 #define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2 523 523 /* Version 3.8.7 and later */ 524 524 #define sqlite3_auto_extension sqlite3_api->auto_extension 525 525 #define sqlite3_bind_blob64 sqlite3_api->bind_blob64 526 526 #define sqlite3_bind_text64 sqlite3_api->bind_text64 527 527 #define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension 528 528 #define sqlite3_load_extension sqlite3_api->load_extension
Changes to src/sqliteInt.h.
3225 3225 3226 3226 /* 3227 3227 ** An objected used to accumulate the text of a string where we 3228 3228 ** do not necessarily know how big the string will be in the end. 3229 3229 */ 3230 3230 struct StrAccum { 3231 3231 sqlite3 *db; /* Optional database for lookaside. Can be NULL */ 3232 - char *zBase; /* A base allocation. Not from malloc. */ 3233 3232 char *zText; /* The string collected so far */ 3234 - u32 nChar; /* Length of the string so far */ 3235 3233 u32 nAlloc; /* Amount of space allocated in zText */ 3236 3234 u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */ 3235 + u32 nChar; /* Length of the string so far */ 3237 3236 u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */ 3238 3237 u8 printfFlags; /* SQLITE_PRINTF flags below */ 3239 3238 }; 3240 3239 #define STRACCUM_NOMEM 1 3241 3240 #define STRACCUM_TOOBIG 2 3242 3241 #define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */ 3243 3242 #define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
Changes to src/test_tclvar.c.
11 11 ************************************************************************* 12 12 ** Code for testing the virtual table interfaces. This code 13 13 ** is not included in the SQLite library. It is used for automated 14 14 ** testing of the SQLite library. 15 15 ** 16 16 ** The emphasis of this file is a virtual table that provides 17 17 ** access to TCL variables. 18 +** 19 +** The TCLVAR eponymous virtual table has a schema like this: 20 +** 21 +** CREATE TABLE tclvar( 22 +** name TEXT, -- base name of the variable: "x" in "$x(y)" 23 +** arrayname TEXT, -- array index name: "y" in "$x(y)" 24 +** value TEXT, -- the value of the variable 25 +** fullname TEXT, -- the full name of the variable 26 +** PRIMARY KEY(fullname) 27 +** ) WITHOUT ROWID; 28 +** 29 +** DELETE, INSERT, and UPDATE operations use the "fullname" field to 30 +** determine the variable to be modified. Changing "value" to NULL 31 +** deletes the variable. 32 +** 33 +** For SELECT operations, the "name" and "arrayname" fields will always 34 +** match the "fullname" field. For DELETE, INSERT, and UPDATE, the 35 +** "name" and "arrayname" fields are ignored and the variable is modified 36 +** according to "fullname" and "value" only. 18 37 */ 19 38 #include "sqliteInt.h" 20 39 #if defined(INCLUDE_SQLITE_TCL_H) 21 40 # include "sqlite_tcl.h" 22 41 #else 23 42 # include "tcl.h" 24 43 #endif ................................................................................ 63 82 void *pAux, 64 83 int argc, const char *const*argv, 65 84 sqlite3_vtab **ppVtab, 66 85 char **pzErr 67 86 ){ 68 87 tclvar_vtab *pVtab; 69 88 static const char zSchema[] = 70 - "CREATE TABLE whatever(name TEXT, arrayname TEXT, value TEXT)"; 89 + "CREATE TABLE x(" 90 + " name TEXT," /* Base name */ 91 + " arrayname TEXT," /* Array index */ 92 + " value TEXT," /* Value */ 93 + " fullname TEXT PRIMARY KEY" /* base(index) name */ 94 + ") WITHOUT ROWID"; 71 95 pVtab = sqlite3MallocZero( sizeof(*pVtab) ); 72 96 if( pVtab==0 ) return SQLITE_NOMEM; 73 97 *ppVtab = &pVtab->base; 74 98 pVtab->interp = (Tcl_Interp *)pAux; 75 99 sqlite3_declare_vtab(db, zSchema); 76 100 return SQLITE_OK; 77 101 } ................................................................................ 247 271 break; 248 272 } 249 273 case 2: { 250 274 Tcl_Obj *pVal = Tcl_GetVar2Ex(interp, z1, *z2?z2:0, TCL_GLOBAL_ONLY); 251 275 sqlite3_result_text(ctx, Tcl_GetString(pVal), -1, SQLITE_TRANSIENT); 252 276 break; 253 277 } 278 + case 3: { 279 + char *z3; 280 + if( p2 ){ 281 + z3 = sqlite3_mprintf("%s(%s)", z1, z2); 282 + sqlite3_result_text(ctx, z3, -1, sqlite3_free); 283 + }else{ 284 + sqlite3_result_text(ctx, z1, -1, SQLITE_TRANSIENT); 285 + } 286 + break; 287 + } 254 288 } 255 289 return SQLITE_OK; 256 290 } 257 291 258 292 static int tclvarRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ 259 293 *pRowid = 0; 260 294 return SQLITE_OK; ................................................................................ 371 405 } 372 406 } 373 407 pIdxInfo->idxStr = zStr; 374 408 pIdxInfo->needToFreeIdxStr = 1; 375 409 376 410 return SQLITE_OK; 377 411 } 412 + 413 +/* 414 +** Invoked for any UPDATE, INSERT, or DELETE against a tclvar table 415 +*/ 416 +static int tclvarUpdate( 417 + sqlite3_vtab *tab, 418 + int argc, 419 + sqlite3_value **argv, 420 + sqlite_int64 *pRowid 421 +){ 422 + tclvar_vtab *pTab = (tclvar_vtab*)tab; 423 + if( argc==1 ){ 424 + /* A DELETE operation. The variable to be deleted is stored in argv[0] */ 425 + const char *zVar = (const char*)sqlite3_value_text(argv[0]); 426 + Tcl_UnsetVar(pTab->interp, zVar, TCL_GLOBAL_ONLY); 427 + return SQLITE_OK; 428 + } 429 + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ 430 + /* An INSERT operation */ 431 + const char *zValue = (const char*)sqlite3_value_text(argv[4]); 432 + const char *zName; 433 + if( sqlite3_value_type(argv[5])!=SQLITE_TEXT ){ 434 + tab->zErrMsg = sqlite3_mprintf("the 'fullname' column must be TEXT"); 435 + return SQLITE_ERROR; 436 + } 437 + zName = (const char*)sqlite3_value_text(argv[5]); 438 + if( zValue ){ 439 + Tcl_SetVar(pTab->interp, zName, zValue, TCL_GLOBAL_ONLY); 440 + }else{ 441 + Tcl_UnsetVar(pTab->interp, zName, TCL_GLOBAL_ONLY); 442 + } 443 + return SQLITE_OK; 444 + } 445 + if( sqlite3_value_type(argv[0])==SQLITE_TEXT 446 + && sqlite3_value_type(argv[1])==SQLITE_TEXT 447 + ){ 448 + /* An UPDATE operation */ 449 + const char *zOldName = (const char*)sqlite3_value_text(argv[0]); 450 + const char *zNewName = (const char*)sqlite3_value_text(argv[1]); 451 + const char *zValue = (const char*)sqlite3_value_text(argv[4]); 452 + 453 + if( strcmp(zOldName, zNewName)!=0 || zValue==0 ){ 454 + Tcl_UnsetVar(pTab->interp, zOldName, TCL_GLOBAL_ONLY); 455 + } 456 + if( zValue!=0 ){ 457 + Tcl_SetVar(pTab->interp, zNewName, zValue, TCL_GLOBAL_ONLY); 458 + } 459 + return SQLITE_OK; 460 + } 461 + tab->zErrMsg = sqlite3_mprintf("prohibited TCL variable change"); 462 + return SQLITE_ERROR; 463 +} 378 464 379 465 /* 380 466 ** A virtual table module that provides read-only access to a 381 467 ** Tcl global variable namespace. 382 468 */ 383 469 static sqlite3_module tclvarModule = { 384 470 0, /* iVersion */ ................................................................................ 390 476 tclvarOpen, /* xOpen - open a cursor */ 391 477 tclvarClose, /* xClose - close a cursor */ 392 478 tclvarFilter, /* xFilter - configure scan constraints */ 393 479 tclvarNext, /* xNext - advance a cursor */ 394 480 tclvarEof, /* xEof - check for end of scan */ 395 481 tclvarColumn, /* xColumn - read data */ 396 482 tclvarRowid, /* xRowid - read data */ 397 - 0, /* xUpdate */ 483 + tclvarUpdate, /* xUpdate */ 398 484 0, /* xBegin */ 399 485 0, /* xSync */ 400 486 0, /* xCommit */ 401 487 0, /* xRollback */ 402 488 0, /* xFindMethod */ 403 489 0, /* xRename */ 404 490 };
Changes to src/update.c.
799 799 regRowid = ++pParse->nMem; 800 800 801 801 /* Start scanning the virtual table */ 802 802 pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0); 803 803 if( pWInfo==0 ) return; 804 804 805 805 /* Populate the argument registers. */ 806 - sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); 807 - if( pRowid ){ 808 - sqlite3ExprCode(pParse, pRowid, regArg+1); 809 - }else{ 810 - sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); 811 - } 812 806 for(i=0; i<pTab->nCol; i++){ 813 807 if( aXRef[i]>=0 ){ 814 808 sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i); 815 809 }else{ 816 810 sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i); 817 811 } 818 812 } 813 + if( HasRowid(pTab) ){ 814 + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg); 815 + if( pRowid ){ 816 + sqlite3ExprCode(pParse, pRowid, regArg+1); 817 + }else{ 818 + sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1); 819 + } 820 + }else{ 821 + Index *pPk; /* PRIMARY KEY index */ 822 + i16 iPk; /* PRIMARY KEY column */ 823 + pPk = sqlite3PrimaryKeyIndex(pTab); 824 + assert( pPk!=0 ); 825 + assert( pPk->nKeyCol==1 ); 826 + iPk = pPk->aiColumn[0]; 827 + sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, iPk, regArg); 828 + sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1); 829 + } 819 830 820 831 bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy); 821 832 822 833 if( bOnePass ){ 823 834 /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded 824 835 ** above. Also, if this is a top-level parse (not a trigger), clear the 825 836 ** multi-write flag so that the VM does not open a statement journal */
Changes to src/vtab.c.
640 640 sqlite3ErrorMsg(pParse, "no such module: %s", zModule); 641 641 rc = SQLITE_ERROR; 642 642 }else{ 643 643 char *zErr = 0; 644 644 rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); 645 645 if( rc!=SQLITE_OK ){ 646 646 sqlite3ErrorMsg(pParse, "%s", zErr); 647 + pParse->rc = rc; 647 648 } 648 649 sqlite3DbFree(db, zErr); 649 650 } 650 651 651 652 return rc; 652 653 } 653 654 /* ................................................................................ 768 769 Index *pIdx; 769 770 pTab->aCol = pNew->aCol; 770 771 pTab->nCol = pNew->nCol; 771 772 pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid); 772 773 pNew->nCol = 0; 773 774 pNew->aCol = 0; 774 775 assert( pTab->pIndex==0 ); 775 - if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){ 776 + assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 ); 777 + if( !HasRowid(pNew) 778 + && pCtx->pVTable->pMod->pModule->xUpdate!=0 779 + && sqlite3PrimaryKeyIndex(pNew)->nKeyCol!=1 780 + ){ 781 + /* WITHOUT ROWID virtual tables must either be read-only (xUpdate==0) 782 + ** or else must have a single-column PRIMARY KEY */ 776 783 rc = SQLITE_ERROR; 777 784 } 778 785 pIdx = pNew->pIndex; 779 786 if( pIdx ){ 780 787 assert( pIdx->pNext==0 ); 781 788 pTab->pIndex = pIdx; 782 789 pNew->pIndex = 0;
Changes to src/whereexpr.c.
190 190 static int isLikeOrGlob( 191 191 Parse *pParse, /* Parsing and code generating context */ 192 192 Expr *pExpr, /* Test this expression */ 193 193 Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ 194 194 int *pisComplete, /* True if the only wildcard is % in the last character */ 195 195 int *pnoCase /* True if uppercase is equivalent to lowercase */ 196 196 ){ 197 - const char *z = 0; /* String on RHS of LIKE operator */ 197 + const u8 *z = 0; /* String on RHS of LIKE operator */ 198 198 Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ 199 199 ExprList *pList; /* List of operands to the LIKE operator */ 200 200 int c; /* One character in z[] */ 201 201 int cnt; /* Number of non-wildcard prefix characters */ 202 202 char wc[4]; /* Wildcard characters */ 203 203 sqlite3 *db = pParse->db; /* Database connection */ 204 204 sqlite3_value *pVal = 0; ................................................................................ 217 217 pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr); 218 218 op = pRight->op; 219 219 if( op==TK_VARIABLE && (db->flags & SQLITE_EnableQPSG)==0 ){ 220 220 Vdbe *pReprepare = pParse->pReprepare; 221 221 int iCol = pRight->iColumn; 222 222 pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB); 223 223 if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ 224 - z = (char *)sqlite3_value_text(pVal); 224 + z = sqlite3_value_text(pVal); 225 225 } 226 226 sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); 227 227 assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); 228 228 }else if( op==TK_STRING ){ 229 - z = pRight->u.zToken; 229 + z = (u8*)pRight->u.zToken; 230 230 } 231 231 if( z ){ 232 232 233 233 /* If the RHS begins with a digit or a minus sign, then the LHS must 234 234 ** be an ordinary column (not a virtual table column) with TEXT affinity. 235 235 ** Otherwise the LHS might be numeric and "lhs >= rhs" would be false 236 236 ** even though "lhs LIKE rhs" is true. But if the RHS does not start ................................................................................ 247 247 } 248 248 } 249 249 250 250 /* Count the number of prefix characters prior to the first wildcard */ 251 251 cnt = 0; 252 252 while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ 253 253 cnt++; 254 - if( c==wc[3] && z[cnt]!=0 ){ 255 - if( z[cnt++]>0xc0 ) while( (z[cnt]&0xc0)==0x80 ){ cnt++; } 256 - } 254 + if( c==wc[3] && z[cnt]!=0 ) cnt++; 257 255 } 258 256 259 257 /* The optimization is possible only if (1) the pattern does not begin 260 258 ** with a wildcard and if (2) the non-wildcard prefix does not end with 261 259 ** an (illegal 0xff) character. The second condition is necessary so 262 260 ** that we can increment the prefix key to find an upper bound for the 263 261 ** range search. ................................................................................ 265 263 if( cnt!=0 && 255!=(u8)z[cnt-1] ){ 266 264 Expr *pPrefix; 267 265 268 266 /* A "complete" match if the pattern ends with "*" or "%" */ 269 267 *pisComplete = c==wc[0] && z[cnt+1]==0; 270 268 271 269 /* Get the pattern prefix. Remove all escapes from the prefix. */ 272 - pPrefix = sqlite3Expr(db, TK_STRING, z); 270 + pPrefix = sqlite3Expr(db, TK_STRING, (char*)z); 273 271 if( pPrefix ){ 274 272 int iFrom, iTo; 275 273 char *zNew = pPrefix->u.zToken; 276 274 zNew[cnt] = 0; 277 275 for(iFrom=iTo=0; iFrom<cnt; iFrom++){ 278 276 if( zNew[iFrom]==wc[3] ) iFrom++; 279 277 zNew[iTo++] = zNew[iFrom];
Added test/bigmmap.test.
1 +# 2017 August 07 2 +# 3 +# The author disclaims copyright to this source code. In place of 4 +# a legal notice, here is a blessing: 5 +# 6 +# May you do good and not evil. 7 +# May you find forgiveness for yourself and forgive others. 8 +# May you share freely, never taking more than you give. 9 +# 10 +#*********************************************************************** 11 +# This file implements regression tests for SQLite library. The 12 +# focus of this script testing the ability of SQLite to use mmap 13 +# to access files larger than 4GiB. 14 +# 15 + 16 +if {[file exists skip-big-file]} return 17 +if {$tcl_platform(os)=="Darwin"} return 18 + 19 +set testdir [file dirname $argv0] 20 +source $testdir/tester.tcl 21 +set testprefix bigmmap 22 + 23 +ifcapable !mmap { 24 + finish_test 25 + return 26 +} 27 + 28 +set mmap_limit 0 29 +db eval { 30 + SELECT compile_options AS x FROM pragma_compile_options 31 + WHERE x LIKE 'max_mmap_size=%' 32 +} { 33 + regexp {MAX_MMAP_SIZE=([0-9]*)} $x -> mmap_limit 34 +} 35 +if {$mmap_limit < [expr 8 * 1<<30]} { 36 + puts "Skipping bigmmap.test - requires SQLITE_MAX_MMAP_SIZE >= 8G" 37 + finish_test 38 + return 39 +} 40 + 41 + 42 +#------------------------------------------------------------------------- 43 +# Create the database file roughly 8GiB in size. Most pages are unused, 44 +# except that there is a table and index clustered around each 1GiB 45 +# boundary. 46 +# 47 +do_execsql_test 1.0 { 48 + PRAGMA page_size = 4096; 49 + CREATE TABLE t0(a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); 50 + WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 ) 51 + INSERT INTO t0 SELECT i, 't0', randomblob(800) FROM s; 52 +} 53 + 54 +for {set i 1} {$i < 8} {incr i} { 55 + fake_big_file [expr $i*1024] [get_pwd]/test.db 56 + hexio_write test.db 28 [format %.8x [expr ($i*1024*1024*1024/4096) - 5]] 57 + 58 + do_execsql_test 1.$i " 59 + CREATE TABLE t$i (a INTEGER PRIMARY KEY, b, c, UNIQUE(b, c)); 60 + WITH s(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM s LIMIT 100 ) 61 + INSERT INTO t$i SELECT i, 't$i', randomblob(800) FROM s; 62 + " 63 +} 64 + 65 +#------------------------------------------------------------------------- 66 +# Check that data can be retrieved from the db with a variety of 67 +# configured mmap size limits. 68 +# 69 +for {set i 0} {$i < 9} {incr i} { 70 + 71 + # Configure a memory mapping $i GB in size. 72 + # 73 + set val [expr $i*1024*1024*1024] 74 + execsql "PRAGMA main.mmap_size = $val" 75 + do_execsql_test 2.$i.0 { 76 + PRAGMA main.mmap_size 77 + } $val 78 + 79 + for {set t 0} {$t < 8} {incr t} { 80 + do_execsql_test 2.$i.$t.1 " 81 + SELECT count(*) FROM t$t; 82 + SELECT count(b || c) FROM t$t GROUP BY b; 83 + " {100 100} 84 + 85 + do_execsql_test 2.$i.$t.2 " 86 + SELECT * FROM t$t AS o WHERE 87 + NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c ) 88 + ORDER BY b, c; 89 + " {} 90 + 91 + do_eqp_test 2.$i.$t.3 " 92 + SELECT * FROM t$t AS o WHERE 93 + NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c ) 94 + ORDER BY b, c; 95 + " " 96 + 0 0 0 {SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1} 97 + 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1} 98 + 1 0 0 {SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)} 99 + " 100 + } 101 +} 102 + 103 +finish_test 104 +
Changes to test/colname.test.
373 373 } {a 1 n 3} 374 374 do_test colname-9.211 { 375 375 execsql2 {SELECT t1.a AS n, v3.a FROM t1 JOIN v3} 376 376 } {n 1 a 3} 377 377 do_test colname-9.210 { 378 378 execsql2 {SELECT t1.a, v3.a AS n FROM t1 JOIN v3} 379 379 } {a 1 n 3} 380 + 381 +# Make sure the quotation marks get removed from the column names 382 +# when constructing a new table from an aggregate SELECT. 383 +# Email from Juergen Palm on 2017-07-11. 384 +# 385 +do_execsql_test colname-10.100 { 386 + DROP TABLE IF EXISTS t1; 387 + CREATE TABLE t1("with space" TEXT); 388 + DROP TABLE IF EXISTS t2; 389 + CREATE TABLE t2 AS SELECT "with space" FROM t1; 390 + PRAGMA table_info(t2); 391 +} {0 {with space} TEXT 0 {} 0} 392 +do_execsql_test colname-10.110 { 393 + DROP TABLE IF EXISTS t3; 394 + CREATE TABLE t3 AS SELECT "with space" FROM t1 GROUP BY 1; 395 + PRAGMA table_info(t3); 396 +} {0 {with space} TEXT 0 {} 0} 380 397 381 398 382 399 finish_test
Changes to test/csv01.test.
89 89 } {5 9} 90 90 91 91 # The rowid column is not visible on a WITHOUT ROWID virtual table 92 92 do_catchsql_test 3.2 { 93 93 SELECT rowid, a FROM t3; 94 94 } {1 {no such column: rowid}} 95 95 96 +# Multi-column WITHOUT ROWID virtual tables may not be writable. 96 97 do_catchsql_test 4.0 { 97 98 DROP TABLE t3; 98 99 CREATE VIRTUAL TABLE temp.t4 USING csv_wr( 99 100 data= 100 101 '1,2,3,4 101 102 5,6,7,8 102 103 9,10,11,12 103 -13,14,15,16 104 -', 104 +13,14,15,16', 105 105 columns=4, 106 106 schema= 107 - 'CREATE TABLE t3(a PRIMARY KEY,b TEXT,c TEXT,d TEXT) WITHOUT ROWID', 107 + 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID', 108 108 testflags=1 109 109 ); 110 110 } {1 {vtable constructor failed: t4}} 111 + 112 +# WITHOUT ROWID tables with a single-column PRIMARY KEY may be writable. 113 +do_catchsql_test 4.1 { 114 + DROP TABLE IF EXISTS t4; 115 + CREATE VIRTUAL TABLE temp.t4 USING csv_wr( 116 + data= 117 +'1,2,3,4 118 +5,6,7,8 119 +9,10,11,12 120 +13,14,15,16', 121 + columns=4, 122 + schema= 123 + 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(b)) WITHOUT ROWID', 124 + testflags=1 125 + ); 126 +} {0 {}} 127 + 128 +do_catchsql_test 4.2 { 129 + DROP TABLE IF EXISTS t5; 130 + CREATE VIRTUAL TABLE temp.t5 USING csv_wr( 131 + data= 132 + '1,2,3,4 133 + 5,6,7,8 134 + 9,10,11,12 135 + 13,14,15,16', 136 + columns=4, 137 + schema= 138 + 'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID', 139 + testflags=1 140 + ); 141 +} {1 {vtable constructor failed: t5}} 142 + 111 143 112 144 finish_test
Changes to test/swarmvtab.test.
234 234 'VALUES 235 235 ("test.db1", "t1", 1, 10), 236 236 ("test.db2", "t1", 11, 20) 237 237 ', 'fetch_db' 238 238 ); 239 239 } {} 240 240 241 -do_catchsql_test 3.3 { SELECT * FROM xyz } {1 {fetch_db error!}} 241 +do_catchsql_test 3.3.2 { SELECT * FROM xyz } {1 {fetch_db error!}} 242 242 243 243 244 244 245 245 finish_test 246 246
Changes to test/vtab2.test.
56 56 } {6} 57 57 58 58 register_tclvar_module [sqlite3_connection_pointer db] 59 59 do_test vtab2-2.1 { 60 60 set ::abc 123 61 61 execsql { 62 62 CREATE VIRTUAL TABLE vars USING tclvar; 63 - SELECT * FROM vars WHERE name='abc'; 63 + SELECT name, arrayname, value FROM vars WHERE name='abc'; 64 64 } 65 65 } [list abc "" 123] 66 66 do_test vtab2-2.2 { 67 67 set A(1) 1 68 68 set A(2) 4 69 69 set A(3) 9 70 70 execsql { 71 - SELECT * FROM vars WHERE name='A'; 71 + SELECT name, arrayname, value FROM vars WHERE name='A'; 72 72 } 73 73 } [list A 1 1 A 2 4 A 3 9] 74 74 unset -nocomplain result 75 75 unset -nocomplain var 76 76 set result {} 77 77 foreach var [lsort [info vars tcl_*]] { 78 78 catch {lappend result $var [set $var]}
Changes to test/vtabE.test.
35 35 set vtabE2(c) d 36 36 37 37 do_test vtabE-1 { 38 38 db eval { 39 39 CREATE VIRTUAL TABLE t1 USING tclvar; 40 40 CREATE VIRTUAL TABLE t2 USING tclvar; 41 41 CREATE TABLE t3(a INTEGER PRIMARY KEY, b); 42 - SELECT t1.*, t2.*, abs(t3.b + abs(t2.value + abs(t1.value))) 42 + SELECT t1.name, t1.arrayname, t1.value, 43 + t2.name, t2.arrayname, t2.value, 44 + abs(t3.b + abs(t2.value + abs(t1.value))) 43 45 FROM t1 LEFT JOIN t2 ON t2.name = t1.arrayname 44 46 LEFT JOIN t3 ON t3.a=t2.value 45 47 WHERE t1.name = 'vtabE' 46 48 ORDER BY t1.value, t2.value; 47 49 } 48 50 } {vtabE vtabE1 11 vtabE1 w x {} vtabE vtabE1 11 vtabE1 y z {} vtabE vtabE2 22 vtabE2 a b {} vtabE vtabE2 22 vtabE2 c d {}} 49 51 50 52 finish_test
Changes to test/vtabH.test.
51 51 52 52 #-------------------------------------------------------------------------- 53 53 54 54 register_tclvar_module db 55 55 set ::xyz 10 56 56 do_execsql_test 2.0 { 57 57 CREATE VIRTUAL TABLE vars USING tclvar; 58 - SELECT * FROM vars WHERE name = 'xyz'; 58 + SELECT name, arrayname, value FROM vars WHERE name = 'xyz'; 59 59 } {xyz {} 10} 60 60 61 61 set x1 aback 62 62 set x2 abaft 63 63 set x3 abandon 64 64 set x4 abandonint 65 65 set x5 babble
Added test/vtabJ.test.
1 +# 2017-08-10 2 +# 3 +# The author disclaims copyright to this source code. In place of 4 +# a legal notice, here is a blessing: 5 +# 6 +# May you do good and not evil. 7 +# May you find forgiveness for yourself and forgive others. 8 +# May you share freely, never taking more than you give. 9 +# 10 +#*********************************************************************** 11 +# This file implements tests of writing to WITHOUT ROWID virtual tables 12 +# using the tclvar eponymous virtual table. 13 +# 14 + 15 +set testdir [file dirname $argv0] 16 +source $testdir/tester.tcl 17 +set testprefix vtabJ 18 + 19 +ifcapable !vtab { 20 + finish_test 21 + return 22 +} 23 + 24 +register_tclvar_module db 25 + 26 +unset -nocomplain vtabJ 27 +do_test 100 { 28 + set vtabJ(1) this 29 + set vtabJ(two) is 30 + set vtabJ(3) {a test} 31 + db eval { 32 + SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; 33 + } 34 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(two) is} 35 + 36 +do_execsql_test 110 { 37 + INSERT INTO tclvar(fullname, value) 38 + VALUES('vtabJ(4)',4),('vtabJ(five)',555); 39 + SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; 40 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is} 41 +do_test 111 { 42 + set res {} 43 + foreach vname [lsort [array names vtabJ]] { 44 + lappend res vtabJ($vname) $vtabJ($vname) 45 + } 46 + set res 47 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(4) 4 vtabJ(five) 555 vtabJ(two) is} 48 + 49 +do_test 120 { 50 + db eval { 51 + INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',444); 52 + } 53 + set vtabJ(4) 54 +} {444} 55 + 56 +do_test 130 { 57 + db eval { 58 + INSERT INTO tclvar(fullname, value) VALUES('vtabJ(4)',NULL); 59 + } 60 + info exists vtabJ(4) 61 +} {0} 62 + 63 +do_test 140 { 64 + db eval { 65 + UPDATE tclvar SET value=55 WHERE fullname='vtabJ(five)'; 66 + } 67 + set vtabJ(five) 68 +} {55} 69 + 70 +do_test 150 { 71 + db eval { 72 + UPDATE tclvar SET fullname='vtabJ(5)' WHERE fullname='vtabJ(five)'; 73 + } 74 + set vtabJ(5) 75 +} {55} 76 +do_test 151 { 77 + info exists vtabJ(five) 78 +} {0} 79 +do_test 152 { 80 + set res {} 81 + foreach vname [lsort [array names vtabJ]] { 82 + lappend res vtabJ($vname) $vtabJ($vname) 83 + } 84 + set res 85 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55 vtabJ(two) is} 86 + 87 +do_execsql_test 160 { 88 + SELECT fullname FROM tclvar WHERE arrayname='two' 89 +} {vtabJ(two)} 90 +do_execsql_test 161 { 91 + DELETE FROM tclvar WHERE arrayname='two'; 92 + SELECT fullname, value FROM tclvar WHERE name='vtabJ' ORDER BY fullname; 93 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55} 94 +do_test 162 { 95 + set res {} 96 + foreach vname [lsort [array names vtabJ]] { 97 + lappend res vtabJ($vname) $vtabJ($vname) 98 + } 99 + set res 100 +} {vtabJ(1) this vtabJ(3) {a test} vtabJ(5) 55} 101 + 102 +# Try to trick the module into updating the same variable twice for a 103 +# single UPDATE statement. 104 +# 105 +do_execsql_test 171 { 106 + INSERT INTO tclvar(fullname, value) VALUES('xx', 'a'); 107 + SELECT name, value FROM tclvar where name = 'xx'; 108 +} {xx a} 109 +do_execsql_test 172 { 110 + UPDATE tclvar SET value = value || 't' 111 + WHERE name = 'xx' OR name = 'x'||'x'; 112 + SELECT name, value FROM tclvar where name = 'xx'; 113 +} {xx at} 114 +do_execsql_test 173 { 115 + UPDATE tclvar SET value = value || 't' 116 + WHERE name = 'xx' OR name BETWEEN 'xx' AND 'xx'; 117 + SELECT name, value FROM tclvar where name = 'xx'; 118 +} {xx att} 119 + 120 +do_execsql_test 181 { 121 + DELETE FROM tclvar WHERE name BETWEEN 'xx' AND 'xx' OR name='xx'; 122 + SELECT name, value FROM tclvar where name = 'xx'; 123 +} {} 124 + 125 + 126 +finish_test
Changes to tool/speed-check.sh.
143 143 else 144 144 ./speedtest1 speedtest1.db $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt 145 145 fi 146 146 size sqlite3.o | tee -a summary-$NAME.txt 147 147 wc sqlite3.c 148 148 if test $doCachegrind -eq 1; then 149 149 cg_anno.tcl cachegrind.out.* >cout-$NAME.txt 150 + echo '*****************************************************' >>cout-$NAME.txt 151 + sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>cout-$NAME.txt 150 152 fi 151 153 if test $doExplain -eq 1; then 152 154 ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt 153 155 fi 154 156 if test "$NAME" != "trunk"; then 155 157 fossil test-diff --tk -c 20 cout-trunk.txt cout-$NAME.txt 156 158 fi