File parser_support.c¶
File List > cubrid > src > parser > parser_support.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.
*
*/
/*
* parser_support.c - Utility functions for parse trees
*/
#ident "$Id$"
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#if defined (_AIX)
#include <stdarg.h>
#endif
#include "authenticate.h"
#include "chartype.h"
#include "parser.h"
#include "parser_message.h"
#include "mem_block.hpp"
#include "memory_alloc.h"
#include "intl_support.h"
#include "error_manager.h"
#include "work_space.h"
#include "oid.h"
#include "class_object.h"
#include "optimizer.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "parser_support.h"
#include "system_parameter.h"
#include "xasl_generation.h"
#include "schema_manager.h"
#include "object_print.h"
#include "show_meta.h"
#include "db.h"
#include "object_printer.hpp"
#include "string_buffer.hpp"
#include "dbtype.h"
#include "parser_allocator.hpp"
#include "execute_schema.h"
#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1) ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */
#define DEFAULT_VAR "."
struct pt_host_vars
{
PT_NODE *inputs;
PT_NODE *outputs;
PT_NODE *out_descr;
PT_NODE *cursor;
};
#define COMPATIBLE_WITH_INSTNUM(node) \
(pt_is_expr_node (node) && PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_INSTNUM_C))
#define NOT_COMPATIBLE_WITH_INSTNUM(node) \
(pt_is_dot_node (node) || pt_is_attr (node) || pt_is_correlated_subquery (node) \
|| (pt_is_expr_node (node) && PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_INSTNUM_NC)))
#define COMPATIBLE_WITH_GROUPBYNUM(node) \
((pt_is_function (node) && node->info.function.function_type == PT_GROUPBY_NUM) \
|| (pt_is_expr_node (node) && PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_GROUPBYNUM_C)))
#define NOT_COMPATIBLE_WITH_GROUPBYNUM(node) \
(pt_is_dot_node (node) || pt_is_attr (node) || pt_is_query (node) \
|| (pt_is_expr_node (node) && PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_GROUPBYNUM_NC)))
/* reserve half a page for the total enum domain size. we used to consider BTREE_ROOT_HEADER size here, but this is
* client and we no longer have that information. but half of page should be enough... the alternative is to get
* that info from server. */
#define DB_ENUM_ELEMENTS_MAX_AGG_SIZE (DB_PAGESIZE / 2)
int qp_Packing_er_code = NO_ERROR;
static const int PACKING_MMGR_CHUNK_SIZE = 1024;
static const int PACKING_MMGR_BLOCK_SIZE = 10;
static int packing_heap_num_slot = 0;
static HL_HEAPID *packing_heap = NULL;
static int packing_level = 0;
static void pt_free_packing_buf (int slot);
static bool pt_datatypes_match (const PT_NODE * a, const PT_NODE * b);
static PT_NODE *pt_get_select_from_spec (const PT_NODE * spec);
static PT_NODE *pt_insert_host_var (PARSER_CONTEXT * parser, PT_NODE * h_var, PT_NODE * list);
static PT_NODE *pt_collect_host_info (PARSER_CONTEXT * parser, PT_NODE * node, void *h_var, int *continue_walk);
static PT_NODE *pt_collect_parameters (PARSER_CONTEXT * parser, PT_NODE * node, void *param_list, int *continue_walk);
static PT_NODE *pt_must_be_filtering (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static bool pt_is_filtering_predicate (PARSER_CONTEXT * parser, PT_NODE * predicate);
static PT_NODE *pt_is_filtering_skip_and_or (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_create_param_for_value (PARSER_CONTEXT * parser, PT_NODE * value, int host_var_index);
static PT_NODE *pt_make_dotted_identifier (PARSER_CONTEXT * parser, const char *identifier_str);
static PT_NODE *pt_make_dotted_identifier_internal (PARSER_CONTEXT * parser, const char *identifier_str, int depth);
static int pt_add_name_col_to_sel_list (PARSER_CONTEXT * parser, PT_NODE * select, const char *identifier_str,
const char *col_alias);
static void pt_add_string_col_to_sel_list (PARSER_CONTEXT * parser, PT_NODE * select, const char *identifier_str,
const char *col_alias);
static PT_NODE *pt_make_pred_name_int_val (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *col_name,
const int int_value);
static PT_NODE *pt_make_pred_name_string_val (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *identifier_str,
const char *str_value);
static PT_NODE *pt_make_pred_with_identifiers (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *lhs_identifier,
const char *rhs_identifier);
static PT_NODE *pt_make_if_with_expressions (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE * expr1, PT_NODE * expr2,
const char *alias);
static PT_NODE *pt_make_if_with_strings (PARSER_CONTEXT * parser, PT_NODE * pred, const char *string1,
const char *string2, const char *alias);
static PT_NODE *pt_make_like_col_expr (PARSER_CONTEXT * parser, PT_NODE * rhs_expr, const char *col_name);
static PT_NODE *pt_make_outer_select_for_show_stmt (PARSER_CONTEXT * parser, PT_NODE * inner_select,
const char *select_alias);
static PT_NODE *pt_make_field_type_expr_node (PARSER_CONTEXT * parser);
static PT_NODE *pt_make_select_count_star (PARSER_CONTEXT * parser);
#if defined(ENABLE_UNUSED_FUNCTION)
static PT_NODE *pt_make_field_extra_expr_node (PARSER_CONTEXT * parser);
static PT_NODE *pt_make_field_key_type_expr_node (PARSER_CONTEXT * parser);
#endif /* ENABLE_UNUSED_FUNCTION */
static PT_NODE *pt_make_sort_spec_with_identifier (PARSER_CONTEXT * parser, const char *identifier,
PT_MISC_TYPE sort_mode);
static PT_NODE *pt_make_sort_spec_with_number (PARSER_CONTEXT * parser, const int number_pos, PT_MISC_TYPE sort_mode);
#if defined(ENABLE_UNUSED_FUNCTION)
static PT_NODE *pt_make_collection_type_subquery_node (PARSER_CONTEXT * parser, const char *table_name);
#endif /* ENABLE_UNUSED_FUNCTION */
static PT_NODE *pt_make_dummy_query_check_table (PARSER_CONTEXT * parser, const char *table_name);
static PT_NODE *pt_make_query_user_groups (PARSER_CONTEXT * parser, const char *user_name);
static void pt_help_show_create_table (PARSER_CONTEXT * parser, PT_NODE * table_name, string_buffer & strbuf);
static int pt_get_query_limit_from_orderby_for (PARSER_CONTEXT * parser, PT_NODE * orderby_for, DB_VALUE * upper_limit,
bool * has_limit);
static int pt_get_query_limit_from_limit (PARSER_CONTEXT * parser, PT_NODE * limit, DB_VALUE * limit_val,
bool add_offset);
static bool pt_check_removable_like_condition (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * expr);
static PT_NODE *pt_create_delete_stmt (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * target_class);
static PT_NODE *pt_is_spec_referenced (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *pt_rewrite_derived_for_upd_del (PARSER_CONTEXT * parser, PT_NODE * spec, PT_SPEC_FLAG what_for,
bool add_as_attr);
static PT_NODE *pt_process_spec_for_delete (PARSER_CONTEXT * parser, PT_NODE * spec);
static PT_NODE *pt_process_spec_for_update (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * name);
static bool check_arg_valid (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_meta, int arg_num, PT_NODE * val);
static PT_NODE *pt_resolve_showstmt_args_unnamed (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_infos,
int arg_info_count, PT_NODE * args);
static PT_NODE *pt_resolve_showstmt_args_named (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_infos,
int arg_info_count, PT_NODE * args);
static bool pt_convert_dblink_select_query (PARSER_CONTEXT * parser, PT_NODE * query_stmt, SERVER_NAME_LIST * snl);
static void pt_convert_dblink_dml_query (PARSER_CONTEXT * parser, PT_NODE * node,
int local_upd, int remote_upd, SERVER_NAME_LIST * snl);
#define NULL_ATTRID -1
/*
* pt_make_integer_value () -
* return: return a PT_NODE for the integer value
* parser(in): parser context
* value_int(in): integer value to make up a PT_NODE
*/
PT_NODE *
pt_make_integer_value (PARSER_CONTEXT * parser, const int value_int)
{
PT_NODE *node;
node = parser_new_node (parser, PT_VALUE);
if (node)
{
node->type_enum = PT_TYPE_INTEGER;
node->info.value.data_value.i = value_int;
}
return node;
}
/*
* pt_make_string_value () -
* return: return a PT_NODE for the string value
* parser(in): parser context
* value_string(in): string value to make up a PT_NODE
*/
PT_NODE *
pt_make_string_value (PARSER_CONTEXT * parser, const char *value_string)
{
PT_NODE *node;
node = parser_new_node (parser, PT_VALUE);
if (node)
{
if (value_string == NULL)
{
node->type_enum = PT_TYPE_NULL;
}
else
{
node->info.value.data_value.str = pt_append_bytes (parser, NULL, value_string, strlen (value_string));
node->type_enum = PT_TYPE_CHAR;
node->info.value.string_type = ' ';
PT_NODE_PRINT_VALUE_TO_TEXT (parser, node);
}
}
return node;
}
/*
* pt_and () - Create a PT_AND node with arguments of the nodes passed in
* return:
* parser(in):
* arg1(in):
* arg2(in):
*/
PT_NODE *
pt_and (PARSER_CONTEXT * parser, const PT_NODE * arg1, const PT_NODE * arg2)
{
PT_NODE *node;
node = parser_new_node (parser, PT_EXPR);
if (node)
{
node->info.expr.op = PT_AND;
node->info.expr.arg1 = (PT_NODE *) arg1;
node->info.expr.arg2 = (PT_NODE *) arg2;
}
return node;
}
/*
* pt_union () - Create a PT_UNION node with arguments of the nodes passed in
* return:
* parser(in):
* arg1(in/out):
* arg2(in/out):
*/
PT_NODE *
pt_union (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2)
{
PT_NODE *node;
int arg1_corr = 0, arg2_corr = 0, corr;
node = parser_new_node (parser, PT_UNION);
if (node)
{
/* set query id # */
node->info.query.id = (UINTPTR) node;
node->info.query.q.union_.arg1 = arg1;
node->info.query.q.union_.arg2 = arg2;
if (arg1)
{
arg1->info.query.is_subquery = PT_IS_UNION_SUBQUERY;
arg1_corr = arg1->info.query.correlation_level;
}
if (arg2)
{
arg2->info.query.is_subquery = PT_IS_UNION_SUBQUERY;
arg2_corr = arg2->info.query.correlation_level;
}
if (arg1_corr)
{
corr = arg1_corr;
if (arg2_corr && arg2_corr < corr)
{
corr = arg2_corr;
}
}
else
{
corr = arg2_corr;
}
if (corr)
{
corr--;
}
node->info.query.correlation_level = corr;
}
return node;
}
/*
* pt_name () - Create a PT_NAME node using the name string passed in
* return:
* parser(in):
* name(in):
*/
PT_NODE *
pt_name (PARSER_CONTEXT * parser, const char *name)
{
PT_NODE *node;
node = parser_new_node (parser, PT_NAME);
if (node)
{
node->info.name.original = pt_append_string (parser, NULL, name);
}
return node;
}
/*
* pt_table_option () - Create a PT_TABLE_OPTION node
* return: the new node or NULL on error
* parser(in):
* option(in): the type of the table option
* val(in): a value associated with the table option or NULL
*/
PT_NODE *
pt_table_option (PARSER_CONTEXT * parser, const PT_TABLE_OPTION_TYPE option, PT_NODE * val)
{
PT_NODE *node;
node = parser_new_node (parser, PT_TABLE_OPTION);
if (node)
{
node->info.table_option.option = option;
node->info.table_option.val = val;
}
return node;
}
/*
* pt_expression () - Create a PT_EXPR node using the arguments passed in
* return:
* parser(in):
* op(in): the expression operation type
* arg1(in):
* arg2(in):
* arg3(in):
*/
PT_NODE *
pt_expression (PARSER_CONTEXT * parser, PT_OP_TYPE op, PT_NODE * arg1, PT_NODE * arg2, PT_NODE * arg3)
{
PT_NODE *node;
node = parser_new_node (parser, PT_EXPR);
if (node)
{
node->info.expr.op = op;
node->info.expr.arg1 = arg1;
node->info.expr.arg2 = arg2;
node->info.expr.arg3 = arg3;
}
return node;
}
PT_NODE *
pt_expression_0 (PARSER_CONTEXT * parser, PT_OP_TYPE op)
{
return pt_expression (parser, op, NULL, NULL, NULL);
}
PT_NODE *
pt_expression_1 (PARSER_CONTEXT * parser, PT_OP_TYPE op, PT_NODE * arg1)
{
return pt_expression (parser, op, arg1, NULL, NULL);
}
PT_NODE *
pt_expression_2 (PARSER_CONTEXT * parser, PT_OP_TYPE op, PT_NODE * arg1, PT_NODE * arg2)
{
return pt_expression (parser, op, arg1, arg2, NULL);
}
PT_NODE *
pt_expression_3 (PARSER_CONTEXT * parser, PT_OP_TYPE op, PT_NODE * arg1, PT_NODE * arg2, PT_NODE * arg3)
{
return pt_expression (parser, op, arg1, arg2, arg3);
}
/*
* pt_node_list () - Create a PT_NODE_LIST node using the arguments passed in
* return:
* parser(in):
* list_type(in):
* list(in):
*/
PT_NODE *
pt_node_list (PARSER_CONTEXT * parser, PT_MISC_TYPE list_type, PT_NODE * list)
{
PT_NODE *node;
node = parser_new_node (parser, PT_NODE_LIST);
if (node)
{
node->info.node_list.list_type = list_type;
node->info.node_list.list = list;
}
return node;
}
/*
* pt_entity () - Create a PT_SPEC node using the node string passed
* for the entity name
* return:
* parser(in):
* entity_name(in):
* range_var(in):
* flat_list(in):
*/
PT_NODE *
pt_entity (PARSER_CONTEXT * parser, const PT_NODE * entity_name, const PT_NODE * range_var, const PT_NODE * flat_list)
{
PT_NODE *node;
node = parser_new_node (parser, PT_SPEC);
if (node)
{
node->info.spec.entity_name = (PT_NODE *) entity_name;
node->info.spec.range_var = (PT_NODE *) range_var;
node->info.spec.flat_entity_list = (PT_NODE *) flat_list;
}
return node;
}
/*
* pt_tuple_value () - create a tuple_value node with the specified name and
* index in a query result
* return : new node or NULL
* parser (in) : parser context
* name (in) : name node
* cursor_p (in) : cursor for which to fetch tuple value
* index (in) : index in cursor column list
*/
PT_NODE *
pt_tuple_value (PARSER_CONTEXT * parser, PT_NODE * name, CURSOR_ID * cursor_p, int index)
{
PT_NODE *node;
node = parser_new_node (parser, PT_TUPLE_VALUE);
if (node)
{
node->info.tuple_value.name = name;
node->info.tuple_value.index = index;
node->info.tuple_value.cursor_p = cursor_p;
}
return node;
}
/*
* pt_insert_value () - Creates an insert value setting node argument as
* original node.
*
* return : PT_INSERT_VALUE node.
* parser (in) : Parser context.
* node (in) : Original node.
*/
PT_NODE *
pt_insert_value (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_NODE *insert_val = parser_new_node (parser, PT_INSERT_VALUE);
if (insert_val != NULL)
{
insert_val->info.insert_value.original_node = node;
}
return insert_val;
}
/*
* pt_datatypes_match () -
* return: 1 if the two data types are not virtual objects or the same
* class of virtual object. 0 otherwise.
* a(in):
* b(in): data types to compare
*/
static bool
pt_datatypes_match (const PT_NODE * a, const PT_NODE * b)
{
if (!a && !b)
{
return true; /* both non objects, ok */
}
if (!a || !b)
{
return true; /* typed and untyped node, ignore difference */
}
if (a->type_enum != PT_TYPE_OBJECT && b->type_enum != PT_TYPE_OBJECT)
{
return true; /* both non objects again, ok */
}
if (a->type_enum != PT_TYPE_OBJECT || b->type_enum != PT_TYPE_OBJECT)
{
return false;
}
if (a->info.data_type.virt_object != b->info.data_type.virt_object)
{
return false;
}
/* both the same flavor virtual objects */
return true;
}
/*
* pt_name_equal () - Tests name nodes for equality
* return: true on equal
* parser(in):
* name1(in):
* name2(in):
*
* Note :
* Assumes semantic processing has resolved name information
*/
bool
pt_name_equal (PARSER_CONTEXT * parser, const PT_NODE * name1, const PT_NODE * name2)
{
if (!name1 || !name2)
{
return false;
}
CAST_POINTER_TO_NODE (name1);
CAST_POINTER_TO_NODE (name2);
if (name1->node_type != PT_NAME)
{
return false;
}
if (name2->node_type != PT_NAME)
{
return false;
}
/* identity */
if (name1 == name2)
{
return true;
}
/* are the id's equal? */
if (name1->info.name.spec_id != name2->info.name.spec_id)
{
return false;
}
/* raw names the same? (NULL not allowed here) */
if (!name1->info.name.original)
{
return false;
}
if (!name2->info.name.original)
{
return false;
}
if (name1->info.name.meta_class != name2->info.name.meta_class)
{
/* check for equivalence class PT_SHARED == PT_NORMAL */
if (name1->info.name.meta_class != PT_SHARED && name1->info.name.meta_class != PT_NORMAL)
{
return false;
}
if (name2->info.name.meta_class != PT_SHARED && name2->info.name.meta_class != PT_NORMAL)
{
return false;
}
}
if (intl_identifier_casecmp (name1->info.name.original, name2->info.name.original) != 0)
{
return false;
}
if (!pt_datatypes_match (name1->data_type, name2->data_type))
{
return false;
}
return true;
}
/*
* pt_find_name () - Looks for a name on a list
* return:
* parser(in):
* name(in):
* list(in):
*/
PT_NODE *
pt_find_name (PARSER_CONTEXT * parser, const PT_NODE * name, const PT_NODE * list)
{
while (list)
{
if (pt_name_equal (parser, name, list))
{
return (PT_NODE *) list;
}
list = list->next;
}
return NULL;
}
/*
* pt_is_aggregate_function () -
* return: true in arg if node is a PT_FUNCTION
* node with a PT_MIN, PT_MAX, PT_SUM, PT_AVG, or PT_COUNT type
* parser(in):
* node(in):
*/
bool
pt_is_aggregate_function (PARSER_CONTEXT * parser, const PT_NODE * node)
{
FUNC_CODE function_type;
if (node->node_type == PT_FUNCTION)
{
function_type = node->info.function.function_type;
if (!node->info.function.analytic.is_analytic
&& (function_type == PT_MIN || function_type == PT_MAX || function_type == PT_SUM || function_type == PT_AVG
|| function_type == PT_STDDEV || function_type == PT_STDDEV_POP || function_type == PT_STDDEV_SAMP
|| function_type == PT_VARIANCE || function_type == PT_VAR_POP || function_type == PT_VAR_SAMP
|| function_type == PT_GROUPBY_NUM || function_type == PT_COUNT || function_type == PT_COUNT_STAR
|| function_type == PT_AGG_BIT_AND || function_type == PT_AGG_BIT_OR || function_type == PT_AGG_BIT_XOR
|| function_type == PT_GROUP_CONCAT || function_type == PT_MEDIAN || function_type == PT_PERCENTILE_CONT
|| function_type == PT_PERCENTILE_DISC || function_type == PT_CUME_DIST
|| function_type == PT_PERCENT_RANK || function_type == PT_JSON_ARRAYAGG
|| function_type == PT_JSON_OBJECTAGG))
{
return true;
}
}
return false;
}
/*
* pt_is_analytic_function () -
* return: true in arg if node is an analytic function
* parser(in):
* node(in):
*/
bool
pt_is_analytic_function (PARSER_CONTEXT * parser, const PT_NODE * node)
{
if (node != NULL && node->node_type == PT_FUNCTION && node->info.function.analytic.is_analytic)
{
return true;
}
else
{
return false;
}
}
/*
* pt_is_expr_wrapped_function () -
* return: true if node is a PT_FUNCTION node with which may be evaluated
* like an expression
* parser(in): parser context
* node(in): PT_FUNTION node
*/
bool
pt_is_expr_wrapped_function (PARSER_CONTEXT * parser, const PT_NODE * node)
{
FUNC_CODE function_type;
if (node->node_type == PT_FUNCTION)
{
function_type = node->info.function.function_type;
if (function_type == F_INSERT_SUBSTRING
|| function_type == F_ELT
|| function_type == F_JSON_ARRAY
|| function_type == F_JSON_ARRAY_APPEND || function_type == F_JSON_ARRAY_INSERT
|| function_type == F_JSON_CONTAINS || function_type == F_JSON_CONTAINS_PATH
|| function_type == F_JSON_DEPTH
|| function_type == F_JSON_EXTRACT
|| function_type == F_JSON_GET_ALL_PATHS
|| function_type == F_JSON_INSERT
|| function_type == F_JSON_KEYS
|| function_type == F_JSON_LENGTH
|| function_type == F_JSON_MERGE || function_type == F_JSON_MERGE_PATCH
|| function_type == F_JSON_OBJECT
|| function_type == F_JSON_PRETTY
|| function_type == F_JSON_QUOTE
|| function_type == F_JSON_REMOVE
|| function_type == F_JSON_REPLACE
|| function_type == F_JSON_SEARCH
|| function_type == F_JSON_SET
|| function_type == F_JSON_TYPE || function_type == F_JSON_UNQUOTE || function_type == F_JSON_VALID
|| function_type == F_REGEXP_COUNT || function_type == F_REGEXP_INSTR || function_type == F_REGEXP_LIKE
|| function_type == F_REGEXP_REPLACE || function_type == F_REGEXP_SUBSTR)
{
return true;
}
}
return false;
}
/*
* pt_is_json_function () -
* return: true if node is a json function
* parser(in): parser context
* node(in): PT_FUNTION node
*/
bool
pt_is_json_function (PARSER_CONTEXT * parser, const PT_NODE * node)
{
FUNC_CODE function_type;
if (node->node_type == PT_FUNCTION)
{
function_type = node->info.function.function_type;
switch (function_type)
{
case F_JSON_ARRAY:
case F_JSON_ARRAY_APPEND:
case F_JSON_ARRAY_INSERT:
case F_JSON_CONTAINS:
case F_JSON_CONTAINS_PATH:
case F_JSON_DEPTH:
case F_JSON_EXTRACT:
case F_JSON_GET_ALL_PATHS:
case F_JSON_INSERT:
case F_JSON_KEYS:
case F_JSON_LENGTH:
case F_JSON_MERGE:
case F_JSON_MERGE_PATCH:
case F_JSON_OBJECT:
case F_JSON_PRETTY:
case F_JSON_QUOTE:
case F_JSON_REMOVE:
case F_JSON_REPLACE:
case F_JSON_SEARCH:
case F_JSON_SET:
case F_JSON_TYPE:
case F_JSON_UNQUOTE:
case F_JSON_VALID:
case PT_JSON_ARRAYAGG:
case PT_JSON_OBJECTAGG:
return true;
default:
return false;
}
}
return false;
}
/*
* pt_find_spec_in_statement () - find the node spec in given statement
* return: the spec with same id as the name, or NULL
* parser(in):
* stmt(in):
* name(in):
*/
PT_NODE *
pt_find_spec_in_statement (PARSER_CONTEXT * parser, const PT_NODE * stmt, const PT_NODE * name)
{
PT_NODE *spec = NULL;
switch (stmt->node_type)
{
case PT_SPEC:
spec = pt_find_spec (parser, stmt, name);
break;
case PT_DELETE:
spec = pt_find_spec (parser, stmt->info.delete_.spec, name);
if (spec == NULL)
{
spec = pt_find_spec (parser, stmt->info.delete_.class_specs, name);
}
break;
case PT_UPDATE:
spec = pt_find_spec (parser, stmt->info.update.spec, name);
if (spec == NULL)
{
spec = pt_find_spec (parser, stmt->info.update.class_specs, name);
}
break;
case PT_MERGE:
spec = pt_find_spec (parser, stmt->info.merge.into, name);
if (spec == NULL)
{
spec = pt_find_spec (parser, stmt->info.merge.using_clause, name);
}
break;
default:
break;
}
return (PT_NODE *) spec;
}
/*
* pt_find_spec () -
* return: the spec in the from list with same id as the name, or NULL
* parser(in):
* from(in):
* name(in):
*/
PT_NODE *
pt_find_spec (PARSER_CONTEXT * parser, const PT_NODE * from, const PT_NODE * name)
{
while (from && from->info.spec.id != name->info.name.spec_id)
{
/* check for path-entities */
if (from->info.spec.path_entities && pt_find_spec (parser, from->info.spec.path_entities, name))
{
break;
}
from = from->next;
}
return (PT_NODE *) from;
}
/*
* pt_find_aggregate_names - find names within select_stack
* returns: unmodified tree
* parser(in): parser context
* tree(in): tree to search into
* arg(in/out): a PT_AGG_NAME_INFO structure
* continue_walk(in/out): walk type
*
* NOTE: this function is called on an aggregate function or it's arguments
* and it returns the maximum level within the stack that owns PT_NAMEs within
* the called-on tree.
*/
PT_NODE *
pt_find_aggregate_names (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
PT_AGG_NAME_INFO *info = (PT_AGG_NAME_INFO *) arg;
PT_NODE *node = NULL, *select_stack;
int level = 0;
bool max_level_has_gby = false;
switch (tree->node_type)
{
case PT_SELECT:
*continue_walk = PT_LIST_WALK;
break;
case PT_DOT_:
node = tree->info.dot.arg2;
break;
case PT_NAME:
node = tree;
break;
default:
break;
}
if (node == NULL || node->node_type != PT_NAME)
{
/* nothing to do */
return tree;
}
else
{
info->name_count++;
}
select_stack = info->select_stack;
while (select_stack != NULL)
{
PT_NODE *select = select_stack->info.pointer.node;
if (select == NULL || select->node_type != PT_SELECT)
{
PT_INTERNAL_ERROR (parser, "stack entry is not SELECT");
return tree;
}
if (level > info->max_level && pt_find_spec (parser, select->info.query.q.select.from, node))
{
/* found! */
info->max_level = level;
max_level_has_gby = (select->info.query.q.select.group_by != NULL);
}
/* next stack level */
select_stack = select_stack->next;
level++;
}
/* Note: we need to deal with corelated queries when an aggregate function in the subquery contains arguments in
* outer-level queries. For example: 'select (select sum(t1.i) from t2) from t1;' It should be evaluted over the
* rows of the nearest outer level. */
if (!max_level_has_gby)
{
info->max_level = level - 1;
}
return tree;
}
/*
* pt_find_aggregate_functions_pre () - finds aggregate functions in a tree
* returns: unmodified tree
* parser(in): parser context
* tree(in): tree to search into
* arg(in/out): a PT_AGG_FIND_INFO structure
* continue_walk(in/out): walk type
*
* NOTE: this routine searches for aggregate functions that belong to the
* SELECT statement at the base of the stack
*/
PT_NODE *
pt_find_aggregate_functions_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
PT_AGG_FIND_INFO *info = (PT_AGG_FIND_INFO *) arg;
PT_NODE *select_stack = info->select_stack;
PT_NODE *stack_top = select_stack;
if (tree == NULL)
{
/* nothing to do */
return tree;
}
while (stack_top != NULL && stack_top->next != NULL)
{
stack_top = stack_top->next;
}
if (stack_top && stack_top->info.pointer.node && stack_top->info.pointer.node->node_type == PT_SELECT
&& stack_top->info.pointer.node->info.query.q.select.where == tree)
{
/* subqueries of WHERE clause will not be walked for this parent query; they must be treated separately as they
* own any aggregates referring upper-level names */
info->stop_on_subquery = true;
}
if (pt_is_aggregate_function (parser, tree))
{
if (tree->info.function.function_type == PT_COUNT_STAR || tree->info.function.function_type == PT_GROUPBY_NUM)
{
/* found count(*), groupby_num() */
if (select_stack == NULL)
{
/* no spec stack, this was not called on a select */
info->out_of_context_count++;
}
else if (select_stack->next == NULL)
{
/* first level on spec stack, this function belongs to the callee statement */
info->base_count++;
if (tree->info.function.function_type == PT_COUNT_STAR)
{
/* can't use count star in loose scan */
info->disable_loose_scan = true;
}
}
}
else
{
PT_AGG_NAME_INFO name_info;
name_info.select_stack = info->select_stack;
name_info.max_level = -1;
name_info.name_count = 0;
(void) parser_walk_tree (parser, tree->info.function.arg_list, pt_find_aggregate_names, &name_info,
pt_continue_walk, NULL);
if (name_info.max_level == 0)
{
/* only names from base SELECT were found */
info->base_count++;
if (tree->info.function.all_or_distinct == PT_ALL && tree->info.function.function_type != PT_MIN
&& tree->info.function.function_type != PT_MAX)
{
/* only DISTINCT allowed for functions other than MIN/MAX */
info->disable_loose_scan = true;
}
}
else if (name_info.max_level < 0 && name_info.name_count > 0)
{
/* no names within stack limit were found */
info->out_of_context_count++;
}
else if (name_info.name_count == 0)
{
/* no names were found at all */
if (select_stack == NULL)
{
info->out_of_context_count++;
}
else if (select_stack->next == NULL)
{
info->base_count++;
}
}
}
}
else if (tree->node_type == PT_SELECT)
{
PT_NODE *spec;
/* we must evaluate nexts before pushing SELECT on stack */
if (tree->next)
{
(void) parser_walk_tree (parser, tree->next, pt_find_aggregate_functions_pre, info,
pt_find_aggregate_functions_post, info);
*continue_walk = PT_LEAF_WALK;
}
/* if we encountered a subquery while walking where clause, stop this walk and make subquery owner of all
* aggregate functions that reference upper-level names */
if (info->stop_on_subquery)
{
PT_AGG_FIND_INFO sub_info;
sub_info.base_count = 0;
sub_info.out_of_context_count = 0;
sub_info.select_stack = NULL;
sub_info.stop_on_subquery = false;
(void) parser_walk_tree (parser, tree, pt_find_aggregate_functions_pre, &sub_info,
pt_find_aggregate_functions_post, &sub_info);
if (sub_info.out_of_context_count > 0)
{
/* mark as agg select; base_count > 0 case will be handled later on */
PT_SELECT_INFO_SET_FLAG (tree, PT_SELECT_INFO_HAS_AGG);
}
*continue_walk = PT_STOP_WALK;
}
/* don't get confused by uncorrelated, set-derived subqueries. */
if (tree->info.query.correlation_level == 0 && (spec = tree->info.query.q.select.from)
&& spec->info.spec.derived_table && spec->info.spec.derived_table_type == PT_IS_SET_EXPR)
{
/* no need to dive into the uncorrelated, set-derived subqueries */
*continue_walk = PT_STOP_WALK;
}
/* stack push */
info->select_stack = pt_pointer_stack_push (parser, info->select_stack, tree);
}
return tree;
}
/*
* pt_find_aggregate_functions_post () - finds aggregate functions in a tree
* returns: unmodified tree
* parser(in): parser context
* tree(in): tree to search into
* arg(in/out): a PT_AGG_FIND_INFO structure
* continue_walk(in/out): walk type
*/
PT_NODE *
pt_find_aggregate_functions_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
PT_AGG_FIND_INFO *info = (PT_AGG_FIND_INFO *) arg;
if (tree->node_type == PT_SELECT)
{
info->select_stack = pt_pointer_stack_pop (parser, info->select_stack, NULL);
}
else
{
PT_NODE *stack_top = info->select_stack;
while (stack_top != NULL && stack_top->next != NULL)
{
stack_top = stack_top->next;
}
if (stack_top && stack_top->info.pointer.node && stack_top->info.pointer.node->node_type == PT_SELECT
&& stack_top->info.pointer.node->info.query.q.select.where == tree)
{
info->stop_on_subquery = false;
}
}
/* nothing can stop us! */
*continue_walk = PT_CONTINUE_WALK;
return tree;
}
/*
* pt_is_order_sensitive_agg_post () -
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_is_order_sensitive_agg_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *order_sensitive_agg = (bool *) arg;
if (*order_sensitive_agg)
{
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_group_concat () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an analytic function node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_group_concat (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_group_concat = (bool *) arg;
if (tree && tree->node_type == PT_FUNCTION && tree->info.function.function_type == PT_GROUP_CONCAT)
{
*has_group_concat = true;
}
if (*has_group_concat)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_order_sensitive_agg () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an order-sensitive aggrigation function node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_order_sensitive_agg (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_group_concat = (bool *) arg;
if (tree && tree->node_type == PT_FUNCTION &&
(tree->info.function.function_type == PT_GROUP_CONCAT
|| tree->info.function.function_type == PT_CUME_DIST
|| tree->info.function.function_type == PT_PERCENT_RANK
|| tree->info.function.function_type == PT_PERCENTILE_CONT
|| tree->info.function.function_type == PT_PERCENTILE_DISC
|| tree->info.function.function_type == F_SEQUENCE || pt_is_json_function (parser, tree)))
{
*has_group_concat = true;
}
if (*has_group_concat)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_analytic_node_post () -
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_is_analytic_node_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_analytic = (bool *) arg;
if (*has_analytic)
{
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_analytic_node () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an analytic function node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_analytic_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_analytic = (bool *) arg;
if (tree && tree->node_type == PT_FUNCTION && tree->info.function.analytic.is_analytic)
{
*has_analytic = true;
}
if (*has_analytic)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_has_non_idx_sarg_coll_pre () - pre function for determining if a tree has
* contains a node with a collation that
* renders it unusable for key range/filter
* returns: input node
* parser(in): parser to use
* tree(in): tree node to analyze
* arg(out): integer, will be set to "1" if node is found unfit
* continue_walk(out): to be set to PT_STOP_WALK where necessary
*/
PT_NODE *
pt_has_non_idx_sarg_coll_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
int *mark = (int *) arg;
assert (tree != NULL);
assert (arg != NULL);
assert (continue_walk != NULL);
if (PT_HAS_COLLATION (tree->type_enum) && (tree->data_type != NULL))
{
int collation_id = tree->data_type->info.data_type.collation_id;
LANG_COLLATION *lang_coll = lang_get_collation (collation_id);
if (!lang_coll->options.allow_index_opt)
{
*mark = 1;
*continue_walk = PT_STOP_WALK;
}
}
return tree;
}
/*
* pt_is_inst_or_orderby_num_node_post () -
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_is_inst_or_orderby_num_node_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_inst_orderby_num = (bool *) arg;
if (*has_inst_orderby_num)
{
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_inst_num_node_post () -
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_is_inst_num_node_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_inst_num = (bool *) arg;
if (*has_inst_num)
{
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_inst_num_node () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an INST_NUM expression node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_inst_num_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_inst_num = (bool *) arg;
if (PT_IS_INSTNUM (tree))
{
*has_inst_num = true;
}
if (*has_inst_num)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_inst_or_orderby_num_node () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an INST_NUM or ORDERBY_NUM expression node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_inst_or_orderby_num_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_inst_orderby_num = (bool *) arg;
if (PT_IS_INSTNUM (tree) || PT_IS_ORDERBYNUM (tree))
{
*has_inst_orderby_num = true;
}
if (*has_inst_orderby_num)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_inst_or_inst_num_node () -
* return:
* parser(in):
* tree(in):
* arg(in/out): true if node is an INST_NUM or ORDERBY_NUM or GROUPBY_NUM expression node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_inst_or_inst_num_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *has_inst_orderby_num = (bool *) arg;
if (PT_IS_INSTNUM (tree) || PT_IS_ORDERBYNUM (tree) || PT_IS_GROUPBYNUM (tree))
{
*has_inst_orderby_num = true;
}
if (*has_inst_orderby_num)
{
*continue_walk = PT_STOP_WALK;
}
else if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return tree;
}
/*
* pt_is_method_call_node () -
* return:
* parser(in):
* node(in):
* arg(in/out): true if node is a method call node
* continue_walk(in/out):
*/
PT_NODE *
pt_is_method_call_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *is_method_call = (bool *) arg;
*continue_walk = PT_CONTINUE_WALK;
if (tree != NULL && tree->node_type == PT_METHOD_CALL)
{
*is_method_call = true;
*continue_walk = PT_STOP_WALK;
}
return tree;
}
/*
* pt_is_ddl_statement () - test PT_NODE statement types,
* without exposing internals
* return:
* node(in):
*/
int
pt_is_ddl_statement (const PT_NODE * node)
{
if (node)
{
switch (node->node_type)
{
case PT_ALTER:
case PT_ALTER_INDEX:
case PT_ALTER_SERIAL:
case PT_ALTER_STORED_PROCEDURE:
case PT_ALTER_TRIGGER:
case PT_ALTER_USER:
case PT_CREATE_ENTITY:
case PT_CREATE_INDEX:
case PT_CREATE_SERIAL:
case PT_CREATE_STORED_PROCEDURE:
case PT_CREATE_TRIGGER:
case PT_CREATE_USER:
case PT_DROP:
case PT_DROP_INDEX:
case PT_DROP_SERIAL:
case PT_DROP_STORED_PROCEDURE:
case PT_DROP_TRIGGER:
case PT_DROP_USER:
case PT_GRANT:
case PT_RENAME:
case PT_REVOKE:
case PT_REMOVE_TRIGGER:
case PT_RENAME_TRIGGER:
case PT_UPDATE_STATS:
/* TODO: check it */
case PT_CREATE_SERVER:
case PT_DROP_SERVER:
case PT_RENAME_SERVER:
case PT_ALTER_SERVER:
case PT_TRUNCATE:
case PT_ALTER_SYNONYM:
case PT_CREATE_SYNONYM:
case PT_DROP_SYNONYM:
case PT_RENAME_SYNONYM:
return true;
default:
break;
}
}
return false;
}
/*
* pt_is_method_call () -
* return:
* node(in/out):
*/
int
pt_is_method_call (PT_NODE * node)
{
if (node == NULL)
{
return false;
}
node = pt_get_end_path_node (node);
return (PT_IS_METHOD (node));
}
/*
* pt_is_attr () -
* return:
* node(in/out):
*/
int
pt_is_attr (PT_NODE * node)
{
if (node == NULL)
{
return false;
}
node = pt_get_end_path_node (node);
if (node->node_type == PT_NAME)
{
if (node->info.name.meta_class == PT_NORMAL || node->info.name.meta_class == PT_SHARED
|| node->info.name.meta_class == PT_OID_ATTR || node->info.name.meta_class == PT_VID_ATTR)
{
return true;
}
}
return false;
}
/*
* pt_is_function_index_expression () - check for function index expression
* return: true if function index expression, false otherwise
* node(in/out): PT_EXPR node
*/
int
pt_is_function_index_expression (PT_NODE * node)
{
if (node == NULL || node->node_type != PT_EXPR)
{
return false;
}
if (!PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_FUNCTION_INDEX))
{
return false;
}
return true;
}
/*
* pt_is_pseudocolumn_node() -
* return:
* tree(in/out):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_is_pseudocolumn_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
int *found = (int *) arg;
if (tree->node_type == PT_EXPR)
{
if (tree->info.expr.op == PT_LEVEL || tree->info.expr.op == PT_CONNECT_BY_ISLEAF
|| tree->info.expr.op == PT_CONNECT_BY_ISCYCLE)
{
*found = 1;
*continue_walk = PT_STOP_WALK;
}
}
return tree;
}
/*
* pt_instnum_compatibility () -
* return:
* expr(in/out):
*/
int
pt_instnum_compatibility (PT_NODE * expr)
{
PT_NODE *arg1 = NULL, *arg2 = NULL, *arg3 = NULL;
if (expr->node_type != PT_EXPR)
{
return true;
}
/* attr and subquery is not compatible with inst_num() */
if (expr->info.expr.op != PT_IF)
{
arg1 = expr->info.expr.arg1;
if (arg1)
{
if (COMPATIBLE_WITH_INSTNUM (arg1))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_C);
}
if (NOT_COMPATIBLE_WITH_INSTNUM (arg1))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_NC);
}
}
}
arg2 = expr->info.expr.arg2;
if (arg2)
{
if (COMPATIBLE_WITH_INSTNUM (arg2))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_C);
}
if (NOT_COMPATIBLE_WITH_INSTNUM (arg2))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_NC);
}
}
if (expr->info.expr.op != PT_CASE && expr->info.expr.op != PT_DECODE)
{
arg3 = expr->info.expr.arg3;
if (arg3)
{
if (COMPATIBLE_WITH_INSTNUM (arg3))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_C);
}
if (NOT_COMPATIBLE_WITH_INSTNUM (arg3))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_NC);
}
}
}
switch (expr->info.expr.op)
{
case PT_AND:
/* AND hides inst_num() compatibility */
return true;
case PT_IS_NULL:
case PT_IS_NOT_NULL:
case PT_EXISTS:
case PT_ASSIGN:
case PT_IFNULL:
/* those operator cannot have inst_num() */
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_INSTNUM_NC);
break;
default:
break;
}
/* detect semantic error in pt_semantic_check_local */
if (PT_EXPR_INFO_IS_FLAGED (expr, PT_EXPR_INFO_INSTNUM_NC))
{
if (expr->info.expr.op != PT_IF)
{
if (arg1 && pt_is_instnum (arg1))
{
PT_EXPR_INFO_SET_FLAG (arg1, PT_EXPR_INFO_INSTNUM_NC);
}
}
if (arg2 && pt_is_instnum (arg2))
{
PT_EXPR_INFO_SET_FLAG (arg2, PT_EXPR_INFO_INSTNUM_NC);
}
if (expr->info.expr.op != PT_CASE && expr->info.expr.op != PT_DECODE)
{
if (arg3 && pt_is_instnum (arg3))
{
PT_EXPR_INFO_SET_FLAG (arg3, PT_EXPR_INFO_INSTNUM_NC);
}
}
}
/* expression is not compatible with inst_num() */
if (PT_EXPR_INFO_IS_FLAGED (expr, PT_EXPR_INFO_INSTNUM_C) && PT_EXPR_INFO_IS_FLAGED (expr, PT_EXPR_INFO_INSTNUM_NC))
{
/* to prevent repeated error */
PT_EXPR_INFO_CLEAR_FLAG (expr, PT_EXPR_INFO_INSTNUM_C);
return false;
}
return true;
}
/*
* pt_groupbynum_compatibility () -
* return:
* expr(in):
*/
int
pt_groupbynum_compatibility (PT_NODE * expr)
{
PT_NODE *arg1, *arg2, *arg3;
if (expr->node_type != PT_EXPR)
{
return true;
}
/* attr and subquery is not compatible with groupby_num() */
arg1 = expr->info.expr.arg1;
if (arg1)
{
if (COMPATIBLE_WITH_GROUPBYNUM (arg1))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_C);
}
if (NOT_COMPATIBLE_WITH_GROUPBYNUM (arg1))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_NC);
}
}
arg2 = expr->info.expr.arg2;
if (arg2)
{
if (COMPATIBLE_WITH_GROUPBYNUM (arg2))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_C);
}
if (NOT_COMPATIBLE_WITH_GROUPBYNUM (arg2))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_NC);
}
}
arg3 = expr->info.expr.arg3;
if (arg3)
{
if (COMPATIBLE_WITH_GROUPBYNUM (arg3))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_C);
}
if (NOT_COMPATIBLE_WITH_GROUPBYNUM (arg3))
{
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_NC);
}
}
switch (expr->info.expr.op)
{
case PT_AND:
/* AND hides groupby_num() compatibility */
return true;
case PT_IS_NULL:
case PT_IS_NOT_NULL:
case PT_EXISTS:
case PT_ASSIGN:
/* those operator cannot have groupby_num() */
PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_NC);
break;
default:
break;
}
/* expression is not compatible with groupby_num() */
if (PT_EXPR_INFO_IS_FLAGED (expr, PT_EXPR_INFO_GROUPBYNUM_C)
&& PT_EXPR_INFO_IS_FLAGED (expr, PT_EXPR_INFO_GROUPBYNUM_NC))
{
/* to prevent repeated error */
PT_EXPR_INFO_CLEAR_FLAG (expr, PT_EXPR_INFO_GROUPBYNUM_C);
return false;
}
return true;
}
/*
* pt_check_instnum_pre () - Identify if the expression tree has inst_num()
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_instnum_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_check_instnum_post () -
* return:
* parser(in):
* node(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_instnum_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *inst_num = (bool *) arg;
if (node->node_type == PT_EXPR && (node->info.expr.op == PT_INST_NUM || node->info.expr.op == PT_ROWNUM))
{
*inst_num = true;
}
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_CONTINUE_WALK;
}
return node;
}
/*
* pt_check_groupbynum_pre () - Identify if the expression has groupby_num()
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_groupbynum_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_check_groupbynum_post () -
* return:
* parser(in):
* node(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_groupbynum_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *grby_num = (bool *) arg;
if (node->node_type == PT_FUNCTION && node->info.function.function_type == PT_GROUPBY_NUM)
{
*grby_num = true;
}
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_CONTINUE_WALK;
}
return node;
}
/*
* pt_check_orderbynum_pre () - Identify if the expression has orderby_num()
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_orderbynum_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_check_orderbynum_post () -
* return:
* parser(in):
* node(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_orderbynum_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *ordby_num = (bool *) arg;
if (node->node_type == PT_EXPR && node->info.expr.op == PT_ORDERBY_NUM)
{
*ordby_num = true;
}
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_CONTINUE_WALK;
}
return node;
}
/*
* pt_check_subquery_pre () - Identify if the expression has sub query
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (node->node_type == PT_SELECT)
{
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_check_subquery_post () -
* return:
* parser(in):
* node(in):
* arg(in/out):
* continue_walk(in/out):
*/
PT_NODE *
pt_check_subquery_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *has_subquery = (bool *) arg;
if (node->node_type == PT_SELECT)
{
if (node->info.query.is_subquery == PT_IS_SUBQUERY)
{
*has_subquery = true;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
}
return node;
}
/*
* pt_expr_disallow_op_pre () - looks if the expression op is in the list
* given as argument and throws an error if
* found
*
* return: node
* parser(in):
* node(in):
* arg(in): integer list with forbidden operators. arg[0] keeps the number of
* operators
* continue_wals (in/out):
*/
PT_NODE *
pt_expr_disallow_op_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int *op_list = (int *) arg;
int i;
if (*continue_walk == PT_STOP_WALK)
{
return node;
}
else if (PT_IS_QUERY_NODE_TYPE (node->node_type))
{
*continue_walk = PT_LIST_WALK;
return node;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
if (!PT_IS_EXPR_NODE (node))
{
return node;
}
assert (op_list != NULL);
for (i = 1; i <= op_list[0]; i++)
{
if (op_list[i] == node->info.expr.op)
{
PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ALLOWED_HERE,
pt_show_binopcode (node->info.expr.op));
}
}
return node;
}
/*
* pt_expr_disallow_op_except_agg () - looks if the expression op is in the list
* given as argument and throws an error if
* found except aggregate function
*
* return: node
* parser(in):
* node(in):
* arg(in): integer list with forbidden operators. arg[0] keeps the number of
* operators
* continue_wals (in/out):
*/
PT_NODE *
pt_expr_disallow_op_except_agg (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int *op_list = (int *) arg;
int i;
if (*continue_walk == PT_STOP_WALK)
{
return node;
}
else if (PT_IS_QUERY_NODE_TYPE (node->node_type) || pt_is_aggregate_function (parser, node))
{
*continue_walk = PT_LIST_WALK;
return node;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
if (!PT_IS_EXPR_NODE (node))
{
return node;
}
assert (op_list != NULL);
for (i = 1; i <= op_list[0]; i++)
{
if (op_list[i] == node->info.expr.op)
{
PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ALLOWED_HERE,
pt_show_binopcode (node->info.expr.op));
}
}
return node;
}
/*
* pt_check_level_expr () - check if expression can be reduced to "LEVEL <= x
* AND ..." or to "LEVEL >= x AND ...".
*
* parser(in): PARSER_CONTEXT
* expr(in): expression PT_NODE
* has_greater(out): can be reduced to LEVEL >= x
* has_lesser(out): can be reduced to LEVEL <= x
*
* NOTE: this was originally designed to check connect by clause in order to
* determine if cycles can be allowed or if we risk to generate infinite
* loops
*/
void
pt_check_level_expr (PARSER_CONTEXT * parser, PT_NODE * expr, bool * has_greater, bool * has_lesser)
{
bool has_greater_1;
bool has_lesser_1;
bool has_greater_2;
bool has_lesser_2;
int op;
PT_NODE *arg1;
PT_NODE *arg2;
*has_greater = 0;
*has_lesser = 0;
if (!expr)
{
return;
}
if (!PT_IS_EXPR_NODE (expr))
{
return;
}
op = expr->info.expr.op;
arg1 = expr->info.expr.arg1;
arg2 = expr->info.expr.arg2;
switch (expr->info.expr.op)
{
case PT_NOT:
/* NOT greater => lesser */
/* NOT lesser => greater */
pt_check_level_expr (parser, arg1, &has_greater_1, &has_lesser_1);
*has_greater = has_lesser_1;
*has_lesser = has_greater_1;
break;
case PT_OR:
/* the OR EXPR will have as result a lesser value or a greater value for LEVEL if both branches have lesser,
* respective greater values for LEVEL */
pt_check_level_expr (parser, arg1, &has_greater_1, &has_lesser_1);
pt_check_level_expr (parser, arg2, &has_greater_2, &has_lesser_2);
*has_greater = has_greater_1 && has_greater_2;
*has_lesser = has_lesser_1 && has_lesser_2;
break;
case PT_AND:
/* the AND EXPR will have as result a lesser value or a greater value for LEVEL if any branch has a lesser,
* respective a greater value for LEVEL */
pt_check_level_expr (parser, arg1, &has_greater_1, &has_lesser_1);
pt_check_level_expr (parser, arg2, &has_greater_2, &has_lesser_2);
*has_greater = has_greater_1 || has_greater_2;
*has_lesser = has_lesser_1 || has_lesser_2;
break;
case PT_EQ:
case PT_LT:
case PT_GT:
case PT_LE:
case PT_GE:
{
bool lhs_level = PT_IS_EXPR_NODE (arg1) && arg1->info.expr.op == PT_LEVEL;
bool rhs_level = PT_IS_EXPR_NODE (arg2) && arg2->info.expr.op == PT_LEVEL;
if ((lhs_level && rhs_level) || (!lhs_level && !rhs_level))
{
/* leave both has_greater and has_lesser as false */
return;
}
if (op == PT_EQ)
{
*has_lesser = true;
*has_greater = true;
}
else if (op == PT_GE || op == PT_GT)
{
if (lhs_level)
{
*has_greater = true;
}
else
{
*has_lesser = true;
}
}
else if (op == PT_LE || op == PT_LT)
{
if (lhs_level)
{
*has_lesser = true;
}
else
{
*has_greater = true;
}
}
}
break;
case PT_BETWEEN:
case PT_RANGE:
case PT_EQ_SOME:
case PT_IS_IN:
if (arg1->info.expr.op == PT_LEVEL)
{
*has_lesser = true;
*has_greater = true;
}
break;
default:
/* leave both has_greater and has_lesser as false */
break;
}
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_arg1_part () - returns arg1 for union, intersection or difference
* return:
* node(in):
*/
PT_NODE *
pt_arg1_part (const PT_NODE * node)
{
if (node && (node->node_type == PT_INTERSECTION || node->node_type == PT_DIFFERENCE || node->node_type == PT_UNION))
{
return node->info.query.q.union_.arg1;
}
return NULL;
}
/*
* pt_arg2_part () - returns arg2 for union, intersection or difference
* return:
* node(in):
*/
PT_NODE *
pt_arg2_part (const PT_NODE * node)
{
if (node && (node->node_type == PT_INTERSECTION || node->node_type == PT_DIFFERENCE || node->node_type == PT_UNION))
{
return node->info.query.q.union_.arg2;
}
return NULL;
}
/*
* pt_select_list_part () - returns select list from select statement
* return:
* node(in):
*/
PT_NODE *
pt_select_list_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SELECT)
{
return node->info.query.q.select.list;
}
return NULL;
}
#endif
/*
* pt_from_list_part () - returns from list from select statement
* return:
* node(in):
*/
PT_NODE *
pt_from_list_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SELECT)
{
return node->info.query.q.select.from;
}
return NULL;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_where_part () - returns where part from select statement
* return:
* node(in):
*/
PT_NODE *
pt_where_part (const PT_NODE * node)
{
if (node)
{
if (node->node_type == PT_SELECT)
{
return node->info.query.q.select.where;
}
if (node->node_type == PT_UPDATE)
{
return node->info.update.search_cond;
}
if (node->node_type == PT_DELETE)
{
return node->info.delete_.search_cond;
}
}
return NULL;
}
/*
* pt_order_by_part () - returns order by part from select statement
* return:
* node(in):
*/
PT_NODE *
pt_order_by_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SELECT)
{
return node->info.query.order_by;
}
return NULL;
}
/*
* pt_group_by_part () - returns group by part from select statement
* return:
* node(in):
*/
PT_NODE *
pt_group_by_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SELECT)
{
return node->info.query.q.select.group_by;
}
return NULL;
}
/*
* pt_having_part () - returns having part from select statement
* return:
* node(in):
*/
PT_NODE *
pt_having_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SELECT)
{
return node->info.query.q.select.having;
}
return NULL;
}
#endif
/*
* pt_from_entity_part () - Returns first entity name of from list node
* return:
* node(in):
*/
PT_NODE *
pt_from_entity_part (const PT_NODE * node)
{
if (node && node->node_type == PT_SPEC)
{
return node->info.spec.entity_name;
}
return NULL;
}
/*
* pt_left_part () - returns arg1 for PT_DOT_ and PT_EXPR
* return:
* node(in):
*/
PT_NODE *
pt_left_part (const PT_NODE * node)
{
if (node == NULL)
{
return NULL;
}
if (node->node_type == PT_EXPR)
{
return node->info.expr.arg1;
}
if (node->node_type == PT_DOT_)
{
return node->info.dot.arg1;
}
return NULL;
}
/*
* pt_right_part () - returns arg2 for PT_DOT_ and PT_EXPR
* return:
* node(in):
*/
PT_NODE *
pt_right_part (const PT_NODE * node)
{
if (node == NULL)
{
return NULL;
}
if (node->node_type == PT_EXPR)
{
return node->info.expr.arg2;
}
if (node->node_type == PT_DOT_)
{
return node->info.dot.arg2;
}
return NULL;
}
/*
* pt_get_end_path_node () -
* return: the original name node at the end of a path expression
* node(in):
*/
PT_NODE *
pt_get_end_path_node (PT_NODE * node)
{
while (node != NULL && node->node_type == PT_DOT_)
{
node = node->info.dot.arg2;
}
return node;
}
/*
* pt_get_first_arg_ignore_prior () -
* return: the first argument of an expression node; if the argument is
* PRIOR (arg) then PRIOR's argument is returned instead
* node(in):
* Note: Also see the related PT_IS_EXPR_WITH_PRIOR_ARG macro.
*/
PT_NODE *
pt_get_first_arg_ignore_prior (PT_NODE * node)
{
PT_NODE *arg1 = NULL;
assert (PT_IS_EXPR_NODE (node));
if (!PT_IS_EXPR_NODE (node))
{
return NULL;
}
arg1 = node->info.expr.arg1;
if (PT_IS_EXPR_NODE_WITH_OPERATOR (arg1, PT_PRIOR))
{
arg1 = arg1->info.expr.arg1;
}
/* Although semantically valid, PRIOR(PRIOR(expr)) is not allowed at runtime so this combination is restricted during
* parsing. See the parser rule for PRIOR for details. */
assert (!PT_IS_EXPR_NODE_WITH_OPERATOR (arg1, PT_PRIOR));
return arg1;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_operator_part () - returns operator for PT_EXPR
* return:
* node(in):
*/
int
pt_operator_part (const PT_NODE * node)
{
if (node)
{
if (node->node_type == PT_EXPR)
{
return node->info.expr.op;
}
}
return 0;
}
#endif
/*
* pt_class_part () -
* return:
* node(in):
*/
PT_NODE *
pt_class_part (const PT_NODE * node)
{
if (node)
{
if (node->node_type == PT_UPDATE)
{
return node->info.update.spec;
}
if (node->node_type == PT_DELETE)
{
return node->info.delete_.spec;
}
if (node->node_type == PT_INSERT)
{
return node->info.insert.spec;
}
if (node->node_type == PT_SELECT)
{
return node->info.query.q.select.from;
}
}
return NULL;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_class_names_part () -
* return:
* node(in):
*/
PT_NODE *
pt_class_names_part (const PT_NODE * node)
{
PT_NODE *temp;
temp = pt_class_part (node);
if (temp)
{
node = temp;
}
if (node && node->node_type == PT_SPEC)
{
return node->info.spec.flat_entity_list;
}
return NULL;
}
/*
* pt_assignments_part () -
* return:
* node(in):
*/
PT_NODE *
pt_assignments_part (const PT_NODE * node)
{
if (node && node->node_type == PT_UPDATE)
{
return node->info.update.assignment;
}
return NULL;
}
#endif
/*
* pt_attrs_part () -
* return:
* node(in):
*/
PT_NODE *
pt_attrs_part (const PT_NODE * node)
{
if (node && node->node_type == PT_INSERT)
{
return node->info.insert.attr_list;
}
return NULL;
}
/*
* pt_values_part () -
* return:
* node(in):
*/
PT_NODE *
pt_values_part (const PT_NODE * node)
{
if (node && node->node_type == PT_INSERT
&& (node->info.insert.value_clauses->info.node_list.list_type != PT_IS_SUBQUERY))
{
return node->info.insert.value_clauses;
}
return NULL;
}
/*
* pt_get_subquery_of_insert_select () -
* return:
* node(in):
*/
PT_NODE *
pt_get_subquery_of_insert_select (const PT_NODE * node)
{
PT_NODE *ptr_values = NULL;
PT_NODE *ptr_subquery = NULL;
if (node == NULL || node->node_type != PT_INSERT)
{
return NULL;
}
ptr_values = node->info.insert.value_clauses;
assert (ptr_values != NULL);
assert (ptr_values->node_type == PT_NODE_LIST);
if (ptr_values->info.node_list.list_type != PT_IS_SUBQUERY)
{
return NULL;
}
assert (ptr_values->next == NULL);
ptr_subquery = ptr_values->info.node_list.list;
assert (PT_IS_QUERY (ptr_subquery));
assert (ptr_subquery->next == NULL);
return ptr_subquery;
}
/*
* pt_string_part () -
* return:
* node(in):
*/
const char *
pt_string_part (const PT_NODE * node)
{
if (node && node->node_type == PT_NAME)
{
return node->info.name.original;
}
return NULL;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_qualifier_part () -
* return:
* node(in):
*/
const char *
pt_qualifier_part (const PT_NODE * node)
{
if (node && node->node_type == PT_NAME)
{
return node->info.name.resolved;
}
return NULL;
}
/*
* pt_object_part () -
* return:
* node(in):
*/
void *
pt_object_part (const PT_NODE * node)
{
if (node && node->node_type == PT_NAME)
{
return node->info.name.db_object;
}
return NULL;
}
#endif
/*
* pt_must_be_filtering () - Finds expressions that are incompatible with
* executing joins before a hierarchical query (connect by). If such
* expressions are found the predicate must be executed after connect by
* (it is a filtering predicate, not a join predicate).
* In addition, it figures out if the predicate refers to only one spec_id
* or more. This is needed to correctly classify the predicate as a join
* predicate.
*/
static PT_NODE *
pt_must_be_filtering (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
MUST_BE_FILTERING_INFO *info = (MUST_BE_FILTERING_INFO *) arg;
if (info->must_be_filtering)
{
*continue_walk = PT_STOP_WALK;
return node;
}
if (PT_IS_QUERY (node))
{
info->must_be_filtering = true;
*continue_walk = PT_STOP_WALK;
return node;
}
if (PT_IS_NAME_NODE (node))
{
if (node->info.name.spec_id != 0 && node->info.name.correlation_level == 0)
{
if (info->first_spec_id == 0)
{
info->first_spec_id = node->info.name.spec_id;
}
else if (info->first_spec_id != node->info.name.spec_id)
{
info->has_second_spec_id = true;
}
}
*continue_walk = PT_CONTINUE_WALK;
return node;
}
if (PT_IS_EXPR_NODE (node)
&& (PT_IS_SERIAL (node->info.expr.op) || PT_IS_NUMBERING_AFTER_EXECUTION (node->info.expr.op)
|| PT_REQUIRES_HIERARCHICAL_QUERY (node->info.expr.op)))
{
info->must_be_filtering = true;
*continue_walk = PT_STOP_WALK;
return node;
}
*continue_walk = PT_CONTINUE_WALK;
return node;
}
/*
* pt_is_filtering_predicate ()
* return: Whether the given predicate is to be executed as filtering after
* the hierarchical query or as a join predicate before the
* hierarchical query.
*/
static bool
pt_is_filtering_predicate (PARSER_CONTEXT * parser, PT_NODE * predicate)
{
MUST_BE_FILTERING_INFO info;
info.must_be_filtering = false;
info.first_spec_id = 0;
info.has_second_spec_id = false;
parser_walk_tree (parser, predicate, pt_must_be_filtering, &info, NULL, NULL);
if (!info.has_second_spec_id)
{
/* It's not a join predicate as it has references to one spec only. */
return true;
}
else
{
/* It references more than one spec (like a join predicate), but we consider it to be a filtering predicate if it
* contains certain expressions. */
return info.must_be_filtering;
}
}
/*
* pt_is_filtering_skip_and_or () Checks for the existence of at least a
* filtering predicate in a tree of predicates.
*/
static PT_NODE *
pt_is_filtering_skip_and_or (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *already_found_filtering = (bool *) arg;
if (*already_found_filtering)
{
*continue_walk = PT_STOP_WALK;
return node;
}
if (PT_IS_EXPR_NODE_WITH_OPERATOR (node, PT_AND) || PT_IS_EXPR_NODE_WITH_OPERATOR (node, PT_OR))
{
*continue_walk = PT_CONTINUE_WALK;
return node;
}
if (pt_is_filtering_predicate (parser, node))
{
*already_found_filtering = true;
}
*continue_walk = PT_STOP_WALK;
return node;
}
/*
* pt_is_filtering_expression ()
* return: Whether the given expression is to be executed as filtering after
* the hierarchical query or as a join expression before the
* hierarchical query.
* Unfortunately this expression can contain PT_AND and PT_OR because
* CNF is not performed in some cases; see TRANSFORM_CNF_OR_COMPACT.
* Because of this we must dig inside the tree to find the predicates.
*/
static bool
pt_is_filtering_expression (PARSER_CONTEXT * parser, PT_NODE * expression)
{
PT_NODE *or_next_save;
bool result = false;
assert (expression->next == NULL);
or_next_save = expression->or_next;
expression->or_next = NULL;
parser_walk_tree (parser, expression, pt_is_filtering_skip_and_or, &result, NULL, NULL);
expression->or_next = or_next_save;
return result;
}
/*
* pt_is_filtering_expression ()
* return: Splits the given predicate list into two: a part to be executed
* before the hierarchical query as it defines the join conditions and
* a second part to be executed as filtering after the connect by
* execution.
*/
void
pt_split_join_preds (PARSER_CONTEXT * parser, PT_NODE * predicates, PT_NODE ** join_part, PT_NODE ** after_cb_filter)
{
PT_NODE *current_conj, *current_pred;
PT_NODE *next_conj = NULL;
for (current_conj = predicates; current_conj != NULL; current_conj = next_conj)
{
bool has_filter_pred = false;
assert (PT_IS_EXPR_NODE (current_conj) || PT_IS_VALUE_NODE (current_conj));
/* It is either fully CNF or not at all. */
assert (!(current_conj->next != NULL
&& (PT_IS_EXPR_NODE_WITH_OPERATOR (current_conj, PT_AND)
|| PT_IS_EXPR_NODE_WITH_OPERATOR (current_conj, PT_OR))));
next_conj = current_conj->next;
current_conj->next = NULL;
for (current_pred = current_conj; current_pred != NULL; current_pred = current_pred->or_next)
{
assert (PT_IS_EXPR_NODE (current_pred) || PT_IS_VALUE_NODE (current_pred));
/* It is either fully CNF or not at all. */
assert (!(current_pred->or_next != NULL
&& (PT_IS_EXPR_NODE_WITH_OPERATOR (current_pred, PT_AND)
|| PT_IS_EXPR_NODE_WITH_OPERATOR (current_pred, PT_OR))));
if (pt_is_filtering_expression (parser, current_pred))
{
has_filter_pred = true;
break;
}
}
if (has_filter_pred)
{
*after_cb_filter = parser_append_node (current_conj, *after_cb_filter);
}
else
{
*join_part = parser_append_node (current_conj, *join_part);
}
}
}
/*
* pt_node_next () - return the next node in a list
* return:
* node(in):
*/
PT_NODE *
pt_node_next (const PT_NODE * node)
{
if (node)
{
return node->next;
}
return NULL;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_set_node_etc () - sets the etc void pointer of a node
* return:
* node(in):
* etc(in):
*/
void
pt_set_node_etc (PT_NODE * node, const void *etc)
{
if (node)
{
node->etc = (void *) etc;
}
}
#endif
/*
* pt_node_etc () - return the etc void pointer from a node
* return:
* node(in):
*/
void *
pt_node_etc (const PT_NODE * node)
{
if (node)
{
return node->etc;
}
return NULL;
}
/*
* pt_null_etc () - sets the etc void pointer to null
* return:
* node(in/out):
*/
void
pt_null_etc (PT_NODE * node)
{
if (node)
{
node->etc = NULL;
}
}
/*
* pt_record_warning () - creates a new PT_ZZ_ERROR_MSG node appends it
* to parser->warning
* return:
* parser(in): pointer to parser structure
* stmt_no(in): source statement where warning was detected
* line_no(in): source line number where warning was detected
* col_no(in): source column number where warning was detected
* msg(in): a helpful explanation of the warning
*/
void
pt_record_warning (PARSER_CONTEXT * parser, int stmt_no, int line_no, int col_no, const char *msg)
{
PT_NODE *node = parser_new_node (parser, PT_ZZ_ERROR_MSG);
node->info.error_msg.statement_number = stmt_no;
node->line_number = line_no;
node->column_number = col_no;
node->info.error_msg.error_message = pt_append_string (parser, NULL, msg);
parser->warnings = parser_append_node (node, parser->warnings);
}
/*
* pt_get_warnings () - return the etc void pointer from a parser
* return:
* parser(in):
*/
PT_NODE *
pt_get_warnings (const PARSER_CONTEXT * parser)
{
if (parser)
{
return parser->warnings;
}
return NULL;
}
/*
* pt_reset_error () - resets the errors recorded in a parser to none
* return:
* parser(in/out):
*/
void
pt_reset_error (PARSER_CONTEXT * parser)
{
if (parser)
{
if (pt_has_error (parser))
{
parser_free_tree (parser, parser->error_msgs);
parser->error_msgs = NULL;
}
parser->oid_included = PT_NO_OID_INCLUDED;
}
return;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_column_updatable () - takes a subquery expansion of a class, and tests
* it for column aka object-master updatability
* return: true on updatable
* parser(in):
* statement(in):
*/
bool
pt_column_updatable (PARSER_CONTEXT * parser, PT_NODE * statement)
{
bool updatable = (statement != NULL);
while (updatable && statement)
{
switch (statement->node_type)
{
case PT_SELECT:
if (statement->info.query.q.select.group_by || statement->info.query.q.select.from->info.spec.derived_table
|| statement->info.query.all_distinct == PT_DISTINCT)
{
updatable = false;
}
if (updatable)
{
updatable = !pt_has_aggregate (parser, statement);
}
break;
case PT_UNION:
if (statement->info.query.all_distinct == PT_DISTINCT)
{
updatable = false;
}
if (updatable)
{
updatable = pt_column_updatable (parser, statement->info.query.q.union_.arg1);
}
if (updatable)
{
updatable = pt_column_updatable (parser, statement->info.query.q.union_.arg2);
}
break;
case PT_DIFFERENCE:
case PT_INTERSECTION:
default:
updatable = false;
break;
}
statement = statement->next;
}
return updatable;
}
#endif
/*
* pt_statement_line_number () -
* return: a statement's starting source line number
* stmt(in):
*/
int
pt_statement_line_number (const PT_NODE * stmt)
{
if (stmt)
{
return stmt->line_number;
}
return 1;
}
/*
* pt_get_select_from_spec () - return a select query_spec's from PT_NAME node
* return: spec's from PT_NAME node if all OK, null otherwise
* spec(in): a parsed SELECT query specification
*/
static PT_NODE *
pt_get_select_from_spec (const PT_NODE * spec)
{
PT_NODE *from_spec, *from_name;
if (!spec || !(from_spec = pt_from_list_part (spec)) || !pt_length_of_list (from_spec)
|| from_spec->node_type != PT_SPEC || !(from_name = from_spec->info.spec.entity_name)
|| from_name->node_type != PT_NAME)
{
return NULL;
}
return from_name;
}
/*
* pt_get_select_from_name () - return a select query_spec's from entity name
* return: spec's from entity name if all OK, null otherwise
* parser(in): the parser context
* spec(in): a parsed SELECT query specification
*/
const char *
pt_get_select_from_name (PARSER_CONTEXT * parser, const PT_NODE * spec)
{
PT_NODE *from_name;
char *result = NULL;
from_name = pt_get_select_from_spec (spec);
if (from_name != NULL)
{
if (from_name->info.name.resolved == NULL)
{
result = (char *) from_name->info.name.original;
}
else
{
result = pt_append_string (parser, NULL, from_name->info.name.resolved);
result = pt_append_string (parser, result, ".");
result = pt_append_string (parser, result, from_name->info.name.original);
}
}
return result;
}
/*
* pt_get_spec_name () - get this SELECT query's from spec name so that
* 'select ... from class foo' yields 'class foo'
* return: selqry's from spec name
* parser(in): the parser context
* selqry(in): a SELECT query
*/
const char *
pt_get_spec_name (PARSER_CONTEXT * parser, const PT_NODE * selqry)
{
char *result = NULL;
PT_NODE *from_spec;
from_spec = pt_from_list_part (selqry);
if (from_spec && from_spec->node_type == PT_SPEC)
{
if (from_spec->info.spec.meta_class == PT_META_CLASS)
{
result = pt_append_string (parser, result, "class ");
}
result = pt_append_string (parser, result, pt_get_select_from_name (parser, selqry));
}
return result;
}
/*
* pt_has_aggregate () -
* return: true if statement has an aggregate node in its parse tree
* parser(in):
* node(in/out):
*
* Note :
* for aggregate select statement, set agg flag for next re-check
*/
bool
pt_has_aggregate (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_AGG_FIND_INFO info;
PT_NODE *save_next;
info.select_stack = NULL;
info.base_count = 0;
info.out_of_context_count = 0;
info.stop_on_subquery = false;
info.disable_loose_scan = false;
if (!node)
{
return false;
}
if (node->node_type == PT_SELECT)
{
bool found = false;
/* STEP 1: check agg flag */
if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_HAS_AGG))
{
/* we've been here before */
return true;
}
/* STEP 2: check GROUP BY, HAVING */
if (node->info.query.q.select.group_by || node->info.query.q.select.having)
{
found = true;
/* fall trough, we need to check for loose scan */
}
/* STEP 3: check tree */
if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_IS_UPD_DEL_QUERY)
|| PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_IS_MERGE_QUERY))
{
/* UPDATE, DELETE and MERGE queries cannot own aggregates from subqueries, so this SELECT can't either */
info.stop_on_subquery = true;
}
save_next = node->next;
node->next = NULL;
(void) parser_walk_tree (parser, node, pt_find_aggregate_functions_pre, &info, pt_find_aggregate_functions_post,
&info);
node->next = save_next;
if (info.base_count > 0)
{
found = true;
if (info.disable_loose_scan)
{
PT_SELECT_INFO_SET_FLAG (node, PT_SELECT_INFO_DISABLE_LOOSE_SCAN);
}
}
if (found)
{
PT_SELECT_INFO_SET_FLAG (node, PT_SELECT_INFO_HAS_AGG);
return true;
}
}
else if (node->node_type == PT_MERGE)
{
/* for MERGE statement, free aggregates in search condition and in update assignments are not allowed; however,
* those contained in subqueries are, even if they reference high level specs */
info.stop_on_subquery = true;
(void) parser_walk_tree (parser, node->info.merge.search_cond, pt_find_aggregate_functions_pre, &info,
pt_find_aggregate_functions_post, &info);
(void) parser_walk_tree (parser, node->info.merge.update.assignment, pt_find_aggregate_functions_pre, &info,
pt_find_aggregate_functions_post, &info);
(void) parser_walk_tree (parser, node->info.merge.insert.value_clauses, pt_find_aggregate_functions_pre, &info,
pt_find_aggregate_functions_post, &info);
if (info.out_of_context_count > 0)
{
return true;
}
}
else
{
info.stop_on_subquery = true;
save_next = node->next;
node->next = NULL;
(void) parser_walk_tree (parser, node, pt_find_aggregate_functions_pre, &info, pt_find_aggregate_functions_post,
&info);
node->next = save_next;
if (info.out_of_context_count > 0)
{
return true;
}
}
return false;
}
/*
* pt_has_define_vars () - check if a statement uses define vars ':='
* return : true if the statement uses define vars ':='
* parser (in) : parser context
* stmt (in) : statement
*/
bool
pt_has_define_vars (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
bool has_define_vars = false;
parser_walk_tree (parser, stmt, pt_is_define_vars, &has_define_vars, NULL, NULL);
return has_define_vars;
}
/*
* pt_is_define_vars () - check if a node is a define vars ':='
* return : node
* parser (in) : parser context
* node (in) : node
* arg (in) :
* continue_walk (in) :
*/
PT_NODE *
pt_is_define_vars (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *is_define_vars = (bool *) arg;
*continue_walk = PT_CONTINUE_WALK;
assert (is_define_vars != NULL);
if (*is_define_vars)
{
/* stop checking, there already is a parameter in the statement */
return node;
}
if (node->node_type == PT_EXPR)
{
if (node->info.expr.op == PT_DEFINE_VARIABLE)
{
*is_define_vars = true;
*continue_walk = PT_STOP_WALK;
}
}
return node;
}
/*
* pt_has_path_expr () - check if a statement has path expr
* return : true if the statement has path expr
* parser (in) : parser context
* stmt (in) : statement
*/
bool
pt_has_path_expr (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
bool pt_has_path_expr = false;
parser_walk_tree (parser, stmt, pt_is_path_expr, &pt_has_path_expr, NULL, NULL);
return pt_has_path_expr;
}
/*
* pt_is_path_expr () - check if a node is a path expr
* return : node
* parser (in) : parser context
* node (in) : node
* arg (in) :
* continue_walk (in) :
*/
PT_NODE *
pt_is_path_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *is_path_expr = (bool *) arg;
*continue_walk = PT_CONTINUE_WALK;
assert (is_path_expr != NULL);
if (*is_path_expr)
{
/* stop checking, there already is a parameter in the statement */
return node;
}
if (pt_is_attr (node) && node->node_type == PT_DOT_)
{
*is_path_expr = true;
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_has_analytic () -
* return: true if statement has an analytic function in its parse tree
* parser(in):
* node(in/out):
*
*/
bool
pt_has_analytic (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_analytic = false;
bool has_analytic_arg1 = false;
bool has_analytic_arg2 = false;
if (!node)
{
return false;
}
switch (node->node_type)
{
case PT_SELECT:
if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_HAS_ANALYTIC))
{
has_analytic = true;
}
else
{
(void) parser_walk_tree (parser, node->info.query.q.select.list, pt_is_analytic_node, &has_analytic,
pt_is_analytic_node_post, &has_analytic);
if (has_analytic)
{
PT_SELECT_INFO_SET_FLAG (node, PT_SELECT_INFO_HAS_ANALYTIC);
}
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
has_analytic_arg1 = pt_has_analytic (parser, node->info.query.q.union_.arg1);
has_analytic_arg2 = pt_has_analytic (parser, node->info.query.q.union_.arg2);
if (has_analytic_arg1 || has_analytic_arg2)
{
has_analytic = true;
}
break;
default:
(void) parser_walk_tree (parser, node, pt_is_analytic_node, &has_analytic, pt_is_analytic_node_post,
&has_analytic);
break;
}
return has_analytic;
}
/*
* pt_has_order_sensitive_agg () -
* return: true if statement has an group_concat function in its parse tree
* parser(in):
* node(in/out):
*
*/
bool
pt_has_order_sensitive_agg (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_order_sensitive_agg = false;
bool has_order_sensitive_agg_arg1 = false;
bool has_order_sensitive_agg_arg2 = false;
if (!node)
{
return false;
}
switch (node->node_type)
{
case PT_SELECT:
(void) parser_walk_tree (parser, node->info.query.q.select.list, pt_is_order_sensitive_agg,
&has_order_sensitive_agg, pt_is_order_sensitive_agg_post, &has_order_sensitive_agg);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
has_order_sensitive_agg_arg1 = pt_has_order_sensitive_agg (parser, node->info.query.q.union_.arg1);
has_order_sensitive_agg_arg2 = pt_has_order_sensitive_agg (parser, node->info.query.q.union_.arg2);
if (has_order_sensitive_agg_arg1 || has_order_sensitive_agg_arg2)
{
has_order_sensitive_agg = true;
}
break;
default:
(void) parser_walk_tree (parser, node, pt_is_order_sensitive_agg, &has_order_sensitive_agg,
pt_is_order_sensitive_agg_post, &has_order_sensitive_agg);
break;
}
return has_order_sensitive_agg;
}
/*
* pt_has_inst_or_orderby_num () - check if tree has an INST_NUM or ORDERBY_NUM
* node somewhere
* return: true if tree has INST_NUM/ORDERBY_NUM
* parser(in):
* node(in):
*/
bool
pt_has_inst_or_orderby_num (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_inst_orderby_num = false;
(void) parser_walk_tree (parser, node, pt_is_inst_or_orderby_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
return has_inst_orderby_num;
}
/*
* pt_has_inst_num () - check if tree has an INST_NUM node somewhere
* return: true if tree has INST_NUM
* parser(in):
* node(in):
*/
bool
pt_has_inst_num (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_inst_num = false;
(void) parser_walk_tree (parser, node, pt_is_inst_num_node, &has_inst_num, pt_is_inst_num_node_post, &has_inst_num);
return has_inst_num;
}
/*
* pt_has_expr_of_inst_in_sel_list () - check if tree has an EXPR node having INST_NUM in select list
* return: true if tree has EXPR node having INST_NUM
* parser(in):
* node(in):
*/
bool
pt_has_expr_of_inst_in_sel_list (PARSER_CONTEXT * parser, PT_NODE * select_list)
{
PT_NODE *save_next, *col;
/*
* FIXME!! : remove this check after expanding syntax related to orderby_num().
* This check is needed because the syntax below is not supported.
* 'select orderby_num() + 10 ...'
* Check MSGCAT_SEMANTIC_ORDERBYNUM_SELECT_LIST_ERR error code
* In XASL Generator, 'ordbynum_val' must be created as a regular variable, not db_value.
*/
col = select_list;
while (col)
{
/* cut off next */
save_next = col->next;
col->next = NULL;
if (!PT_IS_INSTNUM (col) && pt_has_inst_num (parser, col))
{
/* find expr of instnum */
col->next = save_next;
return true;
}
col->next = save_next;
col = col->next;
}
return false;
}
/*
* pt_has_inst_in_where_and_select_list ()
* - check if tree has an INST_NUM or ORDERBY_NUM or GROUPBY_NUM node in where and select_list
* return: true if tree has INST_NUM/ORDERBY_NUM
* parser(in):
* node(in):
*/
bool
pt_has_inst_in_where_and_select_list (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_inst_orderby_num = false;
PT_NODE *where, *select_list, *orderby_for, *having, *using_index;
switch (node->node_type)
{
case PT_SELECT:
using_index = node->info.query.q.select.using_index;
if (using_index != NULL && using_index->info.name.indx_key_limit != NULL)
{
return true;
}
where = node->info.query.q.select.where;
(void) parser_walk_tree (parser, where, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
select_list = node->info.query.q.select.list;
(void) parser_walk_tree (parser, select_list, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
orderby_for = node->info.query.orderby_for;
(void) parser_walk_tree (parser, orderby_for, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
having = node->info.query.q.select.having;
(void) parser_walk_tree (parser, having, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (node->info.query.limit)
{
return true;
}
has_inst_orderby_num |= pt_has_inst_in_where_and_select_list (parser, node->info.query.q.union_.arg1);
has_inst_orderby_num |= pt_has_inst_in_where_and_select_list (parser, node->info.query.q.union_.arg2);
break;
default:
break;
}
return has_inst_orderby_num;
}
/*
* pt_has_having_with_predicate ()
* - check if tree has an HAVING with predicate
* return: true if tree has HAVING with predicate
* parser(in):
* node(in):
*/
bool
pt_has_having_with_predicate (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_having = false;
PT_NODE *having;
switch (node->node_type)
{
case PT_SELECT:
having = node->info.query.q.select.having;
if (having != NULL)
{
/* there is only 'groupby_num <= ' */
if (having->next == NULL && pt_is_expr_node (having) && PT_IS_GROUPBYNUM (having->info.expr.arg1)
&& (having->info.expr.op == PT_LE || having->info.expr.op == PT_LT))
{
return false;
}
return true;
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
has_having |= pt_has_having_with_predicate (parser, node->info.query.q.union_.arg1);
has_having |= pt_has_having_with_predicate (parser, node->info.query.q.union_.arg2);
break;
default:
break;
}
return has_having;
}
/*
* pt_has_inst_or_orderby_num_in_where ()
* - check if tree has an INST_NUM or ORDERBY_NUM or GROUPBY_NUM node in where
* return: true if tree has INST_NUM/ORDERBY_NUM
* parser(in):
* node(in):
*/
bool
pt_has_inst_or_orderby_num_in_where (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool has_inst_orderby_num = false;
PT_NODE *where, *select_list, *orderby_for, *having, *using_index;
switch (node->node_type)
{
case PT_SELECT:
using_index = node->info.query.q.select.using_index;
if (using_index != NULL && using_index->info.name.indx_key_limit != NULL)
{
return true;
}
where = node->info.query.q.select.where;
(void) parser_walk_tree (parser, where, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
orderby_for = node->info.query.orderby_for;
(void) parser_walk_tree (parser, orderby_for, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
having = node->info.query.q.select.having;
(void) parser_walk_tree (parser, having, pt_is_inst_or_inst_num_node, &has_inst_orderby_num,
pt_is_inst_or_orderby_num_node_post, &has_inst_orderby_num);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (node->info.query.limit)
{
return true;
}
has_inst_orderby_num |= pt_has_inst_or_orderby_num_in_where (parser, node->info.query.q.union_.arg1);
has_inst_orderby_num |= pt_has_inst_or_orderby_num_in_where (parser, node->info.query.q.union_.arg2);
break;
default:
break;
}
return has_inst_orderby_num;
}
/*
* pt_set_correlation_level ()
* - set correlation level
* parser(in):
* subquery(in):
* level(in):
*/
void
pt_set_correlation_level (PARSER_CONTEXT * parser, PT_NODE * subquery, int level)
{
switch (subquery->node_type)
{
case PT_SELECT:
subquery->info.query.correlation_level = level;
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
subquery->info.query.correlation_level = level;
pt_set_correlation_level (parser, subquery->info.query.q.union_.arg1, level);
pt_set_correlation_level (parser, subquery->info.query.q.union_.arg2, level);
break;
default:
break;
}
}
/*
* pt_set_pred_order ()
* - set predicate order number
* parser(in):
* pre_pred(in):
* pre_order(in):
*/
void
pt_set_pred_order (PARSER_CONTEXT * parser, PT_NODE * pre_pred, int pre_order)
{
PT_NODE *pred;
/* set pred_order + pre_order to pre_pred */
for (pred = pre_pred; pred; pred = pred->next)
{
if (pt_is_expr_node (pred))
{
pred->info.expr.pred_order += pre_order;
}
}
}
/*
* pt_get_max_pred_order ()
* - get max predicate order number
* parser(in):
* pred(in):
*/
int
pt_get_max_pred_order (PARSER_CONTEXT * parser, PT_NODE * predicate)
{
PT_NODE *pred;
int max_pred_order = 0;
for (pred = predicate; pred; pred = pred->next)
{
if (pt_is_expr_node (pred))
{
if (pred->info.expr.pred_order > max_pred_order)
{
max_pred_order = pred->info.expr.pred_order;
}
}
}
return max_pred_order;
}
/*
* pt_has_nullable_term () - check if tree has an nullable term
* node somewhere
* return: true if tree has nullable term
* parser(in):
* node(in):
*/
bool
pt_has_nullable_term (PARSER_CONTEXT * parser, PT_NODE * node)
{
int has_nullable_term = 0;
PT_NODE *next;
next = node->next;
node->next = NULL;
(void) parser_walk_tree (parser, node, NULL, NULL, qo_check_nullable_expr, &has_nullable_term);
node->next = next;
return has_nullable_term == 0 ? false : true;
}
/*
* pt_insert_host_var () - insert a host_var into a list based on
* its ordinal position
* return: a list of PT_HOST_VAR type nodes
* parser(in): the parser context
* h_var(in): a PT_HOST_VAR type node
* list(in/out): a list of PT_HOST_VAR type nodes
*/
static PT_NODE *
pt_insert_host_var (PARSER_CONTEXT * parser, PT_NODE * h_var, PT_NODE * list)
{
PT_NODE *temp, *tail, *new_node;
if (!list || list->info.host_var.index > h_var->info.host_var.index)
{
/* the new node goes before the rest of the list */
new_node = parser_copy_tree (parser, h_var);
if (new_node == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
new_node->next = list;
list = new_node;
}
else
{
tail = temp = list;
while (temp && temp->info.host_var.index <= h_var->info.host_var.index)
{
tail = temp;
temp = temp->next;
}
if (tail->info.host_var.index < h_var->info.host_var.index)
{
new_node = parser_copy_tree (parser, h_var);
if (new_node == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
tail->next = new_node;
new_node->next = temp;
}
}
return list;
}
/*
* pt_collect_host_info () - collect host_var or cursor info from this node
* return: node
* parser(in): the parser context used in deriving this node
* node(in): a node of the parse tree of an esql statement
* h_var(in/out): a PT_HOST_VARS for depositing host_var or cursor info
* continue_walk(in/out): flag that tells when to stop traversal
*
* Note :
* if node is a host_var then
* append a copy of node into h_var.inputs or h_var.outputs
* or if node is a host descriptor then
* save node into h_var.in_descr or h_var.out_descr
* or if node is an UPDATE or DELETE current of cursor then
* save cursor name into h_var.cursor
*/
static PT_NODE *
pt_collect_host_info (PARSER_CONTEXT * parser, PT_NODE * node, void *h_var, int *continue_walk)
{
PT_HOST_VARS *hvars = (PT_HOST_VARS *) h_var;
switch (node->node_type)
{
case PT_HOST_VAR:
switch (node->info.host_var.var_type)
{
case PT_HOST_IN: /* an input host variable */
hvars->inputs = pt_insert_host_var (parser, node, hvars->inputs);
break;
case PT_HOST_OUT: /* an output host variable */
hvars->outputs = pt_insert_host_var (parser, node, hvars->outputs);
break;
case PT_HOST_OUT_DESCR: /* an output host descriptor */
hvars->out_descr = parser_copy_tree (parser, node);
break;
default:
break;
}
break;
case PT_DELETE:
if (node->info.delete_.cursor_name)
{
hvars->cursor = parser_copy_tree (parser, node->info.delete_.cursor_name);
}
break;
case PT_UPDATE:
if (node->info.update.cursor_name)
{
hvars->cursor = parser_copy_tree (parser, node->info.update.cursor_name);
}
break;
default:
break;
}
return node;
}
/*
* pt_host_info () - collect & return host_var & cursor info
* from a parsed embedded statement
* return: PT_HOST_VARS
* parser(in): the parser context used in deriving stmt
* stmt(in): parse tree of a an esql statement
*
* Note :
* caller assumes responsibility for freeing PT_HOST_VARS via pt_free_host_info
*/
PT_HOST_VARS *
pt_host_info (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
PT_HOST_VARS *result = (PT_HOST_VARS *) calloc (1, sizeof (PT_HOST_VARS));
if (result)
{
memset (result, 0, sizeof (PT_HOST_VARS));
(void) parser_walk_tree (parser, stmt, pt_collect_host_info, (void *) result, NULL, NULL);
}
return result;
}
/*
* pt_free_host_info () - deallocate a PT_HOST_VARS structure
* return:
* hv(in): a PT_HOST_VARS structure created by pt_host_info
*/
void
pt_free_host_info (PT_HOST_VARS * hv)
{
if (hv)
{
free_and_init (hv);
}
}
/*
* pt_collect_parameters () - collect parameter info from this node
* return: node
* parser(in): the parser context used in deriving this node
* node(in): a node of the parse tree of an esql statement
* param_list(in/out): a PT_HOST_VARS for depositing host_var or cursor info
* continue_walk(in/out): flag that tells when to stop traversal
*/
static PT_NODE *
pt_collect_parameters (PARSER_CONTEXT * parser, PT_NODE * node, void *param_list, int *continue_walk)
{
PT_NODE **list = (PT_NODE **) param_list;
if (node->node_type == PT_NAME && node->info.name.meta_class == PT_PARAMETER)
{
if (!pt_find_name (parser, node, (*list)))
{
(*list) = parser_append_node (parser_copy_tree (parser, node), *list);
}
}
return node;
}
/*
* pt_get_parameters () - collect parameters into a list
* return: PT_NODE * a copy of a list of paramater names
* parser(in): the parser context used in deriving stmt
* stmt(in): parse tree of a csql statement
*
* Note :
* caller assumes responsibility for freeing PT_HOST_VARS via pt_free_host_info
*/
PT_NODE *
pt_get_parameters (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
PT_NODE *result = NULL;
(void) parser_walk_tree (parser, stmt, pt_collect_parameters, (void *) &result, NULL, NULL);
return result;
}
/*
* pt_get_cursor () - return a PT_HOST_VARS' cursor information
* return: hv's cursor information
* hv(in): a PT_HOST_VARS structure created by pt_host_info
*/
PT_NODE *
pt_get_cursor (const PT_HOST_VARS * hv)
{
if (hv)
{
return hv->cursor;
}
else
{
return NULL;
}
}
/*
* pt_get_name () - return a PT_NAME's original name
* return: nam's original name if all OK, NULL otherwise.
* nam(in): a PT_NAME node
*/
const char *
pt_get_name (PT_NODE * nam)
{
if (nam && nam->node_type == PT_NAME)
{
assert (nam->info.name.original != NULL);
return nam->info.name.original;
}
else
{
return NULL;
}
}
/*
* pt_host_var_index () - return a PT_HOST_VAR's index
* return: hv's index if all OK, -1 otherwise.
* hv(in): a PT_HOST_VAR type node
*/
int
pt_host_var_index (const PT_NODE * hv)
{
if (hv && hv->node_type == PT_HOST_VAR)
{
return hv->info.host_var.index;
}
else
{
return -1;
}
}
/*
* pt_get_input_host_vars () - return a PT_HOST_VARS' list of input host_vars
* return: hv's list of input host_vars
* hv(in): a PT_HOST_VARS structure created by pt_host_info
*/
PT_NODE *
pt_get_input_host_vars (const PT_HOST_VARS * hv)
{
if (hv)
{
return hv->inputs;
}
else
{
return NULL;
}
}
/*
* pt_get_output_host_vars () - return a PT_HOST_VARS' list of output host_vars
* return: hv's list of output host_vars
* hv(in): a PT_HOST_VARS structure created by pt_host_info
*/
PT_NODE *
pt_get_output_host_vars (const PT_HOST_VARS * hv)
{
if (hv)
{
return hv->outputs;
}
else
{
return NULL;
}
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* pt_get_output_host_descr () - return a PT_HOST_VARS' output host_descriptor
* return: hv's output host_descriptor
* hv(in): a PT_HOST_VARS structure created by pt_host_info
*/
PT_NODE *
pt_get_output_host_descr (PT_HOST_VARS * hv)
{
if (hv)
{
return hv->out_descr;
}
else
{
return NULL;
}
}
#endif
/*
* pt_set_update_object () - convert update statement to
* update object statement
* return: none
* parser(in): the parser context
* node(in/out): an esql statement in parse tree form
*/
void
pt_set_update_object (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_NODE *hostvar;
parser_free_tree (parser, node->info.update.spec);
node->info.update.spec = NULL;
hostvar = parser_new_node (parser, PT_HOST_VAR);
hostvar->info.host_var.str = pt_append_string (parser, NULL, "?");
node->info.update.object_parameter = hostvar;
}
/*
* pt_chop_trailing_dots () - return copy of string with
* its trailing dots removed
* return: copy of msg with any trailing dots removed if all OK,
* NULL otherwise
* parser(in): the parser context
* msg(in) : a null-terminated character string
*/
char *
pt_chop_trailing_dots (PARSER_CONTEXT * parser, const char *msg)
{
char *c, *s = NULL;
int l;
assert (parser != NULL);
if (!msg || (l = strlen (msg)) <= 0)
{
return (char *) "NULL message string";
}
c = pt_append_string (parser, s, msg);
s = &c[l - 1];
while (char_isspace (*s) || *s == '.')
{
s--;
}
*(s + 1) = '\0';
return c;
}
/*
* pt_get_proxy_spec_name () - return a proxy query_spec's "from" entity name
* return: qspec's from entity name if all OK, NULL otherwise
* parser(in): the parser context
* qspec(in): a proxy's SELECT query specification
*/
const char *
pt_get_proxy_spec_name (PARSER_CONTEXT * parser, const char *qspec)
{
PT_NODE **qtree;
const char *from_name = NULL;
assert (parser != NULL);
if (qspec)
{
qtree = parser_parse_string (parser, qspec);
if (qtree && qtree[0])
{
if (!pt_has_error (parser))
{
from_name = pt_get_spec_name (parser, qtree[0]);
}
parser_free_tree (parser, qtree[0]);
}
/* remove error which is occured in this function */
pt_reset_error (parser);
parser->flag.has_internal_error = 0;
}
return from_name;
}
/*
* pt_register_orphan () - Accepts PT_NODE and puts it on the parser's
* orphan list for subsequent freeing
* return: none
* parser(in):
* orphan(in):
*/
void
pt_register_orphan (PARSER_CONTEXT * parser, const PT_NODE * orphan)
{
PT_NODE *dummy;
if (orphan)
{
/* this node has already been freed. */
if (orphan->node_type == PT_LAST_NODE_NUMBER)
{
assert_release (false);
return;
}
dummy = parser_new_node (parser, PT_EXPR);
dummy->info.expr.op = PT_NOT; /* probably not necessary */
dummy->info.expr.arg1 = (PT_NODE *) orphan;
parser->orphans = parser_append_node (dummy, parser->orphans);
}
}
/*
* pt_register_orphan_db_value () - Accepts a db_value, wraps a PT_VALUE node
* around it for convenience, and puts it on the parser's orphan
* list for subsequent freeing
* return: none
* parser(in):
* orphan(in):
*/
void
pt_register_orphan_db_value (PARSER_CONTEXT * parser, const DB_VALUE * orphan)
{
PT_NODE *dummy;
if (orphan)
{
dummy = parser_new_node (parser, PT_VALUE);
dummy->info.value.db_value_is_in_workspace = 1;
dummy->info.value.db_value = *orphan; /* structure copy */
parser->orphans = parser_append_node (dummy, parser->orphans);
}
}
/*
* pt_free_orphans () - Frees all of the registered orphans
* return:
* parser(in):
*/
void
pt_free_orphans (PARSER_CONTEXT * parser)
{
PT_NODE *ptr, *next;
ptr = parser->orphans;
while (ptr)
{
next = ptr->next;
ptr->next = NULL; /* cut off link */
parser_free_tree (parser, ptr);
ptr = next; /* next to the link */
}
parser->orphans = NULL;
}
/*
* pt_free_escape_char () - Frees the escape sequence of a PT_LIKE node and
* leaves only the LIKE pattern in the parse tree.
* parser(in):
* like(in):
* pattern(in):
* escape(in):
*/
void
pt_free_escape_char (PARSER_CONTEXT * const parser, PT_NODE * const like, PT_NODE * const pattern,
PT_NODE * const escape)
{
PT_NODE *const save_arg2 = like->info.expr.arg2;
assert (escape != NULL);
assert (PT_IS_EXPR_NODE_WITH_OPERATOR (save_arg2, PT_LIKE_ESCAPE));
assert (save_arg2->info.expr.arg1 == pattern);
assert (save_arg2->info.expr.arg2 == escape);
save_arg2->info.expr.arg1 = NULL;
parser_free_tree (parser, save_arg2);
like->info.expr.arg2 = pattern;
}
/*
* pt_sort_spec_cover () -
* return: true or false
* cur_list(in): current PT_SORT_SPEC list pointer
* new_list(in): new PT_SORT_SPEC list pointer
*/
bool
pt_sort_spec_cover (PT_NODE * cur_list, PT_NODE * new_list)
{
PT_NODE *s1, *s2;
QFILE_TUPLE_VALUE_POSITION *p1, *p2;
if (new_list == NULL)
{
return false;
}
for (s1 = cur_list, s2 = new_list; s1 && s2; s1 = s1->next, s2 = s2->next)
{
p1 = &(s1->info.sort_spec.pos_descr);
p2 = &(s2->info.sort_spec.pos_descr);
if (p1->pos_no <= 0)
{
s1 = NULL; /* mark as end-of-sort */
}
if (p2->pos_no <= 0)
{
s2 = NULL; /* mark as end-of-sort */
}
/* end-of-sort check */
if (s1 == NULL || s2 == NULL)
{
break;
}
/* equality check */
if (p1->pos_no != p2->pos_no || s1->info.sort_spec.asc_or_desc != s2->info.sort_spec.asc_or_desc
|| (s1->info.sort_spec.nulls_first_or_last != s2->info.sort_spec.nulls_first_or_last))
{
return false;
}
}
return (s2 == NULL) ? true : false;
}
/*
* pt_sort_spec_cover_min_max () -
* return: true or false
* cur_list(in): current PT_SORT_SPEC list pointer
* new_list(in): new PT_SORT_SPEC list pointer
*/
bool
pt_sort_spec_cover_for_min_max (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE * cur_list, PT_NODE * new_list)
{
PT_NODE *s1, *s2;
QFILE_TUPLE_VALUE_POSITION *p1, p2;
if (new_list == NULL || cur_list == NULL)
{
return false;
}
for (s1 = cur_list, s2 = new_list; s1 && s2; s1 = s1->next, s2 = s2->next)
{
p1 = &(s1->info.sort_spec.pos_descr);
if (s2->node_type != PT_NAME)
{
return false;
}
pt_to_pos_descr (parser, &p2, s2, tree, NULL, true);
if (p1->pos_no <= 0)
{
s1 = NULL; /* mark as end-of-sort */
}
if (p2.pos_no <= 0)
{
s2 = NULL; /* mark as end-of-sort */
}
/* end-of-sort check */
if (s1 == NULL || s2 == NULL)
{
break;
}
/* equality check */
if (p1->pos_no != p2.pos_no)
{
return false;
}
}
return true;
}
/* pt_enter_packing_buf() - mark the beginning of another level of packing
* return: none
*/
void
pt_enter_packing_buf (void)
{
++packing_level;
}
/* pt_alloc_packing_buf() - allocate space for packing
* return: pointer to the allocated space if all OK, NULL otherwise
* size(in): the amount of space to be allocated
*/
char *
pt_alloc_packing_buf (int size)
{
char *res;
HL_HEAPID heap_id;
int i;
if (size <= 0)
{
return NULL;
}
if (packing_heap == NULL)
{
packing_heap_num_slot = PACKING_MMGR_BLOCK_SIZE;
packing_heap = (HL_HEAPID *) calloc (packing_heap_num_slot, sizeof (HL_HEAPID));
if (packing_heap == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
PACKING_MMGR_BLOCK_SIZE * sizeof (HL_HEAPID));
return NULL;
}
}
else if (packing_heap_num_slot == packing_level - 1)
{
packing_heap_num_slot += PACKING_MMGR_BLOCK_SIZE;
packing_heap = (HL_HEAPID *) realloc (packing_heap, packing_heap_num_slot * sizeof (HL_HEAPID));
if (packing_heap == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
PACKING_MMGR_BLOCK_SIZE * sizeof (HL_HEAPID));
return NULL;
}
for (i = 0; i < PACKING_MMGR_BLOCK_SIZE; i++)
{
packing_heap[packing_heap_num_slot - i - 1] = 0;
}
}
heap_id = packing_heap[packing_level - 1];
if (heap_id == 0)
{
heap_id = db_create_ostk_heap (PACKING_MMGR_CHUNK_SIZE);
packing_heap[packing_level - 1] = heap_id;
}
if (heap_id == 0)
{
/* make sure an error is set, one way or another */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) PACKING_MMGR_CHUNK_SIZE);
res = NULL;
}
else
{
res = (char *) db_ostk_alloc (heap_id, size);
}
if (res == NULL)
{
qp_Packing_er_code = -1;
}
return res;
}
/* pt_free_packing_buf() - free packing space
* return: none
* slot(in): index of the packing space
*/
static void
pt_free_packing_buf (int slot)
{
if (packing_heap && slot >= 0 && packing_heap[slot])
{
db_destroy_ostk_heap (packing_heap[slot]);
packing_heap[slot] = 0;
}
}
/* pt_exit_packing_buf() - mark the end of another level of packing
* return: none
*/
void
pt_exit_packing_buf (void)
{
--packing_level;
pt_free_packing_buf (packing_level);
}
/* pt_final_packing_buf() - free all resources for packing
* return: none
*/
void
pt_final_packing_buf (void)
{
int i;
for (i = 0; i < packing_level; i++)
{
pt_free_packing_buf (i);
}
free_and_init (packing_heap);
packing_level = packing_heap_num_slot = 0;
}
/*
*
* Function group:
* Query process regulator
*
*/
/*
* regu_set_error_with_zero_args () -
* return:
* err_type(in) : error code
*
* Note: Error reporting function for error messages with no arguments.
*/
void
regu_set_error_with_zero_args (int err_type)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_type, 0);
qp_Packing_er_code = err_type;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* regu_set_error_with_one_args () -
* return:
* err_type(in) : error code
* infor(in) : message
*
* Note: Error reporting function for error messages with one string argument.
*/
void
regu_set_error_with_one_args (int err_type, const char *infor)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_type, 1, infor);
qp_Packing_er_code = err_type;
}
/*
* regu_set_global_error () -
* return:
*
* Note: Set the client side query processor global error code.
*/
void
regu_set_global_error (void)
{
qp_Packing_er_code = ER_REGU_SYSTEM;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* pt_limit_to_numbering_expr () -rewrite limit expr to xxx_num() expr
* return: expr node with numbering
* limit(in): limit node
* num_op(in):
* is_gry_num(in):
*
*/
PT_NODE *
pt_limit_to_numbering_expr (PARSER_CONTEXT * parser, PT_NODE * limit, PT_OP_TYPE num_op, bool is_gby_num)
{
PT_NODE *lhs, *sum, *part1, *part2, *node;
DB_VALUE sum_val;
db_make_null (&sum_val);
if (limit == NULL)
{
return NULL;
}
lhs = sum = part1 = part2 = node = NULL;
if (is_gby_num == true)
{
lhs = parser_new_node (parser, PT_FUNCTION);
if (lhs != NULL)
{
lhs->type_enum = PT_TYPE_INTEGER;
lhs->info.function.function_type = PT_GROUPBY_NUM;
lhs->info.function.arg_list = NULL;
lhs->info.function.all_or_distinct = PT_ALL;
}
}
else
{
lhs = parser_new_node (parser, PT_EXPR);
if (lhs != NULL)
{
lhs->info.expr.op = num_op;
}
}
if (lhs == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
if (limit->next == NULL)
{
node = parser_new_node (parser, PT_EXPR);
if (node == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
if (is_gby_num)
{
PT_EXPR_INFO_SET_FLAG (node, PT_EXPR_INFO_GROUPBYNUM_LIMIT);
}
node->info.expr.op = PT_LE;
node->info.expr.arg1 = lhs;
lhs = NULL;
node->info.expr.arg2 = parser_copy_tree (parser, limit);
if (node->info.expr.arg2 == NULL)
{
goto error_exit;
}
}
else
{
part1 = parser_new_node (parser, PT_EXPR);
if (part1 == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
part1->info.expr.op = PT_GT;
part1->type_enum = PT_TYPE_LOGICAL;
part1->info.expr.arg1 = lhs;
lhs = NULL;
part1->info.expr.arg2 = parser_copy_tree (parser, limit);
if (part1->info.expr.arg2 == NULL)
{
goto error_exit;
}
part2 = parser_new_node (parser, PT_EXPR);
if (part2 == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
part2->info.expr.op = PT_LE;
part2->type_enum = PT_TYPE_LOGICAL;
part2->info.expr.arg1 = parser_copy_tree (parser, part1->info.expr.arg1);
if (part2->info.expr.arg1 == NULL)
{
goto error_exit;
}
sum = parser_new_node (parser, PT_EXPR);
if (sum == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
sum->info.expr.op = PT_PLUS;
sum->type_enum = PT_TYPE_NUMERIC;
sum->data_type = parser_new_node (parser, PT_DATA_TYPE);
if (sum->data_type == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
sum->data_type->type_enum = PT_TYPE_NUMERIC;
sum->data_type->info.data_type.precision = DB_MAX_NUMERIC_PRECISION;
sum->data_type->info.data_type.dec_precision = 0;
sum->info.expr.arg1 = parser_copy_tree (parser, limit);
sum->info.expr.arg2 = parser_copy_tree (parser, limit->next);
if (sum->info.expr.arg1 == NULL || sum->info.expr.arg2 == NULL)
{
goto error_exit;
}
if (limit->node_type == PT_VALUE && limit->next->node_type == PT_VALUE)
{
pt_evaluate_tree (parser, sum, &sum_val, 1);
part2->info.expr.arg2 = pt_dbval_to_value (parser, &sum_val);
if (part2->info.expr.arg2 == NULL)
{
goto error_exit;
}
parser_free_tree (parser, sum);
}
else
{
part2->info.expr.arg2 = sum;
}
sum = NULL;
node = parser_new_node (parser, PT_EXPR);
if (node == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto error_exit;
}
if (is_gby_num)
{
PT_EXPR_INFO_SET_FLAG (part1, PT_EXPR_INFO_GROUPBYNUM_LIMIT);
PT_EXPR_INFO_SET_FLAG (part2, PT_EXPR_INFO_GROUPBYNUM_LIMIT);
}
node->info.expr.op = PT_AND;
node->type_enum = PT_TYPE_LOGICAL;
node->info.expr.arg1 = part1;
node->info.expr.arg2 = part2;
}
return node;
error_exit:
if (lhs)
{
parser_free_tree (parser, lhs);
}
if (sum)
{
parser_free_tree (parser, sum);
}
if (part1)
{
parser_free_tree (parser, part1);
}
if (part2)
{
parser_free_tree (parser, part2);
}
if (node)
{
parser_free_tree (parser, node);
}
return NULL;
}
/*
* pt_to_null_ordering () - get null ordering from a sort spec
* return : null ordering
* sort_spec (in) : sort spec
*/
SORT_NULLS
pt_to_null_ordering (PT_NODE * sort_spec)
{
assert_release (sort_spec != NULL);
assert_release (sort_spec->node_type == PT_SORT_SPEC);
switch (sort_spec->info.sort_spec.nulls_first_or_last)
{
case PT_NULLS_FIRST:
return S_NULLS_FIRST;
case PT_NULLS_LAST:
return S_NULLS_LAST;
case PT_NULLS_DEFAULT:
default:
break;
}
if (sort_spec->info.sort_spec.asc_or_desc == PT_ASC)
{
return S_NULLS_FIRST;
}
return S_NULLS_LAST;
}
/*
* pt_create_param_for_value () - Creates a PT_NODE to be used as a host
* variable that replaces an existing value
* return: the node or NULL on error
* parser(in):
* value(in): the value to be replaced
* host_var_index(in): the index of the host variable that replaces the value
*/
static PT_NODE *
pt_create_param_for_value (PARSER_CONTEXT * parser, PT_NODE * value, int host_var_index)
{
PT_NODE *host_var = parser_new_node (parser, PT_HOST_VAR);
if (host_var == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
host_var->type_enum = value->type_enum;
host_var->expected_domain = value->expected_domain;
host_var->data_type = parser_copy_tree (parser, value->data_type);
host_var->info.host_var.var_type = PT_HOST_IN;
host_var->info.host_var.str = pt_append_string (parser, NULL, "?");
if (PT_IS_VALUE_NODE (value) && value->info.value.host_var_index != -1)
{
/* this value come from a host_var, we just need restore host_var. */
host_var->info.host_var.index = value->info.value.host_var_index;
assert (host_var->info.host_var.index < parser->host_var_count);
}
else
{
host_var->info.host_var.index = host_var_index;
}
host_var->expr_before_const_folding = value->expr_before_const_folding;
return host_var;
}
/*
* pt_rewrite_to_auto_param () - Rewrites a value to a host variable and fills
* in the value of the new host variable in the
* auto parameters values array
* return: the new node or NULL on error
* parser(in):
* value(in): the value to be replaced by a host variable parameter
*/
PT_NODE *
pt_rewrite_to_auto_param (PARSER_CONTEXT * parser, PT_NODE * value)
{
PT_NODE *host_var = NULL;
DB_VALUE *host_var_val = NULL;
DB_VALUE *val = NULL;
int count_to_realloc = 0;
DB_VALUE *larger_host_variables = NULL;
assert (pt_is_const_not_hostvar (value));
/* The index number of auto-parameterized host variables starts after the last one of the user-specified host
* variables. */
host_var = pt_create_param_for_value (parser, value, parser->host_var_count + parser->auto_param_count);
if (host_var == NULL)
{
goto error_exit;
}
PT_NODE_MOVE_NUMBER_OUTERLINK (host_var, value);
if (PT_IS_VALUE_NODE (value) && value->info.value.host_var_index != -1)
{
/* this value come from a host_var, realloc is not needed. */
assert (host_var->info.host_var.index < parser->host_var_count);
}
else
{
/* Expand parser->host_variables by realloc */
count_to_realloc = parser->host_var_count + parser->auto_param_count + 1;
/* We actually allocate around twice more than needed so that we don't do useless copies too often. */
count_to_realloc = (count_to_realloc / 2) * 4;
if (count_to_realloc == 0)
{
count_to_realloc = 1;
}
larger_host_variables = (DB_VALUE *) realloc (parser->host_variables, count_to_realloc * sizeof (DB_VALUE));
if (larger_host_variables == NULL)
{
PT_ERRORm (parser, value, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
goto error_exit;
}
parser->host_variables = larger_host_variables;
++parser->auto_param_count;
larger_host_variables = NULL;
/* Copy the DB_VALUE to parser->host_variables */
host_var_val = parser->host_variables + host_var->info.host_var.index;
val = pt_value_to_db (parser, value);
/* TODO Is it ok to ignore errors here? */
if (val != NULL)
{
(void) pr_clone_value (val, host_var_val);
}
else
{
db_make_null (host_var_val);
}
}
parser_free_tree (parser, value);
return host_var;
error_exit:
if (host_var != NULL)
{
parser_free_tree (parser, host_var);
host_var = NULL;
}
return NULL;
}
/*
* pt_copy_statement_flags () - Copies the special flags relevant for statement
* execution from one node to another. This is
* useful for executing statements that are
* generated by rewriting existing statements
* (see CREATE ... AS SELECT for an example).
* source(in): the statement to copy the flags from
* destination(in/out): the statement to copy the flags to
*
* Note: Not all the PT_NODE flags are copied, only the ones needed for correct
* execution of a statement are copied.
*/
void
pt_copy_statement_flags (PT_NODE * source, PT_NODE * destination)
{
destination->flag.recompile = source->flag.recompile;
destination->flag.cannot_prepare = source->flag.cannot_prepare;
destination->flag.si_datetime = source->flag.si_datetime;
destination->flag.si_tran_id = source->flag.si_tran_id;
}
/*
* pt_get_dup_key_oid_var_index () - Gets the index of the auto-parameterized
* host variable in the INSERT ON DUPLICATE
* KEY UPDATE statement
* return: the index or -1 on error
* parser(in):
* update_statement(in):
*
* Note: The host variable will be replaced at runtime with the value of the
* OID of an existing duplicate key that will be updated. See
* pt_dup_key_update_stmt () to have a better understanding of what will
* be executed.
*/
int
pt_get_dup_key_oid_var_index (PARSER_CONTEXT * parser, PT_NODE * update_statement)
{
PT_NODE *search_cond = NULL;
PT_NODE *oid_node = NULL;
int index = -1;
if (update_statement == NULL || update_statement->node_type != PT_UPDATE)
{
return -1;
}
search_cond = update_statement->info.update.search_cond;
if (search_cond == NULL || search_cond->node_type != PT_EXPR)
{
return -1;
}
oid_node = search_cond->info.expr.arg2;
if (oid_node == NULL || oid_node->node_type != PT_HOST_VAR)
{
return -1;
}
index = oid_node->info.host_var.index;
if (oid_node->info.host_var.var_type != PT_HOST_IN)
{
return -1;
}
if (index < 0 || index >= (parser->host_var_count + parser->auto_param_count))
{
return -1;
}
return index;
}
/*
* pt_dup_key_update_stmt () - Builds a special UPDATE statement to be used
* for INSERT ON DUPLICATE KEY UPDATE statement
* processing
* return: the UPDATE statement or NULL on error
* parser(in):
* spec(in):
* assignment(in):
*
* Note: For the following INSERT statement:
* INSERT INTO tbl VALUES (1) ON DUPLICATE KEY UPDATE id = 4
* the following UPDATE statement will be generated:
* UPDATE tbl SET id = 4 WHERE tbl = OID_OF_DUPLICATE_KEY (tbl)
* After type checking, constant folding and auto-parameterization, the
* OID_OF_DUPLICATE_KEY (tbl) node will be replaced by a host variable.
* At runtime, the variable will be assigned the OID of a duplicate key
* and the UPDATE XASL will be executed.
*/
PT_NODE *
pt_dup_key_update_stmt (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * assignment)
{
PT_NODE *node = parser_new_node (parser, PT_UPDATE);
PT_NODE *name_node = NULL;
PT_NODE *name_arg_node = NULL;
PT_NODE *func_node = NULL;
if (node == NULL)
{
goto error_exit;
}
if (spec->node_type != PT_SPEC || spec->info.spec.entity_name == NULL)
{
goto error_exit;
}
name_node = parser_copy_tree (parser, spec->info.spec.entity_name);
if (name_node == NULL || name_node->node_type != PT_NAME)
{
goto error_exit;
}
PT_NAME_INFO_SET_FLAG (name_node, PT_NAME_INFO_GENERATED_OID);
name_arg_node = parser_copy_tree (parser, name_node);
if (name_arg_node == NULL)
{
goto error_exit;
}
node->info.update.spec = parser_copy_tree (parser, spec);
if (node->info.update.spec == NULL)
{
goto error_exit;
}
/* This will be replaced by a OID PT_VALUE node in constant folding, see pt_fold_const_expr () */
func_node = pt_expression_1 (parser, PT_OID_OF_DUPLICATE_KEY, name_arg_node);
if (func_node == NULL)
{
goto error_exit;
}
name_arg_node = NULL;
node->info.update.search_cond = pt_expression_2 (parser, PT_EQ, name_node, func_node);
if (node->info.update.search_cond == NULL)
{
goto error_exit;
}
/* We need the OID PT_VALUE to become a host variable, see qo_rewrite_queries () */
node->info.update.search_cond->flag.force_auto_parameterize = 1;
/* We don't want constant folding on the WHERE clause because it might result in the host variable being removed from
* the tree. */
node->info.update.search_cond->flag.do_not_fold = 1;
func_node = NULL;
name_node = NULL;
node->info.update.assignment = assignment;
return node;
error_exit:
if (func_node != NULL)
{
parser_free_tree (parser, func_node);
func_node = NULL;
}
if (name_arg_node != NULL)
{
parser_free_tree (parser, name_arg_node);
name_arg_node = NULL;
}
if (name_node != NULL)
{
parser_free_tree (parser, name_node);
name_node = NULL;
}
if (node != NULL)
{
parser_free_tree (parser, node);
node = NULL;
}
return NULL;
}
/*
* pt_fixup_column_type() - Fixes the type of a SELECT column so that it can
* be used for view creation and for CREATE AS SELECT
* statements
* col(in/out): the SELECT statement column
*
* Note: modifies TP_FLOATING_PRECISION_VALUE precision for char/bit constants
* This code is mostly a hack needed because string literals do not have
* the proper precision set. A better fix is to modify
* pt_db_value_initialize () so that the precision information is set.
*/
void
pt_fixup_column_type (PT_NODE * col)
{
int fixed_precision = 0;
if (col->node_type == PT_VALUE)
{
switch (col->type_enum)
{
case PT_TYPE_CHAR:
case PT_TYPE_VARCHAR:
if (col->info.value.data_value.str != NULL)
{
fixed_precision = col->info.value.data_value.str->length;
if (fixed_precision == 0)
{
fixed_precision = 1;
}
}
break;
case PT_TYPE_BIT:
case PT_TYPE_VARBIT:
switch (col->info.value.string_type)
{
case 'B':
if (col->info.value.data_value.str != NULL)
{
fixed_precision = col->info.value.data_value.str->length;
if (fixed_precision == 0)
{
fixed_precision = 1;
}
}
break;
case 'X':
if (col->info.value.data_value.str != NULL)
{
fixed_precision = col->info.value.data_value.str->length;
if (fixed_precision == 0)
{
fixed_precision = 1;
}
}
break;
default:
break;
}
break;
default:
break;
}
}
/* Convert char(max) to varchar(max), bit(max) to varbit(max) */
if ((col->type_enum == PT_TYPE_CHAR || col->type_enum == PT_TYPE_BIT)
&& col->data_type != NULL && (col->data_type->info.data_type.precision == TP_FLOATING_PRECISION_VALUE))
{
if (col->type_enum == PT_TYPE_CHAR)
{
col->type_enum = PT_TYPE_VARCHAR;
col->data_type->type_enum = PT_TYPE_VARCHAR;
}
else
{
col->type_enum = PT_TYPE_VARBIT;
col->data_type->type_enum = PT_TYPE_VARBIT;
}
if (fixed_precision != 0)
{
col->data_type->info.data_type.precision = fixed_precision;
}
}
}
/*
* pt_get_select_query_columns() - Retrieves the columns of a SELECT query
* result
* return: NO_ERROR on success, non-zero for ERROR
* parser(in): Parser context
* create_select(in): the select statement parse tree
* query_columns(out): the columns of the select statement
*
* Note: The code is very similar to the one in db_compile_statement_local ()
*/
int
pt_get_select_query_columns (PARSER_CONTEXT * parser, PT_NODE * create_select, DB_QUERY_TYPE ** query_columns)
{
PT_NODE *temp_copy = NULL;
DB_QUERY_TYPE *qtype = NULL;
int error = NO_ERROR;
assert (query_columns != NULL);
if (pt_node_to_cmd_type (create_select) != CUBRID_STMT_SELECT)
{
ERROR1 (error, ER_UNEXPECTED, "Expecting a select statement.");
goto error_exit;
}
temp_copy = parser_copy_tree (parser, create_select);
if (temp_copy == NULL)
{
error = ER_FAILED;
goto error_exit;
}
qtype = pt_get_titles (parser, create_select);
if (qtype == NULL)
{
/* We ignore this for now and we try again later. */
}
/* Semantic check for the statement */
temp_copy = pt_compile (parser, temp_copy);
if (temp_copy == NULL || pt_has_error (parser))
{
#if 0
assert (er_errid () != NO_ERROR);
#endif
error = er_errid ();
pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, temp_copy);
if (error == NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
goto error_exit;
}
if (qtype == NULL)
{
qtype = pt_get_titles (parser, temp_copy);
}
if (qtype == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
qtype = pt_fillin_type_size (parser, temp_copy, qtype, DB_NO_OIDS, true, true);
if (qtype == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
/* qtype will be freed later */
*query_columns = qtype;
parser_free_tree (parser, temp_copy);
temp_copy = NULL;
return error;
error_exit:
if (qtype != NULL)
{
db_free_query_format (qtype);
qtype = NULL;
}
if (temp_copy != NULL)
{
parser_free_tree (parser, temp_copy);
temp_copy = NULL;
}
return error;
}
/*
* pt_node_list_to_array() - returns an array of nodes(PT_NODE) from a
* PT_NODE list. Used mainly to convert a list of
* argument nodes to an array of argument nodes
* return: NO_ERROR on success, ER_GENERIC_ERROR on failure
* parser(in): Parser context
* arg_list(in): List of nodes (arguments) chained on next
* arg_array(out): array of nodes (arguments)
* array_size(in): the (allocated) size of array
* num_args(out): the number of nodes found in list
*
* Note: the arg_array must be allocated and sized to 'array_size'
*/
int
pt_node_list_to_array (PARSER_CONTEXT * parser, PT_NODE * arg_list, PT_NODE * arg_array[], const int array_size,
int *num_args)
{
PT_NODE *arg = NULL;
int error = NO_ERROR, len = 0;
assert (array_size > 0);
assert (arg_array != NULL);
assert (arg_list != NULL);
assert (num_args != NULL);
*num_args = 0;
for (arg = arg_list; arg != NULL; arg = arg->next)
{
if (len >= array_size)
{
return ER_GENERIC_ERROR;
}
arg_array[len] = arg;
*num_args = ++len;
}
return error;
}
/*
* pt_make_dotted_identifier() - returns an identifier node (type PT_NAME) or
* a PT_DOT node tree
*
* return: node with constructed identifier, NULL if construction fails
* parser(in): Parser context
* identifier_str(in): string containing full identifier name. Dots ('.')
* are used to delimit class names and column
* names; for this reason, column and class names should
* not contain dots.
*/
static PT_NODE *
pt_make_dotted_identifier (PARSER_CONTEXT * parser, const char *identifier_str)
{
return pt_make_dotted_identifier_internal (parser, identifier_str, 0);
}
/*
* pt_make_dotted_identifier_internal() - builds an identifier node
* (type PT_NAME) or tree (type PT_DOT)
*
* return: node with constructed identifier, NULL if construction fails
* parser(in): Parser context
* identifier_str(in): string containing full identifier name (with dots);
* length must be smaller than maximum allowed
* identifier length
* depth(in): depth of current constructed node relative to PT_DOT subtree
*
* Note : the depth argument is used to flag the PT_NAME node corresponding
* to the first scoping name as 'meta_class = PT_NORMAL'.
* This applies only to dotted identifier names.
*/
static PT_NODE *
pt_make_dotted_identifier_internal (PARSER_CONTEXT * parser, const char *identifier_str, int depth)
{
PT_NODE *identifier = NULL;
char *p_dot = NULL;
assert (depth >= 0);
if (strlen (identifier_str) >= SM_MAX_IDENTIFIER_LENGTH)
{
assert (false);
return NULL;
}
p_dot = (char *) strrchr (identifier_str, '.');
if (p_dot != NULL)
{
char string_name1[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
char string_name2[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
PT_NODE *name1 = NULL;
PT_NODE *name2 = NULL;
int position = CAST_BUFLEN (p_dot - identifier_str);
int remaining = strlen (identifier_str) - position - 1;
assert ((remaining > 0) && (remaining < strlen (identifier_str) - 1));
assert ((position > 0) && (position < strlen (identifier_str) - 1));
strncpy (string_name1, identifier_str, position);
string_name1[position] = '\0';
strncpy (string_name2, p_dot + 1, remaining);
string_name2[remaining] = '\0';
/* create PT_DOT_ - must be left - balanced */
name1 = pt_make_dotted_identifier_internal (parser, string_name1, depth + 1);
name2 = pt_name (parser, string_name2);
if (name1 == NULL || name2 == NULL)
{
return NULL;
}
identifier = parser_new_node (parser, PT_DOT_);
if (identifier == NULL)
{
return NULL;
}
identifier->info.dot.arg1 = name1;
identifier->info.dot.arg2 = name2;
}
else
{
identifier = pt_name (parser, identifier_str);
if (identifier == NULL)
{
return NULL;
}
/* it is a dotted identifier, make the first name PT_NORMAL */
if (depth != 0)
{
identifier->info.name.meta_class = PT_NORMAL;
}
}
assert (identifier != NULL);
return identifier;
}
/*
* pt_add_name_col_to_sel_list() - builds a corresponding node for a table
* column and adds it to the end of the select
* list of a SELECT node
*
* return: error code
* parser(in): Parser context
* select(in): SELECT node
* identifier_str(in): string identifying the column (may contain dots)
* col_alias(in): alias of the new select item
*/
static int
pt_add_name_col_to_sel_list (PARSER_CONTEXT * parser, PT_NODE * select, const char *identifier_str,
const char *col_alias)
{
PT_NODE *sel_item = NULL;
assert (select != NULL);
assert (identifier_str != NULL);
sel_item = pt_make_dotted_identifier (parser, identifier_str);
if (sel_item == NULL)
{
return ER_FAILED;
}
sel_item->alias_print = pt_append_string (parser, NULL, col_alias);
select->info.query.q.select.list = parser_append_node (sel_item, select->info.query.q.select.list);
return NO_ERROR;
}
/*
* pt_add_string_col_to_sel_list() - builds a corresponding node for a table
* column and adds it to the end of the select
* list of a SELECT node
*
* return: void
* parser(in): Parser context
* select(in): SELECT node
* value_string(in): string value
* col_alias(in): alias of the new select item
*/
static void
pt_add_string_col_to_sel_list (PARSER_CONTEXT * parser, PT_NODE * select, const char *value_string,
const char *col_alias)
{
PT_NODE *sel_item = NULL;
assert (select != NULL);
assert (value_string != NULL);
sel_item = pt_make_string_value (parser, value_string);
if (sel_item == NULL)
{
return;
}
sel_item->alias_print = pt_append_string (parser, NULL, col_alias);
select->info.query.q.select.list = parser_append_node (sel_item, select->info.query.q.select.list);
}
/*
* pt_add_table_name_to_from_list() - builds a corresponding node for a table
* 'spec' and adds it to the end of the FROM list
* of a SELECT node
*
* return: newly build PT_NODE, NULL if construction fails
* parser(in): Parser context
* select(in): SELECT node
* table_name(in): table name (should not contain dots), may be NULL if spec
* is a subquery (instead of a table)
* table_alias(in): alias of the table
* auth_bypass(in): bit mask of privileges flags that will bypass
* authorizations
*/
PT_NODE *
pt_add_table_name_to_from_list (PARSER_CONTEXT * parser, PT_NODE * select, const char *table_name,
const char *table_alias, const DB_AUTH auth_bypass)
{
PT_NODE *spec = NULL;
PT_NODE *from_item = NULL;
PT_NODE *range_var = NULL;
if (table_name != NULL)
{
from_item = pt_name (parser, table_name);
if (from_item == NULL)
{
return NULL;
}
}
if (table_alias != NULL)
{
range_var = pt_name (parser, table_alias);
if (range_var == NULL)
{
return NULL;
}
}
spec = pt_entity (parser, from_item, range_var, NULL);
if (spec == NULL)
{
return NULL;
}
spec->info.spec.only_all = PT_ONLY;
spec->info.spec.meta_class = PT_CLASS;
select->info.query.q.select.from = parser_append_node (spec, select->info.query.q.select.from);
spec->info.spec.auth_bypass_mask = auth_bypass;
return spec;
}
/*
* pt_make_pred_name_int_val() - builds a predicate node (PT_EXPR) using a
* column identifier on LHS and a integer value on
* RHS
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* op_type(in): operator type; should be a binary operator that makes sense
* for the passed arguments (such as PT_EQ, PT_GT, ...)
* col_name(in): column name (may contain dots)
* int_value(in): integer to assign to PT_VALUE RHS node
*/
static PT_NODE *
pt_make_pred_name_int_val (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *col_name, const int int_value)
{
PT_NODE *pred_rhs = NULL;
PT_NODE *pred_lhs = NULL;
PT_NODE *pred = NULL;
/* create PT_VALUE for rhs */
pred_rhs = pt_make_integer_value (parser, int_value);
/* create PT_NAME for lhs */
pred_lhs = pt_make_dotted_identifier (parser, col_name);
pred = parser_make_expression (parser, op_type, pred_lhs, pred_rhs, NULL);
return pred;
}
/*
* pt_make_pred_name_string_val() - builds a predicate node (PT_EXPR) using an
* identifier on LHS and a string value on
* RHS
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* op_type(in): operator type; should be a binary operator that makes sense
* for the passed arguments (such as PT_EQ, PT_GT, ...)
* identifier_str(in): column name (may contain dots)
* str_value(in): string to assign to PT_VALUE RHS node
*/
static PT_NODE *
pt_make_pred_name_string_val (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *identifier_str,
const char *str_value)
{
PT_NODE *pred_rhs = NULL;
PT_NODE *pred_lhs = NULL;
PT_NODE *pred = NULL;
/* create PT_VALUE for rhs */
pred_rhs = pt_make_string_value (parser, str_value);
/* create PT_NAME for lhs */
pred_lhs = pt_make_dotted_identifier (parser, identifier_str);
pred = parser_make_expression (parser, op_type, pred_lhs, pred_rhs, NULL);
return pred;
}
/*
* pt_make_pred_with_identifiers() - builds a predicate node (PT_EXPR) using
* an identifier on LHS and another identifier on
* RHS
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* op_type(in): operator type; should be a binary operator that makes sense
* for the passed arguments (such as PT_EQ, PT_GT, ...)
* lhs_identifier(in): LHS column name (may contain dots)
* rhs_identifier(in): RHS column name (may contain dots)
*/
static PT_NODE *
pt_make_pred_with_identifiers (PARSER_CONTEXT * parser, PT_OP_TYPE op_type, const char *lhs_identifier,
const char *rhs_identifier)
{
PT_NODE *pred_rhs = NULL;
PT_NODE *pred_lhs = NULL;
PT_NODE *pred = NULL;
/* create PT_DOT_ for lhs */
pred_lhs = pt_make_dotted_identifier (parser, lhs_identifier);
/* create PT_DOT_ for rhs */
pred_rhs = pt_make_dotted_identifier (parser, rhs_identifier);
pred = parser_make_expression (parser, op_type, pred_lhs, pred_rhs, NULL);
return pred;
}
/*
* pt_make_if_with_expressions() - builds an IF (pred, expr_true, expr_false)
* operator node (PT_EXPR) given two expression
* nodes for true/false values
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* pred(in): a node for expression used as predicate
* expr1(in): expression node for true value of predicate
* expr2(in): expression node for false value of predicate
* alias(in): alias for this new node
*/
static PT_NODE *
pt_make_if_with_expressions (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE * expr1, PT_NODE * expr2,
const char *alias)
{
PT_NODE *if_node = NULL;
if_node = parser_make_expression (parser, PT_IF, pred, expr1, expr2);
if (alias != NULL)
{
if_node->alias_print = pt_append_string (parser, NULL, alias);
}
return if_node;
}
/*
* pt_make_if_with_strings() - builds an IF (pred, expr_true, expr_false)
* operator node (PT_EXPR) using two strings as
* true/false values
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* pred(in): a node for expression used as predicate
* string1(in): string used to build a value node for true value of predicate
* string2(in): string used to build a value node for false value of predicate
* alias(in): alias for this new node
*/
static PT_NODE *
pt_make_if_with_strings (PARSER_CONTEXT * parser, PT_NODE * pred, const char *string1, const char *string2,
const char *alias)
{
PT_NODE *val1_node = NULL;
PT_NODE *val2_node = NULL;
PT_NODE *if_node = NULL;
val1_node = pt_make_string_value (parser, string1);
val2_node = pt_make_string_value (parser, string2);
if_node = pt_make_if_with_expressions (parser, pred, val1_node, val2_node, alias);
return if_node;
}
/*
* pt_make_like_col_expr() - builds a LIKE operator node (PT_EXPR) using
* an identifier on LHS an expression node on RHS
* '<col_name> LIKE <rhs_expr>'
*
* return: newly build node (PT_NODE)
* parser(in): Parser context
* rhs_expr(in): expression node
* col_name(in): LHS column name (may contain dots)
*/
static PT_NODE *
pt_make_like_col_expr (PARSER_CONTEXT * parser, PT_NODE * rhs_expr, const char *col_name)
{
PT_NODE *like_lhs = NULL;
PT_NODE *like_node = NULL;
like_lhs = pt_make_dotted_identifier (parser, col_name);
like_node = parser_make_expression (parser, PT_LIKE, like_lhs, rhs_expr, NULL);
return like_node;
}
/*
* pt_make_outer_select_for_show_stmt() - builds a SELECT node and wrap the
* inner supplied SELECT node
* 'SELECT * FROM (<inner_select>) <select_alias>'
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* inner_select(in): PT_SELECT node
* select_alias(in): alias for the 'FROM specs'
*/
static PT_NODE *
pt_make_outer_select_for_show_stmt (PARSER_CONTEXT * parser, PT_NODE * inner_select, const char *select_alias)
{
/* SELECT * from ( SELECT .... ) <select_alias>; */
PT_NODE *val_node = NULL;
PT_NODE *outer_node = NULL;
PT_NODE *alias_subquery = NULL;
PT_NODE *from_item = NULL;
assert (inner_select != NULL);
assert (inner_select->node_type == PT_SELECT);
val_node = parser_new_node (parser, PT_VALUE);
if (val_node)
{
val_node->type_enum = PT_TYPE_STAR;
}
else
{
return NULL;
}
outer_node = parser_new_node (parser, PT_SELECT);
if (outer_node == NULL)
{
return NULL;
}
PT_SELECT_INFO_SET_FLAG (outer_node, PT_SELECT_INFO_READ_ONLY);
outer_node->info.query.q.select.list = parser_append_node (val_node, outer_node->info.query.q.select.list);
inner_select->info.query.is_subquery = PT_IS_SUBQUERY;
alias_subquery = pt_name (parser, select_alias);
/* add to FROM an empty entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, outer_node, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
return NULL;
}
from_item->info.spec.derived_table = inner_select;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_subquery;
from_item->info.spec.derived_table_type = PT_IS_SUBQUERY;
from_item->info.spec.join_type = PT_JOIN_NONE;
return outer_node;
}
/*
* pt_make_outer_select_for_show_columns() - builds a SELECT node and wrap the
* inner supplied SELECT node
* 'SELECT * FROM (<inner_select>) <select_alias>'
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* inner_select(in): PT_SELECT node
* select_alias(in): alias for the 'FROM specs'
* query_names(in): query column names
* query_aliases(in): query column aliasses
* names_length(in): the length of query_names array
* is_show_full(in): non zero if show full columns
* outer_node(out): the result query
*/
static int
pt_make_outer_select_for_show_columns (PARSER_CONTEXT * parser, PT_NODE * inner_select, const char *select_alias,
const char **query_names, const char **query_aliases, int names_length,
int is_show_full, PT_NODE ** outer_node)
{
/* SELECT * from ( SELECT .... ) <select_alias>; */
PT_NODE *alias_subquery = NULL;
PT_NODE *from_item = NULL;
PT_NODE *query = NULL;
int i, error = NO_ERROR;
assert (inner_select != NULL);
assert (inner_select->node_type == PT_SELECT);
assert (outer_node != NULL);
*outer_node = NULL;
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
return ER_OUT_OF_VIRTUAL_MEMORY;
}
if (is_show_full)
{
PT_SELECT_INFO_SET_FLAG (query, PT_SELECT_FULL_INFO_COLS_SCHEMA);
}
else
{
PT_SELECT_INFO_SET_FLAG (query, PT_SELECT_INFO_COLS_SCHEMA);
}
for (i = 0; i < names_length; i++)
{
error = pt_add_name_col_to_sel_list (parser, query, query_names[i], query_aliases ? query_aliases[i] : NULL);
if (error != NO_ERROR)
{
goto error_exit;
}
}
/* add to FROM an empty entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, query, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
error = ER_FAILED;
goto error_exit;
}
inner_select->info.query.is_subquery = PT_IS_SUBQUERY;
alias_subquery = pt_name (parser, select_alias);
from_item->info.spec.derived_table = inner_select;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_subquery;
from_item->info.spec.derived_table_type = PT_IS_SUBQUERY;
from_item->info.spec.join_type = PT_JOIN_NONE;
*outer_node = query;
return NO_ERROR;
error_exit:
if (query)
{
parser_free_tree (parser, query);
}
return error;
}
/*
* pt_make_select_count_star() - builds a 'SELECT COUNT(*)' node
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
*
* Note : The created node is not complete : FROM and WHERE should be filled
* after using this function
*/
static PT_NODE *
pt_make_select_count_star (PARSER_CONTEXT * parser)
{
PT_NODE *query = NULL;
PT_NODE *sel_item = NULL;
query = parser_new_node (parser, PT_SELECT);
sel_item = parser_new_node (parser, PT_FUNCTION);
if (sel_item == NULL || query == NULL)
{
return NULL;
}
sel_item->info.function.arg_list = NULL;
sel_item->info.function.function_type = PT_COUNT_STAR;
query->info.query.q.select.list = parser_append_node (sel_item, query->info.query.q.select.list);
return query;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* pt_make_field_extra_expr_node() - builds the 'Extra' field for the
* SHOW COLUMNS statment
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
*
* IF( (SELECT count(*)
* FROM _db_serial S
* WHERE S.attr_name = A.attr_name AND
* S.class_name = C.class_name
* ) >= 1 ,
* 'auto_increment',
* '' )
* AS Extra
*
* Note : Currently, only 'auto_increment' is diplayed in the Extra field
*/
static PT_NODE *
pt_make_field_extra_expr_node (PARSER_CONTEXT * parser)
{
PT_NODE *where_item1 = NULL;
PT_NODE *where_item2 = NULL;
PT_NODE *from_item = NULL;
PT_NODE *query = NULL;
PT_NODE *extra_node = NULL;
PT_NODE *pred = NULL;
PT_NODE *pred_rhs = NULL;
/* SELECT .. FROM .. WHERE */
query = pt_make_select_count_star (parser);
if (query == NULL)
{
return NULL;
}
from_item = pt_add_table_name_to_from_list (parser, query, CT_SERIAL_NAME, "S", DB_AUTH_NONE);
/* S.attr_name = A.attr_name */
where_item1 = pt_make_pred_with_identifiers (parser, PT_EQ, "S.attr_name", "A.attr_name");
/* S.class_name = C.class_name */
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "S.class_name", "C.class_name");
/* item1 = item2 AND item2 */
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
query->info.query.q.select.where = parser_append_node (where_item1, query->info.query.q.select.where);
/* IF ( SELECT (..) >=1 , 'auto_increment' , '' ) */
pred_rhs = pt_make_integer_value (parser, 1);
pred = parser_make_expression (parser, PT_GE, query, pred_rhs, NULL);
extra_node = pt_make_if_with_strings (parser, pred, "auto_increment", "", "Extra");
return extra_node;
}
#endif /* ENABLE_UNUSED_FUNCTION */
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* pt_make_field_key_type_expr_node() - builds the 'Key' field for the
* SHOW COLUMNS statment
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
*
* IF ( pri_key_count > 0,
* 'PRI' ,
* IF (uni_key_count > 0 ,
* 'UNI',
* IF (mul_count > 0 ,
* 'MUL',
* '')
* )
* )
*
*
* Note : PRI : when the column is part of an index with primary key
* UNI : when the column is part of an index with unique key
* MUL : when the column is the first column in a non-unique index
* if more than one applies to a column, only the one is displayed,
* in the order PRI,UNI,MUL
* '' : no index on the column
*/
static PT_NODE *
pt_make_field_key_type_expr_node (PARSER_CONTEXT * parser)
{
PT_NODE *pri_key_query = NULL;
PT_NODE *uni_key_query = NULL;
PT_NODE *mul_query = NULL;
PT_NODE *key_node = NULL;
{
/* pri_key_count : (SELECT count (*) FROM (SELECT IK.key_attr_name ATTR, I.is_primary_key PRI_KEY FROM
* _db_index_key IK , _db_index I WHERE IK IN I.key_attrs AND IK.key_attr_name = A.attr_name AND I.class_of =
* A.class_of AND A.class_of.class_name=C.class_name) constraints_pri_key WHERE PRI_KEY=1) */
PT_NODE *sub_query = NULL;
PT_NODE *from_item = NULL;
PT_NODE *where_item1 = NULL;
PT_NODE *where_item2 = NULL;
PT_NODE *alias_subquery = NULL;
/* SELECT IK.key_attr_name ATTR, I.is_primary_key PRI_KEY FROM _db_index_key IK , _db_index I WHERE IK IN
* I.key_attrs AND IK.key_attr_name = A.attr_name AND I.class_of = A.class_of AND A.class_of.class_name =
* C.class_name */
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
/* SELECT list : */
pt_add_name_col_to_sel_list (parser, sub_query, "IK.key_attr_name", "ATTR");
pt_add_name_col_to_sel_list (parser, sub_query, "I.is_primary_key", "PRI_KEY");
/* .. FROM : */
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index_key", "IK", DB_AUTH_SELECT);
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index", "I", DB_AUTH_SELECT);
/* .. WHERE : */
where_item1 = pt_make_pred_with_identifiers (parser, PT_IS_IN, "IK", "I.key_attrs");
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "IK.key_attr_name", "A.attr_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "I.class_of", "A.class_of");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "A.class_of.class_name", "C.class_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
/* WHERE clause should be empty */
assert (sub_query->info.query.q.select.where == NULL);
sub_query->info.query.q.select.where = parser_append_node (where_item1, sub_query->info.query.q.select.where);
/* outer query : SELECT count (*) FROM (..) WHERE PRI_KEY=1 */
pri_key_query = pt_make_select_count_star (parser);
if (pri_key_query == NULL)
{
return NULL;
}
/* add to FROM and empy entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, pri_key_query, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
return NULL;
}
alias_subquery = pt_name (parser, "constraints_pri_key");
from_item->info.spec.derived_table = sub_query;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_subquery;
from_item->info.spec.derived_table_type = PT_IS_SUBQUERY;
from_item->info.spec.join_type = PT_JOIN_NONE;
where_item1 = pt_make_pred_name_int_val (parser, PT_EQ, "PRI_KEY", 1);
pri_key_query->info.query.q.select.where =
parser_append_node (where_item1, pri_key_query->info.query.q.select.where);
/* pri_key_count query is done */
}
{
/* uni_key_count : (SELECT count (*) FROM (SELECT IK.key_attr_name ATTR, I.is_unique UNI_KEY FROM _db_index_key IK
* , _db_index I WHERE IK IN I.key_attrs AND IK.key_attr_name = A.attr_name AND I.class_of = A.class_of AND
* A.class_of.class_name = C.class_name) constraints_pri_key WHERE UNI_KEY=1) */
PT_NODE *sub_query = NULL;
PT_NODE *from_item = NULL;
PT_NODE *where_item1 = NULL;
PT_NODE *where_item2 = NULL;
PT_NODE *alias_subquery = NULL;
/* SELECT IK.key_attr_name ATTR, I.is_unique UNI_KEY FROM _db_index_key IK , _db_index I WHERE IK IN I.key_attrs
* AND IK.key_attr_name = A.attr_name AND I.class_of = A.class_of AND A.class_of.class_name = C.class_name */
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
/* SELECT list : */
pt_add_name_col_to_sel_list (parser, sub_query, "IK.key_attr_name", "ATTR");
pt_add_name_col_to_sel_list (parser, sub_query, "I.is_unique", "UNI_KEY");
/* .. FROM : */
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index_key", "IK", DB_AUTH_SELECT);
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index", "I", DB_AUTH_SELECT);
/* .. WHERE : */
where_item1 = pt_make_pred_with_identifiers (parser, PT_IS_IN, "IK", "I.key_attrs");
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "IK.key_attr_name", "A.attr_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "I.class_of", "A.class_of");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "A.class_of.class_name", "C.class_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
/* where should be empty */
assert (sub_query->info.query.q.select.where == NULL);
sub_query->info.query.q.select.where = parser_append_node (where_item1, sub_query->info.query.q.select.where);
/* outer query : SELECT count (*) FROM (..) WHERE PRI_KEY=1 */
uni_key_query = pt_make_select_count_star (parser);
if (uni_key_query == NULL)
{
return NULL;
}
/* add to FROM and empy entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, uni_key_query, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
return NULL;
}
alias_subquery = pt_name (parser, "constraints_uni_key");
from_item->info.spec.derived_table = sub_query;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_subquery;
from_item->info.spec.derived_table_type = PT_IS_SUBQUERY;
from_item->info.spec.join_type = PT_JOIN_NONE;
where_item1 = pt_make_pred_name_int_val (parser, PT_EQ, "UNI_KEY", 1);
uni_key_query->info.query.q.select.where =
parser_append_node (where_item1, uni_key_query->info.query.q.select.where);
/* uni_key_count query is done */
}
{
/* mul_count : (SELECT count (*) FROM (SELECT IK.key_attr_name ATTR FROM _db_index_key IK , _db_index I WHERE IK IN
* I.key_attrs AND IK.key_attr_name = A.attr_name AND I.class_of = A.class_of AND A.class_of.class_name =
* C.class_name AND IK.key_order = 0) constraints_no_index ) */
PT_NODE *sub_query = NULL;
PT_NODE *from_item = NULL;
PT_NODE *where_item1 = NULL;
PT_NODE *where_item2 = NULL;
PT_NODE *alias_subquery = NULL;
/* SELECT IK.key_attr_name ATTR FROM _db_index_key IK , _db_index I WHERE IK IN I.key_attrs AND IK.key_attr_name =
* A.attr_name AND I.class_of = A.class_of AND A.class_of.class_name = C.class_name AND IK.key_order = 0 */
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
/* SELECT list : */
pt_add_name_col_to_sel_list (parser, sub_query, "IK.key_attr_name", "ATTR");
/* .. FROM : */
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index_key", "IK", DB_AUTH_SELECT);
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_index", "I", DB_AUTH_SELECT);
/* .. WHERE : */
where_item1 = pt_make_pred_with_identifiers (parser, PT_IS_IN, "IK", "I.key_attrs");
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "IK.key_attr_name", "A.attr_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "I.class_of", "A.class_of");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "A.class_of.class_name", "C.class_name");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
where_item2 = pt_make_pred_name_int_val (parser, PT_EQ, "IK.key_order", 0);
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
/* where should be empty */
assert (sub_query->info.query.q.select.where == NULL);
sub_query->info.query.q.select.where = parser_append_node (where_item1, sub_query->info.query.q.select.where);
/* outer query : SELECT count (*) FROM (..) WHERE PRI_KEY=1 */
mul_query = pt_make_select_count_star (parser);
/* add to FROM and empy entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, mul_query, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
return NULL;
}
alias_subquery = pt_name (parser, "constraints_no_index");
assert (alias_subquery != NULL);
from_item->info.spec.derived_table = sub_query;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_subquery;
from_item->info.spec.derived_table_type = PT_IS_SUBQUERY;
from_item->info.spec.join_type = PT_JOIN_NONE;
/* mul_count query is done */
}
/* IF ( pri_key_count > 0, 'PRI' , IF (uni_key_count > 0 , 'UNI', IF (mul_count > 0 , 'MUL', '') ) ) */
{
PT_NODE *if_node1 = NULL;
PT_NODE *if_node2 = NULL;
PT_NODE *if_node3 = NULL;
{
/* IF (mul_count > 0 , 'MUL', '' */
PT_NODE *pred_rhs = NULL;
PT_NODE *pred = NULL;
pred_rhs = pt_make_integer_value (parser, 0);
pred = parser_make_expression (parser, PT_GT, mul_query, pred_rhs, NULL);
if_node3 = pt_make_if_with_strings (parser, pred, "MUL", "", NULL);
}
{
/* IF (uni_key_count > 0 , 'UNI', (..IF..) */
PT_NODE *pred_rhs = NULL;
PT_NODE *pred = NULL;
PT_NODE *string1_node = NULL;
pred_rhs = pt_make_integer_value (parser, 0);
pred = parser_make_expression (parser, PT_GT, uni_key_query, pred_rhs, NULL);
string1_node = pt_make_string_value (parser, "UNI");
if_node2 = pt_make_if_with_expressions (parser, pred, string1_node, if_node3, NULL);
}
{
/* pri_key_count > 0, 'PRI', (..IF..) */
PT_NODE *pred_rhs = NULL;
PT_NODE *pred = NULL;
PT_NODE *string1_node = NULL;
pred_rhs = pt_make_integer_value (parser, 0);
pred = parser_make_expression (parser, PT_GT, pri_key_query, pred_rhs, NULL);
string1_node = pt_make_string_value (parser, "PRI");
if_node1 = pt_make_if_with_expressions (parser, pred, string1_node, if_node2, "Key");
}
key_node = if_node1;
}
return key_node;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* pt_make_sort_spec_with_identifier() - builds a SORT_SPEC for GROUP BY or
* ORDER BY using a column indentifier
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* identifier(in): full name of identifier
* sort_mode(in): sorting ascendint or descending; if this parameter is not
* PT_ASC or PT_DESC, the function will return NULL
*/
static PT_NODE *
pt_make_sort_spec_with_identifier (PARSER_CONTEXT * parser, const char *identifier, PT_MISC_TYPE sort_mode)
{
PT_NODE *group_by_node = NULL;
PT_NODE *group_by_col = NULL;
if (sort_mode != PT_ASC && sort_mode != PT_DESC)
{
assert (false);
return NULL;
}
group_by_node = parser_new_node (parser, PT_SORT_SPEC);
if (group_by_node == NULL)
{
return NULL;
}
group_by_col = pt_make_dotted_identifier (parser, identifier);
group_by_node->info.sort_spec.asc_or_desc = sort_mode;
group_by_node->info.sort_spec.expr = group_by_col;
return group_by_node;
}
/*
* pt_make_sort_spec_with_number() - builds a SORT_SPEC for ORDER BY using
* a numeric indentifier
* used in : < ORDER BY <x> <ASC|DESC> >
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* number_pos(in): position number for ORDER BY
* sort_mode(in): sorting ascendint or descending; if this parameter is not
* PT_ASC or PT_DESC, the function will return NULL
*/
static PT_NODE *
pt_make_sort_spec_with_number (PARSER_CONTEXT * parser, const int number_pos, PT_MISC_TYPE sort_mode)
{
PT_NODE *sort_spec_node = NULL;
PT_NODE *sort_spec_num = NULL;
if (sort_mode != PT_ASC && sort_mode != PT_DESC)
{
assert (false);
return NULL;
}
sort_spec_node = parser_new_node (parser, PT_SORT_SPEC);
if (sort_spec_node == NULL)
{
return NULL;
}
sort_spec_num = pt_make_integer_value (parser, number_pos);
sort_spec_node->info.sort_spec.asc_or_desc = sort_mode;
sort_spec_node->info.sort_spec.expr = sort_spec_num;
return sort_spec_node;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* pt_make_collection_type_subquery_node() - builds a SELECT subquery used
* construct the string to display
* the list of sub-types for a collection
* type (SET , SEQUENCE, MULTISET);
* used to build SHOW COLUMNS statement
*
*
* SELECT AA.attr_name ATTR,
* GROUP_CONCAT( TT.type_name ORDER BY 1 SEPARATOR ',')
* Composed_types
* FROM _db_attribute AA, _db_domain DD , _db_data_type TT
* WHERE AA.class_of.class_name = '<table_name>' AND
* DD.data_type = TT.type_id AND
* DD.object_of IN AA.domains
* GROUP BY AA.attr_name)
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* table_name(in): name of table to filter by ; only the columns from this
* table are checked for their type
*
*/
static PT_NODE *
pt_make_collection_type_subquery_node (PARSER_CONTEXT * parser, const char *table_name)
{
PT_NODE *where_item1 = NULL;
PT_NODE *where_item2 = NULL;
PT_NODE *from_item = NULL;
PT_NODE *query = NULL;
assert (table_name != NULL);
/* SELECT .. FROM .. WHERE */
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
return NULL;
}
query->info.query.is_subquery = PT_IS_SUBQUERY;
/* SELECT list : */
pt_add_name_col_to_sel_list (parser, query, "AA.attr_name", "ATTR");
{
/* add GROUP_CONCAT (...) */
PT_NODE *sel_item = NULL;
PT_NODE *group_concat_field = NULL;
PT_NODE *group_concat_sep = NULL;
PT_NODE *order_by_item = NULL;
sel_item = parser_new_node (parser, PT_FUNCTION);
if (sel_item == NULL)
{
return NULL;
}
sel_item->info.function.function_type = PT_GROUP_CONCAT;
sel_item->info.function.all_or_distinct = PT_ALL;
group_concat_field = pt_make_dotted_identifier (parser, "TT.type_name");
group_concat_sep = pt_make_string_value (parser, ",");
sel_item->info.function.arg_list = parser_append_node (group_concat_sep, group_concat_field);
/* add ORDER BY */
assert (sel_item->info.function.order_by == NULL);
/* By 1 */
order_by_item = pt_make_sort_spec_with_number (parser, 1, PT_ASC);
sel_item->info.function.order_by = order_by_item;
sel_item->alias_print = pt_append_string (parser, NULL, "Composed_types");
query->info.query.q.select.list = parser_append_node (sel_item, query->info.query.q.select.list);
}
/* FROM : */
from_item = pt_add_table_name_to_from_list (parser, query, "_db_attribute", "AA", DB_AUTH_SELECT);
from_item = pt_add_table_name_to_from_list (parser, query, "_db_domain", "DD", DB_AUTH_SELECT);
from_item = pt_add_table_name_to_from_list (parser, query, "_db_data_type", "TT", DB_AUTH_SELECT);
/* WHERE : */
/* AA.class_of.class_name = '<table_name>' */
where_item1 = pt_make_pred_name_string_val (parser, PT_EQ, "AA.class_of.class_name", table_name);
/* DD.data_type = TT.type_id */
where_item2 = pt_make_pred_with_identifiers (parser, PT_EQ, "DD.data_type", "TT.type_id");
/* item1 = item2 AND item2 */
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
/* DD.object_of IN AA.domains */
where_item2 = pt_make_pred_with_identifiers (parser, PT_IS_IN, "DD.object_of", "AA.domains");
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
query->info.query.q.select.where = parser_append_node (where_item1, query->info.query.q.select.where);
/* GROUP BY : */
{
PT_NODE *group_by_node = NULL;
group_by_node = pt_make_sort_spec_with_identifier (parser, "AA.attr_name", PT_ASC);
if (group_by_node == NULL)
{
return NULL;
}
query->info.query.q.select.group_by = group_by_node;
}
return query;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* pt_make_dummy_query_check_table() - builds a SELECT subquery used check
* if the table exists; when attached to
* the SHOW statement, it should cause an
* execution error, instead of displaying
* 'no results';
* used to build SHOW COLUMNS statement
*
* SELECT COUNT(*) FROM <table_name> LIMIT 1;
*
* return: newly build node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* table_name(in): name of table
*
*/
static PT_NODE *
pt_make_dummy_query_check_table (PARSER_CONTEXT * parser, const char *table_name)
{
PT_NODE *limit_item = NULL;
PT_NODE *from_item = NULL;
PT_NODE *query = NULL;
assert (table_name != NULL);
/* This query should cause an execution errors when performing SHOW COLUMNS on a non-existing table or when the user
* doesn't have SELECT privilege on that table; A simpler query like: SELECT 1 FROM <table_name> WHERE FALSE; is
* removed during parse tree optimizations, and no error is printed when user has insuficient privileges. We need a
* query which will not be removed on translation (in order to be kept up to the authentication stage, but also with
* low impact on performance. */
/* We use : SELECT COUNT(*) FROM <table_name> LIMIT 1; TODO: this will impact performance so we might find a better
* solution */
query = pt_make_select_count_star (parser);
if (query == NULL)
{
return NULL;
}
from_item = pt_add_table_name_to_from_list (parser, query, table_name, "DUMMY", DB_AUTH_NONE);
limit_item = pt_make_integer_value (parser, 1);
query->info.query.limit = limit_item;
return query;
}
/*
* pt_make_query_show_table() - builds the query used for SHOW TABLES
*
* SELECT * FROM (SELECT C.class_name AS tables_in_<dbname>,
* IF(class_type='CLASS','VIEW','BASE TABLE')
* AS table_type
* FROM db_class C
* WHERE is_system_class='NO') show_tables
* ORDER BY 1;
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* is_full_syntax(in): true, if the SHOW statement contains the 'FULL' token
* like_where_syntax(in): indicator of presence for LIKE or WHERE clauses in
* SHOW statement. Values : 0 = none of LIKE or WHERE,
* 1 = contains LIKE, 2 = contains WHERE
* like_or_where_expr(in): node expression supplied as condition (in WHERE)
* or RHS for LIKE
*
*/
PT_NODE *
pt_make_query_show_table (PARSER_CONTEXT * parser, bool is_full_syntax, int like_where_syntax,
PT_NODE * like_or_where_expr)
{
PT_NODE *node = NULL;
PT_NODE *sub_query = NULL;
PT_NODE *from_item = NULL;
PT_NODE *where_item = NULL;
char tables_col_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
{
char *db_name = db_get_database_name ();
const char *const col_header = "Tables_in_";
if (db_name == NULL)
{
return NULL;
}
strcpy (tables_col_name, col_header);
strncat (tables_col_name, db_name, SM_MAX_IDENTIFIER_LENGTH - strlen (col_header) - 1);
tables_col_name[SM_MAX_IDENTIFIER_LENGTH - 1] = '\0';
db_string_free (db_name);
db_name = NULL;
}
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
/* ------ SELECT list ------- */
pt_add_name_col_to_sel_list (parser, sub_query, "C.class_name", tables_col_name);
/* ------ SELECT ... FROM ------- */
/* db_class is a view on the _db_class table; we are selecting from the view, to avoid checking the authorization as
* this check is already performed by the view */
from_item = pt_add_table_name_to_from_list (parser, sub_query, "db_class", "C", DB_AUTH_SELECT);
/* ------ SELECT ... WHERE ------- */
/* create item for "WHERE is_system_class = 'NO'" */
where_item = pt_make_pred_name_string_val (parser, PT_EQ, "is_system_class", "NO");
sub_query->info.query.q.select.where = parser_append_node (where_item, sub_query->info.query.q.select.where);
if (is_full_syntax)
{
/* SHOW FULL : add second column : 'BASE TABLE' or 'VIEW' */
PT_NODE *eq_node = NULL;
PT_NODE *if_node = NULL;
/* create IF ( class_type = 'CLASS', 'BASE TABLE', 'VIEW') */
eq_node = pt_make_pred_name_string_val (parser, PT_EQ, "class_type", "CLASS");
if_node = pt_make_if_with_strings (parser, eq_node, "BASE TABLE", "VIEW", "Table_type");
/* add IF to SELECT list, list should not be empty at this point */
assert (sub_query->info.query.q.select.list != NULL);
pt_add_name_col_to_sel_list (parser, sub_query, "C.owner_name", "Owner");
sub_query->info.query.q.select.list = parser_append_node (if_node, sub_query->info.query.q.select.list);
}
/* done with subquery, create the enclosing query : SELECT * from ( SELECT .... ) show_tables; */
node = pt_make_outer_select_for_show_stmt (parser, sub_query, "show_tables");
if (node == NULL)
{
return NULL;
}
{
/* add ORDER BY */
PT_NODE *order_by_item = NULL;
assert (node->info.query.order_by == NULL);
/* By Tables_in_<db_name> */
order_by_item = pt_make_sort_spec_with_number (parser, 1, PT_ASC);
node->info.query.order_by = parser_append_node (order_by_item, node->info.query.order_by);
}
if (like_or_where_expr != NULL)
{
if (like_where_syntax == 1)
{
/* make LIKE */
where_item = pt_make_like_col_expr (parser, like_or_where_expr, tables_col_name);
}
else
{
/* WHERE */
assert (like_where_syntax == 2);
where_item = like_or_where_expr;
}
node->info.query.q.select.where = parser_append_node (where_item, node->info.query.q.select.where);
}
else
{
assert (like_where_syntax == 0);
}
return node;
}
/*
* check_arg_valid() - check argument type and set error code if not valid
*
* return: true - valid, false - not valid, semantic check error will set when return false.
* parser(in):
* arg_meta(in): argument validation rule
* arg_num(in): argument sequence
* val(in): argument value node
*/
static bool
check_arg_valid (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_meta, int arg_num, PT_NODE * val)
{
bool valid = false;
switch (arg_meta->type)
{
case AVT_INTEGER:
if (PT_IS_VALUE_NODE (val) && val->type_enum == PT_TYPE_INTEGER)
{
valid = true;
}
else if (arg_meta->optional && PT_IS_NULL_NODE (val))
{
valid = true;
}
if (!valid)
{
/* expect unsigned integer value in here */
PT_ERRORmf2 (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNEXPECTED_NTH_ARGUMENT,
"positive integer value", arg_num);
}
break;
case AVT_STRING:
if (PT_IS_VALUE_NODE (val) && val->type_enum == PT_TYPE_CHAR)
{
valid = true;
}
else if (arg_meta->optional && PT_IS_NULL_NODE (val))
{
valid = true;
}
if (!valid)
{
/* expect string value in here */
PT_ERRORmf2 (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNEXPECTED_NTH_ARGUMENT, "string value",
arg_num);
}
break;
case AVT_IDENTIFIER:
if (PT_IS_NAME_NODE (val))
{
valid = true;
}
else if (arg_meta->optional && PT_IS_NULL_NODE (val))
{
valid = true;
}
if (!valid)
{
/* expect identifier in here */
PT_ERRORmf2 (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNEXPECTED_NTH_ARGUMENT, "identifier",
arg_num);
}
break;
default:
assert (0);
}
return valid;
}
/*
* pt_resolve_showstmt_args_unnamed() -
* do semantic check for unnamed arguments by specified meta data
* from SHOWSTMT_NAMED_ARG.
*
* return: newly build node (PT_NODE) for arguments
* parser(in): Parser context
* arg_infos(in): array for meta data of argument
* arg_info_count(in): array count of arg_infos
* arg_infos(in): argument node to be processed
*/
static PT_NODE *
pt_resolve_showstmt_args_unnamed (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_infos, int arg_info_count,
PT_NODE * args)
{
int i;
char lower_table_name[DB_MAX_IDENTIFIER_LENGTH];
PT_NODE *arg, *id_string;
PT_NODE *prev = NULL, *head = NULL;
if (arg_info_count == 0)
{
return args;
}
/* process each argument by meta information */
i = 0;
arg = args;
for (; i < arg_info_count && arg != NULL; i++, arg = arg->next)
{
if (!check_arg_valid (parser, &arg_infos[i], i + 1, arg))
{
goto error;
}
if (arg_infos[i].type == AVT_IDENTIFIER)
{
/* store user-specified-name in info.name.original. */
parser_walk_tree (parser, arg, NULL, NULL, pt_set_user_specified_name, NULL);
if (pt_has_error (parser))
{
goto error;
}
intl_identifier_lower (arg->info.name.original, lower_table_name);
/* replace identifier node with string value node */
id_string = pt_make_string_value (parser, lower_table_name);
if (id_string == NULL)
{
goto error;
}
id_string->next = arg->next;
arg = id_string;
}
if (prev == NULL)
{
head = arg;
}
else
{
prev->next = arg;
}
prev = arg;
}
if (arg != NULL)
{
/* too many arguments, n-th argument is not needed */
PT_ERRORm (parser, arg, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_TOO_MANY_ARGUMENT);
goto error;
}
if (i < arg_info_count)
{
/* too few arguments */
PT_ERRORmf (parser, head, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_TOO_FEW_ARGUMENT, arg_info_count);
goto error;
}
return head;
error:
return NULL;
}
/*
* pt_resolve_showstmt_args_named() -
* do semantic check for named arguments by specified meta data
* from SHOWSTMT_NAMED_ARG.
*
* return: newly build node (PT_NODE) for arguments
* parser(in): Parser context
* arg_infos(in): array for meta data of argument
* arg_info_count(in): array count of arg_infos
* arg_infos(in): argument node to be processed
*/
static PT_NODE *
pt_resolve_showstmt_args_named (PARSER_CONTEXT * parser, const SHOWSTMT_NAMED_ARG * arg_infos, int arg_info_count,
PT_NODE * args)
{
int i;
bool found = false;
PT_NODE *name_node, *value_node;
PT_NODE *prev = NULL, *res = NULL;
PT_NODE *arg = NULL;
if (arg_info_count == 0)
{
return args;
}
for (i = 0; i < arg_info_count; i++)
{
found = false;
prev = NULL;
for (arg = args; arg != NULL; arg = arg->next)
{
assert (arg->node_type == PT_NAMED_ARG);
name_node = arg->info.named_arg.name;
value_node = arg->info.named_arg.value;
if (strcasecmp (name_node->info.name.original, arg_infos[i].name) == 0)
{
if (!check_arg_valid (parser, &arg_infos[i], i + 1, value_node))
{
goto error;
}
if (arg_infos[i].type == AVT_IDENTIFIER)
{
/* replace identifier node with string value node */
value_node = pt_make_string_value (parser, value_node->info.name.original);
}
res = parser_append_node (value_node, res);
arg->info.named_arg.value = NULL;
/* remove processed arg */
if (prev)
{
prev->next = arg->next;
}
else
{
args = arg->next;
}
found = true;
break;
}
prev = arg;
}
if (!found)
{
/* missing argument */
PT_ERRORmf (parser, args, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MISSING_ARGUMENT, arg_infos[i].name);
goto error;
}
}
/* all argument should be processed and nothing left */
if (args != NULL)
{
/* unknown argument */
PT_ERRORmf (parser, args, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNKNOWN_ARGUMENT,
arg->info.named_arg.name->info.name.original);
goto error;
}
return res;
error:
return NULL;
}
/*
* pt_make_query_showstmt () - builds the query for SHOW statement
*
* return: newly built node (PT_NODE), NULL if construction fails
* parser(in): Parser context
* type (int): show statement type
* arg (in): show statement arguments
*
* Notes: make query as:
*
* SELECT *
* FROM (pt_showstmt_info(type, args))
* [WHERE expr]
* [ORDER BY sort_col asc_or_desc]
*
*/
PT_NODE *
pt_make_query_showstmt (PARSER_CONTEXT * parser, unsigned int type, PT_NODE * args, int like_where_syntax,
PT_NODE * like_or_where_expr)
{
const SHOWSTMT_METADATA *meta = NULL;
const SHOWSTMT_COLUMN_ORDERBY *orderby = NULL;
int num_orderby;
PT_NODE *query = NULL;
PT_NODE *value, *from_item, *showstmt_info;
PT_NODE *order_by_item;
int i;
/* get show column info */
meta = showstmt_get_metadata ((SHOWSTMT_TYPE) type);
if (meta->only_for_dba)
{
if (!au_is_dba_group_member (Au_user))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_DBA_ONLY, 1, meta->alias_print);
return NULL;
}
}
orderby = meta->orderby;
num_orderby = meta->num_orderby;
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
return NULL;
}
PT_SELECT_INFO_SET_FLAG (query, PT_SELECT_INFO_READ_ONLY);
value = parser_new_node (parser, PT_VALUE);
if (value == NULL)
{
goto error;
}
value->type_enum = PT_TYPE_STAR;
query->info.query.q.select.list = parser_append_node (value, query->info.query.q.select.list);
showstmt_info = parser_new_node (parser, PT_SHOWSTMT);
if (showstmt_info == NULL)
{
goto error;
}
showstmt_info->info.showstmt.show_type = (SHOWSTMT_TYPE) type;
if (meta->args != NULL)
{
if (meta->args[0].name == NULL)
{
showstmt_info->info.showstmt.show_args =
pt_resolve_showstmt_args_unnamed (parser, meta->args, meta->arg_size, args);
}
else
{
showstmt_info->info.showstmt.show_args =
pt_resolve_showstmt_args_named (parser, meta->args, meta->arg_size, args);
}
if (showstmt_info->info.showstmt.show_args == NULL)
{
goto error;
}
}
/* add to FROM an empty entity, the entity will be populated later */
from_item = pt_add_table_name_to_from_list (parser, query, NULL, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
goto error;
}
from_item->info.spec.derived_table = showstmt_info;
from_item->info.spec.derived_table_type = PT_IS_SHOWSTMT;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.join_type = PT_JOIN_NONE;
if (like_or_where_expr != NULL)
{
PT_NODE *where_item = NULL;
if (like_where_syntax == 1)
{
/* there would be least one column */
assert (meta->num_cols > 0);
where_item = pt_make_like_col_expr (parser, like_or_where_expr, meta->cols[0].name);
}
else
{
assert (like_where_syntax == 2);
where_item = like_or_where_expr;
}
query->info.query.q.select.where = parser_append_node (where_item, query->info.query.q.select.where);
}
else
{
assert (like_where_syntax == 0);
}
for (i = 0; i < num_orderby; i++)
{
order_by_item = pt_make_sort_spec_with_number (parser, orderby[i].pos, orderby[i].asc ? PT_ASC : PT_DESC);
if (order_by_item == NULL)
{
goto error;
}
query->info.query.order_by = parser_append_node (order_by_item, query->info.query.order_by);
}
return query;
error:
if (query != NULL)
{
parser_free_tree (parser, query);
}
return NULL;
}
/*
* pt_make_query_show_columns() - builds the query for SHOW COLUMNS
*
* return: newly built node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* original_cls_id(in): node (PT_NAME) containing name of class
*
* SELECT Field AS Field,
* Type AS Type
* [Collation AS Collation]
* Null AS Null
* Key AS Key
* Default AS Default
* Extra AS Extra
* [Comment AS Comment]
* FROM
* (SELECT 0 AS Attr_Type,
* 0 AS Def_Order
* "" AS Field,
* 0 AS Type,
* ["" AS Collation]
* "" AS Null,
* 0 AS Key,
* "" AS Default,
* "" AS Extra,
* ["" AS Comment]
* FROM <table> ORDER BY 3, 5)
* [LIKE 'pattern' | WHERE expr];
*
* like_where_syntax(in): indicator of presence for LIKE or WHERE clauses in
* SHOW statement. Values : 0 = none of LIKE or WHERE,
* 1 = contains LIKE, 2 = contains WHERE
* like_or_where_expr(in): node expression supplied as condition (in WHERE)
* or RHS for LIKE
*
* Note : Order is defined by: attr_type (shared attributes first, then
* class attributes, then normal attributes), order of definition in
* table
* [ ] -> optional fields controlled by 'is_show_full' argument
* Note : At execution, all empty fields from inner query will be replaced by
* values that will be read from class schema
*/
PT_NODE *
pt_make_query_show_columns (PARSER_CONTEXT * parser, PT_NODE * original_cls_id, int like_where_syntax,
PT_NODE * like_or_where_expr, int is_show_full)
{
PT_NODE *from_item = NULL;
PT_NODE *order_by_item = NULL;
PT_NODE *sub_query = NULL;
PT_NODE *outer_query = NULL;
char lower_table_name[DB_MAX_IDENTIFIER_LENGTH];
PT_NODE *value = NULL, *value_list = NULL;
DB_VALUE db_valuep[10];
const char **psubquery_aliases = NULL, **pquery_names = NULL, **pquery_aliases = NULL;
int subquery_list_size = is_show_full ? 10 : 8;
int query_list_size = subquery_list_size - 2;
const char *subquery_aliases[] = { "Attr_Type", "Def_Order", "Field", "Type", "Null", "Key", "Default",
"Extra"
};
const char *subquery_full_aliases[] = { "Attr_Type", "Def_Order", "Field", "Type", "Collation", "Null",
"Key", "Default", "Extra", "Comment"
};
const char *query_names[] = { "Field", "Type", "Null", "Key", "Default", "Extra" };
const char *query_aliases[] = { "Field", "Type", "Null", "Key", "Default", "Extra" };
const char *query_full_names[] = { "Field", "Type", "Collation", "Null", "Key", "Default", "Extra",
"Comment"
};
const char *query_full_aliases[] = { "Field", "Type", "Collation", "Null", "Key", "Default", "Extra",
"Comment"
};
int i = 0;
assert (original_cls_id != NULL);
assert (original_cls_id->node_type == PT_NAME);
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
if (is_show_full)
{
PT_SELECT_INFO_SET_FLAG (sub_query, PT_SELECT_FULL_INFO_COLS_SCHEMA);
}
else
{
PT_SELECT_INFO_SET_FLAG (sub_query, PT_SELECT_INFO_COLS_SCHEMA);
}
/* store user-specified-name in info.name.original. */
parser_walk_tree (parser, original_cls_id, NULL, NULL, pt_set_user_specified_name, NULL);
if (pt_has_error (parser))
{
return NULL;
}
intl_identifier_lower (original_cls_id->info.name.original, lower_table_name);
db_make_int (db_valuep + 0, 0);
db_make_int (db_valuep + 1, 0);
for (i = 2; i < subquery_list_size; i++)
{
db_value_domain_default (db_valuep + i, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
}
psubquery_aliases = is_show_full ? subquery_full_aliases : subquery_aliases;
pquery_names = is_show_full ? query_full_names : query_names;
pquery_aliases = is_show_full ? query_full_aliases : query_aliases;
for (i = 0; i < subquery_list_size; i++)
{
value = pt_dbval_to_value (parser, db_valuep + i);
if (value == NULL)
{
goto error;
}
value->alias_print = pt_append_string (parser, NULL, psubquery_aliases[i]);
value_list = parser_append_node (value, value_list);
}
sub_query->info.query.q.select.list = value_list;
value_list = NULL;
from_item = pt_add_table_name_to_from_list (parser, sub_query, lower_table_name, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
goto error;
}
if (pt_make_outer_select_for_show_columns (parser, sub_query, NULL, pquery_names, pquery_aliases, query_list_size,
is_show_full, &outer_query) != NO_ERROR)
{
goto error;
}
order_by_item = pt_make_sort_spec_with_identifier (parser, "Attr_Type", PT_DESC);
if (order_by_item == NULL)
{
goto error;
}
outer_query->info.query.order_by = parser_append_node (order_by_item, outer_query->info.query.order_by);
order_by_item = pt_make_sort_spec_with_identifier (parser, "Def_Order", PT_ASC);
if (order_by_item == NULL)
{
goto error;
}
outer_query->info.query.order_by = parser_append_node (order_by_item, outer_query->info.query.order_by);
/* no ORDER BY to outer SELECT */
/* add LIKE or WHERE from SHOW , if present */
if (like_or_where_expr != NULL)
{
PT_NODE *where_item = NULL;
if (like_where_syntax == 1)
{
/* LIKE token */
where_item = pt_make_like_col_expr (parser, like_or_where_expr, "Field");
}
else
{
/* WHERE token */
assert (like_where_syntax == 2);
where_item = like_or_where_expr;
}
outer_query->info.query.q.select.where = parser_append_node (where_item, outer_query->info.query.q.select.where);
}
else
{
assert (like_where_syntax == 0);
}
return outer_query;
error:
if (outer_query)
{
parser_free_tree (parser, outer_query);
}
else if (sub_query)
{
parser_free_tree (parser, sub_query);
}
if (value_list)
{
parser_free_tree (parser, value_list);
}
return NULL;
}
/*
* pt_help_show_create_table() help to generate create table string.
* parser(in) : Parser context
* table_name(in): table name node
* strbuf(out) : string of create table.
*/
static void
pt_help_show_create_table (PARSER_CONTEXT * parser, PT_NODE * table_name, string_buffer & strbuf)
{
DB_OBJECT *class_op;
int is_class = 0;
/* look up class in all schema's */
class_op = sm_find_class (table_name->info.name.original);
if (class_op == NULL)
{
if (er_errid () != NO_ERROR)
{
PT_ERRORc (parser, table_name, er_msg ());
}
return;
}
is_class = db_is_class (class_op);
if (is_class < 0)
{
if (er_errid () != NO_ERROR)
{
PT_ERRORc (parser, table_name, er_msg ());
}
return;
}
else if (!is_class)
{
PT_ERRORmf2 (parser, table_name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A,
table_name->info.name.original, pt_show_misc_type (PT_CLASS));
}
object_printer obj_print (strbuf);
obj_print.describe_class (class_op);
if (strbuf.len () == 0)
{
int error = er_errid ();
assert (error != NO_ERROR);
if (error == ER_AU_SELECT_FAILURE)
{
PT_ERRORmf2 (parser, table_name, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON, "select",
db_get_class_name (class_op));
}
else
{
PT_ERRORc (parser, table_name, er_msg ());
}
}
}
/*
* pt_make_query_show_create_table() builds the query used for SHOW CREATE
* TABLE
*
* SELECT 'table_name' as TABLE, 'create table ...' as CREATE TABLE
* FROM db_root
*
* return string of create table.
* parser(in) : Parser context
* table_name(in): table name node
*/
PT_NODE *
pt_make_query_show_create_table (PARSER_CONTEXT * parser, PT_NODE * table_name)
{
PT_NODE *select;
assert (table_name != NULL);
assert (table_name->node_type == PT_NAME);
parser_block_allocator alloc (parser);
string_buffer strbuf (alloc);
/* store user-specified-name in info.name.original. */
parser_walk_tree (parser, table_name, NULL, NULL, pt_set_user_specified_name, NULL);
if (pt_has_error (parser))
{
return NULL;
}
pt_help_show_create_table (parser, table_name, strbuf);
if (strbuf.len () == 0)
{
return NULL;
}
select = parser_new_node (parser, PT_SELECT);
if (select == NULL)
{
return NULL;
}
PT_SELECT_INFO_SET_FLAG (select, PT_SELECT_INFO_READ_ONLY);
/*
* SELECT 'table_name' as TABLE, 'create table ...' as CREATE TABLE
* FROM db_root
*/
pt_add_string_col_to_sel_list (parser, select, table_name->info.name.original, "TABLE");
pt_add_string_col_to_sel_list (parser, select, strbuf.get_buffer (), "CREATE TABLE");
pt_add_table_name_to_from_list (parser, select, "dual", NULL, DB_AUTH_SELECT);
return select;
}
/*
* pt_make_query_show_create_view() - builds the query used for SHOW CREATE
* VIEW
*
* SELECT * FROM
* (SELECT IF( VC.vclass_name = '',
* (SELECT COUNT(*) FROM <view_name> LIMIT 1),
* LOWER(VC.owner_name) + '.' + VC.vclass_name )
* AS VIEW,
* IF( VC.comment IS NULL or VC.comment = '',
* VC.vclass_def,
* VC.vclass_def + ' COMMENT=''' + VC.comment + '''' )
* AS [CREATE VIEW]
* FROM db_vclass VC
* WHERE VC.vclass_name=<lower_view_name> AND
* VC.owner_name=<upper_owner_name>)
* show_create_view;
*
* Note : The first column in query (name of view = VC.vclass_name) is wrapped
* with IF, in order to accomodate a dummy query, which has the role to
* trigger the apropiate error if the view doesn't exist or the user
* doesn't have the privilege to SELECT it; the condition in IF
* expression (VC.vclass_name = '') is supposed to always evaluate to
* false (class name cannot be empty), in order for the query to always
* print what it is supposed to (view name); second purpose of the
* condition is to avoid optimisation (otherwise the IF will evalute to
* <VC.vclass_name>)
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* view_identifier(in): node identifier for view
*/
PT_NODE *
pt_make_query_show_create_view (PARSER_CONTEXT * parser, PT_NODE * view_identifier)
{
PT_NODE *node = NULL;
PT_NODE *from_item = NULL;
char lower_view_name[DB_MAX_IDENTIFIER_LENGTH];
char owner_name[DB_MAX_USER_LENGTH];
char upper_owner_name[DB_MAX_USER_LENGTH];
bool is_system_class_or_vclass = false;
assert (view_identifier != NULL);
assert (view_identifier->node_type == PT_NAME);
/* store user-specified-name in info.name.original. */
parser_walk_tree (parser, view_identifier, NULL, NULL, pt_set_user_specified_name, NULL);
if (pt_has_error (parser))
{
return NULL;
}
node = parser_new_node (parser, PT_SELECT);
if (node == NULL)
{
return NULL;
}
intl_identifier_lower (view_identifier->info.name.original, lower_view_name);
sm_qualifier_name (lower_view_name, owner_name, DB_MAX_USER_LENGTH);
intl_identifier_upper (owner_name, upper_owner_name);
if (sm_check_system_class_by_name (lower_view_name) == true)
{
is_system_class_or_vclass = true;
}
/* ------ SELECT list ------- */
{
/* View name : IF( VC.vclass_name = '',
* (SELECT COUNT(*) FROM <view_name> LIMIT 1),
* LOWER(VC.owner_name) + '.' + VC.vclass_name )
* AS View
*/
PT_NODE *if_true_node = NULL;
PT_NODE *if_false_node = NULL;
PT_NODE *pred = NULL;
PT_NODE *view_field = NULL;
PT_NODE *concat_arg = NULL, *concat_sep = NULL;
PT_NODE *qualifier = NULL;
if_true_node = pt_make_dummy_query_check_table (parser, lower_view_name);
if_false_node = pt_make_dotted_identifier (parser, "VC.vclass_name");
/* LOWER(VC.owner_name) + '.' + VC.vclass_name */
if (!is_system_class_or_vclass)
{
concat_arg = pt_make_dotted_identifier (parser, "VC.owner_name");
concat_arg = parser_make_expression (parser, PT_LOWER, concat_arg, NULL, NULL);
concat_sep = pt_make_string_value (parser, ".");
qualifier = parser_make_expression (parser, PT_CONCAT, concat_arg, concat_sep, NULL);
if_false_node = parser_make_expression (parser, PT_CONCAT, qualifier, if_false_node, NULL);
}
pred = pt_make_pred_name_string_val (parser, PT_EQ, "VC.vclass_name", "");
view_field = pt_make_if_with_expressions (parser, pred, if_true_node, if_false_node, "VIEW");
node->info.query.q.select.list = parser_append_node (view_field, node->info.query.q.select.list);
}
{
/* Create View: IF( VC.comment IS NULL or VC.comment = '', VC.vclass_def, VC.vclass_def + ' COMMENT=''' +
* VC.comment + '''' ) AS Create_View */
PT_NODE *if_true_node = NULL;
PT_NODE *if_false_node = NULL;
PT_NODE *pred = NULL;
PT_NODE *comment_node = NULL;
PT_NODE *create_view_field = NULL;
PT_NODE *lhs = NULL, *rhs = NULL;
if_true_node = pt_make_dotted_identifier (parser, "VC.vclass_def");
lhs = pt_make_pred_name_string_val (parser, PT_CONCAT, "VC.vclass_def", " comment='");
rhs = pt_make_pred_name_string_val (parser, PT_CONCAT, "VC.comment", "'");
if_false_node = parser_make_expression (parser, PT_CONCAT, lhs, rhs, NULL);
comment_node = pt_make_dotted_identifier (parser, "VC.comment");
lhs = parser_make_expression (parser, PT_IS_NULL, comment_node, NULL, NULL);
rhs = pt_make_pred_name_string_val (parser, PT_EQ, "VC.comment", "");
pred = parser_make_expression (parser, PT_OR, lhs, rhs, NULL);
create_view_field = pt_make_if_with_expressions (parser, pred, if_true_node, if_false_node, "CREATE VIEW");
node->info.query.q.select.list = parser_append_node (create_view_field, node->info.query.q.select.list);
}
/* ------ SELECT ... FROM ------- */
from_item = pt_add_table_name_to_from_list (parser, node, "db_vclass", "VC", DB_AUTH_SELECT);
/* ------ SELECT ... WHERE ------- */
{
PT_NODE *where_item1 = NULL, *where_item2 = NULL;
/* VC.vclass_name = <lower_view_name> */
where_item1 =
pt_make_pred_name_string_val (parser, PT_EQ, "VC.vclass_name", sm_remove_qualifier_name (lower_view_name));
if (!is_system_class_or_vclass)
{
/* VC.owner_name = <upper_owner_name> */
where_item2 = pt_make_pred_name_string_val (parser, PT_EQ, "VC.owner_name", upper_owner_name);
/* where_item1: where_item1 AND where_item2 */
where_item1 = parser_make_expression (parser, PT_AND, where_item1, where_item2, NULL);
}
/* WHERE list should be empty */
assert (node->info.query.q.select.where == NULL);
node->info.query.q.select.where = parser_append_node (where_item1, node->info.query.q.select.where);
}
return node;
}
PT_NODE *
pt_make_query_show_exec_stats (PARSER_CONTEXT * parser)
{
PT_NODE **node = NULL;
PT_NODE *show_node;
const char *query =
"(SELECT 'data_page_fetches' as [variable] , exec_stats('Num_data_page_fetches') as [value])"
"UNION ALL (SELECT 'data_page_dirties' as [variable] , exec_stats('Num_data_page_dirties') as [value])"
"UNION ALL (SELECT 'data_page_ioreads' as [variable] , exec_stats('Num_data_page_ioreads') as [value])"
"UNION ALL (SELECT 'data_page_iowrites' as [variable] , exec_stats('Num_data_page_iowrites') as [value]);";
/* parser ';' will empty and reset the stack of parser, this make the status machine be right for the next statement,
* and avoid nested parser statement. */
parser_parse_string (parser, ";");
node = parser_parse_string_use_sys_charset (parser, query);
if (node == NULL)
{
return NULL;
}
parser->flag.dont_collect_exec_stats = 1;
show_node = pt_pop (parser);
assert (show_node == node[0]);
return node[0];
}
PT_NODE *
pt_make_query_show_exec_stats_all (PARSER_CONTEXT * parser)
{
PT_NODE **node = NULL;
PT_NODE *show_node;
const char *query =
"(SELECT 'file_creates' as [variable] , exec_stats('Num_file_creates') as [value])"
"UNION ALL (SELECT 'file_removes' as [variable] , exec_stats('Num_file_removes') as [value])"
"UNION ALL (SELECT 'file_ioreads' as [variable] , exec_stats('Num_file_ioreads') as [value])"
"UNION ALL (SELECT 'file_iowrites' as [variable] , exec_stats('Num_file_iowrites') as [value])"
"UNION ALL (SELECT 'file_iosynches' as [variable] , exec_stats('Num_file_iosynches') as [value])"
"UNION ALL (SELECT 'data_page_fetches' as [variable] , exec_stats('Num_data_page_fetches') as [value])"
"UNION ALL (SELECT 'data_page_dirties' as [variable] , exec_stats('Num_data_page_dirties') as [value])"
"UNION ALL (SELECT 'data_page_ioreads' as [variable] , exec_stats('Num_data_page_ioreads') as [value])"
"UNION ALL (SELECT 'data_page_iowrites' as [variable] , exec_stats('Num_data_page_iowrites') as [value])"
"UNION ALL (SELECT 'log_page_ioreads' as [variable] , exec_stats('Num_log_page_ioreads') as [value])"
"UNION ALL (SELECT 'log_page_iowrites' as [variable] , exec_stats('Num_log_page_iowrites') as [value])"
"UNION ALL (SELECT 'log_append_records' as [variable] , exec_stats('Num_log_append_records') as [value])"
"UNION ALL (SELECT 'log_archives' as [variable] , exec_stats('Num_log_archives') as [value])"
"UNION ALL (SELECT 'log_start_checkpoints' as [variable] , exec_stats('Num_log_start_checkpoints') as [value])"
"UNION ALL (SELECT 'log_end_checkpoints' as [variable] , exec_stats('Num_log_end_checkpoints') as [value])"
"UNION ALL (SELECT 'log_wals' as [variable] , exec_stats('Num_log_wals') as [value])"
"UNION ALL (SELECT 'page_locks_acquired' as [variable] , exec_stats('Num_page_locks_acquired') as [value])"
"UNION ALL (SELECT 'object_locks_acquired' as [variable] , exec_stats('Num_object_locks_acquired') as [value])"
"UNION ALL (SELECT 'page_locks_converted' as [variable] , exec_stats('Num_page_locks_converted') as [value])"
"UNION ALL (SELECT 'object_locks_converted' as [variable] , exec_stats('Num_object_locks_converted') as [value])"
"UNION ALL (SELECT 'page_locks_re-requested' as [variable] , exec_stats('Num_page_locks_re-requested') as [value])"
"UNION ALL (SELECT 'object_locks_re-requested' as [variable] , exec_stats('Num_object_locks_re-requested') as [value])"
"UNION ALL (SELECT 'page_locks_waits' as [variable] , exec_stats('Num_page_locks_waits') as [value])"
"UNION ALL (SELECT 'object_locks_waits' as [variable] , exec_stats('Num_object_locks_waits') as [value])"
"UNION ALL (SELECT 'tran_commits' as [variable] , exec_stats('Num_tran_commits') as [value])"
"UNION ALL (SELECT 'tran_rollbacks' as [variable] , exec_stats('Num_tran_rollbacks') as [value])"
"UNION ALL (SELECT 'tran_savepoints' as [variable] , exec_stats('Num_tran_savepoints') as [value])"
"UNION ALL (SELECT 'tran_start_topops' as [variable] , exec_stats('Num_tran_start_topops') as [value])"
"UNION ALL (SELECT 'tran_end_topops' as [variable] , exec_stats('Num_tran_end_topops') as [value])"
"UNION ALL (SELECT 'tran_interrupts' as [variable] , exec_stats('Num_tran_interrupts') as [value])"
"UNION ALL (SELECT 'btree_inserts' as [variable] , exec_stats('Num_btree_inserts') as [value])"
"UNION ALL (SELECT 'btree_deletes' as [variable] , exec_stats('Num_btree_deletes') as [value])"
"UNION ALL (SELECT 'btree_updates' as [variable] , exec_stats('Num_btree_updates') as [value])"
"UNION ALL (SELECT 'btree_covered' as [variable] , exec_stats('Num_btree_covered') as [value])"
"UNION ALL (SELECT 'btree_noncovered' as [variable] , exec_stats('Num_btree_noncovered') as [value])"
"UNION ALL (SELECT 'btree_resumes' as [variable] , exec_stats('Num_btree_resumes') as [value])"
"UNION ALL (SELECT 'btree_multirange_optimization' as [variable] , exec_stats('Num_btree_multirange_optimization') as [value])"
"UNION ALL (SELECT 'btree_splits' as [variable] , exec_stats('Num_btree_splits') as [value])"
"UNION ALL (SELECT 'btree_merges' as [variable] , exec_stats('Num_btree_merges') as [value])"
"UNION ALL (SELECT 'query_selects' as [variable] , exec_stats('Num_query_selects') as [value])"
"UNION ALL (SELECT 'query_inserts' as [variable] , exec_stats('Num_query_inserts') as [value])"
"UNION ALL (SELECT 'query_deletes' as [variable] , exec_stats('Num_query_deletes') as [value])"
"UNION ALL (SELECT 'query_updates' as [variable] , exec_stats('Num_query_updates') as [value])"
"UNION ALL (SELECT 'query_sscans' as [variable] , exec_stats('Num_query_sscans') as [value])"
"UNION ALL (SELECT 'query_iscans' as [variable] , exec_stats('Num_query_iscans') as [value])"
"UNION ALL (SELECT 'query_lscans' as [variable] , exec_stats('Num_query_lscans') as [value])"
"UNION ALL (SELECT 'query_setscans' as [variable] , exec_stats('Num_query_setscans') as [value])"
"UNION ALL (SELECT 'query_methscans' as [variable] , exec_stats('Num_query_methscans') as [value])"
"UNION ALL (SELECT 'query_nljoins' as [variable] , exec_stats('Num_query_nljoins') as [value])"
"UNION ALL (SELECT 'query_mjoins' as [variable] , exec_stats('Num_query_mjoins') as [value])"
"UNION ALL (SELECT 'query_objfetches' as [variable] , exec_stats('Num_query_objfetches') as [value])"
"UNION ALL (SELECT 'query_holdable_cursors' as [variable] , exec_stats('Num_query_holdable_cursors') as [value])"
"UNION ALL (SELECT 'sort_io_pages' as [variable] , exec_stats('Num_sort_io_pages') as [value])"
"UNION ALL (SELECT 'sort_data_pages' as [variable] , exec_stats('Num_sort_data_pages') as [value])"
"UNION ALL (SELECT 'network_requests' as [variable] , exec_stats('Num_network_requests') as [value])"
"UNION ALL (SELECT 'adaptive_flush_pages' as [variable] , exec_stats('Num_adaptive_flush_pages') as [value])"
"UNION ALL (SELECT 'adaptive_flush_log_pages' as [variable] , exec_stats('Num_adaptive_flush_log_pages') as [value])"
"UNION ALL (SELECT 'adaptive_flush_max_pages' as [variable] , exec_stats('Num_adaptive_flush_max_pages') as [value])"
"UNION ALL (SELECT 'prior_lsa_list_size' as [variable] , exec_stats('Num_prior_lsa_list_size') as [value])"
"UNION ALL (SELECT 'prior_lsa_list_maxed' as [variable] , exec_stats('Num_prior_lsa_list_maxed') as [value])"
"UNION ALL (SELECT 'prior_lsa_list_removed' as [variable] , exec_stats('Num_prior_lsa_list_removed') as [value])"
"UNION ALL (SELECT 'heap_stats_bestspace_entries' as [variable] , exec_stats('Num_heap_stats_bestspace_entries') as [value])"
"UNION ALL (SELECT 'heap_stats_bestspace_maxed' as [variable] , exec_stats('Num_heap_stats_bestspace_maxed') as [value])";
/* parser ';' will empty and reset the stack of parser, this make the status machine be right for the next statement,
* and avoid nested parser statement. */
parser_parse_string (parser, ";");
node = parser_parse_string_use_sys_charset (parser, query);
if (node == NULL)
{
return NULL;
}
show_node = pt_pop (parser);
assert (show_node == node[0]);
parser->flag.dont_collect_exec_stats = 1;
return node[0];
}
/*
* pt_make_query_user_groups() - builds the query to return the SET of DB
* groups to which a DB user belongs to.
*
* SELECT SUM(SET{t.g.name})
* FROM db_user U, TABLE(groups) AS t(g)
* WHERE U.name=<user_name>
*
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* user_name(in): DB user name
*/
static PT_NODE *
pt_make_query_user_groups (PARSER_CONTEXT * parser, const char *user_name)
{
PT_NODE *query = NULL;
PT_NODE *sel_item = NULL;
PT_NODE *from_item = NULL;
assert (user_name != NULL);
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
return NULL;
}
/* SELECT list : */
/* SUM(SET{t.g.name}) */
{
PT_NODE *group_name_identifier = NULL;
PT_NODE *set_of_group_name = NULL;
group_name_identifier = pt_make_dotted_identifier (parser, "t.g.name");
set_of_group_name = parser_new_node (parser, PT_VALUE);
if (set_of_group_name == NULL)
{
return NULL;
}
set_of_group_name->info.value.data_value.set = group_name_identifier;
set_of_group_name->type_enum = PT_TYPE_SET;
sel_item = parser_new_node (parser, PT_FUNCTION);
if (sel_item == NULL)
{
return NULL;
}
sel_item->info.function.function_type = PT_SUM;
sel_item->info.function.all_or_distinct = PT_ALL;
sel_item->info.function.arg_list = parser_append_node (set_of_group_name, sel_item->info.function.arg_list);
}
query->info.query.q.select.list = parser_append_node (sel_item, query->info.query.q.select.list);
/* FROM : */
/* db_user U */
from_item = pt_add_table_name_to_from_list (parser, query, "db_user", "U", DB_AUTH_SELECT);
{
/* TABLE(groups) AS t(g) */
PT_NODE *table_col = NULL;
PT_NODE *alias_table = NULL;
PT_NODE *alias_col = NULL;
from_item = pt_add_table_name_to_from_list (parser, query, NULL, NULL, DB_AUTH_SELECT);
if (from_item == NULL)
{
return NULL;
}
table_col = pt_name (parser, "groups");
alias_table = pt_name (parser, "t");
alias_col = pt_name (parser, "g");
if (table_col == NULL || alias_table == NULL || alias_col == NULL)
{
return NULL;
}
table_col->info.name.meta_class = PT_NORMAL;
from_item->info.spec.derived_table = table_col;
from_item->info.spec.meta_class = PT_MISC_NONE;
from_item->info.spec.range_var = alias_table;
from_item->info.spec.as_attr_list = alias_col;
from_item->info.spec.derived_table_type = PT_IS_SET_EXPR;
from_item->info.spec.join_type = PT_JOIN_NONE;
}
/* WHERE : */
{
/* U.name = <user_name> */
PT_NODE *where_item = NULL;
where_item = pt_make_pred_name_string_val (parser, PT_EQ, "U.name", user_name);
/* WHERE list should be empty */
assert (query->info.query.q.select.where == NULL);
query->info.query.q.select.where = parser_append_node (where_item, query->info.query.q.select.where);
}
return query;
}
/*
* pt_make_query_show_grants_curr_usr() - builds the query used for
* SHOW GRANTS for current user
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
*/
PT_NODE *
pt_make_query_show_grants_curr_usr (PARSER_CONTEXT * parser)
{
const char *user_name = NULL;
PT_NODE *node = NULL;
user_name = au_get_current_user_name ();
if (user_name == NULL)
{
return NULL;
}
node = pt_make_query_show_grants (parser, user_name);
if (user_name != NULL)
{
db_string_free ((char *) user_name);
}
return node;
}
static PT_NODE *
pt_set_auth_bypass_mask_for_show (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
DB_AUTH *auth_bypass;
assert (arg != NULL);
if (node && node->node_type == PT_SPEC)
{
auth_bypass = (DB_AUTH *) arg;
node->info.spec.auth_bypass_mask = *auth_bypass;
}
return node;
}
/*
* pt_make_query_show_grants() - builds the query used for SHOW GRANTS for a
* given user
*
* Note : The purpose of GROUP BY is to group all the privilege by user,
* table and the presence of 'WITH GRANT OPTION' flag. We output the
* privileges for the user but also for all groups to which the user
* belongs to : these privileges are shown in separate lines. Multiple
* privileges for the same table are displayed on the same line,
* except when 'WITH GRANT OPTION' is present, case when these
* privileges are displayed on another line.
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* user_name(in): DB user name
*/
PT_NODE *
pt_make_query_show_grants (PARSER_CONTEXT * parser, const char *original_user_name)
{
PT_NODE **node = NULL;
PT_NODE *show_node = NULL;
char user_name[SM_MAX_IDENTIFIER_LENGTH];
// *INDENT-OFF*
const static char *query =
"SELECT "
"CONCAT ('GRANT ', "
"GROUP_CONCAT([auth_type] ORDER BY 1 SEPARATOR ', '), "
"' ON ',"
"IF ([object_type]=5, 'PROCEDURE ', ''), "
"[unique_name], "
"' TO ',"
"[grantee_name],"
"IF ([is_grantable]=1, ' WITH GRANT OPTION', '')"
") AS GRANTS "
"FROM ("
"SELECT "
"CAST ([a].[grantor].[name] AS VARCHAR(255)) AS [grantor_name], " /* string -> varchar(255) */
"CAST ([a].[grantee].[name] AS VARCHAR(255)) AS [grantee_name], " /* string -> varchar(255) */
"[a].[object_type] AS [object_type], "
"[c].[unique_name] AS [unique_name], "
"[a].[auth_type] AS [auth_type], "
"[a].[is_grantable] AS [is_grantable] "
"FROM "
"[_db_auth] AS [a], [_db_class] AS [c] "
"WHERE "
"[a].[object_of] = [c].[class_of] "
"AND [a].[object_type] = 0 "
"AND [c].[is_system_class] = 0 "
"AND ( "
"[a].[grantee].[name] = '%1$s' "
"OR "
"SET {[a].[grantee].[name]} SUBSETEQ ("
"SELECT "
"SUM (SET {[t].[g].[name]}) "
"FROM "
/* AU_USER_CLASS_NAME */
"[db_user] AS [u], TABLE ([u].[groups]) AS [t] ([g]) "
"WHERE "
"[u].[name] = '%1$s'"
") "
") "
"UNION ALL "
"SELECT "
"CAST ([a].[grantor].[name] AS VARCHAR(255)) AS [grantor_name], " /* string -> varchar(255) */
"CAST ([a].[grantee].[name] AS VARCHAR(255)) AS [grantee_name], " /* string -> varchar(255) */
"[a].[object_type] AS [object_type], "
"[s].[unique_name] AS [unique_name], "
"[a].[auth_type] AS [auth_type], "
"[a].[is_grantable] AS [is_grantable] "
"FROM "
"[_db_auth] AS [a], [_db_stored_procedure] AS [s] "
"WHERE "
"[a].[object_of] = [s] "
"AND [a].[object_type] = 5 "
"AND [s].[is_system_generated] = 0 "
"AND ( "
"[a].[grantee].[name] = '%1$s' "
"OR "
"SET {[a].[grantee].[name]} SUBSETEQ ("
"SELECT "
"SUM (SET {[t].[g].[name]}) "
"FROM "
/* AU_USER_CLASS_NAME */
"[db_user] AS [u], TABLE ([u].[groups]) AS [t] ([g]) "
"WHERE "
"[u].[name] = '%1$s'"
") "
") "
") "
"GROUP BY "
"[grantee_name], [unique_name], [is_grantable] ASC "
"ORDER BY 1;";
// *INDENT-ON*
const int buffer_size = 4096; // length of query (1303) + identifier (255) * 4 < 1024
char buffer[buffer_size];
memset (buffer, 0, buffer_size);
/* conversion to uppercase can cause <original_user_name> to double size, if internationalization is used : size
* <user_name> accordingly */
intl_identifier_upper (original_user_name, user_name);
snprintf (buffer, buffer_size, query, user_name);
/* parser ';' will empty and reset the stack of parser, this make the status machine be right for the next statement,
* and avoid nested parser statement. */
parser_parse_string (parser, ";");
node = parser_parse_string_use_sys_charset (parser, buffer);
if (node == NULL)
{
return NULL;
}
parser->flag.dont_collect_exec_stats = 1;
show_node = pt_pop (parser);
assert (show_node == node[0]);
if (show_node)
{
DB_AUTH bypass_auth = DB_AUTH_SELECT;
show_node = parser_walk_tree (parser, show_node, pt_set_auth_bypass_mask_for_show, &bypass_auth, NULL, NULL);
// for backward compatibiltiy
char col_alias[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
const char *const col_header = "Grants for ";
strcpy (col_alias, col_header);
strncat (col_alias, user_name, SM_MAX_IDENTIFIER_LENGTH - strlen (col_header) - 1);
col_alias[SM_MAX_IDENTIFIER_LENGTH - 1] = '\0';
PT_NODE *concat_node = show_node->info.query.q.select.list;
concat_node->alias_print = pt_append_string (parser, NULL, col_alias);
}
return show_node;
}
/*
* pt_is_spec_referenced() - check if the current node references the spec id
* passed as parameter
* return: the current node
* parser(in): Parser context
* node(in):
* void_arg(in): must contain an address to the id of the spec. If the spec id
* is referenced then reference of this parameter is modified to
* 0.
* continue_walk(in):
*
*/
static PT_NODE *
pt_is_spec_referenced (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
UINTPTR spec_id = *(UINTPTR *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
if (node->node_type == PT_NAME && node->info.name.spec_id == spec_id && node->info.name.meta_class != PT_METHOD
&& node->info.name.meta_class != PT_INDEX_NAME)
{
*(UINTPTR *) void_arg = 0;
*continue_walk = PT_STOP_WALK;
return node;
}
if (node->node_type == PT_SPEC)
{
/* The only part of a spec node that could contain references to the given spec_id are derived tables,
* path_entities, path_conjuncts, and on_cond. All the rest of the name nodes for the spec are not references,
* but range variables, class names, etc. We don't want to mess with these. We'll handle the ones that we want by
* hand. */
parser_walk_tree (parser, node->info.spec.derived_table, pt_is_spec_referenced, void_arg, pt_continue_walk, NULL);
parser_walk_tree (parser, node->info.spec.path_entities, pt_is_spec_referenced, void_arg, pt_continue_walk, NULL);
parser_walk_tree (parser, node->info.spec.path_conjuncts, pt_is_spec_referenced, void_arg, pt_continue_walk,
NULL);
parser_walk_tree (parser, node->info.spec.on_cond, pt_is_spec_referenced, void_arg, pt_continue_walk, NULL);
/* don't visit any other leaf nodes */
*continue_walk = PT_LIST_WALK;
return node;
}
/* Data type nodes can not contain any valid references. They do contain class names and other things we don't want.
*/
if (node->node_type == PT_DATA_TYPE)
{
*continue_walk = PT_LIST_WALK;
}
return node;
}
/*
* pt_create_delete_stmt() - create a new simple delete statement
* return: the PT_DELETE node on success or NULL otherwise
* parser(in/out): Parser context
* spec(in): the spec for which the DELETE statement is created
* target_class(in): the PT_NAME node that will appear in the target_classes
* list.
*
* Note: The 'spec' and 'target_class' parameters are assigned to the new
* statement.
*/
static PT_NODE *
pt_create_delete_stmt (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * target_class)
{
PT_NODE *delete_stmt = NULL;
assert (spec != NULL && spec->node_type == PT_SPEC);
delete_stmt = parser_new_node (parser, PT_DELETE);
if (delete_stmt == NULL)
{
PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
delete_stmt->info.delete_.spec = spec;
delete_stmt->info.delete_.target_classes = target_class;
return delete_stmt;
}
/*
* pt_split_delete_stmt() - split DELETE statement into independent DELETE
* statements
* return: NO_ERROR or error code;
* parser(in/out): Parser context
* delete_stmt(in): the source DELETE statement
*
* Note: The function checks each spec if it is referenced. If the spec was
* specified in the target_classes list (for deletion) and is not
* referenced then it removes it from the current statement and a new
* DELETE statement is generated only for this spec. The newly generated
* statements are stored in del_stmt_list member of the current
* PT_DELETE_INFO node.
*/
int
pt_split_delete_stmt (PARSER_CONTEXT * parser, PT_NODE * delete_stmt)
{
PT_NODE *spec = NULL, *prev_spec = NULL, *rem_spec = NULL, *to_delete = NULL;
PT_NODE *prev_name = NULL, *new_del_stmts = NULL, *last_new_del_stmt = NULL;
PT_NODE *rem_name = NULL;
UINTPTR spec_id = 0;
if (delete_stmt == NULL || delete_stmt->node_type != PT_DELETE)
{
PT_INTERNAL_ERROR (parser, "Invalid argument");
return ER_FAILED;
}
/* if we have hints that refers globally to the join statement then we skip the split */
if ((delete_stmt->info.delete_.hint & PT_HINT_ORDERED)
|| ((delete_stmt->info.delete_.hint & PT_HINT_USE_NL) && delete_stmt->info.delete_.use_nl_hint == NULL)
|| ((delete_stmt->info.delete_.hint & PT_HINT_USE_IDX) && delete_stmt->info.delete_.use_idx_hint == NULL)
|| ((delete_stmt->info.delete_.hint & PT_HINT_USE_MERGE) && delete_stmt->info.delete_.use_merge_hint == NULL)
|| ((delete_stmt->info.delete_.hint & PT_HINT_NO_USE_HASH) && delete_stmt->info.delete_.no_use_hash_hint == NULL)
|| ((delete_stmt->info.delete_.hint & PT_HINT_USE_HASH) && delete_stmt->info.delete_.use_hash_hint == NULL))
{
return NO_ERROR;
}
spec = delete_stmt->info.delete_.spec;
/* if the delete statement has only one spec then we do not split anything */
if (spec->next == NULL)
{
return NO_ERROR;
}
/* iterate through specs and put in separate delete statements the specs that aren't referenced */
while (spec != NULL && delete_stmt->info.delete_.target_classes->next != NULL)
{
/* skip the derived tables and tables that will not be deleted */
if (spec->info.spec.derived_table != NULL || !(spec->info.spec.flag & PT_SPEC_FLAG_DELETE))
{
prev_spec = spec;
spec = spec->next;
continue;
}
spec_id = spec->info.spec.id;
to_delete = delete_stmt->info.delete_.target_classes;
/* remove temporarily all target_classes from statement because these references must not be counted, then check
* if the current spec is referenced */
delete_stmt->info.delete_.target_classes = NULL;
parser_walk_tree (parser, delete_stmt, pt_is_spec_referenced, &spec_id, pt_continue_walk, NULL);
delete_stmt->info.delete_.target_classes = to_delete;
/* if the spec is not referenced and if it is not the only remaining spec from the original command that will be
* deleted then remove it */
if (spec_id)
{
/* move the iterator (spec) to the next spec and remove the current spec from the original list of specs */
rem_spec = spec;
spec = spec->next;
rem_spec->next = NULL;
if (prev_spec != NULL)
{
prev_spec->next = spec;
}
else
{
delete_stmt->info.delete_.spec = spec;
}
/* remove PT_NAMEs from target_classes list */
rem_name = prev_name = NULL;
while (to_delete != NULL)
{
/* if the target class name references the removed spec then remove it from target_classes list */
if (to_delete->info.name.spec_id == spec_id)
{
/* free the previous removed PT_NAME */
if (rem_name != NULL)
{
parser_free_tree (parser, rem_name);
}
/* remove the current PT_NAME from target_classes and keep it for a new DELETE statement */
rem_name = to_delete;
to_delete = to_delete->next;
rem_name->next = NULL;
if (prev_name != NULL)
{
prev_name->next = to_delete;
}
else
{
delete_stmt->info.delete_.target_classes = to_delete;
}
}
else
{
prev_name = to_delete;
to_delete = to_delete->next;
}
}
/* because the spec is referenced in the target_classes list, we need to generate a new DELETE statement */
if (new_del_stmts == NULL)
{
last_new_del_stmt = new_del_stmts = pt_create_delete_stmt (parser, rem_spec, rem_name);
}
else
{
last_new_del_stmt->next = pt_create_delete_stmt (parser, rem_spec, rem_name);
last_new_del_stmt = last_new_del_stmt->next;
}
if (last_new_del_stmt == NULL)
{
goto exit_on_error;
}
/* handle hints */
if (last_new_del_stmt != NULL)
{
last_new_del_stmt->info.delete_.hint = delete_stmt->info.delete_.hint;
last_new_del_stmt->flag.recompile = delete_stmt->flag.recompile;
if ((last_new_del_stmt->info.delete_.hint & PT_HINT_LK_TIMEOUT)
&& delete_stmt->info.delete_.waitsecs_hint != NULL)
{
last_new_del_stmt->info.delete_.waitsecs_hint =
parser_copy_tree (parser, delete_stmt->info.delete_.waitsecs_hint);
if (last_new_del_stmt->info.delete_.waitsecs_hint == NULL)
{
goto exit_on_error;
}
}
}
}
else
{
prev_spec = spec;
spec = spec->next;
}
}
delete_stmt->info.delete_.del_stmt_list = new_del_stmts;
return NO_ERROR;
exit_on_error:
if (new_del_stmts != NULL)
{
parser_free_tree (parser, new_del_stmts);
}
if (!pt_has_error (parser))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
}
return ER_GENERIC_ERROR;
}
/*
* pt_make_query_describe_w_identifier() - builds the query used for DESCRIBE
* with a column name
*
* DESCRIBE <table_name> <column_name>
*
* return: newly build node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* original_cls_id(in): node identifier for table (PT_NAME)
* att_id(in): node identifier for attribute (PT_NAME)
*/
PT_NODE *
pt_make_query_describe_w_identifier (PARSER_CONTEXT * parser, PT_NODE * original_cls_id, PT_NODE * att_id)
{
PT_NODE *node = NULL;
PT_NODE *where_node = NULL;
assert (original_cls_id != NULL);
assert (original_cls_id->node_type == PT_NAME);
assert (att_id != NULL);
if (att_id != NULL)
{
assert (att_id->node_type == PT_NAME);
if (att_id->node_type == PT_NAME)
{
char lower_att_name[DB_MAX_IDENTIFIER_LENGTH];
/* build WHERE */
intl_identifier_lower (att_id->info.name.original, lower_att_name);
where_node = pt_make_pred_name_string_val (parser, PT_EQ, "Field", lower_att_name);
}
}
node = pt_make_query_show_columns (parser, original_cls_id, (where_node == NULL) ? 0 : 2, where_node, 0);
return node;
}
/*
* pt_make_query_show_index() - builds the query used for
* SHOW INDEX IN <table>
*
* return: newly built node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* original_cls_id(in): node (PT_NAME) containing name of class
*
* SELECT "" AS Table,
* 0 AS Non_unique,
* "" AS Key_name,
* 0 AS Seq_in_index,
* "" AS Column_name,
* "" AS Collation,
* 0 AS Cardinality,
* 0 AS Sub_part,
* NULL AS Packed,
* "" AS [Null],
* 'BTREE' AS Index_type
* "" AS Func,
* "" AS Comment,
* "" AS Visible
* FROM <table> ORDER BY 3, 5;
*
* Note: At execution, all empty fields will be replaced by values
* that will be read from class schema
*/
PT_NODE *
pt_make_query_show_index (PARSER_CONTEXT * parser, PT_NODE * original_cls_id)
{
PT_NODE *from_item = NULL;
PT_NODE *order_by_item = NULL;
PT_NODE *query = NULL;
char lower_table_name[DB_MAX_IDENTIFIER_LENGTH];
PT_NODE *value = NULL, *value_list = NULL;
DB_VALUE db_valuep[14];
const char *aliases[] = {
"Table", "Non_unique", "Key_name", "Seq_in_index", "Column_name",
"Collation", "Cardinality", "Sub_part", "Packed", "Null", "Index_type",
"Func", "Comment", "Visible"
};
unsigned int i = 0;
assert (original_cls_id != NULL);
assert (original_cls_id->node_type == PT_NAME);
/* store user-specified-name in info.name.original. */
parser_walk_tree (parser, original_cls_id, NULL, NULL, pt_set_user_specified_name, NULL);
if (pt_has_error (parser))
{
return NULL;
}
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
return NULL;
}
PT_SELECT_INFO_SET_FLAG (query, PT_SELECT_INFO_IDX_SCHEMA);
PT_SELECT_INFO_SET_FLAG (query, PT_SELECT_INFO_READ_ONLY);
intl_identifier_lower (original_cls_id->info.name.original, lower_table_name);
db_value_domain_default (db_valuep + 0, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_make_int (db_valuep + 1, 0);
db_value_domain_default (db_valuep + 2, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_make_int (db_valuep + 3, 0);
db_value_domain_default (db_valuep + 4, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_value_domain_default (db_valuep + 5, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_make_int (db_valuep + 6, 0);
db_make_int (db_valuep + 7, 0);
db_make_null (db_valuep + 8);
db_value_domain_default (db_valuep + 9, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_value_domain_default (db_valuep + 10, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_value_domain_default (db_valuep + 11, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
db_make_varchar (db_valuep + 12, DB_DEFAULT_PRECISION, "", 0, LANG_SYS_CODESET, LANG_SYS_COLLATION);
db_value_domain_default (db_valuep + 13, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, 0, LANG_SYS_CODESET,
LANG_SYS_COLLATION, NULL);
for (i = 0; i < sizeof (db_valuep) / sizeof (db_valuep[0]); i++)
{
value = pt_dbval_to_value (parser, db_valuep + i);
if (value == NULL)
{
goto error;
}
value->alias_print = pt_append_string (parser, NULL, aliases[i]);
value_list = parser_append_node (value, value_list);
}
query->info.query.q.select.list = value_list;
value_list = NULL;
from_item = pt_add_table_name_to_from_list (parser, query, lower_table_name, NULL, DB_AUTH_NONE);
if (from_item == NULL)
{
goto error;
}
/* By Key_name */
order_by_item = pt_make_sort_spec_with_number (parser, 3, PT_ASC);
if (order_by_item == NULL)
{
goto error;
}
query->info.query.order_by = parser_append_node (order_by_item, query->info.query.order_by);
/* By Seq_in_index */
order_by_item = pt_make_sort_spec_with_number (parser, 4, PT_ASC);
if (order_by_item == NULL)
{
goto error;
}
query->info.query.order_by = parser_append_node (order_by_item, query->info.query.order_by);
return query;
error:
if (query)
{
parser_free_tree (parser, query);
}
if (value_list)
{
parser_free_tree (parser, value_list);
}
return NULL;
}
/*
* pt_convert_to_logical_expr () - if necessary, creates a logically correct
* expression from the given node
*
* return: - the same node if conversion was not necessary, OR
* - a new PT_EXPR: (node <> 0), OR
* - NULL on failures
* parser (in): Parser context
* node (in): the node to be checked and wrapped
* use_parens (in): set to true if parantheses are needed around the original node
*
* Note: we see if the given node is of type PT_TYPE_LOGICAL, and if not,
* we create an expression of the form "(node <> 0)" - with parens
*/
PT_NODE *
pt_convert_to_logical_expr (PARSER_CONTEXT * parser, PT_NODE * node, bool use_parens_inside, bool use_parens_outside)
{
PT_NODE *expr = NULL;
PT_NODE *zero = NULL;
(void) use_parens_inside;
(void) use_parens_outside;
/* If there's nothing to be done, go away */
if (node == NULL || node->type_enum == PT_TYPE_LOGICAL)
{
return node;
}
/* allocate a new node for the zero value */
zero = parser_new_node (parser, PT_VALUE);
if (NULL == zero)
{
return NULL;
}
zero->info.value.data_value.i = 0;
zero->type_enum = PT_TYPE_INTEGER;
/* make a new expression comparing the node to zero */
expr = parser_make_expression (parser, PT_NE, node, zero, NULL);
if (expr != NULL)
{
expr->type_enum = PT_TYPE_LOGICAL;
}
return expr;
}
/*
* pt_is_operator_arith() - returns TRUE if the operator has an arithmetic
* return type (i.e. +, -, *, /) and FALSE
* otherwise.
*
* return: boolean
* op(in): the operator
*/
bool
pt_is_operator_arith (PT_OP_TYPE op)
{
switch (op)
{
case PT_PLUS:
case PT_MINUS:
case PT_TIMES:
case PT_DIVIDE:
case PT_UNARY_MINUS:
case PT_MOD:
return true;
default:
return false;
}
}
/*
* pt_is_operator_logical() - returns TRUE if the operator has a logical
* return type (i.e. <, >, AND etc.) and FALSE
* otherwise.
*
* return: boolean
* op(in): the operator
*/
bool
pt_is_operator_logical (PT_OP_TYPE op)
{
switch (op)
{
case PT_OR:
case PT_XOR:
case PT_AND:
case PT_IS_NOT:
case PT_IS:
case PT_NOT:
case PT_EXISTS:
case PT_LIKE_ESCAPE:
case PT_LIKE:
case PT_NOT_LIKE:
case PT_RLIKE:
case PT_NOT_RLIKE:
case PT_RLIKE_BINARY:
case PT_NOT_RLIKE_BINARY:
case PT_EQ:
case PT_EQ_ALL:
case PT_EQ_SOME:
case PT_NE:
case PT_NE_ALL:
case PT_NE_SOME:
case PT_GT:
case PT_GT_ALL:
case PT_GT_SOME:
case PT_GE:
case PT_GE_ALL:
case PT_GE_SOME:
case PT_LT:
case PT_LT_ALL:
case PT_LT_SOME:
case PT_LE:
case PT_LE_ALL:
case PT_LE_SOME:
case PT_NULLSAFE_EQ:
case PT_IS_NOT_NULL:
case PT_IS_NULL:
case PT_NOT_BETWEEN:
case PT_BETWEEN:
case PT_IS_IN:
case PT_IS_NOT_IN:
case PT_BETWEEN_GE_LE:
case PT_BETWEEN_GT_LE:
case PT_BETWEEN_GE_LT:
case PT_BETWEEN_GT_LT:
case PT_BETWEEN_EQ_NA:
case PT_BETWEEN_GE_INF:
case PT_BETWEEN_GT_INF:
case PT_BETWEEN_INF_LE:
case PT_BETWEEN_INF_LT:
case PT_SETEQ:
case PT_SETNEQ:
case PT_SUBSET:
case PT_SUBSETEQ:
case PT_SUPERSETEQ:
case PT_SUPERSET:
case PT_RANGE:
return true;
default:
return false;
}
}
/*
* pt_list_has_logical_nodes () - returns TRUE if the node list contains
* top level PT_TYPE_LOGICAL nodes.
*
* return: boolean
* list(in): the node list
*
* Note: this function is important because there are cases (such as arg lists)
* when we want to forbid logical expressions, because of ambiguity over
* the ->next node: is it in a list context, or a CNF context?
*/
bool
pt_list_has_logical_nodes (PT_NODE * list)
{
for (; list; list = list->next)
{
if (list->type_enum == PT_TYPE_LOGICAL)
{
return true;
}
}
return false;
}
/*
* pt_sort_spec_cover_groupby () -
* return: true if group list is covered by sort list
* sort_list(in):
* group_list(in):
*/
bool
pt_sort_spec_cover_groupby (PARSER_CONTEXT * parser, PT_NODE * sort_list, PT_NODE * group_list, PT_NODE * tree)
{
PT_NODE *s1, *s2, *save_node, *col;
QFILE_TUPLE_VALUE_POSITION pos_descr;
int i;
if (group_list == NULL)
{
return false;
}
s1 = sort_list;
s2 = group_list;
while (s1 && s2)
{
pt_to_pos_descr (parser, &pos_descr, s2->info.sort_spec.expr, tree, NULL, false);
if (pos_descr.pos_no > 0)
{
col = tree->info.query.q.select.list;
for (i = 1; i < pos_descr.pos_no && col; i++)
{
col = col->next;
}
if (col != NULL)
{
col = pt_get_end_path_node (col);
if (col->node_type == PT_NAME && PT_NAME_INFO_IS_FLAGED (col, PT_NAME_INFO_CONSTANT))
{
s2 = s2->next;
continue; /* skip out constant order */
}
}
}
save_node = s1->info.sort_spec.expr;
CAST_POINTER_TO_NODE (s1->info.sort_spec.expr);
if (!pt_name_equal (parser, s1->info.sort_spec.expr, s2->info.sort_spec.expr)
|| (s1->info.sort_spec.asc_or_desc != s2->info.sort_spec.asc_or_desc)
|| (s1->info.sort_spec.nulls_first_or_last != s2->info.sort_spec.nulls_first_or_last))
{
s1->info.sort_spec.expr = save_node;
return false;
}
s1->info.sort_spec.expr = save_node;
s1 = s1->next;
s2 = s2->next;
}
return (s2 == NULL) ? true : false;
}
/*
* pt_rewrite_derived_for_upd_del () - adds ROWOID to select list of
* query so it can be later pulled
* when building the SELECT
* statement for UPDATEs and DELETEs
* returns: rewritten subquery or NULL on error
* parser(in): parser context
* spec(in): spec whose derived table will be rewritten
*
* NOTE: query must be a SELECT statement
*/
static PT_NODE *
pt_rewrite_derived_for_upd_del (PARSER_CONTEXT * parser, PT_NODE * spec, PT_SPEC_FLAG what_for, bool add_as_attr)
{
PT_NODE *derived_table = NULL, *as_attr = NULL, *col = NULL, *upd_del_spec = NULL, *spec_list = NULL;
PT_NODE *save_spec = NULL, *save_next = NULL, *flat_copy = NULL;
const char *spec_name = NULL;
int upd_del_count = 0;
derived_table = spec->info.spec.derived_table;
spec_list = derived_table->info.query.q.select.from;
if ((what_for == PT_SPEC_FLAG_DELETE) && (spec_list == NULL || spec_list->next != NULL))
{
PT_INTERNAL_ERROR (parser, "only one spec expected for delete");
return NULL;
}
while (spec_list != NULL)
{
if (spec_list->info.spec.flag & what_for)
{
if (upd_del_count == 0)
{
upd_del_spec = spec_list;
}
upd_del_count++;
}
spec_list = spec_list->next;
}
if (upd_del_spec == NULL)
{
/* no specs; error */
PT_INTERNAL_ERROR (parser, "no target spec for update/delete");
return NULL;
}
if (upd_del_count > 1)
{
PT_ERRORm (parser, spec_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ONLY_ONE_UPDATE_SPEC_ALLOWED);
return NULL;
}
/* dual update/delete checks have been made - check if oids were already added */
if (spec->info.spec.flag & PT_SPEC_FLAG_CONTAINS_OID)
{
return spec;
}
/* retrieve spec's name, which will be appended to OID attribute names */
if (spec->info.spec.range_var && spec->info.spec.range_var->node_type == PT_NAME)
{
spec_name = spec->info.spec.range_var->info.name.original;
}
/* add rowoid to select list of derived table and as_attr_list of spec; it will be pulled later on when building the
* select statement for OID retrieval */
save_spec = derived_table->info.query.q.select.from;
derived_table->info.query.q.select.from = upd_del_spec;
save_next = upd_del_spec->next;
assert (upd_del_spec != NULL);
upd_del_spec->next = NULL;
derived_table = pt_add_row_oid_name (parser, derived_table);
derived_table->info.query.q.select.from = save_spec;
upd_del_spec->next = save_next;
col = derived_table->info.query.q.select.list;
if (col->data_type && col->data_type->info.data_type.virt_object)
{
/* no longer comes from a vobj */
col->data_type->info.data_type.virt_object = NULL;
col->data_type->info.data_type.virt_type_enum = PT_TYPE_NONE;
}
if (add_as_attr)
{
/* add reference for column in select list */
as_attr = pt_name (parser, "rowoid_");
as_attr->info.name.original =
(const char *) pt_append_string (parser, (char *) as_attr->info.name.original, spec_name);
as_attr->info.name.spec_id = spec->info.spec.id;
as_attr->info.name.meta_class = PT_OID_ATTR;
as_attr->type_enum = col->type_enum;
as_attr->data_type = parser_copy_tree (parser, col->data_type);
as_attr->next = spec->info.spec.as_attr_list;
spec->info.spec.as_attr_list = as_attr;
}
/* copy flat_entity_list of derived table's spec to parent spec so it will be correctly handled further on */
flat_copy = parser_copy_tree_list (parser, upd_del_spec->info.spec.flat_entity_list);
spec->info.spec.flat_entity_list = parser_append_node (flat_copy, spec->info.spec.flat_entity_list);
while (flat_copy)
{
if (flat_copy->node_type == PT_NAME)
{
flat_copy->info.name.spec_id = spec->info.spec.id;
}
flat_copy = flat_copy->next;
}
/* all ok */
return spec;
}
/*
* pt_process_spec_for_delete () - recurses trough specs, sets DELETE flag
* and adds OIDs where necessary
* returns: same spec or NULL on error
* parser(in): parser context
* spec(in): spec
*/
static PT_NODE *
pt_process_spec_for_delete (PARSER_CONTEXT * parser, PT_NODE * spec)
{
PT_NODE *derived_table, *from, *ret;
if (parser == NULL || spec == NULL || spec->node_type != PT_SPEC)
{
/* should not get here */
assert (false);
return NULL;
}
/* mark spec */
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_DELETE);
/* fetch derived table of spec */
derived_table = spec->info.spec.derived_table;
if (derived_table == NULL)
{
/* no derived table means nothing to do further */
return spec;
}
/* derived table - walk it's spec */
if (derived_table->node_type != PT_SELECT)
{
PT_INTERNAL_ERROR (parser, "invalid derived spec");
return NULL;
}
from = derived_table->info.query.q.select.from;
if (pt_process_spec_for_delete (parser, from) == NULL)
{
/* error must have been set */
return NULL;
}
/* add oids */
ret = pt_rewrite_derived_for_upd_del (parser, spec, PT_SPEC_FLAG_DELETE, true);
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_CONTAINS_OID);
return ret;
}
/*
* pt_process_spec_for_update () - recurses trough specs, sets UPDATE flag,
* adds OIDs where necessary and resolves name
* returns: resolved name or NULL on error
* parser(in): parser context
* spec(in): spec
* name(in): lhs assignment node
*/
static PT_NODE *
pt_process_spec_for_update (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * name)
{
PT_NODE *as_attr_list = NULL, *attr_list = NULL;
PT_NODE *dt_arg1, *dt_arg2, *derived_table;
PT_NODE *spec_list, *subspec;
PT_NODE *temp_name, *save_dt;
int attr_idx, i;
if (parser == NULL || spec == NULL || name == NULL || spec->node_type != PT_SPEC || name->node_type != PT_NAME)
{
/* should not get here */
assert (false);
return NULL;
}
/* mark spec */
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_UPDATE);
/* fetch derived table of spec */
dt_arg1 = save_dt = spec->info.spec.derived_table;
if (dt_arg1 == NULL)
{
/* no derived table means nothing to do further */
return name;
}
/* check derived table */
if (!(spec->info.spec.flag & PT_SPEC_FLAG_FROM_VCLASS))
{
PT_INTERNAL_ERROR (parser, "derived table not allowed");
return NULL;
}
if (dt_arg1->node_type == PT_UNION)
{
/* union derived table (e.g. UPDATE (view1, view2) SET ...) */
dt_arg2 = dt_arg1->info.query.q.union_.arg2;
dt_arg1 = dt_arg1->info.query.q.union_.arg1;
}
else
{
/* simple derived table */
dt_arg2 = NULL;
}
if (dt_arg1->node_type != PT_SELECT || (dt_arg2 != NULL && dt_arg2->node_type != PT_SELECT))
{
PT_INTERNAL_ERROR (parser, "invalid derived spec");
return NULL;
}
/* find name in as_attr_list */
attr_idx = 0;
as_attr_list = spec->info.spec.as_attr_list;
while (as_attr_list != NULL)
{
if (pt_name_equal (parser, name, as_attr_list))
{
break;
}
as_attr_list = as_attr_list->next;
attr_idx++;
}
if (as_attr_list == NULL)
{
PT_INTERNAL_ERROR (parser, "name not found in as_attr_list");
return NULL;
}
derived_table = dt_arg1;
while (derived_table != NULL)
{
/* resolve name to real name in select list */
attr_list = derived_table->info.query.q.select.list;
for (i = 0; i < attr_idx && attr_list != NULL; i++)
{
attr_list = attr_list->next;
}
if (attr_list == NULL)
{
PT_INTERNAL_ERROR (parser, "name not found in list");
return NULL;
}
/* we have a derived table we know came from a view; resolve name further down */
spec_list = derived_table->info.query.q.select.from;
subspec = pt_find_spec (parser, spec_list, attr_list);
if (subspec == NULL)
{
PT_INTERNAL_ERROR (parser, "spec not found for name");
return NULL;
}
/* don't allow other subspecs to be updated; this error will be hit when two passes of the function, called for
* different assignments, will try to flag two different specs */
while (spec_list != NULL)
{
if ((spec_list != subspec) && (spec_list->info.spec.flag & PT_SPEC_FLAG_UPDATE))
{
PT_ERRORm (parser, spec_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ONLY_ONE_UPDATE_SPEC_ALLOWED);
return NULL;
}
spec_list = spec_list->next;
}
/* recurse */
temp_name = pt_process_spec_for_update (parser, subspec, attr_list);
if (temp_name == NULL)
{
/* error should have been set lower down */
return NULL;
}
if (derived_table == dt_arg1)
{
/* skip second resolved name */
name = temp_name;
}
/* we now have the derived table subtree populated with oids; we can add oids to this derived table's select list
* as well */
spec->info.spec.derived_table = derived_table;
spec = pt_rewrite_derived_for_upd_del (parser, spec, PT_SPEC_FLAG_UPDATE, (derived_table == dt_arg1));
spec->info.spec.derived_table = save_dt;
if (spec == NULL)
{
/* error should have been set lower down */
return NULL;
}
/* next derived table of union */
derived_table = (derived_table == dt_arg1 ? dt_arg2 : NULL);
}
/* spec has OIDs added */
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_CONTAINS_OID);
/* all ok */
return name;
}
/*
* pt_mark_spec_list_for_delete () - mark delete targets
* return: none
* parser(in): the parser context
* delete_statement(in): a delete statement
*/
void
pt_mark_spec_list_for_delete (PARSER_CONTEXT * parser, PT_NODE * statement)
{
PT_NODE *node = NULL, *from;
if (statement->node_type == PT_DELETE)
{
node = statement->info.delete_.target_classes;
}
else if (statement->node_type == PT_MERGE)
{
node = statement->info.merge.into;
}
while (node != NULL)
{
if (statement->node_type == PT_DELETE)
{
from = pt_find_spec_in_statement (parser, statement, node);
if (from == NULL)
{
PT_INTERNAL_ERROR (parser, "invalid spec id");
return;
}
}
else
{
from = node;
}
from = pt_process_spec_for_delete (parser, from);
if (from == NULL)
{
/* error must have been set */
return;
}
node = node->next;
}
}
/*
* pt_mark_spec_list_for_update () - mark update targets
* return: none
* parser(in): the parser context
* statement(in): an update/merge statement
*/
void
pt_mark_spec_list_for_update (PARSER_CONTEXT * parser, PT_NODE * statement)
{
PT_NODE *lhs, *node_tmp, *node, *resolved;
PT_NODE *assignments = NULL, *spec_list = NULL;
if (statement->node_type == PT_UPDATE)
{
assignments = statement->info.update.assignment;
spec_list = statement->info.update.spec;
}
else if (statement->node_type == PT_MERGE)
{
assignments = statement->info.merge.update.assignment;
spec_list = statement->info.merge.into;
}
/* set flags for updatable specs */
node = assignments;
while (node != NULL)
{
lhs = node->info.expr.arg1;
while (lhs != NULL && lhs->node_type != PT_NAME)
{
if (lhs->node_type == PT_EXPR)
{
/* path expression */
lhs = lhs->info.expr.arg1;
}
else if (lhs->node_type == PT_DOT_)
{
/* dot expression */
lhs = lhs->info.dot.arg2;
}
else
{
lhs = NULL;
}
}
if (lhs == NULL)
{
/* should not get here */
PT_INTERNAL_ERROR (parser, "malformed assignment");
return;
}
while (lhs != NULL)
{
/* resolve to spec */
node_tmp = pt_find_spec_in_statement (parser, statement, lhs);
if (node_tmp == NULL)
{
PT_INTERNAL_ERROR (parser, "invalid spec id");
return;
}
/* resolve name and add rowoid attributes where needed */
resolved = pt_process_spec_for_update (parser, node_tmp, lhs);
if (resolved == NULL || resolved->node_type != PT_NAME)
{
/* error should have been set */
break;
}
/* flat_entity_list will be propagated trough derived tables of updatable views, so the assignment name
* should be able to be resolved to them */
lhs->info.name.original = resolved->info.name.original;
/* advance to next name in set (if exists) */
lhs = lhs->next;
}
/* next assignment */
node = node->next;
}
}
/*
* pt_check_grammar_charset_collation () - validates a pair of charset and
* collation nodes and return the associated identifiers
* return: error status
* parser(in): the parser context
* charset_node(in): node containing charset string (PT_VALUE)
* coll_node(in): node containing collation string (PT_VALUE)
* charset(in): validated value for charset (INTL_CHARSET)
* coll_id(in): validated value for collation
*/
int
pt_check_grammar_charset_collation (PARSER_CONTEXT * parser, PT_NODE * charset_node, PT_NODE * coll_node, int *charset,
int *coll_id)
{
bool has_user_charset = false;
assert (charset != NULL);
assert (coll_id != NULL);
*charset = LANG_SYS_CODESET;
*coll_id = LANG_SYS_COLLATION;
if (charset_node != NULL)
{
const char *cs_name;
assert (charset_node->node_type == PT_VALUE);
assert (charset_node->info.value.data_value.str != NULL);
cs_name = (char *) charset_node->info.value.data_value.str->bytes;
*charset = lang_charset_cubrid_name_to_id (cs_name);
if (*charset == INTL_CODESET_NONE)
{
PT_ERRORm (parser, charset_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_CHARSET);
return ER_GENERIC_ERROR;
}
has_user_charset = true;
}
if (coll_node != NULL)
{
LANG_COLLATION *lang_coll;
assert (coll_node->node_type == PT_VALUE);
assert (coll_node->info.value.data_value.str != NULL);
lang_coll = lang_get_collation_by_name ((const char *) coll_node->info.value.data_value.str->bytes);
if (lang_coll != NULL)
{
int coll_charset;
*coll_id = lang_coll->coll.coll_id;
coll_charset = (int) lang_coll->codeset;
if (has_user_charset && coll_charset != *charset)
{
/* error incompatible charset and collation */
PT_ERRORm (parser, coll_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INCOMPATIBLE_CS_COLL);
return ER_GENERIC_ERROR;
}
/* default charset for this collation */
*charset = coll_charset;
}
else
{
PT_ERRORmf (parser, coll_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNKNOWN_COLL,
coll_node->info.value.data_value.str->bytes);
return ER_GENERIC_ERROR;
}
}
else
{
assert (coll_node == NULL);
/* set a default collation for a charset */
switch (*charset)
{
case INTL_CODESET_ISO88591:
*coll_id = LANG_COLL_ISO_BINARY;
break;
case INTL_CODESET_KSC5601_EUC:
*coll_id = LANG_COLL_EUCKR_BINARY;
break;
case INTL_CODESET_UTF8:
*coll_id = LANG_COLL_UTF8_BINARY;
break;
default:
assert (*charset == INTL_CODESET_BINARY);
*coll_id = LANG_COLL_BINARY;
return NO_ERROR;
}
}
return NO_ERROR;
}
/*
* pt_make_tuple_value_reference () - create a PT_TUPLE_VALUE node for a
* SELECT list of a SELECT statement
* return : new node or NULL
* parser (in) : parser context
* name (in) : name node
* select_list (in) : select list of a query
* cursor_p (in) : cursor for the query
*/
PT_NODE *
pt_make_tuple_value_reference (PARSER_CONTEXT * parser, PT_NODE * name, PT_NODE * select_list, CURSOR_ID * cursor_p)
{
int index = 0;
PT_NODE *node = NULL, *new_col = NULL, *last_node = NULL, *next = NULL;
assert_release (select_list != NULL);
for (node = select_list, index = 0; node != NULL; node = node->next, index++)
{
if (pt_check_path_eq (parser, node, name) == 0)
{
/* found it, just make a tuple_value reference to it */
next = name->next;
name->next = NULL;
name = pt_tuple_value (parser, name, cursor_p, index);
name->next = next;
return name;
}
last_node = node;
}
next = name->next;
name->next = NULL;
/* add it to the end of the select_list */
new_col = parser_copy_tree (parser, name);
if (new_col == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
last_node->next = new_col;
/* change name to tuple value */
name = pt_tuple_value (parser, name, cursor_p, index);
if (name == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
name->next = next;
return name;
}
/*
* pt_make_query_show_collation() - builds the query for SHOW COLLATION
*
* SELECT * FROM
* (SELECT coll_name AS [Collation],
* IF (charset_id = 3, 'iso88591',
* IF (charset_id = 5, 'utf8',
* IF (charset_id = 2, 'binary',
* IF (charset_id = 4, 'euckr', 'other')))) AS Charset,
* coll_id AS Id,
* IF (built_in = 0, 'No', 'Yes') AS Built_in,
* IF (expansions = 0, 'No', 'Yes') AS Expansions,
* IF (uca_strength = 1,'Primary',
* IF (uca_strength = 2,'Secondary',
* IF (uca_strength = 3, 'Tertiary',
* IF (uca_strength = 4,'Quaternary',
* IF (uca_strength = 5, 'Identity',
* 'Not applicable'))))) AS Strength
* FROM _db_collation) show_colllation
* ORDER BY 1;
*
* return: newly built node (PT_NODE), NULL if construnction fails
* parser(in): Parser context
* like_where_syntax(in): indicator of presence for LIKE or WHERE clauses in
* SHOW statement. Values : 0 = none of LIKE or WHERE,
* 1 = contains LIKE, 2 = contains WHERE
* like_or_where_expr(in): node expression supplied as condition (in WHERE)
* or RHS for LIKE
*
* Note : Order is defined by: coll_name
*/
PT_NODE *
pt_make_query_show_collation (PARSER_CONTEXT * parser, int like_where_syntax, PT_NODE * like_or_where_expr)
{
PT_NODE *sub_query = NULL;
PT_NODE *node = NULL;
PT_NODE *from_item = NULL;
sub_query = parser_new_node (parser, PT_SELECT);
if (sub_query == NULL)
{
return NULL;
}
/* ------ SELECT list ------- */
pt_add_name_col_to_sel_list (parser, sub_query, "coll_name", "Collation");
/* Charset */
{
PT_NODE *if_node1 = NULL;
PT_NODE *if_node2 = NULL;
PT_NODE *if_node3 = NULL;
PT_NODE *if_node4 = NULL;
{
/* IF (charset_id = 4, 'euckr', 'other') */
PT_NODE *pred = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "charset_id", 4); // INTL_CODESET_KSC5601_EUC
if_node4 = pt_make_if_with_strings (parser, pred, "euckr", "other", NULL);
}
{
/* IF (charset_id = 2, 'binary', IF_NODE_ 4) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "charset_id", 2); // INTL_CODESET_RAW_BYTES
string_node = pt_make_string_value (parser, "binary");
if_node3 = pt_make_if_with_expressions (parser, pred, string_node, if_node4, NULL);
}
{
/* IF (charset_id = 5, 'utf8', IF_NODE_ 3) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "charset_id", 5); // INTL_CODESET_UTF8
string_node = pt_make_string_value (parser, "utf8");
if_node2 = pt_make_if_with_expressions (parser, pred, string_node, if_node3, NULL);
}
{
/* IF (charset_id = 3, 'iso88591', IF_NODE_ 2) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "charset_id", 3); // INTL_CODESET_ISO88591
string_node = pt_make_string_value (parser, "iso88591");
if_node1 = pt_make_if_with_expressions (parser, pred, string_node, if_node2, "Charset");
}
sub_query->info.query.q.select.list = parser_append_node (if_node1, sub_query->info.query.q.select.list);
}
pt_add_name_col_to_sel_list (parser, sub_query, "coll_id", "Id");
/* Built_in */
{
PT_NODE *if_node = NULL;
PT_NODE *pred = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "built_in", 0);
if_node = pt_make_if_with_strings (parser, pred, "No", "Yes", "Built_in");
sub_query->info.query.q.select.list = parser_append_node (if_node, sub_query->info.query.q.select.list);
}
/* Expansions */
{
PT_NODE *if_node = NULL;
PT_NODE *pred = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "expansions", 0);
if_node = pt_make_if_with_strings (parser, pred, "No", "Yes", "Expansions");
sub_query->info.query.q.select.list = parser_append_node (if_node, sub_query->info.query.q.select.list);
}
/* Strength */
{
PT_NODE *if_node1 = NULL;
PT_NODE *if_node2 = NULL;
PT_NODE *if_node3 = NULL;
PT_NODE *if_node4 = NULL;
PT_NODE *if_node5 = NULL;
{
/* IF (uca_strength = 5, 'Identity', 'Not applicable') */
PT_NODE *pred = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "uca_strength", 5);
if_node5 = pt_make_if_with_strings (parser, pred, "Identity", "Not applicable", NULL);
}
{
/* IF (uca_strength = 4,'Quaternary', IF_node_5) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "uca_strength", 4);
string_node = pt_make_string_value (parser, "Quaternary");
if_node4 = pt_make_if_with_expressions (parser, pred, string_node, if_node5, NULL);
}
{
/* IF (uca_strength = 3, 'Tertiary', IF_Node_4) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "uca_strength", 3);
string_node = pt_make_string_value (parser, "Tertiary");
if_node3 = pt_make_if_with_expressions (parser, pred, string_node, if_node4, NULL);
}
{
/* IF (uca_strength = 2,'Secondary', IF_Node_3) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "uca_strength", 2);
string_node = pt_make_string_value (parser, "Secondary");
if_node2 = pt_make_if_with_expressions (parser, pred, string_node, if_node3, NULL);
}
{
/* IF (uca_strength = 1,'Primary', IF_Node_2) */
PT_NODE *pred = NULL;
PT_NODE *string_node = NULL;
pred = pt_make_pred_name_int_val (parser, PT_EQ, "uca_strength", 1);
string_node = pt_make_string_value (parser, "Primary");
if_node1 = pt_make_if_with_expressions (parser, pred, string_node, if_node2, "Strength");
}
sub_query->info.query.q.select.list = parser_append_node (if_node1, sub_query->info.query.q.select.list);
}
/* ------ SELECT ... FROM ------- */
from_item = pt_add_table_name_to_from_list (parser, sub_query, "_db_collation", NULL, DB_AUTH_SELECT);
if (from_item == NULL)
{
return NULL;
}
node = pt_make_outer_select_for_show_stmt (parser, sub_query, "show_columns");
if (node == NULL)
{
return NULL;
}
{
/* add ORDER BY (to outer select) */
PT_NODE *order_by_item = NULL;
assert (node->info.query.order_by == NULL);
/* By Collation */
order_by_item = pt_make_sort_spec_with_number (parser, 1, PT_ASC);
node->info.query.order_by = parser_append_node (order_by_item, node->info.query.order_by);
}
if (like_or_where_expr != NULL)
{
PT_NODE *where_item = NULL;
if (like_where_syntax == 1)
{
/* make LIKE */
where_item = pt_make_like_col_expr (parser, like_or_where_expr, "Collation");
}
else
{
/* WHERE */
assert (like_where_syntax == 2);
where_item = like_or_where_expr;
}
node->info.query.q.select.where = parser_append_node (where_item, node->info.query.q.select.where);
}
else
{
assert (like_where_syntax == 0);
}
return node;
}
/*
* pt_get_query_limit_from_limit () - get the value of the LIMIT clause of a query
* return : error code or NO_ERROR
* parser (in) : parser context
* limit (in) : limit node
* limit_val (in/out) : limit value
*
* Note: this function get the LIMIT clause value of a query as a
* DB_TYPE_BIGINT value. If the LIMIT clause contains a lower limit, the
* returned value is computed as lower bound + range. (i.e.: if it was
* specified as LIMIT :offset, :row_count this function returns :offset + :row_count)
*/
static int
pt_get_query_limit_from_limit (PARSER_CONTEXT * parser, PT_NODE * limit, DB_VALUE * limit_val, bool add_offset)
{
int save_set_host_var;
TP_DOMAIN *domainp = NULL;
int error = NO_ERROR;
db_make_null (limit_val);
if (limit == NULL)
{
return NO_ERROR;
}
domainp = tp_domain_resolve_default (DB_TYPE_BIGINT);
save_set_host_var = parser->flag.set_host_var;
parser->flag.set_host_var = 1;
assert (limit->node_type == PT_VALUE || limit->node_type == PT_HOST_VAR || limit->node_type == PT_EXPR);
pt_evaluate_tree_having_serial (parser, limit, limit_val, 1);
if (pt_has_error (parser))
{
error = ER_FAILED;
goto cleanup;
}
if (DB_IS_NULL (limit_val))
{
/* probably value is not bound yet */
goto cleanup;
}
if (tp_value_coerce (limit_val, limit_val, domainp) != DOMAIN_COMPATIBLE)
{
error = ER_FAILED;
goto cleanup;
}
if (limit->next)
{
DB_VALUE range;
db_make_null (&range);
/* LIMIT :offset, :row_count => return :offset + :row_count */
assert (limit->next->node_type == PT_VALUE || limit->next->node_type == PT_HOST_VAR
|| limit->next->node_type == PT_EXPR);
pt_evaluate_tree_having_serial (parser, limit->next, &range, 1);
if (pt_has_error (parser))
{
error = ER_FAILED;
goto cleanup;
}
if (DB_IS_NULL (&range))
{
/* probably value is not bound yet */
goto cleanup;
}
if (tp_value_coerce (&range, &range, domainp) != DOMAIN_COMPATIBLE)
{
error = ER_FAILED;
goto cleanup;
}
/* add range to current limit */
if (add_offset)
{
db_make_bigint (limit_val, db_get_bigint (limit_val) + db_get_bigint (&range));
}
else
{
db_make_bigint (limit_val, db_get_bigint (&range));
}
}
cleanup:
if (error != NO_ERROR)
{
pr_clear_value (limit_val);
db_make_null (limit_val);
}
parser->flag.set_host_var = save_set_host_var;
return error;
}
/*
* pt_get_query_expr_value () - get the value of an expression
* return : error code or NO_ERROR
* parser (in) : parser context
* expr (in) : expression
* expr_val (in/out) : expression value
*/
int
pt_get_query_expr_value (PARSER_CONTEXT * parser, PT_NODE * expr, DB_VALUE * expr_val)
{
int save_set_host_var;
int error = NO_ERROR;
int type_arg[2];
type_arg[0] = PT_HOST_VAR; /* type */
type_arg[1] = 0; /* found */
db_make_null (expr_val);
if (expr == NULL)
{
return NO_ERROR;
}
(void) parser_walk_tree (parser, expr, pt_find_node_type_pre, type_arg, NULL, NULL);
if (type_arg[1] == 0)
{
return NO_ERROR;
}
save_set_host_var = parser->flag.set_host_var;
parser->flag.set_host_var = 1;
pt_evaluate_tree (parser, expr, expr_val, 1);
if (pt_has_error (parser))
{
error = ER_FAILED;
goto cleanup;
}
if (DB_IS_NULL (expr_val))
{
goto cleanup;
}
cleanup:
if (error != NO_ERROR)
{
pr_clear_value (expr_val);
db_make_null (expr_val);
}
parser->flag.set_host_var = save_set_host_var;
return error;
}
/*
* pt_check_removable_like_condition () - check if LIKE condition is removable
* return : true/false
* parser (in) : parser context
* from (in) : from clause
* expr (in) : expression
*/
static bool
pt_check_removable_like_condition (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * expr)
{
PT_NODE *arg1, *arg2;
PT_NODE *pattern = NULL, *escape = NULL;
DB_VALUE where_val, compressed_pattern;
int num_logical_chars = 0;
int last_safe_logical_pos = 0;
int num_match_many = 0;
int num_match_one = 0;
bool need_recompile = false;
bool has_escape_char = false;
const char *escape_str = NULL;
INTL_CODESET codeset;
if (expr->node_type != PT_EXPR)
{
return false;
}
arg1 = PT_EXPR_ARG1 (expr);
arg2 = PT_EXPR_ARG2 (expr);
switch (expr->info.expr.op)
{
case PT_LIKE:
{
if (!arg1 || !pt_check_not_null_constraint (parser, from, arg1))
{
break;
}
if (PT_IS_EXPR_NODE_WITH_OPERATOR (arg2, PT_LIKE_ESCAPE))
{
pattern = PT_EXPR_ARG1 (arg2);
escape = PT_EXPR_ARG2 (arg2);
assert (escape != NULL);
}
else
{
pattern = arg2;
escape = NULL;
}
if (escape != NULL)
{
if (PT_IS_NULL_NODE (escape))
{
has_escape_char = true;
escape_str = "\\";
}
else
{
int esc_char_len = 0;
assert (pt_is_ascii_string_value_node (escape));
escape_str = (const char *) escape->info.value.data_value.str->bytes;
codeset = db_get_string_codeset (&pattern->info.value.db_value);
intl_char_count ((unsigned char *) escape_str, escape->info.value.data_value.str->length, codeset,
&esc_char_len);
if (esc_char_len != 1)
{
PT_ERRORm (parser, escape, MSGCAT_SET_ERROR, -(ER_QSTR_INVALID_ESCAPE_SEQUENCE));
return false;
}
has_escape_char = true;
}
}
else if (prm_get_bool_value (PRM_ID_REQUIRE_LIKE_ESCAPE_CHARACTER))
{
assert (escape == NULL);
assert (!prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES));
has_escape_char = true;
escape_str = "\\";
}
else
{
has_escape_char = false;
escape_str = NULL;
}
if (pt_get_query_expr_value (parser, pattern, &where_val) == NO_ERROR)
{
if (!DB_IS_NULL (&where_val))
{
db_make_null (&compressed_pattern);
db_compress_like_pattern (&where_val, &compressed_pattern, has_escape_char, escape_str);
db_get_info_for_like_optimization (&compressed_pattern, has_escape_char, escape_str,
&num_logical_chars, &last_safe_logical_pos,
&num_match_many, &num_match_one);
if (num_logical_chars == 1 && num_match_many == 1)
{
need_recompile = true;
}
/* If num_logical_chars is greater than 0 and both num_match_many and num_match_one are 0,
* the PT_LIKE can be replaced with PT_EQ instead.
* See: qo_rewrite_one_like_term() */
}
}
}
default:
break;
}
return need_recompile;
}
PT_NODE *
pt_check_removable_expr_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
PT_NODE **spec_list = (PT_NODE **) arg;
if (tree == NULL)
{
return NULL;
}
switch (tree->node_type)
{
case PT_SPEC:
if (PT_SPEC_IS_ENTITY (tree) && !pt_find_entity (parser, *spec_list, tree->info.spec.id))
{
*spec_list = parser_append_node (parser_copy_tree (parser, tree), *spec_list);
}
break;
case PT_EXPR:
if (pt_check_removable_like_condition (parser, *spec_list, tree))
{
tree->info.expr.flag |= PT_EXPR_INFO_REMOVABLE;
}
break;
default:
break;
}
return tree;
}
PT_NODE *
pt_check_removable_expr_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
bool *is_removable = (bool *) arg;
if (tree == NULL)
{
return NULL;
}
if (PT_IS_EXPR_NODE_WITH_OPERATOR (tree, PT_LIKE))
{
if (PT_EXPR_INFO_IS_FLAGED (tree, PT_EXPR_INFO_REMOVABLE))
{
*is_removable = true;
}
}
return tree;
}
/*
* pt_get_query_limit_value () - get the limit value from a query
* return : error code or NO_ERROR
* parser (in) : parser context
* query (in) : query
* limit_val (in/out) : limit value
*/
int
pt_get_query_limit_value (PARSER_CONTEXT * parser, PT_NODE * query, DB_VALUE * limit_val, bool add_offset)
{
assert_release (limit_val != NULL);
db_make_null (limit_val);
if (query == NULL || !PT_IS_QUERY (query))
{
return NO_ERROR;
}
if (query->info.query.limit)
{
return pt_get_query_limit_from_limit (parser, query->info.query.limit, limit_val, add_offset);
}
if (query->info.query.orderby_for != NULL)
{
int error = NO_ERROR;
bool has_limit = false;
error = pt_get_query_limit_from_orderby_for (parser, query->info.query.orderby_for, limit_val, &has_limit);
if (error != NO_ERROR || !has_limit)
{
return ER_FAILED;
}
}
return NO_ERROR;
}
/*
* pt_get_query_limit_from_query () - get the limit value from a query
* return : error code or NO_ERROR
* parser (in) : parser context
* query (in) : query
*/
DB_BIGINT
pt_get_query_limit_from_query (PARSER_CONTEXT * parser, PT_NODE * query)
{
DB_VALUE limit_val;
if (pt_get_query_limit_value (parser, query, &limit_val, false) != NO_ERROR)
{
/* remove error which is occured in this function */
if (pt_has_error (parser))
{
pt_reset_error (parser);
parser->flag.has_internal_error = 0;
}
return 0;
}
else if (DB_IS_NULL (&limit_val))
{
return 0;
}
return db_get_bigint (&limit_val);
}
/*
* pt_check_ordby_num_for_multi_range_opt () - checks if limit/order by for is valid for multi range opt
*
* return : true/false
* parser (in) : parser context
* query (in) : query
* mro_candidate (out) : if the only failed condition is upper limit for
* orderby_num (), this plan may still use multi range
* optimization if that limit changes
* cannot_eval (out) : upper limit is null or could not be evaluated
*/
bool
pt_check_ordby_num_for_multi_range_opt (PARSER_CONTEXT * parser, PT_NODE * query, bool * mro_candidate,
bool * cannot_eval)
{
DB_VALUE limit_val;
int save_set_host_var;
bool valid = false;
assert_release (query != NULL);
if (cannot_eval != NULL)
{
*cannot_eval = true;
}
if (mro_candidate != NULL)
{
*mro_candidate = false;
}
if (!PT_IS_QUERY (query))
{
return false;
}
db_make_null (&limit_val);
save_set_host_var = parser->flag.set_host_var;
parser->flag.set_host_var = 1;
if (pt_get_query_limit_value (parser, query, &limit_val, true) != NO_ERROR)
{
goto end;
}
if (DB_IS_NULL (&limit_val))
{
goto end_mro_candidate;
}
if (cannot_eval)
{
/* upper limit was successfully evaluated */
*cannot_eval = false;
}
if (db_get_bigint (&limit_val) > prm_get_integer_value (PRM_ID_MULTI_RANGE_OPT_LIMIT))
{
goto end_mro_candidate;
}
else
{
valid = true;
goto end;
}
end_mro_candidate:
/* should be here if multi range optimization could not be validated because upper limit is too large or it could not
* be evaluated. However, the query may still use optimization for different host variable values. */
if (mro_candidate != NULL)
{
*mro_candidate = true;
}
end:
parser->flag.set_host_var = save_set_host_var;
return valid;
}
/*
* pt_get_query_limit_from_orderby_for () - get upper limit value for orderby_for expression
*
* return : true if a valid order by for expression, else false
* parser (in) : parser context
* orderby_for (in) : order by for node
* upper_limit (out) : DB_VALUE pointer that will save the upper limit
*
* Note: Only operations that can reduce to ORDERBY_NUM () </<= VALUE are allowed:
* 1. ORDERBY_NUM () LE/LT EXPR (which evaluates to a value).
* 2. EXPR (which evaluates to a values) GE/GT ORDERBY_NUM ().
* 3. Any number of #1 and #2 expressions linked by PT_AND logical operator.
* Lower limits are allowed.
*/
static int
pt_get_query_limit_from_orderby_for (PARSER_CONTEXT * parser, PT_NODE * orderby_for, DB_VALUE * upper_limit,
bool * has_limit)
{
int op;
PT_NODE *arg_ordby_num = NULL;
PT_NODE *rhs = NULL;
PT_NODE *arg1 = NULL, *arg2 = NULL;
PT_NODE *save_next = NULL;
DB_VALUE limit;
int error = NO_ERROR;
bool lt = false;
if (orderby_for == NULL || upper_limit == NULL)
{
return NO_ERROR;
}
assert_release (has_limit != NULL);
if (!PT_IS_EXPR_NODE (orderby_for))
{
goto unusable_expr;
}
if (orderby_for->or_next != NULL)
{
/* OR operator is now useful */
goto unusable_expr;
}
if (orderby_for->next)
{
/* AND operator */
save_next = orderby_for->next;
orderby_for->next = NULL;
error = pt_get_query_limit_from_orderby_for (parser, orderby_for, upper_limit, has_limit);
orderby_for->next = save_next;
if (error != NO_ERROR)
{
goto unusable_expr;
}
return pt_get_query_limit_from_orderby_for (parser, orderby_for->next, upper_limit, has_limit);
}
op = orderby_for->info.expr.op;
if (op != PT_LT && op != PT_LE && op != PT_GT && op != PT_GE && op != PT_BETWEEN)
{
goto unusable_expr;
}
arg1 = orderby_for->info.expr.arg1;
arg2 = orderby_for->info.expr.arg2;
if (arg1 == NULL || arg2 == NULL)
{
/* safe guard */
goto unusable_expr;
}
/* look for orderby_for argument */
if (PT_IS_EXPR_NODE (arg1) && arg1->info.expr.op == PT_ORDERBY_NUM)
{
arg_ordby_num = arg1;
rhs = arg2;
}
else if (PT_IS_EXPR_NODE (arg2) && arg2->info.expr.op == PT_ORDERBY_NUM)
{
arg_ordby_num = arg2;
rhs = arg1;
/* reverse operators */
switch (op)
{
case PT_LE:
op = PT_GT;
break;
case PT_LT:
op = PT_GE;
break;
case PT_GT:
op = PT_LE;
break;
case PT_GE:
op = PT_LT;
break;
default:
break;
}
}
else
{
/* orderby_for argument was not found, this is not a valid expression */
goto unusable_expr;
}
if (op == PT_GE || op == PT_GT)
{
/* lower limits are acceptable but not useful */
return NO_ERROR;
}
if (op == PT_LT)
{
lt = true;
}
else if (op == PT_BETWEEN)
{
PT_NODE *between_and = orderby_for->info.expr.arg2;
int between_op;
assert (between_and != NULL && between_and->node_type == PT_EXPR);
between_op = between_and->info.expr.op;
switch (between_op)
{
case PT_BETWEEN_GT_LT:
case PT_BETWEEN_GE_LT:
lt = true;
[[fallthrough]];
case PT_BETWEEN_AND:
case PT_BETWEEN_GE_LE:
case PT_BETWEEN_GT_LE:
assert (between_and->info.expr.arg2 != NULL);
rhs = between_and->info.expr.arg2;
break;
case PT_BETWEEN_EQ_NA:
case PT_BETWEEN_GE_INF:
case PT_BETWEEN_GT_INF:
/* lower limits are acceptable but not useful */
return NO_ERROR;
default:
/* be conservative */
goto unusable_expr;
}
}
/* evaluate the rhs expression */
db_make_null (&limit);
if (PT_IS_CONST (rhs) || PT_IS_CAST_CONST_INPUT_HOSTVAR (rhs))
{
pt_evaluate_tree_having_serial (parser, rhs, &limit, 1);
}
if (DB_IS_NULL (&limit)
|| tp_value_coerce (&limit, &limit, tp_domain_resolve_default (DB_TYPE_BIGINT)) != DOMAIN_COMPATIBLE)
{
/* has unusable upper_limit */
pr_clear_value (upper_limit);
*has_limit = true;
return NO_ERROR;
}
if (lt)
{
/* ORDERBY_NUM () < n => ORDERBY_NUM <= n - 1 */
db_make_bigint (&limit, (db_get_bigint (&limit) - 1));
}
if (DB_IS_NULL (upper_limit) || (db_get_bigint (upper_limit) > db_get_bigint (&limit)))
{
/* update upper limit */
if (pr_clone_value (&limit, upper_limit) != NO_ERROR)
{
goto unusable_expr;
}
}
*has_limit = true;
return NO_ERROR;
unusable_expr:
*has_limit = false;
pr_clear_value (upper_limit);
return ER_FAILED;
}
/*
* pt_find_node_type_pre () - Use parser_walk_tree to find a node with a
* specific node type.
*
* return : node.
* parser (in) : parser context.
* node (in) : node in parse tree.
* arg (in) : int array containing node type and found.
* continue_walk (in) : continue walk.
*
* NOTE: Make sure to set found to 0 before calling parser_walk_tree.
*/
PT_NODE *
pt_find_node_type_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int node_type = *((int *) arg);
int *found_p = ((int *) arg) + 1;
if (*found_p || *continue_walk == PT_STOP_WALK || node == NULL)
{
return node;
}
if (node->node_type == node_type)
{
*found_p = 1;
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_find_op_type_pre () - Use parser_walk_tree to find an operator of a
* specific type.
*
* return : node.
* parser (in) : parser context.
* node (in) : node in parse tree.
* arg (in) : int array containing expr type and found.
* continue_walk (in) : continue walk.
*
* NOTE: Make sure to set found to 0 before calling parser_walk_tree.
*/
PT_NODE *
pt_find_op_type_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int op_type = *((int *) arg);
int *found_p = ((int *) arg) + 1;
if (*found_p || *continue_walk == PT_STOP_WALK || node == NULL)
{
return node;
}
if (node->node_type == PT_EXPR && node->info.expr.op == op_type)
{
*found_p = 1;
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* pt_partition_name - get actual class name of a partition from root class name
* and partition suffix
* return: partition class name
* parser (in) : parser context
* class_name (in): partitioned class name
* partition (in) : partition suffix
*/
const char *
pt_partition_name (PARSER_CONTEXT * parser, const char *class_name, const char *partition)
{
char *name = NULL, *buf = NULL;
int size = 0;
size = strlen (class_name) + strlen (partition) + strlen (PARTITIONED_SUB_CLASS_TAG);
buf = (char *) calloc (size + 1, sizeof (char));
if (buf == NULL)
{
PT_ERRORm (parser, NULL, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
sprintf (buf, "%s" PARTITIONED_SUB_CLASS_TAG "%s", class_name, partition);
name = pt_append_string (parser, name, buf);
free (buf);
return name;
}
/*
* pt_free_statement_xasl_id () - free XASL_ID object of a prepared statement
* return : void
* statement (in) : statement
*/
void
pt_free_statement_xasl_id (PT_NODE * statement)
{
if (statement == NULL)
{
return;
}
if (statement->xasl_id != NULL)
{
free_and_init (statement->xasl_id);
}
if (statement->node_type == PT_DELETE)
{
/* free xasl_id for computed individual select statements */
PT_NODE *del_stmt;
for (del_stmt = statement->info.delete_.del_stmt_list; del_stmt != NULL; del_stmt = del_stmt->next)
{
if (del_stmt->xasl_id != NULL)
{
free_and_init (del_stmt->xasl_id);
}
}
}
}
/*
* pt_check_enum_data_type - validate the ENUM data type
* return: NO_ERROR or error code
* parser (in) : parser context
* dt (in/out) : ENUM data type
*
* NOTE: The function removes trailing pads for each ENUM element, checks that
* the number of elements is lower or equal than DB_ENUM_ELEMENTS_MAX, checks
* that the aggregate size of the ENUM elements is not greater than
* DB_ENUM_ELEMENTS_MAX_AGG_SIZE and checks for duplicates
*/
int
pt_check_enum_data_type (PARSER_CONTEXT * parser, PT_NODE * dt)
{
int count = 0, err = NO_ERROR;
PT_NODE *node = NULL, *temp = NULL;
TP_DOMAIN *domain = NULL;
int pad_size = 0, trimmed_length = 0, trimmed_size = 0;
int char_count = 0;
unsigned char pad[2];
bool ti = true;
static bool ignore_trailing_space = prm_get_bool_value (PRM_ID_IGNORE_TRAILING_SPACE);
if (dt == NULL || dt->node_type != PT_DATA_TYPE || dt->type_enum != PT_TYPE_ENUMERATION)
{
return NO_ERROR;
}
/* remove trailing pads for each element */
intl_pad_char ((INTL_CODESET) dt->info.data_type.units, pad, &pad_size);
node = dt->info.data_type.enumeration;
while (node != NULL)
{
intl_char_count (node->info.value.data_value.str->bytes, node->info.value.data_value.str->length,
(INTL_CODESET) dt->info.data_type.units, &char_count);
qstr_trim_trailing (pad, pad_size, node->info.value.data_value.str->bytes, pt_node_to_db_type (node), char_count,
node->info.value.data_value.str->length, (INTL_CODESET) dt->info.data_type.units,
&trimmed_length, &trimmed_size, true);
if (trimmed_size < node->info.value.data_value.str->length)
{
node->info.value.data_value.str =
pt_append_bytes (parser, NULL, (const char *) node->info.value.data_value.str->bytes, trimmed_size);
node->info.value.data_value.str->length = trimmed_size;
if (node->info.value.db_value.need_clear)
{
pr_clear_value (&node->info.value.db_value);
}
if (node->info.value.db_value_is_initialized)
{
node->info.value.db_value_is_initialized = false;
pt_value_to_db (parser, node);
}
}
node = node->next;
count++;
}
/* check that number of elements is lower or equal than DB_ENUM_ELEMENTS_MAX */
if (count > DB_ENUM_ELEMENTS_MAX)
{
PT_ERRORmf2 (parser, dt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ENUM_TYPE_TOO_MANY_VALUES, count,
DB_ENUM_ELEMENTS_MAX);
err = ER_FAILED;
goto end;
}
/* check that the aggregate size of the ENUM elements is not greater than DB_ENUM_ELEMENTS_MAX_AGG_SIZE */
domain = pt_data_type_to_db_domain (parser, dt, NULL);
if (domain == NULL)
{
PT_INTERNAL_ERROR (parser, "Cannot create domain");
return ER_FAILED;
}
count = or_packed_domain_size (domain, 0);
if (count > (int) DB_ENUM_ELEMENTS_MAX_AGG_SIZE)
{
PT_ERRORm (parser, dt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ENUM_AGG_STRINGS_SIZE_TOO_LARGE);
err = ER_FAILED;
goto end;
}
/* check duplicates */
node = dt->info.data_type.enumeration;
while (node != NULL && err == NO_ERROR)
{
temp = node->next;
while (temp != NULL)
{
if (!ignore_trailing_space)
{
ti = (domain->type->id == DB_TYPE_CHAR);
}
if (QSTR_COMPARE (domain->collation_id, node->info.value.data_value.str->bytes,
node->info.value.data_value.str->length, temp->info.value.data_value.str->bytes,
temp->info.value.data_value.str->length, ti) == 0)
{
PT_ERRORm (parser, temp, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ENUM_TYPE_DUPLICATE_VALUES);
err = ER_FAILED;
break;
}
temp = temp->next;
}
node = node->next;
}
end:
if (domain != NULL)
{
tp_domain_free (domain);
}
return err;
}
/*
* pt_recompile_for_like_optimizations () - check if query should be recompiled due to removal of unnecessary LIKE condition
* return : true/false
* parser (in) : parser context
* statement (in) : statement
* xasl_flag (in) : xasl flag
*/
bool
pt_recompile_for_like_optimizations (PARSER_CONTEXT * parser, PT_NODE * statement, int xasl_flag)
{
PT_NODE *spec_list = NULL;
bool is_removable = false;
if (statement->node_type != PT_SELECT)
{
return false;
}
if (!(xasl_flag & LIKE_RECOMPILE_CANDIDATE) || !prm_get_bool_value (PRM_ID_HOSTVAR_PEEKING))
{
return false;
}
parser_walk_tree (parser, statement, pt_check_removable_expr_pre, &spec_list, pt_check_removable_expr_post,
&is_removable);
return is_removable;
}
/*
* pt_recompile_for_limit_optimizations () - verify is query plan should be
* regenerated for a query due to
* limit change
* return : true/false
* parser (in) : parser context
* statement (in) : statement
* xasl_flag (in) : flag which contains plan information
*
* Note: before executing a query plan, we have to verify if the generated
* plan is still valid with the new limit values. There are four cases:
* I. MRO is used but the new limit value is either too large or invalid
* for this plan. In this case we have to recompile.
* II. MRO is not used but the new limit value is valid for generating such
* a plan. In this case we have to recompile.
* III. MRO is not used and the new limit value is invalid for generating this
* plan. In this case we don't have to recompile
* VI. MRO is used and the new limit value is valid for this plan. In this
* case we don't have to recompile
* The same rules apply to SORT-LIMIT plans
*/
bool
pt_recompile_for_limit_optimizations (PARSER_CONTEXT * parser, PT_NODE * statement, int xasl_flag)
{
DB_VALUE limit_val;
DB_BIGINT val = 0;
int limit_opt_flag = (MRO_CANDIDATE | MRO_IS_USED | SORT_LIMIT_CANDIDATE | SORT_LIMIT_USED);
if (!(xasl_flag & limit_opt_flag))
{
return false;
}
if (pt_get_query_limit_value (parser, statement, &limit_val, true) != NO_ERROR)
{
val = 0;
}
else if (DB_IS_NULL (&limit_val))
{
val = 0;
}
else
{
val = db_get_bigint (&limit_val);
}
/* verify MRO */
if (xasl_flag & (MRO_CANDIDATE | MRO_IS_USED))
{
if (val == 0 || val > (DB_BIGINT) prm_get_integer_value (PRM_ID_MULTI_RANGE_OPT_LIMIT))
{
if (xasl_flag & MRO_IS_USED)
{
/* Need to recompile because limit is not suitable for MRO anymore */
return true;
}
}
else if ((xasl_flag & MRO_CANDIDATE) && !(xasl_flag & MRO_IS_USED))
{
/* Suitable limit for MRO but MRO is not used. Recompile to use MRO */
return true;
}
return false;
}
/* verify SORT-LIMIT */
if (xasl_flag & (SORT_LIMIT_CANDIDATE | SORT_LIMIT_USED))
{
if (val == 0 || val > (DB_BIGINT) prm_get_integer_value (PRM_ID_SORT_LIMIT_MAX_COUNT))
{
if (xasl_flag & SORT_LIMIT_USED)
{
/* Need to recompile because limit is not suitable for SORT-LIMIT anymore */
return true;
}
}
else if ((xasl_flag & SORT_LIMIT_CANDIDATE) && !(xasl_flag & SORT_LIMIT_USED))
{
/* Suitable limit for SORT-LIMIT but SORT-LIMIT is not used. Recompile to use SORT-LIMIT */
return true;
}
return false;
}
return false;
}
/*
* pt_make_query_show_trace () -
* return: node
*/
PT_NODE *
pt_make_query_show_trace (PARSER_CONTEXT * parser)
{
PT_NODE *select, *trace_func;
select = parser_new_node (parser, PT_SELECT);
if (select == NULL)
{
return NULL;
}
PT_SELECT_INFO_SET_FLAG (select, PT_SELECT_INFO_READ_ONLY);
trace_func = parser_make_expression (parser, PT_TRACE_STATS, NULL, NULL, NULL);
if (trace_func == NULL)
{
return NULL;
}
trace_func->alias_print = pt_append_string (parser, NULL, "trace");
select->info.query.q.select.list = parser_append_node (trace_func, select->info.query.q.select.list);
if (parser->statement_number == 0) // This is when only the show trace statement is used.
{
parser->flag.dont_collect_exec_stats = 1;
parser->query_trace = false;
}
return select;
}
/*
* pt_has_non_groupby_column_node () - Use parser_walk_tree to check having
* clause.
* return : node.
* parser (in) : parser context.
* node (in) : name node in having clause.
* arg (in) : pt_non_groupby_col_info
* continue_walk (in) : continue walk.
*
* NOTE: Make sure to set has_non_groupby_col to false before calling
* parser_walk_tree.
*/
PT_NODE *
pt_has_non_groupby_column_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NON_GROUPBY_COL_INFO *info = NULL;
PT_NODE *groupby_p = NULL;
if (arg == NULL)
{
assert (false);
return node;
}
info = (PT_NON_GROUPBY_COL_INFO *) arg;
groupby_p = info->groupby;
if (node == NULL || groupby_p == NULL)
{
assert (false);
return node;
}
if (!PT_IS_NAME_NODE (node))
{
return node;
}
if (node->info.name.correlation_level > 0)
{
return node;
}
for (; groupby_p; groupby_p = groupby_p->next)
{
if (!(PT_IS_SORT_SPEC_NODE (groupby_p) && PT_IS_NAME_NODE (groupby_p->info.sort_spec.expr)))
{
continue;
}
if (pt_name_equal (parser, groupby_p->info.sort_spec.expr, node))
{
return node;
}
}
/* the name node is not associated with groupby columns. */
info->has_non_groupby_col = true;
*continue_walk = PT_STOP_WALK;
return node;
}
static DB_DEFAULT_EXPR_TYPE
parse_default_expr_type (const char *str, const int str_size, int *next_len)
{
if (str_size < 4)
{
*next_len = 0;
return DB_DEFAULT_NONE;
}
switch (str[0])
{
case 'S':
if (str_size >= 13 && strncmp (str, "SYS_TIMESTAMP", 13) == 0)
{
*next_len = 13;
return DB_DEFAULT_SYSTIMESTAMP;
}
if (str_size >= 12 && strncmp (str, "SYS_DATETIME", 12) == 0)
{
*next_len = 12;
return DB_DEFAULT_SYSDATETIME;
}
if (str_size >= 8)
{
if (strncmp (str, "SYS_DATE", 8) == 0)
{
*next_len = 8;
return DB_DEFAULT_SYSDATE;
}
if (strncmp (str, "SYS_TIME", 8) == 0)
{
*next_len = 8;
return DB_DEFAULT_SYSTIME;
}
}
break;
case 'C':
if (str_size >= 17 && strncmp (str, "CURRENT_TIMESTAMP", 17) == 0)
{
*next_len = 17;
return DB_DEFAULT_CURRENTTIMESTAMP;
}
if (str_size >= 16 && strncmp (str, "CURRENT_DATETIME", 16) == 0)
{
*next_len = 16;
return DB_DEFAULT_CURRENTDATETIME;
}
if (str_size >= 12)
{
if (strncmp (str, "CURRENT_DATE", 12) == 0)
{
*next_len = 12;
return DB_DEFAULT_CURRENTDATE;
}
if (strncmp (str, "CURRENT_TIME", 12) == 0)
{
*next_len = 12;
return DB_DEFAULT_CURRENTTIME;
}
if (strncmp (str, "CURRENT_USER", 12) == 0)
{
*next_len = 12;
return DB_DEFAULT_CURR_USER;
}
}
break;
case 'U':
if (str_size >= 16 && strncmp (str, "UNIX_TIMESTAMP()", 16) == 0)
{
*next_len = 16;
return DB_DEFAULT_UNIX_TIMESTAMP;
}
if (str_size >= 6 && strncmp (str, "USER()", 6) == 0)
{
*next_len = 6;
return DB_DEFAULT_USER;
}
if (str_size >= 4 && strncmp (str, "USER", 4) == 0)
{
*next_len = 4;
return DB_DEFAULT_CURR_USER;
}
break;
}
*next_len = 0;
return DB_DEFAULT_NONE;
}
/*
* pt_get_default_expression_from_string () - get default value from string
* return : error code or NO_ERROR
*
* parser (in) : parser context
* str (in) : default expression string
* str_size (in) : default expression string size
* default_expr (out) : default expression
*/
void
pt_get_default_expression_from_string (PARSER_CONTEXT * parser, const char *str, const int str_size,
DB_DEFAULT_EXPR * default_expr)
{
assert (parser != NULL && default_expr != NULL);
assert (str != NULL && str_size > 0);
classobj_initialize_default_expr (default_expr);
std::string expr_str (str, str_size);
int curr_idx = 0;
int curr_len = str_size;
const int to_char_size = sizeof ("TO_CHAR(") - 1;
if (str_size > to_char_size && strncmp (str, "TO_CHAR(", to_char_size) == 0)
{
curr_idx += to_char_size;
curr_len -= to_char_size;
default_expr->default_expr_op = T_TO_CHAR;
}
int parsed_len;
default_expr->default_expr_type = parse_default_expr_type (&str[curr_idx], curr_len, &parsed_len);
curr_idx += parsed_len;
curr_len -= parsed_len;
if (default_expr->default_expr_op == T_TO_CHAR)
{
// find next '\''
const char *formatted_string = strchr (&str[curr_idx], '\'') + 1;
// get remaining length before the last '\''
int remaining_len = strchr (formatted_string, '\'') - formatted_string;
default_expr->default_expr_format = strndup (formatted_string, remaining_len);
}
}
/*
* pt_get_default_value_from_attrnode () - get default value from data default node
* return : error code or NO_ERROR
*
* parser (in) : parser context
* data_default_node (in) : attribute node
* default_expr (out) : default expression
*/
void
pt_get_default_expression_from_data_default_node (PARSER_CONTEXT * parser, PT_NODE * data_default_node,
DB_DEFAULT_EXPR * default_expr)
{
PT_NODE *pt_default_expr = NULL;
DB_VALUE *db_value_default_expr_format = NULL;
assert (parser != NULL && default_expr != NULL);
classobj_initialize_default_expr (default_expr);
if (data_default_node != NULL)
{
assert (data_default_node->node_type == PT_DATA_DEFAULT);
default_expr->default_expr_type = data_default_node->info.data_default.default_expr_type;
pt_default_expr = data_default_node->info.data_default.default_value;
if (pt_default_expr && pt_default_expr->node_type == PT_EXPR)
{
if (pt_default_expr->info.expr.op == PT_TO_CHAR)
{
default_expr->default_expr_op = T_TO_CHAR;
assert (pt_default_expr->info.expr.arg2 != NULL
&& pt_default_expr->info.expr.arg2->node_type == PT_VALUE);
if (PT_IS_CHAR_STRING_TYPE (pt_default_expr->info.expr.arg2->type_enum))
{
db_value_default_expr_format = pt_value_to_db (parser, pt_default_expr->info.expr.arg2);
default_expr->default_expr_format = db_get_string (db_value_default_expr_format);
}
}
}
}
}
/*
* pt_has_name_oid () - Check whether the node is oid name
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in):
*/
PT_NODE *
pt_has_name_oid (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *has_name_oid = (bool *) arg;
switch (node->node_type)
{
case PT_NAME:
if (PT_IS_OID_NAME (node))
{
*has_name_oid = true;
*continue_walk = PT_STOP_WALK;
}
break;
case PT_DATA_TYPE:
if (node->type_enum == PT_TYPE_OBJECT)
{
*has_name_oid = true;
*continue_walk = PT_STOP_WALK;
}
break;
default:
break;
}
return node;
}
PT_NODE *
pt_set_user_specified_name (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
const char *dot = NULL;
const char *original_name = NULL;
const char *resolved_name = NULL;
char downcase_resolved_name[DB_MAX_USER_LENGTH] = { '\0' };
const char *user_specified_name = NULL;
assert (continue_walk != NULL);
if (parser == NULL || node == NULL)
{
PT_ERROR (parser, node, "Invalid arguments.");
return NULL;
}
if (pt_has_error (parser))
{
return node;
}
switch (node->node_type)
{
case PT_NAME:
if (PT_NAME_INFO_IS_FLAGED (node, PT_NAME_INFO_USER_SPECIFIED) || node->info.name.meta_class == PT_META_CLASS)
{
original_name = node->info.name.original;
resolved_name = node->info.name.resolved;
}
else
{
return node;
}
break;
case PT_EXPR:
if (PT_IS_SERIAL (node->info.expr.op))
{
if (PT_IS_DOT_NODE (node->info.expr.arg1)
&& PT_IS_NAME_NODE (node->info.expr.arg1->info.dot.arg1)
&& PT_IS_NAME_NODE (node->info.expr.arg1->info.dot.arg2))
{
PT_NODE *owner = node->info.expr.arg1->info.dot.arg1;
PT_NODE *name = node->info.expr.arg1->info.dot.arg2;
original_name = name->info.name.original;
resolved_name = owner->info.name.original;
}
else if (PT_IS_NAME_NODE (node->info.expr.arg1))
{
PT_NODE *name = node->info.expr.arg1;
original_name = name->info.name.original;
resolved_name = name->info.name.resolved;
}
else
{
return node;
}
}
else
{
return node;
}
break;
case PT_ALTER_SYNONYM:
{
const char *synonym_owner_name = NULL;
assert (PT_SYNONYM_NAME (node) != NULL);
synonym_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_NAME (node));
assert (synonym_owner_name != NULL);
PT_SYNONYM_OWNER_NAME (node) = pt_name (parser, synonym_owner_name);
/* If only the comment is changed, PT_SYNONYM_TARGET_NAME (node) can be NULL. */
if (PT_SYNONYM_TARGET_NAME (node) != NULL)
{
const char *target_owner_name = NULL;
if (PT_SYNONYM_IS_DBLINKED (node))
{
target_owner_name = synonym_owner_name;
}
else
{
target_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_TARGET_NAME (node));
}
/* When processing PT_NAME, resolved_name is prefixed to original_name.
* If original_name is the name of a system class/vclass, resolved_name is not prefixed to original_name. */
if (target_owner_name == NULL
&& sm_check_system_class_by_name (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (node))) == true)
{
PT_SYNONYM_TARGET_OWNER_NAME (node) = pt_name (parser, "dba");
}
else
{
assert (target_owner_name != NULL);
PT_SYNONYM_TARGET_OWNER_NAME (node) = pt_name (parser, target_owner_name);
}
}
return node;
}
// break;
case PT_CREATE_SYNONYM:
{
const char *synonym_owner_name = NULL;
const char *target_owner_name = NULL;
assert (PT_SYNONYM_NAME (node) != NULL);
assert (PT_SYNONYM_TARGET_NAME (node) != NULL);
synonym_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_NAME (node));
assert (synonym_owner_name != NULL);
PT_SYNONYM_OWNER_NAME (node) = pt_name (parser, synonym_owner_name);
if (PT_SYNONYM_IS_DBLINKED (node))
{
target_owner_name = synonym_owner_name;
}
else
{
target_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_TARGET_NAME (node));
}
/* When processing PT_NAME, resolved_name is prefixed to original_name.
* If original_name is the name of a system class/vclass, resolved_name is not prefixed to original_name. */
if (target_owner_name == NULL
&& sm_check_system_class_by_name (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (node))) == true)
{
PT_SYNONYM_TARGET_OWNER_NAME (node) = pt_name (parser, "dba");
}
else
{
assert (target_owner_name != NULL);
PT_SYNONYM_TARGET_OWNER_NAME (node) = pt_name (parser, target_owner_name);
}
return node;
}
// break;
case PT_DROP_SYNONYM:
{
const char *synonym_owner_name = NULL;
assert (PT_SYNONYM_NAME (node) != NULL);
synonym_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_NAME (node));
assert (synonym_owner_name != NULL);
PT_SYNONYM_OWNER_NAME (node) = pt_name (parser, synonym_owner_name);
return node;
}
// break;
case PT_RENAME_SYNONYM:
{
const char *old_synonym_owner_name = NULL;
const char *new_synonym_owner_name = NULL;
assert (PT_SYNONYM_OLD_NAME (node) != NULL);
assert (PT_SYNONYM_NEW_NAME (node) != NULL);
old_synonym_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_OLD_NAME (node));
assert (old_synonym_owner_name != NULL);
PT_SYNONYM_OLD_OWNER_NAME (node) = pt_name (parser, old_synonym_owner_name);
new_synonym_owner_name = pt_get_qualifier_name (parser, PT_SYNONYM_NEW_NAME (node));
assert (new_synonym_owner_name != NULL);
PT_SYNONYM_NEW_OWNER_NAME (node) = pt_name (parser, new_synonym_owner_name);
return node;
}
// break;
case PT_CREATE_ENTITY:
{
bool is_dba_group_member = au_is_dba_group_member (Au_user);
if (sm_check_system_class_by_name (PT_NAME_ORIGINAL (PT_CREATE_ENTITY_NAME (node))) && !is_dba_group_member)
{
int error = NO_ERROR;
ERROR_SET_ERROR_1ARG (error, ER_AU_DBA_ONLY, "create system class/vclass");
PT_ERRORc (parser, node, er_msg ());
*continue_walk = PT_STOP_WALK;
}
return node;
}
// break;
case PT_RENAME:
{
if (sm_check_system_class_by_name (PT_NAME_ORIGINAL (PT_RENAME_NEW_NAME (node))))
{
PT_ERROR (parser, node, "It is not allowed to be renamed to the system class name.");
*continue_walk = PT_STOP_WALK;
}
return node;
}
// break;
default:
return node;
}
// *INDENT-OFF*
assert ((node->node_type == PT_NAME && (PT_NAME_INFO_IS_FLAGED (node, PT_NAME_INFO_USER_SPECIFIED) || node->info.name.meta_class == PT_META_CLASS))
|| (node->node_type == PT_EXPR && PT_IS_SERIAL (node->info.expr.op)));
// *INDENT-ON*
assert (original_name && original_name[0] != '\0');
/* DBLink remote SQL: do not merge current schema into SERIAL names that were unqualified in SQL.
* path_id_list (owner.serial.nextval) is PT_DOT_ without USER_SPECIFIED — still explicit; do not strip here.
* object_name sets resolved (qualifier) but may not set USER_SPECIFIED — do not strip when resolved is set.
* pt_compile/name binding has not run yet, so unqualified identifiers still have resolved == NULL. */
if (parser->flag.dblink_skip_implicit_serial_qualifier && node->node_type == PT_EXPR
&& PT_IS_SERIAL (node->info.expr.op))
{
if (PT_IS_NAME_NODE (node->info.expr.arg1))
{
PT_NODE *nm = node->info.expr.arg1;
if (!PT_NAME_INFO_IS_FLAGED (nm, PT_NAME_INFO_USER_SPECIFIED)
&& (nm->info.name.resolved == NULL || nm->info.name.resolved[0] == '\0'))
{
nm->info.name.resolved = NULL;
return node;
}
}
}
if (strchr (original_name, '.'))
{
/* It is already user_specified_name. */
return node;
}
if (strlen (original_name) >= DB_MAX_IDENTIFIER_LENGTH - DB_MAX_SCHEMA_LENGTH)
{
PT_ERRORf2 (parser, node,
"Object name [%s] not allowed. It cannot exceed %d bytes.",
pt_short_print (parser, node), (DB_MAX_IDENTIFIER_LENGTH - DB_MAX_USER_LENGTH) - 1);
*continue_walk = PT_STOP_WALK;
return node;
}
/* In the system_class, user_specified_name does not include user_name.
* So, info.name.original is different for each case below.
*
* original_name resolve_name user_specified_name
* -------------------------------------------------------------------------------
* 1. common_class_name && NULL -> current_user_name.common_class_name
* 2. common_class_name && common_user_name -> common_user_name.common_class_name
* 3. common_class_name && dba_user_name -> dba_user_name.common_class_name
* 4. system_class_name && NULL -> system_class_name
* 5. system_class_name && common_user_name -> common_user_name.system_class_name -> error
* 6. system_class_name && dba_user_name -> dba_user_name.system_class_name -> error
*
* In case 5, raises an error to inform the user of an incorrect customization.
*/
if (node->node_type == PT_NAME && sm_check_system_class_by_name (original_name))
{
/* In case 5, 6 */
if (resolved_name != NULL)
{
PT_ERROR (parser, node, "It is not allowed to specify an owner in the system class name.");
*continue_walk = PT_STOP_WALK;
return node;
}
/* resolved_name == NULL */
/* Skip in case 4 */
return node;
}
if (resolved_name == NULL || resolved_name[0] == '\0')
{
resolved_name = sc_current_schema_name ();
}
else if (intl_identifier_lower_string_size (resolved_name) >= DB_MAX_USER_LENGTH)
{
PT_ERRORf2 (parser, node,
"User name [%s] not allowed. It cannot exceed %d bytes.", resolved_name, DB_MAX_USER_LENGTH);
*continue_walk = PT_STOP_WALK;
return node;
}
intl_identifier_lower (resolved_name, downcase_resolved_name);
/* In case 1, 2, 3 */
user_specified_name = pt_append_string (parser, downcase_resolved_name, ".");
user_specified_name = pt_append_string (parser, user_specified_name, original_name);
assert (intl_identifier_lower_string_size (user_specified_name) < DB_MAX_IDENTIFIER_LENGTH);
if (PT_IS_NAME_NODE (node))
{
node->info.name.original = user_specified_name;
node->info.name.resolved = NULL;
}
else
{
assert (PT_IS_EXPR_NODE (node) && PT_IS_SERIAL (node->info.expr.op));
if (PT_IS_DOT_NODE (node->info.expr.arg1))
{
node->info.expr.arg1->info.dot.arg2->info.name.original = user_specified_name;
node->info.expr.arg1->info.dot.arg2->info.name.resolved = NULL;
parser_free_tree (parser, node->info.expr.arg1->info.dot.arg1);
node->info.expr.arg1 = node->info.expr.arg1->info.dot.arg2;
}
else
{
assert (PT_IS_NAME_NODE (node->info.expr.arg1));
node->info.expr.arg1->info.name.original = user_specified_name;
node->info.expr.arg1->info.name.resolved = NULL;
}
}
return node;
}
/*
* pt_get_qualifier_name() - If the name is a user-specified name, get the user name.
* return : user name or NULL on error
* parser (in) : parser context
* node (in) : node of type PT_NAME
*/
const char *
pt_get_qualifier_name (PARSER_CONTEXT * parser, PT_NODE * node)
{
const char *name = NULL;
char qualifier_name[DB_MAX_USER_LENGTH] = { '\0' };
if (parser == NULL || node == NULL)
{
return NULL;
}
if (PT_IS_NAME_NODE (node) == false)
{
return NULL;
}
if (PT_NAME_ORIGINAL (node) == NULL || (PT_NAME_ORIGINAL (node))[0] == '\0')
{
return NULL;
}
if (sm_qualifier_name (PT_NAME_ORIGINAL (node), qualifier_name, DB_MAX_USER_LENGTH) == NULL)
{
return PT_NAME_RESOLVED (node);
}
return pt_append_string (parser, NULL, qualifier_name);
}
/*
* pt_get_name_with_qualifier_removed() - If the name has a qualifier name, remove it.
* return : name with qualifier name removed
* name (in) : user-specified name or object name
*/
const char *
pt_get_name_with_qualifier_removed (const char *name)
{
return sm_remove_qualifier_name (name);
}
/*
* pt_get_name_without_current_user_name() - If the name is a user-specified name and the specified user is
* the current user, return only the object name.
* return : user name or NULL on error
* parser (in) : parser context
* node (in) : node of type PT_NAME
*/
const char *
pt_get_name_without_current_user_name (const char *name)
{
const char *dot = NULL;
char qualifier_name[DB_MAX_USER_LENGTH];
int qualifier_name_len;
const char *current_schema_name = NULL;
const char *object_name = NULL;
int error = NO_ERROR;
if (name == NULL || name[0] == '\0')
{
return name;
}
/* Find the ending position of the user-specified name. */
dot = strchr (name, '.');
/* If the name is not a user-specified name, it is returned as is. */
if (dot == NULL)
{
/*
* e.g. name: object_name
* object_name: object_name
*/
return name;
}
/* Length of user-specified name. */
qualifier_name_len = STATIC_CAST (int, dot - name);
/* If it exceeds DB_MAX_USER_LENGTH, it is not a user-specified name. */
if (qualifier_name_len >= DB_MAX_USER_LENGTH)
{
return name;
}
/* Copy the user-specified name to the buffer for comparison with the current schema name. */
memcpy (qualifier_name, name, qualifier_name_len);
qualifier_name[qualifier_name_len] = '\0';
current_schema_name = sc_current_schema_name ();
if (intl_identifier_casecmp (qualifier_name, current_schema_name) == 0)
{
/*
* e.g. name: current_schema_name.object_name
* object_name: object_name
*/
object_name = dot + 1;
}
else
{
/*
* e.g. name: other_user_name.object_name
* object_name: other_user_name.object_name
*/
object_name = name;
}
return object_name;
}
static PT_NODE *
pt_mk_spec_derived_dblink_table (PARSER_CONTEXT * parser, PT_NODE * from_tbl)
{
PT_SPEC_INFO *class_spec_info = &from_tbl->info.spec;
PT_NODE *derived_spec;
PT_NODE *new_range_var;
PT_NODE *dbl_col = NULL;
if ((derived_spec = parser_new_node (parser, PT_DBLINK_TABLE)) == NULL)
{
PT_ERRORmf (parser, derived_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
return NULL;
}
if ((new_range_var = parser_new_node (parser, PT_NAME)) == NULL)
{
PT_ERRORmf (parser, derived_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
parser_free_node (parser, derived_spec);
return NULL;
}
parser->custom_print |= PT_PRINT_SUPPRESS_FOR_DBLINK;
derived_spec->info.dblink_table.remote_table_name = NULL;
if (class_spec_info->entity_name->info.name.resolved)
{
derived_spec->info.dblink_table.remote_table_name =
pt_append_string (parser, derived_spec->info.dblink_table.remote_table_name,
class_spec_info->entity_name->info.name.resolved);
derived_spec->info.dblink_table.remote_table_name =
pt_append_string (parser, derived_spec->info.dblink_table.remote_table_name, ".");
}
derived_spec->info.dblink_table.remote_table_name =
pt_append_string (parser, derived_spec->info.dblink_table.remote_table_name,
class_spec_info->entity_name->info.name.original);
parser->custom_print &= ~PT_PRINT_SUPPRESS_FOR_DBLINK;
assert (class_spec_info->remote_server_name->node_type == PT_NAME);
derived_spec->info.dblink_table.is_name = true;
derived_spec->info.dblink_table.conn = class_spec_info->remote_server_name;
if (class_spec_info->remote_server_name->next)
{
derived_spec->info.dblink_table.owner_name = class_spec_info->remote_server_name->next;
class_spec_info->remote_server_name->next = NULL;
}
class_spec_info->remote_server_name = NULL;
// alias table_name
PARSER_VARCHAR *var_buf = 0;
if (class_spec_info->range_var)
{ /* alias table name */
var_buf = pt_print_bytes (parser, class_spec_info->range_var);
}
else
{
// from test_tbl@srv, test_tbl
/* Should be unique.
* What if the remote table and local table name are the same?
* In the case of "<server_name>_<table_naem>", it is necessary to review the length limitation.
*/
var_buf = pt_print_bytes (parser, class_spec_info->entity_name);
}
new_range_var->info.name.original = pt_append_string (parser, NULL, (char *) var_buf->bytes);
derived_spec->info.dblink_table.qstr = class_spec_info->entity_name;;
class_spec_info->entity_name = NULL;
derived_spec->info.dblink_table.qstr->next = class_spec_info->range_var;
class_spec_info->range_var = NULL;
derived_spec->info.dblink_table.cols = NULL;
from_tbl->info.spec.range_var = new_range_var;
from_tbl->info.spec.derived_table = derived_spec;
from_tbl->info.spec.derived_table_type = PT_DERIVED_DBLINK_TABLE;
return from_tbl;
}
static PT_NODE *
pt_get_server_name_list (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PARSER_VARCHAR *vq = NULL;
char *name_ptr = NULL;
char *owner_ptr = NULL;
SERVER_NAME_LIST *snl = (SERVER_NAME_LIST *) arg;
PT_NODE *new_name, *new_owner;
*continue_walk = PT_CONTINUE_WALK;
assert (continue_walk != NULL);
if (node->node_type != PT_SPEC)
{
return node;
}
if (node->info.spec.remote_server_name == NULL)
{
if (node->info.spec.derived_table)
{
if (node->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE)
{
snl->server_cnt++;
snl->has_dblink_query = true;
}
else
{
PT_NODE *derived = node->info.spec.derived_table;
switch (derived->node_type)
{
case PT_SELECT:
parser_walk_tree (parser, derived->info.query.q.select.from, pt_get_server_name_list, snl, NULL,
NULL);
break;
case PT_UNION:
case PT_INTERSECTION:
case PT_DIFFERENCE:
parser_walk_tree (parser, derived->info.query.q.union_.arg1->info.query.q.select.from,
pt_get_server_name_list, snl, NULL, NULL);
parser_walk_tree (parser, derived->info.query.q.union_.arg2->info.query.q.select.from,
pt_get_server_name_list, snl, NULL, NULL);
break;
default:
snl->local_cnt++;
}
return node;
}
}
else
{
snl->local_cnt++;
return node;
}
}
if (node->info.spec.remote_server_name)
{
name_ptr = (char *) node->info.spec.remote_server_name->info.name.original;
if (node->info.spec.remote_server_name->next)
{
owner_ptr = (char *) node->info.spec.remote_server_name->next->info.name.original;
}
}
else
{
if (node->info.spec.derived_table->info.dblink_table.conn)
{
name_ptr = (char *) node->info.spec.derived_table->info.dblink_table.conn->info.name.original;
if (node->info.spec.derived_table->info.dblink_table.owner_name)
{
owner_ptr = (char *) node->info.spec.derived_table->info.dblink_table.owner_name->info.name.original;
}
}
}
snl->server_cnt++;
for (int i = 0; i < snl->server_node_cnt; i++)
{
int node_cnt = 0;
if (name_ptr == NULL || strcasecmp (snl->server[i]->info.name.original, name_ptr) != 0)
{
node_cnt++;
}
if (owner_ptr == NULL && snl->server[i]->next == NULL)
{
snl->server_node_cnt += node_cnt;
return node;
}
if (owner_ptr && snl->server[i]->next)
{
if (strcasecmp (snl->server[i]->next->info.name.original, owner_ptr) != 0)
{
node_cnt++;
}
snl->server_node_cnt += node_cnt;
return node;
}
}
if (name_ptr != NULL)
{
new_name = parser_new_node (parser, PT_NAME);
new_name->info.name.original = pt_append_string (parser, NULL, name_ptr);
if (owner_ptr)
{
new_owner = parser_new_node (parser, PT_NAME);
new_owner->info.name.original = pt_append_string (parser, NULL, owner_ptr);
new_name->next = new_owner;
vq = pt_append_nulstring (parser, vq, owner_ptr);
vq = pt_append_bytes (parser, vq, ".", 1);
}
vq = pt_append_nulstring (parser, vq, name_ptr);
snl->len[snl->server_node_cnt] = (int) strlen ((char *) vq->bytes);
snl->server_full_name[snl->server_node_cnt] = (char *) vq->bytes;
snl->server[snl->server_node_cnt] = new_name;
snl->server_node_cnt++;
}
return node;
}
#if defined (ENABLE_UNUSED_FUNCTION)
static char *
skip_comment (char *ps)
{
while (*ps)
{
if (ps[0] == '*' && ps[1] == '/')
{
ps += 2;
break;
}
ps++;
}
return ps;
}
#endif
#if defined (ENABLE_UNUSED_FUNCTION)
char *
read_id (char *ps, char end_char)
{
while (*ps != end_char)
{
ps++;
}
return ((*ps == end_char) ? (ps + 1) : ps);
}
#endif
#if defined (ENABLE_UNUSED_FUNCTION)
static char *
read_string (char *ps, char end_char, bool no_escape)
{
if (no_escape)
{
while (*ps)
{
if (ps[0] == end_char)
{
if (ps[1] != end_char)
{
return ps + 1;
}
ps++;
}
ps++;
}
}
else
{
while (*ps)
{
if (ps[0] == end_char)
{
if (ps[1] != end_char)
{
return ps + 1;
}
ps++;
}
else if (ps[0] == '\\')
{
switch (ps[1])
{
case '\'':
case '"':
case 'n':
case 'r':
case 't':
case '\\':
case '%':
case '_':
ps++;
break;
default:
break;
}
}
ps++;
}
}
return ps;
}
#endif
#if defined (ENABLE_UNUSED_FUNCTION)
static char *
find_circle_at_char (bool ansi_quotes, bool no_escape, char *ps)
{
while (*ps)
{
switch (*ps)
{
case '@':
{
return ps;
}
break;
case '[':
ps = read_id (ps + 1, ']');
break;
case '`':
ps = read_id (ps + 1, '`');
break;
case '"':
ps = (ansi_quotes ? read_id (ps + 1, '"') : read_string (ps + 1, '"', no_escape));
break;
case '\'':
ps = read_string (ps + 1, '\'', no_escape);
break;
case '/':
case '-':
if (ps[0] == ps[1])
{
ps += 2;
while (*ps)
{
ps++;
if (ps[-1] == '\n')
{
break;
}
}
}
else if (ps[0] == '/' && ps[1] == '*')
{
ps = skip_comment (ps + 2);
}
break;
default:
ps++;
break;
}
}
return NULL;
}
#endif
#if defined (ENABLE_UNUSED_FUNCTION)
static PARSER_VARCHAR *
pt_make_remote_query (PARSER_CONTEXT * parser, char *sql_user_text, SERVER_NAME_LIST * snl)
{
PARSER_VARCHAR *pvc = NULL;
char *ps, *pt, *t;
int adjust_pos = 1;
bool ansi_quotes = prm_get_bool_value (PRM_ID_ANSI_QUOTES);
bool no_escape = prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES);
ps = sql_user_text;
if (snl->server_node_cnt > 0)
{
int i, idx;
int zidx[2] = { 0, 1 };
if (snl->server_node_cnt == 2 && snl->len[0] < snl->len[1])
{
zidx[0] = 1;
zidx[1] = 0;
}
assert (ps != NULL);
while (*ps)
{
t = find_circle_at_char (ansi_quotes, no_escape, ps);
if (!t)
{
break;
}
pvc = pt_append_bytes (parser, pvc, ps, (t - ps));
while (char_isspace (t[1]))
{
t++;
}
if (t[1] == '[' || t[1] == '`' || (ansi_quotes && t[1] == '"'))
{
adjust_pos = 2;
t++;
}
else
{
adjust_pos = 1;
}
for (i = 0; i < snl->server_node_cnt; i++)
{
idx = zidx[i];
if (strncasecmp (t + 1, snl->server_full_name[idx], snl->len[idx]) == 0)
{
char ch = t[snl->len[idx] + adjust_pos];
if (char_isspace (ch) || ch == ',' || ch == ';' || ch == '(' || ch == ')')
{
break;
}
}
}
assert (i < snl->server_node_cnt);
ps = t + snl->len[idx] + adjust_pos;
}
}
pvc = pt_append_nulstring (parser, pvc, ps);
pt = (char *) pvc->bytes;
t = pt + (pvc->length - 1);
while (t > pt)
{
if (*t != ' ' && *t != '\t' && *t != '\n' && *t != '\a')
{
break;
}
t--;
}
t[1] = '\0';
pvc->length = (int) (t - pt) + 1;
return pvc;
}
#endif
static int
pt_check_update_set (PARSER_CONTEXT * parser, PT_NODE * statement, int *local_upd, int *remote_upd)
{
int error = NO_ERROR;
int upd_cls_cnt = 0;
int remote = 0, local = 0;
PT_ASSIGNMENTS_HELPER ea;
PT_NODE *node = NULL, *assignments, *spec;
assignments = statement->info.update.assignment;
spec = statement->info.update.spec;
while (spec)
{
upd_cls_cnt++;
spec = spec->next;
}
spec = statement->info.update.spec;
pt_init_assignments_helper (parser, &ea, assignments);
while (pt_get_next_assignment (&ea))
{
char *tbl_name;
char *tbl_alias = NULL;
char *lhs_name;
bool found = false;
lhs_name = (char *) ea.lhs->info.name.original;
while (spec && !found)
{
if (spec->info.spec.entity_name)
{
tbl_name = (char *) spec->info.spec.entity_name->info.name.original;
if (spec->info.spec.range_var)
{
tbl_alias = (char *) spec->info.spec.range_var->info.name.original;
}
if ((tbl_name && strcmp (tbl_name, lhs_name) == 0) || (tbl_alias && strcmp (tbl_alias, lhs_name) == 0)
|| upd_cls_cnt == 1)
{
found = true;
if (spec->info.spec.remote_server_name)
{
remote++;
}
else
{
local++;
}
}
}
spec = spec->next;
}
if (!found && remote > 0 && local > 0)
{
/* remote & local updates are mixed */
/* the update would be ambiguous */
PT_ERRORmf (parser, assignments, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_AMBIGUOUS_REF_TO, lhs_name);
error = ER_FAILED;
break;
}
}
*remote_upd = remote;
*local_upd = local;
return error;
}
static PT_NODE *
pt_convert_dblink_synonym (PARSER_CONTEXT * parser, PT_NODE * spec, void *is_insert, int *continue_walk)
{
char *class_name;
char target_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
MOP synonym_mop = NULL;
int error = NO_ERROR;
if (spec->node_type != PT_SPEC || spec->info.spec.remote_server_name != NULL || spec->info.spec.entity_name == NULL)
{
return spec;
}
/* If it is a synonym name, change it to the target name. */
class_name = (char *) spec->info.spec.entity_name->info.name.original;
if (class_name == NULL)
{
return spec;
}
synonym_mop = db_find_synonym (class_name);
if (synonym_mop != NULL)
{
char *r;
class_name = db_get_synonym_target_name (synonym_mop, target_name, DB_MAX_IDENTIFIER_LENGTH);
if (class_name == NULL)
{
*continue_walk = PT_STOP_WALK;
return spec;
}
if ((r = (char *) strchr (class_name, '@')) != NULL)
{
/* remote table */
char *synonym_name, *s;
/* for alias */
synonym_name = (char *) spec->info.spec.entity_name->info.name.original;
if ((s = (char *) strchr (synonym_name, '.')) != NULL)
{
synonym_name = s + 1;
}
*r = 0;
spec->info.spec.entity_name->info.name.original = pt_append_string (parser, NULL, class_name);
spec->info.spec.remote_server_name = parser_new_node (parser, PT_NAME);
spec->info.spec.remote_server_name->info.name.original = pt_append_string (parser, NULL, r + 1);
/* it's not necessary to alias name for INSERT statement's table name */
if (!is_insert && spec->info.spec.range_var == NULL)
{
spec->info.spec.range_var = parser_new_node (parser, PT_NAME);
spec->info.spec.range_var->info.name.original = pt_append_string (parser, NULL, synonym_name);
}
}
}
else
{
/* synonym_mop == NULL */
ASSERT_ERROR_AND_SET (error);
if (error == ER_SYNONYM_NOT_EXIST)
{
er_clear ();
error = NO_ERROR;
}
}
return spec;
}
static void
pt_convert_dblink_merge_query (PARSER_CONTEXT * parser, PT_NODE * node, SERVER_NAME_LIST * snl)
{
PT_NODE *target, *source;
bool remote_target = false;
target = node->info.merge.into;
source = node->info.merge.using_clause;
parser_walk_tree (parser, node, pt_convert_dblink_synonym, NULL, NULL, NULL);
if (pt_has_error (parser))
{
return;
}
if (target->info.spec.remote_server_name)
{
remote_target = true;
}
if (remote_target)
{
parser_walk_tree (parser, source, pt_get_server_name_list, snl, NULL, NULL);
if (snl->local_cnt > 0)
{
/* not allowed to merge multi-server */
PT_ERROR (parser, source, "dblink: multi-remote DML is not allowed");
}
else
{
/* only remote merge */
assert (snl->server_cnt > 0);
}
}
pt_convert_dblink_dml_query (parser, node, (int) (remote_target == false), (int) (remote_target == true), snl);
}
static void
pt_convert_dblink_insert_query (PARSER_CONTEXT * parser, PT_NODE * node, SERVER_NAME_LIST * snl)
{
PT_NODE *insert, *spec;
int remote_ins = 0;
bool is_insert = true;
parser_walk_tree (parser, node, pt_convert_dblink_synonym, &is_insert, NULL, NULL);
if (pt_has_error (parser))
{
return;
}
spec = node->info.insert.spec;
if (spec->info.spec.remote_server_name)
{
remote_ins = 1;
}
pt_convert_dblink_dml_query (parser, node, (remote_ins == 0), remote_ins, snl);
return;
}
static void
pt_convert_dblink_delete_query (PARSER_CONTEXT * parser, PT_NODE * node, SERVER_NAME_LIST * snl)
{
PT_NODE *target, *spec;
int remote_del = 0, local_del = 0;
parser_walk_tree (parser, node, pt_convert_dblink_synonym, NULL, NULL, NULL);
if (pt_has_error (parser))
{
return;
}
target = node->info.delete_.target_classes;
while (target)
{
for (spec = node->info.delete_.spec; spec; spec = spec->next)
{
char *a_name = NULL, *t_name, *r_name;
if (spec->info.spec.range_var)
{
a_name = (char *) spec->info.spec.range_var->info.name.original;
}
t_name = (char *) target->info.name.original;
r_name = (char *) target->info.name.resolved;
if (a_name)
{
/* checking alias for remote/local table */
if ((t_name && strcmp (a_name, t_name) == 0) || ((r_name && strcmp (a_name, r_name) == 0)))
{
if (spec->info.spec.remote_server_name)
{
remote_del++;
}
else
{
local_del++;
}
break;
}
}
else
{
if (spec->info.spec.entity_name && t_name
&& strcmp (spec->info.spec.entity_name->info.name.original, t_name) == 0)
{
if (spec->info.spec.remote_server_name)
{
remote_del++;
}
else
{
local_del++;
}
break;
}
}
}
if (spec == NULL)
{
/* not matched: error case */
PT_ERRORmf (parser, node->info.delete_.spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST,
target->info.name.original);
return;
}
target = target->next;
}
pt_convert_dblink_dml_query (parser, node, local_del, remote_del, snl);
return;
}
static void
pt_convert_dblink_update_query (PARSER_CONTEXT * parser, PT_NODE * node, SERVER_NAME_LIST * snl)
{
int local_upd = 0, remote_upd = 0;
int error;
parser_walk_tree (parser, node, pt_convert_dblink_synonym, NULL, NULL, NULL);
if (pt_has_error (parser))
{
return;
}
error = pt_check_update_set (parser, node, &local_upd, &remote_upd);
if (error != NO_ERROR)
{
return;
}
pt_convert_dblink_dml_query (parser, node, local_upd, remote_upd, snl);
return;
}
static PT_NODE *pt_convert_select (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *
pt_check_sub_query_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE *list, *spec;
PT_NODE *sub_sel = NULL;
SERVER_NAME_LIST *snl = (SERVER_NAME_LIST *) arg;
if (node->node_type != PT_SPEC)
{
return node;
}
list = node;
while (list)
{
if (list->info.spec.derived_table)
{
sub_sel = list->info.spec.derived_table;
}
if (list->info.spec.remote_server_name)
{
sub_sel = parser_new_node (parser, PT_SELECT);
sub_sel->info.query.q.select.list = parser_new_node (parser, PT_VALUE);
sub_sel->info.query.q.select.list->type_enum = PT_TYPE_STAR;
spec = parser_new_node (parser, PT_SPEC);
spec->info.spec.only_all = PT_ONLY;
spec->info.spec.meta_class = PT_CLASS;
spec->info.spec.remote_server_name = list->info.spec.remote_server_name;
spec->info.spec.entity_name = list->info.spec.entity_name;
list->info.spec.remote_server_name = NULL;
list->info.spec.entity_name = NULL;
sub_sel->info.query.q.select.from = spec;
list->info.spec.derived_table = sub_sel;
list->info.spec.derived_table_type = PT_IS_SUBQUERY;
}
if (sub_sel && sub_sel->node_type == PT_SELECT)
{
if (parser_walk_tree (parser, sub_sel, NULL, NULL, pt_convert_select, snl))
{
parser_walk_tree (parser, sub_sel, pt_check_dblink_query, NULL, NULL, NULL);
}
}
list = list->next;
}
return node;
}
static void
pt_convert_dblink_dml_query (PARSER_CONTEXT * parser, PT_NODE * node,
int local_upd, int remote_upd, SERVER_NAME_LIST * snl)
{
int i;
int tmp_server_cnt = snl->server_cnt;
unsigned int save_custom_print;
PT_NODE *sub_sel = NULL; /* for select sub-query */
PT_NODE *list = NULL; /* for insert select list */
PT_NODE *into_spec = NULL, *upd_spec = NULL, *server;
PARSER_VARCHAR *comment = NULL, *dml;
switch (node->node_type)
{
case PT_INSERT:
into_spec = node->info.insert.spec;
sub_sel = node->info.insert.value_clauses;
for (list = sub_sel->info.node_list.list; list; list = list->next)
{
if (local_upd > 0)
{
parser_walk_tree (parser, list, pt_check_sub_query_spec, snl, NULL, NULL);
}
else
{
parser_walk_tree (parser, list, pt_get_server_name_list, snl, NULL, NULL);
}
}
sub_sel = NULL;
break;
case PT_DELETE:
upd_spec = node->info.delete_.spec;
break;
case PT_UPDATE:
upd_spec = node->info.update.spec;
break;
case PT_MERGE:
into_spec = node->info.merge.into;
upd_spec = node->info.merge.using_clause;
break;
default:
assert (false);
}
if (local_upd > 0 && upd_spec)
{
parser_walk_tree (parser, node, pt_check_sub_query_spec, snl, NULL, NULL);
}
if (into_spec)
{
parser_walk_tree (parser, into_spec, pt_get_server_name_list, snl, NULL, NULL);
}
if (upd_spec)
{
parser_walk_tree (parser, upd_spec, pt_get_server_name_list, snl, NULL, NULL);
}
if (pt_has_error (parser))
{
return;
}
if (snl->local_cnt > 0 && remote_upd > 0)
{
PT_ERROR (parser, upd_spec ? upd_spec : into_spec, "dblink: local mixed remote DML is not allowed");
return;
}
if (snl->server_cnt == tmp_server_cnt || (local_upd > 0 && remote_upd == 0))
{
/* local update only */
return;
}
if (snl->has_dblink_query)
{
PT_ERROR (parser, upd_spec ? upd_spec : into_spec, "dblink: remote DML has DBLINK query is not allowed");
return;
}
if (snl->server_node_cnt >= 2 && remote_upd > 0)
{
PT_ERROR (parser, upd_spec ? upd_spec : into_spec, "dblink: multi-remote DML is not allowed");
return;
}
/*
** the query which has generic function is set flag.cannont_prepare to 1 by parser
** because the generic function might not be executed,
** However, the dblink DML should be prepared even though it has generic function
*/
node->flag.cannot_prepare = 0;
/*
** The target server must all be the same.
** Therefore, even if multiple tables are specified, only the first information is configured as PT_DBLINK_TABLE_DML.
** Postpone checking that "user.server" and "server" are the same.
*/
PT_NODE *ct = parser_new_node (parser, PT_DBLINK_TABLE_DML);
if (!ct)
{
PT_ERRORmf (parser, ct, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
return;
}
PT_NODE *val = parser_new_node (parser, PT_VALUE);
if (!val)
{
PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
return;
}
val->type_enum = PT_TYPE_CHAR;
val->info.value.string_type = ' ';
/*
It should be set custom print flag for excluding
server name from table-name@server-name for pure remote SQL.
It also should set flag not to convert to serial_next_value
or serial_current_value for ORACLE or other DBMS.
*/
save_custom_print = parser->custom_print;
switch (node->node_type)
{
case PT_INSERT:
comment = pt_append_bytes (parser, NULL, "/* DBLINK INSERT */ ", 20);
break;
case PT_UPDATE:
comment = pt_append_bytes (parser, NULL, "/* DBLINK UPDATE */ ", 20);
break;
case PT_DELETE:
comment = pt_append_bytes (parser, NULL, "/* DBLINK DELETE */ ", 20);
break;
case PT_MERGE:
comment = pt_append_bytes (parser, NULL, "/* DBLINK MERGE */ ", 19);
break;
default:
/* must not reach here */
assert (false);
}
parser->custom_print |=
PT_PRINT_SUPPRESS_SERVER_NAME | PT_PRINT_SUPPRESS_SERIAL_CONV | PT_PRINT_NO_HOST_VAR_INDEX |
PT_PRINT_SUPPRESS_FOR_DBLINK;
/* strip redundant aliases (alias == entity_name) from remote specs before printing;
* without this, "t1@srv1 t1" would be printed as "t1 t1" after server name suppression */
{
PT_NODE *s;
PT_NODE *spec_list;
const char *a_name, *e_name;
/* for update incuding merge node */
spec_list = upd_spec;
for (s = spec_list; s; s = s->next)
{
if (s->info.spec.range_var && s->info.spec.entity_name && s->info.spec.remote_server_name)
{
a_name = s->info.spec.range_var->info.name.original;
e_name = s->info.spec.entity_name->info.name.original;
if (a_name && e_name && strcasecmp (a_name, e_name) == 0)
{
s->info.spec.range_var = NULL;
}
}
}
/* for insert incuding merge node */
spec_list = into_spec;
for (s = spec_list; s; s = s->next)
{
if (s->info.spec.range_var && s->info.spec.entity_name && s->info.spec.remote_server_name)
{
a_name = s->info.spec.range_var->info.name.original;
e_name = s->info.spec.entity_name->info.name.original;
if (a_name && e_name && strcasecmp (a_name, e_name) == 0)
{
s->info.spec.range_var = NULL;
}
}
}
}
dml = pt_print_bytes (parser, node);
val->info.value.data_value.str = pt_append_bytes (parser, comment, (const char *) dml->bytes, dml->length);
parser->custom_print = save_custom_print;
PT_NODE_PRINT_VALUE_TO_TEXT (parser, val);
if (into_spec)
{
server = into_spec->info.spec.remote_server_name;
}
else
{
server = upd_spec->info.spec.remote_server_name;
}
if (server == NULL)
{
/* the subquery target in update query is not allowed */
PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UPDATE_DERIVED_TABLE);
return;
}
if (server->node_type == PT_DBLINK_TABLE_DML)
{
/* already converted */
return;
}
ct->info.dblink_table.is_name = true;
ct->info.dblink_table.conn = server;
if (server->next)
{
assert (server->next->node_type == PT_NAME);
ct->info.dblink_table.owner_name = server->next;
server->next = NULL;
}
ct->info.dblink_table.qstr = val;
for (i = 0; i < snl->server_node_cnt; i++)
{
if (snl->server_node_cnt != 1)
{
if (ct->info.dblink_table.owner_list == NULL && snl->server[i]->next)
{
ct->info.dblink_table.owner_list = snl->server[i]->next;
snl->server[i]->next = NULL;
}
}
if (snl->server[i]->next)
{
parser_free_node (parser, snl->server[i]->next);
}
parser_free_node (parser, snl->server[i]);
}
if (into_spec)
{
into_spec->info.spec.remote_server_name = ct;
pt_resolve_server_names (parser, into_spec);
}
if (upd_spec)
{
upd_spec->info.spec.remote_server_name = ct;
pt_resolve_server_names (parser, upd_spec);
}
return;
}
static bool
pt_convert_dblink_select_query (PARSER_CONTEXT * parser, PT_NODE * query_stmt, SERVER_NAME_LIST * snl)
{
bool has_dblink = false;
PT_QUERY_INFO *query = &query_stmt->info.query;
PT_NODE *from_tbl = query_stmt->info.query.q.select.from;
parser_walk_tree (parser, query_stmt, pt_convert_dblink_synonym, NULL, NULL, NULL);
parser_walk_tree (parser, from_tbl, pt_get_server_name_list, snl, NULL, NULL);
while (from_tbl)
{
if (from_tbl->info.spec.entity_name && from_tbl->info.spec.remote_server_name)
{
assert (from_tbl->info.spec.entity_name->node_type == PT_NAME);
from_tbl = pt_mk_spec_derived_dblink_table (parser, from_tbl);
has_dblink = true;
}
from_tbl = from_tbl->next;
}
return has_dblink;
}
static PT_NODE *
pt_convert_select (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
SERVER_NAME_LIST *snl = (SERVER_NAME_LIST *) arg;
assert (continue_walk && parser && node);
switch (node->node_type)
{
case PT_SELECT:
if (pt_convert_dblink_select_query (parser, node, snl))
{
parser_walk_tree (parser, node, pt_check_dblink_query, NULL, NULL, NULL);
}
break;
default:
break;
}
return node;
}
static PT_NODE *
pt_convert_dml (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
SERVER_NAME_LIST *snl = (SERVER_NAME_LIST *) arg;
assert (continue_walk && parser && node);
switch (node->node_type)
{
case PT_INSERT:
pt_convert_dblink_insert_query (parser, node, snl);
break;
case PT_DELETE:
pt_convert_dblink_delete_query (parser, node, snl);
break;
case PT_UPDATE:
pt_convert_dblink_update_query (parser, node, snl);
break;
case PT_MERGE:
pt_convert_dblink_merge_query (parser, node, snl);
break;
default:
break;
}
return node;
}
// *INDENT-OFF*
#define SET_DBLINK_HOST_VAR_COUNT(ptspec, var_cnt) do { \
if ((ptspec) && (ptspec)->info.spec.remote_server_name) \
{ \
(ptspec)->info.spec.remote_server_name->info.dblink_table.host_vars.count = (var_cnt); \
} \
} while(0)
// *INDENT-ON*
static PT_NODE *
pt_set_print_in_value_for_dblink (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) arg;
if (node->node_type == PT_EXPR && (node->info.expr.op == PT_IS_IN || node->info.expr.op == PT_IS_NOT_IN))
{
node->info.expr.arg2->flag.print_in_value_for_dblink = 1;
}
return node;
}
void
pt_rewrite_for_dblink (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
SERVER_NAME_LIST snl;
memset (&snl, 0x00, sizeof (SERVER_NAME_LIST));
parser->flag.dblink_skip_implicit_serial_qualifier = 0;
parser_walk_tree (parser, stmt, pt_set_print_in_value_for_dblink, NULL, NULL, NULL);
switch (stmt->node_type)
{
case PT_SCOPE:
/* for trigger action */
stmt = stmt->info.scope.stmt->info.trigger_action.expression;
if (stmt == NULL)
{
return;
}
[[fallthrough]];
case PT_INSERT:
case PT_DELETE:
case PT_UPDATE:
case PT_MERGE:
parser_walk_tree (parser, stmt, NULL, NULL, pt_convert_dml, &snl);
if (pt_has_error (parser))
{
return;
}
// Under the current implementation of DBLink, host variables cannot be idenfified at compile time
// due to the lack of schema information of the remote tables.
// Therefore, DBLink in Static SQL DML is prohibited for now.
// Note that this is not the case for Static SQL SELECT statements.
if (parser->flag.is_parsing_static_sql)
{
if (snl.has_dblink_query || snl.server_node_cnt > 0)
{
PT_ERROR (parser, stmt, "DBLink DML is not yet supported for PL/CSQL Static SQL.");
return;
}
}
break;
case PT_CREATE_TRIGGER:
/* for trigger create */
if (stmt->info.create_trigger.trigger_action)
{
PT_NODE *tr_action = stmt->info.create_trigger.trigger_action;
if (tr_action->info.trigger_action.expression)
{
parser_walk_tree (parser, tr_action->info.trigger_action.expression, NULL, NULL, pt_convert_dml, &snl);
}
if (pt_has_error (parser))
{
return;
}
}
break;
case PT_DIFFERENCE:
case PT_INTERSECTION:
case PT_UNION:
case PT_SELECT:
case PT_CREATE_ENTITY:
case PT_ALTER:
parser_walk_tree (parser, stmt, NULL, NULL, pt_convert_select, &snl);
if (snl.has_dblink_query || snl.server_node_cnt > 0)
{
parser->flag.dblink_skip_implicit_serial_qualifier = 1;
}
return;
default:
/* no action */
return;
}
if (snl.has_dblink_query || snl.server_node_cnt > 0)
{
parser->flag.dblink_skip_implicit_serial_qualifier = 1;
}
switch (stmt->node_type)
{
case PT_INSERT:
SET_DBLINK_HOST_VAR_COUNT (stmt->info.insert.spec, parser->host_var_count);
break;
case PT_DELETE:
SET_DBLINK_HOST_VAR_COUNT (stmt->info.delete_.spec, parser->host_var_count);
break;
case PT_UPDATE:
SET_DBLINK_HOST_VAR_COUNT (stmt->info.update.spec, parser->host_var_count);
break;
case PT_MERGE:
SET_DBLINK_HOST_VAR_COUNT (stmt->info.merge.into, parser->host_var_count);
break;
default:
break;
}
return;
}
extern PT_NODE *
pt_make_data_default_expr_node (PARSER_CONTEXT * parser, PT_NODE * expr)
{
PT_NODE *node = parser_new_node (parser, PT_DATA_DEFAULT);
if (node)
{
PT_NODE *def;
node->info.data_default.default_value = expr;
node->info.data_default.shared = PT_DEFAULT;
def = node->info.data_default.default_value;
if (def && def->node_type == PT_EXPR)
{
if (def->info.expr.op == PT_TO_CHAR)
{
if (def->info.expr.arg3)
{
bool has_user_lang = false;
bool dummy;
assert (def->info.expr.arg3->node_type == PT_VALUE);
(void) lang_get_lang_id_from_flag (def->info.expr.arg3->info.value.data_value.i, &dummy,
&has_user_lang);
if (has_user_lang)
{
PT_ERROR (parser, def->info.expr.arg3, "do not allow lang format in default to_char");
}
}
if (def->info.expr.arg1 && def->info.expr.arg1->node_type == PT_EXPR)
{
def = def->info.expr.arg1;
}
}
switch (def->info.expr.op)
{
case PT_SYS_TIME:
node->info.data_default.default_expr_type = DB_DEFAULT_SYSTIME;
break;
case PT_SYS_DATE:
node->info.data_default.default_expr_type = DB_DEFAULT_SYSDATE;
break;
case PT_SYS_DATETIME:
node->info.data_default.default_expr_type = DB_DEFAULT_SYSDATETIME;
break;
case PT_SYS_TIMESTAMP:
node->info.data_default.default_expr_type = DB_DEFAULT_SYSTIMESTAMP;
break;
case PT_CURRENT_TIME:
node->info.data_default.default_expr_type = DB_DEFAULT_CURRENTTIME;
break;
case PT_CURRENT_DATE:
node->info.data_default.default_expr_type = DB_DEFAULT_CURRENTDATE;
break;
case PT_CURRENT_DATETIME:
node->info.data_default.default_expr_type = DB_DEFAULT_CURRENTDATETIME;
break;
case PT_CURRENT_TIMESTAMP:
node->info.data_default.default_expr_type = DB_DEFAULT_CURRENTTIMESTAMP;
break;
case PT_USER:
node->info.data_default.default_expr_type = DB_DEFAULT_USER;
break;
case PT_CURRENT_USER:
node->info.data_default.default_expr_type = DB_DEFAULT_CURR_USER;
break;
case PT_UNIX_TIMESTAMP:
node->info.data_default.default_expr_type = DB_DEFAULT_UNIX_TIMESTAMP;
break;
default:
node->info.data_default.default_expr_type = DB_DEFAULT_NONE;
break;
}
}
else
{
node->info.data_default.default_expr_type = DB_DEFAULT_NONE;
}
}
return node;
}
PT_HINT_ENUM
pt_get_hint_from_query (PARSER_CONTEXT * parser, PT_NODE * query)
{
assert (query != NULL);
assert (pt_is_query (query));
switch (query->node_type)
{
case PT_SELECT:
return query->info.query.q.select.hint;
case PT_INTERSECTION:
case PT_DIFFERENCE:
case PT_UNION:
/* for set operations, get the hint from the query on the left side */
return pt_get_hint_from_query (parser, query->info.query.q.union_.arg1);
default:
return PT_HINT_NONE;
}
}
/*
* pt_count_name_nodes () - returns name node, count by reference
* return:
* parser(in):
* node(in): the node to check
* arg(in/out): count of name nodes
* continue_walk(in):
*/
PT_NODE *
pt_count_name_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int *cnt = (int *) arg;
if (node->node_type == PT_NAME)
{
(*cnt)++;
}
return node;
}