From 4f8f236d6a114c710362fdc302c4763cdf2fd387 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Sat, 26 Sep 2015 19:13:31 -0500 Subject: [PATCH] orm: Add MySQLi executor without prepared statements Simplified executor which uses the mysqli_query() function to process queries. This method is faster on MySQL as it doesn't require the PREPARE overhead, nor require two trips to the database per query. All parameters are escaped and placed directly into the SQL statement. This executor extends the traditional mysqli_fetch_xxx() methods by casting non-null numeric field values to PHP numeric types. --- include/class.orm.php | 86 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/include/class.orm.php b/include/class.orm.php index fc4c9b6c5..861a40b14 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -2897,7 +2897,7 @@ class MySqlCompiler extends SqlCompiler { } } -class MySqlExecutor { +class MySqlPreparedExecutor { var $stmt; var $fields = array(); @@ -3077,6 +3077,90 @@ class MySqlExecutor { } } +/** + * Simplified executor which uses the mysqli_query() function to process + * queries. This method is faster on MySQL as it doesn't require the PREPARE + * overhead, nor require two trips to the database per query. All parameters + * are escaped and placed directly into the SQL statement. With this style, + * it is possible that multiple parameters could compile a statement which + * exceeds the MySQL max_allowed_packet setting. + */ +class MySqlExecutor +extends MySqlPreparedExecutor { + function execute() { + $sql = $this->__toString(); + if (!($this->stmt = db_query($sql, true, !$this->unbuffered))) + throw new InconsistentModelException( + 'Unable to prepare query: '.db_error().' '.$sql); + // mysqli_query() return TRUE for UPDATE queries and friends + if ($this->stmt !== true) + $this->_setupCast(); + return true; + } + + function _setupCast() { + $fields = $this->stmt->fetch_fields(); + $this->types = array(); + foreach ($fields as $F) { + $this->types[] = $F->type; + } + } + + function _cast($record) { + $i=0; + foreach ($record as &$f) { + switch ($this->types[$i++]) { + case MYSQLI_TYPE_DECIMAL: + case MYSQLI_TYPE_NEWDECIMAL: + case MYSQLI_TYPE_LONGLONG: + case MYSQLI_TYPE_FLOAT: + case MYSQLI_TYPE_DOUBLE: + $f = isset($f) ? (double) $f : $f; + break; + + case MYSQLI_TYPE_BIT: + case MYSQLI_TYPE_TINY: + case MYSQLI_TYPE_SHORT: + case MYSQLI_TYPE_LONG: + case MYSQLI_TYPE_INT24: + $f = isset($f) ? (int) $f : $f; + break; + + default: + // No change (leave as string) + } + } + unset($f); + return $record; + } + + function getArray() { + if (!isset($this->stmt)) + $this->execute(); + + if (null === ($record = $this->stmt->fetch_assoc())) + return false; + return $this->_cast($record); + } + + function getRow() { + if (!isset($this->stmt)) + $this->execute(); + + if (null === ($record = $this->stmt->fetch_row())) + return false; + return $this->_cast($record); + } + + function affected_rows() { + return db_affected_rows(); + } + + function insert_id() { + return db_insert_id(); + } +} + class Q implements Serializable { const NEGATED = 0x0001; const ANY = 0x0002; -- GitLab