37 private const QUERY_CHANGE_MASK = (
38 SQLPlatform::QUERY_CHANGE_NONE |
39 SQLPlatform::QUERY_CHANGE_TRX |
40 SQLPlatform::QUERY_CHANGE_ROWS |
41 SQLPlatform::QUERY_CHANGE_SCHEMA |
42 SQLPlatform::QUERY_CHANGE_LOCKS
43 ) & ~SQLPlatform::QUERY_IGNORE_DBO_TRX;
45 private const SCHEMA_CHANGE_VERBS = [
52 private const TRX_VERBS = [
58 'ROLLBACK TO SAVEPOINT',
67 public static function buildQuery(
string $sql, $flags,
string $tablePrefix =
'' ) {
68 $verb = self::getQueryVerb( $sql );
70 if ( ( $flags & self::QUERY_CHANGE_MASK ) == 0 ) {
71 $isWriteQuery = self::isWriteQuery( $sql );
72 if ( $isWriteQuery ) {
73 if ( in_array( $verb, self::SCHEMA_CHANGE_VERBS,
true ) ) {
74 $flags |= SQLPlatform::QUERY_CHANGE_SCHEMA;
76 $flags |= SQLPlatform::QUERY_CHANGE_ROWS;
79 if ( in_array( $verb, self::TRX_VERBS,
true ) ) {
80 $flags |= SQLPlatform::QUERY_CHANGE_TRX;
82 $flags |= SQLPlatform::QUERY_CHANGE_NONE;
86 if ( self::isCreateTemporaryTable( $sql ) ) {
87 $flags |= SQLPlatform::QUERY_CREATE_TEMP;
95 self::getWriteTable( $sql, $tablePrefix )
99 private static function isWriteQuery( $rawSql ) {
103 if ( preg_match(
'/^\s*\(?SELECT\b/i', $rawSql ) ) {
104 return (
bool)preg_match(
'/\bFOR\s+UPDATE\)?\s*$/i', $rawSql );
120 '/^\s*(BEGIN|ROLLBACK|COMMIT|SAVEPOINT|RELEASE|SET|SHOW|EXPLAIN|USE)\b/i',
129 private static function getQueryVerb( $sql ) {
132 '/^\s*(rollback\s+to\s+savepoint|create\s+temporary|[a-z]+)/i',
135 ) ? strtoupper( $m[1] ) :
'';
143 private static function getWriteTable( $sql, $tablePrefix ) {
150 (?:INSERT|REPLACE)\s+(?:\w+\s+)*?INTO
151 | UPDATE(?:\s+OR\s+\w+|\s+IGNORE|\s+ONLY)?
152 | DELETE\s+(?:\w+\s+)*?FROM(?:\s+ONLY)?
153 | CREATE\s+(?:TEMPORARY\s+)?TABLE(?:\s+IF\s+NOT\s+EXISTS)?
154 | DROP\s+(?:TEMPORARY\s+)?TABLE(?:\s+IF\s+EXISTS)?
155 | TRUNCATE\s+(?:TEMPORARY\s+)?TABLE
158 (\w+|`\w+`|
'\w+'|
"\w+")
161 if ( preg_match( $regex, $sql, $m ) ) {
162 $tableName = trim( $m[1],
"\"'`" );
163 if ( str_starts_with( $tableName, $tablePrefix ) ) {
164 $tableName = substr( $tableName, strlen( $tablePrefix ) );
171 private static function isCreateTemporaryTable( $sql ) {
172 return (
bool)preg_match(
'/^CREATE\s+TEMPORARY\s+TABLE\s+/i', $sql );
183 public static function generalizeSQL( $sql ) {
184 # This does the same as the regexp below would do, but in such a way
185 # as to avoid crashing php on some large strings.
186 # $sql = preg_replace( "/'([^\\\\']|\\\\.)*'|\"([^\\\\\"]|\\\\.)*\"/", "'X'", $sql );
188 $sql = str_replace(
"\\\\",
'', $sql );
189 $sql = str_replace(
"\\'",
'', $sql );
190 $sql = str_replace(
"\\\"",
'', $sql );
191 $sql = preg_replace(
"/'.*'/s",
"'X'", $sql );
192 $sql = preg_replace(
'/".*"/s',
"'X'", $sql );
194 # All newlines, tabs, etc replaced by single space
195 $sql = preg_replace(
'/\s+/',
' ', $sql );
198 # except the ones surrounded by characters, e.g. l10n
199 $sql = preg_replace(
'/-?\d+(,-?\d+)+/',
'N,...,N', $sql );
200 $sql = preg_replace(
'/(?<![a-zA-Z])-?\d+(?![a-zA-Z])/',
'N', $sql );