Skip to content

Commit 2625d62

Browse files
committed
Implement inline REFERENCES clause in column definition
1 parent 5265f9d commit 2625d62

2 files changed

Lines changed: 183 additions & 16 deletions

File tree

tests/WP_SQLite_Driver_Metadata_Tests.php

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,135 @@ public function testInformationSchemaForeignKeys(): void {
12111211
);
12121212
}
12131213

1214+
public function testInformationSchemaInlineForeignKeys(): void {
1215+
$this->assertQuery( 'CREATE TABLE t1 (id INT, name VARCHAR(255))' );
1216+
$this->assertQuery(
1217+
'CREATE TABLE t2 (
1218+
id INT,
1219+
t1_id INT REFERENCES t1 (id),
1220+
t1_name VARCHAR(255) REFERENCES t1 (name) ON DELETE CASCADE
1221+
)'
1222+
);
1223+
1224+
// INFORMATION_SCHEMA.TABLE_CONSTRAINTS
1225+
$result = $this->assertQuery( "SELECT * FROM information_schema.table_constraints WHERE table_name = 't2'" );
1226+
$this->assertEquals(
1227+
array(
1228+
(object) array(
1229+
'CONSTRAINT_CATALOG' => 'def',
1230+
'CONSTRAINT_SCHEMA' => 'wp',
1231+
'CONSTRAINT_NAME' => 't2_ibfk_1',
1232+
'TABLE_SCHEMA' => 'wp',
1233+
'TABLE_NAME' => 't2',
1234+
'CONSTRAINT_TYPE' => 'FOREIGN KEY',
1235+
'ENFORCED' => 'YES',
1236+
),
1237+
(object) array(
1238+
'CONSTRAINT_CATALOG' => 'def',
1239+
'CONSTRAINT_SCHEMA' => 'wp',
1240+
'CONSTRAINT_NAME' => 't2_ibfk_2',
1241+
'TABLE_SCHEMA' => 'wp',
1242+
'TABLE_NAME' => 't2',
1243+
'CONSTRAINT_TYPE' => 'FOREIGN KEY',
1244+
'ENFORCED' => 'YES',
1245+
),
1246+
),
1247+
$result
1248+
);
1249+
1250+
// INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
1251+
$result = $this->assertQuery( "SELECT * FROM information_schema.referential_constraints WHERE table_name = 't2'" );
1252+
$this->assertEquals(
1253+
array(
1254+
(object) array(
1255+
'CONSTRAINT_CATALOG' => 'def',
1256+
'CONSTRAINT_SCHEMA' => 'wp',
1257+
'CONSTRAINT_NAME' => 't2_ibfk_1',
1258+
'UNIQUE_CONSTRAINT_CATALOG' => 'def',
1259+
'UNIQUE_CONSTRAINT_SCHEMA' => 'wp',
1260+
'UNIQUE_CONSTRAINT_NAME' => null,
1261+
'MATCH_OPTION' => 'NONE',
1262+
'UPDATE_RULE' => 'NO ACTION',
1263+
'DELETE_RULE' => 'NO ACTION',
1264+
'TABLE_NAME' => 't2',
1265+
'REFERENCED_TABLE_NAME' => 't1',
1266+
),
1267+
(object) array(
1268+
'CONSTRAINT_CATALOG' => 'def',
1269+
'CONSTRAINT_SCHEMA' => 'wp',
1270+
'CONSTRAINT_NAME' => 't2_ibfk_2',
1271+
'UNIQUE_CONSTRAINT_CATALOG' => 'def',
1272+
'UNIQUE_CONSTRAINT_SCHEMA' => 'wp',
1273+
'UNIQUE_CONSTRAINT_NAME' => null,
1274+
'MATCH_OPTION' => 'NONE',
1275+
'UPDATE_RULE' => 'NO ACTION',
1276+
'DELETE_RULE' => 'CASCADE',
1277+
'TABLE_NAME' => 't2',
1278+
'REFERENCED_TABLE_NAME' => 't1',
1279+
),
1280+
),
1281+
$result
1282+
);
1283+
1284+
// INFORMATION_SCHEMA.KEY_COLUMN_USAGE
1285+
$result = $this->assertQuery( "SELECT * FROM information_schema.key_column_usage WHERE table_name = 't2'" );
1286+
$this->assertEquals(
1287+
array(
1288+
(object) array(
1289+
'CONSTRAINT_CATALOG' => 'def',
1290+
'CONSTRAINT_SCHEMA' => 'wp',
1291+
'CONSTRAINT_NAME' => 't2_ibfk_1',
1292+
'TABLE_CATALOG' => 'def',
1293+
'TABLE_SCHEMA' => 'wp',
1294+
'TABLE_NAME' => 't2',
1295+
'COLUMN_NAME' => 't1_id',
1296+
'ORDINAL_POSITION' => '1',
1297+
'POSITION_IN_UNIQUE_CONSTRAINT' => '1',
1298+
'REFERENCED_TABLE_SCHEMA' => 'wp',
1299+
'REFERENCED_TABLE_NAME' => 't1',
1300+
'REFERENCED_COLUMN_NAME' => 'id',
1301+
),
1302+
(object) array(
1303+
'CONSTRAINT_CATALOG' => 'def',
1304+
'CONSTRAINT_SCHEMA' => 'wp',
1305+
'CONSTRAINT_NAME' => 't2_ibfk_2',
1306+
'TABLE_CATALOG' => 'def',
1307+
'TABLE_SCHEMA' => 'wp',
1308+
'TABLE_NAME' => 't2',
1309+
'COLUMN_NAME' => 't1_name',
1310+
'ORDINAL_POSITION' => '1',
1311+
'POSITION_IN_UNIQUE_CONSTRAINT' => '1',
1312+
'REFERENCED_TABLE_SCHEMA' => 'wp',
1313+
'REFERENCED_TABLE_NAME' => 't1',
1314+
'REFERENCED_COLUMN_NAME' => 'name',
1315+
),
1316+
),
1317+
$result
1318+
);
1319+
1320+
// SHOW CREATE TABLE
1321+
$result = $this->assertQuery( 'SHOW CREATE TABLE t2' );
1322+
$this->assertEquals(
1323+
array(
1324+
(object) array(
1325+
'Create Table' => implode(
1326+
"\n",
1327+
array(
1328+
'CREATE TABLE `t2` (',
1329+
' `id` int DEFAULT NULL,',
1330+
' `t1_id` int DEFAULT NULL,',
1331+
' `t1_name` varchar(255) DEFAULT NULL,',
1332+
' CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`),',
1333+
' CONSTRAINT `t2_ibfk_2` FOREIGN KEY (`t1_name`) REFERENCES `t1` (`name`) ON DELETE CASCADE',
1334+
') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci',
1335+
)
1336+
),
1337+
),
1338+
),
1339+
$result
1340+
);
1341+
}
1342+
12141343
public function testInformationSchemaForeignKeysWithMultipleColumns(): void {
12151344
$this->assertQuery( 'CREATE TABLE t1 (id INT, name VARCHAR(255))' );
12161345
$this->assertQuery(

wp-includes/sqlite-ast/class-wp-sqlite-information-schema-builder.php

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -510,34 +510,52 @@ public function record_create_table( WP_Parser_Node $node ): void {
510510
throw $e;
511511
}
512512

513-
// Inline column constraints and indexes.
514-
$index_data = $this->extract_column_statistics_data(
513+
// Extract inline column constraints and indexes.
514+
$index_data = $this->extract_column_statistics_data(
515515
$table_name,
516516
$column_name,
517517
$column_node,
518518
'YES' === $column_data['is_nullable']
519519
);
520+
$constraint_data = $this->extract_table_constraint_data(
521+
$column_node,
522+
$table_name,
523+
$index_data['index_name'] ?? null
524+
);
525+
$referential_constraint_data = $this->extract_referential_constraint_data(
526+
$column_node,
527+
$table_name
528+
);
529+
$key_column_usage_data = $this->extract_key_column_usage_data(
530+
$column_node,
531+
$table_name
532+
);
520533

534+
// Save inline column constraints and indexes.
521535
if ( null !== $index_data ) {
522536
$this->insert_values(
523537
$this->get_table_name( $table_is_temporary, 'statistics' ),
524538
$index_data
525539
);
526540
}
527-
528-
// Save constraint data.
529-
$constraint_data = $this->extract_table_constraint_data(
530-
$column_node,
531-
$table_name,
532-
$index_data['index_name'] ?? null
533-
);
534-
535541
if ( null !== $constraint_data ) {
536542
$this->insert_values(
537543
$this->get_table_name( $table_is_temporary, 'table_constraints' ),
538544
$constraint_data
539545
);
540546
}
547+
if ( null !== $referential_constraint_data ) {
548+
$this->insert_values(
549+
$this->get_table_name( $table_is_temporary, 'referential_constraints' ),
550+
$referential_constraint_data
551+
);
552+
}
553+
foreach ( $key_column_usage_data as $key_column_usage_item ) {
554+
$this->insert_values(
555+
$this->get_table_name( $table_is_temporary, 'key_column_usage' ),
556+
$key_column_usage_item
557+
);
558+
}
541559

542560
$column_position += 1;
543561
}
@@ -1438,11 +1456,15 @@ public function extract_table_constraint_data(
14381456
*
14391457
* @param WP_Parser_Node $node The "tableConstraintDef" AST node.
14401458
* @param string $table_name The table name.
1441-
* @return array The referential constraint data as stored in information schema.
1459+
* @return array|null The referential constraint data as stored in information schema.
14421460
*/
1443-
private function extract_referential_constraint_data( WP_Parser_Node $node, string $table_name ): array {
1461+
private function extract_referential_constraint_data( WP_Parser_Node $node, string $table_name ): ?array {
1462+
$references = $node->get_first_descendant_node( 'references' );
1463+
if ( null === $references ) {
1464+
return null;
1465+
}
1466+
14441467
// Referenced table name.
1445-
$references = $node->get_first_child_node( 'references' );
14461468
$referenced_table = $references->get_first_child_node( 'tableRef' );
14471469
$referenced_identifiers = $referenced_table->get_descendant_nodes( 'identifier' );
14481470
$referenced_table_name = $this->get_value( end( $referenced_identifiers ) );
@@ -1524,8 +1546,12 @@ private function extract_referential_constraint_data( WP_Parser_Node $node, stri
15241546
* @return array The key column usage data as stored in information schema.
15251547
*/
15261548
private function extract_key_column_usage_data( WP_Parser_Node $node, string $table_name ): array {
1549+
$references = $node->get_first_descendant_node( 'references' );
1550+
if ( null === $references ) {
1551+
return array();
1552+
}
1553+
15271554
// Referenced table name.
1528-
$references = $node->get_first_child_node( 'references' );
15291555
$referenced_table = $references->get_first_child_node( 'tableRef' );
15301556
$referenced_identifiers = $referenced_table->get_descendant_nodes( 'identifier' );
15311557
$referenced_table_schema = count( $referenced_identifiers ) > 1
@@ -1535,14 +1561,26 @@ private function extract_key_column_usage_data( WP_Parser_Node $node, string $ta
15351561

15361562
$name = $this->get_table_constraint_name( $node, $table_name );
15371563

1538-
$key_parts = $node->get_first_descendant_node( 'keyList' )->get_child_nodes( 'keyPart' );
1564+
if ( 'columnDefinition' === $node->rule_name ) {
1565+
$identifiers = $node
1566+
->get_first_descendant_node( 'fieldIdentifier' )
1567+
->get_descendant_nodes( 'identifier' );
1568+
$key_parts = array( end( $identifiers ) );
1569+
} else {
1570+
$key_list = $node->get_first_descendant_node( 'keyList' );
1571+
$key_parts = array();
1572+
foreach ( $key_list->get_child_nodes( 'keyPart' ) as $key_part ) {
1573+
$key_parts[] = $key_part->get_first_child_node( 'identifier' );
1574+
}
1575+
}
1576+
15391577
$reference_parts = $references->get_first_child_node( 'identifierListWithParentheses' )
15401578
->get_first_child_node( 'identifierList' )
15411579
->get_child_nodes( 'identifier' );
15421580

15431581
$rows = array();
15441582
foreach ( $key_parts as $i => $key_part ) {
1545-
$column_name = $this->get_value( $key_part->get_first_child_node( 'identifier' ) );
1583+
$column_name = $this->get_value( $key_part );
15461584

15471585
$rows[] = array(
15481586
'constraint_schema' => $this->db_name,

0 commit comments

Comments
 (0)