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
6namespace duckdb {
7
8static 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
20unique_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
65unique_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