/* * Returns value for the column at position iCol (starting from 0). * Reads column data from ref-values table, filtered by ObjectID and sorted by PropertyID * For the sake of better performance, fetches required columns on demand, sequentially. * */ static int _column(sqlite3_vtab_cursor *pCursor, sqlite3_context *pContext, int iCol) { int result = SQLITE_OK; struct flexi_VTabCursor *cur = (void *) pCursor; if (iCol == -1) { sqlite3_result_int64(pContext, cur->lObjectID); goto EXIT; } struct flexi_ClassDef_t *vtab = (void *) cur->base.pVtab; // First, check if column has been already loaded while (cur->iReadCol < iCol) { int colResult = sqlite3_step(cur->pPropertyIterator); if (colResult == SQLITE_DONE) break; if (colResult != SQLITE_ROW) { result = colResult; goto ONERROR; } sqlite3_int64 lPropID = sqlite3_column_int64(cur->pPropertyIterator, 1); if (lPropID < vtab->pProps[cur->iReadCol + 1].iPropID) continue; cur->iReadCol++; if (lPropID == vtab->pProps[cur->iReadCol].iPropID) { sqlite3_int64 lPropIdx = sqlite3_column_int64(cur->pPropertyIterator, 2); /* * No need in any special verification as we expect columns are sorted by property IDs, so * we just assume that once column index is OK, we can process this property data */ cur->pCols[cur->iReadCol] = sqlite3_value_dup(sqlite3_column_value(cur->pPropertyIterator, 4)); } } if (cur->pCols[iCol] == NULL || sqlite3_value_type(cur->pCols[iCol]) == SQLITE_NULL) { sqlite3_result_value(pContext, vtab->pProps[iCol].defaultValue); } else { sqlite3_result_value(pContext, cur->pCols[iCol]); } result = SQLITE_OK; goto EXIT; ONERROR: EXIT: // Map column number to property ID return result; }
/* * Executes SQL statement zSql on zDatabase, using parameters zArgs and file name substitutions zFileArgs * Result data is loaded onto pData, and pRowCnt and pColCnt will indicate number of loaded rows and * number of columns in the row. Total number of items in pData will be nRowCnt * nColCnt */ static int _runSql(char *zDatabase, char *zSrcSql, char *zArgs, char *zFileArgs, Array_t *pData, int *pColCnt, char *zEntryFilePath, char *zSubstFileNames) { int result; sqlite3 *pDB = NULL; sqlite3_stmt *pStmt = NULL; sqlite3_stmt *pArgsStmt = NULL; Array_t sqlArgs; char *zError = NULL; char *zFullFilePath = NULL; sqlite3_stmt *pSubstStmt = NULL; char *zFileContent = NULL; char *zSchemaSql = NULL; char *zSql = strCopy(zSrcSql, -1); /* * Only first 16 substitute parameters will be processed. This is related to the fact that in C there * is no non-hacking way to dynamically build variadic arguments. So, to support list of values we just * limit maximum number of substitute strings to reasonably high number (16) */ const char *zz[16]; memset(&zz, 0, sizeof(zz)); Array_init(&sqlArgs, sizeof(SqlArg_t), (void *) _freeSqlArg); *pColCnt = 0; // Array_init(pData, sizeof(sqlite3_value *), (void *) _freeSqliteValue); /* * Open database (use :memory: if not defined) */ if (zDatabase == NULL || strlen(zDatabase) == 0) { zDatabase = ":memory:"; } CHECK_CALL(sqlite3_open_v2(zDatabase, &pDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, NULL)); CHECK_CALL(sqlite3_enable_load_extension(pDB, 1)); // load extension library CHECK_CALL(sqlite3_load_extension(pDB, "../../bin/libFlexilite", NULL, &zError)); // load and run db schema CHECK_CALL(file_load_utf8("../../sql/dbschema.sql", &zSchemaSql)); CHECK_CALL(sqlite3_exec(pDB, (const char *) zSchemaSql, NULL, NULL, &zError)); /* * Substitute strings */ if (!STR_EMPTY(zSubstFileNames)) { CHECK_STMT_PREPARE(pDB, "select key, value from json_each(:1);", &pSubstStmt); CHECK_CALL(sqlite3_bind_text(pSubstStmt, 1, zSubstFileNames, -1, NULL)); int nSubst = 0; while ((result = sqlite3_step(pSubstStmt)) == SQLITE_ROW) { if (nSubst >= 16) { result = SQLITE_ERROR; zError = "Number of substitute strings must not exceed 16"; goto ONERROR; } sqlite3_free(zFullFilePath); zFullFilePath = NULL; Path_join(&zFullFilePath, zEntryFilePath, (char *) sqlite3_column_text(pSubstStmt, 1)); CHECK_CALL(file_load_utf8(zFullFilePath, &zFileContent)); zz[nSubst++] = zFileContent; zFileContent = NULL; // Memory will be freed by zz } if (result != SQLITE_DONE) goto ONERROR; char *zTemp = zSql; zSql = sqlite3_mprintf(zTemp, zz[0], zz[1], zz[2], zz[3], zz[4], zz[5], zz[6], zz[7], zz[8], zz[9], zz[10], zz[11], zz[12], zz[13], zz[14], zz[15]); sqlite3_free(zTemp); } // TODO use flexi('init') CHECK_STMT_PREPARE(pDB, zSql, &pStmt); // Check if we have arguments JSON. Prepare arguments if (!STR_EMPTY(zArgs)) { CHECK_STMT_PREPARE(pDB, "select value, type from json_each(:1);", &pArgsStmt); CHECK_CALL(sqlite3_bind_text(pArgsStmt, 1, zArgs, -1, NULL)); int nArgCnt = 0; while ((result = sqlite3_step(pArgsStmt)) == SQLITE_ROW) { SqlArg_t item; memset(&item, 0, sizeof(item)); item.pValue = sqlite3_value_dup(sqlite3_column_value(pArgsStmt, 0)); Array_setNth(&sqlArgs, sqlArgs.iCnt, &item); nArgCnt++; } if (result != SQLITE_DONE) goto ONERROR; /* * Process file args */ CHECK_CALL(sqlite3_reset(pArgsStmt)); CHECK_CALL(sqlite3_bind_text(pArgsStmt, 1, zFileArgs, -1, NULL)); while ((result = sqlite3_step(pArgsStmt)) == SQLITE_ROW) { int argNo = sqlite3_column_int(pArgsStmt, 0); if (argNo >= 1 && argNo <= nArgCnt) { sqlite3_free(zFullFilePath); zFullFilePath = NULL; SqlArg_t *arg = Array_getNth(&sqlArgs, (u32) argNo - 1); Path_join(&zFullFilePath, zEntryFilePath, (char *) sqlite3_value_text(arg->pValue)); CHECK_CALL(file_load_utf8(zFullFilePath, &arg->zText)); } } if (result != SQLITE_DONE) goto ONERROR; Array_each(&sqlArgs, (void *) _bindSqlArg, pStmt); } while ((result = sqlite3_step(pStmt)) == SQLITE_ROW) { if (*pColCnt == 0) *pColCnt = sqlite3_column_count(pStmt); int iCol; for (iCol = 0; iCol < *pColCnt; iCol++) { sqlite3_value *pVal = sqlite3_value_dup(sqlite3_column_value(pStmt, iCol)); Array_setNth(pData, pData->iCnt, &pVal); } } if (result != SQLITE_DONE) goto ONERROR; result = SQLITE_OK; goto EXIT; ONERROR: Array_clear(pData); if (pDB && zError == NULL) { zError = (char *) sqlite3_errmsg(pDB); } if (zError != NULL) printf("Error: %s\n", zError); EXIT: sqlite3_free(zSchemaSql); sqlite3_finalize(pStmt); sqlite3_finalize(pArgsStmt); sqlite3_finalize(pSubstStmt); if (pDB != NULL) { result = sqlite3_close(pDB); if (result != SQLITE_OK) { printf("DB Close Error %d. %s\n", result, sqlite3_errmsg(pDB)); } } Array_clear(&sqlArgs); sqlite3_free(zFullFilePath); sqlite3_free(zSql); for (int i = 0; i < ARRAY_LEN(zz); i++) sqlite3_free((void *) zz[i]); if (zFileContent != NULL) sqlite3_free(zFileContent); return result; }
DLL_FUNCTION(sqlite3_value*) BU_SQLite_Value_Duplicate(sqlite3_value* pValue) { return sqlite3_value_dup(pValue); }