duckdb 0.7.2-dev2552.0 → 0.7.2-dev2699.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.
Files changed (71) hide show
  1. package/binding.gyp +7 -7
  2. package/package.json +2 -2
  3. package/src/duckdb/extension/parquet/parquet_statistics.cpp +3 -0
  4. package/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +2 -2
  5. package/src/duckdb/src/common/adbc/adbc.cpp +5 -2
  6. package/src/duckdb/src/common/radix_partitioning.cpp +1 -1
  7. package/src/duckdb/src/execution/index/art/art.cpp +286 -269
  8. package/src/duckdb/src/execution/index/art/art_key.cpp +22 -32
  9. package/src/duckdb/src/execution/index/art/fixed_size_allocator.cpp +224 -0
  10. package/src/duckdb/src/execution/index/art/iterator.cpp +142 -123
  11. package/src/duckdb/src/execution/index/art/leaf.cpp +319 -170
  12. package/src/duckdb/src/execution/index/art/leaf_segment.cpp +42 -0
  13. package/src/duckdb/src/execution/index/art/node.cpp +444 -379
  14. package/src/duckdb/src/execution/index/art/node16.cpp +178 -114
  15. package/src/duckdb/src/execution/index/art/node256.cpp +117 -79
  16. package/src/duckdb/src/execution/index/art/node4.cpp +169 -114
  17. package/src/duckdb/src/execution/index/art/node48.cpp +175 -105
  18. package/src/duckdb/src/execution/index/art/prefix.cpp +405 -127
  19. package/src/duckdb/src/execution/index/art/prefix_segment.cpp +42 -0
  20. package/src/duckdb/src/execution/index/art/swizzleable_pointer.cpp +10 -85
  21. package/src/duckdb/src/execution/operator/join/physical_index_join.cpp +2 -1
  22. package/src/duckdb/src/execution/operator/persistent/base_csv_reader.cpp +2 -2
  23. package/src/duckdb/src/execution/operator/persistent/csv_reader_options.cpp +2 -0
  24. package/src/duckdb/src/execution/operator/persistent/parallel_csv_reader.cpp +4 -0
  25. package/src/duckdb/src/execution/operator/schema/physical_create_index.cpp +11 -12
  26. package/src/duckdb/src/function/table/read_csv.cpp +5 -1
  27. package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
  28. package/src/duckdb/src/include/duckdb/common/queue.hpp +1 -1
  29. package/src/duckdb/src/include/duckdb/execution/index/art/art.hpp +53 -45
  30. package/src/duckdb/src/include/duckdb/execution/index/art/art_key.hpp +29 -24
  31. package/src/duckdb/src/include/duckdb/execution/index/art/fixed_size_allocator.hpp +114 -0
  32. package/src/duckdb/src/include/duckdb/execution/index/art/iterator.hpp +26 -20
  33. package/src/duckdb/src/include/duckdb/execution/index/art/leaf.hpp +63 -39
  34. package/src/duckdb/src/include/duckdb/execution/index/art/leaf_segment.hpp +36 -0
  35. package/src/duckdb/src/include/duckdb/execution/index/art/node.hpp +98 -116
  36. package/src/duckdb/src/include/duckdb/execution/index/art/node16.hpp +48 -36
  37. package/src/duckdb/src/include/duckdb/execution/index/art/node256.hpp +52 -35
  38. package/src/duckdb/src/include/duckdb/execution/index/art/node4.hpp +46 -36
  39. package/src/duckdb/src/include/duckdb/execution/index/art/node48.hpp +57 -35
  40. package/src/duckdb/src/include/duckdb/execution/index/art/prefix.hpp +57 -50
  41. package/src/duckdb/src/include/duckdb/execution/index/art/prefix_segment.hpp +40 -0
  42. package/src/duckdb/src/include/duckdb/execution/index/art/swizzleable_pointer.hpp +38 -31
  43. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_file_handle.hpp +2 -1
  44. package/src/duckdb/src/include/duckdb/execution/operator/persistent/csv_reader_options.hpp +2 -0
  45. package/src/duckdb/src/include/duckdb/main/query_result.hpp +1 -1
  46. package/src/duckdb/src/include/duckdb/parser/statement/insert_statement.hpp +4 -1
  47. package/src/duckdb/src/include/duckdb/parser/transformer.hpp +2 -1
  48. package/src/duckdb/src/include/duckdb/storage/buffer_manager.hpp +0 -5
  49. package/src/duckdb/src/include/duckdb/storage/index.hpp +13 -28
  50. package/src/duckdb/src/include/duckdb/storage/standard_buffer_manager.hpp +0 -2
  51. package/src/duckdb/src/include/duckdb/transaction/cleanup_state.hpp +5 -0
  52. package/src/duckdb/src/include/duckdb.h +26 -0
  53. package/src/duckdb/src/main/capi/helper-c.cpp +7 -0
  54. package/src/duckdb/src/main/client_context.cpp +1 -1
  55. package/src/duckdb/src/main/query_result.cpp +1 -1
  56. package/src/duckdb/src/parser/statement/insert_statement.cpp +15 -6
  57. package/src/duckdb/src/parser/transform/constraint/transform_constraint.cpp +1 -1
  58. package/src/duckdb/src/parser/transform/expression/transform_function.cpp +18 -5
  59. package/src/duckdb/src/parser/transform/statement/transform_insert.cpp +5 -7
  60. package/src/duckdb/src/planner/binder/statement/bind_create.cpp +20 -7
  61. package/src/duckdb/src/planner/binder/statement/bind_insert.cpp +14 -9
  62. package/src/duckdb/src/storage/checkpoint_manager.cpp +11 -9
  63. package/src/duckdb/src/storage/data_table.cpp +6 -3
  64. package/src/duckdb/src/storage/index.cpp +18 -6
  65. package/src/duckdb/src/storage/local_storage.cpp +8 -2
  66. package/src/duckdb/src/storage/standard_buffer_manager.cpp +0 -9
  67. package/src/duckdb/src/storage/wal_replay.cpp +1 -1
  68. package/src/duckdb/src/transaction/cleanup_state.cpp +6 -0
  69. package/src/duckdb/src/transaction/undo_buffer.cpp +8 -0
  70. package/src/duckdb/ub_extension_icu_third_party_icu_i18n.cpp +4 -4
  71. package/src/duckdb/ub_src_execution_index_art.cpp +7 -1
