Error message from table-valued function xConnect is ignored
(1) By David Matson (dmatson) on 2021-07-08 01:08:44 [link] [source]
When developing a table-valued function, I've tried to provide a custom error from xConnect when the table can't be created for some reason (such as due to a programmer syntax error in the SQL passed to sqlite3_declare_vtab). xConnect has an explicit char** pzErr parameter, but it is ignored in this case.
Specifically, vtabCallConstructor copies this error message and returns it up to sqlite3VtabEponymousTableInit, which again copies in correctly. But when it returns to sqlite3LocateTable, that function overwrites the custom error message with "no such table":
if( p==0 ){
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
if( zDbase ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
}
...
Could SQLite preserve the error message in this case?
Below is source with a repro.
Thanks,
David
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "sqlite3.h"
static int testVTabConnect(sqlite3* connection, void* pAux, int argc, const char* const* argv, sqlite3_vtab** ppVtab,
char** pzErr)
{
(void)pAux;
(void)argc;
(void)argv;
sqlite3_vtab* table = (sqlite3_vtab*)sqlite3_malloc(sizeof(*table));
if (table == NULL)
{
return SQLITE_NOMEM;
}
memset(table, 0, sizeof(*table));
int rc = sqlite3_declare_vtab(connection, "CREATE TABLE x(Value INTEGER PRIMARY KEY) BAD SYNTAX HERE;");
if (rc != SQLITE_OK)
{
sqlite3_free(table);
const char* connectionError = sqlite3_errmsg(connection);
size_t errorLength = strlen(connectionError);
char* errorMessageCopy = (char*)sqlite3_malloc64((sqlite3_uint64)errorLength + 1);
if (errorMessageCopy == NULL)
{
return SQLITE_NOMEM;
}
strncpy_s(errorMessageCopy, errorLength + 1, connectionError, errorLength);
*pzErr = errorMessageCopy;
return rc;
}
*ppVtab = table;
return SQLITE_OK;
}
static int testVTabDisconnect(sqlite3_vtab* pVtab)
{
sqlite3_free(pVtab);
return SQLITE_OK;
}
static int testVTabBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo)
{
(void)tab;
pIdxInfo->idxNum = 0;
pIdxInfo->idxStr = NULL;
pIdxInfo->estimatedCost = 1;
pIdxInfo->estimatedRows = 1;
return SQLITE_OK;
}
struct test_vtab_cursor
{
sqlite3_vtab_cursor base; // This base struct must come first.
sqlite_int64 rowid;
};
typedef struct test_vtab_cursor test_vtab_cursor;
static int testVTabOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor)
{
(void)p;
test_vtab_cursor* cursor = (test_vtab_cursor*)sqlite3_malloc(sizeof(*cursor));
if (cursor == NULL)
{
return SQLITE_NOMEM;
}
memset(cursor, 0, sizeof(*cursor));
*ppCursor = &cursor->base;
return SQLITE_OK;
}
static int testVTabClose(sqlite3_vtab_cursor* cur)
{
test_vtab_cursor* cursor = (test_vtab_cursor*)cur;
sqlite3_free(cursor);
return SQLITE_OK;
}
static int testVTabFilter(sqlite3_vtab_cursor* pVtabCursor, int idxNum, const char* idxStr, int argc,
sqlite3_value** argv)
{
assert(idxNum == 0);
(void)idxNum;
assert(idxStr == NULL);
(void)idxStr;
assert(argc == 0);
(void)argc;
(void)argv;
test_vtab_cursor* cursor = (test_vtab_cursor*)pVtabCursor;
cursor->rowid = 0;
return SQLITE_OK;
}
static int testVTabNext(sqlite3_vtab_cursor* cur)
{
test_vtab_cursor* cursor = (test_vtab_cursor*)cur;
++(cursor->rowid);
return SQLITE_OK;
}
static int testVTabEof(sqlite3_vtab_cursor* cur)
{
test_vtab_cursor* cursor = (test_vtab_cursor*)cur;
return cursor->rowid < 1 ? 0 : 1;
}
static int testVTabColumn(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int i)
{
assert(i == 0);
(void)i;
test_vtab_cursor* cursor = (test_vtab_cursor*)cur;
sqlite3_result_int64(ctx, cursor->rowid);
return SQLITE_OK;
}
static int testVTabRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid)
{
test_vtab_cursor* cursor = (test_vtab_cursor*)cur;
*pRowid = cursor->rowid;
return SQLITE_OK;
}
static sqlite3_module testVTabModule = {
3, // iVersion
NULL, // xCreate
testVTabConnect, // xConnect
testVTabBestIndex, // xBestIndex
testVTabDisconnect, //xDisconnect
NULL, // xDestroy
testVTabOpen, //int xOpen
testVTabClose, //int xClose
testVTabFilter, //int xFilter
testVTabNext, // int xNext
testVTabEof, // int xEof
testVTabColumn, // xColumn
testVTabRowid, // xRowid
NULL, // xUpdate
NULL, // xBegin
NULL, // xSync
NULL, // xCommit
NULL, // xRollback
NULL, // xFindFunction
NULL, // xRename
NULL, // xSavepoint
NULL, // xRelease
NULL, // xRollbackTo
NULL, // xShadowName
};
int main()
{
int rc = 0;
int rcCleanup = 0;
int rcCurrentCleanup = 0;
sqlite3* db = NULL;
sqlite3_stmt* stmt = NULL;
rc = sqlite3_open("D:\\test.db", &db);
if (rc)
{
goto cleanup;
}
rc = sqlite3_create_module_v2(db, "testvtab", &testVTabModule, NULL, NULL);
if (rc)
{
goto cleanup;
}
rc = sqlite3_prepare_v2(db, "SELECT count(*) FROM testvtab();", -1, &stmt, NULL);
if (rc)
{
goto cleanup;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW)
{
goto cleanup;
}
printf("%i\n", sqlite3_column_int(stmt, 0));
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
goto cleanup;
}
rc = SQLITE_OK;
cleanup:
if (rc && db)
{
fprintf(stderr, "%s\n", sqlite3_errmsg(db));
}
if (stmt)
{
rcCurrentCleanup = sqlite3_finalize(stmt);
if (rcCurrentCleanup && !rcCleanup)
{
rcCleanup = rcCurrentCleanup;
}
}
if (db)
{
rcCurrentCleanup = sqlite3_close_v2(db);
if (rcCurrentCleanup && !rcCleanup)
{
rcCleanup = rcCurrentCleanup;
}
}
if (rcCleanup)
{
return rcCleanup;
}
return rc;
}
(2) By Dan Kennedy (dan) on 2021-07-09 10:55:19 in reply to 1 [link] [source]
Thanks for reporting this. Should now be fixed here:
https://sqlite.org/src/info/bbbbeb59a6a14b94
Dan.
(3) By Max (Maxulite) on 2021-07-09 11:49:10 in reply to 2 [source]
It looks like it's the same issue as I reported recently (https://sqlite.org/forum/forumpost/db63efd0d1). At least now when switching from 3.36.00 to the check-in version I see my message instead of "no such table ...". Thanks!