Skip to content

Commit 446c44f

Browse files
committed
Correctly parse and bind the CREATE INDEX SQL statements from SQLite files
1 parent c7f2645 commit 446c44f

File tree

5 files changed

+166
-7
lines changed

5 files changed

+166
-7
lines changed

data/db/accounts_sample.db

208 KB
Binary file not shown.

src/sqlite_db.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void SQLiteDB::Close() {
114114

115115
vector<string> SQLiteDB::GetEntries(string entry_type) {
116116
vector<string> result;
117-
SQLiteStatement stmt = Prepare("SELECT name FROM sqlite_master WHERE type='" + entry_type + "'");
117+
SQLiteStatement stmt = Prepare("SELECT name FROM sqlite_master WHERE length(sql) > 0 AND type='" + entry_type + "'");
118118
while (stmt.Step()) {
119119
auto table_name = stmt.GetValue<string>(0);
120120
result.push_back(std::move(table_name));

src/storage/sqlite_transaction.cpp

+50-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
#include "duckdb/parser/parsed_data/create_view_info.hpp"
88
#include "duckdb/catalog/catalog_entry/index_catalog_entry.hpp"
99
#include "duckdb/catalog/catalog_entry/view_catalog_entry.hpp"
10+
#include "duckdb/parser/parser.hpp"
11+
#include "duckdb/parser/statement/create_statement.hpp"
12+
#include "duckdb/parser/parsed_expression_iterator.hpp"
13+
#include "duckdb/parser/expression/columnref_expression.hpp"
1014

1115
namespace duckdb {
1216

@@ -44,6 +48,45 @@ SQLiteTransaction &SQLiteTransaction::Get(ClientContext &context, Catalog &catal
4448
return Transaction::Get(context, catalog).Cast<SQLiteTransaction>();
4549
}
4650

51+
void ExtractColumnIds(const ParsedExpression &expr, TableCatalogEntry &table, CreateIndexInfo &info) {
52+
if (expr.GetExpressionType() == ExpressionType::COLUMN_REF) {
53+
auto &colref = expr.Cast<ColumnRefExpression>();
54+
auto &colname = colref.GetColumnName();
55+
auto &column_def = table.GetColumn(colname);
56+
auto index = column_def.Oid();
57+
if (std::find(info.column_ids.begin(), info.column_ids.end(), index) == info.column_ids.end()) {
58+
info.column_ids.push_back(index);
59+
}
60+
return;
61+
}
62+
ParsedExpressionIterator::EnumerateChildren(expr, [&](const ParsedExpression &child) {
63+
ExtractColumnIds(child, table, info);
64+
});
65+
}
66+
67+
unique_ptr<CreateIndexInfo> FromCreateIndex(ClientContext &context, TableCatalogEntry &table, string sql) {
68+
// parse the SQL statement
69+
Parser parser;
70+
parser.ParseQuery(sql);
71+
72+
if (parser.statements.size() != 1 || parser.statements[0]->type != StatementType::CREATE_STATEMENT) {
73+
throw BinderException(
74+
"Failed to create index from SQL string - \"%s\" - statement did not contain a single CREATE INDEX statement",
75+
sql);
76+
}
77+
auto &create_statement = parser.statements[0]->Cast<CreateStatement>();
78+
if (create_statement.info->type != CatalogType::INDEX_ENTRY) {
79+
throw BinderException(
80+
"Failed to create view from SQL string - \"%s\" - view did not contain a CREATE INDEX statement", sql);
81+
}
82+
auto info = unique_ptr_cast<CreateInfo, CreateIndexInfo>(std::move(create_statement.info));
83+
info->sql = std::move(sql);
84+
for(auto &expr : info->expressions) {
85+
ExtractColumnIds(*expr, table, *info);
86+
}
87+
return info;
88+
}
89+
4790
optional_ptr<CatalogEntry> SQLiteTransaction::GetCatalogEntry(const string &entry_name) {
4891
auto entry = catalog_entries.find(entry_name);
4992
if (entry != catalog_entries.end()) {
@@ -80,17 +123,18 @@ optional_ptr<CatalogEntry> SQLiteTransaction::GetCatalogEntry(const string &entr
80123
break;
81124
}
82125
case CatalogType::INDEX_ENTRY: {
83-
CreateIndexInfo info;
84-
info.index_name = entry_name;
85-
info.constraint_type = IndexConstraintType::NONE;
86-
87126
string table_name;
88127
string sql;
89128
db->GetIndexInfo(entry_name, sql, table_name);
129+
if (sql.empty()) {
130+
throw InternalException("SQL is empty");
131+
}
132+
auto &table = GetCatalogEntry(table_name)->Cast<TableCatalogEntry>();
133+
auto index_info = FromCreateIndex(*context.lock(), table, std::move(sql));
134+
index_info->catalog = sqlite_catalog.GetName();
90135

91136
auto index_entry =
92-
make_uniq<SQLiteIndexEntry>(sqlite_catalog, sqlite_catalog.GetMainSchema(), info, std::move(table_name));
93-
index_entry->sql = std::move(sql);
137+
make_uniq<SQLiteIndexEntry>(sqlite_catalog, sqlite_catalog.GetMainSchema(), *index_info, std::move(table_name));
94138
result = std::move(index_entry);
95139
break;
96140
}
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# name: test/sql/storage/attach_copy_index.test
2+
# description:
3+
# group: [sqlite_storage]
4+
5+
require sqlite_scanner
6+
7+
statement ok
8+
ATTACH 'data/db/accounts_sample.db' AS s (TYPE SQLITE, READ_ONLY)
9+
10+
statement ok
11+
USE s
12+
13+
query III rowsort
14+
SELECT t1.post_date,a1.name,CAST(-CAST(s2.value_num AS DECIMAL(10,2))/s1.value_denom AS DECIMAL(10,2)) AS amount
15+
FROM splits s1
16+
INNER JOIN splits s2 ON s1.tx_guid=s2.tx_guid
17+
INNER JOIN transactions t1 ON t1.guid=s2.tx_guid
18+
INNER JOIN accounts a1 ON s1.account_guid=a1.guid
19+
INNER JOIN accounts a2 ON s2.account_guid=a2.guid
20+
WHERE a1.name!=a2.name and a1.name='Checking';
21+
----
22+
2014-12-24 10:59:00 Checking -100.00
23+
2014-12-24 10:59:00 Checking -200.00
24+
2014-12-24 10:59:00 Checking -30.00
25+
2014-12-24 10:59:00 Checking 1000.00
26+
2018-02-20 10:59:00 Checking -100.00
27+
2018-02-20 10:59:00 Checking 250.00
28+
29+
statement ok
30+
ATTACH '__TEST_DIR__/target.db' AS ddb;
31+
32+
statement ok
33+
COPY FROM DATABASE s to ddb;
34+
35+
statement ok
36+
USE ddb
37+
38+
query III rowsort
39+
SELECT t1.post_date,a1.name,CAST(-CAST(s2.value_num AS DECIMAL(10,2))/s1.value_denom AS DECIMAL(10,2)) AS amount
40+
FROM splits s1
41+
INNER JOIN splits s2 ON s1.tx_guid=s2.tx_guid
42+
INNER JOIN transactions t1 ON t1.guid=s2.tx_guid
43+
INNER JOIN accounts a1 ON s1.account_guid=a1.guid
44+
INNER JOIN accounts a2 ON s2.account_guid=a2.guid
45+
WHERE a1.name!=a2.name and a1.name='Checking';
46+
----
47+
2014-12-24 10:59:00 Checking -100.00
48+
2014-12-24 10:59:00 Checking -200.00
49+
2014-12-24 10:59:00 Checking -30.00
50+
2014-12-24 10:59:00 Checking 1000.00
51+
2018-02-20 10:59:00 Checking -100.00
52+
2018-02-20 10:59:00 Checking 250.00
53+
54+
statement ok
55+
USE s
56+
57+
statement ok
58+
DETACH ddb
59+
60+
statement ok
61+
ATTACH '__TEST_DIR__/target.db' AS ddb;
62+
63+
statement ok
64+
USE ddb
65+
66+
query III rowsort
67+
SELECT t1.post_date,a1.name,CAST(-CAST(s2.value_num AS DECIMAL(10,2))/s1.value_denom AS DECIMAL(10,2)) AS amount
68+
FROM splits s1
69+
INNER JOIN splits s2 ON s1.tx_guid=s2.tx_guid
70+
INNER JOIN transactions t1 ON t1.guid=s2.tx_guid
71+
INNER JOIN accounts a1 ON s1.account_guid=a1.guid
72+
INNER JOIN accounts a2 ON s2.account_guid=a2.guid
73+
WHERE a1.name!=a2.name and a1.name='Checking';
74+
----
75+
2014-12-24 10:59:00 Checking -100.00
76+
2014-12-24 10:59:00 Checking -200.00
77+
2014-12-24 10:59:00 Checking -30.00
78+
2014-12-24 10:59:00 Checking 1000.00
79+
2018-02-20 10:59:00 Checking -100.00
80+
2018-02-20 10:59:00 Checking 250.00
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# name: test/sql/storage/attach_index_expressions.test
2+
# description:
3+
# group: [sqlite_storage]
4+
5+
require sqlite_scanner
6+
7+
statement ok
8+
ATTACH 'data/db/sakila.db' AS s (TYPE SQLITE, READ_ONLY)
9+
10+
query I
11+
SELECT expressions FROM duckdb_indexes() WHERE index_name='idx_actor_last_name';
12+
----
13+
[last_name]
14+
15+
statement ok
16+
ATTACH '__TEST_DIR__/sakila.duckdb' AS sakila_duck;
17+
18+
statement ok
19+
COPY FROM DATABASE s TO sakila_duck
20+
21+
query IIII
22+
SELECT * FROM sakila_duck.store WHERE manager_staff_id=2;
23+
----
24+
2 2 2 2021-03-06 15:52:00
25+
26+
statement ok
27+
DETACH sakila_duck;
28+
29+
statement ok
30+
ATTACH '__TEST_DIR__/sakila.duckdb' AS sakila_duck;
31+
32+
query IIII
33+
SELECT * FROM sakila_duck.store WHERE manager_staff_id=2;
34+
----
35+
2 2 2 2021-03-06 15:52:00

0 commit comments

Comments
 (0)