@@ -7,45 +7,52 @@
7
7
  //===----------------------------------------------------------------------===//
8
8
  #pragma once
9
9
 
10
- #include "duckdb/execution/index/art/node.hpp"
10
+ #include "duckdb/common/constants.hpp"
11
11
 
12
12
  namespace duckdb {
13
13
 
14
- class ART;
15
- class Node;
14
+ // classes
15
+ class MetaBlockReader;
16
16
 
17
- // SwizzleablePointer assumes that the 64-bit blockId always has 0s in the top
18
- // 33 bits. It thus uses 8 bytes of memory rather than 12.
17
+ // structs
18
+ struct BlockPointer;
19
+
20
+ //! SwizzleablePointer provides functions on a (possibly) swizzled pointer. If the swizzle flag is set, then the
21
+ //! pointer points to a storage address (and has no type), otherwise the pointer has a type and stores
22
+ //! other information (e.g., a buffer location)
19
23
  class SwizzleablePointer {
20
24
  public:
21
- ~SwizzleablePointer();
22
- explicit SwizzleablePointer(duckdb::MetaBlockReader &reader);
23
- SwizzleablePointer() : pointer(0) {};
24
-
25
- BlockPointer Serialize(ART &art, duckdb::MetaBlockWriter &writer);
26
-
27
- //! Transforms from Node* to uint64_t
28
- SwizzleablePointer &operator=(const Node *ptr);
25
+ //! Constructs an empty SwizzleablePointer
26
+ SwizzleablePointer() : swizzle_flag(0), type(0), offset(0), buffer_id(0) {};
27
+ //! Constructs a swizzled pointer from a buffer ID and an offset
28
+ explicit SwizzleablePointer(MetaBlockReader &reader);
29
+ //! Constructs a non-swizzled pointer from a buffer ID and an offset
30
+ SwizzleablePointer(uint32_t offset, uint32_t buffer_id)
31
+ : swizzle_flag(0), type(0), offset(offset), buffer_id(buffer_id) {};
32
+
33
+ //! The swizzle flag, set if swizzled, not set otherwise
34
+ uint8_t swizzle_flag : 1;
35
+ //! The type of the pointer, zero if not set
36
+ uint8_t type : 7;
37
+ //! The offset of a memory location
38
+ uint32_t offset : 24;
39
+ //! The buffer ID of a memory location
40
+ uint32_t buffer_id : 32;
29
41
 
30
- //! Checks if pointer is swizzled
31
- bool IsSwizzled();
32
- //! Unswizzle the pointer (if possible)
33
- Node *Unswizzle(ART &art);
34
-
35
- operator bool() const {
36
- return pointer;
42
+ public:
43
+ //! Checks if the pointer is swizzled
44
+ inline bool IsSwizzled() const {
45
+ return swizzle_flag;
46
+ }
47
+ //! Returns true, if neither the swizzle flag nor the type is set, and false otherwise
48
+ inline bool IsSet() const {
49
+ return swizzle_flag || type;
50
+ }
51
+ //! Reset the pointer
52
+ inline void Reset() {
53
+ swizzle_flag = 0;
54
+ type = 0;
37
55
  }
38
-
39
- //! Deletes the underlying object (if necessary) and set the pointer to nullptr
40
- void Reset();
41
-
42
- private:
43
- uint64_t pointer;
44
-
45
- friend bool operator!=(const SwizzleablePointer &s_ptr, const uint64_t &ptr);
46
-
47
- //! Extracts the block info from swizzled pointer
48
- BlockPointer GetSwizzledBlockInfo();
49
56
  };
50
57
 
51
58
  } // namespace duckdb
@@ -16,7 +16,8 @@ namespace duckdb {
16
16
 
17
17
  struct CSVFileHandle {
18
18
  public:
19
- explicit CSVFileHandle(unique_ptr<FileHandle> file_handle_p) : file_handle(std::move(file_handle_p)) {
19
+ explicit CSVFileHandle(unique_ptr<FileHandle> file_handle_p, bool enable_reset = true)
20
+ : file_handle(std::move(file_handle_p)), reset_enabled(enable_reset) {
20
21
  can_seek = file_handle->CanSeek();
21
22
  plain_file_source = file_handle->OnDiskFile() && can_seek;
22
23
  file_size = file_handle->GetFileSize();
@@ -62,6 +62,8 @@ struct BufferedCSVReaderOptions {
62
62
  //! Whether file is compressed or not, and if so which compression type
63
63
  //! AUTO_DETECT (default; infer from file extension)
64
64
  FileCompressionType compression = FileCompressionType::AUTO_DETECT;
65
+ //! Option to convert quoted values to NULL values
66
+ bool allow_quoted_nulls = true;
65
67
 
66
68
  //===--------------------------------------------------------------------===//
67
69
  // CSVAutoOptions
@@ -20,7 +20,7 @@ enum class QueryResultType : uint8_t { MATERIALIZED_RESULT, STREAM_RESULT, PENDI
20
20
 
21
21
  //! A set of properties from the client context that can be used to interpret the query result
22
22
  struct ClientProperties {
23
- string timezone;
23
+ string time_zone;
24
24
  };
25
25
 
26
26
  class BaseQueryResult {
@@ -72,6 +72,9 @@ public:
72
72
  //! CTEs
73
73
  CommonTableExpressionMap cte_map;
74
74
 
75
+ //! Whether or not this a DEFAULT VALUES
76
+ bool default_values = false;
77
+
75
78
  protected:
76
79
  InsertStatement(const InsertStatement &other);
77
80
 
@@ -82,7 +85,7 @@ public:
82
85
 
83
86
  //! If the INSERT statement is inserted DIRECTLY from a values list (i.e. INSERT INTO tbl VALUES (...)) this returns
84
87
  //! the expression list Otherwise, this returns NULL
85
- ExpressionListRef *GetValuesList() const;
88
+ optional_ptr<ExpressionListRef> GetValuesList() const;
86
89
  };
87
90
 
88
91
  } // namespace duckdb
@@ -312,7 +312,8 @@ private:
312
312
  void TransformExpressionList(duckdb_libpgquery::PGList &list, vector<unique_ptr<ParsedExpression>> &result);
313
313
 
314
314
  //! Transform a Postgres PARTITION BY/ORDER BY specification into lists of expressions
315
- void TransformWindowDef(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr);
315
+ void TransformWindowDef(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr,
316
+ const char *window_name = nullptr);
316
317
  //! Transform a Postgres window frame specification into frame expressions
317
318
  void TransformWindowFrame(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr);
318
319
 
@@ -58,15 +58,10 @@ public:
58
58
  //! Construct a managed buffer.
59
59
  virtual unique_ptr<FileBuffer> ConstructManagedBuffer(idx_t size, unique_ptr<FileBuffer> &&source,
60
60
  FileBufferType type = FileBufferType::MANAGED_BUFFER);
61
- //! Increases the currently allocated memory, but the actual allocation does not go through the buffer manager
62
- virtual void IncreaseUsedMemory(idx_t size, bool unsafe = false) = 0;
63
- //! Decrease the currently allocated memory, but the actual deallocation does not go through the buffer manager
64
- virtual void DecreaseUsedMemory(idx_t size) = 0;
65
61
  //! Get the underlying buffer pool responsible for managing the buffers
66
62
  virtual BufferPool &GetBufferPool();
67
63
 
68
64
  // Static methods
69
-
70
65
  DUCKDB_API static BufferManager &GetBufferManager(DatabaseInstance &db);
71
66
  DUCKDB_API static BufferManager &GetBufferManager(ClientContext &context);
72
67
  DUCKDB_API static BufferManager &GetBufferManager(AttachedDatabase &db);
@@ -32,8 +32,7 @@ struct IndexScanState;
32
32
  class Index {
33
33
  public:
34
34
  Index(AttachedDatabase &db, IndexType type, TableIOManager &table_io_manager, const vector<column_t> &column_ids,
35
- const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type,
36
- bool track_memory);
35
+ const vector<unique_ptr<Expression>> &unbound_expressions, IndexConstraintType constraint_type);
37
36
  virtual ~Index() = default;
38
37
 
39
38
  //! The type of the index
@@ -57,25 +56,21 @@ public:
57
56
  AttachedDatabase &db;
58
57
  //! Buffer manager of the database instance
59
58
  BufferManager &buffer_manager;
60
- //! The size of the index in memory
61
- //! This does not track the size of the index meta information, but only allocated nodes and leaves
62
- idx_t memory_size;
63
- //! Flag determining if this index's size is tracked by the buffer manager
64
- bool track_memory;
65
59
 
66
60
  public:
67
61
  //! Initialize a single predicate scan on the index with the given expression and column IDs
68
62
  virtual unique_ptr<IndexScanState> InitializeScanSinglePredicate(const Transaction &transaction, const Value &value,
69
- ExpressionType expressionType) = 0;
63
+ const ExpressionType expression_type) = 0;
70
64
  //! Initialize a two predicate scan on the index with the given expression and column IDs
71
- virtual unique_ptr<IndexScanState> InitializeScanTwoPredicates(Transaction &transaction, const Value &low_value,
72
- ExpressionType low_expression_type,
65
+ virtual unique_ptr<IndexScanState> InitializeScanTwoPredicates(const Transaction &transaction,
66
+ const Value &low_value,
67
+ const ExpressionType low_expression_type,
73
68
  const Value &high_value,
74
- ExpressionType high_expression_type) = 0;
69
+ const ExpressionType high_expression_type) = 0;
75
70
  //! Performs a lookup on the index, fetching up to max_count result IDs. Returns true if all row IDs were fetched,
76
71
  //! and false otherwise
77
- virtual bool Scan(Transaction &transaction, DataTable &table, IndexScanState &state, idx_t max_count,
78
- vector<row_t> &result_ids) = 0;
72
+ virtual bool Scan(const Transaction &transaction, const DataTable &table, IndexScanState &state,
73
+ const idx_t max_count, vector<row_t> &result_ids) = 0;
79
74
 
80
75
  //! Obtain a lock on the index
81
76
  virtual void InitializeLock(IndexLock &state);
@@ -104,23 +99,13 @@ public:
104
99
  //! Obtains a lock and calls MergeIndexes while holding that lock
105
100
  bool MergeIndexes(Index &other_index);
106
101
 
102
+ //! Traverses an ART and vacuums the qualifying nodes. The lock obtained from InitializeLock must be held
103
+ virtual void Vacuum(IndexLock &state) = 0;
104
+ //! Obtains a lock and calls Vacuum while holding that lock
105
+ void Vacuum();
106
+
107
107
  //! Returns the string representation of an index
108
108
  virtual string ToString() = 0;
109
- //! Verifies that the in-memory size value of the index matches its actual size
110
- virtual void Verify() = 0;
111
- //! Increases the memory size by the difference between the old size and the current size
112
- //! and performs verifications
113
- virtual void IncreaseAndVerifyMemorySize(idx_t old_memory_size) = 0;
114
-
115
- //! Increases the in-memory size value
116
- inline void IncreaseMemorySize(idx_t size) {
117
- memory_size += size;
118
- };
119
- //! Decreases the in-memory size value
120
- inline void DecreaseMemorySize(idx_t size) {
121
- D_ASSERT(memory_size >= size);
122
- memory_size -= size;
123
- };
124
109
 
125
110
  //! Returns true if the index is affected by updates on the specified column IDs, and false otherwise
126
111
  bool IndexIsUpdated(const vector<PhysicalIndex> &column_ids) const;
@@ -45,8 +45,6 @@ public:
45
45
  shared_ptr<BlockHandle> RegisterSmallMemory(idx_t block_size) final override;
46
46
 
47
47
  idx_t GetUsedMemory() const final override;
48
- void IncreaseUsedMemory(idx_t amount, bool unsafe = false) final override;
49
- void DecreaseUsedMemory(idx_t amount) final override;
50
48
  idx_t GetMaxMemory() const final override;
51
49
 
52
50
  //! Allocate an in-memory buffer with a single pin.
@@ -10,8 +10,10 @@
10
10
 
11
11
  #include "duckdb/transaction/undo_buffer.hpp"
12
12
  #include "duckdb/common/types/data_chunk.hpp"
13
+ #include "duckdb/common/unordered_map.hpp"
13
14
 
14
15
  namespace duckdb {
16
+
15
17
  class DataTable;
16
18
 
17
19
  struct DeleteInfo;
@@ -22,6 +24,9 @@ public:
22
24
  CleanupState();
23
25
  ~CleanupState();
24
26
 
27
+ // all tables with indexes that possibly need a vacuum (after e.g. a delete)
28
+ unordered_map<string, optional_ptr<DataTable>> indexed_tables;
29
+
25
30
  public:
26
31
  void CleanupEntry(UndoFlags type, data_ptr_t data);
27
32
 
@@ -190,6 +190,23 @@ typedef struct {
190
190
  idx_t size;
191
191
  } duckdb_string;
192
192
 
193
+ /*
194
+ The internal data representation of a VARCHAR/BLOB column
195
+ */
196
+ typedef struct {
197
+ union {
198
+ struct {
199
+ uint32_t length;
200
+ char prefix[4];
201
+ char *ptr;
202
+ } pointer;
203
+ struct {
204
+ uint32_t length;
205
+ char inlined[12];
206
+ } inlined;
207
+ } value;
208
+ } duckdb_string_t;
209
+
193
210
  typedef struct {
194
211
  void *data;
195
212
  idx_t size;
@@ -298,6 +315,7 @@ typedef enum {
298
315
  /*!
299
316
  Creates a new database or opens an existing database file stored at the the given path.
300
317
  If no path is given a new in-memory database is created instead.
318
+ The instantiated database should be closed with 'duckdb_close'
301
319
 
302
320
  * path: Path to the database file on disk, or `nullptr` or `:memory:` to open an in-memory database.
303
321
  * out_database: The result database object.
@@ -331,6 +349,7 @@ DUCKDB_API void duckdb_close(duckdb_database *database);
331
349
  /*!
332
350
  Opens a connection to a database. Connections are required to query the database, and store transactional state
333
351
  associated with the connection.
352
+ The instantiated connection should be closed using 'duckdb_disconnect'
334
353
 
335
354
  * database: The database file to connect to.
336
355
  * out_connection: The result connection object.
@@ -751,6 +770,13 @@ This is the amount of tuples that will fit into a data chunk created by `duckdb_
751
770
  */
752
771
  DUCKDB_API idx_t duckdb_vector_size();
753
772
 
773
+ /*!
774
+ Whether or not the duckdb_string_t value is inlined.
775
+ This means that the data of the string does not have a separate allocation.
776
+
777
+ */
778
+ DUCKDB_API bool duckdb_string_is_inlined(duckdb_string_t string);
779
+
754
780
  //===--------------------------------------------------------------------===//
755
781
  // Date/Time/Timestamp Helpers
756
782
  //===--------------------------------------------------------------------===//
@@ -186,3 +186,10 @@ void duckdb_free(void *ptr) {
186
186
  idx_t duckdb_vector_size() {
187
187
  return STANDARD_VECTOR_SIZE;
188
188
  }
189
+
190
+ bool duckdb_string_is_inlined(duckdb_string_t string_p) {
191
+ static_assert(sizeof(duckdb_string_t) == sizeof(duckdb::string_t),
192
+ "duckdb_string_t should have the same memory layout as duckdb::string_t");
193
+ auto &string = *(duckdb::string_t *)(&string_p);
194
+ return string.IsInlined();
195
+ }
@@ -1156,7 +1156,7 @@ ParserOptions ClientContext::GetParserOptions() const {
1156
1156
 
1157
1157
  ClientProperties ClientContext::GetClientProperties() const {
1158
1158
  ClientProperties properties;
1159
- properties.timezone = ClientConfig::GetConfig(*this).ExtractTimezone();
1159
+ properties.time_zone = ClientConfig::GetConfig(*this).ExtractTimezone();
1160
1160
  return properties;
1161
1161
  }
1162
1162
 
@@ -165,7 +165,7 @@ string QueryResult::HeaderToString() {
165
165
  }
166
166
 
167
167
  string QueryResult::GetConfigTimezone(QueryResult &query_result) {
168
- return query_result.client_properties.timezone;
168
+ return query_result.client_properties.time_zone;
169
169
  }
170
170
 
171
171
  } // namespace duckdb
@@ -27,9 +27,10 @@ InsertStatement::InsertStatement()
27
27
  }
28
28
 
29
29
  InsertStatement::InsertStatement(const InsertStatement &other)
30
- : SQLStatement(other),
31
- select_statement(unique_ptr_cast<SQLStatement, SelectStatement>(other.select_statement->Copy())),
32
- columns(other.columns), table(other.table), schema(other.schema), catalog(other.catalog) {
30
+ : SQLStatement(other), select_statement(unique_ptr_cast<SQLStatement, SelectStatement>(
31
+ other.select_statement ? other.select_statement->Copy() : nullptr)),
32
+ columns(other.columns), table(other.table), schema(other.schema), catalog(other.catalog),
33
+ default_values(other.default_values) {
33
34
  cte_map = other.cte_map.Copy();
34
35
  for (auto &expr : other.returning_list) {
35
36
  returning_list.emplace_back(expr->Copy());
@@ -93,10 +94,15 @@ string InsertStatement::ToString() const {
93
94
  result += " ";
94
95
  auto values_list = GetValuesList();
95
96
  if (values_list) {
97
+ D_ASSERT(!default_values);
96
98
  values_list->alias = string();
97
99
  result += values_list->ToString();
98
- } else {
100
+ } else if (select_statement) {
101
+ D_ASSERT(!default_values);
99
102
  result += select_statement->ToString();
103
+ } else {
104
+ D_ASSERT(default_values);
105
+ result += "DEFAULT VALUES";
100
106
  }
101
107
  if (!or_replace_shorthand_set && on_conflict_info) {
102
108
  auto &conflict_info = *on_conflict_info;
@@ -155,7 +161,10 @@ unique_ptr<SQLStatement> InsertStatement::Copy() const {
155
161
  return unique_ptr<InsertStatement>(new InsertStatement(*this));
156
162
  }
157
163
 
158
- ExpressionListRef *InsertStatement::GetValuesList() const {
164
+ optional_ptr<ExpressionListRef> InsertStatement::GetValuesList() const {
165
+ if (!select_statement) {
166
+ return nullptr;
167
+ }
159
168
  if (select_statement->node->type != QueryNodeType::SELECT_NODE) {
160
169
  return nullptr;
161
170
  }
@@ -178,7 +187,7 @@ ExpressionListRef *InsertStatement::GetValuesList() const {
178
187
  if (!node.from_table || node.from_table->type != TableReferenceType::EXPRESSION_LIST) {
179
188
  return nullptr;
180
189
  }
181
- return (ExpressionListRef *)node.from_table.get();
190
+ return &node.from_table->Cast<ExpressionListRef>();
182
191
  }
183
192
 
184
193
  } // namespace duckdb
@@ -108,7 +108,7 @@ unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGLis
108
108
  pk_columns.emplace_back(reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str);
109
109
  }
110
110
  }
111
- if (pk_columns.size() != fk_columns.size()) {
111
+ if (!pk_columns.empty() && pk_columns.size() != fk_columns.size()) {
112
112
  throw ParserException("The number of referencing and referenced columns for foreign keys must be the same");
113
113
  }
114
114
  return make_uniq<ForeignKeyConstraint>(pk_columns, fk_columns, std::move(fk_info));
@@ -13,15 +13,24 @@
13
13
 
14
14
  namespace duckdb {
15
15
 
16
- void Transformer::TransformWindowDef(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr) {
16
+ void Transformer::TransformWindowDef(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr,
17
+ const char *window_name) {
17
18
  D_ASSERT(window_spec);
18
19
  D_ASSERT(expr);
19
20
 
20
21
  // next: partitioning/ordering expressions
21
22
  if (window_spec->partitionClause) {
23
+ if (window_name && !expr->partitions.empty()) {
24
+ throw ParserException("Cannot override PARTITION BY clause of window \"%s\"", window_name);
25
+ }
22
26
  TransformExpressionList(*window_spec->partitionClause, expr->partitions);
23
27
  }
24
- TransformOrderBy(window_spec->orderClause, expr->orders);
28
+ if (window_spec->orderClause) {
29
+ if (window_name && !expr->orders.empty()) {
30
+ throw ParserException("Cannot override ORDER BY clause of window \"%s\"", window_name);
31
+ }
32
+ TransformOrderBy(window_spec->orderClause, expr->orders);
33
+ }
25
34
  }
26
35
 
27
36
  void Transformer::TransformWindowFrame(duckdb_libpgquery::PGWindowDef *window_spec, WindowExpression *expr) {
@@ -198,6 +207,7 @@ unique_ptr<ParsedExpression> Transformer::TransformFuncCall(duckdb_libpgquery::P
198
207
  D_ASSERT(window_spec);
199
208
  }
200
209
  auto window_ref = window_spec;
210
+ auto window_name = window_ref->refname;
201
211
  if (window_ref->refname) {
202
212
  auto it = window_clauses.find(StringUtil::Lower(string(window_spec->refname)));
203
213
  if (it == window_clauses.end()) {
@@ -208,6 +218,9 @@ unique_ptr<ParsedExpression> Transformer::TransformFuncCall(duckdb_libpgquery::P
208
218
  }
209
219
  in_window_definition = true;
210
220
  TransformWindowDef(window_ref, expr.get());
221
+ if (window_ref != window_spec) {
222
+ TransformWindowDef(window_spec, expr.get(), window_name);
223
+ }
211
224
  TransformWindowFrame(window_spec, expr.get());
212
225
  in_window_definition = false;
213
226
  expr->query_location = root->location;
@@ -299,9 +312,9 @@ unique_ptr<ParsedExpression> Transformer::TransformFuncCall(duckdb_libpgquery::P
299
312
  std::move(filter_expr), std::move(order_bys),
300
313
  root->agg_distinct, false, root->export_state);
301
314
  lowercase_name = "list_sort";
302
- order_bys.reset();
303
- filter_expr.reset();
304
- children.clear();
315
+ order_bys.reset(); // NOLINT
316
+ filter_expr.reset(); // NOLINT
317
+ children.clear(); // NOLINT
305
318
  children.emplace_back(std::move(unordered));
306
319
  children.emplace_back(std::move(sense));
307
320
  children.emplace_back(std::move(nulls));
@@ -26,12 +26,6 @@ unique_ptr<InsertStatement> Transformer::TransformInsert(duckdb_libpgquery::PGNo
26
26
  auto stmt = reinterpret_cast<duckdb_libpgquery::PGInsertStmt *>(node);
27
27
  D_ASSERT(stmt);
28
28
 
29
- if (!stmt->selectStmt) {
30
- // TODO: This should be easy to add, we already support DEFAULT in the values list,
31
- // this could probably just be transformed into VALUES (DEFAULT, DEFAULT, DEFAULT, ..) in the Binder
32
- throw ParserException("DEFAULT VALUES clause is not supported!");
33
- }
34
-
35
29
  auto result = make_uniq<InsertStatement>();
36
30
  if (stmt->withClause) {
37
31
  TransformCTE(reinterpret_cast<duckdb_libpgquery::PGWithClause *>(stmt->withClause), result->cte_map);
@@ -49,7 +43,11 @@ unique_ptr<InsertStatement> Transformer::TransformInsert(duckdb_libpgquery::PGNo
49
43
  if (stmt->returningList) {
50
44
  Transformer::TransformExpressionList(*(stmt->returningList), result->returning_list);
51
45
  }
52
- result->select_statement = TransformSelect(stmt->selectStmt, false);
46
+ if (stmt->selectStmt) {
47
+ result->select_statement = TransformSelect(stmt->selectStmt, false);
48
+ } else {
49
+ result->default_values = true;
50
+ }
53
51
 
54
52
  auto qname = TransformQualifiedName(stmt->relation);
55
53
  result->table = qname.name;
@@ -292,17 +292,30 @@ static void FindMatchingPrimaryKeyColumns(const ColumnList &columns, const vecto
292
292
  } else {
293
293
  pk_names = unique.columns;
294
294
  }
295
- if (pk_names.size() != fk.fk_columns.size()) {
296
- // the number of referencing and referenced columns for foreign keys must be the same
297
- continue;
298
- }
299
295
  if (find_primary_key) {
300
296
  // found matching primary key
297
+ if (pk_names.size() != fk.fk_columns.size()) {
298
+ auto pk_name_str = StringUtil::Join(pk_names, ",");
299
+ auto fk_name_str = StringUtil::Join(fk.fk_columns, ",");
300
+ throw BinderException(
301
+ "Failed to create foreign key: number of referencing (%s) and referenced columns (%s) differ",
302
+ fk_name_str, pk_name_str);
303
+ }
301
304
  fk.pk_columns = pk_names;
302
305
  return;
303
306
  }
304
- if (fk.pk_columns != pk_names) {
305
- // Name mismatch
307
+ if (pk_names.size() != fk.fk_columns.size()) {
308
+ // the number of referencing and referenced columns for foreign keys must be the same
309
+ continue;
310
+ }
311
+ bool equals = true;
312
+ for (idx_t i = 0; i < fk.pk_columns.size(); i++) {
313
+ if (!StringUtil::CIEquals(fk.pk_columns[i], pk_names[i])) {
314
+ equals = false;
315
+ break;
316
+ }
317
+ }
318
+ if (!equals) {
306
319
  continue;
307
320
  }
308
321
  // found match
@@ -540,7 +553,7 @@ BoundStatement Binder::Bind(CreateStatement &stmt) {
540
553
  D_ASSERT(fk.info.pk_keys.empty());
541
554
  D_ASSERT(fk.info.fk_keys.empty());
542
555
  FindForeignKeyIndexes(create_info.columns, fk.fk_columns, fk.info.fk_keys);
543
- if (create_info.table == fk.info.table) {
556
+ if (StringUtil::CIEquals(create_info.table, fk.info.table)) {
544
557
  // self-referential foreign key constraint
545
558
  fk.info.type = ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE;
546
559
  FindMatchingPrimaryKeyColumns(create_info.columns, create_info.constraints, fk);
@@ -9,6 +9,7 @@
9
9
  #include "duckdb/planner/operator/logical_get.hpp"
10
10
  #include "duckdb/common/string_util.hpp"
11
11
  #include "duckdb/function/table/table_scan.hpp"
12
+ #include "duckdb/planner/operator/logical_dummy_scan.hpp"
12
13
  #include "duckdb/planner/operator/logical_projection.hpp"
13
14
  #include "duckdb/planner/expression_iterator.hpp"
14
15
  #include "duckdb/planner/expression_binder/returning_binder.hpp"
@@ -409,7 +410,7 @@ BoundStatement Binder::Bind(InsertStatement &stmt) {
409
410
  AddCTEMap(stmt.cte_map);
410
411
 
411
412
  vector<LogicalIndex> named_column_map;
412
- if (!stmt.columns.empty()) {
413
+ if (!stmt.columns.empty() || stmt.default_values) {
413
414
  // insertion statement specifies column list
414
415
 
415
416
  // create a mapping of (list index) -> (column index)
@@ -448,11 +449,10 @@ BoundStatement Binder::Bind(InsertStatement &stmt) {
448
449
 
449
450
  // bind the default values
450
451
  BindDefaultValues(table.GetColumns(), insert->bound_defaults);
451
- if (!stmt.select_statement) {
452
+ if (!stmt.select_statement && !stmt.default_values) {
452
453
  result.plan = std::move(insert);
453
454
  return result;
454
455
  }
455
-
456
456
  // Exclude the generated columns from this amount
457
457
  idx_t expected_columns = stmt.columns.empty() ? table.GetColumns().PhysicalColumnCount() : stmt.columns.size();
458
458
 
@@ -488,14 +488,19 @@ BoundStatement Binder::Bind(InsertStatement &stmt) {
488
488
  }
489
489
 
490
490
  // parse select statement and add to logical plan
491
- auto select_binder = Binder::CreateBinder(context, this);
492
- auto root_select = select_binder->Bind(*stmt.select_statement);
493
- MoveCorrelatedExpressions(*select_binder);
491
+ unique_ptr<LogicalOperator> root;
492
+ if (stmt.select_statement) {
493
+ auto select_binder = Binder::CreateBinder(context, this);
494
+ auto root_select = select_binder->Bind(*stmt.select_statement);
495
+ MoveCorrelatedExpressions(*select_binder);
494
496
 
495
- CheckInsertColumnCountMismatch(expected_columns, root_select.types.size(), !stmt.columns.empty(),
496
- table.name.c_str());
497
+ CheckInsertColumnCountMismatch(expected_columns, root_select.types.size(), !stmt.columns.empty(),
498
+ table.name.c_str());
497
499
 
498
- auto root = CastLogicalOperatorToTypes(root_select.types, insert->expected_types, std::move(root_select.plan));
500
+ root = CastLogicalOperatorToTypes(root_select.types, insert->expected_types, std::move(root_select.plan));
501
+ } else {
502
+ root = make_uniq<LogicalDummyScan>(GenerateTableIndex());
503
+ }
499
504
  insert->AddChild(std::move(root));
500
505
 
501
506
  BindOnConflictClause(*insert, table, stmt);