Skip to content

Commit 7ce99bc

Browse files
authored
Fix InsertRelation on attached database (#19583)
Fixes #18396 Related PR in duckdb-python: duckdb/duckdb-python#155
2 parents 1555b04 + aea8434 commit 7ce99bc

File tree

9 files changed

+85
-14
lines changed

9 files changed

+85
-14
lines changed

src/include/duckdb/main/relation.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,19 +162,27 @@ class Relation : public enable_shared_from_this<Relation> {
162162

163163
//! Insert the data from this relation into a table
164164
DUCKDB_API shared_ptr<Relation> InsertRel(const string &schema_name, const string &table_name);
165+
DUCKDB_API shared_ptr<Relation> InsertRel(const string &catalog_name, const string &schema_name,
166+
const string &table_name);
165167
DUCKDB_API void Insert(const string &table_name);
166168
DUCKDB_API void Insert(const string &schema_name, const string &table_name);
169+
DUCKDB_API void Insert(const string &catalog_name, const string &schema_name, const string &table_name);
167170
//! Insert a row (i.e.,list of values) into a table
168-
DUCKDB_API void Insert(const vector<vector<Value>> &values);
169-
DUCKDB_API void Insert(vector<vector<unique_ptr<ParsedExpression>>> &&expressions);
171+
DUCKDB_API virtual void Insert(const vector<vector<Value>> &values);
172+
DUCKDB_API virtual void Insert(vector<vector<unique_ptr<ParsedExpression>>> &&expressions);
170173
//! Create a table and insert the data from this relation into that table
171174
DUCKDB_API shared_ptr<Relation> CreateRel(const string &schema_name, const string &table_name,
172175
bool temporary = false,
173176
OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT);
177+
DUCKDB_API shared_ptr<Relation> CreateRel(const string &catalog_name, const string &schema_name,
178+
const string &table_name, bool temporary = false,
179+
OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT);
174180
DUCKDB_API void Create(const string &table_name, bool temporary = false,
175181
OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT);
176182
DUCKDB_API void Create(const string &schema_name, const string &table_name, bool temporary = false,
177183
OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT);
184+
DUCKDB_API void Create(const string &catalog_name, const string &schema_name, const string &table_name,
185+
bool temporary = false, OnCreateConflict on_conflict = OnCreateConflict::ERROR_ON_CONFLICT);
178186

179187
//! Write a relation to a CSV file
180188
DUCKDB_API shared_ptr<Relation>

src/include/duckdb/main/relation/create_table_relation.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ class CreateTableRelation : public Relation {
1616
public:
1717
CreateTableRelation(shared_ptr<Relation> child, string schema_name, string table_name, bool temporary,
1818
OnCreateConflict on_conflict);
19+
CreateTableRelation(shared_ptr<Relation> child, string catalog_name, string schema_name, string table_name,
20+
bool temporary, OnCreateConflict on_conflict);
1921

2022
shared_ptr<Relation> child;
23+
string catalog_name;
2124
string schema_name;
2225
string table_name;
2326
vector<ColumnDefinition> columns;

src/include/duckdb/main/relation/insert_relation.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ namespace duckdb {
1515
class InsertRelation : public Relation {
1616
public:
1717
InsertRelation(shared_ptr<Relation> child, string schema_name, string table_name);
18+
InsertRelation(shared_ptr<Relation> child, string catalog_name, string schema_name, string table_name);
1819

1920
shared_ptr<Relation> child;
21+
string catalog_name;
2022
string schema_name;
2123
string table_name;
2224
vector<ColumnDefinition> columns;

src/include/duckdb/main/relation/table_relation.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class TableRelation : public Relation {
2929

3030
unique_ptr<TableRef> GetTableRef() override;
3131

32+
void Insert(const vector<vector<Value>> &values) override;
33+
void Insert(vector<vector<unique_ptr<ParsedExpression>>> &&expressions) override;
3234
void Update(const string &update, const string &condition = string()) override;
3335
void Update(vector<string> column_names, vector<unique_ptr<ParsedExpression>> &&update,
3436
unique_ptr<ParsedExpression> condition = nullptr) override;

src/main/relation.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -241,15 +241,24 @@ BoundStatement Relation::Bind(Binder &binder) {
241241
}
242242

243243
shared_ptr<Relation> Relation::InsertRel(const string &schema_name, const string &table_name) {
244-
return make_shared_ptr<InsertRelation>(shared_from_this(), schema_name, table_name);
244+
return InsertRel(INVALID_CATALOG, schema_name, table_name);
245+
}
246+
247+
shared_ptr<Relation> Relation::InsertRel(const string &catalog_name, const string &schema_name,
248+
const string &table_name) {
249+
return make_shared_ptr<InsertRelation>(shared_from_this(), catalog_name, schema_name, table_name);
245250
}
246251

247252
void Relation::Insert(const string &table_name) {
248253
Insert(INVALID_SCHEMA, table_name);
249254
}
250255

251256
void Relation::Insert(const string &schema_name, const string &table_name) {
252-
auto insert = InsertRel(schema_name, table_name);
257+
Insert(INVALID_CATALOG, schema_name, table_name);
258+
}
259+
260+
void Relation::Insert(const string &catalog_name, const string &schema_name, const string &table_name) {
261+
auto insert = InsertRel(catalog_name, schema_name, table_name);
253262
auto res = insert->Execute();
254263
if (res->HasError()) {
255264
const string prepended_message = "Failed to insert into table '" + table_name + "': ";
@@ -258,30 +267,37 @@ void Relation::Insert(const string &schema_name, const string &table_name) {
258267
}
259268

260269
void Relation::Insert(const vector<vector<Value>> &values) {
261-
vector<string> column_names;
262-
auto rel = make_shared_ptr<ValueRelation>(context->GetContext(), values, std::move(column_names), "values");
263-
rel->Insert(GetAlias());
270+
throw InvalidInputException("INSERT with values can only be used on base tables!");
264271
}
265272

266273
void Relation::Insert(vector<vector<unique_ptr<ParsedExpression>>> &&expressions) {
267-
vector<string> column_names;
268-
auto rel = make_shared_ptr<ValueRelation>(context->GetContext(), std::move(expressions), std::move(column_names),
269-
"values");
270-
rel->Insert(GetAlias());
274+
(void)std::move(expressions);
275+
throw InvalidInputException("INSERT with expressions can only be used on base tables!");
271276
}
272277

273278
shared_ptr<Relation> Relation::CreateRel(const string &schema_name, const string &table_name, bool temporary,
274279
OnCreateConflict on_conflict) {
275-
return make_shared_ptr<CreateTableRelation>(shared_from_this(), schema_name, table_name, temporary, on_conflict);
280+
return CreateRel(INVALID_CATALOG, schema_name, table_name, temporary, on_conflict);
281+
}
282+
283+
shared_ptr<Relation> Relation::CreateRel(const string &catalog_name, const string &schema_name,
284+
const string &table_name, bool temporary, OnCreateConflict on_conflict) {
285+
return make_shared_ptr<CreateTableRelation>(shared_from_this(), catalog_name, schema_name, table_name, temporary,
286+
on_conflict);
276287
}
277288

278289
void Relation::Create(const string &table_name, bool temporary, OnCreateConflict on_conflict) {
279-
Create(INVALID_SCHEMA, table_name, temporary, on_conflict);
290+
Create(INVALID_CATALOG, INVALID_SCHEMA, table_name, temporary, on_conflict);
280291
}
281292

282293
void Relation::Create(const string &schema_name, const string &table_name, bool temporary,
283294
OnCreateConflict on_conflict) {
284-
auto create = CreateRel(schema_name, table_name, temporary, on_conflict);
295+
Create(INVALID_CATALOG, schema_name, table_name, temporary, on_conflict);
296+
}
297+
298+
void Relation::Create(const string &catalog_name, const string &schema_name, const string &table_name, bool temporary,
299+
OnCreateConflict on_conflict) {
300+
auto create = CreateRel(catalog_name, schema_name, table_name, temporary, on_conflict);
285301
auto res = create->Execute();
286302
if (res->HasError()) {
287303
const string prepended_message = "Failed to create table '" + table_name + "': ";

src/main/relation/create_table_relation.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,21 @@ CreateTableRelation::CreateTableRelation(shared_ptr<Relation> child_p, string sc
1414
TryBindRelation(columns);
1515
}
1616

17+
CreateTableRelation::CreateTableRelation(shared_ptr<Relation> child_p, string catalog_name, string schema_name,
18+
string table_name, bool temporary_p, OnCreateConflict on_conflict)
19+
: Relation(child_p->context, RelationType::CREATE_TABLE_RELATION), child(std::move(child_p)),
20+
catalog_name(std::move(catalog_name)), schema_name(std::move(schema_name)), table_name(std::move(table_name)),
21+
temporary(temporary_p), on_conflict(on_conflict) {
22+
TryBindRelation(columns);
23+
}
24+
1725
BoundStatement CreateTableRelation::Bind(Binder &binder) {
1826
auto select = make_uniq<SelectStatement>();
1927
select->node = child->GetQueryNode();
2028

2129
CreateStatement stmt;
2230
auto info = make_uniq<CreateTableInfo>();
31+
info->catalog = catalog_name;
2332
info->schema = schema_name;
2433
info->table = table_name;
2534
info->query = std::move(select);

src/main/relation/insert_relation.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ InsertRelation::InsertRelation(shared_ptr<Relation> child_p, string schema_name,
1313
TryBindRelation(columns);
1414
}
1515

16+
InsertRelation::InsertRelation(shared_ptr<Relation> child_p, string catalog_name, string schema_name, string table_name)
17+
: Relation(child_p->context, RelationType::INSERT_RELATION), child(std::move(child_p)),
18+
catalog_name(std::move(catalog_name)), schema_name(std::move(schema_name)), table_name(std::move(table_name)) {
19+
TryBindRelation(columns);
20+
}
21+
1622
BoundStatement InsertRelation::Bind(Binder &binder) {
1723
InsertStatement stmt;
1824
auto select = make_uniq<SelectStatement>();
1925
select->node = child->GetQueryNode();
2026

27+
stmt.catalog = catalog_name;
2128
stmt.schema = schema_name;
2229
stmt.table = table_name;
2330
stmt.select_statement = std::move(select);

src/main/relation/table_relation.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "duckdb/parser/query_node/select_node.hpp"
44
#include "duckdb/parser/expression/star_expression.hpp"
55
#include "duckdb/main/relation/delete_relation.hpp"
6+
#include "duckdb/main/relation/value_relation.hpp"
67
#include "duckdb/main/relation/update_relation.hpp"
78
#include "duckdb/parser/parser.hpp"
89
#include "duckdb/main/client_context.hpp"
@@ -87,4 +88,17 @@ void TableRelation::Delete(const string &condition) {
8788
del->Execute();
8889
}
8990

91+
void TableRelation::Insert(const vector<vector<Value>> &values) {
92+
vector<string> column_names;
93+
auto rel = make_shared_ptr<ValueRelation>(context->GetContext(), values, std::move(column_names), "values");
94+
rel->Insert(description->database, description->schema, description->table);
95+
}
96+
97+
void TableRelation::Insert(vector<vector<unique_ptr<ParsedExpression>>> &&expressions) {
98+
vector<string> column_names;
99+
auto rel = make_shared_ptr<ValueRelation>(context->GetContext(), std::move(expressions), std::move(column_names),
100+
"values");
101+
rel->Insert(description->database, description->schema, description->table);
102+
}
103+
90104
} // namespace duckdb

test/api/test_relation_api.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,16 @@ TEST_CASE("Test table creations using the relation API", "[relation_api]") {
506506
result = con.Query("SELECT * FROM new_values ORDER BY k");
507507
REQUIRE(CHECK_COLUMN(result, 0, {4, 5}));
508508
REQUIRE(CHECK_COLUMN(result, 1, {"hello", "hello"}));
509+
510+
// create a table in an attached db and insert values
511+
auto test_dir = TestDirectoryPath();
512+
string db_path = test_dir + "/my_db.db";
513+
REQUIRE_NO_FAIL(con.Query("ATTACH '" + db_path + "' AS my_db;"));
514+
REQUIRE_NOTHROW(values = con.Values({{1, 10}, {2, 5}, {3, 4}}, {"i", "j"}));
515+
REQUIRE_NOTHROW(values->Create(std::string("my_db"), std::string(), std::string("integers")));
516+
result = con.Query("SELECT * FROM my_db.integers ORDER BY i");
517+
REQUIRE(CHECK_COLUMN(result, 0, {1, 2, 3}));
518+
REQUIRE(CHECK_COLUMN(result, 1, {10, 5, 4}));
509519
}
510520

511521
TEST_CASE("Test table creations with on_create_conflict using the relation API", "[relation_api]") {

0 commit comments

Comments
 (0)