duckdb 0.3.3-dev944.0 → 0.3.3-dev977.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 +843 -76
- package/src/duckdb.hpp +93 -10
- package/src/parquet-amalgamation.cpp +31818 -31818
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
|
}
|
|
@@ -48493,9 +48753,9 @@ ExpressionExecutorState::ExpressionExecutorState(const string &name) : profiler(
|
|
|
48493
48753
|
|
|
48494
48754
|
namespace duckdb {
|
|
48495
48755
|
|
|
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,
|
|
48756
|
+
ART::ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions,
|
|
48757
|
+
IndexConstraintType constraint_type)
|
|
48758
|
+
: Index(IndexType::ART, column_ids, unbound_expressions, constraint_type) {
|
|
48499
48759
|
tree = nullptr;
|
|
48500
48760
|
expression_result.Initialize(logical_types);
|
|
48501
48761
|
is_little_endian = IsLittleEndian();
|
|
@@ -48749,43 +49009,19 @@ bool ART::Append(IndexLock &lock, DataChunk &appended_data, Vector &row_identifi
|
|
|
48749
49009
|
}
|
|
48750
49010
|
|
|
48751
49011
|
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);
|
|
49012
|
+
VerifyExistence(chunk, VerifyExistenceType::APPEND);
|
|
49013
|
+
}
|
|
48763
49014
|
|
|
48764
|
-
|
|
48765
|
-
|
|
48766
|
-
|
|
49015
|
+
void ART::VerifyAppendForeignKey(DataChunk &chunk, string *err_msg_ptr) {
|
|
49016
|
+
VerifyExistence(chunk, VerifyExistenceType::APPEND_FK, err_msg_ptr);
|
|
49017
|
+
}
|
|
48767
49018
|
|
|
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
|
-
}
|
|
49019
|
+
void ART::VerifyDeleteForeignKey(DataChunk &chunk, string *err_msg_ptr) {
|
|
49020
|
+
VerifyExistence(chunk, VerifyExistenceType::DELETE_FK, err_msg_ptr);
|
|
48785
49021
|
}
|
|
48786
49022
|
|
|
48787
49023
|
bool ART::InsertToLeaf(Leaf &leaf, row_t row_id) {
|
|
48788
|
-
if (
|
|
49024
|
+
if (IsUnique() && leaf.num_elements != 0) {
|
|
48789
49025
|
return false;
|
|
48790
49026
|
}
|
|
48791
49027
|
leaf.Insert(row_id);
|
|
@@ -49360,6 +49596,70 @@ bool ART::Scan(Transaction &transaction, DataTable &table, IndexScanState &table
|
|
|
49360
49596
|
return true;
|
|
49361
49597
|
}
|
|
49362
49598
|
|
|
49599
|
+
void ART::VerifyExistence(DataChunk &chunk, VerifyExistenceType verify_type, string *err_msg_ptr) {
|
|
49600
|
+
if (verify_type != VerifyExistenceType::DELETE_FK && !IsUnique()) {
|
|
49601
|
+
return;
|
|
49602
|
+
}
|
|
49603
|
+
|
|
49604
|
+
DataChunk expression_result;
|
|
49605
|
+
expression_result.Initialize(logical_types);
|
|
49606
|
+
|
|
49607
|
+
// unique index, check
|
|
49608
|
+
lock_guard<mutex> l(lock);
|
|
49609
|
+
// first resolve the expressions for the index
|
|
49610
|
+
ExecuteExpressions(chunk, expression_result);
|
|
49611
|
+
|
|
49612
|
+
// generate the keys for the given input
|
|
49613
|
+
vector<unique_ptr<Key>> keys;
|
|
49614
|
+
GenerateKeys(expression_result, keys);
|
|
49615
|
+
|
|
49616
|
+
for (idx_t i = 0; i < chunk.size(); i++) {
|
|
49617
|
+
if (!keys[i]) {
|
|
49618
|
+
continue;
|
|
49619
|
+
}
|
|
49620
|
+
Node *node_ptr = Lookup(tree, *keys[i], 0);
|
|
49621
|
+
bool throw_exception =
|
|
49622
|
+
verify_type == VerifyExistenceType::APPEND_FK ? node_ptr == nullptr : node_ptr != nullptr;
|
|
49623
|
+
if (!throw_exception) {
|
|
49624
|
+
continue;
|
|
49625
|
+
}
|
|
49626
|
+
string key_name;
|
|
49627
|
+
for (idx_t k = 0; k < expression_result.ColumnCount(); k++) {
|
|
49628
|
+
if (k > 0) {
|
|
49629
|
+
key_name += ", ";
|
|
49630
|
+
}
|
|
49631
|
+
key_name += unbound_expressions[k]->GetName() + ": " + expression_result.data[k].GetValue(i).ToString();
|
|
49632
|
+
}
|
|
49633
|
+
string exception_msg;
|
|
49634
|
+
switch (verify_type) {
|
|
49635
|
+
case VerifyExistenceType::APPEND: {
|
|
49636
|
+
// node already exists in tree
|
|
49637
|
+
string type = IsPrimary() ? "primary key" : "unique";
|
|
49638
|
+
exception_msg = "duplicate key \"" + key_name + "\" violates ";
|
|
49639
|
+
exception_msg += type + " constraint";
|
|
49640
|
+
break;
|
|
49641
|
+
}
|
|
49642
|
+
case VerifyExistenceType::APPEND_FK: {
|
|
49643
|
+
// found node no exists in tree
|
|
49644
|
+
exception_msg =
|
|
49645
|
+
"violates foreign key constraint because key \"" + key_name + "\" no exist in referenced table";
|
|
49646
|
+
break;
|
|
49647
|
+
}
|
|
49648
|
+
case VerifyExistenceType::DELETE_FK: {
|
|
49649
|
+
// found node exists in tree
|
|
49650
|
+
exception_msg =
|
|
49651
|
+
"violates foreign key constraint because key \"" + key_name + "\" exist in table has foreign key";
|
|
49652
|
+
break;
|
|
49653
|
+
}
|
|
49654
|
+
}
|
|
49655
|
+
if (err_msg_ptr) {
|
|
49656
|
+
err_msg_ptr[i] = exception_msg;
|
|
49657
|
+
} else {
|
|
49658
|
+
throw ConstraintException(exception_msg);
|
|
49659
|
+
}
|
|
49660
|
+
}
|
|
49661
|
+
}
|
|
49662
|
+
|
|
49363
49663
|
} // namespace duckdb
|
|
49364
49664
|
|
|
49365
49665
|
|
|
@@ -62180,19 +62480,16 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62180
62480
|
vector<CatalogEntry *> views;
|
|
62181
62481
|
vector<CatalogEntry *> indexes;
|
|
62182
62482
|
|
|
62183
|
-
Catalog::GetCatalog(ccontext).schemas->
|
|
62184
|
-
|
|
62483
|
+
auto schema_list = Catalog::GetCatalog(ccontext).schemas->GetEntries<SchemaCatalogEntry>(context.client);
|
|
62484
|
+
for (auto &schema : schema_list) {
|
|
62185
62485
|
if (!schema->internal) {
|
|
62186
|
-
// export schema
|
|
62187
62486
|
schemas.push_back(schema);
|
|
62188
62487
|
}
|
|
62189
62488
|
schema->Scan(context.client, CatalogType::TABLE_ENTRY, [&](CatalogEntry *entry) {
|
|
62190
62489
|
if (entry->internal) {
|
|
62191
62490
|
return;
|
|
62192
62491
|
}
|
|
62193
|
-
if (entry->type
|
|
62194
|
-
tables.push_back(entry);
|
|
62195
|
-
} else {
|
|
62492
|
+
if (entry->type != CatalogType::TABLE_ENTRY) {
|
|
62196
62493
|
views.push_back(entry);
|
|
62197
62494
|
}
|
|
62198
62495
|
});
|
|
@@ -62201,7 +62498,12 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62201
62498
|
schema->Scan(context.client, CatalogType::TYPE_ENTRY,
|
|
62202
62499
|
[&](CatalogEntry *entry) { custom_types.push_back(entry); });
|
|
62203
62500
|
schema->Scan(context.client, CatalogType::INDEX_ENTRY, [&](CatalogEntry *entry) { indexes.push_back(entry); });
|
|
62204
|
-
}
|
|
62501
|
+
}
|
|
62502
|
+
|
|
62503
|
+
// consider the order of tables because of foreign key constraint
|
|
62504
|
+
for (idx_t i = 0; i < exported_tables.data.size(); i++) {
|
|
62505
|
+
tables.push_back((CatalogEntry *)exported_tables.data[i].entry);
|
|
62506
|
+
}
|
|
62205
62507
|
|
|
62206
62508
|
// write the schema.sql file
|
|
62207
62509
|
// export order is SCHEMA -> SEQUENCE -> TABLE -> VIEW -> INDEX
|
|
@@ -62219,9 +62521,9 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
|
|
|
62219
62521
|
// write the load.sql file
|
|
62220
62522
|
// for every table, we write COPY INTO statement with the specified options
|
|
62221
62523
|
stringstream load_ss;
|
|
62222
|
-
for (
|
|
62223
|
-
auto table =
|
|
62224
|
-
auto exported_table_info =
|
|
62524
|
+
for (idx_t i = 0; i < exported_tables.data.size(); i++) {
|
|
62525
|
+
auto &table = exported_tables.data[i].entry;
|
|
62526
|
+
auto exported_table_info = exported_tables.data[i].table_data;
|
|
62225
62527
|
WriteCopyStatement(fs, load_ss, table, *info, exported_table_info, function);
|
|
62226
62528
|
}
|
|
62227
62529
|
WriteStringStreamToFile(fs, opener, load_ss, fs.JoinPath(info->file_path, "load.sql"));
|
|
@@ -63749,7 +64051,8 @@ void PhysicalCreateIndex::GetData(ExecutionContext &context, DataChunk &chunk, G
|
|
|
63749
64051
|
unique_ptr<Index> index;
|
|
63750
64052
|
switch (info->index_type) {
|
|
63751
64053
|
case IndexType::ART: {
|
|
63752
|
-
index = make_unique<ART>(column_ids, unbound_expressions,
|
|
64054
|
+
index = make_unique<ART>(column_ids, unbound_expressions,
|
|
64055
|
+
info->unique ? IndexConstraintType::UNIQUE : IndexConstraintType::NONE);
|
|
63753
64056
|
break;
|
|
63754
64057
|
}
|
|
63755
64058
|
default:
|
|
@@ -97600,6 +97903,7 @@ void DuckDBColumnsFun::RegisterFunction(BuiltinFunctions &set) {
|
|
|
97600
97903
|
|
|
97601
97904
|
|
|
97602
97905
|
|
|
97906
|
+
|
|
97603
97907
|
namespace duckdb {
|
|
97604
97908
|
|
|
97605
97909
|
struct DuckDBConstraintsData : public FunctionOperatorData {
|
|
@@ -97758,7 +98062,13 @@ void DuckDBConstraintsFunction(ClientContext &context, const FunctionData *bind_
|
|
|
97758
98062
|
column_index_list.push_back(bound_not_null.index);
|
|
97759
98063
|
break;
|
|
97760
98064
|
}
|
|
97761
|
-
case ConstraintType::FOREIGN_KEY:
|
|
98065
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
98066
|
+
auto &bound_foreign_key = (BoundForeignKeyConstraint &)bound_constraint;
|
|
98067
|
+
for (auto &col_idx : bound_foreign_key.info.fk_keys) {
|
|
98068
|
+
column_index_list.push_back(column_t(col_idx));
|
|
98069
|
+
}
|
|
98070
|
+
break;
|
|
98071
|
+
}
|
|
97762
98072
|
default:
|
|
97763
98073
|
throw NotImplementedException("Unimplemented constraint for duckdb_constraints");
|
|
97764
98074
|
}
|
|
@@ -98435,9 +98745,9 @@ void DuckDBIndexesFunction(ClientContext &context, const FunctionData *bind_data
|
|
|
98435
98745
|
auto table_entry = catalog.GetEntry(context, CatalogType::TABLE_ENTRY, index.info->schema, index.info->table);
|
|
98436
98746
|
output.SetValue(5, count, Value::BIGINT(table_entry->oid));
|
|
98437
98747
|
// is_unique, BOOLEAN
|
|
98438
|
-
output.SetValue(6, count, Value::BOOLEAN(index.index->
|
|
98748
|
+
output.SetValue(6, count, Value::BOOLEAN(index.index->IsUnique()));
|
|
98439
98749
|
// is_primary, BOOLEAN
|
|
98440
|
-
output.SetValue(7, count, Value::BOOLEAN(index.index->
|
|
98750
|
+
output.SetValue(7, count, Value::BOOLEAN(index.index->IsPrimary()));
|
|
98441
98751
|
// expressions, VARCHAR
|
|
98442
98752
|
output.SetValue(8, count, Value());
|
|
98443
98753
|
// sql, VARCHAR
|
|
@@ -133299,6 +133609,9 @@ unique_ptr<Constraint> Constraint::Deserialize(Deserializer &source) {
|
|
|
133299
133609
|
case ConstraintType::UNIQUE:
|
|
133300
133610
|
result = UniqueConstraint::Deserialize(reader);
|
|
133301
133611
|
break;
|
|
133612
|
+
case ConstraintType::FOREIGN_KEY:
|
|
133613
|
+
result = ForeignKeyConstraint::Deserialize(reader);
|
|
133614
|
+
break;
|
|
133302
133615
|
default:
|
|
133303
133616
|
throw InternalException("Unrecognized constraint type for serialization");
|
|
133304
133617
|
}
|
|
@@ -133343,6 +133656,82 @@ unique_ptr<Constraint> CheckConstraint::Deserialize(FieldReader &source) {
|
|
|
133343
133656
|
|
|
133344
133657
|
|
|
133345
133658
|
|
|
133659
|
+
|
|
133660
|
+
|
|
133661
|
+
namespace duckdb {
|
|
133662
|
+
|
|
133663
|
+
ForeignKeyConstraint::ForeignKeyConstraint(vector<string> pk_columns, vector<string> fk_columns, ForeignKeyInfo info)
|
|
133664
|
+
: Constraint(ConstraintType::FOREIGN_KEY), pk_columns(move(pk_columns)), fk_columns(move(fk_columns)),
|
|
133665
|
+
info(move(info)) {
|
|
133666
|
+
}
|
|
133667
|
+
|
|
133668
|
+
string ForeignKeyConstraint::ToString() const {
|
|
133669
|
+
if (info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
133670
|
+
string base = "FOREIGN KEY (";
|
|
133671
|
+
|
|
133672
|
+
for (idx_t i = 0; i < fk_columns.size(); i++) {
|
|
133673
|
+
if (i > 0) {
|
|
133674
|
+
base += ", ";
|
|
133675
|
+
}
|
|
133676
|
+
base += KeywordHelper::WriteOptionallyQuoted(fk_columns[i]);
|
|
133677
|
+
}
|
|
133678
|
+
base += ") REFERENCES ";
|
|
133679
|
+
if (!info.schema.empty()) {
|
|
133680
|
+
base += info.schema;
|
|
133681
|
+
base += ".";
|
|
133682
|
+
}
|
|
133683
|
+
base += info.table;
|
|
133684
|
+
base += "(";
|
|
133685
|
+
|
|
133686
|
+
for (idx_t i = 0; i < pk_columns.size(); i++) {
|
|
133687
|
+
if (i > 0) {
|
|
133688
|
+
base += ", ";
|
|
133689
|
+
}
|
|
133690
|
+
base += KeywordHelper::WriteOptionallyQuoted(pk_columns[i]);
|
|
133691
|
+
}
|
|
133692
|
+
base += ")";
|
|
133693
|
+
|
|
133694
|
+
return base;
|
|
133695
|
+
}
|
|
133696
|
+
|
|
133697
|
+
return "";
|
|
133698
|
+
}
|
|
133699
|
+
|
|
133700
|
+
unique_ptr<Constraint> ForeignKeyConstraint::Copy() const {
|
|
133701
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, info);
|
|
133702
|
+
}
|
|
133703
|
+
|
|
133704
|
+
void ForeignKeyConstraint::Serialize(FieldWriter &writer) const {
|
|
133705
|
+
D_ASSERT(pk_columns.size() <= NumericLimits<uint32_t>::Maximum());
|
|
133706
|
+
writer.WriteList<string>(pk_columns);
|
|
133707
|
+
D_ASSERT(fk_columns.size() <= NumericLimits<uint32_t>::Maximum());
|
|
133708
|
+
writer.WriteList<string>(fk_columns);
|
|
133709
|
+
writer.WriteField<ForeignKeyType>(info.type);
|
|
133710
|
+
writer.WriteString(info.schema);
|
|
133711
|
+
writer.WriteString(info.table);
|
|
133712
|
+
writer.WriteList<idx_t>(info.pk_keys);
|
|
133713
|
+
writer.WriteList<idx_t>(info.fk_keys);
|
|
133714
|
+
}
|
|
133715
|
+
|
|
133716
|
+
unique_ptr<Constraint> ForeignKeyConstraint::Deserialize(FieldReader &source) {
|
|
133717
|
+
ForeignKeyInfo read_info;
|
|
133718
|
+
auto pk_columns = source.ReadRequiredList<string>();
|
|
133719
|
+
auto fk_columns = source.ReadRequiredList<string>();
|
|
133720
|
+
read_info.type = source.ReadRequired<ForeignKeyType>();
|
|
133721
|
+
read_info.schema = source.ReadRequired<string>();
|
|
133722
|
+
read_info.table = source.ReadRequired<string>();
|
|
133723
|
+
read_info.pk_keys = source.ReadRequiredList<idx_t>();
|
|
133724
|
+
read_info.fk_keys = source.ReadRequiredList<idx_t>();
|
|
133725
|
+
|
|
133726
|
+
// column list parsed constraint
|
|
133727
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(read_info));
|
|
133728
|
+
}
|
|
133729
|
+
|
|
133730
|
+
} // namespace duckdb
|
|
133731
|
+
|
|
133732
|
+
|
|
133733
|
+
|
|
133734
|
+
|
|
133346
133735
|
namespace duckdb {
|
|
133347
133736
|
|
|
133348
133737
|
NotNullConstraint::NotNullConstraint(column_t index) : Constraint(ConstraintType::NOT_NULL), index(index) {
|
|
@@ -134755,6 +135144,8 @@ unique_ptr<AlterInfo> AlterTableInfo::Deserialize(FieldReader &reader) {
|
|
|
134755
135144
|
return ChangeColumnTypeInfo::Deserialize(reader, schema, table);
|
|
134756
135145
|
case AlterTableType::SET_DEFAULT:
|
|
134757
135146
|
return SetDefaultInfo::Deserialize(reader, schema, table);
|
|
135147
|
+
case AlterTableType::FOREIGN_KEY_CONSTRAINT:
|
|
135148
|
+
return AlterForeignKeyInfo::Deserialize(reader, schema, table);
|
|
134758
135149
|
default:
|
|
134759
135150
|
throw SerializationException("Unknown alter table type for deserialization!");
|
|
134760
135151
|
}
|
|
@@ -134911,6 +135302,44 @@ unique_ptr<AlterInfo> SetDefaultInfo::Deserialize(FieldReader &reader, string sc
|
|
|
134911
135302
|
return make_unique<SetDefaultInfo>(move(schema), move(table), move(column_name), move(new_default));
|
|
134912
135303
|
}
|
|
134913
135304
|
|
|
135305
|
+
//===--------------------------------------------------------------------===//
|
|
135306
|
+
// AlterForeignKeyInfo
|
|
135307
|
+
//===--------------------------------------------------------------------===//
|
|
135308
|
+
AlterForeignKeyInfo::AlterForeignKeyInfo(string schema_p, string table_p, string fk_table, vector<string> pk_columns,
|
|
135309
|
+
vector<string> fk_columns, vector<idx_t> pk_keys, vector<idx_t> fk_keys,
|
|
135310
|
+
AlterForeignKeyType type_p)
|
|
135311
|
+
: AlterTableInfo(AlterTableType::FOREIGN_KEY_CONSTRAINT, move(schema_p), move(table_p)), fk_table(move(fk_table)),
|
|
135312
|
+
pk_columns(move(pk_columns)), fk_columns(move(fk_columns)), pk_keys(move(pk_keys)), fk_keys(move(fk_keys)),
|
|
135313
|
+
type(type_p) {
|
|
135314
|
+
}
|
|
135315
|
+
AlterForeignKeyInfo::~AlterForeignKeyInfo() {
|
|
135316
|
+
}
|
|
135317
|
+
|
|
135318
|
+
unique_ptr<AlterInfo> AlterForeignKeyInfo::Copy() const {
|
|
135319
|
+
return make_unique_base<AlterInfo, AlterForeignKeyInfo>(schema, name, fk_table, pk_columns, fk_columns, pk_keys,
|
|
135320
|
+
fk_keys, type);
|
|
135321
|
+
}
|
|
135322
|
+
|
|
135323
|
+
void AlterForeignKeyInfo::SerializeAlterTable(FieldWriter &writer) const {
|
|
135324
|
+
writer.WriteString(fk_table);
|
|
135325
|
+
writer.WriteList<string>(pk_columns);
|
|
135326
|
+
writer.WriteList<string>(fk_columns);
|
|
135327
|
+
writer.WriteList<idx_t>(pk_keys);
|
|
135328
|
+
writer.WriteList<idx_t>(fk_keys);
|
|
135329
|
+
writer.WriteField<AlterForeignKeyType>(type);
|
|
135330
|
+
}
|
|
135331
|
+
|
|
135332
|
+
unique_ptr<AlterInfo> AlterForeignKeyInfo::Deserialize(FieldReader &reader, string schema, string table) {
|
|
135333
|
+
auto fk_table = reader.ReadRequired<string>();
|
|
135334
|
+
auto pk_columns = reader.ReadRequiredList<string>();
|
|
135335
|
+
auto fk_columns = reader.ReadRequiredList<string>();
|
|
135336
|
+
auto pk_keys = reader.ReadRequiredList<idx_t>();
|
|
135337
|
+
auto fk_keys = reader.ReadRequiredList<idx_t>();
|
|
135338
|
+
auto type = reader.ReadRequired<AlterForeignKeyType>();
|
|
135339
|
+
return make_unique<AlterForeignKeyInfo>(move(schema), move(table), move(fk_table), move(pk_columns),
|
|
135340
|
+
move(fk_columns), move(pk_keys), move(fk_keys), type);
|
|
135341
|
+
}
|
|
135342
|
+
|
|
134914
135343
|
//===--------------------------------------------------------------------===//
|
|
134915
135344
|
// Alter View
|
|
134916
135345
|
//===--------------------------------------------------------------------===//
|
|
@@ -142779,6 +143208,30 @@ unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGLis
|
|
|
142779
143208
|
}
|
|
142780
143209
|
return make_unique<CheckConstraint>(TransformExpression(constraint->raw_expr));
|
|
142781
143210
|
}
|
|
143211
|
+
case duckdb_libpgquery::PG_CONSTR_FOREIGN: {
|
|
143212
|
+
ForeignKeyInfo fk_info;
|
|
143213
|
+
fk_info.type = ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE;
|
|
143214
|
+
if (constraint->pktable->schemaname) {
|
|
143215
|
+
fk_info.schema = constraint->pktable->schemaname;
|
|
143216
|
+
} else {
|
|
143217
|
+
fk_info.schema = "";
|
|
143218
|
+
}
|
|
143219
|
+
fk_info.table = constraint->pktable->relname;
|
|
143220
|
+
vector<string> pk_columns, fk_columns;
|
|
143221
|
+
for (auto kc = constraint->fk_attrs->head; kc; kc = kc->next) {
|
|
143222
|
+
fk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
|
|
143223
|
+
}
|
|
143224
|
+
for (auto kc = constraint->pk_attrs->head; kc; kc = kc->next) {
|
|
143225
|
+
pk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
|
|
143226
|
+
}
|
|
143227
|
+
if (pk_columns.size() != fk_columns.size()) {
|
|
143228
|
+
throw ParserException("The number of referencing and referenced columns for foreign keys must be the same");
|
|
143229
|
+
}
|
|
143230
|
+
if (fk_columns.empty()) {
|
|
143231
|
+
throw ParserException("The set of referencing and referenced columns for foreign keys must be not empty");
|
|
143232
|
+
}
|
|
143233
|
+
return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(fk_info));
|
|
143234
|
+
}
|
|
142782
143235
|
default:
|
|
142783
143236
|
throw NotImplementedException("Constraint type not handled yet!");
|
|
142784
143237
|
}
|
|
@@ -151630,6 +152083,7 @@ public:
|
|
|
151630
152083
|
} // namespace duckdb
|
|
151631
152084
|
|
|
151632
152085
|
|
|
152086
|
+
|
|
151633
152087
|
namespace duckdb {
|
|
151634
152088
|
|
|
151635
152089
|
SchemaCatalogEntry *Binder::BindSchema(CreateInfo &info) {
|
|
@@ -151816,6 +152270,34 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
|
|
|
151816
152270
|
break;
|
|
151817
152271
|
}
|
|
151818
152272
|
case CatalogType::TABLE_ENTRY: {
|
|
152273
|
+
// If there is a foreign key constraint, resolve primary key column's index from primary key column's name
|
|
152274
|
+
auto &create_info = (CreateTableInfo &)*stmt.info;
|
|
152275
|
+
auto &catalog = Catalog::GetCatalog(context);
|
|
152276
|
+
for (idx_t i = 0; i < create_info.constraints.size(); i++) {
|
|
152277
|
+
auto &cond = create_info.constraints[i];
|
|
152278
|
+
if (cond->type != ConstraintType::FOREIGN_KEY) {
|
|
152279
|
+
continue;
|
|
152280
|
+
}
|
|
152281
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152282
|
+
if (fk.info.type != ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
|
|
152283
|
+
continue;
|
|
152284
|
+
}
|
|
152285
|
+
D_ASSERT(fk.info.pk_keys.empty() && !fk.pk_columns.empty());
|
|
152286
|
+
if (create_info.table == fk.info.table) {
|
|
152287
|
+
fk.info.type = ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE;
|
|
152288
|
+
} else {
|
|
152289
|
+
// have to resolve referenced table
|
|
152290
|
+
auto pk_table_entry_ptr = catalog.GetEntry<TableCatalogEntry>(context, fk.info.schema, fk.info.table);
|
|
152291
|
+
D_ASSERT(!fk.pk_columns.empty() && fk.info.pk_keys.empty());
|
|
152292
|
+
for (auto &keyname : fk.pk_columns) {
|
|
152293
|
+
auto entry = pk_table_entry_ptr->name_map.find(keyname);
|
|
152294
|
+
if (entry == pk_table_entry_ptr->name_map.end()) {
|
|
152295
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152296
|
+
}
|
|
152297
|
+
fk.info.pk_keys.push_back(entry->second);
|
|
152298
|
+
}
|
|
152299
|
+
}
|
|
152300
|
+
}
|
|
151819
152301
|
// We first check if there are any user types, if yes we check to which custom types they refer.
|
|
151820
152302
|
auto bound_info = BindCreateTableInfo(move(stmt.info));
|
|
151821
152303
|
auto root = move(bound_info->query);
|
|
@@ -151850,6 +152332,7 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
|
|
|
151850
152332
|
|
|
151851
152333
|
|
|
151852
152334
|
|
|
152335
|
+
|
|
151853
152336
|
//===----------------------------------------------------------------------===//
|
|
151854
152337
|
// DuckDB
|
|
151855
152338
|
//
|
|
@@ -151986,6 +152469,39 @@ static void BindConstraints(Binder &binder, BoundCreateTableInfo &info) {
|
|
|
151986
152469
|
make_unique<BoundUniqueConstraint>(move(keys), move(key_set), unique.is_primary_key));
|
|
151987
152470
|
break;
|
|
151988
152471
|
}
|
|
152472
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
152473
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152474
|
+
D_ASSERT((fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE && !fk.info.pk_keys.empty()) ||
|
|
152475
|
+
(fk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE && !fk.info.pk_keys.empty()) ||
|
|
152476
|
+
fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE);
|
|
152477
|
+
if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE && fk.info.pk_keys.empty()) {
|
|
152478
|
+
for (auto &keyname : fk.pk_columns) {
|
|
152479
|
+
auto entry = info.name_map.find(keyname);
|
|
152480
|
+
if (entry == info.name_map.end()) {
|
|
152481
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152482
|
+
}
|
|
152483
|
+
fk.info.pk_keys.push_back(entry->second);
|
|
152484
|
+
}
|
|
152485
|
+
}
|
|
152486
|
+
if (fk.info.fk_keys.empty()) {
|
|
152487
|
+
for (auto &keyname : fk.fk_columns) {
|
|
152488
|
+
auto entry = info.name_map.find(keyname);
|
|
152489
|
+
if (entry == info.name_map.end()) {
|
|
152490
|
+
throw ParserException("column \"%s\" named in key does not exist", keyname);
|
|
152491
|
+
}
|
|
152492
|
+
fk.info.fk_keys.push_back(entry->second);
|
|
152493
|
+
}
|
|
152494
|
+
}
|
|
152495
|
+
unordered_set<idx_t> fk_key_set, pk_key_set;
|
|
152496
|
+
for (idx_t i = 0; i < fk.info.pk_keys.size(); i++) {
|
|
152497
|
+
pk_key_set.insert(fk.info.pk_keys[i]);
|
|
152498
|
+
}
|
|
152499
|
+
for (idx_t i = 0; i < fk.info.fk_keys.size(); i++) {
|
|
152500
|
+
fk_key_set.insert(fk.info.fk_keys[i]);
|
|
152501
|
+
}
|
|
152502
|
+
info.bound_constraints.push_back(make_unique<BoundForeignKeyConstraint>(fk.info, pk_key_set, fk_key_set));
|
|
152503
|
+
break;
|
|
152504
|
+
}
|
|
151989
152505
|
default:
|
|
151990
152506
|
throw NotImplementedException("unrecognized constraint type in bind");
|
|
151991
152507
|
}
|
|
@@ -152234,6 +152750,7 @@ BoundStatement Binder::Bind(ExplainStatement &stmt) {
|
|
|
152234
152750
|
|
|
152235
152751
|
|
|
152236
152752
|
|
|
152753
|
+
|
|
152237
152754
|
#include <algorithm>
|
|
152238
152755
|
|
|
152239
152756
|
namespace duckdb {
|
|
@@ -152262,6 +152779,51 @@ string SanitizeExportIdentifier(const string &str) {
|
|
|
152262
152779
|
return result;
|
|
152263
152780
|
}
|
|
152264
152781
|
|
|
152782
|
+
bool IsExistMainKeyTable(string &table_name, vector<TableCatalogEntry *> &unordered) {
|
|
152783
|
+
for (idx_t i = 0; i < unordered.size(); i++) {
|
|
152784
|
+
if (unordered[i]->name == table_name) {
|
|
152785
|
+
return true;
|
|
152786
|
+
}
|
|
152787
|
+
}
|
|
152788
|
+
return false;
|
|
152789
|
+
}
|
|
152790
|
+
|
|
152791
|
+
void ScanForeignKeyTable(vector<TableCatalogEntry *> &ordered, vector<TableCatalogEntry *> &unordered,
|
|
152792
|
+
bool move_only_pk_table) {
|
|
152793
|
+
for (auto i = unordered.begin(); i != unordered.end();) {
|
|
152794
|
+
auto table_entry = *i;
|
|
152795
|
+
bool move_to_ordered = true;
|
|
152796
|
+
for (idx_t j = 0; j < table_entry->constraints.size(); j++) {
|
|
152797
|
+
auto &cond = table_entry->constraints[j];
|
|
152798
|
+
if (cond->type == ConstraintType::FOREIGN_KEY) {
|
|
152799
|
+
auto &fk = (ForeignKeyConstraint &)*cond;
|
|
152800
|
+
if ((move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) ||
|
|
152801
|
+
(!move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE &&
|
|
152802
|
+
IsExistMainKeyTable(fk.info.table, unordered))) {
|
|
152803
|
+
move_to_ordered = false;
|
|
152804
|
+
break;
|
|
152805
|
+
}
|
|
152806
|
+
}
|
|
152807
|
+
}
|
|
152808
|
+
if (move_to_ordered) {
|
|
152809
|
+
ordered.push_back(table_entry);
|
|
152810
|
+
i = unordered.erase(i);
|
|
152811
|
+
} else {
|
|
152812
|
+
i++;
|
|
152813
|
+
}
|
|
152814
|
+
}
|
|
152815
|
+
}
|
|
152816
|
+
|
|
152817
|
+
void ReorderTableEntries(vector<TableCatalogEntry *> &tables) {
|
|
152818
|
+
vector<TableCatalogEntry *> ordered;
|
|
152819
|
+
vector<TableCatalogEntry *> unordered = tables;
|
|
152820
|
+
ScanForeignKeyTable(ordered, unordered, true);
|
|
152821
|
+
while (!unordered.empty()) {
|
|
152822
|
+
ScanForeignKeyTable(ordered, unordered, false);
|
|
152823
|
+
}
|
|
152824
|
+
tables = ordered;
|
|
152825
|
+
}
|
|
152826
|
+
|
|
152265
152827
|
BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
152266
152828
|
// COPY TO a file
|
|
152267
152829
|
auto &config = DBConfig::GetConfig(context);
|
|
@@ -152290,6 +152852,9 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
|
152290
152852
|
});
|
|
152291
152853
|
}
|
|
152292
152854
|
|
|
152855
|
+
// reorder tables because of foreign key constraint
|
|
152856
|
+
ReorderTableEntries(tables);
|
|
152857
|
+
|
|
152293
152858
|
// now generate the COPY statements for each of the tables
|
|
152294
152859
|
auto &fs = FileSystem::GetFileSystem(context);
|
|
152295
152860
|
unique_ptr<LogicalOperator> child_operator;
|
|
@@ -152324,7 +152889,10 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
|
|
|
152324
152889
|
exported_data.schema_name = info->schema;
|
|
152325
152890
|
exported_data.file_path = info->file_path;
|
|
152326
152891
|
|
|
152327
|
-
|
|
152892
|
+
ExportedTableInfo table_info;
|
|
152893
|
+
table_info.entry = table;
|
|
152894
|
+
table_info.table_data = exported_data;
|
|
152895
|
+
exported_tables.data.push_back(table_info);
|
|
152328
152896
|
id++;
|
|
152329
152897
|
|
|
152330
152898
|
// generate the copy statement and bind it
|
|
@@ -160277,6 +160845,8 @@ void WriteOverflowStringsToDisk::AllocateNewBlock(block_id_t new_block_id) {
|
|
|
160277
160845
|
|
|
160278
160846
|
namespace duckdb {
|
|
160279
160847
|
|
|
160848
|
+
void ReorderTableEntries(vector<TableCatalogEntry *> &tables);
|
|
160849
|
+
|
|
160280
160850
|
CheckpointManager::CheckpointManager(DatabaseInstance &db) : db(db) {
|
|
160281
160851
|
}
|
|
160282
160852
|
|
|
@@ -160421,6 +160991,8 @@ void CheckpointManager::WriteSchema(SchemaCatalogEntry &schema) {
|
|
|
160421
160991
|
for (auto &seq : sequences) {
|
|
160422
160992
|
WriteSequence(*seq);
|
|
160423
160993
|
}
|
|
160994
|
+
// reorder tables because of foreign key constraint
|
|
160995
|
+
ReorderTableEntries(tables);
|
|
160424
160996
|
// now write the tables
|
|
160425
160997
|
metadata_writer->Write<uint32_t>(tables.size());
|
|
160426
160998
|
for (auto &table : tables) {
|
|
@@ -165876,7 +166448,133 @@ static void VerifyCheckConstraint(TableCatalogEntry &table, Expression &expr, Da
|
|
|
165876
166448
|
}
|
|
165877
166449
|
}
|
|
165878
166450
|
|
|
165879
|
-
|
|
166451
|
+
static bool FindColumnIndex(const vector<idx_t> *keys_ptr, const vector<column_t> &index_column_ids) {
|
|
166452
|
+
for (idx_t i = 0; i < keys_ptr->size(); i++) {
|
|
166453
|
+
bool is_found = false;
|
|
166454
|
+
for (idx_t j = 0; j < index_column_ids.size(); j++) {
|
|
166455
|
+
if ((*keys_ptr)[i] == index_column_ids[j]) {
|
|
166456
|
+
is_found = true;
|
|
166457
|
+
break;
|
|
166458
|
+
}
|
|
166459
|
+
}
|
|
166460
|
+
if (!is_found) {
|
|
166461
|
+
return false;
|
|
166462
|
+
}
|
|
166463
|
+
}
|
|
166464
|
+
|
|
166465
|
+
return true;
|
|
166466
|
+
}
|
|
166467
|
+
|
|
166468
|
+
static void VerifyForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context, DataChunk &chunk,
|
|
166469
|
+
bool is_append) {
|
|
166470
|
+
const vector<idx_t> *src_keys_ptr = &bfk.info.fk_keys;
|
|
166471
|
+
const vector<idx_t> *dst_keys_ptr = &bfk.info.pk_keys;
|
|
166472
|
+
if (!is_append) {
|
|
166473
|
+
src_keys_ptr = &bfk.info.pk_keys;
|
|
166474
|
+
dst_keys_ptr = &bfk.info.fk_keys;
|
|
166475
|
+
}
|
|
166476
|
+
|
|
166477
|
+
auto table_entry_ptr =
|
|
166478
|
+
Catalog::GetCatalog(context).GetEntry<TableCatalogEntry>(context, bfk.info.schema, bfk.info.table);
|
|
166479
|
+
if (table_entry_ptr == nullptr) {
|
|
166480
|
+
throw InternalException("Can't find table \"%s\" in foreign key constraint", bfk.info.table);
|
|
166481
|
+
}
|
|
166482
|
+
|
|
166483
|
+
// make the data chunk to check
|
|
166484
|
+
vector<LogicalType> types;
|
|
166485
|
+
for (idx_t i = 0; i < table_entry_ptr->columns.size(); i++) {
|
|
166486
|
+
types.emplace_back(table_entry_ptr->columns[i].type);
|
|
166487
|
+
}
|
|
166488
|
+
DataChunk dst_chunk;
|
|
166489
|
+
dst_chunk.InitializeEmpty(types);
|
|
166490
|
+
for (idx_t i = 0; i < src_keys_ptr->size(); i++) {
|
|
166491
|
+
dst_chunk.data[(*dst_keys_ptr)[i]].Reference(chunk.data[(*src_keys_ptr)[i]]);
|
|
166492
|
+
}
|
|
166493
|
+
dst_chunk.SetCardinality(chunk.size());
|
|
166494
|
+
auto data_table = table_entry_ptr->storage.get();
|
|
166495
|
+
|
|
166496
|
+
idx_t count = dst_chunk.size();
|
|
166497
|
+
if (count <= 0) {
|
|
166498
|
+
return;
|
|
166499
|
+
}
|
|
166500
|
+
|
|
166501
|
+
// we need to look at the error messages concurrently in data table's index and transaction local storage's index
|
|
166502
|
+
vector<string> err_msgs, tran_err_msgs;
|
|
166503
|
+
err_msgs.resize(count);
|
|
166504
|
+
tran_err_msgs.resize(count);
|
|
166505
|
+
|
|
166506
|
+
// check whether or not the chunk can be inserted or deleted into the referenced table' storage
|
|
166507
|
+
TableIndexList &table_indices = data_table->info->indexes;
|
|
166508
|
+
table_indices.Scan([&](Index &index) {
|
|
166509
|
+
if (FindColumnIndex(dst_keys_ptr, index.column_ids)) {
|
|
166510
|
+
if (is_append) {
|
|
166511
|
+
index.VerifyAppendForeignKey(dst_chunk, err_msgs.data());
|
|
166512
|
+
} else {
|
|
166513
|
+
index.VerifyDeleteForeignKey(dst_chunk, err_msgs.data());
|
|
166514
|
+
}
|
|
166515
|
+
}
|
|
166516
|
+
return false;
|
|
166517
|
+
});
|
|
166518
|
+
|
|
166519
|
+
// check whether or not the chunk can be inserted or deleted into the referenced table' transaction local storage
|
|
166520
|
+
auto &transaction = Transaction::GetTransaction(context);
|
|
166521
|
+
bool transaction_check = transaction.storage.Find(data_table);
|
|
166522
|
+
if (transaction_check) {
|
|
166523
|
+
vector<unique_ptr<Index>> &transact_index_vec = transaction.storage.GetIndexes(data_table);
|
|
166524
|
+
for (idx_t i = 0; i < transact_index_vec.size(); i++) {
|
|
166525
|
+
if (FindColumnIndex(dst_keys_ptr, transact_index_vec[i]->column_ids)) {
|
|
166526
|
+
if (is_append) {
|
|
166527
|
+
transact_index_vec[i]->VerifyAppendForeignKey(dst_chunk, tran_err_msgs.data());
|
|
166528
|
+
} else {
|
|
166529
|
+
transact_index_vec[i]->VerifyDeleteForeignKey(dst_chunk, tran_err_msgs.data());
|
|
166530
|
+
}
|
|
166531
|
+
}
|
|
166532
|
+
}
|
|
166533
|
+
}
|
|
166534
|
+
|
|
166535
|
+
// we need to look at the error messages concurrently in data table's index and transaction local storage's index
|
|
166536
|
+
for (idx_t i = 0; i < count; i++) {
|
|
166537
|
+
if (!transaction_check) {
|
|
166538
|
+
// if there is no transaction-local data we only need to check if there is an error message in the main
|
|
166539
|
+
// index
|
|
166540
|
+
if (!err_msgs[i].empty()) {
|
|
166541
|
+
throw ConstraintException(err_msgs[i]);
|
|
166542
|
+
} else {
|
|
166543
|
+
continue;
|
|
166544
|
+
}
|
|
166545
|
+
}
|
|
166546
|
+
if (is_append) {
|
|
166547
|
+
// if we are appending we need to check to ensure the foreign key exists in either the transaction-local
|
|
166548
|
+
// storage or the main table
|
|
166549
|
+
if (!err_msgs[i].empty() && !tran_err_msgs[i].empty()) {
|
|
166550
|
+
throw ConstraintException(err_msgs[i]);
|
|
166551
|
+
} else {
|
|
166552
|
+
continue;
|
|
166553
|
+
}
|
|
166554
|
+
}
|
|
166555
|
+
// if we are deleting we need to ensure the foreign key DOES NOT exist in EITHER the transaction-local storage
|
|
166556
|
+
// OR the main table
|
|
166557
|
+
if (!err_msgs[i].empty() || !tran_err_msgs[i].empty()) {
|
|
166558
|
+
string &err_msg = err_msgs[i];
|
|
166559
|
+
if (err_msg.empty()) {
|
|
166560
|
+
err_msg = tran_err_msgs[i];
|
|
166561
|
+
}
|
|
166562
|
+
throw ConstraintException(err_msg);
|
|
166563
|
+
}
|
|
166564
|
+
}
|
|
166565
|
+
}
|
|
166566
|
+
|
|
166567
|
+
static void VerifyAppendForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
|
|
166568
|
+
DataChunk &chunk) {
|
|
166569
|
+
VerifyForeignKeyConstraint(bfk, context, chunk, true);
|
|
166570
|
+
}
|
|
166571
|
+
|
|
166572
|
+
static void VerifyDeleteForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
|
|
166573
|
+
DataChunk &chunk) {
|
|
166574
|
+
VerifyForeignKeyConstraint(bfk, context, chunk, false);
|
|
166575
|
+
}
|
|
166576
|
+
|
|
166577
|
+
void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
|
|
165880
166578
|
for (auto &constraint : table.bound_constraints) {
|
|
165881
166579
|
switch (constraint->type) {
|
|
165882
166580
|
case ConstraintType::NOT_NULL: {
|
|
@@ -165898,7 +166596,14 @@ void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, DataChunk &chu
|
|
|
165898
166596
|
});
|
|
165899
166597
|
break;
|
|
165900
166598
|
}
|
|
165901
|
-
case ConstraintType::FOREIGN_KEY:
|
|
166599
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
166600
|
+
auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
|
|
166601
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE ||
|
|
166602
|
+
bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
166603
|
+
VerifyAppendForeignKeyConstraint(bfk, context, chunk);
|
|
166604
|
+
}
|
|
166605
|
+
break;
|
|
166606
|
+
}
|
|
165902
166607
|
default:
|
|
165903
166608
|
throw NotImplementedException("Constraint type not implemented!");
|
|
165904
166609
|
}
|
|
@@ -165919,7 +166624,7 @@ void DataTable::Append(TableCatalogEntry &table, ClientContext &context, DataChu
|
|
|
165919
166624
|
chunk.Verify();
|
|
165920
166625
|
|
|
165921
166626
|
// verify any constraints on the new chunk
|
|
165922
|
-
VerifyAppendConstraints(table, chunk);
|
|
166627
|
+
VerifyAppendConstraints(table, context, chunk);
|
|
165923
166628
|
|
|
165924
166629
|
// append to the transaction local data
|
|
165925
166630
|
auto &transaction = Transaction::GetTransaction(context);
|
|
@@ -166211,6 +166916,27 @@ void DataTable::RemoveFromIndexes(Vector &row_identifiers, idx_t count) {
|
|
|
166211
166916
|
});
|
|
166212
166917
|
}
|
|
166213
166918
|
|
|
166919
|
+
void DataTable::VerifyDeleteConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
|
|
166920
|
+
for (auto &constraint : table.bound_constraints) {
|
|
166921
|
+
switch (constraint->type) {
|
|
166922
|
+
case ConstraintType::NOT_NULL:
|
|
166923
|
+
case ConstraintType::CHECK:
|
|
166924
|
+
case ConstraintType::UNIQUE:
|
|
166925
|
+
break;
|
|
166926
|
+
case ConstraintType::FOREIGN_KEY: {
|
|
166927
|
+
auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
|
|
166928
|
+
if (bfk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE ||
|
|
166929
|
+
bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
|
|
166930
|
+
VerifyDeleteForeignKeyConstraint(bfk, context, chunk);
|
|
166931
|
+
}
|
|
166932
|
+
break;
|
|
166933
|
+
}
|
|
166934
|
+
default:
|
|
166935
|
+
throw NotImplementedException("Constraint type not implemented!");
|
|
166936
|
+
}
|
|
166937
|
+
}
|
|
166938
|
+
}
|
|
166939
|
+
|
|
166214
166940
|
//===--------------------------------------------------------------------===//
|
|
166215
166941
|
// Delete
|
|
166216
166942
|
//===--------------------------------------------------------------------===//
|
|
@@ -166226,6 +166952,23 @@ idx_t DataTable::Delete(TableCatalogEntry &table, ClientContext &context, Vector
|
|
|
166226
166952
|
auto ids = FlatVector::GetData<row_t>(row_identifiers);
|
|
166227
166953
|
auto first_id = ids[0];
|
|
166228
166954
|
|
|
166955
|
+
// verify any constraints on the delete rows
|
|
166956
|
+
DataChunk verify_chunk;
|
|
166957
|
+
if (first_id >= MAX_ROW_ID) {
|
|
166958
|
+
transaction.storage.FetchChunk(this, row_identifiers, count, verify_chunk);
|
|
166959
|
+
} else {
|
|
166960
|
+
ColumnFetchState fetch_state;
|
|
166961
|
+
vector<column_t> col_ids;
|
|
166962
|
+
vector<LogicalType> types;
|
|
166963
|
+
for (idx_t i = 0; i < column_definitions.size(); i++) {
|
|
166964
|
+
col_ids.push_back(column_definitions[i].oid);
|
|
166965
|
+
types.emplace_back(column_definitions[i].type);
|
|
166966
|
+
}
|
|
166967
|
+
verify_chunk.Initialize(types);
|
|
166968
|
+
Fetch(transaction, verify_chunk, col_ids, row_identifiers, count, fetch_state);
|
|
166969
|
+
}
|
|
166970
|
+
VerifyDeleteConstraints(table, context, verify_chunk);
|
|
166971
|
+
|
|
166229
166972
|
if (first_id >= MAX_ROW_ID) {
|
|
166230
166973
|
// deletion is in transaction-local storage: push delete into local chunk collection
|
|
166231
166974
|
return transaction.storage.Delete(this, row_identifiers, count);
|
|
@@ -166598,8 +167341,8 @@ vector<vector<Value>> DataTable::GetStorageInfo() {
|
|
|
166598
167341
|
namespace duckdb {
|
|
166599
167342
|
|
|
166600
167343
|
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),
|
|
167344
|
+
const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type_p)
|
|
167345
|
+
: type(type), column_ids(column_ids_p), constraint_type(constraint_type_p) {
|
|
166603
167346
|
for (auto &expr : unbound_expressions) {
|
|
166604
167347
|
types.push_back(expr->return_type.InternalType());
|
|
166605
167348
|
logical_types.push_back(expr->return_type);
|
|
@@ -166723,13 +167466,13 @@ void LocalTableStorage::Clear() {
|
|
|
166723
167466
|
table.info->indexes.Scan([&](Index &index) {
|
|
166724
167467
|
D_ASSERT(index.type == IndexType::ART);
|
|
166725
167468
|
auto &art = (ART &)index;
|
|
166726
|
-
if (art.
|
|
167469
|
+
if (art.constraint_type != IndexConstraintType::NONE) {
|
|
166727
167470
|
// unique index: create a local ART index that maintains the same unique constraint
|
|
166728
167471
|
vector<unique_ptr<Expression>> unbound_expressions;
|
|
166729
167472
|
for (auto &expr : art.unbound_expressions) {
|
|
166730
167473
|
unbound_expressions.push_back(expr->Copy());
|
|
166731
167474
|
}
|
|
166732
|
-
indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions),
|
|
167475
|
+
indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions), art.constraint_type));
|
|
166733
167476
|
}
|
|
166734
167477
|
return false;
|
|
166735
167478
|
});
|
|
@@ -167123,6 +167866,30 @@ void LocalStorage::ChangeType(DataTable *old_dt, DataTable *new_dt, idx_t change
|
|
|
167123
167866
|
throw NotImplementedException("FIXME: ALTER TYPE with transaction local data not currently supported");
|
|
167124
167867
|
}
|
|
167125
167868
|
|
|
167869
|
+
void LocalStorage::FetchChunk(DataTable *table, Vector &row_ids, idx_t count, DataChunk &dst_chunk) {
|
|
167870
|
+
auto storage = GetStorage(table);
|
|
167871
|
+
idx_t chunk_idx = GetChunk(row_ids);
|
|
167872
|
+
auto &chunk = storage->collection.GetChunk(chunk_idx);
|
|
167873
|
+
|
|
167874
|
+
VectorData row_ids_data;
|
|
167875
|
+
row_ids.Orrify(count, row_ids_data);
|
|
167876
|
+
auto row_identifiers = (const row_t *)row_ids_data.data;
|
|
167877
|
+
SelectionVector sel(count);
|
|
167878
|
+
for (idx_t i = 0; i < count; ++i) {
|
|
167879
|
+
const auto idx = row_ids_data.sel->get_index(i);
|
|
167880
|
+
sel.set_index(i, row_identifiers[idx] - MAX_ROW_ID);
|
|
167881
|
+
}
|
|
167882
|
+
|
|
167883
|
+
dst_chunk.InitializeEmpty(chunk.GetTypes());
|
|
167884
|
+
dst_chunk.Slice(chunk, sel, count);
|
|
167885
|
+
}
|
|
167886
|
+
|
|
167887
|
+
vector<unique_ptr<Index>> &LocalStorage::GetIndexes(DataTable *table) {
|
|
167888
|
+
auto storage = GetStorage(table);
|
|
167889
|
+
|
|
167890
|
+
return storage->indexes;
|
|
167891
|
+
}
|
|
167892
|
+
|
|
167126
167893
|
} // namespace duckdb
|
|
167127
167894
|
|
|
167128
167895
|
|