Skip to content

Commit 87e6eac

Browse files
authored
Add FOREIGN KEY constraints to CREATE TABLE statement (#252)
Allow to specify foreign keys during table creation, both as an own `table element` or as a column constraint.
1 parent 6e9d1ad commit 87e6eac

File tree

13 files changed

+4789
-4500
lines changed

13 files changed

+4789
-4500
lines changed

src/parser/bison_parser.cpp

Lines changed: 1994 additions & 1952 deletions
Large diffs are not rendered by default.

src/parser/bison_parser.h

Lines changed: 158 additions & 155 deletions
Large diffs are not rendered by default.

src/parser/bison_parser.y

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@
6363
// %output "bison_parser.cpp"
6464
// %defines "bison_parser.h"
6565

66-
// Tell bison to create a reentrant parser
66+
// Raise error on shift/reduce conflict, i.e., when bison's one-token lookahead cannot decide on a single next state.
67+
// Without this line, only a warning is printed. The line raises an error when the expected number of conflicts (0)
68+
// does not occur.
69+
%expect 0
70+
71+
// Tell bison to create a reentrant parser.
6772
%define api.pure full
6873

6974
// Prefix the parser
@@ -120,6 +125,7 @@
120125

121126
hsql::Alias* alias_t;
122127
hsql::AlterAction* alter_action_t;
128+
hsql::ColumnConstraints* column_constraints_t;
123129
hsql::ColumnDefinition* column_t;
124130
hsql::ColumnType column_type_t;
125131
hsql::ConstraintType column_constraint_t;
@@ -136,6 +142,7 @@
136142
hsql::LockingClause* locking_t;
137143
hsql::OrderDescription* order;
138144
hsql::OrderType order_type;
145+
hsql::ReferencesSpecification* references_spec_t;
139146
hsql::SetOperation* set_operator_t;
140147
hsql::TableConstraint* table_constraint_t;
141148
hsql::TableElement* table_element_t;
@@ -146,7 +153,6 @@
146153
hsql::WithDescription* with_description_t;
147154

148155
std::vector<char*>* str_vec;
149-
std::unordered_set<hsql::ConstraintType>* column_constraint_set;
150156
std::vector<hsql::Expr*>* expr_vec;
151157
std::vector<hsql::OrderDescription*>* order_vec;
152158
std::vector<hsql::SQLStatement*>* stmt_vec;
@@ -170,10 +176,10 @@
170176
** Destructor symbols
171177
*********************************/
172178

173-
%destructor { } <fval> <ival> <bval> <join_type> <order_type> <datetime_field> <column_type_t> <column_constraint_t> <import_type_t> <column_constraint_set> <lock_mode_t> <lock_wait_policy_t> <frame_type>
179+
%destructor { } <fval> <ival> <bval> <join_type> <order_type> <datetime_field> <column_type_t> <column_constraint_t> <import_type_t> <lock_mode_t> <lock_wait_policy_t> <frame_type>
174180
%destructor {
175-
free( ($$.name) );
176-
free( ($$.schema) );
181+
free($$.name);
182+
free($$.schema);
177183
} <table_name>
178184
%destructor {
179185
if ($$) {
@@ -183,7 +189,7 @@
183189
}
184190
delete ($$);
185191
} <str_vec>
186-
%destructor { free( ($$) ); } <sval>
192+
%destructor { free($$); } <sval>
187193
%destructor {
188194
if ($$) {
189195
for (auto ptr : *($$)) {
@@ -206,23 +212,24 @@
206212
%token DEALLOCATE PARAMETERS INTERSECT TEMPORARY TIMESTAMP
207213
%token DISTINCT NVARCHAR RESTRICT TRUNCATE ANALYZE BETWEEN
208214
%token CASCADE COLUMNS CONTROL DEFAULT EXECUTE EXPLAIN ENCODING
209-
%token INTEGER NATURAL PREPARE PRIMARY SCHEMAS CHARACTER_VARYING REAL DECIMAL SMALLINT BIGINT
215+
%token INTEGER NATURAL PREPARE SCHEMAS CHARACTER_VARYING REAL DECIMAL SMALLINT BIGINT
210216
%token SPATIAL VARCHAR VIRTUAL DESCRIBE BEFORE COLUMN CREATE DELETE DIRECT
211217
%token DOUBLE ESCAPE EXCEPT EXISTS EXTRACT CAST FORMAT GLOBAL HAVING IMPORT
212218
%token INSERT ISNULL OFFSET RENAME SCHEMA SELECT SORTED
213-
%token TABLES UNIQUE UNLOAD UPDATE VALUES AFTER ALTER CROSS
219+
%token TABLES UNLOAD UPDATE VALUES AFTER ALTER CROSS
214220
%token DELTA FLOAT GROUP INDEX INNER LIMIT LOCAL MERGE MINUS ORDER OVER
215221
%token OUTER RIGHT TABLE UNION USING WHERE CALL CASE CHAR COPY DATE DATETIME
216222
%token DESC DROP ELSE FILE FROM FULL HASH HINT INTO JOIN
217223
%token LEFT LIKE LOAD LONG NULL PARTITION PLAN SHOW TEXT THEN TIME
218-
%token VIEW WHEN WITH ADD ALL AND ASC END FOR INT KEY
224+
%token VIEW WHEN WITH ADD ALL AND ASC END FOR INT
219225
%token NOT OFF SET TOP AS BY IF IN IS OF ON OR TO NO
220226
%token ARRAY CONCAT ILIKE SECOND MINUTE HOUR DAY MONTH YEAR
221227
%token SECONDS MINUTES HOURS DAYS MONTHS YEARS INTERVAL
222228
%token TRUE FALSE BOOLEAN
223229
%token TRANSACTION BEGIN COMMIT ROLLBACK
224230
%token NOWAIT SKIP LOCKED SHARE
225231
%token RANGE ROWS GROUPS UNBOUNDED FOLLOWING PRECEDING CURRENT_ROW
232+
%token UNIQUE PRIMARY FOREIGN KEY REFERENCES
226233

227234
/*********************************
228235
** Non-Terminal types (http://www.gnu.org/software/bison/manual/html_node/Type-Decl.html)
@@ -267,6 +274,7 @@
267274
%type <column_t> column_def
268275
%type <table_element_t> table_elem
269276
%type <column_type_t> column_type
277+
%type <references_spec_t> references_spec
270278
%type <table_constraint_t> table_constraint
271279
%type <update_t> update_clause
272280
%type <locking_t> locking_clause
@@ -275,8 +283,7 @@
275283
%type <with_description_t> with_description
276284
%type <set_operator_t> set_operator set_type
277285
%type <column_constraint_t> column_constraint
278-
%type <column_constraint_set> opt_column_constraints
279-
%type <column_constraint_set> column_constraint_set
286+
%type <column_constraints_t> opt_column_constraints column_constraints
280287
%type <alter_action_t> alter_action
281288
%type <drop_action_t> drop_action
282289
%type <lock_wait_policy_t> opt_row_lock_policy
@@ -478,9 +485,7 @@ file_type : IDENTIFIER {
478485
free($1);
479486
};
480487

481-
file_path : STRING {
482-
$$ = $1;
483-
};
488+
file_path : STRING { $$ = $1; };
484489

485490
opt_import_export_options : WITH '(' import_export_options ')' { $$ = $3; }
486491
| '(' import_export_options ')' { $$ = $2; }
@@ -627,10 +632,11 @@ table_elem : column_def { $$ = $1; }
627632
| table_constraint { $$ = $1; };
628633

629634
column_def : IDENTIFIER column_type opt_column_constraints {
630-
$$ = new ColumnDefinition($1, $2, $3);
635+
$$ = new ColumnDefinition($1, $2, $3->constraints, $3->references);
631636
if (!$$->trySetNullableExplicit()) {
632637
yyerror(&yyloc, result, scanner, ("Conflicting nullability constraints for " + std::string{$1}).c_str());
633638
}
639+
delete $3;
634640
};
635641

636642
column_type : BIGINT { $$ = ColumnType{DataType::BIGINT}; }
@@ -653,7 +659,7 @@ column_type : BIGINT { $$ = ColumnType{DataType::BIGINT}; }
653659
| TEXT { $$ = ColumnType{DataType::TEXT}; }
654660
| TIME opt_time_precision { $$ = ColumnType{DataType::TIME, 0, $2}; }
655661
| TIMESTAMP { $$ = ColumnType{DataType::DATETIME}; }
656-
| VARCHAR '(' INTVAL ')' { $$ = ColumnType{DataType::VARCHAR, $3}; }
662+
| VARCHAR '(' INTVAL ')' { $$ = ColumnType{DataType::VARCHAR, $3}; };
657663

658664
opt_time_precision : '(' INTVAL ')' { $$ = $2; }
659665
| /* empty */ { $$ = 0; };
@@ -662,25 +668,40 @@ opt_decimal_specification : '(' INTVAL ',' INTVAL ')' { $$ = new std::pair<int64
662668
| '(' INTVAL ')' { $$ = new std::pair<int64_t, int64_t>{$2, 0}; }
663669
| /* empty */ { $$ = new std::pair<int64_t, int64_t>{0, 0}; };
664670

665-
opt_column_constraints : column_constraint_set { $$ = $1; }
666-
| /* empty */ { $$ = new std::unordered_set<ConstraintType>(); };
671+
opt_column_constraints : column_constraints { $$ = $1; }
672+
| /* empty */ { $$ = new ColumnConstraints(); };
667673

668-
column_constraint_set : column_constraint {
669-
$$ = new std::unordered_set<ConstraintType>();
670-
$$->insert($1);
674+
column_constraints : column_constraint {
675+
$$ = new ColumnConstraints();
676+
$$->constraints->insert($1);
671677
}
672-
| column_constraint_set column_constraint {
673-
$1->insert($2);
678+
| column_constraints column_constraint {
679+
$1->constraints->insert($2);
674680
$$ = $1;
675681
}
682+
| references_spec {
683+
$$ = new ColumnConstraints();
684+
$$->constraints->insert(ConstraintType::ForeignKey);
685+
$$->references->emplace_back($1);
686+
}
687+
| column_constraints references_spec {
688+
// Multiple foreign keys for the same column could be possible, so we do not raise an error in that case.
689+
// Think of foreign keys referenced on multiple levels (returned item references sold item references items).
690+
$1->constraints->insert(ConstraintType::ForeignKey);
691+
$1->references->emplace_back($2);
692+
$$ = $1;
693+
};
676694

677695
column_constraint : PRIMARY KEY { $$ = ConstraintType::PrimaryKey; }
678696
| UNIQUE { $$ = ConstraintType::Unique; }
679697
| NULL { $$ = ConstraintType::Null; }
680698
| NOT NULL { $$ = ConstraintType::NotNull; };
681699

682700
table_constraint : PRIMARY KEY '(' ident_commalist ')' { $$ = new TableConstraint(ConstraintType::PrimaryKey, $4); }
683-
| UNIQUE '(' ident_commalist ')' { $$ = new TableConstraint(ConstraintType::Unique, $3); };
701+
| UNIQUE '(' ident_commalist ')' { $$ = new TableConstraint(ConstraintType::Unique, $3); }
702+
| FOREIGN KEY '(' ident_commalist ')' references_spec { $$ = new ForeignKeyConstraint($4, $6); };
703+
704+
references_spec : REFERENCES table_name opt_column_list { $$ = new ReferencesSpecification($2.schema, $2.name, $3); };
684705

685706
/******************************
686707
* Drop Statement

0 commit comments

Comments
 (0)