| 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 | |