/*
** Insert a new row into the FTS content table.
*/
int sqlite3Fts5StorageContentInsert(
  Fts5Storage *p, 
  sqlite3_value **apVal, 
  i64 *piRowid
){
  Fts5Config *pConfig = p->pConfig;
  int rc = SQLITE_OK;

  /* Insert the new row into the %_content table. */
  if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
    if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
      *piRowid = sqlite3_value_int64(apVal[1]);
    }else{
      rc = fts5StorageNewRowid(p, piRowid);
    }
  }else{
    sqlite3_stmt *pInsert = 0;    /* Statement to write %_content table */
    int i;                        /* Counter variable */
    rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
    for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
      rc = sqlite3_bind_value(pInsert, i, apVal[i]);
    }
    if( rc==SQLITE_OK ){
      sqlite3_step(pInsert);
      rc = sqlite3_reset(pInsert);
    }
    *piRowid = sqlite3_last_insert_rowid(pConfig->db);
  }

  return rc;
}
Beispiel #2
0
static void UdfInsertFunc(sqlite3_context* aCtx, int aCnt, sqlite3_value** aValues)
	{
	int err;
  	const char* tail = 0;
  	sqlite3* db = 0;
  	
	TEST2(aCnt, 1);
	
	db = sqlite3_context_db_handle(aCtx);/* to test that sqlite3_context_db_handle() can be called */
	TEST(db != 0);
	
	TEST(!TheStmt);
	err = sqlite3_prepare(TheDb, "INSERT INTO t1(x) VALUES(:Val)", -1, &TheStmt, &tail);
	if(err == SQLITE_OK)
		{
		err = sqlite3_bind_value(TheStmt, 1, aValues[0]);
		if(err == SQLITE_OK)
			{
			err = sqlite3_step(TheStmt);
			}
		}
	(void)sqlite3_finalize(TheStmt);
	TheStmt = 0;
	
	sqlite3_result_int(aCtx, err);		
	}
