Exemple #1
0
if (isset($_SESSION['errors'])) {
    $errors = $_SESSION['errors'];
    // read session variable (only necessary if register globals is OFF!)
    // Note: though we clear the session variable, the current error message is still available to this script via '$errors':
    deleteSessionVariable("errors");
    // function 'deleteSessionVariable()' is defined in 'include.inc.php'
} else {
    $errors = array();
}
// initialize the '$errors' variable in order to prevent 'Undefined variable...' messages
if (isset($_SESSION['formVars'])) {
    $formVars = $_SESSION['formVars'];
    // read session variable (only necessary if register globals is OFF!)
    // Remove slashes from parameter values if 'magic_quotes_gpc = On':
    foreach ($formVars as $varname => $value) {
        $formVars[$varname] = stripSlashesIfMagicQuotes($value);
    }
    // function 'stripSlashesIfMagicQuotes()' is defined in 'include.inc.php'
    // Note: though we clear the session variable, the current form variables are still available to this script via '$formVars':
    deleteSessionVariable("formVars");
    // function 'deleteSessionVariable()' is defined in 'include.inc.php'
} else {
    $formVars = array();
}
// --------------------------------------------------------------------
// Initialize preferred display language:
// (note that 'locales.inc.php' has to be included *after* the call to the 'start_session()' function)
include 'includes/locales.inc.php';
// include the locales
// --------------------------------------------------------------------
// If there's no stored message available:
Exemple #2
0
function findDuplicates($sqlQuery, $originalDisplayType)
{
    global $tableRefs, $tableUserData;
    // defined in 'db.inc.php'
    global $alnum, $alpha, $cntrl, $dash, $digit, $graph, $lower, $print, $punct, $space, $upper, $word, $patternModifiers;
    // defined in 'transtab_unicode_charset.inc.php' and 'transtab_latin1_charset.inc.php'
    // re-assign the correct display type (i.e. the view that was active when the user clicked the 'dups' link in the header):
    if (!empty($originalDisplayType)) {
        $displayType = $originalDisplayType;
    }
    // Extract form variables provided by the 'duplicateSearch' form in 'duplicate_search.php':
    if (isset($_REQUEST['matchFieldsSelector'])) {
        if (is_string($_REQUEST['matchFieldsSelector'])) {
            // we accept a string containing a (e.g. comma delimited) list of field names
            $selectedFieldsArray = preg_split("/[^a-z_]+/", $_REQUEST['matchFieldsSelector'], -1, PREG_SPLIT_NO_EMPTY);
        } else {
            // the field list is already provided as array:
            $selectedFieldsArray = $_REQUEST['matchFieldsSelector'];
        }
    } else {
        $selectedFieldsArray = array();
    }
    if (isset($_REQUEST['ignoreWhitespace']) and $_REQUEST['ignoreWhitespace'] == "1") {
        $ignoreWhitespace = "1";
    } else {
        $ignoreWhitespace = "0";
    }
    if (isset($_REQUEST['ignorePunctuation']) and $_REQUEST['ignorePunctuation'] == "1") {
        $ignorePunctuation = "1";
    } else {
        $ignorePunctuation = "0";
    }
    if (isset($_REQUEST['ignoreCharacterCase']) and $_REQUEST['ignoreCharacterCase'] == "1") {
        $ignoreCharacterCase = "1";
    } else {
        $ignoreCharacterCase = "0";
    }
    if (isset($_REQUEST['ignoreAuthorInitials']) and $_REQUEST['ignoreAuthorInitials'] == "1") {
        $ignoreAuthorInitials = "1";
    } else {
        $ignoreAuthorInitials = "0";
    }
    if (isset($_REQUEST['nonASCIIChars'])) {
        $nonASCIIChars = $_REQUEST['nonASCIIChars'];
    } else {
        $nonASCIIChars = "keep";
    }
    // VALIDATE FORM DATA:
    $errors = array();
    // Validate the field selector:
    if (empty($selectedFieldsArray)) {
        $errors["matchFieldsSelector"] = "You must select at least one field:";
    }
    // Validate the 'SQL Query' field:
    if (empty($sqlQuery)) {
        $errors["sqlQuery"] = "You must specify a query string:";
    } elseif (!preg_match("/^SELECT/i", $sqlQuery)) {
        $errors["sqlQuery"] = "You can only execute SELECT queries:";
    }
    // Check if there were any errors:
    if (count($errors) > 0) {
        // In case of an error, we write all form variables back to the '$formVars' array
        // (which 'duplicate_search.php' requires to reload form values):
        foreach ($_REQUEST as $varname => $value) {
            $formVars[$varname] = $value;
        }
        // Since checkbox form fields do only get included in the '$_REQUEST' array if they were marked,
        // we have to add appropriate array elements for all checkboxes that weren't set:
        if (!isset($formVars["ignoreWhitespace"])) {
            $formVars["ignoreWhitespace"] = "0";
        }
        if (!isset($formVars["ignorePunctuation"])) {
            $formVars["ignorePunctuation"] = "0";
        }
        if (!isset($formVars["ignoreCharacterCase"])) {
            $formVars["ignoreCharacterCase"] = "0";
        }
        if (!isset($formVars["ignoreAuthorInitials"])) {
            $formVars["ignoreAuthorInitials"] = "0";
        }
        if (!isset($formVars["showLinks"])) {
            $formVars["showLinks"] = "0";
        }
        // Write back session variables:
        saveSessionVariable("errors", $errors);
        // function 'saveSessionVariable()' is defined in 'include.inc.php'
        saveSessionVariable("formVars", $formVars);
        // There are errors. Relocate back to 'duplicate_search.php':
        header("Location: duplicate_search.php");
        exit;
        // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> !EXIT! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    }
    // CONSTRUCT SQL QUERY (1. DUPLICATE SEARCH):
    // To identify any duplicates within the results of the original query, we build a new query based on the original SQL query:
    $query = $sqlQuery;
    // Replace SELECT list of columns with those from '$selectedFieldsArray' (plus the 'serial' column):
    $selectedFieldsString = implode(", ", $selectedFieldsArray);
    $query = newSELECTclause("SELECT " . $selectedFieldsString . ", serial", $query, false);
    // function 'newSELECTclause()' is defined in 'include.inc.php'
    // Replace any existing ORDER BY clause with the list of columns given in '$selectedFieldsArray':
    $query = newORDERclause("ORDER BY " . $selectedFieldsString, $query, false);
    // function 'newORDERclause()' is defined in 'include.inc.php'
    // Fix escape sequences within the SQL query:
    $query = stripSlashesIfMagicQuotes($query);
    // RUN the query on the database through the connection:
    $result = queryMySQLDatabase($query);
    // function 'queryMySQLDatabase()' is defined in 'include.inc.php'
    // PROCESS RESULTS:
    $recordSerialsArray = array();
    $duplicateRecordSerialsArray = array();
    $rowsFound = @mysql_num_rows($result);
    // Identify any records with matching field data:
    if ($rowsFound > 0) {
        // Count the number of fields:
        $fieldsFound = mysql_num_fields($result);
        // Loop over each row in the result set:
        for ($rowCounter = 0; $row = @mysql_fetch_array($result); $rowCounter++) {
            $recordIdentifier = "";
            // make sure our buffer variable is empty
            // For each row, loop over each field (except for the last one which is the 'serial' field):
            for ($i = 0; $i < $fieldsFound - 1; $i++) {
                // fetch the current attribute name:
                $fieldName = getMySQLFieldInfo($result, $i, "name");
                // function 'getMySQLFieldInfo()' is defined in 'include.inc.php'
                // normalize author names:
                if ($fieldName == "author" and $ignoreAuthorInitials == "1") {
                    // this is a stupid hack that maps the names of the '$row' array keys to those used
                    // by the '$formVars' array (which is required by function 'parsePlaceholderString()')
                    // (eventually, the '$formVars' array should use the MySQL field names as names for its array keys)
                    $formVars = buildFormVarsArray($row);
                    // function 'buildFormVarsArray()' is defined in 'include.inc.php'
                    // ignore initials in author names:
                    $row[$i] = parsePlaceholderString($formVars, "<:authors[0||]:>", "");
                    // function 'parsePlaceholderString()' is defined in 'include.inc.php'
                }
                $recordIdentifier .= $row[$i];
                // merge all field values to form a unique record identifier string
            }
            // Normalize record identifier string:
            if ($ignoreWhitespace == "1") {
                // ignore whitespace
                $recordIdentifier = preg_replace("/\\s+/", "", $recordIdentifier);
            }
            if ($ignorePunctuation == "1") {
                // ignore punctuation
                $recordIdentifier = preg_replace("/[{$punct}]+/{$patternModifiers}", "", $recordIdentifier);
            }
            if ($ignoreCharacterCase == "1") {
                // ignore character case
                $recordIdentifier = strtolower($recordIdentifier);
            }
            if ($nonASCIIChars == "strip") {
                // strip non-ASCII characters
                $recordIdentifier = handleNonASCIIAndUnwantedCharacters($recordIdentifier, "\\S\\s", "strip");
            } elseif ($nonASCIIChars == "transliterate") {
                // transliterate non-ASCII characters
                $recordIdentifier = handleNonASCIIAndUnwantedCharacters($recordIdentifier, "\\S\\s", "transliterate");
            }
            // Check whether the record identifier string has occurred already:
            if (isset($recordSerialsArray[$recordIdentifier])) {
                // this record identifier string has already been seen
                $recordSerialsArray[$recordIdentifier][] = $row["serial"];
            } else {
                // new record identifier string
                $recordSerialsArray[$recordIdentifier] = array($row["serial"]);
            }
            // add a new array element for this record's identifier string (and store its serial number as value within a sub-array)
        }
        // Collect all array elements from '$recordSerialsArray' where their sub-array contains more than one serial number:
        foreach ($recordSerialsArray as $recordSerials) {
            if (count($recordSerials) > 1) {
                foreach ($recordSerials as $recordSerial) {
                    $duplicateRecordSerialsArray[] = $recordSerial;
                }
            }
            // add this record's serial number to the array of duplicate record serials
        }
    } else {
        // TODO!
    }
    if (empty($duplicateRecordSerialsArray)) {
        $duplicateRecordSerialsArray[] = "0";
    }
    // if no duplicate records were found, the non-existing serial number '0' will result in a "nothing found" feedback
    // CONSTRUCT SQL QUERY (2. DUPLICATES DISPLAY):
    // To display any duplicates that were found within the results of the original query, we build again a new query based on the original SQL query:
    $query = $sqlQuery;
    // Replace WHERE clause:
    // TODO: maybe make this into a generic function? (compare with function 'extractWHEREclause()' in 'include.inc.php')
    $duplicateRecordSerialsString = implode("|", $duplicateRecordSerialsArray);
    $query = preg_replace("/(?<=WHERE )(.+?)(?= ORDER BY| LIMIT| GROUP BY| HAVING| PROCEDURE| FOR UPDATE| LOCK IN|[ ;]+(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|FILE)\\b|\$)/i", "serial RLIKE \"^(" . $duplicateRecordSerialsString . ")\$\"", $query);
    // Replace any existing ORDER BY clause with the list of columns given in '$selectedFieldsArray':
    $query = newORDERclause("ORDER BY " . $selectedFieldsString, $query, false);
    return array($query, $displayType);
}
Exemple #3
0
    $showRows = $formVars['showRows'];
    if (isset($formVars['citeStyle'])) {
        $citeStyle = $formVars['citeStyle'];
    } else {
        $citeStyle = "";
    }
    if (preg_match("/%20/", $citeStyle)) {
        // if '$citeStyle' still contains URL encoded data... ('%20' is the URL encoded form of a space, see note below!)
        $citeStyle = rawurldecode($citeStyle);
    }
    // ...URL decode 'citeStyle' statement (it was URL encoded before incorporation into a hidden tag of the 'sqlSearch' form to avoid any HTML syntax errors)
    // NOTE: URL encoded data that are included within a *link* will get URL decoded automatically *before* extraction via '$_REQUEST'!
    //       But, opposed to that, URL encoded data that are included within a form by means of a *hidden form tag* will NOT get URL decoded automatically! Then, URL decoding has to be done manually (as is done here)!
    $citeOrder = $formVars['citeOrder'];
    $sqlQuery = $formVars['sqlQuery'];
    $sqlQuery = stripSlashesIfMagicQuotes($sqlQuery);
    // function 'stripSlashesIfMagicQuotes()' is defined in 'include.inc.php'
    if (isset($formVars['origQueryName'])) {
        $origQueryName = rawurldecode($formVars['origQueryName']);
    } else {
        $origQueryName = "";
    }
}
// set display options according to the fetched attribute values:
if ($showQuery == "1") {
    $checkQuery = " checked";
} else {
    $checkQuery = "";
}
if ($showLinks == "1") {
    $checkLinks = " checked";
Exemple #4
0
}
// check if the user did mark any checkboxes (and set up variables accordingly)
if (empty($recordSerialsArray)) {
    // no checkboxes were marked
    $nothingChecked = true;
} else {
    // some checkboxes were marked
    $nothingChecked = false;
}
// --------------------------------------------------------------------
// CONSTRUCT SQL QUERY:
// --- Embedded sql query: ----------------------
if ($formType == "sqlSearch") {
    $query = preg_replace("/ FROM {$tableUsers}/i", ", user_id FROM {$tableUsers}", $sqlQuery);
    // add 'user_id' column (which is required in order to obtain unique checkbox names as well as for use in the 'getUserID()' function)
    $query = stripSlashesIfMagicQuotes($query);
} elseif ($formType == "refineSearch" or $formType == "displayOptions") {
    list($query, $displayType) = extractFormElementsRefineDisplay($tableUsers, $displayType, $originalDisplayType, $sqlQuery, $showLinks, "", "");
    // function 'extractFormElementsRefineDisplay()' is defined in 'include.inc.php' since it's also used by 'users.php'
} elseif ($formType == "groupSearch") {
    $query = extractFormElementsGroup($sqlQuery);
} elseif ($formType == "queryResults") {
    list($query, $displayType) = extractFormElementsQueryResults($displayType, $originalDisplayType, $sqlQuery, $recordSerialsArray);
} else {
    $query = "SELECT first_name, last_name, abbrev_institution, email, last_login, logins, user_id FROM {$tableUsers} WHERE user_id RLIKE \".+\" ORDER BY last_login DESC, last_name, first_name";
}
// ----------------------------------------------
// (1) OPEN CONNECTION, (2) SELECT DATABASE
connectToMySQLDatabase();
// function 'connectToMySQLDatabase()' is defined in 'include.inc.php'
// (3) RUN the query on the database through the connection:
Exemple #5
0
    // IMPORTANT: We treat any 'call_number' query as specific to every user, i.e. a user can only query his own call numbers.
    $callNumber = "";
}
if (isset($_REQUEST['userID']) and preg_match("/^[0-9]+\$/", $_REQUEST['userID'])) {
    $userID = $_REQUEST['userID'];
} else {
    // will show every record where the user who's identified by user ID "2" has set the selected bit to "yes".
    $userID = "";
}
if (isset($_REQUEST['by'])) {
    $browseByField = $_REQUEST['by'];
} else {
    $browseByField = "";
}
if (isset($_REQUEST['where'])) {
    $where = stripSlashesIfMagicQuotes($_REQUEST['where']);
} else {
    $where = "";
}
if (isset($_REQUEST['queryType'])) {
    $queryType = $_REQUEST['queryType'];
} else {
    $queryType = "";
}
if ($queryType == "or") {
    $queryType = "OR";
}
// we allow for lowercase 'or' but convert it to uppercase (in an attempt to increase consistency & legibility of the SQL query)
if ($queryType != "OR") {
    // if given value is 'OR' multiple parameters will be connected by 'OR', otherwise an 'AND' query will be performed
    $queryType = "AND";
Exemple #6
0
// --------------------------------------------------------------------
// Initialize preferred display language:
// (note that 'locales.inc.php' has to be included *after* the call to the 'start_session()' function)
include 'includes/locales.inc.php';
// include the locales
// --------------------------------------------------------------------
// [ Extract form variables sent through POST/GET by use of the '$_REQUEST' variable ]
// [ !! NOTE !!: for details see <http://www.php.net/release_4_2_1.php> & <http://www.php.net/manual/en/language.variables.predefined.php> ]
// Check if any error occurred while processing the database UPDATE/INSERT/DELETE
$errorNo = $_REQUEST['errorNo'];
$errorMsg = $_REQUEST['errorMsg'];
$errorMsg = stripSlashesIfMagicQuotes($errorMsg);
// function 'stripSlashesIfMagicQuotes()' is defined in 'include.inc.php'
// Extract the header message that was returned by originating script:
$HeaderString = $_REQUEST['headerMsg'];
$HeaderString = stripSlashesIfMagicQuotes($HeaderString);
// Extract the view type requested by the user (either 'Mobile', 'Print', 'Web' or ''):
// ('' will produce the default 'Web' output style)
if (isset($_REQUEST['viewType'])) {
    $viewType = $_REQUEST['viewType'];
} else {
    $viewType = "";
}
// Extract generic variables from the request:
if (isset($_SESSION['oldQuery'])) {
    $oldQuery = $_SESSION['oldQuery'];
} else {
    $oldQuery = array();
}
// --------------------------------------------------------------------
// (4) DISPLAY HEADER & RESULTS
Exemple #7
0
function explainSQLQuery($sourceSQLQuery)
{
    // fix escape sequences within the SQL query:
    $translatedSQL = stripSlashesIfMagicQuotes($sourceSQLQuery);
    //		$translatedSQL = str_replace('\"','"',$sourceSQLQuery); // replace any \" with "
    //		$translatedSQL = preg_replace('/(\\\\)+/','\\\\',$translatedSQL);
    // define an array of search & replace actions:
    // (Note that the order of array elements IS important since it defines when a search/replace action gets executed)
    $sqlSearchReplacePatterns = array(" != " => " is not equal to ", " = " => " is equal to ", " > " => " is greater than ", " >= " => " is equal to or greater than ", " < " => " is less than ", " <= " => " is equal to or less than ", "NOT RLIKE \"\\^([^\"]+?)\\\$\"" => "is not equal to '\\1'", "NOT RLIKE \"\\^" => "does not start with '", "NOT RLIKE \"([^\"]+?)\\\$\"" => "does not end with '\\1'", "NOT RLIKE" => "does not contain", "RLIKE \"\\^([^\"]+?)\\\$\"" => "is equal to '\\1'", "RLIKE \"\\^" => "starts with '", "RLIKE \"([^\"]+?)\\\$\"" => "ends with '\\1'", "RLIKE" => "contains", "AND" => "and");
    // Perform search & replace actions on the SQL query:
    $translatedSQL = searchReplaceText($sqlSearchReplacePatterns, $translatedSQL, false);
    $translatedSQL = str_replace('"', "'", $translatedSQL);
    // replace any remaining " with '
    return $translatedSQL;
}
function parseCQL($sruVersion, $sruQuery, $operation = "")
{
    global $alnum, $alpha, $cntrl, $dash, $digit, $graph, $lower, $print, $punct, $space, $upper, $word, $patternModifiers;
    // defined in 'transtab_unicode_charset.inc.php' and 'transtab_latin1_charset.inc.php'
    // map CQL indexes to refbase field names:
    $indexNamesArray = mapCQLIndexes();
    $searchArray = array();
    // intialize array that will hold information about context set, index name, relation and search value
    $searchSubArray1 = array();
    // --------------------------------
    if (!empty($sruQuery)) {
        // check for presence of context set/index name and any of the main relations:
        if (!preg_match('/^[^\\" <>=]+( +(all|any|exact|within) +| *(<>|<=|>=|<|>|=) *)/', $sruQuery)) {
            // if no context set/index name and relation was given we'll add meaningful defaults:
            if (preg_match("/^suggest\$/i", $operation)) {
                $sruQuery = "main_fields all " . $sruQuery;
            } else {
                $sruQuery = "cql.serverChoice all " . $sruQuery;
            }
            // otherwise we currently use 'cql.serverChoice' (since 'main_fields' isn't yet supported for regular OpenSearch queries)
        }
        // extract the context set:
        if (preg_match('/^([^\\" <>=.]+)\\./', $sruQuery)) {
            $contextSet = preg_replace('/^([^\\" <>=.]+)\\..*/', '\\1', $sruQuery);
        } else {
            $contextSet = "";
        }
        // use the default context set
        // extract the index:
        $indexName = preg_replace('/^(?:[^\\" <>=.]+\\.)?([^\\" <>=.]+).*/', '\\1', $sruQuery);
        // ----------------
        // return a fatal diagnostic if the CQL query does contain an unrecognized 'set.index' identifier:
        // (a) verify that the given context set (if any) is recognized:
        if (!empty($contextSet)) {
            $contextSetIndexConnector = ".";
            $contextSetLabel = "context set '" . $contextSet . "'";
            if (!preg_match("/^(dc|bath|rec|bib|cql)\$/", $contextSet)) {
                returnDiagnostic(15, $contextSet);
                // unsupported context set (function 'returnDiagnostic()' is defined in 'opensearch.php' and 'sru.php')
                exit;
            }
        } else {
            $contextSetIndexConnector = "";
            $contextSetLabel = "empty context set";
        }
        // (b) verify that the given 'set.index' term is recognized:
        if (!isset($indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName])) {
            if (isset($indexNamesArray[$indexName]) or isset($indexNamesArray["dc." . $indexName]) or isset($indexNamesArray["bath." . $indexName]) or isset($indexNamesArray["rec." . $indexName]) or isset($indexNamesArray["bib." . $indexName]) or isset($indexNamesArray["cql." . $indexName])) {
                returnDiagnostic(10, "Unsupported combination of " . $contextSetLabel . " with index '" . $indexName . "'");
                // unsupported combination of context set & index
            } else {
                returnDiagnostic(16, $indexName);
                // unsupported index
            }
            exit;
        }
        // ----------------
        // extract the main relation (relation modifiers aren't supported yet!):
        $mainRelation = preg_replace('/^[^\\" <>=]+( +(all|any|exact|within) +| *(<>|<=|>=|<|>|=) *).*/', '\\1', $sruQuery);
        // remove any runs of leading or trailing whitespace:
        $mainRelation = trim($mainRelation);
        // ----------------
        // extract the search term:
        $searchTerm = preg_replace('/^[^\\" <>=]+(?: +(?:all|any|exact|within) +| *(?:<>|<=|>=|<|>|=) *)(.*)/', '\\1', $sruQuery);
        // remove slashes from search term if 'magic_quotes_gpc = On':
        $searchTerm = stripSlashesIfMagicQuotes($searchTerm);
        // function 'stripSlashesIfMagicQuotes()' is defined in 'include.inc.php'
        // remove any leading or trailing quotes from the search term:
        // (note that multiple query parts connected with boolean operators aren't supported yet!)
        $searchTerm = preg_replace('/^\\"/', '', $searchTerm);
        $searchTerm = preg_replace('/\\"$/', '', $searchTerm);
        // OpenSearch search suggestions ('$operation=suggest'): since CQL matches full words (not sub-strings),
        // we need to make sure that every search term ends with the '*' masking character:
        if (preg_match("/^suggest\$/i", $operation) and $mainRelation != "exact") {
            $searchTerm = preg_replace("/([{$word}]+)(?![?*^])/{$patternModifiers}", "\\1*", $searchTerm);
        }
        // escape meta characters (including '/' that is used as delimiter for the PCRE replace functions below and which gets passed as second argument):
        $searchTerm = preg_quote($searchTerm, "/");
        // escape special regular expression characters: . \ + * ? [ ^ ] $ ( ) { } = ! < > | :
        // account for CQL anchoring ('^') and masking ('*' and '?') characters:
        // NOTE: in the code block above we quote everything to escape possible meta characters,
        //       so all special chars in the block below have to be matched in their escaped form!
        //       (The expression '\\\\' in the patterns below describes only *one* backslash! -> '\'.
        //        The reason for this is that before the regex engine can interpret the \\ into \, PHP interprets it.
        //        Thus, you have to escape your backslashes twice: once for PHP, and once for the regex engine.)
        //
        // more info about masking characters in CQL:  <http://zing.z3950.org/cql/intro.html#6>
        // more info about word anchoring in CQL:      <http://zing.z3950.org/cql/intro.html#6.1>
        // recognize any anchor at the beginning of a search term (like '^foo'):
        // (in CQL, a word beginning with ^ must be the first in its field)
        $searchTerm = preg_replace('/(^| )\\\\\\^/', '\\1^', $searchTerm);
        // convert any anchor at the end of a search term (like 'foo^') to the correct MySQL variant ('foo$'):
        // (in CQL, a word ending with ^ must be the last in its field)
        $searchTerm = preg_replace('/\\\\\\^( |$)/', '$\\1', $searchTerm);
        // recognize any masking ('*' and '?') characters:
        // Note: by "character" we do refer to *word* characters here, i.e., any character that is not a space or punctuation character (see below);
        //       however, I'm not sure if the masking characters '*' and '?' should also include non-word characters!
        $searchTerm = preg_replace('/(?<!\\\\)\\\\\\*/', '[^[:space:][:punct:]]*', $searchTerm);
        // a single asterisk ('*') is used to mask zero or more characters
        $searchTerm = preg_replace('/(?<!\\\\)\\\\\\?/', '[^[:space:][:punct:]]', $searchTerm);
        // a single question mark ('?') is used to mask a single character, thus N consecutive question-marks means mask N characters
        // ----------------
        // construct the WHERE clause:
        $whereClausePart = $indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName];
        // start WHERE clause with field name
        if ($mainRelation == "all") {
            if (preg_match("/ /", $searchTerm)) {
                $searchTermArray = preg_split("/ +/", $searchTerm);
                foreach ($searchTermArray as $searchTermItem) {
                    $whereClauseSubPartsArray[] = " RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTermItem . "([[:space:][:punct:]]|\$)");
                }
                // NOTE: For word-matching relations (like 'all', 'any' or '=') we could also use word boundaries which would be more (too?) restrictive:
                //
                // [[:<:]] , [[:>:]]
                //
                // They match the beginning and end of words, respectively. A word is a sequence of word characters that is not preceded by or
                // followed by word characters. A word character is an alphanumeric character in the alnum class or an underscore (_).
                $whereClausePart .= implode(" AND " . $indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName], $whereClauseSubPartsArray);
            } else {
                $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|\$)");
            }
        } elseif ($mainRelation == "any") {
            $searchTerm = splitAndMerge("/ +/", "|", $searchTerm);
            // function 'splitAndMerge()' is defined in 'include.inc.php'
            $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])(" . $searchTerm . ")([[:space:][:punct:]]|\$)");
        } elseif ($mainRelation == "exact") {
            // 'exact' is used for exact string matching, i.e., it matches field contents exactly
            $whereClausePart .= " = " . quote_smart($searchTerm);
        } elseif ($mainRelation == "within") {
            if (preg_match("/[^ ]+ [^ ]+/", $searchTerm)) {
                $searchTermArray = preg_split("/ +/", $searchTerm);
                $whereClausePart .= " >= " . quote_smart($searchTermArray[0]) . " AND " . $indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName] . " <= " . quote_smart($searchTermArray[1]);
            } else {
                returnDiagnostic(36, "Search term requires two space-separated dimensions. Example: dc.date within \"2004 2005\"");
                exit;
            }
        } elseif ($mainRelation == "=") {
            // matches full words (not sub-strings); '=' is used for word adjacency, the words appear in that order with no others intervening
            $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|\$)");
        } elseif ($mainRelation == "<>") {
            // does this also match full words (and not sub-strings) ?:-/
            $whereClausePart .= " NOT RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|\$)");
        } elseif ($mainRelation == "<") {
            $whereClausePart .= " < " . quote_smart($searchTerm);
        } elseif ($mainRelation == "<=") {
            $whereClausePart .= " <= " . quote_smart($searchTerm);
        } elseif ($mainRelation == ">") {
            $whereClausePart .= " > " . quote_smart($searchTerm);
        } elseif ($mainRelation == ">=") {
            $whereClausePart .= " >= " . quote_smart($searchTerm);
        }
        $searchSubArray1[] = array("_boolean" => "", "_query" => $whereClausePart);
    } else {
        $searchSubArray1[] = array("_boolean" => "", "_query" => "serial RLIKE " . quote_smart(".+"));
    }
    // --------------------------------
    if (!empty($searchSubArray1)) {
        $searchArray[] = array("_boolean" => "", "_query" => $searchSubArray1);
    }
    return $searchArray;
}