ProxySQL MySQL MySQL · DBA · Advanced

ProxySQLQuery Rewriting

Rewrite SQL queries on the fly with ProxySQL replace_pattern rules. Add hints, rename tables, cap results, force indexes and route schemas without changing application code.

ProxySQL can modify SQL queries on the fly before sending them to MySQL backends using the replace_pattern column in mysql_query_rules. This is done using regular expressions — ProxySQL finds the match_pattern and replaces it with replace_pattern.

Query rewriting is extremely useful for:

  • Adding query hints without touching application code
  • Renaming tables/schemas during migration
  • Adding or removing SQL directives (SQL_NO_CACHE, STRAIGHT_JOIN)
  • Fixing poorly written queries from legacy applications
  • Adding LIMIT clauses to runaway queries
SQL — Add SQL_NO_CACHE
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, replace_pattern, apply, comment)
VALUES
  (300, 1, '^(SELECT)', '\1 SQL_NO_CACHE', 0, 'Add SQL_NO_CACHE hint');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

-- Before: SELECT * FROM users WHERE id=1
-- After:  SELECT SQL_NO_CACHE * FROM users WHERE id=1
SQL — Add LIMIT Cap
-- Add LIMIT 10000 to any SELECT without a LIMIT clause
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, replace_pattern, apply, comment)
VALUES
  (301, 1, '^(SELECT (?!.*LIMIT).*)', '\1 LIMIT 10000', 1, 'Cap SELECTs at 10000 rows');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SQL — Table Rename
-- Transparently redirect queries from old_users to new_users
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, replace_pattern, apply, comment)
VALUES
  (302, 1, 'old_users', 'new_users', 0, 'Rename old_users → new_users');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

-- Before: SELECT * FROM old_users WHERE email='a@b.com'
-- After:  SELECT * FROM new_users WHERE email='a@b.com'
SQL — Add Index Hint
-- Force index usage for a slow query
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, replace_pattern, apply, comment)
VALUES
  (303, 1, 'SELECT (.*) FROM orders WHERE status',
   'SELECT \1 FROM orders USE INDEX (idx_status) WHERE status',
   1, 'Force index on orders status queries');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SQL — Schema Routing
-- Route specific user to new schema version
INSERT INTO mysql_query_rules
  (rule_id, active, username, match_pattern, replace_pattern, apply, comment)
VALUES
  (304, 1, 'beta_user', 'myapp\.', 'myapp_v2.', 0, 'Beta users → new schema');

LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
SQL — Verify Rewriting
-- Check rule hits to confirm rewriting is happening
SELECT rule_id, hits, match_pattern, replace_pattern, comment
FROM stats.stats_mysql_query_rules
WHERE replace_pattern IS NOT NULL
ORDER BY hits DESC;

-- Check query digest to see rewritten queries
SELECT digest_text, count_star, hostgroup
FROM stats.stats_mysql_query_digest
ORDER BY count_star DESC LIMIT 10;
  • apply=0 means continue checking rules after rewriting — useful when you want to rewrite AND then route
  • apply=1 stops processing — query is rewritten and routed to destination_hostgroup
  • Use re_modifiers='CASELESS' for case-insensitive matching
  • Test regex patterns carefully — a bad pattern can match unexpected queries
  • ProxySQL uses POSIX ERE (Extended Regular Expressions)
SQL — Case-insensitive Rewriting
-- Case-insensitive rewriting
INSERT INTO mysql_query_rules
  (rule_id, active, match_pattern, replace_pattern, re_modifiers, apply, comment)
VALUES
  (310, 1, 'select', 'SELECT', 'CASELESS', 0, 'Uppercase all selects');

-- Test regex before deploying using mysql_query_rules_fast_routing
-- Check what ProxySQL would match:
SELECT * FROM mysql_query_rules WHERE active=1 ORDER BY rule_id;
⚠ Warning: Always test query rewriting in a development environment first. A bad replace_pattern can corrupt queries silently.