File scan_manager.c¶
File List > cubrid > src > query > scan_manager.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* scan_manager.c - scan management routines
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "jansson.h"
#include "error_manager.h"
#include "heap_file.h"
#include "fetch.h"
#include "list_file.h"
#include "set_scan.h"
#include "system_parameter.h"
#include "btree_load.h"
#include "perf_monitor.h"
#include "query_manager.h"
#include "query_evaluator.h"
#include "query_opfunc.h"
#include "query_reevaluation.hpp"
#include "regu_var.hpp"
#include "locator_sr.h"
#include "log_lsa.hpp"
#include "object_primitive.h"
#include "object_representation.h"
#include "dbtype.h"
#include "xasl_predicate.hpp"
#include "xasl.h"
#include "query_hash_scan.h"
#include "statistics.h"
#include "px_heap_scan.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#if !defined(SERVER_MODE)
#define pthread_mutex_init(a, b)
#define pthread_mutex_destroy(a)
#define pthread_mutex_lock(a) 0
#define pthread_mutex_unlock(a)
static int rv;
#endif
/* this macro is used to make sure that heap file identifier is initialized
* properly that heap file scan routines will work properly.
*/
#define UT_CAST_TO_NULL_HEAP_OID(hfidp,oidp) \
do \
{ \
(oidp)->pageid = NULL_PAGEID; \
(oidp)->volid = (hfidp)->vfid.volid; \
(oidp)->slotid = NULL_SLOTID; \
} \
while (0)
#define GET_NTH_OID(oid_setp, n) ((OID *)((OID *)(oid_setp) + (n)))
/* ISS_RANGE_DETAILS stores information about the two ranges we use
* interchangeably in Index Skip Scan mode: along with the real range, we
* use a "fake" one to obtain the next value for the index's first column.
*
* ISS_RANGE_DETAILS tries to completely encapsulate one of these ranges, so
* that whenever the need arises, we can "swap" them. */
typedef struct iss_range_details ISS_RANGE_DETAILS;
struct iss_range_details
{
int key_cnt;
KEY_RANGE *key_ranges;
SCAN_PRED key_pred;
RANGE_TYPE range_type;
int part_key_desc; /* last partial key domain is descending */
};
typedef int QPROC_KEY_VAL_FU (KEY_VAL_RANGE * key_vals, int key_cnt);
typedef SCAN_CODE (*QP_SCAN_FUNC) (THREAD_ENTRY * thread_p, SCAN_ID * s_id);
typedef enum
{
ROP_NA, ROP_EQ,
ROP_GE, ROP_GT, ROP_GT_INF, ROP_GT_ADJ,
ROP_LE, ROP_LT, ROP_LT_INF, ROP_LT_ADJ
} ROP_TYPE;
struct rop_range_struct
{
ROP_TYPE left;
ROP_TYPE right;
RANGE range;
} rop_range_table[] =
{
{
ROP_NA, ROP_EQ, NA_NA},
{
ROP_GE, ROP_LE, GE_LE},
{
ROP_GE, ROP_LT, GE_LT},
{
ROP_GT, ROP_LE, GT_LE},
{
ROP_GT, ROP_LT, GT_LT},
{
ROP_GE, ROP_LT_INF, GE_INF},
{
ROP_GT, ROP_LT_INF, GT_INF},
{
ROP_GT_INF, ROP_LE, INF_LE},
{
ROP_GT_INF, ROP_LT, INF_LT},
{
ROP_GT_INF, ROP_LT_INF, INF_INF}
};
static const int rop_range_table_size = sizeof (rop_range_table) / sizeof (struct rop_range_struct);
#if defined(SERVER_MODE)
static pthread_mutex_t scan_Iscan_oid_buf_list_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
static BTREE_ISCAN_OID_LIST *scan_Iscan_oid_buf_list = NULL;
static int scan_Iscan_oid_buf_list_count = 0;
#define SCAN_ISCAN_OID_BUF_LIST_DEFAULT_SIZE 10
static void scan_init_scan_pred (SCAN_PRED * scan_pred_p, regu_variable_list_node * regu_list, PRED_EXPR * pred_expr,
PR_EVAL_FNC pr_eval_fnc);
static void scan_init_scan_attrs (SCAN_ATTRS * scan_attrs_p, int num_attrs, ATTR_ID * attr_ids,
HEAP_CACHE_ATTRINFO * attr_cache);
static int scan_init_indx_coverage (THREAD_ENTRY * thread_p, int coverage_enabled, valptr_list_node * output_val_list,
regu_variable_list_node * regu_val_list, VAL_DESCR * vd, QUERY_ID query_id,
int max_key_len, int func_index_col_id, INDX_COV * indx_cov);
static int scan_alloc_oid_list (BTREE_ISCAN_OID_LIST ** oid_list_p);
static int scan_alloc_iscan_oid_buf_list (BTREE_ISCAN_OID_LIST ** oid_list);
static void scan_free_iscan_oid_buf_list (BTREE_ISCAN_OID_LIST * oid_list);
static void rop_to_range (RANGE * range, ROP_TYPE left, ROP_TYPE right);
static void range_to_rop (ROP_TYPE * left, ROP_TYPE * rightk, RANGE range);
static ROP_TYPE compare_val_op (DB_VALUE * val1, ROP_TYPE op1, DB_VALUE * val2, ROP_TYPE op2, int num_index_term);
static int key_val_compare (const void *p1, const void *p2);
static int eliminate_duplicated_keys (KEY_VAL_RANGE * key_vals, int key_cnt);
static int merge_key_ranges (KEY_VAL_RANGE * key_vals, int key_cnt);
static int reverse_key_list (KEY_VAL_RANGE * key_vals, int key_cnt);
static int check_key_vals (KEY_VAL_RANGE * key_vals, int key_cnt, QPROC_KEY_VAL_FU * chk_fn);
static int scan_dbvals_to_midxkey (THREAD_ENTRY * thread_p, DB_VALUE * retval, bool * indexal,
TP_DOMAIN * btree_domainp, int num_term, REGU_VARIABLE * func, VAL_DESCR * vd,
int key_minmax, bool is_iss, TP_DOMAIN ** prebuilt_midxkey_domain);
static int scan_regu_key_to_index_key (THREAD_ENTRY * thread_p, KEY_RANGE * key_ranges, KEY_VAL_RANGE * key_val_range,
INDX_SCAN_ID * iscan_id, TP_DOMAIN * btree_domainp, VAL_DESCR * vd,
int key_range_idx);
static int scan_get_index_oidset (THREAD_ENTRY * thread_p, SCAN_ID * s_id, DB_BIGINT * key_limit_upper,
DB_BIGINT * key_limit_lower);
static void scan_init_scan_id (SCAN_ID * scan_id, bool force_select_lock, SCAN_OPERATION_TYPE scan_op_type, int fixed,
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval,
val_list_node * val_list, VAL_DESCR * vd);
static int scan_init_index_key_limit (THREAD_ENTRY * thread_p, INDX_SCAN_ID * isidp, KEY_INFO * key_infop,
VAL_DESCR * vd);
static SCAN_CODE scan_next_scan_local (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_heap_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_heap_page_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_class_attr_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_index_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_index_key_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_index_node_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_index_lookup_heap (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp,
FILTER_INFO * data_filter, TRAN_ISOLATION isolation);
static SCAN_CODE scan_next_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_showstmt_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_set_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_json_table_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_value_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_method_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_dblink_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_handle_single_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id, QP_SCAN_FUNC next_scan);
static SCAN_CODE scan_prev_scan_local (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static void resolve_domains_on_list_scan (LLIST_SCAN_ID * llsidp, val_list_node * ref_val_list);
static void resolve_domain_on_regu_operand (REGU_VARIABLE * regu_var, val_list_node * ref_val_list,
QFILE_TUPLE_VALUE_TYPE_LIST * p_type_list);
static int scan_init_multi_range_optimization (THREAD_ENTRY * thread_p, MULTI_RANGE_OPT * multi_range_opt,
bool use_range_opt, int max_size);
static int scan_dump_key_into_tuple (THREAD_ENTRY * thread_p, INDX_SCAN_ID * iscan_id, DB_VALUE * key, OID * oid,
QFILE_TUPLE_RECORD * tplrec);
static int scan_save_range_details (INDX_SCAN_ID * isidp_src, ISS_RANGE_DETAILS * rdp_dest);
static int scan_restore_range_details (ISS_RANGE_DETAILS * rdp_src, INDX_SCAN_ID * isidp_dest);
static SCAN_CODE scan_get_next_iss_value (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp);
static SCAN_CODE call_get_next_index_oidset (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp,
bool should_go_to_next_value);
static int scan_key_compare (DB_VALUE * val1, DB_VALUE * val2, int num_index_term);
/* for hash list scan */
static SCAN_CODE scan_build_hash_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_next_hash_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id);
static SCAN_CODE scan_hash_probe_next (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, QFILE_TUPLE * tuple);
static HASH_METHOD check_hash_list_scan (LLIST_SCAN_ID * llsidp, int *val_cnt, int hash_list_scan_type);
/*
* scan_init_iss () - initialize index skip scan structure
* return: error code
* isidp: pointer to index scan id structure that contains iss structure
*/
int
scan_init_iss (INDX_SCAN_ID * isidp)
{
DB_VALUE *last_key = NULL;
INDEX_SKIP_SCAN *iss = NULL;
if (isidp == NULL)
{
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
iss = &isidp->iss;
/* initialize iss structure */
iss->current_op = ISS_OP_NONE;
iss->skipped_range = NULL;
if (isidp->indx_info == NULL)
{
/* no index info specified so no iss needed */
iss->use = false;
return NO_ERROR;
}
iss->use = isidp->indx_info->use_iss != 0;
if (!iss->use)
{
/* if not using iss, nothing more to do */
return NO_ERROR;
}
/* assign range */
iss->skipped_range = &isidp->indx_info->iss_range;
if (iss->skipped_range->key1 == NULL || iss->skipped_range->key1->value.funcp == NULL
|| iss->skipped_range->key1->value.funcp->operand == NULL
|| iss->skipped_range->key1->value.funcp->operand->value.type != TYPE_DBVAL)
{
/* this should never happen */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
/* reset key value */
last_key = &iss->skipped_range->key1->value.funcp->operand->value.value.dbval;
if (!DB_IS_NULL (last_key))
{
pr_clear_value (last_key);
db_make_null (last_key);
}
return NO_ERROR;
}
/*
* scan_init_index_scan () - initialize an index scan structure with the
* specified OID buffer
* return : void
* isidp (in) : index scan
* oid_list (in) : OID list.
* mvcc_snapshot(in) : MVCC snapshot
*/
void
scan_init_index_scan (INDX_SCAN_ID * isidp, struct btree_iscan_oid_list *oid_list, MVCC_SNAPSHOT * mvcc_snapshot)
{
if (isidp == NULL)
{
assert (false);
return;
}
isidp->oid_list = oid_list;
isidp->copy_buf = NULL;
isidp->copy_buf_len = 0;
memset ((void *) (&(isidp->indx_cov)), 0, sizeof (INDX_COV));
isidp->indx_info = NULL;
memset ((void *) (&(isidp->multi_range_opt)), 0, sizeof (MULTI_RANGE_OPT));
scan_init_iss (isidp);
isidp->scan_cache.mvcc_snapshot = mvcc_snapshot;
isidp->need_count_only = false;
isidp->check_not_vacuumed = false;
isidp->not_vacuumed_res = DISK_VALID;
isidp->prebuilt_midxkey_domains = NULL;
}
/*
* scan_save_range_details () - save range details from the index scan id to
* a "backup" iss_range_details structure.
* return: error code
* rdp_dest: pointer to range details structure to be filled with data from isidp_src
* isidp_src: pointer to index scan id
*/
static int
scan_save_range_details (INDX_SCAN_ID * isidp_src, ISS_RANGE_DETAILS * rdp_dest)
{
if (rdp_dest == NULL || isidp_src == NULL || isidp_src->indx_info == NULL)
{
assert (false);
return ER_FAILED;
}
rdp_dest->key_cnt = isidp_src->indx_info->key_info.key_cnt;
rdp_dest->key_ranges = isidp_src->indx_info->key_info.key_ranges;
rdp_dest->key_pred = isidp_src->key_pred;
rdp_dest->range_type = isidp_src->indx_info->range_type;
rdp_dest->part_key_desc = isidp_src->bt_scan.btid_int.part_key_desc;
return NO_ERROR;
}
/*
* scan_restore_range_details () - restore range details from the backup
* structure into the index scan id.
* return: error code
* rdp_src: pointer to range details structure to be restored
* isidp_dest: pointer to index scan id to be filled
*
* Note:
* The index scan is reset so that it is considered a brand new scan (for
* instance, curr_keyno is set to -1 etc.)
*/
static int
scan_restore_range_details (ISS_RANGE_DETAILS * rdp_src, INDX_SCAN_ID * isidp_dest)
{
if (isidp_dest == NULL || rdp_src == NULL || isidp_dest->indx_info == NULL)
{
return ER_FAILED;
}
isidp_dest->curr_keyno = -1;
isidp_dest->indx_info->key_info.key_cnt = rdp_src->key_cnt;
isidp_dest->indx_info->key_info.key_ranges = rdp_src->key_ranges;
isidp_dest->key_pred = rdp_src->key_pred;
isidp_dest->indx_info->range_type = rdp_src->range_type;
isidp_dest->bt_scan.btid_int.part_key_desc = rdp_src->part_key_desc;
return NO_ERROR;
}
/*
* scan_get_next_iss_value () - retrieve the next value to be used in the
* index skip scan: the next value from the
* first column of the index.
* return: S_SUCCESS on successfully retrieving the next value
* S_ERROR on encountering an error
* S_END when the search does not find any "next value" (reached
* end of the btree).
* thread_p(in):
* scan_id (in): scan id
* isidp (in): index scan id
*
* Note:
* This function is called from call_get_next_index_oidset whenever the
* current value for the first index column is exhausted and we need to go on
* to the next possible value of C1. It is ONLY called within an index skip
* scan optimization context (ONLY when isidp->iss.use is TRUE).
* We call scan_get_index_oidset(), which in turn calls btree_range_search,
* but first we set the stage: prepare a "fake" range to obtain the next value
* for C1 (the first column), extract the value that btree_range_search returns
* inside isidp->iss.dbval, fill in slot 0 of the real range ([C1=?]) with
* that new value, and restore the real range as if it were ready to be used
* for the first time.
*/
static SCAN_CODE
scan_get_next_iss_value (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp)
{
/* we are being called either before any other btree range search, or after somebody finished a real range search and
* wants to advance to the next value for the first column of the index. */
INDEX_SKIP_SCAN *iss = NULL;
DB_VALUE *last_key = NULL;
ISS_RANGE_DETAILS scan_range_det, fetch_range_det;
bool descending_skip_key = false;
bool descending_scan = false;
int i;
if (isidp == NULL)
{
/* null pointer was passed to function */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return S_ERROR;
}
if (!isidp->iss.use)
{
/* not using iss but function was called; should not be here */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return S_ERROR;
}
if (isidp->iss.skipped_range == NULL || isidp->iss.skipped_range->key1 == NULL
|| isidp->iss.skipped_range->key1->value.funcp == NULL
|| isidp->iss.skipped_range->key1->value.funcp->operand == NULL
|| isidp->iss.skipped_range->key1->value.funcp->operand->value.type != TYPE_DBVAL)
{
/* the fetch range is corrupted; should not be here */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return S_ERROR;
}
if (isidp->bt_scan.btid_int.key_type == NULL || isidp->bt_scan.btid_int.key_type->setdomain == NULL
|| TP_DOMAIN_TYPE (isidp->bt_scan.btid_int.key_type) != DB_TYPE_MIDXKEY)
{
/* key type is not midxkey so this is not a multi-column index; should not be here */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return S_ERROR;
}
/* populate local variables iss and last_key */
descending_skip_key = isidp->bt_scan.btid_int.key_type->setdomain->is_desc;
iss = &isidp->iss;
last_key = &iss->skipped_range->key1->value.funcp->operand->value.value.dbval;
if (iss->current_op == ISS_OP_NONE)
{
/* first iteration of index skip scan; set up range for fetching first value. key1 might be set but will be
* ignored for INF_INF range. */
iss->current_op = ISS_OP_GET_FIRST_KEY;
iss->skipped_range->range = INF_INF;
}
else if (iss->current_op == ISS_OP_DO_RANGE_SEARCH)
{
/* find out whether the first column of the index is asc or desc */
descending_scan = (isidp->bt_scan.use_desc_index ? true : false);
if (descending_skip_key)
{
descending_scan = !descending_scan;
}
if (descending_scan)
{
/* if we're doing a descending scan, we can stop before searching for first_column < NULL since it won't
* produce any results */
if (DB_IS_NULL (last_key))
{
pr_clear_value (last_key);
db_make_null (last_key);
return S_END;
}
/* set the upper bound to last used key value and lower bound to NULL (i.e. infinity) */
iss->skipped_range->key2 = iss->skipped_range->key1;
iss->skipped_range->key1 = NULL;
}
/* set up ISS state for searching next distinct key */
iss->current_op = ISS_OP_SEARCH_NEXT_DISTINCT_KEY;
iss->skipped_range->range = (descending_scan ? INF_LT : GT_INF);
}
else
{
/* operator is neither ISS_OP_NONE nor ISS_OP_DO_RANGE_SEARCH; we shouldn't be here */
assert_release (false);
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return S_ERROR;
}
/* populate details structure for the fetch range so we can load it */
fetch_range_det.key_cnt = 1;
fetch_range_det.key_ranges = iss->skipped_range;
fetch_range_det.key_pred.pred_expr = NULL;
fetch_range_det.key_pred.pr_eval_fnc = NULL;
fetch_range_det.key_pred.regu_list = NULL;
fetch_range_det.range_type = R_RANGE;
fetch_range_det.part_key_desc = descending_skip_key;
/* save current range details */
if (scan_save_range_details (isidp, &scan_range_det) != NO_ERROR)
{
if (descending_scan)
{
/* return range to initial state before exit */
iss->skipped_range->key1 = iss->skipped_range->key2;
iss->skipped_range->key2 = NULL;
}
return S_ERROR;
}
/* load the range we set up for fetching the next key of the first column */
scan_restore_range_details (&fetch_range_det, isidp);
isidp->curr_keyno = -1;
/* run a scan to get next key for first index column; value will be stored in the lower (or higher) bound of the
* fetch range (i.e. in last_key) */
if (scan_get_index_oidset (thread_p, scan_id, NULL, NULL) != NO_ERROR)
{
if (descending_scan)
{
/* return range to initial state before exit */
iss->skipped_range->key1 = iss->skipped_range->key2;
iss->skipped_range->key2 = NULL;
}
scan_restore_range_details (&scan_range_det, isidp);
return S_ERROR;
}
/* undo bounds swapping if scan is descending */
if (descending_scan)
{
iss->skipped_range->key1 = iss->skipped_range->key2;
iss->skipped_range->key2 = NULL;
}
/* as soon as the scan returned, convert the midxkey dbvalue to real db value (if necessary) */
if (scan_id->s.isid.oids_count == 0)
{
/* no other keys exist; restore scan range and exit */
scan_restore_range_details (&scan_range_det, isidp);
return S_END;
}
else if (DB_VALUE_DOMAIN_TYPE (last_key) == DB_TYPE_MIDXKEY)
{
/* scan_get_index_oidset() returned a midxkey */
DB_VALUE first_midxkey_val;
DB_VALUE tmp;
int ret;
/* initialize temporary variables */
db_make_null (&first_midxkey_val);
db_make_null (&tmp);
/* put pointer to first value of midxkey into first_midxkey_val */
ret = pr_midxkey_get_element_nocopy (&last_key->data.midxkey, 0, &first_midxkey_val, NULL, NULL);
if (ret != NO_ERROR)
{
scan_restore_range_details (&scan_range_det, isidp);
return S_ERROR;
}
/* first_midxkey_val may hold pointer to first value from last_key, which we actually want to place in last_key.
* steps: 1. clone first_midxkey_val to a temp variable so we have a DB_VALUE independent of last_key 2. clear
* last_key (no longer holds important data) 3. clone temp variable to last_key (for later use) 4. clear tmp (we
* no longer need it) */
pr_clone_value (&first_midxkey_val, &tmp);
pr_clear_value (last_key);
pr_clone_value (&tmp, last_key);
pr_clear_value (&tmp);
pr_clear_value (&first_midxkey_val);
}
/* use last_key in scan_range */
for (i = 0; i < scan_range_det.key_cnt; i++)
{
KEY_RANGE *kr = &(scan_range_det.key_ranges[i]);
if (kr == NULL)
{
continue;
}
if (kr->key1 != NULL)
{
assert_release (kr->key1->type == TYPE_FUNC && TP_DOMAIN_TYPE (kr->key1->domain) == DB_TYPE_MIDXKEY);
assert_release (kr->key1->value.funcp->operand);
if (kr->key1->value.funcp->operand != NULL)
{
REGU_VARIABLE *regu = &kr->key1->value.funcp->operand->value;
regu->type = TYPE_DBVAL;
regu->domain = tp_domain_resolve_default (DB_VALUE_DOMAIN_TYPE (last_key));
pr_clear_value (®u->value.dbval);
pr_clone_value (last_key, ®u->value.dbval);
}
}
if (kr->key2 != NULL)
{
assert_release (kr->key2->type == TYPE_FUNC && TP_DOMAIN_TYPE (kr->key2->domain) == DB_TYPE_MIDXKEY);
assert_release (kr->key2->value.funcp->operand);
if (kr->key2->value.funcp->operand != NULL)
{
REGU_VARIABLE *regu = &kr->key2->value.funcp->operand->value;
regu->type = TYPE_DBVAL;
regu->domain = tp_domain_resolve_default (DB_VALUE_DOMAIN_TYPE (last_key));
pr_clear_value (®u->value.dbval);
pr_clone_value (last_key, ®u->value.dbval);
}
}
}
/* restore the range used for normal scanning */
scan_restore_range_details (&scan_range_det, isidp);
/* prepare for range search */
isidp->iss.current_op = ISS_OP_DO_RANGE_SEARCH;
isidp->curr_keyno = -1;
return S_SUCCESS;
}
/*
* scan_init_scan_pred () - initialize SCAN_PRED structure
* return: none
*/
static void
scan_init_scan_pred (SCAN_PRED * scan_pred_p, regu_variable_list_node * regu_list, PRED_EXPR * pred_expr,
PR_EVAL_FNC pr_eval_fnc)
{
assert (scan_pred_p != NULL);
scan_pred_p->regu_list = regu_list;
scan_pred_p->pred_expr = pred_expr;
scan_pred_p->pr_eval_fnc = pr_eval_fnc;
}
/*
* scan_init_scan_attrs () - initialize SCAN_ATTRS structure
* return: none
*/
static void
scan_init_scan_attrs (SCAN_ATTRS * scan_attrs_p, int num_attrs, ATTR_ID * attr_ids, HEAP_CACHE_ATTRINFO * attr_cache)
{
assert (scan_attrs_p != NULL);
scan_attrs_p->num_attrs = num_attrs;
scan_attrs_p->attr_ids = attr_ids;
scan_attrs_p->attr_cache = attr_cache;
}
/*
* scan_init_indx_coverage () - initialize INDX_COV structure
* return: error code
*
* coverage_enabled(in): true if coverage is enabled
* output_val_list(in): output val list
* regu_val_list(in): regu val list
* vd(in): val descriptor
* query_id(in): the query id
* max_key_len(in): the maximum key length
* indx_cov(in/out): index coverage data
*/
static int
scan_init_indx_coverage (THREAD_ENTRY * thread_p, int coverage_enabled, valptr_list_node * output_val_list,
regu_variable_list_node * regu_val_list, VAL_DESCR * vd, QUERY_ID query_id, int max_key_len,
int func_index_col_id, INDX_COV * indx_cov)
{
int err = NO_ERROR;
int num_membuf_pages = 0;
if (indx_cov == NULL)
{
return ER_FAILED;
}
indx_cov->val_descr = vd;
indx_cov->output_val_list = output_val_list;
indx_cov->regu_val_list = regu_val_list;
indx_cov->query_id = query_id;
if (coverage_enabled == false)
{
indx_cov->type_list = NULL;
indx_cov->list_id = NULL;
indx_cov->tplrec = NULL;
indx_cov->lsid = NULL;
indx_cov->max_tuples = 0;
indx_cov->func_index_col_id = -1;
return NO_ERROR;
}
indx_cov->func_index_col_id = func_index_col_id;
indx_cov->type_list =
(QFILE_TUPLE_VALUE_TYPE_LIST *) db_private_alloc (thread_p, sizeof (QFILE_TUPLE_VALUE_TYPE_LIST));
if (indx_cov->type_list == NULL)
{
err = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
if (qdata_get_valptr_type_list (thread_p, output_val_list, indx_cov->type_list) != NO_ERROR)
{
err = ER_FAILED;
goto exit_on_error;
}
if (indx_cov->list_id != NULL && indx_cov->list_id->tfile_vfid != NULL)
{
assert (indx_cov->list_id->tfile_vfid != NULL);
qfile_truncate_list (thread_p, indx_cov->list_id);
}
else
{
/*
* Covering index scan needs large-size memory buffer in order to decrease
* the number of times doing stop-and-resume during btree_range_search.
* To do it, QFILE_FLAG_USE_KEY_BUFFER is introduced. If the flag is set,
* the list file allocates PRM_INDEX_SCAN_KEY_BUFFER_PAGES pages memory
* for its memory buffer, which is generally larger than prm_get_integer_value (PRM_ID_TEMP_MEM_BUFFER_PAGES).
*/
indx_cov->list_id =
qfile_open_list (thread_p, indx_cov->type_list, NULL, query_id, QFILE_FLAG_USE_KEY_BUFFER, indx_cov->list_id);
if (indx_cov->list_id == NULL)
{
err = ER_FAILED;
goto exit_on_error;
}
}
num_membuf_pages = qmgr_get_temp_file_membuf_pages (indx_cov->list_id->tfile_vfid);
assert (num_membuf_pages > 0);
if (max_key_len > 0 && num_membuf_pages > 0)
{
indx_cov->max_tuples = num_membuf_pages * IO_PAGESIZE / max_key_len;
indx_cov->max_tuples = MAX (indx_cov->max_tuples, 1);
}
else
{
indx_cov->max_tuples = IDX_COV_DEFAULT_TUPLES;
}
indx_cov->tplrec = (QFILE_TUPLE_RECORD *) db_private_alloc (thread_p, sizeof (QFILE_TUPLE_RECORD));
if (indx_cov->tplrec == NULL)
{
err = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
indx_cov->tplrec->size = 0;
indx_cov->tplrec->tpl = NULL;
indx_cov->lsid = (QFILE_LIST_SCAN_ID *) db_private_alloc (thread_p, sizeof (QFILE_LIST_SCAN_ID));
if (indx_cov->lsid == NULL)
{
err = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
indx_cov->lsid->status = S_CLOSED;
return NO_ERROR;
exit_on_error:
if (indx_cov->type_list != NULL)
{
if (indx_cov->type_list->domp != NULL)
{
db_private_free_and_init (thread_p, indx_cov->type_list->domp);
}
db_private_free_and_init (thread_p, indx_cov->type_list);
}
if (indx_cov->list_id != NULL)
{
QFILE_FREE_AND_INIT_LIST_ID (indx_cov->list_id);
}
if (indx_cov->tplrec != NULL)
{
if (indx_cov->tplrec->tpl != NULL)
{
db_private_free_and_init (thread_p, indx_cov->tplrec->tpl);
}
db_private_free_and_init (thread_p, indx_cov->tplrec);
}
if (indx_cov->lsid != NULL)
{
db_private_free_and_init (thread_p, indx_cov->lsid);
}
return err;
}
/*
* scan_init_index_key_limit () - initialize/reset index key limits
* return: error code
*/
static int
scan_init_index_key_limit (THREAD_ENTRY * thread_p, INDX_SCAN_ID * isidp, KEY_INFO * key_infop, VAL_DESCR * vd)
{
DB_VALUE *dbvalp;
TP_DOMAIN *domainp = tp_domain_resolve_default (DB_TYPE_BIGINT);
bool is_lower_limit_negative = false;
TP_DOMAIN_STATUS dom_status;
if (key_infop->key_limit_l != NULL)
{
if (fetch_peek_dbval (thread_p, key_infop->key_limit_l, vd, NULL, NULL, NULL, &dbvalp) != NO_ERROR)
{
return ER_FAILED;
}
dom_status = tp_value_coerce (dbvalp, dbvalp, domainp);
if (dom_status != DOMAIN_COMPATIBLE)
{
(void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, dbvalp, domainp);
return ER_FAILED;
}
if (DB_VALUE_DOMAIN_TYPE (dbvalp) != DB_TYPE_BIGINT)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
else
{
isidp->key_limit_lower = db_get_bigint (dbvalp);
}
if (isidp->key_limit_lower < 0)
{
if (key_infop->is_user_given_keylimit == true)
{
/* We don't allow users to give us a bad keylimit bound */
/* still want to have better error code */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_QPROC_INVALID_PARAMETER;
}
/* Optimizer adopts keylimit optimization */
/* SELECT * from t where ROWNUM = 0 order by a: this would sometimes get optimized using keylimit, if the
* circumstances are right. in this case, the lower limit would be "0-1", effectiveley -1. We cannot allow
* that to happen, since -1 is a special value meaning "there is no lower limit", and certain critical
* decisions (such as resetting the key limit for multiple ranges) depend on knowing whether or not there is
* a lower key limit. We set a flag to remember, later on, to "adjust" the key limits such that, if the lower
* limit is negative, to return no results.
*/
is_lower_limit_negative = true;
}
}
else
{
isidp->key_limit_lower = -1;
}
if (key_infop->key_limit_u != NULL)
{
if (fetch_peek_dbval (thread_p, key_infop->key_limit_u, vd, NULL, NULL, NULL, &dbvalp) != NO_ERROR)
{
return ER_FAILED;
}
dom_status = tp_value_coerce (dbvalp, dbvalp, domainp);
if (dom_status != DOMAIN_COMPATIBLE)
{
(void) tp_domain_status_er_set (dom_status, ARG_FILE_LINE, dbvalp, domainp);
return ER_FAILED;
}
if (DB_VALUE_DOMAIN_TYPE (dbvalp) != DB_TYPE_BIGINT)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
return ER_QPROC_INVALID_DATATYPE;
}
else
{
isidp->key_limit_upper = db_get_bigint (dbvalp);
}
if (isidp->key_limit_upper < 0)
{
if (key_infop->is_user_given_keylimit == true)
{
/* We don't allow users to give us a bad keylimit bound */
/* still want to have better error code */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_PARAMETER, 0);
return ER_QPROC_INVALID_PARAMETER;
}
/* Optimizer adopts keylimit optimization */
/* Try to sanitize the upper value. It might have been computed from operations on host variables, which are
* unpredictable.
*/
isidp->key_limit_upper = 0;
}
}
else
{
isidp->key_limit_upper = -1;
}
if (is_lower_limit_negative && isidp->key_limit_upper > 0)
{
/* decrease the upper limit: key_limit_lower is negative */
isidp->key_limit_upper += isidp->key_limit_lower;
if (isidp->key_limit_upper < 0)
{
isidp->key_limit_upper = 0;
}
isidp->key_limit_lower = 0; /* reset it to something usable */
}
return NO_ERROR;
}
/*
* scan_alloc_oid_list () - Allocate an index scan OID list.
*
* return : Error code.
* oid_buf_p (out) : Allocated buffer.
* oid_buf_capacity (out) : Allocated buffer capacity.
*/
static int
scan_alloc_oid_list (BTREE_ISCAN_OID_LIST ** oid_list_p)
{
/* Assert expected arguments. */
assert (oid_list_p != NULL && *oid_list_p == NULL);
/* Allocate OID list entry. */
*oid_list_p = (BTREE_ISCAN_OID_LIST *) malloc (sizeof (BTREE_ISCAN_OID_LIST));
if (*oid_list_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (BTREE_ISCAN_OID_LIST));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
/* Allocate OID list buffer. */
(*oid_list_p)->capacity = ISCAN_OID_BUFFER_CAPACITY / OR_OID_SIZE;
(*oid_list_p)->oidp = (OID *) malloc ((*oid_list_p)->capacity * OR_OID_SIZE);
if ((*oid_list_p)->oidp == NULL)
{
/* Could not allocate memory. */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (*oid_list_p)->capacity * OR_OID_SIZE);
/* Free already allocated. */
free_and_init (*oid_list_p);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
/* Allocation successful. */
/* Initialize other fields. */
(*oid_list_p)->next_list = NULL;
(*oid_list_p)->max_oid_cnt = (*oid_list_p)->capacity;
(*oid_list_p)->oid_cnt = 0;
return NO_ERROR;
}
/*
* scan_free_oid_list () - Free an index scan OID list.
*
* return : void
*/
static void
scan_free_oid_list (BTREE_ISCAN_OID_LIST * oid_list_p)
{
assert (oid_list_p != NULL);
if (oid_list_p->oidp != NULL)
{
free_and_init (oid_list_p->oidp);
}
free_and_init (oid_list_p);
}
/*
* scan_alloc_iscan_oid_buf_list () - Allocate or use a preallocated buffer for index scan OID list.
*
* return : Error code.
* oid_list (out) : Output OID list with allocated buffer.
*/
static int
scan_alloc_iscan_oid_buf_list (BTREE_ISCAN_OID_LIST ** oid_list)
{
#if defined (SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
int error_code = NO_ERROR;
/* Assert expected argument. */
assert (oid_list != NULL && *oid_list == NULL);
/* Is buffer empty? */
if (scan_Iscan_oid_buf_list != NULL)
{
/* Not empty. */
/* Protect by mutex and try to get from buffer. */
rv = pthread_mutex_lock (&scan_Iscan_oid_buf_list_mutex);
/* Was buffer emptied? */
if (scan_Iscan_oid_buf_list != NULL)
{
/* Not empty */
/* Pop first entry. */
*oid_list = scan_Iscan_oid_buf_list;
/* Update buffer. */
scan_Iscan_oid_buf_list = scan_Iscan_oid_buf_list->next_list;
scan_Iscan_oid_buf_list_count--;
pthread_mutex_unlock (&scan_Iscan_oid_buf_list_mutex);
/* Reset next_list link. */
(*oid_list)->next_list = NULL;
return NO_ERROR;
}
/* Empty. */
pthread_mutex_unlock (&scan_Iscan_oid_buf_list_mutex);
}
/* Allocate a new OID list entry. */
error_code = scan_alloc_oid_list (oid_list);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return error_code;
}
/* Safe guard. */
assert (*oid_list != NULL);
assert ((*oid_list)->oidp != NULL);
assert ((*oid_list)->capacity > 0);
/* Success. */
return NO_ERROR;
}
/*
* scan_free_iscan_oid_buf_list () - Free OID buffer from OID list of index scan.
*
* return : Void.
* oid_list (in) : OID list to free.
*/
static void
scan_free_iscan_oid_buf_list (BTREE_ISCAN_OID_LIST * oid_list)
{
#if defined (SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* Free entry. */
rv = pthread_mutex_lock (&scan_Iscan_oid_buf_list_mutex);
/* Is buffer at its full capacity? */
if (scan_Iscan_oid_buf_list_count < MAX_NTRANS)
{
/* Add oid_list to scan_Iscan_oid_buf_list */
oid_list->next_list = scan_Iscan_oid_buf_list;
scan_Iscan_oid_buf_list = oid_list;
scan_Iscan_oid_buf_list_count++;
}
else
{
/* Too many buffers, just free it. */
scan_free_oid_list (oid_list);
}
pthread_mutex_unlock (&scan_Iscan_oid_buf_list_mutex);
}
/*
* rop_to_range () - map left/right to range operator
* return:
* range(out): full-RANGE operator
* left(in): left-side range operator
* right(in): right-side range operator
*/
static void
rop_to_range (RANGE * range, ROP_TYPE left, ROP_TYPE right)
{
int i;
*range = NA_NA;
for (i = 0; i < rop_range_table_size; i++)
{
if (left == rop_range_table[i].left && right == rop_range_table[i].right)
{
/* found match */
*range = rop_range_table[i].range;
break;
}
}
}
/*
* range_to_rop () - map range to left/right operator
* return:
* left(out): left-side range operator
* right(out): right-side range operator
* range(in): full-RANGE operator
*/
static void
range_to_rop (ROP_TYPE * left, ROP_TYPE * right, RANGE range)
{
int i;
*left = ROP_NA;
*right = ROP_NA;
for (i = 0; i < rop_range_table_size; i++)
{
if (range == rop_range_table[i].range)
{
/* found match */
*left = rop_range_table[i].left;
*right = rop_range_table[i].right;
break;
}
}
}
/*
* scan_key_compare ()
* val1(in):
* val2(in):
* num_index_term(in):
* return:
*/
static int
scan_key_compare (DB_VALUE * val1, DB_VALUE * val2, int num_index_term)
{
int rc = DB_UNK;
DB_TYPE key_type;
if (val1 == NULL || val2 == NULL)
{
assert_release (0);
return rc;
}
if (DB_IS_NULL (val1))
{
if (DB_IS_NULL (val2))
{
rc = DB_EQ;
}
else
{
rc = DB_LT;
}
}
else if (DB_IS_NULL (val2))
{
rc = DB_GT;
}
else
{
key_type = DB_VALUE_DOMAIN_TYPE (val1);
if (key_type == DB_TYPE_MIDXKEY)
{
int dummy_diff_column;
rc =
pr_midxkey_compare (db_get_midxkey (val1), db_get_midxkey (val2), 1, 1, num_index_term, NULL,
&dummy_diff_column, NULL, NULL);
}
else
{
rc = tp_value_compare (val1, val2, 1, 1);
}
}
return rc;
}
/*
* compare_val_op () - compare two values specified by range operator
* return:
* val1(in):
* op1(in):
* val2(in):
* op2(in):
* num_index_term(in):
*/
static ROP_TYPE
compare_val_op (DB_VALUE * val1, ROP_TYPE op1, DB_VALUE * val2, ROP_TYPE op2, int num_index_term)
{
int rc;
if (op1 == ROP_GT_INF) /* val1 is -INF */
{
return (op1 == op2) ? ROP_EQ : ROP_LT;
}
if (op1 == ROP_LT_INF) /* val1 is +INF */
{
return (op1 == op2) ? ROP_EQ : ROP_GT;
}
if (op2 == ROP_GT_INF) /* val2 is -INF */
{
return (op2 == op1) ? ROP_EQ : ROP_GT;
}
if (op2 == ROP_LT_INF) /* val2 is +INF */
{
return (op2 == op1) ? ROP_EQ : ROP_LT;
}
rc = scan_key_compare (val1, val2, num_index_term);
if (rc == DB_EQ)
{
/* (val1, op1) == (val2, op2) */
if (op1 == op2)
{
return ROP_EQ;
}
if (op1 == ROP_EQ || op1 == ROP_GE || op1 == ROP_LE)
{
if (op2 == ROP_EQ || op2 == ROP_GE || op2 == ROP_LE)
{
return ROP_EQ;
}
return (op2 == ROP_GT) ? ROP_LT_ADJ : ROP_GT_ADJ;
}
if (op1 == ROP_GT)
{
if (op2 == ROP_EQ || op2 == ROP_GE || op2 == ROP_LE)
{
return ROP_GT_ADJ;
}
return (op2 == ROP_LT) ? ROP_GT : ROP_EQ;
}
if (op1 == ROP_LT)
{
if (op2 == ROP_EQ || op2 == ROP_GE || op2 == ROP_LE)
{
return ROP_LT_ADJ;
}
return (op2 == ROP_GT) ? ROP_LT : ROP_EQ;
}
}
else if (rc == DB_LT)
{
/* (val1, op1) < (val2, op2) */
return ROP_LT;
}
else if (rc == DB_GT)
{
/* (val1, op1) > (val2, op2) */
return ROP_GT;
}
/* tp_value_compare() returned error? */
return (rc == DB_EQ) ? ROP_EQ : ROP_NA;
}
/*
* key_val_compare () - key value sorting function
* return:
* p1 (in): pointer to key1 range
* p2 (in): pointer to key2 range
*/
static int
key_val_compare (const void *p1, const void *p2)
{
int p1_num_index_term, p2_num_index_term;
DB_VALUE *p1_key, *p2_key;
p1_num_index_term = ((KEY_VAL_RANGE *) p1)->num_index_term;
p2_num_index_term = ((KEY_VAL_RANGE *) p2)->num_index_term;
assert_release (p1_num_index_term == p2_num_index_term);
p1_key = &((KEY_VAL_RANGE *) p1)->key1;
p2_key = &((KEY_VAL_RANGE *) p2)->key1;
return scan_key_compare (p1_key, p2_key, p1_num_index_term);
}
/*
* eliminate_duplicated_keys () - elimnate duplicated key values
* return: number of keys, -1 for error
* key_vals (in): pointer to array of KEY_VAL_RANGE structure
* key_cnt (in): number of keys; size of key_vals
*/
static int
eliminate_duplicated_keys (KEY_VAL_RANGE * key_vals, int key_cnt)
{
int n;
KEY_VAL_RANGE *curp, *nextp;
curp = key_vals;
nextp = key_vals + 1;
n = 0;
while (key_cnt > 1 && n < key_cnt - 1)
{
if (tp_value_compare (&curp->key1, &nextp->key1, 1, 1) == DB_EQ)
{
pr_clear_value (&nextp->key1);
pr_clear_value (&nextp->key2);
memmove (nextp, nextp + 1, sizeof (KEY_VAL_RANGE) * (key_cnt - n - 2));
key_cnt--;
}
else
{
curp++;
nextp++;
n++;
}
}
return key_cnt;
}
/*
* merge_key_ranges () - merge search key ranges
* return: number of keys, -1 for error
* key_vals (in): pointer to array of KEY_VAL_RANGE structure
* key_cnt (in): number of keys; size of key_vals
*/
static int
merge_key_ranges (KEY_VAL_RANGE * key_vals, int key_cnt)
{
int cur_n, next_n;
KEY_VAL_RANGE *curp, *nextp;
ROP_TYPE cur_op1, cur_op2, next_op1, next_op2;
ROP_TYPE cmp_1, cmp_2, cmp_3, cmp_4;
bool is_mergeable;
cmp_1 = cmp_2 = cmp_3 = cmp_4 = ROP_NA;
curp = key_vals;
cur_n = 0;
while (key_cnt > 1 && cur_n < key_cnt - 1)
{
range_to_rop (&cur_op1, &cur_op2, curp->range);
nextp = curp + 1;
next_n = cur_n + 1;
while (next_n < key_cnt)
{
range_to_rop (&next_op1, &next_op2, nextp->range);
/* check if the two key ranges are mergable */
is_mergeable = true; /* init */
cmp_1 = cmp_2 = cmp_3 = cmp_4 = ROP_NA;
if (is_mergeable == true)
{
cmp_1 = compare_val_op (&curp->key2, cur_op2, &nextp->key1, next_op1, curp->num_index_term);
if (cmp_1 == ROP_NA || cmp_1 == ROP_LT)
{
is_mergeable = false; /* error or disjoint */
}
}
if (is_mergeable == true)
{
cmp_2 = compare_val_op (&curp->key1, cur_op1, &nextp->key2, next_op2, curp->num_index_term);
if (cmp_2 == ROP_NA || cmp_2 == ROP_GT)
{
is_mergeable = false; /* error or disjoint */
}
}
if (is_mergeable == true)
{
/* determine the lower bound of the merged key range */
cmp_3 = compare_val_op (&curp->key1, cur_op1, &nextp->key1, next_op1, curp->num_index_term);
if (cmp_3 == ROP_NA)
{
is_mergeable = false;
}
}
if (is_mergeable == true)
{
/* determine the upper bound of the merged key range */
cmp_4 = compare_val_op (&curp->key2, cur_op2, &nextp->key2, next_op2, curp->num_index_term);
if (cmp_4 == ROP_NA)
{
is_mergeable = false;
}
}
if (is_mergeable == false)
{
/* they are disjoint */
nextp++;
next_n++;
continue; /* skip and go ahead */
}
/* determine the lower bound of the merged key range */
if (cmp_3 == ROP_GT_ADJ || cmp_3 == ROP_GT)
{
pr_clear_value (&curp->key1);
curp->key1 = nextp->key1; /* bitwise copy */
db_make_null (&nextp->key1);
cur_op1 = next_op1;
}
else
{
pr_clear_value (&nextp->key1);
}
/* determine the upper bound of the merged key range */
if (cmp_4 == ROP_LT || cmp_4 == ROP_LT_ADJ)
{
pr_clear_value (&curp->key2);
curp->key2 = nextp->key2; /* bitwise copy */
db_make_null (&nextp->key2);
cur_op2 = next_op2;
}
else
{
pr_clear_value (&nextp->key2);
}
/* determine the new range type */
rop_to_range (&curp->range, cur_op1, cur_op2);
/* remove merged one(nextp) */
memmove (nextp, nextp + 1, sizeof (KEY_VAL_RANGE) * (key_cnt - next_n - 1));
key_cnt--;
}
curp++;
cur_n++;
}
return key_cnt;
}
/*
* check_key_vals () - check key values
* return: number of keys, -1 for error
* key_vals (in): pointer to array of KEY_VAL_RANGE structure
* key_cnt (in): number of keys; size of key_vals
* chk_fn (in): check function for key_vals
*/
static int
check_key_vals (KEY_VAL_RANGE * key_vals, int key_cnt, QPROC_KEY_VAL_FU * key_val_fn)
{
if (key_cnt <= 1)
{
return key_cnt;
}
qsort ((void *) key_vals, key_cnt, sizeof (KEY_VAL_RANGE), key_val_compare);
return ((*key_val_fn) (key_vals, key_cnt));
}
/*
* scan_dbvals_to_midxkey () -
* return: NO_ERROR or ER_code
*
* retval (out):
* indexal (out):
* btree_domainp (in):
* num_term (in):
* func (in):
* vd (in):
* key_minmax (in):
* is_iss (in)
*/
static int
scan_dbvals_to_midxkey (THREAD_ENTRY * thread_p, DB_VALUE * retval, bool * indexable, TP_DOMAIN * btree_domainp,
int num_term, REGU_VARIABLE * func, VAL_DESCR * vd, int key_minmax, bool is_iss,
TP_DOMAIN ** prebuilt_midxkey_domain)
{
int ret = NO_ERROR;
DB_VALUE *val = NULL;
DB_TYPE val_type_id;
DB_MIDXKEY midxkey;
int idx_ncols = 0, natts, i, j;
int buf_size;
regu_variable_list_node *operand;
char *nullmap_ptr; /* ponter to boundbits */
OR_BUF buf;
bool need_new_setdomain = false;
TP_DOMAIN *idx_setdomain = NULL, *vals_setdomain = NULL;
TP_DOMAIN *idx_dom = NULL, *val_dom = NULL, *dom = NULL, *next = NULL, *prebuilt_domain = NULL;
DB_TYPE idx_type_id;
TP_DOMAIN dom_buf;
DB_VALUE *coerced_values = NULL;
bool *has_coerced_values = NULL;
bool new_setdomain_built = *prebuilt_midxkey_domain != NULL;
*indexable = false;
if (TP_DOMAIN_TYPE (func->domain) != DB_TYPE_MIDXKEY)
{
assert (false);
return ER_FAILED;
}
idx_ncols = btree_domainp->precision;
if (idx_ncols <= 0)
{
assert (false);
return ER_FAILED;
}
idx_setdomain = btree_domainp->setdomain;
#if !defined(NDEBUG)
{
int dom_ncols = 0;
for (idx_dom = idx_setdomain; idx_dom != NULL; idx_dom = idx_dom->next)
{
dom_ncols++;
#if 0
/* idx_dom->precision is -1 in the following cases.
* create index idx on t1(IFNULL(a,'x'), b); -- a is char(n)
* Remove the assert check. */
if (idx_dom->precision < 0)
{
assert (false);
return ER_FAILED;
}
#endif
}
if (dom_ncols <= 0)
{
assert (false);
return ER_FAILED;
}
assert (dom_ncols == idx_ncols);
}
#endif /* NDEBUG */
buf_size = 0;
midxkey.buf = NULL;
midxkey.min_max_val.position = -1;
/* check to need a new setdomain */
for (operand = func->value.funcp->operand, idx_dom = idx_setdomain, i = 0; operand != NULL && idx_dom != NULL;
operand = operand->next, idx_dom = idx_dom->next, i++)
{
ret = fetch_peek_dbval (thread_p, &(operand->value), vd, NULL, NULL, NULL, &val);
if (ret != NO_ERROR)
{
goto err_exit;
}
if (DB_IS_NULL (val))
{
if (is_iss && i == 0)
{
/* If this is INDEX SKIP SCAN we allow the first column to be NULL and we don't need a new domain for it */
continue;
}
else
{
/* to fix multi-column index NULL problem */
goto end;
}
}
idx_type_id = TP_DOMAIN_TYPE (idx_dom);
val_type_id = DB_VALUE_DOMAIN_TYPE (val);
if (!tp_valid_indextype (val_type_id))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TP_CANT_COERCE, 2, pr_type_name (idx_type_id),
pr_type_name (val_type_id));
goto err_exit;
}
if (TP_IS_STRING_TYPE (val_type_id))
{
/* we need to check for maxes */
if (val->data.ch.medium.is_max_string)
{
/* oops, we found max. */
midxkey.min_max_val.position = i;
midxkey.min_max_val.type = MAX_COLUMN;
/* just stop here */
break;
}
}
if (idx_type_id != val_type_id)
{
/* allocate DB_VALUE array to store coerced values. */
if (has_coerced_values == NULL)
{
assert (has_coerced_values == NULL && coerced_values == NULL);
coerced_values = (DB_VALUE *) db_private_alloc (thread_p, sizeof (DB_VALUE) * idx_ncols);
if (coerced_values == NULL)
{
goto err_exit;
}
for (j = 0; j < idx_ncols; j++)
{
db_make_null (&coerced_values[j]);
}
has_coerced_values = (bool *) db_private_alloc (thread_p, sizeof (bool) * idx_ncols);
if (has_coerced_values == NULL)
{
goto err_exit;
}
memset (has_coerced_values, 0x0, sizeof (bool) * idx_ncols);
}
/* Coerce the value to index domain. If there is loss, we should make a new setdomain. */
ret = tp_value_coerce_strict (val, &coerced_values[i], idx_dom);
if (ret != NO_ERROR)
{
need_new_setdomain = true;
}
else
{
has_coerced_values[i] = true;
}
}
else if (idx_type_id == DB_TYPE_NUMERIC || idx_type_id == DB_TYPE_CHAR || idx_type_id == DB_TYPE_BIT)
{
/* skip variable string domain : DB_TYPE_VARCHAR, DB_TYPE_VARBIT */
val_dom = tp_domain_resolve_value (val, &dom_buf);
if (val_dom == NULL)
{
goto err_exit;
}
if (!tp_domain_match_ignore_order (idx_dom, val_dom, TP_EXACT_MATCH))
{
need_new_setdomain = true;
}
}
}
/* calculate midxkey's size & make a new setdomain if need */
/* NOTICE that this will stop the iteration on MAX_COLUMN value if exists.
* Remaining key values including MAX_COLUMN position will be filled as NULL
* by btree_coerce_key at the end of this function.
*/
if (!need_new_setdomain)
{
new_setdomain_built = false;
}
if (new_setdomain_built)
{
prebuilt_domain = (*prebuilt_midxkey_domain)->setdomain;
}
for (operand = func->value.funcp->operand, idx_dom = idx_setdomain, natts = 0;
operand != NULL && idx_dom != NULL
&& (midxkey.min_max_val.position == -1 || natts < midxkey.min_max_val.position);
operand = operand->next, idx_dom = idx_dom->next, natts++)
{
/* If there is coerced value, we will use it regardless of whether a new setdomain is required or not. */
retry:
if (has_coerced_values != NULL && has_coerced_values[natts] == true)
{
assert (coerced_values != NULL);
val = &coerced_values[natts];
}
else
{
ret = fetch_peek_dbval (thread_p, &(operand->value), vd, NULL, NULL, NULL, &val);
if (ret != NO_ERROR)
{
goto err_exit;
}
}
if (new_setdomain_built)
{
dom = prebuilt_domain;
prebuilt_domain = prebuilt_domain->next;
if (natts == 0 && dom->type->id == DB_TYPE_NULL && !DB_IS_NULL (val))
{
need_new_setdomain = true;
new_setdomain_built = false;
dom = NULL;
goto retry;
}
}
else if (need_new_setdomain == true)
{
/* make a value's domain */
val_dom = tp_domain_resolve_value (val, &dom_buf);
if (val_dom == NULL)
{
goto err_exit;
}
val_dom = tp_domain_copy (val_dom, false);
if (val_dom == NULL)
{
goto err_exit;
}
val_dom->is_desc = idx_dom->is_desc;
/* make a new setdomain */
if (vals_setdomain == NULL)
{
assert (dom == NULL);
vals_setdomain = val_dom;
}
else
{
assert (dom != NULL);
dom->next = val_dom;
}
dom = val_dom;
}
else
{
dom = idx_dom;
}
if (DB_IS_NULL (val))
{
if (is_iss && natts == 0)
{
/* We allow the first column to be NULL and we're not writing it in the MIDXKEY buffer */
continue;
}
else
{
/* impossible case */
assert_release (false);
goto end;
}
}
buf_size += dom->type->get_index_size_of_value (val);
}
/* add more domain to setdomain for partial key */
if (need_new_setdomain == true && !new_setdomain_built)
{
assert (dom != NULL);
if (idx_dom != NULL)
{
val_dom = tp_domain_copy (idx_dom, false);
if (val_dom == NULL)
{
goto err_exit;
}
dom->next = val_dom;
}
}
buf_size += or_multi_header_size (idx_ncols);
midxkey.buf = (char *) db_private_alloc (thread_p, buf_size);
if (midxkey.buf == NULL)
{
retval->need_clear = false;
goto err_exit;
}
or_init (&buf, midxkey.buf, buf_size);
nullmap_ptr = midxkey.buf;
or_multi_clear_header (nullmap_ptr, idx_ncols);
or_advance (&buf, or_multi_header_size (idx_ncols));
/* generate multi columns key (values -> midxkey.buf) */
if (new_setdomain_built)
{
dom = (*prebuilt_midxkey_domain)->setdomain;
}
else
{
dom = (vals_setdomain != NULL) ? vals_setdomain : idx_setdomain;
}
for (operand = func->value.funcp->operand, i = 0;
operand != NULL && dom != NULL && (i < natts); operand = operand->next, dom = dom->next, i++)
{
if (has_coerced_values != NULL && has_coerced_values[i] == true)
{
assert (coerced_values != NULL);
val = &coerced_values[i];
}
else
{
ret = fetch_peek_dbval (thread_p, &(operand->value), vd, NULL, NULL, NULL, &val);
if (ret != NO_ERROR)
{
goto err_exit;
}
}
or_multi_put_element_offset (nullmap_ptr, idx_ncols, CAST_BUFLEN (buf.ptr - buf.buffer), i);
if (DB_IS_NULL (val) || dom->type->id == DB_TYPE_NULL)
{
if (is_iss && i == 0)
{
/* There is nothing to write for NULL. Just make sure the bit is not set */
assert (or_multi_is_null (nullmap_ptr, i));
continue;
}
else
{
/* impossible case */
assert_release (false);
goto end;
}
}
dom->type->index_writeval (&buf, val);
or_multi_set_not_null (nullmap_ptr, i);
}
assert (buf_size == CAST_BUFLEN (buf.ptr - buf.buffer));
for (i = natts; i < idx_ncols; i++)
{
assert (or_multi_is_null (nullmap_ptr, i));
or_multi_put_element_offset (nullmap_ptr, idx_ncols, buf_size, i);
}
or_multi_put_size_offset (nullmap_ptr, idx_ncols, buf_size);
/* Make midxkey DB_VALUE */
midxkey.size = buf_size;
midxkey.ncolumns = natts;
if (vals_setdomain != NULL)
{
midxkey.domain = tp_domain_construct (DB_TYPE_MIDXKEY, NULL, idx_ncols, 0, vals_setdomain);
if (midxkey.domain == NULL)
{
goto err_exit;
}
midxkey.domain = tp_domain_cache (midxkey.domain);
*prebuilt_midxkey_domain = midxkey.domain;
}
else
{
midxkey.domain = btree_domainp;
}
if (new_setdomain_built)
{
midxkey.domain = *prebuilt_midxkey_domain;
}
ret = db_make_midxkey (retval, &midxkey);
if (ret != NO_ERROR)
{
goto err_exit;
}
retval->need_clear = true;
*indexable = true;
ret = btree_coerce_key (retval, num_term, btree_domainp, key_minmax);
if (has_coerced_values)
{
db_private_free_and_init (thread_p, has_coerced_values);
}
if (coerced_values)
{
db_private_free_and_init (thread_p, coerced_values);
}
return ret;
end:
if (midxkey.buf)
{
db_private_free_and_init (thread_p, midxkey.buf);
}
if (vals_setdomain != NULL)
{
for (dom = vals_setdomain; dom != NULL; dom = next)
{
next = dom->next;
tp_domain_free (dom);
}
}
if (has_coerced_values)
{
db_private_free_and_init (thread_p, has_coerced_values);
}
if (coerced_values)
{
db_private_free_and_init (thread_p, coerced_values);
}
return ret;
err_exit:
if (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR)
{
ret = ER_FAILED;
}
goto end;
}
/*
* scan_regu_key_to_index_key:
*/
static int
scan_regu_key_to_index_key (THREAD_ENTRY * thread_p, KEY_RANGE * key_ranges, KEY_VAL_RANGE * key_val_range,
INDX_SCAN_ID * iscan_id, TP_DOMAIN * btree_domainp, VAL_DESCR * vd, int key_range_idx)
{
bool indexable = true;
int key_minmax;
int curr_key_prefix_length = 0;
int count;
int ret = NO_ERROR;
DB_TYPE db_type;
int key_len;
regu_variable_list_node *requ_list;
assert ((key_ranges->range >= GE_LE && key_ranges->range <= INF_LT) || (key_ranges->range == EQ_NA));
assert (!(key_ranges->key1 == NULL && key_ranges->key2 == NULL));
if (iscan_id->bt_attrs_prefix_length && iscan_id->bt_num_attrs == 1)
{
curr_key_prefix_length = iscan_id->bt_attrs_prefix_length[0];
}
/* TO_DO : fix to move this to XASL generator */
if (key_ranges->key1)
{
if (key_ranges->key1->type == TYPE_FUNC && key_ranges->key1->value.funcp->ftype == F_MIDXKEY)
{
for (requ_list = key_ranges->key1->value.funcp->operand, count = 0; requ_list; requ_list = requ_list->next)
{
count++;
}
}
else
{
count = 1;
}
key_val_range->num_index_term = count;
}
if (key_ranges->key2)
{
if (key_ranges->key2->type == TYPE_FUNC && key_ranges->key2->value.funcp->ftype == F_MIDXKEY)
{
for (requ_list = key_ranges->key2->value.funcp->operand, count = 0; requ_list; requ_list = requ_list->next)
{
count++;
}
}
else
{
assert_release (key_val_range->num_index_term <= 1);
count = 1;
}
key_val_range->num_index_term = MAX (key_val_range->num_index_term, count);
}
if (key_ranges->key1)
{
if (key_ranges->key1->type == TYPE_FUNC && key_ranges->key1->value.funcp->ftype == F_MIDXKEY)
{
if (key_val_range->range == GT_INF || key_val_range->range == GT_LE || key_val_range->range == GT_LT)
{
key_minmax = BTREE_COERCE_KEY_WITH_MAX_VALUE;
}
else
{
key_minmax = BTREE_COERCE_KEY_WITH_MIN_VALUE;
}
ret =
scan_dbvals_to_midxkey (thread_p, &key_val_range->key1, &indexable, btree_domainp,
key_val_range->num_index_term, key_ranges->key1, vd, key_minmax, iscan_id->iss.use,
&(iscan_id->prebuilt_midxkey_domains[key_range_idx]));
}
else
{
ret = fetch_copy_dbval (thread_p, key_ranges->key1, vd, NULL, NULL, NULL, &key_val_range->key1);
db_type = DB_VALUE_DOMAIN_TYPE (&key_val_range->key1);
if (ret == NO_ERROR && curr_key_prefix_length > 0)
{
if (TP_IS_CHAR_TYPE (db_type) || TP_IS_BIT_TYPE (db_type))
{
key_len = db_get_string_length (&key_val_range->key1);
if (key_len > curr_key_prefix_length)
{
ret = db_string_truncate (&key_val_range->key1, curr_key_prefix_length);
key_val_range->is_truncated = true;
}
}
}
assert (DB_VALUE_TYPE (&key_val_range->key1) != DB_TYPE_MIDXKEY);
}
if (ret != NO_ERROR || indexable == false)
{
key_val_range->range = NA_NA;
return ret;
}
}
if (key_ranges->key2)
{
if (key_ranges->key2->type == TYPE_FUNC && key_ranges->key2->value.funcp->ftype == F_MIDXKEY)
{
if (key_val_range->range == INF_LT || key_val_range->range == GE_LT || key_val_range->range == GT_LT)
{
key_minmax = BTREE_COERCE_KEY_WITH_MIN_VALUE;
}
else
{
key_minmax = BTREE_COERCE_KEY_WITH_MAX_VALUE;
}
ret =
scan_dbvals_to_midxkey (thread_p, &key_val_range->key2, &indexable, btree_domainp,
key_val_range->num_index_term, key_ranges->key2, vd, key_minmax, iscan_id->iss.use,
&(iscan_id->prebuilt_midxkey_domains[key_range_idx]));
}
else
{
ret = fetch_copy_dbval (thread_p, key_ranges->key2, vd, NULL, NULL, NULL, &key_val_range->key2);
db_type = DB_VALUE_DOMAIN_TYPE (&key_val_range->key2);
if (ret == NO_ERROR && curr_key_prefix_length > 0)
{
if (TP_IS_CHAR_TYPE (db_type) || TP_IS_BIT_TYPE (db_type))
{
key_len = db_get_string_length (&key_val_range->key2);
if (key_len > curr_key_prefix_length)
{
ret = db_string_truncate (&key_val_range->key2, curr_key_prefix_length);
key_val_range->is_truncated = true;
}
}
}
assert (DB_VALUE_TYPE (&key_val_range->key2) != DB_TYPE_MIDXKEY);
}
if (ret != NO_ERROR || indexable == false)
{
key_val_range->range = NA_NA;
return ret;
}
}
else
{
if (key_ranges->key1 == NULL)
{
/* impossible case */
assert (false);
key_val_range->range = NA_NA;
return ER_FAILED;
}
if ((iscan_id->indx_info->range_type == R_KEY || iscan_id->indx_info->range_type == R_KEYLIST)
&& key_ranges->key1->type == TYPE_FUNC && key_ranges->key1->value.funcp->ftype == F_MIDXKEY)
{
assert (key_val_range->range == EQ_NA);
ret = pr_clone_value (&key_val_range->key1, &key_val_range->key2);
if (ret != NO_ERROR)
{
key_val_range->range = NA_NA;
return ret;
}
/* Set minmax type opposite to key1 */
if (key_val_range->key2.data.midxkey.min_max_val.type == MIN_COLUMN)
{
key_val_range->key2.data.midxkey.min_max_val.type = MAX_COLUMN;
}
else
{
key_val_range->key2.data.midxkey.min_max_val.type = MIN_COLUMN;
}
}
else
{
ret = pr_clone_value (&key_val_range->key1, &key_val_range->key2);
if (ret != NO_ERROR)
{
key_val_range->range = NA_NA;
return ret;
}
}
}
if (key_val_range->range == EQ_NA)
{
key_val_range->range = GE_LE;
}
switch (iscan_id->indx_info->range_type)
{
case R_KEY:
case R_KEYLIST:
/* When key received as NULL, currently this is assumed an UNBOUND value and no object value in the index is
* equal to NULL value in the index scan context. They can be equal to NULL only in the "is NULL" context. */
/* to fix multi-column index NULL problem */
if (DB_IS_NULL (&key_val_range->key1))
{
key_val_range->range = NA_NA;
return ret;
}
break;
case R_RANGE:
case R_RANGELIST:
/* When key received as NULL, currently this is assumed an UNBOUND value and no object value in the index is
* equal to NULL value in the index scan context. They can be equal to NULL only in the "is NULL" context. */
if (key_val_range->range >= GE_LE && key_val_range->range <= GT_LT)
{
/* to fix multi-column index NULL problem */
if (DB_IS_NULL (&key_val_range->key1) || DB_IS_NULL (&key_val_range->key2))
{
key_val_range->range = NA_NA;
return ret;
}
else
{
int c = DB_UNK;
c = scan_key_compare (&key_val_range->key1, &key_val_range->key2, key_val_range->num_index_term);
if (c == DB_UNK)
{
/* impossible case */
assert_release (false);
key_val_range->range = NA_NA;
return ER_FAILED;
}
else if (c > 0)
{
key_val_range->range = NA_NA;
return ret;
}
}
}
else if (key_val_range->range >= GE_INF && key_val_range->range <= GT_INF)
{
/* to fix multi-column index NULL problem */
if (DB_IS_NULL (&key_val_range->key1))
{
key_val_range->range = NA_NA;
return ret;
}
}
else if (key_val_range->range >= INF_LE && key_val_range->range <= INF_LT)
{
/* to fix multi-column index NULL problem */
if (DB_IS_NULL (&key_val_range->key2))
{
key_val_range->range = NA_NA;
return ret;
}
}
break;
default:
assert_release (false);
break; /* impossible case */
}
return ret;
}
/*
* scan_get_index_oidset () - Fetch the next group of set of object identifiers
* from the index associated with the scan identifier.
* return: NO_ERROR, or ER_code
* s_id(in): Scan identifier
*
* Note: If you feel the need
*/
static int
scan_get_index_oidset (THREAD_ENTRY * thread_p, SCAN_ID * s_id, DB_BIGINT * key_limit_upper,
DB_BIGINT * key_limit_lower)
{
INDX_SCAN_ID *iscan_id;
FILTER_INFO key_filter;
indx_info *indx_infop;
BTREE_SCAN *bts;
int key_cnt, i;
KEY_VAL_RANGE *key_vals;
KEY_RANGE *key_ranges;
RANGE range, saved_range;
int ret = NO_ERROR;
int curr_key_prefix_length = 0;
/* pointer to INDX_SCAN_ID structure */
iscan_id = &s_id->s.isid;
/* pointer to indx_info in INDX_SCAN_ID structure */
indx_infop = iscan_id->indx_info;
/* pointer to index scan info. structure */
bts = &iscan_id->bt_scan;
/* number of keys */
if (iscan_id->curr_keyno == -1) /* very first time */
{
key_cnt = indx_infop->key_info.key_cnt;
}
else
{
key_cnt = iscan_id->key_cnt;
}
/* key values */
key_vals = iscan_id->key_vals;
/* key ranges */
key_ranges = indx_infop->key_info.key_ranges;
if (key_cnt < 1 || !key_vals || !key_ranges)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
return ER_FAILED;
}
if (iscan_id->bt_attrs_prefix_length && iscan_id->bt_num_attrs == 1)
{
curr_key_prefix_length = iscan_id->bt_attrs_prefix_length[0];
}
/* if it is the first time of this scan */
if (iscan_id->curr_keyno == -1 && indx_infop->key_info.key_cnt == key_cnt)
{
/* make DB_VALUE key values from KEY_VALS key ranges */
for (i = 0; i < key_cnt; i++)
{
/* initialize DB_VALUE first for error case */
key_vals[i].range = NA_NA;
db_make_null (&key_vals[i].key1);
db_make_null (&key_vals[i].key2);
key_vals[i].is_truncated = false;
key_vals[i].num_index_term = 0;
key_vals[i].range = key_ranges[i].range;
if (key_vals[i].range == INF_INF)
{
continue;
}
ret =
scan_regu_key_to_index_key (thread_p, &key_ranges[i], &key_vals[i], iscan_id, bts->btid_int.key_type,
s_id->vd, i);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
assert_release (key_vals[i].num_index_term > 0);
}
/* eliminating duplicated keys and merging ranges are required even though the query optimizer does them because
* the search keys or ranges could be unbound values at optimization step such as join attribute */
if (indx_infop->range_type == R_KEYLIST)
{
/* eliminate duplicated keys in the search key list */
key_cnt = iscan_id->key_cnt = check_key_vals (key_vals, key_cnt, eliminate_duplicated_keys);
}
else if (indx_infop->range_type == R_RANGELIST)
{
/* merge search key ranges */
key_cnt = iscan_id->key_cnt = check_key_vals (key_vals, key_cnt, merge_key_ranges);
}
/* if is order by skip and first column is descending, the order will be reversed so reverse the key ranges to be
* desc. */
if ((indx_infop->range_type == R_KEYLIST || indx_infop->range_type == R_RANGELIST)
&& ((indx_infop->orderby_desc && indx_infop->orderby_skip)
|| (indx_infop->groupby_desc && indx_infop->groupby_skip)))
{
/* in both cases we should reverse the key lists if we have a reverse order by or group by which is skipped */
check_key_vals (key_vals, key_cnt, reverse_key_list);
}
if (key_cnt < 0)
{
goto exit_on_error;
}
iscan_id->curr_keyno = 0;
}
/*
* init vars to execute B+tree key range search
*/
ret = NO_ERROR;
key_filter =
{
&iscan_id->key_pred,
&iscan_id->key_attrs,
NULL,
NULL,
s_id->val_list,
s_id->vd,
&iscan_id->cls_oid, iscan_id->bt_attr_ids, &iscan_id->num_vstr, iscan_id->vstr_ids, iscan_id->bt_num_attrs, -1};
iscan_id->oids_count = 0;
key_filter.func_idx_col_id = iscan_id->indx_info->func_idx_col_id;
if (iscan_id->multi_range_opt.use && iscan_id->multi_range_opt.cnt > 0)
{
/* reset any previous results for multiple range optimization */
int i;
for (i = 0; i < iscan_id->multi_range_opt.cnt; i++)
{
if (iscan_id->multi_range_opt.top_n_items[i] != NULL)
{
pr_clear_value (&(iscan_id->multi_range_opt.top_n_items[i]->index_value));
db_private_free_and_init (thread_p, iscan_id->multi_range_opt.top_n_items[i]);
}
}
iscan_id->multi_range_opt.cnt = 0;
}
/* if the end of this scan */
if (iscan_id->curr_keyno > key_cnt)
{
return NO_ERROR;
}
else
{
/* Clear output val list to avoid memory leak. */
regu_variable_list_node *p;
for (p = iscan_id->indx_cov.regu_val_list; p; p = p->next)
{
pr_clear_value (p->value.vfetch_to);
}
}
switch (indx_infop->range_type)
{
case R_KEY:
/* key value search */
/* check prerequisite condition */
range = key_vals[0].range;
if (range == NA_NA)
{
/* skip this key value */
iscan_id->curr_keyno++;
break;
}
if (key_cnt != 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
ret =
btree_prepare_bts (thread_p, bts, &indx_infop->btid, iscan_id, &key_vals[0], &key_filter,
&iscan_id->cls_oid, key_limit_upper, key_limit_lower, true, NULL);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
ret = btree_range_scan (thread_p, bts, btree_range_scan_select_visible_oids);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
iscan_id->oids_count = bts->n_oids_read_last_iteration;
assert (iscan_id->oids_count >= 0);
/* We only want to advance the key ptr if we've exhausted the current crop of oids on the current key. */
if (BTREE_END_OF_SCAN (bts))
{
iscan_id->curr_keyno++;
}
if (iscan_id->multi_range_opt.use)
{
/* with multiple range optimization, we store the only the top N OIDS or index keys: the only valid exit
* condition from 'btree_range_search' is when the index scan has reached the end for this key */
assert (BTREE_END_OF_SCAN (bts));
}
break;
case R_RANGE:
/* range search */
/* check prerequisite condition */
saved_range = range = key_vals[0].range;
if (range == NA_NA)
{
/* skip this key value */
iscan_id->curr_keyno++;
break;
}
if (key_cnt != 1 || range < GE_LE || range > INF_INF)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
if (range >= GE_INF && range <= GT_INF)
{
pr_clear_value (&key_vals[0].key2);
PRIM_SET_NULL (&key_vals[0].key2);
}
if (range >= INF_LE && range <= INF_LT)
{
pr_clear_value (&key_vals[0].key1);
PRIM_SET_NULL (&key_vals[0].key1);
}
if (key_vals[0].is_truncated == true)
{ /* specially, key value search */
range = GE_LE;
}
if (range == INF_INF)
{
/* if we reached the key count limit, break */
if (iscan_id->curr_keyno >= key_cnt)
{
iscan_id->curr_keyno++;
break;
}
pr_clear_value (&key_vals[0].key1);
pr_clear_value (&key_vals[0].key2);
PRIM_SET_NULL (&key_vals[0].key1);
PRIM_SET_NULL (&key_vals[0].key2);
assert_release (key_vals[0].num_index_term == 0);
}
key_vals[0].range = range;
ret =
btree_prepare_bts (thread_p, bts, &indx_infop->btid, iscan_id, &key_vals[0], &key_filter,
&iscan_id->cls_oid, key_limit_upper, key_limit_lower, true, NULL);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
ret = btree_range_scan (thread_p, bts, btree_range_scan_select_visible_oids);
key_vals[0].range = saved_range;
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
iscan_id->oids_count = bts->n_oids_read_last_iteration;
assert (iscan_id->oids_count >= 0);
/* We only want to advance the key ptr if we've exhausted the current crop of oids on the current key. */
if (BTREE_END_OF_SCAN (bts))
{
iscan_id->curr_keyno++;
}
if (iscan_id->multi_range_opt.use)
{
/* with multiple range optimization, we store the only the top N OIDS or index keys: the only valid exit
* condition from 'btree_range_search' is when the index scan has reached the end for this key */
assert (BTREE_END_OF_SCAN (bts));
}
break;
case R_KEYLIST:
/* multiple key value search */
/* for each key value */
while (iscan_id->curr_keyno < key_cnt)
{
/* check prerequisite condition */
range = key_vals[iscan_id->curr_keyno].range;
if (range == NA_NA)
{
/* skip this key value and continue to the next */
iscan_id->curr_keyno++;
if (key_limit_upper && !key_limit_lower && indx_infop->key_info.key_limit_reset)
{
if (scan_init_index_key_limit (thread_p, iscan_id, &indx_infop->key_info, s_id->vd) != NO_ERROR)
{
goto exit_on_error;
}
*key_limit_upper = iscan_id->key_limit_upper;
}
continue;
}
ret =
btree_prepare_bts (thread_p, bts, &indx_infop->btid, iscan_id, &key_vals[iscan_id->curr_keyno],
&key_filter, &iscan_id->cls_oid, key_limit_upper, key_limit_lower, true, NULL);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
ret = btree_range_scan (thread_p, bts, btree_range_scan_select_visible_oids);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
iscan_id->oids_count = bts->n_oids_read_last_iteration;
assert (iscan_id->oids_count >= 0);
/* We only want to advance the key ptr if we've exhausted the current crop of oids on the current key. */
if (BTREE_END_OF_SCAN (bts))
{
iscan_id->curr_keyno++;
/* reset upper key limit, if flag is set */
if (key_limit_upper && !key_limit_lower && indx_infop->key_info.key_limit_reset)
{
if (scan_init_index_key_limit (thread_p, iscan_id, &indx_infop->key_info, s_id->vd) != NO_ERROR)
{
goto exit_on_error;
}
*key_limit_upper = iscan_id->key_limit_upper;
}
}
if (iscan_id->multi_range_opt.use)
{
/* with multiple range optimization, we store the only the top N OIDS or index keys: the only valid exit
* condition from 'btree_range_search' is when the index scan has reached the end for this key */
assert (BTREE_END_OF_SCAN (bts));
/* continue loop : exhaust all keys in one shot when in multiple range search optimization mode */
continue;
}
if (iscan_id->oids_count > 0)
{
/* we've got some result */
break;
}
}
break;
case R_RANGELIST:
/* multiple range search */
/* for each key value */
while (iscan_id->curr_keyno < key_cnt)
{
/* check prerequisite condition */
saved_range = range = key_vals[iscan_id->curr_keyno].range;
if (range == NA_NA)
{
/* skip this key value and continue to the next */
iscan_id->curr_keyno++;
if (key_limit_upper && !key_limit_lower && indx_infop->key_info.key_limit_reset)
{
if (scan_init_index_key_limit (thread_p, iscan_id, &indx_infop->key_info, s_id->vd) != NO_ERROR)
{
goto exit_on_error;
}
*key_limit_upper = iscan_id->key_limit_upper;
}
continue;
}
if (range < GE_LE || range > INF_INF)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
if (range >= GE_INF && range <= GT_INF)
{
pr_clear_value (&key_vals[iscan_id->curr_keyno].key2);
PRIM_SET_NULL (&key_vals[iscan_id->curr_keyno].key2);
}
if (key_vals[iscan_id->curr_keyno].is_truncated == true)
{ /* specially, key value search */
range = GE_LE;
}
if (range >= INF_LE && range <= INF_LT)
{
pr_clear_value (&key_vals[iscan_id->curr_keyno].key1);
PRIM_SET_NULL (&key_vals[iscan_id->curr_keyno].key1);
}
if (range == INF_INF)
{
if (key_cnt != 1)
{
assert_release (0);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
/* if we reached the key count limit, break */
if (iscan_id->curr_keyno >= key_cnt)
{
iscan_id->curr_keyno++;
break;
}
pr_clear_value (&key_vals[0].key1);
pr_clear_value (&key_vals[0].key2);
PRIM_SET_NULL (&key_vals[0].key1);
PRIM_SET_NULL (&key_vals[0].key2);
}
key_vals[iscan_id->curr_keyno].range = range;
ret =
btree_prepare_bts (thread_p, bts, &indx_infop->btid, iscan_id, &key_vals[iscan_id->curr_keyno],
&key_filter, &iscan_id->cls_oid, key_limit_upper, key_limit_lower, true, NULL);
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
ret = btree_range_scan (thread_p, bts, btree_range_scan_select_visible_oids);
key_vals[iscan_id->curr_keyno].range = saved_range;
if (ret != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
goto exit_on_error;
}
iscan_id->oids_count = bts->n_oids_read_last_iteration;
assert (iscan_id->oids_count >= 0);
/* We only want to advance the key ptr if we've exhausted the current crop of oids on the current key. */
if (BTREE_END_OF_SCAN (bts))
{
iscan_id->curr_keyno++;
/* reset upper key limit, if flag is set */
if (key_limit_upper && !key_limit_lower && indx_infop->key_info.key_limit_reset)
{
if (scan_init_index_key_limit (thread_p, iscan_id, &indx_infop->key_info, s_id->vd) != NO_ERROR)
{
goto exit_on_error;
}
*key_limit_upper = iscan_id->key_limit_upper;
}
}
if (iscan_id->multi_range_opt.use)
{
/* with multiple range optimization, we store the only the top N OIDS or index keys: the only valid exit
* condition from 'btree_range_search' is when the index scan has reached the end for this key */
assert (BTREE_END_OF_SCAN (bts));
/* continue loop : exhaust all keys in one shot when in multiple range search optimization mode */
continue;
}
if (iscan_id->oids_count > 0)
{
/* we've got some result */
break;
}
}
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
/* When covering index is used, 'index_scan_in_oid_order' parameter is ignored. */
if (iscan_id->oid_list != NULL && iscan_id->oid_list->oidp != NULL && iscan_id->oids_count > 1
&& iscan_id->iscan_oid_order == true && iscan_id->need_count_only == false)
{
qsort (iscan_id->oid_list->oidp, iscan_id->oids_count, sizeof (OID), oid_compare);
}
end:
if (key_limit_upper != NULL && *key_limit_upper == 0)
{
/* End scan here! */
iscan_id->curr_keyno = key_cnt;
}
/* if the end of this scan */
if (iscan_id->curr_keyno == key_cnt)
{
for (i = 0; i < key_cnt; i++)
{
pr_clear_value (&key_vals[i].key1);
pr_clear_value (&key_vals[i].key2);
}
iscan_id->curr_keyno++; /* to prevent duplicate frees */
}
if (thread_is_on_trace (thread_p))
{
s_id->scan_stats.read_keys += iscan_id->bt_scan.read_keys;
iscan_id->bt_scan.read_keys = 0;
s_id->scan_stats.qualified_keys += iscan_id->bt_scan.qualified_keys;
iscan_id->bt_scan.qualified_keys = 0;
}
return ret;
exit_on_error:
iscan_id->curr_keyno = key_cnt; /* set as end of this scan */
ret = (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
goto end;
}
/*
*
* SCAN MANAGEMENT ROUTINES
*
*/
/*
* scan_init_scan_id () -
* return:
* scan_id(out): Scan identifier
* mvcc_select_lock_needed(in):
* scan_op_type(in): scan operation type
* fixed(in):
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
*
* Note: If you feel the need
*/
static void
scan_init_scan_id (SCAN_ID * scan_id, bool mvcc_select_lock_needed, SCAN_OPERATION_TYPE scan_op_type, int fixed,
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd)
{
scan_id->status = S_OPENED;
scan_id->position = S_BEFORE;
scan_id->direction = S_FORWARD;
scan_id->mvcc_select_lock_needed = mvcc_select_lock_needed;
scan_id->scan_op_type = scan_op_type;
scan_id->fixed = fixed;
scan_id->grouped = grouped; /* is it grouped or single scan? */
scan_id->qualified_block = false;
scan_id->single_fetch = single_fetch;
scan_id->single_fetched = false;
scan_id->null_fetched = false;
scan_id->qualification = QPROC_QUALIFIED;
/* join term */
scan_id->join_dbval = join_dbval;
/* value list and descriptor */
scan_id->val_list = val_list; /* points to the XASL tree */
scan_id->vd = vd; /* set value descriptor pointer */
scan_id->scan_immediately_stop = false;
}
/*
* scan_open_heap_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* mvcc_select_lock_needed(in):
* scan_op_type(in): scan operation type
* fixed(in):
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* cls_oid(in):
* hfid(in):
* regu_list_pred(in):
* pr(in):
* regu_list_rest(in):
* num_attrs_pred(in):
* attrids_pred(in):
* cache_pred(in):
* num_attrs_rest(in):
* attrids_rest(in):
* cache_rest(in):
* cache_recordinfo(in):
* regu_list_recordinfo(in):
*
* Note: If you feel the need
*/
int
scan_open_heap_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
bool mvcc_select_lock_needed, SCAN_OPERATION_TYPE scan_op_type, int fixed, int grouped,
QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list, VAL_DESCR * vd,
/* fields of HEAP_SCAN_ID */
OID * cls_oid, HFID * hfid, regu_variable_list_node * regu_list_pred, PRED_EXPR * pr,
regu_variable_list_node * regu_list_rest, int num_attrs_pred, ATTR_ID * attrids_pred,
HEAP_CACHE_ATTRINFO * cache_pred, int num_attrs_rest, ATTR_ID * attrids_rest,
HEAP_CACHE_ATTRINFO * cache_rest, SCAN_TYPE scan_type, DB_VALUE ** cache_recordinfo,
regu_variable_list_node * regu_list_recordinfo, bool is_partition_table)
{
HEAP_SCAN_ID *hsidp;
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is HEAP SCAN or HEAP SCAN RECORD INFO */
assert (scan_type == S_HEAP_SCAN || scan_type == S_HEAP_SCAN_RECORD_INFO || scan_type == S_HEAP_SAMPLING_SCAN);
scan_id->type = scan_type;
/* initialize SCAN_ID structure */
scan_init_scan_id (scan_id, mvcc_select_lock_needed, scan_op_type, fixed, grouped, single_fetch, join_dbval, val_list,
vd);
/* initialize HEAP_SCAN_ID structure */
hsidp = &scan_id->s.hsid;
/* class object OID */
COPY_OID (&hsidp->cls_oid, cls_oid);
/* heap file identifier */
hsidp->hfid = *hfid; /* bitwise copy */
/* OID within the heap */
UT_CAST_TO_NULL_HEAP_OID (&hsidp->hfid, &hsidp->curr_oid);
/* scan predicates */
scan_init_scan_pred (&hsidp->scan_pred, regu_list_pred, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
/* attribute information from predicates */
scan_init_scan_attrs (&hsidp->pred_attrs, num_attrs_pred, attrids_pred, cache_pred);
/* regulator variable list for other than predicates */
hsidp->rest_regu_list = regu_list_rest;
/* attribute information from other than predicates */
scan_init_scan_attrs (&hsidp->rest_attrs, num_attrs_rest, attrids_rest, cache_rest);
/* flags */
/* do not reset hsidp->caches_inited here */
hsidp->scancache_inited = false;
hsidp->scanrange_inited = false;
hsidp->cache_recordinfo = cache_recordinfo;
hsidp->recordinfo_regu_list = regu_list_recordinfo;
/* for scampling statistics. */
if (scan_type == S_HEAP_SAMPLING_SCAN && !is_partition_table)
{
int total_pages = 0;
if (file_get_num_total_user_pages (thread_p, cls_oid, &total_pages) != NO_ERROR)
{
return ER_FAILED;
}
/* sampling_weight = total_page / sampling_page */
hsidp->sampling.weight = MAX ((total_pages / NUMBER_OF_SAMPLING_PAGES), 1);
}
return NO_ERROR;
}
/*
* scan_open_heap_page_scan () - Opens a page by page heap scan.
*
* return : Error code.
* thread_p (in) :
* scan_id (in) :
* val_list (in) :
* vd (in) :
* cls_oid (in) :
* hfid (in) :
* pr (in) :
* scan_type (in) :
* cache_page_info (in) :
* regu_list_page_info (in) :
*/
int
scan_open_heap_page_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
val_list_node * val_list, VAL_DESCR * vd,
/* fields of HEAP_SCAN_ID */
OID * cls_oid, HFID * hfid, PRED_EXPR * pr, SCAN_TYPE scan_type, DB_VALUE ** cache_page_info,
regu_variable_list_node * regu_list_page_info)
{
HEAP_PAGE_SCAN_ID *hpsidp = NULL;
DB_TYPE single_node_type = DB_TYPE_NULL;
scan_id->type = scan_type;
scan_init_scan_id (scan_id, true, S_SELECT, true, false, QPROC_NO_SINGLE_INNER, NULL, val_list, vd);
hpsidp = &scan_id->s.hpsid;
COPY_OID (&hpsidp->cls_oid, cls_oid);
hpsidp->hfid = *hfid;
hpsidp->cache_page_info = cache_page_info;
hpsidp->page_info_regu_list = regu_list_page_info;
scan_init_scan_pred (&hpsidp->scan_pred, NULL, pr, (pr == NULL) ? NULL : eval_fnc (thread_p, pr, &single_node_type));
VPID_SET_NULL (&hpsidp->curr_vpid);
return NO_ERROR;
}
/*
* scan_open_class_attr_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* cls_oid(in):
* hfid(in):
* regu_list_pred(in):
* pr(in):
* regu_list_rest(in):
* num_attrs_pred(in):
* attrids_pred(in):
* cache_pred(in):
* num_attrs_rest(in):
* attrids_rest(in):
* cache_rest(in):
*
* Note: If you feel the need
*/
int
scan_open_class_attr_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval,
val_list_node * val_list, VAL_DESCR * vd,
/* fields of HEAP_SCAN_ID */
OID * cls_oid, HFID * hfid, regu_variable_list_node * regu_list_pred, PRED_EXPR * pr,
regu_variable_list_node * regu_list_rest, int num_attrs_pred, ATTR_ID * attrids_pred,
HEAP_CACHE_ATTRINFO * cache_pred, int num_attrs_rest, ATTR_ID * attrids_rest,
HEAP_CACHE_ATTRINFO * cache_rest)
{
HEAP_SCAN_ID *hsidp;
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is CLASS ATTR SCAN */
scan_id->type = S_CLASS_ATTR_SCAN;
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
/* initialize HEAP_SCAN_ID structure */
hsidp = &scan_id->s.hsid;
/* class object OID */
COPY_OID (&hsidp->cls_oid, cls_oid);
/* heap file identifier */
hsidp->hfid = *hfid; /* bitwise copy */
/* OID within the heap */
UT_CAST_TO_NULL_HEAP_OID (&hsidp->hfid, &hsidp->curr_oid);
/* scan predicates */
scan_init_scan_pred (&hsidp->scan_pred, regu_list_pred, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
/* attribute information from predicates */
scan_init_scan_attrs (&hsidp->pred_attrs, num_attrs_pred, attrids_pred, cache_pred);
/* regulator vairable list for other than predicates */
hsidp->rest_regu_list = regu_list_rest;
/* attribute information from other than predicates */
scan_init_scan_attrs (&hsidp->rest_attrs, num_attrs_rest, attrids_rest, cache_rest);
/* flags */
/* do not reset hsidp->caches_inited here */
hsidp->scancache_inited = false;
hsidp->scanrange_inited = false;
return NO_ERROR;
}
/*
* scan_open_index_scan () -
* return: NO_ERROR, or ER_code
* scan_id(out): Scan identifier
* mvcc_select_lock_needed(in):
* fixed(in):
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* indx_info(in):
* cls_oid(in):
* hfid(in):
* regu_list_key(in):
* pr_key(in):
* regu_list_pred(in):
* pr(in):
* regu_list_rest(in):
* pr_range(in):
* regu_list_range(in):
* num_attrs_key(in):
* attrids_key(in):
* num_attrs_pred(in):
* attrids_pred(in):
* cache_pred(in):
* num_attrs_rest(in):
* attrids_rest(in):
* cache_rest(in):
* num_attrs_range(in):
* attrids_range(in):
* cache_range(in):
* iscan_oid_order(in):
* query_id(in):
*
* Note: If you feel the need
*/
int
scan_open_index_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
bool mvcc_select_lock_needed, SCAN_OPERATION_TYPE scan_op_type, int fixed, int grouped,
QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list, VAL_DESCR * vd,
/* fields of INDX_SCAN_ID */
indx_info * indx_info, OID * cls_oid, HFID * hfid, regu_variable_list_node * regu_list_key,
PRED_EXPR * pr_key, regu_variable_list_node * regu_list_pred, PRED_EXPR * pr,
regu_variable_list_node * regu_list_rest, PRED_EXPR * pr_range,
regu_variable_list_node * regu_list_range, valptr_list_node * output_val_list,
regu_variable_list_node * regu_val_list, int num_attrs_key, ATTR_ID * attrids_key,
HEAP_CACHE_ATTRINFO * cache_key, int num_attrs_pred, ATTR_ID * attrids_pred,
HEAP_CACHE_ATTRINFO * cache_pred, int num_attrs_rest, ATTR_ID * attrids_rest,
HEAP_CACHE_ATTRINFO * cache_rest, int num_attrs_range, ATTR_ID * attrids_range,
HEAP_CACHE_ATTRINFO * cache_range, bool iscan_oid_order, QUERY_ID query_id,
bool min_max_optimzied_scan)
{
int ret = NO_ERROR;
INDX_SCAN_ID *isidp;
DB_TYPE single_node_type = DB_TYPE_NULL;
BTID *btid;
VPID Root_vpid;
PAGE_PTR Root;
BTREE_ROOT_HEADER *root_header = NULL;
BTREE_SCAN *BTS;
int coverage_enabled;
int func_index_col_id;
static bool oracle_style_empty_string = prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING);
/* scan type is INDEX SCAN */
scan_id->type = S_INDX_SCAN;
/* initialize SCAN_ID structure */
scan_init_scan_id (scan_id, mvcc_select_lock_needed, scan_op_type, fixed, grouped, single_fetch, join_dbval, val_list,
vd);
/* read Root page header info */
btid = &indx_info->btid;
Root_vpid.pageid = btid->root_pageid;
Root_vpid.volid = btid->vfid.volid;
Root = pgbuf_fix (thread_p, &Root_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (Root == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, Root, PAGE_BTREE);
#endif /* !NDEBUG */
root_header = btree_get_root_header (thread_p, Root);
if (root_header == NULL)
{
pgbuf_unfix_and_init (thread_p, Root);
return ER_FAILED;
}
/* initialize INDEX_SCAN_ID structure */
isidp = &scan_id->s.isid;
/* index information */
isidp->indx_info = indx_info;
/* init allocated fields */
isidp->bt_num_attrs = 0;
isidp->bt_attr_ids = NULL;
isidp->vstr_ids = NULL;
isidp->oid_list = NULL;
isidp->curr_oidp = NULL;
isidp->copy_buf = NULL;
isidp->copy_buf_len = 0;
isidp->key_vals = NULL;
isidp->indx_cov.type_list = NULL;
isidp->indx_cov.list_id = indx_info->cov_list_id;
isidp->indx_cov.tplrec = NULL;
isidp->indx_cov.lsid = NULL;
/* index scan info */
BTS = &isidp->bt_scan;
BTREE_INIT_SCAN (BTS);
/* construct BTID_INT structure */
BTS->btid_int.sys_btid = btid;
if (btree_glean_root_header_info
(thread_p, root_header, &BTS->btid_int, (BTS->btid_int.key_type == NULL)) != NO_ERROR)
{
pgbuf_unfix_and_init (thread_p, Root);
goto exit_on_error;
}
BTS->is_btid_int_valid = true;
pgbuf_unfix_and_init (thread_p, Root);
/* initialize key limits */
if (scan_init_index_key_limit (thread_p, isidp, &indx_info->key_info, vd) != NO_ERROR)
{
goto exit_on_error;
}
if (min_max_optimzied_scan && SCAN_IS_INDEX_COVERED (isidp))
{
isidp->key_limit_upper = 1;
}
/* attribute information of the index key */
if (heap_get_indexinfo_of_btid (thread_p, cls_oid, &indx_info->btid, &isidp->bt_type, &isidp->bt_num_attrs,
&isidp->bt_attr_ids, &isidp->bt_attrs_prefix_length, NULL,
&func_index_col_id) != NO_ERROR)
{
goto exit_on_error;
}
/* attribute information of the variable string attrs in index key */
isidp->num_vstr = 0;
isidp->vstr_ids = NULL;
if (oracle_style_empty_string)
{
isidp->num_vstr = isidp->bt_num_attrs; /* init to maximum */
isidp->vstr_ids = (ATTR_ID *) db_private_alloc (thread_p, isidp->num_vstr * sizeof (ATTR_ID));
if (isidp->vstr_ids == NULL)
{
goto exit_on_error;
}
}
/* indicator whether covering index is used or not */
coverage_enabled = (indx_info->coverage != 0) && (scan_op_type == S_SELECT) && !mvcc_select_lock_needed;
scan_id->scan_stats.loose_index_scan = indx_info->ils_prefix_len > 0;
/* is a single range? */
isidp->one_range = false;
/* initial values */
isidp->curr_keyno = -1;
isidp->curr_oidno = -1;
/* OID buffer */
if (coverage_enabled)
{
/* Covering index do not use an oid buffer. */
scan_id->scan_stats.covered_index = true;
}
else
{
ret = scan_alloc_iscan_oid_buf_list (&isidp->oid_list);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
/* Safe guard. */
assert (isidp->oid_list->oidp != NULL);
assert (isidp->oid_list->capacity > 0);
assert (isidp->oid_list->next_list == NULL);
/* Initialize OID list. */
isidp->oid_list->max_oid_cnt = ISCAN_OID_BUFFER_COUNT;
isidp->oid_list->oid_cnt = 0;
/* Initialize current OID pointer to start of buffer. */
isidp->curr_oidp = isidp->oid_list->oidp;
/* Safe guard */
/* OID count limit should not exceed buffer capacity. */
assert (isidp->oid_list->max_oid_cnt <= isidp->oid_list->capacity);
}
/* class object OID */
COPY_OID (&isidp->cls_oid, cls_oid);
/* heap file identifier */
isidp->hfid = *hfid; /* bitwise copy */
/* key filter */
scan_init_scan_pred (&isidp->key_pred, regu_list_key, pr_key,
((pr_key) ? eval_fnc (thread_p, pr_key, &single_node_type) : NULL));
/* attribute information from key filter */
scan_init_scan_attrs (&isidp->key_attrs, num_attrs_key, attrids_key, cache_key);
/* scan predicates */
scan_init_scan_pred (&isidp->scan_pred, regu_list_pred, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
/* attribute information from predicates */
scan_init_scan_attrs (&isidp->pred_attrs, num_attrs_pred, attrids_pred, cache_pred);
/* scan range filter */
scan_init_scan_pred (&isidp->range_pred, regu_list_range, pr_range,
((pr_range) ? eval_fnc (thread_p, pr_range, &single_node_type) : NULL));
/* attribute information from range filter */
scan_init_scan_attrs (&isidp->range_attrs, num_attrs_range, attrids_range, cache_range);
/* regulator variable list for other than predicates */
isidp->rest_regu_list = regu_list_rest;
/* attribute information from other than predicates */
scan_init_scan_attrs (&isidp->rest_attrs, num_attrs_rest, attrids_rest, cache_rest);
/* flags */
/* do not reset hsidp->caches_inited here */
isidp->scancache_inited = false;
/* convert key values in the form of REGU_VARIABLE to the form of DB_VALUE */
isidp->key_cnt = indx_info->key_info.key_cnt;
if (isidp->key_cnt > 0)
{
bool need_copy_buf;
isidp->key_vals = indx_info->key_info.key_vals;
if (isidp->key_vals == NULL)
{
goto exit_on_error;
}
need_copy_buf = false; /* init */
if (BTS->btid_int.key_type == NULL || BTS->btid_int.key_type->type == NULL)
{
goto exit_on_error;
}
/* check for the need of index key copy_buf */
if (TP_DOMAIN_TYPE (BTS->btid_int.key_type) == DB_TYPE_MIDXKEY)
{
/* found multi-column key-val */
need_copy_buf = true;
}
else
{ /* single-column index */
if (indx_info->key_info.key_ranges[0].range != EQ_NA)
{
/* found single-column key-range, not key-val */
if (QSTR_IS_ANY_CHAR_OR_BIT (TP_DOMAIN_TYPE (BTS->btid_int.key_type)))
{
/* this type needs index key copy_buf */
need_copy_buf = true;
}
}
}
if (need_copy_buf)
{
/* alloc index key copy_buf */
isidp->copy_buf = (char *) db_private_alloc (thread_p, DBVAL_BUFSIZE);
if (isidp->copy_buf == NULL)
{
goto exit_on_error;
}
isidp->copy_buf_len = DBVAL_BUFSIZE;
}
}
else
{
isidp->key_cnt = 0;
isidp->key_vals = NULL;
}
isidp->iscan_oid_order = iscan_oid_order;
if (scan_init_indx_coverage (thread_p, coverage_enabled, output_val_list, regu_val_list, vd, query_id,
root_header->node.max_key_len, func_index_col_id, &(isidp->indx_cov)) != NO_ERROR)
{
goto exit_on_error;
}
if (scan_init_iss (isidp) != NO_ERROR)
{
goto exit_on_error;
}
/* initialize multiple range search optimization structure */
{
bool use_multi_range_opt = (isidp->bt_num_attrs > 1 && isidp->indx_info->key_info.key_limit_reset == true
&& isidp->key_limit_upper > 0 && isidp->key_limit_upper < DB_INT32_MAX
&& isidp->key_limit_lower == -1) ? true : false;
if (scan_init_multi_range_optimization (thread_p, &(isidp->multi_range_opt), use_multi_range_opt,
(int) isidp->key_limit_upper) != NO_ERROR)
{
goto exit_on_error;
}
scan_id->scan_stats.multi_range_opt = isidp->multi_range_opt.use;
}
if (isidp->prebuilt_midxkey_domains == NULL && isidp->indx_info->key_info.key_cnt > 0)
{
isidp->prebuilt_midxkey_domains =
(TP_DOMAIN **) db_private_alloc (thread_p, isidp->indx_info->key_info.key_cnt * sizeof (TP_DOMAIN *));
if (isidp->prebuilt_midxkey_domains == NULL)
{
return ER_FAILED;
}
for (int i = 0; i < isidp->indx_info->key_info.key_cnt; i++)
{
isidp->prebuilt_midxkey_domains[i] = NULL;
}
}
return ret;
exit_on_error:
if (isidp->key_vals)
{
isidp->key_vals = NULL;
}
if (isidp->bt_attr_ids)
{
db_private_free_and_init (thread_p, isidp->bt_attr_ids);
}
if (isidp->vstr_ids)
{
db_private_free_and_init (thread_p, isidp->vstr_ids);
}
if (isidp->oid_list != NULL)
{
scan_free_iscan_oid_buf_list (isidp->oid_list);
isidp->oid_list = NULL;
}
if (isidp->copy_buf)
{
db_private_free_and_init (thread_p, isidp->copy_buf);
}
if (isidp->indx_cov.type_list != NULL)
{
if (isidp->indx_cov.type_list->domp != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.type_list->domp);
}
db_private_free_and_init (thread_p, isidp->indx_cov.type_list);
}
if (isidp->indx_cov.tplrec != NULL)
{
if (isidp->indx_cov.tplrec->tpl != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec->tpl);
}
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec);
}
if (isidp->indx_cov.lsid != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.lsid);
}
return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}
/*
* scan_open_index_key_info_scan () - Opens a scan for index key info
*
* return : Error code.
* thread_p (in) : Thread entry.
* scan_id (out) : Pointer where scan data is saved.
* val_list (in) : XASL values list.
* vd (in) : XASL values descriptors.
* indx_info (in) : Index info.
* cls_oid (in) : Class object identifier.
* hfid (in) : Heap file identifier.
* pr (in) : Scan predicate.
* output_val_list (in) : Output value pointers list.
* iscan_oid_order (in) : Index scan OID order.
* query_id (in) : Query identifier.
* key_info_values (in) : Array of value pointers to store key info.
* key_info_regu_list (in) : Regulator variable list for key info.
*/
int
scan_open_index_key_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
val_list_node * val_list, VAL_DESCR * vd,
/* fields of INDX_SCAN_ID */
indx_info * indx_info, OID * cls_oid, HFID * hfid, PRED_EXPR * pr,
valptr_list_node * output_val_list, bool iscan_oid_order, QUERY_ID query_id,
DB_VALUE ** key_info_values, regu_variable_list_node * key_info_regu_list)
{
int ret = NO_ERROR;
INDX_SCAN_ID *isidp = NULL;
BTID *btid = NULL;
VPID root_vpid;
PAGE_PTR root_page = NULL;
BTREE_ROOT_HEADER *root_header = NULL;
BTREE_SCAN *bts = NULL;
int func_index_col_id;
DB_TYPE single_node_type = DB_TYPE_NULL;
static bool oracle_style_empty_string = prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING);
scan_id->type = S_INDX_KEY_INFO_SCAN;
/* initialize SCAN_ID structure */
scan_init_scan_id (scan_id, 1, S_SELECT, false, false, QPROC_NO_SINGLE_INNER, NULL, val_list, vd);
/* read root_page page header info */
btid = &indx_info->btid;
root_vpid.pageid = btid->root_pageid;
root_vpid.volid = btid->vfid.volid;
root_page = pgbuf_fix (thread_p, &root_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (root_page == NULL)
{
return ER_FAILED;
}
root_header = btree_get_root_header (thread_p, root_page);
pgbuf_unfix_and_init (thread_p, root_page);
/* initialize INDEX_SCAN_ID structure */
isidp = &scan_id->s.isid;
/* index information */
isidp->indx_info = indx_info;
/* init allocated fields */
isidp->bt_num_attrs = 0;
isidp->bt_attr_ids = NULL;
isidp->vstr_ids = NULL;
isidp->oid_list = NULL;
isidp->curr_oidp = NULL;
isidp->copy_buf = NULL;
isidp->copy_buf_len = 0;
isidp->key_vals = NULL;
isidp->indx_cov.type_list = NULL;
isidp->indx_cov.list_id = NULL;
isidp->indx_cov.tplrec = NULL;
isidp->indx_cov.lsid = NULL;
/* initialize key limits */
if (scan_init_index_key_limit (thread_p, isidp, &indx_info->key_info, vd) != NO_ERROR)
{
goto exit_on_error;
}
/* index scan info */
bts = &isidp->bt_scan;
BTREE_INIT_SCAN (bts);
/* construct BTID_INT structure */
bts->btid_int.sys_btid = btid;
if (btree_glean_root_header_info
(thread_p, root_header, &bts->btid_int, bts->btid_int.key_type == NULL ? true : false) != NO_ERROR)
{
goto exit_on_error;
}
bts->is_btid_int_valid = true;
/* attribute information of the index key */
if (heap_get_indexinfo_of_btid (thread_p, cls_oid, &indx_info->btid, &isidp->bt_type, &isidp->bt_num_attrs,
NULL, NULL, NULL, &func_index_col_id) != NO_ERROR)
{
goto exit_on_error;
}
/* attribute information of the variable string attrs in index key */
isidp->num_vstr = 0;
isidp->vstr_ids = NULL;
if (oracle_style_empty_string)
{
isidp->num_vstr = isidp->bt_num_attrs; /* init to maximum */
isidp->vstr_ids = (ATTR_ID *) db_private_alloc (thread_p, isidp->num_vstr * sizeof (ATTR_ID));
if (isidp->vstr_ids == NULL)
{
goto exit_on_error;
}
}
/* is a single range? */
isidp->one_range = true;
/* initial values */
isidp->curr_keyno = -1;
isidp->curr_oidno = -1;
/* class object OID */
COPY_OID (&isidp->cls_oid, cls_oid);
/* heap file identifier */
isidp->hfid = *hfid; /* bitwise copy */
/* flags */
/* do not reset hsidp->caches_inited here */
isidp->scancache_inited = false;
/* convert key values in the form of REGU_VARIABLE to the form of DB_VALUE */
isidp->key_cnt = 0;
isidp->key_vals = NULL;
/* scan predicate */
scan_init_scan_pred (&isidp->scan_pred, NULL, pr, ((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
isidp->iscan_oid_order = iscan_oid_order;
if (scan_init_indx_coverage (thread_p, false, NULL, NULL, vd, query_id, root_header->node.max_key_len,
func_index_col_id, &(isidp->indx_cov)) != NO_ERROR)
{
goto exit_on_error;
}
if (scan_init_iss (isidp) != NO_ERROR)
{
goto exit_on_error;
}
/* initialize multiple range search optimization structure */
isidp->multi_range_opt.use = false;
isidp->key_info_values = key_info_values;
isidp->key_info_regu_list = key_info_regu_list;
return ret;
exit_on_error:
if (isidp->key_vals)
{
isidp->key_vals = NULL;
}
if (isidp->bt_attr_ids)
{
db_private_free_and_init (thread_p, isidp->bt_attr_ids);
}
if (isidp->vstr_ids)
{
db_private_free_and_init (thread_p, isidp->vstr_ids);
}
assert (isidp->oid_list == NULL);
if (isidp->copy_buf)
{
db_private_free_and_init (thread_p, isidp->copy_buf);
}
if (isidp->indx_cov.type_list != NULL)
{
if (isidp->indx_cov.type_list->domp != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.type_list->domp);
}
db_private_free_and_init (thread_p, isidp->indx_cov.type_list);
}
if (isidp->indx_cov.list_id != NULL)
{
QFILE_FREE_AND_INIT_LIST_ID (isidp->indx_cov.list_id);
}
if (isidp->indx_cov.tplrec != NULL)
{
if (isidp->indx_cov.tplrec->tpl != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec->tpl);
}
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec);
}
if (isidp->indx_cov.lsid != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.lsid);
}
return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}
/*
* scan_open_index_node_info_scan () - Opens a scan on b-tree nodes.
*
* return : Error code.
* thread_p (in) : Thread entry.
* scan_id (out) : Scan data.
* val_list (in) : XASL value list.
* vd (in) : XASL value descriptors.
* indx_info (in) : Index info.
* pr (in) : Scan predicate.
* node_info_values (in) : Array of value pointers to store b-tree node
* information.
* node_info_regu_list (in) : Regulator variable list.
*/
int
scan_open_index_node_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
val_list_node * val_list, VAL_DESCR * vd,
/* fields of INDX_SCAN_ID */
indx_info * indx_info, PRED_EXPR * pr, DB_VALUE ** node_info_values,
regu_variable_list_node * node_info_regu_list)
{
INDEX_NODE_SCAN_ID *idx_nsid_p = NULL;
VPID root_vpid;
PAGE_PTR root_page = NULL;
BTREE_ROOT_HEADER *root_header = NULL;
DB_TYPE single_node_type = DB_TYPE_NULL;
BTID *btid = NULL;
assert (scan_id != NULL);
scan_id->type = S_INDX_NODE_INFO_SCAN;
/* initialize SCAN_ID structure */
scan_init_scan_id (scan_id, 1, S_SELECT, false, false, QPROC_NO_SINGLE_INNER, NULL, val_list, vd);
idx_nsid_p = &scan_id->s.insid;
idx_nsid_p->indx_info = indx_info;
/* scan predicate */
scan_init_scan_pred (&idx_nsid_p->scan_pred, NULL, pr, ((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
BTREE_NODE_SCAN_INIT (&idx_nsid_p->btns);
/* read root_page page header info */
btid = &indx_info->btid;
root_vpid.pageid = btid->root_pageid;
root_vpid.volid = btid->vfid.volid;
root_page = pgbuf_fix (thread_p, &root_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
if (root_page == NULL)
{
return ER_FAILED;
}
root_header = btree_get_root_header (thread_p, root_page);
pgbuf_unfix_and_init (thread_p, root_page);
/* construct BTID_INT structure */
idx_nsid_p->btns.btid_int.sys_btid = btid;
if (btree_glean_root_header_info (thread_p, root_header, &idx_nsid_p->btns.btid_int, true) != NO_ERROR)
{
return ER_FAILED;
}
idx_nsid_p->node_info_values = node_info_values;
idx_nsid_p->node_info_regu_list = node_info_regu_list;
idx_nsid_p->caches_inited = false;
return NO_ERROR;
}
/*
* scan_open_list_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* list_id(in):
* regu_list_pred(in):
* pr(in):
* regu_list_rest(in):
*/
int
scan_open_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd,
/* fields of LLIST_SCAN_ID */
QFILE_LIST_ID * list_id, regu_variable_list_node * regu_list_pred, PRED_EXPR * pr,
regu_variable_list_node * regu_list_rest, regu_variable_list_node * regu_list_build,
regu_variable_list_node * regu_list_probe, int hash_list_scan_yn, bool is_read_only)
{
LLIST_SCAN_ID *llsidp;
int val_cnt;
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is LIST SCAN */
scan_id->type = S_LIST_SCAN;
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
/* initialize LLIST_SCAN_ID structure */
llsidp = &scan_id->s.llsid;
/* list file ID */
llsidp->list_id = list_id; /* points to XASL tree */
/* scan predicates */
scan_init_scan_pred (&llsidp->scan_pred, regu_list_pred, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
/* regulator variable list for other than predicates */
llsidp->rest_regu_list = regu_list_rest;
/* init for hash list scan */
/* regulator variable list for build, probe */
llsidp->hlsid.build_regu_list = regu_list_build;
llsidp->hlsid.probe_regu_list = regu_list_probe;
llsidp->hlsid.need_coerce_type = false;
llsidp->is_read_only = is_read_only;
/* check if hash list scan is possible? */
llsidp->hlsid.hash_list_scan_type = check_hash_list_scan (llsidp, &val_cnt, hash_list_scan_yn);
if (llsidp->hlsid.hash_list_scan_type != HASH_METH_NOT_USE)
{
bool on_trace;
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL tv_diff;
on_trace = thread_is_on_trace (thread_p);
if (on_trace)
{
tsc_getticks (&start_tick);
}
/* create hash table */
if (llsidp->hlsid.hash_list_scan_type == HASH_METH_HASH_FILE)
{
llsidp->hlsid.file.hash_table = (FHSID *) db_private_alloc (thread_p, sizeof (FHSID));
if (llsidp->hlsid.file.hash_table == NULL)
{
return S_ERROR;
}
if (fhs_create (thread_p, llsidp->hlsid.file.hash_table, llsidp->list_id->tuple_cnt) == NULL)
{
return S_ERROR;
}
}
else
{
llsidp->hlsid.memory.hash_table = mht_create_hls ("Hash List Scan", llsidp->list_id->tuple_cnt, NULL, NULL);
if (llsidp->hlsid.memory.hash_table == NULL)
{
return S_ERROR;
}
}
/* alloc temp key */
llsidp->hlsid.temp_key = qdata_alloc_hscan_key (thread_p, val_cnt, false);
llsidp->hlsid.temp_new_key = qdata_alloc_hscan_key (thread_p, val_cnt, true);
if (scan_start_scan (thread_p, scan_id) != NO_ERROR)
{
return S_ERROR;
}
if (scan_build_hash_list_scan (thread_p, scan_id) == S_ERROR)
{
return S_ERROR;
}
#if HASH_LIST_SCAN_DUMP_HASH_TABLE
if (llsidp->hlsid.hash_list_scan_type != HASH_METH_HASH_FILE)
{
if (llsidp->list_id->tuple_cnt <= DUMP_HASH_TABLE_LIMIT)
{
mht_dump_hls (thread_p, stdout, llsidp->hlsid.memory.hash_table, 1, qdata_print_hash_scan_entry,
(void *) &(llsidp->list_id->type_list), (void *) &(llsidp->hlsid.hash_list_scan_type));
printf ("temp file : tuple count = %ld, file_size = %dK\n", llsidp->list_id->tuple_cnt,
llsidp->list_id->page_cnt * 16);
}
}
#endif
scan_end_scan (thread_p, scan_id);
if (on_trace)
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&tv_diff, end_tick, start_tick);
TSC_ADD_TIMEVAL (scan_id->scan_stats.elapsed_hash_build, tv_diff);
}
}
else
{
llsidp->hlsid.memory.hash_table = NULL;
llsidp->hlsid.memory.curr_hash_entry = NULL;
llsidp->hlsid.temp_key = NULL;
llsidp->hlsid.temp_new_key = NULL;
}
return NO_ERROR;
}
/*
* scan_open_showstmt_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* show_type(in):
* arg_list(in):
*/
int
scan_open_showstmt_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd,
/* fields of SHOWSTMT_SCAN_ID */
PRED_EXPR * pr, SHOWSTMT_TYPE show_type, regu_variable_list_node * arg_list)
{
SHOWSTMT_SCAN_ID *stsidp;
int i, arg_cnt, out_cnt;
regu_variable_list_node *regu_var_p;
REGU_VARIABLE *regu;
QPROC_DB_VALUE_LIST valp;
DB_VALUE **arg_values = NULL, **out_values = NULL;
DB_TYPE single_node_type = DB_TYPE_NULL;
int error;
/* scan type is S_SHOWSTMT_SCAN */
scan_id->type = S_SHOWSTMT_SCAN;
/* initialize SCAN_ID structure */
/* readonly_scan = true, fixed = true */
scan_init_scan_id (scan_id, true, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
/* initialize SHOWSTMT_SCAN_ID structure */
stsidp = &scan_id->s.stsid;
for (regu_var_p = arg_list, i = 0; regu_var_p; regu_var_p = regu_var_p->next)
{
i++;
}
arg_cnt = i;
if (arg_cnt > 0)
{
arg_values = (DB_VALUE **) db_private_alloc (thread_p, arg_cnt * sizeof (DB_VALUE *));
if (arg_values == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
for (regu_var_p = arg_list, i = 0; regu_var_p; regu_var_p = regu_var_p->next, i++)
{
regu = ®u_var_p->value;
assert (regu != NULL && regu->type == TYPE_POS_VALUE);
error = fetch_peek_dbval (thread_p, regu, vd, NULL, NULL, NULL, &arg_values[i]);
if (error != NO_ERROR)
{
goto exit_on_error;
}
}
assert (i == arg_cnt);
}
/* prepare out_values */
out_cnt = val_list->val_cnt;
out_values = (DB_VALUE **) db_private_alloc (thread_p, out_cnt * sizeof (DB_VALUE *));
if (out_values == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
for (valp = val_list->valp, i = 0; valp; valp = valp->next, i++)
{
out_values[i] = valp->val;
}
assert (i == out_cnt);
stsidp->show_type = show_type;
stsidp->arg_values = arg_values;
stsidp->arg_cnt = arg_cnt;
stsidp->out_values = out_values;
stsidp->out_cnt = out_cnt;
stsidp->cursor = 0;
stsidp->ctx = NULL;
/* scan predicates */
scan_init_scan_pred (&stsidp->scan_pred, NULL, pr, ((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
return NO_ERROR;
exit_on_error:
if (arg_values != NULL)
{
db_private_free_and_init (thread_p, arg_values);
}
if (out_values != NULL)
{
db_private_free_and_init (thread_p, out_values);
}
return error;
}
/*
* scan_open_values_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* valptr_list(in):
*/
int
scan_open_values_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd,
/* fields of REGU_VALUES_SCAN_ID */
valptr_list_node * valptr_list)
{
REGU_VALUES_SCAN_ID *rvsidp;
assert (valptr_list != NULL);
scan_id->type = S_VALUES_SCAN;
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
rvsidp = &scan_id->s.rvsid;
rvsidp->regu_list = valptr_list->valptrp;
rvsidp->value_cnt = valptr_list->valptr_cnt;
return NO_ERROR;
}
/*
* scan_open_set_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* set_ptr(in):
* regu_list_pred(in):
* pr(in):
*/
int
scan_open_set_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd,
/* fields of SET_SCAN_ID */
REGU_VARIABLE * set_ptr, regu_variable_list_node * regu_list_pred, PRED_EXPR * pr)
{
SET_SCAN_ID *ssidp;
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is SET SCAN */
scan_id->type = S_SET_SCAN;
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
/* initialize SET_SCAN_ID structure */
ssidp = &scan_id->s.ssid;
ssidp->set_ptr = set_ptr; /* points to XASL tree */
/* scan predicates */
scan_init_scan_pred (&ssidp->scan_pred, regu_list_pred, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
return NO_ERROR;
}
/*
* scan_open_json_table_scan () -
* return: NO_ERROR
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
*/
int
scan_open_json_table_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, int grouped, QPROC_SINGLE_FETCH single_fetch,
DB_VALUE * join_dbval, val_list_node * val_list, VAL_DESCR * vd, PRED_EXPR * pr)
{
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is JSON_TABLE SCAN */
assert (scan_id->type == S_JSON_TABLE_SCAN);
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
// scan_init_scan_pred
scan_init_scan_pred (&scan_id->s.jtid.get_predicate (), NULL, pr,
((pr) ? eval_fnc (thread_p, pr, &single_node_type) : NULL));
scan_id->s.jtid.set_value_descriptor (vd);
return NO_ERROR;
}
/*
* scan_open_method_scan () -
* return: NO_ERROR, or ER_code
* scan_id(out): Scan identifier
* grouped(in):
* single_fetch(in):
* join_dbval(in):
* val_list(in):
* vd(in):
* list_id(in):
* meth_sig_list(in):
*
* Note: If you feel the need
*/
int
scan_open_method_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
/* fields of SCAN_ID */
int grouped, QPROC_SINGLE_FETCH single_fetch, DB_VALUE * join_dbval, val_list_node * val_list,
VAL_DESCR * vd,
/* */
QFILE_LIST_ID * list_id, PL_SIGNATURE_ARRAY_TYPE * sig_array)
{
/* scan type is METHOD SCAN */
scan_id->type = S_METHOD_SCAN;
/* initialize SCAN_ID structure */
/* mvcc_select_lock_needed = false, fixed = true */
scan_init_scan_id (scan_id, false, S_SELECT, true, grouped, single_fetch, join_dbval, val_list, vd);
int error = scan_id->s.msid.init (thread_p, sig_array, list_id);
if (error == NO_ERROR)
{
error = scan_id->s.msid.open ();
if (error != NO_ERROR)
{
/* it needs to clear the related resources */
scan_id->s.msid.clear (false);
}
}
return error;
}
/*
* scan_open_dblink_scan () -
* return: NO_ERROR, or ER_code
* scan_id(out): Scan identifier
* conn_url(in):
* conn_user(in):
* conn_password(in):
* sql_text(in):
*
*/
int
scan_open_dblink_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id,
struct access_spec_node *spec, VAL_DESCR * vd, VAL_LIST * val_list, DBLINK_HOST_VARS * host_vars)
{
DBLINK_SCAN_ID *dblid = &scan_id->s.dblid;
DB_TYPE single_node_type = DB_TYPE_NULL;
/* scan type is DBLINK SCAN */
scan_id->type = S_DBLINK_SCAN;
/* initialize SCAN_ID structure */
scan_init_scan_id (scan_id, false, S_SELECT, true, 0, spec->single_fetch, NULL, val_list, vd);
/* scan predicates */
scan_init_scan_pred (&dblid->scan_pred, NULL, spec->where_pred,
((spec->where_pred) ? eval_fnc (thread_p, spec->where_pred, &single_node_type) : NULL));
return dblink_open_scan (thread_p, &scan_id->s.dblid.scan_info, spec, vd, host_vars);
}
/*
* scan_start_scan () - Start the scan process on the given scan identifier.
* return: NO_ERROR, or ER_code
* scan_id(out): Scan identifier
*
* Note: If you feel the need
*/
int
scan_start_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
int ret = NO_ERROR;
int i;
HEAP_SCAN_ID *hsidp = NULL;
INDX_SCAN_ID *isidp = NULL;
INDEX_NODE_SCAN_ID *insidp = NULL;
LLIST_SCAN_ID *llsidp = NULL;
SET_SCAN_ID *ssidp = NULL;
REGU_VALUES_SCAN_ID *rvsidp = NULL;
REGU_VALUE_LIST *regu_value_list = NULL;
regu_variable_list_node *list_node = NULL;
MVCC_SNAPSHOT *mvcc_snapshot = NULL;
JSON_TABLE_SCAN_ID *jtidp = NULL;
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_SAMPLING_SCAN:
hsidp = &scan_id->s.hsid;
UT_CAST_TO_NULL_HEAP_OID (&hsidp->hfid, &hsidp->curr_oid);
if (!OID_IS_ROOTOID (&hsidp->cls_oid))
{
mvcc_snapshot = logtb_get_mvcc_snapshot (thread_p);
if (mvcc_snapshot == NULL)
{
goto exit_on_error;
}
}
if (scan_id->grouped)
{
ret = heap_scanrange_start (thread_p, &hsidp->scan_range, &hsidp->hfid, &hsidp->cls_oid, mvcc_snapshot);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
hsidp->scanrange_inited = true;
}
else
{
/* A new argument(is_indexscan = false) is appended */
ret =
heap_scancache_start (thread_p, &hsidp->scan_cache, &hsidp->hfid, &hsidp->cls_oid, scan_id->fixed,
mvcc_snapshot);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
hsidp->scancache_inited = true;
}
if (hsidp->caches_inited != true)
{
hsidp->pred_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &hsidp->cls_oid, hsidp->pred_attrs.num_attrs, hsidp->pred_attrs.attr_ids,
hsidp->pred_attrs.attr_cache);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
hsidp->rest_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &hsidp->cls_oid, hsidp->rest_attrs.num_attrs, hsidp->rest_attrs.attr_ids,
hsidp->rest_attrs.attr_cache);
if (ret != NO_ERROR)
{
heap_attrinfo_end (thread_p, hsidp->pred_attrs.attr_cache);
goto exit_on_error;
}
if (hsidp->cache_recordinfo != NULL)
{
/* initialize cache_recordinfo values */
for (i = 0; i < HEAP_RECORD_INFO_COUNT; i++)
{
db_make_null (hsidp->cache_recordinfo[i]);
}
}
hsidp->caches_inited = true;
}
break;
case S_PARALLEL_HEAP_SCAN:
#if SERVER_MODE && !WINDOWS
scan_start_parallel_heap_scan (thread_p, scan_id);
#endif /* SERVER_MODE && !WINDOWS */
break;
case S_HEAP_PAGE_SCAN:
VPID_SET_NULL (&scan_id->s.hpsid.curr_vpid);
break;
case S_CLASS_ATTR_SCAN:
hsidp = &scan_id->s.hsid;
hsidp->pred_attrs.attr_cache->num_values = -1;
if (hsidp->caches_inited != true)
{
ret =
heap_attrinfo_start (thread_p, &hsidp->cls_oid, hsidp->pred_attrs.num_attrs, hsidp->pred_attrs.attr_ids,
hsidp->pred_attrs.attr_cache);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
hsidp->rest_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &hsidp->cls_oid, hsidp->rest_attrs.num_attrs, hsidp->rest_attrs.attr_ids,
hsidp->rest_attrs.attr_cache);
if (ret != NO_ERROR)
{
heap_attrinfo_end (thread_p, hsidp->pred_attrs.attr_cache);
goto exit_on_error;
}
hsidp->caches_inited = true;
}
break;
case S_INDX_SCAN:
isidp = &scan_id->s.isid;
if (!OID_IS_ROOTOID (&isidp->cls_oid))
{
mvcc_snapshot = logtb_get_mvcc_snapshot (thread_p);
if (mvcc_snapshot == NULL)
{
goto exit_on_error;
}
}
/* A new argument(is_indexscan = true) is appended */
ret =
heap_scancache_start (thread_p, &isidp->scan_cache, &isidp->hfid, &isidp->cls_oid, scan_id->fixed,
mvcc_snapshot);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
isidp->scancache_inited = true;
if (isidp->caches_inited != true)
{
if (isidp->range_pred.regu_list != NULL)
{
isidp->range_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &isidp->cls_oid, isidp->range_attrs.num_attrs,
isidp->range_attrs.attr_ids, isidp->range_attrs.attr_cache);
if (ret != NO_ERROR)
{
goto exit_on_error;
}
}
if (isidp->key_pred.regu_list != NULL)
{
isidp->key_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &isidp->cls_oid, isidp->key_attrs.num_attrs, isidp->key_attrs.attr_ids,
isidp->key_attrs.attr_cache);
if (ret != NO_ERROR)
{
if (isidp->range_pred.regu_list != NULL)
{
heap_attrinfo_end (thread_p, isidp->range_attrs.attr_cache);
}
goto exit_on_error;
}
}
isidp->pred_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &isidp->cls_oid, isidp->pred_attrs.num_attrs, isidp->pred_attrs.attr_ids,
isidp->pred_attrs.attr_cache);
if (ret != NO_ERROR)
{
if (isidp->range_pred.regu_list != NULL)
{
heap_attrinfo_end (thread_p, isidp->range_attrs.attr_cache);
}
if (isidp->key_pred.regu_list != NULL)
{
heap_attrinfo_end (thread_p, isidp->key_attrs.attr_cache);
}
goto exit_on_error;
}
isidp->rest_attrs.attr_cache->num_values = -1;
ret =
heap_attrinfo_start (thread_p, &isidp->cls_oid, isidp->rest_attrs.num_attrs, isidp->rest_attrs.attr_ids,
isidp->rest_attrs.attr_cache);
if (ret != NO_ERROR)
{
if (isidp->range_pred.regu_list != NULL)
{
heap_attrinfo_end (thread_p, isidp->range_attrs.attr_cache);
}
if (isidp->key_pred.regu_list != NULL)
{
heap_attrinfo_end (thread_p, isidp->key_attrs.attr_cache);
}
heap_attrinfo_end (thread_p, isidp->pred_attrs.attr_cache);
goto exit_on_error;
}
isidp->caches_inited = true;
}
isidp->oids_count = 0;
isidp->curr_keyno = -1;
isidp->curr_oidno = -1;
isidp->one_range = false;
break;
case S_INDX_KEY_INFO_SCAN:
isidp = &scan_id->s.isid;
if (!isidp->caches_inited)
{
for (i = 0; i < BTREE_KEY_INFO_COUNT; i++)
{
db_make_null (isidp->key_info_values[i]);
}
}
isidp->caches_inited = true;
isidp->oids_count = 0;
isidp->curr_keyno = -1;
isidp->curr_oidno = -1;
break;
case S_INDX_NODE_INFO_SCAN:
insidp = &scan_id->s.insid;
if (!insidp->caches_inited)
{
for (i = 0; i < BTREE_NODE_INFO_COUNT; i++)
{
db_make_null (insidp->node_info_values[i]);
}
insidp->caches_inited = true;
}
BTREE_NODE_SCAN_INIT (&insidp->btns);
break;
case S_LIST_SCAN:
llsidp = &scan_id->s.llsid;
/* open list file scan */
if (qfile_open_list_scan (llsidp->list_id, &llsidp->lsid) != NO_ERROR)
{
goto exit_on_error;
}
llsidp->lsid.is_read_only = llsidp->is_read_only;
qfile_start_scan_fix (thread_p, &llsidp->lsid);
break;
case S_SHOWSTMT_SCAN:
if (showstmt_start_scan (thread_p, scan_id) != NO_ERROR)
{
goto exit_on_error;
}
break;
case S_VALUES_SCAN:
rvsidp = &scan_id->s.rvsid;
if (rvsidp->regu_list == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
}
for (list_node = rvsidp->regu_list; list_node; list_node = list_node->next)
{
regu_value_list = list_node->value.value.reguval_list;
assert (regu_value_list != NULL && regu_value_list->regu_list != NULL);
regu_value_list->current_value = regu_value_list->regu_list;
}
break;
case S_SET_SCAN:
ssidp = &scan_id->s.ssid;
db_make_null (&ssidp->set);
break;
case S_JSON_TABLE_SCAN:
jtidp = &scan_id->s.jtid;
// todo: what else to add here?
break;
case S_METHOD_SCAN:
case S_DBLINK_SCAN:
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
goto exit_on_error;
} /* switch (scan_id->type) */
/* set scan status as started */
scan_id->position = S_BEFORE;
scan_id->direction = S_FORWARD;
scan_id->status = S_STARTED;
scan_id->qualified_block = false;
scan_id->single_fetched = false;
scan_id->null_fetched = false;
return ret;
exit_on_error:
return (ret == NO_ERROR && (ret = er_errid ()) == NO_ERROR) ? ER_FAILED : ret;
}
/*
* scan_reset_scan_block () - Move the scan back to the beginning point inside the current scan block.
* return: S_SUCCESS, S_END, S_ERROR
* s_id(in/out): Scan identifier
*
* Note: If you feel the need
*/
SCAN_CODE
scan_reset_scan_block (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
SCAN_CODE status = S_SUCCESS;
s_id->single_fetched = false;
s_id->null_fetched = false;
switch (s_id->type)
{
case S_HEAP_SCAN:
if (s_id->grouped)
{
OID_SET_NULL (&s_id->s.hsid.curr_oid);
}
else
{
s_id->position = (s_id->direction == S_FORWARD) ? S_BEFORE : S_AFTER;
OID_SET_NULL (&s_id->s.hsid.curr_oid);
}
break;
#if SERVER_MODE && !WINDOWS
case S_PARALLEL_HEAP_SCAN:
if (scan_reset_scan_block_parallel_heap_scan (thread_p, s_id) != NO_ERROR)
{
status = S_ERROR;
break;
}
break;
#endif /* SERVER_MODE && !WINDOWS */
case S_INDX_SCAN:
if (s_id->grouped)
{
if (s_id->direction == S_FORWARD && s_id->s.isid.iscan_oid_order == true)
{
s_id->s.isid.curr_oidno = s_id->s.isid.oids_count;
s_id->direction = S_BACKWARD;
}
else
{
s_id->s.isid.curr_oidno = -1;
s_id->direction = S_FORWARD;
}
/* reinitialize index skip scan structure */
if (scan_init_iss (&s_id->s.isid) != NO_ERROR)
{
status = S_ERROR;
break;
}
}
else
{
INDX_COV *indx_cov_p;
s_id->s.isid.curr_oidno = -1;
s_id->s.isid.curr_keyno = -1;
s_id->position = S_BEFORE;
BTREE_RESET_SCAN (&s_id->s.isid.bt_scan);
/* reset key limits */
if (s_id->s.isid.indx_info)
{
if (scan_init_index_key_limit (thread_p, &s_id->s.isid, &s_id->s.isid.indx_info->key_info, s_id->vd) !=
NO_ERROR)
{
status = S_ERROR;
break;
}
}
/* reinitialize index skip scan structure */
if (scan_init_iss (&s_id->s.isid) != NO_ERROR)
{
status = S_ERROR;
break;
}
/* reset index covering */
indx_cov_p = &(s_id->s.isid.indx_cov);
if (indx_cov_p->lsid != NULL)
{
qfile_close_scan (thread_p, indx_cov_p->lsid);
}
if (indx_cov_p->list_id != NULL)
{
static int temp_cache_max_pages = prm_get_integer_value (PRM_ID_MAX_PAGES_IN_TEMP_FILE_CACHE);
if (indx_cov_p->list_id->page_cnt - indx_cov_p->list_id->tfile_vfid->membuf_npages > temp_cache_max_pages)
{
qfile_destroy_list (thread_p, indx_cov_p->list_id);
indx_cov_p->list_id =
qfile_open_list (thread_p, indx_cov_p->type_list, NULL, indx_cov_p->query_id, 0,
indx_cov_p->list_id);
if (indx_cov_p->list_id == NULL)
{
status = S_ERROR;
break;
}
}
else if (qfile_truncate_list (thread_p, indx_cov_p->list_id) != NO_ERROR)
{
status = S_ERROR;
break;
}
}
}
break;
case S_LIST_SCAN:
/* may have scanned some already so clean up */
qfile_end_scan_fix (thread_p, &s_id->s.llsid.lsid);
qfile_close_scan (thread_p, &s_id->s.llsid.lsid);
/* open list file scan for this outer row */
if (qfile_open_list_scan (s_id->s.llsid.list_id, &s_id->s.llsid.lsid) != NO_ERROR)
{
status = S_ERROR;
break;
}
qfile_start_scan_fix (thread_p, &s_id->s.llsid.lsid);
s_id->position = S_BEFORE;
s_id->s.llsid.lsid.position = S_BEFORE;
break;
case S_SHOWSTMT_SCAN:
s_id->s.stsid.cursor = 0;
s_id->position = S_BEFORE;
break;
case S_CLASS_ATTR_SCAN:
case S_SET_SCAN:
case S_JSON_TABLE_SCAN:
s_id->position = S_BEFORE;
break;
case S_DBLINK_SCAN:
status = dblink_scan_reset (&s_id->s.dblid.scan_info);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
status = S_ERROR;
break;
} /* switch (s_id->type) */
return status;
}
/*
* scan_next_scan_block () - Move the scan to the next scan block.
* If there are no more scan blocks left, S_END is returned.
* return: S_SUCCESS, S_END, S_ERROR
* s_id(in/out): Scan identifier
*
* Note: If you feel the need
*/
SCAN_CODE
scan_next_scan_block (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
SCAN_CODE sp_scan;
s_id->single_fetched = false;
s_id->null_fetched = false;
s_id->qualified_block = false;
switch (s_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_PAGE_SCAN:
case S_HEAP_SAMPLING_SCAN:
case S_PARALLEL_HEAP_SCAN:
if (s_id->grouped)
{
/* grouped, fixed scan */
if (s_id->direction == S_FORWARD)
{
sp_scan = heap_scanrange_to_following (thread_p, &s_id->s.hsid.scan_range, NULL);
}
else
{
sp_scan = heap_scanrange_to_prior (thread_p, &s_id->s.hsid.scan_range, NULL);
}
if (sp_scan == S_SUCCESS || sp_scan == S_END)
{
return sp_scan;
}
else
{
return S_ERROR;
}
}
else
{
if (s_id->direction == S_FORWARD)
{
if (s_id->position == S_BEFORE)
{
return S_SUCCESS;
}
else
{
return S_END;
}
}
else
{
if (s_id->position == S_AFTER)
{
return S_SUCCESS;
}
else
{
return S_END;
}
}
}
case S_INDX_SCAN:
if (s_id->grouped)
{
if ((s_id->direction == S_FORWARD && s_id->position == S_BEFORE)
|| (!BTREE_END_OF_SCAN (&s_id->s.isid.bt_scan) || s_id->s.isid.indx_info->range_type == R_KEYLIST
|| s_id->s.isid.indx_info->range_type == R_RANGELIST))
{
if (!(s_id->position == S_BEFORE && s_id->s.isid.one_range == true))
{
/* get the next set of object identifiers specified in the range */
if (scan_get_index_oidset (thread_p, s_id, NULL, NULL) != NO_ERROR)
{
return S_ERROR;
}
if (s_id->s.isid.oids_count == 0)
{ /* range is empty */
s_id->position = S_AFTER;
return S_END;
}
if (s_id->position == S_BEFORE && BTREE_END_OF_SCAN (&s_id->s.isid.bt_scan)
&& s_id->s.isid.indx_info->range_type != R_KEYLIST
&& s_id->s.isid.indx_info->range_type != R_RANGELIST)
{
s_id->s.isid.one_range = true;
}
}
if (s_id->s.isid.iscan_oid_order == true)
{
s_id->position = S_ON;
s_id->direction = S_BACKWARD;
s_id->s.isid.curr_oidno = s_id->s.isid.oids_count;
}
return S_SUCCESS;
}
else
{
s_id->position = S_AFTER;
return S_END;
}
}
else
{
return ((s_id->position == S_BEFORE) ? S_SUCCESS : S_END);
}
case S_INDX_KEY_INFO_SCAN:
case S_INDX_NODE_INFO_SCAN:
if (s_id->grouped)
{
assert (0);
return S_ERROR;
}
return ((s_id->position == S_BEFORE) ? S_SUCCESS : S_END);
case S_CLASS_ATTR_SCAN:
case S_LIST_SCAN:
case S_SHOWSTMT_SCAN:
case S_SET_SCAN:
case S_METHOD_SCAN:
case S_DBLINK_SCAN:
case S_JSON_TABLE_SCAN:
case S_VALUES_SCAN:
return (s_id->position == S_BEFORE) ? S_SUCCESS : S_END;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
return S_ERROR;
}
}
/*
* scan_end_scan () - End the scan process on the given scan identifier.
* return:
* scan_id(in/out): Scan identifier
*
* Note: If you feel the need
*/
void
scan_end_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
HEAP_SCAN_ID *hsidp;
INDX_SCAN_ID *isidp;
LLIST_SCAN_ID *llsidp;
REGU_VALUES_SCAN_ID *rvsidp;
SET_SCAN_ID *ssidp;
KEY_VAL_RANGE *key_vals;
JSON_TABLE_SCAN_ID *jtidp;
int i;
if (scan_id == NULL)
{
return;
}
if ((scan_id->status == S_ENDED) || (scan_id->status == S_CLOSED))
{
return;
}
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_SAMPLING_SCAN:
hsidp = &scan_id->s.hsid;
/* do not free attr_cache here. xs_clear_access_spec_list() will free attr_caches. */
if (scan_id->grouped)
{
if (hsidp->scanrange_inited)
{
heap_scanrange_end (thread_p, &hsidp->scan_range);
}
}
else
{
if (hsidp->scancache_inited)
{
(void) heap_scancache_end (thread_p, &hsidp->scan_cache);
}
}
/* switch scan direction for further iterations */
if (scan_id->direction == S_FORWARD)
{
scan_id->direction = S_BACKWARD;
}
else
{
scan_id->direction = S_FORWARD;
}
break;
case S_PARALLEL_HEAP_SCAN:
#if SERVER_MODE && !WINDOWS
scan_end_parallel_heap_scan (thread_p, scan_id);
#endif /* SERVER_MODE && !WINDOWS */
break;
case S_CLASS_ATTR_SCAN:
/* do not free attr_cache here. xs_clear_access_spec_list() will free attr_caches. */
break;
case S_INDX_SCAN:
isidp = &scan_id->s.isid;
/* do not free attr_cache here. xs_clear_access_spec_list() will free attr_caches. */
if (isidp->scancache_inited)
{
(void) heap_scancache_end (thread_p, &isidp->scan_cache);
}
if (isidp->curr_keyno >= 0 && isidp->curr_keyno < isidp->key_cnt)
{
key_vals = isidp->key_vals;
for (i = 0; i < isidp->key_cnt; i++)
{
pr_clear_value (&key_vals[i].key1);
pr_clear_value (&key_vals[i].key2);
}
}
/* clear all the used keys */
btree_scan_clear_key (&(isidp->bt_scan));
/* clear last_key */
(void) scan_init_iss (isidp);
break;
case S_LIST_SCAN:
llsidp = &scan_id->s.llsid;
qfile_end_scan_fix (thread_p, &llsidp->lsid);
qfile_close_scan (thread_p, &llsidp->lsid);
break;
case S_SHOWSTMT_SCAN:
showstmt_end_scan (thread_p, scan_id);
break;
case S_VALUES_SCAN:
rvsidp = &scan_id->s.rvsid;
break;
case S_SET_SCAN:
ssidp = &scan_id->s.ssid;
pr_clear_value (&ssidp->set);
break;
case S_JSON_TABLE_SCAN:
jtidp = &scan_id->s.jtid;
jtidp->end (thread_p);
break;
case S_METHOD_SCAN:
case S_DBLINK_SCAN:
break;
default:
break;
}
scan_id->status = S_ENDED;
}
/*
* scan_close_scan () - The scan identifier is closed and allocated areas and page buffers are freed.
* return:
* scan_id(in/out): Scan identifier
*
* Note: If you feel the need
*/
void
scan_close_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
INDX_SCAN_ID *isidp;
SHOWSTMT_SCAN_ID *stsidp;
LLIST_SCAN_ID *llsidp;
if (scan_id == NULL || scan_id->status == S_CLOSED)
{
return;
}
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_PAGE_SCAN:
case S_CLASS_ATTR_SCAN:
case S_VALUES_SCAN:
case S_HEAP_SAMPLING_SCAN:
break;
case S_PARALLEL_HEAP_SCAN:
#if SERVER_MODE && !WINDOWS
scan_close_parallel_heap_scan (thread_p, scan_id);
#endif /* SERVER_MODE && !WINDOWS */
break;
case S_INDX_SCAN:
isidp = &scan_id->s.isid;
if (isidp->prebuilt_midxkey_domains != NULL)
{
for (int i = 0; i < isidp->indx_info->key_info.key_cnt; i++)
{
if (isidp->prebuilt_midxkey_domains[i])
{
tp_domain_free (isidp->prebuilt_midxkey_domains[i]);
isidp->prebuilt_midxkey_domains[i] = NULL;
}
}
db_private_free_and_init (thread_p, isidp->prebuilt_midxkey_domains);
}
if (isidp->key_vals)
{
isidp->key_vals = NULL;
}
/* free allocated memory for the scan */
if (isidp->bt_attr_ids)
{
db_private_free_and_init (thread_p, isidp->bt_attr_ids);
}
if (isidp->bt_attrs_prefix_length)
{
db_private_free_and_init (thread_p, isidp->bt_attrs_prefix_length);
}
if (isidp->vstr_ids)
{
db_private_free_and_init (thread_p, isidp->vstr_ids);
}
if (isidp->oid_list != NULL)
{
scan_free_iscan_oid_buf_list (isidp->oid_list);
isidp->oid_list = NULL;
}
/* free index key copy_buf */
if (isidp->copy_buf)
{
db_private_free_and_init (thread_p, isidp->copy_buf);
}
/* free index covering */
if (isidp->indx_cov.lsid != NULL)
{
qfile_close_scan (thread_p, isidp->indx_cov.lsid);
db_private_free_and_init (thread_p, isidp->indx_cov.lsid);
}
if (isidp->indx_cov.type_list != NULL)
{
if (isidp->indx_cov.type_list->domp != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.type_list->domp);
}
db_private_free_and_init (thread_p, isidp->indx_cov.type_list);
}
if (isidp->indx_cov.tplrec != NULL)
{
if (isidp->indx_cov.tplrec->tpl != NULL)
{
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec->tpl);
}
db_private_free_and_init (thread_p, isidp->indx_cov.tplrec);
}
/* free multiple range optimization struct */
if (isidp->multi_range_opt.top_n_items != NULL)
{
int i;
for (i = 0; i < isidp->multi_range_opt.size; i++)
{
if (isidp->multi_range_opt.top_n_items[i] != NULL)
{
pr_clear_value (&(isidp->multi_range_opt.top_n_items[i]->index_value));
db_private_free_and_init (thread_p, isidp->multi_range_opt.top_n_items[i]);
}
}
db_private_free_and_init (thread_p, isidp->multi_range_opt.top_n_items);
isidp->multi_range_opt.top_n_items = NULL;
db_private_free_and_init (thread_p, isidp->multi_range_opt.tplrec.tpl);
isidp->multi_range_opt.tplrec.tpl = 0;
}
/* free buffer */
if (isidp->multi_range_opt.buffer != NULL)
{
db_private_free_and_init (thread_p, isidp->multi_range_opt.buffer);
}
if (isidp->multi_range_opt.sort_att_idx != NULL)
{
db_private_free_and_init (thread_p, isidp->multi_range_opt.sort_att_idx);
}
if (isidp->multi_range_opt.is_desc_order != NULL)
{
db_private_free_and_init (thread_p, isidp->multi_range_opt.is_desc_order);
}
if (isidp->multi_range_opt.sort_col_dom != NULL)
{
db_private_free_and_init (thread_p, isidp->multi_range_opt.sort_col_dom);
}
memset ((void *) (&(isidp->multi_range_opt)), 0, sizeof (MULTI_RANGE_OPT));
btree_range_scan_free_matched_idx (&isidp->bt_scan);
break;
case S_LIST_SCAN:
llsidp = &scan_id->s.llsid;
/* clear hash list scan table */
if (llsidp->hlsid.hash_list_scan_type == HASH_METH_IN_MEM
|| llsidp->hlsid.hash_list_scan_type == HASH_METH_HYBRID)
{
#if HASH_LIST_SCAN_DUMP_HASH_TABLE
if (llsidp->list_id->tuple_cnt <= DUMP_HASH_TABLE_LIMIT)
{
(void) mht_dump_hls (thread_p, stdout, llsidp->hlsid.memory.hash_table, 1, qdata_print_hash_scan_entry,
(void *) &(llsidp->list_id->type_list),
(void *) &(llsidp->hlsid.hash_list_scan_type));
printf ("temp file : tuple count = %ld, file_size = %dK\n", llsidp->list_id->tuple_cnt,
llsidp->list_id->page_cnt * 16);
}
#endif
mht_clear_hls (llsidp->hlsid.memory.hash_table, qdata_free_hscan_entry, (void *) thread_p);
mht_destroy_hls (llsidp->hlsid.memory.hash_table);
}
else if (llsidp->hlsid.hash_list_scan_type == HASH_METH_HASH_FILE)
{
fhs_destroy (thread_p, llsidp->hlsid.file.hash_table);
db_private_free_and_init (thread_p, llsidp->hlsid.file.hash_table);
}
/* free temp keys and values */
if (llsidp->hlsid.temp_key != NULL)
{
qdata_free_hscan_key (thread_p, llsidp->hlsid.temp_key, llsidp->hlsid.temp_key->val_count);
llsidp->hlsid.temp_key = NULL;
}
/* free temp new keys and values */
if (llsidp->hlsid.temp_new_key != NULL)
{
qdata_free_hscan_key (thread_p, llsidp->hlsid.temp_new_key, llsidp->hlsid.temp_new_key->val_count);
llsidp->hlsid.temp_new_key = NULL;
}
break;
case S_SHOWSTMT_SCAN:
stsidp = &scan_id->s.stsid;
if (stsidp->arg_values != NULL)
{
db_private_free_and_init (thread_p, stsidp->arg_values);
}
if (stsidp->out_values != NULL)
{
db_private_free_and_init (thread_p, stsidp->out_values);
}
break;
case S_SET_SCAN:
break;
case S_METHOD_SCAN:
scan_id->s.msid.close ();
break;
case S_DBLINK_SCAN:
dblink_close_scan (&scan_id->s.dblid.scan_info, false);
break;
case S_JSON_TABLE_SCAN:
break;
default:
/* S_VALUES_SCAN */
break;
}
scan_id->status = S_CLOSED;
}
/*
* call_get_next_index_oidset () - Wrapper for scan_get_next_oidset, accounts
* for scan variations, such as the "index
* skip scan" optimization.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* thread_p (in):
* scan_id (in/out):
* isidp (in/out):
* should_go_to_next_value (in): see Notes
*
* Note:
* This function tries to obtain the next set of OIDs for the scan to consume.
* The real heavy-lifting function is get_next_index_oidset(), which we call,
* this one is a wrapper.
* If the "Index Skip Scan" optimization is used, we cycle through successive
* values of the first index column until we find one that lets
* get_index_oidset() return with some results.
* We also handle the case where we are called just because a new crop of OIDs
* is needed.
*
* The boolean should_go_to_next_value controls whether we skip to the next
* value for the first index column, or we still have data to read for the
* current value (i.e. because the buffer was full. It is controlled by
* the caller by evaluating BTREE_END_OF_SCAN. If this is true, there are no
* more OIDS for the current value of the first index column and we should
* "skip" to the next one.
*/
static SCAN_CODE
call_get_next_index_oidset (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp,
bool should_go_to_next_value)
{
DB_BIGINT *p_kl_upper = NULL;
DB_BIGINT *p_kl_lower = NULL;
int oids_cnt;
/*
* WHILE (true)
* {
* if (iss && should-skip-to-next-iss-value)
* obtain-next-iss-value() or return if it does not find anything;
*
* get-index-oidset();
*
* if (oids count == 0) // did not find anything
* {
* should-skip-to-next-iss-value = true;
* if (iss)
* continue; // to allow the while () to fetch the next value for the
* // first column in the index
* return S_END; // BTRS returned nothing and we are not in ISS mode. Leave.
* }
*
* break; //at least one OID found. get out of the loop.
*}
*/
while (1)
{
if (isidp->iss.use && should_go_to_next_value)
{
SCAN_CODE code = scan_get_next_iss_value (thread_p, scan_id, isidp);
if (code != S_SUCCESS)
{
/* anything wrong? or even end of scan? just leave */
return code;
}
}
p_kl_lower = isidp->key_limit_lower == -1 ? NULL : &isidp->key_limit_lower;
p_kl_upper = isidp->key_limit_upper == -1 ? NULL : &isidp->key_limit_upper;
if (scan_get_index_oidset (thread_p, scan_id, p_kl_upper, p_kl_lower) != NO_ERROR)
{
return S_ERROR;
}
oids_cnt = isidp->multi_range_opt.use ? isidp->multi_range_opt.cnt : isidp->oids_count;
if (oids_cnt == 0)
{
if (isidp->iss.use)
{
should_go_to_next_value = true;
continue;
}
return S_END; /* no ISS, no oids, this is the end of scan. */
}
/* We have at least one OID. Break the loop, allow normal processing. */
break;
}
return S_SUCCESS;
}
/*
* scan_next_scan_local () - The scan is moved to the next scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_scan_local (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
SCAN_CODE status;
bool on_trace;
UINT64 old_fetches = 0, old_ioreads = 0;
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL tv_diff;
SCAN_STATS old_scan_stats;
on_trace = thread_is_on_trace (thread_p);
if (on_trace)
{
tsc_getticks (&start_tick);
old_fetches = perfmon_get_from_statistic (thread_p, PSTAT_PB_NUM_FETCHES);
old_ioreads = perfmon_get_from_statistic (thread_p, PSTAT_PB_NUM_IOREADS);
if (scan_id->partition_stats != NULL)
{
memcpy (&old_scan_stats, &scan_id->scan_stats, sizeof (SCAN_STATS));
}
}
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_SAMPLING_SCAN:
status = scan_next_heap_scan (thread_p, scan_id);
break;
#if SERVER_MODE && !WINDOWS
case S_PARALLEL_HEAP_SCAN:
status = scan_next_parallel_heap_scan (thread_p, scan_id);
break;
#endif /* SERVER_MODE && !WINDOWS */
case S_HEAP_PAGE_SCAN:
status = scan_next_heap_page_scan (thread_p, scan_id);
break;
case S_CLASS_ATTR_SCAN:
status = scan_next_class_attr_scan (thread_p, scan_id);
break;
case S_INDX_SCAN:
status = scan_next_index_scan (thread_p, scan_id);
break;
case S_INDX_KEY_INFO_SCAN:
status = scan_next_index_key_info_scan (thread_p, scan_id);
break;
case S_INDX_NODE_INFO_SCAN:
status = scan_next_index_node_info_scan (thread_p, scan_id);
break;
case S_LIST_SCAN:
if (scan_id->s.llsid.hlsid.hash_list_scan_type != HASH_METH_NOT_USE)
{
status = scan_next_hash_list_scan (thread_p, scan_id);
}
else
{
status = scan_next_list_scan (thread_p, scan_id);
}
break;
case S_SHOWSTMT_SCAN:
status = scan_next_showstmt_scan (thread_p, scan_id);
break;
case S_VALUES_SCAN:
status = scan_next_value_scan (thread_p, scan_id);
break;
case S_SET_SCAN:
status = scan_next_set_scan (thread_p, scan_id);
break;
case S_JSON_TABLE_SCAN:
status = scan_next_json_table_scan (thread_p, scan_id);
break;
case S_METHOD_SCAN:
status = scan_next_method_scan (thread_p, scan_id);
break;
case S_DBLINK_SCAN:
status = scan_next_dblink_scan (thread_p, scan_id);
break;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
return S_ERROR;
}
if (on_trace)
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&tv_diff, end_tick, start_tick);
TSC_ADD_TIMEVAL (scan_id->scan_stats.elapsed_scan, tv_diff);
scan_id->scan_stats.num_fetches += perfmon_get_from_statistic (thread_p, PSTAT_PB_NUM_FETCHES) - old_fetches;
scan_id->scan_stats.num_ioreads += perfmon_get_from_statistic (thread_p, PSTAT_PB_NUM_IOREADS) - old_ioreads;
if (scan_id->partition_stats != NULL)
{
perfmon_add_timeval (&scan_id->partition_stats->elapsed_scan, &old_scan_stats.elapsed_scan,
&scan_id->scan_stats.elapsed_scan);
scan_id->partition_stats->num_fetches += scan_id->scan_stats.num_fetches - old_scan_stats.num_fetches;
scan_id->partition_stats->num_ioreads += scan_id->scan_stats.num_ioreads - old_scan_stats.num_ioreads;
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SCAN_RECORD_INFO:
case S_HEAP_SAMPLING_SCAN:
case S_LIST_SCAN:
scan_id->partition_stats->read_rows += scan_id->scan_stats.read_rows - old_scan_stats.read_rows;
scan_id->partition_stats->qualified_rows +=
scan_id->scan_stats.qualified_rows - old_scan_stats.qualified_rows;
perfmon_add_timeval (&scan_id->partition_stats->elapsed_hash_build, &old_scan_stats.elapsed_hash_build,
&scan_id->scan_stats.elapsed_hash_build);
break;
case S_INDX_SCAN:
scan_id->partition_stats->read_keys += scan_id->scan_stats.read_keys - old_scan_stats.read_keys;
scan_id->partition_stats->qualified_keys +=
scan_id->scan_stats.qualified_keys - old_scan_stats.qualified_keys;
scan_id->partition_stats->key_qualified_rows +=
scan_id->scan_stats.key_qualified_rows - old_scan_stats.key_qualified_rows;
scan_id->partition_stats->data_qualified_rows +=
scan_id->scan_stats.data_qualified_rows - old_scan_stats.data_qualified_rows;
perfmon_add_timeval (&scan_id->partition_stats->elapsed_lookup, &old_scan_stats.elapsed_lookup,
&scan_id->scan_stats.elapsed_lookup);
break;
default:
break;
}
assert (scan_id->partition_stats->covered_index == scan_id->scan_stats.covered_index);
assert (scan_id->partition_stats->multi_range_opt == scan_id->scan_stats.multi_range_opt);
assert (scan_id->partition_stats->index_skip_scan == scan_id->scan_stats.index_skip_scan);
assert (scan_id->partition_stats->loose_index_scan == scan_id->scan_stats.loose_index_scan);
assert (scan_id->partition_stats->noscan == scan_id->scan_stats.noscan);
assert (scan_id->partition_stats->agl == NULL);
}
}
return status;
}
typedef enum
{
OBJ_GET_WITHOUT_LOCK = 0,
OBJ_REPEAT_GET_WITH_LOCK = 1,
OBJ_GET_WITH_LOCK_COMPLETE = 2
} OBJECT_GET_STATUS;
/*
* scan_next_heap_scan () - The scan is moved to the next heap scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_heap_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
HEAP_SCAN_ID *hsidp;
FILTER_INFO data_filter;
RECDES recdes = RECDES_INITIALIZER;
SCAN_CODE sp_scan;
DB_LOGICAL ev_res;
OID current_oid, *p_current_oid = NULL;
MVCC_SCAN_REEV_DATA mvcc_sel_reev_data;
MVCC_REEV_DATA mvcc_reev_data;
UPDDEL_MVCC_COND_REEVAL upd_reev;
OID retry_oid;
LOG_LSA ref_lsa;
bool is_peeking;
OBJECT_GET_STATUS object_get_status;
regu_variable_list_node *p;
hsidp = &scan_id->s.hsid;
p_current_oid = &hsidp->curr_oid;
if (scan_id->mvcc_select_lock_needed)
{
COPY_OID (¤t_oid, &hsidp->curr_oid);
p_current_oid = ¤t_oid;
}
else
{
p_current_oid = &hsidp->curr_oid;
}
/* set data filter information */
data_filter =
{
&hsidp->scan_pred,
&hsidp->pred_attrs, NULL, NULL, scan_id->val_list, scan_id->vd, &hsidp->cls_oid, NULL, NULL, NULL, 0, -1};
is_peeking = scan_id->fixed;
if (scan_id->grouped)
{
is_peeking = PEEK;
}
while (1)
{
COPY_OID (&retry_oid, &hsidp->curr_oid);
object_get_status = OBJ_GET_WITHOUT_LOCK;
restart_scan_oid:
/* get next object */
if (scan_id->grouped)
{
/* grouped, fixed scan */
sp_scan = heap_scanrange_next (thread_p, &hsidp->curr_oid, &recdes, &hsidp->scan_range, is_peeking);
}
else
{
recdes.data = NULL;
if (scan_id->direction == S_FORWARD)
{
/* move forward */
if (scan_id->type == S_HEAP_SCAN)
{
sp_scan =
heap_next (thread_p, &hsidp->hfid, &hsidp->cls_oid, &hsidp->curr_oid, &recdes, &hsidp->scan_cache,
is_peeking);
}
else if (scan_id->type == S_HEAP_SAMPLING_SCAN)
{
sp_scan =
heap_next_sampling (thread_p, &hsidp->hfid, &hsidp->cls_oid, &hsidp->curr_oid, &recdes,
&hsidp->scan_cache, is_peeking, &hsidp->sampling);
}
else
{
assert (scan_id->type == S_HEAP_SCAN_RECORD_INFO);
sp_scan =
heap_next_record_info (thread_p, &hsidp->hfid, &hsidp->cls_oid, &hsidp->curr_oid, &recdes,
&hsidp->scan_cache, is_peeking, hsidp->cache_recordinfo);
}
}
else
{
/* move backward */
if (scan_id->type == S_HEAP_SCAN)
{
sp_scan =
heap_prev (thread_p, &hsidp->hfid, &hsidp->cls_oid, &hsidp->curr_oid, &recdes, &hsidp->scan_cache,
is_peeking);
}
else
{
assert (scan_id->type == S_HEAP_SCAN_RECORD_INFO);
sp_scan =
heap_prev_record_info (thread_p, &hsidp->hfid, &hsidp->cls_oid, &hsidp->curr_oid, &recdes,
&hsidp->scan_cache, is_peeking, hsidp->cache_recordinfo);
}
}
}
if (sp_scan != S_SUCCESS)
{
/* scan error or end of scan */
return (sp_scan == S_END) ? S_END : S_ERROR;
}
if (hsidp->scan_cache.page_watcher.pgptr != NULL)
{
LSA_COPY (&ref_lsa, pgbuf_get_lsa (hsidp->scan_cache.page_watcher.pgptr));
}
/* evaluate the predicates to see if the object qualifies */
scan_id->scan_stats.read_rows++;
ev_res = eval_data_filter (thread_p, p_current_oid, &recdes, &hsidp->scan_cache, &data_filter);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
if (is_peeking == PEEK && hsidp->scan_cache.page_watcher.pgptr != NULL
&& PGBUF_IS_PAGE_CHANGED (hsidp->scan_cache.page_watcher.pgptr, &ref_lsa))
{
is_peeking = COPY;
COPY_OID (&hsidp->curr_oid, &retry_oid);
goto restart_scan_oid;
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
/* Data filter passed. If object should be locked and is not locked yet, lock it. */
if (scan_id->mvcc_select_lock_needed)
{
/* data filter already initialized, don't have key or range init scan reevaluation structure */
upd_reev.init (*scan_id);
mvcc_sel_reev_data.set_filters (upd_reev);
mvcc_sel_reev_data.qualification = &scan_id->qualification;
mvcc_reev_data.set_scan_reevaluation (mvcc_sel_reev_data);
COPY_OID (¤t_oid, &hsidp->curr_oid);
if (scan_id->fixed)
{
/* Reset recdes.data */
recdes.data = NULL;
}
/* get with lock and reevaluate if the visible version wasn't the latest version */
sp_scan =
locator_lock_and_get_object_with_evaluation (thread_p, ¤t_oid, NULL, &recdes, &hsidp->scan_cache,
is_peeking, NULL_CHN, &mvcc_reev_data, LOG_WARNING_IF_DELETED);
if (sp_scan == S_SUCCESS && mvcc_reev_data.filter_result == V_FALSE)
{
continue;
}
else if (er_errid () == ER_HEAP_UNKNOWN_OBJECT || sp_scan == S_DOESNT_EXIST)
{
er_clear ();
continue;
}
else if (sp_scan != S_SUCCESS)
{
return S_ERROR;
}
}
assert (OID_EQ (&hsidp->cls_oid, &hsidp->scan_cache.node.class_oid));
if (hsidp->scan_cache.mvcc_disabled_class)
{
LOCK lock = NULL_LOCK;
int tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
TRAN_ISOLATION tran_isolation = logtb_find_isolation (tran_index);
if (scan_id->scan_op_type == S_DELETE || scan_id->scan_op_type == S_UPDATE)
{
lock = X_LOCK;
}
else if (oid_is_serial (&hsidp->cls_oid))
{
/* S_SELECT is currently handled only for serial, but may be extended to the other non-MVCC classes
* if needed */
lock = S_LOCK;
}
if (lock != NULL_LOCK && hsidp->scan_cache.page_watcher.pgptr != NULL)
{
if (tran_isolation == TRAN_READ_COMMITTED && lock == S_LOCK)
{
if (lock_hold_object_instant (thread_p, &hsidp->curr_oid, &hsidp->cls_oid, lock) == LK_GRANTED)
{
lock = NULL_LOCK;
/* object_need_rescan needs to be kept false (page is still fixed, no other transaction could
* have change it) */
}
}
else
{
if (lock_object (thread_p, &hsidp->curr_oid, &hsidp->cls_oid, lock, LK_COND_LOCK) == LK_GRANTED)
{
/* successfully locked */
lock = NULL_LOCK;
/* object_need_rescan needs to be kept false (page is still fixed, no other transaction could
* have change it) */
}
}
}
if (lock != NULL_LOCK)
{
VPID curr_vpid;
VPID_SET_NULL (&curr_vpid);
if (hsidp->scan_cache.page_watcher.pgptr != NULL)
{
pgbuf_get_vpid (hsidp->scan_cache.page_watcher.pgptr, &curr_vpid);
pgbuf_ordered_unfix (thread_p, &hsidp->scan_cache.page_watcher);
}
#if defined (SERVER_MODE)
else
{
if (object_get_status == OBJ_GET_WITHOUT_LOCK)
{
/* page not fixed, recdes was read without lock, object may have changed */
object_get_status = OBJ_REPEAT_GET_WITH_LOCK;
}
else if (object_get_status == OBJ_REPEAT_GET_WITH_LOCK)
{
/* already read with lock, set flag to continue scanning next object */
object_get_status = OBJ_GET_WITH_LOCK_COMPLETE;
}
}
#endif
if (lock_object (thread_p, &hsidp->curr_oid, &hsidp->cls_oid, lock, LK_UNCOND_LOCK) != LK_GRANTED)
{
return S_ERROR;
}
if (!heap_does_exist (thread_p, NULL, &hsidp->curr_oid))
{
/* not qualified, continue to the next tuple */
lock_unlock_object_donot_move_to_non2pl (thread_p, &hsidp->curr_oid, &hsidp->cls_oid, lock);
continue;
}
if (tran_isolation == TRAN_READ_COMMITTED && lock == S_LOCK)
{
/* release acquired lock in RC */
lock_unlock_object_donot_move_to_non2pl (thread_p, &hsidp->curr_oid, &hsidp->cls_oid, lock);
}
assert (hsidp->scan_cache.page_watcher.pgptr == NULL);
if (!VPID_ISNULL (&curr_vpid)
&& pgbuf_ordered_fix (thread_p, &curr_vpid, OLD_PAGE, PGBUF_LATCH_READ,
&hsidp->scan_cache.page_watcher) != NO_ERROR)
{
return S_ERROR;
}
if (object_get_status == OBJ_REPEAT_GET_WITH_LOCK
|| (hsidp->scan_cache.page_watcher.pgptr != NULL
&& PGBUF_IS_PAGE_CHANGED (hsidp->scan_cache.page_watcher.pgptr, &ref_lsa)))
{
is_peeking = COPY;
COPY_OID (&hsidp->curr_oid, &retry_oid);
goto restart_scan_oid;
}
}
}
scan_id->scan_stats.qualified_rows++;
if (hsidp->rest_regu_list)
{
/* read the rest of the values from the heap into the attribute cache */
if (heap_attrinfo_read_dbvalues (thread_p, p_current_oid, &recdes, hsidp->rest_attrs.attr_cache) != NO_ERROR)
{
return S_ERROR;
}
if (is_peeking == PEEK && hsidp->scan_cache.page_watcher.pgptr != NULL
&& PGBUF_IS_PAGE_CHANGED (hsidp->scan_cache.page_watcher.pgptr, &ref_lsa))
{
is_peeking = COPY;
COPY_OID (&hsidp->curr_oid, &retry_oid);
goto restart_scan_oid;
}
/* fetch the rest of the values from the object instance */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, hsidp->rest_regu_list, scan_id->vd, &hsidp->cls_oid, p_current_oid, NULL,
PEEK) != NO_ERROR)
{
return S_ERROR;
}
if (is_peeking != 0 && hsidp->scan_cache.page_watcher.pgptr != NULL
&& PGBUF_IS_PAGE_CHANGED (hsidp->scan_cache.page_watcher.pgptr, &ref_lsa))
{
is_peeking = COPY;
COPY_OID (&hsidp->curr_oid, &retry_oid);
goto restart_scan_oid;
}
}
}
if (hsidp->recordinfo_regu_list != NULL)
{
/* fetch the record info values */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, hsidp->recordinfo_regu_list, scan_id->vd, &hsidp->cls_oid, p_current_oid,
NULL, PEEK) != NO_ERROR)
{
return S_ERROR;
}
if (is_peeking == PEEK && hsidp->scan_cache.page_watcher.pgptr != NULL
&& PGBUF_IS_PAGE_CHANGED (hsidp->scan_cache.page_watcher.pgptr, &ref_lsa))
{
is_peeking = COPY;
COPY_OID (&hsidp->curr_oid, &retry_oid);
goto restart_scan_oid;
}
}
}
return S_SUCCESS;
}
}
/*
* scan_next_heap_page_scan () - The scan is moved to the next page.
*
* return : Error code.
* thread_p (in) : Thread entry.
* scan_id (in) : Scan data.
*/
static SCAN_CODE
scan_next_heap_page_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
HEAP_PAGE_SCAN_ID *hpsidp = NULL;
FILTER_INFO data_filter;
SCAN_CODE sp_scan;
DB_LOGICAL ev_res;
hpsidp = &scan_id->s.hpsid;
data_filter =
{
&hpsidp->scan_pred, NULL, NULL, NULL, scan_id->val_list, scan_id->vd, &hpsidp->cls_oid, NULL, NULL, NULL, 0, -1};
while (true)
{
if (scan_id->direction == S_FORWARD)
{
/* move forward */
sp_scan = heap_page_next (thread_p, &hpsidp->cls_oid, &hpsidp->hfid, &hpsidp->curr_vpid,
hpsidp->cache_page_info);
}
else
{
/* move backward */
sp_scan = heap_page_prev (thread_p, &hpsidp->cls_oid, &hpsidp->hfid, &hpsidp->curr_vpid,
hpsidp->cache_page_info);
}
if (sp_scan != S_SUCCESS)
{
return (sp_scan == S_END) ? S_END : S_ERROR;
}
/* evaluate filter to see if the page qualifies */
ev_res = eval_data_filter (thread_p, &hpsidp->cls_oid, NULL, NULL, &data_filter);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
else if (ev_res != V_TRUE)
{
/* V_FALSE || V_UNKNOWN */
continue;
}
if (hpsidp->page_info_regu_list != NULL)
{
/* fetch the page info values */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, hpsidp->page_info_regu_list, scan_id->vd, &hpsidp->cls_oid, NULL, NULL,
PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
}
return S_SUCCESS;
}
}
/*
* scan_next_class_attr_scan () - The scan is moved to the next class attribute scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_class_attr_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
HEAP_SCAN_ID *hsidp;
FILTER_INFO data_filter;
DB_LOGICAL ev_res;
hsidp = &scan_id->s.hsid;
data_filter =
{
&hsidp->scan_pred,
&hsidp->pred_attrs, NULL, NULL, scan_id->val_list, scan_id->vd, &hsidp->cls_oid, NULL, NULL, NULL, 0, -1};
if (scan_id->position == S_BEFORE)
{
/* Class attribute scans are always single row scan. */
scan_id->position = S_AFTER;
/* evaluate the predicates to see if the object qualifies */
ev_res = eval_data_filter (thread_p, NULL, NULL, NULL, &data_filter);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE)
{ /* V_FALSE || V_UNKNOWN */
return S_END; /* not qualified */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE)
{ /* V_TRUE || V_UNKNOWN */
return S_END; /* qualified */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE)
{ /* V_FALSE || V_UNKNOWN */
return S_END; /* not qualified */
}
}
if (hsidp->rest_regu_list)
{
/* read the rest of the values from the heap into the attribute cache */
if (heap_attrinfo_read_dbvalues (thread_p, NULL, NULL, hsidp->rest_attrs.attr_cache) != NO_ERROR)
{
return S_ERROR;
}
/* fetch the rest of the values from the object instance */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, hsidp->rest_regu_list, scan_id->vd, &hsidp->cls_oid, NULL, NULL, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
}
return S_SUCCESS;
}
else
{
/* Class attribute scans are always single row scan. */
return S_END;
}
}
/*
* scan_next_index_scan () - The scan is moved to the next index scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_index_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
INDX_SCAN_ID *isidp;
FILTER_INFO data_filter;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
SCAN_CODE lookup_status;
TRAN_ISOLATION isolation;
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL tv_diff;
isidp = &scan_id->s.isid;
assert (!OID_ISNULL (&isidp->cls_oid));
/* multi range optimization safe guard : fall-back to normal output (OID list or covering index instead of "on the
* fly" lists), if sorting column is not yet set at this stage; also 'grouped' is not supported */
if (isidp->multi_range_opt.use && (isidp->multi_range_opt.sort_att_idx == NULL || scan_id->grouped))
{
isidp->multi_range_opt.use = false;
scan_id->scan_stats.multi_range_opt = false;
}
/* set data filter information */
data_filter =
{
&isidp->scan_pred,
&isidp->pred_attrs, NULL, NULL, scan_id->val_list, scan_id->vd, &isidp->cls_oid, NULL, NULL, NULL, 0, -1};
/* Due to the length of time that we hold onto the oid list, it is possible at lower isolation levels (UNCOMMITTED
* INSTANCES) that the index/heap may have changed since the oid list was read from the btree. In particular, some
* of the instances that we are reading may have been deleted by the time we go to fetch them via heap_get_visible_version ().
* According to the semantics of UNCOMMITTED, it is ok if they are deleted out from under us and we can ignore the
* SCAN_DOESNT_EXIST error. */
isolation = logtb_find_current_isolation (thread_p);
while (1)
{
/* get next object from OID list */
if (scan_id->grouped)
{
assert (isidp->oid_list != NULL);
/* grouped scan */
if (scan_id->direction == S_FORWARD)
{
/* move forward (to the next object) */
if (isidp->curr_oidno == -1)
{
isidp->curr_oidno = 0; /* first oid number */
isidp->curr_oidp = isidp->oid_list->oidp;
}
else if (isidp->curr_oidno < isidp->oids_count - 1)
{
isidp->curr_oidno++;
isidp->curr_oidp++;
}
else
{
return S_END;
}
}
else
{
/* move backward (to the previous object */
if (isidp->curr_oidno == isidp->oids_count)
{
isidp->curr_oidno = isidp->oids_count - 1;
isidp->curr_oidp = GET_NTH_OID (isidp->oid_list->oidp, isidp->curr_oidno);
}
else if (isidp->curr_oidno > 0)
{
isidp->curr_oidno--;
isidp->curr_oidp = GET_NTH_OID (isidp->oid_list->oidp, isidp->curr_oidno);
}
else
{
return S_END;
}
}
}
else
{
/* non-grouped, regular index scan */
if (scan_id->position == S_BEFORE)
{
SCAN_CODE ret;
/* Either we are not using ISS, or we are using it, and in this case, we are supposed to be here for the
* first time */
assert_release (!isidp->iss.use || isidp->iss.current_op == ISS_OP_NONE);
ret = call_get_next_index_oidset (thread_p, scan_id, isidp, true);
if (ret != S_SUCCESS)
{
return ret;
}
scan_id->position = S_ON;
isidp->curr_oidno = 0; /* first oid number */
if (isidp->need_count_only == true)
{
/* no more scan is needed. just return */
return S_SUCCESS;
}
if (SCAN_IS_INDEX_COVERED (isidp))
{
qfile_close_list (thread_p, isidp->indx_cov.list_id);
if (qfile_open_list_scan (isidp->indx_cov.list_id, isidp->indx_cov.lsid) != NO_ERROR)
{
return S_ERROR;
}
}
else
{
if (isidp->multi_range_opt.use)
{
assert (isidp->curr_oidno < isidp->multi_range_opt.cnt);
assert (isidp->multi_range_opt.top_n_items[isidp->curr_oidno] != NULL);
isidp->curr_oidp = &(isidp->multi_range_opt.top_n_items[isidp->curr_oidno]->inst_oid);
}
else
{
assert (isidp->oid_list != NULL);
isidp->curr_oidp = GET_NTH_OID (isidp->oid_list->oidp, isidp->curr_oidno);
}
assert (HEAP_ISVALID_OID (thread_p, isidp->curr_oidp) != DISK_INVALID);
}
}
else if (scan_id->position == S_GO_BACKWARD)
{
SCAN_CODE ret;
isidp->curr_keyno = -1;
if (SCAN_IS_INDEX_COVERED (isidp))
{
isidp->key_limit_upper = 1;
qfile_reopen_list_as_append_mode (thread_p, isidp->indx_cov.list_id);
}
ret = call_get_next_index_oidset (thread_p, scan_id, isidp, true);
if (ret != S_SUCCESS)
{
return ret;
}
isidp->curr_oidno = 0; /* first oid number */
scan_id->position = S_ON;
}
else if (scan_id->position == S_ON)
{
int oids_cnt;
/* we are in the S_ON case */
oids_cnt = isidp->multi_range_opt.use ? isidp->multi_range_opt.cnt : isidp->oids_count;
/* if there are OIDs left */
if (isidp->curr_oidno < oids_cnt - 1)
{
isidp->curr_oidno++;
if (isidp->need_count_only == true)
{
/* no more scan is needed. just return */
return S_SUCCESS;
}
if (!SCAN_IS_INDEX_COVERED (isidp))
{
if (isidp->multi_range_opt.use)
{
assert (isidp->curr_oidno < isidp->multi_range_opt.cnt);
assert (isidp->multi_range_opt.top_n_items[isidp->curr_oidno] != NULL);
isidp->curr_oidp = &(isidp->multi_range_opt.top_n_items[isidp->curr_oidno]->inst_oid);
}
else
{
assert (isidp->oid_list != NULL);
isidp->curr_oidp = GET_NTH_OID (isidp->oid_list->oidp, isidp->curr_oidno);
}
assert (HEAP_ISVALID_OID (thread_p, isidp->curr_oidp) != DISK_INVALID);
}
else
{
/* TODO: Index covering case keeps OID's in isidp->indx_cov.list_id and not in isidp->oid_list.
* Fall through. */
}
}
else
{
/* there are no more OIDs left. Decide what to do */
/* We can ignore the END OF SCAN signal if we're certain there can be more results, for instance if
* we have a multiple range scan, or if we have the "index skip scan" optimization on */
if (BTREE_END_OF_SCAN (&isidp->bt_scan) && isidp->indx_info->range_type != R_RANGELIST
&& isidp->indx_info->range_type != R_KEYLIST && !isidp->iss.use)
{
return S_END;
}
else
{
SCAN_CODE ret;
bool go_to_next_iss_value;
/* a list in a range is exhausted */
if (isidp->multi_range_opt.use)
{
/* for "on the fly" case (multi range opt), all ranges are exhausted from first shoot, force
* exit */
isidp->oids_count = 0;
return S_END;
}
if (SCAN_IS_INDEX_COVERED (isidp))
{
INDX_COV *indx_cov_p = &isidp->indx_cov;
static int temp_cache_max_pages = prm_get_integer_value (PRM_ID_MAX_PAGES_IN_TEMP_FILE_CACHE);
qfile_close_scan (thread_p, indx_cov_p->lsid);
if (indx_cov_p->list_id->page_cnt - indx_cov_p->list_id->tfile_vfid->membuf_npages >
temp_cache_max_pages)
{
/* close current list and start a new one */
qfile_destroy_list (thread_p, indx_cov_p->list_id);
indx_cov_p->list_id =
qfile_open_list (thread_p, indx_cov_p->type_list, NULL, indx_cov_p->query_id, 0,
indx_cov_p->list_id);
if (indx_cov_p->list_id == NULL)
{
return S_ERROR;
}
}
else if (qfile_truncate_list (thread_p, indx_cov_p->list_id) != NO_ERROR)
{
return S_ERROR;
}
}
/* if this the current scan is not done (i.e. the buffer was full and we need to fetch more rows,
* do not go to the next value */
go_to_next_iss_value = BTREE_END_OF_SCAN (&isidp->bt_scan)
&& (isidp->indx_info->range_type == R_KEY || isidp->indx_info->range_type == R_RANGE);
ret = call_get_next_index_oidset (thread_p, scan_id, isidp, go_to_next_iss_value);
if (ret != S_SUCCESS)
{
return ret;
}
isidp->curr_oidno = 0; /* first oid number */
if (isidp->need_count_only == true)
{
/* no more scan is needed. just return */
return S_SUCCESS;
}
if (SCAN_IS_INDEX_COVERED (isidp))
{
qfile_close_list (thread_p, isidp->indx_cov.list_id);
if (qfile_open_list_scan (isidp->indx_cov.list_id, isidp->indx_cov.lsid) != NO_ERROR)
{
return S_ERROR;
}
}
else
{
assert (isidp->oid_list != NULL);
isidp->curr_oidp = isidp->oid_list->oidp;
assert (HEAP_ISVALID_OID (thread_p, isidp->curr_oidp) != DISK_INVALID);
}
}
}
}
else if (scan_id->position == S_AFTER)
{
return S_END;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_UNKNOWN_CRSPOS, 0);
return S_ERROR;
}
}
assert (scan_id->position == S_ON);
scan_id->scan_stats.key_qualified_rows++;
/* get pages for read */
if (!SCAN_IS_INDEX_COVERED (isidp))
{
perfmon_inc_stat (thread_p, PSTAT_BT_NUM_NONCOVERED);
assert (isidp->curr_oidno >= 0);
assert (isidp->curr_oidp != NULL);
assert (HEAP_ISVALID_OID (thread_p, isidp->curr_oidp) != DISK_INVALID);
if (thread_is_on_trace (thread_p))
{
tsc_getticks (&start_tick);
}
lookup_status = scan_next_index_lookup_heap (thread_p, scan_id, isidp, &data_filter, isolation);
if (thread_is_on_trace (thread_p))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&tv_diff, end_tick, start_tick);
TSC_ADD_TIMEVAL (scan_id->scan_stats.elapsed_lookup, tv_diff);
}
if (lookup_status == S_SUCCESS)
{
scan_id->scan_stats.data_qualified_rows++;
}
else if (lookup_status == S_DOESNT_EXIST)
{
if (scan_id->mvcc_select_lock_needed && isidp->key_limit_upper != -1)
{
isidp->key_limit_upper++;
}
/* not qualified, continue to the next tuple */
continue;
}
else
{
/* S_ERROR, S_END */
return lookup_status;
}
}
else
{
/* TO DO - in MVCC when mvcc_select_lock_needed is true index coverage must be disabled */
if (isidp->multi_range_opt.use)
{
assert (isidp->curr_oidno < isidp->multi_range_opt.cnt);
assert (isidp->multi_range_opt.top_n_items[isidp->curr_oidno] != NULL);
if (scan_dump_key_into_tuple (thread_p, isidp,
&(isidp->multi_range_opt.top_n_items[isidp->curr_oidno]->index_value),
isidp->curr_oidp, &isidp->multi_range_opt.tplrec) != NO_ERROR)
{
return S_ERROR;
}
tplrec.tpl = isidp->multi_range_opt.tplrec.tpl;
tplrec.size = isidp->multi_range_opt.tplrec.size;
}
else
{
if (qfile_scan_list_next (thread_p, isidp->indx_cov.lsid, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
}
perfmon_inc_stat (thread_p, PSTAT_BT_NUM_COVERED);
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, isidp->indx_cov.regu_val_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
}
return S_SUCCESS;
}
}
/*
* scan_next_index_lookup_heap () - fetch heap record and evaluate data filter
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR, S_DOESNT_EXIST)
* scan_id(in/out): Scan identifier
* isidp(in/out): Index scan identifier
* data_filter(in): data filter information
* isolation(in): transaction isolation level
*
* Note: If the tuple is not qualified for data filter, S_DOESNT_EXIST is returned.
*/
static SCAN_CODE
scan_next_index_lookup_heap (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, INDX_SCAN_ID * isidp,
FILTER_INFO * data_filter, TRAN_ISOLATION isolation)
{
SCAN_CODE sp_scan;
DB_LOGICAL ev_res;
RECDES recdes = RECDES_INITIALIZER;
indx_info *indx_infop;
BTID *btid;
char *indx_name_p;
char *class_name_p;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
assert (scan_id != NULL);
assert (isidp != NULL);
assert (isidp->curr_oidp != NULL && !OID_ISNULL (isidp->curr_oidp));
if (scan_id->fixed == false)
{
recdes.data = NULL;
}
sp_scan = heap_get_visible_version (thread_p, isidp->curr_oidp, NULL, &recdes, &isidp->scan_cache, scan_id->fixed,
NULL_CHN);
if (sp_scan == S_SNAPSHOT_NOT_SATISFIED)
{
if (SCAN_IS_INDEX_COVERED (isidp))
{
/* goto the next tuple */
if (!isidp->multi_range_opt.use)
{
if (qfile_scan_list_next (thread_p, isidp->indx_cov.lsid, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
}
}
return S_DOESNT_EXIST; /* not qualified, continue to the next tuple */
}
else if (sp_scan == S_ERROR)
{
ASSERT_ERROR ();
return sp_scan;
}
else
{
assert (sp_scan == S_SUCCESS || sp_scan == S_SUCCESS_CHN_UPTODATE);
}
/* evaluate the predicates to see if the object qualifies */
ev_res = eval_data_filter (thread_p, isidp->curr_oidp, &recdes, &isidp->scan_cache, data_filter);
// no key filter evaluation is required here.
ev_res = update_logical_result (thread_p, ev_res, (int *) &scan_id->qualification);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
else if (ev_res != V_TRUE)
{
return S_DOESNT_EXIST;
}
if (scan_id->mvcc_select_lock_needed)
{
UPDDEL_MVCC_COND_REEVAL upd_reev;
MVCC_SCAN_REEV_DATA mvcc_sel_reev_data;
MVCC_REEV_DATA mvcc_reev_data;
upd_reev.init (*scan_id);
mvcc_sel_reev_data.set_filters (upd_reev);
mvcc_sel_reev_data.qualification = &scan_id->qualification;
mvcc_reev_data.set_scan_reevaluation (mvcc_sel_reev_data);
sp_scan = locator_lock_and_get_object_with_evaluation (thread_p, isidp->curr_oidp, NULL, &recdes,
&isidp->scan_cache, scan_id->fixed, NULL_CHN,
&mvcc_reev_data, LOG_WARNING_IF_DELETED);
if (sp_scan == S_SUCCESS)
{
switch (mvcc_reev_data.filter_result)
{
case V_ERROR:
return S_ERROR;
case V_FALSE:
return S_DOESNT_EXIST;
default:
break;
}
}
}
if (sp_scan == S_DOESNT_EXIST || er_errid () == ER_HEAP_UNKNOWN_OBJECT)
{
er_clear ();
if (SCAN_IS_INDEX_COVERED (isidp))
{
/* goto the next tuple */
if (!isidp->multi_range_opt.use)
{
if (qfile_scan_list_next (thread_p, isidp->indx_cov.lsid, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
}
}
return S_DOESNT_EXIST; /* not qualified, continue to the next tuple */
}
if (sp_scan != S_SUCCESS && sp_scan != S_SNAPSHOT_NOT_SATISFIED)
{
/* check end of scan */
if (sp_scan == S_END)
{
assert (false); /* is impossible case */
return S_END;
}
indx_infop = isidp->indx_info;
btid = &indx_infop->btid;
indx_name_p = NULL;
class_name_p = NULL;
/* check scan error */
if (er_errid () == NO_ERROR)
{
(void) heap_get_indexinfo_of_btid (thread_p, &isidp->cls_oid, btid, NULL, NULL, NULL, NULL, &indx_name_p,
NULL);
if (heap_get_class_name (thread_p, &isidp->cls_oid, &class_name_p) != NO_ERROR)
{
/* ignore */
er_clear ();
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_LC_INCONSISTENT_BTREE_ENTRY_TYPE2, 11,
(indx_name_p) ? indx_name_p : "*UNKNOWN-INDEX*", (class_name_p) ? class_name_p : "*UNKNOWN-CLASS*",
isidp->cls_oid.volid, isidp->cls_oid.pageid, isidp->cls_oid.slotid, isidp->curr_oidp->volid,
isidp->curr_oidp->pageid, isidp->curr_oidp->slotid, btid->vfid.volid, btid->vfid.fileid,
btid->root_pageid);
if (class_name_p)
{
free_and_init (class_name_p);
}
if (indx_name_p)
{
free_and_init (indx_name_p);
}
}
return S_ERROR;
}
assert (OID_EQ (&isidp->cls_oid, &isidp->scan_cache.node.class_oid));
if (!scan_id->mvcc_select_lock_needed && isidp->scan_cache.mvcc_disabled_class)
{
/* Data filter passed. If object should be locked and is not locked yet, lock it. */
LOCK lock = NULL_LOCK;
int tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
TRAN_ISOLATION tran_isolation = logtb_find_isolation (tran_index);
if (scan_id->scan_op_type == S_DELETE || scan_id->scan_op_type == S_UPDATE)
{
lock = X_LOCK;
}
else if (oid_is_serial (&isidp->cls_oid))
{
/* S_SELECT is currently handled only for serial, but may be extended to the other non-MVCC classes if
* needed */
lock = S_LOCK;
}
if (lock != NULL_LOCK)
{
if (tran_isolation == TRAN_READ_COMMITTED && lock == S_LOCK)
{
if (lock_hold_object_instant (thread_p, isidp->curr_oidp, &isidp->cls_oid, lock) == LK_GRANTED)
{
lock = NULL_LOCK;
}
}
else
{
if (lock_object (thread_p, isidp->curr_oidp, &isidp->cls_oid, lock, LK_COND_LOCK) == LK_GRANTED)
{
/* successfully locked */
lock = NULL_LOCK;
}
}
}
if (lock != NULL_LOCK)
{
if (isidp->scan_cache.page_watcher.pgptr != NULL)
{
pgbuf_ordered_unfix (thread_p, &isidp->scan_cache.page_watcher);
}
if (lock_object (thread_p, isidp->curr_oidp, &isidp->cls_oid, lock, LK_UNCOND_LOCK) != LK_GRANTED)
{
return S_ERROR;
}
if (!heap_does_exist (thread_p, NULL, isidp->curr_oidp))
{
/* not qualified, continue to the next tuple */
lock_unlock_object_donot_move_to_non2pl (thread_p, isidp->curr_oidp, &isidp->cls_oid, lock);
return S_DOESNT_EXIST;
}
if (tran_isolation == TRAN_READ_COMMITTED && lock == S_LOCK)
{
/* release acquired lock in RC */
lock_unlock_object_donot_move_to_non2pl (thread_p, isidp->curr_oidp, &isidp->cls_oid, lock);
}
}
}
if (isidp->rest_regu_list)
{
/* read the rest of the values from the heap into the attribute cache */
if (heap_attrinfo_read_dbvalues (thread_p, isidp->curr_oidp, &recdes, isidp->rest_attrs.attr_cache) != NO_ERROR)
{
return S_ERROR;
}
/* fetch the rest of the values from the object instance */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, isidp->rest_regu_list, scan_id->vd, &isidp->cls_oid, isidp->curr_oidp, NULL,
PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
}
return S_SUCCESS;
}
/*
* scan_next_index_key_info_scan () - Scans each key in index and obtains
* information about that key.
*
* return : Scan code.
* thread_p (in) : Thread entry.
* scan_id (in) : Scan data.
*/
static SCAN_CODE
scan_next_index_key_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
INDX_SCAN_ID *isidp = NULL;
FILTER_INFO data_filter;
SCAN_CODE sp_scan;
DB_LOGICAL ev_res;
isidp = &scan_id->s.isid;
data_filter =
{
&isidp->scan_pred, NULL, NULL, NULL, scan_id->val_list, scan_id->vd, &isidp->cls_oid, NULL, NULL, NULL, 0, -1};
while (true)
{
sp_scan =
btree_get_next_key_info (thread_p, &isidp->indx_info->btid, &isidp->bt_scan, 1, &isidp->cls_oid,
isidp, isidp->key_info_values);
if (sp_scan != S_SUCCESS)
{
return (sp_scan == S_END) ? S_END : S_ERROR;
}
ev_res = eval_data_filter (thread_p, NULL, NULL, NULL, &data_filter);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
else if (ev_res != V_TRUE)
{
/* V_FALSE || V_UNKNOWN */
continue;
}
if (isidp->key_info_regu_list != NULL && scan_id->val_list != NULL)
{
if (fetch_val_list (thread_p, isidp->key_info_regu_list, scan_id->vd, &isidp->cls_oid, NULL, NULL, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
return S_SUCCESS;
}
}
/*
* scan_next_index_node_info_scan () - Scans for nodes in b-tree and obtains
* information about the nodes.
*
* return : Scan code.
* thread_p (in) : Thread entry.
* scan_id (in) : Scan data.
*/
static SCAN_CODE
scan_next_index_node_info_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
INDEX_NODE_SCAN_ID *insidp = NULL;
FILTER_INFO data_filter;
SCAN_CODE sp_scan;
DB_LOGICAL ev_res;
insidp = &scan_id->s.insid;
data_filter =
{
&insidp->scan_pred, NULL, NULL, NULL, scan_id->val_list, scan_id->vd, NULL, NULL, NULL, NULL, 0, -1};
while (true)
{
sp_scan = btree_get_next_node_info (thread_p, &insidp->indx_info->btid, &insidp->btns, insidp->node_info_values);
if (sp_scan != S_SUCCESS)
{
return (sp_scan == S_END) ? S_END : S_ERROR;
}
ev_res = eval_data_filter (thread_p, NULL, NULL, NULL, &data_filter);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
else if (ev_res != V_TRUE)
{
/* V_FALSE || V_UNKNOWN */
continue;
}
if (insidp->node_info_regu_list != NULL && scan_id->val_list != NULL)
{
if (fetch_val_list (thread_p, insidp->node_info_regu_list, scan_id->vd, NULL, NULL, NULL, PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
return S_SUCCESS;
}
}
/*
* scan_next_list_scan () - The scan is moved to the next list scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
LLIST_SCAN_ID *llsidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
llsidp = &scan_id->s.llsid;
tplrec.size = 0;
tplrec.tpl = (QFILE_TUPLE) NULL;
resolve_domains_on_list_scan (llsidp, scan_id->val_list);
while ((qp_scan = qfile_scan_list_next (thread_p, &llsidp->lsid, &tplrec, PEEK)) == S_SUCCESS)
{
/* fetch the values for the predicate from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->scan_pred.regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
scan_id->scan_stats.read_rows++;
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (llsidp->scan_pred.pr_eval_fnc && llsidp->scan_pred.pred_expr)
{
ev_res = (*llsidp->scan_pred.pr_eval_fnc) (thread_p, llsidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
scan_id->scan_stats.qualified_rows++;
/* fetch the rest of the values from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->rest_regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
if (llsidp->tplrecp)
{
llsidp->tplrecp->size = tplrec.size;
llsidp->tplrecp->tpl = tplrec.tpl;
}
return S_SUCCESS;
}
return qp_scan;
}
/*
* scan_next_value_scan () - The scan is moved to the next value scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_value_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
REGU_VALUES_SCAN_ID *rvsidp;
regu_variable_list_node *list_node;
REGU_VALUE_LIST *regu_value_list;
int i;
rvsidp = &scan_id->s.rvsid;
if (scan_id->position == S_BEFORE)
{
scan_id->position = S_ON;
}
else if (scan_id->position == S_ON)
{
for (i = 0, list_node = rvsidp->regu_list; list_node; ++i, list_node = list_node->next)
{
regu_value_list = list_node->value.value.reguval_list;
if (regu_value_list == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_CRSPOS, 0);
return S_ERROR;
}
assert (regu_value_list->current_value != NULL);
regu_value_list->current_value = regu_value_list->current_value->next;
if (regu_value_list->current_value == NULL)
{
scan_id->position = S_AFTER;
if (i == 0)
{
return S_END;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_CRSPOS, 0);
return S_ERROR;
}
}
}
}
else if (scan_id->position == S_AFTER)
{
return S_END;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_UNKNOWN_CRSPOS, 0);
return S_ERROR;
}
return S_SUCCESS;
}
/*
* scan_next_showstmt_scan () - The scan is moved to the next value scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_showstmt_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
SHOWSTMT_SCAN_ID *stsidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
stsidp = &scan_id->s.stsid;
if (scan_id->position == S_BEFORE)
{
scan_id->position = S_ON;
}
if (scan_id->position == S_ON)
{
while ((qp_scan = showstmt_next_scan (thread_p, scan_id)) == S_SUCCESS)
{
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (stsidp->scan_pred.pr_eval_fnc && stsidp->scan_pred.pred_expr)
{
ev_res = (*stsidp->scan_pred.pr_eval_fnc) (thread_p, stsidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
return S_SUCCESS;
}
}
else if (scan_id->position == S_AFTER)
{
return S_END;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_UNKNOWN_CRSPOS, 0);
return S_ERROR;
}
return qp_scan;
}
/*
* scan_next_set_scan () - The scan is moved to the next set scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_set_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
SET_SCAN_ID *ssidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
REGU_VARIABLE *func;
regu_variable_list_node *ptr;
int size;
ssidp = &scan_id->s.ssid;
/* if we are in the before position, fetch the set */
if (scan_id->position == S_BEFORE)
{
func = ssidp->set_ptr;
if (func->type == TYPE_FUNC && func->value.funcp->ftype == F_SEQUENCE)
{
size = 0;
for (ptr = func->value.funcp->operand; ptr; ptr = ptr->next)
{
size++;
}
ssidp->operand = func->value.funcp->operand;
ssidp->set_card = size;
}
else
{
pr_clear_value (&ssidp->set);
if (fetch_copy_dbval (thread_p, ssidp->set_ptr, scan_id->vd, NULL, NULL, NULL, &ssidp->set) != NO_ERROR)
{
return S_ERROR;
}
}
}
/* evaluate set expression and put resultant set in DB_VALUE */
while ((qp_scan = qproc_next_set_scan (thread_p, scan_id)) == S_SUCCESS)
{
assert (scan_id->val_list != NULL);
assert (scan_id->val_list->val_cnt == 1);
ev_res = V_TRUE;
if (ssidp->scan_pred.pr_eval_fnc && ssidp->scan_pred.pred_expr)
{
ev_res = (*ssidp->scan_pred.pr_eval_fnc) (thread_p, ssidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
return S_SUCCESS;
} /* while ((qp_scan = ) == S_SUCCESS) */
return qp_scan;
}
/*
* scan_next_json_table_scan () - The scan is moved to the next json_table scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_json_table_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
int error_code = NO_ERROR;
SCAN_CODE sc;
// the status of the scan will be put in scan_id->status
error_code = scan_id->s.jtid.next_scan (thread_p, *scan_id, sc);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
return S_ERROR;
}
return sc;
}
/*
* scan_next_method_scan () - The scan is moved to the next method scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_method_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
SCAN_CODE qp_scan;
val_list_node vl;
QPROC_DB_VALUE_LIST src_valp;
QPROC_DB_VALUE_LIST dest_valp;
/* execute method scan */
qp_scan = scan_id->s.msid.next_scan (vl);
if (qp_scan == S_SUCCESS)
{
/* copy the result into the value list of the scan ID */
for (src_valp = vl.valp, dest_valp = scan_id->val_list->valp; src_valp && dest_valp;
src_valp = src_valp->next, dest_valp = dest_valp->next)
{
if (DB_IS_NULL (src_valp->val))
{
pr_clear_value (dest_valp->val);
}
else if (DB_VALUE_DOMAIN_TYPE (src_valp->val) != DB_VALUE_DOMAIN_TYPE (dest_valp->val))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_DATATYPE, 0);
qp_scan = S_ERROR;
break;
}
else if (!qdata_copy_db_value (dest_valp->val, src_valp->val))
{
qp_scan = S_ERROR;
break;
}
pr_clear_value (src_valp->val);
}
}
else if (qp_scan == S_END)
{
scan_id->position = S_AFTER;
}
/* clear */
for (src_valp = vl.valp; src_valp; src_valp = src_valp->next)
{
db_private_free_and_init (thread_p, src_valp->val);
}
return qp_scan;
}
/*
* scan_next_dblink_scan () - The scan is moved to the next dblink scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_dblink_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
DBLINK_SCAN_ID *vaidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
vaidp = &scan_id->s.dblid;
/* execute dblink scan */
while ((qp_scan = dblink_scan_next (&vaidp->scan_info, scan_id->val_list)) == S_SUCCESS)
{
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (vaidp->scan_pred.pr_eval_fnc && vaidp->scan_pred.pred_expr)
{
ev_res = (*vaidp->scan_pred.pr_eval_fnc) (thread_p, vaidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
return S_SUCCESS;
}
/* scan error or end of scan */
if (qp_scan == S_END)
{
scan_id->position = S_AFTER;
return S_END;
}
else
{
return S_ERROR;
}
}
/*
* scan_handle_single_scan () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: This second order function applies the given next-scan function,
* then enforces the single_fetch , null_fetch semantics.
* Note that when "single_fetch", "null_fetch" is asserted, at least one
* qualified scan item, the NULL row, is returned.
*/
static SCAN_CODE
scan_handle_single_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id, QP_SCAN_FUNC next_scan)
{
SCAN_CODE result = S_ERROR;
if (s_id->scan_immediately_stop == true)
{
result = S_END;
goto end;
}
switch (s_id->single_fetch)
{
case QPROC_NO_SINGLE_INNER:
result = (*next_scan) (thread_p, s_id);
if (result == S_ERROR)
{
goto exit_on_error;
}
break;
case QPROC_SINGLE_OUTER:
/* already returned a row? */
/* if scan works in a single_fetch mode and first qualified scan item has already been fetched, return
* end_of_scan. */
if (s_id->single_fetched)
{
result = S_END;
}
else
/* if it is known that scan has no qualified items, return the NULL row, without searching. */
if (s_id->join_dbval && DB_IS_NULL (s_id->join_dbval))
{
qdata_set_value_list_to_null (s_id->val_list);
s_id->single_fetched = true;
result = S_SUCCESS;
}
else
{
result = (*next_scan) (thread_p, s_id);
if (result == S_ERROR)
{
goto exit_on_error;
}
if (result == S_END)
{
qdata_set_value_list_to_null (s_id->val_list);
result = S_SUCCESS;
}
s_id->single_fetched = true;
}
break;
case QPROC_SINGLE_INNER: /* currently, not used */
/* already returned a row? */
/* if scan works in a single_fetch mode and first qualified scan item has already been fetched, return
* end_of_scan. */
if (s_id->single_fetched)
{
result = S_END;
}
/* if it is known that scan has no qualified items, return the NULL row, without searching. */
else if (s_id->join_dbval && DB_IS_NULL (s_id->join_dbval))
{
result = S_END;
}
else
{
result = (*next_scan) (thread_p, s_id);
if (result == S_ERROR)
{
goto exit_on_error;
}
if (result == S_SUCCESS)
{
s_id->single_fetched = true;
}
}
break;
case QPROC_NO_SINGLE_OUTER:
/* already returned a NULL row? if scan works in a left outer join mode and a NULL row has already fetched,
* return end_of_scan. */
if (s_id->null_fetched)
{
result = S_END;
}
else
{
result = (*next_scan) (thread_p, s_id);
if (result == S_ERROR)
{
goto exit_on_error;
}
if (result == S_END)
{
if (!s_id->single_fetched)
{
/* no qualified items, return a NULL row */
qdata_set_value_list_to_null (s_id->val_list);
s_id->null_fetched = true;
result = S_SUCCESS;
}
}
if (result == S_SUCCESS)
{
s_id->single_fetched = true;
}
}
break;
}
end:
/* maintain what is apparently supposed to be an invariant-- S_END implies position is "after" the scan */
if (result == S_END)
{
if (s_id->direction != S_BACKWARD)
{
s_id->position = S_AFTER;
}
else
{
s_id->position = S_BEFORE;
}
}
return result;
exit_on_error:
return S_ERROR;
}
/*
* scan_next_scan () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*/
SCAN_CODE
scan_next_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
return scan_handle_single_scan (thread_p, s_id, scan_next_scan_local);
}
/*
* scan_prev_scan_local () - The scan is moved to the previous scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned.
* If an error occurs, S_ERROR is returned. This routine currently supports only LIST FILE scans.
*/
static SCAN_CODE
scan_prev_scan_local (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
LLIST_SCAN_ID *llsidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
QFILE_TUPLE_RECORD tplrec;
switch (scan_id->type)
{
case S_LIST_SCAN:
llsidp = &scan_id->s.llsid;
tplrec.size = 0;
tplrec.tpl = (QFILE_TUPLE) NULL;
while ((qp_scan = qfile_scan_list_prev (thread_p, &llsidp->lsid, &tplrec, PEEK)) == S_SUCCESS)
{
/* fetch the values for the predicate from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->scan_pred.regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (llsidp->scan_pred.pr_eval_fnc && llsidp->scan_pred.pred_expr)
{
ev_res = (*llsidp->scan_pred.pr_eval_fnc) (thread_p, llsidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
/* fetch the rest of the values from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->rest_regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
if (llsidp->tplrecp)
{
llsidp->tplrecp->size = tplrec.size;
llsidp->tplrecp->tpl = tplrec.tpl;
}
return S_SUCCESS;
}
if (qp_scan == S_END)
{
scan_id->position = S_BEFORE;
}
return qp_scan;
default:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INVALID_XASLNODE, 0);
return S_ERROR;
} /* switch (scan_id->type) */
}
/*
* scan_prev_scan () - The scan is moved to the previous scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned.
* If an error occurs, S_ERROR is returned. This routine currently supports only LIST FILE scans.
*/
SCAN_CODE
scan_prev_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
return scan_handle_single_scan (thread_p, s_id, scan_prev_scan_local);
}
/*
* scan_save_scan_pos () - Save current scan position information.
* return:
* scan_id(in/out): Scan identifier
* scan_pos(in/out): Set to contain current scan position
*
* Note: This routine currently assumes only LIST FILE scans.
*/
void
scan_save_scan_pos (SCAN_ID * s_id, SCAN_POS * scan_pos)
{
scan_pos->status = s_id->status;
scan_pos->position = s_id->position;
qfile_save_current_scan_tuple_position (&s_id->s.llsid.lsid, &scan_pos->ls_tplpos);
}
/*
* scan_jump_scan_pos () - Jump to the given scan position and move the scan
* from that point on in the forward direction.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
* scan_pos(in/out): Set to contain current scan position
*
* Note: This routine currently assumes only LIST FILE scans.
*/
SCAN_CODE
scan_jump_scan_pos (THREAD_ENTRY * thread_p, SCAN_ID * s_id, SCAN_POS * scan_pos)
{
LLIST_SCAN_ID *llsidp;
DB_LOGICAL ev_res;
QFILE_TUPLE_RECORD tplrec;
SCAN_CODE qp_scan;
llsidp = &s_id->s.llsid;
/* put back saved scan position */
s_id->status = scan_pos->status;
s_id->position = scan_pos->position;
/* jump to the previouslt saved scan position and continue from that point on forward */
tplrec.size = 0;
tplrec.tpl = (QFILE_TUPLE) NULL;
qp_scan = qfile_jump_scan_tuple_position (thread_p, &llsidp->lsid, &scan_pos->ls_tplpos, &tplrec, PEEK);
if (qp_scan != S_SUCCESS)
{
if (qp_scan == S_END)
{
s_id->position = S_AFTER;
}
return qp_scan;
}
do
{
/* fetch the value for the predicate from the tuple */
if (s_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->scan_pred.regu_list, s_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (llsidp->scan_pred.pr_eval_fnc && llsidp->scan_pred.pred_expr)
{
ev_res = (*llsidp->scan_pred.pr_eval_fnc) (thread_p, llsidp->scan_pred.pred_expr, s_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (s_id->qualification == QPROC_QUALIFIED)
{
if (ev_res == V_TRUE)
{
/* nop */ ;
}
/* qualified, return it */
}
else if (s_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res == V_FALSE)
{
ev_res = V_TRUE; /* not qualified, return it */
}
}
else if (s_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
s_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
s_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
ev_res = V_TRUE; /* return it */
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res == V_TRUE)
{
/* nop */ ;
}
/* qualified, return it */
}
if (ev_res == V_TRUE)
{
/* fetch the rest of the values from the tuple */
if (s_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->rest_regu_list, s_id->vd, NULL, NULL, tplrec.tpl, PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
if (llsidp->tplrecp)
{
llsidp->tplrecp->size = tplrec.size;
llsidp->tplrecp->tpl = tplrec.tpl;
}
return S_SUCCESS;
}
}
while ((qp_scan = qfile_scan_list_next (thread_p, &llsidp->lsid, &tplrec, PEEK)) == S_SUCCESS);
if (qp_scan == S_END)
{
s_id->position = S_AFTER;
}
return qp_scan;
}
/*
* scan_initialize () - initialize scan management routine
* return: NO_ERROR if all OK, ER status otherwise
*/
int
scan_initialize (void)
{
BTREE_ISCAN_OID_LIST *new_oid_list = NULL;
int error_code = NO_ERROR;
int i = 0;
/* Initialize */
scan_Iscan_oid_buf_list = NULL;
scan_Iscan_oid_buf_list_count = 0;
/* Allocate oid buffer list. */
for (i = 0; i < SCAN_ISCAN_OID_BUF_LIST_DEFAULT_SIZE; i++)
{
/* Allocate new entry. */
new_oid_list = NULL;
error_code = scan_alloc_oid_list (&new_oid_list);
if (error_code != NO_ERROR)
{
ASSERT_ERROR ();
/* Free what was already allocated. */
scan_finalize ();
/* Return error. */
return error_code;
}
/* Safe guard. */
assert (new_oid_list->oidp != NULL);
assert (new_oid_list->capacity > 0);
/* Save new buffer to buffer list. */
new_oid_list->next_list = scan_Iscan_oid_buf_list;
scan_Iscan_oid_buf_list = new_oid_list;
scan_Iscan_oid_buf_list_count++;
}
/* Success. */
return NO_ERROR;
}
/*
* scan_finalize () - finalize scan management routine
* return:
*/
void
scan_finalize (void)
{
BTREE_ISCAN_OID_LIST *oid_list_p;
while (scan_Iscan_oid_buf_list != NULL)
{
/* Save current. */
oid_list_p = scan_Iscan_oid_buf_list;
/* Advance to next. */
scan_Iscan_oid_buf_list = oid_list_p->next_list;
/* Free current. */
scan_free_oid_list (oid_list_p);
}
/* Reset count. */
scan_Iscan_oid_buf_list_count = 0;
}
/*
* reverse_key_list () - reverses the key list
* return: number of keys, -1 for error
* key_vals (in): pointer to array of KEY_VAL_RANGE structure
* key_cnt (in): number of keys; size of key_vals
*/
static int
reverse_key_list (KEY_VAL_RANGE * key_vals, int key_cnt)
{
int i, j;
KEY_VAL_RANGE temp;
for (i = 0, j = key_cnt - 1; i < j; i++, j--)
{
temp = key_vals[i];
key_vals[i] = key_vals[j];
key_vals[j] = temp;
}
return key_cnt;
}
/*
* resolve_domains_on_list_scan () - scans the structures in a list scan id
* and resolves the domains in sub-components like regu variables from scan
* predicates;
*
* llsidp (in/out): pointer to list scan id structure
* ref_val_list (in): list of DB_VALUEs (val_list_node) used as reference
*
* Note : this function is used in context of HV late binding
*/
static void
resolve_domains_on_list_scan (LLIST_SCAN_ID * llsidp, val_list_node * ref_val_list)
{
regu_variable_list_node *scan_regu = NULL;
assert (llsidp != NULL);
if (llsidp->list_id == NULL || ref_val_list == NULL)
{
return;
}
/* resolve domains on regu_list of scan predicate */
for (scan_regu = llsidp->scan_pred.regu_list; scan_regu != NULL; scan_regu = scan_regu->next)
{
if ((TP_DOMAIN_TYPE (scan_regu->value.domain) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (scan_regu->value.domain)) && scan_regu->value.type == TYPE_POSITION)
{
int pos = scan_regu->value.value.pos_descr.pos_no;
TP_DOMAIN *new_dom = NULL;
assert (pos < llsidp->list_id->type_list.type_cnt);
new_dom = llsidp->list_id->type_list.domp[pos];
if (TP_DOMAIN_TYPE (new_dom) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (new_dom) != TP_DOMAIN_COLL_NORMAL)
{
continue;
}
scan_regu->value.value.pos_descr.dom = new_dom;
scan_regu->value.domain = new_dom;
}
}
/* resolve domains on rest_regu_list of scan predicate */
for (scan_regu = llsidp->rest_regu_list; scan_regu != NULL; scan_regu = scan_regu->next)
{
if ((TP_DOMAIN_TYPE (scan_regu->value.domain) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (scan_regu->value.domain) != TP_DOMAIN_COLL_NORMAL)
&& scan_regu->value.type == TYPE_POSITION)
{
int pos = scan_regu->value.value.pos_descr.pos_no;
TP_DOMAIN *new_dom = NULL;
assert (pos < llsidp->list_id->type_list.type_cnt);
new_dom = llsidp->list_id->type_list.domp[pos];
if (TP_DOMAIN_TYPE (new_dom) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (new_dom) != TP_DOMAIN_COLL_NORMAL)
{
continue;
}
scan_regu->value.value.pos_descr.dom = new_dom;
scan_regu->value.domain = new_dom;
}
}
/* resolve domains on predicate expression of scan predicate */
if (llsidp->scan_pred.pred_expr == NULL)
{
return;
}
if (llsidp->scan_pred.pred_expr->type == T_EVAL_TERM)
{
EVAL_TERM ev_t = llsidp->scan_pred.pred_expr->pe.m_eval_term;
if (ev_t.et_type == T_COMP_EVAL_TERM)
{
if (ev_t.et.et_comp.lhs != NULL
&& (TP_DOMAIN_TYPE (ev_t.et.et_comp.lhs->domain) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (ev_t.et.et_comp.lhs->domain) != TP_DOMAIN_COLL_NORMAL))
{
resolve_domain_on_regu_operand (ev_t.et.et_comp.lhs, ref_val_list, &(llsidp->list_id->type_list));
}
if (ev_t.et.et_comp.rhs != NULL
&& (TP_DOMAIN_TYPE (ev_t.et.et_comp.rhs->domain) == DB_TYPE_VARIABLE
|| TP_DOMAIN_COLLATION_FLAG (ev_t.et.et_comp.rhs->domain) != TP_DOMAIN_COLL_NORMAL))
{
resolve_domain_on_regu_operand (ev_t.et.et_comp.rhs, ref_val_list, &(llsidp->list_id->type_list));
}
}
}
}
/*
* resolve_domain_on_regu_operand () - resolves a domain on a regu variable
* from a scan list; helper functions for 'resolve_domains_on_list_scan'
*
* regu_var (in/out): regulator variable with unresolved domain
* ref_val_list (in): list of DB_VALUEs (val_list_node) used for cross-checking
* p_type_list (in): list of domains used as reference
*
* Note : this function is used in context of HV late binding
*/
static void
resolve_domain_on_regu_operand (REGU_VARIABLE * regu_var, val_list_node * ref_val_list,
QFILE_TUPLE_VALUE_TYPE_LIST * p_type_list)
{
assert (regu_var != NULL);
assert (ref_val_list != NULL);
if (regu_var->type == TYPE_CONSTANT)
{
QPROC_DB_VALUE_LIST value_list;
int pos = 0;
bool found = false;
/* search in ref_val_list for the corresponding DB_VALUE */
for (value_list = ref_val_list->valp; value_list != NULL; value_list = value_list->next, pos++)
{
if (regu_var->value.dbvalptr == ref_val_list->valp->val)
{
found = true;
break;
}
}
if (found)
{
assert (pos < p_type_list->type_cnt);
regu_var->domain = p_type_list->domp[pos];
}
}
}
/*
* scan_init_multi_range_optimization () - initialize structure for multiple range optimization
*
* return: error code
*
* multi_range_opt(in): multiple range optimization structure
* use_range_opt(in): to use or not optimization
* max_size(in): size of arrays for the top N values
*/
static int
scan_init_multi_range_optimization (THREAD_ENTRY * thread_p, MULTI_RANGE_OPT * multi_range_opt, bool use_range_opt,
int max_size)
{
int err = NO_ERROR;
if (multi_range_opt == NULL)
{
return ER_FAILED;
}
memset ((void *) (multi_range_opt), 0, sizeof (MULTI_RANGE_OPT));
multi_range_opt->use = use_range_opt;
multi_range_opt->cnt = 0;
if (use_range_opt)
{
multi_range_opt->size = max_size;
/* we don't have sort information here, just set an invalid value */
multi_range_opt->sort_att_idx = NULL;
multi_range_opt->is_desc_order = NULL;
multi_range_opt->num_attrs = 0;
multi_range_opt->top_n_items =
(RANGE_OPT_ITEM **) db_private_alloc (thread_p, max_size * sizeof (RANGE_OPT_ITEM *));
if (multi_range_opt->top_n_items == NULL)
{
err = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
multi_range_opt->buffer = (RANGE_OPT_ITEM **) db_private_alloc (thread_p, max_size * sizeof (RANGE_OPT_ITEM *));
if (multi_range_opt->buffer == NULL)
{
err = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit_on_error;
}
memset (multi_range_opt->top_n_items, 0, max_size * sizeof (RANGE_OPT_ITEM *));
multi_range_opt->tplrec.size = 0;
multi_range_opt->tplrec.tpl = NULL;
perfmon_inc_stat (thread_p, PSTAT_BT_NUM_MULTI_RANGE_OPT);
}
return err;
exit_on_error:
if (multi_range_opt->top_n_items != NULL)
{
db_private_free_and_init (thread_p, multi_range_opt->top_n_items);
}
if (multi_range_opt->buffer != NULL)
{
db_private_free_and_init (thread_p, multi_range_opt->buffer);
}
return err;
}
/*
* scan_dump_key_into_tuple () - outputs the value stored in 'key' into the tuple 'tplrec'
*
* return: error code
* iscan_id(in):
* key(in): MIDXKEY key (as it is retreived from index)
* oid(in): oid (required if objects are stored in 'key')
* tplrec(out):
*
* Note : this function is used by multiple range search optimization;
* although not required here, the key should be a MIDXKEY value,
* when multiple range search optimization is enabled.
*/
static int
scan_dump_key_into_tuple (THREAD_ENTRY * thread_p, INDX_SCAN_ID * iscan_id, DB_VALUE * key, OID * oid,
QFILE_TUPLE_RECORD * tplrec)
{
int error;
regu_variable_list_node *p;
if (iscan_id == NULL || iscan_id->indx_cov.val_descr == NULL || iscan_id->indx_cov.output_val_list == NULL
|| iscan_id->rest_attrs.attr_cache == NULL)
{
return ER_FAILED;
}
error = btree_attrinfo_read_dbvalues (thread_p, key, NULL, iscan_id->bt_attr_ids, iscan_id->bt_num_attrs,
iscan_id->rest_attrs.attr_cache, -1, NULL);
if (error != NO_ERROR)
{
return error;
}
for (p = iscan_id->rest_regu_list; p; p = p->next)
{
pr_clear_value (p->value.vfetch_to);
}
error = fetch_val_list (thread_p, iscan_id->rest_regu_list, iscan_id->indx_cov.val_descr, NULL, oid, NULL, PEEK);
if (error != NO_ERROR)
{
return error;
}
error = qdata_copy_valptr_list_to_tuple (thread_p, iscan_id->indx_cov.output_val_list, iscan_id->indx_cov.val_descr,
tplrec);
if (error != NO_ERROR)
{
return error;
}
return NO_ERROR;
}
#if defined (SERVER_MODE)
/*
* scan_print_stats_json () -
* return:
* scan_id(in):
*/
void
scan_print_stats_json (SCAN_ID * scan_id, json_t * scan_stats)
{
json_t *scan, *lookup;
if (scan_id == NULL || scan_stats == NULL)
{
return;
}
scan = json_pack ("{s:i, s:I, s:I}", "time", TO_MSEC (scan_id->scan_stats.elapsed_scan), "fetch",
scan_id->scan_stats.num_fetches, "ioread", scan_id->scan_stats.num_ioreads);
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_LIST_SCAN:
case S_PARALLEL_HEAP_SCAN:
json_object_set_new (scan, "readrows", json_integer (scan_id->scan_stats.read_rows));
json_object_set_new (scan, "rows", json_integer (scan_id->scan_stats.qualified_rows));
if (scan_id->type == S_HEAP_SCAN || scan_id->type == S_PARALLEL_HEAP_SCAN)
{
if (scan_id->scan_stats.agl)
{
SCAN_AGL *agl;
char *agl_index;
int len = 0;
for (agl = scan_id->scan_stats.agl; agl; agl = agl->next)
{
len += strlen (agl->agg_index_name) + 2; /* for ", " */
}
agl_index = (char *) malloc (len);
if (agl_index == NULL)
{
return;
}
*agl_index = '\0';
for (agl = scan_id->scan_stats.agl; agl; agl = agl->next)
{
if (*agl_index)
{
sprintf (agl_index + strlen (agl_index), ", %s", agl->agg_index_name);
}
else
{
sprintf (agl_index, "%s", agl->agg_index_name);
}
}
json_object_set_new (scan, "agl", json_string (agl_index));
free (agl_index);
}
if (scan_id->scan_stats.noscan)
{
json_object_set_new (scan_stats, "noscan", scan);
}
else
{
json_object_set_new (scan_stats, "heap", scan);
}
}
else
{
json_object_set_new (scan_stats, "temp", scan);
}
break;
case S_INDX_SCAN:
json_object_set_new (scan, "readkeys", json_integer (scan_id->scan_stats.read_keys));
json_object_set_new (scan, "filteredkeys", json_integer (scan_id->scan_stats.qualified_keys));
json_object_set_new (scan, "rows", json_integer (scan_id->scan_stats.key_qualified_rows));
json_object_set_new (scan_stats, "btree", scan);
if (scan_id->scan_stats.covered_index == true)
{
json_object_set_new (scan_stats, "covered", json_true ());
}
else
{
lookup = json_pack ("{s:i, s:i}", "time", TO_MSEC (scan_id->scan_stats.elapsed_lookup), "rows",
scan_id->scan_stats.data_qualified_rows);
json_object_set_new (scan_stats, "lookup", lookup);
}
if (scan_id->scan_stats.multi_range_opt == true)
{
json_object_set_new (scan_stats, "mro", json_true ());
}
if (scan_id->scan_stats.index_skip_scan == true)
{
json_object_set_new (scan_stats, "iss", json_true ());
}
if (scan_id->scan_stats.loose_index_scan == true)
{
json_object_set_new (scan_stats, "loose", json_true ());
}
break;
case S_SHOWSTMT_SCAN:
json_object_set_new (scan_stats, "show", scan);
break;
case S_SET_SCAN:
json_object_set_new (scan_stats, "set", scan);
break;
case S_METHOD_SCAN:
json_object_set_new (scan_stats, "method", scan);
break;
case S_DBLINK_SCAN:
json_object_set_new (scan_stats, "dblink", scan);
break;
case S_CLASS_ATTR_SCAN:
json_object_set_new (scan_stats, "class_attr", scan);
break;
default:
json_object_set_new (scan_stats, "noscan", scan);
break;
}
}
/*
* scan_print_stats_text () -
* return:
* scan_id(in):
*/
void
scan_print_stats_text (FILE * fp, SCAN_ID * scan_id)
{
if (scan_id == NULL)
{
return;
}
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_HEAP_SAMPLING_SCAN:
case S_PARALLEL_HEAP_SCAN:
if (scan_id->scan_stats.noscan)
{
fprintf (fp, "(noscan"); /* aggregate optimization is not a scan */
}
else
{
fprintf (fp, "(heap");
}
break;
case S_INDX_SCAN:
fprintf (fp, "(btree");
break;
case S_LIST_SCAN:
if (scan_id->s.llsid.hlsid.hash_list_scan_type == HASH_METH_IN_MEM)
{
fprintf (fp, "(hash temp(m), build time: %d,", TO_MSEC (scan_id->scan_stats.elapsed_hash_build));
}
else if (scan_id->s.llsid.hlsid.hash_list_scan_type == HASH_METH_HYBRID)
{
fprintf (fp, "(hash temp(h), build time: %d,", TO_MSEC (scan_id->scan_stats.elapsed_hash_build));
}
else if (scan_id->s.llsid.hlsid.hash_list_scan_type == HASH_METH_HASH_FILE)
{
fprintf (fp, "(hash temp(f), build time: %d,", TO_MSEC (scan_id->scan_stats.elapsed_hash_build));
}
else
{
fprintf (fp, "(temp");
}
break;
case S_SHOWSTMT_SCAN:
fprintf (fp, "(show");
break;
case S_SET_SCAN:
fprintf (fp, "(set");
break;
case S_METHOD_SCAN:
fprintf (fp, "(method");
break;
case S_DBLINK_SCAN:
fprintf (fp, "(dblink");
break;
case S_CLASS_ATTR_SCAN:
fprintf (fp, "(class_attr");
break;
default:
fprintf (fp, "(noscan");
break;
}
fprintf (fp, " time: %d, fetch: %llu, ioread: %llu", TO_MSEC (scan_id->scan_stats.elapsed_scan),
(unsigned long long int) scan_id->scan_stats.num_fetches,
(unsigned long long int) scan_id->scan_stats.num_ioreads);
switch (scan_id->type)
{
case S_HEAP_SCAN:
case S_PARALLEL_HEAP_SCAN:
case S_LIST_SCAN:
case S_HEAP_SAMPLING_SCAN:
fprintf (fp, ", readrows: %llu, rows: %llu", (unsigned long long int) scan_id->scan_stats.read_rows,
(unsigned long long int) scan_id->scan_stats.qualified_rows);
if (scan_id->scan_stats.agl)
{
SCAN_AGL *agl;
fprintf (fp, ", agl: ");
for (agl = scan_id->scan_stats.agl; agl; agl = agl->next)
{
fprintf (fp, "%s", agl->agg_index_name);
if (agl->next)
{
fprintf (fp, ", ");
}
}
}
fprintf (fp, ")");
break;
case S_INDX_SCAN:
fprintf (fp, ", readkeys: %llu, filteredkeys: %llu, rows: %llu",
(unsigned long long int) scan_id->scan_stats.read_keys,
(unsigned long long int) scan_id->scan_stats.qualified_keys,
(unsigned long long int) scan_id->scan_stats.key_qualified_rows);
if (scan_id->scan_stats.covered_index == true)
{
fprintf (fp, ", covered: true");
}
if (scan_id->scan_stats.min_max_only_scan == true)
{
fprintf (fp, ", min_max_only: true");
}
if (scan_id->s.isid.need_count_only == true)
{
fprintf (fp, ", count_only: true");
}
if (scan_id->scan_stats.multi_range_opt == true)
{
fprintf (fp, ", mro: true");
}
if (scan_id->scan_stats.index_skip_scan == true)
{
fprintf (fp, ", iss: true");
}
if (scan_id->scan_stats.loose_index_scan == true)
{
fprintf (fp, ", loose: true");
}
fprintf (fp, ")");
if (scan_id->scan_stats.covered_index == false)
{
fprintf (fp, " (lookup time: %d, rows: %llu)", TO_MSEC (scan_id->scan_stats.elapsed_lookup),
(unsigned long long int) scan_id->scan_stats.data_qualified_rows);
}
break;
default:
fprintf (fp, ")");
break;
}
}
#endif /* SERVER_MODE */
/*
* scan_build_hash_list_scan () - build hash table from list
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_build_hash_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
LLIST_SCAN_ID *llsidp;
SCAN_CODE qp_scan;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
HASH_SCAN_KEY *key, *new_key;
HASH_SCAN_VALUE *new_value;
unsigned int hash_key;
TFTID tftid;
llsidp = &scan_id->s.llsid;
key = llsidp->hlsid.temp_key;
new_key = llsidp->hlsid.temp_new_key;
tplrec.size = 0;
tplrec.tpl = (QFILE_TUPLE) NULL;
resolve_domains_on_list_scan (llsidp, scan_id->val_list);
while ((qp_scan = qfile_scan_list_next (thread_p, &llsidp->lsid, &tplrec, PEEK)) == S_SUCCESS)
{
/* fetch the values for the predicate from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->scan_pred.regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
scan_id->scan_stats.read_rows++;
/* build key */
if (qdata_build_hscan_key (thread_p, scan_id->vd, llsidp->hlsid.build_regu_list, key) != NO_ERROR)
{
return S_ERROR;
}
/* create new key */
if (llsidp->hlsid.need_coerce_type)
{
new_key = qdata_copy_hscan_key_without_alloc (thread_p, key, llsidp->hlsid.probe_regu_list, new_key);
if (new_key == NULL)
{
return S_ERROR;
}
}
else
{
new_key = key;
}
/* make hash key */
hash_key = qdata_hash_scan_key (new_key, UINT_MAX, llsidp->hlsid.hash_list_scan_type);
switch (llsidp->hlsid.hash_list_scan_type)
{
case HASH_METH_IN_MEM:
/* create new value */
new_value = qdata_alloc_hscan_value (thread_p, tplrec.tpl);
if (new_value == NULL)
{
return S_ERROR;
}
/* add to hash table */
if (mht_put_hls (llsidp->hlsid.memory.hash_table, (void *) &hash_key, (void *) new_value) == NULL)
{
return S_ERROR;
}
break;
case HASH_METH_HYBRID:
/* create new value */
new_value = qdata_alloc_hscan_value_OID (thread_p, &llsidp->lsid);
if (new_value == NULL)
{
return S_ERROR;
}
/* add to hash table */
if (mht_put_hls (llsidp->hlsid.memory.hash_table, (void *) &hash_key, (void *) new_value) == NULL)
{
return S_ERROR;
}
break;
case HASH_METH_HASH_FILE:
/* curr_offset is int and tftid.offset is short. */
/* the offset is a position within a page(16K), so it can be stored as a short type. */
SET_TFTID (tftid, llsidp->lsid.curr_vpid.volid, llsidp->lsid.curr_vpid.pageid, llsidp->lsid.curr_offset);
/* add to hash table */
if (fhs_insert (thread_p, llsidp->hlsid.file.hash_table, (void *) &hash_key, &tftid) == NULL)
{
return S_ERROR;
}
break;
default:
return S_ERROR;
}
}
return qp_scan;
}
/*
* scan_next_hash_list_scan () - The scan is moved to the next hash list scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_next_hash_list_scan (THREAD_ENTRY * thread_p, SCAN_ID * scan_id)
{
LLIST_SCAN_ID *llsidp;
SCAN_CODE qp_scan;
DB_LOGICAL ev_res;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
tplrec.size = 0;
tplrec.tpl = (QFILE_TUPLE) NULL;
llsidp = &scan_id->s.llsid;
while ((qp_scan = scan_hash_probe_next (thread_p, scan_id, &tplrec.tpl)) == S_SUCCESS)
{
/* fetch the values for the predicate from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->scan_pred.regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) !=
NO_ERROR)
{
return S_ERROR;
}
}
scan_id->scan_stats.read_rows++;
/* evaluate the predicate to see if the tuple qualifies */
ev_res = V_TRUE;
if (llsidp->scan_pred.pr_eval_fnc && llsidp->scan_pred.pred_expr)
{
ev_res = (*llsidp->scan_pred.pr_eval_fnc) (thread_p, llsidp->scan_pred.pred_expr, scan_id->vd, NULL);
if (ev_res == V_ERROR)
{
return S_ERROR;
}
}
if (scan_id->qualification == QPROC_QUALIFIED)
{
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_NOT_QUALIFIED)
{
if (ev_res != V_FALSE) /* V_TRUE || V_UNKNOWN */
{
continue; /* qualified, continue to the next tuple */
}
}
else if (scan_id->qualification == QPROC_QUALIFIED_OR_NOT)
{
if (ev_res == V_TRUE)
{
scan_id->qualification = QPROC_QUALIFIED;
}
else if (ev_res == V_FALSE)
{
scan_id->qualification = QPROC_NOT_QUALIFIED;
}
else /* V_UNKNOWN */
{
/* nop */
;
}
}
else
{ /* invalid value; the same as QPROC_QUALIFIED */
if (ev_res != V_TRUE) /* V_FALSE || V_UNKNOWN */
{
continue; /* not qualified, continue to the next tuple */
}
}
scan_id->scan_stats.qualified_rows++;
/* fetch the rest of the values from the tuple */
if (scan_id->val_list)
{
if (fetch_val_list (thread_p, llsidp->rest_regu_list, scan_id->vd, NULL, NULL, tplrec.tpl, PEEK) != NO_ERROR)
{
return S_ERROR;
}
}
if (llsidp->tplrecp)
{
llsidp->tplrecp->size = tplrec.size;
llsidp->tplrecp->tpl = tplrec.tpl;
}
return S_SUCCESS;
}
return qp_scan;
}
/*
* scan_hash_probe_next () - The scan is moved to the next hash list scan item.
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* scan_id(in/out): Scan identifier
*
* Note: If there are no more scan items, S_END is returned. If an error occurs, S_ERROR is returned.
*/
static SCAN_CODE
scan_hash_probe_next (THREAD_ENTRY * thread_p, SCAN_ID * scan_id, QFILE_TUPLE * tuple)
{
LLIST_SCAN_ID *llsidp;
HASH_SCAN_KEY *key;
HASH_SCAN_VALUE *hvalue;
QFILE_LIST_SCAN_ID *scan_id_p;
QFILE_TUPLE_POSITION tuple_pos;
QFILE_TUPLE_SIMPLE_POS *simple_pos;
QFILE_TUPLE_RECORD tplrec = { NULL, 0 };
unsigned int hash_key;
EH_SEARCH eh_search;
TFTID result;
llsidp = &scan_id->s.llsid;
key = llsidp->hlsid.temp_key;
scan_id_p = &llsidp->lsid;
if (scan_id_p->position == S_BEFORE)
{
/* build key */
if (qdata_build_hscan_key (thread_p, scan_id->vd, llsidp->hlsid.probe_regu_list, key) != NO_ERROR)
{
return S_ERROR;
}
/* make hash key */
hash_key = qdata_hash_scan_key (key, UINT_MAX, llsidp->hlsid.hash_list_scan_type);
llsidp->hlsid.curr_hash_key = hash_key;
switch (llsidp->hlsid.hash_list_scan_type)
{
case HASH_METH_IN_MEM:
case HASH_METH_HYBRID:
/* init curr_hash_entry */
llsidp->hlsid.memory.curr_hash_entry = NULL;
/* get value from hash table */
hvalue =
(HASH_SCAN_VALUE *) mht_get_hls (llsidp->hlsid.memory.hash_table, (void *) &hash_key,
(void **) &llsidp->hlsid.memory.curr_hash_entry);
if (hvalue == NULL)
{
return S_END;
}
if (llsidp->hlsid.hash_list_scan_type == HASH_METH_IN_MEM)
{
*tuple = hvalue->tuple;
}
else if (llsidp->hlsid.hash_list_scan_type == HASH_METH_HYBRID)
{
MAKE_TUPLE_POSTION (tuple_pos, hvalue->pos, scan_id_p);
if (qfile_jump_scan_tuple_position (thread_p, scan_id_p, &tuple_pos, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
*tuple = tplrec.tpl;
}
else
{
return S_ERROR;
}
scan_id_p->position = S_ON;
return S_SUCCESS;
case HASH_METH_HASH_FILE:
/* init curr_oid and get value from hash table */
eh_search = fhs_search (thread_p, &llsidp->hlsid, &result);
switch (eh_search)
{
case EH_KEY_FOUND:
MAKE_TFTID_TO_TUPLE_POSTION (tuple_pos, result, scan_id_p);
if (qfile_jump_scan_tuple_position (thread_p, scan_id_p, &tuple_pos, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
*tuple = tplrec.tpl;
scan_id_p->position = S_ON;
return S_SUCCESS;
case EH_KEY_NOTFOUND:
return S_END;
case EH_ERROR_OCCURRED:
default:
return S_ERROR;
}
break;
default:
return S_ERROR;
}
}
else if (scan_id_p->position == S_ON)
{
switch (llsidp->hlsid.hash_list_scan_type)
{
case HASH_METH_IN_MEM:
case HASH_METH_HYBRID:
hvalue =
(HASH_SCAN_VALUE *) mht_get_next_hls (llsidp->hlsid.memory.hash_table,
(void *) &llsidp->hlsid.curr_hash_key,
(void **) &llsidp->hlsid.memory.curr_hash_entry);
if (hvalue == NULL)
{
if (llsidp->hlsid.hash_list_scan_type == HASH_METH_HYBRID)
{
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
}
scan_id_p->position = S_AFTER;
return S_END;
}
if (llsidp->hlsid.hash_list_scan_type == HASH_METH_IN_MEM)
{
*tuple = ((HASH_SCAN_VALUE *) llsidp->hlsid.memory.curr_hash_entry->data)->tuple;
}
else if (llsidp->hlsid.hash_list_scan_type == HASH_METH_HYBRID)
{
simple_pos = ((HASH_SCAN_VALUE *) llsidp->hlsid.memory.curr_hash_entry->data)->pos;
MAKE_TUPLE_POSTION (tuple_pos, simple_pos, scan_id_p);
if (qfile_jump_scan_tuple_position (thread_p, scan_id_p, &tuple_pos, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
*tuple = tplrec.tpl;
}
else
{
return S_ERROR;
}
return S_SUCCESS;
case HASH_METH_HASH_FILE:
eh_search = fhs_search_next (thread_p, &llsidp->hlsid, &result);
switch (eh_search)
{
case EH_KEY_FOUND:
MAKE_TFTID_TO_TUPLE_POSTION (tuple_pos, result, scan_id_p);
if (qfile_jump_scan_tuple_position (thread_p, scan_id_p, &tuple_pos, &tplrec, PEEK) != S_SUCCESS)
{
return S_ERROR;
}
*tuple = tplrec.tpl;
return S_SUCCESS;
case EH_KEY_NOTFOUND:
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
scan_id_p->position = S_AFTER;
return S_END;
case EH_ERROR_OCCURRED:
default:
return S_ERROR;
}
return S_END;
default:
return S_ERROR;
}
}
else if (scan_id_p->position == S_AFTER)
{
return S_END;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_UNKNOWN_CRSPOS, 0);
return S_ERROR;
}
/* Can't reach here */
return S_ERROR;
}
/*
* check_hash_list_scan () - Check if hash list scan is possible
* return: int 1: in-memory 2: hybrid in-memory
* llsidp (in): list scan id pointer
* node :
* 1. count of tuple of list file > 0
* 2. list file size check
* 3. regu_list_build, regu_list_probe is not null
* 4. The number of probe regu_var and build regu match
* 5. type of regu var is not oid && vobj
* 6. list file from dptr is not allowed
*/
static HASH_METHOD
check_hash_list_scan (LLIST_SCAN_ID * llsidp, int *val_cnt, int hash_list_scan_yn)
{
int build_cnt;
regu_variable_list_node *build, *probe;
DB_TYPE vtype1, vtype2;
UINT64 mem_limit = prm_get_bigint_value (PRM_ID_MAX_HASH_LIST_SCAN_SIZE);
assert (hash_list_scan_yn == 0 || hash_list_scan_yn == 1);
/* no_hash_list_scan sql hint check */
if (hash_list_scan_yn == 0)
{
return HASH_METH_NOT_USE;
}
/* count of tuple of list file > 0 */
if (llsidp->list_id->tuple_cnt <= 0)
{
return HASH_METH_NOT_USE;
}
/* regu_list_build, regu_list_probe is not null */
if (llsidp->hlsid.build_regu_list == NULL || llsidp->hlsid.probe_regu_list == NULL)
{
return HASH_METH_NOT_USE;
}
build = llsidp->hlsid.build_regu_list;
probe = llsidp->hlsid.probe_regu_list;
for (build_cnt = 0; build && probe; build_cnt++)
{
/* type of regu var is not oid && vobj */
/* This is the case when type coercion is impossible. so use list scan */
/* In the list scan, Vobj is converted to oid for comparison at tp_value_compare_with_error(). */
vtype1 = REGU_VARIABLE_GET_TYPE (&probe->value);
vtype2 = REGU_VARIABLE_GET_TYPE (&build->value);
if ((vtype1 == DB_TYPE_OBJECT && vtype2 == DB_TYPE_OID) || (vtype2 == DB_TYPE_OBJECT && vtype1 == DB_TYPE_OID)
|| (vtype1 == DB_TYPE_VOBJ || vtype2 == DB_TYPE_VOBJ))
{
return HASH_METH_NOT_USE;
}
if (vtype1 != vtype2)
{
llsidp->hlsid.need_coerce_type = true;
}
build = build->next;
probe = probe->next;
}
/* The number of probe regu_var and build regu match */
if (build != NULL || probe != NULL)
{
return HASH_METH_NOT_USE;
}
*val_cnt = build_cnt;
/* 6. list file from dptr is not allowed */
/* Since dptr is searched after scan_open_scan, it is checked when llsidp->list_id->tuple_cnt <= 0 */
/* list file size check */
if (mem_limit == 0)
{
return HASH_METH_NOT_USE;
}
else if ((UINT64) llsidp->list_id->page_cnt * DB_PAGESIZE <= mem_limit)
{
return HASH_METH_IN_MEM;
}
else if ((UINT64) llsidp->list_id->tuple_cnt * (sizeof (HENTRY_HLS) + sizeof (QFILE_TUPLE_SIMPLE_POS)) <= mem_limit)
{
/* bytes of 1 row = sizeof(HENTRY_HLS) + sizeof(QFILE_TUPLE_SIMPLE_POS) = 44 bytes (64bit) */
/* HENTRY_HLS = pointer(8bytes) * 4 = 32 bytes */
/* SIMPLE_POS = pageid(4bytes) + volid(2bytes) + padding(2bytes) + offset(4bytes) = 12 bytes */
return HASH_METH_HYBRID;
}
else
{
return HASH_METH_HASH_FILE;
}
return HASH_METH_NOT_USE;
}