Details
-
Type:
Improvement
-
Status:
Open
-
Priority:
Major
-
Resolution: Unresolved
-
Affects Version/s: 2.0.5
-
Fix Version/s: None
-
Component/s: Drivers
-
Labels:None
Description
SQL statements may contain question marks in strings, inline comments or comment blocks. The current implementation of the method "convertPositionalToNamedPlaceholders()" doesn't consider these implications and falsely replaces them by named bind variables.
Replacement code with example:
<?php $s = '-- Testkomm?ntar select /* ? *//* ??? */ ?||\'H"al?l"o?\' as "h?""llo" union /* "? Kommentar" \' */ /* ?" */ select \'/*Hallo\'||to_char( ? ) union select \'--Welt\' union-- select ?'; echo 'In: ' . $s . PHP_EOL; $bind = 0; $skip = array( '--' => PHP_EOL, '/*' => '*/', '"' => '"', "'" => "'" ); for( $i = 0; $i < strlen( $s ) /* size of string might change! */; /* yes, no increment here! */ ) { // Skipping comments and literals foreach( $skip as $begin => $end ) { $matches = substr_compare( $s, $begin, $i, strlen( $begin ) ); if( $matches !== false && $matches == 0 ) { $pos = strpos( $s, $end, $i+strlen( $begin ) ); // echo "Found $begin, skipping at $i to $end at $pos" . PHP_EOL; if( $pos === false ) { // No more data or illegal statement - anyway: no more replacements! // echo "EOD" . PHP_EOL; break 2; } $i = $pos + strlen( $end ); continue 2; // Ensure we match /*..*//*..*/, '''' or """" - that's why we don't ++$i in the for-loop! } } if( $s[$i] == "?" ) { // Positional to named // echo "Replace $bind" . PHP_EOL; $r = ':name' . ++$bind; $s = substr_replace( $s, $r, $i, 1 ); $i += strlen( $r ); } ++$i; } echo 'Out: ' . $s . PHP_EOL;
Ok, sent you a PM but I'll put my response here, too:
"Painfully slow" at what circumstances? Benchmarks?
This was meant as an example implementation to show the problems of the current implementation - there's always room for improvement.
And never forget: Premature optimization is the root of all evil! ;-p
You want speed - try this one:
<?php $sql = '-- Testkomm?ntar select /* ? *//* ??? */ ?||\'H"al?l"o?\' as "h?""llo" union /* "? Kommentar" \' */ /* ?" */ select \'/*Hallo\'||to_char( ? ) union select \'--Welt\' union-- select ?'; echo 'In: ' . $sql . PHP_EOL; $map = array(); for( $cnt = 0; $cnt < 1000; ++$cnt ) { $bind = 0; $s = $sql; if( isset( $map[$s] ) ) continue; $strlen = strlen( $s ); for( $i = 0; $i < $strlen /* size of string might change! */; /* yes, no increment here! */ ) { $c1 = $s[$i]; $c2 = isset( $s[$i+1] ) ? $s[$i+1] : ''; if( $c1 == '-' && $c2 == '-') { $pos = strpos( $s, PHP_EOL, $i+2 ); if( $pos === false ) { break 2; } $i = $pos + 2; } elseif( $c1 == '/' && $c2 == '*' ) { $pos = strpos( $s, '*/', $i+2 ); if( $pos === false ) { break 2; } $i = $pos + 2; } elseif( $c1 == '"' ) { $pos = strpos( $s, '"', $i+1 ); if( $pos === false ) { break 2; } $i = $pos + 1; } elseif( $c1 == "'" ) { $pos = strpos( $s, "'", $i+1 ); if( $pos === false ) { break 2; } $i = $pos + 1; } elseif( $c1 == "?" ) { // Positional to named // echo "Replace $bind" . PHP_EOL; $r = ':name' . ++$bind; $s = substr_replace( $s, $r, $i, 1 ); $rLen = strlen( $r ); $i += $rLen; $strlen += $rLen - 1; } else ++$i; } $map[$sql] = $s; } echo 'Out: ' . $map[$sql] . PHP_EOL;ATTENTION: Each "break 2;" means, you have an invalid statement! Do, whatever the statement class is supposed to do in this case!
Regards, Carsten