-
Notifications
You must be signed in to change notification settings - Fork 55
Allow SQL expressions in array condition keys #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,7 +66,22 @@ abstract class xPDOQuery extends xPDOCriteria { | |
| 'MIN(', | ||
| 'AVG(' | ||
| ); | ||
| protected $_quotable= array ('string', 'password', 'date', 'datetime', 'timestamp', 'time', 'json', 'array', 'float'); | ||
| protected $_quotable = [ | ||
| 'string', | ||
| 'password', | ||
| 'date', | ||
| 'datetime', | ||
| 'timestamp', | ||
| 'time', | ||
| 'json', | ||
| 'array', | ||
| 'float', | ||
| 'double', | ||
| 'object', | ||
| 'resource', | ||
| 'unknown type', | ||
| 'resource (closed)' | ||
| ]; | ||
| protected $_class= null; | ||
| protected $_alias= null; | ||
| protected $_tableClass = null; | ||
|
|
@@ -737,30 +752,42 @@ public function parseConditions($conditions, $conjunction = xPDOQuery::SQL_AND) | |
| $key= $key_operator[1]; | ||
| $operator= strtoupper($key_operator[2]); | ||
| } | ||
| if (strpos($key, '.') !== false) { | ||
| $key_parts= explode('.', $key); | ||
| $alias= trim($key_parts[0], " {$this->xpdo->_escapeCharOpen}{$this->xpdo->_escapeCharClose}"); | ||
| $key= $key_parts[1]; | ||
| } | ||
| if (!array_key_exists($key, $fieldMeta)) { | ||
| if (array_key_exists($key, $fieldAliases)) { | ||
| $key= $fieldAliases[$key]; | ||
| } elseif ($this->isConditionalClause($key)) { | ||
| continue; | ||
| $operand = $key; | ||
| $isColumnIdentifier = $this->isColumnIdentifier($key); | ||
| if ($isColumnIdentifier) { | ||
| if (strpos($key, '.') !== false) { | ||
| $key_parts = explode('.', $key); | ||
| $alias = trim( | ||
| $key_parts[0], | ||
| " {$this->xpdo->_escapeCharOpen}{$this->xpdo->_escapeCharClose}" | ||
| ); | ||
| $key = $key_parts[1]; | ||
| } | ||
| if (!array_key_exists($key, $fieldMeta)) { | ||
| if (array_key_exists($key, $fieldAliases)) { | ||
| $key = $fieldAliases[$key]; | ||
| } | ||
| } | ||
| $operand = "{$this->xpdo->escape($alias)}.{$this->xpdo->escape($key)}"; | ||
| } | ||
| if (!empty($key)) { | ||
| if (!empty($key) && !$this->isConditionalClause($key)) { | ||
| if ($val === null) { | ||
| $type= \PDO::PARAM_NULL; | ||
| if (!in_array($operator, array('IS', 'IS NOT'))) { | ||
| $operator= $operator === '!=' ? 'IS NOT' : 'IS'; | ||
| } | ||
| } | ||
| elseif (isset($fieldMeta[$key]) && !in_array($fieldMeta[$key]['phptype'], $this->_quotable)) { | ||
| $type= \PDO::PARAM_INT; | ||
| } | ||
| else { | ||
| $type= \PDO::PARAM_STR; | ||
| elseif ($isColumnIdentifier) { | ||
| if (isset($fieldMeta[$key]) && !in_array( | ||
| $fieldMeta[$key]['phptype'], | ||
| $this->_quotable | ||
| )) { | ||
| $type = \PDO::PARAM_INT; | ||
| } else { | ||
| $type = \PDO::PARAM_STR; | ||
| } | ||
| } else { | ||
| $type = $this->isQuotable($val) ? \PDO::PARAM_STR : \PDO::PARAM_INT; | ||
| } | ||
| if (in_array($operator, array('IN', 'NOT IN')) && is_array($val)) { | ||
| $vals = array(); | ||
|
|
@@ -785,12 +812,12 @@ public function parseConditions($conditions, $conjunction = xPDOQuery::SQL_AND) | |
| $this->xpdo->log(xPDO::LOG_LEVEL_ERROR, "Encountered empty {$operator} condition with key {$key}"); | ||
| } | ||
| $val = "(" . implode(',', $vals) . ")"; | ||
| $sql = "{$this->xpdo->escape($alias)}.{$this->xpdo->escape($key)} {$operator} {$val}"; | ||
| $sql = "{$operand} {$operator} {$val}"; | ||
| $result[]= new xPDOQueryCondition(array('sql' => $sql, 'binding' => null, 'conjunction' => $conj)); | ||
| continue; | ||
| } | ||
| $field= array (); | ||
| $field['sql']= $this->xpdo->escape($alias) . '.' . $this->xpdo->escape($key) . ' ' . $operator . ' ?'; | ||
| $field['sql']= $operand . ' ' . $operator . ' ?'; | ||
|
Comment on lines
755
to
+820
|
||
| $field['binding']= array ( | ||
| 'value' => $val, | ||
| 'type' => $type, | ||
|
|
@@ -808,8 +835,8 @@ public function parseConditions($conditions, $conjunction = xPDOQuery::SQL_AND) | |
| elseif ($this->isConditionalClause($conditions)) { | ||
| $result= new xPDOQueryCondition(array( | ||
| 'sql' => $conditions | ||
| ,'binding' => null | ||
| ,'conjunction' => $conjunction | ||
| ,'binding' => null | ||
| ,'conjunction' => $conjunction | ||
| )); | ||
| } | ||
| elseif (($pktype == 'integer' && is_numeric($conditions)) || ($pktype == 'string' && is_string($conditions) && static::isValidClause($conditions))) { | ||
|
|
@@ -937,4 +964,10 @@ public function __debugInfo() | |
| 'bindings' => $this->bindings, | ||
| ]; | ||
| } | ||
|
|
||
| protected function isQuotable($value) { | ||
| return in_array(gettype($value), $this->_quotable); | ||
| } | ||
|
|
||
| abstract protected function isColumnIdentifier($key); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _quotable array now includes PHP types returned by gettype() ('double', 'object', 'resource', 'unknown type', 'resource (closed)'), but it previously contained database field types ('string', 'password', 'date', 'datetime', 'timestamp', 'time', 'json', 'array', 'float'). This mixed usage creates confusion: the array is used with phptype from field metadata (which are database types) at line 781-783, and with gettype() results (which are PHP types) at line 969. The 'float' entry could match both phptype 'float' and gettype 'double', but 'array' is a database type that won't be returned by gettype. Consider separating these into two distinct arrays or documenting which values correspond to which use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, they are pseudo-PHP types already, not direct representations of database field types, meant to bridge actual database types to a type represented in PHP. This was added so that conditions that do not directly represent a field in the database model can handle values appropriately in the resulting PDO prepared statement.