MDL-49231 mod_glossary: Fix SQL queries for Oracle

This commit is contained in:
Juan Leyva 2015-12-30 12:36:05 +01:00
parent baf25486b4
commit 06d58e6e67
2 changed files with 104 additions and 107 deletions

View File

@ -159,17 +159,6 @@ class mod_glossary_entry_query_builder {
return $DB->count_records_sql($this->build_query(true), $this->params);
}
/**
* Distinct a field.
*
* @param string $field The field.
* @param string $table The table name, without the prefix 'glossary_'.
*/
public function distinct($field, $table) {
$field = self::resolve_field($field, $table);
array_unshift($this->fields, 'DISTINCT(' . $field . ')');
}
/**
* Filter a field using a letter.
*
@ -289,78 +278,6 @@ class mod_glossary_entry_query_builder {
$this->params['filtertermb'] = $term;
}
/**
* Filter by search terms.
*
* Note that this does not handle invalid or too short terms. This requires the alias
* table to be joined in the query. See {@link self::join_alias()}.
*
* @param array $terms Array of terms.
* @param bool $fullsearch Whether or not full search should be enabled.
*/
public function filter_by_search_terms(array $terms, $fullsearch = true) {
global $DB;
static $i = 0;
if ($DB->sql_regex_supported()) {
$regexp = $DB->sql_regex(true);
$notregexp = $DB->sql_regex(false);
}
$params = array();
$conceptfield = self::resolve_field('concept', 'entries');
$aliasfield = self::resolve_field('alias', 'alias');
$definitionfield = self::resolve_field('definition', 'entries');
$conditions = array();
foreach ($terms as $searchterm) {
$i++;
$not = false; // Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle
// will use it to simulate the "-" operator with LIKE clause.
if (empty($fullsearch)) {
// With fullsearch disabled, look only within concepts and aliases.
$concat = $DB->sql_concat($conceptfield, "' '", "COALESCE($aliasfield, :emptychar{$i})");
} else {
// With fullsearch enabled, look also within definitions.
$concat = $DB->sql_concat($conceptfield, "' '", $definitionfield, "' '", "COALESCE($aliasfield, :emptychar{$i})");
}
$params['emptychar' . $i] = '';
// Under Oracle and MSSQL, trim the + and - operators and perform simpler LIKE (or NOT LIKE) queries.
if (!$DB->sql_regex_supported()) {
if (substr($searchterm, 0, 1) === '-') {
$not = true;
}
$searchterm = trim($searchterm, '+-');
}
if (substr($searchterm, 0, 1) === '+') {
$searchterm = trim($searchterm, '+-');
$conditions[] = "$concat $regexp :searchterm{$i}";
$params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
} else if (substr($searchterm, 0, 1) === "-") {
$searchterm = trim($searchterm, '+-');
$conditions[] = "$concat $notregexp :searchterm{$i}";
$params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
} else {
$conditions[] = $DB->sql_like($concat, ":searchterm{$i}", false, true, $not);
$params['searchterm' . $i] = '%' . $DB->sql_like_escape($searchterm) . '%';
}
}
// When there are no conditions we add a negative one to ensure that we don't return anything.
if (empty($conditions)) {
$conditions[] = '1 = 2';
}
$this->where[] = implode(' AND ', $conditions);
$this->params = array_merge($this->params, $params);
}
/**
* Convenience method to get get the SQL statement for the full name.
*
@ -411,7 +328,7 @@ class mod_glossary_entry_query_builder {
* Join the alias table.
*
* Note that this may cause the same entry to be returned more than once. You might want
* to add a distinct on the entry id. See {@link self::distinct()}.
* to add a distinct on the entry id.
*
* @return void
*/

View File

