modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/fts3/fts3_aux.c (about) 1 /* 2 ** 2011 Jan 27 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 */ 14 #include "fts3Int.h" 15 #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) 16 17 #include <string.h> 18 #include <assert.h> 19 20 typedef struct Fts3auxTable Fts3auxTable; 21 typedef struct Fts3auxCursor Fts3auxCursor; 22 23 struct Fts3auxTable { 24 sqlite3_vtab base; /* Base class used by SQLite core */ 25 Fts3Table *pFts3Tab; 26 }; 27 28 struct Fts3auxCursor { 29 sqlite3_vtab_cursor base; /* Base class used by SQLite core */ 30 Fts3MultiSegReader csr; /* Must be right after "base" */ 31 Fts3SegFilter filter; 32 char *zStop; 33 int nStop; /* Byte-length of string zStop */ 34 int iLangid; /* Language id to query */ 35 int isEof; /* True if cursor is at EOF */ 36 sqlite3_int64 iRowid; /* Current rowid */ 37 38 int iCol; /* Current value of 'col' column */ 39 int nStat; /* Size of aStat[] array */ 40 struct Fts3auxColstats { 41 sqlite3_int64 nDoc; /* 'documents' values for current csr row */ 42 sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */ 43 } *aStat; 44 }; 45 46 /* 47 ** Schema of the terms table. 48 */ 49 #define FTS3_AUX_SCHEMA \ 50 "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)" 51 52 /* 53 ** This function does all the work for both the xConnect and xCreate methods. 54 ** These tables have no persistent representation of their own, so xConnect 55 ** and xCreate are identical operations. 56 */ 57 static int fts3auxConnectMethod( 58 sqlite3 *db, /* Database connection */ 59 void *pUnused, /* Unused */ 60 int argc, /* Number of elements in argv array */ 61 const char * const *argv, /* xCreate/xConnect argument array */ 62 sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ 63 char **pzErr /* OUT: sqlite3_malloc'd error message */ 64 ){ 65 char const *zDb; /* Name of database (e.g. "main") */ 66 char const *zFts3; /* Name of fts3 table */ 67 int nDb; /* Result of strlen(zDb) */ 68 int nFts3; /* Result of strlen(zFts3) */ 69 int nByte; /* Bytes of space to allocate here */ 70 int rc; /* value returned by declare_vtab() */ 71 Fts3auxTable *p; /* Virtual table object to return */ 72 73 UNUSED_PARAMETER(pUnused); 74 75 /* The user should invoke this in one of two forms: 76 ** 77 ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table); 78 ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table); 79 */ 80 if( argc!=4 && argc!=5 ) goto bad_args; 81 82 zDb = argv[1]; 83 nDb = (int)strlen(zDb); 84 if( argc==5 ){ 85 if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){ 86 zDb = argv[3]; 87 nDb = (int)strlen(zDb); 88 zFts3 = argv[4]; 89 }else{ 90 goto bad_args; 91 } 92 }else{ 93 zFts3 = argv[3]; 94 } 95 nFts3 = (int)strlen(zFts3); 96 97 rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA); 98 if( rc!=SQLITE_OK ) return rc; 99 100 nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; 101 p = (Fts3auxTable *)sqlite3_malloc(nByte); 102 if( !p ) return SQLITE_NOMEM; 103 memset(p, 0, nByte); 104 105 p->pFts3Tab = (Fts3Table *)&p[1]; 106 p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; 107 p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; 108 p->pFts3Tab->db = db; 109 p->pFts3Tab->nIndex = 1; 110 111 memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); 112 memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); 113 sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); 114 115 *ppVtab = (sqlite3_vtab *)p; 116 return SQLITE_OK; 117 118 bad_args: 119 sqlite3Fts3ErrMsg(pzErr, "invalid arguments to fts4aux constructor"); 120 return SQLITE_ERROR; 121 } 122 123 /* 124 ** This function does the work for both the xDisconnect and xDestroy methods. 125 ** These tables have no persistent representation of their own, so xDisconnect 126 ** and xDestroy are identical operations. 127 */ 128 static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){ 129 Fts3auxTable *p = (Fts3auxTable *)pVtab; 130 Fts3Table *pFts3 = p->pFts3Tab; 131 int i; 132 133 /* Free any prepared statements held */ 134 for(i=0; i<SizeofArray(pFts3->aStmt); i++){ 135 sqlite3_finalize(pFts3->aStmt[i]); 136 } 137 sqlite3_free(pFts3->zSegmentsTbl); 138 sqlite3_free(p); 139 return SQLITE_OK; 140 } 141 142 #define FTS4AUX_EQ_CONSTRAINT 1 143 #define FTS4AUX_GE_CONSTRAINT 2 144 #define FTS4AUX_LE_CONSTRAINT 4 145 146 /* 147 ** xBestIndex - Analyze a WHERE and ORDER BY clause. 148 */ 149 static int fts3auxBestIndexMethod( 150 sqlite3_vtab *pVTab, 151 sqlite3_index_info *pInfo 152 ){ 153 int i; 154 int iEq = -1; 155 int iGe = -1; 156 int iLe = -1; 157 int iLangid = -1; 158 int iNext = 1; /* Next free argvIndex value */ 159 160 UNUSED_PARAMETER(pVTab); 161 162 /* This vtab delivers always results in "ORDER BY term ASC" order. */ 163 if( pInfo->nOrderBy==1 164 && pInfo->aOrderBy[0].iColumn==0 165 && pInfo->aOrderBy[0].desc==0 166 ){ 167 pInfo->orderByConsumed = 1; 168 } 169 170 /* Search for equality and range constraints on the "term" column. 171 ** And equality constraints on the hidden "languageid" column. */ 172 for(i=0; i<pInfo->nConstraint; i++){ 173 if( pInfo->aConstraint[i].usable ){ 174 int op = pInfo->aConstraint[i].op; 175 int iCol = pInfo->aConstraint[i].iColumn; 176 177 if( iCol==0 ){ 178 if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i; 179 if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i; 180 if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i; 181 if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i; 182 if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i; 183 } 184 if( iCol==4 ){ 185 if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i; 186 } 187 } 188 } 189 190 if( iEq>=0 ){ 191 pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT; 192 pInfo->aConstraintUsage[iEq].argvIndex = iNext++; 193 pInfo->estimatedCost = 5; 194 }else{ 195 pInfo->idxNum = 0; 196 pInfo->estimatedCost = 20000; 197 if( iGe>=0 ){ 198 pInfo->idxNum += FTS4AUX_GE_CONSTRAINT; 199 pInfo->aConstraintUsage[iGe].argvIndex = iNext++; 200 pInfo->estimatedCost /= 2; 201 } 202 if( iLe>=0 ){ 203 pInfo->idxNum += FTS4AUX_LE_CONSTRAINT; 204 pInfo->aConstraintUsage[iLe].argvIndex = iNext++; 205 pInfo->estimatedCost /= 2; 206 } 207 } 208 if( iLangid>=0 ){ 209 pInfo->aConstraintUsage[iLangid].argvIndex = iNext++; 210 pInfo->estimatedCost--; 211 } 212 213 return SQLITE_OK; 214 } 215 216 /* 217 ** xOpen - Open a cursor. 218 */ 219 static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ 220 Fts3auxCursor *pCsr; /* Pointer to cursor object to return */ 221 222 UNUSED_PARAMETER(pVTab); 223 224 pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor)); 225 if( !pCsr ) return SQLITE_NOMEM; 226 memset(pCsr, 0, sizeof(Fts3auxCursor)); 227 228 *ppCsr = (sqlite3_vtab_cursor *)pCsr; 229 return SQLITE_OK; 230 } 231 232 /* 233 ** xClose - Close a cursor. 234 */ 235 static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ 236 Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; 237 Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; 238 239 sqlite3Fts3SegmentsClose(pFts3); 240 sqlite3Fts3SegReaderFinish(&pCsr->csr); 241 sqlite3_free((void *)pCsr->filter.zTerm); 242 sqlite3_free(pCsr->zStop); 243 sqlite3_free(pCsr->aStat); 244 sqlite3_free(pCsr); 245 return SQLITE_OK; 246 } 247 248 static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ 249 if( nSize>pCsr->nStat ){ 250 struct Fts3auxColstats *aNew; 251 aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, 252 sizeof(struct Fts3auxColstats) * nSize 253 ); 254 if( aNew==0 ) return SQLITE_NOMEM; 255 memset(&aNew[pCsr->nStat], 0, 256 sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat) 257 ); 258 pCsr->aStat = aNew; 259 pCsr->nStat = nSize; 260 } 261 return SQLITE_OK; 262 } 263 264 /* 265 ** xNext - Advance the cursor to the next row, if any. 266 */ 267 static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ 268 Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; 269 Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; 270 int rc; 271 272 /* Increment our pretend rowid value. */ 273 pCsr->iRowid++; 274 275 for(pCsr->iCol++; pCsr->iCol<pCsr->nStat; pCsr->iCol++){ 276 if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK; 277 } 278 279 rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); 280 if( rc==SQLITE_ROW ){ 281 int i = 0; 282 int nDoclist = pCsr->csr.nDoclist; 283 char *aDoclist = pCsr->csr.aDoclist; 284 int iCol; 285 286 int eState = 0; 287 288 if( pCsr->zStop ){ 289 int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm; 290 int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n); 291 if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){ 292 pCsr->isEof = 1; 293 return SQLITE_OK; 294 } 295 } 296 297 if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; 298 memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); 299 iCol = 0; 300 301 while( i<nDoclist ){ 302 sqlite3_int64 v = 0; 303 304 i += sqlite3Fts3GetVarint(&aDoclist[i], &v); 305 switch( eState ){ 306 /* State 0. In this state the integer just read was a docid. */ 307 case 0: 308 pCsr->aStat[0].nDoc++; 309 eState = 1; 310 iCol = 0; 311 break; 312 313 /* State 1. In this state we are expecting either a 1, indicating 314 ** that the following integer will be a column number, or the 315 ** start of a position list for column 0. 316 ** 317 ** The only difference between state 1 and state 2 is that if the 318 ** integer encountered in state 1 is not 0 or 1, then we need to 319 ** increment the column 0 "nDoc" count for this term. 320 */ 321 case 1: 322 assert( iCol==0 ); 323 if( v>1 ){ 324 pCsr->aStat[1].nDoc++; 325 } 326 eState = 2; 327 /* fall through */ 328 329 case 2: 330 if( v==0 ){ /* 0x00. Next integer will be a docid. */ 331 eState = 0; 332 }else if( v==1 ){ /* 0x01. Next integer will be a column number. */ 333 eState = 3; 334 }else{ /* 2 or greater. A position. */ 335 pCsr->aStat[iCol+1].nOcc++; 336 pCsr->aStat[0].nOcc++; 337 } 338 break; 339 340 /* State 3. The integer just read is a column number. */ 341 default: assert( eState==3 ); 342 iCol = (int)v; 343 if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM; 344 pCsr->aStat[iCol+1].nDoc++; 345 eState = 2; 346 break; 347 } 348 } 349 350 pCsr->iCol = 0; 351 rc = SQLITE_OK; 352 }else{ 353 pCsr->isEof = 1; 354 } 355 return rc; 356 } 357 358 /* 359 ** xFilter - Initialize a cursor to point at the start of its data. 360 */ 361 static int fts3auxFilterMethod( 362 sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ 363 int idxNum, /* Strategy index */ 364 const char *idxStr, /* Unused */ 365 int nVal, /* Number of elements in apVal */ 366 sqlite3_value **apVal /* Arguments for the indexing scheme */ 367 ){ 368 Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; 369 Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; 370 int rc; 371 int isScan = 0; 372 int iLangVal = 0; /* Language id to query */ 373 374 int iEq = -1; /* Index of term=? value in apVal */ 375 int iGe = -1; /* Index of term>=? value in apVal */ 376 int iLe = -1; /* Index of term<=? value in apVal */ 377 int iLangid = -1; /* Index of languageid=? value in apVal */ 378 int iNext = 0; 379 380 UNUSED_PARAMETER(nVal); 381 UNUSED_PARAMETER(idxStr); 382 383 assert( idxStr==0 ); 384 assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0 385 || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT 386 || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) 387 ); 388 389 if( idxNum==FTS4AUX_EQ_CONSTRAINT ){ 390 iEq = iNext++; 391 }else{ 392 isScan = 1; 393 if( idxNum & FTS4AUX_GE_CONSTRAINT ){ 394 iGe = iNext++; 395 } 396 if( idxNum & FTS4AUX_LE_CONSTRAINT ){ 397 iLe = iNext++; 398 } 399 } 400 if( iNext<nVal ){ 401 iLangid = iNext++; 402 } 403 404 /* In case this cursor is being reused, close and zero it. */ 405 testcase(pCsr->filter.zTerm); 406 sqlite3Fts3SegReaderFinish(&pCsr->csr); 407 sqlite3_free((void *)pCsr->filter.zTerm); 408 sqlite3_free(pCsr->aStat); 409 memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); 410 411 pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; 412 if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN; 413 414 if( iEq>=0 || iGe>=0 ){ 415 const unsigned char *zStr = sqlite3_value_text(apVal[0]); 416 assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) ); 417 if( zStr ){ 418 pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); 419 pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); 420 if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; 421 } 422 } 423 424 if( iLe>=0 ){ 425 pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe])); 426 pCsr->nStop = sqlite3_value_bytes(apVal[iLe]); 427 if( pCsr->zStop==0 ) return SQLITE_NOMEM; 428 } 429 430 if( iLangid>=0 ){ 431 iLangVal = sqlite3_value_int(apVal[iLangid]); 432 433 /* If the user specified a negative value for the languageid, use zero 434 ** instead. This works, as the "languageid=?" constraint will also 435 ** be tested by the VDBE layer. The test will always be false (since 436 ** this module will not return a row with a negative languageid), and 437 ** so the overall query will return zero rows. */ 438 if( iLangVal<0 ) iLangVal = 0; 439 } 440 pCsr->iLangid = iLangVal; 441 442 rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL, 443 pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr 444 ); 445 if( rc==SQLITE_OK ){ 446 rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); 447 } 448 449 if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor); 450 return rc; 451 } 452 453 /* 454 ** xEof - Return true if the cursor is at EOF, or false otherwise. 455 */ 456 static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){ 457 Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; 458 return pCsr->isEof; 459 } 460 461 /* 462 ** xColumn - Return a column value. 463 */ 464 static int fts3auxColumnMethod( 465 sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ 466 sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ 467 int iCol /* Index of column to read value from */ 468 ){ 469 Fts3auxCursor *p = (Fts3auxCursor *)pCursor; 470 471 assert( p->isEof==0 ); 472 switch( iCol ){ 473 case 0: /* term */ 474 sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); 475 break; 476 477 case 1: /* col */ 478 if( p->iCol ){ 479 sqlite3_result_int(pCtx, p->iCol-1); 480 }else{ 481 sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC); 482 } 483 break; 484 485 case 2: /* documents */ 486 sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc); 487 break; 488 489 case 3: /* occurrences */ 490 sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc); 491 break; 492 493 default: /* languageid */ 494 assert( iCol==4 ); 495 sqlite3_result_int(pCtx, p->iLangid); 496 break; 497 } 498 499 return SQLITE_OK; 500 } 501 502 /* 503 ** xRowid - Return the current rowid for the cursor. 504 */ 505 static int fts3auxRowidMethod( 506 sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ 507 sqlite_int64 *pRowid /* OUT: Rowid value */ 508 ){ 509 Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; 510 *pRowid = pCsr->iRowid; 511 return SQLITE_OK; 512 } 513 514 /* 515 ** Register the fts3aux module with database connection db. Return SQLITE_OK 516 ** if successful or an error code if sqlite3_create_module() fails. 517 */ 518 int sqlite3Fts3InitAux(sqlite3 *db){ 519 static const sqlite3_module fts3aux_module = { 520 0, /* iVersion */ 521 fts3auxConnectMethod, /* xCreate */ 522 fts3auxConnectMethod, /* xConnect */ 523 fts3auxBestIndexMethod, /* xBestIndex */ 524 fts3auxDisconnectMethod, /* xDisconnect */ 525 fts3auxDisconnectMethod, /* xDestroy */ 526 fts3auxOpenMethod, /* xOpen */ 527 fts3auxCloseMethod, /* xClose */ 528 fts3auxFilterMethod, /* xFilter */ 529 fts3auxNextMethod, /* xNext */ 530 fts3auxEofMethod, /* xEof */ 531 fts3auxColumnMethod, /* xColumn */ 532 fts3auxRowidMethod, /* xRowid */ 533 0, /* xUpdate */ 534 0, /* xBegin */ 535 0, /* xSync */ 536 0, /* xCommit */ 537 0, /* xRollback */ 538 0, /* xFindFunction */ 539 0, /* xRename */ 540 0, /* xSavepoint */ 541 0, /* xRelease */ 542 0 /* xRollbackTo */ 543 }; 544 int rc; /* Return code */ 545 546 rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0); 547 return rc; 548 } 549 550 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */