@@ -3005,6 +3005,8 @@ private function translate( $node ): ?string {
30053005 return $ this ->translate_query_expression ( $ node );
30063006 case 'querySpecification ' :
30073007 return $ this ->translate_query_specification ( $ node );
3008+ case 'tableRef ' :
3009+ return $ this ->translate_table_ref ( $ node );
30083010 case 'qualifiedIdentifier ' :
30093011 case 'tableRefWithWildcard ' :
30103012 $ parts = $ node ->get_descendant_nodes ( 'identifier ' );
@@ -3404,14 +3406,7 @@ private function translate_qualified_identifier(
34043406
34053407 // Database-level object name (table, view, procedure, trigger, etc.).
34063408 if ( null !== $ object_node ) {
3407- if ( $ is_information_schema ) {
3408- $ object_name = $ this ->unquote_sqlite_identifier (
3409- $ this ->translate_sequence ( $ object_node ->get_children () )
3410- );
3411- $ parts [] = $ this ->information_schema_builder ->get_table_name ( false , $ object_name );
3412- } else {
3413- $ parts [] = $ this ->translate ( $ object_node );
3414- }
3409+ $ parts [] = $ this ->translate ( $ object_node );
34153410 }
34163411
34173412 // Object child name (column, index, etc.).
@@ -3939,6 +3934,93 @@ public function translate_select_item( WP_Parser_Node $node ): string {
39393934 return sprintf ( '%s AS %s ' , $ item , $ alias );
39403935 }
39413936
3937+ /**
3938+ * Translate a MySQL table reference to SQLite.
3939+ *
3940+ * When the table reference targets an information schema table, we replace
3941+ * it with a subquery, injecting the configured database name dynamically.
3942+ *
3943+ * For example, the following query:
3944+ *
3945+ * SELECT *, t.*, t.table_schema FROM information_schema.tables t
3946+ *
3947+ * Will be translated to:
3948+ *
3949+ * SELECT *, `t`.*, `t`.`table_schema` FROM (
3950+ * SELECT
3951+ * `TABLE_CATALOG`,
3952+ * IIF(`TABLE_SCHEMA` = 'information_schema', `TABLE_SCHEMA`, 'database_name') AS `TABLE_SCHEMA`,
3953+ * `TABLE_NAME`,
3954+ * ...
3955+ * FROM `_wp_sqlite_mysql_information_schema_tables` AS `tables`
3956+ * ) `t`
3957+ *
3958+ * The same logic will be applied to table references in JOIN clauses as well.
3959+ *
3960+ * @param WP_Parser_Node $node The "tableRef" AST node.
3961+ * @return string The translated value.
3962+ * @throws WP_SQLite_Driver_Exception When the translation fails.
3963+ */
3964+ public function translate_table_ref ( WP_Parser_Node $ node ): string {
3965+ // Information schema is currently accessible only in read-only queries.
3966+ if ( ! $ this ->is_readonly ) {
3967+ return $ this ->translate_sequence ( $ node ->get_children () );
3968+ }
3969+
3970+ // The table reference is in "<schema>.<table>" or "<table>" format.
3971+ $ parts = $ node ->get_descendant_nodes ( 'identifier ' );
3972+ $ table = array_pop ( $ parts );
3973+ $ schema = array_pop ( $ parts );
3974+
3975+ $ schema_name = $ schema ? $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ schema ) ) : null ;
3976+ $ table_name = $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ table ) );
3977+
3978+ // When the table reference targets an information schema table,
3979+ // we need to inject the configured database name dynamically.
3980+ if (
3981+ ( null === $ schema_name && 'information_schema ' === $ this ->db_name )
3982+ || ( null !== $ schema_name && 'information_schema ' === strtolower ( $ schema_name ) )
3983+ ) {
3984+ $ table_is_temporary = $ this ->information_schema_builder ->temporary_table_exists ( $ table_name );
3985+ $ sqlite_table_name = $ this ->information_schema_builder ->get_table_name ( $ table_is_temporary , $ table_name );
3986+
3987+ // We need to fetch the SQLite column information, because the information
3988+ // schema tables don't contain records for the information schema itself.
3989+ $ columns = $ this ->execute_sqlite_query (
3990+ 'SELECT name FROM pragma_table_info(?) ' ,
3991+ array ( $ sqlite_table_name )
3992+ )->fetchAll ( PDO ::FETCH_COLUMN );
3993+
3994+ // List all columns in the table, replacing columns targeting database
3995+ // name columns with the configured database name.
3996+ $ expanded_list = array ();
3997+ foreach ( $ columns as $ column ) {
3998+ $ quoted_column = $ this ->quote_sqlite_identifier ( $ column );
3999+ if ( str_contains ( strtolower ( $ column ), 'schema ' ) ) {
4000+ $ expanded_list [] = sprintf (
4001+ "IIF(%s = 'information_schema', %s, %s) AS %s " ,
4002+ $ quoted_column ,
4003+ $ quoted_column ,
4004+ $ this ->connection ->quote ( $ this ->main_db_name ),
4005+ strtoupper ( $ quoted_column )
4006+ );
4007+ } else {
4008+ $ expanded_list [] = $ quoted_column ;
4009+ }
4010+ }
4011+ $ column_list = implode ( ', ' , $ expanded_list );
4012+
4013+ // Compose information schema subquery.
4014+ return sprintf (
4015+ '(SELECT %s FROM %s AS %s) ' ,
4016+ $ column_list ,
4017+ $ this ->quote_sqlite_identifier ( $ sqlite_table_name ),
4018+ $ this ->quote_sqlite_identifier ( $ table_name ),
4019+ );
4020+ }
4021+ return $ this ->translate_sequence ( $ node ->get_children () );
4022+ }
4023+
39424024 /**
39434025 * Recreate an existing table using data in the information schema.
39444026 *
0 commit comments