duckdb 0.8.1-dev111.0 → 0.8.1-dev125.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/duckdb/src/catalog/catalog.cpp +5 -4
- package/src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp +113 -0
- package/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp +4 -0
- package/src/duckdb/src/catalog/catalog_search_path.cpp +32 -12
- package/src/duckdb/src/common/exception.cpp +4 -1
- package/src/duckdb/src/common/exception_format_value.cpp +19 -14
- package/src/duckdb/src/function/table/version/pragma_version.cpp +2 -2
- package/src/duckdb/src/include/duckdb/catalog/catalog_entry/duck_table_entry.hpp +2 -0
- package/src/duckdb/src/include/duckdb/catalog/catalog_entry/table_catalog_entry.hpp +6 -0
- package/src/duckdb/src/include/duckdb/catalog/catalog_search_path.hpp +6 -2
- package/src/duckdb/src/include/duckdb/common/exception.hpp +3 -0
- package/src/duckdb/src/main/settings/settings.cpp +2 -2
- package/src/duckdb/src/planner/binder/statement/bind_update.cpp +2 -114
package/package.json
CHANGED
@@ -690,15 +690,16 @@ bool Catalog::TypeExists(ClientContext &context, const string &catalog_name, con
|
|
690
690
|
vector<reference<SchemaCatalogEntry>> Catalog::GetSchemas(ClientContext &context, const string &catalog_name) {
|
691
691
|
vector<reference<Catalog>> catalogs;
|
692
692
|
if (IsInvalidCatalog(catalog_name)) {
|
693
|
-
|
693
|
+
reference_set_t<Catalog> inserted_catalogs;
|
694
694
|
|
695
695
|
auto &search_path = *context.client_data->catalog_search_path;
|
696
696
|
for (auto &entry : search_path.Get()) {
|
697
|
-
|
697
|
+
auto &catalog = Catalog::GetCatalog(context, entry.catalog);
|
698
|
+
if (inserted_catalogs.find(catalog) != inserted_catalogs.end()) {
|
698
699
|
continue;
|
699
700
|
}
|
700
|
-
|
701
|
-
catalogs.push_back(
|
701
|
+
inserted_catalogs.insert(catalog);
|
702
|
+
catalogs.push_back(catalog);
|
702
703
|
}
|
703
704
|
} else {
|
704
705
|
catalogs.push_back(Catalog::GetCatalog(context, catalog_name));
|
@@ -16,6 +16,9 @@
|
|
16
16
|
#include "duckdb/parser/constraints/list.hpp"
|
17
17
|
#include "duckdb/function/table/table_scan.hpp"
|
18
18
|
#include "duckdb/storage/table_storage_info.hpp"
|
19
|
+
#include "duckdb/planner/operator/logical_get.hpp"
|
20
|
+
#include "duckdb/planner/operator/logical_projection.hpp"
|
21
|
+
#include "duckdb/planner/operator/logical_update.hpp"
|
19
22
|
|
20
23
|
namespace duckdb {
|
21
24
|
|
@@ -730,4 +733,114 @@ TableStorageInfo DuckTableEntry::GetStorageInfo(ClientContext &context) {
|
|
730
733
|
return result;
|
731
734
|
}
|
732
735
|
|
736
|
+
static void BindExtraColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update,
|
737
|
+
physical_index_set_t &bound_columns) {
|
738
|
+
if (bound_columns.size() <= 1) {
|
739
|
+
return;
|
740
|
+
}
|
741
|
+
idx_t found_column_count = 0;
|
742
|
+
physical_index_set_t found_columns;
|
743
|
+
for (idx_t i = 0; i < update.columns.size(); i++) {
|
744
|
+
if (bound_columns.find(update.columns[i]) != bound_columns.end()) {
|
745
|
+
// this column is referenced in the CHECK constraint
|
746
|
+
found_column_count++;
|
747
|
+
found_columns.insert(update.columns[i]);
|
748
|
+
}
|
749
|
+
}
|
750
|
+
if (found_column_count > 0 && found_column_count != bound_columns.size()) {
|
751
|
+
// columns in this CHECK constraint were referenced, but not all were part of the UPDATE
|
752
|
+
// add them to the scan and update set
|
753
|
+
for (auto &check_column_id : bound_columns) {
|
754
|
+
if (found_columns.find(check_column_id) != found_columns.end()) {
|
755
|
+
// column is already projected
|
756
|
+
continue;
|
757
|
+
}
|
758
|
+
// column is not projected yet: project it by adding the clause "i=i" to the set of updated columns
|
759
|
+
auto &column = table.GetColumns().GetColumn(check_column_id);
|
760
|
+
update.expressions.push_back(make_uniq<BoundColumnRefExpression>(
|
761
|
+
column.Type(), ColumnBinding(proj.table_index, proj.expressions.size())));
|
762
|
+
proj.expressions.push_back(make_uniq<BoundColumnRefExpression>(
|
763
|
+
column.Type(), ColumnBinding(get.table_index, get.column_ids.size())));
|
764
|
+
get.column_ids.push_back(check_column_id.index);
|
765
|
+
update.columns.push_back(check_column_id);
|
766
|
+
}
|
767
|
+
}
|
768
|
+
}
|
769
|
+
|
770
|
+
static bool TypeSupportsRegularUpdate(const LogicalType &type) {
|
771
|
+
switch (type.id()) {
|
772
|
+
case LogicalTypeId::LIST:
|
773
|
+
case LogicalTypeId::MAP:
|
774
|
+
case LogicalTypeId::UNION:
|
775
|
+
// lists and maps and unions don't support updates directly
|
776
|
+
return false;
|
777
|
+
case LogicalTypeId::STRUCT: {
|
778
|
+
auto &child_types = StructType::GetChildTypes(type);
|
779
|
+
for (auto &entry : child_types) {
|
780
|
+
if (!TypeSupportsRegularUpdate(entry.second)) {
|
781
|
+
return false;
|
782
|
+
}
|
783
|
+
}
|
784
|
+
return true;
|
785
|
+
}
|
786
|
+
default:
|
787
|
+
return true;
|
788
|
+
}
|
789
|
+
}
|
790
|
+
|
791
|
+
void DuckTableEntry::BindUpdateConstraints(LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update) {
|
792
|
+
TableCatalogEntry::BindUpdateConstraints(get, proj, update);
|
793
|
+
// check the constraints and indexes of the table to see if we need to project any additional columns
|
794
|
+
// we do this for indexes with multiple columns and CHECK constraints in the UPDATE clause
|
795
|
+
// suppose we have a constraint CHECK(i + j < 10); now we need both i and j to check the constraint
|
796
|
+
// if we are only updating one of the two columns we add the other one to the UPDATE set
|
797
|
+
// with a "useless" update (i.e. i=i) so we can verify that the CHECK constraint is not violated
|
798
|
+
for (auto &constraint : GetBoundConstraints()) {
|
799
|
+
if (constraint->type == ConstraintType::CHECK) {
|
800
|
+
auto &check = constraint->Cast<BoundCheckConstraint>();
|
801
|
+
// check constraint! check if we need to add any extra columns to the UPDATE clause
|
802
|
+
BindExtraColumns(*this, get, proj, update, check.bound_columns);
|
803
|
+
}
|
804
|
+
}
|
805
|
+
auto &table_storage = GetStorage();
|
806
|
+
if (update.return_chunk) {
|
807
|
+
physical_index_set_t all_columns;
|
808
|
+
for (idx_t i = 0; i < table_storage.column_definitions.size(); i++) {
|
809
|
+
all_columns.insert(PhysicalIndex(i));
|
810
|
+
}
|
811
|
+
BindExtraColumns(*this, get, proj, update, all_columns);
|
812
|
+
}
|
813
|
+
// for index updates we always turn any update into an insert and a delete
|
814
|
+
// we thus need all the columns to be available, hence we check if the update touches any index columns
|
815
|
+
// If the returning keyword is used, we need access to the whole row in case the user requests it.
|
816
|
+
// Therefore switch the update to a delete and insert.
|
817
|
+
update.update_is_del_and_insert = false;
|
818
|
+
table_storage.info->indexes.Scan([&](Index &index) {
|
819
|
+
if (index.IndexIsUpdated(update.columns)) {
|
820
|
+
update.update_is_del_and_insert = true;
|
821
|
+
return true;
|
822
|
+
}
|
823
|
+
return false;
|
824
|
+
});
|
825
|
+
|
826
|
+
// we also convert any updates on LIST columns into delete + insert
|
827
|
+
for (auto &col_index : update.columns) {
|
828
|
+
auto &column = GetColumns().GetColumn(col_index);
|
829
|
+
if (!TypeSupportsRegularUpdate(column.Type())) {
|
830
|
+
update.update_is_del_and_insert = true;
|
831
|
+
break;
|
832
|
+
}
|
833
|
+
}
|
834
|
+
|
835
|
+
if (update.update_is_del_and_insert) {
|
836
|
+
// the update updates a column required by an index or requires returning the updated rows,
|
837
|
+
// push projections for all columns
|
838
|
+
physical_index_set_t all_columns;
|
839
|
+
for (idx_t i = 0; i < table_storage.column_definitions.size(); i++) {
|
840
|
+
all_columns.insert(PhysicalIndex(i));
|
841
|
+
}
|
842
|
+
BindExtraColumns(*this, get, proj, update, all_columns);
|
843
|
+
}
|
844
|
+
}
|
845
|
+
|
733
846
|
} // namespace duckdb
|
@@ -214,6 +214,10 @@ DataTable &TableCatalogEntry::GetStorage() {
|
|
214
214
|
const vector<unique_ptr<BoundConstraint>> &TableCatalogEntry::GetBoundConstraints() {
|
215
215
|
throw InternalException("Calling GetBoundConstraints on a TableCatalogEntry that is not a DuckTableEntry");
|
216
216
|
}
|
217
|
+
|
217
218
|
// LCOV_EXCL_STOP
|
218
219
|
|
220
|
+
void TableCatalogEntry::BindUpdateConstraints(LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update) {
|
221
|
+
}
|
222
|
+
|
219
223
|
} // namespace duckdb
|
@@ -126,37 +126,57 @@ void CatalogSearchPath::Reset() {
|
|
126
126
|
SetPaths(empty);
|
127
127
|
}
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
string CatalogSearchPath::GetSetName(CatalogSetPathType set_type) {
|
130
|
+
switch (set_type) {
|
131
|
+
case CatalogSetPathType::SET_SCHEMA:
|
132
|
+
return "SET schema";
|
133
|
+
case CatalogSetPathType::SET_SCHEMAS:
|
134
|
+
return "SET search_path";
|
135
|
+
default:
|
136
|
+
throw InternalException("Unrecognized CatalogSetPathType");
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
void CatalogSearchPath::Set(vector<CatalogSearchEntry> new_paths, CatalogSetPathType set_type) {
|
141
|
+
if (set_type != CatalogSetPathType::SET_SCHEMAS && new_paths.size() != 1) {
|
142
|
+
throw CatalogException("%s can set only 1 schema. This has %d", GetSetName(set_type), new_paths.size());
|
132
143
|
}
|
133
144
|
for (auto &path : new_paths) {
|
134
|
-
|
145
|
+
auto schema_entry = Catalog::GetSchema(context, path.catalog, path.schema, OnEntryNotFound::RETURN_NULL);
|
146
|
+
if (schema_entry) {
|
147
|
+
// we are setting a schema - update the catalog and schema
|
135
148
|
if (path.catalog.empty()) {
|
136
|
-
|
137
|
-
|
149
|
+
path.catalog = GetDefault().catalog;
|
150
|
+
}
|
151
|
+
continue;
|
152
|
+
}
|
153
|
+
// only schema supplied - check if this is a catalog instead
|
154
|
+
if (path.catalog.empty()) {
|
155
|
+
auto catalog = Catalog::GetCatalogEntry(context, path.schema);
|
156
|
+
if (catalog) {
|
157
|
+
auto schema = catalog->GetSchema(context, DEFAULT_SCHEMA, OnEntryNotFound::RETURN_NULL);
|
138
158
|
if (schema) {
|
139
159
|
path.catalog = std::move(path.schema);
|
140
160
|
path.schema = schema->name;
|
141
161
|
continue;
|
142
162
|
}
|
143
163
|
}
|
144
|
-
throw CatalogException("SET %s: No catalog + schema named %s found.",
|
145
|
-
is_set_schema ? "schema" : "search_path", path.ToString());
|
146
164
|
}
|
165
|
+
throw CatalogException("%s: No catalog + schema named \"%s\" found.", GetSetName(set_type), path.ToString());
|
147
166
|
}
|
148
|
-
if (
|
167
|
+
if (set_type == CatalogSetPathType::SET_SCHEMA) {
|
149
168
|
if (new_paths[0].catalog == TEMP_CATALOG || new_paths[0].catalog == SYSTEM_CATALOG) {
|
150
|
-
throw CatalogException("
|
169
|
+
throw CatalogException("%s cannot be set to internal schema \"%s\"", GetSetName(set_type),
|
170
|
+
new_paths[0].catalog);
|
151
171
|
}
|
152
172
|
}
|
153
173
|
this->set_paths = std::move(new_paths);
|
154
174
|
SetPaths(set_paths);
|
155
175
|
}
|
156
176
|
|
157
|
-
void CatalogSearchPath::Set(CatalogSearchEntry new_value,
|
177
|
+
void CatalogSearchPath::Set(CatalogSearchEntry new_value, CatalogSetPathType set_type) {
|
158
178
|
vector<CatalogSearchEntry> new_paths {std::move(new_value)};
|
159
|
-
Set(std::move(new_paths),
|
179
|
+
Set(std::move(new_paths), set_type);
|
160
180
|
}
|
161
181
|
|
162
182
|
const vector<CatalogSearchEntry> &CatalogSearchPath::Get() {
|
@@ -73,8 +73,11 @@ string Exception::ConstructMessageRecursive(const string &msg, std::vector<Excep
|
|
73
73
|
parameter_count++;
|
74
74
|
}
|
75
75
|
if (parameter_count != values.size()) {
|
76
|
-
throw InternalException("
|
76
|
+
throw InternalException("Primary exception: %s\nSecondary exception in ConstructMessageRecursive: Expected %d "
|
77
|
+
"parameters, received %d",
|
78
|
+
msg.c_str(), parameter_count, values.size());
|
77
79
|
}
|
80
|
+
|
78
81
|
#endif
|
79
82
|
return ExceptionFormatValue::Format(msg, values);
|
80
83
|
}
|
@@ -68,22 +68,27 @@ ExceptionFormatValue ExceptionFormatValue::CreateFormatValue(hugeint_t value) {
|
|
68
68
|
}
|
69
69
|
|
70
70
|
string ExceptionFormatValue::Format(const string &msg, std::vector<ExceptionFormatValue> &values) {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
71
|
+
try {
|
72
|
+
std::vector<duckdb_fmt::basic_format_arg<duckdb_fmt::printf_context>> format_args;
|
73
|
+
for (auto &val : values) {
|
74
|
+
switch (val.type) {
|
75
|
+
case ExceptionFormatValueType::FORMAT_VALUE_TYPE_DOUBLE:
|
76
|
+
format_args.push_back(duckdb_fmt::internal::make_arg<duckdb_fmt::printf_context>(val.dbl_val));
|
77
|
+
break;
|
78
|
+
case ExceptionFormatValueType::FORMAT_VALUE_TYPE_INTEGER:
|
79
|
+
format_args.push_back(duckdb_fmt::internal::make_arg<duckdb_fmt::printf_context>(val.int_val));
|
80
|
+
break;
|
81
|
+
case ExceptionFormatValueType::FORMAT_VALUE_TYPE_STRING:
|
82
|
+
format_args.push_back(duckdb_fmt::internal::make_arg<duckdb_fmt::printf_context>(val.str_val));
|
83
|
+
break;
|
84
|
+
}
|
83
85
|
}
|
86
|
+
return duckdb_fmt::vsprintf(msg, duckdb_fmt::basic_format_args<duckdb_fmt::printf_context>(
|
87
|
+
format_args.data(), static_cast<int>(format_args.size())));
|
88
|
+
} catch (std::exception &ex) {
|
89
|
+
throw InternalException(std::string("Primary exception: ") + msg +
|
90
|
+
"\nSecondary exception in ExceptionFormatValue: " + ex.what());
|
84
91
|
}
|
85
|
-
return duckdb_fmt::vsprintf(msg, duckdb_fmt::basic_format_args<duckdb_fmt::printf_context>(
|
86
|
-
format_args.data(), static_cast<int>(format_args.size())));
|
87
92
|
}
|
88
93
|
|
89
94
|
} // namespace duckdb
|
@@ -1,8 +1,8 @@
|
|
1
1
|
#ifndef DUCKDB_VERSION
|
2
|
-
#define DUCKDB_VERSION "0.8.1-
|
2
|
+
#define DUCKDB_VERSION "0.8.1-dev125"
|
3
3
|
#endif
|
4
4
|
#ifndef DUCKDB_SOURCE_ID
|
5
|
-
#define DUCKDB_SOURCE_ID "
|
5
|
+
#define DUCKDB_SOURCE_ID "09486d2b9d"
|
6
6
|
#endif
|
7
7
|
#include "duckdb/function/table/system_functions.hpp"
|
8
8
|
#include "duckdb/main/database.hpp"
|
@@ -45,6 +45,8 @@ public:
|
|
45
45
|
return true;
|
46
46
|
}
|
47
47
|
|
48
|
+
void BindUpdateConstraints(LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update) override;
|
49
|
+
|
48
50
|
private:
|
49
51
|
unique_ptr<CatalogEntry> RenameColumn(ClientContext &context, RenameColumnInfo &info);
|
50
52
|
unique_ptr<CatalogEntry> AddColumn(ClientContext &context, AddColumnInfo &info);
|
@@ -40,6 +40,10 @@ class TableColumnInfo;
|
|
40
40
|
class TableIndexInfo;
|
41
41
|
class TableStorageInfo;
|
42
42
|
|
43
|
+
class LogicalGet;
|
44
|
+
class LogicalProjection;
|
45
|
+
class LogicalUpdate;
|
46
|
+
|
43
47
|
//! A table catalog entry
|
44
48
|
class TableCatalogEntry : public StandardEntry {
|
45
49
|
public:
|
@@ -102,6 +106,8 @@ public:
|
|
102
106
|
//! Returns the storage info of this table
|
103
107
|
virtual TableStorageInfo GetStorageInfo(ClientContext &context) = 0;
|
104
108
|
|
109
|
+
DUCKDB_API virtual void BindUpdateConstraints(LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update);
|
110
|
+
|
105
111
|
protected:
|
106
112
|
// This is used to serialize the entry by #Serialize(Serializer& ). It is virtual to allow
|
107
113
|
// Custom catalog implementations to override the default implementation. We can not make
|
@@ -35,14 +35,16 @@ private:
|
|
35
35
|
static string WriteOptionallyQuoted(const string &input);
|
36
36
|
};
|
37
37
|
|
38
|
+
enum class CatalogSetPathType { SET_SCHEMA, SET_SCHEMAS };
|
39
|
+
|
38
40
|
//! The schema search path, in order by which entries are searched if no schema entry is provided
|
39
41
|
class CatalogSearchPath {
|
40
42
|
public:
|
41
43
|
DUCKDB_API explicit CatalogSearchPath(ClientContext &client_p);
|
42
44
|
CatalogSearchPath(const CatalogSearchPath &other) = delete;
|
43
45
|
|
44
|
-
DUCKDB_API void Set(CatalogSearchEntry new_value,
|
45
|
-
DUCKDB_API void Set(vector<CatalogSearchEntry> new_paths,
|
46
|
+
DUCKDB_API void Set(CatalogSearchEntry new_value, CatalogSetPathType set_type);
|
47
|
+
DUCKDB_API void Set(vector<CatalogSearchEntry> new_paths, CatalogSetPathType set_type);
|
46
48
|
DUCKDB_API void Reset();
|
47
49
|
|
48
50
|
DUCKDB_API const vector<CatalogSearchEntry> &Get();
|
@@ -59,6 +61,8 @@ public:
|
|
59
61
|
private:
|
60
62
|
void SetPaths(vector<CatalogSearchEntry> new_paths);
|
61
63
|
|
64
|
+
string GetSetName(CatalogSetPathType set_type);
|
65
|
+
|
62
66
|
private:
|
63
67
|
ClientContext &context;
|
64
68
|
vector<CatalogSearchEntry> paths;
|
@@ -105,6 +105,9 @@ public:
|
|
105
105
|
|
106
106
|
template <typename... Args>
|
107
107
|
static string ConstructMessage(const string &msg, Args... params) {
|
108
|
+
const std::size_t num_args = sizeof...(Args);
|
109
|
+
if (num_args == 0)
|
110
|
+
return msg;
|
108
111
|
std::vector<ExceptionFormatValue> values;
|
109
112
|
return ConstructMessageRecursive(msg, values, params...);
|
110
113
|
}
|
@@ -988,7 +988,7 @@ void SchemaSetting::ResetLocal(ClientContext &context) {
|
|
988
988
|
void SchemaSetting::SetLocal(ClientContext &context, const Value &input) {
|
989
989
|
auto parameter = input.ToString();
|
990
990
|
auto &client_data = ClientData::Get(context);
|
991
|
-
client_data.catalog_search_path->Set(CatalogSearchEntry::Parse(parameter),
|
991
|
+
client_data.catalog_search_path->Set(CatalogSearchEntry::Parse(parameter), CatalogSetPathType::SET_SCHEMA);
|
992
992
|
}
|
993
993
|
|
994
994
|
Value SchemaSetting::GetSetting(ClientContext &context) {
|
@@ -1008,7 +1008,7 @@ void SearchPathSetting::ResetLocal(ClientContext &context) {
|
|
1008
1008
|
void SearchPathSetting::SetLocal(ClientContext &context, const Value &input) {
|
1009
1009
|
auto parameter = input.ToString();
|
1010
1010
|
auto &client_data = ClientData::Get(context);
|
1011
|
-
client_data.catalog_search_path->Set(CatalogSearchEntry::ParseList(parameter),
|
1011
|
+
client_data.catalog_search_path->Set(CatalogSearchEntry::ParseList(parameter), CatalogSetPathType::SET_SCHEMAS);
|
1012
1012
|
}
|
1013
1013
|
|
1014
1014
|
Value SearchPathSetting::GetSetting(ClientContext &context) {
|
@@ -20,119 +20,6 @@
|
|
20
20
|
|
21
21
|
namespace duckdb {
|
22
22
|
|
23
|
-
static void BindExtraColumns(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj, LogicalUpdate &update,
|
24
|
-
physical_index_set_t &bound_columns) {
|
25
|
-
if (bound_columns.size() <= 1) {
|
26
|
-
return;
|
27
|
-
}
|
28
|
-
idx_t found_column_count = 0;
|
29
|
-
physical_index_set_t found_columns;
|
30
|
-
for (idx_t i = 0; i < update.columns.size(); i++) {
|
31
|
-
if (bound_columns.find(update.columns[i]) != bound_columns.end()) {
|
32
|
-
// this column is referenced in the CHECK constraint
|
33
|
-
found_column_count++;
|
34
|
-
found_columns.insert(update.columns[i]);
|
35
|
-
}
|
36
|
-
}
|
37
|
-
if (found_column_count > 0 && found_column_count != bound_columns.size()) {
|
38
|
-
// columns in this CHECK constraint were referenced, but not all were part of the UPDATE
|
39
|
-
// add them to the scan and update set
|
40
|
-
for (auto &check_column_id : bound_columns) {
|
41
|
-
if (found_columns.find(check_column_id) != found_columns.end()) {
|
42
|
-
// column is already projected
|
43
|
-
continue;
|
44
|
-
}
|
45
|
-
// column is not projected yet: project it by adding the clause "i=i" to the set of updated columns
|
46
|
-
auto &column = table.GetColumns().GetColumn(check_column_id);
|
47
|
-
update.expressions.push_back(make_uniq<BoundColumnRefExpression>(
|
48
|
-
column.Type(), ColumnBinding(proj.table_index, proj.expressions.size())));
|
49
|
-
proj.expressions.push_back(make_uniq<BoundColumnRefExpression>(
|
50
|
-
column.Type(), ColumnBinding(get.table_index, get.column_ids.size())));
|
51
|
-
get.column_ids.push_back(check_column_id.index);
|
52
|
-
update.columns.push_back(check_column_id);
|
53
|
-
}
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
static bool TypeSupportsRegularUpdate(const LogicalType &type) {
|
58
|
-
switch (type.id()) {
|
59
|
-
case LogicalTypeId::LIST:
|
60
|
-
case LogicalTypeId::MAP:
|
61
|
-
case LogicalTypeId::UNION:
|
62
|
-
// lists and maps and unions don't support updates directly
|
63
|
-
return false;
|
64
|
-
case LogicalTypeId::STRUCT: {
|
65
|
-
auto &child_types = StructType::GetChildTypes(type);
|
66
|
-
for (auto &entry : child_types) {
|
67
|
-
if (!TypeSupportsRegularUpdate(entry.second)) {
|
68
|
-
return false;
|
69
|
-
}
|
70
|
-
}
|
71
|
-
return true;
|
72
|
-
}
|
73
|
-
default:
|
74
|
-
return true;
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
static void BindUpdateConstraints(TableCatalogEntry &table, LogicalGet &get, LogicalProjection &proj,
|
79
|
-
LogicalUpdate &update) {
|
80
|
-
if (!table.IsDuckTable()) {
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
// check the constraints and indexes of the table to see if we need to project any additional columns
|
84
|
-
// we do this for indexes with multiple columns and CHECK constraints in the UPDATE clause
|
85
|
-
// suppose we have a constraint CHECK(i + j < 10); now we need both i and j to check the constraint
|
86
|
-
// if we are only updating one of the two columns we add the other one to the UPDATE set
|
87
|
-
// with a "useless" update (i.e. i=i) so we can verify that the CHECK constraint is not violated
|
88
|
-
for (auto &constraint : table.GetBoundConstraints()) {
|
89
|
-
if (constraint->type == ConstraintType::CHECK) {
|
90
|
-
auto &check = constraint->Cast<BoundCheckConstraint>();
|
91
|
-
// check constraint! check if we need to add any extra columns to the UPDATE clause
|
92
|
-
BindExtraColumns(table, get, proj, update, check.bound_columns);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
auto &storage = table.GetStorage();
|
96
|
-
if (update.return_chunk) {
|
97
|
-
physical_index_set_t all_columns;
|
98
|
-
for (idx_t i = 0; i < storage.column_definitions.size(); i++) {
|
99
|
-
all_columns.insert(PhysicalIndex(i));
|
100
|
-
}
|
101
|
-
BindExtraColumns(table, get, proj, update, all_columns);
|
102
|
-
}
|
103
|
-
// for index updates we always turn any update into an insert and a delete
|
104
|
-
// we thus need all the columns to be available, hence we check if the update touches any index columns
|
105
|
-
// If the returning keyword is used, we need access to the whole row in case the user requests it.
|
106
|
-
// Therefore switch the update to a delete and insert.
|
107
|
-
update.update_is_del_and_insert = false;
|
108
|
-
storage.info->indexes.Scan([&](Index &index) {
|
109
|
-
if (index.IndexIsUpdated(update.columns)) {
|
110
|
-
update.update_is_del_and_insert = true;
|
111
|
-
return true;
|
112
|
-
}
|
113
|
-
return false;
|
114
|
-
});
|
115
|
-
|
116
|
-
// we also convert any updates on LIST columns into delete + insert
|
117
|
-
for (auto &col_index : update.columns) {
|
118
|
-
auto &column = table.GetColumns().GetColumn(col_index);
|
119
|
-
if (!TypeSupportsRegularUpdate(column.Type())) {
|
120
|
-
update.update_is_del_and_insert = true;
|
121
|
-
break;
|
122
|
-
}
|
123
|
-
}
|
124
|
-
|
125
|
-
if (update.update_is_del_and_insert) {
|
126
|
-
// the update updates a column required by an index or requires returning the updated rows,
|
127
|
-
// push projections for all columns
|
128
|
-
physical_index_set_t all_columns;
|
129
|
-
for (idx_t i = 0; i < storage.column_definitions.size(); i++) {
|
130
|
-
all_columns.insert(PhysicalIndex(i));
|
131
|
-
}
|
132
|
-
BindExtraColumns(table, get, proj, update, all_columns);
|
133
|
-
}
|
134
|
-
}
|
135
|
-
|
136
23
|
// This creates a LogicalProjection and moves 'root' into it as a child
|
137
24
|
// unless there are no expressions to project, in which case it just returns 'root'
|
138
25
|
unique_ptr<LogicalOperator> Binder::BindUpdateSet(LogicalOperator &op, unique_ptr<LogicalOperator> root,
|
@@ -239,7 +126,8 @@ BoundStatement Binder::Bind(UpdateStatement &stmt) {
|
|
239
126
|
auto proj = unique_ptr_cast<LogicalOperator, LogicalProjection>(std::move(proj_tmp));
|
240
127
|
|
241
128
|
// bind any extra columns necessary for CHECK constraints or indexes
|
242
|
-
BindUpdateConstraints(
|
129
|
+
table.BindUpdateConstraints(*get, *proj, *update);
|
130
|
+
|
243
131
|
// finally add the row id column to the projection list
|
244
132
|
proj->expressions.push_back(make_uniq<BoundColumnRefExpression>(
|
245
133
|
LogicalType::ROW_TYPE, ColumnBinding(get->table_index, get->column_ids.size())));
|