PluginProbe
SQLite Database Integration / 2.2.0
SQLite Database Integration v2.2.0
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
← All changes | wp-includes/sqlite/class-wp-sqlite-translator.php +80 -137 2.2.212.2.0 View file →
@@ -368,11 +368,10 @@
368 368 PDO::ATTR_STRINGIFY_FETCHES => true,
369 369 PDO::ATTR_TIMEOUT => 5,
370 370 );
371 371
372 - $dsn = 'sqlite:' . FQDB;
373 - $pdo_class = PHP_VERSION_ID >= 80400 ? PDO\SQLite::class : PDO::class;
374 - $pdo = new $pdo_class( $dsn, null, null, $options );
372 + $dsn = 'sqlite:' . FQDB;
373 + $pdo = new PDO( $dsn, null, null, $options ); // phpcs:ignore WordPress.DB.RestrictedClasses
375 374 } catch ( PDOException $ex ) {
376 375 $status = $ex->getCode();
377 376 if ( self::SQLITE_BUSY === $status || self::SQLITE_LOCKED === $status ) {
378 377 $locked = true;
@@ -915,14 +914,14 @@
915 914 );
916 915 }
917 916
918 917 if ( count( $table->primary_key ) > 1 ) {
919 - $definitions[] = 'PRIMARY KEY (' . implode( ', ', array_map( array( $this, 'quote_identifier' ), $table->primary_key ) ) . ')';
918 + $definitions[] = 'PRIMARY KEY ("' . implode( '", "', $table->primary_key ) . '")';
920 919 }
921 920
922 921 $create_query = (
923 922 $table->create_table .
924 - $this->quote_identifier( $table->name ) . ' (' . "\n" .
923 + '"' . $table->name . '" (' . "\n" .
925 924 implode( ",\n", $definitions ) .
926 925 ')'
927 926 );
928 927
@@ -939,9 +938,9 @@
939 938 $unique = 'UNIQUE ';
940 939 }
941 940 $index_name = $this->generate_index_name( $table->name, $constraint->name );
942 941 $this->execute_sqlite_query(
943 - 'CREATE ' . $unique . 'INDEX ' . $if_not_exists . ' ' . $this->quote_identifier( $index_name ) . ' ON ' . $this->quote_identifier( $table->name ) . ' (' . implode( ', ', array_map( array( $this, 'quote_identifier' ), $constraint->columns ) ) . ')'
942 + "CREATE $unique INDEX $if_not_exists \"$index_name\" ON \"{$table->name}\" (\"" . implode( '", "', $constraint->columns ) . '")'
944 943 );
945 944 $this->update_data_type_cache(
946 945 $table->name,
947 946 $index_name,
@@ -1149,30 +1148,9 @@
1149 1148 WP_SQLite_Token::TYPE_KEYWORD,
1150 1149 WP_SQLite_Token::FLAG_KEYWORD_FUNCTION,
1151 1150 array( 'DEFAULT' )
1152 1151 ) ) {
1153 - // Consume the next token (could be a value, opening paren, etc.)
1154 - $default_token = $this->rewriter->consume();
1155 - $result->default = $default_token->token;
1156 -
1157 - // Check if the default value is wrapped in parentheses (for function calls like (now()))
1158 - if ( $default_token->matches( WP_SQLite_Token::TYPE_OPERATOR, null, array( '(' ) ) ) {
1159 - // Track parenthesis depth to consume the complete expression
1160 - $paren_depth = 1;
1161 - $default_value = '(';
1162 -
1163 - while ( $paren_depth > 0 && ( $next_token = $this->rewriter->consume() ) ) {
1164 - $default_value .= $next_token->token;
1165 -
1166 - if ( $next_token->matches( WP_SQLite_Token::TYPE_OPERATOR, null, array( '(' ) ) ) {
1167 - ++$paren_depth;
1168 - } elseif ( $next_token->matches( WP_SQLite_Token::TYPE_OPERATOR, null, array( ')' ) ) ) {
1169 - --$paren_depth;
1170 - }
1171 - }
1172 -
1173 - $result->default = $default_value;
1174 - }
1152 + $result->default = $this->rewriter->consume()->token;
1175 1153 continue;
1176 1154 }
1177 1155
1178 1156 if (
@@ -1207,9 +1185,9 @@
1207 1185 *
1208 1186 * @return string
1209 1187 */
1210 1188 private function make_sqlite_field_definition( $field ) {
1211 - $definition = $this->quote_identifier( $field->name ) . ' ' . $field->sqlite_data_type;
1189 + $definition = '"' . $field->name . '" ' . $field->sqlite_data_type;
1212 1190 if ( $field->auto_increment ) {
1213 1191 $definition .= ' PRIMARY KEY AUTOINCREMENT';
1214 1192 } elseif ( $field->primary_key ) {
1215 1193 $definition .= ' PRIMARY KEY ';
@@ -1381,11 +1359,10 @@
1381 1359 // Naive rewriting of DELETE JOIN query.
1382 1360 // @TODO: Actually rewrite the query instead of using a hardcoded workaround.
1383 1361 if ( str_contains( $updated_query, ' JOIN ' ) ) {
1384 1362 $table_prefix = isset( $GLOBALS['table_prefix'] ) ? $GLOBALS['table_prefix'] : 'wp_';
1385 - $quoted_table = $this->quote_identifier( $table_prefix . 'options' );
1386 1363 $this->execute_sqlite_query(
1387 - "DELETE FROM $quoted_table WHERE option_id IN (SELECT MIN(option_id) FROM $quoted_table GROUP BY option_name HAVING COUNT(*) > 1)"
1364 + "DELETE FROM {$table_prefix}options WHERE option_id IN (SELECT MIN(option_id) FROM {$table_prefix}options GROUP BY option_name HAVING COUNT(*) > 1)"
1388 1365 );
1389 1366 $this->set_result_from_affected_rows();
1390 1367 return;
1391 1368 }
@@ -1417,9 +1394,9 @@
1417 1394 // The DELETE query targets multiple tables – rewrite it into a
1418 1395 // SELECT to fetch the IDs of the rows to delete, then delete them
1419 1396 // using a separate DELETE query.
1420 1397
1421 - $this->table_name = $this->normalize_column_name( $rewriter->skip()->value );
1398 + $this->table_name = $rewriter->skip()->value;
1422 1399 $rewriter->add( new WP_SQLite_Token( 'SELECT', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ) );
1423 1400
1424 1401 /*
1425 1402 * Get table name.
@@ -1433,9 +1410,9 @@
1433 1410 $index = array_search( $from, $rewriter->input_tokens, true );
1434 1411 for ( $i = $index + 1; $i < $rewriter->max; $i++ ) {
1435 1412 // Assume the table name is the first token after FROM.
1436 1413 if ( ! $rewriter->input_tokens[ $i ]->is_semantically_void() ) {
1437 - $this->table_name = $this->normalize_column_name( $rewriter->input_tokens[ $i ]->value );
1414 + $this->table_name = $rewriter->input_tokens[ $i ]->value;
1438 1415 break;
1439 1416 }
1440 1417 }
1441 1418 if ( ! $this->table_name ) {
@@ -1445,9 +1422,9 @@
1445 1422 /*
1446 1423 * Now, let's figure out the primary key name.
1447 1424 * This assumes that all listed table names are the same.
1448 1425 */
1449 - $q = $this->execute_sqlite_query( 'SELECT l.name FROM pragma_table_info(' . $this->pdo->quote( $this->table_name ) . ') as l WHERE l.pk = 1;' );
1426 + $q = $this->execute_sqlite_query( 'SELECT l.name FROM pragma_table_info("' . $this->table_name . '") as l WHERE l.pk = 1;' );
1450 1427 $pk_name = $q->fetch()['name'];
1451 1428
1452 1429 /*
1453 1430 * Good, we can finally create the SELECT query.
@@ -1468,9 +1445,9 @@
1468 1445 // Insert .id AS id_1 after the table alias.
1469 1446 $rewriter->add_many(
1470 1447 array(
1471 1448 new WP_SQLite_Token( '.', WP_SQLite_Token::TYPE_OPERATOR, WP_SQLite_Token::FLAG_OPERATOR_SQL ),
1472 - new WP_SQLite_Token( $this->quote_identifier( $pk_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
1449 + new WP_SQLite_Token( $pk_name, WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
1473 1450 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
1474 1451 new WP_SQLite_Token( 'AS', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
1475 1452 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
1476 1453 new WP_SQLite_Token( 'id_' . $alias_nb, WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
@@ -1491,17 +1468,14 @@
1491 1468 $ids_to_delete[] = $id['id_0'];
1492 1469 $ids_to_delete[] = $id['id_1'];
1493 1470 }
1494 1471
1495 - $quoted_table = $this->quote_identifier( $this->table_name );
1496 - $quoted_pk = $this->quote_identifier( $pk_name );
1497 - if ( count( $ids_to_delete ) ) {
1498 - $placeholders = implode( ',', array_fill( 0, count( $ids_to_delete ), '?' ) );
1499 - $stmt = $this->execute_sqlite_query( "DELETE FROM {$quoted_table} WHERE {$quoted_pk} IN ({$placeholders})" );
1500 - $stmt->execute( $ids_to_delete );
1501 - } else {
1502 - $this->execute_sqlite_query( "DELETE FROM {$quoted_table} WHERE 0=1" );
1503 - }
1472 + $query = (
1473 + count( $ids_to_delete )
1474 + ? "DELETE FROM {$this->table_name} WHERE {$pk_name} IN (" . implode( ',', $ids_to_delete ) . ')'
1475 + : "DELETE FROM {$this->table_name} WHERE 0=1"
1476 + );
1477 + $this->execute_sqlite_query( $query );
1504 1478 $this->set_result_from_affected_rows(
1505 1479 count( $ids_to_delete )
1506 1480 );
1507 1481 }
@@ -1722,9 +1696,9 @@
1722 1696 * @throws PDOException When the table is not found.
1723 1697 */
1724 1698 private function execute_describe() {
1725 1699 $this->rewriter->skip();
1726 - $this->table_name = $this->normalize_column_name( $this->rewriter->consume()->value );
1700 + $this->table_name = $this->rewriter->consume()->value;
1727 1701 $this->set_results_from_fetched_data(
1728 1702 $this->describe( $this->table_name )
1729 1703 );
1730 1704 if ( ! $this->results ) {
@@ -1768,14 +1742,14 @@
1768 1742 WHEN 0 THEN ''
1769 1743 ELSE 'PRI'
1770 1744 END
1771 1745 ) as `Key`
1772 - FROM pragma_table_info(" . $this->pdo->quote( $table_name ) . ') p
1773 - LEFT JOIN ' . self::DATA_TYPES_CACHE_TABLE . ' d
1774 - ON d.`table` = ' . $this->pdo->quote( $table_name ) . '
1746 + FROM pragma_table_info(\"$table_name\") p
1747 + LEFT JOIN " . self::DATA_TYPES_CACHE_TABLE . " d
1748 + ON d.`table` = \"$table_name\"
1775 1749 AND d.`column_or_index` = p.`name`
1776 1750 ;
1777 - '
1751 + "
1778 1752 )
1779 1753 ->fetchAll( $this->pdo_fetch_mode );
1780 1754 }
1781 1755
@@ -1840,9 +1814,9 @@
1840 1814 WP_SQLite_Token::TYPE_KEYWORD,
1841 1815 WP_SQLite_Token::FLAG_KEYWORD_RESERVED
1842 1816 )
1843 1817 ) {
1844 - $this->table_name = $this->normalize_column_name( $token->value );
1818 + $this->table_name = $token->value;
1845 1819 }
1846 1820
1847 1821 $this->remember_last_reserved_keyword( $token );
1848 1822
@@ -1894,9 +1868,9 @@
1894 1868 new WP_SQLite_Token( 'rowid', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
1895 1869 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
1896 1870 new WP_SQLite_Token( 'FROM', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
1897 1871 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
1898 - new WP_SQLite_Token( $this->quote_identifier( $this->table_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
1872 + new WP_SQLite_Token( $this->table_name, WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
1899 1873 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
1900 1874 )
1901 1875 );
1902 1876 }
@@ -1919,9 +1893,9 @@
1919 1893
1920 1894 // Consume and record the table name.
1921 1895 $this->insert_columns = array();
1922 1896 $this->rewriter->consume(); // INTO.
1923 - $this->table_name = $this->normalize_column_name( $this->rewriter->consume()->value ); // Table name.
1897 + $this->table_name = $this->rewriter->consume()->value; // Table name.
1924 1898
1925 1899 /*
1926 1900 * A list of columns is given if the opening parenthesis
1927 1901 * is earlier than the VALUES keyword.
@@ -2198,9 +2172,9 @@
2198 2172 )
2199 2173 ) {
2200 2174 return false;
2201 2175 }
2202 - $table_name = $this->normalize_column_name( $this->rewriter->peek_nth( 2 )->value );
2176 + $table_name = $this->rewriter->peek_nth( 2 )->value;
2203 2177 if ( 'dual' === strtolower( $table_name ) ) {
2204 2178 return false;
2205 2179 }
2206 2180 return $table_name;
@@ -2251,9 +2225,12 @@
2251 2225 *
2252 2226 * @return bool True if the parameter was extracted successfully, false otherwise.
2253 2227 */
2254 2228 private function extract_bound_parameter( $token, &$params ) {
2255 - if ( ! $token->matches( WP_SQLite_Token::TYPE_STRING )
2229 + if ( ! $token->matches(
2230 + WP_SQLite_Token::TYPE_STRING,
2231 + WP_SQLite_Token::FLAG_STRING_SINGLE_QUOTES
2232 + )
2256 2233 || 'AS' === $this->last_reserved_keyword
2257 2234 ) {
2258 2235 return false;
2259 2236 }
@@ -2536,9 +2513,9 @@
2536 2513 }
2537 2514
2538 2515 $this->rewriter->add( new WP_SQLite_Token( 'STRFTIME', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_FUNCTION ) );
2539 2516 $this->rewriter->add( new WP_SQLite_Token( '(', WP_SQLite_Token::TYPE_OPERATOR ) );
2540 - $this->rewriter->add( new WP_SQLite_Token( $this->pdo->quote( $new_format ), WP_SQLite_Token::TYPE_STRING ) );
2517 + $this->rewriter->add( new WP_SQLite_Token( "'$new_format'", WP_SQLite_Token::TYPE_STRING ) );
2541 2518 $this->rewriter->add( new WP_SQLite_Token( ',', WP_SQLite_Token::TYPE_OPERATOR ) );
2542 2519
2543 2520 // Add the buffered tokens back to the stream.
2544 2521 $this->rewriter->add_many( $first_arg );
@@ -2611,9 +2588,9 @@
2611 2588 break;
2612 2589 }
2613 2590 }
2614 2591
2615 - $this->rewriter->add( new WP_SQLite_Token( $this->pdo->quote( "{$interval_op}$num $unit" ), WP_SQLite_Token::TYPE_STRING ) );
2592 + $this->rewriter->add( new WP_SQLite_Token( "'{$interval_op}$num $unit'", WP_SQLite_Token::TYPE_STRING ) );
2616 2593 return true;
2617 2594 }
2618 2595
2619 2596 /**
@@ -2709,11 +2686,18 @@
2709 2686 * @param string $pattern The LIKE pattern.
2710 2687 * @return string The escaped GLOB pattern.
2711 2688 */
2712 2689 private function escape_like_to_glob( $pattern ) {
2690 + // Remove surrounding quotes
2691 + $pattern = trim( $pattern, "'\"" );
2692 +
2713 2693 $pattern = str_replace( '%', '*', $pattern );
2714 2694 $pattern = str_replace( '_', '?', $pattern );
2715 - return $this->pdo->quote( $pattern );
2695 +
2696 + // No need to escape special characters in this case
2697 + // because GLOB doesn't require escaping in the same way LIKE does
2698 + // Return the pattern wrapped in single quotes
2699 + return "'" . $pattern . "'";
2716 2700 }
2717 2701
2718 2702 /**
2719 2703 * Detect GROUP BY.
@@ -2948,9 +2932,9 @@
2948 2932
2949 2933 $max = count( $conflict_columns );
2950 2934 $i = 0;
2951 2935 foreach ( $conflict_columns as $conflict_column ) {
2952 - $this->rewriter->add( new WP_SQLite_Token( $this->quote_identifier( $conflict_column ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ) );
2936 + $this->rewriter->add( new WP_SQLite_Token( '"' . $conflict_column . '"', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ) );
2953 2937 if ( ++$i < $max ) {
2954 2938 $this->rewriter->add( new WP_SQLite_Token( ',', WP_SQLite_Token::TYPE_OPERATOR ) );
2955 2939 $this->rewriter->add( new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ) );
2956 2940 }
@@ -2986,14 +2970,14 @@
2986 2970 *
2987 2971 * @return array
2988 2972 */
2989 2973 private function get_keys( $table_name, $only_unique = false ) {
2990 - $query = $this->execute_sqlite_query( 'SELECT * FROM pragma_index_list(' . $this->pdo->quote( $table_name ) . ') as l;' );
2974 + $query = $this->execute_sqlite_query( 'SELECT * FROM pragma_index_list("' . $table_name . '") as l;' );
2991 2975 $indices = $query->fetchAll();
2992 2976 $results = array();
2993 2977 foreach ( $indices as $index ) {
2994 2978 if ( ! $only_unique || '1' === $index['unique'] ) {
2995 - $query = $this->execute_sqlite_query( 'SELECT * FROM pragma_index_info(' . $this->pdo->quote( $index['name'] ) . ') as l;' );
2979 + $query = $this->execute_sqlite_query( 'SELECT * FROM pragma_index_info("' . $index['name'] . '") as l;' );
2996 2980 $results[] = array(
2997 2981 'index' => $index,
2998 2982 'columns' => $query->fetchAll(),
2999 2983 );
@@ -3042,9 +3026,9 @@
3042 3026 new WP_SQLite_Token( 'ALTER', WP_SQLite_Token::TYPE_KEYWORD ),
3043 3027 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3044 3028 new WP_SQLite_Token( 'TABLE', WP_SQLite_Token::TYPE_KEYWORD ),
3045 3029 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3046 - new WP_SQLite_Token( $this->quote_identifier( $this->table_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3030 + new WP_SQLite_Token( $this->table_name, WP_SQLite_Token::TYPE_KEYWORD ),
3047 3031 )
3048 3032 );
3049 3033 $op_type = strtoupper( $this->rewriter->consume()->token ?? '' );
3050 3034 $op_raw_subject = $this->rewriter->consume()->token ?? '';
@@ -3146,9 +3130,9 @@
3146 3130 );
3147 3131
3148 3132 // Drop ON UPDATE trigger by the old column name.
3149 3133 $on_update_trigger_name = $this->get_column_on_update_current_timestamp_trigger_name( $this->table_name, $from_name );
3150 - $this->execute_sqlite_query( 'DROP TRIGGER IF EXISTS ' . $this->quote_identifier( $on_update_trigger_name ) );
3134 + $this->execute_sqlite_query( "DROP TRIGGER IF EXISTS \"$on_update_trigger_name\"" );
3151 3135
3152 3136 /*
3153 3137 * In SQLite, there is no direct equivalent to the CHANGE COLUMN
3154 3138 * statement from MySQL. We need to do a bit of work to emulate it.
@@ -3179,9 +3163,9 @@
3179 3163 $token = $create_table->consume();
3180 3164 if ( ! $token ) {
3181 3165 break;
3182 3166 }
3183 - if ( ( WP_SQLite_Token::TYPE_STRING !== $token->type && WP_SQLite_Token::TYPE_SYMBOL !== $token->type )
3167 + if ( WP_SQLite_Token::TYPE_STRING !== $token->type
3184 3168 || $from_name !== $this->normalize_column_name( $token->value ) ) {
3185 3169 continue;
3186 3170 }
3187 3171
@@ -3215,11 +3199,10 @@
3215 3199 } else {
3216 3200 // Otherwise, just add the new name in place of the old name we dropped.
3217 3201 $create_table->add(
3218 3202 new WP_SQLite_Token(
3219 - $this->quote_identifier( $new_field->name ),
3220 - WP_SQLite_Token::TYPE_KEYWORD,
3221 - WP_SQLite_Token::FLAG_KEYWORD_KEY
3203 + "`$new_field->name`",
3204 + WP_SQLite_Token::TYPE_KEYWORD
3222 3205 )
3223 3206 );
3224 3207 }
3225 3208 }
@@ -3224,26 +3207,24 @@
3224 3207 }
3225 3208 }
3226 3209
3227 3210 // 3. Copy the data out of the old table
3228 - $cache_table_name = "_tmp__{$this->table_name}_" . rand( 10000000, 99999999 );
3229 - $quoted_cache_table = $this->quote_identifier( $cache_table_name );
3230 - $quoted_table = $this->quote_identifier( $this->table_name );
3211 + $cache_table_name = "_tmp__{$this->table_name}_" . rand( 10000000, 99999999 );
3231 3212 $this->execute_sqlite_query(
3232 - "CREATE TABLE $quoted_cache_table as SELECT * FROM $quoted_table"
3213 + "CREATE TABLE `$cache_table_name` as SELECT * FROM `$this->table_name`"
3233 3214 );
3234 3215
3235 3216 // 4. Drop the old table to free up the indexes names
3236 - $this->execute_sqlite_query( "DROP TABLE $quoted_table" );
3217 + $this->execute_sqlite_query( "DROP TABLE `$this->table_name`" );
3237 3218
3238 3219 // 5. Create a new table from the updated schema
3239 3220 $this->execute_sqlite_query( $create_table->get_updated_query() );
3240 3221
3241 3222 // 6. Copy the data from step 3 to the new table
3242 - $this->execute_sqlite_query( "INSERT INTO $quoted_table SELECT * FROM $quoted_cache_table" );
3223 + $this->execute_sqlite_query( "INSERT INTO {$this->table_name} SELECT * FROM $cache_table_name" );
3243 3224
3244 3225 // 7. Drop the old table copy
3245 - $this->execute_sqlite_query( "DROP TABLE $quoted_cache_table" );
3226 + $this->execute_sqlite_query( "DROP TABLE `$cache_table_name`" );
3246 3227
3247 3228 // 8. Restore any indexes that were dropped in step 4
3248 3229 foreach ( $old_indexes as $row ) {
3249 3230 /*
@@ -3256,10 +3237,10 @@
3256 3237
3257 3238 $columns = array();
3258 3239 foreach ( $row['columns'] as $column ) {
3259 3240 $columns[] = ( $column['name'] === $from_name )
3260 - ? $this->quote_identifier( $new_field->name )
3261 - : $this->quote_identifier( $column['name'] );
3241 + ? '`' . $new_field->name . '`'
3242 + : '`' . $column['name'] . '`';
3262 3243 }
3263 3244
3264 3245 $unique = '1' === $row['index']['unique'] ? 'UNIQUE' : '';
3265 3246
@@ -3267,9 +3248,9 @@
3267 3248 * Use IF NOT EXISTS to avoid collisions with indexes that were
3268 3249 * a part of the CREATE TABLE statement
3269 3250 */
3270 3251 $this->execute_sqlite_query(
3271 - "CREATE $unique INDEX IF NOT EXISTS " . $this->quote_identifier( $row['index']['name'] ) . " ON $quoted_table (" . implode( ', ', $columns ) . ')'
3252 + "CREATE $unique INDEX IF NOT EXISTS `{$row['index']['name']}` ON $this->table_name (" . implode( ', ', $columns ) . ')'
3272 3253 );
3273 3254 }
3274 3255
3275 3256 // Add the ON UPDATE trigger if needed.
@@ -3296,13 +3277,13 @@
3296 3277 new WP_SQLite_Token( 'CREATE', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
3297 3278 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3298 3279 new WP_SQLite_Token( $sqlite_index_type, WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
3299 3280 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3300 - new WP_SQLite_Token( $this->quote_identifier( $sqlite_index_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3281 + new WP_SQLite_Token( "\"$sqlite_index_name\"", WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3301 3282 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3302 3283 new WP_SQLite_Token( 'ON', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
3303 3284 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3304 - new WP_SQLite_Token( $this->quote_identifier( $this->table_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3285 + new WP_SQLite_Token( "\"$this->table_name\"", WP_SQLite_Token::TYPE_STRING, WP_SQLite_Token::FLAG_STRING_DOUBLE_QUOTES ),
3305 3286 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3306 3287 new WP_SQLite_Token( '(', WP_SQLite_Token::TYPE_OPERATOR ),
3307 3288 )
3308 3289 );
@@ -3328,10 +3309,10 @@
3328 3309 break;
3329 3310 }
3330 3311 // $token is field name.
3331 3312 if ( ! $token->matches( WP_SQLite_Token::TYPE_OPERATOR ) ) {
3332 - $token->token = $this->quote_identifier( $this->normalize_column_name( $token->token ) );
3333 - $token->value = $token->token;
3313 + $token->token = '`' . $this->normalize_column_name( $token->token ) . '`';
3314 + $token->value = '`' . $this->normalize_column_name( $token->token ) . '`';
3334 3315 }
3335 3316
3336 3317 /*
3337 3318 * Optionally, it may be followed by a size like `(20)`.
@@ -3354,9 +3335,9 @@
3354 3335 new WP_SQLite_Token( 'DROP', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
3355 3336 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3356 3337 new WP_SQLite_Token( 'INDEX', WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_RESERVED ),
3357 3338 new WP_SQLite_Token( ' ', WP_SQLite_Token::TYPE_WHITESPACE ),
3358 - new WP_SQLite_Token( $this->quote_identifier( $this->table_name . '__' . $key_name ), WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3339 + new WP_SQLite_Token( "\"{$this->table_name}__$key_name\"", WP_SQLite_Token::TYPE_KEYWORD, WP_SQLite_Token::FLAG_KEYWORD_KEY ),
3359 3340 )
3360 3341 );
3361 3342 } else {
3362 3343 throw new Exception( 'Unknown operation: ' . $op_type );
@@ -3614,9 +3595,9 @@
3614 3595 );
3615 3596 $tables = $this->strip_sqlite_system_tables( $stmt->fetchAll( $this->pdo_fetch_mode ) );
3616 3597 foreach ( $tables as $table ) {
3617 3598 $table_name = $table->Name; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
3618 - $stmt = $this->execute_sqlite_query( 'SELECT COUNT(1) as `Rows` FROM ' . $this->quote_identifier( $table_name ) );
3599 + $stmt = $this->execute_sqlite_query( "SELECT COUNT(1) as `Rows` FROM $table_name" );
3619 3600 $rows = $stmt->fetchall( $this->pdo_fetch_mode );
3620 3601 $table->Rows = $rows[0]->Rows; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
3621 3602 }
3622 3603
@@ -3684,9 +3665,9 @@
3684 3665 array_unshift( $key_definitions, $pk_definition );
3685 3666 }
3686 3667
3687 3668 $sql_parts = array(
3688 - 'CREATE TABLE ' . $this->quote_identifier( $table_name ) . ' (',
3669 + "CREATE TABLE `$table_name` (",
3689 3670 "\t" . implode( ",\n\t", array_merge( $column_definitions, $key_definitions ) ),
3690 3671 ');',
3691 3672 );
3692 3673
@@ -3706,9 +3687,9 @@
3706 3687 *
3707 3688 * @return stdClass[]
3708 3689 */
3709 3690 protected function get_table_columns( $table_name ) {
3710 - return $this->execute_sqlite_query( 'PRAGMA table_info(' . $this->pdo->quote( $table_name ) . ');' )
3691 + return $this->execute_sqlite_query( "PRAGMA table_info(\"$table_name\");" )
3711 3692 ->fetchAll( $this->pdo_fetch_mode );
3712 3693 }
3713 3694
3714 3695 /**
@@ -3725,9 +3706,9 @@
3725 3706 foreach ( $columns as $column ) {
3726 3707 $mysql_type = $this->get_cached_mysql_data_type( $table_name, $column->name );
3727 3708 $is_auto_incr = $auto_increment_column && strtolower( $auto_increment_column ) === strtolower( $column->name );
3728 3709 $definition = array();
3729 - $definition[] = $this->quote_identifier( $column->name );
3710 + $definition[] = '`' . $column->name . '`';
3730 3711 $definition[] = $mysql_type ?? $column->name;
3731 3712
3732 3713 if ( '1' === $column->notnull ) {
3733 3714 $definition[] = 'NOT NULL';
@@ -3783,9 +3764,9 @@
3783 3764
3784 3765 // Remove the prefix from the index name if there is any. We use __ as a separator.
3785 3766 $index_name = explode( '__', $key['index']['name'], 2 )[1] ?? $key['index']['name'];
3786 3767
3787 - $key_definition[] = $this->quote_identifier( $index_name );
3768 + $key_definition[] = sprintf( '`%s`', $index_name );
3788 3769
3789 3770 $cols = array_map(
3790 3771 function ( $column ) use ( $table_name, $key_length_limit ) {
3791 3772 $data_type = strtolower( $this->get_cached_mysql_data_type( $table_name, $column['name'] ) );
@@ -3803,11 +3784,11 @@
3803 3784 str_ends_with( $data_type, 'text' ) ||
3804 3785 str_ends_with( $data_type, 'blob' ) ||
3805 3786 str_starts_with( $data_type, 'var' )
3806 3787 ) {
3807 - return $this->quote_identifier( $column['name'] ) . '(' . $data_length . ')';
3788 + return sprintf( '`%s`(%s)', $column['name'], $data_length );
3808 3789 }
3809 - return $this->quote_identifier( $column['name'] );
3790 + return sprintf( '`%s`', $column['name'] );
3810 3791 },
3811 3792 $key['columns']
3812 3793 );
3813 3794
@@ -3838,9 +3819,9 @@
3838 3819 );
3839 3820
3840 3821 foreach ( $columns as $column ) {
3841 3822 if ( '0' !== $column->pk ) {
3842 - $primary_keys[] = $this->quote_identifier( $column->name );
3823 + $primary_keys[] = sprintf( '`%s`', $column->name );
3843 3824 }
3844 3825 }
3845 3826
3846 3827 return ! empty( $primary_keys )
@@ -3855,21 +3836,15 @@
3855 3836 *
3856 3837 * @return string|null
3857 3838 */
3858 3839 private function get_autoincrement_column( $table_name ) {
3859 - $create_table = $this->get_sqlite_create_table( $table_name );
3840 + preg_match(
3841 + '/"([^"]+)"\s+integer\s+primary\s+key\s+autoincrement/i',
3842 + $this->get_sqlite_create_table( $table_name ),
3843 + $matches
3844 + );
3860 3845
3861 - // Match backtick-quoted identifiers (with escaped backticks ``).
3862 - if ( preg_match( '/`((?:[^`]|``)+)`\s+integer\s+primary\s+key\s+autoincrement/i', $create_table, $matches ) ) {
3863 - return str_replace( '``', '`', $matches[1] );
3864 - }
3865 -
3866 - // Match double-quote-quoted identifiers (with escaped double-quotes "").
3867 - if ( preg_match( '/"((?:[^"]|"")+)"\s+integer\s+primary\s+key\s+autoincrement/i', $create_table, $matches ) ) {
3868 - return str_replace( '""', '"', $matches[1] );
3869 - }
3870 -
3871 - return null;
3846 + return $matches[1] ?? null;
3872 3847 }
3873 3848
3874 3849 /**
3875 3850 * Gets the columns from a table for the SHOW COLUMNS query.
@@ -4049,41 +4024,12 @@
4049 4024 *
4050 4025 * @return string The normalized column name.
4051 4026 */
4052 4027 private function normalize_column_name( $column_name ) {
4053 - $first = substr( $column_name, 0, 1 );
4054 - $last = substr( $column_name, -1 );
4055 -
4056 - // Strip matching surrounding quotes and unescape doubled instances.
4057 - if ( '`' === $first && '`' === $last && strlen( $column_name ) >= 2 ) {
4058 - return str_replace( '``', '`', substr( $column_name, 1, -1 ) );
4059 - }
4060 - if ( '"' === $first && '"' === $last && strlen( $column_name ) >= 2 ) {
4061 - return str_replace( '""', '"', substr( $column_name, 1, -1 ) );
4062 - }
4063 - if ( "'" === $first && "'" === $last && strlen( $column_name ) >= 2 ) {
4064 - return str_replace( "''", "'", substr( $column_name, 1, -1 ) );
4065 - }
4066 -
4067 - return $column_name;
4028 + return trim( $column_name, '`\'"' );
4068 4029 }
4069 4030
4070 4031 /**
4071 - * Quotes an identifier for safe use in SQLite queries.
4072 - *
4073 - * Wraps the identifier in backticks and escapes any internal backticks
4074 - * by doubling them. This ensures identifiers with special characters
4075 - * are properly escaped in the target SQLite query context.
4076 - *
4077 - * @param string $identifier The unquoted identifier.
4078 - *
4079 - * @return string The properly quoted identifier.
4080 - */
4081 - private function quote_identifier( $identifier ) {
4082 - return '`' . str_replace( '`', '``', $identifier ) . '`';
4083 - }
4084 -
4085 - /**
4086 4032 * Normalizes an index type.
4087 4033 *
4088 4034 * @param string $index_type The index type.
4089 4035 *
@@ -4518,17 +4464,14 @@
4518 4464
4519 4465 // The trigger wouldn't work for virtual and "WITHOUT ROWID" tables,
4520 4466 // but currently that can't happen as we're not creating such tables.
4521 4467 // See: https://www.sqlite.org/rowidtable.html
4522 - $quoted_trigger = $this->quote_identifier( $trigger_name );
4523 - $quoted_table = $this->quote_identifier( $table );
4524 - $quoted_column = $this->quote_identifier( $column );
4525 4468 $this->execute_sqlite_query(
4526 - "CREATE TRIGGER $quoted_trigger
4527 - AFTER UPDATE ON $quoted_table
4469 + "CREATE TRIGGER \"$trigger_name\"
4470 + AFTER UPDATE ON \"$table\"
4528 4471 FOR EACH ROW
4529 4472 BEGIN
4530 - UPDATE $quoted_table SET $quoted_column = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;
4473 + UPDATE \"$table\" SET \"$column\" = CURRENT_TIMESTAMP WHERE rowid = NEW.rowid;
4531 4474 END"
4532 4475 );
4533 4476 }
4534 4477