int sqlite3Fts5StorageConfigValue(
  Fts5Storage *p, 
  const char *z,
  sqlite3_value *pVal,
  int iVal
){
  sqlite3_stmt *pReplace = 0;
  int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0);
  if( rc==SQLITE_OK ){
    sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC);
    if( pVal ){
      sqlite3_bind_value(pReplace, 2, pVal);
    }else{
      sqlite3_bind_int(pReplace, 2, iVal);
    }
    sqlite3_step(pReplace);
    rc = sqlite3_reset(pReplace);
  }
  if( rc==SQLITE_OK && pVal ){
    int iNew = p->pConfig->iCookie + 1;
    rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
    if( rc==SQLITE_OK ){
      p->pConfig->iCookie = iNew;
    }
  }
  return rc;
}
Beispiel #4
0
static void _bindSqlArg(const char *zKey, const sqlite3_int64 index, SqlArg_t *pArg,
                        const Array_t *arr, sqlite3_stmt *pStmt, bool *bStop)
{
    UNUSED_PARAM(zKey);
    UNUSED_PARAM(arr);
    UNUSED_PARAM(bStop);

    if (pArg->zText)
        sqlite3_bind_text(pStmt, (int) index + 1, pArg->zText, -1, NULL);
    else sqlite3_bind_value(pStmt, (int) index + 1, pArg->pValue);
}
Beispiel #5
0
/* insert into %_content (rowid, content) values ([rowid], [zContent]) */
static int content_insert(fulltext_vtab *v, sqlite3_value *rowid,
                          const char *zContent, int nContent){
  sqlite3_stmt *s;
  int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3_bind_value(s, 1, rowid);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3_bind_text(s, 2, zContent, nContent, SQLITE_STATIC);
  if( rc!=SQLITE_OK ) return rc;

  return sql_single_step_statement(v, CONTENT_INSERT_STMT, &s);
}
Beispiel #6
0
/* 
** Echo virtual table module xFilter method.
*/
static int echoFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  int rc;
  int i;

  echo_cursor *pCur = (echo_cursor *)pVtabCursor;
  echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab;
  sqlite3 *db = pVtab->db;

  if( simulateVtabError(pVtab, "xFilter") ){
    return SQLITE_ERROR;
  }

  /* Check that idxNum matches idxStr */
  assert( idxNum==hashString(idxStr) );

  /* Log arguments to the ::echo_module Tcl variable */
  appendToEchoModule(pVtab->interp, "xFilter");
  appendToEchoModule(pVtab->interp, idxStr);
  for(i=0; i<argc; i++){
    appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i]));
  }

  sqlite3_finalize(pCur->pStmt);
  pCur->pStmt = 0;

  /* Prepare the SQL statement created by echoBestIndex and bind the
  ** runtime parameters passed to this function to it.
  */
  rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0);
  assert( pCur->pStmt || rc!=SQLITE_OK );
  for(i=0; rc==SQLITE_OK && i<argc; i++){
    rc = sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
  }

  /* If everything was successful, advance to the first row of the scan */
  if( rc==SQLITE_OK ){
    rc = echoNext(pVtabCursor);
  }

  return rc;
}
Beispiel #7
0
static int echoFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  int rc;
  int i;

  echo_cursor *pCur = (echo_cursor *)pVtabCursor;
  echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab;
  sqlite3 *db = pVtab->db;

  if( simulateVtabError(pVtab, "xFilter") ){
    return SQLITE_ERROR;
  }

  
  assert( idxNum==hashString(idxStr) );

  
  appendToEchoModule(pVtab->interp, "xFilter");
  appendToEchoModule(pVtab->interp, idxStr);
  for(i=0; i<argc; i++){
    appendToEchoModule(pVtab->interp, (const char*)sqlite3_value_text(argv[i]));
  }

  sqlite3_finalize(pCur->pStmt);
  pCur->pStmt = 0;

  rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0);
  assert( pCur->pStmt || rc!=SQLITE_OK );
  for(i=0; rc==SQLITE_OK && i<argc; i++){
    rc = sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
  }

  
  if( rc==SQLITE_OK ){
    rc = echoNext(pVtabCursor);
  }

  return rc;
}
Beispiel #8
0
int echoUpdate(
  sqlite3_vtab *tab, 
  int nData, 
  sqlite3_value **apData, 
  sqlite_int64 *pRowid
){
  echo_vtab *pVtab = (echo_vtab *)tab;
  sqlite3 *db = pVtab->db;
  int rc = SQLITE_OK;

  sqlite3_stmt *pStmt;
  char *z = 0;               
  int bindArgZero = 0;       
  int bindArgOne = 0;        
  int i;                     

  assert( nData==pVtab->nCol+2 || nData==1 );

  assert( pVtab->inTransaction );

  if( simulateVtabError(pVtab, "xUpdate") ){
    return SQLITE_ERROR;
  }

  
  if( nData>1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
    char *zSep = " SET";
    z = sqlite3_mprintf("UPDATE %Q", pVtab->zTableName);
    if( !z ){
      rc = SQLITE_NOMEM;
    }

    bindArgOne = (apData[1] && sqlite3_value_type(apData[1])==SQLITE_INTEGER);
    bindArgZero = 1;

    if( bindArgOne ){
       string_concat(&z, " SET rowid=?1 ", 0, &rc);
       zSep = ",";
    }
    for(i=2; i<nData; i++){
      if( apData[i]==0 ) continue;
      string_concat(&z, sqlite3_mprintf(
          "%s %Q=?%d", zSep, pVtab->aCol[i-2], i), 1, &rc);
      zSep = ",";
    }
    string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 1, &rc);
  }

  
  else if( nData==1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
    z = sqlite3_mprintf("DELETE FROM %Q WHERE rowid = ?1", pVtab->zTableName);
    if( !z ){
      rc = SQLITE_NOMEM;
    }
    bindArgZero = 1;
  }

  
  else if( nData>2 && sqlite3_value_type(apData[0])==SQLITE_NULL ){
    int ii;
    char *zInsert = 0;
    char *zValues = 0;
  
    zInsert = sqlite3_mprintf("INSERT INTO %Q (", pVtab->zTableName);
    if( !zInsert ){
      rc = SQLITE_NOMEM;
    }
    if( sqlite3_value_type(apData[1])==SQLITE_INTEGER ){
      bindArgOne = 1;
      zValues = sqlite3_mprintf("?");
      string_concat(&zInsert, "rowid", 0, &rc);
    }

    assert((pVtab->nCol+2)==nData);
    for(ii=2; ii<nData; ii++){
      string_concat(&zInsert, 
          sqlite3_mprintf("%s%Q", zValues?", ":"", pVtab->aCol[ii-2]), 1, &rc);
      string_concat(&zValues, 
          sqlite3_mprintf("%s?%d", zValues?", ":"", ii), 1, &rc);
    }

    string_concat(&z, zInsert, 1, &rc);
    string_concat(&z, ") VALUES(", 0, &rc);
    string_concat(&z, zValues, 1, &rc);
    string_concat(&z, ")", 0, &rc);
  }

  
  else{
    assert(0);
    return SQLITE_ERROR;
  }

  if( rc==SQLITE_OK ){
    rc = sqlite3_prepare(db, z, -1, &pStmt, 0);
  }
  assert( rc!=SQLITE_OK || pStmt );
  sqlite3_free(z);
  if( rc==SQLITE_OK ) {
    if( bindArgZero ){
      sqlite3_bind_value(pStmt, nData, apData[0]);
    }
    if( bindArgOne ){
      sqlite3_bind_value(pStmt, 1, apData[1]);
    }
    for(i=2; i<nData && rc==SQLITE_OK; i++){
      if( apData[i] ) rc = sqlite3_bind_value(pStmt, i, apData[i]);
    }
    if( rc==SQLITE_OK ){
      sqlite3_step(pStmt);
      rc = sqlite3_finalize(pStmt);
    }else{
      sqlite3_finalize(pStmt);
    }
  }

  if( pRowid && rc==SQLITE_OK ){
    *pRowid = sqlite3_last_insert_rowid(db);
  }
  if( rc!=SQLITE_OK ){
    tab->zErrMsg = sqlite3_mprintf("echo-vtab-error: %s", sqlite3_errmsg(db));
  }

  return rc;
}
Beispiel #9
0
int SQLiteSTMT::Bind(int iCol, const sqlite3_value* value) {
  return sqlite3_bind_value(handle, iCol, value);
}
Beispiel #10
0
/*
** The xUpdate method for echo module virtual tables.
** 
**    apData[0]  apData[1]  apData[2..]
**
**    INTEGER                              DELETE            
**
**    INTEGER    NULL       (nCol args)    UPDATE (do not set rowid)
**    INTEGER    INTEGER    (nCol args)    UPDATE (with SET rowid = <arg1>)
**
**    NULL       NULL       (nCol args)    INSERT INTO (automatic rowid value)
**    NULL       INTEGER    (nCol args)    INSERT (incl. rowid value)
**
*/
int echoUpdate(
  sqlite3_vtab *tab, 
  int nData, 
  sqlite3_value **apData, 
  sqlite_int64 *pRowid
){
  echo_vtab *pVtab = (echo_vtab *)tab;
  sqlite3 *db = pVtab->db;
  int rc = SQLITE_OK;

  sqlite3_stmt *pStmt = 0;
  char *z = 0;               /* SQL statement to execute */
  int bindArgZero = 0;       /* True to bind apData[0] to sql var no. nData */
  int bindArgOne = 0;        /* True to bind apData[1] to sql var no. 1 */
  int i;                     /* Counter variable used by for loops */

  assert( nData==pVtab->nCol+2 || nData==1 );

  /* Ticket #3083 - make sure we always start a transaction prior to
  ** making any changes to a virtual table */
  assert( pVtab->inTransaction );

  if( simulateVtabError(pVtab, "xUpdate") ){
    return SQLITE_ERROR;
  }

  /* If apData[0] is an integer and nData>1 then do an UPDATE */
  if( nData>1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
    char *zSep = " SET";
    z = sqlite3_mprintf("UPDATE %Q", pVtab->zTableName);
    if( !z ){
      rc = SQLITE_NOMEM;
    }

    bindArgOne = (apData[1] && sqlite3_value_type(apData[1])==SQLITE_INTEGER);
    bindArgZero = 1;

    if( bindArgOne ){
       string_concat(&z, " SET rowid=?1 ", 0, &rc);
       zSep = ",";
    }
    for(i=2; i<nData; i++){
      if( apData[i]==0 ) continue;
      string_concat(&z, sqlite3_mprintf(
          "%s %Q=?%d", zSep, pVtab->aCol[i-2], i), 1, &rc);
      zSep = ",";
    }
    string_concat(&z, sqlite3_mprintf(" WHERE rowid=?%d", nData), 1, &rc);
  }

  /* If apData[0] is an integer and nData==1 then do a DELETE */
  else if( nData==1 && sqlite3_value_type(apData[0])==SQLITE_INTEGER ){
    z = sqlite3_mprintf("DELETE FROM %Q WHERE rowid = ?1", pVtab->zTableName);
    if( !z ){
      rc = SQLITE_NOMEM;
    }
    bindArgZero = 1;
  }

  /* If the first argument is NULL and there are more than two args, INSERT */
  else if( nData>2 && sqlite3_value_type(apData[0])==SQLITE_NULL ){
    int ii;
    char *zInsert = 0;
    char *zValues = 0;
  
    zInsert = sqlite3_mprintf("INSERT INTO %Q (", pVtab->zTableName);
    if( !zInsert ){
      rc = SQLITE_NOMEM;
    }
    if( sqlite3_value_type(apData[1])==SQLITE_INTEGER ){
      bindArgOne = 1;
      zValues = sqlite3_mprintf("?");
      string_concat(&zInsert, "rowid", 0, &rc);
    }

    assert((pVtab->nCol+2)==nData);
    for(ii=2; ii<nData; ii++){
      string_concat(&zInsert, 
          sqlite3_mprintf("%s%Q", zValues?", ":"", pVtab->aCol[ii-2]), 1, &rc);
      string_concat(&zValues, 
          sqlite3_mprintf("%s?%d", zValues?", ":"", ii), 1, &rc);
    }

    string_concat(&z, zInsert, 1, &rc);
    string_concat(&z, ") VALUES(", 0, &rc);
    string_concat(&z, zValues, 1, &rc);
    string_concat(&z, ")", 0, &rc);
  }

  /* Anything else is an error */
  else{
    assert(0);
    return SQLITE_ERROR;
  }

  if( rc==SQLITE_OK ){
    rc = sqlite3_prepare(db, z, -1, &pStmt, 0);
  }
  assert( rc!=SQLITE_OK || pStmt );
  sqlite3_free(z);
  if( rc==SQLITE_OK ) {
    if( bindArgZero ){
      sqlite3_bind_value(pStmt, nData, apData[0]);
    }
    if( bindArgOne ){
      sqlite3_bind_value(pStmt, 1, apData[1]);
    }
    for(i=2; i<nData && rc==SQLITE_OK; i++){
      if( apData[i] ) rc = sqlite3_bind_value(pStmt, i, apData[i]);
    }
    if( rc==SQLITE_OK ){
      sqlite3_step(pStmt);
      rc = sqlite3_finalize(pStmt);
    }else{
      sqlite3_finalize(pStmt);
    }
  }

  if( pRowid && rc==SQLITE_OK ){
    *pRowid = sqlite3_last_insert_rowid(db);
  }
  if( rc!=SQLITE_OK ){
    tab->zErrMsg = sqlite3_mprintf("echo-vtab-error: %s", sqlite3_errmsg(db));
  }

  return rc;
}
/**
 * This function is invoked as:
 *
 *  _TOKENIZE('<token_table>', <data_row_id>, <data>, <delimiter>,
 *             <use_token_index>, <data_tag>)
 *
 * If <use_token_index> is omitted, it is treated as 0.
 * If <data_tag> is omitted, it is treated as NULL.
 *
 * It will split <data> on each instance of <delimiter> and insert each token
 * into <token_table>. The following columns in <token_table> are used:
 * token TEXT, source INTEGER, token_index INTEGER, tag (any type)
 * The token_index column is not required if <use_token_index> is 0.
 * The tag column is not required if <data_tag> is NULL.
 *
 * One row is inserted for each token in <data>.
 * In each inserted row, 'source' is <data_row_id>.
 * In the first inserted row, 'token' is the hex collation key of
 * the entire <data> string, and 'token_index' is 0.
 * In each row I (where 1 <= I < N, and N is the number of tokens in <data>)
 * 'token' will be set to the hex collation key of the I:th token (0-based).
 * If <use_token_index> != 0, 'token_index' is set to I.
 * If <data_tag> is not NULL, 'tag' is set to <data_tag>.
 *
 * In other words, there will be one row for the entire string,
 * and one row for each token except the first one.
 *
 * The function returns the number of tokens generated.
 */
