showqueries debugging tool now inserts parameters in place (#5885)

This commit is contained in:
Daniel Hensby 2016-09-14 22:15:19 +01:00 committed by Damian Mooyman
parent a9df28c791
commit f25b88b146
4 changed files with 68 additions and 36 deletions

View File

@ -38,10 +38,10 @@ Append the option and corresponding value to your URL in your browser's address
## Database ## Database
| URL Variable | | Values | | Description | | URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- | | ------------ | | --------- | | ----------- |
| showqueries | | 1 | | List all SQL queries executed | | showqueries | | 1\|inline | | List all SQL queries executed, the `inline` option will do a fudge replacement of parameterised queries |
| previewwrite | | 1 | | List all insert / update SQL queries, and **don't** execute them. Useful for previewing writes to the database. | | previewwrite | | 1 | | List all insert / update SQL queries, and **don't** execute them. Useful for previewing writes to the database. |
## Security Redirects ## Security Redirects

View File

@ -282,7 +282,7 @@ class DB {
* *
* @param array|integer $input An array of items needing placeholders, or a * @param array|integer $input An array of items needing placeholders, or a
* number to specify the number of placeholders * number to specify the number of placeholders
* @param string The string to join each placeholder together with * @param string $join The string to join each placeholder together with
* @return string|null Either a list of placeholders, or null * @return string|null Either a list of placeholders, or null
*/ */
public static function placeholders($input, $join = ', ') { public static function placeholders($input, $join = ', ') {
@ -297,6 +297,60 @@ class DB {
return implode($join, array_fill(0, $number, '?')); return implode($join, array_fill(0, $number, '?'));
} }
/**
* @param string $sql The parameterised query
* @param array $parameters The parameters to inject into the query
*
* @return string
*/
public static function inline_parameters($sql, $parameters) {
$segments = preg_split('/\?/', $sql);
$joined = '';
$inString = false;
$numSegments = count($segments);
for($i = 0; $i < $numSegments; $i++) {
$input = $segments[$i];
// Append next segment
$joined .= $segments[$i];
// Don't add placeholder after last segment
if($i === $numSegments - 1) {
break;
}
// check string escape on previous fragment
// Remove escaped backslashes, count them!
$input = preg_replace('/\\\\\\\\/', '', $input);
// Count quotes
$totalQuotes = substr_count($input, "'"); // Includes double quote escaped quotes
$escapedQuotes = substr_count($input, "\\'");
if((($totalQuotes - $escapedQuotes) % 2) !== 0) {
$inString = !$inString;
}
// Append placeholder replacement
if($inString) {
// Literal question mark
$joined .= '?';
continue;
}
// Encode and insert next parameter
$next = array_shift($parameters);
if(is_array($next) && isset($next['value'])) {
$next = $next['value'];
}
if (is_bool($next)) {
$value = $next ? '1' : '0';
}
elseif (is_int($next)) {
$value = $next;
}
else {
$value = DB::is_active() ? Convert::raw2sql($next, true) : $next;
}
$joined .= $value;
}
return $joined;
}
/** /**
* Execute the given SQL parameterised query with the specified arguments * Execute the given SQL parameterised query with the specified arguments
* *

View File

@ -141,7 +141,8 @@ abstract class SS_Database {
$sql, $sql,
function($sql) use($connector, $parameters, $errorLevel) { function($sql) use($connector, $parameters, $errorLevel) {
return $connector->preparedQuery($sql, $parameters, $errorLevel); return $connector->preparedQuery($sql, $parameters, $errorLevel);
} },
$parameters
); );
} }
@ -174,13 +175,18 @@ abstract class SS_Database {
* *
* @param string $sql Query to run, and single parameter to callback * @param string $sql Query to run, and single parameter to callback
* @param callable $callback Callback to execute code * @param callable $callback Callback to execute code
* @param array $parameters Parameters for any parameterised query
* @return mixed Result of query * @return mixed Result of query
*/ */
protected function benchmarkQuery($sql, $callback) { protected function benchmarkQuery($sql, $callback, $parameters = array()) {
if (isset($_REQUEST['showqueries']) && Director::isDev()) { if (isset($_REQUEST['showqueries']) && Director::isDev()) {
$starttime = microtime(true); $starttime = microtime(true);
$result = $callback($sql); $result = $callback($sql);
$endtime = round(microtime(true) - $starttime, 4); $endtime = round(microtime(true) - $starttime, 4);
// replace parameters as closely as possible to what we'd expect the DB to put in
if (strtolower($_REQUEST['showqueries']) == 'inline') {
$sql = DB::inline_parameters($sql, $parameters);
}
Debug::message("\n$sql\n{$endtime}s\n", false); Debug::message("\n$sql\n{$endtime}s\n", false);
return $result; return $result;
} else { } else {

View File

@ -160,35 +160,7 @@ class SQLQuery_ParameterInjector {
* @return string * @return string
*/ */
protected function injectValues($sql, $parameters) { protected function injectValues($sql, $parameters) {
$segments = preg_split('/\?/', $sql); return DB::inline_parameters($sql, $parameters);
$joined = '';
$inString = false;
for($i = 0; $i < count($segments); $i++) {
// Append next segment
$joined .= $segments[$i];
// Don't add placeholder after last segment
if($i === count($segments) - 1) {
break;
}
// check string escape on previous fragment
if($this->checkStringTogglesLiteral($segments[$i])) {
$inString = !$inString;
}
// Append placeholder replacement
if($inString) {
// Literal questionmark
$joined .= '?';
continue;
}
// Encode and insert next parameter
$next = array_shift($parameters);
if(is_array($next) && isset($next['value'])) {
$next = $next['value'];
}
$joined .= "'".Convert::raw2sql($next)."'";
}
return $joined;
} }
/** /**