function getRecords($options)
{
    $originalOptions = $options;
    //Save original options in case we need them later.
    global $VIEWER_NAME, $TABLE_PREFIX, $SETTINGS;
    $VIEWER_NAME = "getRecords(" . @$options['tableName'] . ")";
    // error checking
    _getRecords_errorChecking($options);
    // load schema
    $schema = loadSchema($options['tableName']);
    if (!$schema) {
        die("{$VIEWER_NAME}: Couldn't load schema for '" . htmlencode($options['tableName']) . "'!");
    }
    // set defaults
    if (!array_key_exists('orderBy', $options)) {
        $options['orderBy'] = getFirstDefinedValue(_getOrderByFromUrl($schema), $schema['listPageOrder']);
    }
    if (!array_key_exists('loadUploads', $options)) {
        $options['loadUploads'] = true;
    }
    if (!array_key_exists('allowSearch', $options)) {
        $options['allowSearch'] = true;
    }
    if (!array_key_exists('requireSearchMatch', $options)) {
        $options['requireSearchMatch'] = false;
    }
    if (!array_key_exists('loadCreatedBy', $options)) {
        $options['loadCreatedBy'] = true;
    }
    if (!array_key_exists('loadListDetails', $options)) {
        $options['loadListDetails'] = true;
    }
    if (!array_key_exists('loadPseudoFields', $options)) {
        $options['loadPseudoFields'] = true;
    }
    $options['perPage'] = intval(@$options['perPage']);
    $options['pageNum'] = intval(@$options['pageNum']) ? max(intval($options['pageNum']), 1) : max(intval(@$_REQUEST['page']), 1);
    $options['limit'] = intval(@$options['perPage']) ? intval($options['perPage']) : intval(@$options['limit']);
    $options['offset'] = intval(@$options['perPage']) ? ($options['pageNum'] - 1) * $options['perPage'] + @$options['offset'] : @$options['offset'];
    $options['offset'] = min($options['offset'], PHP_INT_MAX);
    // don't overflow php integers when casting to int later (can cause negative numbers which is harmless but causes mysql error)
    if ($options['offset'] && !$options['limit']) {
        $options['limit'] = 1000000;
    }
    // if offset and no limit set limit to high number as per MySQL docs
    // special behaviour if a "preview" is requested
    $isSingleMenuWithBlankWhere = !empty($schema['menuType']) && $schema['menuType'] == 'single' && @$options['where'] == '';
    // single record sections have blank where statements when generated by code generator
    $hasWhereWithPreviewNum = @$options['where'] == "num = '9999999999'";
    // and regular detail viewers have: //  'where'       => whereRecordNumberInUrl(0),
    if (@$_REQUEST['preview:menu'] == @$options['tableName'] && ($isSingleMenuWithBlankWhere || $hasWhereWithPreviewNum)) {
        // checking for 999... prevent an inifine loop
        return _getRecords_preview($schema, $options);
    }
    // get query
    $query = _getRecords_getQuery($options, $schema);
    // load from cache
    if (@$options['useCache']) {
        if (!function_exists('turboCache_load')) {
            die("Error: 'useCaching' enabled but no caching plugin found!<br/>Either disable 'useCaching' or install caching plugin.");
        }
        $results = turboCache_load($options['tableName'], $query);
        if ($results) {
            list($rows, $listDetails, $schema) = $results;
            $listDetails['fromCache'] = 1;
            return array($rows, $listDetails, $schema);
        }
    }
    // Get records
    list($rows, $totalRecords) = _getRecords_loadResults($query, $options, $schema);
    // Add _tableName key
    foreach ($rows as $key => $record) {
        $rows[$key]['_tableName'] = $options['tableName'];
    }
    // Add pseudo-fields
    if (@$options['loadPseudoFields']) {
        _getRecords_addPseudoFields($rows, $options, $schema);
    }
    // Add uploads
    if (@$options['loadUploads']) {
        _getRecords_addUploadFields($rows, $options, $schema);
    }
    // Add createdBy.fields to records
    // // v2.50 set fields for alternate accounts table, eg: otherTable.username
    if (@$options['loadCreatedBy'] && @$schema['createdByUserNum']) {
        $accountsTables = array_filter(array_unique(array('accounts', @$GLOBALS['WSM_ACCOUNTS_TABLE'])));
        foreach ($accountsTables as $accountsTable) {
            _getRecords_joinTable($rows, $options, $accountsTable);
        }
    }
    // Add joinTable fields
    if (@$options['joinTable']) {
        _getRecords_joinTable($rows, $options);
    }
    // get List Details
    $listDetails = array();
    if ($options['loadListDetails']) {
        $listDetails = _getRecords_getListDetails($options, count($rows), $totalRecords, $schema);
    }
    // See if pagenum is too high.  If so, call getRecords() again with an in-bounds page num
    if ($listDetails && $options['pageNum'] > $listDetails['totalPages']) {
        $originalOptions['pageNum'] = $listDetails['totalPages'];
        return getRecords($originalOptions);
    }
    // save to cache
    if (@$options['useCache']) {
        turboCache_save($options['tableName'], $query, array($rows, $listDetails, $schema));
    }
    //
    $rows = applyFilters('viewer_output_rows', $rows, $listDetails, $schema);
    //
    return array($rows, $listDetails, $schema);
}
function getListOptionsFromSchema($fieldSchema, $record = null, $useCache = false, $listValues = null)
{
    global $TABLE_PREFIX;
    $listOptions = array();
    $optionsType = @$fieldSchema['optionsType'];
    // get list values to lookup
    $listValuesAsCSV = '';
    if ($listValues) {
        foreach ($listValues as $value) {
            $listValuesAsCSV .= "'" . mysql_escape($value) . "',";
        }
        $listValuesAsCSV = chop($listValuesAsCSV, ',');
        // remove trailing comma
    }
    ### parse text options
    if ($optionsType == 'text') {
        // parse
        $optionText = explode("\n", @$fieldSchema['optionsText']);
        foreach ($optionText as $optionString) {
            if (preg_match("/(^|[^\\|])(\\|\\|)*(\\|)(?!\\|)/", $optionString, $match, PREG_OFFSET_CAPTURE)) {
                $delimiterOffset = $match[3][1];
                $value = substr($optionString, 0, $delimiterOffset);
                $label = substr($optionString, $delimiterOffset + 1);
            } else {
                $value = $optionString;
                $label = $optionString;
            }
            $value = str_replace("||", "|", $value);
            $label = str_replace("||", "|", $label);
            // remove trailing whitespace
            $value = rtrim($value);
            $label = rtrim($label);
            $listOptions[] = array($value, $label);
        }
    } else {
        $cacheTable = '';
        // create query
        if ($optionsType == 'table') {
            $valueField = @$fieldSchema['optionsValueField'];
            $labelField = @$fieldSchema['optionsLabelField'];
            $selectTable = $TABLE_PREFIX . $fieldSchema['optionsTablename'];
            $tableSchema = loadSchema($fieldSchema['optionsTablename']);
            $where = $listValuesAsCSV ? "WHERE `{$valueField}` IN ({$listValuesAsCSV})" : '';
            $orderBy = @$tableSchema['listPageOrder'] ? "ORDER BY {$tableSchema['listPageOrder']}" : '';
            $query = "SELECT `{$valueField}`, `{$labelField}` FROM `{$selectTable}` {$where} {$orderBy} LIMIT 0, 999";
            $cacheTable = $fieldSchema['optionsTablename'];
        } else {
            if ($optionsType == 'query') {
                $filterFieldValue = @$record[@$fieldSchema['filterField']];
                $GLOBALS['ESCAPED_FILTER_VALUE'] = mysql_escape($filterFieldValue);
                $query = getEvalOutput($fieldSchema['optionsQuery']);
                if (preg_match("/\\bFROM\\s+(\\S+)/", $query, $matches)) {
                    $cacheTable = $matches[1];
                    $cacheTable = preg_replace("/\\W/", '', $cacheTable);
                    // remove ` quotes, etc
                }
            } else {
                die("Unknown optionsType '{$optionsType}'!");
            }
        }
        // load cache module
        if ($useCache && $cacheTable) {
            $libDir = dirname(__FILE__);
            if (file_exists("{$libDir}/viewer_turboCache.php")) {
                require_once "{$libDir}/viewer_turboCache.php";
            }
            // load cached result
            if (!function_exists('turboCache_load')) {
                die("Error: 'useCaching' enabled but no caching plugin found!<br/>Either disable 'useCaching' or install caching plugin.");
            }
            $listOptions = turboCache_load($cacheTable, $query);
            if ($listOptions) {
                return $listOptions;
            }
        }
        // execute query
        $result = @mysql_query($query);
        if (!$result) {
            $error = "There was an error creating the list field '" . @$fieldSchema['name'] . "'.\n\n";
            $error .= "MySQL Error: " . mysql_error() . "\n\n";
            header("Content-type: text/plain");
            die($error);
        }
        while ($row = mysql_fetch_row($result)) {
            $value = $row[0];
            $label = array_key_exists(1, $row) ? $row[1] : $value;
            // use value if no label specified
            $listOptions[] = array($value, $label);
        }
        if (is_resource($result)) {
            mysql_free_result($result);
        }
        // save to cache
        if ($useCache && $cacheTable) {
            turboCache_save($cacheTable, $query, $listOptions);
        }
    }
    //
    return $listOptions;
}