static void tokenize(sqlite3_context * context, int argc, sqlite3_value ** argv)
{
    //ALOGD("enter tokenize");
    int err;
    int useTokenIndex = 0;
    int useDataTag = 0;

    if (!(argc >= 4 || argc <= 6)) {
        ALOGE("Tokenize requires 4 to 6 arguments");
        sqlite3_result_null(context);
        return;
    }

    if (argc > 4) {
        useTokenIndex = sqlite3_value_int(argv[4]);
    }

    if (argc > 5) {
        useDataTag = (sqlite3_value_type(argv[5]) != SQLITE_NULL);
    }

    sqlite3 * handle = sqlite3_context_db_handle(context);
    UCollator* collator = (UCollator*)sqlite3_user_data(context);
    char const * tokenTable = (char const *)sqlite3_value_text(argv[0]);
    if (tokenTable == NULL) {
        ALOGE("tokenTable null");
        sqlite3_result_null(context);
        return;
    }

    // Get or create the prepared statement for the insertions
    sqlite3_stmt * statement = (sqlite3_stmt *)sqlite3_get_auxdata(context, 0);
    if (!statement) {
        char const * tokenIndexCol = useTokenIndex ? ", token_index" : "";
        char const * tokenIndexParam = useTokenIndex ? ", ?" : "";
        char const * dataTagCol = useDataTag ? ", tag" : "";
        char const * dataTagParam = useDataTag ? ", ?" : "";
        char * sql = sqlite3_mprintf("INSERT INTO %s (token, source%s%s) VALUES (?, ?%s%s);",
                tokenTable, tokenIndexCol, dataTagCol, tokenIndexParam, dataTagParam);
        err = sqlite3_prepare_v2(handle, sql, -1, &statement, NULL);
        sqlite3_free(sql);
        if (err) {
            ALOGE("prepare failed");
            sqlite3_result_null(context);
            return;
        }
        // This binds the statement to the table it was compiled against, which is argv[0].
        // If this function is ever called with a different table the finalizer will be called
        // and sqlite3_get_auxdata() will return null above, forcing a recompile for the new table.
        sqlite3_set_auxdata(context, 0, statement, tokenize_auxdata_delete);
    } else {
        // Reset the cached statement so that binding the row ID will work properly
        sqlite3_reset(statement);
    }

    // Bind the row ID of the source row
    int64_t rowID = sqlite3_value_int64(argv[1]);
    err = sqlite3_bind_int64(statement, 2, rowID);
    if (err != SQLITE_OK) {
        ALOGE("bind failed");
        sqlite3_result_null(context);
        return;
    }

    // Bind <data_tag> to the tag column
    if (useDataTag) {
        int dataTagParamIndex = useTokenIndex ? 4 : 3;
        err = sqlite3_bind_value(statement, dataTagParamIndex, argv[5]);
        if (err != SQLITE_OK) {
            ALOGE("bind failed");
            sqlite3_result_null(context);
            return;
        }
    }

    // Get the raw bytes for the string to tokenize
    // the string will be modified by following code
    // however, sqlite did not reuse the string, so it is safe to not dup it
    UChar * origData = (UChar *)sqlite3_value_text16(argv[2]);
    if (origData == NULL) {
        sqlite3_result_null(context);
        return;
    }

    // Get the raw bytes for the delimiter
    const UChar * delim = (const UChar *)sqlite3_value_text16(argv[3]);
    if (delim == NULL) {
        ALOGE("can't get delimiter");
        sqlite3_result_null(context);
        return;
    }

    UChar * token = NULL;
    UChar *state;
    int numTokens = 0;

    do {
        if (numTokens == 0) {
            token = origData;
        }

        // Reset the program so we can use it to perform the insert
        sqlite3_reset(statement);
        UErrorCode status = U_ZERO_ERROR;
        char keybuf[1024];
        uint32_t result = ucol_getSortKey(collator, token, -1, (uint8_t*)keybuf, sizeof(keybuf)-1);
        if (result > sizeof(keybuf)) {
            // TODO allocate memory for this super big string
            ALOGE("ucol_getSortKey needs bigger buffer %d", result);
            break;
        }
        uint32_t keysize = result-1;
        uint32_t base16Size = keysize*2;
        char *base16buf = (char*)malloc(base16Size);
        base16Encode(base16buf, keybuf, keysize);
        err = sqlite3_bind_text(statement, 1, base16buf, base16Size, SQLITE_STATIC);

        if (err != SQLITE_OK) {
            ALOGE(" sqlite3_bind_text16 error %d", err);
            free(base16buf);
            break;
        }

        if (useTokenIndex) {
            err = sqlite3_bind_int(statement, 3, numTokens);
            if (err != SQLITE_OK) {
                ALOGE(" sqlite3_bind_int error %d", err);
                free(base16buf);
                break;
            }
        }

        err = sqlite3_step(statement);
        free(base16buf);

        if (err != SQLITE_DONE) {
            ALOGE(" sqlite3_step error %d", err);
            break;
        }
        numTokens++;
        if (numTokens == 1) {
            // first call
            u_strtok_r(origData, delim, &state);
        }
    } while ((token = u_strtok_r(NULL, delim, &state)) != NULL);
    sqlite3_result_int(context, numTokens);
}
Beispiel #12
0
DLL_FUNCTION(int32_t) BU_SQLite_Bind_Value(sqlite3_stmt* pStmt, int32_t index, const sqlite3_value* pValue) {
#pragma comment(linker, "/EXPORT:BU_SQLite_Bind_Value=_BU_SQLite_Bind_Value@12")
	return sqlite3_bind_value(pStmt, index, pValue);
}
Beispiel #13
0
/*
 * Performs INSERT, UPDATE and DELETE operations
 * argc == 1 -> DELETE, argv[0] - object ID or SQL_NULL
 * argv[1]: SQL_NULL ? allocate object ID and return it in pRowid : ID for new object
 *
 * argc = 1
The single row with rowid equal to argv[0] is deleted. No insert occurs.

argc > 1
argv[0] = NULL
A new row is inserted with a rowid argv[1] and column values in argv[2] and following.
 If argv[1] is an SQL NULL, the a new unique rowid is generated automatically.

argc > 1
argv[0] ≠ NULL
argv[0] = argv[1]
The row with rowid argv[0] is updated with new values in argv[2] and following parameters.

argc > 1
argv[0] ≠ NULL
argv[0] ≠ argv[1]
The row with rowid argv[0] is updated with rowid argv[1] and new values in argv[2] and following parameters.
 This will occur when an SQL statement updates a rowid, as in the statement:

UPDATE table SET rowid=rowid+1 WHERE ...;
 */
