duckdb 0.3.3-dev957.0 → 0.3.3-dev995.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/duckdb.cpp +856 -85
- package/src/duckdb.hpp +91 -11
- package/src/parquet-amalgamation.cpp +35803 -35801
package/src/duckdb.cpp
CHANGED
|
@@ -1300,12 +1300,116 @@ public:
|
|
|
1300
1300
|
|
|
1301
1301
|
|
|
1302
1302
|
|
|
1303
|
+
//===----------------------------------------------------------------------===//
|
|
1304
|
+
// DuckDB
|
|
1305
|
+
//
|
|
1306
|
+
// duckdb/planner/constraints/bound_foreign_key_constraint.hpp
|
|
1307
|
+
//
|
|
1308
|
+
//
|
|
1309
|
+
//===----------------------------------------------------------------------===//
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
namespace duckdb {
|
|
1317
|
+
|
|
1318
|
+
class BoundForeignKeyConstraint : public BoundConstraint {
|
|
1319
|
+
public:
|
|
1320
|
+
BoundForeignKeyConstraint(ForeignKeyInfo info_p, unordered_set<idx_t> pk_key_set_p,
|
|
1321
|
+
unordered_set<idx_t> fk_key_set_p)
|
|
1322
|
+
: BoundConstraint(ConstraintType::FOREIGN_KEY), info(move(info_p)), pk_key_set(move(pk_key_set_p)),
|
|
1323
|
+
fk_key_set(move(fk_key_set_p)) {
|
|
1324
|
+
#ifdef DEBUG
|
|
1325
|
+
D_ASSERT(info.pk_keys.size() == pk_key_set.size());
|
|
1326
|
+
for (auto &key : info.pk_keys) {
|
|
1327
|
+
D_ASSERT(pk_key_set.find(key) != pk_key_set.end());
|
|
1328
|
+
}
|
|
1329
|
+
D_ASSERT(info.fk_keys.size() == fk_key_set.size());
|
|
1330
|
+
for (auto &key : info.fk_keys) {
|
|
1331
|
+
D_ASSERT(fk_key_set.find(key) != fk_key_set.end());
|
|
1332
|
+
}
|
|
1333
|
+
#endif
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
ForeignKeyInfo info;
|
|
1337
|
+
//! The same keys but stored as an unordered set
|
|
1338
|
+
unordered_set<idx_t> pk_key_set;
|
|
1339
|
+
//! The same keys but stored as an unordered set
|
|
1340
|
+
unordered_set<idx_t> fk_key_set;
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
} // namespace duckdb
|
|
1344
|
+
|
|
1345
|
+
//===----------------------------------------------------------------------===//
|
|
1346
|
+
// DuckDB
|
|
1347
|
+
//
|
|
1348
|
+
// duckdb/parser/constraints/foreign_key_constraint.hpp
|
|
1349
|
+
//
|
|
1350
|
+
//
|
|
1351
|
+
//===----------------------------------------------------------------------===//
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
namespace duckdb {
|
|
1359
|
+
|
|
1360
|
+
class ForeignKeyConstraint : public Constraint {
|
|
1361
|
+
public:
|
|
1362
|
+
DUCKDB_API ForeignKeyConstraint(vector<string> pk_columns, vector<string> fk_columns, ForeignKeyInfo info);
|
|
1363
|
+
|
|
1364
|
+
//! The set of main key table's columns
|
|
1365
|
+
vector<string> pk_columns;
|
|
1366
|
+
//! The set of foreign key table's columns
|
|
1367
|
+
vector<string> fk_columns;
|
|
1368
|
+
ForeignKeyInfo info;
|
|
1369
|
+
|
|
1370
|
+
public:
|
|
1371
|
+
DUCKDB_API string ToString() const override;
|
|
1372
|
+
|
|
1373
|
+
DUCKDB_API unique_ptr<Constraint> Copy() const override;
|
|
1374
|
+
|
|
1375
|
+
//! Serialize to a stand-alone binary blob
|
|
1376
|
+
DUCKDB_API void Serialize(FieldWriter &writer) const override;
|
|
1377
|
+
//! Deserializes a ParsedConstraint
|
|
1378
|
+
DUCKDB_API static unique_ptr<Constraint> Deserialize(FieldReader &source);
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
} // namespace duckdb
|
|
1382
|
+
|
|
1303
1383
|
|
|
1304
1384
|
#include <algorithm>
|
|
1305
1385
|
#include <sstream>
|
|
1306
1386
|
|
|
1307
1387
|
namespace duckdb {
|
|
1308
1388
|
|
|
1389
|
+
void FindForeignKeyInformation(CatalogEntry *entry, AlterForeignKeyType alter_fk_type,
|
|
1390
|
+
vector<unique_ptr<AlterForeignKeyInfo>> &fk_arrays) {
|
|
1391
|
+
if (entry->type != CatalogType::TABLE_ENTRY) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
auto *table_entry = (TableCatalogEntry *)entry;
|
|
1395
|
+
for (idx_t i = 0; i < table_entry->constraints.size(); i++) {
|
|
1396
|
+
auto &cond = table_entry->constraints[i];
|
|
1397
|
+
if (cond->type != ConstraintType::FOREIGN_KEY) {
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
1401
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
1402
|
+
fk_arrays.push_back(make_unique<AlterForeignKeyInfo>(fk.info.schema, fk.info.table, entry->name,
|
|
1403
|
+
fk.pk_columns, fk.fk_columns, fk.info.pk_keys,
|
|
1404
|
+
fk.info.fk_keys, alter_fk_type));
|
|
1405
|
+
} else if (fk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE &&
|
|
1406
|
+
alter_fk_type == AlterForeignKeyType::AFT_DELETE) {
|
|
1407
|
+
throw CatalogException("Could not drop the table because this table is main key table of the table \"%s\"",
|
|
1408
|
+
fk.info.table);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1309
1413
|
SchemaCatalogEntry::SchemaCatalogEntry(Catalog *catalog, string name_p, bool internal)
|
|
1310
1414
|
: CatalogEntry(CatalogType::SCHEMA_ENTRY, catalog, move(name_p)),
|
|
1311
1415
|
tables(*catalog, make_unique<DefaultViewGenerator>(*catalog, this)), indexes(*catalog), table_functions(*catalog),
|
|
@@ -1371,7 +1475,23 @@ CatalogEntry *SchemaCatalogEntry::CreateType(ClientContext &context, CreateTypeI
|
|
|
1371
1475
|
CatalogEntry *SchemaCatalogEntry::CreateTable(ClientContext &context, BoundCreateTableInfo *info) {
|
|
1372
1476
|
auto table = make_unique<TableCatalogEntry>(catalog, this, info);
|
|
1373
1477
|
table->storage->info->cardinality = table->storage->GetTotalRows();
|
|
1374
|
-
|
|
1478
|
+
CatalogEntry *entry = AddEntry(context, move(table), info->Base().on_conflict, info->dependencies);
|
|
1479
|
+
if (!entry) {
|
|
1480
|
+
return nullptr;
|
|
1481
|
+
}
|
|
1482
|
+
// add a foreign key constraint in main key table if there is a foreign key constraint
|
|
1483
|
+
vector<unique_ptr<AlterForeignKeyInfo>> fk_arrays;
|
|
1484
|
+
FindForeignKeyInformation(entry, AlterForeignKeyType::AFT_ADD, fk_arrays);
|
|
1485
|
+
for (idx_t i = 0; i < fk_arrays.size(); i++) {
|
|
1486
|
+
// alter primary key table
|
|
1487
|
+
AlterForeignKeyInfo *fk_info = fk_arrays[i].get();
|
|
1488
|
+
catalog->Alter(context, fk_info);
|
|
1489
|
+
|
|
1490
|
+
// make a dependency between this table and referenced table
|
|
1491
|
+
auto &set = GetCatalogSet(CatalogType::TABLE_ENTRY);
|
|
1492
|
+
info->dependencies.insert(set.GetEntry(context, fk_info->name));
|
|
1493
|
+
}
|
|
1494
|
+
return entry;
|
|
1375
1495
|
}
|
|
1376
1496
|
|
|
1377
1497
|
CatalogEntry *SchemaCatalogEntry::CreateView(ClientContext &context, CreateViewInfo *info) {
|
|
@@ -1475,9 +1595,20 @@ void SchemaCatalogEntry::DropEntry(ClientContext &context, DropInfo *info) {
|
|
|
1475
1595
|
throw CatalogException("Existing object %s is of type %s, trying to replace with type %s", info->name,
|
|
1476
1596
|
CatalogTypeToString(existing_entry->type), CatalogTypeToString(info->type));
|
|
1477
1597
|
}
|
|
1598
|
+
|
|
1599
|
+
// if there is a foreign key constraint, get that information
|
|
1600
|
+
vector<unique_ptr<AlterForeignKeyInfo>> fk_arrays;
|
|
1601
|
+
FindForeignKeyInformation(existing_entry, AlterForeignKeyType::AFT_DELETE, fk_arrays);
|
|
1602
|
+
|
|
1478
1603
|
if (!set.DropEntry(context, info->name, info->cascade)) {
|
|
1479
1604
|
throw InternalException("Could not drop element because of an internal error");
|
|
1480
1605
|
}
|
|
1606
|
+
|
|
1607
|
+
// remove the foreign key constraint in main key table if main key table's name is valid
|
|
1608
|
+
for (idx_t i = 0; i < fk_arrays.size(); i++) {
|
|
1609
|
+
// alter primary key tablee
|
|
1610
|
+
Catalog::GetCatalog(context).Alter(context, fk_arrays[i].get());
|
|
1611
|
+
}
|
|
1481
1612
|
}
|
|
1482
1613
|
|
|
1483
1614
|
void SchemaCatalogEntry::Alter(ClientContext &context, AlterInfo *info) {
|
|
@@ -1706,6 +1837,7 @@ public:
|
|
|
1706
1837
|
|
|
1707
1838
|
|
|
1708
1839
|
|
|
1840
|
+
|
|
1709
1841
|
//===----------------------------------------------------------------------===//
|
|
1710
1842
|
// DuckDB
|
|
1711
1843
|
//
|
|
@@ -1802,6 +1934,7 @@ public:
|
|
|
1802
1934
|
|
|
1803
1935
|
|
|
1804
1936
|
|
|
1937
|
+
|
|
1805
1938
|
//===----------------------------------------------------------------------===//
|
|
1806
1939
|
// DuckDB
|
|
1807
1940
|
//
|
|
@@ -2497,10 +2630,16 @@ struct ARTIndexScanState : public IndexScanState {
|
|
|
2497
2630
|
idx_t result_index = 0;
|
|
2498
2631
|
};
|
|
2499
2632
|
|
|
2633
|
+
enum VerifyExistenceType : uint8_t {
|
|
2634
|
+
APPEND = 0, // for purpose to append into table
|
|
2635
|
+
APPEND_FK = 1, // for purpose to append into table has foreign key
|
|
2636
|
+
DELETE_FK = 2 // for purpose to delete from table related to foreign key
|
|
2637
|
+
};
|
|
2638
|
+
|
|
2500
2639
|
class ART : public Index {
|
|
2501
2640
|
public:
|
|
2502
2641
|
ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions,
|
|
2503
|
-
|
|
2642
|
+
IndexConstraintType constraint_type);
|
|
2504
2643
|
~ART() override;
|
|
2505
2644
|
|
|
2506
2645
|
//! Root of the tree
|
|
@@ -2527,6 +2666,10 @@ public:
|
|
|
2527
2666
|
bool Append(IndexLock &lock, DataChunk &entries, Vector &row_identifiers) override;
|
|
2528
2667
|
//! Verify that data can be appended to the index
|
|
2529
2668
|
void VerifyAppend(DataChunk &chunk) override;
|
|
2669
|
+
//! Verify that data can be appended to the index for foreign key constraint
|
|
2670
|
+
void VerifyAppendForeignKey(DataChunk &chunk, string *err_msg_ptr) override;
|
|
2671
|
+
//! Verify that data can be delete from the index for foreign key constraint
|
|
2672
|
+
void VerifyDeleteForeignKey(DataChunk &chunk, string *err_msg_ptr) override;
|
|
2530
2673
|
//! Delete entries in the index
|
|
2531
2674
|
void Delete(IndexLock &lock, DataChunk &entries, Vector &row_identifiers) override;
|
|
2532
2675
|
//! Insert data into the index.
|
|
@@ -2571,6 +2714,8 @@ private:
|
|
|
2571
2714
|
vector<row_t> &result_ids);
|
|
2572
2715
|
|
|
2573
2716
|
void GenerateKeys(DataChunk &input, vector<unique_ptr<Key>> &keys);
|
|
2717
|
+
|
|
2718
|
+
void VerifyExistence(DataChunk &chunk, VerifyExistenceType verify_type, string *err_msg_ptr = NULL);
|
|
2574
2719
|
};
|
|
2575
2720
|
|
|
2576
2721
|
} // namespace duckdb
|
|
@@ -2672,6 +2817,27 @@ idx_t TableCatalogEntry::GetColumnIndex(string &column_name, bool if_exists) {
|
|
|
2672
2817
|
return idx_t(entry->second);
|
|
2673
2818
|
}
|
|
2674
2819
|
|
|
2820
|
+
void AddDataTableIndex(DataTable *storage, vector<ColumnDefinition> &columns, vector<idx_t> &keys,
|
|
2821
|
+
IndexConstraintType constraint_type) {
|
|
2822
|
+
// fetch types and create expressions for the index from the columns
|
|
2823
|
+
vector<column_t> column_ids;
|
|
2824
|
+
vector<unique_ptr<Expression>> unbound_expressions;
|
|
2825
|
+
vector<unique_ptr<Expression>> bound_expressions;
|
|
2826
|
+
idx_t key_nr = 0;
|
|
2827
|
+
for (auto &key : keys) {
|
|
2828
|
+
D_ASSERT(key < columns.size());
|
|
2829
|
+
|
|
2830
|
+
unbound_expressions.push_back(make_unique<BoundColumnRefExpression>(columns[key].name, columns[key].type,
|
|
2831
|
+
ColumnBinding(0, column_ids.size())));
|
|
2832
|
+
|
|
2833
|
+
bound_expressions.push_back(make_unique<BoundReferenceExpression>(columns[key].type, key_nr++));
|
|
2834
|
+
column_ids.push_back(key);
|
|
2835
|
+
}
|
|
2836
|
+
// create an adaptive radix tree around the expressions
|
|
2837
|
+
auto art = make_unique<ART>(column_ids, move(unbound_expressions), constraint_type);
|
|
2838
|
+
storage->AddIndex(move(art), bound_expressions);
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2675
2841
|
TableCatalogEntry::TableCatalogEntry(Catalog *catalog, SchemaCatalogEntry *schema, BoundCreateTableInfo *info,
|
|
2676
2842
|
std::shared_ptr<DataTable> inherited_storage)
|
|
2677
2843
|
: StandardEntry(CatalogType::TABLE_ENTRY, schema, catalog, info->Base().table), storage(move(inherited_storage)),
|
|
@@ -2695,29 +2861,24 @@ TableCatalogEntry::TableCatalogEntry(Catalog *catalog, SchemaCatalogEntry *schem
|
|
|
2695
2861
|
}
|
|
2696
2862
|
storage = make_shared<DataTable>(catalog->db, schema->name, name, move(colum_def_copy), move(info->data));
|
|
2697
2863
|
|
|
2698
|
-
// create the unique indexes for the UNIQUE and PRIMARY KEY constraints
|
|
2864
|
+
// create the unique indexes for the UNIQUE and PRIMARY KEY and FOREIGN KEY constraints
|
|
2699
2865
|
for (idx_t i = 0; i < bound_constraints.size(); i++) {
|
|
2700
2866
|
auto &constraint = bound_constraints[i];
|
|
2701
2867
|
if (constraint->type == ConstraintType::UNIQUE) {
|
|
2702
2868
|
// unique constraint: create a unique index
|
|
2703
2869
|
auto &unique = (BoundUniqueConstraint &)*constraint;
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
bound_expressions.push_back(make_unique<BoundReferenceExpression>(columns[key].type, key_nr++));
|
|
2716
|
-
column_ids.push_back(key);
|
|
2870
|
+
IndexConstraintType constraint_type = IndexConstraintType::UNIQUE;
|
|
2871
|
+
if (unique.is_primary_key) {
|
|
2872
|
+
constraint_type = IndexConstraintType::PRIMARY;
|
|
2873
|
+
}
|
|
2874
|
+
AddDataTableIndex(storage.get(), columns, unique.keys, constraint_type);
|
|
2875
|
+
} else if (constraint->type == ConstraintType::FOREIGN_KEY) {
|
|
2876
|
+
// foreign key constraint: create a foreign key index
|
|
2877
|
+
auto &bfk = (BoundForeignKeyConstraint &)*constraint;
|
|
2878
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE ||
|
|
2879
|
+
bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
2880
|
+
AddDataTableIndex(storage.get(), columns, bfk.info.fk_keys, IndexConstraintType::FOREIGN);
|
|
2717
2881
|
}
|
|
2718
|
-
// create an adaptive radix tree around the expressions
|
|
2719
|
-
auto art = make_unique<ART>(column_ids, move(unbound_expressions), true, unique.is_primary_key);
|
|
2720
|
-
storage->AddIndex(move(art), bound_expressions);
|
|
2721
2882
|
}
|
|
2722
2883
|
}
|
|
2723
2884
|
}
|
|
@@ -2760,6 +2921,10 @@ unique_ptr<CatalogEntry> TableCatalogEntry::AlterEntry(ClientContext &context, A
|
|
|
2760
2921
|
auto change_type_info = (ChangeColumnTypeInfo *)table_info;
|
|
2761
2922
|
return ChangeColumnType(context, *change_type_info);
|
|
2762
2923
|
}
|
|
2924
|
+
case AlterTableType::FOREIGN_KEY_CONSTRAINT: {
|
|
2925
|
+
auto foreign_key_constraint_info = (AlterForeignKeyInfo *)table_info;
|
|
2926
|
+
return SetForeignKeyConstraint(context, *foreign_key_constraint_info);
|
|
2927
|
+
}
|
|
2763
2928
|
default:
|
|
2764
2929
|
throw InternalException("Unrecognized alter table type!");
|
|
2765
2930
|
}
|
|
@@ -2810,6 +2975,26 @@ unique_ptr<CatalogEntry> TableCatalogEntry::RenameColumn(ClientContext &context,
|
|
|
2810
2975
|
}
|
|
2811
2976
|
break;
|
|
2812
2977
|
}
|
|
2978
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
2979
|
+
// FOREIGN KEY constraint: possibly need to rename columns
|
|
2980
|
+
auto &fk = (ForeignKeyConstraint &)*copy;
|
|
2981
|
+
vector<string> columns = fk.pk_columns;
|
|
2982
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
2983
|
+
columns = fk.fk_columns;
|
|
2984
|
+
} else if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
2985
|
+
for (idx_t i = 0; i < fk.fk_columns.size(); i++) {
|
|
2986
|
+
columns.push_back(fk.fk_columns[i]);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
for (idx_t i = 0; i < columns.size(); i++) {
|
|
2990
|
+
if (columns[i] == info.old_name) {
|
|
2991
|
+
throw CatalogException(
|
|
2992
|
+
"Cannot rename column \"%s\" because this is involved in the foreign key constraint",
|
|
2993
|
+
info.old_name);
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
break;
|
|
2997
|
+
}
|
|
2813
2998
|
default:
|
|
2814
2999
|
throw InternalException("Unsupported constraint for entry!");
|
|
2815
3000
|
}
|
|
@@ -2906,6 +3091,27 @@ unique_ptr<CatalogEntry> TableCatalogEntry::RemoveColumn(ClientContext &context,
|
|
|
2906
3091
|
create_info->constraints.push_back(move(copy));
|
|
2907
3092
|
break;
|
|
2908
3093
|
}
|
|
3094
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
3095
|
+
auto copy = constraint->Copy();
|
|
3096
|
+
auto &fk = (ForeignKeyConstraint &)*copy;
|
|
3097
|
+
vector<string> columns = fk.pk_columns;
|
|
3098
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
3099
|
+
columns = fk.fk_columns;
|
|
3100
|
+
} else if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
3101
|
+
for (idx_t i = 0; i < fk.fk_columns.size(); i++) {
|
|
3102
|
+
columns.push_back(fk.fk_columns[i]);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
for (idx_t i = 0; i < columns.size(); i++) {
|
|
3106
|
+
if (columns[i] == info.removed_column) {
|
|
3107
|
+
throw CatalogException(
|
|
3108
|
+
"Cannot drop column \"%s\" because there is a FOREIGN KEY constraint that depends on it",
|
|
3109
|
+
info.removed_column);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
create_info->constraints.push_back(move(copy));
|
|
3113
|
+
break;
|
|
3114
|
+
}
|
|
2909
3115
|
default:
|
|
2910
3116
|
throw InternalException("Unsupported constraint for entry!");
|
|
2911
3117
|
}
|
|
@@ -2981,6 +3187,21 @@ unique_ptr<CatalogEntry> TableCatalogEntry::ChangeColumnType(ClientContext &cont
|
|
|
2981
3187
|
}
|
|
2982
3188
|
break;
|
|
2983
3189
|
}
|
|
3190
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
3191
|
+
auto &bfk = (BoundForeignKeyConstraint &)*bound_constraints[i];
|
|
3192
|
+
unordered_set<idx_t> key_set = bfk.pk_key_set;
|
|
3193
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
3194
|
+
key_set = bfk.fk_key_set;
|
|
3195
|
+
} else if (bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
3196
|
+
for (idx_t i = 0; i < bfk.info.fk_keys.size(); i++) {
|
|
3197
|
+
key_set.insert(bfk.info.fk_keys[i]);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
if (key_set.find(change_idx) != key_set.end()) {
|
|
3201
|
+
throw BinderException("Cannot change the type of a column that has a FOREIGN KEY constraint specified");
|
|
3202
|
+
}
|
|
3203
|
+
break;
|
|
3204
|
+
}
|
|
2984
3205
|
default:
|
|
2985
3206
|
throw InternalException("Unsupported constraint for entry!");
|
|
2986
3207
|
}
|
|
@@ -3004,6 +3225,39 @@ unique_ptr<CatalogEntry> TableCatalogEntry::ChangeColumnType(ClientContext &cont
|
|
|
3004
3225
|
new_storage);
|
|
3005
3226
|
}
|
|
3006
3227
|
|
|
3228
|
+
unique_ptr<CatalogEntry> TableCatalogEntry::SetForeignKeyConstraint(ClientContext &context, AlterForeignKeyInfo &info) {
|
|
3229
|
+
auto create_info = make_unique<CreateTableInfo>(schema->name, name);
|
|
3230
|
+
|
|
3231
|
+
for (idx_t i = 0; i < columns.size(); i++) {
|
|
3232
|
+
create_info->columns.push_back(columns[i].Copy());
|
|
3233
|
+
}
|
|
3234
|
+
for (idx_t i = 0; i < constraints.size(); i++) {
|
|
3235
|
+
auto constraint = constraints[i]->Copy();
|
|
3236
|
+
if (constraint->type == ConstraintType::FOREIGN_KEY) {
|
|
3237
|
+
ForeignKeyConstraint &fk = (ForeignKeyConstraint &)*constraint;
|
|
3238
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE && fk.info.table == info.fk_table) {
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
create_info->constraints.push_back(move(constraint));
|
|
3243
|
+
}
|
|
3244
|
+
if (info.type == AlterForeignKeyType::AFT_ADD) {
|
|
3245
|
+
ForeignKeyInfo fk_info;
|
|
3246
|
+
fk_info.type = ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE;
|
|
3247
|
+
fk_info.schema = info.schema;
|
|
3248
|
+
fk_info.table = info.fk_table;
|
|
3249
|
+
fk_info.pk_keys = info.pk_keys;
|
|
3250
|
+
fk_info.fk_keys = info.fk_keys;
|
|
3251
|
+
create_info->constraints.push_back(
|
|
3252
|
+
make_unique<ForeignKeyConstraint>(info.pk_columns, info.fk_columns, move(fk_info)));
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
auto binder = Binder::CreateBinder(context);
|
|
3256
|
+
auto bound_create_info = binder->BindCreateTableInfo(move(create_info));
|
|
3257
|
+
|
|
3258
|
+
return make_unique<TableCatalogEntry>(catalog, schema, (BoundCreateTableInfo *)bound_create_info.get(), storage);
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3007
3261
|
ColumnDefinition &TableCatalogEntry::GetColumn(const string &name) {
|
|
3008
3262
|
auto entry = name_map.find(name);
|
|
3009
3263
|
if (entry == name_map.end() || entry->second == COLUMN_IDENTIFIER_ROW_ID) {
|
|
@@ -3085,6 +3339,12 @@ string TableCatalogEntry::ToSQL() {
|
|
|
3085
3339
|
}
|
|
3086
3340
|
extra_constraints.push_back(constraint->ToString());
|
|
3087
3341
|
}
|
|
3342
|
+
} else if (constraint->type == ConstraintType::FOREIGN_KEY) {
|
|
3343
|
+
auto &fk = (ForeignKeyConstraint &)*constraint;
|
|
3344
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE ||
|
|
3345
|
+
fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
3346
|
+
extra_constraints.push_back(constraint->ToString());
|
|
3347
|
+
}
|
|
3088
3348
|
} else {
|
|
3089
3349
|
extra_constraints.push_back(constraint->ToString());
|
|
3090
3350
|
}
|
|
@@ -13410,7 +13670,7 @@ struct LengthFun {
|
|
|
13410
13670
|
|
|
13411
13671
|
struct LikeFun {
|
|
13412
13672
|
static void RegisterFunction(BuiltinFunctions &set);
|
|
13413
|
-
static bool Glob(const char *s, idx_t slen, const char *pattern, idx_t plen);
|
|
13673
|
+
DUCKDB_API static bool Glob(const char *s, idx_t slen, const char *pattern, idx_t plen);
|
|
13414
13674
|
};
|
|
13415
13675
|
|
|
13416
13676
|
struct LikeEscapeFun {
|
|
@@ -13734,10 +13994,14 @@ void FileSystem::FileSync(FileHandle &handle) {
|
|
|
13734
13994
|
throw NotImplementedException("%s: FileSync is not implemented!", GetName());
|
|
13735
13995
|
}
|
|
13736
13996
|
|
|
13737
|
-
vector<string> FileSystem::Glob(const string &path) {
|
|
13997
|
+
vector<string> FileSystem::Glob(const string &path, FileOpener *opener) {
|
|
13738
13998
|
throw NotImplementedException("%s: Glob is not implemented!", GetName());
|
|
13739
13999
|
}
|
|
13740
14000
|
|
|
14001
|
+
vector<string> FileSystem::Glob(const string &path, ClientContext &context) {
|
|
14002
|
+
return Glob(path, GetFileOpener(context));
|
|
14003
|
+
}
|
|
14004
|
+
|
|
13741
14005
|
void FileSystem::RegisterSubSystem(unique_ptr<FileSystem> sub_fs) {
|
|
13742
14006
|
throw NotImplementedException("%s: Can't register a sub system on a non-virtual file system", GetName());
|
|
13743
14007
|
}
|
|
@@ -15850,7 +16114,7 @@ public:
|
|
|
15850
16114
|
void FileSync(FileHandle &handle) override;
|
|
15851
16115
|
|
|
15852
16116
|
//! Runs a glob on the file system, returning a list of matching files
|
|
15853
|
-
vector<string> Glob(const string &path) override;
|
|
16117
|
+
vector<string> Glob(const string &path, FileOpener *opener = nullptr) override;
|
|
15854
16118
|
|
|
15855
16119
|
bool CanHandleFile(const string &fpath) override {
|
|
15856
16120
|
//! Whether or not a sub-system can handle a specific file path
|
|
@@ -16774,7 +17038,7 @@ static void GlobFiles(FileSystem &fs, const string &path, const string &glob, bo
|
|
|
16774
17038
|
});
|
|
16775
17039
|
}
|
|
16776
17040
|
|
|
16777
|
-
vector<string> LocalFileSystem::Glob(const string &path) {
|
|
17041
|
+
vector<string> LocalFileSystem::Glob(const string &path, FileOpener *opener) {
|
|
16778
17042
|
if (path.empty()) {
|
|
16779
17043
|
return vector<string>();
|
|
16780
17044
|
}
|
|
@@ -45790,8 +46054,8 @@ public:
|
|
|
45790
46054
|
FindFileSystem(filename)->RemoveFile(filename);
|
|
45791
46055
|
}
|
|
45792
46056
|
|
|
45793
|
-
vector<string> Glob(const string &path) override {
|
|
45794
|
-
return FindFileSystem(path)->Glob(path);
|
|
46057
|
+
virtual vector<string> Glob(const string &path, FileOpener *opener = nullptr) override {
|
|
46058
|
+
return FindFileSystem(path)->Glob(path, opener);
|
|
45795
46059
|
}
|
|
45796
46060
|
|
|
45797
46061
|
void RegisterSubSystem(unique_ptr<FileSystem> fs) override {
|
|
@@ -48493,9 +48757,9 @@ ExpressionExecutorState::ExpressionExecutorState(const string &name) : profiler(
|
|
|
48493
48757
|
|
|
48494
48758
|
namespace duckdb {
|
|
48495
48759
|
|
|
48496
|
-
ART::ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions,
|
|
48497
|
-
|
|
48498
|
-
: Index(IndexType::ART, column_ids, unbound_expressions,
|
|
48760
|
+
ART::ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions,
|
|
48761
|
+
IndexConstraintType constraint_type)
|
|
48762
|
+
: Index(IndexType::ART, column_ids, unbound_expressions, constraint_type) {
|
|
48499
48763
|
tree = nullptr;
|
|
48500
48764
|
expression_result.Initialize(logical_types);
|
|
48501
48765
|
is_little_endian = IsLittleEndian();
|
|
@@ -48749,43 +49013,19 @@ bool ART::Append(IndexLock &lock, DataChunk &appended_data, Vector &row_identifi
|
|
|
48749
49013
|
}
|
|
48750
49014
|
|
|
48751
49015
|
void ART::VerifyAppend(DataChunk &chunk) {
|
|
48752
|
-
|
|
48753
|
-
|
|
48754
|
-
}
|
|
48755
|
-
|
|
48756
|
-
DataChunk expression_result;
|
|
48757
|
-
expression_result.Initialize(logical_types);
|
|
48758
|
-
|
|
48759
|
-
// unique index, check
|
|
48760
|
-
lock_guard<mutex> l(lock);
|
|
48761
|
-
// first resolve the expressions for the index
|
|
48762
|
-
ExecuteExpressions(chunk, expression_result);
|
|
49016
|
+
VerifyExistence(chunk, VerifyExistenceType::APPEND);
|
|
49017
|
+
}
|
|
48763
49018
|
|
|
48764
|
-
|
|
48765
|
-
|
|
48766
|
-
|
|
49019
|
+
void ART::VerifyAppendForeignKey(DataChunk &chunk, string *err_msg_ptr) {
|
|
49020
|
+
VerifyExistence(chunk, VerifyExistenceType::APPEND_FK, err_msg_ptr);
|
|
49021
|
+
}
|
|
48767
49022
|
|
|
48768
|
-
|
|
48769
|
-
|
|
48770
|
-
continue;
|
|
48771
|
-
}
|
|
48772
|
-
if (Lookup(tree, *keys[i], 0) != nullptr) {
|
|
48773
|
-
string key_name;
|
|
48774
|
-
for (idx_t k = 0; k < expression_result.ColumnCount(); k++) {
|
|
48775
|
-
if (k > 0) {
|
|
48776
|
-
key_name += ", ";
|
|
48777
|
-
}
|
|
48778
|
-
key_name += unbound_expressions[k]->GetName() + ": " + expression_result.data[k].GetValue(i).ToString();
|
|
48779
|
-
}
|
|
48780
|
-
// node already exists in tree
|
|
48781
|
-
throw ConstraintException("duplicate key \"%s\" violates %s constraint", key_name,
|
|
48782
|
-
is_primary ? "primary key" : "unique");
|
|
48783
|
-
}
|
|
48784
|
-
}
|
|
49023
|
+
void ART::VerifyDeleteForeignKey(DataChunk &chunk, string *err_msg_ptr) {
|
|
49024
|
+
VerifyExistence(chunk, VerifyExistenceType::DELETE_FK, err_msg_ptr);
|
|
48785
49025
|
}
|
|
48786
49026
|
|
|
48787
49027
|
bool ART::InsertToLeaf(Leaf &leaf, row_t row_id) {
|
|
48788
|
-
if (
|
|
49028
|
+
if (IsUnique() && leaf.num_elements != 0) {
|
|
48789
49029
|
return false;
|
|
48790
49030
|
}
|
|
48791
49031
|
leaf.Insert(row_id);
|
|
@@ -49360,6 +49600,70 @@ bool ART::Scan(Transaction &transaction, DataTable &table, IndexScanState &table
|
|
|
49360
49600
|
return true;
|
|
49361
49601
|
}
|
|
49362
49602
|
|
|
49603
|
+
void ART::VerifyExistence(DataChunk &chunk, VerifyExistenceType verify_type, string *err_msg_ptr) {
|
|
49604
|
+
if (verify_type != VerifyExistenceType::DELETE_FK && !IsUnique()) {
|
|
49605
|
+
return;
|
|
49606
|
+
}
|
|
49607
|
+
|
|
49608
|
+
DataChunk expression_result;
|
|
49609
|
+
expression_result.Initialize(logical_types);
|
|
49610
|
+
|
|
49611
|
+
// unique index, check
|
|
49612
|
+
lock_guard<mutex> l(lock);
|
|
49613
|
+
// first resolve the expressions for the index
|
|
49614
|
+
ExecuteExpressions(chunk, expression_result);
|
|
49615
|
+
|
|
49616
|
+
// generate the keys for the given input
|
|
49617
|
+
vector<unique_ptr<Key>> keys;
|
|
49618
|
+
GenerateKeys(expression_result, keys);
|
|
49619
|
+
|
|
49620
|
+
for (idx_t i = 0; i < chunk.size(); i++) {
|
|
49621
|
+
if (!keys[i]) {
|
|
49622
|
+
continue;
|
|
49623
|
+
}
|
|
49624
|
+
Node *node_ptr = Lookup(tree, *keys[i], 0);
|
|
49625
|
+
bool throw_exception =
|
|
49626
|
+
verify_type == VerifyExistenceType::APPEND_FK ? node_ptr == nullptr : node_ptr != nullptr;
|
|
49627
|
+
if (!throw_exception) {
|
|
49628
|
+
continue;
|
|
49629
|
+
}
|
|
49630
|
+
string key_name;
|
|
49631
|
+
for (idx_t k = 0; k < expression_result.ColumnCount(); k++) {
|
|
49632
|
+
if (k > 0) {
|
|
49633
|
+
key_name += ", ";
|
|
49634
|
+
}
|
|
49635
|
+
key_name += unbound_expressions[k]->GetName() + ": " + expression_result.data[k].GetValue(i).ToString();
|
|
49636
|
+
}
|
|
49637
|
+
string exception_msg;
|
|
49638
|
+
switch (verify_type) {
|
|
49639
|
+
case VerifyExistenceType::APPEND: {
|
|
49640
|
+
// node already exists in tree
|
|
49641
|
+
string type = IsPrimary() ? "primary key" : "unique";
|
|
49642
|
+
exception_msg = "duplicate key \"" + key_name + "\" violates ";
|
|
49643
|
+
exception_msg += type + " constraint";
|
|
49644
|
+
break;
|
|
49645
|
+
}
|
|
49646
|
+
case VerifyExistenceType::APPEND_FK: {
|
|
49647
|
+
// found node no exists in tree
|
|
49648
|
+
exception_msg =
|
|
49649
|
+
"violates foreign key constraint because key \"" + key_name + "\" no exist in referenced table";
|
|
49650
|
+
break;
|
|
49651
|
+
}
|
|
49652
|
+
case VerifyExistenceType::DELETE_FK: {
|
|
49653
|
+
// found node exists in tree
|
|
49654
|
+
exception_msg =
|
|
49655
|
+
"violates foreign key constraint because key \"" + key_name + "\" exist in table has foreign key";
|
|
49656
|
+
break;
|
|
49657
|
+
}
|
|
49658
|
+
}
|
|
49659
|
+
if (err_msg_ptr) {
|
|
49660
|
+
err_msg_ptr[i] = exception_msg;
|
|
49661
|
+
} else {
|
|
49662
|
+
throw ConstraintException(exception_msg);
|
|
49663
|
+
}
|
|
49664
|
+
}
|
|
49665
|
+
}
|
|
49666
|
+
|
|
49363
49667
|
} // namespace duckdb
|
|
49364
49668
|
|
|
49365
49669
|
|
|
@@ -62180,19 +62484,16 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62180
62484
|
vector<CatalogEntry *> views;
|
|
62181
62485
|
vector<CatalogEntry *> indexes;
|
|
62182
62486
|
|
|
62183
|
-
Catalog::GetCatalog(ccontext).schemas->
|
|
62184
|
-
|
|
62487
|
+
auto schema_list = Catalog::GetCatalog(ccontext).schemas->GetEntries<SchemaCatalogEntry>(context.client);
|
|
62488
|
+
for (auto &schema : schema_list) {
|
|
62185
62489
|
if (!schema->internal) {
|
|
62186
|
-
// export schema
|
|
62187
62490
|
schemas.push_back(schema);
|
|
62188
62491
|
}
|
|
62189
62492
|
schema->Scan(context.client, CatalogType::TABLE_ENTRY, [&](CatalogEntry *entry) {
|
|
62190
62493
|
if (entry->internal) {
|
|
62191
62494
|
return;
|
|
62192
62495
|
}
|
|
62193
|
-
if (entry->type
|
|
62194
|
-
tables.push_back(entry);
|
|
62195
|
-
} else {
|
|
62496
|
+
if (entry->type != CatalogType::TABLE_ENTRY) {
|
|
62196
62497
|
views.push_back(entry);
|
|
62197
62498
|
}
|
|
62198
62499
|
});
|
|
@@ -62201,7 +62502,12 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62201
62502
|
schema->Scan(context.client, CatalogType::TYPE_ENTRY,
|
|
62202
62503
|
[&](CatalogEntry *entry) { custom_types.push_back(entry); });
|
|
62203
62504
|
schema->Scan(context.client, CatalogType::INDEX_ENTRY, [&](CatalogEntry *entry) { indexes.push_back(entry); });
|
|
62204
|
-
}
|
|
62505
|
+
}
|
|
62506
|
+
|
|
62507
|
+
// consider the order of tables because of foreign key constraint
|
|
62508
|
+
for (idx_t i = 0; i < exported_tables.data.size(); i++) {
|
|
62509
|
+
tables.push_back((CatalogEntry *)exported_tables.data[i].entry);
|
|
62510
|
+
}
|
|
62205
62511
|
|
|
62206
62512
|
// write the schema.sql file
|
|
62207
62513
|
// export order is SCHEMA -> SEQUENCE -> TABLE -> VIEW -> INDEX
|
|
@@ -62219,9 +62525,9 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62219
62525
|
// write the load.sql file
|
|
62220
62526
|
// for every table, we write COPY INTO statement with the specified options
|
|
62221
62527
|
stringstream load_ss;
|
|
62222
|
-
for (
|
|
62223
|
-
auto table =
|
|
62224
|
-
auto exported_table_info =
|
|
62528
|
+
for (idx_t i = 0; i < exported_tables.data.size(); i++) {
|
|
62529
|
+
auto &table = exported_tables.data[i].entry;
|
|
62530
|
+
auto exported_table_info = exported_tables.data[i].table_data;
|
|
62225
62531
|
WriteCopyStatement(fs, load_ss, table, *info, exported_table_info, function);
|
|
62226
62532
|
}
|
|
62227
62533
|
WriteStringStreamToFile(fs, opener, load_ss, fs.JoinPath(info->file_path, "load.sql"));
|
|
@@ -63749,7 +64055,8 @@ void PhysicalCreateIndex::GetData(ExecutionContext &context, DataChunk &chunk, G
|
|
|
63749
64055
|
unique_ptr<Index> index;
|
|
63750
64056
|
switch (info->index_type) {
|
|
63751
64057
|
case IndexType::ART: {
|
|
63752
|
-
index = make_unique<ART>(column_ids, unbound_expressions,
|
|
64058
|
+
index = make_unique<ART>(column_ids, unbound_expressions,
|
|
64059
|
+
info->unique ? IndexConstraintType::UNIQUE : IndexConstraintType::NONE);
|
|
63753
64060
|
break;
|
|
63754
64061
|
}
|
|
63755
64062
|
default:
|
|
@@ -95778,7 +96085,7 @@ static unique_ptr<FunctionData> ReadCSVBind(ClientContext &context, CopyInfo &in
|
|
|
95778
96085
|
string file_pattern = info.file_path;
|
|
95779
96086
|
|
|
95780
96087
|
auto &fs = FileSystem::GetFileSystem(context);
|
|
95781
|
-
bind_data->files = fs.Glob(file_pattern);
|
|
96088
|
+
bind_data->files = fs.Glob(file_pattern, context);
|
|
95782
96089
|
if (bind_data->files.empty()) {
|
|
95783
96090
|
throw IOException("No files found that match the pattern \"%s\"", file_pattern);
|
|
95784
96091
|
}
|
|
@@ -96159,7 +96466,7 @@ static unique_ptr<FunctionData> GlobFunctionBind(ClientContext &context, vector<
|
|
|
96159
96466
|
}
|
|
96160
96467
|
auto result = make_unique<GlobFunctionBindData>();
|
|
96161
96468
|
auto &fs = FileSystem::GetFileSystem(context);
|
|
96162
|
-
result->files = fs.Glob(StringValue::Get(inputs[0]));
|
|
96469
|
+
result->files = fs.Glob(StringValue::Get(inputs[0]), context);
|
|
96163
96470
|
return_types.emplace_back(LogicalType::VARCHAR);
|
|
96164
96471
|
names.emplace_back("file");
|
|
96165
96472
|
return move(result);
|
|
@@ -96882,7 +97189,7 @@ static unique_ptr<FunctionData> ReadCSVBind(ClientContext &context, vector<Value
|
|
|
96882
97189
|
auto &file_pattern = StringValue::Get(inputs[0]);
|
|
96883
97190
|
|
|
96884
97191
|
auto &fs = FileSystem::GetFileSystem(context);
|
|
96885
|
-
result->files = fs.Glob(file_pattern);
|
|
97192
|
+
result->files = fs.Glob(file_pattern, context);
|
|
96886
97193
|
if (result->files.empty()) {
|
|
96887
97194
|
throw IOException("No files found that match the pattern \"%s\"", file_pattern);
|
|
96888
97195
|
}
|
|
@@ -97600,6 +97907,7 @@ void DuckDBColumnsFun::RegisterFunction(BuiltinFunctions &set) {
|
|
|
97600
97907
|
|
|
97601
97908
|
|
|
97602
97909
|
|
|
97910
|
+
|
|
97603
97911
|
namespace duckdb {
|
|
97604
97912
|
|
|
97605
97913
|
struct DuckDBConstraintsData : public FunctionOperatorData {
|
|
@@ -97758,7 +98066,13 @@ void DuckDBConstraintsFunction(ClientContext &context, const FunctionData *bind_
|
|
|
97758
98066
|
column_index_list.push_back(bound_not_null.index);
|
|
97759
98067
|
break;
|
|
97760
98068
|
}
|
|
97761
|
-
case ConstraintType::FOREIGN_KEY:
|
|
98069
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
98070
|
+
auto &bound_foreign_key = (BoundForeignKeyConstraint &)bound_constraint;
|
|
98071
|
+
for (auto &col_idx : bound_foreign_key.info.fk_keys) {
|
|
98072
|
+
column_index_list.push_back(column_t(col_idx));
|
|
98073
|
+
}
|
|
98074
|
+
break;
|
|
98075
|
+
}
|
|
97762
98076
|
default:
|
|
97763
98077
|
throw NotImplementedException("Unimplemented constraint for duckdb_constraints");
|
|
97764
98078
|
}
|
|
@@ -98435,9 +98749,9 @@ void DuckDBIndexesFunction(ClientContext &context, const FunctionData *bind_data
|
|
|
98435
98749
|
auto table_entry = catalog.GetEntry(context, CatalogType::TABLE_ENTRY, index.info->schema, index.info->table);
|
|
98436
98750
|
output.SetValue(5, count, Value::BIGINT(table_entry->oid));
|
|
98437
98751
|
// is_unique, BOOLEAN
|
|
98438
|
-
output.SetValue(6, count, Value::BOOLEAN(index.index->
|
|
98752
|
+
output.SetValue(6, count, Value::BOOLEAN(index.index->IsUnique()));
|
|
98439
98753
|
// is_primary, BOOLEAN
|
|
98440
|
-
output.SetValue(7, count, Value::BOOLEAN(index.index->
|
|
98754
|
+
output.SetValue(7, count, Value::BOOLEAN(index.index->IsPrimary()));
|
|
98441
98755
|
// expressions, VARCHAR
|
|
98442
98756
|
output.SetValue(8, count, Value());
|
|
98443
98757
|
// sql, VARCHAR
|
|
@@ -133299,6 +133613,9 @@ unique_ptr<Constraint> Constraint::Deserialize(Deserializer &source) {
|
|
|
133299
133613
|
case ConstraintType::UNIQUE:
|
|
133300
133614
|
result = UniqueConstraint::Deserialize(reader);
|
|
133301
133615
|
break;
|
|
133616
|
+
case ConstraintType::FOREIGN_KEY:
|
|
133617
|
+
result = ForeignKeyConstraint::Deserialize(reader);
|
|
133618
|
+
break;
|
|
133302
133619
|
default:
|
|
133303
133620
|
throw InternalException("Unrecognized constraint type for serialization");
|
|
133304
133621
|
}
|
|
@@ -133343,6 +133660,82 @@ unique_ptr<Constraint> CheckConstraint::Deserialize(FieldReader &source) {
|
|
|
133343
133660
|
|
|
133344
133661
|
|
|
133345
133662
|
|
|
133663
|
+
|
|
133664
|
+
|
|
133665
|
+
namespace duckdb {
|
|
133666
|
+
|
|
133667
|
+
ForeignKeyConstraint::ForeignKeyConstraint(vector<string> pk_columns, vector<string> fk_columns, ForeignKeyInfo info)
|
|
133668
|
+
: Constraint(ConstraintType::FOREIGN_KEY), pk_columns(move(pk_columns)), fk_columns(move(fk_columns)),
|
|
133669
|
+
info(move(info)) {
|
|
133670
|
+
}
|
|
133671
|
+
|
|
133672
|
+
string ForeignKeyConstraint::ToString() const {
|
|
133673
|
+
if (info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
133674
|
+
string base = "FOREIGN KEY (";
|
|
133675
|
+
|
|
133676
|
+
for (idx_t i = 0; i < fk_columns.size(); i++) {
|
|
133677
|
+
if (i > 0) {
|
|
133678
|
+
base += ", ";
|
|
133679
|
+
}
|
|
133680
|
+
base += KeywordHelper::WriteOptionallyQuoted(fk_columns[i]);
|
|
133681
|
+
}
|
|
133682
|
+
base += ") REFERENCES ";
|
|
133683
|
+
if (!info.schema.empty()) {
|
|
133684
|
+
base += info.schema;
|
|
133685
|
+
base += ".";
|
|
133686
|
+
}
|
|
133687
|
+
base += info.table;
|
|
133688
|
+
base += "(";
|
|
133689
|
+
|
|
133690
|
+
for (idx_t i = 0; i < pk_columns.size(); i++) {
|
|
133691
|
+
if (i > 0) {
|
|
133692
|
+
base += ", ";
|
|
133693
|
+
}
|
|
133694
|
+
base += KeywordHelper::WriteOptionallyQuoted(pk_columns[i]);
|
|
133695
|
+
}
|
|
133696
|
+
base += ")";
|
|
133697
|
+
|
|
133698
|
+
return base;
|
|
133699
|
+
}
|
|
133700
|
+
|
|
133701
|
+
return "";
|
|
133702
|
+
}
|
|
133703
|
+
|
|
133704
|
+
unique_ptr<Constraint> ForeignKeyConstraint::Copy() const {
|
|
133705
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, info);
|
|
133706
|
+
}
|
|
133707
|
+
|
|
133708
|
+
void ForeignKeyConstraint::Serialize(FieldWriter &writer) const {
|
|
133709
|
+
D_ASSERT(pk_columns.size() <= NumericLimits<uint32_t>::Maximum());
|
|
133710
|
+
writer.WriteList<string>(pk_columns);
|
|
133711
|
+
D_ASSERT(fk_columns.size() <= NumericLimits<uint32_t>::Maximum());
|
|
133712
|
+
writer.WriteList<string>(fk_columns);
|
|
133713
|
+
writer.WriteField<ForeignKeyType>(info.type);
|
|
133714
|
+
writer.WriteString(info.schema);
|
|
133715
|
+
writer.WriteString(info.table);
|
|
133716
|
+
writer.WriteList<idx_t>(info.pk_keys);
|
|
133717
|
+
writer.WriteList<idx_t>(info.fk_keys);
|
|
133718
|
+
}
|
|
133719
|
+
|
|
133720
|
+
unique_ptr<Constraint> ForeignKeyConstraint::Deserialize(FieldReader &source) {
|
|
133721
|
+
ForeignKeyInfo read_info;
|
|
133722
|
+
auto pk_columns = source.ReadRequiredList<string>();
|
|
133723
|
+
auto fk_columns = source.ReadRequiredList<string>();
|
|
133724
|
+
read_info.type = source.ReadRequired<ForeignKeyType>();
|
|
133725
|
+
read_info.schema = source.ReadRequired<string>();
|
|
133726
|
+
read_info.table = source.ReadRequired<string>();
|
|
133727
|
+
read_info.pk_keys = source.ReadRequiredList<idx_t>();
|
|
133728
|
+
read_info.fk_keys = source.ReadRequiredList<idx_t>();
|
|
133729
|
+
|
|
133730
|
+
// column list parsed constraint
|
|
133731
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(read_info));
|
|
133732
|
+
}
|
|
133733
|
+
|
|
133734
|
+
} // namespace duckdb
|
|
133735
|
+
|
|
133736
|
+
|
|
133737
|
+
|
|
133738
|
+
|
|
133346
133739
|
namespace duckdb {
|
|
133347
133740
|
|
|
133348
133741
|
NotNullConstraint::NotNullConstraint(column_t index) : Constraint(ConstraintType::NOT_NULL), index(index) {
|
|
@@ -134755,6 +135148,8 @@ unique_ptr<AlterInfo> AlterTableInfo::Deserialize(FieldReader &reader) {
|
|
|
134755
135148
|
return ChangeColumnTypeInfo::Deserialize(reader, schema, table);
|
|
134756
135149
|
case AlterTableType::SET_DEFAULT:
|
|
134757
135150
|
return SetDefaultInfo::Deserialize(reader, schema, table);
|
|
135151
|
+
case AlterTableType::FOREIGN_KEY_CONSTRAINT:
|
|
135152
|
+
return AlterForeignKeyInfo::Deserialize(reader, schema, table);
|
|
134758
135153
|
default:
|
|
134759
135154
|
throw SerializationException("Unknown alter table type for deserialization!");
|
|
134760
135155
|
}
|
|
@@ -134911,6 +135306,44 @@ unique_ptr<AlterInfo> SetDefaultInfo::Deserialize(FieldReader &reader, string sc
|
|
|
134911
135306
|
return make_unique<SetDefaultInfo>(move(schema), move(table), move(column_name), move(new_default));
|
|
134912
135307
|
}
|
|
134913
135308
|
|
|
135309
|
+
//===--------------------------------------------------------------------===//
|
|
135310
|
+
// AlterForeignKeyInfo
|
|
135311
|
+
//===--------------------------------------------------------------------===//
|
|
135312
|
+
AlterForeignKeyInfo::AlterForeignKeyInfo(string schema_p, string table_p, string fk_table, vector<string> pk_columns,
|
|
135313
|
+
vector<string> fk_columns, vector<idx_t> pk_keys, vector<idx_t> fk_keys,
|
|
135314
|
+
AlterForeignKeyType type_p)
|
|
135315
|
+
: AlterTableInfo(AlterTableType::FOREIGN_KEY_CONSTRAINT, move(schema_p), move(table_p)), fk_table(move(fk_table)),
|
|
135316
|
+
pk_columns(move(pk_columns)), fk_columns(move(fk_columns)), pk_keys(move(pk_keys)), fk_keys(move(fk_keys)),
|
|
135317
|
+
type(type_p) {
|
|
135318
|
+
}
|
|
135319
|
+
AlterForeignKeyInfo::~AlterForeignKeyInfo() {
|
|
135320
|
+
}
|
|
135321
|
+
|
|
135322
|
+
unique_ptr<AlterInfo> AlterForeignKeyInfo::Copy() const {
|
|
135323
|
+
return make_unique_base<AlterInfo, AlterForeignKeyInfo>(schema, name, fk_table, pk_columns, fk_columns, pk_keys,
|
|
135324
|
+
fk_keys, type);
|
|
135325
|
+
}
|
|
135326
|
+
|
|
135327
|
+
void AlterForeignKeyInfo::SerializeAlterTable(FieldWriter &writer) const {
|
|
135328
|
+
writer.WriteString(fk_table);
|
|
135329
|
+
writer.WriteList<string>(pk_columns);
|
|
135330
|
+
writer.WriteList<string>(fk_columns);
|
|
135331
|
+
writer.WriteList<idx_t>(pk_keys);
|
|
135332
|
+
writer.WriteList<idx_t>(fk_keys);
|
|
135333
|
+
writer.WriteField<AlterForeignKeyType>(type);
|
|
135334
|
+
}
|
|
135335
|
+
|
|
135336
|
+
unique_ptr<AlterInfo> AlterForeignKeyInfo::Deserialize(FieldReader &reader, string schema, string table) {
|
|
135337
|
+
auto fk_table = reader.ReadRequired<string>();
|
|
135338
|
+
auto pk_columns = reader.ReadRequiredList<string>();
|
|
135339
|
+
auto fk_columns = reader.ReadRequiredList<string>();
|
|
135340
|
+
auto pk_keys = reader.ReadRequiredList<idx_t>();
|
|
135341
|
+
auto fk_keys = reader.ReadRequiredList<idx_t>();
|
|
135342
|
+
auto type = reader.ReadRequired<AlterForeignKeyType>();
|
|
135343
|
+
return make_unique<AlterForeignKeyInfo>(move(schema), move(table), move(fk_table), move(pk_columns),
|
|
135344
|
+
move(fk_columns), move(pk_keys), move(fk_keys), type);
|
|
135345
|
+
}
|
|
135346
|
+
|
|
134914
135347
|
//===--------------------------------------------------------------------===//
|
|
134915
135348
|
// Alter View
|
|
134916
135349
|
//===--------------------------------------------------------------------===//
|
|
@@ -142779,6 +143212,30 @@ unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGLis
|
|
|
142779
143212
|
}
|
|
142780
143213
|
return make_unique<CheckConstraint>(TransformExpression(constraint->raw_expr));
|
|
142781
143214
|
}
|
|
143215
|
+
case duckdb_libpgquery::PG_CONSTR_FOREIGN: {
|
|
143216
|
+
ForeignKeyInfo fk_info;
|
|
143217
|
+
fk_info.type = ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE;
|
|
143218
|
+
if (constraint->pktable->schemaname) {
|
|
143219
|
+
fk_info.schema = constraint->pktable->schemaname;
|
|
143220
|
+
} else {
|
|
143221
|
+
fk_info.schema = "";
|
|
143222
|
+
}
|
|
143223
|
+
fk_info.table = constraint->pktable->relname;
|
|
143224
|
+
vector<string> pk_columns, fk_columns;
|
|
143225
|
+
for (auto kc = constraint->fk_attrs->head; kc; kc = kc->next) {
|
|
143226
|
+
fk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
|
|
143227
|
+
}
|
|
143228
|
+
for (auto kc = constraint->pk_attrs->head; kc; kc = kc->next) {
|
|
143229
|
+
pk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
|
|
143230
|
+
}
|
|
143231
|
+
if (pk_columns.size() != fk_columns.size()) {
|
|
143232
|
+
throw ParserException("The number of referencing and referenced columns for foreign keys must be the same");
|
|
143233
|
+
}
|
|
143234
|
+
if (fk_columns.empty()) {
|
|
143235
|
+
throw ParserException("The set of referencing and referenced columns for foreign keys must be not empty");
|
|
143236
|
+
}
|
|
143237
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(fk_info));
|
|
143238
|
+
}
|
|
142782
143239
|
default:
|
|
142783
143240
|
throw NotImplementedException("Constraint type not handled yet!");
|
|
142784
143241
|
}
|
|
@@ -151630,6 +152087,7 @@ public:
|
|
|
151630
152087
|
} // namespace duckdb
|
|
151631
152088
|
|
|
151632
152089
|
|
|
152090
|
+
|
|
151633
152091
|
namespace duckdb {
|
|
151634
152092
|
|
|
151635
152093
|
SchemaCatalogEntry *Binder::BindSchema(CreateInfo &info) {
|
|
@@ -151816,6 +152274,34 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
|
|
|
151816
152274
|
break;
|
|
151817
152275
|
}
|
|
151818
152276
|
case CatalogType::TABLE_ENTRY: {
|
|
152277
|
+
// If there is a foreign key constraint, resolve primary key column's index from primary key column's name
|
|
152278
|
+
auto &create_info = (CreateTableInfo &)*stmt.info;
|
|
152279
|
+
auto &catalog = Catalog::GetCatalog(context);
|
|
152280
|
+
for (idx_t i = 0; i < create_info.constraints.size(); i++) {
|
|
152281
|
+
auto &cond = create_info.constraints[i];
|
|
152282
|
+
if (cond->type != ConstraintType::FOREIGN_KEY) {
|
|
152283
|
+
continue;
|
|
152284
|
+
}
|
|
152285
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152286
|
+
if (fk.info.type != ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
152287
|
+
continue;
|
|
152288
|
+
}
|
|
152289
|
+
D_ASSERT(fk.info.pk_keys.empty() && !fk.pk_columns.empty());
|
|
152290
|
+
if (create_info.table == fk.info.table) {
|
|
152291
|
+
fk.info.type = ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE;
|
|
152292
|
+
} else {
|
|
152293
|
+
// have to resolve referenced table
|
|
152294
|
+
auto pk_table_entry_ptr = catalog.GetEntry<TableCatalogEntry>(context, fk.info.schema, fk.info.table);
|
|
152295
|
+
D_ASSERT(!fk.pk_columns.empty() && fk.info.pk_keys.empty());
|
|
152296
|
+
for (auto &keyname : fk.pk_columns) {
|
|
152297
|
+
auto entry = pk_table_entry_ptr->name_map.find(keyname);
|
|
152298
|
+
if (entry == pk_table_entry_ptr->name_map.end()) {
|
|
152299
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152300
|
+
}
|
|
152301
|
+
fk.info.pk_keys.push_back(entry->second);
|
|
152302
|
+
}
|
|
152303
|
+
}
|
|
152304
|
+
}
|
|
151819
152305
|
// We first check if there are any user types, if yes we check to which custom types they refer.
|
|
151820
152306
|
auto bound_info = BindCreateTableInfo(move(stmt.info));
|
|
151821
152307
|
auto root = move(bound_info->query);
|
|
@@ -151850,6 +152336,7 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
|
|
|
151850
152336
|
|
|
151851
152337
|
|
|
151852
152338
|
|
|
152339
|
+
|
|
151853
152340
|
//===----------------------------------------------------------------------===//
|
|
151854
152341
|
// DuckDB
|
|
151855
152342
|
//
|
|
@@ -151986,6 +152473,39 @@ static void BindConstraints(Binder &binder, BoundCreateTableInfo &info) {
|
|
|
151986
152473
|
make_unique<BoundUniqueConstraint>(move(keys), move(key_set), unique.is_primary_key));
|
|
151987
152474
|
break;
|
|
151988
152475
|
}
|
|
152476
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
152477
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152478
|
+
D_ASSERT((fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE && !fk.info.pk_keys.empty()) ||
|
|
152479
|
+
(fk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE && !fk.info.pk_keys.empty()) ||
|
|
152480
|
+
fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE);
|
|
152481
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE && fk.info.pk_keys.empty()) {
|
|
152482
|
+
for (auto &keyname : fk.pk_columns) {
|
|
152483
|
+
auto entry = info.name_map.find(keyname);
|
|
152484
|
+
if (entry == info.name_map.end()) {
|
|
152485
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152486
|
+
}
|
|
152487
|
+
fk.info.pk_keys.push_back(entry->second);
|
|
152488
|
+
}
|
|
152489
|
+
}
|
|
152490
|
+
if (fk.info.fk_keys.empty()) {
|
|
152491
|
+
for (auto &keyname : fk.fk_columns) {
|
|
152492
|
+
auto entry = info.name_map.find(keyname);
|
|
152493
|
+
if (entry == info.name_map.end()) {
|
|
152494
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152495
|
+
}
|
|
152496
|
+
fk.info.fk_keys.push_back(entry->second);
|
|
152497
|
+
}
|
|
152498
|
+
}
|
|
152499
|
+
unordered_set<idx_t> fk_key_set, pk_key_set;
|
|
152500
|
+
for (idx_t i = 0; i < fk.info.pk_keys.size(); i++) {
|
|
152501
|
+
pk_key_set.insert(fk.info.pk_keys[i]);
|
|
152502
|
+
}
|
|
152503
|
+
for (idx_t i = 0; i < fk.info.fk_keys.size(); i++) {
|
|
152504
|
+
fk_key_set.insert(fk.info.fk_keys[i]);
|
|
152505
|
+
}
|
|
152506
|
+
info.bound_constraints.push_back(make_unique<BoundForeignKeyConstraint>(fk.info, pk_key_set, fk_key_set));
|
|
152507
|
+
break;
|
|
152508
|
+
}
|
|
151989
152509
|
default:
|
|
151990
152510
|
throw NotImplementedException("unrecognized constraint type in bind");
|
|
151991
152511
|
}
|
|
@@ -152234,6 +152754,7 @@ BoundStatement Binder::Bind(ExplainStatement &stmt) {
|
|
|
152234
152754
|
|
|
152235
152755
|
|
|
152236
152756
|
|
|
152757
|
+
|
|
152237
152758
|
#include <algorithm>
|
|
152238
152759
|
|
|
152239
152760
|
namespace duckdb {
|
|
@@ -152262,6 +152783,51 @@ string SanitizeExportIdentifier(const string &str) {
|
|
|
152262
152783
|
return result;
|
|
152263
152784
|
}
|
|
152264
152785
|
|
|
152786
|
+
bool IsExistMainKeyTable(string &table_name, vector<TableCatalogEntry *> &unordered) {
|
|
152787
|
+
for (idx_t i = 0; i < unordered.size(); i++) {
|
|
152788
|
+
if (unordered[i]->name == table_name) {
|
|
152789
|
+
return true;
|
|
152790
|
+
}
|
|
152791
|
+
}
|
|
152792
|
+
return false;
|
|
152793
|
+
}
|
|
152794
|
+
|
|
152795
|
+
void ScanForeignKeyTable(vector<TableCatalogEntry *> &ordered, vector<TableCatalogEntry *> &unordered,
|
|
152796
|
+
bool move_only_pk_table) {
|
|
152797
|
+
for (auto i = unordered.begin(); i != unordered.end();) {
|
|
152798
|
+
auto table_entry = *i;
|
|
152799
|
+
bool move_to_ordered = true;
|
|
152800
|
+
for (idx_t j = 0; j < table_entry->constraints.size(); j++) {
|
|
152801
|
+
auto &cond = table_entry->constraints[j];
|
|
152802
|
+
if (cond->type == ConstraintType::FOREIGN_KEY) {
|
|
152803
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152804
|
+
if ((move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) ||
|
|
152805
|
+
(!move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE &&
|
|
152806
|
+
IsExistMainKeyTable(fk.info.table, unordered))) {
|
|
152807
|
+
move_to_ordered = false;
|
|
152808
|
+
break;
|
|
152809
|
+
}
|
|
152810
|
+
}
|
|
152811
|
+
}
|
|
152812
|
+
if (move_to_ordered) {
|
|
152813
|
+
ordered.push_back(table_entry);
|
|
152814
|
+
i = unordered.erase(i);
|
|
152815
|
+
} else {
|
|
152816
|
+
i++;
|
|
152817
|
+
}
|
|
152818
|
+
}
|
|
152819
|
+
}
|
|
152820
|
+
|
|
152821
|
+
void ReorderTableEntries(vector<TableCatalogEntry *> &tables) {
|
|
152822
|
+
vector<TableCatalogEntry *> ordered;
|
|
152823
|
+
vector<TableCatalogEntry *> unordered = tables;
|
|
152824
|
+
ScanForeignKeyTable(ordered, unordered, true);
|
|
152825
|
+
while (!unordered.empty()) {
|
|
152826
|
+
ScanForeignKeyTable(ordered, unordered, false);
|
|
152827
|
+
}
|
|
152828
|
+
tables = ordered;
|
|
152829
|
+
}
|
|
152830
|
+
|
|
152265
152831
|
BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
152266
152832
|
// COPY TO a file
|
|
152267
152833
|
auto &config = DBConfig::GetConfig(context);
|
|
@@ -152290,6 +152856,9 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
|
152290
152856
|
});
|
|
152291
152857
|
}
|
|
152292
152858
|
|
|
152859
|
+
// reorder tables because of foreign key constraint
|
|
152860
|
+
ReorderTableEntries(tables);
|
|
152861
|
+
|
|
152293
152862
|
// now generate the COPY statements for each of the tables
|
|
152294
152863
|
auto &fs = FileSystem::GetFileSystem(context);
|
|
152295
152864
|
unique_ptr<LogicalOperator> child_operator;
|
|
@@ -152324,7 +152893,10 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
|
152324
152893
|
exported_data.schema_name = info->schema;
|
|
152325
152894
|
exported_data.file_path = info->file_path;
|
|
152326
152895
|
|
|
152327
|
-
|
|
152896
|
+
ExportedTableInfo table_info;
|
|
152897
|
+
table_info.entry = table;
|
|
152898
|
+
table_info.table_data = exported_data;
|
|
152899
|
+
exported_tables.data.push_back(table_info);
|
|
152328
152900
|
id++;
|
|
152329
152901
|
|
|
152330
152902
|
// generate the copy statement and bind it
|
|
@@ -160277,6 +160849,8 @@ void WriteOverflowStringsToDisk::AllocateNewBlock(block_id_t new_block_id) {
|
|
|
160277
160849
|
|
|
160278
160850
|
namespace duckdb {
|
|
160279
160851
|
|
|
160852
|
+
void ReorderTableEntries(vector<TableCatalogEntry *> &tables);
|
|
160853
|
+
|
|
160280
160854
|
CheckpointManager::CheckpointManager(DatabaseInstance &db) : db(db) {
|
|
160281
160855
|
}
|
|
160282
160856
|
|
|
@@ -160421,6 +160995,8 @@ void CheckpointManager::WriteSchema(SchemaCatalogEntry &schema) {
|
|
|
160421
160995
|
for (auto &seq : sequences) {
|
|
160422
160996
|
WriteSequence(*seq);
|
|
160423
160997
|
}
|
|
160998
|
+
// reorder tables because of foreign key constraint
|
|
160999
|
+
ReorderTableEntries(tables);
|
|
160424
161000
|
// now write the tables
|
|
160425
161001
|
metadata_writer->Write<uint32_t>(tables.size());
|
|
160426
161002
|
for (auto &table : tables) {
|
|
@@ -165876,7 +166452,133 @@ static void VerifyCheckConstraint(TableCatalogEntry &table, Expression &expr, Da
|
|
|
165876
166452
|
}
|
|
165877
166453
|
}
|
|
165878
166454
|
|
|
165879
|
-
|
|
166455
|
+
static bool FindColumnIndex(const vector<idx_t> *keys_ptr, const vector<column_t> &index_column_ids) {
|
|
166456
|
+
for (idx_t i = 0; i < keys_ptr->size(); i++) {
|
|
166457
|
+
bool is_found = false;
|
|
166458
|
+
for (idx_t j = 0; j < index_column_ids.size(); j++) {
|
|
166459
|
+
if ((*keys_ptr)[i] == index_column_ids[j]) {
|
|
166460
|
+
is_found = true;
|
|
166461
|
+
break;
|
|
166462
|
+
}
|
|
166463
|
+
}
|
|
166464
|
+
if (!is_found) {
|
|
166465
|
+
return false;
|
|
166466
|
+
}
|
|
166467
|
+
}
|
|
166468
|
+
|
|
166469
|
+
return true;
|
|
166470
|
+
}
|
|
166471
|
+
|
|
166472
|
+
static void VerifyForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context, DataChunk &chunk,
|
|
166473
|
+
bool is_append) {
|
|
166474
|
+
const vector<idx_t> *src_keys_ptr = &bfk.info.fk_keys;
|
|
166475
|
+
const vector<idx_t> *dst_keys_ptr = &bfk.info.pk_keys;
|
|
166476
|
+
if (!is_append) {
|
|
166477
|
+
src_keys_ptr = &bfk.info.pk_keys;
|
|
166478
|
+
dst_keys_ptr = &bfk.info.fk_keys;
|
|
166479
|
+
}
|
|
166480
|
+
|
|
166481
|
+
auto table_entry_ptr =
|
|
166482
|
+
Catalog::GetCatalog(context).GetEntry<TableCatalogEntry>(context, bfk.info.schema, bfk.info.table);
|
|
166483
|
+
if (table_entry_ptr == nullptr) {
|
|
166484
|
+
throw InternalException("Can't find table \"%s\" in foreign key constraint", bfk.info.table);
|
|
166485
|
+
}
|
|
166486
|
+
|
|
166487
|
+
// make the data chunk to check
|
|
166488
|
+
vector<LogicalType> types;
|
|
166489
|
+
for (idx_t i = 0; i < table_entry_ptr->columns.size(); i++) {
|
|
166490
|
+
types.emplace_back(table_entry_ptr->columns[i].type);
|
|
166491
|
+
}
|
|
166492
|
+
DataChunk dst_chunk;
|
|
166493
|
+
dst_chunk.InitializeEmpty(types);
|
|
166494
|
+
for (idx_t i = 0; i < src_keys_ptr->size(); i++) {
|
|
166495
|
+
dst_chunk.data[(*dst_keys_ptr)[i]].Reference(chunk.data[(*src_keys_ptr)[i]]);
|
|
166496
|
+
}
|
|
166497
|
+
dst_chunk.SetCardinality(chunk.size());
|
|
166498
|
+
auto data_table = table_entry_ptr->storage.get();
|
|
166499
|
+
|
|
166500
|
+
idx_t count = dst_chunk.size();
|
|
166501
|
+
if (count <= 0) {
|
|
166502
|
+
return;
|
|
166503
|
+
}
|
|
166504
|
+
|
|
166505
|
+
// we need to look at the error messages concurrently in data table's index and transaction local storage's index
|
|
166506
|
+
vector<string> err_msgs, tran_err_msgs;
|
|
166507
|
+
err_msgs.resize(count);
|
|
166508
|
+
tran_err_msgs.resize(count);
|
|
166509
|
+
|
|
166510
|
+
// check whether or not the chunk can be inserted or deleted into the referenced table' storage
|
|
166511
|
+
TableIndexList &table_indices = data_table->info->indexes;
|
|
166512
|
+
table_indices.Scan([&](Index &index) {
|
|
166513
|
+
if (FindColumnIndex(dst_keys_ptr, index.column_ids)) {
|
|
166514
|
+
if (is_append) {
|
|
166515
|
+
index.VerifyAppendForeignKey(dst_chunk, err_msgs.data());
|
|
166516
|
+
} else {
|
|
166517
|
+
index.VerifyDeleteForeignKey(dst_chunk, err_msgs.data());
|
|
166518
|
+
}
|
|
166519
|
+
}
|
|
166520
|
+
return false;
|
|
166521
|
+
});
|
|
166522
|
+
|
|
166523
|
+
// check whether or not the chunk can be inserted or deleted into the referenced table' transaction local storage
|
|
166524
|
+
auto &transaction = Transaction::GetTransaction(context);
|
|
166525
|
+
bool transaction_check = transaction.storage.Find(data_table);
|
|
166526
|
+
if (transaction_check) {
|
|
166527
|
+
vector<unique_ptr<Index>> &transact_index_vec = transaction.storage.GetIndexes(data_table);
|
|
166528
|
+
for (idx_t i = 0; i < transact_index_vec.size(); i++) {
|
|
166529
|
+
if (FindColumnIndex(dst_keys_ptr, transact_index_vec[i]->column_ids)) {
|
|
166530
|
+
if (is_append) {
|
|
166531
|
+
transact_index_vec[i]->VerifyAppendForeignKey(dst_chunk, tran_err_msgs.data());
|
|
166532
|
+
} else {
|
|
166533
|
+
transact_index_vec[i]->VerifyDeleteForeignKey(dst_chunk, tran_err_msgs.data());
|
|
166534
|
+
}
|
|
166535
|
+
}
|
|
166536
|
+
}
|
|
166537
|
+
}
|
|
166538
|
+
|
|
166539
|
+
// we need to look at the error messages concurrently in data table's index and transaction local storage's index
|
|
166540
|
+
for (idx_t i = 0; i < count; i++) {
|
|
166541
|
+
if (!transaction_check) {
|
|
166542
|
+
// if there is no transaction-local data we only need to check if there is an error message in the main
|
|
166543
|
+
// index
|
|
166544
|
+
if (!err_msgs[i].empty()) {
|
|
166545
|
+
throw ConstraintException(err_msgs[i]);
|
|
166546
|
+
} else {
|
|
166547
|
+
continue;
|
|
166548
|
+
}
|
|
166549
|
+
}
|
|
166550
|
+
if (is_append) {
|
|
166551
|
+
// if we are appending we need to check to ensure the foreign key exists in either the transaction-local
|
|
166552
|
+
// storage or the main table
|
|
166553
|
+
if (!err_msgs[i].empty() && !tran_err_msgs[i].empty()) {
|
|
166554
|
+
throw ConstraintException(err_msgs[i]);
|
|
166555
|
+
} else {
|
|
166556
|
+
continue;
|
|
166557
|
+
}
|
|
166558
|
+
}
|
|
166559
|
+
// if we are deleting we need to ensure the foreign key DOES NOT exist in EITHER the transaction-local storage
|
|
166560
|
+
// OR the main table
|
|
166561
|
+
if (!err_msgs[i].empty() || !tran_err_msgs[i].empty()) {
|
|
166562
|
+
string &err_msg = err_msgs[i];
|
|
166563
|
+
if (err_msg.empty()) {
|
|
166564
|
+
err_msg = tran_err_msgs[i];
|
|
166565
|
+
}
|
|
166566
|
+
throw ConstraintException(err_msg);
|
|
166567
|
+
}
|
|
166568
|
+
}
|
|
166569
|
+
}
|
|
166570
|
+
|
|
166571
|
+
static void VerifyAppendForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
|
|
166572
|
+
DataChunk &chunk) {
|
|
166573
|
+
VerifyForeignKeyConstraint(bfk, context, chunk, true);
|
|
166574
|
+
}
|
|
166575
|
+
|
|
166576
|
+
static void VerifyDeleteForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
|
|
166577
|
+
DataChunk &chunk) {
|
|
166578
|
+
VerifyForeignKeyConstraint(bfk, context, chunk, false);
|
|
166579
|
+
}
|
|
166580
|
+
|
|
166581
|
+
void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
|
|
165880
166582
|
for (auto &constraint : table.bound_constraints) {
|
|
165881
166583
|
switch (constraint->type) {
|
|
165882
166584
|
case ConstraintType::NOT_NULL: {
|
|
@@ -165898,7 +166600,14 @@ void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, DataChunk &chu
|
|
|
165898
166600
|
});
|
|
165899
166601
|
break;
|
|
165900
166602
|
}
|
|
165901
|
-
case ConstraintType::FOREIGN_KEY:
|
|
166603
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
166604
|
+
auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
|
|
166605
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE ||
|
|
166606
|
+
bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
166607
|
+
VerifyAppendForeignKeyConstraint(bfk, context, chunk);
|
|
166608
|
+
}
|
|
166609
|
+
break;
|
|
166610
|
+
}
|
|
165902
166611
|
default:
|
|
165903
166612
|
throw NotImplementedException("Constraint type not implemented!");
|
|
165904
166613
|
}
|
|
@@ -165919,7 +166628,7 @@ void DataTable::Append(TableCatalogEntry &table, ClientContext &context, DataChu
|
|
|
165919
166628
|
chunk.Verify();
|
|
165920
166629
|
|
|
165921
166630
|
// verify any constraints on the new chunk
|
|
165922
|
-
VerifyAppendConstraints(table, chunk);
|
|
166631
|
+
VerifyAppendConstraints(table, context, chunk);
|
|
165923
166632
|
|
|
165924
166633
|
// append to the transaction local data
|
|
165925
166634
|
auto &transaction = Transaction::GetTransaction(context);
|
|
@@ -166211,6 +166920,27 @@ void DataTable::RemoveFromIndexes(Vector &row_identifiers, idx_t count) {
|
|
|
166211
166920
|
});
|
|
166212
166921
|
}
|
|
166213
166922
|
|
|
166923
|
+
void DataTable::VerifyDeleteConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
|
|
166924
|
+
for (auto &constraint : table.bound_constraints) {
|
|
166925
|
+
switch (constraint->type) {
|
|
166926
|
+
case ConstraintType::NOT_NULL:
|
|
166927
|
+
case ConstraintType::CHECK:
|
|
166928
|
+
case ConstraintType::UNIQUE:
|
|
166929
|
+
break;
|
|
166930
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
166931
|
+
auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
|
|
166932
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE ||
|
|
166933
|
+
bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
166934
|
+
VerifyDeleteForeignKeyConstraint(bfk, context, chunk);
|
|
166935
|
+
}
|
|
166936
|
+
break;
|
|
166937
|
+
}
|
|
166938
|
+
default:
|
|
166939
|
+
throw NotImplementedException("Constraint type not implemented!");
|
|
166940
|
+
}
|
|
166941
|
+
}
|
|
166942
|
+
}
|
|
166943
|
+
|
|
166214
166944
|
//===--------------------------------------------------------------------===//
|
|
166215
166945
|
// Delete
|
|
166216
166946
|
//===--------------------------------------------------------------------===//
|
|
@@ -166226,6 +166956,23 @@ idx_t DataTable::Delete(TableCatalogEntry &table, ClientContext &context, Vector
|
|
|
166226
166956
|
auto ids = FlatVector::GetData<row_t>(row_identifiers);
|
|
166227
166957
|
auto first_id = ids[0];
|
|
166228
166958
|
|
|
166959
|
+
// verify any constraints on the delete rows
|
|
166960
|
+
DataChunk verify_chunk;
|
|
166961
|
+
if (first_id >= MAX_ROW_ID) {
|
|
166962
|
+
transaction.storage.FetchChunk(this, row_identifiers, count, verify_chunk);
|
|
166963
|
+
} else {
|
|
166964
|
+
ColumnFetchState fetch_state;
|
|
166965
|
+
vector<column_t> col_ids;
|
|
166966
|
+
vector<LogicalType> types;
|
|
166967
|
+
for (idx_t i = 0; i < column_definitions.size(); i++) {
|
|
166968
|
+
col_ids.push_back(column_definitions[i].oid);
|
|
166969
|
+
types.emplace_back(column_definitions[i].type);
|
|
166970
|
+
}
|
|
166971
|
+
verify_chunk.Initialize(types);
|
|
166972
|
+
Fetch(transaction, verify_chunk, col_ids, row_identifiers, count, fetch_state);
|
|
166973
|
+
}
|
|
166974
|
+
VerifyDeleteConstraints(table, context, verify_chunk);
|
|
166975
|
+
|
|
166229
166976
|
if (first_id >= MAX_ROW_ID) {
|
|
166230
166977
|
// deletion is in transaction-local storage: push delete into local chunk collection
|
|
166231
166978
|
return transaction.storage.Delete(this, row_identifiers, count);
|
|
@@ -166598,8 +167345,8 @@ vector<vector<Value>> DataTable::GetStorageInfo() {
|
|
|
166598
167345
|
namespace duckdb {
|
|
166599
167346
|
|
|
166600
167347
|
Index::Index(IndexType type, const vector<column_t> &column_ids_p,
|
|
166601
|
-
const vector<unique_ptr<Expression>> &unbound_expressions,
|
|
166602
|
-
: type(type), column_ids(column_ids_p),
|
|
167348
|
+
const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type_p)
|
|
167349
|
+
: type(type), column_ids(column_ids_p), constraint_type(constraint_type_p) {
|
|
166603
167350
|
for (auto &expr : unbound_expressions) {
|
|
166604
167351
|
types.push_back(expr->return_type.InternalType());
|
|
166605
167352
|
logical_types.push_back(expr->return_type);
|
|
@@ -166723,13 +167470,13 @@ void LocalTableStorage::Clear() {
|
|
|
166723
167470
|
table.info->indexes.Scan([&](Index &index) {
|
|
166724
167471
|
D_ASSERT(index.type == IndexType::ART);
|
|
166725
167472
|
auto &art = (ART &)index;
|
|
166726
|
-
if (art.
|
|
167473
|
+
if (art.constraint_type != IndexConstraintType::NONE) {
|
|
166727
167474
|
// unique index: create a local ART index that maintains the same unique constraint
|
|
166728
167475
|
vector<unique_ptr<Expression>> unbound_expressions;
|
|
166729
167476
|
for (auto &expr : art.unbound_expressions) {
|
|
166730
167477
|
unbound_expressions.push_back(expr->Copy());
|
|
166731
167478
|
}
|
|
166732
|
-
indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions),
|
|
167479
|
+
indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions), art.constraint_type));
|
|
166733
167480
|
}
|
|
166734
167481
|
return false;
|
|
166735
167482
|
});
|
|
@@ -167123,6 +167870,30 @@ void LocalStorage::ChangeType(DataTable *old_dt, DataTable *new_dt, idx_t change
|
|
|
167123
167870
|
throw NotImplementedException("FIXME: ALTER TYPE with transaction local data not currently supported");
|
|
167124
167871
|
}
|
|
167125
167872
|
|
|
167873
|
+
void LocalStorage::FetchChunk(DataTable *table, Vector &row_ids, idx_t count, DataChunk &dst_chunk) {
|
|
167874
|
+
auto storage = GetStorage(table);
|
|
167875
|
+
idx_t chunk_idx = GetChunk(row_ids);
|
|
167876
|
+
auto &chunk = storage->collection.GetChunk(chunk_idx);
|
|
167877
|
+
|
|
167878
|
+
VectorData row_ids_data;
|
|
167879
|
+
row_ids.Orrify(count, row_ids_data);
|
|
167880
|
+
auto row_identifiers = (const row_t *)row_ids_data.data;
|
|
167881
|
+
SelectionVector sel(count);
|
|
167882
|
+
for (idx_t i = 0; i < count; ++i) {
|
|
167883
|
+
const auto idx = row_ids_data.sel->get_index(i);
|
|
167884
|
+
sel.set_index(i, row_identifiers[idx] - MAX_ROW_ID);
|
|
167885
|
+
}
|
|
167886
|
+
|
|
167887
|
+
dst_chunk.InitializeEmpty(chunk.GetTypes());
|
|
167888
|
+
dst_chunk.Slice(chunk, sel, count);
|
|
167889
|
+
}
|
|
167890
|
+
|
|
167891
|
+
vector<unique_ptr<Index>> &LocalStorage::GetIndexes(DataTable *table) {
|
|
167892
|
+
auto storage = GetStorage(table);
|
|
167893
|
+
|
|
167894
|
+
return storage->indexes;
|
|
167895
|
+
}
|
|
167896
|
+
|
|
167126
167897
|
} // namespace duckdb
|
|
167127
167898
|
|
|
167128
167899
|
|