File parse_tree.c¶
File List > cubrid > src > parser > parse_tree.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.
*
*/
/*
* parse_tree.c -
*/
#ident "$Id$"
#include "config.h"
#include <stddef.h>
#include <assert.h>
#include <setjmp.h>
#include <time.h>
#if !defined(WINDOWS)
#include <sys/time.h>
#endif
#include "porting.h"
#include "dbi.h"
#include "parser.h"
#include "jansson.h"
#include "memory_alloc.h"
#include "hide_password.h"
#if defined(SERVER_MODE)
#include "connection_error.h"
#endif /* SERVER_MODE */
#include "dbtype.h"
#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1) ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */
/*
* this should be big enough for "largish" select statements to print.
* It is sized at 8192 less enough to let it fit in 2 blocks with some
* overhead for malloc headers plus string block header.
*/
#define STRINGS_PER_BLOCK (8192-(4*sizeof(long)+sizeof(char *)+40))
#define HASH_NUMBER 128
#define NODES_PER_BLOCK 256
typedef struct parser_node_block PARSER_NODE_BLOCK;
struct parser_node_block
{
PARSER_NODE_BLOCK *next;
int parser_id;
PT_NODE nodes[NODES_PER_BLOCK];
};
typedef struct parser_node_free_list PARSER_NODE_FREE_LIST;
struct parser_node_free_list
{
PARSER_NODE_FREE_LIST *next;
PT_NODE *node;
int parser_id;
};
typedef struct parser_string_block PARSER_STRING_BLOCK;
struct parser_string_block
{
PARSER_STRING_BLOCK *next;
int parser_id;
int last_string_start;
int last_string_end;
int block_end;
union aligned
{
double dummy;
char chars[STRINGS_PER_BLOCK];
} u;
};
/* Global reserved name table including info for each reserved name */
PT_RESERVED_NAME pt_Reserved_name_table[] = {
/* record info attributes */
{"t_pageid", RESERVED_T_PAGEID, DB_TYPE_INTEGER}
,
{"t_slotid", RESERVED_T_SLOTID, DB_TYPE_INTEGER}
,
{"t_volumeid", RESERVED_T_VOLUMEID, DB_TYPE_INTEGER}
,
{"t_offset", RESERVED_T_OFFSET, DB_TYPE_INTEGER}
,
{"t_length", RESERVED_T_LENGTH, DB_TYPE_INTEGER}
,
{"t_rectype", RESERVED_T_REC_TYPE, DB_TYPE_INTEGER}
,
{"t_reprid", RESERVED_T_REPRID, DB_TYPE_INTEGER}
,
{"t_chn", RESERVED_T_CHN, DB_TYPE_INTEGER}
,
{"t_insid", RESERVED_T_MVCC_INSID, DB_TYPE_BIGINT}
,
{"t_delid", RESERVED_T_MVCC_DELID, DB_TYPE_BIGINT}
,
{"t_flags", RESERVED_T_MVCC_FLAGS, DB_TYPE_INTEGER}
,
{"t_prev_version", RESERVED_T_MVCC_PREV_VERSION_LSA, DB_TYPE_INTEGER}
/* page header info attributes */
,
{"p_class_oid", RESERVED_P_CLASS_OID, DB_TYPE_OBJECT}
,
{"p_cur_volumeid", RESERVED_P_CUR_VOLUMEID, DB_TYPE_INTEGER}
,
{"p_cur_pageid", RESERVED_P_CUR_PAGEID, DB_TYPE_INTEGER}
,
{"p_prev_pageid", RESERVED_P_PREV_PAGEID, DB_TYPE_INTEGER}
,
{"p_next_pageid", RESERVED_P_NEXT_PAGEID, DB_TYPE_INTEGER}
,
{"p_num_slots", RESERVED_P_NUM_SLOTS, DB_TYPE_INTEGER}
,
{"p_num_records", RESERVED_P_NUM_RECORDS, DB_TYPE_INTEGER}
,
{"p_anchor_type", RESERVED_P_ANCHOR_TYPE, DB_TYPE_INTEGER}
,
{"p_alignment", RESERVED_P_ALIGNMENT, DB_TYPE_INTEGER}
,
{"p_total_free", RESERVED_P_TOTAL_FREE, DB_TYPE_INTEGER}
,
{"p_cont_free", RESERVED_P_CONT_FREE, DB_TYPE_INTEGER}
,
{"p_offset_to_free_area", RESERVED_P_OFFSET_TO_FREE_AREA, DB_TYPE_INTEGER}
,
{"p_is_saving", RESERVED_P_IS_SAVING, DB_TYPE_INTEGER}
,
{"p_update_best", RESERVED_P_UPDATE_BEST, DB_TYPE_INTEGER}
/* key info attributes */
,
{"key_volumeid", RESERVED_KEY_VOLUMEID, DB_TYPE_INTEGER}
,
{"key_pageid", RESERVED_KEY_PAGEID, DB_TYPE_INTEGER}
,
{"key_slotid", RESERVED_KEY_SLOTID, DB_TYPE_INTEGER}
,
{"key_key", RESERVED_KEY_KEY, DB_TYPE_NULL} /* Types should be determined at compilation */
,
{"key_oid_count", RESERVED_KEY_OID_COUNT, DB_TYPE_INTEGER}
,
{"key_first_oid", RESERVED_KEY_FIRST_OID, DB_TYPE_OBJECT}
,
{"key_overflow_key", RESERVED_KEY_OVERFLOW_KEY, DB_TYPE_STRING}
,
{"key_overflow_oids", RESERVED_KEY_OVERFLOW_OIDS, DB_TYPE_STRING}
/* B-tree node info */
,
{"bt_node_volumeid", RESERVED_BT_NODE_VOLUMEID, DB_TYPE_INTEGER}
,
{"bt_node_pageid", RESERVED_BT_NODE_PAGEID, DB_TYPE_INTEGER}
,
{"bt_node_type", RESERVED_BT_NODE_TYPE, DB_TYPE_STRING}
,
{"bt_node_key_count", RESERVED_BT_NODE_KEY_COUNT, DB_TYPE_INTEGER}
,
{"bt_node_first_key", RESERVED_BT_NODE_FIRST_KEY, DB_TYPE_NULL}
,
{"bt_node_last_key", RESERVED_BT_NODE_LAST_KEY, DB_TYPE_NULL}
};
#if defined(SERVER_MODE)
/* this is a kludge because many platforms do not handle extern
* linking per ANSI. This should be deleted when nodes get used in server.
*/
static pthread_mutex_t blocks_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t free_lists_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t parser_memory_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t parser_id_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* SERVER_MODE */
static PARSER_NODE_BLOCK *parser_Node_blocks[HASH_NUMBER];
static PARSER_NODE_FREE_LIST *parser_Node_free_lists[HASH_NUMBER];
static PARSER_STRING_BLOCK *parser_String_blocks[HASH_NUMBER];
static int parser_id = 1;
static PT_NODE *parser_create_node_block (const PARSER_CONTEXT * parser);
static void pt_free_node_blocks (const PARSER_CONTEXT * parser);
static PARSER_STRING_BLOCK *parser_create_string_block (const PARSER_CONTEXT * parser, const int length);
static void pt_free_a_string_block (const PARSER_CONTEXT * parser, PARSER_STRING_BLOCK * string_to_free);
static PARSER_STRING_BLOCK *pt_find_string_block (const PARSER_CONTEXT * parser, const char *old_string);
static char *pt_append_string_for (const PARSER_CONTEXT * parser, const char *old_string, const char *new_tail,
const int wrap_with_single_quote);
static PARSER_VARCHAR *pt_append_bytes_for (const PARSER_CONTEXT * parser, PARSER_VARCHAR * old_string,
const char *new_tail, const int new_tail_length);
static int pt_register_parser (const PARSER_CONTEXT * parser);
static void pt_unregister_parser (const PARSER_CONTEXT * parser);
static void pt_free_string_blocks (const PARSER_CONTEXT * parser);
/*
* pt_create_node_block () - creates a new block of nodes, links the block
* on the hash list for the parser, and returns the free list of new nodes
* return:
* parser(in):
*/
static PT_NODE *
parser_create_node_block (const PARSER_CONTEXT * parser)
{
int idhash, inode;
PARSER_NODE_BLOCK *block;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
block = (PARSER_NODE_BLOCK *) malloc (sizeof (PARSER_NODE_BLOCK));
if (!block)
{
if (parser->jmp_env_active)
{
/* long jump back to routine that set up the jump env for clean up and run down. */
longjmp (((PARSER_CONTEXT *) parser)->jmp_env, 1);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PARSER_NODE_BLOCK));
return NULL;
}
}
/* remember which parser allocated this block */
block->parser_id = parser->id;
/* link blocks on the hash list for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&blocks_lock);
#endif /* SERVER_MODE */
block->next = parser_Node_blocks[idhash];
parser_Node_blocks[idhash] = block;
/* link nodes for free list */
for (inode = 1; inode < NODES_PER_BLOCK; inode++)
{
block->nodes[inode - 1].next = &block->nodes[inode];
}
block->nodes[NODES_PER_BLOCK - 1].next = NULL;
#if defined(SERVER_MODE)
pthread_mutex_unlock (&blocks_lock);
#endif /* SERVER_MODE */
/* return head of free list */
return &block->nodes[0];
}
/*
* parser_create_node () - creates a new node for a given parser.
* First it tries the parser's free list. If empty it adds a new
* block of nodes to the free list
* return:
* parser(in):
*/
PT_NODE *
parser_create_node (const PARSER_CONTEXT * parser)
{
int idhash;
PARSER_NODE_FREE_LIST *free_list;
PT_NODE *node;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find free list for for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&free_lists_lock);
#endif /* SERVER_MODE */
free_list = parser_Node_free_lists[idhash];
while (free_list != NULL && free_list->parser_id != parser->id)
{
free_list = free_list->next;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
if (free_list == NULL)
{
/* this is an programming error ! The parser does not exist! */
return NULL;
}
if (free_list->node == NULL)
{
/* do not need to use mutex : only used by one parser(and one thread) */
free_list->node = parser_create_node_block (parser);
if (free_list->node == NULL)
{
return NULL;
}
}
node = free_list->node;
free_list->node = free_list->node->next;
node->parser_id = parser->id; /* consistency check */
return node;
}
/*
* pt_free_node_blocks () - frees all node blocks allocated to this parser
* return: none
* parser(in):
*/
static void
pt_free_node_blocks (const PARSER_CONTEXT * parser)
{
int idhash;
PARSER_NODE_BLOCK *block;
PARSER_NODE_BLOCK **previous_block;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* unlink blocks on the hash list for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&blocks_lock);
#endif /* SERVER_MODE */
previous_block = &parser_Node_blocks[idhash];
block = *previous_block;
while (block != NULL)
{
if (block->parser_id == parser->id)
{
/* remove it from list, and free it */
*previous_block = block->next;
free_and_init (block);
}
else
{
/* keep it, and move to next block pointer */
previous_block = &block->next;
}
/* re-establish invariant */
block = *previous_block;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&blocks_lock);
#endif /* SERVER_MODE */
}
/*
* parser_create_string_block () - reates a new block of strings, links the block
* on the hash list for the parser, and returns the block
* return:
* parser(in):
* length(in):
*/
static PARSER_STRING_BLOCK *
parser_create_string_block (const PARSER_CONTEXT * parser, const int length)
{
int idhash;
PARSER_STRING_BLOCK *block;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
if (length < (int) STRINGS_PER_BLOCK)
{
block = (PARSER_STRING_BLOCK *) malloc (sizeof (PARSER_STRING_BLOCK));
if (!block)
{
if (parser->jmp_env_active)
{
/* long jump back to routine that set up the jump env for clean up and run down. */
longjmp (((PARSER_CONTEXT *) parser)->jmp_env, 1);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PARSER_STRING_BLOCK));
return NULL;
}
}
block->block_end = STRINGS_PER_BLOCK - 1;
}
else
{
/* This is an unusually large string. Allocate a special block for it, with space for one string, plus some space
* for appending to. */
block = (PARSER_STRING_BLOCK *) malloc (sizeof (PARSER_STRING_BLOCK) + (length + 1001 - STRINGS_PER_BLOCK));
if (!block)
{
if (parser->jmp_env_active)
{
/* long jump back to routine that set up the jump env for clean up and run down. */
longjmp (((PARSER_CONTEXT *) parser)->jmp_env, 1);
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(sizeof (PARSER_STRING_BLOCK) + (length + 1001 - STRINGS_PER_BLOCK)));
return NULL;
}
}
block->block_end = CAST_BUFLEN (length + 1001 - 1);
}
/* remember which parser allocated this block */
block->parser_id = parser->id;
block->last_string_start = -1;
block->last_string_end = -1;
block->u.chars[0] = 0;
/* link blocks on the hash list for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_memory_lock);
#endif /* SERVER_MODE */
block->next = parser_String_blocks[idhash];
parser_String_blocks[idhash] = block;
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_memory_lock);
#endif /* SERVER_MODE */
return block;
}
/*
* parser_allocate_string_buffer () - creates memory for a given parser
* return: allocated memory pointer
* parser(in):
* length(in):
* align(in):
*
* Note :
* First it tries to find length + 1 bytes in the parser's free strings list.
* If there is no room, it adds a new block of strings to the free
* strings list, at least large enough to hold new length plus
* 1 (for a null character). Thus, one can call it by
* copy_of_foo = pt_create_string(parser, strlen(foo));
*/
void *
parser_allocate_string_buffer (const PARSER_CONTEXT * parser, const int length, const int align)
{
int idhash;
PARSER_STRING_BLOCK *block;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find free string list for for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_memory_lock);
#endif /* SERVER_MODE */
block = parser_String_blocks[idhash];
while (block != NULL
&& (block->parser_id != parser->id
|| ((block->block_end - block->last_string_end) < (length + (align - 1) + 1))))
{
block = block->next;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_memory_lock);
#endif /* SERVER_MODE */
if (block == NULL)
{
block = parser_create_string_block (parser, length + (align - 1) + 1);
if (block == NULL)
{
return NULL;
}
}
/* set start to the aligned length */
block->last_string_start = CAST_BUFLEN ((block->last_string_end + (align - 1) + 1) & ~(align - 1));
block->last_string_end = CAST_BUFLEN (block->last_string_start + length);
block->u.chars[block->last_string_start] = 0;
return &block->u.chars[block->last_string_start];
}
/*
* pt_free_a_string_block() - finds a string block, removes it from
* the hash table linked list frees the memory
* return:
* parser(in):
* string_to_free(in):
*/
static void
pt_free_a_string_block (const PARSER_CONTEXT * parser, PARSER_STRING_BLOCK * string_to_free)
{
PARSER_STRING_BLOCK **previous_string;
PARSER_STRING_BLOCK *string;
int idhash;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find string holding old_string for for this parse_id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_memory_lock);
#endif /* SERVER_MODE */
previous_string = &parser_String_blocks[idhash];
string = *previous_string;
while (string != string_to_free)
{
previous_string = &string->next;
string = *previous_string;
}
if (string)
{
*previous_string = string->next;
free_and_init (string);
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_memory_lock);
#endif /* SERVER_MODE */
}
/*
* pt_find_string_block () - finds a string block from same parser that
* has oldstring as its last string
* return:
* parser(in):
* old_string(in):
*/
static PARSER_STRING_BLOCK *
pt_find_string_block (const PARSER_CONTEXT * parser, const char *old_string)
{
PARSER_STRING_BLOCK *string;
int idhash;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find string holding old_string for for this parse_id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_memory_lock);
#endif /* SERVER_MODE */
string = parser_String_blocks[idhash];
while (string != NULL
&& (string->parser_id != parser->id || &(string->u.chars[string->last_string_start]) != old_string))
{
string = string->next;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_memory_lock);
#endif /* SERVER_MODE */
return string;
}
/*
* pt_append_string_for () - appends a tail to a string for a given parser
* return:
* parser(in):
* old_string(in/out):
* new_tail(in):
* wrap_with_single_quote(in):
*
* Note :
* The space allocated is at least their combined lengths plus one
* (for a null character). The two strings are logically concatenated
* and copied into the result string. The physical operation is typically
* more efficient, and conservative of memory.
* The given old_string is OVERWRITTEN.
*/
static char *
pt_append_string_for (const PARSER_CONTEXT * parser, const char *old_string, const char *new_tail,
const int wrap_with_single_quote)
{
PARSER_STRING_BLOCK *string;
char *s;
int new_tail_length;
/* here, you know you have two non-NULL pointers */
string = pt_find_string_block (parser, old_string);
new_tail_length = strlen (new_tail);
if (wrap_with_single_quote)
{
new_tail_length += 2; /* for opening/closing "'" */
}
/* if we did not find old_string at the end of a string buffer, or if there is not room to concatenate the tail, copy
* both to new string */
if ((string == NULL) || ((string->block_end - string->last_string_end) < new_tail_length))
{
s = (char *) parser_allocate_string_buffer (parser, strlen (old_string) + new_tail_length, sizeof (char));
if (s == NULL)
{
return NULL;
}
strcpy (s, old_string);
if (wrap_with_single_quote)
{
strcat (s, "'");
}
strcat (s, new_tail);
if (wrap_with_single_quote)
{
strcat (s, "'");
}
/* We might be appending to ever-growing buffers. Detect if there was a string found, but it was out of space,
* and it was the ONLY string in the buffer. If this happened, free it. */
if (string != NULL
/* && (already know there was not room, see above) */
&& string->last_string_start == 0)
{
/* old_string is the only contents of string, free it. */
pt_free_a_string_block (parser, string);
}
}
else
{
/* found old_string at end of buffer with enough room concatenate new_tail in place when repeatedly adding to a
* buffer, eg. print buffer, this will grow the allocation efficiently to the needed size. */
s = &string->u.chars[string->last_string_end];
if (wrap_with_single_quote)
{
strcpy (s, "'");
strcpy (s + 1, new_tail);
strcpy (s + new_tail_length - 1, "'");
}
else
{
strcpy (s, new_tail);
}
string->last_string_end += new_tail_length;
s = &string->u.chars[string->last_string_start];
}
return s;
}
/*
* pt_append_bytes_for () - appends a tail to a old_string for a given parser
* return:
* parser(in):
* old_string(in/out):
* new_tail(in):
* new_tail_length(in):
*
* Note :
* The space allocated is at least their combined lengths plus one
* (for a null character) plus the size of a long. The two strings are
* logically concatenated and copied into the result string. The physical
* operation is typically more efficient, and conservative of memory.
* The given VARCHAR old_string is OVERWRITTEN.
*
* All VARCHAR strings for a parser will be freed by either parser_free_parser
* or parser_free_strings.
*/
static PARSER_VARCHAR *
pt_append_bytes_for (const PARSER_CONTEXT * parser, PARSER_VARCHAR * old_string, const char *new_tail,
const int new_tail_length)
{
PARSER_STRING_BLOCK *string;
char *s;
if (0 < parser->max_print_len && parser->max_print_len < old_string->length)
{
return old_string;
}
/* here, you know you have two non-NULL pointers */
string = pt_find_string_block (parser, (char *) old_string);
/* if we did not find old_string at the end of a string buffer, or if there is not room to concatenate the tail, copy
* both to new string */
if ((string == NULL) || ((string->block_end - string->last_string_end) < new_tail_length))
{
s = (char *) parser_allocate_string_buffer (parser,
offsetof (PARSER_VARCHAR,
bytes) + old_string->length + new_tail_length,
sizeof (long));
if (s == NULL)
{
return NULL;
}
memcpy (s, old_string, old_string->length + offsetof (PARSER_VARCHAR, bytes));
old_string = (PARSER_VARCHAR *) s;
memcpy (&old_string->bytes[old_string->length], new_tail, new_tail_length);
old_string->length += (int) new_tail_length;
old_string->bytes[old_string->length] = 0; /* nul terminate */
/* We might be appending to ever-growing buffers. Detect if there was a string found, but it was out of space,
* and it was the ONLY string in the buffer. If this happened, free it. */
if (string != NULL
/* && (already know there was not room, see above) */
&& string->last_string_start == 0)
{
/* old_string is the only contents of string, free it. */
pt_free_a_string_block (parser, string);
}
}
else
{
/* found old_string at end of buffer with enough room concatenate new_tail in place when repeatedly adding to a
* buffer, eg. print buffer, this will grow the allocation efficiently to the needed size. */
memcpy (&old_string->bytes[old_string->length], new_tail, new_tail_length);
old_string->length += (int) new_tail_length;
old_string->bytes[old_string->length] = 0; /* nul terminate */
string->last_string_end += (int) new_tail_length;
s = &string->u.chars[string->last_string_start];
}
return old_string;
}
/*
* pt_register_parser () - registers parser as existing by creating free list
* return: NO_ERROR on success, non-zero for ERROR
* parser(in):
*/
static int
pt_register_parser (const PARSER_CONTEXT * parser)
{
int idhash;
PARSER_NODE_FREE_LIST *free_list;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find free list for for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&free_lists_lock);
#endif /* SERVER_MODE */
free_list = parser_Node_free_lists[idhash];
while (free_list != NULL && free_list->parser_id != parser->id)
{
free_list = free_list->next;
}
if (free_list == NULL)
{
/* this is the first time this parser allocated a node. */
/* set up a free list. This will only be done once per parser. */
free_list = (PARSER_NODE_FREE_LIST *) calloc (sizeof (PARSER_NODE_FREE_LIST), 1);
if (free_list == NULL)
{
#if defined(SERVER_MODE)
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PARSER_NODE_FREE_LIST));
return ER_FAILED;
}
free_list->parser_id = parser->id;
free_list->next = parser_Node_free_lists[idhash];
parser_Node_free_lists[idhash] = free_list;
}
else
{
#if defined(SERVER_MODE)
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
return NO_ERROR;
}
/*
* pt_unregister_parser () - unregisters parser as existing,
* or registers it as not existing
* return: none
* parser(in):
*/
static void
pt_unregister_parser (const PARSER_CONTEXT * parser)
{
int idhash;
PARSER_NODE_FREE_LIST *free_list;
PARSER_NODE_FREE_LIST **previous_free_list;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* find free list for for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&free_lists_lock);
#endif /* SERVER_MODE */
previous_free_list = &parser_Node_free_lists[idhash];
free_list = *previous_free_list;
while (free_list != NULL && free_list->parser_id != parser->id)
{
previous_free_list = &free_list->next;
free_list = *previous_free_list;
}
if (free_list)
{
/* all is ok, remove the free list from the hash list of free lists. */
*previous_free_list = free_list->next;
free_and_init (free_list);
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
}
void
parser_free_node_resources (PT_NODE * node)
{
/* before we free this node, see if we need to clear a db_value */
if (node->node_type == PT_VALUE && node->info.value.db_value_is_in_workspace)
{
db_value_clear (&node->info.value.db_value);
}
if (node->node_type == PT_INSERT_VALUE && node->info.insert_value.is_evaluated)
{
db_value_clear (&node->info.insert_value.value);
}
if (node->node_type == PT_JSON_TABLE_COLUMN)
{
PT_JSON_TABLE_COLUMN_INFO *col = &node->info.json_table_column_info;
db_value_clear (col->on_empty.m_default_value);
col->on_empty.m_default_value = NULL;
db_value_clear (col->on_error.m_default_value);
col->on_error.m_default_value = NULL;
// db_values on_empty.m_default_value & on_error.m_default_value are allocated using area_alloc
}
}
/*
* parser_free_node () - Return this node to this parser's node memory pool
* return:
* parser(in):
* node(in):
*
* Note :
* This only makes this memory eligible for re-use
* by the current parser. To return memory to virtual memory pool,
* pt_free_nodes or parser_free_parser should be called.
*/
void
parser_free_node (const PARSER_CONTEXT * parser, PT_NODE * node)
{
int idhash;
PARSER_NODE_FREE_LIST *free_list;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
if (node == NULL)
{
assert_release (false);
return;
}
assert_release (node->node_type != PT_LAST_NODE_NUMBER);
if (node->node_type == PT_SPEC)
{
/* prevent same spec_id on a parser tree */
return;
}
/* find free list for for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&free_lists_lock);
#endif /* SERVER_MODE */
free_list = parser_Node_free_lists[idhash];
while (free_list != NULL && free_list->parser_id != parser->id)
{
free_list = free_list->next;
}
#if defined(SERVER_MODE)
/* we can unlock mutex, since this free_list is only used by one parser */
pthread_mutex_unlock (&free_lists_lock);
#endif /* SERVER_MODE */
if (free_list == NULL)
{
/* this is an programming error ! The parser does not exist! */
return;
}
if (node->parser_id != parser->id)
{
/* this is an programming error ! The node is not from this parser. */
return;
}
parser_free_node_resources (node);
/*
* Always set the node type to maximum. This may
* keep us from doing bad things to the free list if we try to free
* this structure more than once. We shouldn't be doing that (i.e.,
* we should always be building trees, not graphs) but sometimes it
* does by accident, and if we don't watch for it we wind up with
* fatal errors. A common symptom is stack exhaustion during parser_free_tree
* as we try to recursively walk a cyclic free list.
*/
node->node_type = PT_LAST_NODE_NUMBER;
node->next = free_list->node;
free_list->node = node;
}
/*
* parser_alloc () - allocate memory for a given parser
* return:
* parser(in):
* length(in):
*
* Note :
* The space allocated is at least length plus 8.
* The space allocated is double word aligned ( a multiple of 8 ).
* Thus, one can call it by
* foo_buffer = parser_alloc (parser, strlen(foo));
* ALL BUFFERS for a parser will be FREED by either parser_free_parser
* or parser_free_strings.
*/
void *
parser_alloc (const PARSER_CONTEXT * parser, const int length)
{
void *pointer;
pointer = parser_allocate_string_buffer (parser, length + sizeof (long), sizeof (double));
if (pointer)
memset (pointer, 0, length);
return pointer;
}
/*
* pt_append_string () - appends a tail to a string for a given parser
* return:
* parser(in):
* old_string(in/out):
* new_tail(in):
*
* Note :
* The space allocated is at least their combined lengths plus one
* (for a null character). The two strings are logically concatenated
* and copied into the result string. The physical operation is typically
* more efficient, and conservative of memory
*
* Note :
* pt_append_string won't modify old_string but it will return it to caller if new_tail is NULL.
*/
char *
pt_append_string (const PARSER_CONTEXT * parser, const char *old_string, const char *new_tail)
{
char *s;
if (new_tail == NULL)
{
s = CONST_CAST (char *, old_string); // it is up to caller
}
else if (old_string == NULL)
{
s = (char *) parser_allocate_string_buffer (parser, strlen (new_tail), sizeof (char));
if (s == NULL)
{
return NULL;
}
strcpy (s, new_tail);
}
else
{
s = pt_append_string_for (parser, old_string, new_tail, false);
}
return s;
}
/*
* pt_append_bytes () - appends a byte tail to a string for a given parser
* return:
* parser(in):
* old_string(in/out):
* new_tail(in):
* new_tail_length(in):
*/
PARSER_VARCHAR *
pt_append_bytes (const PARSER_CONTEXT * parser, PARSER_VARCHAR * old_string, const char *new_tail,
const int new_tail_length)
{
PARSER_VARCHAR *s;
if (old_string == NULL)
{
old_string =
(PARSER_VARCHAR *) parser_allocate_string_buffer ((PARSER_CONTEXT *) parser, offsetof (PARSER_VARCHAR, bytes),
sizeof (long));
if (old_string == NULL)
{
return NULL;
}
old_string->length = 0;
old_string->bytes[0] = 0;
}
if (new_tail == NULL)
{
s = old_string;
}
else
{
s = pt_append_bytes_for ((PARSER_CONTEXT *) parser, old_string, new_tail, new_tail_length);
}
return s;
}
/*
* pt_append_varchar () -
* return:
* parser(in):
* old_string(in/out):
* new_tail(in):
*/
PARSER_VARCHAR *
pt_append_varchar (const PARSER_CONTEXT * parser, PARSER_VARCHAR * old_string, const PARSER_VARCHAR * new_tail)
{
if (new_tail == NULL)
{
return old_string;
}
return pt_append_bytes (parser, old_string, (char *) new_tail->bytes, new_tail->length);
}
/*
* pt_append_nulstring () - Append a nul terminated string to
* a PARSER_VARCHAR binary string
* return:
* parser(in):
* bstring(in):
* nulstring(in):
*/
PARSER_VARCHAR *
pt_append_nulstring (const PARSER_CONTEXT * parser, PARSER_VARCHAR * bstring, const char *nulstring)
{
if (nulstring == NULL)
{
return bstring;
}
return pt_append_bytes (parser, bstring, nulstring, strlen (nulstring));
}
/*
* pt_get_varchar_bytes () - return a PARSER_VARCHAR byte pointer
* return:
* string(in):
*/
const unsigned char *
pt_get_varchar_bytes (const PARSER_VARCHAR * string)
{
if (string != NULL)
{
return string->bytes;
}
return NULL;
}
/*
* pt_get_varchar_length () - return a PARSER_VARCHAR length
* return:
* string(in):
*/
int
pt_get_varchar_length (const PARSER_VARCHAR * string)
{
if (string != NULL)
{
return string->length;
}
return 0;
}
/*
* pt_free_string_blocks () - Return parser's string memory pool to virtual memory
* return: none
* parser(in):
*/
static void
pt_free_string_blocks (const PARSER_CONTEXT * parser)
{
int idhash;
PARSER_STRING_BLOCK *block;
PARSER_STRING_BLOCK **previous_block;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
/* unlink blocks on the hash list for this id */
idhash = parser->id % HASH_NUMBER;
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_memory_lock);
#endif /* SERVER_MODE */
previous_block = &parser_String_blocks[idhash];
block = *previous_block;
while (block != NULL)
{
if (block->parser_id == parser->id)
{
/* remove it from list, and free it */
*previous_block = block->next;
free_and_init (block);
}
else
{
/* keep it, and move to next block pointer */
previous_block = &block->next;
}
/* re-establish invariant */
block = *previous_block;
}
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_memory_lock);
#endif /* SERVER_MODE */
}
/*
* parser_create_parser () - creates a parser context
* The pointer can be passed to top level
* parse functions and then freed by parser_free_parser.
* return:
*/
PARSER_CONTEXT *
parser_create_parser (void)
{
PARSER_CONTEXT *parser;
struct timeval t;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
struct drand48_data rand_buf;
parser = (PARSER_CONTEXT *) calloc (sizeof (PARSER_CONTEXT), 1);
if (parser == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PARSER_CONTEXT));
return NULL;
}
INIT_HIDE_PASSWORD_INFO (&parser->hide_pwd_info);
#if !defined (SERVER_MODE)
parser_init_func_vectors ();
#endif /* !SERVER_MODE */
#if defined(SERVER_MODE)
rv = pthread_mutex_lock (&parser_id_lock);
#endif /* SERVER_MODE */
parser->id = parser_id++;
#if defined(SERVER_MODE)
pthread_mutex_unlock (&parser_id_lock);
#endif /* SERVER_MODE */
if (pt_register_parser (parser) == ER_FAILED)
{
free_and_init (parser);
return NULL;
}
parser->execution_values.row_count = -1;
/* Generate random values for rand() and drand() */
gettimeofday (&t, NULL);
srand48_r (t.tv_usec, &rand_buf);
lrand48_r (&rand_buf, &parser->lrand);
drand48_r (&rand_buf, &parser->drand);
db_make_null (&parser->sys_datetime);
db_make_null (&parser->sys_epochtime);
/* initialization */
parser->query_id = NULL_QUERY_ID;
parser->flag.is_in_and_list = 0;
parser->flag.is_holdable = 0;
parser->flag.is_xasl_pinned_reference = 0;
parser->flag.recompile_xasl_pinned = 0;
parser->auto_param_count = 0;
parser->flag.return_generated_keys = 0;
parser->flag.is_system_generated_stmt = 0;
parser->flag.has_internal_error = 0;
parser->max_print_len = 0;
parser->flag.is_auto_commit = 0;
parser->flag.is_parsing_static_sql = 0;
parser->flag.is_unloading_plcsql_def = 0;
parser->flag.is_parsing_trigger = 0;
parser->external_into_label = NULL;
parser->external_into_label_cnt = 0;
return parser;
}
/*
* parser_free_parser() - clean up all parse structures after they are through
* being used. Values which need to be persistent, should have been
* copied by now
* return:
* parser(in):
*/
void
parser_free_parser (PARSER_CONTEXT * parser)
{
DB_VALUE *hv;
int i;
assert (parser != NULL);
QUIT_HIDE_PASSWORD_INFO (&parser->hide_pwd_info);
/* free remote dblink cols */
pt_free_dblink_remote_cols (parser);
/* free string blocks */
pt_free_string_blocks (parser);
/* free node blocks */
pt_free_node_blocks (parser);
pt_unregister_parser (parser);
if (parser->error_buffer)
{
free ((char *) parser->error_buffer);
}
if (parser->host_variables)
{
for (i = 0, hv = parser->host_variables; i < parser->host_var_count + parser->auto_param_count; i++, hv++)
{
db_value_clear (hv);
}
free_and_init (parser->host_variables);
}
if (parser->host_var_expected_domains)
{
free_and_init (parser->host_var_expected_domains);
}
parser_free_lcks_classes (parser);
/* free remaining plan trace string */
if (parser->query_trace == true && parser->num_plan_trace > 0)
{
for (i = 0; i < parser->num_plan_trace; i++)
{
if (parser->plan_trace[i].format == QUERY_TRACE_TEXT)
{
if (parser->plan_trace[i].trace.text_plan != NULL)
{
free_and_init (parser->plan_trace[i].trace.text_plan);
}
}
else if (parser->plan_trace[i].format == QUERY_TRACE_JSON)
{
if (parser->plan_trace[i].trace.json_plan != NULL)
{
json_object_clear (parser->plan_trace[i].trace.json_plan);
json_decref (parser->plan_trace[i].trace.json_plan);
parser->plan_trace[i].trace.json_plan = NULL;
}
}
}
parser->num_plan_trace = 0;
}
free_and_init (parser);
}
/*
* parser_free_lcks_classes() - free allocated memory in pt_class_pre_fetch()
* and pt_find_lck_classes ()
* return: void
* parser(in):
*/
void
parser_free_lcks_classes (PARSER_CONTEXT * parser)
{
int i;
if (parser->lcks_classes)
{
for (i = 0; i < parser->num_lcks_classes; i++)
{
free_and_init (parser->lcks_classes[i]);
}
free_and_init (parser->lcks_classes);
parser->num_lcks_classes = 0;
}
return;
}
/*
* pt_init_assignments_helper() - initialize enumeration of assignments
* return: void
* parser(in):
* helper(in): address of assignments enumeration structure
* assignment(in): assignments list to enumerate
*/
void
pt_init_assignments_helper (PARSER_CONTEXT * parser, PT_ASSIGNMENTS_HELPER * helper, PT_NODE * assignment)
{
helper->parser = parser;
helper->assignment = assignment;
helper->lhs = NULL;
helper->rhs = NULL;
helper->is_rhs_const = false;
helper->is_n_column = false;
}
/*
* pt_get_next_assignment() - get next assignment
* return: returns left side of an assignment
* ea(in/out): structure used in assignments enumeration.
*
* Note :
* The function fills the ENUM_ASSIGNMENT structure with details related to
* next assignment. In case there is a multiple assignment of form
* (i1, i2, i3)=(select * from t) then the left side of assignment is split
* and each element of it is returned as if there is a separate assignment and
* the right side is always returned the same. In this case the is_n_columns
* is set to true.
*/
PT_NODE *
pt_get_next_assignment (PT_ASSIGNMENTS_HELPER * ea)
{
PT_NODE *lhs = ea->lhs, *rhs = NULL;
ea->is_rhs_const = false;
if (lhs != NULL)
{
if (lhs->next != NULL)
{
ea->is_n_column = true;
ea->lhs = lhs->next;
return ea->lhs;
}
ea->assignment = ea->assignment->next;
}
if (ea->assignment != NULL)
{
lhs = ea->assignment->info.expr.arg1;
ea->rhs = rhs = ea->assignment->info.expr.arg2;
ea->is_rhs_const = PT_IS_CONST_NOT_HOSTVAR (rhs);
if (lhs->node_type == PT_NAME)
{
ea->is_n_column = false;
ea->lhs = lhs;
return ea->lhs;
}
else
{ /* PT_IS_N_COLUMN_UPDATE_EXPR(lhs) == true */
ea->is_n_column = true;
ea->lhs = lhs->info.expr.arg1;
return ea->lhs;
}
}
else
{
ea->lhs = NULL;
ea->rhs = NULL;
ea->is_n_column = false;
return NULL;
}
}
/*
* pt_count_assignments() - count assignments
* return: the number of assignments in the assignments list
* parser(in):
* assignments(in): assignments to count.
*
* Note :
* Multiple assignments are split and each component is counted.
*/
int
pt_count_assignments (PARSER_CONTEXT * parser, PT_NODE * assignments)
{
PT_ASSIGNMENTS_HELPER ea;
int cnt = 0;
pt_init_assignments_helper (parser, &ea, assignments);
while (pt_get_next_assignment (&ea))
{
cnt++;
}
return cnt;
}
bool
pt_is_json_value_type (PT_TYPE_ENUM type)
{
if (type == PT_TYPE_MAYBE || type == PT_TYPE_NULL)
{
return true;
}
DB_TYPE converted_type = pt_type_enum_to_db (type);
return db_is_json_value_type (converted_type);
}
bool
pt_is_json_doc_type (PT_TYPE_ENUM type)
{
if (type == PT_TYPE_MAYBE || type == PT_TYPE_NULL)
{
return true;
}
DB_TYPE converted_type = pt_type_enum_to_db (type);
return db_is_json_doc_type (converted_type);
}