static int _update(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid)
{
    int result = SQLITE_OK;
    struct flexi_ClassDef_t *vtab = (struct flexi_ClassDef_t *) pVTab;
    sqlite3_stmt *pDel;
    sqlite3_stmt *pDelRtree;
    sqlite3_stmt *pInsObj;
    sqlite3_stmt *pInsProp;
    sqlite3_stmt *pUpdProp;

    if (argc == 1)
        // Delete
    {
        if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
            // Nothing to delete. Exit
        {
            return SQLITE_OK;
        }

        sqlite3_int64 lOldID = sqlite3_value_int64(argv[0]);

        CHECK_CALL(
                flexi_Context_stmtInit(vtab->pCtx, STMT_DEL_OBJ, "delete from [.objects] where ObjectID = :1;", &pDel));
        sqlite3_bind_int64(pDel, 1, lOldID);
        CHECK_STMT_STEP(pDel, vtab->pCtx->db);

        // TODO Move rtree delete init here
        pDelRtree = vtab->pCtx->pStmts[STMT_DEL_RTREE];
        assert(pDelRtree);
        CHECK_CALL(sqlite3_reset(pDelRtree));
        sqlite3_bind_int64(pDelRtree, 1, lOldID);
        CHECK_STMT_STEP(pDelRtree, vtab->pCtx->db);
    }
    else
    {
        if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
            // Insert new row
        {
            const char *zInsObjSQL = "insert into [.objects] (ObjectID, ClassID, ctlo) values (:1, :2, :3); "
                    "select last_insert_rowid();";
            flexi_Context_stmtInit(vtab->pCtx, STMT_INS_OBJ, zInsObjSQL, &pInsObj);

            sqlite3_bind_value(pInsObj, 1, argv[1]); // Object ID, normally null
            sqlite3_bind_int64(pInsObj, 2, vtab->lClassID);
            sqlite3_bind_int(pInsObj, 3, vtab->xCtloMask);

            CHECK_STMT_STEP(pInsObj, vtab->pCtx->db);

            if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
            {
                *pRowid = sqlite3_last_insert_rowid(vtab->pCtx->db);
            }
            else *pRowid = sqlite3_value_int64(argv[1]);

            const char *zInsPropSQL = "insert into [.ref-values] (ObjectID, PropertyID, PropIndex, ctlv, [Value])"
                    " values (:1, :2, :3, :4, :5);";
            CHECK_CALL(flexi_Context_stmtInit(vtab->pCtx, STMT_INS_PROP, zInsPropSQL, &pInsProp));
            CHECK_CALL(flexi_upsert_props(vtab, *pRowid, pInsProp, 0, argc, argv));
        }
        else
        {
            sqlite3_int64 lNewID = sqlite3_value_int64(argv[1]);
            *pRowid = lNewID;
            if (argv[0] != argv[1])
                // Special case - Object ID update
            {
                sqlite3_int64 lOldID = sqlite3_value_int64(argv[0]);

                // TODO Move stmt init here
                sqlite3_stmt *pUpdObjID = vtab->pCtx->pStmts[STMT_UPD_OBJ_ID];
                CHECK_CALL(sqlite3_reset(pUpdObjID));
                sqlite3_bind_int64(pUpdObjID, 1, lNewID);
                sqlite3_bind_int64(pUpdObjID, 2, vtab->lClassID);
                sqlite3_bind_int64(pUpdObjID, 3, lOldID);
                CHECK_STMT_STEP(pUpdObjID, vtab->pCtx->db);
            }

            const char *zUpdPropSQL = "insert or replace into [.ref-values] (ObjectID, PropertyID, PropIndex, ctlv, [Value])"
                    " values (:1, :2, :3, :4, :5);";
            flexi_Context_stmtInit(vtab->pCtx, STMT_UPD_PROP, zUpdPropSQL, &pUpdProp);
            CHECK_CALL(flexi_upsert_props(vtab, *pRowid, pUpdProp, 1, argc, argv));
        }
    }

    result = SQLITE_OK;
    goto EXIT;

    ONERROR:
    printf("%s", sqlite3_errmsg(vtab->pCtx->db));

    EXIT:

    return result;
}
Beispiel #14
0
/*
 * Saves property values for the given object ID
 */
