123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870 |
- /*
- ** 2014 Jun 09
- **
- ** 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 is an SQLite module implementing full-text search.
- */
- #include "fts5Int.h"
- /*
- ** This variable is set to false when running tests for which the on disk
- ** structures should not be corrupt. Otherwise, true. If it is false, extra
- ** assert() conditions in the fts5 code are activated - conditions that are
- ** only true if it is guaranteed that the fts5 database is not corrupt.
- */
- #ifdef SQLITE_DEBUG
- int sqlite3_fts5_may_be_corrupt = 1;
- #endif
- typedef struct Fts5Auxdata Fts5Auxdata;
- typedef struct Fts5Auxiliary Fts5Auxiliary;
- typedef struct Fts5Cursor Fts5Cursor;
- typedef struct Fts5FullTable Fts5FullTable;
- typedef struct Fts5Sorter Fts5Sorter;
- typedef struct Fts5TokenizerModule Fts5TokenizerModule;
- /*
- ** NOTES ON TRANSACTIONS:
- **
- ** SQLite invokes the following virtual table methods as transactions are
- ** opened and closed by the user:
- **
- ** xBegin(): Start of a new transaction.
- ** xSync(): Initial part of two-phase commit.
- ** xCommit(): Final part of two-phase commit.
- ** xRollback(): Rollback the transaction.
- **
- ** Anything that is required as part of a commit that may fail is performed
- ** in the xSync() callback. Current versions of SQLite ignore any errors
- ** returned by xCommit().
- **
- ** And as sub-transactions are opened/closed:
- **
- ** xSavepoint(int S): Open savepoint S.
- ** xRelease(int S): Commit and close savepoint S.
- ** xRollbackTo(int S): Rollback to start of savepoint S.
- **
- ** During a write-transaction the fts5_index.c module may cache some data
- ** in-memory. It is flushed to disk whenever xSync(), xRelease() or
- ** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo()
- ** is called.
- **
- ** Additionally, if SQLITE_DEBUG is defined, an instance of the following
- ** structure is used to record the current transaction state. This information
- ** is not required, but it is used in the assert() statements executed by
- ** function fts5CheckTransactionState() (see below).
- */
- struct Fts5TransactionState {
- int eState; /* 0==closed, 1==open, 2==synced */
- int iSavepoint; /* Number of open savepoints (0 -> none) */
- };
- /*
- ** A single object of this type is allocated when the FTS5 module is
- ** registered with a database handle. It is used to store pointers to
- ** all registered FTS5 extensions - tokenizers and auxiliary functions.
- */
- struct Fts5Global {
- fts5_api api; /* User visible part of object (see fts5.h) */
- sqlite3 *db; /* Associated database connection */
- i64 iNextId; /* Used to allocate unique cursor ids */
- Fts5Auxiliary *pAux; /* First in list of all aux. functions */
- Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
- Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */
- Fts5Cursor *pCsr; /* First in list of all open cursors */
- u32 aLocaleHdr[4];
- };
- /*
- ** Size of header on fts5_locale() values. And macro to access a buffer
- ** containing a copy of the header from an Fts5Config pointer.
- */
- #define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
- #define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
- #define FTS5_INSTTOKEN_SUBTYPE 73
- /*
- ** Each auxiliary function registered with the FTS5 module is represented
- ** by an object of the following type. All such objects are stored as part
- ** of the Fts5Global.pAux list.
- */
- struct Fts5Auxiliary {
- Fts5Global *pGlobal; /* Global context for this function */
- char *zFunc; /* Function name (nul-terminated) */
- void *pUserData; /* User-data pointer */
- fts5_extension_function xFunc; /* Callback function */
- void (*xDestroy)(void*); /* Destructor function */
- Fts5Auxiliary *pNext; /* Next registered auxiliary function */
- };
- /*
- ** Each tokenizer module registered with the FTS5 module is represented
- ** by an object of the following type. All such objects are stored as part
- ** of the Fts5Global.pTok list.
- **
- ** bV2Native:
- ** True if the tokenizer was registered using xCreateTokenizer_v2(), false
- ** for xCreateTokenizer(). If this variable is true, then x2 is populated
- ** with the routines as supplied by the caller and x1 contains synthesized
- ** wrapper routines. In this case the user-data pointer passed to
- ** x1.xCreate should be a pointer to the Fts5TokenizerModule structure,
- ** not a copy of pUserData.
- **
- ** Of course, if bV2Native is false, then x1 contains the real routines and
- ** x2 the synthesized ones. In this case a pointer to the Fts5TokenizerModule
- ** object should be passed to x2.xCreate.
- **
- ** The synthesized wrapper routines are necessary for xFindTokenizer(_v2)
- ** calls.
- */
- struct Fts5TokenizerModule {
- char *zName; /* Name of tokenizer */
- void *pUserData; /* User pointer passed to xCreate() */
- int bV2Native; /* True if v2 native tokenizer */
- fts5_tokenizer x1; /* Tokenizer functions */
- fts5_tokenizer_v2 x2; /* V2 tokenizer functions */
- void (*xDestroy)(void*); /* Destructor function */
- Fts5TokenizerModule *pNext; /* Next registered tokenizer module */
- };
- struct Fts5FullTable {
- Fts5Table p; /* Public class members from fts5Int.h */
- Fts5Storage *pStorage; /* Document store */
- Fts5Global *pGlobal; /* Global (connection wide) data */
- Fts5Cursor *pSortCsr; /* Sort data from this cursor */
- int iSavepoint; /* Successful xSavepoint()+1 */
- #ifdef SQLITE_DEBUG
- struct Fts5TransactionState ts;
- #endif
- };
- struct Fts5MatchPhrase {
- Fts5Buffer *pPoslist; /* Pointer to current poslist */
- int nTerm; /* Size of phrase in terms */
- };
- /*
- ** pStmt:
- ** SELECT rowid, <fts> FROM <fts> ORDER BY +rank;
- **
- ** aIdx[]:
- ** There is one entry in the aIdx[] array for each phrase in the query,
- ** the value of which is the offset within aPoslist[] following the last
- ** byte of the position list for the corresponding phrase.
- */
- struct Fts5Sorter {
- sqlite3_stmt *pStmt;
- i64 iRowid; /* Current rowid */
- const u8 *aPoslist; /* Position lists for current row */
- int nIdx; /* Number of entries in aIdx[] */
- int aIdx[1]; /* Offsets into aPoslist for current row */
- };
- /*
- ** Virtual-table cursor object.
- **
- ** iSpecial:
- ** If this is a 'special' query (refer to function fts5SpecialMatch()),
- ** then this variable contains the result of the query.
- **
- ** iFirstRowid, iLastRowid:
- ** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the
- ** cursor iterates in ascending order of rowids, iFirstRowid is the lower
- ** limit of rowids to return, and iLastRowid the upper. In other words, the
- ** WHERE clause in the user's query might have been:
- **
- ** <tbl> MATCH <expr> AND rowid BETWEEN $iFirstRowid AND $iLastRowid
- **
- ** If the cursor iterates in descending order of rowid, iFirstRowid
- ** is the upper limit (i.e. the "first" rowid visited) and iLastRowid
- ** the lower.
- */
- struct Fts5Cursor {
- sqlite3_vtab_cursor base; /* Base class used by SQLite core */
- Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */
- int *aColumnSize; /* Values for xColumnSize() */
- i64 iCsrId; /* Cursor id */
- /* Zero from this point onwards on cursor reset */
- int ePlan; /* FTS5_PLAN_XXX value */
- int bDesc; /* True for "ORDER BY rowid DESC" queries */
- i64 iFirstRowid; /* Return no rowids earlier than this */
- i64 iLastRowid; /* Return no rowids later than this */
- sqlite3_stmt *pStmt; /* Statement used to read %_content */
- Fts5Expr *pExpr; /* Expression for MATCH queries */
- Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */
- int csrflags; /* Mask of cursor flags (see below) */
- i64 iSpecial; /* Result of special query */
- /* "rank" function. Populated on demand from vtab.xColumn(). */
- char *zRank; /* Custom rank function */
- char *zRankArgs; /* Custom rank function args */
- Fts5Auxiliary *pRank; /* Rank callback (or NULL) */
- int nRankArg; /* Number of trailing arguments for rank() */
- sqlite3_value **apRankArg; /* Array of trailing arguments */
- sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */
- /* Auxiliary data storage */
- Fts5Auxiliary *pAux; /* Currently executing extension function */
- Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */
- /* Cache used by auxiliary API functions xInst() and xInstCount() */
- Fts5PoslistReader *aInstIter; /* One for each phrase */
- int nInstAlloc; /* Size of aInst[] array (entries / 3) */
- int nInstCount; /* Number of phrase instances */
- int *aInst; /* 3 integers per phrase instance */
- };
- /*
- ** Bits that make up the "idxNum" parameter passed indirectly by
- ** xBestIndex() to xFilter().
- */
- #define FTS5_BI_MATCH 0x0001 /* <tbl> MATCH ? */
- #define FTS5_BI_RANK 0x0002 /* rank MATCH ? */
- #define FTS5_BI_ROWID_EQ 0x0004 /* rowid == ? */
- #define FTS5_BI_ROWID_LE 0x0008 /* rowid <= ? */
- #define FTS5_BI_ROWID_GE 0x0010 /* rowid >= ? */
- #define FTS5_BI_ORDER_RANK 0x0020
- #define FTS5_BI_ORDER_ROWID 0x0040
- #define FTS5_BI_ORDER_DESC 0x0080
- /*
- ** Values for Fts5Cursor.csrflags
- */
- #define FTS5CSR_EOF 0x01
- #define FTS5CSR_REQUIRE_CONTENT 0x02
- #define FTS5CSR_REQUIRE_DOCSIZE 0x04
- #define FTS5CSR_REQUIRE_INST 0x08
- #define FTS5CSR_FREE_ZRANK 0x10
- #define FTS5CSR_REQUIRE_RESEEK 0x20
- #define FTS5CSR_REQUIRE_POSLIST 0x40
- #define BitFlagAllTest(x,y) (((x) & (y))==(y))
- #define BitFlagTest(x,y) (((x) & (y))!=0)
- /*
- ** Macros to Set(), Clear() and Test() cursor flags.
- */
- #define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag))
- #define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag))
- #define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag))
- struct Fts5Auxdata {
- Fts5Auxiliary *pAux; /* Extension to which this belongs */
- void *pPtr; /* Pointer value */
- void(*xDelete)(void*); /* Destructor */
- Fts5Auxdata *pNext; /* Next object in linked list */
- };
- #ifdef SQLITE_DEBUG
- #define FTS5_BEGIN 1
- #define FTS5_SYNC 2
- #define FTS5_COMMIT 3
- #define FTS5_ROLLBACK 4
- #define FTS5_SAVEPOINT 5
- #define FTS5_RELEASE 6
- #define FTS5_ROLLBACKTO 7
- static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){
- switch( op ){
- case FTS5_BEGIN:
- assert( p->ts.eState==0 );
- p->ts.eState = 1;
- p->ts.iSavepoint = -1;
- break;
- case FTS5_SYNC:
- assert( p->ts.eState==1 || p->ts.eState==2 );
- p->ts.eState = 2;
- break;
- case FTS5_COMMIT:
- assert( p->ts.eState==2 );
- p->ts.eState = 0;
- break;
- case FTS5_ROLLBACK:
- assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 );
- p->ts.eState = 0;
- break;
- case FTS5_SAVEPOINT:
- assert( p->ts.eState>=1 );
- assert( iSavepoint>=0 );
- assert( iSavepoint>=p->ts.iSavepoint );
- p->ts.iSavepoint = iSavepoint;
- break;
-
- case FTS5_RELEASE:
- assert( p->ts.eState>=1 );
- assert( iSavepoint>=0 );
- assert( iSavepoint<=p->ts.iSavepoint );
- p->ts.iSavepoint = iSavepoint-1;
- break;
- case FTS5_ROLLBACKTO:
- assert( p->ts.eState>=1 );
- assert( iSavepoint>=-1 );
- /* The following assert() can fail if another vtab strikes an error
- ** within an xSavepoint() call then SQLite calls xRollbackTo() - without
- ** having called xSavepoint() on this vtab. */
- /* assert( iSavepoint<=p->ts.iSavepoint ); */
- p->ts.iSavepoint = iSavepoint;
- break;
- }
- }
- #else
- # define fts5CheckTransactionState(x,y,z)
- #endif
- /*
- ** Return true if pTab is a contentless table. If parameter bIncludeUnindexed
- ** is true, this includes contentless tables that store UNINDEXED columns
- ** only.
- */
- static int fts5IsContentless(Fts5FullTable *pTab, int bIncludeUnindexed){
- int eContent = pTab->p.pConfig->eContent;
- return (
- eContent==FTS5_CONTENT_NONE
- || (bIncludeUnindexed && eContent==FTS5_CONTENT_UNINDEXED)
- );
- }
- /*
- ** Delete a virtual table handle allocated by fts5InitVtab().
- */
- static void fts5FreeVtab(Fts5FullTable *pTab){
- if( pTab ){
- sqlite3Fts5IndexClose(pTab->p.pIndex);
- sqlite3Fts5StorageClose(pTab->pStorage);
- sqlite3Fts5ConfigFree(pTab->p.pConfig);
- sqlite3_free(pTab);
- }
- }
- /*
- ** The xDisconnect() virtual table method.
- */
- static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
- fts5FreeVtab((Fts5FullTable*)pVtab);
- return SQLITE_OK;
- }
- /*
- ** The xDestroy() virtual table method.
- */
- static int fts5DestroyMethod(sqlite3_vtab *pVtab){
- Fts5Table *pTab = (Fts5Table*)pVtab;
- int rc = sqlite3Fts5DropAll(pTab->pConfig);
- if( rc==SQLITE_OK ){
- fts5FreeVtab((Fts5FullTable*)pVtab);
- }
- return rc;
- }
- /*
- ** This function is the implementation of both the xConnect and xCreate
- ** methods of the FTS3 virtual table.
- **
- ** The argv[] array contains the following:
- **
- ** argv[0] -> module name ("fts5")
- ** argv[1] -> database name
- ** argv[2] -> table name
- ** argv[...] -> "column name" and other module argument fields.
- */
- static int fts5InitVtab(
- int bCreate, /* True for xCreate, false for xConnect */
- sqlite3 *db, /* The SQLite database connection */
- void *pAux, /* Hash table containing tokenizers */
- int argc, /* Number of elements in argv array */
- const char * const *argv, /* xCreate/xConnect argument array */
- sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
- char **pzErr /* Write any error message here */
- ){
- Fts5Global *pGlobal = (Fts5Global*)pAux;
- const char **azConfig = (const char**)argv;
- int rc = SQLITE_OK; /* Return code */
- Fts5Config *pConfig = 0; /* Results of parsing argc/argv */
- Fts5FullTable *pTab = 0; /* New virtual table object */
- /* Allocate the new vtab object and parse the configuration */
- pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable));
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr);
- assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
- }
- if( rc==SQLITE_OK ){
- pConfig->pzErrmsg = pzErr;
- pTab->p.pConfig = pConfig;
- pTab->pGlobal = pGlobal;
- if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){
- rc = sqlite3Fts5LoadTokenizer(pConfig);
- }
- }
- /* Open the index sub-system */
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr);
- }
- /* Open the storage sub-system */
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageOpen(
- pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr
- );
- }
- /* Call sqlite3_declare_vtab() */
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ConfigDeclareVtab(pConfig);
- }
- /* Load the initial configuration */
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie-1);
- }
- if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
- rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
- }
- if( pConfig ) pConfig->pzErrmsg = 0;
- if( rc!=SQLITE_OK ){
- fts5FreeVtab(pTab);
- pTab = 0;
- }else if( bCreate ){
- fts5CheckTransactionState(pTab, FTS5_BEGIN, 0);
- }
- *ppVTab = (sqlite3_vtab*)pTab;
- return rc;
- }
- /*
- ** The xConnect() and xCreate() methods for the virtual table. All the
- ** work is done in function fts5InitVtab().
- */
- static int fts5ConnectMethod(
- sqlite3 *db, /* Database connection */
- void *pAux, /* Pointer to tokenizer hash table */
- int argc, /* Number of elements in argv array */
- const char * const *argv, /* xCreate/xConnect argument array */
- sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
- char **pzErr /* OUT: sqlite3_malloc'd error message */
- ){
- return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr);
- }
- static int fts5CreateMethod(
- sqlite3 *db, /* Database connection */
- void *pAux, /* Pointer to tokenizer hash table */
- int argc, /* Number of elements in argv array */
- const char * const *argv, /* xCreate/xConnect argument array */
- sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
- char **pzErr /* OUT: sqlite3_malloc'd error message */
- ){
- return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
- }
- /*
- ** The different query plans.
- */
- #define FTS5_PLAN_MATCH 1 /* (<tbl> MATCH ?) */
- #define FTS5_PLAN_SOURCE 2 /* A source cursor for SORTED_MATCH */
- #define FTS5_PLAN_SPECIAL 3 /* An internal query */
- #define FTS5_PLAN_SORTED_MATCH 4 /* (<tbl> MATCH ? ORDER BY rank) */
- #define FTS5_PLAN_SCAN 5 /* No usable constraint */
- #define FTS5_PLAN_ROWID 6 /* (rowid = ?) */
- /*
- ** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
- ** extension is currently being used by a version of SQLite too old to
- ** support index-info flags. In that case this function is a no-op.
- */
- static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
- #if SQLITE_VERSION_NUMBER>=3008012
- #ifndef SQLITE_CORE
- if( sqlite3_libversion_number()>=3008012 )
- #endif
- {
- pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
- }
- #endif
- }
- static int fts5UsePatternMatch(
- Fts5Config *pConfig,
- struct sqlite3_index_constraint *p
- ){
- assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
- assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
- if( pConfig->t.ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
- return 1;
- }
- if( pConfig->t.ePattern==FTS5_PATTERN_LIKE
- && (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB)
- ){
- return 1;
- }
- return 0;
- }
- /*
- ** Implementation of the xBestIndex method for FTS5 tables. Within the
- ** WHERE constraint, it searches for the following:
- **
- ** 1. A MATCH constraint against the table column.
- ** 2. A MATCH constraint against the "rank" column.
- ** 3. A MATCH constraint against some other column.
- ** 4. An == constraint against the rowid column.
- ** 5. A < or <= constraint against the rowid column.
- ** 6. A > or >= constraint against the rowid column.
- **
- ** Within the ORDER BY, the following are supported:
- **
- ** 5. ORDER BY rank [ASC|DESC]
- ** 6. ORDER BY rowid [ASC|DESC]
- **
- ** Information for the xFilter call is passed via both the idxNum and
- ** idxStr variables. Specifically, idxNum is a bitmask of the following
- ** flags used to encode the ORDER BY clause:
- **
- ** FTS5_BI_ORDER_RANK
- ** FTS5_BI_ORDER_ROWID
- ** FTS5_BI_ORDER_DESC
- **
- ** idxStr is used to encode data from the WHERE clause. For each argument
- ** passed to the xFilter method, the following is appended to idxStr:
- **
- ** Match against table column: "m"
- ** Match against rank column: "r"
- ** Match against other column: "M<column-number>"
- ** LIKE against other column: "L<column-number>"
- ** GLOB against other column: "G<column-number>"
- ** Equality constraint against the rowid: "="
- ** A < or <= against the rowid: "<"
- ** A > or >= against the rowid: ">"
- **
- ** This function ensures that there is at most one "r" or "=". And that if
- ** there exists an "=" then there is no "<" or ">".
- **
- ** If an unusable MATCH operator is present in the WHERE clause, then
- ** SQLITE_CONSTRAINT is returned.
- **
- ** Costs are assigned as follows:
- **
- ** a) If a MATCH operator is present, the cost depends on the other
- ** constraints also present. As follows:
- **
- ** * No other constraints: cost=1000.0
- ** * One rowid range constraint: cost=750.0
- ** * Both rowid range constraints: cost=500.0
- ** * An == rowid constraint: cost=100.0
- **
- ** b) Otherwise, if there is no MATCH:
- **
- ** * No other constraints: cost=1000000.0
- ** * One rowid range constraint: cost=750000.0
- ** * Both rowid range constraints: cost=250000.0
- ** * An == rowid constraint: cost=10.0
- **
- ** Costs are not modified by the ORDER BY clause.
- */
- static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
- Fts5Table *pTab = (Fts5Table*)pVTab;
- Fts5Config *pConfig = pTab->pConfig;
- const int nCol = pConfig->nCol;
- int idxFlags = 0; /* Parameter passed through to xFilter() */
- int i;
- char *idxStr;
- int iIdxStr = 0;
- int iCons = 0;
- int bSeenEq = 0;
- int bSeenGt = 0;
- int bSeenLt = 0;
- int nSeenMatch = 0;
- int bSeenRank = 0;
- assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
- assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
- assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
- assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
- assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
- if( pConfig->bLock ){
- pTab->base.zErrMsg = sqlite3_mprintf(
- "recursively defined fts5 content table"
- );
- return SQLITE_ERROR;
- }
- idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 8 + 1);
- if( idxStr==0 ) return SQLITE_NOMEM;
- pInfo->idxStr = idxStr;
- pInfo->needToFreeIdxStr = 1;
- for(i=0; i<pInfo->nConstraint; i++){
- struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
- int iCol = p->iColumn;
- if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
- || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
- ){
- /* A MATCH operator or equivalent */
- if( p->usable==0 || iCol<0 ){
- /* As there exists an unusable MATCH constraint this is an
- ** unusable plan. Return SQLITE_CONSTRAINT. */
- idxStr[iIdxStr] = 0;
- return SQLITE_CONSTRAINT;
- }else{
- if( iCol==nCol+1 ){
- if( bSeenRank ) continue;
- idxStr[iIdxStr++] = 'r';
- bSeenRank = 1;
- }else{
- nSeenMatch++;
- idxStr[iIdxStr++] = 'M';
- sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
- idxStr += strlen(&idxStr[iIdxStr]);
- assert( idxStr[iIdxStr]=='\0' );
- }
- pInfo->aConstraintUsage[i].argvIndex = ++iCons;
- pInfo->aConstraintUsage[i].omit = 1;
- }
- }else if( p->usable ){
- if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){
- assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB );
- idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G';
- sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
- idxStr += strlen(&idxStr[iIdxStr]);
- pInfo->aConstraintUsage[i].argvIndex = ++iCons;
- assert( idxStr[iIdxStr]=='\0' );
- nSeenMatch++;
- }else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
- idxStr[iIdxStr++] = '=';
- bSeenEq = 1;
- pInfo->aConstraintUsage[i].argvIndex = ++iCons;
- }
- }
- }
- if( bSeenEq==0 ){
- for(i=0; i<pInfo->nConstraint; i++){
- struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
- if( p->iColumn<0 && p->usable ){
- int op = p->op;
- if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
- if( bSeenLt ) continue;
- idxStr[iIdxStr++] = '<';
- pInfo->aConstraintUsage[i].argvIndex = ++iCons;
- bSeenLt = 1;
- }else
- if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
- if( bSeenGt ) continue;
- idxStr[iIdxStr++] = '>';
- pInfo->aConstraintUsage[i].argvIndex = ++iCons;
- bSeenGt = 1;
- }
- }
- }
- }
- idxStr[iIdxStr] = '\0';
- /* Set idxFlags flags for the ORDER BY clause
- **
- ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
- */
- if( pInfo->nOrderBy==1 ){
- int iSort = pInfo->aOrderBy[0].iColumn;
- if( iSort==(pConfig->nCol+1) && nSeenMatch>0 ){
- idxFlags |= FTS5_BI_ORDER_RANK;
- }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
- idxFlags |= FTS5_BI_ORDER_ROWID;
- }
- if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
- pInfo->orderByConsumed = 1;
- if( pInfo->aOrderBy[0].desc ){
- idxFlags |= FTS5_BI_ORDER_DESC;
- }
- }
- }
- /* Calculate the estimated cost based on the flags set in idxFlags. */
- if( bSeenEq ){
- pInfo->estimatedCost = nSeenMatch ? 1000.0 : 10.0;
- if( nSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
- }else if( bSeenLt && bSeenGt ){
- pInfo->estimatedCost = nSeenMatch ? 5000.0 : 250000.0;
- }else if( bSeenLt || bSeenGt ){
- pInfo->estimatedCost = nSeenMatch ? 7500.0 : 750000.0;
- }else{
- pInfo->estimatedCost = nSeenMatch ? 10000.0 : 1000000.0;
- }
- for(i=1; i<nSeenMatch; i++){
- pInfo->estimatedCost *= 0.4;
- }
- pInfo->idxNum = idxFlags;
- return SQLITE_OK;
- }
- static int fts5NewTransaction(Fts5FullTable *pTab){
- Fts5Cursor *pCsr;
- for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
- if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
- }
- return sqlite3Fts5StorageReset(pTab->pStorage);
- }
- /*
- ** Implementation of xOpen method.
- */
- static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
- Fts5FullTable *pTab = (Fts5FullTable*)pVTab;
- Fts5Config *pConfig = pTab->p.pConfig;
- Fts5Cursor *pCsr = 0; /* New cursor object */
- sqlite3_int64 nByte; /* Bytes of space to allocate */
- int rc; /* Return code */
- rc = fts5NewTransaction(pTab);
- if( rc==SQLITE_OK ){
- nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
- pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte);
- if( pCsr ){
- Fts5Global *pGlobal = pTab->pGlobal;
- memset(pCsr, 0, (size_t)nByte);
- pCsr->aColumnSize = (int*)&pCsr[1];
- pCsr->pNext = pGlobal->pCsr;
- pGlobal->pCsr = pCsr;
- pCsr->iCsrId = ++pGlobal->iNextId;
- }else{
- rc = SQLITE_NOMEM;
- }
- }
- *ppCsr = (sqlite3_vtab_cursor*)pCsr;
- return rc;
- }
- static int fts5StmtType(Fts5Cursor *pCsr){
- if( pCsr->ePlan==FTS5_PLAN_SCAN ){
- return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC : FTS5_STMT_SCAN_ASC;
- }
- return FTS5_STMT_LOOKUP;
- }
- /*
- ** This function is called after the cursor passed as the only argument
- ** is moved to point at a different row. It clears all cached data
- ** specific to the previous row stored by the cursor object.
- */
- static void fts5CsrNewrow(Fts5Cursor *pCsr){
- CsrFlagSet(pCsr,
- FTS5CSR_REQUIRE_CONTENT
- | FTS5CSR_REQUIRE_DOCSIZE
- | FTS5CSR_REQUIRE_INST
- | FTS5CSR_REQUIRE_POSLIST
- );
- }
- static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- Fts5Auxdata *pData;
- Fts5Auxdata *pNext;
- sqlite3_free(pCsr->aInstIter);
- sqlite3_free(pCsr->aInst);
- if( pCsr->pStmt ){
- int eStmt = fts5StmtType(pCsr);
- sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
- }
- if( pCsr->pSorter ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- sqlite3_finalize(pSorter->pStmt);
- sqlite3_free(pSorter);
- }
- if( pCsr->ePlan!=FTS5_PLAN_SOURCE ){
- sqlite3Fts5ExprFree(pCsr->pExpr);
- }
- for(pData=pCsr->pAuxdata; pData; pData=pNext){
- pNext = pData->pNext;
- if( pData->xDelete ) pData->xDelete(pData->pPtr);
- sqlite3_free(pData);
- }
- sqlite3_finalize(pCsr->pRankArgStmt);
- sqlite3_free(pCsr->apRankArg);
- if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){
- sqlite3_free(pCsr->zRank);
- sqlite3_free(pCsr->zRankArgs);
- }
- sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
- memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
- }
- /*
- ** Close the cursor. For additional information see the documentation
- ** on the xClose method of the virtual table interface.
- */
- static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
- if( pCursor ){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- Fts5Cursor **pp;
- fts5FreeCursorComponents(pCsr);
- /* Remove the cursor from the Fts5Global.pCsr list */
- for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
- *pp = pCsr->pNext;
- sqlite3_free(pCsr);
- }
- return SQLITE_OK;
- }
- static int fts5SorterNext(Fts5Cursor *pCsr){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int rc;
- rc = sqlite3_step(pSorter->pStmt);
- if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
- CsrFlagSet(pCsr, FTS5CSR_EOF|FTS5CSR_REQUIRE_CONTENT);
- }else if( rc==SQLITE_ROW ){
- const u8 *a;
- const u8 *aBlob;
- int nBlob;
- int i;
- int iOff = 0;
- rc = SQLITE_OK;
- pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0);
- nBlob = sqlite3_column_bytes(pSorter->pStmt, 1);
- aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1);
- /* nBlob==0 in detail=none mode. */
- if( nBlob>0 ){
- for(i=0; i<(pSorter->nIdx-1); i++){
- int iVal;
- a += fts5GetVarint32(a, iVal);
- iOff += iVal;
- pSorter->aIdx[i] = iOff;
- }
- pSorter->aIdx[i] = &aBlob[nBlob] - a;
- pSorter->aPoslist = a;
- }
- fts5CsrNewrow(pCsr);
- }
- return rc;
- }
- /*
- ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors
- ** open on table pTab.
- */
- static void fts5TripCursors(Fts5FullTable *pTab){
- Fts5Cursor *pCsr;
- for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
- if( pCsr->ePlan==FTS5_PLAN_MATCH
- && pCsr->base.pVtab==(sqlite3_vtab*)pTab
- ){
- CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
- }
- }
- }
- /*
- ** If the REQUIRE_RESEEK flag is set on the cursor passed as the first
- ** argument, close and reopen all Fts5IndexIter iterators that the cursor
- ** is using. Then attempt to move the cursor to a rowid equal to or laster
- ** (in the cursors sort order - ASC or DESC) than the current rowid.
- **
- ** If the new rowid is not equal to the old, set output parameter *pbSkip
- ** to 1 before returning. Otherwise, leave it unchanged.
- **
- ** Return SQLITE_OK if successful or if no reseek was required, or an
- ** error code if an error occurred.
- */
- static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
- int rc = SQLITE_OK;
- assert( *pbSkip==0 );
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- int bDesc = pCsr->bDesc;
- i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
- rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc);
- if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
- *pbSkip = 1;
- }
- CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
- fts5CsrNewrow(pCsr);
- if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- *pbSkip = 1;
- }
- }
- return rc;
- }
- /*
- ** Advance the cursor to the next row in the table that matches the
- ** search criteria.
- **
- ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
- ** even if we reach end-of-file. The fts5EofMethod() will be called
- ** subsequently to determine whether or not an EOF was hit.
- */
- static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int rc;
- assert( (pCsr->ePlan<3)==
- (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
- );
- assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
- /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
- ** clear any token mappings accumulated at the fts5_index.c level. In
- ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
- ** we need to retain the mappings for the entire query. */
- if( pCsr->ePlan==FTS5_PLAN_MATCH
- && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
- ){
- sqlite3Fts5ExprClearTokens(pCsr->pExpr);
- }
- if( pCsr->ePlan<3 ){
- int bSkip = 0;
- if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
- rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
- CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
- fts5CsrNewrow(pCsr);
- }else{
- switch( pCsr->ePlan ){
- case FTS5_PLAN_SPECIAL: {
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- rc = SQLITE_OK;
- break;
- }
-
- case FTS5_PLAN_SORTED_MATCH: {
- rc = fts5SorterNext(pCsr);
- break;
- }
-
- default: {
- Fts5Config *pConfig = ((Fts5Table*)pCursor->pVtab)->pConfig;
- pConfig->bLock++;
- rc = sqlite3_step(pCsr->pStmt);
- pConfig->bLock--;
- if( rc!=SQLITE_ROW ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- rc = sqlite3_reset(pCsr->pStmt);
- if( rc!=SQLITE_OK ){
- pCursor->pVtab->zErrMsg = sqlite3_mprintf(
- "%s", sqlite3_errmsg(pConfig->db)
- );
- }
- }else{
- rc = SQLITE_OK;
- CsrFlagSet(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
- }
- break;
- }
- }
- }
-
- return rc;
- }
- static int fts5PrepareStatement(
- sqlite3_stmt **ppStmt,
- Fts5Config *pConfig,
- const char *zFmt,
- ...
- ){
- sqlite3_stmt *pRet = 0;
- int rc;
- char *zSql;
- va_list ap;
- va_start(ap, zFmt);
- zSql = sqlite3_vmprintf(zFmt, ap);
- if( zSql==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
- SQLITE_PREPARE_PERSISTENT, &pRet, 0);
- if( rc!=SQLITE_OK ){
- sqlite3Fts5ConfigErrmsg(pConfig, "%s", sqlite3_errmsg(pConfig->db));
- }
- sqlite3_free(zSql);
- }
- va_end(ap);
- *ppStmt = pRet;
- return rc;
- }
- static int fts5CursorFirstSorted(
- Fts5FullTable *pTab,
- Fts5Cursor *pCsr,
- int bDesc
- ){
- Fts5Config *pConfig = pTab->p.pConfig;
- Fts5Sorter *pSorter;
- int nPhrase;
- sqlite3_int64 nByte;
- int rc;
- const char *zRank = pCsr->zRank;
- const char *zRankArgs = pCsr->zRankArgs;
-
- nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
- pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
- if( pSorter==0 ) return SQLITE_NOMEM;
- memset(pSorter, 0, (size_t)nByte);
- pSorter->nIdx = nPhrase;
- /* TODO: It would be better to have some system for reusing statement
- ** handles here, rather than preparing a new one for each query. But that
- ** is not possible as SQLite reference counts the virtual table objects.
- ** And since the statement required here reads from this very virtual
- ** table, saving it creates a circular reference.
- **
- ** If SQLite a built-in statement cache, this wouldn't be a problem. */
- rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
- "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s",
- pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
- (zRankArgs ? ", " : ""),
- (zRankArgs ? zRankArgs : ""),
- bDesc ? "DESC" : "ASC"
- );
- pCsr->pSorter = pSorter;
- if( rc==SQLITE_OK ){
- assert( pTab->pSortCsr==0 );
- pTab->pSortCsr = pCsr;
- rc = fts5SorterNext(pCsr);
- pTab->pSortCsr = 0;
- }
- if( rc!=SQLITE_OK ){
- sqlite3_finalize(pSorter->pStmt);
- sqlite3_free(pSorter);
- pCsr->pSorter = 0;
- }
- return rc;
- }
- static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
- int rc;
- Fts5Expr *pExpr = pCsr->pExpr;
- rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc);
- if( sqlite3Fts5ExprEof(pExpr) ){
- CsrFlagSet(pCsr, FTS5CSR_EOF);
- }
- fts5CsrNewrow(pCsr);
- return rc;
- }
- /*
- ** Process a "special" query. A special query is identified as one with a
- ** MATCH expression that begins with a '*' character. The remainder of
- ** the text passed to the MATCH operator are used as the special query
- ** parameters.
- */
- static int fts5SpecialMatch(
- Fts5FullTable *pTab,
- Fts5Cursor *pCsr,
- const char *zQuery
- ){
- int rc = SQLITE_OK; /* Return code */
- const char *z = zQuery; /* Special query text */
- int n; /* Number of bytes in text at z */
- while( z[0]==' ' ) z++;
- for(n=0; z[n] && z[n]!=' '; n++);
- assert( pTab->p.base.zErrMsg==0 );
- pCsr->ePlan = FTS5_PLAN_SPECIAL;
- if( n==5 && 0==sqlite3_strnicmp("reads", z, n) ){
- pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
- }
- else if( n==2 && 0==sqlite3_strnicmp("id", z, n) ){
- pCsr->iSpecial = pCsr->iCsrId;
- }
- else{
- /* An unrecognized directive. Return an error message. */
- pTab->p.base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
- rc = SQLITE_ERROR;
- }
- return rc;
- }
- /*
- ** Search for an auxiliary function named zName that can be used with table
- ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary
- ** structure. Otherwise, if no such function exists, return NULL.
- */
- static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){
- Fts5Auxiliary *pAux;
- for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
- if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux;
- }
- /* No function of the specified name was found. Return 0. */
- return 0;
- }
- static int fts5FindRankFunction(Fts5Cursor *pCsr){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- Fts5Config *pConfig = pTab->p.pConfig;
- int rc = SQLITE_OK;
- Fts5Auxiliary *pAux = 0;
- const char *zRank = pCsr->zRank;
- const char *zRankArgs = pCsr->zRankArgs;
- if( zRankArgs ){
- char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
- if( zSql ){
- sqlite3_stmt *pStmt = 0;
- rc = sqlite3_prepare_v3(pConfig->db, zSql, -1,
- SQLITE_PREPARE_PERSISTENT, &pStmt, 0);
- sqlite3_free(zSql);
- assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
- if( rc==SQLITE_OK ){
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- sqlite3_int64 nByte;
- pCsr->nRankArg = sqlite3_column_count(pStmt);
- nByte = sizeof(sqlite3_value*)*pCsr->nRankArg;
- pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte);
- if( rc==SQLITE_OK ){
- int i;
- for(i=0; i<pCsr->nRankArg; i++){
- pCsr->apRankArg[i] = sqlite3_column_value(pStmt, i);
- }
- }
- pCsr->pRankArgStmt = pStmt;
- }else{
- rc = sqlite3_finalize(pStmt);
- assert( rc!=SQLITE_OK );
- }
- }
- }
- }
- if( rc==SQLITE_OK ){
- pAux = fts5FindAuxiliary(pTab, zRank);
- if( pAux==0 ){
- assert( pTab->p.base.zErrMsg==0 );
- pTab->p.base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
- rc = SQLITE_ERROR;
- }
- }
- pCsr->pRank = pAux;
- return rc;
- }
- static int fts5CursorParseRank(
- Fts5Config *pConfig,
- Fts5Cursor *pCsr,
- sqlite3_value *pRank
- ){
- int rc = SQLITE_OK;
- if( pRank ){
- const char *z = (const char*)sqlite3_value_text(pRank);
- char *zRank = 0;
- char *zRankArgs = 0;
- if( z==0 ){
- if( sqlite3_value_type(pRank)==SQLITE_NULL ) rc = SQLITE_ERROR;
- }else{
- rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs);
- }
- if( rc==SQLITE_OK ){
- pCsr->zRank = zRank;
- pCsr->zRankArgs = zRankArgs;
- CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK);
- }else if( rc==SQLITE_ERROR ){
- pCsr->base.pVtab->zErrMsg = sqlite3_mprintf(
- "parse error in rank function: %s", z
- );
- }
- }else{
- if( pConfig->zRank ){
- pCsr->zRank = (char*)pConfig->zRank;
- pCsr->zRankArgs = (char*)pConfig->zRankArgs;
- }else{
- pCsr->zRank = (char*)FTS5_DEFAULT_RANK;
- pCsr->zRankArgs = 0;
- }
- }
- return rc;
- }
- static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
- if( pVal ){
- int eType = sqlite3_value_numeric_type(pVal);
- if( eType==SQLITE_INTEGER ){
- return sqlite3_value_int64(pVal);
- }
- }
- return iDefault;
- }
- /*
- ** Set the error message on the virtual table passed as the first argument.
- */
- static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){
- va_list ap; /* ... printf arguments */
- va_start(ap, zFormat);
- sqlite3_free(p->p.base.zErrMsg);
- p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
- va_end(ap);
- }
- /*
- ** Arrange for subsequent calls to sqlite3Fts5Tokenize() to use the locale
- ** specified by pLocale/nLocale. The buffer indicated by pLocale must remain
- ** valid until after the final call to sqlite3Fts5Tokenize() that will use
- ** the locale.
- */
- static void sqlite3Fts5SetLocale(
- Fts5Config *pConfig,
- const char *zLocale,
- int nLocale
- ){
- Fts5TokenizerConfig *pT = &pConfig->t;
- pT->pLocale = zLocale;
- pT->nLocale = nLocale;
- }
- /*
- ** Clear any locale configured by an earlier call to sqlite3Fts5SetLocale().
- */
- void sqlite3Fts5ClearLocale(Fts5Config *pConfig){
- sqlite3Fts5SetLocale(pConfig, 0, 0);
- }
- /*
- ** Return true if the value passed as the only argument is an
- ** fts5_locale() value.
- */
- int sqlite3Fts5IsLocaleValue(Fts5Config *pConfig, sqlite3_value *pVal){
- int ret = 0;
- if( sqlite3_value_type(pVal)==SQLITE_BLOB ){
- /* Call sqlite3_value_bytes() after sqlite3_value_blob() in this case.
- ** If the blob was created using zeroblob(), then sqlite3_value_blob()
- ** may call malloc(). If this malloc() fails, then the values returned
- ** by both value_blob() and value_bytes() will be 0. If value_bytes() were
- ** called first, then the NULL pointer returned by value_blob() might
- ** be dereferenced. */
- const u8 *pBlob = sqlite3_value_blob(pVal);
- int nBlob = sqlite3_value_bytes(pVal);
- if( nBlob>FTS5_LOCALE_HDR_SIZE
- && 0==memcmp(pBlob, FTS5_LOCALE_HDR(pConfig), FTS5_LOCALE_HDR_SIZE)
- ){
- ret = 1;
- }
- }
- return ret;
- }
- /*
- ** Value pVal is guaranteed to be an fts5_locale() value, according to
- ** sqlite3Fts5IsLocaleValue(). This function extracts the text and locale
- ** from the value and returns them separately.
- **
- ** If successful, SQLITE_OK is returned and (*ppText) and (*ppLoc) set
- ** to point to buffers containing the text and locale, as utf-8,
- ** respectively. In this case output parameters (*pnText) and (*pnLoc) are
- ** set to the sizes in bytes of these two buffers.
- **
- ** Or, if an error occurs, then an SQLite error code is returned. The final
- ** value of the four output parameters is undefined in this case.
- */
- int sqlite3Fts5DecodeLocaleValue(
- sqlite3_value *pVal,
- const char **ppText,
- int *pnText,
- const char **ppLoc,
- int *pnLoc
- ){
- const char *p = sqlite3_value_blob(pVal);
- int n = sqlite3_value_bytes(pVal);
- int nLoc = 0;
- assert( sqlite3_value_type(pVal)==SQLITE_BLOB );
- assert( n>FTS5_LOCALE_HDR_SIZE );
- for(nLoc=FTS5_LOCALE_HDR_SIZE; p[nLoc]; nLoc++){
- if( nLoc==(n-1) ){
- return SQLITE_MISMATCH;
- }
- }
- *ppLoc = &p[FTS5_LOCALE_HDR_SIZE];
- *pnLoc = nLoc - FTS5_LOCALE_HDR_SIZE;
- *ppText = &p[nLoc+1];
- *pnText = n - nLoc - 1;
- return SQLITE_OK;
- }
- /*
- ** Argument pVal is the text of a full-text search expression. It may or
- ** may not have been wrapped by fts5_locale(). This function extracts
- ** the text of the expression, and sets output variable (*pzText) to
- ** point to a nul-terminated buffer containing the expression.
- **
- ** If pVal was an fts5_locale() value, then sqlite3Fts5SetLocale() is called
- ** to set the tokenizer to use the specified locale.
- **
- ** If output variable (*pbFreeAndReset) is set to true, then the caller
- ** is required to (a) call sqlite3Fts5ClearLocale() to reset the tokenizer
- ** locale, and (b) call sqlite3_free() to free (*pzText).
- */
- static int fts5ExtractExprText(
- Fts5Config *pConfig, /* Fts5 configuration */
- sqlite3_value *pVal, /* Value to extract expression text from */
- char **pzText, /* OUT: nul-terminated buffer of text */
- int *pbFreeAndReset /* OUT: Free (*pzText) and clear locale */
- ){
- int rc = SQLITE_OK;
- if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
- const char *pText = 0;
- int nText = 0;
- const char *pLoc = 0;
- int nLoc = 0;
- rc = sqlite3Fts5DecodeLocaleValue(pVal, &pText, &nText, &pLoc, &nLoc);
- *pzText = sqlite3Fts5Mprintf(&rc, "%.*s", nText, pText);
- if( rc==SQLITE_OK ){
- sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
- }
- *pbFreeAndReset = 1;
- }else{
- *pzText = (char*)sqlite3_value_text(pVal);
- *pbFreeAndReset = 0;
- }
- return rc;
- }
- /*
- ** This is the xFilter interface for the virtual table. See
- ** the virtual table xFilter method documentation for additional
- ** information.
- **
- ** There are three possible query strategies:
- **
- ** 1. Full-text search using a MATCH operator.
- ** 2. A by-rowid lookup.
- ** 3. A full-table scan.
- */
- static int fts5FilterMethod(
- sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
- int idxNum, /* Strategy index */
- const char *idxStr, /* Unused */
- int nVal, /* Number of elements in apVal */
- sqlite3_value **apVal /* Arguments for the indexing scheme */
- ){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
- Fts5Config *pConfig = pTab->p.pConfig;
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int rc = SQLITE_OK; /* Error code */
- int bDesc; /* True if ORDER BY [rank|rowid] DESC */
- int bOrderByRank; /* True if ORDER BY rank */
- sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
- sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
- sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
- sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
- int iCol; /* Column on LHS of MATCH operator */
- char **pzErrmsg = pConfig->pzErrmsg;
- int bPrefixInsttoken = pConfig->bPrefixInsttoken;
- int i;
- int iIdxStr = 0;
- Fts5Expr *pExpr = 0;
- assert( pConfig->bLock==0 );
- if( pCsr->ePlan ){
- fts5FreeCursorComponents(pCsr);
- memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr));
- }
- assert( pCsr->pStmt==0 );
- assert( pCsr->pExpr==0 );
- assert( pCsr->csrflags==0 );
- assert( pCsr->pRank==0 );
- assert( pCsr->zRank==0 );
- assert( pCsr->zRankArgs==0 );
- assert( pTab->pSortCsr==0 || nVal==0 );
- assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
- pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- /* Decode the arguments passed through to this function. */
- for(i=0; i<nVal; i++){
- switch( idxStr[iIdxStr++] ){
- case 'r':
- pRank = apVal[i];
- break;
- case 'M': {
- char *zText = 0;
- int bFreeAndReset = 0;
- int bInternal = 0;
- rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
- if( rc!=SQLITE_OK ) goto filter_out;
- if( zText==0 ) zText = "";
- if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
- pConfig->bPrefixInsttoken = 1;
- }
- iCol = 0;
- do{
- iCol = iCol*10 + (idxStr[iIdxStr]-'0');
- iIdxStr++;
- }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
- if( zText[0]=='*' ){
- /* The user has issued a query of the form "MATCH '*...'". This
- ** indicates that the MATCH expression is not a full text query,
- ** but a request for an internal parameter. */
- rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
- bInternal = 1;
- }else{
- char **pzErr = &pTab->p.base.zErrMsg;
- rc = sqlite3Fts5ExprNew(pConfig, 0, iCol, zText, &pExpr, pzErr);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
- pExpr = 0;
- }
- }
- if( bFreeAndReset ){
- sqlite3_free(zText);
- sqlite3Fts5ClearLocale(pConfig);
- }
- if( bInternal || rc!=SQLITE_OK ) goto filter_out;
- break;
- }
- case 'L':
- case 'G': {
- int bGlob = (idxStr[iIdxStr-1]=='G');
- const char *zText = (const char*)sqlite3_value_text(apVal[i]);
- iCol = 0;
- do{
- iCol = iCol*10 + (idxStr[iIdxStr]-'0');
- iIdxStr++;
- }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
- if( zText ){
- rc = sqlite3Fts5ExprPattern(pConfig, bGlob, iCol, zText, &pExpr);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
- pExpr = 0;
- }
- if( rc!=SQLITE_OK ) goto filter_out;
- break;
- }
- case '=':
- pRowidEq = apVal[i];
- break;
- case '<':
- pRowidLe = apVal[i];
- break;
- default: assert( idxStr[iIdxStr-1]=='>' );
- pRowidGe = apVal[i];
- break;
- }
- }
- bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
- pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
- /* Set the cursor upper and lower rowid limits. Only some strategies
- ** actually use them. This is ok, as the xBestIndex() method leaves the
- ** sqlite3_index_constraint.omit flag clear for range constraints
- ** on the rowid field. */
- if( pRowidEq ){
- pRowidLe = pRowidGe = pRowidEq;
- }
- if( bDesc ){
- pCsr->iFirstRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
- pCsr->iLastRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
- }else{
- pCsr->iLastRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
- pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
- }
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
- if( rc!=SQLITE_OK ) goto filter_out;
- if( pTab->pSortCsr ){
- /* If pSortCsr is non-NULL, then this call is being made as part of
- ** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
- ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will
- ** return results to the user for this query. The current cursor
- ** (pCursor) is used to execute the query issued by function
- ** fts5CursorFirstSorted() above. */
- assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
- assert( nVal==0 && bOrderByRank==0 && bDesc==0 );
- assert( pCsr->iLastRowid==LARGEST_INT64 );
- assert( pCsr->iFirstRowid==SMALLEST_INT64 );
- if( pTab->pSortCsr->bDesc ){
- pCsr->iLastRowid = pTab->pSortCsr->iFirstRowid;
- pCsr->iFirstRowid = pTab->pSortCsr->iLastRowid;
- }else{
- pCsr->iLastRowid = pTab->pSortCsr->iLastRowid;
- pCsr->iFirstRowid = pTab->pSortCsr->iFirstRowid;
- }
- pCsr->ePlan = FTS5_PLAN_SOURCE;
- pCsr->pExpr = pTab->pSortCsr->pExpr;
- rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }else if( pCsr->pExpr ){
- assert( rc==SQLITE_OK );
- rc = fts5CursorParseRank(pConfig, pCsr, pRank);
- if( rc==SQLITE_OK ){
- if( bOrderByRank ){
- pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
- rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
- }else{
- pCsr->ePlan = FTS5_PLAN_MATCH;
- rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }
- }
- }else if( pConfig->zContent==0 ){
- fts5SetVtabError(pTab,"%s: table does not support scanning",pConfig->zName);
- rc = SQLITE_ERROR;
- }else{
- /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
- ** by rowid (ePlan==FTS5_PLAN_ROWID). */
- pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
- rc = sqlite3Fts5StorageStmt(
- pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg
- );
- if( rc==SQLITE_OK ){
- if( pRowidEq!=0 ){
- assert( pCsr->ePlan==FTS5_PLAN_ROWID );
- sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
- }else{
- sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
- sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
- }
- rc = fts5NextMethod(pCursor);
- }
- }
- filter_out:
- sqlite3Fts5ExprFree(pExpr);
- pConfig->pzErrmsg = pzErrmsg;
- pConfig->bPrefixInsttoken = bPrefixInsttoken;
- return rc;
- }
- /*
- ** This is the xEof method of the virtual table. SQLite calls this
- ** routine to find out if it has reached the end of a result set.
- */
- static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- return (CsrFlagTest(pCsr, FTS5CSR_EOF) ? 1 : 0);
- }
- /*
- ** Return the rowid that the cursor currently points to.
- */
- static i64 fts5CursorRowid(Fts5Cursor *pCsr){
- assert( pCsr->ePlan==FTS5_PLAN_MATCH
- || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
- || pCsr->ePlan==FTS5_PLAN_SOURCE
- || pCsr->ePlan==FTS5_PLAN_SCAN
- || pCsr->ePlan==FTS5_PLAN_ROWID
- );
- if( pCsr->pSorter ){
- return pCsr->pSorter->iRowid;
- }else if( pCsr->ePlan>=FTS5_PLAN_SCAN ){
- return sqlite3_column_int64(pCsr->pStmt, 0);
- }else{
- return sqlite3Fts5ExprRowid(pCsr->pExpr);
- }
- }
- /*
- ** This is the xRowid method. The SQLite core calls this routine to
- ** retrieve the rowid for the current row of the result set. fts5
- ** exposes %_content.rowid as the rowid for the virtual table. The
- ** rowid should be written to *pRowid.
- */
- static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int ePlan = pCsr->ePlan;
-
- assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
- if( ePlan==FTS5_PLAN_SPECIAL ){
- *pRowid = 0;
- }else{
- *pRowid = fts5CursorRowid(pCsr);
- }
- return SQLITE_OK;
- }
- /*
- ** If the cursor requires seeking (bSeekRequired flag is set), seek it.
- ** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
- **
- ** If argument bErrormsg is true and an error occurs, an error message may
- ** be left in sqlite3_vtab.zErrMsg.
- */
- static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
- int rc = SQLITE_OK;
- /* If the cursor does not yet have a statement handle, obtain one now. */
- if( pCsr->pStmt==0 ){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- int eStmt = fts5StmtType(pCsr);
- rc = sqlite3Fts5StorageStmt(
- pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0)
- );
- assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 );
- assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
- }
- if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
- assert( pCsr->pExpr );
- sqlite3_reset(pCsr->pStmt);
- sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
- pTab->pConfig->bLock++;
- rc = sqlite3_step(pCsr->pStmt);
- pTab->pConfig->bLock--;
- if( rc==SQLITE_ROW ){
- rc = SQLITE_OK;
- CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
- }else{
- rc = sqlite3_reset(pCsr->pStmt);
- if( rc==SQLITE_OK ){
- rc = FTS5_CORRUPT;
- fts5SetVtabError((Fts5FullTable*)pTab,
- "fts5: missing row %lld from content table %s",
- fts5CursorRowid(pCsr),
- pTab->pConfig->zContent
- );
- }else if( pTab->pConfig->pzErrmsg ){
- fts5SetVtabError((Fts5FullTable*)pTab,
- "%s", sqlite3_errmsg(pTab->pConfig->db)
- );
- }
- }
- }
- return rc;
- }
- /*
- ** This function is called to handle an FTS INSERT command. In other words,
- ** an INSERT statement of the form:
- **
- ** INSERT INTO fts(fts) VALUES($pCmd)
- ** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
- **
- ** Argument pVal is the value assigned to column "fts" by the INSERT
- ** statement. This function returns SQLITE_OK if successful, or an SQLite
- ** error code if an error occurs.
- **
- ** The commands implemented by this function are documented in the "Special
- ** INSERT Directives" section of the documentation. It should be updated if
- ** more commands are added to this function.
- */
- static int fts5SpecialInsert(
- Fts5FullTable *pTab, /* Fts5 table object */
- const char *zCmd, /* Text inserted into table-name column */
- sqlite3_value *pVal /* Value inserted into rank column */
- ){
- Fts5Config *pConfig = pTab->p.pConfig;
- int rc = SQLITE_OK;
- int bError = 0;
- int bLoadConfig = 0;
- if( 0==sqlite3_stricmp("delete-all", zCmd) ){
- if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
- fts5SetVtabError(pTab,
- "'delete-all' may only be used with a "
- "contentless or external content fts5 table"
- );
- rc = SQLITE_ERROR;
- }else{
- rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
- }
- bLoadConfig = 1;
- }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
- if( fts5IsContentless(pTab, 1) ){
- fts5SetVtabError(pTab,
- "'rebuild' may not be used with a contentless fts5 table"
- );
- rc = SQLITE_ERROR;
- }else{
- rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
- }
- bLoadConfig = 1;
- }else if( 0==sqlite3_stricmp("optimize", zCmd) ){
- rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
- }else if( 0==sqlite3_stricmp("merge", zCmd) ){
- int nMerge = sqlite3_value_int(pVal);
- rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
- }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
- int iArg = sqlite3_value_int(pVal);
- rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, iArg);
- #ifdef SQLITE_DEBUG
- }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
- pConfig->bPrefixIndex = sqlite3_value_int(pVal);
- #endif
- }else if( 0==sqlite3_stricmp("flush", zCmd) ){
- rc = sqlite3Fts5FlushToDisk(&pTab->p);
- }else{
- rc = sqlite3Fts5FlushToDisk(&pTab->p);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
- }
- if( rc==SQLITE_OK ){
- if( bError ){
- rc = SQLITE_ERROR;
- }else{
- rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0);
- }
- }
- }
- if( rc==SQLITE_OK && bLoadConfig ){
- pTab->p.pConfig->iCookie--;
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
- }
- return rc;
- }
- static int fts5SpecialDelete(
- Fts5FullTable *pTab,
- sqlite3_value **apVal
- ){
- int rc = SQLITE_OK;
- int eType1 = sqlite3_value_type(apVal[1]);
- if( eType1==SQLITE_INTEGER ){
- sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2], 0);
- }
- return rc;
- }
- static void fts5StorageInsert(
- int *pRc,
- Fts5FullTable *pTab,
- sqlite3_value **apVal,
- i64 *piRowid
- ){
- int rc = *pRc;
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, 0, apVal, piRowid);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
- }
- *pRc = rc;
- }
- /*
- **
- ** This function is called when the user attempts an UPDATE on a contentless
- ** table. Parameter bRowidModified is true if the UPDATE statement modifies
- ** the rowid value. Parameter apVal[] contains the new values for each user
- ** defined column of the fts5 table. pConfig is the configuration object of the
- ** table being updated (guaranteed to be contentless). The contentless_delete=1
- ** and contentless_unindexed=1 options may or may not be set.
- **
- ** This function returns SQLITE_OK if the UPDATE can go ahead, or an SQLite
- ** error code if it cannot. In this case an error message is also loaded into
- ** pConfig. Output parameter (*pbContent) is set to true if the caller should
- ** update the %_content table only - not the FTS index or any other shadow
- ** table. This occurs when an UPDATE modifies only UNINDEXED columns of the
- ** table.
- **
- ** An UPDATE may proceed if:
- **
- ** * The only columns modified are UNINDEXED columns, or
- **
- ** * The contentless_delete=1 option was specified and all of the indexed
- ** columns (not a subset) have been modified.
- */
- static int fts5ContentlessUpdate(
- Fts5Config *pConfig,
- sqlite3_value **apVal,
- int bRowidModified,
- int *pbContent
- ){
- int ii;
- int bSeenIndex = 0; /* Have seen modified indexed column */
- int bSeenIndexNC = 0; /* Have seen unmodified indexed column */
- int rc = SQLITE_OK;
- for(ii=0; ii<pConfig->nCol; ii++){
- if( pConfig->abUnindexed[ii]==0 ){
- if( sqlite3_value_nochange(apVal[ii]) ){
- bSeenIndexNC++;
- }else{
- bSeenIndex++;
- }
- }
- }
- if( bSeenIndex==0 && bRowidModified==0 ){
- *pbContent = 1;
- }else{
- if( bSeenIndexNC || pConfig->bContentlessDelete==0 ){
- rc = SQLITE_ERROR;
- sqlite3Fts5ConfigErrmsg(pConfig,
- (pConfig->bContentlessDelete ?
- "%s a subset of columns on fts5 contentless-delete table: %s" :
- "%s contentless fts5 table: %s")
- , "cannot UPDATE", pConfig->zName
- );
- }
- }
- return rc;
- }
- /*
- ** This function is the implementation of the xUpdate callback used by
- ** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
- ** inserted, updated or deleted.
- **
- ** A delete specifies a single argument - the rowid of the row to remove.
- **
- ** Update and insert operations pass:
- **
- ** 1. The "old" rowid, or NULL.
- ** 2. The "new" rowid.
- ** 3. Values for each of the nCol matchable columns.
- ** 4. Values for the two hidden columns (<tablename> and "rank").
- */
- static int fts5UpdateMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- int nArg, /* Size of argument array */
- sqlite3_value **apVal, /* Array of arguments */
- sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
- ){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- Fts5Config *pConfig = pTab->p.pConfig;
- int eType0; /* value_type() of apVal[0] */
- int rc = SQLITE_OK; /* Return code */
- int bUpdateOrDelete = 0;
- /* A transaction must be open when this is called. */
- assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
- assert( pVtab->zErrMsg==0 );
- assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
- assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER
- || sqlite3_value_type(apVal[0])==SQLITE_NULL
- );
- assert( pTab->p.pConfig->pzErrmsg==0 );
- if( pConfig->pgsz==0 ){
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
- if( rc!=SQLITE_OK ) return rc;
- }
- pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- /* Put any active cursors into REQUIRE_SEEK state. */
- fts5TripCursors(pTab);
- eType0 = sqlite3_value_type(apVal[0]);
- if( eType0==SQLITE_NULL
- && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
- ){
- /* A "special" INSERT op. These are handled separately. */
- const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]);
- if( pConfig->eContent!=FTS5_CONTENT_NORMAL
- && 0==sqlite3_stricmp("delete", z)
- ){
- if( pConfig->bContentlessDelete ){
- fts5SetVtabError(pTab,
- "'delete' may not be used with a contentless_delete=1 table"
- );
- rc = SQLITE_ERROR;
- }else{
- rc = fts5SpecialDelete(pTab, apVal);
- bUpdateOrDelete = 1;
- }
- }else{
- rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
- }
- }else{
- /* A regular INSERT, UPDATE or DELETE statement. The trick here is that
- ** any conflict on the rowid value must be detected before any
- ** modifications are made to the database file. There are 4 cases:
- **
- ** 1) DELETE
- ** 2) UPDATE (rowid not modified)
- ** 3) UPDATE (rowid modified)
- ** 4) INSERT
- **
- ** Cases 3 and 4 may violate the rowid constraint.
- */
- int eConflict = SQLITE_ABORT;
- if( pConfig->eContent==FTS5_CONTENT_NORMAL || pConfig->bContentlessDelete ){
- eConflict = sqlite3_vtab_on_conflict(pConfig->db);
- }
- assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
- assert( nArg!=1 || eType0==SQLITE_INTEGER );
- /* DELETE */
- if( nArg==1 ){
- /* It is only possible to DELETE from a contentless table if the
- ** contentless_delete=1 flag is set. */
- if( fts5IsContentless(pTab, 1) && pConfig->bContentlessDelete==0 ){
- fts5SetVtabError(pTab,
- "cannot DELETE from contentless fts5 table: %s", pConfig->zName
- );
- rc = SQLITE_ERROR;
- }else{
- i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0);
- bUpdateOrDelete = 1;
- }
- }
- /* INSERT or UPDATE */
- else{
- int eType1 = sqlite3_value_numeric_type(apVal[1]);
- /* It is an error to write an fts5_locale() value to a table without
- ** the locale=1 option. */
- if( pConfig->bLocale==0 ){
- int ii;
- for(ii=0; ii<pConfig->nCol; ii++){
- sqlite3_value *pVal = apVal[ii+2];
- if( sqlite3Fts5IsLocaleValue(pConfig, pVal) ){
- fts5SetVtabError(pTab, "fts5_locale() requires locale=1");
- rc = SQLITE_MISMATCH;
- goto update_out;
- }
- }
- }
- if( eType0!=SQLITE_INTEGER ){
- /* An INSERT statement. If the conflict-mode is REPLACE, first remove
- ** the current entry (if any). */
- if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
- i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
- rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0);
- bUpdateOrDelete = 1;
- }
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
- }
- /* UPDATE */
- else{
- Fts5Storage *pStorage = pTab->pStorage;
- i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
- i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
- int bContent = 0; /* Content only update */
- /* If this is a contentless table (including contentless_unindexed=1
- ** tables), check if the UPDATE may proceed. */
- if( fts5IsContentless(pTab, 1) ){
- rc = fts5ContentlessUpdate(pConfig, &apVal[2], iOld!=iNew, &bContent);
- if( rc!=SQLITE_OK ) goto update_out;
- }
- if( eType1!=SQLITE_INTEGER ){
- rc = SQLITE_MISMATCH;
- }else if( iOld!=iNew ){
- assert( bContent==0 );
- if( eConflict==SQLITE_REPLACE ){
- rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageDelete(pStorage, iNew, 0, 0);
- }
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
- }else{
- rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageContentInsert(pStorage, 0, apVal, pRowid);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 0);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageIndexInsert(pStorage, apVal, *pRowid);
- }
- }
- }else if( bContent ){
- /* This occurs when an UPDATE on a contentless table affects *only*
- ** UNINDEXED columns. This is a no-op for contentless_unindexed=0
- ** tables, or a write to the %_content table only for =1 tables. */
- assert( fts5IsContentless(pTab, 1) );
- rc = sqlite3Fts5StorageFindDeleteRow(pStorage, iOld);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5StorageContentInsert(pStorage, 1, apVal, pRowid);
- }
- }else{
- rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
- fts5StorageInsert(&rc, pTab, apVal, pRowid);
- }
- bUpdateOrDelete = 1;
- sqlite3Fts5StorageReleaseDeleteRow(pStorage);
- }
- }
- }
- if( rc==SQLITE_OK
- && bUpdateOrDelete
- && pConfig->bSecureDelete
- && pConfig->iVersion==FTS5_CURRENT_VERSION
- ){
- rc = sqlite3Fts5StorageConfigValue(
- pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE
- );
- if( rc==SQLITE_OK ){
- pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE;
- }
- }
- update_out:
- pTab->p.pConfig->pzErrmsg = 0;
- return rc;
- }
- /*
- ** Implementation of xSync() method.
- */
- static int fts5SyncMethod(sqlite3_vtab *pVtab){
- int rc;
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
- pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- rc = sqlite3Fts5FlushToDisk(&pTab->p);
- pTab->p.pConfig->pzErrmsg = 0;
- return rc;
- }
- /*
- ** Implementation of xBegin() method.
- */
- static int fts5BeginMethod(sqlite3_vtab *pVtab){
- int rc = fts5NewTransaction((Fts5FullTable*)pVtab);
- if( rc==SQLITE_OK ){
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0);
- }
- return rc;
- }
- /*
- ** Implementation of xCommit() method. This is a no-op. The contents of
- ** the pending-terms hash-table have already been flushed into the database
- ** by fts5SyncMethod().
- */
- static int fts5CommitMethod(sqlite3_vtab *pVtab){
- UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0);
- return SQLITE_OK;
- }
- /*
- ** Implementation of xRollback(). Discard the contents of the pending-terms
- ** hash-table. Any changes made to the database are reverted by SQLite.
- */
- static int fts5RollbackMethod(sqlite3_vtab *pVtab){
- int rc;
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
- rc = sqlite3Fts5StorageRollback(pTab->pStorage);
- return rc;
- }
- static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*);
- static void *fts5ApiUserData(Fts5Context *pCtx){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- return pCsr->pAux->pUserData;
- }
- static int fts5ApiColumnCount(Fts5Context *pCtx){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol;
- }
- static int fts5ApiColumnTotalSize(
- Fts5Context *pCtx,
- int iCol,
- sqlite3_int64 *pnToken
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken);
- }
- static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
- }
- /*
- ** Implementation of xTokenize_v2() API.
- */
- static int fts5ApiTokenize_v2(
- Fts5Context *pCtx,
- const char *pText, int nText,
- const char *pLoc, int nLoc,
- void *pUserData,
- int (*xToken)(void*, int, const char*, int, int, int)
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
- int rc = SQLITE_OK;
- sqlite3Fts5SetLocale(pTab->pConfig, pLoc, nLoc);
- rc = sqlite3Fts5Tokenize(pTab->pConfig,
- FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken
- );
- sqlite3Fts5SetLocale(pTab->pConfig, 0, 0);
- return rc;
- }
- /*
- ** Implementation of xTokenize() API. This is just xTokenize_v2() with NULL/0
- ** passed as the locale.
- */
- static int fts5ApiTokenize(
- Fts5Context *pCtx,
- const char *pText, int nText,
- void *pUserData,
- int (*xToken)(void*, int, const char*, int, int, int)
- ){
- return fts5ApiTokenize_v2(pCtx, pText, nText, 0, 0, pUserData, xToken);
- }
- static int fts5ApiPhraseCount(Fts5Context *pCtx){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
- }
- static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
- }
- /*
- ** Argument pStmt is an SQL statement of the type used by Fts5Cursor. This
- ** function extracts the text value of column iCol of the current row.
- ** Additionally, if there is an associated locale, it invokes
- ** sqlite3Fts5SetLocale() to configure the tokenizer. In all cases the caller
- ** should invoke sqlite3Fts5ClearLocale() to clear the locale at some point
- ** after this function returns.
- **
- ** If successful, (*ppText) is set to point to a buffer containing the text
- ** value as utf-8 and SQLITE_OK returned. (*pnText) is set to the size of that
- ** buffer in bytes. It is not guaranteed to be nul-terminated. If an error
- ** occurs, an SQLite error code is returned. The final values of the two
- ** output parameters are undefined in this case.
- */
- static int fts5TextFromStmt(
- Fts5Config *pConfig,
- sqlite3_stmt *pStmt,
- int iCol,
- const char **ppText,
- int *pnText
- ){
- sqlite3_value *pVal = sqlite3_column_value(pStmt, iCol+1);
- const char *pLoc = 0;
- int nLoc = 0;
- int rc = SQLITE_OK;
- if( pConfig->bLocale
- && pConfig->eContent==FTS5_CONTENT_EXTERNAL
- && sqlite3Fts5IsLocaleValue(pConfig, pVal)
- ){
- rc = sqlite3Fts5DecodeLocaleValue(pVal, ppText, pnText, &pLoc, &nLoc);
- }else{
- *ppText = (const char*)sqlite3_value_text(pVal);
- *pnText = sqlite3_value_bytes(pVal);
- if( pConfig->bLocale && pConfig->eContent==FTS5_CONTENT_NORMAL ){
- pLoc = (const char*)sqlite3_column_text(pStmt, iCol+1+pConfig->nCol);
- nLoc = sqlite3_column_bytes(pStmt, iCol+1+pConfig->nCol);
- }
- }
- sqlite3Fts5SetLocale(pConfig, pLoc, nLoc);
- return rc;
- }
- static int fts5ApiColumnText(
- Fts5Context *pCtx,
- int iCol,
- const char **pz,
- int *pn
- ){
- int rc = SQLITE_OK;
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
- assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
- if( iCol<0 || iCol>=pTab->pConfig->nCol ){
- rc = SQLITE_RANGE;
- }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab), 0) ){
- *pz = 0;
- *pn = 0;
- }else{
- rc = fts5SeekCursor(pCsr, 0);
- if( rc==SQLITE_OK ){
- rc = fts5TextFromStmt(pTab->pConfig, pCsr->pStmt, iCol, pz, pn);
- sqlite3Fts5ClearLocale(pTab->pConfig);
- }
- }
- return rc;
- }
- /*
- ** This is called by various API functions - xInst, xPhraseFirst,
- ** xPhraseFirstColumn etc. - to obtain the position list for phrase iPhrase
- ** of the current row. This function works for both detail=full tables (in
- ** which case the position-list was read from the fts index) or for other
- ** detail= modes if the row content is available.
- */
- static int fts5CsrPoslist(
- Fts5Cursor *pCsr, /* Fts5 cursor object */
- int iPhrase, /* Phrase to find position list for */
- const u8 **pa, /* OUT: Pointer to position list buffer */
- int *pn /* OUT: Size of (*pa) in bytes */
- ){
- Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
- int rc = SQLITE_OK;
- int bLive = (pCsr->pSorter==0);
- if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
- rc = SQLITE_RANGE;
- }else if( pConfig->eDetail!=FTS5_DETAIL_FULL
- && fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1)
- ){
- *pa = 0;
- *pn = 0;
- return SQLITE_OK;
- }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
- if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
- Fts5PoslistPopulator *aPopulator;
- int i;
- aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
- if( aPopulator==0 ) rc = SQLITE_NOMEM;
- if( rc==SQLITE_OK ){
- rc = fts5SeekCursor(pCsr, 0);
- }
- for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
- const char *z = 0;
- int n = 0;
- rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5ExprPopulatePoslists(
- pConfig, pCsr->pExpr, aPopulator, i, z, n
- );
- }
- sqlite3Fts5ClearLocale(pConfig);
- }
- sqlite3_free(aPopulator);
- if( pCsr->pSorter ){
- sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
- }
- }
- CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
- }
- if( rc==SQLITE_OK ){
- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
- *pn = pSorter->aIdx[iPhrase] - i1;
- *pa = &pSorter->aPoslist[i1];
- }else{
- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
- }
- }else{
- *pa = 0;
- *pn = 0;
- }
- return rc;
- }
- /*
- ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
- ** correctly for the current view. Return SQLITE_OK if successful, or an
- ** SQLite error code otherwise.
- */
- static int fts5CacheInstArray(Fts5Cursor *pCsr){
- int rc = SQLITE_OK;
- Fts5PoslistReader *aIter; /* One iterator for each phrase */
- int nIter; /* Number of iterators/phrases */
- int nCol = ((Fts5Table*)pCsr->base.pVtab)->pConfig->nCol;
-
- nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
- if( pCsr->aInstIter==0 ){
- sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nIter;
- pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte);
- }
- aIter = pCsr->aInstIter;
- if( aIter ){
- int nInst = 0; /* Number instances seen so far */
- int i;
- /* Initialize all iterators */
- for(i=0; i<nIter && rc==SQLITE_OK; i++){
- const u8 *a;
- int n;
- rc = fts5CsrPoslist(pCsr, i, &a, &n);
- if( rc==SQLITE_OK ){
- sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
- }
- }
- if( rc==SQLITE_OK ){
- while( 1 ){
- int *aInst;
- int iBest = -1;
- for(i=0; i<nIter; i++){
- if( (aIter[i].bEof==0)
- && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
- ){
- iBest = i;
- }
- }
- if( iBest<0 ) break;
- nInst++;
- if( nInst>=pCsr->nInstAlloc ){
- int nNewSize = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
- aInst = (int*)sqlite3_realloc64(
- pCsr->aInst, nNewSize*sizeof(int)*3
- );
- if( aInst ){
- pCsr->aInst = aInst;
- pCsr->nInstAlloc = nNewSize;
- }else{
- nInst--;
- rc = SQLITE_NOMEM;
- break;
- }
- }
- aInst = &pCsr->aInst[3 * (nInst-1)];
- aInst[0] = iBest;
- aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
- aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
- assert( aInst[1]>=0 );
- if( aInst[1]>=nCol ){
- rc = FTS5_CORRUPT;
- break;
- }
- sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
- }
- }
- pCsr->nInstCount = nInst;
- CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST);
- }
- return rc;
- }
- static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
- || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){
- *pnInst = pCsr->nInstCount;
- }
- return rc;
- }
- static int fts5ApiInst(
- Fts5Context *pCtx,
- int iIdx,
- int *piPhrase,
- int *piCol,
- int *piOff
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
- || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
- ){
- if( iIdx<0 || iIdx>=pCsr->nInstCount ){
- rc = SQLITE_RANGE;
- }else{
- *piPhrase = pCsr->aInst[iIdx*3];
- *piCol = pCsr->aInst[iIdx*3 + 1];
- *piOff = pCsr->aInst[iIdx*3 + 2];
- }
- }
- return rc;
- }
- static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){
- return fts5CursorRowid((Fts5Cursor*)pCtx);
- }
- static int fts5ColumnSizeCb(
- void *pContext, /* Pointer to int */
- int tflags,
- const char *pUnused, /* Buffer containing token */
- int nUnused, /* Size of token in bytes */
- int iUnused1, /* Start offset of token */
- int iUnused2 /* End offset of token */
- ){
- int *pCnt = (int*)pContext;
- UNUSED_PARAM2(pUnused, nUnused);
- UNUSED_PARAM2(iUnused1, iUnused2);
- if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
- (*pCnt)++;
- }
- return SQLITE_OK;
- }
- static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- Fts5Config *pConfig = pTab->p.pConfig;
- int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
- if( pConfig->bColumnsize ){
- i64 iRowid = fts5CursorRowid(pCsr);
- rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
- }else if( !pConfig->zContent || pConfig->eContent==FTS5_CONTENT_UNINDEXED ){
- int i;
- for(i=0; i<pConfig->nCol; i++){
- if( pConfig->abUnindexed[i]==0 ){
- pCsr->aColumnSize[i] = -1;
- }
- }
- }else{
- int i;
- rc = fts5SeekCursor(pCsr, 0);
- for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
- if( pConfig->abUnindexed[i]==0 ){
- const char *z = 0;
- int n = 0;
- pCsr->aColumnSize[i] = 0;
- rc = fts5TextFromStmt(pConfig, pCsr->pStmt, i, &z, &n);
- if( rc==SQLITE_OK ){
- rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_AUX,
- z, n, (void*)&pCsr->aColumnSize[i], fts5ColumnSizeCb
- );
- }
- sqlite3Fts5ClearLocale(pConfig);
- }
- }
- }
- CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
- }
- if( iCol<0 ){
- int i;
- *pnToken = 0;
- for(i=0; i<pConfig->nCol; i++){
- *pnToken += pCsr->aColumnSize[i];
- }
- }else if( iCol<pConfig->nCol ){
- *pnToken = pCsr->aColumnSize[iCol];
- }else{
- *pnToken = 0;
- rc = SQLITE_RANGE;
- }
- return rc;
- }
- /*
- ** Implementation of the xSetAuxdata() method.
- */
- static int fts5ApiSetAuxdata(
- Fts5Context *pCtx, /* Fts5 context */
- void *pPtr, /* Pointer to save as auxdata */
- void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Auxdata *pData;
- /* Search through the cursors list of Fts5Auxdata objects for one that
- ** corresponds to the currently executing auxiliary function. */
- for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
- if( pData->pAux==pCsr->pAux ) break;
- }
- if( pData ){
- if( pData->xDelete ){
- pData->xDelete(pData->pPtr);
- }
- }else{
- int rc = SQLITE_OK;
- pData = (Fts5Auxdata*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Auxdata));
- if( pData==0 ){
- if( xDelete ) xDelete(pPtr);
- return rc;
- }
- pData->pAux = pCsr->pAux;
- pData->pNext = pCsr->pAuxdata;
- pCsr->pAuxdata = pData;
- }
- pData->xDelete = xDelete;
- pData->pPtr = pPtr;
- return SQLITE_OK;
- }
- static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Auxdata *pData;
- void *pRet = 0;
- for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
- if( pData->pAux==pCsr->pAux ) break;
- }
- if( pData ){
- pRet = pData->pPtr;
- if( bClear ){
- pData->pPtr = 0;
- pData->xDelete = 0;
- }
- }
- return pRet;
- }
- static void fts5ApiPhraseNext(
- Fts5Context *pCtx,
- Fts5PhraseIter *pIter,
- int *piCol, int *piOff
- ){
- if( pIter->a>=pIter->b ){
- *piCol = -1;
- *piOff = -1;
- }else{
- int iVal;
- pIter->a += fts5GetVarint32(pIter->a, iVal);
- if( iVal==1 ){
- /* Avoid returning a (*piCol) value that is too large for the table,
- ** even if the position-list is corrupt. The caller might not be
- ** expecting it. */
- int nCol = ((Fts5Table*)(((Fts5Cursor*)pCtx)->base.pVtab))->pConfig->nCol;
- pIter->a += fts5GetVarint32(pIter->a, iVal);
- *piCol = (iVal>=nCol ? nCol-1 : iVal);
- *piOff = 0;
- pIter->a += fts5GetVarint32(pIter->a, iVal);
- }
- *piOff += (iVal-2);
- }
- }
- static int fts5ApiPhraseFirst(
- Fts5Context *pCtx,
- int iPhrase,
- Fts5PhraseIter *pIter,
- int *piCol, int *piOff
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- int n;
- int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
- if( rc==SQLITE_OK ){
- assert( pIter->a || n==0 );
- pIter->b = (pIter->a ? &pIter->a[n] : 0);
- *piCol = 0;
- *piOff = 0;
- fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
- }
- return rc;
- }
- static void fts5ApiPhraseNextColumn(
- Fts5Context *pCtx,
- Fts5PhraseIter *pIter,
- int *piCol
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
- if( pIter->a>=pIter->b ){
- *piCol = -1;
- }else{
- int iIncr;
- pIter->a += fts5GetVarint32(&pIter->a[0], iIncr);
- *piCol += (iIncr-2);
- }
- }else{
- while( 1 ){
- int dummy;
- if( pIter->a>=pIter->b ){
- *piCol = -1;
- return;
- }
- if( pIter->a[0]==0x01 ) break;
- pIter->a += fts5GetVarint32(pIter->a, dummy);
- }
- pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
- }
- }
- static int fts5ApiPhraseFirstColumn(
- Fts5Context *pCtx,
- int iPhrase,
- Fts5PhraseIter *pIter,
- int *piCol
- ){
- int rc = SQLITE_OK;
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
- if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int n;
- if( pSorter ){
- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
- n = pSorter->aIdx[iPhrase] - i1;
- pIter->a = &pSorter->aPoslist[i1];
- }else{
- rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
- }
- if( rc==SQLITE_OK ){
- assert( pIter->a || n==0 );
- pIter->b = (pIter->a ? &pIter->a[n] : 0);
- *piCol = 0;
- fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
- }
- }else{
- int n;
- rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
- if( rc==SQLITE_OK ){
- assert( pIter->a || n==0 );
- pIter->b = (pIter->a ? &pIter->a[n] : 0);
- if( n<=0 ){
- *piCol = -1;
- }else if( pIter->a[0]==0x01 ){
- pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
- }else{
- *piCol = 0;
- }
- }
- }
- return rc;
- }
- /*
- ** xQueryToken() API implemenetation.
- */
- static int fts5ApiQueryToken(
- Fts5Context* pCtx,
- int iPhrase,
- int iToken,
- const char **ppOut,
- int *pnOut
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
- }
- /*
- ** xInstToken() API implemenetation.
- */
- static int fts5ApiInstToken(
- Fts5Context *pCtx,
- int iIdx,
- int iToken,
- const char **ppOut, int *pnOut
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- int rc = SQLITE_OK;
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
- || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
- ){
- if( iIdx<0 || iIdx>=pCsr->nInstCount ){
- rc = SQLITE_RANGE;
- }else{
- int iPhrase = pCsr->aInst[iIdx*3];
- int iCol = pCsr->aInst[iIdx*3 + 1];
- int iOff = pCsr->aInst[iIdx*3 + 2];
- i64 iRowid = fts5CursorRowid(pCsr);
- rc = sqlite3Fts5ExprInstToken(
- pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
- );
- }
- }
- return rc;
- }
- static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
- int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
- );
- /*
- ** The xColumnLocale() API.
- */
- static int fts5ApiColumnLocale(
- Fts5Context *pCtx,
- int iCol,
- const char **pzLocale,
- int *pnLocale
- ){
- int rc = SQLITE_OK;
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
- *pzLocale = 0;
- *pnLocale = 0;
- assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
- if( iCol<0 || iCol>=pConfig->nCol ){
- rc = SQLITE_RANGE;
- }else if(
- pConfig->abUnindexed[iCol]==0
- && 0==fts5IsContentless((Fts5FullTable*)pCsr->base.pVtab, 1)
- && pConfig->bLocale
- ){
- rc = fts5SeekCursor(pCsr, 0);
- if( rc==SQLITE_OK ){
- const char *zDummy = 0;
- int nDummy = 0;
- rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &zDummy, &nDummy);
- if( rc==SQLITE_OK ){
- *pzLocale = pConfig->t.pLocale;
- *pnLocale = pConfig->t.nLocale;
- }
- sqlite3Fts5ClearLocale(pConfig);
- }
- }
- return rc;
- }
- static const Fts5ExtensionApi sFts5Api = {
- 4, /* iVersion */
- fts5ApiUserData,
- fts5ApiColumnCount,
- fts5ApiRowCount,
- fts5ApiColumnTotalSize,
- fts5ApiTokenize,
- fts5ApiPhraseCount,
- fts5ApiPhraseSize,
- fts5ApiInstCount,
- fts5ApiInst,
- fts5ApiRowid,
- fts5ApiColumnText,
- fts5ApiColumnSize,
- fts5ApiQueryPhrase,
- fts5ApiSetAuxdata,
- fts5ApiGetAuxdata,
- fts5ApiPhraseFirst,
- fts5ApiPhraseNext,
- fts5ApiPhraseFirstColumn,
- fts5ApiPhraseNextColumn,
- fts5ApiQueryToken,
- fts5ApiInstToken,
- fts5ApiColumnLocale,
- fts5ApiTokenize_v2
- };
- /*
- ** Implementation of API function xQueryPhrase().
- */
- static int fts5ApiQueryPhrase(
- Fts5Context *pCtx,
- int iPhrase,
- void *pUserData,
- int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*)
- ){
- Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
- int rc;
- Fts5Cursor *pNew = 0;
- rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew);
- if( rc==SQLITE_OK ){
- pNew->ePlan = FTS5_PLAN_MATCH;
- pNew->iFirstRowid = SMALLEST_INT64;
- pNew->iLastRowid = LARGEST_INT64;
- pNew->base.pVtab = (sqlite3_vtab*)pTab;
- rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr);
- }
- if( rc==SQLITE_OK ){
- for(rc = fts5CursorFirst(pTab, pNew, 0);
- rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0;
- rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew)
- ){
- rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData);
- if( rc!=SQLITE_OK ){
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
- break;
- }
- }
- }
- fts5CloseMethod((sqlite3_vtab_cursor*)pNew);
- return rc;
- }
- static void fts5ApiInvoke(
- Fts5Auxiliary *pAux,
- Fts5Cursor *pCsr,
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
- ){
- assert( pCsr->pAux==0 );
- assert( pCsr->ePlan!=FTS5_PLAN_SPECIAL );
- pCsr->pAux = pAux;
- pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv);
- pCsr->pAux = 0;
- }
- static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){
- Fts5Cursor *pCsr;
- for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
- if( pCsr->iCsrId==iCsrId ) break;
- }
- return pCsr;
- }
- /*
- ** Parameter zFmt is a printf() style formatting string. This function
- ** formats it using the trailing arguments and returns the result as
- ** an error message to the context passed as the first argument.
- */
- static void fts5ResultError(sqlite3_context *pCtx, const char *zFmt, ...){
- char *zErr = 0;
- va_list ap;
- va_start(ap, zFmt);
- zErr = sqlite3_vmprintf(zFmt, ap);
- sqlite3_result_error(pCtx, zErr, -1);
- sqlite3_free(zErr);
- va_end(ap);
- }
- static void fts5ApiCallback(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
- ){
- Fts5Auxiliary *pAux;
- Fts5Cursor *pCsr;
- i64 iCsrId;
- assert( argc>=1 );
- pAux = (Fts5Auxiliary*)sqlite3_user_data(context);
- iCsrId = sqlite3_value_int64(argv[0]);
- pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
- if( pCsr==0 || (pCsr->ePlan==0 || pCsr->ePlan==FTS5_PLAN_SPECIAL) ){
- fts5ResultError(context, "no such cursor: %lld", iCsrId);
- }else{
- sqlite3_vtab *pTab = pCsr->base.pVtab;
- fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);
- sqlite3_free(pTab->zErrMsg);
- pTab->zErrMsg = 0;
- }
- }
- /*
- ** Given cursor id iId, return a pointer to the corresponding Fts5Table
- ** object. Or NULL If the cursor id does not exist.
- */
- Fts5Table *sqlite3Fts5TableFromCsrid(
- Fts5Global *pGlobal, /* FTS5 global context for db handle */
- i64 iCsrId /* Id of cursor to find */
- ){
- Fts5Cursor *pCsr;
- pCsr = fts5CursorFromCsrid(pGlobal, iCsrId);
- if( pCsr ){
- return (Fts5Table*)pCsr->base.pVtab;
- }
- return 0;
- }
- /*
- ** Return a "position-list blob" corresponding to the current position of
- ** cursor pCsr via sqlite3_result_blob(). A position-list blob contains
- ** the current position-list for each phrase in the query associated with
- ** cursor pCsr.
- **
- ** A position-list blob begins with (nPhrase-1) varints, where nPhrase is
- ** the number of phrases in the query. Following the varints are the
- ** concatenated position lists for each phrase, in order.
- **
- ** The first varint (if it exists) contains the size of the position list
- ** for phrase 0. The second (same disclaimer) contains the size of position
- ** list 1. And so on. There is no size field for the final position list,
- ** as it can be derived from the total size of the blob.
- */
- static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){
- int i;
- int rc = SQLITE_OK;
- int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
- Fts5Buffer val;
- memset(&val, 0, sizeof(Fts5Buffer));
- switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){
- case FTS5_DETAIL_FULL:
- /* Append the varints */
- for(i=0; i<(nPhrase-1); i++){
- const u8 *dummy;
- int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy);
- sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
- }
- /* Append the position lists */
- for(i=0; i<nPhrase; i++){
- const u8 *pPoslist;
- int nPoslist;
- nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist);
- sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
- }
- break;
- case FTS5_DETAIL_COLUMNS:
- /* Append the varints */
- for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){
- const u8 *dummy;
- int nByte;
- rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte);
- sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
- }
- /* Append the position lists */
- for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
- const u8 *pPoslist;
- int nPoslist;
- rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist);
- sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
- }
- break;
- default:
- break;
- }
- sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
- return rc;
- }
- /*
- ** This is the xColumn method, called by SQLite to request a value from
- ** the row that the supplied cursor currently points to.
- */
- static int fts5ColumnMethod(
- sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
- int iCol /* Index of column to read value from */
- ){
- Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab);
- Fts5Config *pConfig = pTab->p.pConfig;
- Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
- int rc = SQLITE_OK;
-
- assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
- if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){
- if( iCol==pConfig->nCol ){
- sqlite3_result_int64(pCtx, pCsr->iSpecial);
- }
- }else
- if( iCol==pConfig->nCol ){
- /* User is requesting the value of the special column with the same name
- ** as the table. Return the cursor integer id number. This value is only
- ** useful in that it may be passed as the first argument to an FTS5
- ** auxiliary function. */
- sqlite3_result_int64(pCtx, pCsr->iCsrId);
- }else if( iCol==pConfig->nCol+1 ){
- /* The value of the "rank" column. */
- if( pCsr->ePlan==FTS5_PLAN_SOURCE ){
- fts5PoslistBlob(pCtx, pCsr);
- }else if(
- pCsr->ePlan==FTS5_PLAN_MATCH
- || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
- ){
- if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){
- fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
- }
- }
- }else{
- if( !sqlite3_vtab_nochange(pCtx) && pConfig->eContent!=FTS5_CONTENT_NONE ){
- pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- rc = fts5SeekCursor(pCsr, 1);
- if( rc==SQLITE_OK ){
- sqlite3_value *pVal = sqlite3_column_value(pCsr->pStmt, iCol+1);
- if( pConfig->bLocale
- && pConfig->eContent==FTS5_CONTENT_EXTERNAL
- && sqlite3Fts5IsLocaleValue(pConfig, pVal)
- ){
- const char *z = 0;
- int n = 0;
- rc = fts5TextFromStmt(pConfig, pCsr->pStmt, iCol, &z, &n);
- if( rc==SQLITE_OK ){
- sqlite3_result_text(pCtx, z, n, SQLITE_TRANSIENT);
- }
- sqlite3Fts5ClearLocale(pConfig);
- }else{
- sqlite3_result_value(pCtx, pVal);
- }
- }
- pConfig->pzErrmsg = 0;
- }
- }
- return rc;
- }
- /*
- ** This routine implements the xFindFunction method for the FTS3
- ** virtual table.
- */
- static int fts5FindFunctionMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- int nUnused, /* Number of SQL function arguments */
- const char *zName, /* Name of SQL function */
- void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
- void **ppArg /* OUT: User data for *pxFunc */
- ){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- Fts5Auxiliary *pAux;
- UNUSED_PARAM(nUnused);
- pAux = fts5FindAuxiliary(pTab, zName);
- if( pAux ){
- *pxFunc = fts5ApiCallback;
- *ppArg = (void*)pAux;
- return 1;
- }
- /* No function of the specified name was found. Return 0. */
- return 0;
- }
- /*
- ** Implementation of FTS5 xRename method. Rename an fts5 table.
- */
- static int fts5RenameMethod(
- sqlite3_vtab *pVtab, /* Virtual table handle */
- const char *zName /* New name of table */
- ){
- int rc;
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
- return rc;
- }
- int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
- fts5TripCursors((Fts5FullTable*)pTab);
- return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
- }
- /*
- ** The xSavepoint() method.
- **
- ** Flush the contents of the pending-terms table to disk.
- */
- static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- int rc = SQLITE_OK;
- fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
- rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
- if( rc==SQLITE_OK ){
- pTab->iSavepoint = iSavepoint+1;
- }
- return rc;
- }
- /*
- ** The xRelease() method.
- **
- ** This is a no-op.
- */
- static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- int rc = SQLITE_OK;
- fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
- if( (iSavepoint+1)<pTab->iSavepoint ){
- rc = sqlite3Fts5FlushToDisk(&pTab->p);
- if( rc==SQLITE_OK ){
- pTab->iSavepoint = iSavepoint;
- }
- }
- return rc;
- }
- /*
- ** The xRollbackTo() method.
- **
- ** Discard the contents of the pending terms table.
- */
- static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- int rc = SQLITE_OK;
- fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
- fts5TripCursors(pTab);
- if( (iSavepoint+1)<=pTab->iSavepoint ){
- pTab->p.pConfig->pgsz = 0;
- rc = sqlite3Fts5StorageRollback(pTab->pStorage);
- }
- return rc;
- }
- /*
- ** Register a new auxiliary function with global context pGlobal.
- */
- static int fts5CreateAux(
- fts5_api *pApi, /* Global context (one per db handle) */
- const char *zName, /* Name of new function */
- void *pUserData, /* User data for aux. function */
- fts5_extension_function xFunc, /* Aux. function implementation */
- void(*xDestroy)(void*) /* Destructor for pUserData */
- ){
- Fts5Global *pGlobal = (Fts5Global*)pApi;
- int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
- if( rc==SQLITE_OK ){
- Fts5Auxiliary *pAux;
- sqlite3_int64 nName; /* Size of zName in bytes, including \0 */
- sqlite3_int64 nByte; /* Bytes of space to allocate */
- nName = strlen(zName) + 1;
- nByte = sizeof(Fts5Auxiliary) + nName;
- pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte);
- if( pAux ){
- memset(pAux, 0, (size_t)nByte);
- pAux->zFunc = (char*)&pAux[1];
- memcpy(pAux->zFunc, zName, nName);
- pAux->pGlobal = pGlobal;
- pAux->pUserData = pUserData;
- pAux->xFunc = xFunc;
- pAux->xDestroy = xDestroy;
- pAux->pNext = pGlobal->pAux;
- pGlobal->pAux = pAux;
- }else{
- rc = SQLITE_NOMEM;
- }
- }
- return rc;
- }
- /*
- ** This function is used by xCreateTokenizer_v2() and xCreateTokenizer().
- ** It allocates and partially populates a new Fts5TokenizerModule object.
- ** The new object is already linked into the Fts5Global context before
- ** returning.
- **
- ** If successful, SQLITE_OK is returned and a pointer to the new
- ** Fts5TokenizerModule object returned via output parameter (*ppNew). All
- ** that is required is for the caller to fill in the methods in
- ** Fts5TokenizerModule.x1 and x2, and to set Fts5TokenizerModule.bV2Native
- ** as appropriate.
- **
- ** If an error occurs, an SQLite error code is returned and the final value
- ** of (*ppNew) undefined.
- */
- static int fts5NewTokenizerModule(
- Fts5Global *pGlobal, /* Global context (one per db handle) */
- const char *zName, /* Name of new function */
- void *pUserData, /* User data for aux. function */
- void(*xDestroy)(void*), /* Destructor for pUserData */
- Fts5TokenizerModule **ppNew
- ){
- int rc = SQLITE_OK;
- Fts5TokenizerModule *pNew;
- sqlite3_int64 nName; /* Size of zName and its \0 terminator */
- sqlite3_int64 nByte; /* Bytes of space to allocate */
- nName = strlen(zName) + 1;
- nByte = sizeof(Fts5TokenizerModule) + nName;
- *ppNew = pNew = (Fts5TokenizerModule*)sqlite3Fts5MallocZero(&rc, nByte);
- if( pNew ){
- pNew->zName = (char*)&pNew[1];
- memcpy(pNew->zName, zName, nName);
- pNew->pUserData = pUserData;
- pNew->xDestroy = xDestroy;
- pNew->pNext = pGlobal->pTok;
- pGlobal->pTok = pNew;
- if( pNew->pNext==0 ){
- pGlobal->pDfltTok = pNew;
- }
- }
- return rc;
- }
- /*
- ** An instance of this type is used as the Fts5Tokenizer object for
- ** wrapper tokenizers - those that provide access to a v1 tokenizer via
- ** the fts5_tokenizer_v2 API, and those that provide access to a v2 tokenizer
- ** via the fts5_tokenizer API.
- */
- typedef struct Fts5VtoVTokenizer Fts5VtoVTokenizer;
- struct Fts5VtoVTokenizer {
- int bV2Native; /* True if v2 native tokenizer */
- fts5_tokenizer x1; /* Tokenizer functions */
- fts5_tokenizer_v2 x2; /* V2 tokenizer functions */
- Fts5Tokenizer *pReal;
- };
- /*
- ** Create a wrapper tokenizer. The context argument pCtx points to the
- ** Fts5TokenizerModule object.
- */
- static int fts5VtoVCreate(
- void *pCtx,
- const char **azArg,
- int nArg,
- Fts5Tokenizer **ppOut
- ){
- Fts5TokenizerModule *pMod = (Fts5TokenizerModule*)pCtx;
- Fts5VtoVTokenizer *pNew = 0;
- int rc = SQLITE_OK;
- pNew = (Fts5VtoVTokenizer*)sqlite3Fts5MallocZero(&rc, sizeof(*pNew));
- if( rc==SQLITE_OK ){
- pNew->x1 = pMod->x1;
- pNew->x2 = pMod->x2;
- pNew->bV2Native = pMod->bV2Native;
- if( pMod->bV2Native ){
- rc = pMod->x2.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal);
- }else{
- rc = pMod->x1.xCreate(pMod->pUserData, azArg, nArg, &pNew->pReal);
- }
- if( rc!=SQLITE_OK ){
- sqlite3_free(pNew);
- pNew = 0;
- }
- }
- *ppOut = (Fts5Tokenizer*)pNew;
- return rc;
- }
- /*
- ** Delete an Fts5VtoVTokenizer wrapper tokenizer.
- */
- static void fts5VtoVDelete(Fts5Tokenizer *pTok){
- Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
- if( p ){
- if( p->bV2Native ){
- p->x2.xDelete(p->pReal);
- }else{
- p->x1.xDelete(p->pReal);
- }
- sqlite3_free(p);
- }
- }
- /*
- ** xTokenizer method for a wrapper tokenizer that offers the v1 interface
- ** (no support for locales).
- */
- static int fts5V1toV2Tokenize(
- Fts5Tokenizer *pTok,
- void *pCtx, int flags,
- const char *pText, int nText,
- int (*xToken)(void*, int, const char*, int, int, int)
- ){
- Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
- assert( p->bV2Native );
- return p->x2.xTokenize(p->pReal, pCtx, flags, pText, nText, 0, 0, xToken);
- }
- /*
- ** xTokenizer method for a wrapper tokenizer that offers the v2 interface
- ** (with locale support).
- */
- static int fts5V2toV1Tokenize(
- Fts5Tokenizer *pTok,
- void *pCtx, int flags,
- const char *pText, int nText,
- const char *pLocale, int nLocale,
- int (*xToken)(void*, int, const char*, int, int, int)
- ){
- Fts5VtoVTokenizer *p = (Fts5VtoVTokenizer*)pTok;
- assert( p->bV2Native==0 );
- UNUSED_PARAM2(pLocale,nLocale);
- return p->x1.xTokenize(p->pReal, pCtx, flags, pText, nText, xToken);
- }
- /*
- ** Register a new tokenizer. This is the implementation of the
- ** fts5_api.xCreateTokenizer_v2() method.
- */
- static int fts5CreateTokenizer_v2(
- fts5_api *pApi, /* Global context (one per db handle) */
- const char *zName, /* Name of new function */
- void *pUserData, /* User data for aux. function */
- fts5_tokenizer_v2 *pTokenizer, /* Tokenizer implementation */
- void(*xDestroy)(void*) /* Destructor for pUserData */
- ){
- Fts5Global *pGlobal = (Fts5Global*)pApi;
- int rc = SQLITE_OK;
- if( pTokenizer->iVersion>2 ){
- rc = SQLITE_ERROR;
- }else{
- Fts5TokenizerModule *pNew = 0;
- rc = fts5NewTokenizerModule(pGlobal, zName, pUserData, xDestroy, &pNew);
- if( pNew ){
- pNew->x2 = *pTokenizer;
- pNew->bV2Native = 1;
- pNew->x1.xCreate = fts5VtoVCreate;
- pNew->x1.xTokenize = fts5V1toV2Tokenize;
- pNew->x1.xDelete = fts5VtoVDelete;
- }
- }
- return rc;
- }
- /*
- ** The fts5_api.xCreateTokenizer() method.
- */
- static int fts5CreateTokenizer(
- fts5_api *pApi, /* Global context (one per db handle) */
- const char *zName, /* Name of new function */
- void *pUserData, /* User data for aux. function */
- fts5_tokenizer *pTokenizer, /* Tokenizer implementation */
- void(*xDestroy)(void*) /* Destructor for pUserData */
- ){
- Fts5TokenizerModule *pNew = 0;
- int rc = SQLITE_OK;
- rc = fts5NewTokenizerModule(
- (Fts5Global*)pApi, zName, pUserData, xDestroy, &pNew
- );
- if( pNew ){
- pNew->x1 = *pTokenizer;
- pNew->x2.xCreate = fts5VtoVCreate;
- pNew->x2.xTokenize = fts5V2toV1Tokenize;
- pNew->x2.xDelete = fts5VtoVDelete;
- }
- return rc;
- }
- /*
- ** Search the global context passed as the first argument for a tokenizer
- ** module named zName. If found, return a pointer to the Fts5TokenizerModule
- ** object. Otherwise, return NULL.
- */
- static Fts5TokenizerModule *fts5LocateTokenizer(
- Fts5Global *pGlobal, /* Global (one per db handle) object */
- const char *zName /* Name of tokenizer module to find */
- ){
- Fts5TokenizerModule *pMod = 0;
- if( zName==0 ){
- pMod = pGlobal->pDfltTok;
- }else{
- for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){
- if( sqlite3_stricmp(zName, pMod->zName)==0 ) break;
- }
- }
- return pMod;
- }
- /*
- ** Find a tokenizer. This is the implementation of the
- ** fts5_api.xFindTokenizer_v2() method.
- */
- static int fts5FindTokenizer_v2(
- fts5_api *pApi, /* Global context (one per db handle) */
- const char *zName, /* Name of tokenizer */
- void **ppUserData,
- fts5_tokenizer_v2 **ppTokenizer /* Populate this object */
- ){
- int rc = SQLITE_OK;
- Fts5TokenizerModule *pMod;
- pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName);
- if( pMod ){
- if( pMod->bV2Native ){
- *ppUserData = pMod->pUserData;
- }else{
- *ppUserData = (void*)pMod;
- }
- *ppTokenizer = &pMod->x2;
- }else{
- *ppTokenizer = 0;
- *ppUserData = 0;
- rc = SQLITE_ERROR;
- }
- return rc;
- }
- /*
- ** Find a tokenizer. This is the implementation of the
- ** fts5_api.xFindTokenizer() method.
- */
- static int fts5FindTokenizer(
- fts5_api *pApi, /* Global context (one per db handle) */
- const char *zName, /* Name of new function */
- void **ppUserData,
- fts5_tokenizer *pTokenizer /* Populate this object */
- ){
- int rc = SQLITE_OK;
- Fts5TokenizerModule *pMod;
- pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName);
- if( pMod ){
- if( pMod->bV2Native==0 ){
- *ppUserData = pMod->pUserData;
- }else{
- *ppUserData = (void*)pMod;
- }
- *pTokenizer = pMod->x1;
- }else{
- memset(pTokenizer, 0, sizeof(*pTokenizer));
- *ppUserData = 0;
- rc = SQLITE_ERROR;
- }
- return rc;
- }
- /*
- ** Attempt to instantiate the tokenizer.
- */
- int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){
- const char **azArg = pConfig->t.azArg;
- const int nArg = pConfig->t.nArg;
- Fts5TokenizerModule *pMod = 0;
- int rc = SQLITE_OK;
- pMod = fts5LocateTokenizer(pConfig->pGlobal, nArg==0 ? 0 : azArg[0]);
- if( pMod==0 ){
- assert( nArg>0 );
- rc = SQLITE_ERROR;
- sqlite3Fts5ConfigErrmsg(pConfig, "no such tokenizer: %s", azArg[0]);
- }else{
- int (*xCreate)(void*, const char**, int, Fts5Tokenizer**) = 0;
- if( pMod->bV2Native ){
- xCreate = pMod->x2.xCreate;
- pConfig->t.pApi2 = &pMod->x2;
- }else{
- pConfig->t.pApi1 = &pMod->x1;
- xCreate = pMod->x1.xCreate;
- }
-
- rc = xCreate(pMod->pUserData,
- (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok
- );
- if( rc!=SQLITE_OK ){
- if( rc!=SQLITE_NOMEM ){
- sqlite3Fts5ConfigErrmsg(pConfig, "error in tokenizer constructor");
- }
- }else if( pMod->bV2Native==0 ){
- pConfig->t.ePattern = sqlite3Fts5TokenizerPattern(
- pMod->x1.xCreate, pConfig->t.pTok
- );
- }
- }
- if( rc!=SQLITE_OK ){
- pConfig->t.pApi1 = 0;
- pConfig->t.pApi2 = 0;
- pConfig->t.pTok = 0;
- }
- return rc;
- }
- /*
- ** xDestroy callback passed to sqlite3_create_module(). This is invoked
- ** when the db handle is being closed. Free memory associated with
- ** tokenizers and aux functions registered with this db handle.
- */
- static void fts5ModuleDestroy(void *pCtx){
- Fts5TokenizerModule *pTok, *pNextTok;
- Fts5Auxiliary *pAux, *pNextAux;
- Fts5Global *pGlobal = (Fts5Global*)pCtx;
- for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){
- pNextAux = pAux->pNext;
- if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData);
- sqlite3_free(pAux);
- }
- for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){
- pNextTok = pTok->pNext;
- if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData);
- sqlite3_free(pTok);
- }
- sqlite3_free(pGlobal);
- }
- /*
- ** Implementation of the fts5() function used by clients to obtain the
- ** API pointer.
- */
- static void fts5Fts5Func(
- sqlite3_context *pCtx, /* Function call context */
- int nArg, /* Number of args */
- sqlite3_value **apArg /* Function arguments */
- ){
- Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
- fts5_api **ppApi;
- UNUSED_PARAM(nArg);
- assert( nArg==1 );
- ppApi = (fts5_api**)sqlite3_value_pointer(apArg[0], "fts5_api_ptr");
- if( ppApi ) *ppApi = &pGlobal->api;
- }
- /*
- ** Implementation of fts5_source_id() function.
- */
- static void fts5SourceIdFunc(
- sqlite3_context *pCtx, /* Function call context */
- int nArg, /* Number of args */
- sqlite3_value **apUnused /* Function arguments */
- ){
- assert( nArg==0 );
- UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "--FTS5-SOURCE-ID--", -1, SQLITE_TRANSIENT);
- }
- /*
- ** Implementation of fts5_locale(LOCALE, TEXT) function.
- **
- ** If parameter LOCALE is NULL, or a zero-length string, then a copy of
- ** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as
- ** text, and the value returned is a blob consisting of:
- **
- ** * The 4 bytes 0x00, 0xE0, 0xB2, 0xEb (FTS5_LOCALE_HEADER).
- ** * The LOCALE, as utf-8 text, followed by
- ** * 0x00, followed by
- ** * The TEXT, as utf-8 text.
- **
- ** There is no final nul-terminator following the TEXT value.
- */
- static void fts5LocaleFunc(
- sqlite3_context *pCtx, /* Function call context */
- int nArg, /* Number of args */
- sqlite3_value **apArg /* Function arguments */
- ){
- const char *zLocale = 0;
- int nLocale = 0;
- const char *zText = 0;
- int nText = 0;
- assert( nArg==2 );
- UNUSED_PARAM(nArg);
- zLocale = (const char*)sqlite3_value_text(apArg[0]);
- nLocale = sqlite3_value_bytes(apArg[0]);
- zText = (const char*)sqlite3_value_text(apArg[1]);
- nText = sqlite3_value_bytes(apArg[1]);
- if( zLocale==0 || zLocale[0]=='\0' ){
- sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
- }else{
- Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
- u8 *pBlob = 0;
- u8 *pCsr = 0;
- int nBlob = 0;
- nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
- pBlob = (u8*)sqlite3_malloc(nBlob);
- if( pBlob==0 ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- pCsr = pBlob;
- memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE);
- pCsr += FTS5_LOCALE_HDR_SIZE;
- memcpy(pCsr, zLocale, nLocale);
- pCsr += nLocale;
- (*pCsr++) = 0x00;
- if( zText ) memcpy(pCsr, zText, nText);
- assert( &pCsr[nText]==&pBlob[nBlob] );
- sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
- }
- }
- /*
- ** Implementation of fts5_insttoken() function.
- */
- static void fts5InsttokenFunc(
- sqlite3_context *pCtx, /* Function call context */
- int nArg, /* Number of args */
- sqlite3_value **apArg /* Function arguments */
- ){
- assert( nArg==1 );
- (void)nArg;
- sqlite3_result_value(pCtx, apArg[0]);
- sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
- }
- /*
- ** Return true if zName is the extension on one of the shadow tables used
- ** by this module.
- */
- static int fts5ShadowName(const char *zName){
- static const char *azName[] = {
- "config", "content", "data", "docsize", "idx"
- };
- unsigned int i;
- for(i=0; i<sizeof(azName)/sizeof(azName[0]); i++){
- if( sqlite3_stricmp(zName, azName[i])==0 ) return 1;
- }
- return 0;
- }
- /*
- ** Run an integrity check on the FTS5 data structures. Return a string
- ** if anything is found amiss. Return a NULL pointer if everything is
- ** OK.
- */
- static int fts5IntegrityMethod(
- sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
- const char *zSchema, /* Name of schema in which this table lives */
- const char *zTabname, /* Name of the table itself */
- int isQuick, /* True if this is a quick-check */
- char **pzErr /* Write error message here */
- ){
- Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- int rc;
- assert( pzErr!=0 && *pzErr==0 );
- UNUSED_PARAM(isQuick);
- assert( pTab->p.pConfig->pzErrmsg==0 );
- pTab->p.pConfig->pzErrmsg = pzErr;
- rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
- if( *pzErr==0 && rc!=SQLITE_OK ){
- if( (rc&0xff)==SQLITE_CORRUPT ){
- *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
- zSchema, zTabname);
- rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
- }else{
- *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
- " FTS5 table %s.%s: %s",
- zSchema, zTabname, sqlite3_errstr(rc));
- }
- }
- sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
- pTab->p.pConfig->pzErrmsg = 0;
- return rc;
- }
- static int fts5Init(sqlite3 *db){
- static const sqlite3_module fts5Mod = {
- /* iVersion */ 4,
- /* xCreate */ fts5CreateMethod,
- /* xConnect */ fts5ConnectMethod,
- /* xBestIndex */ fts5BestIndexMethod,
- /* xDisconnect */ fts5DisconnectMethod,
- /* xDestroy */ fts5DestroyMethod,
- /* xOpen */ fts5OpenMethod,
- /* xClose */ fts5CloseMethod,
- /* xFilter */ fts5FilterMethod,
- /* xNext */ fts5NextMethod,
- /* xEof */ fts5EofMethod,
- /* xColumn */ fts5ColumnMethod,
- /* xRowid */ fts5RowidMethod,
- /* xUpdate */ fts5UpdateMethod,
- /* xBegin */ fts5BeginMethod,
- /* xSync */ fts5SyncMethod,
- /* xCommit */ fts5CommitMethod,
- /* xRollback */ fts5RollbackMethod,
- /* xFindFunction */ fts5FindFunctionMethod,
- /* xRename */ fts5RenameMethod,
- /* xSavepoint */ fts5SavepointMethod,
- /* xRelease */ fts5ReleaseMethod,
- /* xRollbackTo */ fts5RollbackToMethod,
- /* xShadowName */ fts5ShadowName,
- /* xIntegrity */ fts5IntegrityMethod
- };
- int rc;
- Fts5Global *pGlobal = 0;
- pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
- if( pGlobal==0 ){
- rc = SQLITE_NOMEM;
- }else{
- void *p = (void*)pGlobal;
- memset(pGlobal, 0, sizeof(Fts5Global));
- pGlobal->db = db;
- pGlobal->api.iVersion = 3;
- pGlobal->api.xCreateFunction = fts5CreateAux;
- pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
- pGlobal->api.xFindTokenizer = fts5FindTokenizer;
- pGlobal->api.xCreateTokenizer_v2 = fts5CreateTokenizer_v2;
- pGlobal->api.xFindTokenizer_v2 = fts5FindTokenizer_v2;
- /* Initialize pGlobal->aLocaleHdr[] to a 128-bit pseudo-random vector.
- ** The constants below were generated randomly. */
- sqlite3_randomness(sizeof(pGlobal->aLocaleHdr), pGlobal->aLocaleHdr);
- pGlobal->aLocaleHdr[0] ^= 0xF924976D;
- pGlobal->aLocaleHdr[1] ^= 0x16596E13;
- pGlobal->aLocaleHdr[2] ^= 0x7C80BEAA;
- pGlobal->aLocaleHdr[3] ^= 0x9B03A67F;
- assert( sizeof(pGlobal->aLocaleHdr)==16 );
- rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
- if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
- if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
- if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);
- if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
- if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(
- db, "fts5", 1, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
- );
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(
- db, "fts5_source_id", 0,
- SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
- p, fts5SourceIdFunc, 0, 0
- );
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(
- db, "fts5_locale", 2,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
- p, fts5LocaleFunc, 0, 0
- );
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(
- db, "fts5_insttoken", 1,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
- p, fts5InsttokenFunc, 0, 0
- );
- }
- }
- /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
- ** fts5_test_mi.c is compiled and linked into the executable. And call
- ** its entry point to enable the matchinfo() demo. */
- #ifdef SQLITE_FTS5_ENABLE_TEST_MI
- if( rc==SQLITE_OK ){
- extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
- rc = sqlite3Fts5TestRegisterMatchinfo(db);
- }
- #endif
- return rc;
- }
- /*
- ** The following functions are used to register the module with SQLite. If
- ** this module is being built as part of the SQLite core (SQLITE_CORE is
- ** defined), then sqlite3_open() will call sqlite3Fts5Init() directly.
- **
- ** Or, if this module is being built as a loadable extension,
- ** sqlite3Fts5Init() is omitted and the two standard entry points
- ** sqlite3_fts_init() and sqlite3_fts5_init() defined instead.
- */
- #ifndef SQLITE_CORE
- #ifdef _WIN32
- __declspec(dllexport)
- #endif
- int sqlite3_fts_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
- ){
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
- return fts5Init(db);
- }
- #ifdef _WIN32
- __declspec(dllexport)
- #endif
- int sqlite3_fts5_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
- ){
- SQLITE_EXTENSION_INIT2(pApi);
- (void)pzErrMsg; /* Unused parameter */
- return fts5Init(db);
- }
- #else
- int sqlite3Fts5Init(sqlite3 *db){
- return fts5Init(db);
- }
- #endif
|