Skip to content

Commit 926d452

Browse files
committed
full support for CHECK and FORIEGN KEY index conditions.
avoid some out of band reads
1 parent 482601c commit 926d452

2 files changed

Lines changed: 85 additions & 11 deletions

File tree

src/SQLParser.php

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ function parse_create_definition($tokens, &$i){
296296
$fields = array();
297297
$indexes = array();
298298

299-
while ($tokens[$i] != ')'){
299+
while ($i < count($tokens) && $tokens[$i] != ')'){
300300

301301
$these_tokens = $this->slice_until_next_field($tokens, $i);
302302

@@ -452,23 +452,54 @@ function parse_field_or_key(&$tokens, &$fields, &$indexes){
452452
return;
453453

454454

455-
# unsupported
455+
# FOREIGN KEY [index_name] (index_col_name,...) reference_definition
456+
# reference_definition:
457+
# REFERENCES tbl_name (index_col_name,...)
458+
# [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
459+
# [ON DELETE reference_option]
460+
# [ON UPDATE reference_option]
456461

457462
case 'FOREIGN KEY':
458463

459-
# TODO
460-
$fields[] = array(
461-
'_' => 'FOREIGN KEY',
462-
'tokens' => $tokens,
464+
$index = array(
465+
'type' => 'FOREIGN',
463466
);
467+
468+
array_shift($tokens);
469+
470+
if ($tokens[0] != '('){
471+
$index['name'] = $this->decode_identifier(array_shift($tokens));
472+
}
473+
474+
$this->parse_index_columns($tokens, $index);
475+
476+
if ($tokens[0] == 'REFERENCES'){
477+
array_shift($tokens);
478+
$index['ref_table'] = $this->decode_identifier(array_shift($tokens));
479+
480+
$old_cols = $index['cols'];
481+
$index['cols'] = array();
482+
$this->parse_index_columns($tokens, $index);
483+
$index['ref_cols'] = $index['cols'];
484+
$index['cols'] = $old_cols;
485+
486+
if (count($tokens) >= 1 && $tokens[0] == 'MATCH FULL' ){ $index['ref_match'] = 'FULL' ; array_shift($tokens); }
487+
if (count($tokens) >= 1 && $tokens[0] == 'MATCH PARTIAL'){ $index['ref_match'] = 'PARTIAL'; array_shift($tokens); }
488+
if (count($tokens) >= 1 && $tokens[0] == 'MATCH SIMPLE' ){ $index['ref_match'] = 'SIMPLE' ; array_shift($tokens); }
489+
490+
if (count($tokens) > 1 && $tokens[0] == 'ON DELETE'){ array_shift($tokens); $index['ref_on_delete'] = array_shift($tokens); }
491+
if (count($tokens) > 1 && $tokens[0] == 'ON UPDATE'){ array_shift($tokens); $index['ref_on_delete'] = array_shift($tokens); }
492+
}
493+
494+
if (count($tokens)) $index['more'] = $tokens;
495+
$indexes[] = $index;
464496
return;
465497

466498
case 'CHECK':
467499

468-
# TODO
469-
$fields[] = array(
470-
'_' => 'CHECK',
471-
'tokens' => $tokens,
500+
$indexes[] = array(
501+
'type' => 'CHECK',
502+
'tokens' => array_slice($tokens, 1),
472503
);
473504
return;
474505
}
@@ -481,7 +512,7 @@ function slice_until_next_field($tokens, &$i){
481512
$out = array();
482513
$stack = 0;
483514

484-
while ($i <= count($tokens)){
515+
while ($i < count($tokens)){
485516
$next = $tokens[$i];
486517
if ($next == '('){
487518
$stack++;
@@ -748,6 +779,14 @@ function _extract_tokens($sql, &$source_map){
748779
'IF NOT EXISTS',
749780
'NOT NULL',
750781
'WITH PARSER',
782+
'MATCH FULL',
783+
'MATCH PARTIAL',
784+
'MATCH SIMPLE',
785+
'ON DELETE',
786+
'ON UPDATE',
787+
'SET NULL',
788+
'NO ACTION',
789+
'SET DEFAULT',
751790
);
752791

753792
$singles = array(

tests/IndexTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,41 @@ function testIndexOptions(){
174174
$this->assertEquals($tbl['indexes'][0]['parser'], "foo");
175175
}
176176

177+
function testForeignKeys(){
178+
179+
# [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) reference_definition
180+
# reference_definition:
181+
# REFERENCES tbl_name (index_col_name,...)
182+
# [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
183+
# [ON DELETE reference_option]
184+
# [ON UPDATE reference_option]
185+
186+
187+
$tbl = $this->get_first_table("CREATE TABLE foo (bar INT, FOREIGN KEY (bar) REFERENCES f_foo (f_bar) MATCH FULL ON DELETE SET NULL");
188+
$this->assertEquals($tbl['indexes'][0]['type'], "FOREIGN");
189+
$this->assertEquals($tbl['indexes'][0]['cols'], array(array("name" => "bar")));
190+
$this->assertEquals($tbl['indexes'][0]['ref_table'], "f_foo");
191+
$this->assertEquals($tbl['indexes'][0]['ref_cols'], array(array("name" => "f_bar")));
192+
$this->assertEquals($tbl['indexes'][0]['ref_match'], "FULL");
193+
$this->assertEquals($tbl['indexes'][0]['ref_on_delete'], "SET NULL");
194+
}
195+
196+
function testChecks(){
197+
198+
# CHECK (expr)
199+
200+
# checks are not actually supported in MySQL, but can be defined.
201+
# just extract the expression
202+
203+
$tbl = $this->get_first_table("CREATE TABLE foo (bar INT, CHECK (bar > 4)");
204+
$this->assertEquals($tbl['indexes'][0]['type'], 'CHECK');
205+
$this->assertEquals(count($tbl['indexes'][0]['tokens']), 5);
206+
207+
$tbl = $this->get_first_table("CREATE TABLE foo (bar INT, CHECK (bar > 2 AND (bar <> 5 OR bar=6))");
208+
$this->assertEquals($tbl['indexes'][0]['type'], 'CHECK');
209+
$this->assertEquals(count($tbl['indexes'][0]['tokens']), 16);
210+
}
211+
177212
function get_first_table($str){
178213
$obj = new iamcal\SQLParser();
179214
$obj->parse($str);

0 commit comments

Comments
 (0)