[DBAL-124] OCI8 Adapter (convertPositionalToNamedPlaceholders) Statemachine doesn't consider comments and strings Created: 20/May/11 Updated: 28/Jun/11 |
|
| Status: | Open |
| Project: | Doctrine DBAL |
| Component/s: | Drivers |
| Affects Version/s: | 2.0.5 |
| Fix Version/s: | None |
| Type: | Improvement | Priority: | Major |
| Reporter: | Carsten Hetzel | Assignee: | Benjamin Eberlei |
| Resolution: | Unresolved | Votes: | 0 |
| 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; |
| Comments |
| Comment by Benjamin Eberlei [ 19/Jun/11 ] |
|
This algorithmus is painfully slow. There has to be something better, why do you need to foreach the loop inside the for? |
| Comment by Carsten Hetzel [ 28/Jun/11 ] |
|
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 |