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/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
- return AddEntry(context, move(table), info->Base().on_conflict, info->dependencies);
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
- bool is_unique = false, bool is_primary = false);
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
- // fetch types and create expressions for the index from the columns
2705
- vector<column_t> column_ids;
2706
- vector<unique_ptr<Expression>> unbound_expressions;
2707
- vector<unique_ptr<Expression>> bound_expressions;
2708
- idx_t key_nr = 0;
2709
- for (auto &key : unique.keys) {
2710
- D_ASSERT(key < columns.size());
2711
-
2712
- unbound_expressions.push_back(make_unique<BoundColumnRefExpression>(
2713
- columns[key].name, columns[key].type, ColumnBinding(0, column_ids.size())));
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, bool is_unique,
48497
- bool is_primary)
48498
- : Index(IndexType::ART, column_ids, unbound_expressions, is_unique, is_primary) {
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
- if (!is_unique) {
48753
- return;
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
- // generate the keys for the given input
48765
- vector<unique_ptr<Key>> keys;
48766
- GenerateKeys(expression_result, keys);
49015
+ void ART::VerifyAppendForeignKey(DataChunk &chunk, string *err_msg_ptr) {
49016
+ VerifyExistence(chunk, VerifyExistenceType::APPEND_FK, err_msg_ptr);
49017
+ }
48767
49018
 
48768
- for (idx_t i = 0; i < chunk.size(); i++) {
48769
- if (!keys[i]) {
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 (is_unique && leaf.num_elements != 0) {
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->Scan(context.client, [&](CatalogEntry *entry) {
62184
- auto schema = (SchemaCatalogEntry *)entry;
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 == CatalogType::TABLE_ENTRY) {
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 (auto const &kv : exported_tables.data) {
62223
- auto table = kv.first;
62224
- auto exported_table_info = kv.second;
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, info->unique);
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->is_unique));
98748
+ output.SetValue(6, count, Value::BOOLEAN(index.index->IsUnique()));
98439
98749
  // is_primary, BOOLEAN
98440
- output.SetValue(7, count, Value::BOOLEAN(index.index->is_primary));
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
- exported_tables.data[table] = exported_data;
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
- void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, DataChunk &chunk) {
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, bool is_unique, bool is_primary)
166602
- : type(type), column_ids(column_ids_p), is_unique(is_unique), is_primary(is_primary) {
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.is_unique) {
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), true));
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