static int flexi_upsert_props(struct flexi_ClassDef_t *pVTab, sqlite3_int64 lObjectID,
                              sqlite3_stmt *pStmt, int bDeleteNulls, int argc, sqlite3_value **argv)
{
    int result;

    sqlite3_stmt *pDelProp;

    CHECK_CALL(flexi_validate(pVTab, argc, argv));

    // Values are coming from index 2 (0 and 1 used for object IDs)
    for (int ii = 2; ii < argc; ii++)
    {
        struct flexi_PropDef_t *pProp = &pVTab->pProps[ii - 2];
        sqlite3_value *pVal = argv[ii];

        /*
         * Check if this is range property. If so, actual value can be specified either directly
         * in format 'LoValue|HiValue', or via following computed bound properties.
         * Base range property has priority, so if it is not NULL, it will be used as property value
        */
        int bIsNull = !(argv[ii] != NULL && sqlite3_value_type(argv[ii]) != SQLITE_NULL);
        if (IS_RANGE_PROPERTY(pProp->type))
        {
            assert(ii + 2 < argc);
            if (bIsNull)
            {
                if (argv[ii + 1] != NULL && sqlite3_value_type(argv[ii + 1]) != SQLITE_NULL
                    && argv[ii + 2] != NULL && sqlite3_value_type(argv[ii + 2]) != SQLITE_NULL)
                {
                    bIsNull = 0;
                }
            }
        }

        // Check if value is not null
        if (!bIsNull)
        {
            // TODO Check if this is a mapped column
            CHECK_CALL(sqlite3_reset(pStmt));
            sqlite3_bind_int64(pStmt, 1, lObjectID);
            sqlite3_bind_int64(pStmt, 2, pProp->iPropID);
            sqlite3_bind_int(pStmt, 3, 0);
            sqlite3_bind_int(pStmt, 4, pProp->xCtlv);

            if (!IS_RANGE_PROPERTY(pProp->type))
            {
                sqlite3_bind_value(pStmt, 5, pVal);
            }
            else
            {
                //                if (argv[ii] == NULL || sqlite3_value_type(argv[ii]) == SQLITE_NULL)
                //                {
                //                    char *zRange = NULL;
                //                    switch (pProp->type)
                //                    {
                //                        case PROP_TYPE_INTEGER_RANGE:
                //                            zRange = sqlite3_mprintf("%li|%li",
                //                                                     sqlite3_value_int64(argv[ii + 1]),
                //                                                     sqlite3_value_int64(argv[ii + 2]));
                //                            break;
                //
                //                        case PROP_TYPE_DECIMAL_RANGE:
                //                        {
                //                            double d0 = sqlite3_value_double(argv[ii + 1]);
                //                            double d1 = sqlite3_value_double(argv[ii + 2]);
                //                            long long i0 = (long long) (d0 * 10000);
                //                            long long i1 = (long long) (d1 * 10000);
                //                            zRange = sqlite3_mprintf("%li|%li", i0, i1);
                //                        }
                //
                //                            break;
                //
                //                        default:
                //                            zRange = sqlite3_mprintf("%f|%f",
                //                                                     sqlite3_value_double(argv[ii + 1]),
                //                                                     sqlite3_value_double(argv[ii + 2]));
                //                            break;
                //                    }
                //
                //                    sqlite3_bind_text(pStmt, 5, zRange, -1, NULL);
                //                    sqlite3_free(zRange);
                //                }
                //                else
                //                {
                //                    sqlite3_bind_value(pStmt, 5, pVal);
                //                }
                //                ii += 2;
            }

            CHECK_STMT_STEP(pStmt, pVTab->pCtx->db);
        }
        else
        {
            // Null value

            // TODO Check if this is a mapped column
            if (bDeleteNulls && pProp->cRngBound == 0)
            {
                const char *zDelPropSQL = "delete from [.ref-values] where ObjectID = :1 and PropertyID = :2 and PropIndex = :3;";
                CHECK_CALL(flexi_Context_stmtInit(pVTab->pCtx, STMT_DEL_PROP, zDelPropSQL, &pDelProp));
                sqlite3_bind_int64(pDelProp, 1, lObjectID);
                sqlite3_bind_int64(pDelProp, 2, pProp->iPropID);
                sqlite3_bind_int(pDelProp, 3, 0);
                CHECK_STMT_STEP(pDelProp, pVTab->pCtx->db);
            }
        }
    }

    result = SQLITE_OK;
    goto EXIT;

    ONERROR:

    if (pVTab->base.zErrMsg == NULL)
    {
        // TODO Set message?
    }

    EXIT:
    return result;
}
Beispiel #15
0
/*
 * Generates dynamic SQL to find list of object IDs.
 * idxNum may be 0 or 1. When 1, idxStr will have all constraints appended by FindBestIndex.
 * Depending on number of constraint arguments in idxStr generated SQL will have of the following constructs:
 * 1. argc == 1 or all argv are for rtree search
 * 1.1. Unique index: select ObjectID from [.ref-values] where PropertyID = :1 and Value OP :2 and ctlv =
 * 1.2. Index: select ObjectID from [.ref-values] where PropertyID = :1 and Value OP :2 and ctlv =
 * 1.3. Match for full text search with index:
 * select id from [.full_text_data] where PropertyID = :1 and Value match :2
 * 1.4. Linear scan without index:
 * select ObjectID from [.ref-values] where PropertyID = :1 and Value OP :2
 * 1.5. Search by rtree:
 * select id from [.range_data] where ClassID = :1 and A0 OP :2 and A1 OP :3 and...
 *
 * 2.argc > 1
 * General pattern would be:
 * <SQL for argv == 0> intersect <SQL for argv == 1>...
 */
