Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Use N separate cursors when scanning an index with N columns to collect sqlite_stat4 data. This fixes a problem with collecting incorrect nEq values from multi-column indexes. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sqlite_stat4 |
Files: | files | file ages | folders |
SHA1: |
3a71afe67418ce00097cd9714c395fe9 |
User & Date: | dan 2013-08-05 18:00:56.397 |
Context
2013-08-05
| ||
19:04 | Modify the vdbe code generated by ANALYZE to use fewer memory cells and cursor slots. (check-in: 4a51cf289f user: dan tags: sqlite_stat4) | |
18:00 | Use N separate cursors when scanning an index with N columns to collect sqlite_stat4 data. This fixes a problem with collecting incorrect nEq values from multi-column indexes. (check-in: 3a71afe674 user: dan tags: sqlite_stat4) | |
05:34 | Fix a couple of problems in code related to sqlite_stat4. (check-in: badd24d987 user: dan tags: sqlite_stat4) | |
Changes
Changes to src/analyze.c.
︙ | ︙ | |||
387 388 389 390 391 392 393 | pSample->iRowid = rowid; pSample->iHash = h; pSample->isPSample = isPSample; pSample->nSumEq = nSumEq; for(i=0; i<p->nCol; i++){ pSample->anEq[i] = sqlite3_value_int64(aEq[i]); pSample->anLt[i] = sqlite3_value_int64(aLt[i]); | | > | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | pSample->iRowid = rowid; pSample->iHash = h; pSample->isPSample = isPSample; pSample->nSumEq = nSumEq; for(i=0; i<p->nCol; i++){ pSample->anEq[i] = sqlite3_value_int64(aEq[i]); pSample->anLt[i] = sqlite3_value_int64(aLt[i]); pSample->anDLt[i] = sqlite3_value_int64(aDLt[i])-1; assert( sqlite3_value_int64(aDLt[i])>0 ); } /* Find the new minimum */ if( p->nSample==p->mxSample ){ u32 iHash = 0; /* Hash corresponding to iMin/nSumEq entry */ i64 nMinEq = LARGEST_INT64; /* Smallest nSumEq seen so far */ assert( iMin = -1 ); |
︙ | ︙ | |||
560 561 562 563 564 565 566 | #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ return; } #endif | | > < | > > | | | < < < < < < > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > | > > > < < < < < < < < < < < < < < < < < < < | < | < < < < < > | < < | > > > | | < < < < | > > > > | > | < > | | < | | | > | > | > | > > > > > | | < | < | | | < | | < | < < < < < | < | < < < | < < < < < < < < | | > > < < | > | < < < < > | | < < < < < < < < < < < | | | | | | < < < | < < < < | | | | | < < > | < < < | < | < < | < | < | | | > < < < < < < < < < | < < < < < < | < < < < < | > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 | #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ return; } #endif /* Establish a read-lock on the table at the shared-cache level. ** Also open a read-only cursor on the table. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); iTabCur = pParse->nTab++; sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns indexed by pIdx */ KeyInfo *pKey; /* KeyInfo structure for pIdx */ int addrIfNot = 0; /* address of OP_IfNot */ int *aChngAddr; /* Array of jump instruction addresses */ int regRowid; /* Register for rowid of current row */ int regPrev; /* First in array of previous values */ int regDLte; /* First in array of nDlt registers */ int regLt; /* First in array of nLt registers */ int regEq; /* First in array of nEq registers */ int regCnt; /* Number of index entries */ int regEof; /* True once cursors are all at EOF */ int endOfScan; /* Label to jump to once scan is finished */ if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0; VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName)); nCol = pIdx->nColumn; aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol); if( aChngAddr==0 ) continue; pKey = sqlite3IndexKeyinfo(pParse, pIdx); /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); /* ** The following pseudo-code demonstrates the way the VM scans an index ** to call stat4_push() and collect the values for the sqlite_stat1 ** entry. The code below is for an index with 2 columns. The actual ** VM code generated may be for any number of columns. ** ** One cursor is opened for each column in the index (nCol). All cursors ** scan concurrently the index from start to end. All variables used in ** the pseudo-code are initialized to zero. ** ** Rewind csr(0) ** Rewind csr(1) ** ** next_0: ** regPrev(0) = csr(0)[0] ** regDLte(0) += 1 ** regLt(0) += regEq(0) ** regEq(0) = 0 ** do { ** regEq(0) += 1 ** Next csr(0) ** }while ( csr(0)[0] == regPrev(0) ) ** ** next_1: ** regPrev(1) = csr(1)[1] ** regDLte(1) += 1 ** regLt(1) += regEq(1) ** regEq(1) = 0 ** regRowid = csr(1)[rowid] // innermost cursor only ** do { ** regEq(1) += 1 ** regCnt += 1 // innermost cursor only ** Next csr(1) ** }while ( csr(1)[0..1] == regPrev(0..1) ) ** ** stat4_push(regRowid, regEq, regLt, regDLte); ** ** if( eof( csr(1) ) ) goto endOfScan ** if( csr(1)[0] != regPrev(0) ) goto next_0 ** goto next_1 ** ** endOfScan: ** // done! ** ** The last two lines above modify the contents of the regDLte array ** so that each element contains the number of distinct key prefixes ** of the corresponding length. As required to calculate the contents ** of the sqlite_stat1 entry. ** ** Currently, the last memory cell allocated (that with the largest ** integer identifier) is regStat4. Immediately following regStat4 ** we allocate the following: ** ** regRowid - 1 register ** regEq - nCol registers ** regLt - nCol registers ** regDLte - nCol registers ** regCnt - 1 register ** regPrev - nCol registers ** regEof - 1 register ** ** The regRowid, regEq, regLt and regDLte registers must be positioned in ** that order immediately following regStat4 so that they can be passed ** to the stat4_push() function. ** ** All of the above are initialized to contain integer value 0. */ regRowid = regStat4+1; /* Rowid argument */ regEq = regRowid+1; /* First in array of nEq value registers */ regLt = regEq+nCol; /* First in array of nLt value registers */ regDLte = regLt+nCol; /* First in array of nDLt value registers */ regCnt = regDLte+nCol; /* Row counter */ regPrev = regCnt+1; /* First in array of prev. value registers */ regEof = regPrev+nCol; /* True once last row read from index */ if( regEof+1>pParse->nMem ){ pParse->nMem = regPrev+nCol; } /* Open a read-only cursor for each column of the index. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); iIdxCur = pParse->nTab++; pParse->nTab += (nCol-1); for(i=0; i<nCol; i++){ int iMode = (i==0 ? P4_KEYINFO_HANDOFF : P4_KEYINFO); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur+i, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, iMode); VdbeComment((v, "%s", pIdx->zName)); } #ifdef SQLITE_ENABLE_STAT4 /* Invoke the stat4_init() function. The arguments are: ** ** * the number of rows in the index, ** * the number of columns in the index, ** * the recommended number of samples for the stat4 table. */ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+1); sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+2); sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT4_SAMPLES, regStat4+3); sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4); sqlite3VdbeChangeP4(v, -1, (char*)&stat4InitFuncdef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, 3); #endif /* SQLITE_ENABLE_STAT4 */ /* Initialize all the memory registers allocated above to 0. */ for(i=regRowid; i<=regEof; i++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, i); } /* Rewind all cursors open on the index. If the table is entry, this ** will cause control to jump to address endOfScan immediately. */ endOfScan = sqlite3VdbeMakeLabel(v); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur+i, endOfScan); } for(i=0; i<nCol; i++){ char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); int iCsr = iIdxCur+i; int iDo; int iNe; /* Jump here to exit do{...}while loop */ int j; int bInner = (i==(nCol-1)); /* True for innermost cursor */ /* Implementation of the following pseudo-code: ** ** regPrev(i) = csr(i)[i] ** regDLte(i) += 1 ** regLt(i) += regEq(i) ** regEq(i) = 0 ** regRowid = csr(i)[rowid] // innermost cursor only */ aChngAddr[i] = sqlite3VdbeAddOp3(v, OP_Column, iCsr, i, regPrev+i); VdbeComment((v, "regPrev(%d) = csr(%d)(%d)", i, i, i)); sqlite3VdbeAddOp2(v, OP_AddImm, regDLte+i, 1); VdbeComment((v, "regDLte(%d) += 1", i)); sqlite3VdbeAddOp3(v, OP_Add, regEq+i, regLt+i, regLt+i); VdbeComment((v, "regLt(%d) += regEq(%d)", i, i)); sqlite3VdbeAddOp2(v, OP_Integer, 0, regEq+i); VdbeComment((v, "regEq(%d) = 0", i)); if( bInner ) sqlite3VdbeAddOp2(v, OP_IdxRowid, iCsr, regRowid); /* This bit: ** ** do { ** regEq(i) += 1 ** regCnt += 1 // innermost cursor only ** Next csr(i) ** if( Eof csr(i) ){ ** regEof = 1 // innermost cursor only ** break ** } ** }while ( csr(i)[0..i] == regPrev(0..i) ) */ iDo = sqlite3VdbeAddOp2(v, OP_AddImm, regEq+i, 1); VdbeComment((v, "regEq(%d) += 1", i)); if( bInner ){ sqlite3VdbeAddOp2(v, OP_AddImm, regCnt, 1); VdbeComment((v, "regCnt += 1")); } sqlite3VdbeAddOp2(v, OP_Next, iCsr, sqlite3VdbeCurrentAddr(v)+2+bInner); if( bInner ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); iNe = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, iNe); for(j=0; j<=i; j++){ sqlite3VdbeAddOp3(v, OP_Column, iCsr, j, regCol); sqlite3VdbeAddOp4(v, OP_Ne, regCol, iNe, regPrev+j, pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); VdbeComment((v, "if( regPrev(%d) != csr(%d)(%d) )", j, i, j)); } sqlite3VdbeAddOp2(v, OP_Goto, 0, iDo); sqlite3VdbeResolveLabel(v, iNe); } /* Invoke stat4_push() */ sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp2); sqlite3VdbeChangeP4(v, -1, (char*)&stat4PushFuncdef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, 2 + 3*nCol); sqlite3VdbeAddOp2(v, OP_If, regEof, endOfScan); for(i=0; i<nCol-1; i++){ char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur+nCol-1, i, regCol); sqlite3VdbeAddOp3(v, OP_Ne, regCol, aChngAddr[i], regPrev+i); sqlite3VdbeChangeP4(v, -1, pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); } sqlite3VdbeAddOp2(v, OP_Goto, 0, aChngAddr[nCol-1]); sqlite3DbFree(db, aChngAddr); sqlite3VdbeResolveLabel(v, endOfScan); /* Close all the cursors */ for(i=0; i<nCol; i++){ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur+i); VdbeComment((v, "close index cursor %d", i)); } #ifdef SQLITE_ENABLE_STAT4 /* Add rows to the sqlite_stat4 table */ regLoop = regStat4+1; sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop); shortJump = sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1); |
︙ | ︙ | |||
854 855 856 857 858 859 860 | ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, regCnt, regStat1); jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regCnt); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1); | | | < | < > > > > | 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 | ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, regCnt, regStat1); jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regCnt); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1); sqlite3VdbeAddOp3(v, OP_Add, regCnt, regDLte+i, regTemp); sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1); sqlite3VdbeAddOp3(v, OP_Divide, regDLte+i, regTemp, regTemp); sqlite3VdbeAddOp1(v, OP_ToInt, regTemp); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1); } if( pIdx->pPartIdxWhere!=0 ) sqlite3VdbeJumpHere(v, jZeroRows); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pIdx->pPartIdxWhere==0 ) sqlite3VdbeJumpHere(v, jZeroRows); } /* Create a single sqlite_stat1 entry containing NULL as the index ** name and the row count as the content. */ if( pOnlyIdx==0 && needTableCnt ){ VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1); jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3VdbeJumpHere(v, jZeroRows); } sqlite3VdbeAddOp1(v, OP_Close, iTabCur); /* TODO: Not sure about this... */ if( pParse->nMem<regRec ) pParse->nMem = regRec; } /* ** Generate code that will cause the most recent index analysis to ** be loaded into internal hash tables where is can be used. |
︙ | ︙ |
Changes to src/test_func.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | */ #include "sqlite3.h" #include "tcl.h" #include <stdlib.h> #include <string.h> #include <assert.h> /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed. */ static void *testContextMalloc(sqlite3_context *context, int nByte){ | > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | */ #include "sqlite3.h" #include "tcl.h" #include <stdlib.h> #include <string.h> #include <assert.h> #include "sqliteInt.h" #include "vdbeInt.h" /* ** Allocate nByte bytes of space using sqlite3_malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed. */ static void *testContextMalloc(sqlite3_context *context, int nByte){ |
︙ | ︙ | |||
454 455 456 457 458 459 460 461 462 463 464 465 466 467 | zOut[14-i*2+1] = "0123456789abcdef"[v.x[i]&0xf]; } } zOut[16] = 0; sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); } static int registerTestFunctions(sqlite3 *db){ static const struct { char *zName; signed char nArg; unsigned char eTextRep; /* 1: UTF-16. 0: UTF-8 */ void (*xFunc)(sqlite3_context*,int,sqlite3_value **); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 | zOut[14-i*2+1] = "0123456789abcdef"[v.x[i]&0xf]; } } zOut[16] = 0; sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT); } /* ** tclcmd: test_decode(record) ** ** This function implements an SQL user-function that accepts a blob ** containing a formatted database record as its only argument. It returns ** a tcl list (type SQLITE_TEXT) containing each of the values stored ** in the record. */ static void test_decode( sqlite3_context *context, int argc, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); u8 *pRec; u8 *pEndHdr; /* Points to one byte past record header */ u8 *pHdr; /* Current point in record header */ u8 *pBody; /* Current point in record data */ u64 nHdr; /* Bytes in record header */ Tcl_Obj *pRet; /* Return value */ pRet = Tcl_NewObj(); Tcl_IncrRefCount(pRet); assert( argc==1 ); pRec = (u8*)sqlite3_value_blob(argv[0]); pHdr = pRec + sqlite3GetVarint(pRec, &nHdr); pBody = pEndHdr = &pRec[nHdr]; while( pHdr<pEndHdr ){ Tcl_Obj *pVal = 0; u64 iSerialType; Mem mem; memset(&mem, 0, sizeof(mem)); mem.db = db; mem.enc = SQLITE_UTF8; pHdr += sqlite3GetVarint(pHdr, &iSerialType); pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem); sqlite3VdbeMemStoreType(&mem); switch( sqlite3_value_type(&mem) ){ case SQLITE_TEXT: pVal = Tcl_NewStringObj((const char*)sqlite3_value_text(&mem), -1); break; case SQLITE_BLOB: { char hexdigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int n = sqlite3_value_bytes(&mem); u8 *z = (u8*)sqlite3_value_blob(&mem); int i; pVal = Tcl_NewStringObj("x'", -1); for(i=0; i<n; i++){ char hex[3]; hex[0] = hexdigit[((z[i] >> 4) & 0x0F)]; hex[1] = hexdigit[(z[i] & 0x0F)]; hex[2] = '\0'; Tcl_AppendStringsToObj(pVal, hex, 0); } Tcl_AppendStringsToObj(pVal, "'", 0); break; } case SQLITE_FLOAT: pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem)); break; case SQLITE_INTEGER: pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem)); break; case SQLITE_NULL: pVal = Tcl_NewStringObj("NULL", -1); break; default: assert( 0 ); } Tcl_ListObjAppendElement(0, pRet, pVal); if( mem.zMalloc ){ sqlite3DbFree(db, mem.zMalloc); } } sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); Tcl_DecrRefCount(pRet); } static int registerTestFunctions(sqlite3 *db){ static const struct { char *zName; signed char nArg; unsigned char eTextRep; /* 1: UTF-16. 0: UTF-8 */ void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
︙ | ︙ | |||
478 479 480 481 482 483 484 485 486 487 488 489 490 491 | { "test_auxdata", -1, SQLITE_UTF8, test_auxdata}, { "test_error", 1, SQLITE_UTF8, test_error}, { "test_error", 2, SQLITE_UTF8, test_error}, { "test_eval", 1, SQLITE_UTF8, test_eval}, { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, }; int i; for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0); } | > | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 | { "test_auxdata", -1, SQLITE_UTF8, test_auxdata}, { "test_error", 1, SQLITE_UTF8, test_error}, { "test_error", 2, SQLITE_UTF8, test_error}, { "test_eval", 1, SQLITE_UTF8, test_eval}, { "test_isolation", 2, SQLITE_UTF8, test_isolation}, { "test_counter", 1, SQLITE_UTF8, counterFunc}, { "real2hex", 1, SQLITE_UTF8, real2hex}, { "test_decode", 1, SQLITE_UTF8, test_decode}, }; int i; for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg, aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, 0, 0); } |
︙ | ︙ |
Changes to test/analyze9.test.
︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | do_test 1.0 { execsql { CREATE TABLE t1(a TEXT, b TEXT); } for {set i 0} {$i < 5} {incr i} { execsql {INSERT INTO t1 VALUES ('('||($i%10)||')', '('||($i%7)||')')} } execsql { CREATE INDEX i1 ON t1(a, b) } } {} do_execsql_test 1.1 { ANALYZE; } {} do_execsql_test 1.2 { SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4; } { t1 i1 {1 1} {0 0} {0 0} ...(0)(0) t1 i1 {1 1} {1 1} {1 1} ...(1)(1) t1 i1 {1 1} {2 2} {2 2} ...(2)(2) t1 i1 {1 1} {3 3} {3 3} ...(3)(3) t1 i1 {1 1} {4 4} {4 4} ...(4)(4) } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | do_test 1.0 { execsql { CREATE TABLE t1(a TEXT, b TEXT); } for {set i 0} {$i < 5} {incr i} { execsql {INSERT INTO t1 VALUES ('('||($i%10)||')', '('||($i%7)||')')} } execsql { CREATE INDEX i1 ON t1(a, b) } } {} do_execsql_test 1.1 { ANALYZE; } {} do_execsql_test 1.3 { SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4; } { t1 i1 {1 1} {0 0} {0 0} {(0) (0)} t1 i1 {1 1} {1 1} {1 1} {(1) (1)} t1 i1 {1 1} {2 2} {2 2} {(2) (2)} t1 i1 {1 1} {3 3} {3 3} {(3) (3)} t1 i1 {1 1} {4 4} {4 4} {(4) (4)} } do_execsql_test 1.2 { SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4; } { t1 i1 {1 1} {0 0} {0 0} ...(0)(0) t1 i1 {1 1} {1 1} {1 1} ...(1)(1) t1 i1 {1 1} {2 2} {2 2} ...(2)(2) t1 i1 {1 1} {3 3} {3 3} ...(3)(3) t1 i1 {1 1} {4 4} {4 4} ...(4)(4) } #------------------------------------------------------------------------- # This is really just to test SQL user function "test_decode". # reset_db do_execsql_test 2.1 { CREATE TABLE t1(a, b, c); INSERT INTO t1 VALUES('some text', 14, NULL); INSERT INTO t1 VALUES(22.0, NULL, x'656667'); CREATE INDEX i1 ON t1(a, b, c); ANALYZE; SELECT test_decode(sample) FROM sqlite_stat4; } { {22.0 NULL x'656667'} {{some text} 14 NULL} } #------------------------------------------------------------------------- # reset_db do_execsql_test 3.1 { CREATE TABLE t2(a, b); CREATE INDEX i2 ON t2(a, b); BEGIN; } do_test 3.2 { for {set i 0} {$i < 1000} {incr i} { set a [expr $i / 10] set b [expr int(rand() * 15.0)] execsql { INSERT INTO t2 VALUES($a, $b) } } execsql COMMIT } {} db func lindex lindex # Each value of "a" occurs exactly 10 times in the table. # do_execsql_test 3.3.1 { SELECT count(*) FROM t2 GROUP BY a; } [lrange [string repeat "10 " 100] 0 99] # The first element in the "nEq" list of all samples should therefore be 10. # do_execsql_test 3.3.2 { ANALYZE; SELECT lindex(nEq, 0) FROM sqlite_stat4; } [lrange [string repeat "10 " 100] 0 23] finish_test |