Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/MainDistributionPipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ concurrency:
jobs:
duckdb-stable-build:
name: Build extension binaries
uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.4.0
uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@main
with:
duckdb_version: main
extension_name: sqlite_scanner
Expand All @@ -23,7 +23,7 @@ jobs:
duckdb-stable-deploy:
name: Deploy extension binaries
needs: duckdb-stable-build
uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@v1.4.0
uses: duckdb/extension-ci-tools/.github/workflows/_extension_deploy.yml@main
secrets: inherit
with:
duckdb_version: main
Expand Down
1 change: 1 addition & 0 deletions src/include/sqlite_scanner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct SqliteBindData : public TableFunctionData {
vector<string> names;
vector<LogicalType> types;
string sql;
vector<Value> params;

RowIdInfo row_id_info;
bool all_varchar = false;
Expand Down
3 changes: 3 additions & 0 deletions src/include/sqlite_stmt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ class SQLiteStatement {
throw InternalException("Unsupported type for SQLiteStatement::Bind");
}
void BindText(idx_t col, const string_t &value);
void BindText(idx_t col, const string &value);
void BindBlob(idx_t col, const string_t &value);
void BindBlob(idx_t col, const string &value);
void BindValue(Vector &col, idx_t c, idx_t r);
void BindParameter(const Value &param, idx_t param_idx);
int GetType(idx_t col);
string GetName(idx_t col);
idx_t GetColumnCount();
Expand Down
5 changes: 5 additions & 0 deletions src/sqlite_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ static void SqliteInitInternal(ClientContext &context, const SqliteBindData &bin
sql = bind_data.sql;
}
local_state.stmt = local_state.db->Prepare(sql.c_str());

for (idx_t i = 0; i < bind_data.params.size(); i++) {
const Value &param = bind_data.params[i];
local_state.stmt.BindParameter(param, i);
}
}

static unique_ptr<NodeStatistics> SqliteCardinality(ClientContext &context, const FunctionData *bind_data_p) {
Expand Down
31 changes: 31 additions & 0 deletions src/sqlite_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,18 @@ void SQLiteStatement::BindBlob(idx_t col, const string_t &value) {
SQLiteUtils::Check(sqlite3_bind_blob(stmt, col + 1, value.GetDataUnsafe(), value.GetSize(), nullptr), db);
}

void SQLiteStatement::BindBlob(idx_t col, const string &value) {
SQLiteUtils::Check(sqlite3_bind_blob(stmt, col + 1, value.c_str(), value.length(), nullptr), db);
}

void SQLiteStatement::BindText(idx_t col, const string_t &value) {
SQLiteUtils::Check(sqlite3_bind_text(stmt, col + 1, value.GetDataUnsafe(), value.GetSize(), nullptr), db);
}

void SQLiteStatement::BindText(idx_t col, const string &value) {
SQLiteUtils::Check(sqlite3_bind_text(stmt, col + 1, value.c_str(), value.length(), nullptr), db);
}

template <>
void SQLiteStatement::Bind(idx_t col, std::nullptr_t value) {
SQLiteUtils::Check(sqlite3_bind_null(stmt, col + 1), db);
Expand Down Expand Up @@ -181,4 +189,27 @@ void SQLiteStatement::BindValue(Vector &col, idx_t c, idx_t r) {
}
}

void SQLiteStatement::BindParameter(const Value &param, idx_t param_idx) {
if (param.IsNull()) {
Bind<std::nullptr_t>(param_idx, nullptr);
} else {
switch (param.type().id()) {
case LogicalTypeId::BIGINT:
Bind<int64_t>(param_idx, BigIntValue::Get(param));
break;
case LogicalTypeId::DOUBLE:
Bind<double>(param_idx, DoubleValue::Get(param));
break;
case LogicalTypeId::BLOB:
BindBlob(param_idx, StringValue::Get(param));
break;
case LogicalTypeId::VARCHAR:
BindText(param_idx, StringValue::Get(param));
break;
default:
throw InternalException("Unsupported parameter type \"%s\", index: %zu for SQLite::BindValue", param.type().ToString(), param_idx);
}
}
}

} // namespace duckdb
29 changes: 29 additions & 0 deletions src/storage/sqlite_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,33 @@ static unique_ptr<FunctionData> SQLiteQueryBind(ClientContext &context, TableFun
StringUtil::RTrim(sql);
}