@ -3605,7 +3605,7 @@ function glossary_get_authors($glossary, $context, $limit, $from, $options = arr
}
$sqlselectcount = "SELECT COUNT(DISTINCT(u.id))";
$sqlselect = "SELECT DISTINCT(u.id), $userfields";
$sqlselect = "SELECT DISTINCT(u.id) AS userId, $userfields";
$sql = " FROM {user} u
JOIN {glossary_entries} ge
ON ge.userid = u.id
@ -3640,6 +3640,77 @@ function glossary_get_categories($glossary, $from, $limit) {
return array($categories, $count);
}
/**
* Get the SQL where clause for searching terms.
*
* Note that this does not handle invalid or too short terms.
*
* @param array $terms Array of terms.
* @param bool $fullsearch Whether or not full search should be enabled.
* @return array The first element being the where clause, the second array of parameters.
* @since Moodle 3.1
*/
function glossary_get_search_terms_sql(array $terms, $fullsearch = true) {
global $DB;
static $i = 0;
if ($DB->sql_regex_supported()) {
$regexp = $DB->sql_regex(true);
$notregexp = $DB->sql_regex(false);
}
$params = array();
$conditions = array();
foreach ($terms as $searchterm) {
$i++;
$not = false; // Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle
// will use it to simulate the "-" operator with LIKE clause.
if (empty($fullsearch)) {
// With fullsearch disabled, look only within concepts and aliases.
$concat = $DB->sql_concat('ge.concept', "' '", "COALESCE(al.alias, :emptychar{$i})");
} else {
// With fullsearch enabled, look also within definitions.
$concat = $DB->sql_concat('ge.concept', "' '", 'ge.definition', "' '", "COALESCE(al.alias, :emptychar{$i})");
}
$params['emptychar' . $i] = '';
// Under Oracle and MSSQL, trim the + and - operators and perform simpler LIKE (or NOT LIKE) queries.
if (!$DB->sql_regex_supported()) {
if (substr($searchterm, 0, 1) === '-') {
$not = true;
}
$searchterm = trim($searchterm, '+-');
}
if (substr($searchterm, 0, 1) === '+') {
$searchterm = trim($searchterm, '+-');
$conditions[] = "$concat $regexp :searchterm{$i}";
$params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
} else if (substr($searchterm, 0, 1) === "-") {
$searchterm = trim($searchterm, '+-');
$conditions[] = "$concat $notregexp :searchterm{$i}";
$params['searchterm' . $i] = '(^|[^a-zA-Z0-9])' . preg_quote($searchterm, '|') . '([^a-zA-Z0-9]|$)';
} else {
$conditions[] = $DB->sql_like($concat, ":searchterm{$i}", false, true, $not);
$params['searchterm' . $i] = '%' . $DB->sql_like_escape($searchterm) . '%';
}
}
// When there are no conditions we add a negative one to ensure that we don't return anything.
if (empty($conditions)) {
$conditions[] = '1 = 2';
}
$where = implode(' AND ', $conditions);
return array($where, $params);
}
/**
* Returns the entries of a glossary by search.
*
@ -3658,7 +3729,8 @@ function glossary_get_categories($glossary, $from, $limit) {
* @since Moodle 3.1
*/
function glossary_get_entries_by_search($glossary, $context, $query, $fullsearch, $order, $sort, $from, $limit,
$options = array()) {
$options = array()) {
global $DB, $USER;
// Remove too little terms.
$terms = explode(' ', $query);
@ -3668,34 +3740,43 @@ function glossary_get_entries_by_search($glossary, $context, $query, $fullsearch
}
}
// Build the query.
$qb = new mod_glossary_entry_query_builder($glossary);
if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
$qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL);
} else {
$qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF);
}
list($searchcond, $params) = glossary_get_search_terms_sql($terms, $fullsearch);
$qb->add_field('*', 'entries');
$qb->join_alias();
$qb->distinct('id', 'entries');
$qb->join_user();
$qb->add_user_fields();
$qb->filter_by_search_terms($terms, $fullsearch);
$userfields = user_picture::fields('u', null, 'userdataid', 'userdata');
// Need one inner view here to avoid distinct + text.
$sqlwrapheader = 'SELECT ge.*, ge.concept AS glossarypivot, ' . $userfields . '
FROM {glossary_entries} ge
LEFT JOIN {user} u ON u.id = ge.userid
JOIN ( ';
$sqlwrapfooter = ' ) gei ON (ge.id = gei.id)';
$sqlselect = "SELECT DISTINCT ge.id";
$sqlfrom = "FROM {glossary_entries} ge
LEFT JOIN {glossary_alias} al ON al.entryid = ge.id";
if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) {
$approvedsql = '';
} else {
$approvedsql = 'AND (ge.approved <> 0 OR ge.userid = :myid)';
$params['myid'] = $USER->id;
}
if ($order == 'CREATION') {
$qb->order_by('timecreated', 'entries', $sort);
$sqlorderby = "ORDER BY ge.timecreated $sort";
} else if ($order == 'UPDATE') {
$qb->order_by('timemodified', 'entries', $sort);
$sqlorderby = "ORDER BY ge.timemodified $sort";
} else {
$qb->order_by('concept', 'entries', $sort);
$sqlorderby = "ORDER BY ge.concept $sort";
}
$qb->order_by('id', 'entries', $sort); // Sort on ID to avoid random ordering when entries share an ordering value.
$qb->limit($from, $limit);
$sqlorderby .= " , ge.id ASC"; // Sort on ID to avoid random ordering when entries share an ordering value.
$sqlwhere = "WHERE ($searchcond) $approvedsql";
// Fetching the entries.
$count = $qb->count_records();
$entries = $qb->get_recordset();
$count = $DB->count_records_sql("SELECT COUNT(DISTINCT(ge.id)) $sqlfrom $sqlwhere", $params);
$query = "$sqlwrapheader $sqlselect $sqlfrom $sqlwhere $sqlwrapfooter $sqlorderby";
$entries = $DB->get_recordset_sql($query, $params, $from, $limit);
return array($entries, $count);
}
@ -3726,7 +3807,6 @@ function glossary_get_entries_by_term($glossary, $context, $term, $from, $limit,
$qb->add_field('*', 'entries');
$qb->join_alias();
$qb->distinct('id', 'entries');
$qb->join_user();
$qb->add_user_fields();
$qb->filter_by_term($term);