📄 fts3_expr.c
字号:
** Note that when using the old query syntax, the OR operator has a higher** precedence than the AND operator.*/static int opPrecedence(Fts3Expr *p){ assert( p->eType!=FTSQUERY_PHRASE ); if( sqlite3_fts3_enable_parentheses ){ return p->eType; }else if( p->eType==FTSQUERY_NEAR ){ return 1; }else if( p->eType==FTSQUERY_OR ){ return 2; } assert( p->eType==FTSQUERY_AND ); return 3;}/*** Argument ppHead contains a pointer to the current head of a query ** expression tree being parsed. pPrev is the expression node most recently** inserted into the tree. This function adds pNew, which is always a binary** operator node, into the expression tree based on the relative precedence** of pNew and the existing nodes of the tree. This may result in the head** of the tree changing, in which case *ppHead is set to the new root node.*/static void insertBinaryOperator( Fts3Expr **ppHead, /* Pointer to the root node of a tree */ Fts3Expr *pPrev, /* Node most recently inserted into the tree */ Fts3Expr *pNew /* New binary node to insert into expression tree */){ Fts3Expr *pSplit = pPrev; while( pSplit->pParent && opPrecedence(pSplit->pParent)<=opPrecedence(pNew) ){ pSplit = pSplit->pParent; } if( pSplit->pParent ){ assert( pSplit->pParent->pRight==pSplit ); pSplit->pParent->pRight = pNew; pNew->pParent = pSplit->pParent; }else{ *ppHead = pNew; } pNew->pLeft = pSplit; pSplit->pParent = pNew;}/*** Parse the fts3 query expression found in buffer z, length n. This function** returns either when the end of the buffer is reached or an unmatched ** closing bracket - ')' - is encountered.**** If successful, SQLITE_OK is returned, *ppExpr is set to point to the** parsed form of the expression and *pnConsumed is set to the number of** bytes read from buffer z. Otherwise, *ppExpr is set to 0 and SQLITE_NOMEM** (out of memory error) or SQLITE_ERROR (parse error) is returned.*/static int fts3ExprParse( ParseContext *pParse, /* fts3 query parse context */ const char *z, int n, /* Text of MATCH query */ Fts3Expr **ppExpr, /* OUT: Parsed query structure */ int *pnConsumed /* OUT: Number of bytes consumed */){ Fts3Expr *pRet = 0; Fts3Expr *pPrev = 0; Fts3Expr *pNotBranch = 0; /* Only used in legacy parse mode */ int nIn = n; const char *zIn = z; int rc = SQLITE_OK; int isRequirePhrase = 1; while( rc==SQLITE_OK ){ Fts3Expr *p = 0; int nByte = 0; rc = getNextNode(pParse, zIn, nIn, &p, &nByte); if( rc==SQLITE_OK ){ int isPhrase; if( !sqlite3_fts3_enable_parentheses && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot ){ /* Create an implicit NOT operator. */ Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr)); if( !pNot ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } memset(pNot, 0, sizeof(Fts3Expr)); pNot->eType = FTSQUERY_NOT; pNot->pRight = p; if( pNotBranch ){ pNotBranch->pLeft = p; pNot->pRight = pNotBranch; } pNotBranch = pNot; }else{ int eType = p->eType; assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); /* The isRequirePhrase variable is set to true if a phrase or ** an expression contained in parenthesis is required. If a ** binary operator (AND, OR, NOT or NEAR) is encounted when ** isRequirePhrase is set, this is a syntax error. */ if( !isPhrase && isRequirePhrase ){ sqlite3Fts3ExprFree(p); rc = SQLITE_ERROR; goto exprparse_out; } if( isPhrase && !isRequirePhrase ){ /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); pAnd = sqlite3_malloc(sizeof(Fts3Expr)); if( !pAnd ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } memset(pAnd, 0, sizeof(Fts3Expr)); pAnd->eType = FTSQUERY_AND; insertBinaryOperator(&pRet, pPrev, pAnd); pPrev = pAnd; } /* This test catches attempts to make either operand of a NEAR ** operator something other than a phrase. For example, either of ** the following: ** ** (bracketed expression) NEAR phrase ** phrase NEAR (bracketed expression) ** ** Return an error in either case. */ if( pPrev && ( (eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE) || (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR) )){ sqlite3Fts3ExprFree(p); rc = SQLITE_ERROR; goto exprparse_out; } if( isPhrase ){ if( pRet ){ assert( pPrev && pPrev->pLeft && pPrev->pRight==0 ); pPrev->pRight = p; p->pParent = pPrev; }else{ pRet = p; } }else{ insertBinaryOperator(&pRet, pPrev, p); } isRequirePhrase = !isPhrase; } assert( nByte>0 ); } assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) ); nIn -= nByte; zIn += nByte; pPrev = p; } if( rc==SQLITE_DONE && pRet && isRequirePhrase ){ rc = SQLITE_ERROR; } if( rc==SQLITE_DONE ){ rc = SQLITE_OK; if( !sqlite3_fts3_enable_parentheses && pNotBranch ){ if( !pRet ){ rc = SQLITE_ERROR; }else{ pNotBranch->pLeft = pRet; pRet = pNotBranch; } } } *pnConsumed = n - nIn;exprparse_out: if( rc!=SQLITE_OK ){ sqlite3Fts3ExprFree(pRet); sqlite3Fts3ExprFree(pNotBranch); pRet = 0; } *ppExpr = pRet; return rc;}/*** Parameters z and n contain a pointer to and length of a buffer containing** an fts3 query expression, respectively. This function attempts to parse the** query expression and create a tree of Fts3Expr structures representing the** parsed expression. If successful, *ppExpr is set to point to the head** of the parsed expression tree and SQLITE_OK is returned. If an error** occurs, either SQLITE_NOMEM (out-of-memory error) or SQLITE_ERROR (parse** error) is returned and *ppExpr is set to 0.**** If parameter n is a negative number, then z is assumed to point to a** nul-terminated string and the length is determined using strlen().**** The first parameter, pTokenizer, is passed the fts3 tokenizer module to** use to normalize query tokens while parsing the expression. The azCol[]** array, which is assumed to contain nCol entries, should contain the names** of each column in the target fts3 table, in order from left to right. ** Column names must be nul-terminated strings.**** The iDefaultCol parameter should be passed the index of the table column** that appears on the left-hand-side of the MATCH operator (the default** column to match against for tokens for which a column name is not explicitly** specified as part of the query string), or -1 if tokens may by default** match any table column.*/int sqlite3Fts3ExprParse( sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ char **azCol, /* Array of column names for fts3 table */ int nCol, /* Number of entries in azCol[] */ int iDefaultCol, /* Default column to query */ const char *z, int n, /* Text of MATCH query */ Fts3Expr **ppExpr /* OUT: Parsed query structure */){ int nParsed; int rc; ParseContext sParse; sParse.pTokenizer = pTokenizer; sParse.azCol = (const char **)azCol; sParse.nCol = nCol; sParse.iDefaultCol = iDefaultCol; sParse.nNest = 0; if( z==0 ){ *ppExpr = 0; return SQLITE_OK; } if( n<0 ){ n = strlen(z); } rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); /* Check for mismatched parenthesis */ if( rc==SQLITE_OK && sParse.nNest ){ rc = SQLITE_ERROR; sqlite3Fts3ExprFree(*ppExpr); *ppExpr = 0; } return rc;}/*** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().*/void sqlite3Fts3ExprFree(Fts3Expr *p){ if( p ){ sqlite3Fts3ExprFree(p->pLeft); sqlite3Fts3ExprFree(p->pRight); sqlite3_free(p); }}/*********************************************************************************************************************************************************** Everything after this point is just test code.*/#ifdef SQLITE_TEST#include <stdio.h>/*** Function to query the hash-table of tokenizers (see README.tokenizers).*/static int queryTestTokenizer( sqlite3 *db, const char *zName, const sqlite3_tokenizer_module **pp){ int rc; sqlite3_stmt *pStmt; const char zSql[] = "SELECT fts3_tokenizer(?)"; *pp = 0; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); if( rc!=SQLITE_OK ){ return rc; } sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(pStmt) ){ if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){ memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp)); } } return sqlite3_finalize(pStmt);}/*** This function is part of the test interface for the query parser. It** writes a text representation of the query expression pExpr into the** buffer pointed to by argument zBuf. It is assumed that zBuf is large ** enough to store the required text representation.*/static void exprToString(Fts3Expr *pExpr, char *zBuf){ switch( pExpr->eType ){ case FTSQUERY_PHRASE: { Fts3Phrase *pPhrase = pExpr->pPhrase; int i; zBuf += sprintf(zBuf, "PHRASE %d %d", pPhrase->iColumn, pPhrase->isNot); for(i=0; i<pPhrase->nToken; i++){ zBuf += sprintf(zBuf," %.*s",pPhrase->aToken[i].n,pPhrase->aToken[i].z); zBuf += sprintf(zBuf,"%s", (pPhrase->aToken[i].isPrefix?"+":"")); } return; } case FTSQUERY_NEAR: zBuf += sprintf(zBuf, "NEAR/%d ", pExpr->nNear); break; case FTSQUERY_NOT: zBuf += sprintf(zBuf, "NOT "); break; case FTSQUERY_AND: zBuf += sprintf(zBuf, "AND "); break; case FTSQUERY_OR: zBuf += sprintf(zBuf, "OR "); break; } zBuf += sprintf(zBuf, "{"); exprToString(pExpr->pLeft, zBuf); zBuf += strlen(zBuf); zBuf += sprintf(zBuf, "} "); zBuf += sprintf(zBuf, "{"); exprToString(pExpr->pRight, zBuf); zBuf += strlen(zBuf); zBuf += sprintf(zBuf, "}");}/*** This is the implementation of a scalar SQL function used to test the ** expression parser. It should be called as follows:**** fts3_exprtest(<tokenizer>, <expr>, <column 1>, ...);**** The first argument, <tokenizer>, is the name of the fts3 tokenizer used** to parse the query expression (see README.tokenizers). The second argument** is the query expression to parse. Each subsequent argument is the name** of a column of the fts3 table that the query expression may refer to.** For example:**** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2');*/static void fts3ExprTest( sqlite3_context *context, int argc, sqlite3_value **argv){ sqlite3_tokenizer_module const *pModule = 0; sqlite3_tokenizer *pTokenizer = 0; int rc; char **azCol = 0; const char *zExpr; int nExpr; int nCol; int ii; Fts3Expr *pExpr; sqlite3 *db = sqlite3_context_db_handle(context); if( argc<3 ){ sqlite3_result_error(context, "Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1 ); return; } rc = queryTestTokenizer(db, (const char *)sqlite3_value_text(argv[0]), &pModule); if( rc==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); goto exprtest_out; }else if( !pModule ){ sqlite3_result_error(context, "No such tokenizer module", -1); goto exprtest_out; } rc = pModule->xCreate(0, 0, &pTokenizer); assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); if( rc==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); goto exprtest_out; } pTokenizer->pModule = pModule; zExpr = (const char *)sqlite3_value_text(argv[1]); nExpr = sqlite3_value_bytes(argv[1]); nCol = argc-2; azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); if( !azCol ){ sqlite3_result_error_nomem(context); goto exprtest_out; } for(ii=0; ii<nCol; ii++){ azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]); } rc = sqlite3Fts3ExprParse( pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr ); if( rc==SQLITE_NOMEM ){ sqlite3_result_error_nomem(context); goto exprtest_out; }else if( rc==SQLITE_OK ){ char zBuf[4096]; exprToString(pExpr, zBuf); sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); sqlite3Fts3ExprFree(pExpr); }else{ sqlite3_result_error(context, "Error parsing expression", -1); }exprtest_out: if( pModule && pTokenizer ){ rc = pModule->xDestroy(pTokenizer); } sqlite3_free(azCol);}/*** Register the query expression parser test function fts3_exprtest() ** with database connection db. */void sqlite3Fts3ExprInitTestInterface(sqlite3* db){ sqlite3_create_function( db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0 );}#endif#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -