diff --git a/src/query.c b/src/query.c index afa104b34..44a0a7b86 100644 --- a/src/query.c +++ b/src/query.c @@ -8,6 +8,11 @@ static int value_type(sqlite3_stmt *stmt, int i) { int type = sqlite3_column_type(stmt, i); + + if (type == SQLITE_NULL) { + return SQLITE_NULL; + } + const char *column_type_name = sqlite3_column_decltype(stmt, i); if (column_type_name != NULL) { if ((strcasecmp(column_type_name, "DATETIME") == 0) || @@ -16,12 +21,11 @@ static int value_type(sqlite3_stmt *stmt, int i) if (type == SQLITE_INTEGER) { type = DQLITE_UNIXTIME; } else { - dqlite_assert(type == SQLITE_TEXT || - type == SQLITE_NULL); + dqlite_assert(type == SQLITE_TEXT); type = DQLITE_ISO8601; } } else if (strcasecmp(column_type_name, "BOOLEAN") == 0) { - dqlite_assert(type == SQLITE_INTEGER || type == SQLITE_NULL); + dqlite_assert(type == SQLITE_INTEGER); type = DQLITE_BOOLEAN; } } diff --git a/test/integration/test_client.c b/test/integration/test_client.c index 9826ef4a4..aa288dcb7 100644 --- a/test/integration/test_client.c +++ b/test/integration/test_client.c @@ -180,3 +180,42 @@ TEST(client, semicolons, setUp, tearDown, 0, NULL) return MUNIT_OK; } + +/* A NULL value must be encoded with the SQLITE_NULL type tag regardless of + * the column's declared type. */ +TEST(client, nullPreservedAcrossDeclaredTypes, setUp, tearDown, 0, NULL) +{ + struct fixture *f = data; + uint64_t last_insert_id; + uint64_t rows_affected; + struct row *row; + (void)params; + + EXEC_SQL("CREATE TABLE t (" + " int_col INTEGER," + " text_col TEXT," + " real_col REAL," + " blob_col BLOB," + " bool_col BOOLEAN," + " datetime_col DATETIME," + " date_col DATE," + " timestamp_col TIMESTAMP)", + &last_insert_id, &rows_affected); + EXEC_SQL("INSERT INTO t VALUES " + "(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)", + &last_insert_id, &rows_affected); + + QUERY_SQL("SELECT int_col, text_col, real_col, blob_col, " + " bool_col, datetime_col, date_col, timestamp_col " + "FROM t", + &f->rows); + munit_assert_uint(f->rows.column_count, ==, 8); + row = f->rows.next; + munit_assert_not_null(row); + for (unsigned i = 0; i < f->rows.column_count; i++) { + munit_assert_int(row->values[i].type, ==, SQLITE_NULL); + } + munit_assert_ptr_null(row->next); + + return MUNIT_OK; +}