File list_file.c¶
File List > cubrid > src > query > list_file.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.
*
*/
/*
* list_file.c - Query List File Manager
*/
#ident "$Id$"
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <search.h>
#include <stddef.h>
#include <assert.h>
#include "list_file.h"
#include "binaryheap.h"
#include "db_value_printer.hpp"
#include "dbtype.h"
#include "error_manager.h"
#include "file_manager.h"
#include "log_append.hpp"
#include "object_primitive.h"
#include "object_representation.h"
#include "query_manager.h"
#include "query_opfunc.h"
#include "stream_to_xasl.h"
#include "thread_entry.hpp"
#include "thread_manager.hpp" // for thread_sleep
#include "xasl.h"
#include "xasl_cache.h"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
#if defined(CS_MODE)
#error Belongs to not client module
#endif
/* TODO */
#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;
#define thread_sleep(a)
#endif /* not SERVER_MODE */
#define QFILE_CHECK_LIST_FILE_IS_CLOSED(list_id) \
assert (list_id != NULL &&(!VPID_ISNULL(&list_id->last_vpid) ? (list_id->last_pgptr != NULL) : true))
#define QFILE_DEFAULT_PAGES 4
#if defined (SERVER_MODE)
#define LS_PUT_NEXT_VPID(ptr) \
do \
{ \
OR_PUT_INT ((ptr) + QFILE_NEXT_PAGE_ID_OFFSET, NULL_PAGEID_IN_PROGRESS); \
OR_PUT_SHORT ((ptr) + QFILE_NEXT_VOL_ID_OFFSET, NULL_VOLID); \
} \
while (0)
#else
#define LS_PUT_NEXT_VPID(ptr) \
do \
{ \
OR_PUT_INT ((ptr) + QFILE_NEXT_PAGE_ID_OFFSET, NULL_PAGEID); \
OR_PUT_SHORT ((ptr) + QFILE_NEXT_VOL_ID_OFFSET, NULL_VOLID); \
} \
while (0)
#endif
typedef struct qfile_cleanup_candidate QFILE_CACHE_CLEANUP_CANDIDATE;
struct qfile_cleanup_candidate
{
QFILE_LIST_CACHE_ENTRY *qcache;
double weight;
};
typedef SCAN_CODE (*ADVANCE_FUCTION) (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID *, QFILE_TUPLE_RECORD *,
QFILE_LIST_SCAN_ID *, QFILE_TUPLE_RECORD *, QFILE_TUPLE_VALUE_TYPE_LIST *);
/* query result(list file) cache related things */
typedef struct qfile_list_cache QFILE_LIST_CACHE;
struct qfile_list_cache
{
MHT_TABLE **list_hts; /* array of memory hash tables for list cache; pool for list_ht of XASL_CACHE_ENTRY */
int *free_ht_list; /* array of freed hash tables */
int next_ht_no; /* the next freed hash table number */
unsigned int n_hts; /* number of elements of list_hts */
int n_entries; /* total number of cache entries */
int n_pages; /* total number of pages used by the cache */
unsigned int lookup_counter; /* counter of cache lookup */
unsigned int hit_counter; /* counter of cache hit */
unsigned int miss_counter; /* counter of cache miss */
unsigned int full_counter; /* counter of cache full & replacement */
};
typedef struct qfile_list_cache_candidate QFILE_LIST_CACHE_CANDIDATE;
struct qfile_list_cache_candidate
{
int total_num; /* total number of cache entries */
int num_candidates; /* number of candidates */
int num_victims; /* number of victims */
int selcnt;
QFILE_LIST_CACHE_ENTRY **time_candidates; /* candidates who are old aged */
QFILE_LIST_CACHE_ENTRY **ref_candidates; /* candidates who are recently used */
QFILE_LIST_CACHE_ENTRY **victims; /* victims; cache entries to be deleted */
int c_idx;
int v_idx;
bool include_in_use;
};
/* list cache entry pooling */
#define FIXED_SIZE_OF_POOLED_LIST_CACHE_ENTRY 4096
#define ADDITION_FOR_POOLED_LIST_CACHE_ENTRY offsetof(QFILE_POOLED_LIST_CACHE_ENTRY, s.entry)
#define POOLED_LIST_CACHE_ENTRY_FROM_LIST_CACHE_ENTRY(p) \
((QFILE_POOLED_LIST_CACHE_ENTRY *) ((char*) p - ADDITION_FOR_POOLED_LIST_CACHE_ENTRY))
typedef union qfile_pooled_list_cache_entry QFILE_POOLED_LIST_CACHE_ENTRY;
union qfile_pooled_list_cache_entry
{
struct
{
int next; /* next entry in the free list */
QFILE_LIST_CACHE_ENTRY entry; /* list cache entry data */
} s;
char dummy[FIXED_SIZE_OF_POOLED_LIST_CACHE_ENTRY];
/*
* 4K size including list cache entry itself
* and reserved spaces for list_cache_ent.param_values
*/
};
static const int RESERVED_SIZE_FOR_LIST_CACHE_ENTRY =
(FIXED_SIZE_OF_POOLED_LIST_CACHE_ENTRY - ADDITION_FOR_POOLED_LIST_CACHE_ENTRY);
typedef struct qfile_list_cache_entry_pool QFILE_LIST_CACHE_ENTRY_POOL;
struct qfile_list_cache_entry_pool
{
QFILE_POOLED_LIST_CACHE_ENTRY *pool; /* pre-allocated array */
int n_entries; /* number of entries in the pool */
int free_list; /* the head(first entry) of the free list */
};
/*
* query result(list file) cache related things
*/
/* list cache and related information */
static QFILE_LIST_CACHE qfile_List_cache = { NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0 };
/* information of candidates to be removed from XASL cache */
static QFILE_LIST_CACHE_CANDIDATE qfile_List_cache_candidate = { 0, 0, 0, 0, NULL, NULL, NULL, 0, 0, false };
/* list cache entry pool */
static QFILE_LIST_CACHE_ENTRY_POOL qfile_List_cache_entry_pool = { NULL, 0, 0 };
/* sort list freelist */
static LF_FREELIST qfile_sort_list_Freelist;
static void *qfile_alloc_sort_list (void);
static int qfile_dealloc_sort_list (void *sort_list);
static LF_ENTRY_DESCRIPTOR qfile_sort_list_entry_desc = {
offsetof (SORT_LIST, local_next),
offsetof (SORT_LIST, next),
offsetof (SORT_LIST, del_id),
0, /* does not have a key, not used in a hash table */
0, /* does not have a mutex */
LF_EM_NOT_USING_MUTEX,
LF_ENTRY_DESCRIPTOR_MAX_ALLOC,
qfile_alloc_sort_list,
qfile_dealloc_sort_list,
NULL,
NULL,
NULL, NULL, NULL, /* no key */
NULL /* no inserts */
};
/*
* Query File Manager Constants/Global Variables
*/
int qfile_Is_list_cache_disabled;
static int qfile_Max_tuple_page_size;
static int qfile_get_sort_list_size (SORT_LIST * sort_list);
static int qfile_compare_tuple_values (QFILE_TUPLE tplp1, QFILE_TUPLE tplp2, TP_DOMAIN * domain, int *cmp);
static void qfile_initialize_page_header (PAGE_PTR page_p);
static void qfile_set_dirty_page_and_skip_logging (THREAD_ENTRY * thread_p, PAGE_PTR page_p, VFID * vfid_p,
int free_page);
static bool qfile_is_first_tuple (QFILE_LIST_ID * list_id_p);
static bool qfile_is_last_page_full (QFILE_LIST_ID * list_id_p, int tuple_length, const bool is_ovf_page);
static void qfile_set_dirty_page (THREAD_ENTRY * thread_p, PAGE_PTR page_p, int free_page, QMGR_TEMP_FILE * vfid_p);
static PAGE_PTR qfile_allocate_new_page (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR page_p,
bool is_ovf_page);
static PAGE_PTR qfile_allocate_new_ovf_page (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR page_p,
PAGE_PTR prev_page_p, int tuple_length, int offset,
int *tuple_page_size_p);
static int qfile_allocate_new_page_if_need (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR * page_p,
int tuple_length, bool is_ovf_page);
static void qfile_add_tuple_to_list_id (QFILE_LIST_ID * list_id_p, PAGE_PTR page_p, int tuple_length,
int written_tuple_length);
static int qfile_save_single_bound_item_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p,
int tuple_length);
static int qfile_save_normal_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p,
int tuple_length);
static int qfile_save_sort_key_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p,
int tuple_length);
static int qfile_save_merge_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p,
int *tuple_length_p);
static int qfile_compare_tuple_helper (QFILE_TUPLE lhs, QFILE_TUPLE rhs, QFILE_TUPLE_VALUE_TYPE_LIST * types, int *cmp);
static SCAN_CODE qfile_advance_single (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * next_scan,
QFILE_TUPLE_RECORD * next_tpl, QFILE_LIST_SCAN_ID * last_scan,
QFILE_TUPLE_RECORD * last_tpl, QFILE_TUPLE_VALUE_TYPE_LIST * types);
static SCAN_CODE qfile_advance_group (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * next_scan,
QFILE_TUPLE_RECORD * next_tpl, QFILE_LIST_SCAN_ID * last_scan,
QFILE_TUPLE_RECORD * last_tpl, QFILE_TUPLE_VALUE_TYPE_LIST * types);
static int qfile_add_one_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * dst, QFILE_TUPLE lhs, QFILE_TUPLE rhs);
static int qfile_add_two_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * dst, QFILE_TUPLE lhs, QFILE_TUPLE rhs);
static int qfile_advance (THREAD_ENTRY * thread_p, ADVANCE_FUCTION advance_func, QFILE_TUPLE_RECORD * side_p,
QFILE_TUPLE_RECORD * last_side_p, QFILE_LIST_SCAN_ID * scan_p,
QFILE_LIST_SCAN_ID * last_scan_p, QFILE_LIST_ID * side_file_p, int *have_side_p);
static int qfile_copy_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * to_list_id_p, QFILE_LIST_ID * from_list_id_p);
static void qfile_close_and_free_list_file (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id);
static QFILE_LIST_ID *qfile_union_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id1, QFILE_LIST_ID * list_id2,
int flag);
static SORT_STATUS qfile_get_next_sort_item (THREAD_ENTRY * thread_p, RECDES * recdes, void *arg);
static int qfile_put_next_sort_item (THREAD_ENTRY * thread_p, const RECDES * recdes, void *arg);
static SORT_INFO *qfile_initialize_sort_info (SORT_INFO * info, QFILE_LIST_ID * listid, SORT_LIST * sort_list);
static void qfile_clear_sort_info (SORT_INFO * info);
static int qfile_copy_list_pages (THREAD_ENTRY * thread_p, VPID * old_first_vpidp, QMGR_TEMP_FILE * old_tfile_vfidp,
VPID * new_first_vpidp, VPID * new_last_vpidp, QMGR_TEMP_FILE * new_tfile_vfidp);
static int qfile_get_tuple_from_current_list (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
QFILE_TUPLE_RECORD * tuple_record_p);
static SCAN_CODE qfile_scan_next (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * s_id);
static SCAN_CODE qfile_scan_prev (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * s_id);
static SCAN_CODE qfile_retrieve_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
QFILE_TUPLE_RECORD * tuple_record_p, int peek);
static SCAN_CODE qfile_scan_list (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
SCAN_CODE (*scan_func) (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID *),
QFILE_TUPLE_RECORD * tuple_record_p, int peek);
#if defined(SERVER_MODE)
static int qfile_compare_tran_id (const void *t1, const void *t2);
#endif /* SERVER_MODE */
static unsigned int qfile_hash_db_value_array (const void *key, unsigned int htsize);
static int qfile_compare_equal_db_value_array (const void *key1, const void *key2);
/* for list cache */
static int qfile_assign_list_cache (void);
static QFILE_LIST_CACHE_ENTRY *qfile_allocate_list_cache_entry (int req_size);
static int qfile_free_list_cache_entry (THREAD_ENTRY * thread_p, void *data, void *args);
static int qfile_print_list_cache_entry (THREAD_ENTRY * thread_p, FILE * fp, const void *key, void *data, void *args);
static void qfile_add_uncommitted_list_cache_entry (int tran_index, QFILE_LIST_CACHE_ENTRY * lent);
static void qfile_delete_uncommitted_list_cache_entry (int tran_index, QFILE_LIST_CACHE_ENTRY * lent);
static int qfile_delete_list_cache_entry (THREAD_ENTRY * thread_p, void *data);
static int qfile_end_use_of_list_cache_entry_local (THREAD_ENTRY * thread_p, void *data, void *args);
static bool qfile_is_early_time (struct timeval *a, struct timeval *b);
static int qfile_get_list_cache_entry_size_for_allocate (int nparam);
#if defined(SERVER_MODE)
static int *qfile_get_list_cache_entry_tran_index_array (QFILE_LIST_CACHE_ENTRY * ent);
#endif /* SERVER_MODE */
static DB_VALUE *qfile_get_list_cache_entry_param_values (QFILE_LIST_CACHE_ENTRY * ent);
static int qfile_compare_with_null_value (int o0, int o1, SUBKEY_INFO key_info);
static int qfile_compare_with_interpolation_domain (char *fp0, char *fp1, SUBKEY_INFO * subkey,
SORTKEY_INFO * key_info);
#if defined(SERVER_MODE)
static BH_CMP_RESULT
qfile_compare_cleanup_candidates (const void *left, const void *right, BH_CMP_ARG ignore_arg)
{
double left_weight = ((QFILE_CACHE_CLEANUP_CANDIDATE *) left)->weight;
double right_weight = ((QFILE_CACHE_CLEANUP_CANDIDATE *) right)->weight;
if (left_weight < right_weight)
{
return BH_LT;
}
else if (left_weight == right_weight)
{
return BH_EQ;
}
else
{
return BH_GT;
}
}
static int
qfile_list_cache_cleanup (THREAD_ENTRY * thread_p)
{
BINARY_HEAP *bh = NULL;
QFILE_CACHE_CLEANUP_CANDIDATE candidate;
int candidate_index;
unsigned int i, n;
struct timeval current_time;
int cleanup_count = prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_ENTRIES) * 8 / 10;
int cleanup_pages = prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_PAGES) * 8 / 10;
if (cleanup_count < 1)
{
cleanup_count = 1;
}
if (cleanup_pages < 1)
{
cleanup_pages = 1;
}
bh =
bh_create (thread_p, cleanup_count, sizeof (QFILE_CACHE_CLEANUP_CANDIDATE), qfile_compare_cleanup_candidates, NULL);
if (bh == NULL)
{
return ER_FAILED;
}
gettimeofday (¤t_time, NULL);
/* Collect candidates for cleanup. */
for (n = 0; n < qfile_List_cache.n_hts; n++)
{
MHT_TABLE *ht;
HENTRY_PTR *hvector;
HENTRY_PTR hentry;
INT64 page_ref;
INT64 lru_sec;
ht = qfile_List_cache.list_hts[n];
for (hvector = ht->table, i = 0; i < ht->size; hvector++, i++)
{
if (*hvector != NULL)
{
/* Go over the linked list */
for (hentry = *hvector; hentry != NULL; hentry = hentry->next)
{
candidate.qcache = (QFILE_LIST_CACHE_ENTRY *) hentry->data;
assert (candidate.qcache);
assert (candidate.qcache->xcache_entry);
if (candidate.qcache->last_ta_idx > 0)
{
// exclude in-transaction
continue;
}
page_ref = candidate.qcache->list_id.page_cnt + 1;
lru_sec = current_time.tv_sec - candidate.qcache->time_last_used.tv_sec + 1;
candidate.weight = (double) (candidate.qcache->ref_count + 1) / (double) (page_ref * lru_sec);
(void) bh_try_insert (bh, &candidate, NULL);
}
}
}
}
/* need to sort before traverse */
bh_to_sorted_array (bh);
/* traverse for weight ordering, from light weight to heavy weight */
for (candidate_index = 0; candidate_index < bh->element_count; candidate_index++)
{
bh_element_at (bh, candidate_index, &candidate);
qfile_delete_list_cache_entry (thread_p, candidate.qcache);
if (qfile_List_cache.n_entries <= cleanup_count)
{
if (qfile_List_cache.n_pages <= cleanup_pages)
{
break;
}
}
}
bh_destroy (thread_p, bh);
return NO_ERROR;
}
#endif
int
qcache_get_new_ht_no (THREAD_ENTRY * thread_p)
{
int ht_no = -1;
if (qfile_List_cache.next_ht_no >= 0)
{
ht_no = qfile_List_cache.next_ht_no;
qfile_List_cache.next_ht_no = qfile_List_cache.free_ht_list[qfile_List_cache.next_ht_no];
}
return ht_no;
}
void
qcache_free_ht_no (THREAD_ENTRY * thread_p, int ht_no)
{
(void) mht_clear (qfile_List_cache.list_hts[ht_no], NULL, NULL);
qfile_List_cache.free_ht_list[ht_no] = qfile_List_cache.next_ht_no;
qfile_List_cache.next_ht_no = ht_no;
}
/* qfile_modify_type_list () -
* return:
* type_list(in):
* list_id(out):
*/
int
qfile_modify_type_list (QFILE_TUPLE_VALUE_TYPE_LIST * type_list_p, QFILE_LIST_ID * list_id_p)
{
size_t type_list_size;
list_id_p->type_list.type_cnt = type_list_p->type_cnt;
list_id_p->type_list.domp = NULL;
if (list_id_p->type_list.type_cnt != 0)
{
type_list_size = list_id_p->type_list.type_cnt * DB_SIZEOF (TP_DOMAIN *);
list_id_p->type_list.domp = (TP_DOMAIN **) malloc (type_list_size);
if (list_id_p->type_list.domp == NULL)
{
return ER_FAILED;
}
memcpy (list_id_p->type_list.domp, type_list_p->domp, type_list_size);
}
list_id_p->tpl_descr.f_valp = NULL;
return NO_ERROR;
}
/*
* qfile_copy_list_id - Copy contents of source list_id into destination list_id
* return: NO_ERROR or ER_FAILED
* dest_list_id(out): destination list_id
* src_list_id(in): source list_id
* include_sort_list(in):
* mode(in): dependent mode
*/
int
qfile_copy_list_id (QFILE_LIST_ID * dest_list_id_p, const QFILE_LIST_ID * src_list_id_p, bool is_include_sort_list,
QFILE_DEPENDENT_MODE dep_mode)
{
size_t type_list_size;
memcpy (dest_list_id_p, src_list_id_p, DB_SIZEOF (QFILE_LIST_ID));
/* copy domain info of type list */
dest_list_id_p->type_list.domp = NULL;
if (dest_list_id_p->type_list.type_cnt > 0)
{
type_list_size = dest_list_id_p->type_list.type_cnt * sizeof (TP_DOMAIN *);
dest_list_id_p->type_list.domp = (TP_DOMAIN **) malloc (type_list_size);
if (dest_list_id_p->type_list.domp == NULL)
{
return ER_FAILED;
}
memcpy (dest_list_id_p->type_list.domp, src_list_id_p->type_list.domp, type_list_size);
}
/* copy sort list */
dest_list_id_p->sort_list = NULL;
if (is_include_sort_list && src_list_id_p->sort_list)
{
SORT_LIST *src, *dest = NULL;
int len;
len = qfile_get_sort_list_size (src_list_id_p->sort_list);
if (len > 0)
{
dest = qfile_allocate_sort_list (NULL, len);
if (dest == NULL)
{
free_and_init (dest_list_id_p->type_list.domp);
return ER_FAILED;
}
}
/* copy sort list item */
for (src = src_list_id_p->sort_list, dest_list_id_p->sort_list = dest; src != NULL && dest != NULL;
src = src->next, dest = dest->next)
{
dest->s_order = src->s_order;
dest->s_nulls = src->s_nulls;
dest->pos_descr.dom = src->pos_descr.dom;
dest->pos_descr.pos_no = src->pos_descr.pos_no;
}
}
else
{
dest_list_id_p->sort_list = NULL;
}
memset (&dest_list_id_p->tpl_descr, 0, sizeof (QFILE_TUPLE_DESCRIPTOR));
if (src_list_id_p->dependent_list_id != NULL)
{
switch (dep_mode)
{
case QFILE_SKIP_DEPENDENT:
dest_list_id_p->dependent_list_id = NULL;
break;
case QFILE_MOVE_DEPENDENT:
/* transfer ownership */
assert (dest_list_id_p->dependent_list_id == src_list_id_p->dependent_list_id);
const_cast < QFILE_LIST_ID * >(src_list_id_p)->dependent_list_id = NULL;
break;
case QFILE_PROHIBIT_DEPENDENT:
default:
/* impossible case */
assert_release_error (false);
return er_errid ();
}
}
else
{
assert (dest_list_id_p->dependent_list_id == NULL);
}
qfile_update_qlist_count (thread_get_thread_entry_info (), dest_list_id_p, 1);
return NO_ERROR;
}
/*
* qfile_clone_list_id () - Clone (allocate and copy) the list_id
* return: cloned list id
* list_id(in): source list id
* incluse_sort_list(in):
*/
QFILE_LIST_ID *
qfile_clone_list_id (const QFILE_LIST_ID * list_id_p, bool is_include_sort_list, QFILE_DEPENDENT_MODE dep_mode)
{
QFILE_LIST_ID *cloned_id_p;
/* allocate new LIST_ID to be returned */
cloned_id_p = (QFILE_LIST_ID *) malloc (DB_SIZEOF (QFILE_LIST_ID));
if (cloned_id_p)
{
if (qfile_copy_list_id (cloned_id_p, list_id_p, is_include_sort_list, dep_mode) != NO_ERROR)
{
free_and_init (cloned_id_p);
}
}
return cloned_id_p;
}
/*
* qfile_clear_list_id () -
* list_id(in/out): List identifier
*
* Note: The allocated areas inside the area pointed by the list_id is
* freed and the area pointed by list_id is set to null values.
*/
void
qfile_clear_list_id (QFILE_LIST_ID * list_id_p)
{
qfile_update_qlist_count (thread_get_thread_entry_info (), list_id_p, -1);
if (list_id_p->tpl_descr.f_valp)
{
free_and_init (list_id_p->tpl_descr.f_valp);
}
if (list_id_p->sort_list)
{
qfile_free_sort_list (NULL, list_id_p->sort_list);
list_id_p->sort_list = NULL;
}
if (list_id_p->type_list.domp != NULL)
{
free_and_init (list_id_p->type_list.domp);
}
if (list_id_p->dependent_list_id != NULL)
{
qfile_clear_list_id (list_id_p->dependent_list_id);
free_and_init (list_id_p->dependent_list_id);
}
QFILE_CLEAR_LIST_ID (list_id_p);
}
/*
* qfile_free_list_id () -
* return:
* list_id(in/out): List identifier
*
* Note: The allocated areas inside the area pointed by the list_id and
* the area itself pointed by the list_id are freed.
*/
void
qfile_free_list_id (QFILE_LIST_ID * list_id_p)
{
/* This function is remained for debugging purpose. Do not call this function directly. Use
* QFILE_FREE_AND_INIT_LIST_ID macro. */
qfile_clear_list_id (list_id_p);
free (list_id_p);
}
/*
* qfile_free_sort_list () -
* return:
* sort_list(in): Sort item list pointer
*
* Note: The area allocated for sort_list is freed.
*/
void
qfile_free_sort_list (THREAD_ENTRY * thread_p, SORT_LIST * sort_list_p)
{
LF_TRAN_ENTRY *t_entry;
SORT_LIST *tmp;
/* get tran entry */
t_entry = thread_get_tran_entry (thread_p, THREAD_TS_FREE_SORT_LIST);
while (sort_list_p != NULL)
{
tmp = sort_list_p;
sort_list_p = sort_list_p->next;
tmp->next = NULL;
if (lf_freelist_retire (t_entry, &qfile_sort_list_Freelist, tmp) != NO_ERROR)
{
assert (false);
return;
}
}
}
/*
* qfile_allocate_sort_list () -
* return: sort item list, or NULL
* cnt(in): Number of nodes in the list
*
* Note: A linked list of cnt sort structure nodes is allocated and returned.
*
* Note: Only qfile_free_sort_list function should be used to free the area
* since the linked list is allocated in a contigous region.
*/
SORT_LIST *
qfile_allocate_sort_list (THREAD_ENTRY * thread_p, int count)
{
LF_TRAN_ENTRY *t_entry;
SORT_LIST *head = NULL, *tail = NULL, *tmp;
if (count <= 0)
{
return NULL;
}
/* fetch tran entry */
t_entry = thread_get_tran_entry (thread_p, THREAD_TS_FREE_SORT_LIST);
/* allocate complete list */
while (count > 0)
{
tmp = (SORT_LIST *) lf_freelist_claim (t_entry, &qfile_sort_list_Freelist);
if (tmp == NULL)
{
assert (false);
return NULL;
}
if (head == NULL)
{
head = tmp;
tail = tmp;
}
else
{
tail->next = tmp;
tail = tmp;
}
count--;
}
return head;
}
/*
* qfile_alloc_sort_list () - allocate a sort list
* returns: new sort list
*/
static void *
qfile_alloc_sort_list (void)
{
return malloc (sizeof (SORT_LIST));
}
/*
* qfile_dealloc_sort_list () - deallocate a sort list
* returns: error code or NO_ERROR
* sort_list(in): sort list to free
*/
static int
qfile_dealloc_sort_list (void *sort_list)
{
free (sort_list);
return NO_ERROR;
}
/*
* qfile_get_sort_list_size () -
* return: the number of sort_list item
* sort_list(in): sort item list pointer
*
*/
static int
qfile_get_sort_list_size (SORT_LIST * sort_list_p)
{
SORT_LIST *s;
int len = 0;
for (s = sort_list_p; s; s = s->next)
{
++len;
}
return len;
}
/*
* qfile_is_sort_list_covered () -
* return: true or false
* covering_list(in): covering sort item list pointer
* covered_list(in): covered sort item list pointer
*
* Note: if covering_list covers covered_list returns true.
* otherwise, returns false.
*/
bool
qfile_is_sort_list_covered (SORT_LIST * covering_list_p, SORT_LIST * covered_list_p)
{
SORT_LIST *s1, *s2;
if (covered_list_p == NULL)
{
return false;
}
for (s1 = covering_list_p, s2 = covered_list_p; s1 && s2; s1 = s1->next, s2 = s2->next)
{
if (s1->s_order != s2->s_order || s1->s_nulls != s2->s_nulls || s1->pos_descr.pos_no != s2->pos_descr.pos_no)
{
return false;
}
}
if (s1 == NULL && s2)
{
return false;
}
else
{
return true;
}
}
/*
* qfile_compare_tuple_values () -
* return: NO_ERROR or error code
* tpl1(in): First tuple value
* tpl2(in): Second tuple value
* domain(in): both tuple values must be of the same domain
*
* Note: This routine checks if two tuple values are equal.
* Coercion is not done.
* If both values are UNBOUND, the values are treated as equal.
*/
static int
qfile_compare_tuple_values (QFILE_TUPLE tuple1, QFILE_TUPLE tuple2, TP_DOMAIN * domain_p, int *compare_result)
{
OR_BUF buf;
DB_VALUE dbval1, dbval2;
int length1, length2;
const PR_TYPE *pr_type_p;
bool is_copy;
DB_TYPE type = TP_DOMAIN_TYPE (domain_p);
int rc;
pr_type_p = domain_p->type;
is_copy = false;
is_copy = pr_is_set_type (type) ? true : false;
length1 = QFILE_GET_TUPLE_VALUE_LENGTH (tuple1);
/* zero length means NULL */
if (length1 == 0)
{
db_make_null (&dbval1);
}
else
{
or_init (&buf, (char *) tuple1 + QFILE_TUPLE_VALUE_HEADER_SIZE, length1);
rc = pr_type_p->data_readval (&buf, &dbval1, domain_p, -1, is_copy, NULL, 0);
if (rc != NO_ERROR)
{
return ER_FAILED;
}
}
length2 = QFILE_GET_TUPLE_VALUE_LENGTH (tuple2);
/* zero length means NULL */
if (length2 == 0)
{
db_make_null (&dbval2);
}
else
{
or_init (&buf, (char *) tuple2 + QFILE_TUPLE_VALUE_HEADER_SIZE, length2);
rc = pr_type_p->data_readval (&buf, &dbval2, domain_p, -1, is_copy, NULL, 0);
if (rc != NO_ERROR)
{
pr_clear_value (&dbval1);
return ER_FAILED;
}
}
if (length1 == 0 && length2 == 0)
{
*compare_result = DB_EQ; /* NULL values compare equal in this routine */
}
else if (length1 == 0)
{
*compare_result = DB_LT;
}
else if (length2 == 0)
{
*compare_result = DB_GT;
}
else
{
*compare_result = pr_type_p->cmpval (&dbval1, &dbval2, 0, 1, NULL, domain_p->collation_id);
}
pr_clear_value (&dbval1);
pr_clear_value (&dbval2);
return NO_ERROR;
}
/*
* qfile_unify_types () -
* return:
* list_id1(in/out): Destination list identifier
* list_id2(in): Source list identifier
*
* Note: For every destination type which is DB_TYPE_NULL,
* set it to the source type.
* This should probably set an error for non-null mismatches.
*/
int
qfile_unify_types (QFILE_LIST_ID * list_id1_p, const QFILE_LIST_ID * list_id2_p)
{
int i;
int max_count = list_id1_p->type_list.type_cnt;
DB_TYPE type1, type2;
if (max_count != list_id2_p->type_list.type_cnt)
{
/* error, but is ignored for now. */
if (max_count > list_id2_p->type_list.type_cnt)
{
max_count = list_id2_p->type_list.type_cnt;
}
}
for (i = 0; i < max_count; i++)
{
type1 = TP_DOMAIN_TYPE (list_id1_p->type_list.domp[i]);
type2 = TP_DOMAIN_TYPE (list_id2_p->type_list.domp[i]);
if (type1 == DB_TYPE_VARIABLE)
{
/* The domain of list1 is not resolved, because there is no tuple. */
assert_release (list_id1_p->tuple_cnt == 0);
list_id1_p->type_list.domp[i] = list_id2_p->type_list.domp[i];
continue;
}
else if (type2 == DB_TYPE_VARIABLE)
{
/* The domain of list2 is not resolved, because there is no tuple. */
assert_release (list_id2_p->tuple_cnt == 0);
continue;
}
if (type1 == DB_TYPE_NULL)
{
list_id1_p->type_list.domp[i] = list_id2_p->type_list.domp[i];
}
else
{
if (type2 != DB_TYPE_NULL && (list_id1_p->type_list.domp[i] != list_id2_p->type_list.domp[i]))
{
if (type1 == type2
&& ((pr_is_string_type (type1) && pr_is_variable_type (type1)) || (type1 == DB_TYPE_JSON)))
{
/* OK for variable string types with different precision or json types */
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_INCOMPATIBLE_TYPES, 0);
return ER_QPROC_INCOMPATIBLE_TYPES;
}
}
}
if (TP_DOMAIN_COLLATION_FLAG (list_id1_p->type_list.domp[i]) != TP_DOMAIN_COLL_NORMAL
|| TP_DOMAIN_COLLATION_FLAG (list_id2_p->type_list.domp[i]) != TP_DOMAIN_COLL_NORMAL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QSTR_INCOMPATIBLE_COLLATIONS, 0);
return ER_QSTR_INCOMPATIBLE_COLLATIONS;
}
}
return NO_ERROR;
}
/*
* qfile_locate_tuple_value () -
* return: V_BOUND/V_UNBOUND
* tpl(in): tuple
* index(in): value position number
* tpl_val(out): set to point to the specified value
* val_size(out): set to the value size
*
* Note: Sets the tpl_val pointer to the specified value.
*
* Note: The index validity check must be done by the caller.
*/
QFILE_TUPLE_VALUE_FLAG
qfile_locate_tuple_value (QFILE_TUPLE tuple, int index, char **tuple_value_p, int *value_size_p)
{
tuple += QFILE_TUPLE_LENGTH_SIZE;
return qfile_locate_tuple_value_r (tuple, index, tuple_value_p, value_size_p);
}
/*
* qfile_locate_tuple_value_r () -
* return: V_BOUND/V_UNBOUND
* tpl(in): tuple
* index(in): value position number
* tpl_val(out): set to point to the specified value
* val_size(out): set to the value size
*
* Note: The index validity check must be done by the caller.
*/
QFILE_TUPLE_VALUE_FLAG
qfile_locate_tuple_value_r (QFILE_TUPLE tuple, int index, char **tuple_value_p, int *value_size_p)
{
int i;
for (i = 0; i < index; i++)
{
tuple += QFILE_TUPLE_VALUE_HEADER_SIZE + QFILE_GET_TUPLE_VALUE_LENGTH (tuple);
}
*tuple_value_p = tuple + QFILE_TUPLE_VALUE_HEADER_SIZE;
*value_size_p = QFILE_GET_TUPLE_VALUE_LENGTH (tuple);
if (QFILE_GET_TUPLE_VALUE_FLAG (tuple) == V_UNBOUND)
{
return V_UNBOUND;
}
else
{
return V_BOUND;
}
}
/*
* qfile_locate_tuple_next_value () -
* return: error code or no error
* iterator(in): OR_BUF for iterating tuple values
* buf(out): output OR_BUF to be positioned on next tuple value
* flag(out): V_BOUND/V_UNBOUND
*/
int
qfile_locate_tuple_next_value (OR_BUF * iterator, OR_BUF * buf, QFILE_TUPLE_VALUE_FLAG * flag)
{
int value_size = QFILE_GET_TUPLE_VALUE_LENGTH (iterator->ptr);
*flag = QFILE_GET_TUPLE_VALUE_FLAG (iterator->ptr);
/* initialize output buffer */
or_init (buf, iterator->ptr + QFILE_TUPLE_VALUE_HEADER_SIZE, value_size);
/* advance iterator */
return or_advance (iterator, QFILE_TUPLE_VALUE_HEADER_SIZE + value_size);
}
#if !defined(NDEBUG)
/*
* qfile_print_tuple () - Prints the tuple content associated with the type list
* return: none
* type_list(in): type list
* tpl(in): tuple
* Note: Each tuple start is aligned with MAX_ALIGNMENT
* Each tuple value header is aligned with MAX_ALIGNMENT,
* Each tuple value is aligned with MAX_ALIGNMENT
*/
void
qfile_print_tuple (QFILE_TUPLE_VALUE_TYPE_LIST * type_list_p, QFILE_TUPLE tuple)
{
DB_VALUE dbval;
const PR_TYPE *pr_type_p;
int i;
char *tuple_p;
OR_BUF buf;
db_make_null (&dbval);
if (type_list_p == NULL || type_list_p->type_cnt <= 0)
{
return;
}
fprintf (stdout, "\n{ ");
tuple_p = (char *) tuple + QFILE_TUPLE_LENGTH_SIZE;
for (i = 0; i < type_list_p->type_cnt; i++)
{
if (QFILE_GET_TUPLE_VALUE_FLAG (tuple_p) == V_BOUND)
{
pr_type_p = type_list_p->domp[i]->type;
or_init (&buf, tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, QFILE_GET_TUPLE_VALUE_LENGTH (tuple_p));
pr_type_p->data_readval (&buf, &dbval, type_list_p->domp[i], -1, false /* Don't copy */ , NULL, 0);
db_fprint_value (stdout, &dbval);
if (db_value_need_clear (&dbval))
{
pr_clear_value (&dbval);
}
}
else
{
fprintf (stdout, "VALUE_UNBOUND");
}
if (i != type_list_p->type_cnt - 1)
{
fprintf (stdout, " , ");
}
tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE + QFILE_GET_TUPLE_VALUE_LENGTH (tuple_p);
}
fprintf (stdout, " }\n");
}
#endif
static void
qfile_initialize_page_header (PAGE_PTR page_p)
{
OR_PUT_INT (page_p + QFILE_TUPLE_COUNT_OFFSET, 0);
OR_PUT_INT (page_p + QFILE_PREV_PAGE_ID_OFFSET, NULL_PAGEID);
OR_PUT_INT (page_p + QFILE_NEXT_PAGE_ID_OFFSET, NULL_PAGEID);
OR_PUT_INT (page_p + QFILE_LAST_TUPLE_OFFSET, 0);
OR_PUT_INT (page_p + QFILE_OVERFLOW_PAGE_ID_OFFSET, NULL_PAGEID);
OR_PUT_SHORT (page_p + QFILE_PREV_VOL_ID_OFFSET, NULL_VOLID);
OR_PUT_SHORT (page_p + QFILE_NEXT_VOL_ID_OFFSET, NULL_VOLID);
OR_PUT_SHORT (page_p + QFILE_OVERFLOW_VOL_ID_OFFSET, NULL_VOLID);
#if !defined(NDEBUG)
/* suppress valgrind UMW error */
memset (page_p + QFILE_RESERVED_OFFSET, 0, QFILE_PAGE_HEADER_SIZE - QFILE_RESERVED_OFFSET);
#endif
}
static void
qfile_set_dirty_page_and_skip_logging (THREAD_ENTRY * thread_p, PAGE_PTR page_p, VFID * vfid_p, int free_page)
{
LOG_DATA_ADDR addr;
addr.vfid = vfid_p;
addr.pgptr = page_p;
addr.offset = -1;
log_skip_logging (thread_p, &addr);
pgbuf_set_dirty (thread_p, page_p, free_page);
}
/*
* qfile_load_xasl_node_header () - Load XASL node header from xasl stream
*
* return : void
* thread_p (in) : thread entry
* xasl_stream (in) : XASL stream
* xasl_header_p (out) : pointer to XASL node header
*/
void
qfile_load_xasl_node_header (THREAD_ENTRY * thread_p, char *xasl_stream, xasl_node_header * xasl_header_p)
{
if (xasl_header_p == NULL)
{
/* cannot save XASL node header */
return;
}
/* initialize XASL node header */
INIT_XASL_NODE_HEADER (xasl_header_p);
if (xasl_stream == NULL)
{
/* cannot obtain XASL stream */
return;
}
/* get XASL node header from stream */
if (stx_map_stream_to_xasl_node_header (thread_p, xasl_header_p, xasl_stream) != NO_ERROR)
{
assert (false);
}
}
/*
* qfile_initialize () -
* return: int (true : successful initialization,
* false: unsuccessful initialization)
*
* Note: This routine initializes the query file manager structures
* and global variables.
*/
int
qfile_initialize (void)
{
qfile_Is_list_cache_disabled =
((prm_get_integer_value (PRM_ID_LIST_QUERY_CACHE_MODE) == QFILE_LIST_QUERY_CACHE_MODE_OFF)
|| (prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_ENTRIES) <= 0)
|| (prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_PAGES) <= 0));
qfile_Max_tuple_page_size = QFILE_MAX_TUPLE_SIZE_IN_PAGE;
if (lf_freelist_init (&qfile_sort_list_Freelist, 10, 100, &qfile_sort_list_entry_desc, &free_sort_list_Ts) !=
NO_ERROR)
{
return ER_FAILED;
}
return true;
}
/* qfile_finalize () -
* return:
*/
void
qfile_finalize (void)
{
lf_freelist_destroy (&qfile_sort_list_Freelist);
}
/*
* qfile_open_list () -
* return: QFILE_LIST_ID *, or NULL
* type_list(in/out): type list for the list file to be created
* sort_list(in): sort info for the list file to be created
* query_id(in): query id associated with this list file
* flag(in): {QFILE_FLAG_RESULT_FILE, QFILE_FLAG_DISTINCT, QFILE_FLAG_ALL}
* whether to do 'all' or 'distinct' operation
*
* Note: A list file is created by using the specified type list and
* the list file identifier is set. The first page of the list
* file is allocated only when the first tuple is inserted to
* list file, if any.
* A 'SORT_LIST' is associated to the output list file according to
* 'sort_list_p' input argument (if not null), or created if the
* QFILE_FLAG_DISTINCT flag is specified; if neither QFILE_FLAG_DISTINCT
* or 'sort_list_p' are supplied, no SORT_LIST is associated.
*
*/
QFILE_LIST_ID *
qfile_open_list (THREAD_ENTRY * thread_p, QFILE_TUPLE_VALUE_TYPE_LIST * type_list_p, SORT_LIST * sort_list_p,
QUERY_ID query_id, int flag, QFILE_LIST_ID * existing_list_id)
{
QFILE_LIST_ID *list_id_p;
int len, i;
SORT_LIST *src_sort_list_p, *dest_sort_list_p;
size_t type_list_size;
if (existing_list_id != NULL)
{
list_id_p = existing_list_id;
}
else
{
list_id_p = (QFILE_LIST_ID *) malloc (sizeof (QFILE_LIST_ID));
}
if (list_id_p == NULL)
{
return NULL;
}
QFILE_CLEAR_LIST_ID (list_id_p);
list_id_p->tuple_cnt = 0;
list_id_p->page_cnt = 0;
list_id_p->type_list.type_cnt = type_list_p->type_cnt;
list_id_p->query_id = query_id;
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_RESULT_FILE) && !QFILE_IS_LIST_CACHE_DISABLED)
{
list_id_p->tfile_vfid = qmgr_create_result_file (thread_p, query_id);
}
else if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_USE_KEY_BUFFER))
{
list_id_p->tfile_vfid = qmgr_create_new_temp_file (thread_p, query_id, TEMP_FILE_MEMBUF_KEY_BUFFER);
}
else
{
list_id_p->tfile_vfid = qmgr_create_new_temp_file (thread_p, query_id, TEMP_FILE_MEMBUF_NORMAL);
}
if (list_id_p->tfile_vfid == NULL)
{
if (existing_list_id == NULL)
{
free_and_init (list_id_p);
}
return NULL;
}
if (QFILE_IS_FLAG_SET (flag, QFILE_NOT_USE_MEMBUF))
{
list_id_p->tfile_vfid->membuf_last = prm_get_integer_value (PRM_ID_TEMP_MEM_BUFFER_PAGES) - 1;
list_id_p->tfile_vfid->membuf = NULL;
list_id_p->tfile_vfid->membuf_npages = 0;
list_id_p->tfile_vfid->membuf_type = TEMP_FILE_MEMBUF_NONE;
}
VFID_COPY (&(list_id_p->temp_vfid), &(list_id_p->tfile_vfid->temp_vfid));
list_id_p->type_list.domp = NULL;
if (list_id_p->type_list.type_cnt != 0)
{
type_list_size = list_id_p->type_list.type_cnt * DB_SIZEOF (TP_DOMAIN *);
list_id_p->type_list.domp = (TP_DOMAIN **) malloc (type_list_size);
if (list_id_p->type_list.domp == NULL)
{
if (existing_list_id == NULL)
{
free_and_init (list_id_p);
}
return NULL;
}
memcpy (list_id_p->type_list.domp, type_list_p->domp, type_list_size);
}
/* build sort_list */
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_DISTINCT))
{
len = list_id_p->type_list.type_cnt;
if (len > 0)
{
dest_sort_list_p = qfile_allocate_sort_list (thread_p, len);
if (dest_sort_list_p == NULL)
{
free_and_init (list_id_p->type_list.domp);
if (existing_list_id == NULL)
{
free_and_init (list_id_p);
}
return NULL;
}
for (i = 0, list_id_p->sort_list = dest_sort_list_p; i < len; i++, dest_sort_list_p = dest_sort_list_p->next)
{
dest_sort_list_p->s_order = S_ASC;
dest_sort_list_p->s_nulls = S_NULLS_FIRST;
dest_sort_list_p->pos_descr.dom = list_id_p->type_list.domp[i];
dest_sort_list_p->pos_descr.pos_no = i;
}
}
}
else if (sort_list_p != NULL)
{
len = qfile_get_sort_list_size (sort_list_p);
if (len > 0)
{
dest_sort_list_p = qfile_allocate_sort_list (thread_p, len);
if (dest_sort_list_p == NULL)
{
free_and_init (list_id_p->type_list.domp);
if (existing_list_id == NULL)
{
free_and_init (list_id_p);
}
return NULL;
}
for (src_sort_list_p = sort_list_p, list_id_p->sort_list = dest_sort_list_p; src_sort_list_p;
src_sort_list_p = src_sort_list_p->next, dest_sort_list_p = dest_sort_list_p->next)
{
dest_sort_list_p->s_order = src_sort_list_p->s_order;
dest_sort_list_p->s_nulls = src_sort_list_p->s_nulls;
dest_sort_list_p->pos_descr.dom = src_sort_list_p->pos_descr.dom;
dest_sort_list_p->pos_descr.pos_no = src_sort_list_p->pos_descr.pos_no;
}
}
}
else
{
/* no DISTINCT and no source SORT_LIST supplied */
list_id_p->sort_list = NULL;
}
qfile_update_qlist_count (thread_p, list_id_p, 1);
return list_id_p;
}
/*
* qfile_close_list () -
* return: none
* list_id(in/out): List file identifier
*
* Note: The specified list file is closed and memory buffer for the
* list file is freed.
*/
void
qfile_close_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p)
{
if (list_id_p)
{
if (list_id_p->last_pgptr != NULL)
{
QFILE_PUT_NEXT_VPID_NULL (list_id_p->last_pgptr);
qmgr_free_old_page_and_init (thread_p, list_id_p->last_pgptr, list_id_p->tfile_vfid);
}
}
}
/*
* qfile_reopen_list_as_append_mode () -
* thread_p(in) :
* list_id_p(in):
*
* Note:
*/
int
qfile_reopen_list_as_append_mode (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p)
{
PAGE_PTR last_page_ptr;
QMGR_TEMP_FILE *temp_file_p;
if (list_id_p->tfile_vfid == NULL)
{
/* Invalid list_id_p. list_id_p might be cleared or not be opened. list_id_p must have valid QMGR_TEMP_FILE to
* reopen. */
assert_release (0);
return ER_FAILED;
}
if (VPID_ISNULL (&list_id_p->first_vpid))
{
assert_release (VPID_ISNULL (&list_id_p->last_vpid));
assert_release (list_id_p->last_pgptr == NULL);
return NO_ERROR;
}
if (list_id_p->last_pgptr != NULL)
{
return NO_ERROR;
}
temp_file_p = list_id_p->tfile_vfid;
if (temp_file_p->membuf && list_id_p->last_vpid.volid == NULL_VOLID)
{
/* The last page is in the membuf */
assert_release (temp_file_p->membuf_last >= list_id_p->last_vpid.pageid);
/* The page of last record in the membuf */
last_page_ptr = temp_file_p->membuf[list_id_p->last_vpid.pageid];
}
else
{
assert_release (!VPID_ISNULL (&list_id_p->last_vpid));
last_page_ptr =
pgbuf_fix (thread_p, &list_id_p->last_vpid, OLD_PAGE, PGBUF_LATCH_WRITE, PGBUF_UNCONDITIONAL_LATCH);
if (last_page_ptr == NULL)
{
return ER_FAILED;
}
#if !defined (NDEBUG)
(void) pgbuf_check_page_ptype (thread_p, last_page_ptr, PAGE_QRESULT);
#endif /* !NDEBUG */
}
list_id_p->last_pgptr = last_page_ptr;
return NO_ERROR;
}
static bool
qfile_is_first_tuple (QFILE_LIST_ID * list_id_p)
{
return VPID_ISNULL (&list_id_p->first_vpid);
}
static bool
qfile_is_last_page_full (QFILE_LIST_ID * list_id_p, int tuple_length, const bool is_ovf_page)
{
assert (tuple_length >= 0 && list_id_p->last_offset >= 0);
assert ((tuple_length + list_id_p->last_offset) >= 0);
assert (list_id_p->last_offset <= DB_PAGESIZE);
if (!is_ovf_page && list_id_p->last_offset <= QFILE_PAGE_HEADER_SIZE)
{
/* empty page - it must have at least one tuple record. */
assert (list_id_p->last_offset == QFILE_PAGE_HEADER_SIZE);
return false;
}
return (tuple_length + list_id_p->last_offset) > DB_PAGESIZE;
}
static void
qfile_set_dirty_page (THREAD_ENTRY * thread_p, PAGE_PTR page_p, int free_page, QMGR_TEMP_FILE * vfid_p)
{
LOG_DATA_ADDR addr;
addr.vfid = NULL;
addr.pgptr = page_p;
addr.offset = -1;
qmgr_set_dirty_page (thread_p, page_p, free_page, &addr, vfid_p);
}
static PAGE_PTR
qfile_allocate_new_page (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR page_p, bool is_ovf_page)
{
PAGE_PTR new_page_p;
VPID new_vpid;
#if defined (SERVER_MODE)
if (qmgr_is_query_interrupted (thread_p, list_id_p->query_id) == true)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INTERRUPTED, 0);
return NULL;
}
#endif /* SERVER_MODE */
new_page_p = qmgr_get_new_page (thread_p, &new_vpid, list_id_p->tfile_vfid);
if (new_page_p == NULL)
{
return NULL;
}
QFILE_PUT_TUPLE_COUNT (new_page_p, 0);
QFILE_PUT_PREV_VPID (new_page_p, &list_id_p->last_vpid);
/*
* For streaming query support, set next_vpid differently
*/
if (is_ovf_page)
{
QFILE_PUT_NEXT_VPID_NULL (new_page_p);
}
else
{
LS_PUT_NEXT_VPID (new_page_p);
}
QFILE_PUT_LAST_TUPLE_OFFSET (new_page_p, QFILE_PAGE_HEADER_SIZE);
QFILE_PUT_OVERFLOW_VPID_NULL (new_page_p);
if (page_p)
{
QFILE_PUT_NEXT_VPID (page_p, &new_vpid);
}
list_id_p->page_cnt++;
if (page_p)
{
qfile_set_dirty_page (thread_p, page_p, FREE, list_id_p->tfile_vfid);
}
else
{
/* first list file tuple */
QFILE_COPY_VPID (&list_id_p->first_vpid, &new_vpid);
}
QFILE_COPY_VPID (&list_id_p->last_vpid, &new_vpid);
list_id_p->last_pgptr = new_page_p;
list_id_p->last_offset = QFILE_PAGE_HEADER_SIZE;
return new_page_p;
}
static PAGE_PTR
qfile_allocate_new_ovf_page (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR page_p, PAGE_PTR prev_page_p,
int tuple_length, int offset, int *tuple_page_size_p)
{
PAGE_PTR new_page_p;
VPID new_vpid;
*tuple_page_size_p = MIN (tuple_length - offset, qfile_Max_tuple_page_size);
new_page_p = qmgr_get_new_page (thread_p, &new_vpid, list_id_p->tfile_vfid);
if (new_page_p == NULL)
{
return NULL;
}
list_id_p->page_cnt++;
QFILE_PUT_NEXT_VPID_NULL (new_page_p);
QFILE_PUT_TUPLE_COUNT (new_page_p, QFILE_OVERFLOW_TUPLE_COUNT_FLAG);
QFILE_PUT_OVERFLOW_TUPLE_PAGE_SIZE (new_page_p, *tuple_page_size_p);
QFILE_PUT_OVERFLOW_VPID_NULL (new_page_p);
/*
* connect the previous page to this page and free,
* if it is not the first page
*/
QFILE_PUT_OVERFLOW_VPID (prev_page_p, &new_vpid);
if (prev_page_p != page_p)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
return new_page_p;
}
static int
qfile_allocate_new_page_if_need (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR * page_p,
int tuple_length, bool is_ovf_page)
{
PAGE_PTR new_page_p;
if (qfile_is_first_tuple (list_id_p) || qfile_is_last_page_full (list_id_p, tuple_length, is_ovf_page))
{
new_page_p = qfile_allocate_new_page (thread_p, list_id_p, *page_p, is_ovf_page);
if (new_page_p == NULL)
{
return ER_FAILED;
}
*page_p = new_page_p;
}
if (page_p)
{
if (*page_p)
{
QFILE_PUT_TUPLE_COUNT (*page_p, QFILE_GET_TUPLE_COUNT (*page_p) + 1);
QFILE_PUT_LAST_TUPLE_OFFSET (*page_p, list_id_p->last_offset);
return NO_ERROR;
}
}
return ER_FAILED;
}
static void
qfile_add_tuple_to_list_id (QFILE_LIST_ID * list_id_p, PAGE_PTR page_p, int tuple_length, int written_tuple_length)
{
QFILE_PUT_PREV_TUPLE_LENGTH (page_p, list_id_p->lasttpl_len);
list_id_p->tuple_cnt++;
list_id_p->lasttpl_len = tuple_length;
list_id_p->last_offset += written_tuple_length;
assert (list_id_p->last_offset <= DB_PAGESIZE);
}
/*
* qfile_add_tuple_to_list () - The given tuple is added to the end of the list file
* return: int (NO_ERROR or ER_FAILED)
* list_id(in): List File Identifier
* tpl(in): Tuple to be added
*
*/
int
qfile_add_tuple_to_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, QFILE_TUPLE tuple)
{
PAGE_PTR cur_page_p, new_page_p, prev_page_p;
int tuple_length;
char *page_p, *tuple_p;
int offset, tuple_page_size;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
cur_page_p = list_id_p->last_pgptr;
tuple_length = QFILE_GET_TUPLE_LENGTH (tuple);
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_page_size = MIN (tuple_length, qfile_Max_tuple_page_size);
assert ((list_id_p->last_offset + tuple_page_size) <= DB_PAGESIZE);
memcpy (page_p, tuple, tuple_page_size);
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_page_size);
prev_page_p = cur_page_p;
for (offset = tuple_page_size, tuple_p = (char *) tuple + offset; offset < tuple_length;
offset += tuple_page_size, tuple_p += tuple_page_size)
{
new_page_p =
qfile_allocate_new_ovf_page (thread_p, list_id_p, cur_page_p, prev_page_p, tuple_length, offset,
&tuple_page_size);
if (new_page_p == NULL)
{
if (prev_page_p != cur_page_p)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
return ER_FAILED;
}
memcpy ((char *) new_page_p + QFILE_PAGE_HEADER_SIZE, tuple_p, tuple_page_size);
prev_page_p = new_page_p;
}
if (prev_page_p != cur_page_p)
{
QFILE_PUT_OVERFLOW_VPID_NULL (prev_page_p);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
static int
qfile_save_single_bound_item_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p,
int tuple_length)
{
int align;
align = DB_ALIGN (tuple_descr_p->item_size, MAX_ALIGNMENT);
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, align);
tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE;
memcpy (tuple_p, tuple_descr_p->item, tuple_descr_p->item_size);
#if !defined(NDEBUG)
/* suppress valgrind UMW error */
memset (tuple_p + tuple_descr_p->item_size, 0, align - tuple_descr_p->item_size);
#endif
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
return NO_ERROR;
}
static int
qfile_save_normal_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p, int tuple_length)
{
int i, tuple_value_size;
int total_tuple_value_size = 0;
for (i = 0; i < tuple_descr_p->f_cnt; i++)
{
if (qdata_copy_db_value_to_tuple_value (tuple_descr_p->f_valp[i], tuple_p, &tuple_value_size) != NO_ERROR)
{
return ER_FAILED;
}
total_tuple_value_size += tuple_value_size;
tuple_p += tuple_value_size;
}
assert_release (total_tuple_value_size <= tuple_length);
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
return NO_ERROR;
}
static int
qfile_save_sort_key_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p, int tuple_length)
{
SORTKEY_INFO *key_info_p;
SORT_REC *sort_rec_p;
int i, c, nkeys, len;
char *src_p;
key_info_p = (SORTKEY_INFO *) (tuple_descr_p->sortkey_info);
nkeys = key_info_p->nkeys;
sort_rec_p = (SORT_REC *) (tuple_descr_p->sort_rec);
for (i = 0; i < nkeys; i++)
{
c = key_info_p->key[i].permuted_col;
if (sort_rec_p->s.offset[c] == 0)
{
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_UNBOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, 0);
}
else
{
src_p = (char *) sort_rec_p + sort_rec_p->s.offset[c] - QFILE_TUPLE_VALUE_HEADER_SIZE;
len = QFILE_GET_TUPLE_VALUE_LENGTH (src_p);
memcpy (tuple_p, src_p, len + QFILE_TUPLE_VALUE_HEADER_SIZE);
tuple_p += len;
}
tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE;
}
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
return NO_ERROR;
}
static int
qfile_save_merge_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, char *tuple_p, char *page_p, int *tuple_length_p)
{
QFILE_TUPLE_RECORD *tuple_rec1_p, *tuple_rec2_p;
QFILE_LIST_MERGE_INFO *merge_info_p;
char *src_p;
int i, tuple_value_size;
INT32 ls_unbound[2] = { 0, 0 };
QFILE_PUT_TUPLE_VALUE_FLAG ((char *) ls_unbound, V_UNBOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH ((char *) ls_unbound, 0);
tuple_rec1_p = tuple_descr_p->tplrec1;
tuple_rec2_p = tuple_descr_p->tplrec2;
merge_info_p = tuple_descr_p->merge_info;
*tuple_length_p = QFILE_TUPLE_LENGTH_SIZE;
for (i = 0; i < merge_info_p->ls_pos_cnt; i++)
{
if (merge_info_p->ls_outer_inner_list[i] == QFILE_OUTER_LIST)
{
if (tuple_rec1_p)
{
QFILE_GET_TUPLE_VALUE_HEADER_POSITION (tuple_rec1_p->tpl, merge_info_p->ls_pos_list[i], src_p);
}
else
{
src_p = (char *) ls_unbound;
}
}
else
{
if (tuple_rec2_p)
{
QFILE_GET_TUPLE_VALUE_HEADER_POSITION (tuple_rec2_p->tpl, merge_info_p->ls_pos_list[i], src_p);
}
else
{
src_p = (char *) ls_unbound;
}
}
tuple_value_size = QFILE_TUPLE_VALUE_HEADER_SIZE + QFILE_GET_TUPLE_VALUE_LENGTH (src_p);
memcpy (tuple_p, src_p, tuple_value_size);
tuple_p += tuple_value_size;
*tuple_length_p += tuple_value_size;
}
QFILE_PUT_TUPLE_LENGTH (page_p, *tuple_length_p);
return NO_ERROR;
}
int
qfile_save_tuple (QFILE_TUPLE_DESCRIPTOR * tuple_descr_p, QFILE_TUPLE_TYPE tuple_type, char *page_p,
int *tuple_length_p)
{
char *tuple_p;
tuple_p = (char *) page_p + QFILE_TUPLE_LENGTH_SIZE;
switch (tuple_type)
{
case T_SINGLE_BOUND_ITEM:
(void) qfile_save_single_bound_item_tuple (tuple_descr_p, tuple_p, page_p, *tuple_length_p);
break;
case T_NORMAL:
if (qfile_save_normal_tuple (tuple_descr_p, tuple_p, page_p, *tuple_length_p) != NO_ERROR)
{
return ER_FAILED;
}
break;
case T_SORTKEY:
(void) qfile_save_sort_key_tuple (tuple_descr_p, tuple_p, page_p, *tuple_length_p);
break;
case T_MERGE:
(void) qfile_save_merge_tuple (tuple_descr_p, tuple_p, page_p, tuple_length_p);
break;
default:
return ER_FAILED;
}
return NO_ERROR;
}
/*
* qfile_generate_tuple_into_list () -
* return: int (NO_ERROR or ER_FAILED)
* list_id(in/out): List File Identifier
* tpl_type(in): tuple descriptor type
* - single bound field tuple or multi field tuple
*
*/
int
qfile_generate_tuple_into_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, QFILE_TUPLE_TYPE tuple_type)
{
QFILE_TUPLE_DESCRIPTOR *tuple_descr_p;
PAGE_PTR cur_page_p;
int tuple_length;
char *page_p;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
cur_page_p = list_id_p->last_pgptr;
tuple_descr_p = &(list_id_p->tpl_descr);
tuple_length = tuple_descr_p->tpl_size;
assert (tuple_length <= qfile_Max_tuple_page_size);
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
if (tuple_type == T_NORMAL)
{
if (qfile_save_normal_tuple (tuple_descr_p, page_p + QFILE_TUPLE_LENGTH_SIZE, page_p, tuple_length) != NO_ERROR)
{
return ER_FAILED;
}
}
else if (qfile_save_tuple (tuple_descr_p, tuple_type, page_p, &tuple_length) != NO_ERROR)
{
return ER_FAILED;
}
assert ((page_p + tuple_length - cur_page_p) <= DB_PAGESIZE);
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_length);
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_fast_intint_tuple_to_list () - generate a two integer value tuple into a listfile
* return: int (NO_ERROR or ER_FAILED)
* list_id(in/out): List File Identifier
* v1(in): first int value
* v2(in): second int value
*
* NOTE: This function is meant to skip usual validation od DB_VALUES and
* disk size computation in order to generate the tuple as fast as possible.
* Also, it must write tuples identical to tuples generated by
* qfile_generate_tuple_into_list via the built tuple descriptor. Generated
* tuples must be readable and scanable via usual qfile routines.
*/
int
qfile_fast_intint_tuple_to_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, int v1, int v2)
{
PAGE_PTR cur_page_p;
int tuple_length, tuple_value_length, tuple_value_size;
char *page_p, *tuple_p;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
/* compute sizes */
tuple_value_size = DB_ALIGN (tp_Integer.disksize, MAX_ALIGNMENT);
tuple_value_length = QFILE_TUPLE_VALUE_HEADER_SIZE + tuple_value_size;
tuple_length = QFILE_TUPLE_LENGTH_SIZE + tuple_value_length * 2;
/* fetch page or alloc if necessary */
cur_page_p = list_id_p->last_pgptr;
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_p = page_p + QFILE_TUPLE_LENGTH_SIZE;
/* write the two not-null integers */
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, tuple_value_size);
OR_PUT_INT (tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, v1);
tuple_p += tuple_value_length;
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, tuple_value_size);
OR_PUT_INT (tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, v2);
/* list_id maintainance stuff */
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_length);
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_fast_intval_tuple_to_list () - generate a two value tuple into a file
* return: int (NO_ERROR, error code or positive overflow tuple size)
* list_id(in/out): List File Identifier
* v1(in): integer value
* v2(in): generic value
*
* NOTE: This function is meant to partially skip usual validation of DB_VALUES
* and disk size computation in order to generate the tuple as fast as
* possible. Also, it must write tuples identical to tuples generated by
* qfile_generate_tuple_into_list via the built tuple descriptor. Generated
* tuples must be readable and scanable via usual qfile routines.
*/
int
qfile_fast_intval_tuple_to_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, int v1, DB_VALUE * v2)
{
PAGE_PTR cur_page_p;
int tuple_length, tuple_int_value_size, tuple_int_value_length;
int tuple_value_size, tuple_value_length;
char *page_p, *tuple_p;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
/* compute sizes */
tuple_int_value_size = DB_ALIGN (tp_Integer.disksize, MAX_ALIGNMENT);
tuple_int_value_length = QFILE_TUPLE_VALUE_HEADER_SIZE + tuple_int_value_size;
tuple_value_size = DB_ALIGN (pr_data_writeval_disk_size (v2), MAX_ALIGNMENT);
tuple_value_length = QFILE_TUPLE_VALUE_HEADER_SIZE + tuple_value_size;
tuple_length = QFILE_TUPLE_LENGTH_SIZE + tuple_int_value_length + tuple_value_length;
/* register tuple size and see if we can write it or not */
list_id_p->tpl_descr.tpl_size = tuple_length;
if (tuple_length > QFILE_MAX_TUPLE_SIZE_IN_PAGE)
{
/* can't write it here */
return tuple_length;
}
/* fetch page or alloc if necessary */
cur_page_p = list_id_p->last_pgptr;
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_p = page_p + QFILE_TUPLE_LENGTH_SIZE;
/* write the two not-null integers */
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, tuple_int_value_size);
OR_PUT_INT (tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, v1);
tuple_p += tuple_int_value_length;
if (DB_IS_NULL (v2))
{
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_UNBOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, 0);
}
else
{
DB_TYPE dbval_type = DB_VALUE_DOMAIN_TYPE (v2);
const PR_TYPE *pr_type = pr_type_from_id (dbval_type);
OR_BUF buf;
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, tuple_value_size);
or_init (&buf, tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, tuple_value_size);
if (pr_type == NULL || pr_type->data_writeval (&buf, v2) != NO_ERROR)
{
return ER_FAILED;
}
}
/* list_id maintainance stuff */
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_length);
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_fast_val_tuple_to_list () - generate a one value tuple into a file
* return: int (NO_ERROR, error code or positive overflow tuple size)
* list_id(in/out): List File Identifier
* val(in): integer value
*
* NOTE: This function is meant to partially skip usual validation of DB_VALUES
* and disk size computation in order to generate the tuple as fast as
* possible. Also, it must write tuples identical to tuples generated by
* qfile_generate_tuple_into_list via the built tuple descriptor. Generated
* tuples must be readable and scanable via usual qfile routines.
*/
int
qfile_fast_val_tuple_to_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, DB_VALUE * val)
{
PAGE_PTR cur_page_p;
int tuple_length;
int tuple_value_size, tuple_value_length;
char *page_p, *tuple_p;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
tuple_value_size = DB_ALIGN (pr_data_writeval_disk_size (val), MAX_ALIGNMENT);
tuple_value_length = QFILE_TUPLE_VALUE_HEADER_SIZE + tuple_value_size;
tuple_length = QFILE_TUPLE_LENGTH_SIZE + tuple_value_length;
/* register tuple size and see if we can write it or not */
list_id_p->tpl_descr.tpl_size = tuple_length;
if (tuple_length > QFILE_MAX_TUPLE_SIZE_IN_PAGE)
{
/* can't write it here */
return tuple_length;
}
/* fetch page or alloc if necessary */
cur_page_p = list_id_p->last_pgptr;
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_p = page_p + QFILE_TUPLE_LENGTH_SIZE;
/* write the two not-null integers */
QFILE_PUT_TUPLE_LENGTH (page_p, tuple_length);
if (DB_IS_NULL (val))
{
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_UNBOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, 0);
}
else
{
DB_TYPE dbval_type = DB_VALUE_DOMAIN_TYPE (val);
const PR_TYPE *pr_type = pr_type_from_id (dbval_type);
OR_BUF buf;
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, tuple_value_size);
or_init (&buf, tuple_p + QFILE_TUPLE_VALUE_HEADER_SIZE, tuple_value_size);
if (pr_type == NULL || pr_type->data_writeval (&buf, val) != NO_ERROR)
{
return ER_FAILED;
}
}
/* list_id maintainance stuff */
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_length);
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_add_overflow_tuple_to_list () -
* return: int (NO_ERROR or ER_FAILED)
* list_id(in/out): List File Identifier
* ovfl_tpl_pg(in): First page of the overflow tuple to be added
* input_list_id(in):
*
* Note: The indicated overflow tuple is added to the end of the list
* file. The given page contains the initial portion of the
* tuple and the rest of the tuple is formed from following
* overflow pages.
*
* Note: This routine is a specific routine of qfile_add_tuple_to_list used by list file
* sorting mechanism.
*/
int
qfile_add_overflow_tuple_to_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR ovf_tuple_page_p,
QFILE_LIST_ID * input_list_id_p)
{
PAGE_PTR cur_page_p, new_page_p = NULL, prev_page_p, ovf_page_p;
int tuple_length;
char *page_p;
int offset, tuple_page_size;
QFILE_TUPLE tuple;
VPID ovf_vpid;
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
tuple = (char *) ovf_tuple_page_p + QFILE_PAGE_HEADER_SIZE;
cur_page_p = list_id_p->last_pgptr;
tuple_length = QFILE_GET_TUPLE_LENGTH (tuple);
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, true) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_page_size = MIN (tuple_length, qfile_Max_tuple_page_size);
memcpy (page_p, tuple, tuple_page_size);
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_page_size);
prev_page_p = cur_page_p;
QFILE_GET_OVERFLOW_VPID (&ovf_vpid, ovf_tuple_page_p);
for (offset = tuple_page_size; offset < tuple_length; offset += tuple_page_size)
{
ovf_page_p = qmgr_get_old_page (thread_p, &ovf_vpid, input_list_id_p->tfile_vfid);
if (ovf_page_p == NULL)
{
if (new_page_p)
{
qfile_set_dirty_page (thread_p, new_page_p, FREE, list_id_p->tfile_vfid);
}
return ER_FAILED;
}
QFILE_GET_OVERFLOW_VPID (&ovf_vpid, ovf_page_p);
new_page_p =
qfile_allocate_new_ovf_page (thread_p, list_id_p, cur_page_p, prev_page_p, tuple_length, offset,
&tuple_page_size);
if (new_page_p == NULL)
{
if (prev_page_p != cur_page_p)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
qmgr_free_old_page_and_init (thread_p, ovf_page_p, input_list_id_p->tfile_vfid);
return ER_FAILED;
}
memcpy ((char *) new_page_p + QFILE_PAGE_HEADER_SIZE, (char *) ovf_page_p + QFILE_PAGE_HEADER_SIZE,
tuple_page_size);
qmgr_free_old_page_and_init (thread_p, ovf_page_p, input_list_id_p->tfile_vfid);
prev_page_p = new_page_p;
}
if (prev_page_p != cur_page_p)
{
QFILE_PUT_OVERFLOW_VPID_NULL (prev_page_p);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_get_first_page () -
* return: int (NO_ERROR or ER_FAILED)
* list_id(in): List File Identifier
*/
int
qfile_get_first_page (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p)
{
PAGE_PTR new_page_p;
VPID new_vpid;
if (list_id_p->tfile_vfid == NULL)
{
list_id_p->tfile_vfid = qmgr_create_new_temp_file (thread_p, list_id_p->query_id, TEMP_FILE_MEMBUF_NORMAL);
if (list_id_p->tfile_vfid == NULL)
{
return ER_FAILED;
}
}
new_page_p = qmgr_get_new_page (thread_p, &new_vpid, list_id_p->tfile_vfid);
if (new_page_p == NULL)
{
return ER_FAILED;
}
list_id_p->page_cnt++;
QFILE_PUT_TUPLE_COUNT (new_page_p, 0);
QFILE_PUT_PREV_VPID (new_page_p, &list_id_p->last_vpid);
LS_PUT_NEXT_VPID (new_page_p);
QFILE_COPY_VPID (&list_id_p->first_vpid, &new_vpid);
QFILE_COPY_VPID (&list_id_p->last_vpid, &new_vpid);
list_id_p->last_pgptr = new_page_p;
list_id_p->last_offset = QFILE_PAGE_HEADER_SIZE;
QFILE_PUT_OVERFLOW_VPID_NULL (new_page_p);
list_id_p->lasttpl_len = 0;
list_id_p->last_offset += ((0 + list_id_p->last_offset > DB_PAGESIZE) ? DB_PAGESIZE : 0);
qfile_set_dirty_page (thread_p, new_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_destroy_list () -
* return: int
* list_id(in): List File Identifier
*
* Note: All the pages of the list file are deallocated from the query
* file, the memory areas for the list file identifier are freed
* and the number of pages deallocated is returned. This routine
* is basically called for temporarily created list files.
*/
void
qfile_destroy_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p)
{
if (list_id_p)
{
if (list_id_p->tfile_vfid)
{
qmgr_free_list_temp_file (thread_p, list_id_p->query_id, list_id_p->tfile_vfid);
}
else
{
/* because qmgr_free_list_temp_file() destroy only FILE_TEMP file */
if (!VFID_ISNULL (&list_id_p->temp_vfid))
{
file_temp_retire (thread_p, &list_id_p->temp_vfid);
}
}
if (list_id_p->dependent_list_id != NULL)
{
qfile_destroy_list (thread_p, list_id_p->dependent_list_id);
QFILE_FREE_AND_INIT_LIST_ID (list_id_p->dependent_list_id);
}
qfile_clear_list_id (list_id_p);
}
}
/*
* xqfile_get_list_file_page () -
* return: NO_ERROR or ER_ code
* query_id(in):
* volid(in): List file page volume identifier
* pageid(in): List file page identifier
* page_bufp(out): Buffer to contain list file page content
* page_sizep(out):
*
* Note: This routine is basically called by the C/S communication
* routines to fetch and copy the indicated list file page to
* the buffer area. The area pointed by the buffer must have
* been allocated by the caller and should be big enough to
* store a list file page.
*/
int
xqfile_get_list_file_page (THREAD_ENTRY * thread_p, QUERY_ID query_id, VOLID vol_id, PAGEID page_id, char *page_buf_p,
int *page_size_p)
{
QMGR_QUERY_ENTRY *query_entry_p = NULL;
QFILE_LIST_ID *list_id_p;
QMGR_TEMP_FILE *tfile_vfid_p;
VPID vpid, next_vpid;
PAGE_PTR page_p;
int one_page_size = DB_PAGESIZE;
int tran_index;
assert (NULL_PAGEID < page_id);
VPID_SET (&vpid, vol_id, page_id);
*page_size_p = 0;
if (query_id == NULL_QUERY_ID)
{
assert (false);
return ER_QPROC_UNKNOWN_QUERYID;
}
else if (query_id >= SHRT_MAX)
{
tfile_vfid_p = (QMGR_TEMP_FILE *) query_id;
goto get_page;
}
else
{
tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
query_entry_p = qmgr_get_query_entry (thread_p, query_id, tran_index);
if (query_entry_p == NULL)
{
return ER_QPROC_UNKNOWN_QUERYID;
}
assert (query_entry_p->errid == NO_ERROR);
assert (query_entry_p->query_status == QUERY_COMPLETED);
if (query_entry_p->list_id == NULL)
{
assert (query_entry_p->list_id != NULL);
*page_size_p = 0;
return NO_ERROR;
}
assert (NULL_PAGEID < query_entry_p->list_id->first_vpid.pageid);
/* unexpected no result */
if (vol_id == NULL_VOLID && page_id == NULL_PAGEID)
{
assert (vol_id != NULL_VOLID || page_id != NULL_PAGEID);
*page_size_p = 0;
return NO_ERROR;
}
list_id_p = query_entry_p->list_id;
tfile_vfid_p = list_id_p->tfile_vfid;
}
get_page:
/* append pages until a network page is full */
while ((*page_size_p + DB_PAGESIZE) <= IO_MAX_PAGE_SIZE)
{
page_p = qmgr_get_old_page (thread_p, &vpid, tfile_vfid_p);
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
/* find next page to append */
QFILE_GET_OVERFLOW_VPID (&next_vpid, page_p);
if (next_vpid.pageid == NULL_PAGEID)
{
QFILE_GET_NEXT_VPID (&next_vpid, page_p);
}
assert (next_vpid.pageid != NULL_PAGEID_IN_PROGRESS);
if (QFILE_GET_TUPLE_COUNT (page_p) == QFILE_OVERFLOW_TUPLE_COUNT_FLAG
|| QFILE_GET_OVERFLOW_PAGE_ID (page_p) != NULL_PAGEID)
{
one_page_size = DB_PAGESIZE;
}
else
{
one_page_size = (QFILE_GET_LAST_TUPLE_OFFSET (page_p)
+ QFILE_GET_TUPLE_LENGTH (page_p + QFILE_GET_LAST_TUPLE_OFFSET (page_p)));
if (one_page_size < QFILE_PAGE_HEADER_SIZE)
{
one_page_size = QFILE_PAGE_HEADER_SIZE;
}
if (one_page_size > DB_PAGESIZE)
{
one_page_size = DB_PAGESIZE;
}
}
memcpy ((page_buf_p + *page_size_p), page_p, one_page_size);
qmgr_free_old_page_and_init (thread_p, page_p, tfile_vfid_p);
*page_size_p += DB_PAGESIZE;
/* next page to append does not exists, stop appending */
if (next_vpid.pageid == NULL_PAGEID)
{
break;
}
vpid = next_vpid;
}
*page_size_p += one_page_size - DB_PAGESIZE;
return NO_ERROR;
}
/*
* qfile_add_item_to_list () -
* return: int (NO_ERROR or ER_FAILED)
* item(in): Value in disk representation form
* item_size(in): Size of the value
* list_id(in): List File Identifier
*
* Note: The given item is added to the end of the given list file.
* The list file must be of a single column.
*/
int
qfile_add_item_to_list (THREAD_ENTRY * thread_p, char *item_p, int item_size, QFILE_LIST_ID * list_id_p)
{
QFILE_TUPLE tuple;
int tuple_length, align;
char *tuple_p;
tuple_length = QFILE_TUPLE_LENGTH_SIZE + QFILE_TUPLE_VALUE_HEADER_SIZE + item_size;
align = DB_ALIGN (item_size, MAX_ALIGNMENT) - item_size;
tuple_length += align;
if (tuple_length < QFILE_MAX_TUPLE_SIZE_IN_PAGE)
{
/* SMALL_TUPLE */
list_id_p->tpl_descr.item = item_p;
list_id_p->tpl_descr.item_size = item_size;
list_id_p->tpl_descr.tpl_size = tuple_length;
if (qfile_generate_tuple_into_list (thread_p, list_id_p, T_SINGLE_BOUND_ITEM) != NO_ERROR)
{
return ER_FAILED;
}
}
else
{
/* BIG_TUPLE */
tuple = (QFILE_TUPLE) malloc (tuple_length);
if (tuple == NULL)
{
return ER_FAILED;
}
QFILE_PUT_TUPLE_LENGTH (tuple, tuple_length);
tuple_p = (char *) tuple + QFILE_TUPLE_LENGTH_SIZE;
QFILE_PUT_TUPLE_VALUE_FLAG (tuple_p, V_BOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (tuple_p, item_size + align);
tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE;
memcpy (tuple_p, item_p, item_size);
#if !defined(NDEBUG)
/* suppress valgrind UMW error */
memset (tuple_p + item_size, 0, align);
#endif
if (qfile_add_tuple_to_list (thread_p, list_id_p, tuple) != NO_ERROR)
{
free_and_init (tuple);
return ER_FAILED;
}
free_and_init (tuple);
}
return NO_ERROR;
}
/*
* qfile_compare_tuple_helper () -
* return:
* lhs(in):
* rhs(in):
* types(in):
*/
static int
qfile_compare_tuple_helper (QFILE_TUPLE lhs, QFILE_TUPLE rhs, QFILE_TUPLE_VALUE_TYPE_LIST * types, int *cmp)
{
char *lhs_tuple_p, *rhs_tuple_p;
int i, result;
lhs_tuple_p = (char *) lhs + QFILE_TUPLE_LENGTH_SIZE;
rhs_tuple_p = (char *) rhs + QFILE_TUPLE_LENGTH_SIZE;
for (i = 0; i < types->type_cnt; i++)
{
result = qfile_compare_tuple_values (lhs_tuple_p, rhs_tuple_p, types->domp[i], cmp);
if (result != NO_ERROR)
{
return result;
}
if (*cmp != 0)
{
return NO_ERROR;
}
lhs_tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE + QFILE_GET_TUPLE_VALUE_LENGTH (lhs_tuple_p);
rhs_tuple_p += QFILE_TUPLE_VALUE_HEADER_SIZE + QFILE_GET_TUPLE_VALUE_LENGTH (rhs_tuple_p);
}
*cmp = 0;
return NO_ERROR;
}
/*
* qfile_advance_single () -
* return:
* next_scan(in):
* next_tpl(in):
* last_scan(in):
* last_tpl(in):
* types(in):
*/
static SCAN_CODE
qfile_advance_single (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * next_scan_p, QFILE_TUPLE_RECORD * next_tuple_p,
QFILE_LIST_SCAN_ID * last_scan_p, QFILE_TUPLE_RECORD * last_tuple_p,
QFILE_TUPLE_VALUE_TYPE_LIST * types)
{
if (next_scan_p == NULL)
{
return S_END;
}
return qfile_scan_list_next (thread_p, next_scan_p, next_tuple_p, PEEK);
}
/*
* qfile_advance_group () -
* return:
* next_scan(in/out):
* next_tpl(out):
* last_scan(in/out):
* last_tpl(out):
* types(in):
*/
static SCAN_CODE
qfile_advance_group (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * next_scan_p, QFILE_TUPLE_RECORD * next_tuple_p,
QFILE_LIST_SCAN_ID * last_scan_p, QFILE_TUPLE_RECORD * last_tuple_p,
QFILE_TUPLE_VALUE_TYPE_LIST * types)
{
SCAN_CODE status;
int error_code, cmp;
if (next_scan_p == NULL)
{
return S_END;
}
status = S_SUCCESS;
switch (last_scan_p->position)
{
case S_BEFORE:
status = qfile_scan_list_next (thread_p, next_scan_p, next_tuple_p, PEEK);
break;
case S_ON:
do
{
status = qfile_scan_list_next (thread_p, next_scan_p, next_tuple_p, PEEK);
if (status != S_SUCCESS)
{
break;
}
error_code = qfile_compare_tuple_helper (last_tuple_p->tpl, next_tuple_p->tpl, types, &cmp);
}
while (error_code == NO_ERROR && cmp == 0);
break;
case S_AFTER:
default:
status = S_END;
break;
}
if (status == S_SUCCESS)
{
QFILE_TUPLE_POSITION next_pos;
qfile_save_current_scan_tuple_position (next_scan_p, &next_pos);
status = qfile_jump_scan_tuple_position (thread_p, last_scan_p, &next_pos, last_tuple_p, PEEK);
}
return status;
}
/*
* qfile_add_one_tuple () -
* return:
* dst(in/out):
* lhs(in):
* rhs(in):
*/
static int
qfile_add_one_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * dest_list_p, QFILE_TUPLE lhs, QFILE_TUPLE rhs)
{
return qfile_add_tuple_to_list (thread_p, dest_list_p, lhs);
}
/*
* qfile_add_two_tuple () -
* return:
* dst(in):
* lhs(in):
* rhs(in):
*/
static int
qfile_add_two_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * dest_list_p, QFILE_TUPLE lhs, QFILE_TUPLE rhs)
{
int error;
error = qfile_add_tuple_to_list (thread_p, dest_list_p, lhs);
if (error == NO_ERROR)
{
error = qfile_add_tuple_to_list (thread_p, dest_list_p, rhs);
}
return error;
}
static int
qfile_advance (THREAD_ENTRY * thread_p, ADVANCE_FUCTION advance_func, QFILE_TUPLE_RECORD * side_p,
QFILE_TUPLE_RECORD * last_side_p, QFILE_LIST_SCAN_ID * scan_p, QFILE_LIST_SCAN_ID * last_scan_p,
QFILE_LIST_ID * side_file_p, int *have_side_p)
{
SCAN_CODE scan_result;
scan_result = (*advance_func) (thread_p, scan_p, side_p, last_scan_p, last_side_p, &side_file_p->type_list);
switch (scan_result)
{
case S_SUCCESS:
*have_side_p = 1;
break;
case S_END:
*have_side_p = 0;
break;
default:
return ER_FAILED;
}
return NO_ERROR;
}
/*
* qfile_combine_two_list () -
* return: QFILE_LIST_ID *, or NULL
* lhs_file(in): pointer to a QFILE_LIST_ID for one of the input files
* rhs_file(in): pointer to a QFILE_LIST_ID for the other input file, or NULL
* flag(in): {QFILE_FLAG_UNION, QFILE_FLAG_DIFFERENCE, QFILE_FLAG_INTERSECT,
* QFILE_FLAG_ALL, QFILE_FLAG_DISTINCT}
* the kind of combination desired (union, diff, or intersect) and
* whether to do 'all' or 'distinct'
*
*/
QFILE_LIST_ID *
qfile_combine_two_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * lhs_file_p, QFILE_LIST_ID * rhs_file_p, int flag)
{
QFILE_LIST_ID *dest_list_id_p = NULL;
QFILE_LIST_SCAN_ID lhs_scan_id, rhs_scan_id, last_lhs_scan_id, last_rhs_scan_id;
QFILE_LIST_SCAN_ID *lhs_scan_p = NULL, *rhs_scan_p = NULL;
QFILE_LIST_SCAN_ID *last_lhs_scan_p = NULL, *last_rhs_scan_p = NULL;
int have_lhs = 0, have_rhs = 0, cmp;
QFILE_TUPLE_RECORD lhs = { NULL, 0 };
QFILE_TUPLE_RECORD rhs = { NULL, 0 };
QFILE_TUPLE_RECORD last_lhs = { NULL, 0 };
QFILE_TUPLE_RECORD last_rhs = { NULL, 0 };
QUERY_OPTIONS distinct_or_all;
ADVANCE_FUCTION advance_func;
int (*act_left_func) (THREAD_ENTRY * thread_p, QFILE_LIST_ID *, QFILE_TUPLE);
int (*act_right_func) (THREAD_ENTRY * thread_p, QFILE_LIST_ID *, QFILE_TUPLE);
int (*act_both_func) (THREAD_ENTRY * thread_p, QFILE_LIST_ID *, QFILE_TUPLE, QFILE_TUPLE);
advance_func = NULL;
act_left_func = NULL;
act_right_func = NULL;
act_both_func = NULL;
/* result cached list_id cannot be used to append the union of results */
if (!lhs_file_p->is_result_cached && !rhs_file_p->is_result_cached)
{
if (QFILE_IS_FLAG_SET_BOTH (flag, QFILE_FLAG_UNION, QFILE_FLAG_ALL))
{
return qfile_union_list (thread_p, lhs_file_p, rhs_file_p, flag);
}
}
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_DISTINCT))
{
distinct_or_all = Q_DISTINCT;
}
else
{
distinct_or_all = Q_ALL;
}
/* for using result-cache */
if (!QFILE_IS_FLAG_SET_BOTH (flag, QFILE_FLAG_UNION, QFILE_FLAG_ALL) && lhs_file_p->tuple_cnt > 1)
{
lhs_file_p = qfile_sort_list (thread_p, lhs_file_p, NULL, distinct_or_all, true);
if (lhs_file_p == NULL)
{
goto error;
}
}
if (qfile_open_list_scan (lhs_file_p, &lhs_scan_id) != NO_ERROR)
{
goto error;
}
lhs_scan_p = &lhs_scan_id;
if (rhs_file_p)
{
/* for using result-cache */
if (!QFILE_IS_FLAG_SET_BOTH (flag, QFILE_FLAG_UNION, QFILE_FLAG_ALL) && rhs_file_p->tuple_cnt > 1)
{
rhs_file_p = qfile_sort_list (thread_p, rhs_file_p, NULL, distinct_or_all, true);
if (rhs_file_p == NULL)
{
goto error;
}
}
if (qfile_open_list_scan (rhs_file_p, &rhs_scan_id) != NO_ERROR)
{
goto error;
}
rhs_scan_p = &rhs_scan_id;
}
dest_list_id_p = qfile_open_list (thread_p, &lhs_file_p->type_list, NULL, lhs_file_p->query_id, flag, NULL);
if (dest_list_id_p == NULL)
{
goto error;
}
if (rhs_file_p && qfile_unify_types (dest_list_id_p, rhs_file_p) != NO_ERROR)
{
goto error;
}
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_INTERSECT))
{
act_left_func = NULL;
act_right_func = NULL;
act_both_func = qfile_add_one_tuple;
}
else if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_DIFFERENCE))
{
act_left_func = qfile_add_tuple_to_list;
act_right_func = NULL;
act_both_func = NULL;
}
else
{
/* QFILE_FLAG_UNION */
act_left_func = qfile_add_tuple_to_list;
act_right_func = qfile_add_tuple_to_list;
act_both_func = qfile_add_one_tuple;
}
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_DISTINCT))
{
advance_func = qfile_advance_group;
if (qfile_open_list_scan (lhs_file_p, &last_lhs_scan_id) != NO_ERROR)
{
goto error;
}
last_lhs_scan_p = &last_lhs_scan_id;
if (rhs_file_p)
{
if (qfile_open_list_scan (rhs_file_p, &last_rhs_scan_id) != NO_ERROR)
{
goto error;
}
last_rhs_scan_p = &last_rhs_scan_id;
}
}
else
{
/* QFILE_FLAG_ALL */
advance_func = qfile_advance_single;
if (QFILE_IS_FLAG_SET (flag, QFILE_FLAG_UNION))
{
act_both_func = qfile_add_two_tuple;
}
}
while (1)
{
if (!have_lhs)
{
if (qfile_advance (thread_p, advance_func, &lhs, &last_lhs, lhs_scan_p, last_lhs_scan_p, lhs_file_p,
&have_lhs) != NO_ERROR)
{
goto error;
}
}
if (!have_rhs)
{
if (qfile_advance (thread_p, advance_func, &rhs, &last_rhs, rhs_scan_p, last_rhs_scan_p, rhs_file_p,
&have_rhs) != NO_ERROR)
{
goto error;
}
}
if (!have_lhs || !have_rhs)
{
break;
}
if (qfile_compare_tuple_helper (lhs.tpl, rhs.tpl, &lhs_file_p->type_list, &cmp) != NO_ERROR)
{
goto error;
}
if (cmp < 0)
{
if (act_left_func && act_left_func (thread_p, dest_list_id_p, lhs.tpl) != NO_ERROR)
{
goto error;
}
have_lhs = 0;
}
else if (cmp == 0)
{
if (act_both_func && act_both_func (thread_p, dest_list_id_p, lhs.tpl, rhs.tpl) != NO_ERROR)
{
goto error;
}
have_lhs = 0;
have_rhs = 0;
}
else
{
if (act_right_func && act_right_func (thread_p, dest_list_id_p, rhs.tpl) != NO_ERROR)
{
goto error;
}
have_rhs = 0;
}
}
while (have_lhs)
{
if (act_left_func && act_left_func (thread_p, dest_list_id_p, lhs.tpl) != NO_ERROR)
{
goto error;
}
if (qfile_advance (thread_p, advance_func, &lhs, &last_lhs, lhs_scan_p, last_lhs_scan_p, lhs_file_p, &have_lhs) !=
NO_ERROR)
{
goto error;
}
}
while (have_rhs)
{
if (act_right_func && act_right_func (thread_p, dest_list_id_p, rhs.tpl) != NO_ERROR)
{
goto error;
}
if (qfile_advance (thread_p, advance_func, &rhs, &last_rhs, rhs_scan_p, last_rhs_scan_p, rhs_file_p, &have_rhs) !=
NO_ERROR)
{
goto error;
}
}
success:
if (lhs_scan_p)
{
qfile_close_scan (thread_p, lhs_scan_p);
}
if (rhs_scan_p)
{
qfile_close_scan (thread_p, rhs_scan_p);
}
if (last_lhs_scan_p)
{
qfile_close_scan (thread_p, last_lhs_scan_p);
}
if (last_rhs_scan_p)
{
qfile_close_scan (thread_p, last_rhs_scan_p);
}
if (lhs_file_p)
{
qfile_close_list (thread_p, lhs_file_p);
}
if (rhs_file_p)
{
qfile_close_list (thread_p, rhs_file_p);
}
if (dest_list_id_p)
{
qfile_close_list (thread_p, dest_list_id_p);
}
return dest_list_id_p;
error:
if (dest_list_id_p)
{
qfile_close_list (thread_p, dest_list_id_p);
QFILE_FREE_AND_INIT_LIST_ID (dest_list_id_p);
}
goto success;
}
int
qfile_append_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * base_list_id, QFILE_LIST_ID * append_list_id)
{
VPID old_vpid = VPID_INITIALIZER, new_vpid = VPID_INITIALIZER, prev_vpid = VPID_INITIALIZER;
VPID old_overflow_vpid = VPID_INITIALIZER, new_overflow_vpid = VPID_INITIALIZER;
PAGE_PTR old_page = NULL, new_page = NULL, prev_page = NULL;
PAGE_PTR old_overflow_page = NULL, new_overflow_page = NULL, prev_overflow_page = NULL;
assert (thread_p != NULL);
assert (base_list_id != NULL);
assert (append_list_id != NULL);
assert (base_list_id->last_pgptr == NULL);
assert (append_list_id->last_pgptr == NULL);
assert (append_list_id->tuple_cnt > 0);
assert (!VPID_ISNULL (&append_list_id->first_vpid));
if (!VPID_ISNULL (&base_list_id->last_vpid))
{
prev_vpid = base_list_id->last_vpid;
prev_page = qmgr_get_old_page (thread_p, &prev_vpid, base_list_id->tfile_vfid);
if (prev_page == NULL)
{
goto error_exit;
}
}
old_vpid = append_list_id->first_vpid;
while (!VPID_ISNULL (&old_vpid))
{
/* last_vpid can be NULL if the tuple_cnt of base_list_id is 0 */
assert (VPID_ISNULL (&base_list_id->last_vpid) || !VPID_ISNULL (&prev_vpid));
assert (VPID_ISNULL (&base_list_id->last_vpid) || prev_page != NULL);
old_page = qmgr_get_old_page (thread_p, &old_vpid, append_list_id->tfile_vfid);
if (old_page == NULL)
{
goto error_exit;
}
new_page = qmgr_get_new_page (thread_p, &new_vpid, base_list_id->tfile_vfid);
if (new_page == NULL)
{
goto error_exit;
}
if (prev_page != NULL)
{
QFILE_PUT_NEXT_VPID (prev_page, &new_vpid);
qfile_set_dirty_page (thread_p, prev_page, FREE, base_list_id->tfile_vfid);
}
else
{
QFILE_COPY_VPID (&base_list_id->first_vpid, &new_vpid);
}
QFILE_COPY_VPID (&base_list_id->last_vpid, &new_vpid);
prev_page = new_page;
assert (prev_overflow_page == NULL);
memcpy (new_page, old_page, DB_PAGESIZE);
if (!VPID_ISNULL (&prev_vpid))
{
QFILE_PUT_PREV_VPID (new_page, &prev_vpid);
}
QFILE_COPY_VPID (&prev_vpid, &new_vpid);
/* overflow page */
QFILE_GET_OVERFLOW_VPID (&old_overflow_vpid, old_page);
while (!VPID_ISNULL (&old_overflow_vpid))
{
if (prev_overflow_page == NULL)
{
/* prev_page = new_page = prev_overflow_page */
prev_overflow_page = new_page;
}
old_overflow_page = qmgr_get_old_page (thread_p, &old_overflow_vpid, append_list_id->tfile_vfid);
if (old_overflow_page == NULL)
{
goto error_exit;
}
new_overflow_page = qmgr_get_new_page (thread_p, &new_overflow_vpid, base_list_id->tfile_vfid);
if (new_overflow_page == NULL)
{
goto error_exit;
}
QFILE_PUT_OVERFLOW_VPID (prev_overflow_page, &new_overflow_vpid);
if (prev_page != prev_overflow_page)
{
qfile_set_dirty_page (thread_p, prev_overflow_page, FREE, base_list_id->tfile_vfid);
}
prev_overflow_page = new_overflow_page;
memcpy (new_overflow_page, old_overflow_page, DB_PAGESIZE);
/* next overflow page */
QFILE_GET_OVERFLOW_VPID (&old_overflow_vpid, old_overflow_page);
qmgr_free_old_page_and_init (thread_p, old_overflow_page, append_list_id->tfile_vfid);
}
if (new_overflow_page != NULL)
{
assert (prev_overflow_page == new_overflow_page);
prev_overflow_page = NULL;
QFILE_PUT_OVERFLOW_VPID_NULL (new_overflow_page);
qfile_set_dirty_page (thread_p, new_overflow_page, FREE, base_list_id->tfile_vfid);
new_overflow_page = NULL;
}
/* next page */
QFILE_GET_NEXT_VPID (&old_vpid, old_page);
qmgr_free_old_page_and_init (thread_p, old_page, append_list_id->tfile_vfid);
}
if (new_page != NULL)
{
qfile_set_dirty_page (thread_p, new_page, FREE, base_list_id->tfile_vfid);
}
base_list_id->tuple_cnt += append_list_id->tuple_cnt;
base_list_id->page_cnt += append_list_id->page_cnt;
base_list_id->last_offset = append_list_id->last_offset;
base_list_id->lasttpl_len = append_list_id->lasttpl_len;
ASSERT_NO_ERROR_OR_INTERRUPTED ();
return NO_ERROR;
error_exit:
if (prev_page != NULL)
{
new_page = (prev_page != new_page) ? new_page : NULL;
prev_overflow_page = (prev_overflow_page != prev_page) ? prev_overflow_page : NULL;
qmgr_free_old_page_and_init (thread_p, prev_page, base_list_id->tfile_vfid);
}
if (new_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, new_page, base_list_id->tfile_vfid);
}
if (prev_overflow_page != NULL)
{
new_overflow_page = (prev_overflow_page != new_overflow_page) ? new_overflow_page : NULL;
qmgr_free_old_page_and_init (thread_p, prev_overflow_page, base_list_id->tfile_vfid);
}
if (new_overflow_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, new_overflow_page, base_list_id->tfile_vfid);
}
if (old_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, old_page, append_list_id->tfile_vfid);
}
if (old_overflow_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, old_overflow_page, append_list_id->tfile_vfid);
}
assert_release_error (er_errid () != NO_ERROR);
return er_errid ();
}
int
qfile_connect_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * base_list_id, QFILE_LIST_ID * append_list_id)
{
PAGE_PTR base_last_page = NULL, append_first_page = NULL;
QFILE_LIST_ID *base_last;
assert (thread_p != NULL);
assert (base_list_id != NULL);
assert (append_list_id != NULL);
/* Check if qfile_close_list was called */
assert (base_list_id->last_pgptr == NULL);
assert (append_list_id->last_pgptr == NULL);
assert (base_list_id->tuple_cnt > 0);
assert (!VPID_ISNULL (&base_list_id->last_vpid));
assert (append_list_id->tuple_cnt > 0);
assert (!VPID_ISNULL (&append_list_id->first_vpid));
assert (append_list_id->tfile_vfid->membuf == NULL);
#if !defined (NDEBUG)
{
for (QFILE_LIST_ID * list_id = base_list_id->dependent_list_id; list_id != NULL;
list_id = list_id->dependent_list_id)
{
assert (list_id != append_list_id);
}
}
#endif /* !NDEBUG */
base_last_page = qmgr_get_old_page (thread_p, &base_list_id->last_vpid, base_list_id->tfile_vfid);
if (base_last_page == NULL)
{
goto error_exit;
}
append_first_page = qmgr_get_old_page (thread_p, &append_list_id->first_vpid, append_list_id->tfile_vfid);
if (append_first_page == NULL)
{
goto error_exit;
}
QFILE_PUT_NEXT_VPID (base_last_page, &append_list_id->first_vpid);
qfile_set_dirty_page (thread_p, base_last_page, FREE, base_list_id->tfile_vfid);
QFILE_PUT_PREV_VPID (append_first_page, &base_list_id->last_vpid);
qfile_set_dirty_page (thread_p, append_first_page, FREE, append_list_id->tfile_vfid);
base_list_id->last_vpid = append_list_id->last_vpid;
base_list_id->tuple_cnt += append_list_id->tuple_cnt;
base_list_id->page_cnt += append_list_id->page_cnt;
base_list_id->last_offset = append_list_id->last_offset;
base_list_id->lasttpl_len = append_list_id->lasttpl_len;
for (base_last = base_list_id; base_last->dependent_list_id != NULL; base_last = base_last->dependent_list_id)
{
;
}
base_last->dependent_list_id = append_list_id;
ASSERT_NO_ERROR_OR_INTERRUPTED ();
return NO_ERROR;
error_exit:
if (base_last_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, base_last_page, base_list_id->tfile_vfid);
}
if (append_first_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, append_first_page, append_list_id->tfile_vfid);
}
assert_release_error (er_errid () != NO_ERROR);
return er_errid ();
}
int
qfile_truncate_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id)
{
int error_code = NO_ERROR;
int i;
PAGE_PTR page_p;
QMGR_TEMP_FILE *tfile_vfid_p = list_id->tfile_vfid;
if (list_id->last_pgptr != NULL)
{
qfile_close_list (thread_p, list_id);
}
list_id->tuple_cnt = 0;
list_id->page_cnt = 0;
list_id->first_vpid.pageid = NULL_PAGEID;
list_id->first_vpid.volid = NULL_VOLID;
list_id->last_vpid.pageid = NULL_PAGEID;
list_id->last_vpid.volid = NULL_VOLID;
list_id->last_pgptr = NULL;
list_id->last_offset = QFILE_NULL_PAGE_OFFSET;
list_id->lasttpl_len = 0;
if (tfile_vfid_p != NULL)
{
switch (tfile_vfid_p->membuf_type)
{
case TEMP_FILE_MEMBUF_NONE:
break;
case TEMP_FILE_MEMBUF_KEY_BUFFER:
case TEMP_FILE_MEMBUF_NORMAL:
{
tfile_vfid_p->membuf_last = -1;
}
break;
default:
assert (false);
break;
}
error_code = file_temp_truncate (thread_p, &tfile_vfid_p->temp_vfid);
}
return error_code;
}
/*
* qfile_copy_tuple_descr_to_tuple () - generate a tuple into a tuple record
* structure from a tuple descriptor
* return: NO_ERROR or error code
* thread_p(in): thread
* tpl_descr(in): tuple descriptor
* tpl_rec(in): tuple record
*
* NOTE: tpl_rec's fields will be private_alloc'd by this function and it's the
* caller's resposability to properly dispose the memory
*/
int
qfile_copy_tuple_descr_to_tuple (THREAD_ENTRY * thread_p, QFILE_TUPLE_DESCRIPTOR * tpl_descr,
QFILE_TUPLE_RECORD * tplrec)
{
char *tuple_p;
int i, size;
assert (tpl_descr != NULL && tplrec != NULL);
/* alloc tuple record with tuple descriptor footprint */
tplrec->size = tpl_descr->tpl_size;
tplrec->tpl = (char *) db_private_alloc (thread_p, tplrec->size);
if (tplrec->tpl == NULL)
{
return ER_FAILED;
}
/* put length */
QFILE_PUT_TUPLE_LENGTH (tplrec->tpl, tplrec->size);
tuple_p = tplrec->tpl + QFILE_TUPLE_LENGTH_SIZE;
/* build tuple */
for (i = 0; i < tpl_descr->f_cnt; i++)
{
if (qdata_copy_db_value_to_tuple_value (tpl_descr->f_valp[i], tuple_p, &size) != NO_ERROR)
{
/* error has already been set */
db_private_free_and_init (thread_p, tplrec->tpl);
return ER_FAILED;
}
tuple_p += size;
assert (tuple_p <= tplrec->tpl + tplrec->size);
}
/* all ok */
return NO_ERROR;
}
static int
qfile_copy_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_ID * to_list_id_p, QFILE_LIST_ID * from_list_id_p)
{
QFILE_LIST_SCAN_ID scan_id;
QFILE_TUPLE_RECORD tuple_record = { NULL, 0 };
SCAN_CODE qp_scan;
/* scan through the first list file and add the tuples to the result list file. */
if (qfile_open_list_scan (from_list_id_p, &scan_id) != NO_ERROR)
{
return ER_FAILED;
}
while (true)
{
qp_scan = qfile_scan_list_next (thread_p, &scan_id, &tuple_record, PEEK);
if (qp_scan != S_SUCCESS)
{
break;
}
if (qfile_add_tuple_to_list (thread_p, to_list_id_p, tuple_record.tpl) != NO_ERROR)
{
qfile_close_scan (thread_p, &scan_id);
return ER_FAILED;
}
}
qfile_close_scan (thread_p, &scan_id);
if (qp_scan != S_END)
{
return ER_FAILED;
}
return NO_ERROR;
}
static void
qfile_close_and_free_list_file (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id)
{
qfile_close_list (thread_p, list_id);
QFILE_FREE_AND_INIT_LIST_ID (list_id);
}
/*
* qfile_union_list () -
* return: IST_ID *, or NULL
* list_id1(in): First list file identifier
* list_id2(in): Second list file identifier
* flag(in):
*
* Note: This routine takes the union of two list files by getting the
* tuples of the both list files and generates a new list file.
* The source list files are not affected.
*/
static QFILE_LIST_ID *
qfile_union_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id1_p, QFILE_LIST_ID * list_id2_p, int flag)
{
QFILE_LIST_ID *result_list_id_p, *base, *tail;
if (list_id1_p->tuple_cnt == 0)
{
base = list_id2_p;
tail = NULL;
}
else if (list_id2_p->tuple_cnt == 0)
{
base = list_id1_p;
tail = NULL;
}
else
{
base = list_id1_p;
tail = list_id2_p;
}
result_list_id_p = qfile_clone_list_id (base, false, QFILE_MOVE_DEPENDENT);
if (result_list_id_p == NULL)
{
return NULL;
}
if (tail != NULL)
{
if (qfile_reopen_list_as_append_mode (thread_p, result_list_id_p) != NO_ERROR)
{
goto error;
}
if (qfile_unify_types (result_list_id_p, tail) != NO_ERROR)
{
goto error;
}
if (qfile_copy_tuple (thread_p, result_list_id_p, tail) != NO_ERROR)
{
goto error;
}
qfile_close_list (thread_p, result_list_id_p);
}
/* clear base list_id to prevent double free of tfile_vfid */
qfile_clear_list_id (base);
return result_list_id_p;
error:
qfile_close_list (thread_p, result_list_id_p);
qfile_free_list_id (result_list_id_p);
return NULL;
}
/*
* qfile_reallocate_tuple () - reallocates a tuple to the desired size.
* If it cant, it sets an error and returns 0
* return: nt 1 succes, 0 failure
* tplrec(in): tuple descriptor
* tpl_size(in): desired size
* file(in):
* line(in):
*
*/
int
qfile_reallocate_tuple (QFILE_TUPLE_RECORD * tuple_record_p, int tuple_size)
{
QFILE_TUPLE tuple;
if (tuple_record_p->size == 0)
{
tuple_record_p->tpl = (QFILE_TUPLE) db_private_alloc (NULL, tuple_size);
}
else
{
/*
* Don't leak the original tuple if we get a malloc failure!
*/
tuple = (QFILE_TUPLE) db_private_realloc (NULL, tuple_record_p->tpl, tuple_size);
if (tuple == NULL)
{
db_private_free_and_init (NULL, tuple_record_p->tpl);
}
tuple_record_p->tpl = tuple;
}
if (tuple_record_p->tpl == NULL)
{
return ER_FAILED;
}
tuple_record_p->size = tuple_size;
return NO_ERROR;
}
#if !defined(NDEBUG)
/*
* qfile_print_list () - Dump the content of the list file to the standard output
* return: none
* list_id(in): List File Identifier
*/
void
qfile_print_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p)
{
QFILE_LIST_SCAN_ID scan_id;
QFILE_TUPLE_RECORD tuple_record = { NULL, 0 };
if (!list_id_p || list_id_p->type_list.type_cnt < 0)
{
fprintf (stdout, "\n <invalid tuple list> ");
return;
}
if (list_id_p->type_list.type_cnt == 0)
{
fprintf (stdout, "\n <empty tuple list> ");
return;
}
if (qfile_open_list_scan (list_id_p, &scan_id) != NO_ERROR)
{
return;
}
while (qfile_scan_list_next (thread_p, &scan_id, &tuple_record, PEEK) == S_SUCCESS)
{
qfile_print_tuple (&list_id_p->type_list, tuple_record.tpl);
}
qfile_close_scan (thread_p, &scan_id);
}
#endif
/*
* Sorting Related Routines
*/
/* qfile_make_sort_key () -
* return:
* info(in):
* key(in):
* input_scan(in):
* tplrec(in):
*/
SORT_STATUS
qfile_make_sort_key (THREAD_ENTRY * thread_p, SORTKEY_INFO * key_info_p, RECDES * key_record_p,
QFILE_LIST_SCAN_ID * input_scan_p, QFILE_TUPLE_RECORD * tuple_record_p)
{
int i, nkeys, length;
SORT_REC *sort_record_p;
char *data;
SCAN_CODE scan_status;
char *field_data;
int field_length, offset;
SORT_STATUS status;
scan_status = qfile_scan_list_next (thread_p, input_scan_p, tuple_record_p, PEEK);
if (scan_status != S_SUCCESS)
{
return ((scan_status == S_END) ? SORT_NOMORE_RECS : SORT_ERROR_OCCURRED);
}
nkeys = key_info_p->nkeys;
sort_record_p = (SORT_REC *) key_record_p->data;
sort_record_p->next = NULL;
if (key_info_p->use_original)
{
/* P_sort_key */
/* get sort_key body start position, align data to 8 bytes boundary */
data = &(sort_record_p->s.original.body[0]);
data = PTR_ALIGN (data, MAX_ALIGNMENT);
length = CAST_BUFLEN (data - key_record_p->data); /* i.e, 12 */
/* STEP 1: build header(tuple_ID) */
if (length <= key_record_p->area_size)
{
sort_record_p->s.original.pageid = input_scan_p->curr_vpid.pageid;
sort_record_p->s.original.volid = input_scan_p->curr_vpid.volid;
sort_record_p->s.original.offset = input_scan_p->curr_offset;
}
/* STEP 2: build body */
for (i = 0; i < nkeys; i++)
{
/* Position ourselves at the next field, and find out its length */
QFILE_GET_TUPLE_VALUE_HEADER_POSITION (tuple_record_p->tpl, key_info_p->key[i].col, field_data);
field_length =
((QFILE_GET_TUPLE_VALUE_FLAG (field_data) == V_BOUND) ? QFILE_GET_TUPLE_VALUE_LENGTH (field_data) : 0);
length += QFILE_TUPLE_VALUE_HEADER_SIZE + field_length;
if (length <= key_record_p->area_size)
{
memcpy (data, field_data, QFILE_TUPLE_VALUE_HEADER_SIZE + field_length);
}
/*
* Always pretend that we copied the data, even if we didn't.
* That will allow us to find out how big the record really needs
* to be.
*/
data += QFILE_TUPLE_VALUE_HEADER_SIZE + field_length;
}
}
else
{
/* A_sort_key */
/* get sort_key body start position, align data to 8 bytes boundary */
data = (char *) &sort_record_p->s.offset[nkeys];
data = PTR_ALIGN (data, MAX_ALIGNMENT);
length = CAST_BUFLEN (data - key_record_p->data); /* i.e, 4 + 4 * (n - 1) */
/* STEP 1: build header(offset_MAP) - go on with STEP 2 */
/* STEP 2: build body */
for (i = 0; i < nkeys; i++)
{
/* Position ourselves at the next field, and find out its length */
QFILE_GET_TUPLE_VALUE_HEADER_POSITION (tuple_record_p->tpl, key_info_p->key[i].col, field_data);
field_length =
((QFILE_GET_TUPLE_VALUE_FLAG (field_data) == V_BOUND) ? QFILE_GET_TUPLE_VALUE_LENGTH (field_data) : 0);
if (field_length)
{
/* non-NULL value */
offset = CAST_BUFLEN (data - key_record_p->data + QFILE_TUPLE_VALUE_HEADER_SIZE);
length = offset + field_length;
if (length <= key_record_p->area_size)
{
sort_record_p->s.offset[i] = offset;
memcpy (data, field_data, QFILE_TUPLE_VALUE_HEADER_SIZE + field_length);
}
/*
* Always pretend that we copied the data, even if we didn't.
* That will allow us to find out how big the record really
* needs to be.
*/
data += QFILE_TUPLE_VALUE_HEADER_SIZE + field_length;
}
else
{
/* do not copy NULL-value field */
if (length <= key_record_p->area_size)
{
sort_record_p->s.offset[i] = 0;
}
}
}
}
key_record_p->length = CAST_BUFLEN (data - key_record_p->data);
if (key_record_p->length <= key_record_p->area_size)
{
status = SORT_SUCCESS;
}
else
{
scan_status = qfile_scan_prev (thread_p, input_scan_p);
status = ((scan_status == S_ERROR) ? SORT_ERROR_OCCURRED : SORT_REC_DOESNT_FIT);
}
return status;
}
/* qfile_generate_sort_tuple () -
* return:
* info(in):
* sort_rec(in):
* output_recdes(out):
*/
QFILE_TUPLE
qfile_generate_sort_tuple (SORTKEY_INFO * key_info_p, SORT_REC * sort_record_p, RECDES * output_recdes_p)
{
int nkeys, size, i;
char *tuple_p, *field_p;
char *p;
int c;
char *src;
int len;
nkeys = key_info_p->nkeys;
size = QFILE_TUPLE_LENGTH_SIZE;
for (i = 0; i < nkeys; i++)
{
size += QFILE_TUPLE_VALUE_HEADER_SIZE;
if (sort_record_p->s.offset[i] != 0)
{
p = (char *) sort_record_p + sort_record_p->s.offset[i] - QFILE_TUPLE_VALUE_HEADER_SIZE;
size += QFILE_GET_TUPLE_VALUE_LENGTH (p);
}
}
if (output_recdes_p->area_size < size)
{
if (output_recdes_p->area_size == 0)
{
tuple_p = (char *) db_private_alloc (NULL, size);
}
else
{
tuple_p = (char *) db_private_realloc (NULL, output_recdes_p->data, size);
}
if (tuple_p == NULL)
{
return NULL;
}
output_recdes_p->data = tuple_p;
output_recdes_p->area_size = size;
}
tuple_p = output_recdes_p->data;
field_p = tuple_p + QFILE_TUPLE_LENGTH_SIZE;
for (i = 0; i < nkeys; i++)
{
c = key_info_p->key[i].permuted_col;
if (sort_record_p->s.offset[c] == 0)
{
QFILE_PUT_TUPLE_VALUE_FLAG (field_p, V_UNBOUND);
QFILE_PUT_TUPLE_VALUE_LENGTH (field_p, 0);
}
else
{
src = (char *) sort_record_p + sort_record_p->s.offset[c] - QFILE_TUPLE_VALUE_HEADER_SIZE;
len = QFILE_GET_TUPLE_VALUE_LENGTH (src);
memcpy (field_p, src, len + QFILE_TUPLE_VALUE_HEADER_SIZE);
field_p += len;
}
field_p += QFILE_TUPLE_VALUE_HEADER_SIZE;
}
QFILE_PUT_TUPLE_LENGTH (tuple_p, field_p - tuple_p);
return tuple_p;
}
/*
* qfile_get_next_sort_item () -
* return: SORT_STATUS
* recdes(in): Temporary record descriptor
* arg(in): Scan identifier
*
* Note: This routine is called by the sorting module to get the next
* input item to sort. The scan identifier opened on the list file
* is used to get the next list file tuple and return it to the sorting module
* within the record descriptor. If the record descriptor area is not big
* enough to hold the tuple, it is indicated to the sorting module and
* scan position remains unchanged. The routine is supposed to be called again
* with a bigger record descriptor. If there are no more tuples left
* in the list file, or if an error occurs, the sorting module is informed with
* necessary return codes.
*/
static SORT_STATUS
qfile_get_next_sort_item (THREAD_ENTRY * thread_p, RECDES * recdes_p, void *arg)
{
SORT_INFO *sort_info_p;
QFILE_SORT_SCAN_ID *scan_id_p;
sort_info_p = (SORT_INFO *) arg;
scan_id_p = sort_info_p->s_id;
return qfile_make_sort_key (thread_p, &sort_info_p->key_info, recdes_p, scan_id_p->s_id, &scan_id_p->tplrec);
}
/*
* qfile_put_next_sort_item () -
* return: SORT_STATUS
* recdes(in): Temporary record descriptor
* arg(in): Scan identifier
*
* Note: This routine is called by the sorting module to output the next sorted
* item.
*
* We have two versions of the put_next function:
* ls_sort_put_next_long and ls_sort_put_next_short. The long version
* is building sort keys from records that hold more fields than sort keys.
* It optimizes by simply keeping a pointer to the original record, and
* when the sort keys are delivered back from the sort module, it uses
* the pointer to retrieve the original record with all of the fields and
* delivers that record to the output listfile.
*
* The short version is used when we we're sorting on all of the fields of
* the record. In that case there's no point in remembering the original record,
* and we save the space occupied by the pointer. We also avoid the relatively
* random traversal of the input file when rendering the output file,
* since we can reconstruct the input records without actually consulting
* the input file.
*/
static int
qfile_put_next_sort_item (THREAD_ENTRY * thread_p, const RECDES * recdes_p, void *arg)
{
SORT_INFO *sort_info_p;
SORT_REC *key_p;
int error;
QFILE_TUPLE_DESCRIPTOR *tuple_descr_p;
int nkeys, i;
char *p;
PAGE_PTR page_p;
VPID vpid;
QFILE_LIST_ID *list_id_p;
char *data;
error = NO_ERROR;
sort_info_p = (SORT_INFO *) arg;
for (key_p = (SORT_REC *) recdes_p->data; key_p && error == NO_ERROR; key_p = key_p->next)
{
if (sort_info_p->key_info.use_original)
{
/* P_sort_key */
list_id_p = &(sort_info_p->s_id->s_id->list_id);
vpid.pageid = key_p->s.original.pageid;
vpid.volid = key_p->s.original.volid;
#if 0 /* SortCache */
/* check if page is already fixed */
if (VPID_EQ (&(sort_info_p->fixed_vpid), &vpid))
{
/* use cached page pointer */
page_p = sort_info_p->fixed_page;
}
else
{
/* free currently fixed page */
if (sort_info_p->fixed_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, sort_info_p->fixed_page, list_id_p->tfile_vfid);
}
/* fix page and cache fixed vpid */
page_p = qmgr_get_old_page (thread_p, &vpid, list_id_p->tfile_vfid);
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
/* cache page pointer */
sort_info_p->fixed_vpid = vpid;
sort_info_p->fixed_page = page_p;
}
#else /* not SortCache */
page_p = qmgr_get_old_page_simple_fix (thread_p, &vpid, list_id_p->tfile_vfid);
if (page_p == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
#endif /* not SortCache */
QFILE_GET_OVERFLOW_VPID (&vpid, page_p);
if (vpid.pageid == NULL_PAGEID)
{
/*
* This is the normal case of a non-overflow tuple. We can use
* the page image directly, since we know that the tuple resides
* entirely on that page.
*/
data = page_p + key_p->s.original.offset;
error = qfile_add_tuple_to_list (thread_p, sort_info_p->output_file, data);
}
else
{
assert (NULL_PAGEID < vpid.pageid); /* should not be NULL_PAGEID_IN_PROGRESS */
/*
* Rats; this tuple requires overflow pages. We need to copy
* all of the pages from the input file to the output file.
*/
error = qfile_add_overflow_tuple_to_list (thread_p, sort_info_p->output_file, page_p, list_id_p);
}
#if 1 /* not SortCache */
qmgr_free_old_page_simple_fix_and_init (thread_p, page_p, list_id_p->tfile_vfid);
#endif /* not SortCache */
}
else
{
/* A_sort_key */
nkeys = sort_info_p->key_info.nkeys; /* get sort_key field number */
/* generate tuple descriptor */
tuple_descr_p = &(sort_info_p->output_file->tpl_descr);
/* determine how big a tuple we'll need */
tuple_descr_p->tpl_size = QFILE_TUPLE_LENGTH_SIZE + (QFILE_TUPLE_VALUE_HEADER_SIZE * nkeys);
for (i = 0; i < nkeys; i++)
{
if (key_p->s.offset[i] != 0)
{
/*
* Remember, the offset[] value points to the start of the
* value's *data* (i.e., after the valflag/vallen nonsense),
* and is measured from the start of the sort_rec.
*/
p = (char *) key_p + key_p->s.offset[i] - QFILE_TUPLE_VALUE_HEADER_SIZE;
tuple_descr_p->tpl_size += QFILE_GET_TUPLE_VALUE_LENGTH (p);
}
}
if (tuple_descr_p->tpl_size < QFILE_MAX_TUPLE_SIZE_IN_PAGE)
{
/* SMALL QFILE_TUPLE */
/* set tuple descriptor */
tuple_descr_p->sortkey_info = (void *) (&sort_info_p->key_info);
tuple_descr_p->sort_rec = (void *) key_p;
/* generate sort_key driven tuple into list file page */
error = qfile_generate_tuple_into_list (thread_p, sort_info_p->output_file, T_SORTKEY);
}
else
{
/* BIG QFILE_TUPLE */
/*
* We didn't record the original vpid, and we should just
* reconstruct the original record from this sort key (rather
* than pressure the page buffer pool by reading in the original
* page to get the original tuple).
*/
if (qfile_generate_sort_tuple (&sort_info_p->key_info, key_p, &sort_info_p->output_recdes) == NULL)
{
error = ER_FAILED;
}
else
{
error = qfile_add_tuple_to_list (thread_p, sort_info_p->output_file, sort_info_p->output_recdes.data);
}
}
}
}
return (error == NO_ERROR) ? NO_ERROR : er_errid ();
}
/*
* qfile_compare_partial_sort_record () -
* return: -1, 0, or 1, strcmp-style
* pk0(in): Pointer to pointer to first sort record
* pk1(in): Pointer to pointer to second sort record
* arg(in): Pointer to sort info
*
* Note: These routines are used for relative comparisons of two sort
* records during sorting.
*/
int
qfile_compare_partial_sort_record (const void *pk0, const void *pk1, void *arg)
{
SORTKEY_INFO *key_info_p;
SORT_REC *k0, *k1;
int i, n;
int o0, o1;
int order;
char *d0, *d1;
char *fp0, *fp1; /* sort_key field pointer */
key_info_p = (SORTKEY_INFO *) arg;
n = key_info_p->nkeys;
order = 0;
k0 = *(SORT_REC **) pk0;
k1 = *(SORT_REC **) pk1;
/* get body start position of k0, k1 */
fp0 = &(k0->s.original.body[0]);
fp0 = PTR_ALIGN (fp0, MAX_ALIGNMENT);
fp1 = &(k1->s.original.body[0]);
fp1 = PTR_ALIGN (fp1, MAX_ALIGNMENT);
for (i = 0; i < n; i++)
{
if (QFILE_GET_TUPLE_VALUE_FLAG (fp0) == V_BOUND)
{
o0 = 1;
}
else
{
o0 = 0; /* NULL */
}
if (QFILE_GET_TUPLE_VALUE_FLAG (fp1) == V_BOUND)
{
o1 = 1;
}
else
{
o1 = 0; /* NULL */
}
if (o0 && o1)
{
if (key_info_p->key[i].use_cmp_dom)
{
order = qfile_compare_with_interpolation_domain (fp0, fp1, &key_info_p->key[i], key_info_p);
}
else
{
d0 = fp0 + QFILE_TUPLE_VALUE_HEADER_LENGTH;
d1 = fp1 + QFILE_TUPLE_VALUE_HEADER_LENGTH;
order = (*key_info_p->key[i].sort_f) (d0, d1, key_info_p->key[i].col_dom, 0, 1, NULL);
}
order = key_info_p->key[i].is_desc ? -order : order;
}
else
{
order = qfile_compare_with_null_value (o0, o1, key_info_p->key[i]);
}
if (order != 0)
{
break;
}
fp0 += QFILE_TUPLE_VALUE_HEADER_LENGTH + QFILE_GET_TUPLE_VALUE_LENGTH (fp0);
fp1 += QFILE_TUPLE_VALUE_HEADER_LENGTH + QFILE_GET_TUPLE_VALUE_LENGTH (fp1);
}
return order;
}
int
qfile_compare_all_sort_record (const void *pk0, const void *pk1, void *arg)
{
SORTKEY_INFO *key_info_p;
SORT_REC *k0, *k1;
int i, n;
int order;
int o0, o1;
char *d0, *d1;
key_info_p = (SORTKEY_INFO *) arg;
n = key_info_p->nkeys;
order = 0;
k0 = *(SORT_REC **) pk0;
k1 = *(SORT_REC **) pk1;
for (i = 0; i < n; i++)
{
o0 = k0->s.offset[i];
o1 = k1->s.offset[i];
if (o0 && o1)
{
d0 = (char *) k0 + o0;
d1 = (char *) k1 + o1;
order = (*key_info_p->key[i].sort_f) (d0, d1, key_info_p->key[i].col_dom, 0, 1, NULL);
order = key_info_p->key[i].is_desc ? -order : order;
}
else
{
order = qfile_compare_with_null_value (o0, o1, key_info_p->key[i]);
}
if (order != 0)
{
break;
}
}
return order;
}
/*
* qfile_compare_with_null_value () -
* return: -1, 0, or 1, strcmp-style
* o0(in): The first value
* o1(in): The second value
* key_info(in): Sub-key info
*
* Note: These routines are internally used for relative comparisons
* of two values which include NULL value.
*/
static int
qfile_compare_with_null_value (int o0, int o1, SUBKEY_INFO key_info)
{
/* At least one of the values sholud be NULL */
assert (o0 == 0 || o1 == 0);
if (o0 == 0 && o1 == 0)
{
/* both are unbound */
return 0;
}
else if (o0 == 0)
{
/* NULL compare_op !NULL */
assert (o1 != 0);
if (key_info.is_nulls_first)
{
return -1;
}
else
{
return 1;
}
}
else
{
/* !NULL compare_op NULL */
assert (o1 == 0);
if (key_info.is_nulls_first)
{
return 1;
}
else
{
return -1;
}
}
}
/* qfile_get_estimated_pages_for_sorting () -
* return:
* listid(in):
* info(in):
*
* Note: Make an estimate of input page count to be passed to the sorting
* module. We want this to be an upper bound, because the sort
* package already has a limit on sort buffer size, and sorting
* proceeds faster with larger in-memory use.
*/
int
qfile_get_estimated_pages_for_sorting (QFILE_LIST_ID * list_id_p, SORTKEY_INFO * key_info_p)
{
int prorated_pages, sort_key_size, sort_key_overhead;
prorated_pages = (int) MAX (list_id_p->page_cnt, 1);
if (key_info_p->use_original == 1)
{
/* P_sort_key */
/*
* Every Part sort key record will have one int of overhead
* per field in the key (for the offset vector).
*/
sort_key_size = (int) offsetof (SORT_REC, s.original.body[0]);
sort_key_overhead = (int) ceil (((double) (list_id_p->tuple_cnt * sort_key_size)) / DB_PAGESIZE);
}
else
{
/* A_sort_key */
/*
* Every Part sort key record will have one int of overhead
* per field in the key (for the offset vector).
*/
sort_key_size =
(int) offsetof (SORT_REC, s.offset[0]) + sizeof (((SORT_REC *) 0)->s.offset[0]) * key_info_p->nkeys;
sort_key_overhead = (int) ceil (((double) (list_id_p->tuple_cnt * sort_key_size)) / DB_PAGESIZE);
}
return prorated_pages + sort_key_overhead;
}
/* qfile_initialize_sort_key_info () -
* return:
* info(in):
* list(in):
* types(in):
*/
SORTKEY_INFO *
qfile_initialize_sort_key_info (SORTKEY_INFO * key_info_p, SORT_LIST * list_p, QFILE_TUPLE_VALUE_TYPE_LIST * types)
{
int i, n;
SUBKEY_INFO *subkey;
if (types == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (list_p)
{
n = qfile_get_sort_list_size (list_p);
}
else
{
n = types->type_cnt;
}
key_info_p->nkeys = n;
key_info_p->use_original = (n != types->type_cnt);
key_info_p->error = NO_ERROR;
if (n <= (int) DIM (key_info_p->default_keys))
{
key_info_p->key = key_info_p->default_keys;
}
else
{
key_info_p->key = (SUBKEY_INFO *) db_private_alloc (NULL, n * sizeof (SUBKEY_INFO));
if (key_info_p->key == NULL)
{
return NULL;
}
}
if (list_p)
{
SORT_LIST *p;
for (i = 0, p = list_p; p; i++, p = p->next)
{
assert_release (p->pos_descr.pos_no >= 0);
assert_release (p->pos_descr.dom != NULL);
subkey = &key_info_p->key[i];
subkey->col = p->pos_descr.pos_no;
subkey->col_dom = p->pos_descr.dom;
subkey->cmp_dom = NULL;
subkey->use_cmp_dom = false;
if (p->pos_descr.dom->type->id == DB_TYPE_VARIABLE)
{
subkey->sort_f = types->domp[i]->type->get_data_cmpdisk_function ();
}
else
{
subkey->sort_f = p->pos_descr.dom->type->get_data_cmpdisk_function ();
}
subkey->is_desc = (p->s_order == S_ASC) ? 0 : 1;
subkey->is_nulls_first = (p->s_nulls == S_NULLS_LAST) ? 0 : 1;
if (key_info_p->use_original)
{
key_info_p->key[i].permuted_col = i;
}
else
{
key_info_p->key[p->pos_descr.pos_no].permuted_col = i;
}
}
}
else
{
for (i = 0; i < n; i++)
{
SUBKEY_INFO *subkey;
subkey = &key_info_p->key[i];
subkey->col = i;
subkey->permuted_col = i;
subkey->col_dom = types->domp[i];
subkey->cmp_dom = NULL;
subkey->use_cmp_dom = false;
subkey->sort_f = types->domp[i]->type->get_data_cmpdisk_function ();
subkey->is_desc = 0;
subkey->is_nulls_first = 1;
}
}
return key_info_p;
}
/* qfile_clear_sort_key_info () -
* return:
* info(in):
*/
void
qfile_clear_sort_key_info (SORTKEY_INFO * key_info_p)
{
if (!key_info_p)
{
return;
}
if (key_info_p->key && key_info_p->key != key_info_p->default_keys)
{
db_private_free_and_init (NULL, key_info_p->key);
}
key_info_p->key = NULL;
key_info_p->nkeys = 0;
}
/* qfile_initialize_sort_info () -
* return:
* info(in):
* listid(in):
* sort_list(in):
*/
static SORT_INFO *
qfile_initialize_sort_info (SORT_INFO * sort_info_p, QFILE_LIST_ID * list_id_p, SORT_LIST * sort_list_p)
{
sort_info_p->key_info.key = NULL;
#if 0 /* SortCache */
VPID_SET_NULL (&(sort_info_p->fixed_vpid));
sort_info_p->fixed_page = NULL;
#endif /* SortCache */
sort_info_p->output_recdes.data = NULL;
sort_info_p->output_recdes.area_size = 0;
if (qfile_initialize_sort_key_info (&sort_info_p->key_info, sort_list_p, &list_id_p->type_list) == NULL)
{
return NULL;
}
return sort_info_p;
}
/* qfile_clear_sort_info () -
* return: none
* info(in): Pointer to info block to be initialized
*
* Note: Free all internal structures in the given SORT_INFO block.
*/
static void
qfile_clear_sort_info (SORT_INFO * sort_info_p)
{
#if 0 /* SortCache */
QFILE_LIST_ID *list_idp;
list_idp = &(sort_info_p->s_id->s_id->list_id);
if (sort_info_p->fixed_page != NULL)
{
qmgr_free_old_page_and_init (thread_p, sort_info_p->fixed_page, list_idp->tfile_vfid);
}
#endif /* SortCache */
qfile_clear_sort_key_info (&sort_info_p->key_info);
if (sort_info_p->output_recdes.data)
{
db_private_free_and_init (NULL, sort_info_p->output_recdes.data);
}
sort_info_p->output_recdes.data = NULL;
sort_info_p->output_recdes.area_size = 0;
}
/*
* qfile_sort_list_with_func () -
* return: QFILE_LIST_ID *, or NULL
* list_id(in): Source list file identifier
* sort_list(in): List of comparison items
* option(in):
* ls_flag(in):
* get_fn(in):
* put_fn(in):
* cmp_fn(in):
* extra_arg(in):
* limit(in):
* do_close(in):
* parallelism(in)
* orderby_stats(in)
*/
QFILE_LIST_ID *
qfile_sort_list_with_func (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, SORT_LIST * sort_list_p,
QUERY_OPTIONS option, int flag, SORT_GET_FUNC * get_func, SORT_PUT_FUNC * put_func,
SORT_CMP_FUNC * cmp_func, void *extra_arg, int limit, bool do_close, int parallelism,
ORDERBY_STATS * orderby_stats)
{
QFILE_LIST_ID *srlist_id;
QFILE_LIST_SCAN_ID t_scan_id;
QFILE_SORT_SCAN_ID s_scan_id;
SORT_INFO info;
int sort_result, estimated_pages;
SORT_DUP_OPTION dup_option;
SORT_PARALLEL_TYPE parallel_type;
/* The result file must be closed for parallel processing. If not closed, Latch contention may occur. */
qfile_close_list (thread_p, list_id_p);
srlist_id = qfile_open_list (thread_p, &list_id_p->type_list, sort_list_p, list_id_p->query_id, flag, NULL);
if (srlist_id == NULL)
{
return NULL;
}
/* open a scan on the unsorted list file */
if (qfile_open_list_scan (list_id_p, &t_scan_id) != NO_ERROR)
{
qfile_close_and_free_list_file (thread_p, srlist_id);
return NULL;
}
if (qfile_initialize_sort_info (&info, list_id_p, sort_list_p) == NULL)
{
qfile_close_scan (thread_p, &t_scan_id);
qfile_close_and_free_list_file (thread_p, srlist_id);
return NULL;
}
info.s_id = &s_scan_id;
info.output_file = srlist_id;
info.input_file = list_id_p;
info.extra_arg = extra_arg;
info.sort_list_p = sort_list_p;
info.flag = flag;
info.parallelism = parallelism;
info.orderby_stats = orderby_stats;
if (get_func == NULL)
{
get_func = &qfile_get_next_sort_item;
}
if (put_func == NULL)
{
put_func = &qfile_put_next_sort_item;
}
if (put_func == qfile_put_next_sort_item)
{
parallel_type = SORT_ORDER_BY;
}
else
{
/* TO_DO: px_sort for qexec_ordby_put_next */
parallel_type = SORT_ORDER_WITH_LIMIT;
}
if (cmp_func == NULL)
{
if (info.key_info.use_original == 1)
{
cmp_func = &qfile_compare_partial_sort_record;
}
else
{
cmp_func = &qfile_compare_all_sort_record;
}
}
s_scan_id.s_id = &t_scan_id;
s_scan_id.tplrec.size = 0;
s_scan_id.tplrec.tpl = (char *) NULL;
estimated_pages = qfile_get_estimated_pages_for_sorting (list_id_p, &info.key_info);
dup_option = ((option == Q_DISTINCT) ? SORT_ELIM_DUP : SORT_DUP);
sort_result =
sort_listfile (thread_p, NULL_VOLID, estimated_pages, get_func, &info, put_func, &info, cmp_func, &info.key_info,
dup_option, limit, srlist_id->tfile_vfid->tde_encrypted, parallel_type);
if (sort_result < 0)
{
#if 0 /* SortCache */
qfile_clear_sort_info (&info);
qfile_close_scan (&t_scan_id);
#else /* not SortCache */
qfile_close_scan (thread_p, &t_scan_id);
qfile_clear_sort_info (&info);
#endif /* not SortCache */
qfile_close_list (thread_p, list_id_p);
if (list_id_p->is_result_cached)
{
qfile_clear_list_id (list_id_p);
}
else
{
qfile_destroy_list (thread_p, list_id_p);
}
qfile_close_and_free_list_file (thread_p, srlist_id);
return NULL;
}
if (do_close)
{
qfile_close_list (thread_p, srlist_id);
}
#if 0 /* SortCache */
qfile_clear_sort_info (&info);
qfile_close_scan (&t_scan_id);
#else /* not SortCache */
qfile_close_scan (thread_p, &t_scan_id);
qfile_clear_sort_info (&info);
#endif /* not SortCache */
qfile_close_list (thread_p, list_id_p);
if (list_id_p->is_result_cached)
{
qfile_clear_list_id (list_id_p);
}
else
{
qfile_destroy_list (thread_p, list_id_p);
}
qfile_copy_list_id (list_id_p, srlist_id, true, QFILE_MOVE_DEPENDENT);
QFILE_FREE_AND_INIT_LIST_ID (srlist_id);
return list_id_p;
}
/*
* qfile_sort_list () -
* return: QFILE_LIST_ID *, or NULL
* list_id(in): Source list file identifier
* sort_list(in): List of comparison items
* option(in):
* do_close(in);
*
* Note: This routine sorts the specified list file tuples according
* to the list of comparison items and generates a sorted list
* file. The source list file is not affected by the routine.
*/
QFILE_LIST_ID *
qfile_sort_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, SORT_LIST * sort_list_p, QUERY_OPTIONS option,
bool do_close)
{
int ls_flag;
if (sort_list_p && qfile_is_sort_list_covered (list_id_p->sort_list, sort_list_p) == true)
{
/* no need to sort here */
return list_id_p;
}
ls_flag = (option == Q_DISTINCT) ? QFILE_FLAG_DISTINCT : QFILE_FLAG_ALL;
return qfile_sort_list_with_func (thread_p, list_id_p, sort_list_p, option, ls_flag, NULL, NULL, NULL, NULL,
NO_SORT_LIMIT, do_close, 0, NULL);
}
/*
* qfile_copy_list_pages () -
* return:
* old_first_vpidp(in) :
* old_tfile_vfidp(in) :
* new_first_vpidp(in) :
* new_last_vpidp(in) :
* new_tfile_vfidp(in) :
*/
static int
qfile_copy_list_pages (THREAD_ENTRY * thread_p, VPID * old_first_vpid_p, QMGR_TEMP_FILE * old_tfile_vfid_p,
VPID * new_first_vpid_p, VPID * new_last_vpid_p, QMGR_TEMP_FILE * new_tfile_vfid_p)
{
PAGE_PTR old_page_p, old_ovfl_page_p, prev_page_p, new_page_p, new_ovfl_page_p;
VPID old_next_vpid, old_ovfl_vpid, prev_vpid, new_ovfl_vpid;
old_page_p = qmgr_get_old_page (thread_p, old_first_vpid_p, old_tfile_vfid_p);
if (old_page_p == NULL)
{
return ER_FAILED;
}
new_page_p = qmgr_get_new_page (thread_p, new_first_vpid_p, new_tfile_vfid_p);
if (new_page_p == NULL)
{
qmgr_free_old_page_and_init (thread_p, old_page_p, old_tfile_vfid_p);
return ER_FAILED;
}
*new_last_vpid_p = *new_first_vpid_p;
while (true)
{
(void) memcpy (new_page_p, old_page_p, DB_PAGESIZE);
QFILE_GET_OVERFLOW_VPID (&old_ovfl_vpid, old_page_p);
prev_page_p = new_page_p;
new_ovfl_page_p = NULL;
while (!VPID_ISNULL (&old_ovfl_vpid))
{
old_ovfl_page_p = qmgr_get_old_page (thread_p, &old_ovfl_vpid, old_tfile_vfid_p);
if (old_ovfl_page_p == NULL)
{
qmgr_free_old_page_and_init (thread_p, old_page_p, old_tfile_vfid_p);
qfile_set_dirty_page (thread_p, new_page_p, FREE, new_tfile_vfid_p);
return ER_FAILED;
}
new_ovfl_page_p = qmgr_get_new_page (thread_p, &new_ovfl_vpid, new_tfile_vfid_p);
if (new_ovfl_page_p == NULL)
{
qmgr_free_old_page_and_init (thread_p, old_ovfl_page_p, old_tfile_vfid_p);
qmgr_free_old_page_and_init (thread_p, old_page_p, old_tfile_vfid_p);
qfile_set_dirty_page (thread_p, new_page_p, FREE, new_tfile_vfid_p);
return ER_FAILED;
}
(void) memcpy (new_ovfl_page_p, old_ovfl_page_p, DB_PAGESIZE);
QFILE_GET_OVERFLOW_VPID (&old_ovfl_vpid, old_ovfl_page_p);
qmgr_free_old_page_and_init (thread_p, old_ovfl_page_p, old_tfile_vfid_p);
QFILE_PUT_OVERFLOW_VPID (prev_page_p, &new_ovfl_vpid);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, new_tfile_vfid_p);
prev_page_p = new_ovfl_page_p;
}
if (new_ovfl_page_p)
{
qfile_set_dirty_page (thread_p, new_ovfl_page_p, FREE, new_tfile_vfid_p);
}
QFILE_GET_NEXT_VPID (&old_next_vpid, old_page_p);
qmgr_free_old_page_and_init (thread_p, old_page_p, old_tfile_vfid_p);
if (VPID_ISNULL (&old_next_vpid))
{
qfile_set_dirty_page (thread_p, new_page_p, FREE, new_tfile_vfid_p);
new_page_p = NULL;
break;
}
old_page_p = qmgr_get_old_page (thread_p, &old_next_vpid, old_tfile_vfid_p);
prev_page_p = new_page_p;
prev_vpid = *new_last_vpid_p;
if (old_page_p == NULL)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, new_tfile_vfid_p);
return ER_FAILED;
}
new_page_p = qmgr_get_new_page (thread_p, new_last_vpid_p, new_tfile_vfid_p);
if (new_page_p == NULL)
{
qmgr_free_old_page_and_init (thread_p, old_page_p, old_tfile_vfid_p);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, new_tfile_vfid_p);
return ER_FAILED;
}
QFILE_PUT_PREV_VPID (new_page_p, &prev_vpid);
QFILE_PUT_NEXT_VPID (prev_page_p, new_last_vpid_p);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, new_tfile_vfid_p);
}
if (new_page_p)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, new_tfile_vfid_p);
}
return NO_ERROR;
}
/*
* qfile_duplicate_list () -
* return:
* list_id(in):
* flag(in):
*
* Note: This routine duplicates the specified list file.
* The source list file is not affected by the routine.
*/
QFILE_LIST_ID *
qfile_duplicate_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, int flag)
{
QFILE_LIST_ID *dup_list_id_p;
dup_list_id_p = qfile_open_list (thread_p, &list_id_p->type_list, NULL, list_id_p->query_id, flag, NULL);
if (dup_list_id_p == NULL)
{
return NULL;
}
if (qfile_copy_list_pages (thread_p, &list_id_p->first_vpid, list_id_p->tfile_vfid, &dup_list_id_p->first_vpid,
&dup_list_id_p->last_vpid, dup_list_id_p->tfile_vfid) != NO_ERROR)
{
qfile_destroy_list (thread_p, dup_list_id_p);
QFILE_FREE_AND_INIT_LIST_ID (dup_list_id_p);
return NULL;
}
dup_list_id_p->tuple_cnt = list_id_p->tuple_cnt;
dup_list_id_p->page_cnt = list_id_p->page_cnt;
return dup_list_id_p;
}
/*
* List File Scan Routines
*/
/*
* qfile_get_tuple () -
* return:
* first_page(in):
* tuplep(in):
* tplrec(in):
* list_idp(in):
*/
int
qfile_get_tuple (THREAD_ENTRY * thread_p, PAGE_PTR first_page_p, QFILE_TUPLE tuple, QFILE_TUPLE_RECORD * tuple_record_p,
QFILE_LIST_ID * list_id_p)
{
VPID ovfl_vpid;
char *tuple_p;
int offset;
int tuple_length, tuple_page_size;
int max_tuple_page_size;
PAGE_PTR page_p;
page_p = first_page_p;
tuple_length = QFILE_GET_TUPLE_LENGTH (tuple);
if (tuple_record_p->size < tuple_length)
{
if (qfile_reallocate_tuple (tuple_record_p, tuple_length) != NO_ERROR)
{
return ER_FAILED;
}
}
tuple_p = (char *) tuple_record_p->tpl;
if (QFILE_GET_OVERFLOW_PAGE_ID (page_p) == NULL_PAGEID)
{
/* tuple is inside the page */
memcpy (tuple_p, tuple, tuple_length);
return NO_ERROR;
}
else
{
/* tuple has overflow pages */
offset = 0;
max_tuple_page_size = qfile_Max_tuple_page_size;
do
{
QFILE_GET_OVERFLOW_VPID (&ovfl_vpid, page_p);
tuple_page_size = MIN (tuple_length - offset, max_tuple_page_size);
memcpy (tuple_p, (char *) page_p + QFILE_PAGE_HEADER_SIZE, tuple_page_size);
tuple_p += tuple_page_size;
offset += tuple_page_size;
if (page_p != first_page_p)
{
qmgr_free_old_page_and_init (thread_p, page_p, list_id_p->tfile_vfid);
}
if (ovfl_vpid.pageid != NULL_PAGEID)
{
page_p = qmgr_get_old_page (thread_p, &ovfl_vpid, list_id_p->tfile_vfid);
if (page_p == NULL)
{
return ER_FAILED;
}
}
}
while (ovfl_vpid.pageid != NULL_PAGEID);
}
return NO_ERROR;
}
/*
* qfile_get_tuple_from_current_list () -
* return: int (NO_ERROR or ER_FAILED)
* s_id(in): Scan identifier
* tplrec(in): Tuple record descriptor
*
* Note: Fetch the current tuple of the given scan identifier into the
* tuple descriptor.
*/
static int
qfile_get_tuple_from_current_list (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
QFILE_TUPLE_RECORD * tuple_record_p)
{
PAGE_PTR page_p;
QFILE_TUPLE tuple;
page_p = scan_id_p->curr_pgptr;
tuple = (char *) page_p + scan_id_p->curr_offset;
return qfile_get_tuple (thread_p, page_p, tuple, tuple_record_p, &scan_id_p->list_id);
}
/*
* qfile_scan_next () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in/out): Scan identifier
*
* Note: The scan is moved to the next scan item. If there are no more
* scan items, S_END is returned. If an error occurs,
* S_ERROR is returned.
*
* Note: The scan identifier must be of type LIST FILE scan identifier.
*/
static SCAN_CODE
qfile_scan_next (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
PAGE_PTR page_p, next_page_p;
VPID next_vpid;
if (scan_id_p->position == S_BEFORE)
{
if (scan_id_p->list_id.tuple_cnt > 0)
{
if (scan_id_p->is_read_only)
{
page_p =
qmgr_get_old_page_read_only (thread_p, &scan_id_p->list_id.first_vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
page_p = qmgr_get_old_page (thread_p, &scan_id_p->list_id.first_vpid, scan_id_p->list_id.tfile_vfid);
}
if (page_p == NULL)
{
return S_ERROR;
}
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &scan_id_p->list_id.first_vpid);
scan_id_p->curr_pgptr = page_p;
scan_id_p->curr_offset = QFILE_PAGE_HEADER_SIZE;
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + QFILE_PAGE_HEADER_SIZE;
scan_id_p->curr_tplno = 0;
scan_id_p->position = S_ON;
return S_SUCCESS;
}
else
{
return S_END;
}
}
else if (scan_id_p->position == S_ON)
{
if (scan_id_p->curr_tplno < QFILE_GET_TUPLE_COUNT (scan_id_p->curr_pgptr) - 1)
{
scan_id_p->curr_offset += QFILE_GET_TUPLE_LENGTH (scan_id_p->curr_tpl);
scan_id_p->curr_tpl += QFILE_GET_TUPLE_LENGTH (scan_id_p->curr_tpl);
scan_id_p->curr_tplno++;
return S_SUCCESS;
}
else if (qfile_has_next_page (scan_id_p->curr_pgptr))
{
QFILE_GET_NEXT_VPID (&next_vpid, scan_id_p->curr_pgptr);
if (scan_id_p->is_read_only)
{
next_page_p = qmgr_get_old_page_read_only (thread_p, &next_vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
next_page_p = qmgr_get_old_page (thread_p, &next_vpid, scan_id_p->list_id.tfile_vfid);
}
if (next_page_p == NULL)
{
return S_ERROR;
}
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &next_vpid);
scan_id_p->curr_pgptr = next_page_p;
scan_id_p->curr_tplno = 0;
scan_id_p->curr_offset = QFILE_PAGE_HEADER_SIZE;
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + QFILE_PAGE_HEADER_SIZE;
return S_SUCCESS;
}
else
{
scan_id_p->position = S_AFTER;
if (!scan_id_p->keep_page_on_finish)
{
scan_id_p->curr_vpid.pageid = NULL_PAGEID;
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
}
return S_END;
}
}
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;
}
}
/*
* qfile_scan_prev () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in/out): Scan identifier
*
* Note: The scan is moved to previous scan item. If there are no more
* scan items, S_END is returned. If an error occurs,
* S_ERROR is returned.
*/
static SCAN_CODE
qfile_scan_prev (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
PAGE_PTR page_p, prev_page_p;
VPID prev_vpid;
if (scan_id_p->position == S_BEFORE)
{
return S_END;
}
else if (scan_id_p->position == S_ON)
{
if (scan_id_p->curr_tplno > 0)
{
scan_id_p->curr_offset -= QFILE_GET_PREV_TUPLE_LENGTH (scan_id_p->curr_tpl);
scan_id_p->curr_tpl -= QFILE_GET_PREV_TUPLE_LENGTH (scan_id_p->curr_tpl);
scan_id_p->curr_tplno--;
return S_SUCCESS;
}
else if (QFILE_GET_PREV_PAGE_ID (scan_id_p->curr_pgptr) != NULL_PAGEID)
{
QFILE_GET_PREV_VPID (&prev_vpid, scan_id_p->curr_pgptr);
if (scan_id_p->is_read_only)
{
prev_page_p = qmgr_get_old_page_read_only (thread_p, &prev_vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
prev_page_p = qmgr_get_old_page (thread_p, &prev_vpid, scan_id_p->list_id.tfile_vfid);
}
if (prev_page_p == NULL)
{
return S_ERROR;
}
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &prev_vpid);
scan_id_p->curr_pgptr = prev_page_p;
scan_id_p->curr_tplno = QFILE_GET_TUPLE_COUNT (prev_page_p) - 1;
scan_id_p->curr_offset = QFILE_GET_LAST_TUPLE_OFFSET (prev_page_p);
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + scan_id_p->curr_offset;
return S_SUCCESS;
}
else
{
scan_id_p->position = S_BEFORE;
scan_id_p->curr_vpid.pageid = NULL_PAGEID;
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
return S_END;
}
}
else if (scan_id_p->position == S_AFTER)
{
if (VPID_ISNULL (&scan_id_p->list_id.first_vpid))
{
return S_END;
}
if (scan_id_p->is_read_only)
{
page_p = qmgr_get_old_page_read_only (thread_p, &scan_id_p->list_id.last_vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
page_p = qmgr_get_old_page (thread_p, &scan_id_p->list_id.last_vpid, scan_id_p->list_id.tfile_vfid);
}
if (page_p == NULL)
{
return S_ERROR;
}
scan_id_p->position = S_ON;
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &scan_id_p->list_id.last_vpid);
scan_id_p->curr_pgptr = page_p;
scan_id_p->curr_tplno = QFILE_GET_TUPLE_COUNT (page_p) - 1;
scan_id_p->curr_offset = QFILE_GET_LAST_TUPLE_OFFSET (page_p);
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + scan_id_p->curr_offset;
return S_SUCCESS;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_UNKNOWN_CRSPOS, 0);
return S_ERROR;
}
}
/*
* qfile_save_current_scan_tuple_position () -
* return:
* s_id(in): Scan identifier
* ls_tplpos(in/out): Set to contain current scan tuple position
*
* Note: Save current scan tuple position information.
*/
void
qfile_save_current_scan_tuple_position (QFILE_LIST_SCAN_ID * scan_id_p, QFILE_TUPLE_POSITION * tuple_position_p)
{
tuple_position_p->status = scan_id_p->status;
tuple_position_p->position = scan_id_p->position;
tuple_position_p->vpid.pageid = scan_id_p->curr_vpid.pageid;
tuple_position_p->vpid.volid = scan_id_p->curr_vpid.volid;
tuple_position_p->offset = scan_id_p->curr_offset;
tuple_position_p->tpl = scan_id_p->curr_tpl;
tuple_position_p->tplno = scan_id_p->curr_tplno;
}
static SCAN_CODE
qfile_retrieve_tuple (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p, QFILE_TUPLE_RECORD * tuple_record_p,
int peek)
{
int tuple_size;
if (QFILE_GET_OVERFLOW_PAGE_ID (scan_id_p->curr_pgptr) == NULL_PAGEID)
{
if (peek)
{
tuple_record_p->tpl = scan_id_p->curr_tpl;
}
else
{
tuple_size = QFILE_GET_TUPLE_LENGTH (scan_id_p->curr_tpl);
if (tuple_record_p->size < tuple_size)
{
if (qfile_reallocate_tuple (tuple_record_p, tuple_size) != NO_ERROR)
{
return S_ERROR;
}
}
memcpy (tuple_record_p->tpl, scan_id_p->curr_tpl, tuple_size);
}
}
else
{
/* tuple has overflow pages */
if (peek)
{
if (qfile_get_tuple_from_current_list (thread_p, scan_id_p, &scan_id_p->tplrec) != NO_ERROR)
{
return S_ERROR;
}
tuple_record_p->tpl = scan_id_p->tplrec.tpl;
}
else
{
if (qfile_get_tuple_from_current_list (thread_p, scan_id_p, tuple_record_p) != NO_ERROR)
{
return S_ERROR;
}
}
}
return S_SUCCESS;
}
/*
* qfile_jump_scan_tuple_position() -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in/out): Scan identifier
* ls_tplpos(in): Scan tuple position
* tplrec(in/out): Tuple record descriptor
* peek(in): Peek or Copy Tuple
*
* Note: Jump to the given list file scan position and fetch the tuple
* to the given tuple descriptor, either by peeking or by
* copying depending on the value of the peek parameter.
*
* Note: Saved scan can only be on "ON" or "AFTER" positions.
*/
SCAN_CODE
qfile_jump_scan_tuple_position (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
QFILE_TUPLE_POSITION * tuple_position_p, QFILE_TUPLE_RECORD * tuple_record_p, int peek)
{
PAGE_PTR page_p;
if (tuple_position_p->position == S_ON)
{
if (scan_id_p->position == S_ON)
{
if (scan_id_p->curr_vpid.pageid != tuple_position_p->vpid.pageid
|| scan_id_p->curr_vpid.volid != tuple_position_p->vpid.volid)
{
if (scan_id_p->is_read_only)
{
page_p =
qmgr_get_old_page_read_only (thread_p, &tuple_position_p->vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
page_p = qmgr_get_old_page (thread_p, &tuple_position_p->vpid, scan_id_p->list_id.tfile_vfid);
}
if (page_p == NULL)
{
return S_ERROR;
}
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &tuple_position_p->vpid);
scan_id_p->curr_pgptr = page_p;
}
}
else
{
if (scan_id_p->is_read_only)
{
page_p = qmgr_get_old_page_read_only (thread_p, &tuple_position_p->vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
page_p = qmgr_get_old_page (thread_p, &tuple_position_p->vpid, scan_id_p->list_id.tfile_vfid);
}
if (page_p == NULL)
{
return S_ERROR;
}
QFILE_COPY_VPID (&scan_id_p->curr_vpid, &tuple_position_p->vpid);
scan_id_p->curr_pgptr = page_p;
}
}
else
{
if (scan_id_p->position == S_ON)
{
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
}
}
scan_id_p->status = tuple_position_p->status;
scan_id_p->position = tuple_position_p->position;
scan_id_p->curr_offset = tuple_position_p->offset;
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + scan_id_p->curr_offset;
scan_id_p->curr_tplno = tuple_position_p->tplno;
if (scan_id_p->position == S_ON)
{
return qfile_retrieve_tuple (thread_p, scan_id_p, tuple_record_p, peek);
}
else if (scan_id_p->position == S_BEFORE || 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;
}
}
/*
* qfile_start_scan_fix () -
* return: int (NO_ERROR or ER_FAILED)
* s_id(in/out): Scan identifier
*
* Note: Start a scan operation which will keep the accessed list file
* pages fixed in the buffer pool. The routine starts the scan
* operation either from the beginning or from the last point
* scan fix ended.
*/
int
qfile_start_scan_fix (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
if (scan_id_p->position == S_ON && !scan_id_p->curr_pgptr)
{
if (scan_id_p->is_read_only)
{
scan_id_p->curr_pgptr =
qmgr_get_old_page_read_only (thread_p, &scan_id_p->curr_vpid, scan_id_p->list_id.tfile_vfid);
}
else
{
scan_id_p->curr_pgptr = qmgr_get_old_page (thread_p, &scan_id_p->curr_vpid, scan_id_p->list_id.tfile_vfid);
}
if (scan_id_p->curr_pgptr == NULL)
{
return ER_FAILED;
}
scan_id_p->curr_tpl = (char *) scan_id_p->curr_pgptr + scan_id_p->curr_offset;
}
else
{
scan_id_p->status = S_STARTED;
scan_id_p->position = S_BEFORE;
}
return NO_ERROR;
}
/*
* qfile_open_list_scan () -
* return: int (NO_ERROR or ER_FAILED)
* list_id(in): List identifier
* s_id(in/out): Scan identifier
*
* Note: A scan identifier is created to scan through the given list of tuples.
*/
int
qfile_open_list_scan (QFILE_LIST_ID * list_id_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
scan_id_p->status = S_OPENED;
scan_id_p->position = S_BEFORE;
scan_id_p->keep_page_on_finish = 0;
scan_id_p->is_read_only = false;
scan_id_p->curr_vpid.pageid = NULL_PAGEID;
scan_id_p->curr_vpid.volid = NULL_VOLID;
QFILE_CLEAR_LIST_ID (&scan_id_p->list_id);
if (qfile_copy_list_id (&scan_id_p->list_id, list_id_p, true, QFILE_SKIP_DEPENDENT) != NO_ERROR)
{
return ER_FAILED;
}
scan_id_p->tplrec.size = 0;
scan_id_p->tplrec.tpl = NULL;
return NO_ERROR;
}
/*
* qfile_scan_list () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in): Scan identifier
* tplrec(in/out): Tuple record descriptor
* peek(in): Peek or Copy Tuple
*
* Note: The regular LIST FILE scan is moved to the next scan tuple and
* the tuple is fetched to the tuple record descriptor. If there
* are no more scan items(tuples), S_END is returned. If
* an error occurs, S_ERROR is returned. If peek is true,
* the tplrec->tpl pointer is directly set to point to scan list
* file page, otherwise the tuple content is copied to the
* tplrec tuple area. If the area inside the tplrec is not enough
* it is reallocated by this routine.
*
* Note1: The pointer set by a PEEK operation is valid until another scan
* operation on the list file or until scan is closed.
*
* Note2: When the PEEK is specified, the area pointed by the tuple must not
* be modified by the caller.
*/
static SCAN_CODE
qfile_scan_list (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p,
SCAN_CODE (*scan_func) (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID *),
QFILE_TUPLE_RECORD * tuple_record_p, int peek)
{
SCAN_CODE qp_scan;
qp_scan = (*scan_func) (thread_p, scan_id_p);
if (qp_scan == S_SUCCESS)
{
qp_scan = qfile_retrieve_tuple (thread_p, scan_id_p, tuple_record_p, peek);
}
return qp_scan;
}
/*
* qfile_scan_list_next () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in): Scan identifier
* tplrec(in/out): Tuple record descriptor
* peek(in): Peek or Copy Tuple
*/
SCAN_CODE
qfile_scan_list_next (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p, QFILE_TUPLE_RECORD * tuple_record_p,
int peek)
{
return qfile_scan_list (thread_p, scan_id_p, qfile_scan_next, tuple_record_p, peek);
}
/*
* qfile_scan_list_prev () -
* return: SCAN_CODE (S_SUCCESS, S_END, S_ERROR)
* s_id(in): Scan identifier
* tplrec(in/out): Tuple record descriptor
* peek(in): Peek or Copy Tuple
*/
SCAN_CODE
qfile_scan_list_prev (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p, QFILE_TUPLE_RECORD * tuple_record_p,
int peek)
{
return qfile_scan_list (thread_p, scan_id_p, qfile_scan_prev, tuple_record_p, peek);
}
/*
* qfile_end_scan_fix () -
* return:
* s_id(in/out) : Scan identifier
*
* Note: End a scan fix operation by freeing the current scan page pointer.
*/
void
qfile_end_scan_fix (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
if (scan_id_p->position == S_ON && scan_id_p->curr_pgptr)
{
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
}
else
{
scan_id_p->status = S_ENDED;
scan_id_p->position = S_AFTER;
}
}
/*
* qfile_close_scan () -
* return:
* s_id(in) : Scan identifier
*
* Note: The scan identifier is closed and allocated areas and page
* buffers are freed.
*/
void
qfile_close_scan (THREAD_ENTRY * thread_p, QFILE_LIST_SCAN_ID * scan_id_p)
{
if (scan_id_p->status == S_CLOSED)
{
return;
}
if ((scan_id_p->position == S_ON || (scan_id_p->position == S_AFTER && scan_id_p->keep_page_on_finish))
&& scan_id_p->curr_pgptr)
{
qmgr_free_old_page_and_init (thread_p, scan_id_p->curr_pgptr, scan_id_p->list_id.tfile_vfid);
}
if (scan_id_p->tplrec.tpl != NULL)
{
db_private_free_and_init (thread_p, scan_id_p->tplrec.tpl);
scan_id_p->tplrec.size = 0;
}
qfile_clear_list_id (&scan_id_p->list_id);
scan_id_p->status = S_CLOSED;
}
#if defined(SERVER_MODE)
/*
* qfile_compare_tran_id () -
* return:
* t1(in) :
* t2(in) :
*/
static int
qfile_compare_tran_id (const void *t1, const void *t2)
{
return *((int *) t1) - *((int *) t2);
}
#endif /* SERVER_MODE */
/*
* qfile_hash_db_value_array () - Hash an array of DB_VALUE (DB_VALUE_ARRAY type)
* return:
* key(in) :
* htsize(in) :
*/
static unsigned int
qfile_hash_db_value_array (const void *key, unsigned int htsize)
{
unsigned int hash = 0;
int i;
const DB_VALUE_ARRAY *array = (DB_VALUE_ARRAY *) key;
const DB_VALUE *val;
if (key != NULL && array->size > 0)
{
for (i = 0, val = array->vals; i < array->size; i++, val++)
{
hash |= mht_valhash (val, htsize);
hash <<= 8;
}
hash |= array->size;
}
return (hash % htsize);
}
/*
* qfile_compare_equal_db_value_array () - Compare two arrays of DB_VALUE
* (DB_VALUE_ARRAY type)
* return:
* key1(in) :
* key2(in) :
*/
static int
qfile_compare_equal_db_value_array (const void *key1, const void *key2)
{
int i;
const DB_VALUE_ARRAY *array1 = (DB_VALUE_ARRAY *) key1, *array2 = (DB_VALUE_ARRAY *) key2;
const DB_VALUE *val1, *val2;
if (key1 == key2 || (array1->size == 0 && array2->size == 0))
{
return true;
}
if (key1 == NULL || key2 == NULL || array1->size != array2->size)
{
return false;
}
for (i = 0, val1 = array1->vals, val2 = array2->vals; i < array1->size; i += 3, val1 += 3, val2 += 3)
{
if (mht_compare_dbvalues_are_equal (val1, val2) == false)
{
return false;
}
}
for (i = 1, val1 = array1->vals + 1, val2 = array2->vals + 1; i < array1->size; i += 3, val1 += 3, val2 += 3)
{
if (mht_compare_dbvalues_are_equal (val1, val2) == false)
{
return false;
}
}
for (i = 2, val1 = array1->vals + 2, val2 = array2->vals + 2; i < array1->size; i += 3, val1 += 3, val2 += 3)
{
if (mht_compare_dbvalues_are_equal (val1, val2) == false)
{
return false;
}
}
return true;
}
/*
* qfile_initialize_list_cache () - Initialize list cache
* return:
*/
int
qfile_initialize_list_cache (THREAD_ENTRY * thread_p)
{
unsigned int i;
int n;
QFILE_POOLED_LIST_CACHE_ENTRY *pent;
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return NO_ERROR;
}
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return ER_FAILED;
}
/* array of memory hash tables for list cache; pool for list_ht of QFILE_LIST_CACHE_ENTRY */
if (qfile_List_cache.list_hts)
{
/* if the hash table already exist, clear it out */
for (i = 0; i < qfile_List_cache.n_hts; i++)
{
(void) mht_map_no_key (thread_p, qfile_List_cache.list_hts[i], qfile_free_list_cache_entry,
qfile_List_cache.list_hts[i]);
(void) mht_clear (qfile_List_cache.list_hts[i], NULL, NULL);
qfile_List_cache.free_ht_list[i] = i + 1;
}
qfile_List_cache.free_ht_list[i - 1] = -1;
}
else
{
/* create */
qfile_List_cache.n_hts = prm_get_integer_value (PRM_ID_XASL_CACHE_MAX_ENTRIES) + 10;
qfile_List_cache.list_hts = (MHT_TABLE **) calloc (qfile_List_cache.n_hts, sizeof (MHT_TABLE *));
qfile_List_cache.free_ht_list = (int *) calloc (qfile_List_cache.n_hts, sizeof (int));
if (qfile_List_cache.list_hts == NULL)
{
goto error;
}
for (i = 0; i < qfile_List_cache.n_hts; i++)
{
qfile_List_cache.list_hts[i] =
mht_create ("list file cache (DB_VALUE list)", qfile_List_cache.n_hts,
qfile_hash_db_value_array, qfile_compare_equal_db_value_array);
qfile_List_cache.free_ht_list[i] = i + 1;
if (qfile_List_cache.list_hts[i] == NULL)
{
goto error;
}
}
qfile_List_cache.free_ht_list[i - 1] = -1;
}
qfile_List_cache.n_entries = 0;
qfile_List_cache.n_pages = 0;
qfile_List_cache.lookup_counter = 0;
qfile_List_cache.hit_counter = 0;
qfile_List_cache.miss_counter = 0;
qfile_List_cache.full_counter = 0;
/* list cache entry pool */
if (qfile_List_cache_entry_pool.pool)
{
free_and_init (qfile_List_cache_entry_pool.pool);
}
qfile_List_cache_entry_pool.n_entries = prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_ENTRIES) + 10;
qfile_List_cache_entry_pool.pool =
(QFILE_POOLED_LIST_CACHE_ENTRY *) calloc (qfile_List_cache_entry_pool.n_entries,
sizeof (QFILE_POOLED_LIST_CACHE_ENTRY));
if (qfile_List_cache_entry_pool.pool == NULL)
{
goto error;
}
qfile_List_cache_entry_pool.free_list = 0;
for (pent = qfile_List_cache_entry_pool.pool, n = 0; pent && n < qfile_List_cache_entry_pool.n_entries - 1;
pent++, n++)
{
pent->s.next = n + 1;
}
if (pent != NULL)
{
pent->s.next = -1;
}
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return NO_ERROR;
error:
if (qfile_List_cache_entry_pool.pool)
{
free_and_init (qfile_List_cache_entry_pool.pool);
}
qfile_List_cache_entry_pool.n_entries = 0;
qfile_List_cache_entry_pool.free_list = -1;
if (qfile_List_cache.list_hts)
{
for (i = 0; i < qfile_List_cache.n_hts && qfile_List_cache.list_hts[i]; i++)
{
mht_destroy (qfile_List_cache.list_hts[i]);
}
free_and_init (qfile_List_cache.list_hts);
free_and_init (qfile_List_cache.free_ht_list);
}
qfile_List_cache.n_hts = 0;
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return ER_FAILED;
}
/*
* qfile_finalize_list_cache () - Finalize list cache
* return:
*/
int
qfile_finalize_list_cache (THREAD_ENTRY * thread_p)
{
unsigned int i;
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return NO_ERROR;
}
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return ER_FAILED;
}
/* array of memory hash tables for list cache; pool for list_ht of QFILE_LIST_CACHE_ENTRY */
if (qfile_List_cache.list_hts)
{
for (i = 0; i < qfile_List_cache.n_hts; i++)
{
bool invalidate = true;
(void) mht_map_no_key (thread_p, qfile_List_cache.list_hts[i], qfile_end_use_of_list_cache_entry_local,
&invalidate);
mht_destroy (qfile_List_cache.list_hts[i]);
}
free_and_init (qfile_List_cache.list_hts);
}
if (qfile_List_cache.free_ht_list)
{
free_and_init (qfile_List_cache.free_ht_list);
}
/* list cache entry pool */
if (qfile_List_cache_entry_pool.pool)
{
free_and_init (qfile_List_cache_entry_pool.pool);
}
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return NO_ERROR;
}
/*
* qfile_clear_list_cache () - Clear out list cache hash table
* return:
* list_ht_no(in) :
*/
int
qfile_clear_list_cache (THREAD_ENTRY * thread_p, XASL_CACHE_ENTRY * xcache_entry, bool invalidate)
{
int rc;
int cnt;
int list_ht_no;
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
goto end;
}
if (QFILE_IS_LIST_CACHE_DISABLED || xcache_entry->list_ht_no < 0)
{
goto end;
}
if (qfile_List_cache.n_hts == 0)
{
goto end;
}
list_ht_no = xcache_entry->list_ht_no;
if (qfile_get_list_cache_number_of_entries (list_ht_no) == 0)
{
/* if no entries, to invalidate free the entry here */
if (invalidate)
{
xcache_entry->list_ht_no = -1;
qcache_free_ht_no (thread_p, list_ht_no);
}
goto end;
}
cnt = 0;
do
{
rc =
mht_map_no_key (thread_p, qfile_List_cache.list_hts[list_ht_no], qfile_end_use_of_list_cache_entry_local,
&invalidate);
if (rc != NO_ERROR)
{
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
thread_sleep (10); /* 10 msec */
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return ER_FAILED;
}
}
}
while (rc != NO_ERROR && cnt++ < 10);
if (rc != NO_ERROR)
{
/* unhappy condition; if this happens, memory leak will occurrs */
er_log_debug (ARG_FILE_LINE, "ls_clear_list_cache: failed to delete all entries\n");
}
end:
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return NO_ERROR;
}
/*
* qfile_allocate_list_cache_entry () - Allocate the entry or get one from the pool
* return:
* req_size(in) :
*/
static QFILE_LIST_CACHE_ENTRY *
qfile_allocate_list_cache_entry (int req_size)
{
/* this function should be called within CSECT_QPROC_LIST_CACHE */
QFILE_POOLED_LIST_CACHE_ENTRY *pent = NULL;
if (req_size > RESERVED_SIZE_FOR_LIST_CACHE_ENTRY || qfile_List_cache_entry_pool.free_list == -1)
{
/* malloc from the heap if required memory size is bigger than reserved, or the pool is exhausted */
pent = (QFILE_POOLED_LIST_CACHE_ENTRY *) malloc (req_size + ADDITION_FOR_POOLED_LIST_CACHE_ENTRY);
if (pent != NULL)
{
/* mark as to be freed rather than returning back to the pool */
pent->s.next = -2;
}
else
{
er_log_debug (ARG_FILE_LINE, "ls_alloc_list_cache_ent: allocation failed\n");
}
}
else
{
/* get one from the pool */
assert ((qfile_List_cache_entry_pool.free_list <= qfile_List_cache_entry_pool.n_entries)
&& (qfile_List_cache_entry_pool.free_list >= 0));
pent = &qfile_List_cache_entry_pool.pool[qfile_List_cache_entry_pool.free_list];
assert (pent->s.next <= qfile_List_cache_entry_pool.n_entries && pent->s.next >= -1);
qfile_List_cache_entry_pool.free_list = pent->s.next;
pent->s.next = -1;
}
/* initialize */
if (pent)
{
assert (sizeof (pent->s.entry) >= sizeof (QFILE_LIST_CACHE_ENTRY));
(void) memset ((void *) &pent->s.entry, 0, sizeof (QFILE_LIST_CACHE_ENTRY));
}
else
{
return NULL;
}
return &pent->s.entry;
}
/*
* qfile_free_list_cache_entry () - Remove the entry from the hash and free it
* Can be used by mht_map_no_key() function
* return:
* data(in) :
* args(in) :
*/
static int
qfile_free_list_cache_entry (THREAD_ENTRY * thread_p, void *data, void *args)
{
/* this function should be called within CSECT_QPROC_LIST_CACHE */
QFILE_POOLED_LIST_CACHE_ENTRY *pent;
QFILE_LIST_CACHE_ENTRY *lent = (QFILE_LIST_CACHE_ENTRY *) data;
HL_HEAPID old_pri_heap_id;
int i;
#if !defined (NDEBUG)
int idx;
#endif
if (data == NULL)
{
return ER_FAILED;
}
/*
* Clear out parameter values. (DB_VALUE containers)
* Remind that the parameter values are cloned in global heap context(0)
*/
old_pri_heap_id = db_change_private_heap (thread_p, 0);
for (i = 0; i < lent->param_values.size; i++)
{
(void) pr_clear_value (&lent->param_values.vals[i]);
}
(void) db_change_private_heap (thread_p, old_pri_heap_id);
/* if this entry is from the pool return it, else free it */
pent = POOLED_LIST_CACHE_ENTRY_FROM_LIST_CACHE_ENTRY (lent);
if (pent->s.next == -2)
{
free_and_init (pent);
}
else
{
/* return it back to the pool */
(void) memset (&pent->s.entry, 0, sizeof (QFILE_LIST_CACHE_ENTRY));
pent->s.next = qfile_List_cache_entry_pool.free_list;
#if !defined (NDEBUG)
idx = (int) (pent - qfile_List_cache_entry_pool.pool);
assert (idx <= qfile_List_cache_entry_pool.n_entries && idx >= 0);
#endif
qfile_List_cache_entry_pool.free_list = CAST_BUFLEN (pent - qfile_List_cache_entry_pool.pool);
}
return NO_ERROR;
}
/*
* qfile_print_list_cache_entry () - Print the entry
* Will be used by mht_dump() function
* return:
* fp(in) :
* key(in) :
* data(in) :
* args(in) :
*/
static int
qfile_print_list_cache_entry (THREAD_ENTRY * thread_p, FILE * fp, const void *key, void *data, void *args)
{
QFILE_LIST_CACHE_ENTRY *ent = (QFILE_LIST_CACHE_ENTRY *) data;
int i;
char str[20];
TP_DOMAIN **d;
time_t tmp_time;
struct tm *c_time_struct, tm_val;
if (!ent)
{
return false;
}
if (fp == NULL)
{
fp = stdout;
}
fprintf (fp, "LIST_CACHE_ENTRY (%p) {\n", data);
fprintf (fp, " param_values = [");
for (i = 0; i < ent->param_values.size; i++)
{
fprintf (fp, " ");
db_fprint_value (fp, &ent->param_values.vals[i]);
}
fprintf (fp, " ]\n");
fprintf (fp, " list_id = { type_list { %d", ent->list_id.type_list.type_cnt);
for (i = 0, d = ent->list_id.type_list.domp; i < ent->list_id.type_list.type_cnt && d && *d; i++, d++)
{
fprintf (fp, " %s/%d", (*d)->type->name, TP_DOMAIN_TYPE ((*d)));
}
fprintf (fp,
" } tuple_cnt %lld page_cnt %d first_vpid { %d %d } last_vpid { %d %d } lasttpl_len %d query_id %lld "
" temp_vfid { %d %d } }\n", (long long) ent->list_id.tuple_cnt, ent->list_id.page_cnt,
ent->list_id.first_vpid.pageid, ent->list_id.first_vpid.volid, ent->list_id.last_vpid.pageid,
ent->list_id.last_vpid.volid, ent->list_id.lasttpl_len, (long long) ent->list_id.query_id,
ent->list_id.temp_vfid.fileid, ent->list_id.temp_vfid.volid);
#if defined(SERVER_MODE)
fprintf (fp, " tran_isolation = %d\n", ent->tran_isolation);
fprintf (fp, " tran_index_array = [");
for (i = 0; (unsigned int) i < ent->last_ta_idx; i++)
{
fprintf (fp, " %d", ent->tran_index_array[i]);
}
fprintf (fp, " ]\n");
fprintf (fp, " last_ta_idx = %lld\n", (long long) ent->last_ta_idx);
#endif /* SERVER_MODE */
fprintf (fp, " query_string = %s\n", ent->query_string);
tmp_time = ent->time_created.tv_sec;
c_time_struct = localtime_r (&tmp_time, &tm_val);
if (c_time_struct == NULL)
{
fprintf (fp, " ent->time_created.tv_sec is invalid (%ld)\n", ent->time_last_used.tv_sec);
}
else
{
(void) strftime (str, sizeof (str), "%x %X", c_time_struct);
fprintf (fp, " time_created = %s.%d\n", str, (int) ent->time_created.tv_usec);
}
tmp_time = ent->time_last_used.tv_sec;
c_time_struct = localtime_r (&tmp_time, &tm_val);
if (c_time_struct == NULL)
{
fprintf (fp, " ent->time_last_used.tv_sec is invalid (%ld)\n", ent->time_last_used.tv_sec);
}
else
{
(void) strftime (str, sizeof (str), "%x %X", c_time_struct);
fprintf (fp, " time_last_used = %s.%d\n", str, (int) ent->time_last_used.tv_usec);
fprintf (fp, " ref_count = %d\n", ent->ref_count);
fprintf (fp, " deletion_marker = %s\n", (ent->deletion_marker) ? "true" : "false");
fprintf (fp, " invalidate = %s\n", (ent->invalidate) ? "true" : "false");
fprintf (fp, "}\n");
}
return true;
}
/*
* qfile_dump_list_cache_internal () -
* return:
* fp(in) :
*/
int
qfile_dump_list_cache_internal (THREAD_ENTRY * thread_p, FILE * fp)
{
unsigned int i;
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return ER_FAILED;
}
if (!fp)
{
fp = stdout;
}
fprintf (fp,
"LIST_CACHE {\n n_hts %d\n n_entries %d n_pages %d\n"
" lookup_counter %d\n hit_counter %d\n miss_counter %d\n full_counter %d\n}\n",
qfile_List_cache.n_hts, qfile_List_cache.n_entries, qfile_List_cache.n_pages,
qfile_List_cache.lookup_counter, qfile_List_cache.hit_counter, qfile_List_cache.miss_counter,
qfile_List_cache.full_counter);
for (i = 0; i < qfile_List_cache.n_hts; i++)
{
if (mht_count (qfile_List_cache.list_hts[i]) > 0)
{
fprintf (fp, "\nlist_hts[%d] %p\n", i, (void *) qfile_List_cache.list_hts[i]);
(void) mht_dump (thread_p, fp, qfile_List_cache.list_hts[i], true, qfile_print_list_cache_entry, NULL);
}
}
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return NO_ERROR;
}
#if defined (CUBRID_DEBUG)
/*
* qfile_dump_list_cache () -
* return:
* fname(in) :
*/
int
qfile_dump_list_cache (THREAD_ENTRY * thread_p, const char *fname)
{
int rc;
FILE *fp;
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return ER_FAILED;
}
if (qfile_List_cache.n_hts == 0 || !qfile_List_cache.list_hts)
{
return ER_FAILED;
}
fp = (fname) ? fopen (fname, "a") : stdout;
if (!fp)
{
fp = stdout;
}
rc = qfile_dump_list_cache_internal (thread_p, fp);
if (fp != stdout)
{
fclose (fp);
}
return rc;
}
#endif
/*
* qfile_delete_list_cache_entry () - Delete a list cache entry
* Can be used by mht_map_no_key() function
* return:
* data(in/out) :
* args(in) :
*/
static int
qfile_delete_list_cache_entry (THREAD_ENTRY * thread_p, void *data)
{
/* this function should be called within CSECT_QPROC_LIST_CACHE */
QFILE_LIST_CACHE_ENTRY *lent = (QFILE_LIST_CACHE_ENTRY *) data;
int error_code = ER_FAILED;
bool invalidate;
int ht_no;
if (data == NULL || lent->list_ht_no < 0)
{
return ER_FAILED;
}
invalidate = lent->invalidate;
ht_no = lent->list_ht_no;
/* update counter */
qfile_List_cache.n_entries--;
qfile_List_cache.n_pages -= lent->list_id.page_cnt;
/* remove the entry from the hash table */
if (mht_rem2 (qfile_List_cache.list_hts[lent->list_ht_no], &lent->param_values, lent, NULL, NULL) != NO_ERROR)
{
if (!lent->deletion_marker)
{
char *s = NULL;
if (lent->param_values.size > 0)
{
s = pr_valstring (&lent->param_values.vals[0]);
}
er_log_debug (ARG_FILE_LINE,
"ls_delete_list_cache_ent: mht_rem failed for param_values { %d %s ...}\n",
lent->param_values.size, s ? s : "(null)");
if (s)
{
db_private_free (thread_p, s);
}
}
}
/* destroy the temp file of XASL_ID */
if (!VFID_ISNULL (&lent->list_id.temp_vfid)
&& file_temp_retire_preserved (thread_p, &lent->list_id.temp_vfid) != NO_ERROR)
{
er_log_debug (ARG_FILE_LINE, "ls_delete_list_cache_ent: fl_destroy failed for vfid { %d %d }\n",
lent->list_id.temp_vfid.fileid, lent->list_id.temp_vfid.volid);
}
/* clear list_id */
qfile_update_qlist_count (thread_p, &lent->list_id, 1);
qfile_clear_list_id (&lent->list_id);
/* to check if it's the last list cache entry of the hash table */
if (invalidate && qfile_get_list_cache_number_of_entries (ht_no) == 0)
{
/* this hash table has no entries and invalidated
* it needs to free
*/
lent->xcache_entry->list_ht_no = -1;
qcache_free_ht_no (thread_p, ht_no);
}
error_code = qfile_free_list_cache_entry (thread_p, lent, NULL);
return error_code;
}
/*
* qfile_end_use_of_list_cache_entry_local ()
* return:
* data(in) : a list cached entry
* args(in) : invalidate is true if the xcache entry is erased
*/
static int
qfile_end_use_of_list_cache_entry_local (THREAD_ENTRY * thread_p, void *data, void *args)
{
QFILE_LIST_CACHE_ENTRY *lent = (QFILE_LIST_CACHE_ENTRY *) data;
if (lent == NULL)
{
return ER_FAILED;
}
if (lent->invalidate == false)
{
lent->invalidate = *((bool *) args);
}
return qfile_end_use_of_list_cache_entry (thread_p, lent, true);
}
/*
* qfile_lookup_list_cache_entry () - Lookup the list cache with the parameter
* values (DB_VALUE array) bound to the query
* return:
* list_ht_no(in) :
* params(in) :
*
* Note: Look up the hash table to get the cached result with the parameter
* values as the key.
*/
QFILE_LIST_CACHE_ENTRY *
qfile_lookup_list_cache_entry (THREAD_ENTRY * thread_p, XASL_CACHE_ENTRY * xasl, const DB_VALUE_ARRAY * params,
bool * result_cached)
{
QFILE_LIST_CACHE_ENTRY *lent = NULL;
#if defined(SERVER_MODE)
int tran_index;
TRAN_ISOLATION tran_isolation;
bool new_tran = true;
#if defined(WINDOWS)
unsigned int num_elements;
#else
size_t num_elements;
#endif
#endif /* SERVER_MODE */
#if defined (SERVER_MODE) && !defined (NDEBUG)
size_t i_idx, num_active_users;
#endif
*result_cached = false;
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return NULL;
}
if (qfile_List_cache.n_hts == 0)
{
return NULL;
}
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return NULL;
}
if (xasl->list_ht_no < 0)
{
if ((xasl->list_ht_no = qcache_get_new_ht_no (thread_p)) < 0)
{
goto end;
}
}
#if defined(SERVER_MODE)
tran_index =
#endif
LOG_FIND_THREAD_TRAN_INDEX (thread_p);
/* look up the hash table with the key */
lent = (QFILE_LIST_CACHE_ENTRY *) mht_get (qfile_List_cache.list_hts[xasl->list_ht_no], params);
qfile_List_cache.lookup_counter++; /* counter */
if (lent)
{
#if defined(SERVER_MODE)
unsigned int i;
#endif
/* check if it is marked to be deleted */
if (lent->deletion_marker)
{
#if defined(SERVER_MODE)
if (lent->last_ta_idx == 0)
#endif
{
qfile_delete_list_cache_entry (thread_p, lent);
}
goto end;
}
*result_cached = true;
#if defined(SERVER_MODE)
/* check if the cache is owned by me */
for (i = 0; i < lent->last_ta_idx; i++)
{
if (lent->tran_index_array[i] == tran_index)
{
new_tran = false;
break;
}
}
/* finally, we found an useful cache entry to reuse */
if (new_tran)
{
/* record my transaction id into the entry and adjust timestamp and reference counter */
lent->uncommitted_marker = true;
if (lent->last_ta_idx < (size_t) MAX_NTRANS)
{
num_elements = (int) lent->last_ta_idx;
(void) lsearch (&tran_index, lent->tran_index_array, &num_elements, sizeof (int), qfile_compare_tran_id);
lent->last_ta_idx = num_elements;
}
#if !defined (NDEBUG)
for (i_idx = 0, num_active_users = 0; i_idx < lent->last_ta_idx; i_idx++)
{
if (lent->tran_index_array[i_idx] > 0)
{
num_active_users++;
}
}
assert (lent->last_ta_idx == num_active_users);
#endif
}
#endif /* SERVER_MODE */
(void) gettimeofday (&lent->time_last_used, NULL);
lent->ref_count++;
}
end:
if (*result_cached)
{
qfile_List_cache.hit_counter++; /* counter */
}
else
{
qfile_List_cache.miss_counter++; /* counter */
}
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
if (*result_cached)
{
return lent;
}
return NULL;
}
static bool
qfile_is_early_time (struct timeval *a, struct timeval *b)
{
if (a->tv_sec == b->tv_sec)
{
return a->tv_usec < b->tv_usec;
}
else
{
return a->tv_sec < b->tv_sec;
}
}
/*
* qfile_update_list_cache_entry () - Update list cache entry if exist or create new
* one
* return:
* list_ht_no_ptr(in/out) :
* params(in) :
* list_id(in) :
* query_string(in) :
*
* Note: Put the query result into the proper hash table with the key of
* the parameter values (DB_VALUE array) and the data of LIST ID.
* If there already exists the entry with the same key, update its data.
* As a side effect, the given 'list_hash_no' will be change if it was -1.
*/
QFILE_LIST_CACHE_ENTRY *
qfile_update_list_cache_entry (THREAD_ENTRY * thread_p, int list_ht_no, const DB_VALUE_ARRAY * params,
const QFILE_LIST_ID * list_id, XASL_CACHE_ENTRY * xasl)
{
QFILE_LIST_CACHE_ENTRY *lent;
MHT_TABLE *ht;
#if defined(SERVER_MODE)
int tran_index;
TRAN_ISOLATION tran_isolation;
#if defined(WINDOWS)
unsigned int num_elements;
#else
size_t num_elements;
#endif
#if !defined (NDEBUG)
size_t i_idx, num_active_users;
#endif
#endif /* SERVER_MODE */
HL_HEAPID old_pri_heap_id;
int i;
int alloc_size;
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return NULL;
}
if (qfile_List_cache.n_hts == 0)
{
return NULL;
}
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return NULL;
}
#if defined(SERVER_MODE)
tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
tran_isolation = logtb_find_isolation (tran_index);
#endif /* SERVER_MODE */
/*
* The other competing thread which is running the same query
* already updated this entry after that this and the thread had failed
* to find the query in the cache.
* The other case is that the query could be marked not to look up
* the cache but update the cache with its result.
* Then, try to delete the previous one and insert new one.
* If fail to delete, leave the cache without touch.
*/
ht = qfile_List_cache.list_hts[list_ht_no];
do
{
/* check again whether the entry is in the cache */
lent = (QFILE_LIST_CACHE_ENTRY *) mht_get (ht, params);
if (lent == NULL)
{
break;
}
#if defined(SERVER_MODE)
/* check in-use by other transaction */
if (lent->last_ta_idx > 0)
{
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return lent;
}
/* the entry that is in the cache is same with mine; do not duplicate the cache entry */
/* record my transaction id into the entry and adjust timestamp and reference counter */
if (lent->last_ta_idx < (size_t) MAX_NTRANS)
{
num_elements = (int) lent->last_ta_idx;
(void) lsearch (&tran_index, lent->tran_index_array, &num_elements, sizeof (int), qfile_compare_tran_id);
lent->last_ta_idx = num_elements;
}
#if !defined (NDEBUG)
for (i_idx = 0, num_active_users = 0; i_idx < lent->last_ta_idx; i_idx++)
{
if (lent->tran_index_array[i_idx] > 0)
{
num_active_users++;
}
}
assert (lent->last_ta_idx == num_active_users);
#endif
(void) gettimeofday (&lent->time_last_used, NULL);
lent->ref_count++;
#endif /* SERVER_MODE */
}
while (0);
/* return with the one from the cache */
if (lent != NULL)
{
goto end;
}
#if defined(SERVER_MODE)
/* check the number of list cache entries */
if (qfile_List_cache.n_entries >= prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_ENTRIES)
|| qfile_List_cache.n_pages >= prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_PAGES))
{
if (qfile_list_cache_cleanup (thread_p) != NO_ERROR)
{
goto end;
}
}
#endif
/* make new QFILE_LIST_CACHE_ENTRY */
/* get new entry from the QFILE_LIST_CACHE_ENTRY_POOL */
alloc_size = qfile_get_list_cache_entry_size_for_allocate (params->size);
lent = qfile_allocate_list_cache_entry (alloc_size);
if (lent == NULL)
{
goto end;
}
lent->list_ht_no = list_ht_no;
#if defined(SERVER_MODE)
lent->uncommitted_marker = true;
lent->tran_isolation = tran_isolation;
lent->last_ta_idx = 0;
lent->tran_index_array =
(int *) memset (qfile_get_list_cache_entry_tran_index_array (lent), 0, MAX_NTRANS * sizeof (int));
#endif /* SERVER_MODE */
lent->param_values.size = params->size;
lent->param_values.vals = qfile_get_list_cache_entry_param_values (lent);
/*
* Copy parameter values. (DB_VALUE containers)
* Changing private heap to the global one (0, malloc/free) is
* needed because cloned db values last beyond request processing time
* boundary.
*/
old_pri_heap_id = db_change_private_heap (thread_p, 0);
for (i = 0; i < lent->param_values.size; i++)
{
/* (void) pr_clear_value(&(lent->param_values.vals[i])); */
(void) pr_clone_value (¶ms->vals[i], &lent->param_values.vals[i]);
}
(void) db_change_private_heap (thread_p, old_pri_heap_id);
/* copy the QFILE_LIST_ID */
if (qfile_copy_list_id (&lent->list_id, list_id, false, QFILE_PROHIBIT_DEPENDENT) != NO_ERROR)
{
qfile_delete_list_cache_entry (thread_p, lent);
lent = NULL;
goto end;
}
lent->list_id.tfile_vfid = NULL;
lent->query_string = xasl->sql_info.sql_hash_text;
(void) gettimeofday (&lent->time_created, NULL);
(void) gettimeofday (&lent->time_last_used, NULL);
lent->ref_count = 0;
lent->deletion_marker = false;
lent->invalidate = false;
lent->xcache_entry = xasl;
/* record my transaction id into the entry */
#if defined(SERVER_MODE)
if (lent->last_ta_idx < (size_t) MAX_NTRANS)
{
num_elements = (int) lent->last_ta_idx;
(void) lsearch (&tran_index, lent->tran_index_array, &num_elements, sizeof (int), qfile_compare_tran_id);
lent->last_ta_idx = num_elements;
}
#if !defined (NDEBUG)
for (i_idx = 0, num_active_users = 0; i_idx < lent->last_ta_idx; i_idx++)
{
if (lent->tran_index_array[i_idx] > 0)
{
num_active_users++;
}
}
assert (lent->last_ta_idx == num_active_users);
#endif
#endif /* SERVER_MODE */
/* insert (or update) the entry into the hash table */
if (mht_put_new (ht, &lent->param_values, lent) == NULL)
{
char *s;
s = ((lent->param_values.size > 0) ? pr_valstring (&lent->param_values.vals[0]) : NULL);
er_log_debug (ARG_FILE_LINE, "ls_update_list_cache_ent: mht_rem failed for param_values { %d %s ...}\n",
lent->param_values.size, s ? s : "(null)");
if (s)
{
db_private_free (thread_p, s);
}
qfile_delete_list_cache_entry (thread_p, lent);
lent = NULL;
goto end;
}
/* update counter */
qfile_List_cache.n_entries++;
qfile_List_cache.n_pages += lent->list_id.page_cnt;
end:
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return lent;
}
/*
* qfile_end_use_of_list_cache_entry () - End use of list cache entry
* return:
* lent(in/out) :
* marker(in) :
*/
int
qfile_end_use_of_list_cache_entry (THREAD_ENTRY * thread_p, QFILE_LIST_CACHE_ENTRY * lent, bool marker)
{
#if defined(SERVER_MODE)
int tran_index;
int *p, *r;
#if defined(WINDOWS)
unsigned int num_elements;
#else
size_t num_elements;
#endif
#endif /* SERVER_MODE */
#if defined (SERVER_MODE) && !defined (NDEBUG)
size_t i_idx, num_active_users;
#endif
if (QFILE_IS_LIST_CACHE_DISABLED)
{
return ER_FAILED;
}
if (lent == NULL || qfile_List_cache.n_hts == 0)
{
return ER_FAILED;
}
if (csect_enter (thread_p, CSECT_QPROC_LIST_CACHE, INF_WAIT) != NO_ERROR)
{
return ER_FAILED;
}
#if defined(SERVER_MODE)
/* remove my transaction id from the entry and do compaction */
tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
r = &lent->tran_index_array[lent->last_ta_idx];
do
{
/* find my tran_id */
num_elements = (int) lent->last_ta_idx;
p = (int *) lfind (&tran_index, lent->tran_index_array, &num_elements, sizeof (int), qfile_compare_tran_id);
lent->last_ta_idx = num_elements;
if (p)
{
*p = 0;
/* do compaction */
if (p + 1 < r)
{
(void) memcpy (p, p + 1, sizeof (int) * (r - p - 1));
}
lent->last_ta_idx--;
r--;
}
}
while (p && p < r);
if (lent->last_ta_idx == 0)
{
lent->uncommitted_marker = false;
}
#if !defined (NDEBUG)
for (i_idx = 0, num_active_users = 0; i_idx < lent->last_ta_idx; i_idx++)
{
if (lent->tran_index_array[i_idx] > 0)
{
num_active_users++;
}
}
assert (lent->last_ta_idx == num_active_users);
#endif
#endif /* SERVER_MODE */
/* if this entry will be deleted */
#if defined(SERVER_MODE)
if (lent->last_ta_idx == 0)
#endif
{
/* to check if it's the last transaction using the lent */
if (marker || lent->deletion_marker)
{
qfile_delete_list_cache_entry (thread_p, lent);
}
}
#if defined(SERVER_MODE)
else
{
/* to avoid resetting the deletion_marker
* that has already been set by other transaction */
if (lent->deletion_marker == false)
{
lent->deletion_marker = marker;
}
}
#endif
csect_exit (thread_p, CSECT_QPROC_LIST_CACHE);
return NO_ERROR;
}
static int
qfile_get_list_cache_entry_size_for_allocate (int nparam)
{
#if defined(SERVER_MODE)
return sizeof (QFILE_LIST_CACHE_ENTRY) /* space for structure */
+ sizeof (int) * MAX_NTRANS /* space for tran_index_array */
+ sizeof (DB_VALUE) * nparam; /* space for param_values.vals */
#else /* SERVER_MODE */
return sizeof (QFILE_LIST_CACHE_ENTRY) /* space for structure */
+ sizeof (DB_VALUE) * nparam; /* space for param_values.vals */
#endif /* SERVER_MODE */
}
#if defined(SERVER_MODE)
static int *
qfile_get_list_cache_entry_tran_index_array (QFILE_LIST_CACHE_ENTRY * ent)
{
return (int *) ((char *) ent + sizeof (QFILE_LIST_CACHE_ENTRY));
}
#endif /* SERVER_MODE */
static DB_VALUE *
qfile_get_list_cache_entry_param_values (QFILE_LIST_CACHE_ENTRY * ent)
{
#if defined(SERVER_MODE)
return (DB_VALUE *) ((char *) ent + sizeof (QFILE_LIST_CACHE_ENTRY) + sizeof (TRANID) * MAX_NTRANS);
#else /* SERVER_MODE */
return (DB_VALUE *) ((char *) ent + sizeof (QFILE_LIST_CACHE_ENTRY));
#endif /* SERVER_MODE */
}
/*
* qfile_add_tuple_get_pos_in_list () - The given tuple is added to the end of
* the list file. The position in the list file is returned.
* return:
* list_id(in):
* tuple(in):
* tuple_pos(out):
*/
int
qfile_add_tuple_get_pos_in_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, QFILE_TUPLE tuple,
QFILE_TUPLE_POSITION * tuple_pos)
{
PAGE_PTR cur_page_p, new_page_p, prev_page_p;
int tuple_length;
char *page_p, *tuple_p;
int offset, tuple_page_size;
if (list_id_p == NULL)
{
return ER_FAILED;
}
QFILE_CHECK_LIST_FILE_IS_CLOSED (list_id_p);
cur_page_p = list_id_p->last_pgptr;
tuple_length = QFILE_GET_TUPLE_LENGTH (tuple);
if (qfile_allocate_new_page_if_need (thread_p, list_id_p, &cur_page_p, tuple_length, false) != NO_ERROR)
{
return ER_FAILED;
}
page_p = (char *) cur_page_p + list_id_p->last_offset;
tuple_page_size = MIN (tuple_length, qfile_Max_tuple_page_size);
memcpy (page_p, tuple, tuple_page_size);
/* get the information for our QFILE_TUPLE_POSITION */
if (tuple_pos)
{
tuple_pos->offset = list_id_p->last_offset;
tuple_pos->tpl = page_p;
tuple_pos->tplno = list_id_p->tuple_cnt;
tuple_pos->vpid = list_id_p->last_vpid;
}
qfile_add_tuple_to_list_id (list_id_p, page_p, tuple_length, tuple_page_size);
prev_page_p = cur_page_p;
for (offset = tuple_page_size, tuple_p = (char *) tuple + offset; offset < tuple_length;
offset += tuple_page_size, tuple_p += tuple_page_size)
{
new_page_p =
qfile_allocate_new_ovf_page (thread_p, list_id_p, cur_page_p, prev_page_p, tuple_length, offset,
&tuple_page_size);
if (new_page_p == NULL)
{
if (prev_page_p != cur_page_p)
{
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
return ER_FAILED;
}
memcpy ((char *) new_page_p + QFILE_PAGE_HEADER_SIZE, tuple_p, tuple_page_size);
prev_page_p = new_page_p;
}
if (prev_page_p != cur_page_p)
{
QFILE_PUT_OVERFLOW_VPID_NULL (prev_page_p);
qfile_set_dirty_page (thread_p, prev_page_p, FREE, list_id_p->tfile_vfid);
}
qfile_set_dirty_page (thread_p, cur_page_p, DONT_FREE, list_id_p->tfile_vfid);
return NO_ERROR;
}
/*
* qfile_has_next_page() - returns whether the page has the next page or not.
* If false, that means the page is the last page of the list file.
* return: true/false
* page_p(in):
*/
bool
qfile_has_next_page (PAGE_PTR page_p)
{
return (QFILE_GET_NEXT_PAGE_ID (page_p) != NULL_PAGEID && QFILE_GET_NEXT_PAGE_ID (page_p) != NULL_PAGEID_IN_PROGRESS);
}
/*
* qfile_update_domains_on_type_list() - Update domain pointers belongs to
* type list of a given list file
* return: error code
*
*/
int
qfile_update_domains_on_type_list (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, valptr_list_node * valptr_list_p)
{
REGU_VARIABLE_LIST reg_var_p;
int i, count = 0;
assert (list_id_p != NULL);
list_id_p->is_domain_resolved = true;
reg_var_p = valptr_list_p->valptrp;
for (i = 0; i < valptr_list_p->valptr_cnt; i++, reg_var_p = reg_var_p->next)
{
if (REGU_VARIABLE_IS_FLAGED (®_var_p->value, REGU_VARIABLE_HIDDEN_COLUMN))
{
continue;
}
if (count >= list_id_p->type_list.type_cnt)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
goto exit_on_error;
}
if (TP_DOMAIN_TYPE (list_id_p->type_list.domp[count]) == DB_TYPE_VARIABLE)
{
if (TP_DOMAIN_TYPE (reg_var_p->value.domain) == DB_TYPE_VARIABLE)
{
/* In this case, we cannot resolve the value's domain. We will try to do for the next tuple. */
if (list_id_p->is_domain_resolved)
{
list_id_p->is_domain_resolved = false;
}
}
else
{
list_id_p->type_list.domp[count] = reg_var_p->value.domain;
}
}
if (list_id_p->type_list.domp[count]->collation_flag != TP_DOMAIN_COLL_NORMAL)
{
if (reg_var_p->value.domain->collation_flag != TP_DOMAIN_COLL_NORMAL)
{
/* In this case, we cannot resolve the value's domain. We will try to do for the next tuple. */
if (list_id_p->is_domain_resolved)
{
list_id_p->is_domain_resolved = false;
}
}
else
{
list_id_p->type_list.domp[count] = reg_var_p->value.domain;
}
}
count++;
}
/* The number of columns should be same. */
if (count != list_id_p->type_list.type_cnt)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
goto exit_on_error;
}
return NO_ERROR;
exit_on_error:
list_id_p->is_domain_resolved = false;
return ER_FAILED;
}
/*
* qfile_set_tuple_column_value() - Set column value for a fixed size column
* of a tuple inside a list file (in-place)
* return: error code
* list_id_p(in): list file id
* curr_page_p(in): use this only with curr_pgptr of your open scanner!
* vpid_p(in): real VPID
* tuple_p(in): pointer to the tuple inside the page
* col_num(in): column number
* value_p(in): new column value
* domain_p(in): column domain
*
* Note: Caller is responsible to ensure that the size of the column data
* is fixed!
*/
int
qfile_set_tuple_column_value (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id_p, PAGE_PTR curr_page_p, VPID * vpid_p,
QFILE_TUPLE tuple_p, int col_num, DB_VALUE * value_p, TP_DOMAIN * domain_p)
{
PAGE_PTR page_p;
QFILE_TUPLE_VALUE_FLAG flag;
QFILE_TUPLE_RECORD tuple_rec = { NULL, 0 };
const PR_TYPE *pr_type;
OR_BUF buf;
char *ptr;
int length;
int error = NO_ERROR;
pr_type = domain_p->type;
if (pr_type == NULL)
{
return ER_FAILED;
}
/* get a pointer to the page */
if (curr_page_p != NULL)
{
page_p = curr_page_p;
}
else
{
page_p = qmgr_get_old_page (thread_p, vpid_p, list_id_p->tfile_vfid);
}
if (page_p == NULL)
{
return ER_FAILED;
}
/* locate and update tuple value inside the page or in overflow pages */
if (QFILE_GET_OVERFLOW_PAGE_ID (page_p) == NULL_PAGEID)
{
/* tuple is inside the page, locate column and set the new value */
flag = (QFILE_TUPLE_VALUE_FLAG) qfile_locate_tuple_value (tuple_p, col_num, &ptr, &length);
if (flag == V_BOUND)
{
or_init (&buf, ptr, length);
if (pr_type->data_writeval (&buf, value_p) != NO_ERROR)
{
error = ER_FAILED;
goto cleanup;
}
}
else
{
error = ER_FAILED;
goto cleanup;
}
qfile_set_dirty_page (thread_p, page_p, DONT_FREE, list_id_p->tfile_vfid);
}
else
{
/* tuple is in overflow pages */
if (curr_page_p)
{
/* tuple_p is not a tuple pointer inside the current page, it is a copy made by qfile_scan_list_next(), so
* avoid fetching it twice, and make sure it doesn't get freed at cleanup stage of this function. For
* reference see how qfile_retrieve_tuple() handles overflow pages. */
tuple_rec.tpl = tuple_p;
tuple_rec.size = QFILE_GET_TUPLE_LENGTH (tuple_p);
}
else
{
if (qfile_get_tuple (thread_p, page_p, tuple_p, &tuple_rec, list_id_p) != NO_ERROR)
{
error = ER_FAILED;
goto cleanup;
}
}
/* locate column and set the new value */
flag = (QFILE_TUPLE_VALUE_FLAG) qfile_locate_tuple_value (tuple_rec.tpl, col_num, &ptr, &length);
if (flag == V_BOUND)
{
or_init (&buf, ptr, length);
if (pr_type->data_writeval (&buf, value_p) != NO_ERROR)
{
error = ER_FAILED;
goto cleanup;
}
}
else
{
error = ER_FAILED;
goto cleanup;
}
/* flush the tuple back into overflow pages */
if (qfile_overwrite_tuple (thread_p, page_p, tuple_p, &tuple_rec, list_id_p) != NO_ERROR)
{
error = ER_FAILED;
goto cleanup;
}
qfile_set_dirty_page (thread_p, page_p, DONT_FREE, list_id_p->tfile_vfid);
}
cleanup:
/* free the tuple record if used */
if (tuple_rec.tpl != NULL && tuple_rec.tpl != tuple_p)
{
db_private_free_and_init (NULL, tuple_rec.tpl);
}
/* free the page */
if (page_p != curr_page_p)
{
qmgr_free_old_page_and_init (thread_p, page_p, list_id_p->tfile_vfid);
}
return error;
}
/*
* qfile_overwrite_tuple () - Overwrite a tuple inside a list file with a tuple
* record of the same size
* return: error code
* first_page_p(in): pointer to the first page where the tuple is located
* tuple(in): pointer to the tuple to be overwritten
* tuple_record_p(in): tuple record to overwrite with
* list_id_p(in): list file id
*
* Note: The caller should use responsibly this function!
*/
int
qfile_overwrite_tuple (THREAD_ENTRY * thread_p, PAGE_PTR first_page_p, QFILE_TUPLE tuple,
QFILE_TUPLE_RECORD * tuple_record_p, QFILE_LIST_ID * list_id_p)
{
VPID ovfl_vpid;
char *tuple_p;
int offset;
int tuple_length, tuple_page_size;
int max_tuple_page_size;
PAGE_PTR page_p;
page_p = first_page_p;
tuple_length = QFILE_GET_TUPLE_LENGTH (tuple);
/* sanity check */
if (tuple_length != tuple_record_p->size)
{
assert (false);
return ER_FAILED;
}
tuple_p = (char *) tuple_record_p->tpl;
if (QFILE_GET_OVERFLOW_PAGE_ID (page_p) == NULL_PAGEID)
{
/* tuple is inside the page */
memcpy (tuple, tuple_p, tuple_length);
return NO_ERROR;
}
else
{
/* tuple has overflow pages */
offset = 0;
max_tuple_page_size = qfile_Max_tuple_page_size;
do
{
QFILE_GET_OVERFLOW_VPID (&ovfl_vpid, page_p);
tuple_page_size = MIN (tuple_length - offset, max_tuple_page_size);
memcpy ((char *) page_p + QFILE_PAGE_HEADER_SIZE, tuple_p, tuple_page_size);
tuple_p += tuple_page_size;
offset += tuple_page_size;
if (page_p != first_page_p)
{
qfile_set_dirty_page (thread_p, page_p, FREE, list_id_p->tfile_vfid);
}
if (ovfl_vpid.pageid != NULL_PAGEID)
{
page_p = qmgr_get_old_page (thread_p, &ovfl_vpid, list_id_p->tfile_vfid);
if (page_p == NULL)
{
return ER_FAILED;
}
}
}
while (ovfl_vpid.pageid != NULL_PAGEID);
}
return NO_ERROR;
}
/*
* qfile_compare_with_interpolation_domain () -
* return: compare result
* fp0(in):
* fp1(in):
* subkey(in):
*
* NOTE: median analytic function sort string in different domain
*/
static int
qfile_compare_with_interpolation_domain (char *fp0, char *fp1, SUBKEY_INFO * subkey, SORTKEY_INFO * key_info)
{
int order = 0;
DB_VALUE val0, val1;
OR_BUF buf0, buf1;
TP_DOMAIN *cast_domain = NULL;
TP_DOMAIN_STATUS status = DOMAIN_COMPATIBLE;
int error = NO_ERROR;
char *d0, *d1;
assert (fp0 != NULL && fp1 != NULL && subkey != NULL && key_info != NULL);
db_make_null (&val0);
db_make_null (&val1);
d0 = fp0 + QFILE_TUPLE_VALUE_HEADER_LENGTH;
d1 = fp1 + QFILE_TUPLE_VALUE_HEADER_LENGTH;
if (subkey->cmp_dom == NULL)
{
/* get the proper domain NOTE: col_dom is string type. See qexec_initialize_analytic_state */
pr_clear_value (&val0);
or_init (&buf0, d0, QFILE_GET_TUPLE_VALUE_LENGTH (fp0));
error =
subkey->col_dom->type->data_readval (&buf0, &val0, subkey->col_dom, QFILE_GET_TUPLE_VALUE_LENGTH (fp0), false,
NULL, 0);
if (error != NO_ERROR || DB_IS_NULL (&val0))
{
goto end;
}
error = qdata_update_interpolation_func_value_and_domain (&val0, &val0, &cast_domain);
if (error != NO_ERROR)
{
subkey->cmp_dom = NULL;
goto end;
}
else
{
subkey->cmp_dom = cast_domain;
}
}
/* cast to proper domain, then compare */
pr_clear_value (&val0);
pr_clear_value (&val1);
or_init (&buf0, d0, QFILE_GET_TUPLE_VALUE_LENGTH (fp0));
or_init (&buf1, d1, QFILE_GET_TUPLE_VALUE_LENGTH (fp1));
error =
subkey->col_dom->type->data_readval (&buf0, &val0, subkey->col_dom, QFILE_GET_TUPLE_VALUE_LENGTH (fp0), false,
NULL, 0);
if (error != NO_ERROR)
{
goto end;
}
error =
subkey->col_dom->type->data_readval (&buf1, &val1, subkey->col_dom, QFILE_GET_TUPLE_VALUE_LENGTH (fp1), false,
NULL, 0);
if (error != NO_ERROR)
{
goto end;
}
cast_domain = subkey->cmp_dom;
status = tp_value_cast (&val0, &val0, cast_domain, false);
if (status != DOMAIN_COMPATIBLE)
{
error = ER_ARG_CAN_NOT_BE_CASTED_TO_DESIRED_DOMAIN;
goto end;
}
status = tp_value_cast (&val1, &val1, cast_domain, false);
if (status != DOMAIN_COMPATIBLE)
{
error = ER_ARG_CAN_NOT_BE_CASTED_TO_DESIRED_DOMAIN;
goto end;
}
/* compare */
order = cast_domain->type->cmpval (&val0, &val1, 0, 1, NULL, cast_domain->collation_id);
end:
pr_clear_value (&val0);
pr_clear_value (&val1);
/* record error */
if (error != NO_ERROR)
{
key_info->error = error;
}
return order;
}
void
qfile_update_qlist_count (THREAD_ENTRY * thread_p, const QFILE_LIST_ID * list_p, int inc)
{
#if defined (SERVER_MODE)
if (list_p == NULL || list_p->type_list.type_cnt == 0)
{
return;
}
THREAD_ENTRY *main_thread_p = thread_get_main_thread (thread_p);
main_thread_p->m_qlist_count.fetch_add (inc);
if (prm_get_bool_value (PRM_ID_LOG_QUERY_LISTS))
{
er_print_callstack (ARG_FILE_LINE, "[thread %d with tran index %d] update qlist_count by %d to %d\n",
main_thread_p->index, main_thread_p->tran_index, inc, main_thread_p->m_qlist_count.load ());
}
#endif // SERVER_MODE
}
int
qfile_get_list_cache_number_of_entries (int ht_no)
{
assert_release (ht_no >= 0);
return (qfile_List_cache.list_hts[ht_no]->nentries);
}
bool
qfile_has_no_cache_entries ()
{
return (qfile_List_cache.n_entries == 0);
}
/*
* qfile_collect_list_sector_info () - Collect data page sectors from list_id and all dependent list files.
* membuf exists only in the first list_id (not in dependent_list_id).
* Disk sectors are collected via file_get_all_data_sectors for each dependent list_id.
*
* return : error code
* thread_p (in) : thread entry
* list_id (in) : list file identifier (may have dependent_list_id chain)
* sector_info (out) : output sector info (caller must free with qfile_free_list_sector_info)
*/
int
qfile_collect_list_sector_info (THREAD_ENTRY * thread_p, QFILE_LIST_ID * list_id, QFILE_LIST_SECTOR_INFO * sector_info)
{
QFILE_LIST_ID *current;
FILE_FTAB_COLLECTOR collector = FILE_FTAB_COLLECTOR_INITIALIZER;
int error = NO_ERROR;
assert (thread_p != NULL);
assert (list_id != NULL);
assert (list_id->tfile_vfid != NULL);
assert (sector_info != NULL);
/* reset sector_info */
qfile_free_list_sector_info (thread_p, sector_info);
/* membuf exists only in the first list_id */
if (list_id->tfile_vfid->membuf != NULL && list_id->tfile_vfid->membuf_last >= 0)
{
assert (list_id->tfile_vfid->membuf_npages > 0);
sector_info->membuf_tfile = list_id->tfile_vfid;
}
for (current = list_id; current != NULL; current = current->dependent_list_id)
{
assert (current->tfile_vfid != NULL);
if (VFID_ISNULL (¤t->tfile_vfid->temp_vfid))
{
continue;
}
error = file_get_all_data_sectors (thread_p, ¤t->tfile_vfid->temp_vfid, &collector);
if (error != NO_ERROR)
{
goto error_exit;
}
if (collector.nsects > 0)
{
int old_cnt = sector_info->sector_cnt;
int new_cnt = old_cnt + collector.nsects;
FILE_PARTIAL_SECTOR *merged_sectors =
(FILE_PARTIAL_SECTOR *) db_private_realloc (thread_p, sector_info->sectors,
new_cnt * sizeof (FILE_PARTIAL_SECTOR));
if (merged_sectors == NULL)
{
goto error_exit;
}
sector_info->sectors = merged_sectors;
void **merged_tfiles =
(void **) db_private_realloc (thread_p, sector_info->tfiles, new_cnt * sizeof (void *));
if (merged_tfiles == NULL)
{
goto error_exit;
}
sector_info->tfiles = merged_tfiles;
memcpy (sector_info->sectors + old_cnt, collector.partsect_ftab,
collector.nsects * sizeof (FILE_PARTIAL_SECTOR));
for (int i = 0; i < collector.nsects; i++)
{
sector_info->tfiles[old_cnt + i] = (void *) current->tfile_vfid;
}
sector_info->sector_cnt = new_cnt;
}
if (collector.partsect_ftab != NULL)
{
db_private_free_and_init (thread_p, collector.partsect_ftab);
}
}
return NO_ERROR;
error_exit:
if (collector.partsect_ftab != NULL)
{
db_private_free_and_init (thread_p, collector.partsect_ftab);
}
qfile_free_list_sector_info (thread_p, sector_info);
assert_release_error (er_errid () != NO_ERROR);
return er_errid ();
}
/*
* qfile_free_list_sector_info () - Free sector info allocated by qfile_collect_list_sector_info.
*
* thread_p (in) : thread entry
* sector_info (in) : sector info to free
*/
void
qfile_free_list_sector_info (THREAD_ENTRY * thread_p, QFILE_LIST_SECTOR_INFO * sector_info)
{
assert (thread_p != NULL);
assert (sector_info != NULL);
if (sector_info->sectors != NULL)
{
db_private_free_and_init (thread_p, sector_info->sectors);
}
if (sector_info->tfiles != NULL)
{
db_private_free_and_init (thread_p, sector_info->tfiles);
}
sector_info->membuf_tfile = NULL;
sector_info->sector_cnt = 0;
}