1 | #include "duckdb/parser/column_definition.hpp" |
2 | #include "duckdb/parser/constraint.hpp" |
3 | #include "duckdb/parser/constraints/list.hpp" |
4 | #include "duckdb/parser/transformer.hpp" |
5 | |
6 | namespace duckdb { |
7 | |
8 | static void ParseSchemaTableNameFK(duckdb_libpgquery::PGRangeVar *input, ForeignKeyInfo &fk_info) { |
9 | if (input->catalogname) { |
10 | throw ParserException("FOREIGN KEY constraints cannot be defined cross-database" ); |
11 | } |
12 | if (input->schemaname) { |
13 | fk_info.schema = input->schemaname; |
14 | } else { |
15 | fk_info.schema = "" ; |
16 | }; |
17 | fk_info.table = input->relname; |
18 | } |
19 | |
20 | unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGListCell *cell) { |
21 | auto constraint = reinterpret_cast<duckdb_libpgquery::PGConstraint *>(cell->data.ptr_value); |
22 | switch (constraint->contype) { |
23 | case duckdb_libpgquery::PG_CONSTR_UNIQUE: |
24 | case duckdb_libpgquery::PG_CONSTR_PRIMARY: { |
25 | bool is_primary_key = constraint->contype == duckdb_libpgquery::PG_CONSTR_PRIMARY; |
26 | vector<string> columns; |
27 | for (auto kc = constraint->keys->head; kc; kc = kc->next) { |
28 | columns.emplace_back(args&: reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str); |
29 | } |
30 | return make_uniq<UniqueConstraint>(args&: columns, args&: is_primary_key); |
31 | } |
32 | case duckdb_libpgquery::PG_CONSTR_CHECK: { |
33 | auto expression = TransformExpression(node: constraint->raw_expr); |
34 | if (expression->HasSubquery()) { |
35 | throw ParserException("subqueries prohibited in CHECK constraints" ); |
36 | } |
37 | return make_uniq<CheckConstraint>(args: TransformExpression(node: constraint->raw_expr)); |
38 | } |
39 | case duckdb_libpgquery::PG_CONSTR_FOREIGN: { |
40 | ForeignKeyInfo fk_info; |
41 | fk_info.type = ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE; |
42 | ParseSchemaTableNameFK(input: constraint->pktable, fk_info); |
43 | vector<string> pk_columns, fk_columns; |
44 | for (auto kc = constraint->fk_attrs->head; kc; kc = kc->next) { |
45 | fk_columns.emplace_back(args&: reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str); |
46 | } |
47 | if (constraint->pk_attrs) { |
48 | for (auto kc = constraint->pk_attrs->head; kc; kc = kc->next) { |
49 | pk_columns.emplace_back(args&: reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str); |
50 | } |
51 | } |
52 | if (!pk_columns.empty() && pk_columns.size() != fk_columns.size()) { |
53 | throw ParserException("The number of referencing and referenced columns for foreign keys must be the same" ); |
54 | } |
55 | if (fk_columns.empty()) { |
56 | throw ParserException("The set of referencing and referenced columns for foreign keys must be not empty" ); |
57 | } |
58 | return make_uniq<ForeignKeyConstraint>(args&: pk_columns, args&: fk_columns, args: std::move(fk_info)); |
59 | } |
60 | default: |
61 | throw NotImplementedException("Constraint type not handled yet!" ); |
62 | } |
63 | } |
64 | |
65 | unique_ptr<Constraint> Transformer::TransformConstraint(duckdb_libpgquery::PGListCell *cell, ColumnDefinition &column, |
66 | idx_t index) { |
67 | auto constraint = reinterpret_cast<duckdb_libpgquery::PGConstraint *>(cell->data.ptr_value); |
68 | D_ASSERT(constraint); |
69 | switch (constraint->contype) { |
70 | case duckdb_libpgquery::PG_CONSTR_NOTNULL: |
71 | return make_uniq<NotNullConstraint>(args: LogicalIndex(index)); |
72 | case duckdb_libpgquery::PG_CONSTR_CHECK: |
73 | return TransformConstraint(cell); |
74 | case duckdb_libpgquery::PG_CONSTR_PRIMARY: |
75 | return make_uniq<UniqueConstraint>(args: LogicalIndex(index), args: true); |
76 | case duckdb_libpgquery::PG_CONSTR_UNIQUE: |
77 | return make_uniq<UniqueConstraint>(args: LogicalIndex(index), args: false); |
78 | case duckdb_libpgquery::PG_CONSTR_NULL: |
79 | return nullptr; |
80 | case duckdb_libpgquery::PG_CONSTR_GENERATED_VIRTUAL: { |
81 | if (column.DefaultValue()) { |
82 | throw InvalidInputException("DEFAULT constraint on GENERATED column \"%s\" is not allowed" , column.Name()); |
83 | } |
84 | column.SetGeneratedExpression(TransformExpression(node: constraint->raw_expr)); |
85 | return nullptr; |
86 | } |
87 | case duckdb_libpgquery::PG_CONSTR_GENERATED_STORED: |
88 | throw InvalidInputException("Can not create a STORED generated column!" ); |
89 | case duckdb_libpgquery::PG_CONSTR_DEFAULT: |
90 | column.SetDefaultValue(TransformExpression(node: constraint->raw_expr)); |
91 | return nullptr; |
92 | case duckdb_libpgquery::PG_CONSTR_COMPRESSION: |
93 | column.SetCompressionType(CompressionTypeFromString(str: constraint->compression_name)); |
94 | if (column.CompressionType() == CompressionType::COMPRESSION_AUTO) { |
95 | throw ParserException("Unrecognized option for column compression, expected none, uncompressed, rle, " |
96 | "dictionary, pfor, bitpacking or fsst" ); |
97 | } |
98 | return nullptr; |
99 | case duckdb_libpgquery::PG_CONSTR_FOREIGN: { |
100 | ForeignKeyInfo fk_info; |
101 | fk_info.type = ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE; |
102 | ParseSchemaTableNameFK(input: constraint->pktable, fk_info); |
103 | |
104 | vector<string> pk_columns, fk_columns; |
105 | fk_columns.emplace_back(args: column.Name().c_str()); |
106 | if (constraint->pk_attrs) { |
107 | for (auto kc = constraint->pk_attrs->head; kc; kc = kc->next) { |
108 | pk_columns.emplace_back(args&: reinterpret_cast<duckdb_libpgquery::PGValue *>(kc->data.ptr_value)->val.str); |
109 | } |
110 | } |
111 | if (!pk_columns.empty() && pk_columns.size() != fk_columns.size()) { |
112 | throw ParserException("The number of referencing and referenced columns for foreign keys must be the same" ); |
113 | } |
114 | return make_uniq<ForeignKeyConstraint>(args&: pk_columns, args&: fk_columns, args: std::move(fk_info)); |
115 | } |
116 | default: |
117 | throw NotImplementedException("Constraint not implemented!" ); |
118 | } |
119 | } |
120 | |
121 | } // namespace duckdb |
122 | |