duckdb 0.3.3-dev948.0 → 0.3.3-dev993.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
  }
@@ -13410,7 +13670,7 @@ struct LengthFun {
13410
13670
 
13411
13671
  struct LikeFun {
13412
13672
  static void RegisterFunction(BuiltinFunctions &set);
13413
- static bool Glob(const char *s, idx_t slen, const char *pattern, idx_t plen);
13673
+ DUCKDB_API static bool Glob(const char *s, idx_t slen, const char *pattern, idx_t plen);
13414
13674
  };
13415
13675
 
13416
13676
  struct LikeEscapeFun {
@@ -13734,10 +13994,14 @@ void FileSystem::FileSync(FileHandle &handle) {
13734
13994
  throw NotImplementedException("%s: FileSync is not implemented!", GetName());
13735
13995
  }
13736
13996
 
13737
- vector<string> FileSystem::Glob(const string &path) {
13997
+ vector<string> FileSystem::Glob(const string &path, FileOpener *opener) {
13738
13998
  throw NotImplementedException("%s: Glob is not implemented!", GetName());
13739
13999
  }
13740
14000
 
14001
+ vector<string> FileSystem::Glob(const string &path, ClientContext &context) {
14002
+ return Glob(path, GetFileOpener(context));
14003
+ }
14004
+
13741
14005
  void FileSystem::RegisterSubSystem(unique_ptr<FileSystem> sub_fs) {
13742
14006
  throw NotImplementedException("%s: Can't register a sub system on a non-virtual file system", GetName());
13743
14007
  }
@@ -15850,7 +16114,7 @@ public:
15850
16114
  void FileSync(FileHandle &handle) override;
15851
16115
 
15852
16116
  //! Runs a glob on the file system, returning a list of matching files
15853
- vector<string> Glob(const string &path) override;
16117
+ vector<string> Glob(const string &path, FileOpener *opener = nullptr) override;
15854
16118
 
15855
16119
  bool CanHandleFile(const string &fpath) override {
15856
16120
  //! Whether or not a sub-system can handle a specific file path
@@ -16774,7 +17038,7 @@ static void GlobFiles(FileSystem &fs, const string &path, const string &glob, bo
16774
17038
  });
16775
17039
  }
16776
17040
 
16777
- vector<string> LocalFileSystem::Glob(const string &path) {
17041
+ vector<string> LocalFileSystem::Glob(const string &path, FileOpener *opener) {
16778
17042
  if (path.empty()) {
16779
17043
  return vector<string>();
16780
17044
  }
@@ -45790,8 +46054,8 @@ public:
45790
46054
  FindFileSystem(filename)->RemoveFile(filename);
45791
46055
  }
45792
46056
 
45793
- vector<string> Glob(const string &path) override {
45794
- return FindFileSystem(path)->Glob(path);
46057
+ virtual vector<string> Glob(const string &path, FileOpener *opener = nullptr) override {
46058
+ return FindFileSystem(path)->Glob(path, opener);
45795
46059
  }
45796
46060
 
45797
46061
  void RegisterSubSystem(unique_ptr<FileSystem> fs) override {
@@ -48493,9 +48757,9 @@ ExpressionExecutorState::ExpressionExecutorState(const string &name) : profiler(
48493
48757
 
48494
48758
  namespace duckdb {
48495
48759
 
48496
- ART::ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions, bool is_unique,
48497
- bool is_primary)
48498
- : Index(IndexType::ART, column_ids, unbound_expressions, is_unique, is_primary) {
48760
+ ART::ART(const vector<column_t> &column_ids, const vector<unique_ptr<Expression>> &unbound_expressions,
48761
+ IndexConstraintType constraint_type)
48762
+ : Index(IndexType::ART, column_ids, unbound_expressions, constraint_type) {
48499
48763
  tree = nullptr;
48500
48764
  expression_result.Initialize(logical_types);
48501
48765
  is_little_endian = IsLittleEndian();
@@ -48749,43 +49013,19 @@ bool ART::Append(IndexLock &lock, DataChunk &appended_data, Vector &row_identifi
48749
49013
  }
48750
49014
 
48751
49015
  void ART::VerifyAppend(DataChunk &chunk) {
48752
- 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);
49016
+ VerifyExistence(chunk, VerifyExistenceType::APPEND);
49017
+ }
48763
49018
 
48764
- // generate the keys for the given input
48765
- vector<unique_ptr<Key>> keys;
48766
- GenerateKeys(expression_result, keys);
49019
+ void ART::VerifyAppendForeignKey(DataChunk &chunk, string *err_msg_ptr) {
49020
+ VerifyExistence(chunk, VerifyExistenceType::APPEND_FK, err_msg_ptr);
49021
+ }
48767
49022
 
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
- }
49023
+ void ART::VerifyDeleteForeignKey(DataChunk &chunk, string *err_msg_ptr) {
49024
+ VerifyExistence(chunk, VerifyExistenceType::DELETE_FK, err_msg_ptr);
48785
49025
  }
48786
49026
 
48787
49027
  bool ART::InsertToLeaf(Leaf &leaf, row_t row_id) {
48788
- if (is_unique && leaf.num_elements != 0) {
49028
+ if (IsUnique() && leaf.num_elements != 0) {
48789
49029
  return false;
48790
49030
  }
48791
49031
  leaf.Insert(row_id);
@@ -49360,6 +49600,70 @@ bool ART::Scan(Transaction &transaction, DataTable &table, IndexScanState &table
49360
49600
  return true;
49361
49601
  }
49362
49602
 
49603
+ void ART::VerifyExistence(DataChunk &chunk, VerifyExistenceType verify_type, string *err_msg_ptr) {
49604
+ if (verify_type != VerifyExistenceType::DELETE_FK && !IsUnique()) {
49605
+ return;
49606
+ }
49607
+
49608
+ DataChunk expression_result;
49609
+ expression_result.Initialize(logical_types);
49610
+
49611
+ // unique index, check
49612
+ lock_guard<mutex> l(lock);
49613
+ // first resolve the expressions for the index
49614
+ ExecuteExpressions(chunk, expression_result);
49615
+
49616
+ // generate the keys for the given input
49617
+ vector<unique_ptr<Key>> keys;
49618
+ GenerateKeys(expression_result, keys);
49619
+
49620
+ for (idx_t i = 0; i < chunk.size(); i++) {
49621
+ if (!keys[i]) {
49622
+ continue;
49623
+ }
49624
+ Node *node_ptr = Lookup(tree, *keys[i], 0);
49625
+ bool throw_exception =
49626
+ verify_type == VerifyExistenceType::APPEND_FK ? node_ptr == nullptr : node_ptr != nullptr;
49627
+ if (!throw_exception) {
49628
+ continue;
49629
+ }
49630
+ string key_name;
49631
+ for (idx_t k = 0; k < expression_result.ColumnCount(); k++) {
49632
+ if (k > 0) {
49633
+ key_name += ", ";
49634
+ }
49635
+ key_name += unbound_expressions[k]->GetName() + ": " + expression_result.data[k].GetValue(i).ToString();
49636
+ }
49637
+ string exception_msg;
49638
+ switch (verify_type) {
49639
+ case VerifyExistenceType::APPEND: {
49640
+ // node already exists in tree
49641
+ string type = IsPrimary() ? "primary key" : "unique";
49642
+ exception_msg = "duplicate key \"" + key_name + "\" violates ";
49643
+ exception_msg += type + " constraint";
49644
+ break;
49645
+ }
49646
+ case VerifyExistenceType::APPEND_FK: {
49647
+ // found node no exists in tree
49648
+ exception_msg =
49649
+ "violates foreign key constraint because key \"" + key_name + "\" no exist in referenced table";
49650
+ break;
49651
+ }
49652
+ case VerifyExistenceType::DELETE_FK: {
49653
+ // found node exists in tree
49654
+ exception_msg =
49655
+ "violates foreign key constraint because key \"" + key_name + "\" exist in table has foreign key";
49656
+ break;
49657
+ }
49658
+ }
49659
+ if (err_msg_ptr) {
49660
+ err_msg_ptr[i] = exception_msg;
49661
+ } else {
49662
+ throw ConstraintException(exception_msg);
49663
+ }
49664
+ }
49665
+ }
49666
+
49363
49667
  } // namespace duckdb
49364
49668
 
49365
49669
 
@@ -62180,19 +62484,16 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
62180
62484
  vector<CatalogEntry *> views;
62181
62485
  vector<CatalogEntry *> indexes;
62182
62486
 
62183
- Catalog::GetCatalog(ccontext).schemas->Scan(context.client, [&](CatalogEntry *entry) {
62184
- auto schema = (SchemaCatalogEntry *)entry;
62487
+ auto schema_list = Catalog::GetCatalog(ccontext).schemas->GetEntries<SchemaCatalogEntry>(context.client);
62488
+ for (auto &schema : schema_list) {
62185
62489
  if (!schema->internal) {
62186
- // export schema
62187
62490
  schemas.push_back(schema);
62188
62491
  }
62189
62492
  schema->Scan(context.client, CatalogType::TABLE_ENTRY, [&](CatalogEntry *entry) {
62190
62493
  if (entry->internal) {
62191
62494
  return;
62192
62495
  }
62193
- if (entry->type == CatalogType::TABLE_ENTRY) {
62194
- tables.push_back(entry);
62195
- } else {
62496
+ if (entry->type != CatalogType::TABLE_ENTRY) {
62196
62497
  views.push_back(entry);
62197
62498
  }
62198
62499
  });
@@ -62201,7 +62502,12 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
62201
62502
  schema->Scan(context.client, CatalogType::TYPE_ENTRY,
62202
62503
  [&](CatalogEntry *entry) { custom_types.push_back(entry); });
62203
62504
  schema->Scan(context.client, CatalogType::INDEX_ENTRY, [&](CatalogEntry *entry) { indexes.push_back(entry); });
62204
- });
62505
+ }
62506
+
62507
+ // consider the order of tables because of foreign key constraint
62508
+ for (idx_t i = 0; i < exported_tables.data.size(); i++) {
62509
+ tables.push_back((CatalogEntry *)exported_tables.data[i].entry);
62510
+ }
62205
62511
 
62206
62512
  // write the schema.sql file
62207
62513
  // export order is SCHEMA -> SEQUENCE -> TABLE -> VIEW -> INDEX
@@ -62219,9 +62525,9 @@ void PhysicalExport::GetData(ExecutionContext &context, DataChunk &chunk, Global
62219
62525
  // write the load.sql file
62220
62526
  // for every table, we write COPY INTO statement with the specified options
62221
62527
  stringstream load_ss;
62222
- for (auto const &kv : exported_tables.data) {
62223
- auto table = kv.first;
62224
- auto exported_table_info = kv.second;
62528
+ for (idx_t i = 0; i < exported_tables.data.size(); i++) {
62529
+ auto &table = exported_tables.data[i].entry;
62530
+ auto exported_table_info = exported_tables.data[i].table_data;
62225
62531
  WriteCopyStatement(fs, load_ss, table, *info, exported_table_info, function);
62226
62532
  }
62227
62533
  WriteStringStreamToFile(fs, opener, load_ss, fs.JoinPath(info->file_path, "load.sql"));
@@ -63749,7 +64055,8 @@ void PhysicalCreateIndex::GetData(ExecutionContext &context, DataChunk &chunk, G
63749
64055
  unique_ptr<Index> index;
63750
64056
  switch (info->index_type) {
63751
64057
  case IndexType::ART: {
63752
- index = make_unique<ART>(column_ids, unbound_expressions, info->unique);
64058
+ index = make_unique<ART>(column_ids, unbound_expressions,
64059
+ info->unique ? IndexConstraintType::UNIQUE : IndexConstraintType::NONE);
63753
64060
  break;
63754
64061
  }
63755
64062
  default:
@@ -95778,7 +96085,7 @@ static unique_ptr<FunctionData> ReadCSVBind(ClientContext &context, CopyInfo &in
95778
96085
  string file_pattern = info.file_path;
95779
96086
 
95780
96087
  auto &fs = FileSystem::GetFileSystem(context);
95781
- bind_data->files = fs.Glob(file_pattern);
96088
+ bind_data->files = fs.Glob(file_pattern, context);
95782
96089
  if (bind_data->files.empty()) {
95783
96090
  throw IOException("No files found that match the pattern \"%s\"", file_pattern);
95784
96091
  }
@@ -96159,7 +96466,7 @@ static unique_ptr<FunctionData> GlobFunctionBind(ClientContext &context, vector<
96159
96466
  }
96160
96467
  auto result = make_unique<GlobFunctionBindData>();
96161
96468
  auto &fs = FileSystem::GetFileSystem(context);
96162
- result->files = fs.Glob(StringValue::Get(inputs[0]));
96469
+ result->files = fs.Glob(StringValue::Get(inputs[0]), context);
96163
96470
  return_types.emplace_back(LogicalType::VARCHAR);
96164
96471
  names.emplace_back("file");
96165
96472
  return move(result);
@@ -96882,7 +97189,7 @@ static unique_ptr<FunctionData> ReadCSVBind(ClientContext &context, vector<Value
96882
97189
  auto &file_pattern = StringValue::Get(inputs[0]);
96883
97190
 
96884
97191
  auto &fs = FileSystem::GetFileSystem(context);
96885
- result->files = fs.Glob(file_pattern);
97192
+ result->files = fs.Glob(file_pattern, context);
96886
97193
  if (result->files.empty()) {
96887
97194
  throw IOException("No files found that match the pattern \"%s\"", file_pattern);
96888
97195
  }
@@ -97600,6 +97907,7 @@ void DuckDBColumnsFun::RegisterFunction(BuiltinFunctions &set) {
97600
97907
 
97601
97908
 
97602
97909
 
97910
+
97603
97911
  namespace duckdb {
97604
97912
 
97605
97913
  struct DuckDBConstraintsData : public FunctionOperatorData {
@@ -97758,7 +98066,13 @@ void DuckDBConstraintsFunction(ClientContext &context, const FunctionData *bind_
97758
98066
  column_index_list.push_back(bound_not_null.index);
97759
98067
  break;
97760
98068
  }
97761
- case ConstraintType::FOREIGN_KEY:
98069
+ case ConstraintType::FOREIGN_KEY: {
98070
+ auto &bound_foreign_key = (BoundForeignKeyConstraint &)bound_constraint;
98071
+ for (auto &col_idx : bound_foreign_key.info.fk_keys) {
98072
+ column_index_list.push_back(column_t(col_idx));
98073
+ }
98074
+ break;
98075
+ }
97762
98076
  default:
97763
98077
  throw NotImplementedException("Unimplemented constraint for duckdb_constraints");
97764
98078
  }
@@ -98435,9 +98749,9 @@ void DuckDBIndexesFunction(ClientContext &context, const FunctionData *bind_data
98435
98749
  auto table_entry = catalog.GetEntry(context, CatalogType::TABLE_ENTRY, index.info->schema, index.info->table);
98436
98750
  output.SetValue(5, count, Value::BIGINT(table_entry->oid));
98437
98751
  // is_unique, BOOLEAN
98438
- output.SetValue(6, count, Value::BOOLEAN(index.index->is_unique));
98752
+ output.SetValue(6, count, Value::BOOLEAN(index.index->IsUnique()));
98439
98753
  // is_primary, BOOLEAN
98440
- output.SetValue(7, count, Value::BOOLEAN(index.index->is_primary));
98754
+ output.SetValue(7, count, Value::BOOLEAN(index.index->IsPrimary()));
98441
98755
  // expressions, VARCHAR
98442
98756
  output.SetValue(8, count, Value());
98443
98757
  // sql, VARCHAR
@@ -133299,6 +133613,9 @@ unique_ptr<Constraint> Constraint::Deserialize(Deserializer &source) {
133299
133613
  case ConstraintType::UNIQUE:
133300
133614
  result = UniqueConstraint::Deserialize(reader);
133301
133615
  break;
133616
+ case ConstraintType::FOREIGN_KEY:
133617
+ result = ForeignKeyConstraint::Deserialize(reader);
133618
+ break;
133302
133619
  default:
133303
133620
  throw InternalException("Unrecognized constraint type for serialization");
133304
133621
  }
@@ -133343,6 +133660,82 @@ unique_ptr<Constraint> CheckConstraint::Deserialize(FieldReader &source) {
133343
133660
 
133344
133661
 
133345
133662
 
133663
+
133664
+
133665
+ namespace duckdb {
133666
+
133667
+ ForeignKeyConstraint::ForeignKeyConstraint(vector<string> pk_columns, vector<string> fk_columns, ForeignKeyInfo info)
133668
+ : Constraint(ConstraintType::FOREIGN_KEY), pk_columns(move(pk_columns)), fk_columns(move(fk_columns)),
133669
+ info(move(info)) {
133670
+ }
133671
+
133672
+ string ForeignKeyConstraint::ToString() const {
133673
+ if (info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
133674
+ string base = "FOREIGN KEY (";
133675
+
133676
+ for (idx_t i = 0; i < fk_columns.size(); i++) {
133677
+ if (i > 0) {
133678
+ base += ", ";
133679
+ }
133680
+ base += KeywordHelper::WriteOptionallyQuoted(fk_columns[i]);
133681
+ }
133682
+ base += ") REFERENCES ";
133683
+ if (!info.schema.empty()) {
133684
+ base += info.schema;
133685
+ base += ".";
133686
+ }
133687
+ base += info.table;
133688
+ base += "(";
133689
+
133690
+ for (idx_t i = 0; i < pk_columns.size(); i++) {
133691
+ if (i > 0) {
133692
+ base += ", ";
133693
+ }
133694
+ base += KeywordHelper::WriteOptionallyQuoted(pk_columns[i]);
133695
+ }
133696
+ base += ")";
133697
+
133698
+ return base;
133699
+ }
133700
+
133701
+ return "";
133702
+ }
133703
+
133704
+ unique_ptr<Constraint> ForeignKeyConstraint::Copy() const {
133705
+ return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, info);
133706
+ }
133707
+
133708
+ void ForeignKeyConstraint::Serialize(FieldWriter &writer) const {
133709
+ D_ASSERT(pk_columns.size() <= NumericLimits<uint32_t>::Maximum());
133710
+ writer.WriteList<string>(pk_columns);
133711
+ D_ASSERT(fk_columns.size() <= NumericLimits<uint32_t>::Maximum());
133712
+ writer.WriteList<string>(fk_columns);
133713
+ writer.WriteField<ForeignKeyType>(info.type);
133714
+ writer.WriteString(info.schema);
133715
+ writer.WriteString(info.table);
133716
+ writer.WriteList<idx_t>(info.pk_keys);
133717
+ writer.WriteList<idx_t>(info.fk_keys);
133718
+ }
133719
+
133720
+ unique_ptr<Constraint> ForeignKeyConstraint::Deserialize(FieldReader &source) {
133721
+ ForeignKeyInfo read_info;
133722
+ auto pk_columns = source.ReadRequiredList<string>();
133723
+ auto fk_columns = source.ReadRequiredList<string>();
133724
+ read_info.type = source.ReadRequired<ForeignKeyType>();
133725
+ read_info.schema = source.ReadRequired<string>();
133726
+ read_info.table = source.ReadRequired<string>();
133727
+ read_info.pk_keys = source.ReadRequiredList<idx_t>();
133728
+ read_info.fk_keys = source.ReadRequiredList<idx_t>();
133729
+
133730
+ // column list parsed constraint
133731
+ return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(read_info));
133732
+ }
133733
+
133734
+ } // namespace duckdb
133735
+
133736
+
133737
+
133738
+
133346
133739
  namespace duckdb {
133347
133740
 
133348
133741
  NotNullConstraint::NotNullConstraint(column_t index) : Constraint(ConstraintType::NOT_NULL), index(index) {
@@ -134755,6 +135148,8 @@ unique_ptr<AlterInfo> AlterTableInfo::Deserialize(FieldReader &reader) {
134755
135148
  return ChangeColumnTypeInfo::Deserialize(reader, schema, table);
134756
135149
  case AlterTableType::SET_DEFAULT:
134757
135150
  return SetDefaultInfo::Deserialize(reader, schema, table);
135151
+ case AlterTableType::FOREIGN_KEY_CONSTRAINT:
135152
+ return AlterForeignKeyInfo::Deserialize(reader, schema, table);
134758
135153
  default:
134759
135154
  throw SerializationException("Unknown alter table type for deserialization!");
134760
135155
  }
@@ -134911,6 +135306,44 @@ unique_ptr<AlterInfo> SetDefaultInfo::Deserialize(FieldReader &reader, string sc
134911
135306
  return make_unique<SetDefaultInfo>(move(schema), move(table), move(column_name), move(new_default));
134912
135307
  }
134913
135308
 
135309
+ //===--------------------------------------------------------------------===//
135310
+ // AlterForeignKeyInfo
135311
+ //===--------------------------------------------------------------------===//
135312
+ AlterForeignKeyInfo::AlterForeignKeyInfo(string schema_p, string table_p, string fk_table, vector<string> pk_columns,
135313
+ vector<string> fk_columns, vector<idx_t> pk_keys, vector<idx_t> fk_keys,
135314
+ AlterForeignKeyType type_p)
135315
+ : AlterTableInfo(AlterTableType::FOREIGN_KEY_CONSTRAINT, move(schema_p), move(table_p)), fk_table(move(fk_table)),
135316
+ pk_columns(move(pk_columns)), fk_columns(move(fk_columns)), pk_keys(move(pk_keys)), fk_keys(move(fk_keys)),
135317
+ type(type_p) {
135318
+ }
135319
+ AlterForeignKeyInfo::~AlterForeignKeyInfo() {
135320
+ }
135321
+
135322
+ unique_ptr<AlterInfo> AlterForeignKeyInfo::Copy() const {
135323
+ return make_unique_base<AlterInfo, AlterForeignKeyInfo>(schema, name, fk_table, pk_columns, fk_columns, pk_keys,
135324
+ fk_keys, type);
135325
+ }
135326
+
135327
+ void AlterForeignKeyInfo::SerializeAlterTable(FieldWriter &writer) const {
135328
+ writer.WriteString(fk_table);
135329
+ writer.WriteList<string>(pk_columns);
135330
+ writer.WriteList<string>(fk_columns);
135331
+ writer.WriteList<idx_t>(pk_keys);
135332
+ writer.WriteList<idx_t>(fk_keys);
135333
+ writer.WriteField<AlterForeignKeyType>(type);
135334
+ }
135335
+
135336
+ unique_ptr<AlterInfo> AlterForeignKeyInfo::Deserialize(FieldReader &reader, string schema, string table) {
135337
+ auto fk_table = reader.ReadRequired<string>();
135338
+ auto pk_columns = reader.ReadRequiredList<string>();
135339
+ auto fk_columns = reader.ReadRequiredList<string>();
135340
+ auto pk_keys = reader.ReadRequiredList<idx_t>();
135341
+ auto fk_keys = reader.ReadRequiredList<idx_t>();
135342
+ auto type = reader.ReadRequired<AlterForeignKeyType>();
135343
+ return make_unique<AlterForeignKeyInfo>(move(schema), move(table), move(fk_table), move(pk_columns),
135344
+ move(fk_columns), move(pk_keys), move(fk_keys), type);
135345
+ }
135346
+
134914
135347
  //===--------------------------------------------------------------------===//
134915
135348
  // Alter View
134916
135349
  //===--------------------------------------------------------------------===//
@@ -142779,6 +143212,30 @@ unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGLis
142779
143212
  }
142780
143213
  return make_unique<CheckConstraint>(TransformExpression(constraint->raw_expr));
142781
143214
  }
143215
+ case duckdb_libpgquery::PG_CONSTR_FOREIGN: {
143216
+ ForeignKeyInfo fk_info;
143217
+ fk_info.type = ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE;
143218
+ if (constraint->pktable->schemaname) {
143219
+ fk_info.schema = constraint->pktable->schemaname;
143220
+ } else {
143221
+ fk_info.schema = "";
143222
+ }
143223
+ fk_info.table = constraint->pktable->relname;
143224
+ vector<string> pk_columns, fk_columns;
143225
+ for (auto kc = constraint->fk_attrs->head; kc; kc = kc->next) {
143226
+ fk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
143227
+ }
143228
+ for (auto kc = constraint->pk_attrs->head; kc; kc = kc->next) {
143229
+ pk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
143230
+ }
143231
+ if (pk_columns.size() != fk_columns.size()) {
143232
+ throw ParserException("The number of referencing and referenced columns for foreign keys must be the same");
143233
+ }
143234
+ if (fk_columns.empty()) {
143235
+ throw ParserException("The set of referencing and referenced columns for foreign keys must be not empty");
143236
+ }
143237
+ return make_unique<ForeignKeyConstraint>(pk_columns, fk_columns, move(fk_info));
143238
+ }
142782
143239
  default:
142783
143240
  throw NotImplementedException("Constraint type not handled yet!");
142784
143241
  }
@@ -151630,6 +152087,7 @@ public:
151630
152087
  } // namespace duckdb
151631
152088
 
151632
152089
 
152090
+
151633
152091
  namespace duckdb {
151634
152092
 
151635
152093
  SchemaCatalogEntry *Binder::BindSchema(CreateInfo &info) {
@@ -151816,6 +152274,34 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
151816
152274
  break;
151817
152275
  }
151818
152276
  case CatalogType::TABLE_ENTRY: {
152277
+ // If there is a foreign key constraint, resolve primary key column's index from primary key column's name
152278
+ auto &create_info = (CreateTableInfo &)*stmt.info;
152279
+ auto &catalog = Catalog::GetCatalog(context);
152280
+ for (idx_t i = 0; i < create_info.constraints.size(); i++) {
152281
+ auto &cond = create_info.constraints[i];
152282
+ if (cond->type != ConstraintType::FOREIGN_KEY) {
152283
+ continue;
152284
+ }
152285
+ auto &fk = (ForeignKeyConstraint &)*cond;
152286
+ if (fk.info.type != ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) {
152287
+ continue;
152288
+ }
152289
+ D_ASSERT(fk.info.pk_keys.empty() && !fk.pk_columns.empty());
152290
+ if (create_info.table == fk.info.table) {
152291
+ fk.info.type = ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE;
152292
+ } else {
152293
+ // have to resolve referenced table
152294
+ auto pk_table_entry_ptr = catalog.GetEntry<TableCatalogEntry>(context, fk.info.schema, fk.info.table);
152295
+ D_ASSERT(!fk.pk_columns.empty() && fk.info.pk_keys.empty());
152296
+ for (auto &keyname : fk.pk_columns) {
152297
+ auto entry = pk_table_entry_ptr->name_map.find(keyname);
152298
+ if (entry == pk_table_entry_ptr->name_map.end()) {
152299
+ throw ParserException("column \"%s\" named in key does not exist", keyname);
152300
+ }
152301
+ fk.info.pk_keys.push_back(entry->second);
152302
+ }
152303
+ }
152304
+ }
151819
152305
  // We first check if there are any user types, if yes we check to which custom types they refer.
151820
152306
  auto bound_info = BindCreateTableInfo(move(stmt.info));
151821
152307
  auto root = move(bound_info->query);
@@ -151850,6 +152336,7 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
151850
152336
 
151851
152337
 
151852
152338
 
152339
+
151853
152340
  //===----------------------------------------------------------------------===//
151854
152341
  // DuckDB
151855
152342
  //
@@ -151986,6 +152473,39 @@ static void BindConstraints(Binder &binder, BoundCreateTableInfo &info) {
151986
152473
  make_unique<BoundUniqueConstraint>(move(keys), move(key_set), unique.is_primary_key));
151987
152474
  break;
151988
152475
  }
152476
+ case ConstraintType::FOREIGN_KEY: {
152477
+ auto &fk = (ForeignKeyConstraint &)*cond;
152478
+ D_ASSERT((fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE && !fk.info.pk_keys.empty()) ||
152479
+ (fk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE && !fk.info.pk_keys.empty()) ||
152480
+ fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE);
152481
+ if (fk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE && fk.info.pk_keys.empty()) {
152482
+ for (auto &keyname : fk.pk_columns) {
152483
+ auto entry = info.name_map.find(keyname);
152484
+ if (entry == info.name_map.end()) {
152485
+ throw ParserException("column \"%s\" named in key does not exist", keyname);
152486
+ }
152487
+ fk.info.pk_keys.push_back(entry->second);
152488
+ }
152489
+ }
152490
+ if (fk.info.fk_keys.empty()) {
152491
+ for (auto &keyname : fk.fk_columns) {
152492
+ auto entry = info.name_map.find(keyname);
152493
+ if (entry == info.name_map.end()) {
152494
+ throw ParserException("column \"%s\" named in key does not exist", keyname);
152495
+ }
152496
+ fk.info.fk_keys.push_back(entry->second);
152497
+ }
152498
+ }
152499
+ unordered_set<idx_t> fk_key_set, pk_key_set;
152500
+ for (idx_t i = 0; i < fk.info.pk_keys.size(); i++) {
152501
+ pk_key_set.insert(fk.info.pk_keys[i]);
152502
+ }
152503
+ for (idx_t i = 0; i < fk.info.fk_keys.size(); i++) {
152504
+ fk_key_set.insert(fk.info.fk_keys[i]);
152505
+ }
152506
+ info.bound_constraints.push_back(make_unique<BoundForeignKeyConstraint>(fk.info, pk_key_set, fk_key_set));
152507
+ break;
152508
+ }
151989
152509
  default:
151990
152510
  throw NotImplementedException("unrecognized constraint type in bind");
151991
152511
  }
@@ -152234,6 +152754,7 @@ BoundStatement Binder::Bind(ExplainStatement &stmt) {
152234
152754
 
152235
152755
 
152236
152756
 
152757
+
152237
152758
  #include <algorithm>
152238
152759
 
152239
152760
  namespace duckdb {
@@ -152262,6 +152783,51 @@ string SanitizeExportIdentifier(const string &str) {
152262
152783
  return result;
152263
152784
  }
152264
152785
 
152786
+ bool IsExistMainKeyTable(string &table_name, vector<TableCatalogEntry *> &unordered) {
152787
+ for (idx_t i = 0; i < unordered.size(); i++) {
152788
+ if (unordered[i]->name == table_name) {
152789
+ return true;
152790
+ }
152791
+ }
152792
+ return false;
152793
+ }
152794
+
152795
+ void ScanForeignKeyTable(vector<TableCatalogEntry *> &ordered, vector<TableCatalogEntry *> &unordered,
152796
+ bool move_only_pk_table) {
152797
+ for (auto i = unordered.begin(); i != unordered.end();) {
152798
+ auto table_entry = *i;
152799
+ bool move_to_ordered = true;
152800
+ for (idx_t j = 0; j < table_entry->constraints.size(); j++) {
152801
+ auto &cond = table_entry->constraints[j];
152802
+ if (cond->type == ConstraintType::FOREIGN_KEY) {
152803
+ auto &fk = (ForeignKeyConstraint &)*cond;
152804
+ if ((move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) ||
152805
+ (!move_only_pk_table && fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE &&
152806
+ IsExistMainKeyTable(fk.info.table, unordered))) {
152807
+ move_to_ordered = false;
152808
+ break;
152809
+ }
152810
+ }
152811
+ }
152812
+ if (move_to_ordered) {
152813
+ ordered.push_back(table_entry);
152814
+ i = unordered.erase(i);
152815
+ } else {
152816
+ i++;
152817
+ }
152818
+ }
152819
+ }
152820
+
152821
+ void ReorderTableEntries(vector<TableCatalogEntry *> &tables) {
152822
+ vector<TableCatalogEntry *> ordered;
152823
+ vector<TableCatalogEntry *> unordered = tables;
152824
+ ScanForeignKeyTable(ordered, unordered, true);
152825
+ while (!unordered.empty()) {
152826
+ ScanForeignKeyTable(ordered, unordered, false);
152827
+ }
152828
+ tables = ordered;
152829
+ }
152830
+
152265
152831
  BoundStatement Binder::Bind(ExportStatement &stmt) {
152266
152832
  // COPY TO a file
152267
152833
  auto &config = DBConfig::GetConfig(context);
@@ -152290,6 +152856,9 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
152290
152856
  });
152291
152857
  }
152292
152858
 
152859
+ // reorder tables because of foreign key constraint
152860
+ ReorderTableEntries(tables);
152861
+
152293
152862
  // now generate the COPY statements for each of the tables
152294
152863
  auto &fs = FileSystem::GetFileSystem(context);
152295
152864
  unique_ptr<LogicalOperator> child_operator;
@@ -152324,7 +152893,10 @@ BoundStatement Binder::Bind(ExportStatement &stmt) {
152324
152893
  exported_data.schema_name = info->schema;
152325
152894
  exported_data.file_path = info->file_path;
152326
152895
 
152327
- exported_tables.data[table] = exported_data;
152896
+ ExportedTableInfo table_info;
152897
+ table_info.entry = table;
152898
+ table_info.table_data = exported_data;
152899
+ exported_tables.data.push_back(table_info);
152328
152900
  id++;
152329
152901
 
152330
152902
  // generate the copy statement and bind it
@@ -160277,6 +160849,8 @@ void WriteOverflowStringsToDisk::AllocateNewBlock(block_id_t new_block_id) {
160277
160849
 
160278
160850
  namespace duckdb {
160279
160851
 
160852
+ void ReorderTableEntries(vector<TableCatalogEntry *> &tables);
160853
+
160280
160854
  CheckpointManager::CheckpointManager(DatabaseInstance &db) : db(db) {
160281
160855
  }
160282
160856
 
@@ -160421,6 +160995,8 @@ void CheckpointManager::WriteSchema(SchemaCatalogEntry &schema) {
160421
160995
  for (auto &seq : sequences) {
160422
160996
  WriteSequence(*seq);
160423
160997
  }
160998
+ // reorder tables because of foreign key constraint
160999
+ ReorderTableEntries(tables);
160424
161000
  // now write the tables
160425
161001
  metadata_writer->Write<uint32_t>(tables.size());
160426
161002
  for (auto &table : tables) {
@@ -165876,7 +166452,133 @@ static void VerifyCheckConstraint(TableCatalogEntry &table, Expression &expr, Da
165876
166452
  }
165877
166453
  }
165878
166454
 
165879
- void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, DataChunk &chunk) {
166455
+ static bool FindColumnIndex(const vector<idx_t> *keys_ptr, const vector<column_t> &index_column_ids) {
166456
+ for (idx_t i = 0; i < keys_ptr->size(); i++) {
166457
+ bool is_found = false;
166458
+ for (idx_t j = 0; j < index_column_ids.size(); j++) {
166459
+ if ((*keys_ptr)[i] == index_column_ids[j]) {
166460
+ is_found = true;
166461
+ break;
166462
+ }
166463
+ }
166464
+ if (!is_found) {
166465
+ return false;
166466
+ }
166467
+ }
166468
+
166469
+ return true;
166470
+ }
166471
+
166472
+ static void VerifyForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context, DataChunk &chunk,
166473
+ bool is_append) {
166474
+ const vector<idx_t> *src_keys_ptr = &bfk.info.fk_keys;
166475
+ const vector<idx_t> *dst_keys_ptr = &bfk.info.pk_keys;
166476
+ if (!is_append) {
166477
+ src_keys_ptr = &bfk.info.pk_keys;
166478
+ dst_keys_ptr = &bfk.info.fk_keys;
166479
+ }
166480
+
166481
+ auto table_entry_ptr =
166482
+ Catalog::GetCatalog(context).GetEntry<TableCatalogEntry>(context, bfk.info.schema, bfk.info.table);
166483
+ if (table_entry_ptr == nullptr) {
166484
+ throw InternalException("Can't find table \"%s\" in foreign key constraint", bfk.info.table);
166485
+ }
166486
+
166487
+ // make the data chunk to check
166488
+ vector<LogicalType> types;
166489
+ for (idx_t i = 0; i < table_entry_ptr->columns.size(); i++) {
166490
+ types.emplace_back(table_entry_ptr->columns[i].type);
166491
+ }
166492
+ DataChunk dst_chunk;
166493
+ dst_chunk.InitializeEmpty(types);
166494
+ for (idx_t i = 0; i < src_keys_ptr->size(); i++) {
166495
+ dst_chunk.data[(*dst_keys_ptr)[i]].Reference(chunk.data[(*src_keys_ptr)[i]]);
166496
+ }
166497
+ dst_chunk.SetCardinality(chunk.size());
166498
+ auto data_table = table_entry_ptr->storage.get();
166499
+
166500
+ idx_t count = dst_chunk.size();
166501
+ if (count <= 0) {
166502
+ return;
166503
+ }
166504
+
166505
+ // we need to look at the error messages concurrently in data table's index and transaction local storage's index
166506
+ vector<string> err_msgs, tran_err_msgs;
166507
+ err_msgs.resize(count);
166508
+ tran_err_msgs.resize(count);
166509
+
166510
+ // check whether or not the chunk can be inserted or deleted into the referenced table' storage
166511
+ TableIndexList &table_indices = data_table->info->indexes;
166512
+ table_indices.Scan([&](Index &index) {
166513
+ if (FindColumnIndex(dst_keys_ptr, index.column_ids)) {
166514
+ if (is_append) {
166515
+ index.VerifyAppendForeignKey(dst_chunk, err_msgs.data());
166516
+ } else {
166517
+ index.VerifyDeleteForeignKey(dst_chunk, err_msgs.data());
166518
+ }
166519
+ }
166520
+ return false;
166521
+ });
166522
+
166523
+ // check whether or not the chunk can be inserted or deleted into the referenced table' transaction local storage
166524
+ auto &transaction = Transaction::GetTransaction(context);
166525
+ bool transaction_check = transaction.storage.Find(data_table);
166526
+ if (transaction_check) {
166527
+ vector<unique_ptr<Index>> &transact_index_vec = transaction.storage.GetIndexes(data_table);
166528
+ for (idx_t i = 0; i < transact_index_vec.size(); i++) {
166529
+ if (FindColumnIndex(dst_keys_ptr, transact_index_vec[i]->column_ids)) {
166530
+ if (is_append) {
166531
+ transact_index_vec[i]->VerifyAppendForeignKey(dst_chunk, tran_err_msgs.data());
166532
+ } else {
166533
+ transact_index_vec[i]->VerifyDeleteForeignKey(dst_chunk, tran_err_msgs.data());
166534
+ }
166535
+ }
166536
+ }
166537
+ }
166538
+
166539
+ // we need to look at the error messages concurrently in data table's index and transaction local storage's index
166540
+ for (idx_t i = 0; i < count; i++) {
166541
+ if (!transaction_check) {
166542
+ // if there is no transaction-local data we only need to check if there is an error message in the main
166543
+ // index
166544
+ if (!err_msgs[i].empty()) {
166545
+ throw ConstraintException(err_msgs[i]);
166546
+ } else {
166547
+ continue;
166548
+ }
166549
+ }
166550
+ if (is_append) {
166551
+ // if we are appending we need to check to ensure the foreign key exists in either the transaction-local
166552
+ // storage or the main table
166553
+ if (!err_msgs[i].empty() && !tran_err_msgs[i].empty()) {
166554
+ throw ConstraintException(err_msgs[i]);
166555
+ } else {
166556
+ continue;
166557
+ }
166558
+ }
166559
+ // if we are deleting we need to ensure the foreign key DOES NOT exist in EITHER the transaction-local storage
166560
+ // OR the main table
166561
+ if (!err_msgs[i].empty() || !tran_err_msgs[i].empty()) {
166562
+ string &err_msg = err_msgs[i];
166563
+ if (err_msg.empty()) {
166564
+ err_msg = tran_err_msgs[i];
166565
+ }
166566
+ throw ConstraintException(err_msg);
166567
+ }
166568
+ }
166569
+ }
166570
+
166571
+ static void VerifyAppendForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
166572
+ DataChunk &chunk) {
166573
+ VerifyForeignKeyConstraint(bfk, context, chunk, true);
166574
+ }
166575
+
166576
+ static void VerifyDeleteForeignKeyConstraint(const BoundForeignKeyConstraint &bfk, ClientContext &context,
166577
+ DataChunk &chunk) {
166578
+ VerifyForeignKeyConstraint(bfk, context, chunk, false);
166579
+ }
166580
+
166581
+ void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
165880
166582
  for (auto &constraint : table.bound_constraints) {
165881
166583
  switch (constraint->type) {
165882
166584
  case ConstraintType::NOT_NULL: {
@@ -165898,7 +166600,14 @@ void DataTable::VerifyAppendConstraints(TableCatalogEntry &table, DataChunk &chu
165898
166600
  });
165899
166601
  break;
165900
166602
  }
165901
- case ConstraintType::FOREIGN_KEY:
166603
+ case ConstraintType::FOREIGN_KEY: {
166604
+ auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
166605
+ if (bfk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE ||
166606
+ bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
166607
+ VerifyAppendForeignKeyConstraint(bfk, context, chunk);
166608
+ }
166609
+ break;
166610
+ }
165902
166611
  default:
165903
166612
  throw NotImplementedException("Constraint type not implemented!");
165904
166613
  }
@@ -165919,7 +166628,7 @@ void DataTable::Append(TableCatalogEntry &table, ClientContext &context, DataChu
165919
166628
  chunk.Verify();
165920
166629
 
165921
166630
  // verify any constraints on the new chunk
165922
- VerifyAppendConstraints(table, chunk);
166631
+ VerifyAppendConstraints(table, context, chunk);
165923
166632
 
165924
166633
  // append to the transaction local data
165925
166634
  auto &transaction = Transaction::GetTransaction(context);
@@ -166211,6 +166920,27 @@ void DataTable::RemoveFromIndexes(Vector &row_identifiers, idx_t count) {
166211
166920
  });
166212
166921
  }
166213
166922
 
166923
+ void DataTable::VerifyDeleteConstraints(TableCatalogEntry &table, ClientContext &context, DataChunk &chunk) {
166924
+ for (auto &constraint : table.bound_constraints) {
166925
+ switch (constraint->type) {
166926
+ case ConstraintType::NOT_NULL:
166927
+ case ConstraintType::CHECK:
166928
+ case ConstraintType::UNIQUE:
166929
+ break;
166930
+ case ConstraintType::FOREIGN_KEY: {
166931
+ auto &bfk = *reinterpret_cast<BoundForeignKeyConstraint *>(constraint.get());
166932
+ if (bfk.info.type == ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE ||
166933
+ bfk.info.type == ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE) {
166934
+ VerifyDeleteForeignKeyConstraint(bfk, context, chunk);
166935
+ }
166936
+ break;
166937
+ }
166938
+ default:
166939
+ throw NotImplementedException("Constraint type not implemented!");
166940
+ }
166941
+ }
166942
+ }
166943
+
166214
166944
  //===--------------------------------------------------------------------===//
166215
166945
  // Delete
166216
166946
  //===--------------------------------------------------------------------===//
@@ -166226,6 +166956,23 @@ idx_t DataTable::Delete(TableCatalogEntry &table, ClientContext &context, Vector
166226
166956
  auto ids = FlatVector::GetData<row_t>(row_identifiers);
166227
166957
  auto first_id = ids[0];
166228
166958
 
166959
+ // verify any constraints on the delete rows
166960
+ DataChunk verify_chunk;
166961
+ if (first_id >= MAX_ROW_ID) {
166962
+ transaction.storage.FetchChunk(this, row_identifiers, count, verify_chunk);
166963
+ } else {
166964
+ ColumnFetchState fetch_state;
166965
+ vector<column_t> col_ids;
166966
+ vector<LogicalType> types;
166967
+ for (idx_t i = 0; i < column_definitions.size(); i++) {
166968
+ col_ids.push_back(column_definitions[i].oid);
166969
+ types.emplace_back(column_definitions[i].type);
166970
+ }
166971
+ verify_chunk.Initialize(types);
166972
+ Fetch(transaction, verify_chunk, col_ids, row_identifiers, count, fetch_state);
166973
+ }
166974
+ VerifyDeleteConstraints(table, context, verify_chunk);
166975
+
166229
166976
  if (first_id >= MAX_ROW_ID) {
166230
166977
  // deletion is in transaction-local storage: push delete into local chunk collection
166231
166978
  return transaction.storage.Delete(this, row_identifiers, count);
@@ -166598,8 +167345,8 @@ vector<vector<Value>> DataTable::GetStorageInfo() {
166598
167345
  namespace duckdb {
166599
167346
 
166600
167347
  Index::Index(IndexType type, const vector<column_t> &column_ids_p,
166601
- const vector<unique_ptr<Expression>> &unbound_expressions, bool is_unique, bool is_primary)
166602
- : type(type), column_ids(column_ids_p), is_unique(is_unique), is_primary(is_primary) {
167348
+ const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type_p)
167349
+ : type(type), column_ids(column_ids_p), constraint_type(constraint_type_p) {
166603
167350
  for (auto &expr : unbound_expressions) {
166604
167351
  types.push_back(expr->return_type.InternalType());
166605
167352
  logical_types.push_back(expr->return_type);
@@ -166723,13 +167470,13 @@ void LocalTableStorage::Clear() {
166723
167470
  table.info->indexes.Scan([&](Index &index) {
166724
167471
  D_ASSERT(index.type == IndexType::ART);
166725
167472
  auto &art = (ART &)index;
166726
- if (art.is_unique) {
167473
+ if (art.constraint_type != IndexConstraintType::NONE) {
166727
167474
  // unique index: create a local ART index that maintains the same unique constraint
166728
167475
  vector<unique_ptr<Expression>> unbound_expressions;
166729
167476
  for (auto &expr : art.unbound_expressions) {
166730
167477
  unbound_expressions.push_back(expr->Copy());
166731
167478
  }
166732
- indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions), true));
167479
+ indexes.push_back(make_unique<ART>(art.column_ids, move(unbound_expressions), art.constraint_type));
166733
167480
  }
166734
167481
  return false;
166735
167482
  });
@@ -167123,6 +167870,30 @@ void LocalStorage::ChangeType(DataTable *old_dt, DataTable *new_dt, idx_t change
167123
167870
  throw NotImplementedException("FIXME: ALTER TYPE with transaction local data not currently supported");
167124
167871
  }
167125
167872
 
167873
+ void LocalStorage::FetchChunk(DataTable *table, Vector &row_ids, idx_t count, DataChunk &dst_chunk) {
167874
+ auto storage = GetStorage(table);
167875
+ idx_t chunk_idx = GetChunk(row_ids);
167876
+ auto &chunk = storage->collection.GetChunk(chunk_idx);
167877
+
167878
+ VectorData row_ids_data;
167879
+ row_ids.Orrify(count, row_ids_data);
167880
+ auto row_identifiers = (const row_t *)row_ids_data.data;
167881
+ SelectionVector sel(count);
167882
+ for (idx_t i = 0; i < count; ++i) {
167883
+ const auto idx = row_ids_data.sel->get_index(i);
167884
+ sel.set_index(i, row_identifiers[idx] - MAX_ROW_ID);
167885
+ }
167886
+
167887
+ dst_chunk.InitializeEmpty(chunk.GetTypes());
167888
+ dst_chunk.Slice(chunk, sel, count);
167889
+ }
167890
+
167891
+ vector<unique_ptr<Index>> &LocalStorage::GetIndexes(DataTable *table) {
167892
+ auto storage = GetStorage(table);
167893
+
167894
+ return storage->indexes;
167895
+ }
167896
+
167126
167897
  } // namespace duckdb
167127
167898
 
167128
167899