Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Have fts5 interpret column lists that begin with a "-" character as "match any column except" lists. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
e517545650631d1e8a7ee63c6646a8b1 |
User & Date: | dan 2016-08-09 19:26:57.822 |
Context
2016-08-09
| ||
19:48 | Minor update to the way fts5 column filters are parsed. (check-in: 14864f2b84 user: dan tags: trunk) | |
19:26 | Have fts5 interpret column lists that begin with a "-" character as "match any column except" lists. (check-in: e517545650 user: dan tags: trunk) | |
2016-08-08
| ||
13:40 | Version 3.14 (check-in: d5e9805702 user: drh tags: trunk, release, version-3.14.0) | |
Changes
Changes to ext/fts5/fts5Int.h.
︙ | ︙ | |||
733 734 735 736 737 738 739 740 741 742 743 744 745 746 | void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*); void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); /* ** End of interface to code in fts5_expr.c. **************************************************************************/ | > | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 | void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*); void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*); void sqlite3Fts5ParseNodeFree(Fts5ExprNode*); void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*); void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*); void sqlite3Fts5ParseColsetNegative(Fts5Parse*, int); void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p); void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*); /* ** End of interface to code in fts5_expr.c. **************************************************************************/ |
︙ | ︙ |
Changes to ext/fts5/fts5_expr.c.
︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | ** Parse context. */ struct Fts5Parse { Fts5Config *pConfig; char *zErr; int rc; int nPhrase; /* Size of apPhrase array */ Fts5ExprPhrase **apPhrase; /* Array of all phrases */ Fts5ExprNode *pExpr; /* Result of a successful parse */ }; void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); | > | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | ** Parse context. */ struct Fts5Parse { Fts5Config *pConfig; char *zErr; int rc; int nPhrase; /* Size of apPhrase array */ int bNegativeCollist; /* Column list being parsed started with - */ Fts5ExprPhrase **apPhrase; /* Array of all phrases */ Fts5ExprNode *pExpr; /* Result of a successful parse */ }; void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){ va_list ap; va_start(ap, zFmt); |
︙ | ︙ | |||
163 164 165 166 167 168 169 170 171 172 173 174 175 176 | case ')': tok = FTS5_RP; break; case '{': tok = FTS5_LCP; break; case '}': tok = FTS5_RCP; break; case ':': tok = FTS5_COLON; break; case ',': tok = FTS5_COMMA; break; case '+': tok = FTS5_PLUS; break; case '*': tok = FTS5_STAR; break; case '\0': tok = FTS5_EOF; break; case '"': { const char *z2; tok = FTS5_STRING; for(z2=&z[1]; 1; z2++){ | > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | case ')': tok = FTS5_RP; break; case '{': tok = FTS5_LCP; break; case '}': tok = FTS5_RCP; break; case ':': tok = FTS5_COLON; break; case ',': tok = FTS5_COMMA; break; case '+': tok = FTS5_PLUS; break; case '*': tok = FTS5_STAR; break; case '-': tok = FTS5_MINUS; break; case '\0': tok = FTS5_EOF; break; case '"': { const char *z2; tok = FTS5_STRING; for(z2=&z[1]; 1; z2++){ |
︙ | ︙ | |||
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 | /* Check that the array is in order and contains no duplicate entries. */ for(i=1; i<pNew->nCol; i++) assert( pNew->aiCol[i]>pNew->aiCol[i-1] ); #endif } return pNew; } Fts5Colset *sqlite3Fts5ParseColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ Fts5Colset *pColset, /* Existing colset object */ Fts5Token *p ){ Fts5Colset *pRet = 0; int iCol; char *z; /* Dequoted copy of token p */ z = sqlite3Fts5Strndup(&pParse->rc, p->p, p->n); if( pParse->rc==SQLITE_OK ){ Fts5Config *pConfig = pParse->pConfig; sqlite3Fts5Dequote(z); for(iCol=0; iCol<pConfig->nCol; iCol++){ if( 0==sqlite3_stricmp(pConfig->azCol[iCol], z) ) break; } if( iCol==pConfig->nCol ){ sqlite3Fts5ParseError(pParse, "no such column: %s", z); }else{ pRet = fts5ParseColset(pParse, pColset, iCol); } sqlite3_free(z); } if( pRet==0 ){ assert( pParse->rc!=SQLITE_OK ); sqlite3_free(pColset); } return pRet; } void sqlite3Fts5ParseSetColset( Fts5Parse *pParse, Fts5ExprNearset *pNear, Fts5Colset *pColset ){ if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 | /* Check that the array is in order and contains no duplicate entries. */ for(i=1; i<pNew->nCol; i++) assert( pNew->aiCol[i]>pNew->aiCol[i-1] ); #endif } return pNew; } /* ** The second argument passed to this function may be NULL, or it may be ** an existing Fts5Colset object. If it is passed NULL, this function ** returns a pointer to a new Fts5Colset object containing entries for ** all table columns except column iCol. If an OOM error occurs trying to ** allocate the Fts5Colset object, an error code is stored in pParse and ** NULL returned. ** ** If the second argument is not NULL, a copy of it is returned. Before ** returning, any entry for column iCol is removed. It is not an error ** if the Fts5Colset object does not contain an entry for column iCol ** when this function is called. */ static Fts5Colset *fts5ParseNegativeColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ Fts5Colset *p, /* Existing colset object */ int iCol /* New column to add to colset object */ ){ int i; Fts5Colset *pRet = p; if( pRet==0 ){ int nCol = pParse->pConfig->nCol; pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5Colset) + sizeof(int)*nCol ); if( pRet==0 ) return 0; pRet->nCol = nCol; for(i=0; i<nCol; i++){ pRet->aiCol[i] = i; } } for(i=0; i<pRet->nCol; i++){ if( pRet->aiCol[i]==iCol ){ int nByte = sizeof(int)*(pRet->nCol-i-1); if( nByte ){ memmove(&pRet->aiCol[i], &pRet->aiCol[i+1], nByte); } pRet->nCol--; break; } } return pRet; } Fts5Colset *sqlite3Fts5ParseColset( Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */ Fts5Colset *pColset, /* Existing colset object */ Fts5Token *p ){ Fts5Colset *pRet = 0; int iCol; char *z; /* Dequoted copy of token p */ z = sqlite3Fts5Strndup(&pParse->rc, p->p, p->n); if( pParse->rc==SQLITE_OK ){ Fts5Config *pConfig = pParse->pConfig; sqlite3Fts5Dequote(z); for(iCol=0; iCol<pConfig->nCol; iCol++){ if( 0==sqlite3_stricmp(pConfig->azCol[iCol], z) ) break; } if( iCol==pConfig->nCol ){ sqlite3Fts5ParseError(pParse, "no such column: %s", z); }else if( pParse->bNegativeCollist ){ pRet = fts5ParseNegativeColset(pParse, pColset, iCol); }else{ pRet = fts5ParseColset(pParse, pColset, iCol); } sqlite3_free(z); } if( pRet==0 ){ assert( pParse->rc!=SQLITE_OK ); sqlite3_free(pColset); } return pRet; } /* ** Set (bVal==1) or clear (bVal==0) the Fts5Parse.bNegativeCollist flag. ** ** The parser calls this function as it begins to parse a colset (Fts5Colset ** object) with bVal set to 1 if the colset begins with a "-" or 0 otherwise. */ void sqlite3Fts5ParseColsetNegative(Fts5Parse *pParse, int bVal){ pParse->bNegativeCollist = bVal; } void sqlite3Fts5ParseSetColset( Fts5Parse *pParse, Fts5ExprNearset *pNear, Fts5Colset *pColset ){ if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
︙ | ︙ |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 | ** Fts5Iter.poslist buffer and then set the output pointer to point ** to this buffer. */ fts5BufferZero(&pIter->poslist); fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist); pIter->base.pData = pIter->poslist.p; } } /* ** xSetOutputs callback used by detail=col when there is a column filter ** and there are 100 or more columns. Also called as a fallback from ** fts5IterSetOutputs_Col100 if the column-list spans more than one page. */ static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){ | > > > > > > > > | 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 | ** Fts5Iter.poslist buffer and then set the output pointer to point ** to this buffer. */ fts5BufferZero(&pIter->poslist); fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist); pIter->base.pData = pIter->poslist.p; } } /* ** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match ** against no columns at all). */ static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){ pIter->base.nData = 0; } /* ** xSetOutputs callback used by detail=col when there is a column filter ** and there are 100 or more columns. Also called as a fallback from ** fts5IterSetOutputs_Col100 if the column-list spans more than one page. */ static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){ |
︙ | ︙ | |||
3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 | if( pConfig->eDetail==FTS5_DETAIL_NONE ){ pIter->xSetOutputs = fts5IterSetOutputs_None; } else if( pIter->pColset==0 ){ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset; } else if( pConfig->eDetail==FTS5_DETAIL_FULL ){ pIter->xSetOutputs = fts5IterSetOutputs_Full; } else{ assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS ); | > > > > | 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 | if( pConfig->eDetail==FTS5_DETAIL_NONE ){ pIter->xSetOutputs = fts5IterSetOutputs_None; } else if( pIter->pColset==0 ){ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset; } else if( pIter->pColset->nCol==0 ){ pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset; } else if( pConfig->eDetail==FTS5_DETAIL_FULL ){ pIter->xSetOutputs = fts5IterSetOutputs_Full; } else{ assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS ); |
︙ | ︙ |
Changes to ext/fts5/fts5parse.y.
︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y); } %type colset {Fts5Colset*} %destructor colset { sqlite3_free($$); } %type colsetlist {Fts5Colset*} %destructor colsetlist { sqlite3_free($$); } colset(A) ::= LCP colsetlist(X) RCP. { A = X; } colset(A) ::= STRING(X). { A = sqlite3Fts5ParseColset(pParse, 0, &X); } colsetlist(A) ::= colsetlist(Y) STRING(X). { A = sqlite3Fts5ParseColset(pParse, Y, &X); } | > > > > > > | > > > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | A = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, Y); } %type colset {Fts5Colset*} %destructor colset { sqlite3_free($$); } %type colsetlist {Fts5Colset*} %destructor colsetlist { sqlite3_free($$); } %type minus_opt {int} colset(A) ::= LCP colsetlist(X) RCP. { A = X; } colset(A) ::= MINUS STRING(X). { sqlite3Fts5ParseColsetNegative(pParse, 1); A = sqlite3Fts5ParseColset(pParse, 0, &X); } colset(A) ::= STRING(X). { sqlite3Fts5ParseColsetNegative(pParse, 0); A = sqlite3Fts5ParseColset(pParse, 0, &X); } colsetlist(A) ::= colsetlist(Y) STRING(X). { A = sqlite3Fts5ParseColset(pParse, Y, &X); } colsetlist(A) ::= minus_opt(M) STRING(X). { sqlite3Fts5ParseColsetNegative(pParse, M); A = sqlite3Fts5ParseColset(pParse, 0, &X); } minus_opt(A) ::= MINUS. { A = 1; } minus_opt(A) ::= . { A = 0; } %type nearset {Fts5ExprNearset*} %type nearphrases {Fts5ExprNearset*} %destructor nearset { sqlite3Fts5ParseNearsetFree($$); } %destructor nearphrases { sqlite3Fts5ParseNearsetFree($$); } nearset(A) ::= phrase(X). { A = sqlite3Fts5ParseNearset(pParse, 0, X); } |
︙ | ︙ |
Added ext/fts5/test/fts5colset.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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 | # 2016 August 10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # This file implements regression tests for SQLite library. The # focus of this script is testing the FTS5 module. # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5colset # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test return } foreach_detail_mode $::testprefix { if {[detail_is_none]} continue do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(a, b, c, d, detail=%DETAIL%); INSERT INTO t1 VALUES('a', 'b', 'c', 'd'); -- 1 INSERT INTO t1 VALUES('d', 'a', 'b', 'c'); -- 2 INSERT INTO t1 VALUES('c', 'd', 'a', 'b'); -- 3 INSERT INTO t1 VALUES('b', 'c', 'd', 'a'); -- 4 } foreach {tn q res} { 1 "a" {1 2 3 4} 2 "{a} : a" {1} 3 "{-a} : a" {2 3 4} 4 "{-a c} : a" {2 4} 5 "{-d d c} : a" {1 2} 6 "{-d c b a} : a" {} 7 "{-\"a\"} : b" {1 2 3} 8 "- c : a" {1 2 4} 9 "-c : a" {1 2 4} 10 "-\"c\" : a" {1 2 4} } { breakpoint do_execsql_test 1.$tn { SELECT rowid FROM t1($q) } $res } } finish_test |