static int _filter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr,
                   int argc, sqlite3_value **argv)
{
    static char *range_columns[] = {"A0", "A1", "B0", "B1", "C0", "C1", "D0", "D1"};

    int result;
    struct flexi_VTabCursor *cur = (void *) pCursor;
    struct flexi_ClassDef_t *vtab = (struct flexi_ClassDef_t *) cur->base.pVtab;
    char *zSQL = NULL;

    // Subquery for [.range_data]
    char *zRangeSQL = NULL;

    if (idxNum == 0 || argc == 0)
        // No special index used. Apply linear scan
    {
        CHECK_STMT_PREPARE(
                vtab->pCtx->db, "select ObjectID from [.objects] where ClassID = :1;",
                &cur->pObjectIterator);
        sqlite3_bind_int64(cur->pObjectIterator, 1, vtab->lClassID);
    }
    else
    {
        assert(argc * 8 == strlen(idxStr));

        const char *zIdxTuple = idxStr;
        for (int i = 0; i < argc; i++)
        {
            int op;
            int colIdx;
            sscanf(zIdxTuple, "%2X|%4X|", &op, &colIdx);
            colIdx--;
            zIdxTuple += 8;

            assert(colIdx >= -1 && colIdx < vtab->propsByName.count);

            if (zSQL != NULL)
            {
                void *pTmp = zSQL;
                zSQL = sqlite3_mprintf("%s intersect ", pTmp);
                sqlite3_free(pTmp);
            }

            char *zOp;
            switch (op)
            {
                case SQLITE_INDEX_CONSTRAINT_EQ:
                    zOp = "=";
                    break;
                case SQLITE_INDEX_CONSTRAINT_GT:
                    zOp = ">";
                    break;
                case SQLITE_INDEX_CONSTRAINT_LE:
                    zOp = "<=";
                    break;
                case SQLITE_INDEX_CONSTRAINT_LT:
                    zOp = "<";
                    break;
                case SQLITE_INDEX_CONSTRAINT_GE:
                    zOp = ">=";
                    break;
                default:
                    assert(op == SQLITE_INDEX_CONSTRAINT_MATCH);
                    zOp = "match";
                    break;
            }

            if (colIdx == -1)
                // Search by rowid / ObjectID
            {
                void *pTmp = zSQL;
                zSQL = sqlite3_mprintf(
                        "%s select ObjectID from [.objects] where ObjectID %s :%d",
                        pTmp, zOp, i + 1);
                sqlite3_free(pTmp);
            }
            else
            {
                struct flexi_PropDef_t *prop = &vtab->pProps[colIdx];
                if (IS_RANGE_PROPERTY(prop->type))
                    // Special case: range data request
                {
                    assert(prop->cRangeColumn > 0);

                    if (zRangeSQL == NULL)
                    {
                        zRangeSQL = sqlite3_mprintf(
                                "select id from [.range_data] where ClassID0 = %d and ClassID1 = %d ",
                                vtab->lClassID, vtab->lClassID);
                    }
                    void *pTmp = zRangeSQL;
                    zRangeSQL = sqlite3_mprintf("%s and %s %s :%d", pTmp, range_columns[prop->cRangeColumn - 1],
                                                zOp, i + 1);
                    sqlite3_free(pTmp);
                }
                else
                    // Normal column
                {
                    void *zTmp = zSQL;

                    if (op == SQLITE_INDEX_CONSTRAINT_MATCH && prop->bFullTextIndex)
                        // full text search
                    {
                        // TODO Generate lookup on [.full_text_data]
                    }
                    else
                    {
                        zSQL = sqlite3_mprintf
                                ("%sselect ObjectID from [.ref-values] where "
                                         "[PropertyID] = %d and [PropIndex] = 0 and ", zTmp,
                                 prop->iPropID);
                        sqlite3_free(zTmp);
                        if (op != SQLITE_INDEX_CONSTRAINT_MATCH)
                        {
                            zTmp = zSQL;
                            zSQL = sqlite3_mprintf("%s[Value] %s :%d", zTmp, zOp, i + 1);
                            sqlite3_free(zTmp);

                            if (prop->bIndexed)
                            {
                                void *pTmp = zSQL;
                                zSQL = sqlite3_mprintf("%s and (ctlv & %d) = %d", pTmp, CTLV_INDEX, CTLV_INDEX);
                                sqlite3_free(pTmp);
                            }
                            else
                                if (prop->bUnique)
                                {
                                    void *pTmp = zSQL;
                                    zSQL = sqlite3_mprintf("%s and (ctlv & %d) = %d", pTmp, CTLV_UNIQUE_INDEX,
                                                           CTLV_UNIQUE_INDEX);
                                    sqlite3_free(pTmp);
                                }
                        }
                        else
                        {
                            /*
                             * TODO
                             * mem database
                             *
                             */
                            zTmp = zSQL;
                            zSQL = sqlite3_mprintf("%smatch_text(:%d, [Value])", zTmp, i + 1);
                            sqlite3_free(zTmp);
                        }
                    }
                }
            }
        }

        if (zRangeSQL != NULL)
        {
            void *pTmp = zSQL;
            zSQL = sqlite3_mprintf("%s intersect %s", pTmp, zRangeSQL);
            sqlite3_free(pTmp);
        }

        CHECK_STMT_PREPARE(vtab->pCtx->db, zSQL, &cur->pObjectIterator);
        // Bind arguments
        for (int ii = 0; ii < argc; ii++)
        {
            sqlite3_bind_value(cur->pObjectIterator, ii + 1, argv[ii]);
        }
    }

    CHECK_CALL(_next(pCursor));

    result = SQLITE_OK;
    goto EXIT;

    ONERROR:

    EXIT:
    sqlite3_free(zSQL);
    sqlite3_free(zRangeSQL);

    return result;
}