vector<Value> params;
auto params_it = input.named_parameters.find("params");
if (params_it != input.named_parameters.end()) {
Value &struct_val = params_it->second;
if (struct_val.IsNull()) {
throw BinderException("Parameters to sqlite_query cannot be NULL");
}
if (struct_val.type().id() != LogicalTypeId::STRUCT) {
throw BinderException("Query parameters must be specified in a STRUCT");
}
params = StructValue::GetChildren(struct_val);
for (idx_t i = 0; i < params.size(); i++) {
const Value &param = params[i];
switch(param.type().id()) {
case LogicalTypeId::BIGINT:
case LogicalTypeId::DOUBLE:
case LogicalTypeId::BLOB:
case LogicalTypeId::VARCHAR:
break;
default:
if (!param.IsNull()) {
throw BinderException("Unsupported parameter type \"%s\", index: %zu", param.type().ToString(), i);
}
}
}
}

auto &con = transaction.GetDB();
auto stmt = con.Prepare(sql);
if (!stmt.stmt) {
Expand All @@ -57,6 +84,7 @@ static unique_ptr<FunctionData> SQLiteQueryBind(ClientContext &context, TableFun
}
result->rows_per_group = optional_idx();
result->sql = std::move(sql);
result->params = std::move(params);
result->all_varchar = true;
result->file_name = sqlite_catalog.GetDBPath();
result->global_db = &con;
Expand All @@ -70,5 +98,6 @@ SQLiteQueryFunction::SQLiteQueryFunction()
init_local = scan_function.init_local;
function = scan_function.function;
global_initialization = TableFunctionInitialization::INITIALIZE_ON_SCHEDULE;
named_parameters["params"] = LogicalType::ANY;
}
} // namespace duckdb
68 changes: 68 additions & 0 deletions test/sql/storage/attach_sqlite_query.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ SELECT * FROM sqlite_query(s, 'SELECT unixepoch(''1992-01-01'') a')
----
694224000

query I
SELECT * FROM sqlite_query(s, 'SELECT unixepoch(?) a', params=row('1992-01-01'))
----
694224000

statement ok
CREATE OR REPLACE TABLE s.tbl AS SELECT * FROM range(10000) t(r);

Expand All @@ -40,6 +45,13 @@ GUINESS ED
GUINESS PENELOPE
GUINESS SEAN

query II
SELECT last_name, first_name FROM sqlite_query(sakila, 'SELECT * FROM actor WHERE last_name=?', params=row('GUINESS')) ORDER BY ALL
----
GUINESS ED
GUINESS PENELOPE
GUINESS SEAN

statement error
SELECT * FROM sqlite_query(s, '')
----
Expand All @@ -54,3 +66,59 @@ statement error
SELECT * FROM sqlite_query(s, 'CREATE TABLE my_table(a,b,c)')
----
query must return data

query I
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=row(NULL))
----
NULL

query I
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=row(42::BIGINT))
----
42

query I
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=row(42.123::DOUBLE))
----
42.123

query I
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=row('foo'::BLOB))
----
foo

query I
SELECT * from sqlite_query(sakila, 'SELECT ? || ? a', params=row('foo', 'bar'))
----
foobar

statement ok
PREPARE p1 as SELECT * from sqlite_query(sakila, 'SELECT ? || ? a', params=row(?, ?))

query I
EXECUTE p1('foo', 'bar')
----
foobar

query I
EXECUTE p1('baz', 'boo')
----
bazboo

statement ok
DEALLOCATE p1

statement error
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=row(42::TINYINT))
----
Binder Error: Unsupported parameter type "TINYINT", index: 0

statement error
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=NULL)
----
Binder Error: Parameters to sqlite_query cannot be NULL

statement error
SELECT * from sqlite_query(sakila, 'SELECT ? a', params=42)
----
Binder Error: Query parameters must be specified in a STRUCT
Loading