}
/**
- * Validate an acceptable column name for sorting results.
+ * Validate that a string is a valid MySQL column name or alias.
*
* @param $str
*
* @return bool
*/
- public static function mysqlColumnName($str) {
+ public static function mysqlColumnNameOrAlias($str) {
// Check not empty.
if (empty($str)) {
return FALSE;
}
- // Ensure it only contains valid characters (alphanumeric, hyphens and
- // underscores).
- //
- // MySQL permits column names that don't match this (eg containing spaces),
- // but CiviCRM won't create those ...
- if (!preg_match('/^[\w-]{1,64}(\.[\w-]{1,64})?$/i', $str)) {
+ // Ensure the string contains only valid characters:
+ // For column names: alphanumeric and underscores
+ // For aliases: backticks, alphanumeric hyphens and underscores.
+ if (!preg_match('^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})$/i', $str)) {
return FALSE;
}
// at all, so we split and loop over.
$parts = explode(',', $str);
foreach ($parts as $part) {
- if (!preg_match('/^(([\w-]{1,64})((\.)([\w-]{1,64}))?( (asc|desc))?)$/i', trim($part))) {
+ if (!preg_match('/^((`[\w-]{1,64}`|\w{1,64})\.)?(`[\w-]{1,64}`|\w{1,64})( (asc|desc))?$/i', trim($part))) {
return FALSE;
}
}
}
break;
- case 'MysqlColumnName':
- if (CRM_Utils_Rule::mysqlColumnName($data)) {
+ case 'mysqlColumnNameOrAlias':
+ if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) {
+ $data = str_replace('`', '', $data);
$parts = explode('.', $data);
$data = '`' . implode('`.`', $parts) . '`';
if (CRM_Utils_Rule::mysqlOrderBy($data)) {
$parts = explode(',', $data);
foreach ($parts as &$part) {
- $part = preg_replace_callback('/(?:([\w]+)(?:(?:\.)([\w]+))?(?: (asc|desc))?)/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part));
+ $part = preg_replace_callback('/^(?:(?:((?:`[\w-]{1,64}`|\w{1,64}))(?:\.))?(`[\w-]{1,64}`|\w{1,64})(?: (asc|desc))?)$/i', array('CRM_Utils_Type', 'mysqlOrderByCallback'), trim($part));
}
return implode(', ', $parts);
}
}
break;
- case 'MysqlColumnName':
- if (CRM_Utils_Rule::mysqlColumnName($data)) {
+ case 'mysqlColumnNameOrAlias':
+ if (CRM_Utils_Rule::mysqlColumnNameOrAlias($data)) {
return $data;
}
break;
*/
public static function mysqlOrderByCallback($matches) {
$output = '';
+ $matches = str_replace('`', '', $matches);
- // Column or table name.
+ // Table name.
if (isset($matches[1])) {
- $output .= '`' . $matches[1] . '`';
+ $output .= '`' . $matches[1] . '`.';
}
- // Column name in case there is a table.
+ // Column name.
if (isset($matches[2]) && $matches[2]) {
- $output .= '.`' . $matches[2] . '`';
+ $output .= '`' . $matches[2] . '`';
}
// Sort order.
array(-10, 'Positive', NULL),
array('-10', 'Positive', NULL),
array('-10foo', 'Positive', NULL),
- array('civicrm_column_name', 'MysqlColumnName', 'civicrm_column_name'),
- array('table.civicrm_column_name', 'MysqlColumnName', 'table.civicrm_column_name'),
- array('table.civicrm_column_name.toomanydots', 'MysqlColumnName', NULL),
- array('Home-street_address', 'MysqlColumnName', 'Home-street_address'),
- array('column_name, sleep(5)', 'MysqlColumnName', NULL),
- array(str_repeat('a', 64), 'MysqlColumnName', str_repeat('a', 64)),
- array(str_repeat('a', 65), 'MysqlColumnName', NULL),
- array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'MysqlColumnName', str_repeat('a', 64) . '.' . str_repeat('a', 64)),
- array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'MysqlColumnName', NULL),
- array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'MysqlColumnName', NULL),
+ array('civicrm_column_name', 'mysqlColumnNameOrAlias', 'civicrm_column_name'),
+ array('table.civicrm_column_name', 'mysqlColumnNameOrAlias', 'table.civicrm_column_name'),
+ array('table.civicrm_column_name.toomanydots', 'mysqlColumnNameOrAlias', NULL),
+ array('Home-street_address', 'mysqlColumnNameOrAlias', NULL),
+ array('`Home-street_address`', 'mysqlColumnNameOrAlias', '`Home-street_address`'),
+ array('table.`Home-street_address`', 'mysqlColumnNameOrAlias', 'table.`Home-street_address`'),
+ array('`table-alias`.`Home-street_address`', 'mysqlColumnNameOrAlias', '`table-alias`.`Home-street_address`'),
+ array('`table-alias`.column', 'mysqlColumnNameOrAlias', '`table-alias`.column'),
+ array('column_name, sleep(5)', 'mysqlColumnNameOrAlias', NULL),
+ array(str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64)),
+ array(str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL),
+ array(str_repeat('a', 64) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', str_repeat('a', 64) . '.' . str_repeat('a', 64)),
+ array(str_repeat('a', 64) . '.' . str_repeat('a', 65), 'mysqlColumnNameOrAlias', NULL),
+ array(str_repeat('a', 65) . '.' . str_repeat('a', 64), 'mysqlColumnNameOrAlias', NULL),
array('asc', 'MysqlOrderByDirection', 'asc'),
array('DESC', 'MysqlOrderByDirection', 'desc'),
array('DESCc', 'MysqlOrderByDirection', NULL),
array('table.civicrm_column_name desc', 'MysqlOrderBy', 'table.civicrm_column_name desc'),
array('table.civicrm_column_name desc,other_column, another_column desc', 'MysqlOrderBy', 'table.civicrm_column_name desc,other_column, another_column desc'),
+ array('table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column', 'MysqlOrderBy', 'table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column'),
);
}
array('DESCc', 'MysqlOrderByDirection', NULL),
array('table.civicrm_column_name desc', 'MysqlOrderBy', '`table`.`civicrm_column_name` desc'),
array('table.civicrm_column_name desc,other_column,another_column desc', 'MysqlOrderBy', '`table`.`civicrm_column_name` desc, `other_column`, `another_column` desc'),
+ array('table.`Home-street_address` asc, `table-alias`.`Home-street_address` desc,`table-alias`.column', 'MysqlOrderBy', '`table`.`Home-street_address` asc, `table-alias`.`Home-street_address` desc, `table-alias`.`column`'),
);
}