File view_transform.c¶
File List > cubrid > src > parser > view_transform.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.
*
*/
/*
* view_transform.c - Functions for the translation of virtual queries
*/
#ident "$Id$"
#include <assert.h>
#include "authenticate.h"
#include "view_transform.h"
#include "parser.h"
#include "parser_message.h"
#include "schema_manager.h"
#include "semantic_check.h"
#include "optimizer.h"
#include "execute_schema.h"
#include "dbi.h"
#include "object_accessor.h"
#include "object_primitive.h"
#include "locator_cl.h"
#include "virtual_object.h"
#include "dbtype.h"
#include "boot.h"
#define MAX_STACK_OBJECTS 500
#define PT_PUSHABLE_TERM(p) \
((p)->out.pushable && (p)->out.found && !(p)->out.others_found)
#define MAX_CYCLE 300
#define MQ_IS_OUTER_JOIN_SPEC(s) \
( \
((s)->info.spec.join_type == PT_JOIN_LEFT_OUTER \
|| (s)->info.spec.join_type == PT_JOIN_RIGHT_OUTER) || \
((s)->next && \
((s)->next->info.spec.join_type == PT_JOIN_LEFT_OUTER \
|| (s)->next->info.spec.join_type == PT_JOIN_RIGHT_OUTER)) \
)
#define MQ_IS_LEFT_JOIN_SPEC(s) \
( \
((s)->info.spec.join_type == PT_JOIN_LEFT_OUTER) || \
((s)->next && \
((s)->next->info.spec.join_type == PT_JOIN_LEFT_OUTER)) \
)
#define MQ_FIX_SPEC_ID(tmp_hint, hint, info) \
tmp_hint = hint; \
while (tmp_hint) \
{ \
if (tmp_hint->info.name.spec_id == info->old_id) \
{ \
tmp_hint->info.name.spec_id = info->new_id; \
} \
tmp_hint = tmp_hint->next; \
}
typedef enum
{ FIND_ID_INLINE_VIEW = 0, FIND_ID_VCLASS } FIND_ID_TYPE;
typedef struct find_id_info
{
struct
{ /* input section */
PT_NODE *spec;
PT_NODE *others_spec_list;
PT_NODE *attr_list;
PT_NODE *subquery;
} in;
FIND_ID_TYPE type;
struct
{ /* output section */
bool found;
bool others_found;
bool correlated_found;
bool pushable;
} out;
} FIND_ID_INFO;
typedef struct mq_bump_core_info
{
int match_level;
int increment;
}
MQ_BUMP_CORR_INFO;
typedef struct
{
UINTPTR old_id; /* spec id to replace in method name nodes */
UINTPTR new_id; /* spec it to replace it with */
} PT_RESOLVE_METHOD_NAME_INFO;
typedef struct check_pushable_info
{
bool check_query;
bool check_method;
bool query_found;
bool method_found;
} CHECK_PUSHABLE_INFO;
static unsigned int top_cycle = 0;
static DB_OBJECT *cycle_buffer[MAX_CYCLE];
typedef struct path_lambda_info PATH_LAMBDA_INFO;
struct path_lambda_info
{
PT_NODE lambda_name;
PT_NODE *lambda_expr;
UINTPTR spec_id;
PT_NODE *new_specs; /* for adding shared attr specs */
};
typedef struct exists_info EXISTS_INFO;
struct exists_info
{
PT_NODE *spec;
int referenced;
};
typedef struct pt_reset_select_spec_info PT_RESET_SELECT_SPEC_INFO;
struct pt_reset_select_spec_info
{
UINTPTR id;
PT_NODE **statement;
};
typedef struct replace_name_info REPLACE_NAME_INFO;
struct replace_name_info
{
PT_NODE *path;
UINTPTR spec_id;
PT_NODE *newspec; /* for new sharedd attr specs */
};
typedef struct spec_reset_info SPEC_RESET_INFO;
struct spec_reset_info
{
PT_NODE *statement;
PT_NODE **sub_paths;
PT_NODE *old_next;
};
typedef struct extra_specs_frame PT_EXTRA_SPECS_FRAME;
struct extra_specs_frame
{
struct extra_specs_frame *next;
PT_NODE *extra_specs;
};
typedef struct mq_lambda_arg MQ_LAMBDA_ARG;
struct mq_lambda_arg
{
PT_NODE *name_list;
PT_NODE *tree_list;
PT_EXTRA_SPECS_FRAME *spec_frames;
};
typedef struct set_names_info SET_NAMES_INFO;
struct set_names_info
{
DB_OBJECT *object;
UINTPTR id;
};
enum pushable_type
{
HAS_ERROR = 0,
NON_PUSHABLE = 1,
PUSHABLE = 2
};
typedef enum pushable_type PUSHABLE_TYPE;
static PT_NODE *mq_bump_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_bump_corr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_union_bump_correlation (PARSER_CONTEXT * parser, PT_NODE * left, PT_NODE * right);
static DB_AUTH mq_compute_authorization (DB_OBJECT * class_object);
static DB_AUTH mq_compute_query_authorization (PT_NODE * statement);
static void mq_set_union_query (PARSER_CONTEXT * parser, PT_NODE * statement, PT_MISC_TYPE is_union);
static PT_NODE *mq_flatten_union (PARSER_CONTEXT * parser, PT_NODE * statement);
static PT_NODE *mq_rewrite_agg_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_rewrite_agg_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static bool mq_conditionally_add_objects (PARSER_CONTEXT * parser, PT_NODE * flat, DB_OBJECT *** classes, int *index,
int *max);
static PT_UPDATABILITY mq_updatable_local (PARSER_CONTEXT * parser, PT_NODE * statement, DB_OBJECT *** classes, int *i,
int *max);
static PT_NODE *mq_substitute_select_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec,
PT_NODE * class_);
static PT_NODE *mq_substitute_select_for_inline_view (PARSER_CONTEXT * parser, PT_NODE * statement,
PT_NODE * query_spec, PT_NODE * derived_table);
static PT_NODE *mq_remove_select_list_for_inline_view (PARSER_CONTEXT * parser, PT_NODE * statement,
PT_NODE * derived_table, PT_NODE ** new_spec);
static PT_NODE *mq_substitute_inline_view_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement,
PT_NODE * subquery, PT_NODE * derived_spec, PT_NODE * order_by);
static PT_NODE *mq_substitute_spec_in_method_and_hints (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
int *continue_walk);
static PT_NODE *mq_substitute_subquery_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec,
PT_NODE * class_, PT_NODE * order_by, int what_for);
static PT_NODE *mq_substitute_subquery_list_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement,
PT_NODE * query_spec_list, PT_NODE * class_,
PT_NODE * order_by, int what_for);
static int mq_translatable_class (PARSER_CONTEXT * parser, PT_NODE * class_);
static int mq_is_union_translation (PARSER_CONTEXT * parser, PT_NODE * spec);
static int mq_check_authorization_path_entities (PARSER_CONTEXT * parser, PT_NODE * class_spec, int what_for);
static int mq_check_subqueries_for_prepare (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * subquery);
static PT_NODE *mq_translate_tree (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE * spec_list, PT_NODE * order_by,
int what_for);
static PT_NODE *mq_class_meth_corr_subq_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
int *continue_walk);
static bool mq_has_class_methods_corr_subqueries (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_check_pushable (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static bool pt_check_pushable_subquery_select_list (PARSER_CONTEXT * parser, PT_NODE * query, int pos);
static PT_NODE *pt_find_only_name_id (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static bool pt_check_pushable_term (PARSER_CONTEXT * parser, PT_NODE * term, FIND_ID_INFO * infop);
static PUSHABLE_TYPE mq_is_pushable_subquery (PARSER_CONTEXT * parser, PT_NODE * subquery, PT_NODE * mainquery,
PT_NODE * class_spec, bool is_vclass, PT_NODE * order_by,
PT_NODE * class_);
static PUSHABLE_TYPE mq_is_removable_select_list (PARSER_CONTEXT * parser, PT_NODE * subquery, PT_NODE * mainquery);
static void pt_copypush_terms (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * query, PT_NODE * term_list,
FIND_ID_TYPE type);
static int mq_copypush_sargable_terms_dblink (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec,
PT_NODE * new_query, FIND_ID_INFO * infop);
static int mq_copypush_sargable_terms_helper (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec,
PT_NODE * subquery, FIND_ID_INFO * infop);
static PT_NODE *mq_rewrite_vclass_spec_as_derived (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec,
PT_NODE * query_spec, bool remove_sel_list);
static PT_NODE *mq_translate_select (PARSER_CONTEXT * parser, PT_NODE * select_statement);
static void mq_check_update (PARSER_CONTEXT * parser, PT_NODE * update_statement);
static void mq_check_delete (PARSER_CONTEXT * parser, PT_NODE * delete_stmt);
static PT_NODE *mq_translate_update (PARSER_CONTEXT * parser, PT_NODE * update_statement);
static PT_NODE *mq_resolve_insert_statement (PARSER_CONTEXT * parser, PT_NODE * insert_statement);
static PT_NODE *mq_translate_insert (PARSER_CONTEXT * parser, PT_NODE * insert_statement);
static PT_NODE *mq_translate_delete (PARSER_CONTEXT * parser, PT_NODE * delete_statement);
static void mq_check_merge (PARSER_CONTEXT * parser, PT_NODE * merge_statement);
static PT_NODE *mq_translate_merge (PARSER_CONTEXT * parser, PT_NODE * merge_statement);
static void mq_push_paths_select (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec);
static PT_NODE *mq_check_rewrite_select (PARSER_CONTEXT * parser, PT_NODE * select_statement);
static PT_NODE *mq_push_paths (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk);
static PT_NODE *mq_translate_local (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk);
static int mq_check_using_index (PARSER_CONTEXT * parser, PT_NODE * using_index);
#if defined(ENABLE_UNUSED_FUNCTION)
static PT_NODE *mq_collapse_dot (PARSER_CONTEXT * parser, PT_NODE * tree);
#endif /* ENABLE_UNUSED_FUNCTION */
static PT_NODE *mq_set_types (PARSER_CONTEXT * parser, PT_NODE * query_spec, PT_NODE * attributes,
DB_OBJECT * vclass_object, int cascaded_check);
static PT_NODE *mq_translate_subqueries (PARSER_CONTEXT * parser, DB_OBJECT * class_object, PT_NODE * attributes,
DB_AUTH * authorization);
static void mq_invert_assign (PARSER_CONTEXT * parser, PT_NODE * attr, PT_NODE * &expr, PT_NODE * inverted);
static void mq_invert_subqueries (PARSER_CONTEXT * parser, PT_NODE * select_statements, PT_NODE * attributes);
static void mq_set_non_updatable_oid (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE * virt_entity);
static bool mq_check_cycle (DB_OBJECT * class_object);
static PT_NODE *mq_mark_location (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *mq_check_non_updatable_vclass_oid (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
int *continue_walk);
static bool mq_check_vclass_for_insert (PARSER_CONTEXT * parser, PT_NODE * stmt);
static PT_NODE *mq_rewrite_upd_del_top_level_specs (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg,
int *continue_walk);
static PT_NODE *mq_translate_helper (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *mq_lookup_symbol (PARSER_CONTEXT * parser, PT_NODE * attr_list, PT_NODE * attr);
static PT_NODE *mq_coerce_resolved (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_set_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_reset_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_clear_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_clear_other_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_reset_spec_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_reset_spec_in_method_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
int *continue_walk);
static PT_NODE *mq_get_references_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_referenced_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_referenced_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static int mq_is_referenced (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec);
static PT_NODE *mq_set_references_local (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec);
static PT_NODE *mq_reset_select_spec_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_reset_select_specs (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_new_spec (PARSER_CONTEXT * parser, const char *class_name);
static PT_NODE *mq_replace_name_with_path (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_substitute_path (PARSER_CONTEXT * parser, PT_NODE * node, PATH_LAMBDA_INFO * path_info);
static PT_NODE *mq_substitute_path_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_path_name_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * lambda_name,
PT_NODE * lambda_expr, UINTPTR spec_id);
static PT_NODE *mq_reset_spec_distr_subpath_pre (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg,
int *continue_walk);
static PT_NODE *mq_reset_spec_distr_subpath_post (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg,
int *continue_walk);
static PT_NODE *mq_translate_paths (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * root_spec);
static void mq_invert_insert_select (PARSER_CONTEXT * parser, PT_NODE * attr, PT_NODE * subquery);
static void mq_invert_insert_subquery (PARSER_CONTEXT * parser, PT_NODE ** attr, PT_NODE * subquery);
static PT_NODE *mq_push_arg2 (PARSER_CONTEXT * parser, PT_NODE * query, PT_NODE * dot_arg2);
static PT_NODE *mq_lambda_node_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk);
static PT_NODE *mq_lambda_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_set_virt_object (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_fix_derived (PARSER_CONTEXT * parser, PT_NODE * select_statement, PT_NODE * spec);
static PT_NODE *mq_translate_value (PARSER_CONTEXT * parser, PT_NODE * value);
static void mq_push_dot_in_query (PARSER_CONTEXT * parser, PT_NODE * query, int i, PT_NODE * name);
static PT_NODE *mq_clean_dot (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *mq_fetch_subqueries_for_update_local (PARSER_CONTEXT * parser, PT_NODE * class_, PT_FETCH_AS fetch_as,
DB_AUTH what_for, PARSER_CONTEXT ** qry_cache);
static PT_NODE *mq_fetch_select_for_real_class_update (PARSER_CONTEXT * parser, PT_NODE * vclass, PT_NODE * real_class,
PT_FETCH_AS fetch_as, DB_AUTH what_for);
static PT_NODE *mq_fetch_expression_for_real_class_update (PARSER_CONTEXT * parser, DB_OBJECT * vclass_obj,
PT_NODE * attr, PT_NODE * real_class, PT_FETCH_AS fetch_as,
DB_AUTH what_for, UINTPTR * spec_id);
static PT_NODE *mq_set_names_dbobject (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static bool mq_is_updatable_local (DB_OBJECT * class_object, PT_FETCH_AS fetch_as);
static PT_NODE *mq_fetch_one_real_class_get_cache (DB_OBJECT * vclass_object, PARSER_CONTEXT ** query_cache);
static PT_NODE *mq_reset_specs_from_column (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * column);
static PT_NODE *mq_path_spec_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * root_spec,
PT_NODE ** prev_ptr, PT_NODE * old_spec, PT_NODE * new_spec);
static PT_NODE *mq_generate_unique (PARSER_CONTEXT * parser, PT_NODE * name_list);
extern PT_NODE *mq_fetch_attributes (PARSER_CONTEXT * parser, PT_NODE * class_);
extern PT_NODE *mq_lambda (PARSER_CONTEXT * parser, PT_NODE * tree_with_names, PT_NODE * name_node,
PT_NODE * corresponding_tree);
extern PT_NODE *mq_class_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * class_,
PT_NODE * corresponding_spec, PT_NODE * class_where_part, PT_NODE * class_check_part,
PT_NODE * class_group_by_part, PT_NODE * class_having_part);
static PT_NODE *mq_inline_view_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * derived_table,
PT_NODE * corresponding_spec, PT_NODE * class_where_part,
PT_NODE * class_check_part, PT_NODE * class_group_by_part,
PT_NODE * class_having_part);
static PT_NODE *mq_fix_derived_in_union (PARSER_CONTEXT * parser, PT_NODE * statement, UINTPTR spec_id);
static PT_NODE *mq_fetch_subqueries (PARSER_CONTEXT * parser, PT_NODE * class_);
static PT_NODE *mq_fetch_subqueries_for_update (PARSER_CONTEXT * parser, PT_NODE * class_, PT_FETCH_AS fetch_as,
DB_AUTH what_for);
static PT_NODE *mq_rename_resolved (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * statement, const char *newname);
static PT_NODE *mq_reset_ids_and_references (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec);
static PT_NODE *mq_reset_ids_and_references_helper (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec,
bool get_spec_referenced_attr);
static PT_NODE *mq_push_path (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, PT_NODE * path);
static PT_NODE *mq_derived_path (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * path);
#if defined(ENABLE_UNUSED_FUNCTION)
static int mq_mget_exprs (DB_OBJECT ** objects, int rows, char **exprs, int cols, int qOnErr, DB_VALUE * values,
int *results, char *emsg);
#endif /* ENABLE_UNUSED_FUNCTION */
static void mq_insert_symbol (PARSER_CONTEXT * parser, PT_NODE ** listhead, PT_NODE * attr);
static const char *get_authorization_name (DB_AUTH auth);
static PT_NODE *mq_add_dummy_from_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *mq_update_order_by (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec,
PT_NODE * class_, PT_NODE * derived_spec, bool skip_adding_hidden_col);
static bool mq_is_order_dependent_node (PT_NODE * node);
static bool mq_mark_order_dependent_nodes (PT_NODE * node);
static PT_NODE *mq_rewrite_order_dependent_nodes (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select,
int *unique);
static PT_NODE *mq_rewrite_order_dependent_query (PARSER_CONTEXT * parser, PT_NODE * select, int *unique);
static PT_NODE *mq_bump_order_dep_corr_lvl_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *mq_bump_order_dep_corr_lvl_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
int *continue_walk);
static void mq_bump_order_dep_corr_lvl (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *mq_reset_references_to_query_string (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
int *continue_walk);
static void mq_auto_param_merge_clauses (PARSER_CONTEXT * parser, PT_NODE * stmt);
static PT_NODE *pt_check_for_update_subquery (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_odku_refs_pre (PARSER_CONTEXT * parser, PT_NODE * odku, void *arg, int *continue_walk);
static void pt_check_odku_refs_view (PARSER_CONTEXT * parser, PT_NODE * statement);
static int pt_check_for_update_clause (PARSER_CONTEXT * parser, PT_NODE * query, bool root);
static int pt_for_update_prepare_query_internal (PARSER_CONTEXT * parser, PT_NODE * query);
static int pt_for_update_prepare_query (PARSER_CONTEXT * parser, PT_NODE * query);
static PT_NODE *mq_replace_virtual_oid_with_real_oid (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
int *continue_walk);
static void mq_copy_view_error_msgs (PARSER_CONTEXT * parser, PARSER_CONTEXT * query_cache);
static void mq_copy_sql_hint (PARSER_CONTEXT * parser, PT_NODE * dest_query, PT_NODE * src_query);
static bool mq_is_rownum_only_predicate (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * node, PT_NODE * order_by,
PT_NODE * subquery, PT_NODE * class_);
static bool mq_check_keep_join_pred (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * node, PT_NODE * subquery,
PT_NODE * class_);
static PT_NODE *mq_update_analytic_sort_spec_expr (PARSER_CONTEXT * parser, PT_NODE * new_select_list,
PT_NODE * old_select_list);
static PT_NODE *mq_inline_cte_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *mq_rewrite_cte_as_derived (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *mq_count_cte_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void mq_check_cte_inline_or_materialize (PARSER_CONTEXT * parser, PT_NODE * node);
/*
* mq_is_outer_join_spec () - determine if a spec is outer joined in a spec list
* returns: boolean
* parser(in): parser context
* spec(in): table spec to check
*/
bool
mq_is_outer_join_spec (PARSER_CONTEXT * parser, PT_NODE * spec)
{
if (spec == NULL)
{
/* should not be here */
PT_INTERNAL_ERROR (parser, "function called with wrong arguments");
return false;
}
assert (spec->node_type == PT_SPEC);
if (spec->info.spec.join_type == PT_JOIN_LEFT_OUTER)
{
/* directly on the right side of a left outer join */
return true;
}
spec = spec->next;
while (spec)
{
switch (spec->info.spec.join_type)
{
case PT_JOIN_NONE:
case PT_JOIN_CROSS:
/* joins from this point forward do not matter */
return false;
case PT_JOIN_RIGHT_OUTER:
/* right outer joined */
return true;
#if 1 /* TODO - */
case PT_JOIN_NATURAL: /* not used */
case PT_JOIN_INNER:
case PT_JOIN_LEFT_OUTER:
case PT_JOIN_FULL_OUTER: /* not used */
case PT_JOIN_UNION: /* not used */
break;
#endif
}
spec = spec->next;
}
/* if we reached this point, it's not outer joined */
return false;
}
/*
* mq_is_right_outer_join_spec () - determine if a spec is right outer joined in a spec list
* returns: boolean
* parser(in): parser context
* spec(in): table spec to check
*/
bool
mq_is_right_outer_join_spec (PARSER_CONTEXT * parser, PT_NODE * spec)
{
if (spec == NULL)
{
/* should not be here */
PT_INTERNAL_ERROR (parser, "function called with wrong arguments");
return false;
}
assert (spec->node_type == PT_SPEC);
spec = spec->next;
while (spec)
{
switch (spec->info.spec.join_type)
{
case PT_JOIN_NONE:
case PT_JOIN_CROSS:
/* joins from this point forward do not matter */
return false;
case PT_JOIN_RIGHT_OUTER:
/* right outer joined */
return true;
#if 1 /* TODO - */
case PT_JOIN_NATURAL: /* not used */
case PT_JOIN_INNER:
case PT_JOIN_LEFT_OUTER:
case PT_JOIN_FULL_OUTER: /* not used */
case PT_JOIN_UNION: /* not used */
break;
#endif
}
spec = spec->next;
}
/* if we reached this point, it's not outer joined */
return false;
}
/*
* mq_bump_corr_pre() - Bump the correlation level of all matching
* correlated queries
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_bump_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
MQ_BUMP_CORR_INFO *info = (MQ_BUMP_CORR_INFO *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
if (!PT_IS_SELECT (node))
return node;
/* Can not increment threshold for list portion of walk. Since those queries are not sub-queries of this query.
* Consequently, we recurse separately for the list leading from a query. */
if (node->next)
{
node->next = mq_bump_correlation_level (parser, node->next, info->increment, info->match_level);
}
*continue_walk = PT_LEAF_WALK;
if (node->info.query.correlation_level != 0)
{
if (node->info.query.correlation_level >= info->match_level)
{
node->info.query.correlation_level += info->increment;
}
}
else
{
/* if the correlation level is 0, there cannot be correlated subqueries crossing this level */
*continue_walk = PT_STOP_WALK;
}
/* increment threshold as we dive into selects and unions */
info->match_level++;
return node;
} /* mq_bump_corr_pre */
/*
* mq_bump_corr_post() - Unwind the info stack
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_bump_corr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
MQ_BUMP_CORR_INFO *info = (MQ_BUMP_CORR_INFO *) void_arg;
if (!PT_IS_QUERY_NODE_TYPE (node->node_type))
return node;
info->match_level--;
return node;
} /* mq_bump_corr_post */
/*
* mq_bump_correlation_level() - Bump the correlation level of all matching
* correlated queries
* return:
* parser(in):
* node(in):
* increment(in):
* match(in):
*/
PT_NODE *
mq_bump_correlation_level (PARSER_CONTEXT * parser, PT_NODE * node, int increment, int match)
{
MQ_BUMP_CORR_INFO info;
info.match_level = match;
info.increment = increment;
return parser_walk_tree (parser, node, mq_bump_corr_pre, &info, mq_bump_corr_post, &info);
}
/*
* mq_union_bump_correlation() - Union left and right sides,
* bumping correlation numbers
* return:
* parser(in):
* left(in):
* right(in):
*/
static PT_NODE *
mq_union_bump_correlation (PARSER_CONTEXT * parser, PT_NODE * left, PT_NODE * right)
{
if (left->info.query.correlation_level)
left = mq_bump_correlation_level (parser, left, 1, left->info.query.correlation_level);
if (right->info.query.correlation_level)
right = mq_bump_correlation_level (parser, right, 1, right->info.query.correlation_level);
return pt_union (parser, left, right);
}
/*
* mq_compute_authorization() -
* return: authorization in terms of the what_for mask
* class_object(in):
*/
static DB_AUTH
mq_compute_authorization (DB_OBJECT * class_object)
{
DB_AUTH auth = (DB_AUTH) 0;
if (db_check_authorization (class_object, DB_AUTH_SELECT) == NO_ERROR)
{
auth = (DB_AUTH) (auth + DB_AUTH_SELECT);
}
if (db_check_authorization (class_object, DB_AUTH_INSERT) == NO_ERROR)
{
auth = (DB_AUTH) (auth + DB_AUTH_INSERT);
}
if (db_check_authorization (class_object, DB_AUTH_UPDATE) == NO_ERROR)
{
auth = (DB_AUTH) (auth + DB_AUTH_UPDATE);
}
if (db_check_authorization (class_object, DB_AUTH_DELETE) == NO_ERROR)
{
auth = (DB_AUTH) (auth + DB_AUTH_DELETE);
}
return auth;
}
/*
* mq_compute_query_authorization() -
* return: authorization intersection of a query
* statement(in):
*/
static DB_AUTH
mq_compute_query_authorization (PT_NODE * statement)
{
PT_NODE *spec;
PT_NODE *flat;
DB_AUTH auth = (DB_AUTH) 0;
switch (statement->node_type)
{
case PT_SELECT:
spec = statement->info.query.q.select.from;
if (spec == NULL)
{
auth = DB_AUTH_SELECT;
}
else
{
auth = DB_AUTH_ALL;
/* select authorization is computed at semantic check */
/* its moot to compute other authorization on entire join, since its non-updateable */
for (flat = spec->info.spec.flat_entity_list; flat != NULL; flat = flat->next)
{
auth = (DB_AUTH) (auth & mq_compute_authorization (flat->info.name.db_object));
}
}
break;
case PT_UNION:
auth = mq_compute_query_authorization (statement->info.query.q.union_.arg1);
auth = (DB_AUTH) (auth & mq_compute_query_authorization (statement->info.query.q.union_.arg2));
break;
case PT_DIFFERENCE:
case PT_INTERSECTION:
/* select authorization is computed at semantic check */
/* again moot to compute other authorization, since this is not updatable. */
auth = DB_AUTH_SELECT;
break;
default: /* should not get here, that is an error! */
#if defined(CUBRID_DEBUG)
fprintf (stdout, "Illegal parse node type %d, in %s, at line %d. \n", statement->node_type, __FILE__, __LINE__);
#endif /* CUBRID_DEBUG */
break;
}
return auth;
}
/*
* mq_set_union_query() - Mark top level selects as PT_IS__UNION_QUERY
* return: B_AUTH authorization
* parser(in):
* statement(in):
* is_union(in):
*/
static void
mq_set_union_query (PARSER_CONTEXT * parser, PT_NODE * statement, PT_MISC_TYPE is_union)
{
if (statement)
switch (statement->node_type)
{
case PT_SELECT:
statement->info.query.is_subquery = is_union;
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
statement->info.query.is_subquery = is_union;
mq_set_union_query (parser, statement->info.query.q.union_.arg1, is_union);
mq_set_union_query (parser, statement->info.query.q.union_.arg2, is_union);
break;
default:
/* should not get here, that is an error! */
/* its almost certainly recoverable, so ignore it */
assert (0);
break;
}
}
/*
* mq_flatten_union() -
* return: returns an error if it fails.
* parser(in):
* statement(in):the parse tree of a union statement.
*
* Note :
* "legal" candidates for leafs are delete, update, and insert statements.
* "illegal" candidates are intersection and difference, which
* reslt in error return (NULL).
*/
static PT_NODE *
mq_flatten_union (PARSER_CONTEXT * parser, PT_NODE * statement)
{
PT_NODE *lhs = statement;
PT_NODE *rhs = NULL;
if (!statement)
return NULL; /* bullet proofing */
if (statement->node_type == PT_UNION && statement->info.query.all_distinct == PT_ALL)
{
lhs = statement->info.query.q.union_.arg1;
rhs = statement->info.query.q.union_.arg2;
if (!lhs || !rhs)
return NULL; /* bullet proofing */
if (lhs->node_type == PT_UNION)
lhs = mq_flatten_union (parser, lhs);
if (rhs->node_type == PT_UNION)
rhs = mq_flatten_union (parser, rhs);
/* propagate error detected in recursion, if any */
if (!lhs || !rhs)
return NULL;
/* append right hand side list to end of left hand side list */
parser_append_node (rhs, lhs);
}
return lhs;
}
/*
* mq_rewrite_agg_names() - re-sets PT_NAME node ids for conversion of
* aggregate selects. It also coerces path expressions into names, and
* pushes subqueries and SET() functions down into the derived table.
* It places each name on the referenced attrs list.
* return:
* parser(in):
* node(in):
* void_arg(in/out):
* continue_walk(in/out):
*/
static PT_NODE *
mq_rewrite_agg_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_AGG_REWRITE_INFO *info = (PT_AGG_REWRITE_INFO *) void_arg;
PT_AGG_NAME_INFO name_info;
PT_NODE *old_from = info->from;
PT_NODE *new_from = info->new_from;
PT_NODE *derived_select = info->derived_select;
PT_NODE *node_next;
PT_TYPE_ENUM type;
PT_NODE *data_type;
PT_NODE *temp;
PT_NODE *arg2;
PT_NODE *temparg2;
int i = 0;
int line_no, col_no, is_hidden_column;
bool agg_found;
*continue_walk = PT_CONTINUE_WALK;
switch (node->node_type)
{
case PT_DOT_:
if ((arg2 = node->info.dot.arg2) && pt_find_entity (parser, old_from, node->info.dot.arg2->info.name.spec_id))
{
/* we should put this in the referenced name list, ie the select list of the derived select statement (if not
* already there) then change this node to a generated name. */
node_next = node->next;
type = node->type_enum;
data_type = parser_copy_tree_list (parser, node->data_type);
node->next = NULL;
temp = derived_select->info.query.q.select.list;
i = 0;
while (temp)
{
if (temp->node_type == PT_DOT_ && (temparg2 = temp->info.dot.arg2)
&& pt_name_equal (parser, temparg2, arg2))
{
break;
}
temp = temp->next;
i++;
}
line_no = node->line_number;
col_no = node->column_number;
is_hidden_column = node->flag.is_hidden_column;
if (!temp)
{
/* This was not found. add it */
derived_select->info.query.q.select.list =
parser_append_node (node, derived_select->info.query.q.select.list);
}
else
{
parser_free_tree (parser, node);
}
node = pt_name (parser, mq_generate_name (parser, "a", &i));
node->info.name.meta_class = PT_NORMAL;
node->info.name.spec_id = new_from->info.spec.id;
node->next = node_next;
node->type_enum = type;
node->data_type = data_type;
node->line_number = line_no;
node->column_number = col_no;
node->flag.is_hidden_column = is_hidden_column;
mq_insert_symbol (parser, &new_from->info.spec.as_attr_list, node);
}
break;
case PT_NAME:
/* is the name an attribute name ? */
if ((node->info.name.meta_class == PT_NORMAL || node->info.name.meta_class == PT_OID_ATTR
|| node->info.name.meta_class == PT_VID_ATTR || node->info.name.meta_class == PT_SHARED
|| node->info.name.meta_class == PT_META_ATTR || node->info.name.meta_class == PT_META_CLASS
|| node->info.name.meta_class == PT_METHOD) && pt_find_entity (parser, old_from, node->info.name.spec_id))
{
if (node->info.name.meta_class == PT_METHOD)
{
/* for method_name, only reset spec_id */
node->info.name.spec_id = new_from->info.spec.id;
goto push_complete;
}
/* we should put this in the referenced name list, ie the select list of the derived select statement (if not
* already there) then change this node to a generated name. */
node_next = node->next;
type = node->type_enum;
data_type = parser_copy_tree_list (parser, node->data_type);
node->next = NULL;
temp = derived_select->info.query.q.select.list;
i = 0;
while (temp)
{
if (temp->node_type == PT_NAME && pt_name_equal (parser, temp, node))
break;
temp = temp->next;
i++;
}
line_no = node->line_number;
col_no = node->column_number;
is_hidden_column = node->flag.is_hidden_column;
if (!temp)
{
derived_select->info.query.q.select.list =
parser_append_node (node, derived_select->info.query.q.select.list);
}
else
{
parser_free_tree (parser, node);
}
node = pt_name (parser, mq_generate_name (parser, "a", &i));
node->info.name.meta_class = PT_NORMAL;
node->info.name.spec_id = new_from->info.spec.id;
node->next = node_next;
node->type_enum = type;
node->data_type = data_type;
node->line_number = line_no;
node->column_number = col_no;
node->flag.is_hidden_column = is_hidden_column;
mq_insert_symbol (parser, &new_from->info.spec.as_attr_list, node);
push_complete:
/* once we push it, we don't need to dive in */
*continue_walk = PT_LIST_WALK;
}
break;
case PT_FUNCTION:
/* We need to push the set functions down with their subqueries. init. info->from is already set at
* mq_rewrite_aggregate_as_derived */
agg_found = false;
/* init name finding structure */
name_info.max_level = -1;
name_info.name_count = 0;
name_info.select_stack = info->select_stack;
if (pt_is_set_type (node))
{
if (!pt_is_query (node->info.function.arg_list))
{
(void) parser_walk_tree (parser, node, pt_find_aggregate_names, &name_info, pt_continue_walk, NULL);
agg_found = (name_info.max_level == 0 || name_info.name_count == 0);
}
}
else if (pt_is_aggregate_function (parser, node))
{
if (node->info.function.function_type == PT_COUNT_STAR || node->info.function.function_type == PT_GROUPBY_NUM)
{
/* found count(*), groupby_num() */
agg_found = (info->depth == 0);
}
else
{
/* check for aggregation function for example: SELECT (SELECT max(x.i) FROM y ...) ... FROM x */
(void) parser_walk_tree (parser, node->info.function.arg_list, pt_find_aggregate_names, &name_info,
pt_continue_walk, NULL);
agg_found = (name_info.max_level == 0 || name_info.name_count == 0);
}
}
/* rewrite if necessary */
if (agg_found)
{
node_next = node->next;
type = node->type_enum;
data_type = parser_copy_tree_list (parser, node->data_type);
node->next = NULL;
for (i = 0, temp = derived_select->info.query.q.select.list; temp; temp = temp->next, i++)
; /* empty */
derived_select->info.query.q.select.list =
parser_append_node (node, derived_select->info.query.q.select.list);
line_no = node->line_number;
col_no = node->column_number;
is_hidden_column = node->flag.is_hidden_column;
node = pt_name (parser, mq_generate_name (parser, "a", &i));
node->info.name.meta_class = PT_NORMAL;
node->info.name.spec_id = new_from->info.spec.id;
node->next = node_next;
node->type_enum = type;
node->data_type = data_type;
node->line_number = line_no;
node->column_number = col_no;
node->flag.is_hidden_column = is_hidden_column;
mq_insert_symbol (parser, &new_from->info.spec.as_attr_list, node);
/* once we push it, we don't need to dive in */
*continue_walk = PT_LIST_WALK;
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
case PT_SELECT:
/* Can not increment level for list portion of walk. Since those queries are not sub-queries of this query.
* Consequently, we recurse separately for the list leading from a query. Can't just call
* pt_to_uncorr_subquery_list() directly since it needs to do a leaf walk and we want to do a full walk on the
* next list. */
if (node->next)
{
node->next =
parser_walk_tree (parser, node->next, mq_rewrite_agg_names, info, mq_rewrite_agg_names_post, info);
}
if (node->info.query.correlation_level == 0)
{
/* no need to dive into the uncorrelated subquery */
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_LEAF_WALK;
}
/* push SELECT on stack */
if (node->node_type == PT_SELECT)
{
info->select_stack = pt_pointer_stack_push (parser, info->select_stack, node);
}
info->depth++; /* increase query depth as we dive into subqueries */
break;
case PT_DATA_TYPE:
*continue_walk = PT_STOP_WALK;
break;
default:
break;
}
return node;
}
/*
* mq_rewrite_agg_names_post() -
* return:
* parser(in):
* node(in):
* void_arg(in/out):
* continue_walk(in):
*/
static PT_NODE *
mq_rewrite_agg_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_AGG_REWRITE_INFO *info = (PT_AGG_REWRITE_INFO *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
switch (node->node_type)
{
case PT_SELECT:
info->select_stack = pt_pointer_stack_pop (parser, info->select_stack, NULL);
[[fallthrough]];
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
info->depth--; /* decrease query depth */
break;
default:
break;
}
return node;
}
/*
* mq_conditionally_add_objects() - places the object pointers on an array
* return: false on detection of duplicates in the classes
* parser(in):
* flat(in):
* classes(in/out):
* index(in/out):
* max(in/out):
*/
static bool
mq_conditionally_add_objects (PARSER_CONTEXT * parser, PT_NODE * flat, DB_OBJECT *** classes, int *index, int *max)
{
int i;
DB_OBJECT *class_object;
DB_OBJECT **temp;
while (flat)
{
class_object = flat->info.name.db_object;
for (i = 0; i < *index; i++)
{
if ((*classes)[i] == class_object)
return false;
}
if (*index >= *max)
{
temp = (DB_OBJECT **) parser_alloc (parser, (*max + MAX_STACK_OBJECTS) * sizeof (DB_OBJECT *));
if (temp == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_alloc");
return false;
}
memcpy (temp, *classes, *max * sizeof (DB_OBJECT *));
/* don't keep dangling pointers */
memset (*classes, 0, *max * sizeof (DB_OBJECT *));
*classes = temp;
*max = *max + MAX_STACK_OBJECTS;
}
(*classes)[(*index)] = class_object;
(*index)++;
flat = flat->next;
}
return true;
}
/*
* mq_updatable_local() - takes a subquery expansion of a class_, and
* tests it for updatability
* return: true on updatable
* parser(in):
* statement(in):
* classes(in/out):
* num_classes(in/out):
* max(in/out):
*/
static PT_UPDATABILITY
mq_updatable_local (PARSER_CONTEXT * parser, PT_NODE * statement, DB_OBJECT *** classes, int *num_classes, int *max)
{
PT_UPDATABILITY global = PT_UPDATABLE;
while ((statement != NULL) && (PT_NOT_UPDATABLE != global))
{
PT_UPDATABILITY local = PT_UPDATABLE;
if (statement && statement->info.query.all_distinct == PT_DISTINCT)
{
/* distinct */
local = (PT_UPDATABILITY) (local & PT_NOT_UPDATABLE);
}
switch (statement->node_type)
{
case PT_SELECT:
if (statement->info.query.q.select.group_by /* aggregate */
|| statement->info.query.q.select.having /* aggregate */
|| statement->info.query.q.select.connect_by /* HQ */
|| statement->info.query.q.select.from == NULL /* no spec */
|| PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_READ_ONLY)) /* system generated read-only */
{
local = (PT_UPDATABILITY) (local & PT_NOT_UPDATABLE);
}
if (local != PT_NOT_UPDATABLE)
{
PT_NODE *spec = statement->info.query.q.select.from;
while (spec != NULL)
{
if (spec->info.spec.derived_table != NULL)
{
if (spec->info.spec.flag & PT_SPEC_FLAG_FROM_VCLASS)
{
/* derived table from former view */
local = (PT_UPDATABILITY) (local &
mq_updatable_local (parser, spec->info.spec.derived_table, classes,
num_classes, max));
}
else
{
/* derived tables are not updatable */
local = PT_NOT_UPDATABLE;
break;
}
}
spec = spec->next;
}
}
if (local != PT_NOT_UPDATABLE
&& (pt_has_aggregate (parser, statement) || pt_has_analytic (parser, statement)))
{
/* aggregate and analytic queries are not updatable */
local = (PT_UPDATABILITY) (local & PT_NOT_UPDATABLE);
}
if (local != PT_NOT_UPDATABLE)
{
PT_NODE *from;
int i = 0;
bool is_reuse_oid_class;
int client_type = db_get_client_type ();
for (from = statement->info.query.q.select.from; from != NULL; from = from->next)
{
(void) mq_conditionally_add_objects (parser, from->info.spec.flat_entity_list, classes, num_classes,
max);
}
for (i = 0; i < *num_classes; ++i)
{
is_reuse_oid_class = sm_is_reuse_oid_class ((*classes)[i]);
if (is_reuse_oid_class
|| (!BOOT_ADMIN_CSQL_CLIENT_TYPE (client_type) && (sm_is_system_class ((*classes)[i]) > 0)))
{
local = (PT_UPDATABILITY) (local & PT_NOT_UPDATABLE);
if (parser->view_cache)
{
parser->view_cache->has_reuse_oid_table = is_reuse_oid_class;
}
break;
}
}
}
if (local != PT_NOT_UPDATABLE && statement->info.query.q.select.from->next)
{
/* last check is for partially updatable queries */
local = (PT_UPDATABILITY) (local & PT_PARTIALLY_UPDATABLE);
}
break;
case PT_UNION:
if (local != PT_NOT_UPDATABLE)
{
local =
(PT_UPDATABILITY) (local &
mq_updatable_local (parser, statement->info.query.q.union_.arg1, classes,
num_classes, max));
local =
(PT_UPDATABILITY) (local &
mq_updatable_local (parser, statement->info.query.q.union_.arg2, classes,
num_classes, max));
}
break;
default:
local = (PT_UPDATABILITY) (local & PT_NOT_UPDATABLE);
break;
}
/* next statement */
global = (PT_UPDATABILITY) (local & global);
statement = statement->next;
}
return global;
}
/*
* mq_updatable() - takes a subquery expansion of a class_, and
* tests it for updatability
* return: true on updatable
* parser(in):
* statement(in):
*/
PT_UPDATABILITY
mq_updatable (PARSER_CONTEXT * parser, PT_NODE * statement)
{
PT_UPDATABILITY updatable;
int num_classes = 0;
int max = MAX_STACK_OBJECTS;
DB_OBJECT *class_stack_array[MAX_STACK_OBJECTS];
DB_OBJECT **classes = class_stack_array;
updatable = mq_updatable_local (parser, statement, &classes, &num_classes, &max);
/* don't keep dangling pointers on stack or in virtual memory */
memset (classes, 0, max * sizeof (DB_OBJECT *));
return updatable;
}
/*
* mq_substitute_select_in_statement() - takes a subquery expansion of a class_,
* in the form of a select, and a parse tree containing references to
* the class and its attributes, and substitutes matching select
* expressions for each attribute, and matching referenced classes
* for each class
* return: PT_NODE *, parse tree with local db table/class queries
* expanded to local db expressions
* parser(in): parser context
* statement(in/out): statement into which class will be expanded
* query_spec(in): query of class that will be expanded
* class(in): class name of class that will be expanded
*/
static PT_NODE *
mq_substitute_select_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec, PT_NODE * class_)
{
PT_RESOLVE_METHOD_NAME_INFO info;
PT_NODE *query_spec_from, *query_spec_columns;
PT_NODE *attributes, *attr;
PT_NODE *col;
/* Replace columns/attributes. for each column/attribute name in table/class class, replace with actual select
* column. */
query_spec_columns = query_spec->info.query.q.select.list;
query_spec_from = query_spec->info.query.q.select.from;
if (query_spec_from == NULL)
{
PT_INTERNAL_ERROR (parser, "translate");
return NULL;
}
/* fix spec_id for method calls and sql hints in the statement */
info.old_id = class_->info.name.spec_id;
info.new_id = query_spec_from->info.spec.id;
(void) parser_walk_tree (parser, statement, mq_substitute_spec_in_method_and_hints, &info, NULL, NULL);
/* get vclass spec attrs */
attributes = mq_fetch_attributes (parser, class_);
if (attributes == NULL)
{
return NULL;
}
col = query_spec_columns;
attr = attributes;
if (PT_IS_VALUE_QUERY (query_spec) && col != NULL && attr != NULL)
{
assert (col->node_type == PT_NODE_LIST);
col = col->info.node_list.list;
/* skip oid */
attr = attr->next;
}
for (; col && attr; col = col->next, attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = class_->info.name.spec_id;
}
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col)
{ /* error */
PT_ERRORmf (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_QSPEC_COLS_GT_ATTRS,
class_->info.name.original);
statement = NULL;
}
if (attr)
{ /* error */
PT_ERRORmf (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS,
class_->info.name.original);
statement = NULL;
}
/* substitute attributes for query_spec_columns in statement */
statement = mq_lambda (parser, statement, attributes, query_spec_columns);
/* if is_mergeable == NON_PUSHABLE, then vspec_as_derived == 1 */
if (statement->info.query.flag.vspec_as_derived == 1)
{
PT_SELECT_INFO_CLEAR_FLAG (statement, PT_SELECT_INFO_HAS_ANALYTIC);
if (pt_has_analytic (parser, statement))
{
PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_HAS_ANALYTIC);
statement->info.query.q.select.list =
mq_update_analytic_sort_spec_expr (parser, statement->info.query.q.select.list, query_spec_columns);
if (statement->info.query.q.select.list == NULL)
{
return NULL;
}
}
}
/* replace table */
if (statement)
{
statement =
mq_class_lambda (parser, statement, class_, query_spec_from, query_spec->info.query.q.select.where,
query_spec->info.query.q.select.check_where, query_spec->info.query.q.select.group_by,
query_spec->info.query.q.select.having);
if (PT_SELECT_INFO_IS_FLAGED (query_spec, PT_SELECT_INFO_HAS_AGG))
{
/* mark as agg select */
if (statement && statement->node_type == PT_SELECT)
{
PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_HAS_AGG);
}
}
}
return statement;
}
/*
* mq_substitute_select_for_inline_view () - takes a subquery expansion of a class_,
* in the form of a select, and a parse tree containing references to
* the class and its attributes, and substitutes matching select
* expressions for each attribute, and matching referenced classes
* for each class
* return: PT_NODE *, parse tree with local db table/class queries
* expanded to local db expressions
* parser(in): parser context
* statement(in/out): statement into which class will be expanded
* query_spec(in): query of class that will be expanded
* derived_spec(in): class name of class that will be expanded
*/
static PT_NODE *
mq_substitute_select_for_inline_view (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * subquery,
PT_NODE * derived_spec)
{
PT_RESOLVE_METHOD_NAME_INFO info;
PT_NODE *query_spec_from, *query_spec_columns;
PT_NODE *attributes, *attr;
PT_NODE *col;
/* Replace columns/attributes. for each column/attribute name in table/class class, replace with actual select
* column. */
if (derived_spec == NULL || !PT_SPEC_IS_DERIVED (derived_spec))
{
PT_INTERNAL_ERROR (parser, "translate inline view");
return NULL;
}
query_spec_columns = subquery->info.query.q.select.list;
query_spec_from = subquery->info.query.q.select.from;
if (query_spec_from == NULL)
{
PT_INTERNAL_ERROR (parser, "translate inline view");
return NULL;
}
/* fix spec_id for method calls and sql hints in the statement */
info.old_id = derived_spec->info.spec.id;
info.new_id = query_spec_from->info.spec.id;
(void) parser_walk_tree (parser, statement, mq_substitute_spec_in_method_and_hints, &info, NULL, NULL);
/* get vclass spec attrs */
attributes = derived_spec->info.spec.as_attr_list;
if (attributes == NULL)
{
return NULL;
}
col = query_spec_columns;
attr = attributes;
if (PT_IS_VALUE_QUERY (subquery) && col != NULL && attr != NULL)
{
assert (col->node_type == PT_NODE_LIST);
col = col->info.node_list.list;
/* skip oid */
attr = attr->next;
}
for (; col && attr; col = col->next, attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = derived_spec->info.spec.id;
}
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col)
{ /* error */
PT_ERRORmf (parser, derived_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_QSPEC_COLS_GT_ATTRS,
derived_spec->info.spec.range_var->info.name.original);
statement = NULL;
}
if (attr)
{ /* error */
PT_ERRORmf (parser, derived_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS,
derived_spec->info.spec.range_var->info.name.original);
statement = NULL;
}
/* substitute attributes for query_spec_columns in statement */
statement = mq_lambda (parser, statement, attributes, query_spec_columns);
/* replace table */
if (statement)
{
statement =
mq_inline_view_lambda (parser, statement, derived_spec, query_spec_from, subquery->info.query.q.select.where,
subquery->info.query.q.select.check_where, subquery->info.query.q.select.group_by,
subquery->info.query.q.select.having);
if (PT_SELECT_INFO_IS_FLAGED (subquery, PT_SELECT_INFO_HAS_AGG))
{
/* mark as agg select */
if (statement && statement->node_type == PT_SELECT)
{
PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_HAS_AGG);
}
}
}
return statement;
}
/*
* mq_remove_select_list_for_inline_view () - remove unnecessary select list
* return: PT_NODE *, statement
* parser(in): parser context
* statement(in/out): statement into which class will be expanded
* query_spec(in): query of class that will be expanded
* derived_spec(in): class name of class that will be expanded
*/
static PT_NODE *
mq_remove_select_list_for_inline_view (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * derived_spec,
PT_NODE ** new_spec)
{
PT_NODE *query_spec_columns, *save_order_by, *save_select_list, *save_order_by_for;
PT_NODE *attributes, *attr, *as_attr_list;
PT_NODE *col, *new_select_list, *spec, *subquery;
bool is_unset_hidden_col;
PT_NODE *derived_table;
PT_NODE *tmp_query = NULL;
assert (PT_IS_SELECT (statement));
if (derived_spec == NULL || !PT_SPEC_IS_DERIVED (derived_spec))
{
PT_INTERNAL_ERROR (parser, "remove select list");
goto exit_on_error;
}
/* find derived_spec */
spec = statement->info.query.q.select.from;
while (spec && derived_spec->info.spec.id != spec->info.spec.id)
{
spec = spec->next;
}
if (spec == NULL)
{
goto exit_on_error;
}
subquery = spec->info.spec.derived_table;
/* get query attrs */
query_spec_columns = subquery->info.query.q.select.list;
/* get derived spec attrs */
attributes = spec->info.spec.as_attr_list;
if (attributes == NULL)
{
goto exit_on_error;
}
col = query_spec_columns;
attr = attributes;
if (PT_IS_VALUE_QUERY (subquery) && col != NULL && attr != NULL)
{
assert (col->node_type == PT_NODE_LIST);
col = col->info.node_list.list;
/* skip oid */
attr = attr->next;
}
for (; col && attr; col = col->next, attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = spec->info.spec.id;
}
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col)
{ /* error */
PT_ERRORmf (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_QSPEC_COLS_GT_ATTRS,
spec->info.spec.range_var->info.name.original);
goto exit_on_error;
}
if (attr)
{ /* error */
PT_ERRORmf (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS,
spec->info.spec.range_var->info.name.original);
goto exit_on_error;
}
new_select_list = mq_get_references (parser, statement, spec);
for (col = new_select_list; col; col = col->next)
{
if (col->flag.is_hidden_column)
{
col->flag.is_hidden_column = 0;
}
}
if (new_select_list == NULL)
{
/* case of constant attr. e.g.) count(*), count(1) */
/* just add one of integer value */
new_select_list = pt_make_integer_value (parser, 1);
}
/* cut off select list and order by */
save_order_by = subquery->info.query.order_by;
save_order_by_for = subquery->info.query.orderby_for;
save_select_list = subquery->info.query.q.select.list;
subquery->info.query.order_by = NULL;
subquery->info.query.q.select.list = NULL;
subquery->info.query.orderby_for = NULL;
tmp_query = parser_copy_tree (parser, subquery);
tmp_query->info.query.q.select.list = new_select_list;
/* revert subquery */
subquery->info.query.order_by = save_order_by;
subquery->info.query.q.select.list = save_select_list;
subquery->info.query.orderby_for = save_order_by_for;
if (subquery->info.query.order_by)
{
tmp_query = mq_update_order_by (parser, tmp_query, subquery, NULL, spec, false);
if (tmp_query == NULL)
{
goto exit_on_error;
}
}
/* check whether to unset hidden col. If no hidden column in original, remove hidden settings */
is_unset_hidden_col = true;
for (col = subquery->info.query.q.select.list; col; col = col->next)
{
if (col->flag.is_hidden_column)
{
is_unset_hidden_col = false;
break;
}
}
if (is_unset_hidden_col)
{
for (col = tmp_query->info.query.q.select.list; col; col = col->next)
{
if (col->flag.is_hidden_column)
{
col->flag.is_hidden_column = 0;
}
}
}
/* copy select_list to as_attr_list before mq_lambda() */
as_attr_list = parser_copy_tree_list (parser, tmp_query->info.query.q.select.list);
/* substitute attributes for query_spec_columns in statement */
tmp_query = mq_lambda (parser, tmp_query, attributes, query_spec_columns);
/* assign tmp_query */
parser_free_tree (parser, spec->info.spec.derived_table);
spec->info.spec.derived_table = tmp_query;
parser_free_tree (parser, spec->info.spec.as_attr_list);
spec->info.spec.as_attr_list = as_attr_list;
/* If analytic functions are not used in new_select_list, they are removed from the subquery.
* If no analytic functions are used anymore, PT_SELECT_INFO_HAS_ANALYTIC should be cleared.
* If PT_SELECT_INFO_HAS_ANALYTIC is set, pt_has_analytic skips pt_is_analytic_node and pt_is_analytic_node_post,
* so it needs to be cleared first. After that, check again whether PT_SELECT_INFO_HAS_ANALYTIC should be set.
*/
PT_SELECT_INFO_CLEAR_FLAG (spec->info.spec.derived_table, PT_SELECT_INFO_HAS_ANALYTIC);
if (pt_has_analytic (parser, spec->info.spec.derived_table))
{
PT_SELECT_INFO_SET_FLAG (spec->info.spec.derived_table, PT_SELECT_INFO_HAS_ANALYTIC);
derived_table = derived_spec->info.spec.derived_table;
tmp_query->info.query.q.select.list =
mq_update_analytic_sort_spec_expr (parser, tmp_query->info.query.q.select.list,
derived_table->info.query.q.select.list);
if (tmp_query->info.query.q.select.list == NULL)
{ /* error */
goto exit_on_error;
}
}
*new_spec = spec;
return statement;
exit_on_error:
if (tmp_query)
{
parser_free_tree (parser, tmp_query);
}
*new_spec = NULL;
/* When an error occurs, the statement before the change is returned. */
return statement;
}
/*
* mq_substitute_spec_in_method_and_hints() - substitue spec id in method and hints
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_substitute_spec_in_method_and_hints (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_RESOLVE_METHOD_NAME_INFO *info = (PT_RESOLVE_METHOD_NAME_INFO *) void_arg;
PT_NODE *tmp_hint = NULL;
switch (node->node_type)
{
case PT_METHOD_CALL:
if (PT_IS_METHOD (node) && (node->info.method_call.method_name)
&& (node->info.method_call.method_name->info.name.spec_id == info->old_id))
{
node->info.method_call.method_name->info.name.spec_id = info->new_id;
}
break;
case PT_SELECT:
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.leading, info);
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.use_nl, info);
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.use_idx, info);
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.index_ss, info);
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.index_ls, info);
MQ_FIX_SPEC_ID (tmp_hint, node->info.query.q.select.use_merge, info);
break;
default:
/* do nothing */
break;
}
return node;
}
/*
* check if it refers view or inline view at "on duplicate key update" clause
*/
static PT_NODE *
pt_check_odku_refs_pre (PARSER_CONTEXT * parser, PT_NODE * odku, void *arg, int *continue_walk)
{
PT_NODE *sel_spec = (PT_NODE *) arg;
PT_NODE *spec;
*continue_walk = PT_CONTINUE_WALK;
if (odku == NULL || odku->node_type != PT_NAME)
{
return odku;
}
for (spec = sel_spec; spec; spec = spec->next)
{
if (spec->info.spec.id == odku->info.name.spec_id)
{
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_REFERENCED_AT_ODKU);
}
}
return odku;
}
/*
* Checks if there is a view or inline view referenced in the odku clause.
* If there is a referenced the view, set a flag
* for the view to indicate that it is referenced in odku.
* This flag will be used when determining "pushable" in mq_is_pushable_subquery.
*/
static void
pt_check_odku_refs_view (PARSER_CONTEXT * parser, PT_NODE * statement)
{
PT_NODE *odku = statement->info.insert.odku_assignments;
if (odku)
{
PT_NODE *sel_spec, *values;
values = statement->info.insert.value_clauses->info.node_list.list;
while (values)
{
if (values->node_type == PT_SELECT)
{
sel_spec = values->info.query.q.select.from;
while (odku)
{
(void) parser_walk_tree (parser, odku->info.expr.arg2, pt_check_odku_refs_pre, sel_spec, NULL, NULL);
odku = odku->next;
}
}
values = values->next;
}
}
}
/*
* mq_is_pushable_subquery () - check if a subquery is pushable
* returns: true if pushable, false otherwise
* parser(in): parser context
* query(in): query to check
* is_only_spec(in): true if query is not joined in parent statement
*
* NOTE:
* | SELECT ...
* main <-| FROM ( subquery ) <== query check
* | WHERE term
*
* 1. MAIN QUERY CHECK
* It should be merged in the following cases.
* - INSERT query
* It is not pushable(mergeable) in the following cases.
* - Class is Spec set(spec set??)
* - select for schema
* - has CONNECT BY
* - is merge query
* - is CTE query
* - view spec is outer join spec
* - main query's where has define_vars ':='
* - subquery has order_by and main query has inst_num or analytic or order-sensitive aggrigation function
*
* 2. SUB QUERY CHECK
* It is not pushable(mergeable) in the following cases.
* - NOT SELECT node (UNION, DIFFERENCE, INTERSECTION)
* - is value query
* - has 'for update'
* - is correlated subquery
* - is CTE query
* - has NOT 'FROM'
* - has outer join spec and CTE spec
* - has CONNECT BY (Hierarchical Queries)
* - has DISTINCT
* - has aggregate or orderby_for or analytic
* - has inst num or orderby_num
* - has method
* TO_DO : check all cases using is_vclass
*/
static PUSHABLE_TYPE
mq_is_pushable_subquery (PARSER_CONTEXT * parser, PT_NODE * subquery, PT_NODE * mainquery, PT_NODE * class_spec,
bool is_vclass, PT_NODE * order_by, PT_NODE * class_)
{
PT_NODE *pred, *statement_spec = NULL, *orderby_for, *select_list;
CHECK_PUSHABLE_INFO cpi;
bool is_pushable_query, is_outer_joined;
bool is_only_spec, is_rownum_only, is_orderby_for;
/* check nulls */
if (subquery == NULL)
{
PT_INTERNAL_ERROR (parser, "wrong arguments passed to function");
return HAS_ERROR;
}
assert (parser);
assert (PT_IS_QUERY_NODE_TYPE (subquery->node_type));
/* get statement spec */
switch (mainquery->node_type)
{
case PT_SELECT:
statement_spec = mainquery->info.query.q.select.from;
pred = mainquery->info.query.q.select.where;
select_list = mainquery->info.query.q.select.list;
break;
case PT_UPDATE:
statement_spec = mainquery->info.update.spec;
pred = mainquery->info.update.search_cond;
select_list = NULL;
break;
case PT_DELETE:
statement_spec = mainquery->info.delete_.spec;
pred = mainquery->info.delete_.search_cond;
select_list = NULL;
break;
case PT_INSERT:
/* since INSERT can not have a spec list or statement conditions, there is nothing to check */
statement_spec = mainquery->info.insert.spec;
pred = NULL;
select_list = NULL;
break;
case PT_MERGE:
statement_spec = mainquery->info.merge.into;
pred = mainquery->info.merge.insert.search_cond;
select_list = NULL;
break;
default:
/* should not get here */
assert (false);
PT_INTERNAL_ERROR (parser, "unknown node");
return HAS_ERROR;
}
/****************************/
/*** ODKU REFERENCE CHECK ***/
/****************************/
/* odku refers view or inline view */
if (PT_IS_SPEC_FLAG_SET (class_spec, PT_SPEC_FLAG_REFERENCED_AT_ODKU))
{
return NON_PUSHABLE;
}
/*****************************/
/**** 1. MAIN QUERY CHECK ****/
/*****************************/
/* NO_MERGE and QUERY_CACHE hint check */
if (subquery->info.query.q.select.hint & (PT_HINT_NO_MERGE | PT_HINT_QUERY_CACHE))
{
return NON_PUSHABLE;
}
/* select node from UPDATE, DELETE */
if (pt_is_select (mainquery) &&
(mainquery->info.query.scan_op_type == S_DELETE || mainquery->info.query.scan_op_type == S_UPDATE))
{
return NON_PUSHABLE;
}
/* determine if class_spec is the only spec in the statement */
is_rownum_only = mq_is_rownum_only_predicate (parser, statement_spec, mainquery, order_by, subquery, class_);
is_only_spec =
((statement_spec->next == NULL && (pred == NULL || is_rownum_only)
&& (subquery->info.query.order_by == NULL || order_by == NULL)) ? true : false);
/* check if orderby_for set to PT_EXPR_INFO_ROWNUM_ONLY */
orderby_for = subquery->info.query.orderby_for;
is_orderby_for = orderby_for && (order_by
|| pt_is_distinct (mainquery) || pt_has_aggregate (parser, mainquery)
|| (orderby_for->node_type == PT_EXPR
&& !PT_EXPR_INFO_IS_FLAGED (orderby_for, PT_EXPR_INFO_ROWNUM_ONLY)));
/* do not rewrite vclass_query as a derived table if spec belongs to an insert statement. */
if (mainquery->node_type == PT_INSERT)
{
/* pushable */
return PUSHABLE;
}
/* check for (non-pushable) spec set (spec set??) */
if (class_spec->info.spec.entity_name != NULL && class_spec->info.spec.entity_name->node_type == PT_SPEC)
{
/* not pushable */
return NON_PUSHABLE;
}
/* select for schema */
if (PT_IS_SELECT (mainquery)
&& (PT_SELECT_INFO_IS_FLAGED (mainquery, PT_SELECT_INFO_COLS_SCHEMA)
|| PT_SELECT_INFO_IS_FLAGED (mainquery, PT_SELECT_FULL_INFO_COLS_SCHEMA)))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for CONNECT BY */
if (PT_IS_SELECT (mainquery) && mainquery->info.query.q.select.connect_by)
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for MERGE query */
if (!is_vclass && PT_IS_SELECT (mainquery) && PT_SELECT_INFO_IS_FLAGED (mainquery, PT_SELECT_INFO_IS_MERGE_QUERY))
{
/* not pushable */
return NON_PUSHABLE;
}
if (!is_only_spec && (mq_is_outer_join_spec (parser, class_spec) || MQ_IS_OUTER_JOIN_SPEC (class_spec)))
{
/* view for single table + left outer join can be merged */
if (pt_length_of_list (subquery->info.query.q.select.from) != 1 || !MQ_IS_LEFT_JOIN_SPEC (class_spec)
|| mq_is_right_outer_join_spec (parser, class_spec) || pt_has_path_expr (parser, subquery)
|| !mq_check_keep_join_pred (parser, class_spec, mainquery, subquery, class_))
{
/* not pushable */
return NON_PUSHABLE;
}
}
/* determine if main query's where has define_vars ':=' */
if (pt_has_define_vars (parser, mainquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* subquery has order_by and main query has analytic or order-sensitive aggrigation */
if (subquery->info.query.order_by
&& (pt_has_analytic (parser, mainquery) || pt_has_order_sensitive_agg (parser, mainquery)
|| pt_has_expr_of_inst_in_sel_list (parser, select_list)))
{
/* not pushable */
return NON_PUSHABLE;
}
/* subquery has order_by and main query has inst_num */
if (subquery->info.query.order_by && pt_has_inst_in_where_and_select_list (parser, mainquery))
{
/* only can be mergeable in case of rownum only predicate and updatable order by */
/* query containing distinct, agg cannot add hidden cols, so orderby may be removed during view merging. */
if (!is_rownum_only || pt_has_aggregate (parser, mainquery))
{
/* not pushable */
return NON_PUSHABLE;
}
else if (pt_is_distinct (mainquery))
{
if (pt_length_of_list (select_list) == 1 && PT_IS_INSTNUM (select_list)
&& !pt_has_inst_or_orderby_num_in_where (parser, mainquery))
{
/* case of 'select distinct rownum from (subq)' can be view-merged */
}
else
{
return NON_PUSHABLE;
}
}
}
/*****************************/
/**** 2. SUB QUERY CHECK *****/
/*****************************/
/* check for non-SELECTs */
if (!PT_IS_SELECT (subquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for value query */
if (PT_IS_VALUE_QUERY (subquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for 'for update' */
if (PT_SELECT_INFO_IS_FLAGED (subquery, PT_SELECT_INFO_FOR_UPDATE))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for correlated subquery */
if (pt_is_correlated_subquery (subquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for CTE query */
/* TODO : Queries with CTE can be also view merged */
/* After view merging, 'spec->info.spec.cte_pointer' must be changed to the copied value of the newly added WITH CLAUSE. */
/* see pt_resolve_cte_specs() and pt_resolve_spec_to_cte() */
if (mainquery->info.query.with != NULL || subquery->info.query.with != NULL)
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for FROM */
if (subquery->info.query.q.select.from == NULL)
{
/* not pushable */
return NON_PUSHABLE;
}
/* determine if spec is outer joined and CTE spec */
for (PT_NODE * spec = subquery->info.query.q.select.from; spec; spec = spec->next)
{
if (!is_only_spec && mq_is_outer_join_spec (parser, spec))
{
/* subquery has outer joins; not pushable */
return NON_PUSHABLE;
}
if (PT_SPEC_IS_CTE (spec))
{
/* subquery has CTE spec; not pushable */
return NON_PUSHABLE;
}
}
/* check for CONNECT BY */
if (subquery->info.query.q.select.connect_by)
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for DISTINCT */
if (pt_is_distinct (subquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for aggregate or orderby_for or analytic */
if (pt_has_aggregate (parser, subquery) || is_orderby_for || pt_has_analytic (parser, subquery))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check inst num or orderby_num */
if (!is_only_spec && pt_has_inst_in_where_and_select_list (parser, subquery))
{
return NON_PUSHABLE;
}
/* check method */
cpi.check_query = false; /* subqueries are pushable */
cpi.check_method = true; /* methods are non-pushable */
cpi.method_found = false;
cpi.query_found = false;
parser_walk_tree (parser, subquery, pt_check_pushable, (void *) &cpi, NULL, NULL);
if (cpi.method_found)
{
/* query not pushable */
return NON_PUSHABLE;
}
/* if we got this far, query is pushable */
return PUSHABLE;
}
/*
* mq_is_removable_select_list () - check if a select_list is removalbe
* returns: true if pushable, false otherwise
* parser(in): parser context
* query(in): query to check
*
* NOTE:
* It is not removable in the following cases.
* - Not select query
* - value query
* - merge query
* - update, delete query
* - schema query
* - cte query
* - hierarchical query
* - has distinct
* - has method
* - sub query's select list has define_vars ':='
*/
static PUSHABLE_TYPE
mq_is_removable_select_list (PARSER_CONTEXT * parser, PT_NODE * subquery, PT_NODE * mainquery)
{
CHECK_PUSHABLE_INFO cpi;
/* NO_MERGE and QUERY_CACHE hint check */
if (subquery->info.query.q.select.hint & (PT_HINT_NO_MERGE | PT_HINT_QUERY_CACHE))
{
return NON_PUSHABLE;
}
/* check for select query */
if (!(PT_IS_SELECT (subquery) && PT_IS_SELECT (mainquery)))
{
return NON_PUSHABLE;
}
/* check for value query */
if (PT_IS_VALUE_QUERY (subquery))
{
return NON_PUSHABLE;
}
/* check for merge, update, delete, schema query */
if (PT_SELECT_INFO_IS_FLAGED (mainquery, PT_SELECT_INFO_IS_MERGE_QUERY | PT_SELECT_INFO_IS_UPD_DEL_QUERY
| PT_SELECT_INFO_COLS_SCHEMA | PT_SELECT_FULL_INFO_COLS_SCHEMA))
{
return NON_PUSHABLE;
}
/* check for DISTINCT */
if (pt_is_distinct (subquery))
{
return NON_PUSHABLE;
}
/* check for CONNECT BY */
if (mainquery->info.query.q.select.connect_by || subquery->info.query.q.select.connect_by)
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for sub query's select list has define_vars ':=' */
if (pt_has_define_vars (parser, subquery->info.query.q.select.list))
{
/* not pushable */
return NON_PUSHABLE;
}
/* check for CTE query */
if (mainquery->info.query.with != NULL || subquery->info.query.with != NULL)
{
return NON_PUSHABLE;
}
/* determine if spec is CTE spec */
for (PT_NODE * spec = subquery->info.query.q.select.from; spec; spec = spec->next)
{
if (PT_SPEC_IS_CTE (spec))
{
/* subquery has CTE spec; not pushable */
return NON_PUSHABLE;
}
}
return PUSHABLE;
}
/*
* mq_update_order_by() - update the position number of order by clause and
* add hidden column(s) at the end of the output list if
* necessary.
* return: PT_NODE *, parse tree with local db table/class queries expanded
* to local db expressions
* parser(in): parser context
* statement(in): statement into which class will be expanded
* query_spec(in): query of class that will be expanded
* class(in): class name of class that will be expanded
*
* Note:
* It includes 3 steps to update the position number of order by clause.
* 1) Get the attr info from the vclass;
* 2) Find the corresponding attr by the position number of order by clause;
* 3) Compare the corresponding attr with the attr of output list,
* a) if attr is in output list, update the position number of order by
* clause;
* b) if not, append a hidden column at the end of the output list, and
* update the position number of order by clause.
*
*/
static PT_NODE *
mq_update_order_by (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec, PT_NODE * class_,
PT_NODE * derived_spec, bool skip_adding_hidden_col)
{
PT_NODE *order, *val;
PT_NODE *attributes, *attr, *prev_order;
PT_NODE *save_data_type, *node, *result, *order_by;
PT_NODE *free_node = NULL, *save_next, *where;
PT_NODE *ord_num = NULL, *ins_num = NULL, *prev_orderby_for;
int attr_count;
int i;
UINTPTR spec_id;
assert (statement->node_type == PT_SELECT && query_spec->info.query.order_by != NULL);
order_by = parser_copy_tree_list (parser, query_spec->info.query.order_by);
/* 1 get vclass spec attrs */
if (class_ != NULL)
{
attributes = mq_fetch_attributes (parser, class_);
if (attributes == NULL)
{
return NULL;
}
spec_id = class_->info.name.spec_id;
}
else if (derived_spec != NULL)
{
attributes = derived_spec->info.spec.as_attr_list;
spec_id = derived_spec->info.spec.id;
}
else
{
return NULL;
}
attr_count = 0;
for (attr = attributes; attr != NULL; attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = spec_id;
attr_count++;
}
prev_order = NULL;
/* update the position number of order by clause */
order = order_by;
while (order != NULL)
{
assert (order->node_type == PT_SORT_SPEC);
val = order->info.sort_spec.expr;
assert (val->node_type == PT_VALUE);
save_next = order->next;
if (attr_count < val->info.value.data_value.i)
{
/* order by is a hidden column and not in attribute list, in this case, we need append a hidden column at the
* end of the output list. */
/* 2 find from spec columns */
for (i = 1, attr = query_spec->info.query.q.select.list; i < val->info.value.data_value.i; i++)
{
assert (attr != NULL);
attr = attr->next;
}
for (i = 1, node = statement->info.query.q.select.list; node != NULL; node = node->next)
{
i++;
}
node = NULL;
}
else
{
/* 2 find the corresponding attribute */
for (i = 1, attr = attributes; i < val->info.value.data_value.i; i++)
{
assert (attr != NULL);
attr = attr->next;
}
assert (attr != NULL && attr->node_type == PT_NAME);
save_data_type = attr->data_type;
attr->data_type = NULL;
for (node = statement->info.query.q.select.list, i = 1; node != NULL; node = node->next, i++)
{
/* 3 check whether attr is found in output list */
if (pt_name_equal (parser, node, attr))
{
/* if yes, update position number of order by clause */
val->info.value.data_value.i = i;
val->info.value.db_value.data.i = i;
val->info.value.text = NULL;
order->info.sort_spec.pos_descr.pos_no = i;
break;
}
}
attr->data_type = save_data_type;
}
/* if attr is not found in output list, append a hidden column at the end of the output list. */
if (node == NULL)
{
if (skip_adding_hidden_col)
{
/* remove unnecessary order */
if (prev_order == NULL)
{
order_by = save_next;
}
else
{
prev_order->next = save_next;
}
/* free order */
order->next = NULL;
free_node = parser_append_node (order, free_node);
}
else
{
/* add column of order to select list */
result = parser_copy_tree (parser, attr);
if (result == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
/* mark as a hidden column */
result->flag.is_hidden_column = 1;
parser_append_node (result, statement->info.query.q.select.list);
/* update position number of order by clause */
val->info.value.data_value.i = i;
val->info.value.db_value.data.i = i;
val->info.value.text = NULL;
order->info.sort_spec.pos_descr.pos_no = i;
prev_order = order;
}
}
else
{
prev_order = order;
}
order = save_next;
}
/* generate orderby_num(), inst_num() */
if (!(ord_num = parser_new_node (parser, PT_EXPR)) || !(ins_num = parser_new_node (parser, PT_EXPR)))
{
if (ord_num)
{
parser_free_tree (parser, ord_num);
}
PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
ord_num->type_enum = PT_TYPE_BIGINT;
ord_num->info.expr.op = PT_ORDERBY_NUM;
PT_EXPR_INFO_SET_FLAG (ord_num, PT_EXPR_INFO_ORDERBYNUM_C);
ins_num->type_enum = PT_TYPE_BIGINT;
ins_num->info.expr.op = PT_INST_NUM;
PT_EXPR_INFO_SET_FLAG (ins_num, PT_EXPR_INFO_INSTNUM_C);
/* Check whether orderby_num and rownum need to be changed */
if (statement->info.query.order_by == NULL && order_by != NULL)
{
/* case 1 : order by of mainquery is newly added ==> rownum of mainquery is changed to orderby_num */
statement->info.query.q.select.list =
pt_lambda_with_arg (parser, statement->info.query.q.select.list, ins_num, ord_num, false, 2, false);
/* move prev orderby_for to orderby_for */
if (query_spec->info.query.orderby_for)
{
prev_orderby_for = parser_copy_tree (parser, query_spec->info.query.orderby_for);
statement->info.query.orderby_for = parser_append_node (prev_orderby_for, statement->info.query.orderby_for);
}
/* replace rownum of where to orderby_num */
where = statement->info.query.q.select.where;
if (where != NULL && PT_EXPR_INFO_IS_FLAGED (where, PT_EXPR_INFO_ROWNUM_ONLY))
{
where = pt_lambda_with_arg (parser, where, ins_num, ord_num, false, 2, false);
/* move rownum only predicate to orderby_for */
statement->info.query.orderby_for = parser_append_node (where, statement->info.query.orderby_for);
statement->info.query.q.select.where = NULL;
}
}
else if (query_spec->info.query.order_by != NULL && order_by == NULL)
{
/* case 2 : order by of subqery is totally removed ==> orderby_num of subquery is changed to rownum */
/* if where of subquery has rownum, orderby_num, can not view-merged. so is not needed to change */
query_spec->info.query.q.select.list =
pt_lambda_with_arg (parser, query_spec->info.query.q.select.list, ord_num, ins_num, false, 2, false);
/* move orderby_for of subquery to where of mainquery */
if (query_spec->info.query.orderby_for)
{
prev_orderby_for = parser_copy_tree (parser, query_spec->info.query.orderby_for);
prev_orderby_for = pt_lambda_with_arg (parser, prev_orderby_for, ord_num, ins_num, false, 2, false);
statement->info.query.q.select.where =
parser_append_node (prev_orderby_for, statement->info.query.q.select.where);
}
}
else
{
/* if both the main and subqueries have order by, should not have orderby_for. can't be view-merged */
assert (query_spec->info.query.orderby_for == NULL);
}
parser_free_tree (parser, ord_num);
parser_free_tree (parser, ins_num);
statement->info.query.order_by = parser_append_node (order_by, statement->info.query.order_by);
if (free_node != NULL)
{
parser_free_tree (parser, free_node);
}
return statement;
}
/*
* mq_substitute_inline_view_in_statement() - This takes a subquery expansion of
* a class_, in the form of a select, or union of selects,
* and a parse tree containing references to the class and its attributes,
* and substitutes matching select expressions for each attribute,
* and matching referenced classes for each class
* return: PT_NODE *, parse tree with local db table/class queries
* expanded to local db expressions
* parser(in):
* statement(in):
* subquery(in):
* derived_spec(in):
* order_by(in):
*
* Note:
* 1) Order-by is passed down into sub portions of the unions, intersections and
* differences.
* This gives better algorithmic order when order by is present, allowing
* sorting on smaller pieces, followed by linear merges.
*
* 2) All/distinct is NOT similarly passed down, since it is NOT a transitive
* operation with mixtures of union, intersection and difference.
* It may be true that if the top level guy is distinct, you will
* get the same results if all sub levels are also distinct.
* Anyway, it is safe not to do this, and may be not be safe to do.
*
*/
static PT_NODE *
mq_substitute_inline_view_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * subquery,
PT_NODE * derived_spec, PT_NODE * order_by)
{
PT_NODE *tmp_result, *result, *statement_next;
PT_NODE *spec, *new_spec = NULL;
PUSHABLE_TYPE is_mergeable;
UINTPTR spec_id;
result = tmp_result = NULL; /* init */
is_mergeable = PUSHABLE;
statement_next = statement->next;
/* make a local copy of the statement */
tmp_result = parser_copy_tree (parser, statement);
if (tmp_result == NULL)
{
if (!pt_has_error (parser))
{
PT_INTERNAL_ERROR (parser, "failed to copy node tree");
}
goto exit_on_error;
}
/* Due to tree copy, the spec ids of methods are broken. Reset spec ids */
mq_reset_ids_in_methods (parser, tmp_result);
/* check whether subquery is pushable */
is_mergeable = mq_is_pushable_subquery (parser, subquery, tmp_result, derived_spec, false, order_by, NULL);
if (is_mergeable == HAS_ERROR)
{
goto exit_on_error;
}
if (is_mergeable == NON_PUSHABLE)
{
/* rewrite inline view spec */
/* remove unnecessary select list of subquery. */
/* TO_DO : support for union query */
if (mq_is_removable_select_list (parser, subquery, tmp_result) == PUSHABLE)
{
tmp_result = mq_remove_select_list_for_inline_view (parser, tmp_result, derived_spec, &new_spec);
if (tmp_result == NULL)
{
goto exit_on_error;
}
}
/* no translation per se, but need to fix up proxy objects */
spec_id = new_spec ? new_spec->info.spec.id : derived_spec->info.spec.id;
result = mq_fix_derived_in_union (parser, tmp_result, spec_id);
}
else
{
/* expand vclass_query in parent statement */
mq_copy_sql_hint (parser, tmp_result, subquery);
if (tmp_result->node_type == PT_SELECT)
{
if (!order_by && subquery->info.query.order_by)
{
/* update the position number of order by clause and add a hidden column into the output list if
* necessary. */
tmp_result =
mq_update_order_by (parser, tmp_result, subquery, NULL, derived_spec,
pt_is_distinct (tmp_result) || pt_has_aggregate (parser, tmp_result));
if (tmp_result == NULL)
{
goto exit_on_error;
}
}
}
result = mq_substitute_select_for_inline_view (parser, tmp_result, subquery, derived_spec);
}
/* set query id # */
if (result)
{
if (PT_IS_QUERY (result))
{
result->info.query.id = (UINTPTR) result;
}
}
else
{
goto exit_on_error;
}
if (result && PT_IS_QUERY (result))
{
if (subquery->info.query.all_distinct == PT_DISTINCT)
{
if (is_mergeable == NON_PUSHABLE)
{
/* result has been substituted. skip and go ahead */
}
else
{
result->info.query.all_distinct = PT_DISTINCT;
}
}
}
if (result)
{
result->next = statement_next;
}
return result;
exit_on_error:
if (tmp_result)
{
parser_free_tree (parser, tmp_result);
}
return NULL;
}
/*
* mq_substitute_subquery_in_statement() - This takes a subquery expansion of
* a class_, in the form of a select, or union of selects,
* and a parse tree containing references to the class and its attributes,
* and substitutes matching select expressions for each attribute,
* and matching referenced classes for each class
* return: PT_NODE *, parse tree with local db table/class queries
* expanded to local db expressions
* parser(in):
* statement(in):
* query_spec(in):
* class(in):
* order_by(in):
* what_for(in):
*
* Note:
* 1) Order-by is passed down into sub portions of the unions, intersections and
* differences.
* This gives better algorithmic order when order by is present, allowing
* sorting on smaller pieces, followed by linear merges.
*
* 2) All/distinct is NOT similarly passed down, since it is NOT a transitive
* operation with mixtures of union, intersection and difference.
* It may be true that if the top level guy is distinct, you will
* get the same results if all sub levels are also distinct.
* Anyway, it is safe not to do this, and may be not be safe to do.
*
*/
static PT_NODE *
mq_substitute_subquery_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec,
PT_NODE * class_, PT_NODE * order_by, int what_for)
{
PT_NODE *tmp_result, *result, *arg1, *arg2, *statement_next;
PT_NODE *class_spec, *statement_spec = NULL;
PT_NODE *derived_table, *derived_spec, *derived_class;
PT_NODE *attributes;
bool is_pushable_query, is_outer_joined;
bool is_only_spec;
PUSHABLE_TYPE is_mergeable;
result = tmp_result = NULL; /* init */
class_spec = NULL;
is_mergeable = PUSHABLE;
statement_next = statement->next;
switch (query_spec->node_type)
{
case PT_SELECT:
/* make a local copy of the statement */
tmp_result = parser_copy_tree (parser, statement);
if (tmp_result == NULL)
{
if (!pt_has_error (parser))
{
PT_INTERNAL_ERROR (parser, "failed to copy node tree");
}
goto exit_on_error;
}
/* Due to tree copy, the spec ids of methods are broken. Reset spec ids */
mq_reset_ids_in_methods (parser, tmp_result);
/* get statement spec */
switch (tmp_result->node_type)
{
case PT_SELECT:
statement_spec = tmp_result->info.query.q.select.from;
break;
case PT_UPDATE:
statement_spec = tmp_result->info.update.spec;
break;
case PT_DELETE:
statement_spec = tmp_result->info.delete_.spec;
break;
case PT_INSERT:
/* since INSERT can not have a spec list or statement conditions, there is nothing to check */
statement_spec = tmp_result->info.insert.spec;
break;
case PT_MERGE:
statement_spec = tmp_result->info.merge.into;
break;
default:
/* should not get here */
assert (false);
PT_INTERNAL_ERROR (parser, "unknown node");
goto exit_on_error;
}
/* check found spec */
class_spec = pt_find_spec (parser, statement_spec, class_);
if (class_spec == NULL)
{
/* class_'s spec was not found in spec list */
PT_INTERNAL_ERROR (parser, "class spec not found");
goto exit_on_error;
}
is_mergeable = mq_is_pushable_subquery (parser, query_spec, tmp_result, class_spec, true, order_by, class_);
if (is_mergeable == HAS_ERROR)
{
goto exit_on_error;
}
if (is_mergeable == NON_PUSHABLE)
{
/* rewrite vclass query as a derived table */
PT_NODE *tmp_class = NULL;
bool is_removable_select_list = mq_is_removable_select_list (parser, query_spec, tmp_result) == PUSHABLE;
/* rewrite vclass spec */
class_spec =
mq_rewrite_vclass_spec_as_derived (parser, tmp_result, class_spec, query_spec, is_removable_select_list);
/* get derived expending spec node */
if (!class_spec || !(derived_table = class_spec->info.spec.derived_table)
|| !(derived_spec = derived_table->info.query.q.select.from)
|| !(derived_class = derived_spec->info.spec.flat_entity_list))
{
goto exit_on_error;
}
tmp_class = parser_copy_tree (parser, class_);
if (tmp_class == NULL)
{
goto exit_on_error;
}
tmp_class->info.name.spec_id = derived_class->info.name.spec_id;
/* merge HINT of vclass spec */
mq_copy_sql_hint (parser, derived_table, query_spec);
if (query_spec->info.query.order_by)
{
/* update the position number of order by clause */
derived_table = mq_update_order_by (parser, derived_table, query_spec, tmp_class, NULL, false);
if (derived_table == NULL)
{
goto exit_on_error;
}
derived_table->info.query.flag.order_siblings = query_spec->info.query.flag.order_siblings;
}
class_spec->info.spec.derived_table =
mq_substitute_select_in_statement (parser, class_spec->info.spec.derived_table, query_spec, tmp_class);
if (tmp_class)
{
parser_free_tree (parser, tmp_class);
}
derived_table = class_spec->info.spec.derived_table;
if (derived_table == NULL)
{ /* error */
goto exit_on_error;
}
if (PT_IS_QUERY (derived_table))
{
if (query_spec->info.query.all_distinct == PT_DISTINCT)
{
derived_table->info.query.all_distinct = PT_DISTINCT;
}
}
result = tmp_result;
}
else
{
/* expand vclass_query in parent statement */
mq_copy_sql_hint (parser, tmp_result, query_spec);
if (tmp_result->node_type == PT_SELECT)
{
if (!order_by && query_spec->info.query.order_by)
{
/* update the position number of order by clause and add a hidden column into the output list if
* necessary. */
tmp_result =
mq_update_order_by (parser, tmp_result, query_spec, class_, NULL,
pt_is_distinct (tmp_result) || pt_has_aggregate (parser, tmp_result));
if (tmp_result == NULL)
{
goto exit_on_error;
}
}
}
result = mq_substitute_select_in_statement (parser, tmp_result, query_spec, class_);
}
/* set query id # */
if (result)
{
if (PT_IS_QUERY (result))
{
result->info.query.id = (UINTPTR) result;
}
}
else
{
goto exit_on_error;
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (pt_has_aggregate (parser, statement))
{
/* this error will not occur now unless there is a system error. The above condition will cause the query to
* be rewritten earlier. */
PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_REL_RESTRICTS_AGG_2);
result = NULL;
}
else if (statement->node_type == PT_SELECT)
{
PT_NODE *inside_order_by = NULL;
/* the vclasses are rewritten in mq_push_paths->mq_rewrite_vclass_spec_as_derived;
* this should guarantee that vclasses appear only in simple, generated SELECTs, without order_by;
*/
assert (order_by == NULL);
arg1 = query_spec->info.query.q.union_.arg1;
arg2 = query_spec->info.query.q.union_.arg2;
if (query_spec->info.query.order_by != NULL)
{
/* update the position number of order by clause */
statement =
mq_update_order_by (parser, statement, query_spec, class_, NULL, pt_is_distinct (statement)
|| pt_has_aggregate (parser, statement));
if (statement == NULL)
{
goto exit_on_error;
}
inside_order_by = statement->info.query.order_by;
statement->info.query.order_by = NULL;
}
arg1 = mq_substitute_subquery_in_statement (parser, statement, arg1, class_, order_by, what_for);
arg2 = mq_substitute_subquery_in_statement (parser, statement, arg2, class_, order_by, what_for);
if (arg1 && arg2)
{
result = mq_union_bump_correlation (parser, arg1, arg2);
/* reset node_type in case it was difference or intersection */
if (result)
{
result->node_type = query_spec->node_type;
}
}
else
{
if (query_spec->node_type == PT_INTERSECTION)
{
result = NULL;
}
else if (query_spec->node_type == PT_DIFFERENCE)
{
result = arg1;
}
else
{
if (arg1)
{
result = arg1;
}
else
{
result = arg2;
}
}
}
if (result != NULL)
{
if (query_spec->info.query.order_by != NULL)
{
result->info.query.order_by = inside_order_by;
result->info.query.flag.order_siblings = query_spec->info.query.flag.order_siblings;
}
if (query_spec->info.query.orderby_for != NULL)
{
result->info.query.orderby_for =
parser_append_node (parser_copy_tree_list (parser, query_spec->info.query.orderby_for),
result->info.query.orderby_for);
}
if (query_spec->info.query.limit != NULL)
{
result->info.query.limit =
parser_append_node (parser_copy_tree_list (parser, query_spec->info.query.limit),
result->info.query.limit);
result->info.query.flag.rewrite_limit = 1;
}
}
}
else if (statement->node_type == PT_UPDATE || statement->node_type == PT_DELETE)
{
/* create table t1(a int); create view v1 as select * from t1 t1 union select * from t1 t2; update t1,v1 set
* t1.a=v1.a; rewrite to update t1,(select * from t1 t1 union select * from t1 t2) v1 set t1.a=v1.a; delete
* a from t1 a,v1 b where a.a=b.a; rewrite to delete a from t1 a,(select * from t1 t1 union select * from t1
* t2) b where a.a=b.a */
tmp_result = parser_copy_tree (parser, statement);
if (tmp_result == NULL)
{
if (!pt_has_error (parser))
{
PT_INTERNAL_ERROR (parser, "failed to copy node tree");
}
goto exit_on_error;
}
if (statement->node_type == PT_UPDATE)
{
statement_spec = tmp_result->info.update.spec;
}
else /* PT_DELETE */
{
statement_spec = tmp_result->info.delete_.spec;
}
class_spec = pt_find_spec (parser, statement_spec, class_);
if (class_spec == NULL)
{
/* class_'s spec was not found in spec list */
PT_INTERNAL_ERROR (parser, "class spec not found");
goto exit_on_error;
}
class_spec = mq_rewrite_vclass_spec_as_derived (parser, tmp_result, class_spec, query_spec, false);
if (class_spec == NULL)
{
goto exit_on_error;
}
result = tmp_result;
}
else
{
/* cannot be here */
assert (0);
}
break;
default:
/* should not get here, that is an error! */
assert (0);
break;
}
if (result && PT_IS_QUERY (result))
{
if (query_spec->info.query.all_distinct == PT_DISTINCT)
{
if (is_mergeable == NON_PUSHABLE)
{
/* result has been substituted. skip and go ahead */
}
else
{
result->info.query.all_distinct = PT_DISTINCT;
}
}
}
if (result)
{
result->next = statement_next;
}
return result;
exit_on_error:
if (tmp_result)
{
parser_free_tree (parser, tmp_result);
}
return NULL;
}
/*
* mq_substitute_subquery_list_in_statement() - takes a subquery list and
* applies substitution to each one
* return: PT_NODE *, translated list of statements
* parser(in):
* statement(in):
* query_spec_list(in):
* class(in):
* order_by(in):
* what_for(in):
*/
static PT_NODE *
mq_substitute_subquery_list_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * query_spec_list,
PT_NODE * class_, PT_NODE * order_by, int what_for)
{
PT_NODE *query_spec = query_spec_list;
PT_NODE *result_list = NULL;
PT_NODE *result;
while (query_spec)
{
result = mq_substitute_subquery_in_statement (parser, statement, query_spec, class_, order_by, what_for);
if (result)
{
result_list = parser_append_node (result, result_list);
}
else if (er_errid_if_has_error () != NO_ERROR || pt_has_error (parser))
{
return NULL;
}
query_spec = query_spec->next;
}
return result_list;
}
/*
* mq_translatable_class() -
* return: 1 on translatable
* parser(in):
* class(in):
*/
static int
mq_translatable_class (PARSER_CONTEXT * parser, PT_NODE * class_)
{
/* if its a meta class_, its not translatable, even if its a meta vclass. */
if (class_->info.name.meta_class == PT_META_CLASS || class_->info.name.meta_class == PT_CLASSOID_ATTR)
{
return 0;
}
/* vclasses, aka views, are otherwise translatable */
if (db_is_vclass (class_->info.name.db_object) > 0)
{
return 1;
}
return 0;
}
/*
* mq_is_union_translation() - tests a spec for a union translation
* return:
* parser(in):
* spec(in):
*/
static int
mq_is_union_translation (PARSER_CONTEXT * parser, PT_NODE * spec)
{
PT_NODE *entity;
PT_NODE *subquery;
int had_some_real_classes = 0;
int had_some_virtual_classes = 0;
if (spec == NULL || spec->info.spec.flat_entity_list == NULL)
{
return false;
}
if (spec->info.spec.meta_class != PT_META_CLASS && spec->info.spec.derived_table_type != PT_IS_WHACKED_SPEC)
{
for (entity = spec->info.spec.flat_entity_list; entity != NULL; entity = entity->next)
{
if (!mq_translatable_class (parser, entity))
{
/* no translation for above cases */
had_some_real_classes++;
}
else
{
had_some_virtual_classes++;
subquery = mq_fetch_subqueries (parser, entity);
if (subquery == NULL && er_has_error ())
{
if (!pt_has_error (parser))
{
/* Some unexpected errors (like ER_INTERRUPTED due to timeout) should be handled. */
PT_ERROR (parser, entity, er_msg ());
}
return er_errid ();
}
if (subquery && subquery->node_type != PT_SELECT)
{
return true;
}
}
}
}
if (had_some_virtual_classes > 1)
return true;
if (had_some_virtual_classes && had_some_real_classes)
return true;
return false;
}
/*
* mq_check_authorization_path_entities() -
* return: NO_ERROR on success, non-zero for ERROR
* parser(in):
* class_spec(in):
* what_for(in):
*/
static int
mq_check_authorization_path_entities (PARSER_CONTEXT * parser, PT_NODE * class_spec, int what_for)
{
PT_NODE *path_spec, *entity;
int error;
bool skip_auth_check = false;
error = NO_ERROR; /* init */
/* check for authorization bypass: this feature should be used only for specs in SHOW statements; Note : all classes
* expanded under the current spec sub-tree will be skipped by the authorization process */
if (((int) class_spec->info.spec.auth_bypass_mask & what_for) == what_for)
{
assert (what_for != DB_AUTH_NONE);
skip_auth_check = true;
}
/* Traverse each path list */
for (path_spec = class_spec->info.spec.path_entities; path_spec != NULL; path_spec = path_spec->next)
{
/* Traverse entities */
for (entity = path_spec->info.spec.flat_entity_list; entity != NULL; entity = entity->next)
{
/* check for current path */
if (skip_auth_check)
{
continue;
}
error = db_check_authorization (entity->info.name.db_object, (DB_AUTH) what_for);
if (error != NO_ERROR)
{ /* authorization fails */
PT_ERRORmf2 (parser, entity, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON,
get_authorization_name ((DB_AUTH) what_for),
db_get_class_name (entity->info.name.db_object));
return error;
}
}
/* Traverse sub-path list */
error = mq_check_authorization_path_entities (parser, path_spec, what_for);
if (error != NO_ERROR)
{ /* authorization fails */
break;
}
}
return error;
}
/*
* mq_check_subqueries_for_prepare () -
* return:
* parser(in):
* node(in):
* subquery(in):
*/
static int
mq_check_subqueries_for_prepare (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * subquery)
{
if (node->flag.cannot_prepare == 1)
{
return 1;
}
while (subquery)
{
if (subquery->flag.cannot_prepare == 1)
{
return 1;
}
subquery = subquery->next;
}
return node->flag.cannot_prepare;
}
/*
* mq_translate_tree() - translates a tree against a list of classes
* return: PT_NODE *, parse tree with view and virtual class queries expanded
* to leaf classes or local db tables/classes
* parser(in):
* tree(in/out):
* spec_list(in):
* order_by(in):
* what_for(in):
*/
static PT_NODE *
mq_translate_tree (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE * spec_list, PT_NODE * order_by, int what_for)
{
PT_NODE *entity;
PT_NODE *class_spec;
PT_NODE *subquery;
PT_NODE *tree_union;
PT_NODE *my_class;
PT_NODE *first_tree, *prev_tree;
PT_NODE *real_classes;
PT_NODE *real_flat_classes;
PT_NODE *real_part;
PT_NODE *substituted;
PT_NODE *my_spec;
int had_some_virtual_classes;
int delete_old_node = false;
/* for each table/class in class list, do leaf expansion or vclass/view expansion. */
first_tree = tree;
for (class_spec = spec_list; class_spec != NULL; class_spec = class_spec->next)
{
/* need to loop through entity specs! Currently, theres no way to represent the all correct results in a parse
* tree or in xasl. */
bool skip_auth_check = false;
int update_flag = class_spec->info.spec.flag & PT_SPEC_FLAG_UPDATE;
int delete_flag = class_spec->info.spec.flag & PT_SPEC_FLAG_DELETE;
bool fetch_for_update;
PT_FETCH_AS fetch_as = PT_NORMAL_SELECT;
if ((what_for == DB_AUTH_SELECT) || (what_for == DB_AUTH_UPDATE && !update_flag)
|| (what_for == DB_AUTH_DELETE && !delete_flag))
{
/* used either in a query or an UPDATE/DELETE that does not alter the subquery */
fetch_for_update = false;
}
else
{
fetch_for_update = true;
if (what_for == DB_AUTH_UPDATE && tree->node_type != PT_MERGE)
{
/* we allow partial access for UPDATE statements */
fetch_as = PT_PARTIAL_SELECT;
}
}
had_some_virtual_classes = 0;
real_classes = NULL;
tree_union = NULL;
prev_tree = tree;
if (((int) class_spec->info.spec.auth_bypass_mask & what_for) == what_for)
{
assert (what_for != DB_AUTH_NONE);
skip_auth_check = true;
}
if (PT_SPEC_IS_DERIVED (class_spec))
{
subquery = class_spec->info.spec.derived_table;
if (class_spec->info.spec.derived_table_type == PT_IS_SUBQUERY && PT_IS_QUERY (subquery))
{
/* in-line view is merged into main query if it is possible */
delete_old_node = true;
tree = mq_substitute_inline_view_in_statement (parser, tree, subquery, class_spec, order_by);
if (tree == NULL)
{
return NULL;
}
}
else
{
/* no translation per se, but need to fix up proxy objects */
tree = mq_fix_derived_in_union (parser, tree, class_spec->info.spec.id);
}
/* check SELECT authorization rather than the authrization of opcode * */
/* always check if one has SELECT authorization for path-expr */
if (mq_check_authorization_path_entities (parser, class_spec, DB_AUTH_SELECT) != NO_ERROR)
{
return NULL; /* authorization fails */
}
}
else if (class_spec->info.spec.meta_class != PT_META_CLASS
&& (class_spec->info.spec.derived_table_type != PT_IS_WHACKED_SPEC))
{
for (entity = class_spec->info.spec.flat_entity_list; entity != NULL; entity = entity->next)
{
if (mq_translatable_class (parser, entity) == 0
|| (PT_IS_SELECT (tree)
&& (PT_SELECT_INFO_IS_FLAGED (tree, PT_SELECT_INFO_COLS_SCHEMA)
|| PT_SELECT_INFO_IS_FLAGED (tree, PT_SELECT_FULL_INFO_COLS_SCHEMA))))
{
/* no translation for above cases */
my_class = parser_copy_tree (parser, entity);
if (!my_class)
{
return NULL;
}
/* check for authorization bypass: this feature should be used only for specs in SHOW statements;
* Note : all classes expanded under the current spec sub-tree will be skipped by the authorization
* process */
if (!skip_auth_check
&& (db_check_authorization (my_class->info.name.db_object, (DB_AUTH) what_for) != NO_ERROR))
{
PT_ERRORmf2 (parser, entity, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON,
get_authorization_name ((DB_AUTH) what_for),
db_get_class_name (my_class->info.name.db_object));
return NULL;
}
my_class->next = real_classes;
real_classes = my_class;
}
else
{
had_some_virtual_classes = 1;
if (pt_has_error (parser))
{
return NULL;
}
if (PT_IS_FOR_PL_COMPILE (parser) && sm_is_system_vclass (entity->info.name.original))
{
continue;
}
if (!fetch_for_update)
{
subquery = mq_fetch_subqueries (parser, entity);
}
else
{
subquery = mq_fetch_subqueries_for_update (parser, entity, fetch_as, (DB_AUTH) what_for);
}
if (subquery != NULL)
{
if (tree == NULL || pt_has_error (parser))
{
/* an error was discovered parsing the sub query. */
return NULL;
}
tree->flag.cannot_prepare = mq_check_subqueries_for_prepare (parser, tree, subquery);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "\n<subqueries of %s are>\n %s\n", entity->info.name.original,
parser_print_tree_list (parser, subquery));
#endif /* CUBRID_DEBUG */
substituted =
mq_substitute_subquery_list_in_statement (parser, tree, subquery, entity, order_by, what_for);
#ifdef CUBRID_DEBUG
fprintf (stdout, "\n<substituted %s with subqueries is>\n %s\n", entity->info.name.original,
parser_print_tree_list (parser, substituted));
#endif /* CUBRID_DEBUG */
if (substituted != NULL)
{
if (tree_union != NULL)
{
if (what_for == DB_AUTH_SELECT)
{
tree_union = mq_union_bump_correlation (parser, tree_union, substituted);
if (tree_union && order_by)
{
tree_union->info.query.order_by = parser_copy_tree_list (parser, order_by);
}
}
else
{
parser_append_node (substituted, tree_union);
}
}
else
{
tree_union = substituted;
}
}
}
if (er_errid_if_has_error () != NO_ERROR || pt_has_error (parser))
{
return NULL;
}
}
}
/* check SELECT authorization rather than the authrization of opcode always check if one has SELECT
* authorization for path-expr */
if (mq_check_authorization_path_entities (parser, class_spec, DB_AUTH_SELECT) != NO_ERROR)
{
return NULL; /* authorization fails */
}
}
if (had_some_virtual_classes)
{
delete_old_node = true;
/* at least some of the classes were virtual were any "real" classes members of the class spec? */
real_part = NULL;
if (real_classes != NULL)
{
real_flat_classes = parser_copy_tree_list (parser, real_classes);
for (entity = real_classes; entity != NULL; entity = entity->next)
{
/* finish building new entity spec */
entity->info.name.resolved = NULL;
}
my_spec =
pt_entity (parser, real_classes, parser_copy_tree (parser, class_spec->info.spec.range_var),
real_flat_classes);
my_spec->info.spec.id = class_spec->info.spec.id;
real_part =
mq_class_lambda (parser, parser_copy_tree (parser, tree), real_flat_classes, my_spec, NULL, NULL, NULL,
NULL);
}
/* if the class spec had mixed real and virtual parts, recombine them. */
if (real_part != NULL)
{
if (tree_union != NULL)
{
tree = mq_union_bump_correlation (parser, real_part, tree_union);
}
else
{
/* there were some vclasses, but all have vacuous query specs. */
tree = real_part;
}
}
else if (tree_union != NULL)
{
tree = tree_union;
}
else
{
if (tree && tree->node_type != PT_SELECT && tree->node_type != PT_UNION
&& tree->node_type != PT_DIFFERENCE && tree->node_type != PT_INTERSECTION)
{
tree = NULL;
}
}
}
else
{
/* Getting here means there were NO vclasses. all classes involved are "real" classes, so don't rewrite this
* tree. */
}
/* free intermediate tree */
if (delete_old_node && prev_tree != tree && prev_tree != first_tree)
{
prev_tree->next = NULL;
parser_free_tree (parser, prev_tree);
}
}
/*
* We need to free first_tree at this point if the original tree pointer has
* been reassgned. We can't simply parser_free_tree() the node since the new tree
* may still have pointers to the lower nodes in the tree. So, we set
* the NEXT pointer to NULL and then free it so the new tree is not
* corrupted.
*/
if (delete_old_node && (tree != first_tree))
{
PT_NODE_COPY_NUMBER_OUTERLINK (tree, first_tree);
first_tree->next = NULL;
parser_free_tree (parser, first_tree);
}
tree = mq_reset_ids_in_statement (parser, tree);
return tree;
}
/*
* mq_class_meth_corr_subq_pre() - Checks for class methods or subqueries
* which are correlated level 1
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_class_meth_corr_subq_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
bool *found = (bool *) void_arg;
if (!node || *found)
return node;
*continue_walk = PT_CONTINUE_WALK;
if (PT_IS_CLASS_METHOD (node))
{
/* found class method */
*found = true;
}
else if (pt_is_query (node))
{
/* don't dive into subqueries */
*continue_walk = PT_LIST_WALK;
/* found correlated subquery */
if (node->info.query.correlation_level == 1)
{
*found = true;
}
}
else if (pt_is_aggregate_function (parser, node))
{
/* don't dive into aggreate functions */
*continue_walk = PT_LIST_WALK;
}
if (*found)
{
/* don't walk */
*continue_walk = PT_STOP_WALK;
}
return node;
} /* mq_class_meth_corr_subq_pre */
/*
* mq_has_class_methods_corr_subqueries
*
* Description:
*
* Returns true if the query contains class methods or
* subqueries which are correlated level 1.
*/
/*
* mq_has_class_methods_corr_subqueries() - checks class methods or
* subqueries which are correlated level 1
* return: true on checked
* parser(in):
* node(in):
*/
static bool
mq_has_class_methods_corr_subqueries (PARSER_CONTEXT * parser, PT_NODE * node)
{
bool found = false;
(void) parser_walk_tree (parser, node->info.query.q.select.list, mq_class_meth_corr_subq_pre, &found, NULL, NULL);
if (!found)
{
(void) parser_walk_tree (parser, node->info.query.q.select.having, mq_class_meth_corr_subq_pre, &found, NULL,
NULL);
}
return found;
} /* mq_has_class_methods_corr_subqueries */
/*
* pt_check_pushable() - check for pushable
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in):
*
* Note:
* subquery, method, rownum, inst_num(), orderby_num(), groupby_num()
* does not pushable if we find these in corresponding item
* in select_list of query
*/
static PT_NODE *
pt_check_pushable (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
CHECK_PUSHABLE_INFO *cinfop = (CHECK_PUSHABLE_INFO *) arg;
if (!tree || *continue_walk == PT_STOP_WALK)
{
return tree;
}
switch (tree->node_type)
{
case PT_SELECT:
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (cinfop->check_query)
{
cinfop->query_found = true; /* not pushable */
}
break;
case PT_METHOD_CALL:
if (cinfop->check_method)
{
cinfop->method_found = true; /* not pushable */
}
break;
case PT_EXPR:
if (PT_IS_EXPR_NODE_WITH_NON_PUSHABLE (tree))
{
cinfop->method_found = true;
}
break;
default:
break;
} /* switch (tree->node_type) */
if (cinfop->query_found || cinfop->method_found)
{
/* not pushable */
/* do not need to traverse anymore */
*continue_walk = PT_STOP_WALK;
}
return tree;
}
/*
* pt_check_pushable_select_list() - check for pushable
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in):
*
* Note:
* subquery, method does not pushable if we find these in corresponding item
* in select_list of query
*/
static PT_NODE *
pt_check_pushable_select_list (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
CHECK_PUSHABLE_INFO *cinfop = (CHECK_PUSHABLE_INFO *) arg;
if (!tree || *continue_walk == PT_STOP_WALK)
{
return tree;
}
switch (tree->node_type)
{
case PT_SELECT:
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (cinfop->check_query)
{
cinfop->query_found = true; /* not pushable */
}
break;
case PT_METHOD_CALL:
if (cinfop->check_method)
{
cinfop->method_found = true; /* not pushable */
}
break;
default:
break;
} /* switch (tree->node_type) */
if (cinfop->query_found || cinfop->method_found)
{
/* not pushable */
/* do not need to traverse anymore */
*continue_walk = PT_STOP_WALK;
}
return tree;
}
/*
* pt_check_pushable_subquery_select_list() -
* return: true on pushable query
* parser(in):
* query(in):
* pos(in):
* Note:
* subquery, method does not pushable if we find these in corresponding item
* in select_list of query
*/
static bool
pt_check_pushable_subquery_select_list (PARSER_CONTEXT * parser, PT_NODE * query, int pos)
{
bool pushable = false; /* guess as not pushable */
switch (query->node_type)
{
case PT_DBLINK_TABLE:
pushable = true;
break;
case PT_SELECT:
{
CHECK_PUSHABLE_INFO cinfo;
PT_NODE *list;
int i;
cinfo.check_query = true;
cinfo.check_method = true;
cinfo.query_found = false;
cinfo.method_found = false;
/* Traverse select list */
for (list = query->info.query.q.select.list, i = 0; list; list = list->next, i++)
{
if (i != pos)
{
continue;
}
switch (list->node_type)
{
case PT_SELECT:
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
cinfo.query_found = true; /* not pushable */
break;
case PT_METHOD_CALL:
cinfo.method_found = true; /* not pushable */
break;
case PT_EXPR:
if (PT_IS_EXPR_NODE_WITH_NON_PUSHABLE (list))
{
cinfo.method_found = true;
break;
}
[[fallthrough]];
default: /* do traverse */
parser_walk_leaves (parser, list, pt_check_pushable, &cinfo, NULL, NULL);
break;
} /* switch (list->node_type) */
/* check for subquery, method does not pushable in select_list of subquery */
if (cinfo.query_found || cinfo.method_found)
{
pushable = false;
break; /* not pushable */
}
else
{
pushable = true;
break;
}
} /* for (list = ...) */
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (pt_check_pushable_subquery_select_list (parser, query->info.query.q.union_.arg1, pos)
&& pt_check_pushable_subquery_select_list (parser, query->info.query.q.union_.arg2, pos))
{
pushable = true; /* OK */
}
break;
default: /* should not get here, that is an error! */
#if defined(CUBRID_DEBUG)
fprintf (stdout, "Illegal parse node type %d, in %s, at line %d. \n", query->node_type, __FILE__, __LINE__);
#endif /* CUBRID_DEBUG */
break;
} /* switch (query->node_type) */
return pushable;
}
/*
* pt_find_only_name_id() - returns true if node name with the given
* spec id is found
* return:
* parser(in):
* tree(in):
* arg(in/out):
* continue_walk(in):
*/
static PT_NODE *
pt_find_only_name_id (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
FIND_ID_INFO *infop = (FIND_ID_INFO *) arg;
PT_NODE *spec, *node = tree;
/* do not need to traverse anymore */
if (!tree || *continue_walk == PT_STOP_WALK)
{
return tree;
}
*continue_walk = PT_CONTINUE_WALK;
switch (node->node_type)
{
case PT_SELECT:
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
/* simply give up when we find query in predicate refer QA fixed/fderiv3.sql:line165 */
infop->out.others_found = true;
break;
case PT_METHOD_CALL:
/* simply give up when we find method in predicate */
infop->out.others_found = true;
break;
case PT_DOT_:
*continue_walk = PT_LIST_WALK;
/* only check left side of DOT expression, left side is of no interest */
do
{
node = node->info.dot.arg1;
}
while (node && node->node_type == PT_DOT_);
if (node == NULL)
{
/* nothing found ... */
break;
}
if (node->node_type != PT_NAME)
{
/* nothing found ... */
break;
}
[[fallthrough]];
case PT_NAME:
spec = infop->in.spec;
/* match specified spec */
if (!PT_IS_OID_NAME (node) && node->info.name.spec_id == spec->info.spec.id)
{
infop->out.found = true;
/* check for subquery, method: does not pushable if we find subquery, method in corresponding item in
* select_list of query */
if (infop->out.pushable)
{
PT_NODE *attr;
UINTPTR save_spec_id;
int i;
for (attr = infop->in.attr_list, i = 0; attr; attr = attr->next, i++)
{
if (attr->node_type != PT_NAME)
{
attr = NULL; /* unknown error */
break;
}
save_spec_id = attr->info.name.spec_id; /* save */
attr->info.name.spec_id = node->info.name.spec_id;
/* found match in as_attr_list */
if (pt_name_equal (parser, node, attr))
{
infop->out.pushable = pt_check_pushable_subquery_select_list (parser, infop->in.subquery, i);
break;
}
attr->info.name.spec_id = save_spec_id; /* restore */
} /* for (attr = ... ) */
if (!attr)
{
/* impossible case. simply give up */
infop->out.pushable = false;
}
}
}
else
{
/* check for other spec */
for (spec = infop->in.others_spec_list; spec; spec = spec->next)
{
if (node->info.name.spec_id == spec->info.spec.id)
{
infop->out.others_found = true;
break;
}
}
/* not found in other spec */
if (!spec)
{
/* is correlated other spec */
infop->out.correlated_found = true;
}
}
break;
case PT_EXPR:
/* simply give up when we find rownum, inst_num(), orderby_num() in predicate */
if (node->info.expr.op == PT_ROWNUM || node->info.expr.op == PT_INST_NUM || node->info.expr.op == PT_ORDERBY_NUM)
{
infop->out.others_found = true;
}
break;
case PT_FUNCTION:
/* simply give up when we find groupby_num() in predicate */
if (node->info.function.function_type == PT_GROUPBY_NUM)
{
infop->out.others_found = true;
}
break;
case PT_DATA_TYPE:
/* don't walk data type */
*continue_walk = PT_LIST_WALK;
break;
default:
break;
} /* switch (node->node_type) */
if (infop->out.others_found || !infop->out.pushable)
{
/* do not need to traverse anymore */
*continue_walk = PT_STOP_WALK;
}
return tree;
}
/*
* pt_check_pushable_term() -
* return:
* parser(in):
* term(in): CNF expression
* infop(in):
*/
static bool
pt_check_pushable_term (PARSER_CONTEXT * parser, PT_NODE * term, FIND_ID_INFO * infop)
{
bool is_correlated_with_agg = false;
bool is_correlated_with_dblink = false;
PT_NODE *derived;
/* init output section */
infop->out.found = false;
infop->out.others_found = false;
infop->out.correlated_found = false;
infop->out.pushable = true; /* guess as true */
parser_walk_leaves (parser, term, pt_find_only_name_id, infop, NULL, NULL);
if (infop->out.correlated_found)
{
if (pt_has_aggregate (parser, infop->in.subquery))
{
/* When a correlated term is pushed to a subquery that includes an aggregate function, */
/* group_by processing can be repeatedly performed. */
/* This may cause performance degradation. In this case, copypush is not performed. */
is_correlated_with_agg = true;
}
if (infop->in.spec)
{
derived = infop->in.spec->info.spec.derived_table;
if (derived->node_type == PT_DBLINK_TABLE)
{
/* When a correlated term is pushed to a subquery that includes a dblink */
/* the pushed predicated can be transferred to remote server */
/* This may cause error because the remote's query could not process the correlated term */
is_correlated_with_dblink = true;
}
}
}
return PT_PUSHABLE_TERM (infop) && !is_correlated_with_agg && !is_correlated_with_dblink;
}
/*
* pt_remove_cast_wrap_for_dblink () - copies exactly a term node excluding cast node for dblink,
and returns a pointer to the copy. It is eligible for a walk "pre" function
* return:
* parser(in):
* old_node(in):
* arg(in):
* continue_walk(in):
*/
static PT_NODE *
pt_remove_cast_wrap_for_dblink (PARSER_CONTEXT * parser, PT_NODE * old_node, void *arg, int *continue_walk)
{
PT_NODE *new_node = old_node;
if (old_node->node_type == PT_EXPR)
{
if (old_node->info.expr.op == PT_CAST && PT_EXPR_INFO_IS_FLAGED (old_node, PT_EXPR_INFO_CAST_WRAP))
{
new_node = old_node->info.expr.arg1;
parser_free_node (parser, old_node);
}
}
return new_node;
}
/*
* pt_copypush_terms() - push sargable term into the derived subquery
* return:
* parser(in):
* spec(in):
* query(in/out):
* term_list(in):
* type(in):
*
* Note:
* assumes cnf conversion is done
*/
static void
pt_copypush_terms (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * query, PT_NODE * term_list, FIND_ID_TYPE type)
{
PT_NODE *push_term_list;
PARSER_VARCHAR *rewritten = NULL;
PARSER_VARCHAR *pushed_pred, *query_str, *col_list;
unsigned int save_custom;
int max_pred_order;
if (query == NULL || term_list == NULL)
{
return;
}
switch (query->node_type)
{
case PT_SELECT:
/* copy terms */
push_term_list = parser_copy_tree_list (parser, term_list);
/* substitute as_attr_list's columns for select_list's columns in search condition */
if (type == FIND_ID_INLINE_VIEW)
{
push_term_list =
mq_lambda (parser, push_term_list, spec->info.spec.as_attr_list, query->info.query.q.select.list);
}
/* copy and put it in query's search condition */
if (pt_has_aggregate (parser, query))
{
/* push into HAVING clause */
query->info.query.q.select.having = parser_append_node (push_term_list, query->info.query.q.select.having);
}
else
{
/* Set predicates to be evaluated first */
max_pred_order = pt_get_max_pred_order (parser, push_term_list);
pt_set_pred_order (parser, query->info.query.q.select.where, max_pred_order + 1);
/* push into WHERE clause */
query->info.query.q.select.where = parser_append_node (push_term_list, query->info.query.q.select.where);
}
break;
case PT_DBLINK_TABLE:
if (pt_check_dblink_column_alias (parser, query) != NO_ERROR)
{
return;
}
/* copy terms */
query->info.dblink_table.pushed_pred = parser_copy_tree_list (parser, term_list);
/* remove the cast wrap from pushed predicate */
query->info.dblink_table.pushed_pred =
parser_walk_tree (parser, query->info.dblink_table.pushed_pred, pt_remove_cast_wrap_for_dblink, NULL, NULL,
NULL);
/* print the pushed predicates */
save_custom = parser->custom_print;
parser->custom_print |=
PT_CONVERT_RANGE | PT_SUPPRESS_RESOLVED | PT_PRINT_NO_HOST_VAR_INDEX | PT_PRINT_SUPPRESS_FOR_DBLINK;
pushed_pred = pt_print_and_list (parser, query->info.dblink_table.pushed_pred);
/* wrapped query SELECT * FROM */
rewritten = pt_append_bytes (parser, rewritten, "SELECT * FROM (", 15);
query_str = query->info.dblink_table.qstr->info.value.data_value.str;
rewritten = pt_append_varchar (parser, rewritten, query_str);
/* alias name: cublink */
rewritten = pt_append_bytes (parser, rewritten, ") cublink", 9);
#if 0
if (query->info.dblink_table.cols != NULL)
{
/* aliased column list */
rewritten = pt_append_bytes (parser, rewritten, "(", 1);
col_list = pt_print_bytes_l (parser, spec->info.spec.as_attr_list);
rewritten = pt_append_varchar (parser, rewritten, col_list);
rewritten = pt_append_bytes (parser, rewritten, ")", 1);
}
#endif
if (pushed_pred != NULL)
{
/* where predicate */
rewritten = pt_append_bytes (parser, rewritten, " WHERE ", 7);
rewritten = pt_append_varchar (parser, rewritten, pushed_pred);
}
query->info.dblink_table.rewritten = rewritten;
parser->custom_print = save_custom;
#if 0
printf ("===> rewriting query = %s\n", (char *) rewritten->bytes);
#endif
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
(void) pt_copypush_terms (parser, spec, query->info.query.q.union_.arg1, term_list, type);
(void) pt_copypush_terms (parser, spec, query->info.query.q.union_.arg2, term_list, type);
break;
default:
break;
} /* switch (query->node_type) */
return;
}
/*
* mq_is_rownum_only_predicate () - check if predicates only have rownum
* return: bool
* parser(in):
* spec(in):
* node(in):
*
* Note:
* arg1 op arg2
* rownum '= > <' no restriction
*/
bool
mq_is_rownum_only_predicate (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * node, PT_NODE * order_by,
PT_NODE * subquery, PT_NODE * class_)
{
PT_NODE *where, *from, *attributes, *query_spec_columns, *col, *attr, *pred;
PT_NODE *arg1, *arg2, *sub_where, *sub_sel_list, *sub_order_by, *save_next;
bool result;
if (!pt_is_select (node))
{
return false;
}
if (PT_IS_VALUE_QUERY (subquery))
{
return false;
}
/* check order by */
if (order_by)
{
return false;
}
/* check only spec */
if (spec->next != NULL)
{
return false;
}
/* subquery check */
if (!pt_is_select (subquery))
{
return false;
}
/* check if select list of mainquery has expr of instnum */
col = node->info.query.q.select.list;
if (pt_has_expr_of_inst_in_sel_list (parser, col))
{
return false;
}
/* check instnum, order_by of subquery */
sub_where = subquery->info.query.q.select.where;
sub_sel_list = subquery->info.query.q.select.list;
sub_order_by = subquery->info.query.order_by;
if (sub_order_by && (pt_has_inst_num (parser, sub_where) || pt_has_inst_num (parser, sub_sel_list)))
{
return false;
}
/* get attr_list */
if (PT_SPEC_IS_DERIVED (spec))
{
attributes = spec->info.spec.as_attr_list;
}
else if (class_ != NULL)
{
attributes = mq_fetch_attributes (parser, class_);
}
else
{
return false;
}
query_spec_columns = subquery->info.query.q.select.list;
col = query_spec_columns;
attr = attributes;
for (; col && attr; col = col->next, attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = spec->info.spec.id;
}
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col != NULL || attr != NULL)
{ /* error */
return false;
}
where = parser_copy_tree_list (parser, node->info.query.q.select.where);
/* substitute attributes for query_spec_columns in statement */
where = mq_lambda (parser, where, attributes, query_spec_columns);
result = true;
pred = where;
while (pred != NULL)
{
if (pred->or_next != NULL)
{
result = false;
break;
}
if (!PT_IS_EXPR_NODE_WITH_COMP_OP (pred))
{
result = false;
break;
}
arg1 = pred->info.expr.arg1;
arg2 = pred->info.expr.arg2;
/* check rownum and constant */
if (!(PT_IS_INSTNUM (arg1) || PT_IS_INSTNUM (arg2) || PT_IS_ORDERBYNUM (arg1) || PT_IS_ORDERBYNUM (arg2)))
{
result = false;
break;
}
pred = pred->next;
}
/* set PT_EXPR_INFO_ROWNUM_ONLY flag */
if (result)
{
pred = node->info.query.q.select.where;
while (pred != NULL)
{
PT_EXPR_INFO_SET_FLAG (pred, PT_EXPR_INFO_ROWNUM_ONLY);
pred = pred->next;
}
}
if (where != NULL)
{
parser_free_tree (parser, where);
}
return result;
}
/*
* mq_check_keep_join_pred () - check if join predicates don't convert to const predicates
* return: bool
* parser(in):
* spec(in):
* node(in):
*
* Note:
*/
bool
mq_check_keep_join_pred (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * node, PT_NODE * subquery, PT_NODE * class_)
{
PT_NODE *on_cond, *from, *attributes, *query_spec_columns, *col, *attr, *pred, *ori_on_cond, *ori_pred;
PT_NODE *arg1, *arg2, *sub_where, *sub_sel_list, *sub_order_by, *save_next, *ori_save_next;
int num_node, ori_num_node;
bool result;
if (PT_IS_VALUE_QUERY (subquery))
{
return false;
}
/* subquery check */
if (!pt_is_select (subquery))
{
return false;
}
/* get attr_list */
if (PT_SPEC_IS_DERIVED (spec))
{
attributes = spec->info.spec.as_attr_list;
}
else if (class_ != NULL)
{
attributes = mq_fetch_attributes (parser, class_);
}
else
{
return false;
}
query_spec_columns = subquery->info.query.q.select.list;
col = query_spec_columns;
attr = attributes;
for (; col && attr; col = col->next, attr = attr->next)
{
/* set spec_id */
attr->info.name.spec_id = spec->info.spec.id;
}
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col != NULL || attr != NULL)
{ /* error */
return false;
}
on_cond = parser_copy_tree_list (parser, spec->info.spec.on_cond);
/* substitute attributes for query_spec_columns in statement */
on_cond = mq_lambda (parser, on_cond, attributes, query_spec_columns);
result = true;
ori_on_cond = spec->info.spec.on_cond;
pred = on_cond;
ori_pred = ori_on_cond;
while (pred != NULL && ori_pred != NULL)
{
save_next = pred->next;
pred->next = NULL;
ori_save_next = ori_pred->next;
ori_pred->next = NULL;
num_node = ori_num_node = 0;
(void) parser_walk_tree (parser, pred, pt_count_name_nodes, &num_node, NULL, NULL);
(void) parser_walk_tree (parser, ori_pred, pt_count_name_nodes, &ori_num_node, NULL, NULL);
pred = pred->next = save_next;
ori_pred = ori_pred->next = ori_save_next;
/* check if join pred change to const pred */
if (ori_num_node >= 2 && ori_num_node != num_node)
{
result = false;
break;
}
}
if (on_cond != NULL)
{
parser_free_tree (parser, on_cond);
}
return result;
}
#if 0
/*
* mq_copypush_sargable_terms_dblink() -
* return:
* parser(in):
* statement(in):
* spec(in):
* new_query(in/out):
* infop(in):
*/
static int
mq_copypush_sargable_terms_dblink (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, PT_NODE * new_query,
FIND_ID_INFO * infop)
{
PT_NODE *term, *new_term, *push_term_list;
int push_cnt, copy_cnt;
PT_NODE *save_next;
/* init */
push_term_list = NULL;
push_cnt = 0;
for (term = statement->info.query.q.select.where; term; term = term->next)
{
if (pt_sargable_term (parser, term, infop) && PT_PUSHABLE_TERM (infop))
{
/* copy term */
new_term = parser_copy_tree (parser, term);
/* for term, mark as copy-pushed term */
if (term->node_type == PT_EXPR)
{
PT_EXPR_INFO_SET_FLAG (term, PT_EXPR_INFO_COPYPUSH);
}
push_term_list = parser_append_node (new_term, push_term_list);
push_cnt++;
}
}
if (push_cnt)
{
/* copy and push term in new_query's search condition */
(void) pt_copypush_terms (parser, spec, new_query, push_term_list, infop->type);
/* free alloced */
parser_free_tree (parser, push_term_list);
}
return push_cnt;
}
#endif
/*
* mq_is_dblink_pushable_term () - check if the predicate is pushable for dblink
* return: bool
* parser(in):
* term(in):
*/
static bool
mq_is_dblink_pushable_term (PARSER_CONTEXT * parser, PT_NODE * term)
{
if (term->node_type == PT_EXPR)
{
if (pt_is_operator_logical (term->info.expr.op) || pt_is_operator_arith (term->info.expr.op))
{
if (mq_is_dblink_pushable_term (parser, term->info.expr.arg1))
{
if (term->info.expr.arg2)
{
if (mq_is_dblink_pushable_term (parser, term->info.expr.arg2))
{
/* every argument is pushable */
return true;
}
/* some argument is not pushable */
return false;
}
/* every argument is pushable */
return true;
}
/* it's not pushable because the expression may have function-like */
return false;
}
else
{
/* wrapped cast should be pushed and it will be removed while rewriting the dblink query */
if (term->info.expr.op == PT_CAST && PT_EXPR_INFO_IS_FLAGED (term, PT_EXPR_INFO_CAST_WRAP))
{
/* it needs to check if the argument is pushable */
if (mq_is_dblink_pushable_term (parser, term->info.expr.arg1))
{
return true;
}
}
/* other expression like built-in and stored function and etc. */
return false;
}
}
switch (term->node_type)
{
case PT_NAME:
case PT_HOST_VAR:
case PT_VALUE:
return true;
default:
return false;
}
}
/*
* mq_copypush_sargable_terms_helper() -
* return:
* parser(in):
* statement(in):
* spec(in):
* new_query(in/out):
* infop(in):
* note :
* | SELECT ...
* 1.main <-| FROM ( subquery ) <== 2.suquery check
* | WHERE term <== 3.term check
*
* It is not pushable in the following cases.
* 1. main query check
* - hierarchical query
*
* 2. subquery check
* - has inst num or orderby_num
* - has analytic functions
*
* 3. term check
* - on_cond term
* - nullable-term of outer join spec
* - query in predicate(term)
* - method in predicate(term)
* - correlated column with aggregation in predicate(term)
* - OID column
*
* 4. select_list of subquery which is matched to term check
* - query in subquery_select_list
* - method in subquery_select_list
*/
static int
mq_copypush_sargable_terms_helper (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, PT_NODE * subquery,
FIND_ID_INFO * infop)
{
PT_NODE *term, *new_term, *push_term_list;
int push_cnt, push_correlated_cnt, copy_cnt;
PT_NODE *temp;
int nullable_cnt; /* nullable terms count */
PT_NODE *save_next, *in_spec;
bool is_outer_joined;
/* init */
push_term_list = NULL;
push_cnt = 0;
push_correlated_cnt = 0;
copy_cnt = -1;
/* 1.main query check (statement) */
/* NO_PUSH_PRED hint check */
if (statement->info.query.q.select.hint & PT_HINT_NO_PUSH_PRED)
{
return 0;
}
/* do NOT copy-push for a hierarchical query */
if (statement->info.query.q.select.connect_by != NULL)
{
return 0;
}
/* 2.subquery check */
if (subquery == NULL)
{
return 0;
}
/* do NOT copy-push for a cached query */
if (subquery->info.query.q.select.hint & PT_HINT_QUERY_CACHE)
{
return 0;
}
/* check inst num or orderby_num */
if (pt_has_inst_in_where_and_select_list (parser, subquery))
{
return 0;
}
/* subquery has analytic functions */
if (PT_IS_QUERY (subquery)
&& (pt_has_analytic (parser, subquery) || PT_SELECT_INFO_IS_FLAGED (subquery, PT_SELECT_INFO_COLS_SCHEMA)
|| PT_SELECT_INFO_IS_FLAGED (subquery, PT_SELECT_FULL_INFO_COLS_SCHEMA) || PT_IS_VALUE_QUERY (subquery)))
{
return 0;
}
/* 3.term check */
/* check outer join spec. */
is_outer_joined = mq_is_outer_join_spec (parser, spec);
/* term(predicate) check */
in_spec = statement->info.query.q.select.from;
for (term = statement->info.query.q.select.where; term; term = term->next)
{
/* check for dblink's function term */
if (in_spec->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE)
{
if (parser->flag.is_parsing_static_sql)
{
continue;
}
if (!mq_is_dblink_pushable_term (parser, term))
{
continue;
}
}
/* check for on_cond term */
assert (term->node_type == PT_EXPR || term->node_type == PT_VALUE);
if ((term->node_type == PT_EXPR && term->info.expr.location > 0)
|| (term->node_type == PT_VALUE && term->info.value.location > 0))
{
continue; /* do not copy-push on_cond-term */
}
/* check for nullable-term of outer join spec */
if (is_outer_joined && pt_has_nullable_term (parser, term))
{
continue;
}
if (pt_check_pushable_term (parser, term, infop))
{
/* copy term */
new_term = parser_copy_tree (parser, term);
/* for term, mark as copy-pushed term */
if (term->node_type == PT_EXPR)
{
PT_EXPR_INFO_SET_FLAG (term, PT_EXPR_INFO_COPYPUSH);
PT_EXPR_INFO_CLEAR_FLAG (new_term, PT_EXPR_INFO_COPYPUSH);
}
push_term_list = parser_append_node (new_term, push_term_list);
push_cnt++;
if (infop->out.correlated_found)
{
push_correlated_cnt++;
}
}
} /* for (term = ...) */
if (push_cnt)
{
/* copy and push term in new_query's search condition */
(void) pt_copypush_terms (parser, spec, subquery, push_term_list, infop->type);
if (push_correlated_cnt)
{
/* set correlation level */
if (subquery->info.query.correlation_level == 0)
{
pt_set_correlation_level (parser, subquery, statement->info.query.correlation_level + 1);
}
}
/* free alloced */
parser_free_tree (parser, push_term_list);
}
return push_cnt;
}
/*
* mq_copypush_sargable_terms() -
* return:
* parser(in):
* statement(in):
* spec(in):
*/
int
mq_copypush_sargable_terms (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
int push_cnt = 0; /* init */
FIND_ID_INFO info;
PT_NODE *subquery = NULL;
info.type = FIND_ID_INLINE_VIEW; /* inline view */
/* init input section */
info.in.spec = spec;
info.in.others_spec_list = statement->info.query.q.select.from;
info.in.attr_list = spec->info.spec.as_attr_list;
info.in.subquery = subquery = spec->info.spec.derived_table;
push_cnt = mq_copypush_sargable_terms_helper (parser, statement, spec, subquery, &info);
return push_cnt;
}
/*
* mq_rewrite_vclass_spec_as_derived() -
* return: rewritten SPEC with spec as simple derived select subquery
* parser(in):
* statement(in):
* spec(in):
* query_spec(in):
*/
static PT_NODE *
mq_rewrite_vclass_spec_as_derived (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, PT_NODE * query_spec,
bool remove_sel_list)
{
PT_NODE *new_query = parser_new_node (parser, PT_SELECT);
PT_NODE *new_spec, *v_attr_list;
PT_NODE *v_attr;
PT_NODE *from, *entity_name;
FIND_ID_INFO info;
PT_NODE *col;
bool is_value_query = false;
PT_NODE *attrs = NULL;
if (new_query == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
/* mark as a derived vclass spec query */
new_query->info.query.flag.vspec_as_derived = 1;
if (query_spec != NULL && PT_IS_VALUE_QUERY (query_spec))
{
is_value_query = true;
}
if (is_value_query)
{
new_query->flag.is_value_query = 1;
attrs = mq_fetch_attributes (parser, spec->info.spec.flat_entity_list);
if (attrs == NULL && (pt_has_error (parser) || er_has_error ()))
{
return NULL;
}
attrs = parser_copy_tree_list (parser, attrs);
if (attrs != NULL && attrs->type_enum == PT_TYPE_OBJECT)
{
attrs = attrs->next; /* skip oid */
/* copy node_list value query list has nothing to do with table attributes */
new_query->info.query.q.select.list = parser_copy_tree_list (parser, query_spec->info.query.q.select.list);
}
}
else
{
new_query->info.query.q.select.list = mq_get_references (parser, statement, spec);
for (col = new_query->info.query.q.select.list; col; col = col->next)
{
if (col->flag.is_hidden_column)
{
col->flag.is_hidden_column = 0;
}
}
if (remove_sel_list)
{
/* Do not add except for referenced columns. */
if (new_query->info.query.q.select.list == NULL)
{
/* case of constant attr. e.g.) count(*), count(1) */
/* just add one of integer value */
new_query->info.query.q.select.list = pt_make_integer_value (parser, 1);
}
}
else
{
/* add view's attributes to select list */
v_attr_list = mq_fetch_attributes (parser, spec->info.spec.flat_entity_list);
if (v_attr_list == NULL && (pt_has_error (parser) || er_has_error ()))
{
return NULL;
}
v_attr_list = parser_copy_tree_list (parser, v_attr_list);
/* exclude the first oid attr, append non-exist attrs to select list */
if (v_attr_list && v_attr_list->type_enum == PT_TYPE_OBJECT)
{
v_attr_list = v_attr_list->next; /* skip oid attr */
}
for (v_attr = v_attr_list; v_attr; v_attr = v_attr->next)
{
v_attr->info.name.spec_id = spec->info.spec.id; /* init spec id */
mq_insert_symbol (parser, &new_query->info.query.q.select.list, v_attr);
} /* for (v_attr = ...) */
/* free alloced */
if (v_attr_list)
{
parser_free_tree (parser, v_attr_list);
}
}
}
new_spec = parser_copy_tree (parser, spec);
if (new_spec == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
new_query->info.query.q.select.from = new_spec;
new_query->info.query.is_subquery = PT_IS_SUBQUERY;
new_query->flag.cannot_prepare = statement->flag.cannot_prepare;
/* free path entities, which will be handled by push_path */
parser_free_tree (parser, new_spec->info.spec.path_entities);
new_spec->info.spec.path_entities = NULL;
/* remove outer join info, which is included in the spec too */
new_spec->info.spec.join_type = PT_JOIN_NONE;
parser_free_tree (parser, new_spec->info.spec.on_cond);
new_spec->info.spec.on_cond = NULL;
/* free old class spec stuff */
parser_free_tree (parser, spec->info.spec.flat_entity_list);
spec->info.spec.flat_entity_list = NULL;
parser_free_tree (parser, spec->info.spec.except_list);
spec->info.spec.except_list = NULL;
parser_free_tree (parser, spec->info.spec.entity_name);
spec->info.spec.entity_name = NULL;
if (is_value_query)
{
for (v_attr = attrs; v_attr != NULL; v_attr = v_attr->next)
{
v_attr->info.name.spec_id = spec->info.spec.id;
mq_insert_symbol (parser, &spec->info.spec.as_attr_list, v_attr);
}
parser_free_tree (parser, attrs);
}
else
{
spec->info.spec.as_attr_list = parser_copy_tree_list (parser, new_query->info.query.q.select.list);
}
spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FROM_VCLASS);
if (PT_IS_SELECT (query_spec) && query_spec->info.query.q.select.connect_by)
{
/* query spec of the vclass is hierarchical */
new_query->info.query.q.select.connect_by =
parser_copy_tree_list (parser, query_spec->info.query.q.select.connect_by);
new_query->info.query.q.select.start_with =
parser_copy_tree_list (parser, query_spec->info.query.q.select.start_with);
new_query->info.query.q.select.after_cb_filter =
parser_copy_tree_list (parser, query_spec->info.query.q.select.after_cb_filter);
new_query->info.query.q.select.check_cycles = query_spec->info.query.q.select.check_cycles;
new_query->info.query.q.select.single_table_opt = query_spec->info.query.q.select.single_table_opt;
}
new_query = mq_reset_ids_and_references (parser, new_query, new_spec);
spec->info.spec.derived_table = new_query;
return spec;
}
/*
* mq_rewrite_dblink_as_derived -
* return: rewritten dblink as a derived table (like a subquery)
* parser(in):
* query(in): it should be a spec node with dblink
*/
static PT_NODE *
mq_rewrite_dblink_as_derived (PARSER_CONTEXT * parser, PT_NODE * query)
{
PT_NODE *new_query = NULL, *derived = NULL;
PT_NODE *range = NULL, *spec = NULL, *temp, *node = NULL;
PT_NODE **head;
int i = 0;
/* set line number to range name */
range = pt_name (parser, "_dbl");
if (range == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
/* construct new spec We are now copying the query and updating the spec_id references */
spec = parser_new_node (parser, PT_SPEC);
if (spec == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
derived = query;
spec->info.spec.derived_table = derived;
spec->info.spec.derived_table_type = PT_DERIVED_DBLINK_TABLE;
spec->info.spec.range_var = range;
spec->info.spec.id = (UINTPTR) spec;
range->info.name.spec_id = (UINTPTR) spec;
new_query = parser_new_node (parser, PT_SELECT);
if (new_query == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
new_query->info.query.q.select.from = spec;
temp = derived->info.dblink_table.cols;
head = &new_query->info.query.q.select.list;
while (temp)
{
/* we have the original name */
node = pt_name (parser, temp->info.attr_def.attr_name->info.name.original);
if (node == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
/* set line, column number */
node->line_number = temp->line_number;
node->column_number = temp->column_number;
node->info.name.meta_class = PT_NORMAL;
node->info.name.resolved = range->info.name.original;
node->info.name.spec_id = spec->info.spec.id;
node->type_enum = temp->type_enum;
node->data_type = parser_copy_tree (parser, temp->data_type);
spec->info.spec.as_attr_list = parser_append_node (node, spec->info.spec.as_attr_list);
*head = parser_copy_tree (parser, node);
head = &((*head)->next);
temp = temp->next;
}
/* move query id # */
new_query->info.query.id = query->info.query.id;
new_query->flag.recompile = query->flag.recompile;
return new_query;
exit_on_error:
if (node != NULL)
{
parser_free_node (parser, node);
}
if (new_query != NULL)
{
parser_free_node (parser, new_query);
}
if (spec != NULL)
{
parser_free_node (parser, spec);
}
if (range != NULL)
{
parser_free_node (parser, range);
}
return NULL;
}
/*
* mq_rewrite_query_as_derived () -
* return: rewritten select statement with derived table subquery
* parser(in):
* query(in):
*
* Note: returned result depends on global schema state.
* It was qo_rewrite_query_as_derived and moved to here to be public.
*/
PT_NODE *
mq_rewrite_query_as_derived (PARSER_CONTEXT * parser, PT_NODE * query)
{
PT_NODE *new_query = NULL, *derived = NULL;
PT_NODE *range = NULL, *spec = NULL, *temp, *node = NULL;
PT_NODE **head;
int i = 0;
/* set line number to range name */
range = pt_name (parser, "d3201");
if (range == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
/* construct new spec We are now copying the query and updating the spec_id references */
spec = parser_new_node (parser, PT_SPEC);
if (spec == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
derived = parser_copy_tree (parser, query);
derived = mq_reset_ids_in_statement (parser, derived);
/* increase correlation level of the query */
if (derived != NULL && query->info.query.correlation_level)
{
derived = mq_bump_correlation_level (parser, derived, 1, derived->info.query.correlation_level);
}
spec->info.spec.derived_table = derived;
spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
spec->info.spec.range_var = range;
spec->info.spec.id = (UINTPTR) spec;
range->info.name.spec_id = (UINTPTR) spec;
new_query = parser_new_node (parser, PT_SELECT);
if (new_query == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
if (query->info.query.correlation_level)
{
new_query->info.query.correlation_level = query->info.query.correlation_level;
}
new_query->info.query.q.select.from = spec;
new_query->info.query.is_subquery = query->info.query.is_subquery;
temp = pt_get_select_list (parser, spec->info.spec.derived_table);
head = &new_query->info.query.q.select.list;
while (temp)
{
/* generate as_attr_list */
if (temp->node_type == PT_NAME && temp->info.name.original != NULL)
{
/* we have the original name */
node = pt_name (parser, temp->info.name.original);
}
else
{
/* don't have name for attribute; generate new name */
node = pt_name (parser, mq_generate_name (parser, "a", &i));
}
if (node == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
goto exit_on_error;
}
/* set line, column number */
node->line_number = temp->line_number;
node->column_number = temp->column_number;
node->info.name.meta_class = PT_NORMAL;
node->info.name.resolved = range->info.name.original;
node->info.name.spec_id = spec->info.spec.id;
node->type_enum = temp->type_enum;
node->data_type = parser_copy_tree (parser, temp->data_type);
spec->info.spec.as_attr_list = parser_append_node (node, spec->info.spec.as_attr_list);
/* keep out hidden columns from derived select list */
if (query->info.query.order_by && temp->flag.is_hidden_column)
{
temp->flag.is_hidden_column = 0;
}
else
{
if (temp->node_type == PT_NAME && temp->info.name.meta_class == PT_SHARED)
{
/* This should not get lambda replaced during translation. Copy this node as-is rather than rewriting. */
*head = parser_copy_tree (parser, temp);
}
else
{
*head = parser_copy_tree (parser, node);
}
head = &((*head)->next);
}
temp = temp->next;
}
/* move query id # */
new_query->info.query.id = query->info.query.id;
new_query->flag.recompile = query->flag.recompile;
query->info.query.id = 0;
return new_query;
exit_on_error:
if (node != NULL)
{
parser_free_node (parser, node);
}
if (new_query != NULL)
{
parser_free_node (parser, new_query);
}
if (derived != NULL)
{
parser_free_node (parser, derived);
}
if (spec != NULL)
{
parser_free_node (parser, spec);
}
if (range != NULL)
{
parser_free_node (parser, range);
}
return NULL;
}
/*
* mq_rewrite_cte_as_derived () -
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in):
*
* Note: This function is used to rewrite CTE as a derived table.
*/
static PT_NODE *
mq_rewrite_cte_as_derived (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE **with_clause = NULL;
PT_NODE *cte_definition_list, *curr, *next;
switch (node->node_type)
{
case PT_SELECT:
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
if (node->info.query.with != NULL)
{
with_clause = &node->info.query.with;
}
break;
case PT_DELETE:
if (node->info.delete_.with != NULL)
{
with_clause = &node->info.delete_.with;
}
break;
case PT_UPDATE:
if (node->info.update.with != NULL)
{
with_clause = &node->info.update.with;
}
break;
default:
break;
}
if (with_clause == NULL || *with_clause == NULL)
{
return node;
}
mq_check_cte_inline_or_materialize (parser, *with_clause);
/* rewrite the main query considering the reference count. */
node = parser_walk_tree (parser, node, mq_inline_cte_pre, NULL, NULL, NULL);
cte_definition_list = (*with_clause)->info.with_clause.cte_definition_list;
curr = cte_definition_list;
while (curr)
{
if (curr->info.cte.recursive_part == NULL && !(curr->info.cte.is_materialized))
{
next = curr->next;
cte_definition_list = pt_remove_from_list (parser, curr, cte_definition_list);
curr = next;
continue;
}
curr = curr->next;
}
if (cte_definition_list != NULL)
{
(*with_clause)->info.with_clause.cte_definition_list = cte_definition_list;
}
else
{
parser_free_tree (parser, *with_clause);
*with_clause = NULL;
}
return node;
}
/*
* mq_check_cte_inline_or_materialize () -
* return:
* parser(in):
* node(in):
*
*/
static void
mq_check_cte_inline_or_materialize (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_NODE *cte;
PT_HINT_ENUM hint;
bool has_click_counter = false;
assert (node->node_type == PT_WITH_CLAUSE);
for (cte = node->info.with_clause.cte_definition_list; cte; cte = cte->next)
{
if (node->info.with_clause.recursive != 0)
{
/* if WITH RECURSIVE clause is used, CTE is always materialized */
cte->info.cte.is_materialized = true;
continue;
}
else if (cte->info.cte.recursive_part != NULL)
{
/* recursive CTE is always materialized when referenced at least once */
cte->info.cte.is_materialized = (cte->info.cte.referenced_count >= 1);
continue;
}
has_click_counter = false;
/* CTE containing functions like incr, rownum etc. cannot be rewritten as inline view
* since it may change the query results. Handle it same as CTE with materialize hint. */
(void) parser_walk_tree (parser, cte->info.cte.non_recursive_part,
mq_has_click_counter, &has_click_counter, NULL, NULL);
/* false subquery cannot be rewritten as inline view */
if (!has_click_counter && pt_is_query (cte->info.cte.non_recursive_part))
{
hint = pt_get_hint_from_query (parser, cte->info.cte.non_recursive_part);
if (hint &
(PT_HINT_MATERIALIZE_CTE | PT_HINT_SELECT_BTREE_NODE_INFO | PT_HINT_SELECT_KEY_INFO |
PT_HINT_QUERY_CACHE))
{
/* materialize CTE if it is referenced at least once. */
cte->info.cte.is_materialized = (cte->info.cte.referenced_count >= 1);
continue;
}
else if (hint & PT_HINT_INLINE_CTE)
{
cte->info.cte.is_materialized = false;
continue;
}
cte->info.cte.is_materialized = (cte->info.cte.referenced_count >= 2);
}
else
{
cte->info.cte.is_materialized = true;
}
}
}
/*
* mq_count_cte_references () -
* return:
* parser(in):
* node(in):
* arg(in):
*/
static PT_NODE *
mq_count_cte_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE *node_pointer, *cte;
if (node == NULL)
{
return NULL;
}
switch (node->node_type)
{
case PT_WITH_CLAUSE:
*continue_walk = PT_LIST_WALK;
break;
case PT_SPEC:
if (PT_SPEC_IS_CTE (node))
{
node_pointer = PT_SPEC_CTE_POINTER (node);
CAST_POINTER_TO_NODE (node_pointer);
assert (node_pointer->node_type == PT_CTE);
// count the references of indirectly referenced cte to prevent them from being removed or rewritten as inline views.
// cte with reference count less than 2 are rewritten as inline views or removed, so we need to count indirect references.
// e.g.
// with
// cte1 as (select /*+ materialize */ c1, c2 from t1), : directly = 0, indirectly = 1
// cte2 as (select c1, c2 from cte1) : directly = 1, indirectly = 0
// select /*+ recompile */ c1, c2 from cte2;
//
(void) parser_walk_tree (parser, node_pointer->info.cte.non_recursive_part, mq_count_cte_references, NULL,
pt_continue_walk, NULL);
node_pointer->info.cte.referenced_count++;
}
break;
default:
break;
}
return node;
}
/*
* mq_has_click_counter () -
* return:
* parser(in):
* node(in):
* arg(in):
*/
PT_NODE *
mq_has_click_counter (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
bool *has_click_counter = (bool *) arg;
if (node == NULL)
{
return NULL;
}
switch (node->node_type)
{
case PT_EXPR:
if (node->info.expr.op == PT_INCR || node->info.expr.op == PT_DECR)
{
*has_click_counter = true;
*continue_walk = PT_STOP_WALK;
}
break;
default:
break;
}
return node;
}
/*
* mq_inline_cte_pre () - This function is used to rewrite the CTE.
* return:
* parser(in):
* node(in):
* arg(in):
*/
static PT_NODE *
mq_inline_cte_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE *derived, *tbl_name, *attributes, *spec, *attr, *cte;
if (node == NULL)
{
return NULL;
}
switch (node->node_type)
{
case PT_SPEC:
if (PT_SPEC_IS_CTE (node))
{
cte = PT_SPEC_CTE_POINTER (node);
CAST_POINTER_TO_NODE (cte);
if (cte->info.cte.is_materialized)
{
return node;
}
attributes = parser_copy_tree_list (parser, cte->info.cte.as_attr_list);
derived = parser_copy_tree (parser, cte->info.cte.non_recursive_part);
tbl_name = parser_copy_tree (parser, cte->info.cte.name);
tbl_name->info.name.spec_id = node->info.spec.id;
for (attr = attributes; attr; attr = attr->next)
{
attr->info.name.meta_class = PT_NORMAL;
attr->info.name.spec_id = node->info.spec.id;
/* no need to copy data_type, because it is already created. */
}
node->info.spec.derived_table = derived;
node->info.spec.derived_table_type = PT_IS_SUBQUERY;
node->info.spec.as_attr_list = attributes;
node->info.spec.range_var = tbl_name;
if (node->info.spec.cte_pointer)
{
parser_free_tree (parser, node->info.spec.cte_pointer);
}
if (node->info.spec.cte_name)
{
parser_free_node (parser, node->info.spec.cte_name);
}
}
break;
default:
break;
}
return node;
}
/*
* mq_rewrite_aggregate_as_derived() -
* return: rewritten select statement with derived table
* subquery to form accumulation on
* parser(in):
* agg_sel(in):
*/
PT_NODE *
mq_rewrite_aggregate_as_derived (PARSER_CONTEXT * parser, PT_NODE * agg_sel)
{
PT_NODE *derived, *range, *spec;
PT_AGG_REWRITE_INFO info;
PT_NODE *col, *tmp, *as_attr_list;
int idx;
/* create new subquery as derived */
derived = parser_new_node (parser, PT_SELECT);
if (derived == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
/* move hint, from, where, group_by, using_index part over */
/* If the NO_MERGE hint moves to the derived subquery, it will not affect the agg_sel subquery.
* Therefore, the NO_MERGE hint is not moved to the derived subquery.
* Additionally, if the subquery has the QUERY_CACHE hint, it should not be merged, so it is treated together with the NO_MERGE hint.
* All hints except for NO_MERGE and QUERY_CACHE are moved to the derived subquery. */
derived->info.query.q.select.hint =
agg_sel->info.query.q.select.hint & ~(PT_HINT_NO_MERGE | PT_HINT_QUERY_CACHE | PT_HINT_NO_SUBQUERY_CACHE);
agg_sel->info.query.q.select.hint &= (PT_HINT_NO_MERGE | PT_HINT_QUERY_CACHE | PT_HINT_NO_SUBQUERY_CACHE);
derived->info.query.q.select.leading = agg_sel->info.query.q.select.leading;
agg_sel->info.query.q.select.leading = NULL;
derived->info.query.q.select.use_nl = agg_sel->info.query.q.select.use_nl;
agg_sel->info.query.q.select.use_nl = NULL;
derived->info.query.q.select.use_idx = agg_sel->info.query.q.select.use_idx;
agg_sel->info.query.q.select.use_idx = NULL;
derived->info.query.q.select.index_ss = agg_sel->info.query.q.select.index_ss;
agg_sel->info.query.q.select.index_ss = NULL;
derived->info.query.q.select.index_ls = agg_sel->info.query.q.select.index_ls;
agg_sel->info.query.q.select.index_ls = NULL;
derived->info.query.q.select.use_merge = agg_sel->info.query.q.select.use_merge;
agg_sel->info.query.q.select.use_merge = NULL;
derived->info.query.q.select.no_use_hash = agg_sel->info.query.q.select.no_use_hash;
agg_sel->info.query.q.select.no_use_hash = NULL;
derived->info.query.q.select.use_hash = agg_sel->info.query.q.select.use_hash;
agg_sel->info.query.q.select.use_hash = NULL;
derived->info.query.q.select.num_parallel_threads = agg_sel->info.query.q.select.num_parallel_threads;
/* keep agg_sel->info.query.q.select.num_parallel_threads unchanged */
derived->info.query.q.select.from = agg_sel->info.query.q.select.from;
agg_sel->info.query.q.select.from = NULL;
derived->info.query.q.select.where = agg_sel->info.query.q.select.where;
/* move original group_by to where in place */
agg_sel->info.query.q.select.where = agg_sel->info.query.q.select.having;
agg_sel->info.query.q.select.having = NULL;
derived->info.query.q.select.group_by = agg_sel->info.query.q.select.group_by;
agg_sel->info.query.q.select.group_by = NULL;
/* move agg flag */
PT_SELECT_INFO_SET_FLAG (derived, PT_SELECT_INFO_HAS_AGG);
PT_SELECT_INFO_CLEAR_FLAG (agg_sel, PT_SELECT_INFO_HAS_AGG);
derived->info.query.q.select.using_index = agg_sel->info.query.q.select.using_index;
agg_sel->info.query.q.select.using_index = NULL;
/* if the original statment is a merge, the new derived table becomes a merge and the outer select becomes a plain
* vanilla select. */
derived->info.query.q.select.flavor = agg_sel->info.query.q.select.flavor;
agg_sel->info.query.q.select.flavor = PT_USER_SELECT;
/* set correlation level */
derived->info.query.correlation_level = agg_sel->info.query.correlation_level;
if (derived->info.query.correlation_level)
{
derived = mq_bump_correlation_level (parser, derived, 1, derived->info.query.correlation_level);
}
/* derived tables are always subqueries */
derived->info.query.is_subquery = PT_IS_SUBQUERY;
derived->flag.cannot_prepare = agg_sel->flag.cannot_prepare;
/* move spec over */
info.from = derived->info.query.q.select.from;
info.derived_select = derived;
info.select_stack = pt_pointer_stack_push (parser, NULL, derived);
/* set derived range variable */
range = parser_copy_tree (parser, info.from->info.spec.range_var);
if (range == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
/* construct new spec */
spec = parser_new_node (parser, PT_SPEC);
if (spec == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
spec->info.spec.derived_table = derived;
spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
spec->info.spec.range_var = range;
spec->info.spec.id = (UINTPTR) spec;
range->info.name.spec_id = (UINTPTR) spec;
info.new_from = spec;
/* construct derived select list, convert agg_select names and paths */
info.depth = 0; /* init */
agg_sel->info.query.q.select.list =
parser_walk_tree (parser, agg_sel->info.query.q.select.list, mq_rewrite_agg_names, &info, mq_rewrite_agg_names_post,
&info);
info.depth = 0; /* init */
agg_sel->info.query.q.select.where =
parser_walk_tree (parser, agg_sel->info.query.q.select.where, mq_rewrite_agg_names, &info,
mq_rewrite_agg_names_post, &info);
/* cleanup */
(void) pt_pointer_stack_pop (parser, info.select_stack, NULL);
if (!derived->info.query.q.select.list)
{
/* we are doing something without names. Must be count(*) */
derived->info.query.q.select.list = pt_resolve_star (parser, derived->info.query.q.select.from, NULL);
/* reconstruct as_attr_list */
idx = 0;
as_attr_list = NULL;
for (col = derived->info.query.q.select.list; col; col = col->next)
{
tmp = pt_name (parser, mq_generate_name (parser, "a", &idx));
tmp->info.name.meta_class = PT_NORMAL;
tmp->info.name.resolved = range->info.name.original;
tmp->info.name.spec_id = spec->info.spec.id;
tmp->type_enum = col->type_enum;
tmp->data_type = parser_copy_tree (parser, col->data_type);
as_attr_list = parser_append_node (tmp, as_attr_list);
}
spec->info.spec.as_attr_list = as_attr_list;
}
agg_sel->info.query.q.select.from = spec;
return agg_sel;
}
/*
* mq_translate_select() - recursively expands each sub-query in the where part
* Then it expands this select statement against the classes which
* appear in the from list
* return: translated parse tree
* parser(in):
* select_statement(in):
*/
static PT_NODE *
mq_translate_select (PARSER_CONTEXT * parser, PT_NODE * select_statement)
{
PT_NODE *from;
PT_NODE *order_by = NULL;
PT_NODE *into = NULL;
PT_NODE *tree = NULL;
PT_MISC_TYPE all_distinct = PT_ALL;
int unique = 0;
if (select_statement)
{
from = select_statement->info.query.q.select.from;
order_by = select_statement->info.query.order_by;
select_statement->info.query.order_by = NULL;
into = select_statement->info.query.into_list;
select_statement->info.query.into_list = NULL;
all_distinct = select_statement->info.query.all_distinct;
/* for each table/class in select_statements from part, do leaf expansion or vclass/view expansion. */
tree = mq_translate_tree (parser, select_statement, from, order_by, DB_AUTH_SELECT);
if (tree == NULL)
{
if (pt_has_error (parser))
{
return NULL;
}
else if (er_has_error ())
{
/* Some unexpected errors (like ER_INTERRUPTED due to timeout) should be handled. */
PT_ERROR (parser, select_statement, er_msg ());
return NULL;
}
}
select_statement = tree;
}
/* restore the into part. and order by, if they are not already set. */
if (select_statement)
{
if (!select_statement->info.query.order_by)
{
select_statement->info.query.order_by = order_by;
}
select_statement->info.query.into_list = into;
if (all_distinct == PT_DISTINCT)
{
/* only set this to distinct. If the current spec is "all" bute the view is on a "distinct" query, the result
* is still distinct. */
select_statement->info.query.all_distinct = all_distinct;
}
}
/* check for order dependent nodes */
if (select_statement != NULL && select_statement->node_type == PT_SELECT
&& select_statement->info.query.order_by != NULL)
{
PT_NODE *list = NULL;
int order_dep_count = 0, list_pos = 1;
/* check select list for order dependent nodes */
list = select_statement->info.query.q.select.list;
while (list != NULL)
{
if (mq_is_order_dependent_node (list))
{
PT_NODE *sort_spec = select_statement->info.query.order_by;
bool is_sorted = false;
/* search in sort spec */
while (sort_spec != NULL)
{
is_sorted |= (sort_spec->info.sort_spec.pos_descr.pos_no == list_pos);
sort_spec = sort_spec->next;
}
if (!is_sorted)
{
/* mark nodes */
mq_mark_order_dependent_nodes (list);
order_dep_count++;
}
}
list = list->next;
list_pos++;
}
if (order_dep_count > 0)
{
/* rewrite statement */
select_statement = mq_rewrite_order_dependent_query (parser, select_statement, &unique);
/* reset IDs and recompile paths */
mq_reset_ids_in_statement (parser, select_statement);
}
}
/* check for analytic/aggregate combo */
if (pt_has_analytic (parser, select_statement) && pt_has_aggregate (parser, select_statement))
{
/* we can't process analytics and aggregates in the same statement; we must build a subquery for the aggregation
* and keep the parent statement for the analytics */
mq_rewrite_aggregate_as_derived (parser, select_statement);
mq_reset_ids_in_statement (parser, select_statement);
}
return select_statement;
}
/*
* mq_check_update() - checks duplicated column names
* return:
* parser(in):
* update_statement(in):
*/
static void
mq_check_update (PARSER_CONTEXT * parser, PT_NODE * update_statement)
{
pt_no_double_updates (parser, update_statement);
pt_no_attr_and_meta_attr_updates (parser, update_statement);
}
/*
* mq_check_delete() - checks for duplicate classes in table_list
* parser(in): parser context
* delete_stmt(in): delete statement
*
* NOTE: applies to multi-table delete statements:
* DELETE <table_list> FROM <table_reference> ...
* NOTE: all errors will be returned as parser errors
*/
static void
mq_check_delete (PARSER_CONTEXT * parser, PT_NODE * delete_stmt)
{
PT_NODE *table, *search;
if (delete_stmt == NULL)
{
return;
}
assert (delete_stmt->node_type == PT_DELETE);
for (table = delete_stmt->info.delete_.target_classes; table; table = table->next)
{
for (search = table->next; search; search = search->next)
{
/* check if search is duplicate of table */
if (!pt_user_specified_name_compare (table->info.name.resolved, search->info.name.resolved)
&& table->info.name.spec_id == search->info.name.spec_id)
{
/* same class found twice in table_list */
PT_ERRORmf (parser, search, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_CLASS_OR_ALIAS,
search->info.name.resolved);
return;
}
}
}
}
/*
* mq_translate_update() - leaf expansion or view expansion for update
* return:
* parser(in):
* update_statement(in):
*/
static PT_NODE *
mq_translate_update (PARSER_CONTEXT * parser, PT_NODE * update_statement)
{
PT_NODE *from;
PT_NODE save = *update_statement;
from = update_statement->info.update.spec;
/* translate statement */
update_statement = mq_translate_tree (parser, update_statement, from, NULL, DB_AUTH_UPDATE);
if (update_statement)
{
/* mark specs on translated tree and add oids where necessary */
pt_mark_spec_list_for_update (parser, update_statement);
/* check update statement */
mq_check_update (parser, update_statement);
}
if (update_statement == NULL && !pt_has_error (parser))
{
PT_ERRORm (parser, &save, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_UPDATE_EMPTY);
}
return update_statement;
}
/*
* mq_resolve_insert_statement() - resolve names in insert statement.
* return: insert statement with resolved names.
* parser(in):
* insert_statement(in/out):
*/
static PT_NODE *
mq_resolve_insert_statement (PARSER_CONTEXT * parser, PT_NODE * insert_statement)
{
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
PT_NODE *odku = insert_statement->info.insert.odku_assignments;
PT_NODE *from = insert_statement->info.insert.spec;
PT_NODE *attr;
/* reset spec_id for names not referencing the insert statement */
if (odku != NULL)
{
odku = parser_walk_tree (parser, odku, mq_clear_other_ids, from, NULL, NULL);
}
for (attr = insert_statement->info.insert.attr_list; attr != NULL; attr = attr->next)
{
attr = parser_walk_tree (parser, attr, mq_clear_other_ids, from, NULL, NULL);
}
/* do name resolving */
insert_statement = pt_resolve_names (parser, insert_statement, &sc_info);
if (insert_statement == NULL || pt_has_error (parser))
{
return insert_statement;
}
insert_statement = mq_reset_ids (parser, insert_statement, from);
if (odku != NULL)
{
/* need to check this in case something went wrong */
insert_statement = pt_check_odku_assignments (parser, insert_statement);
}
return insert_statement;
}
/*
* mq_replace_virtual_oid_with_real_oid () - Used for insert translate, forces
* sub-queries to select real
* instance OID's instead of virtual
* OID's.
*
* return : Parse tree updated node.
* parser (in) : Parser context.
* node (in) : Parse tree node.
* arg (in) : Void argument.
* continue_walk (in) : Continue walk.
*/
static PT_NODE *
mq_replace_virtual_oid_with_real_oid (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
int error = NO_ERROR;
if (node->node_type == PT_NAME)
{
if (node->info.name.meta_class == PT_VID_ATTR)
{
node->info.name.meta_class = PT_OID_ATTR;
if (node->data_type)
{
/* set virtual object to NULL and real class entity_name will be used */
node->data_type->info.data_type.virt_object = NULL;
}
}
else if (node->info.name.meta_class == PT_PARAMETER)
{
/* replace with an object value */
DB_VALUE db_val;
DB_OBJECT *obj = NULL;
PT_NODE *result = NULL;
pt_evaluate_tree_having_serial (parser, node, &db_val, 1);
if (pt_has_error (parser))
{
/* quit */
return node;
}
if (DB_VALUE_TYPE (&db_val) == DB_TYPE_VOBJ)
{
error = vid_vobj_to_object (&db_val, &obj);
if (error != NO_ERROR)
{
PT_ERRORc (parser, node, er_msg ());
return node;
}
db_make_object (&db_val, obj);
}
else if (DB_VALUE_TYPE (&db_val) == DB_TYPE_OBJECT)
{
obj = db_real_instance (db_get_object (&db_val));
db_make_object (&db_val, obj);
}
else
{
/* do nothing */
return node;
}
result = pt_dbval_to_value (parser, &db_val);
if (result == NULL)
{
PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
return node;
}
PT_NODE_MOVE_NUMBER_OUTERLINK (result, node);
parser_free_tree (parser, node);
return result;
}
}
return node;
}
/*
* mq_translate_insert() - leaf expansion or vclass/view expansion
* return:
* parser(in):
* insert_statement(in):
*/
static PT_NODE *
mq_translate_insert (PARSER_CONTEXT * parser, PT_NODE * insert_statement)
{
PT_NODE *from = NULL, *val = NULL, *attr = NULL, **val_hook = NULL;
PT_NODE *next = insert_statement->next;
PT_NODE *flat = NULL, *temp = NULL, **last = NULL;
PT_NODE save = *insert_statement;
PT_NODE *subquery = NULL;
PT_SPEC_INFO *from_spec = NULL;
bool viable;
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
int what_for = DB_AUTH_INSERT;
int is_class = 0;
insert_statement->next = NULL;
from = insert_statement->info.insert.spec;
if (insert_statement->info.insert.odku_assignments != NULL)
{
what_for = DB_AUTH_INSERT_UPDATE;
}
if (insert_statement->info.insert.do_replace)
{
assert (insert_statement->info.insert.odku_assignments == NULL);
what_for = DB_AUTH_REPLACE;
}
insert_statement = mq_translate_tree (parser, insert_statement, from, NULL, what_for);
/* check there are no double assignments after translate */
pt_no_double_insert_assignments (parser, insert_statement);
if (pt_has_error (parser))
{
return NULL;
}
if (insert_statement)
{
PT_NODE *t_save = insert_statement; /* save start node pointer */
PT_NODE *head = NULL;
PT_NODE *prev = NULL;
while (insert_statement)
{
PT_NODE *crt_list = insert_statement->info.insert.value_clauses;
bool multiple_tuples_insert = crt_list->next != NULL;
if (crt_list->info.node_list.list_type == PT_IS_VALUE)
{
/* deal with case 3 */
attr = insert_statement->info.insert.attr_list;
val_hook = &crt_list->info.node_list.list;
val = *val_hook;
while (attr && val)
{
if (val->node_type == PT_INSERT && val->etc)
{
PT_NODE *val_next = val->next;
PT_NODE *flat;
DB_OBJECT *real_class = NULL;
/* TODO what about multiple tuples insert? What should this code do? We give up processing for
* now. */
if (multiple_tuples_insert)
{
return NULL;
}
/* this is case 3 above. Need to choose the appropriate nested insert statement. */
/* do you solve your problem? */
if (head)
{ /* after the first loop */
val->next = head;
head = val;
val->etc = NULL;
}
else
{ /* the first loop */
val->next = (PT_NODE *) val->etc;
head = val;
val->etc = NULL;
}
if (attr->data_type && attr->data_type->info.data_type.entity)
{
real_class = attr->data_type->info.data_type.entity->info.name.db_object;
}
/* if there is a real class this must match, use it. otherwise it must be a "db_object" type, so
* any will do. */
if (real_class)
{
while (val)
{
if (val->info.insert.spec && (flat = val->info.insert.spec->info.spec.flat_entity_list)
&& flat->info.name.db_object == real_class)
{
break; /* found it */
}
prev = val;
val = val->next;
}
}
if (val)
{
if (val == head)
{
head = head->next;
}
else
{
prev->next = val->next;
}
}
else
{
val = parser_new_node (parser, PT_VALUE);
if (val == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
val->type_enum = PT_TYPE_NULL;
}
val->next = val_next;
/* and finally replace it */
*val_hook = val;
}
attr = attr->next;
val_hook = &val->next;
val = *val_hook;
}
}
insert_statement = insert_statement->next;
}
if (head)
{
parser_free_tree (parser, head);
}
insert_statement = t_save;
/* Now deal with case 1, 2 */
last = &insert_statement;
/* now pick a viable insert statement */
while (*last)
{
temp = *last;
from = temp->info.insert.spec;
from_spec = &(from->info.spec);
/* try to retrieve info from derived table */
if (from_spec->derived_table_type == PT_IS_SUBQUERY)
{
from = parser_copy_tree (parser, from_spec->derived_table->info.query.q.select.from);
temp->info.insert.spec = from;
temp = mq_resolve_insert_statement (parser, temp);
if (temp == NULL || pt_has_error (parser))
{
return NULL;
}
}
flat = from->info.spec.flat_entity_list;
if (flat == NULL)
{
if (from_spec->remote_server_name)
{
last = &temp->next;
continue;
}
assert (false);
return NULL;
}
viable = false;
is_class = db_is_class (flat->info.name.db_object);
if (is_class < 0)
{
return NULL;
}
if (is_class)
{
viable = true;
}
if (viable)
{
/* propagate temp's type information upward now because mq_check_insert_compatibility calls
* pt_class_assignable which expects accurate type information. */
sc_info.top_node = temp;
sc_info.donot_fold = false;
(void) pt_semantic_type (parser, temp, &sc_info);
}
/* here we just go to the next item in the list. If it is a nested insert, the correct one will be selected
* from the list at the outer level. If it is a top level insert, the correct one will be determined at run
* time. */
last = &temp->next;
}
}
if (insert_statement)
{
if (insert_statement->info.insert.is_subinsert == PT_IS_SUBINSERT)
{
/* kludge to pass along nested list */
insert_statement->etc = insert_statement->next;
insert_statement->next = next;
}
}
else if (!pt_has_error (parser))
{
PT_ERRORm (parser, &save, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_INSERT_EMPTY);
}
if (pt_has_error (parser))
{
return NULL;
}
subquery = pt_get_subquery_of_insert_select (insert_statement);
if (subquery != NULL)
{
(void) parser_walk_tree (parser, subquery, mq_replace_virtual_oid_with_real_oid, NULL, NULL, NULL);
}
if (subquery != NULL && PT_IS_SELECT (subquery) && insert_statement->info.insert.odku_assignments != NULL)
{
/* The odku_assignments might refer nodes from the SELECT statements. Even though name resolving was already
* performed on odku_assigments, we have to redo it here. If the SELECT target was a view, it has been rewritten
* by mq_translate_local and names which referenced it were not updated. */
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
PT_NODE *odku = insert_statement->info.insert.odku_assignments;
from = insert_statement->info.insert.spec;
/* reset spec_id for names not referencing the insert statement */
odku = parser_walk_tree (parser, odku, mq_clear_other_ids, from, NULL, NULL);
if (odku == NULL)
{
return NULL;
}
/* redo name resolving */
insert_statement = pt_resolve_names (parser, insert_statement, &sc_info);
if (insert_statement == NULL || pt_has_error (parser))
{
return NULL;
}
/* need to recheck this in case something went wrong */
insert_statement = pt_check_odku_assignments (parser, insert_statement);
}
/* no need rewrite names in case of dblink query */
if (insert_statement->info.insert.spec->info.spec.remote_server_name == NULL)
{
insert_rewrite_names_in_value_clauses (parser, insert_statement);
}
if (pt_has_error (parser))
{
return NULL;
}
return insert_statement;
}
/*
* mq_translate_delete() - leaf expansion or view expansion for delete
* return:
* parser(in):
* delete_statement(in):
*/
static PT_NODE *
mq_translate_delete (PARSER_CONTEXT * parser, PT_NODE * delete_statement)
{
PT_NODE *from;
PT_NODE save = *delete_statement;
/* translate */
from = delete_statement->info.delete_.spec;
delete_statement = mq_translate_tree (parser, delete_statement, from, NULL, DB_AUTH_DELETE);
if (delete_statement != NULL)
{
/* mark specs again on translated tree and add oids where necessary */
pt_mark_spec_list_for_delete (parser, delete_statement);
/* check delete statement */
mq_check_delete (parser, delete_statement);
if (pt_has_error (parser))
{
/* checking failed */
return NULL;
}
}
else if (!pt_has_error (parser))
{
PT_ERRORm (parser, &save, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_DELETE_EMPTY);
}
return delete_statement;
}
/*
* mq_check_merge() - checks duplicated column names
* return:
* parser(in):
* merge_statement(in):
*/
static void
mq_check_merge (PARSER_CONTEXT * parser, PT_NODE * merge_statement)
{
pt_no_double_updates (parser, merge_statement);
pt_no_attr_and_meta_attr_updates (parser, merge_statement);
}
/*
* mq_translate_merge() - leaf expansion or vclass/view expansion for merge
* return:
* parser(in):
* merge_statement(in):
*/
static PT_NODE *
mq_translate_merge (PARSER_CONTEXT * parser, PT_NODE * merge_statement)
{
PT_NODE *from, *flat;
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
DB_AUTH auth = DB_AUTH_NONE;
from = merge_statement->info.merge.into;
if (merge_statement->info.merge.update.assignment)
{
/* flag spec for update/delete */
auth = (DB_AUTH) (auth | DB_AUTH_UPDATE);
if (merge_statement->info.merge.update.has_delete)
{
auth = (DB_AUTH) (auth | DB_AUTH_DELETE);
}
}
if (merge_statement->info.merge.insert.value_clauses)
{
auth = (DB_AUTH) (auth | DB_AUTH_INSERT);
}
merge_statement = mq_translate_tree (parser, merge_statement, from, NULL, auth);
/* check statement */
if (merge_statement)
{
if (merge_statement->info.merge.update.assignment)
{
/* mark specs again and add oids where necessary */
pt_mark_spec_list_for_update (parser, merge_statement);
if (merge_statement->info.merge.update.has_delete)
{
pt_mark_spec_list_for_delete (parser, merge_statement);
}
}
(void) mq_check_merge (parser, merge_statement);
flat = merge_statement->info.merge.into->info.spec.flat_entity_list;
if (flat)
{
sc_info.top_node = merge_statement;
sc_info.donot_fold = false;
merge_statement = pt_semantic_type (parser, merge_statement, &sc_info);
}
}
return merge_statement;
}
/*
* mq_push_paths_select() -
* return:
* parser(in):
* statement(in): select statement
* spec(in): spec to examine as root of paths
*
* Note:
* 1) virtual paths rooted in virtual objects coming from
* query derived table pushed into those queries
* 2) virtual path specs of muliple virtual classes
* converted into a derived path spec
* 3) virtual paths specs of a single virtual class
* which translates to a union of real classes
* also being converted into a derived path spec
*/
static void
mq_push_paths_select (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
PT_NODE **paths;
PT_NODE *path;
PT_NODE *flat;
PT_NODE *subquery;
PT_NODE **path_next;
while (spec)
{
paths = &spec->info.spec.path_entities;
while (*paths)
{
path = *paths;
path_next = &path->next;
flat = path->info.spec.flat_entity_list;
if (flat)
{
if (db_is_class (flat->info.name.db_object) <= 0)
{
if (spec->info.spec.derived_table_type == PT_IS_SUBQUERY)
{
/* need to push this path inside spec */
*paths = path->next;
path->next = NULL;
mq_push_path (parser, statement, spec, path);
/* its gone, free it */
parser_free_tree (parser, path);
path_next = paths;
break; /* out of while */
}
else if (spec->info.spec.derived_table_type && db_is_vclass (flat->info.name.db_object) > 0)
{
subquery = mq_fetch_subqueries_for_update (parser, flat, PT_NORMAL_SELECT, DB_AUTH_SELECT);
if (!subquery || subquery->next || flat->next)
{
/* this is non-updatable, or turns into a union, It must be rewritten to a derived path join. */
*paths = path->next;
path->next = NULL;
path = mq_derived_path (parser, statement, path);
/* path is rewritten, put it back */
path->next = *paths;
path_next = &path->next;
*paths = path;
break; /* out of while */
}
}
}
}
/* if the path root is virtual, may need to fix up sub paths */
if (path->info.spec.path_entities)
{
mq_push_paths_select (parser, statement, path->info.spec.path_entities);
}
paths = path_next;
}
spec = spec->next;
}
}
/*
* mq_check_rewrite_select() -
* return: rewrited parse tree
* parser(in):
* select_statement(in):
*
* Note:
* 1) virtual specs which are part of a join AND which would
* translate to a union, are rewritten as derived.
* (This is an optimization to avoid multiplying the number
* of subqueries, joins and unions occuring.)
*
* 2) virtual specs of aggregate selects which would translate
* to a union are rewritten as derived.
*/
static PT_NODE *
mq_check_rewrite_select (PARSER_CONTEXT * parser, PT_NODE * select_statement)
{
PT_NODE *from;
int is_union_translation = 0;
assert (!PT_SELECT_INFO_IS_FLAGED (select_statement, PT_SELECT_INFO_COLS_SCHEMA | PT_SELECT_FULL_INFO_COLS_SCHEMA));
/* Convert to cnf and tag taggable terms */
select_statement->info.query.q.select.where = pt_cnf (parser, select_statement->info.query.q.select.where);
if (select_statement->info.query.q.select.having)
{
select_statement->info.query.q.select.having = pt_cnf (parser, select_statement->info.query.q.select.having);
}
from = select_statement->info.query.q.select.from;
if (from && (from->next || pt_has_aggregate (parser, select_statement)))
{
/* when translating joins, its important to maintain linearity of the translation. The cross-product of unions is
* exponential. Therefore, we convert cross-products of unions to cross-products of derived tables. */
is_union_translation = mq_is_union_translation (parser, from);
if (is_union_translation < NO_ERROR)
{
return NULL;
}
if (is_union_translation != 0)
{
select_statement->info.query.q.select.from = from =
mq_rewrite_vclass_spec_as_derived (parser, select_statement, from, NULL, false);
if (from == NULL)
{
return NULL;
}
}
while (from->next)
{
is_union_translation = mq_is_union_translation (parser, from->next);
if (is_union_translation < NO_ERROR)
{
return NULL;
}
if (is_union_translation != 0)
{
from->next = mq_rewrite_vclass_spec_as_derived (parser, select_statement, from->next, NULL, false);
}
from = from->next;
}
}
else
{
is_union_translation = false;
/* see 'xtests/10010_vclass_set.sql' and 'err_xtests/check21.sql' */
if (select_statement->info.query.is_view_spec == 0 && select_statement->info.query.oids_included == 0)
{
is_union_translation = mq_is_union_translation (parser, from);
if (is_union_translation < NO_ERROR)
{
return NULL;
}
if (is_union_translation != 0)
{
select_statement->info.query.q.select.from =
mq_rewrite_vclass_spec_as_derived (parser, select_statement, from, NULL, false);
}
}
}
return select_statement;
}
/*
* mq_push_paths() - rewrites from specs, and path specs, to things
* mq_translate_select can handle
* return:
* parser(in):
* statement(in):
* void_arg(in):
* cw(in):
*/
static PT_NODE *
mq_push_paths (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk)
{
PT_NODE *tmp_node = NULL;
if (statement == NULL)
{
return NULL;
}
switch (statement->node_type)
{
case PT_SELECT:
if (!PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_COLS_SCHEMA | PT_SELECT_FULL_INFO_COLS_SCHEMA))
{
tmp_node = mq_check_rewrite_select (parser, statement);
if (tmp_node == NULL)
{
if (!pt_has_error (parser) && er_has_error ())
{
/* Some unexpected errors (like ER_INTERRUPTED due to timeout) should be handled. */
PT_ERROR (parser, statement, er_msg ());
}
statement = tmp_node;
break;
}
statement = tmp_node;
}
if (!PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_IS_MERGE_QUERY))
{
mq_push_paths_select (parser, statement, statement->info.query.q.select.from);
}
break;
default:
statement = statement;
break;
}
return statement;
}
/*
* mq_rewrite_dblink_as_subquery () - rewrite dblink as a subquery
* return: PT_NODE *
* parser(in): parser environment
* node(in): possible dblink query
*/
static PT_NODE *
mq_rewrite_dblink_as_subquery (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *walk)
{
PT_NODE *spec, *derived = NULL;
PT_NODE *derived_table;
if (node->node_type != PT_SELECT)
{
return node;
}
for (spec = node->info.query.q.select.from; spec; spec = spec->next)
{
if ((derived_table = spec->info.spec.derived_table)
&& spec->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE)
{
derived = mq_rewrite_dblink_as_derived (parser, derived_table);
if (derived == NULL)
{
break;
}
derived->info.query.is_subquery = PT_IS_SUBQUERY;
spec->info.spec.derived_table = derived;
spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
}
}
*walk = PT_STOP_WALK;
return node;
}
/*
* mq_resolve_n_check_using_index () - check and resolve the using index clause
* return: PT_NODE *
* parser(in): parser environment
* aggregate_rewrote_as_derived(in):
* statement(in):
*/
static PT_NODE *
mq_resolve_n_check_using_index (PARSER_CONTEXT * parser, PT_NODE * statement, bool aggregate_rewrote_as_derived)
{
PT_NODE *indexp, *spec = NULL;
PT_NODE **using_index = NULL;
assert (statement);
switch (statement->node_type)
{
case PT_SELECT:
spec = statement->info.query.q.select.from;
if (aggregate_rewrote_as_derived && spec != NULL)
{
PT_NODE *derived_table = spec->info.spec.derived_table;
assert (PT_SPEC_IS_DERIVED (spec));
using_index = &derived_table->info.query.q.select.using_index;
spec = derived_table->info.query.q.select.from;
}
else
{
using_index = &statement->info.query.q.select.using_index;
}
break;
case PT_UPDATE:
using_index = &statement->info.update.using_index;
spec = statement->info.update.spec;
break;
case PT_DELETE:
using_index = &statement->info.delete_.using_index;
spec = statement->info.delete_.spec;
break;
default:
break;
}
/* resolve using index */
indexp = using_index ? *using_index : NULL;
if (indexp != NULL && spec != NULL)
{
bool is_ignore = false;
PT_NODE *prev = NULL;
while (indexp)
{
/*
** is_ignore will be set at pt_resolve_using_index().
** If is_ignore is true, ignore the error and remove the hint.
*/
if (pt_resolve_using_index (parser, indexp, spec, &is_ignore))
{
prev = indexp;
indexp = indexp->next;
}
else if (is_ignore == false)
{
return NULL;
}
else
{
// clear error
er_clearid ();
pt_reset_error (parser);
PT_NODE *tmp = indexp;
if (*using_index == indexp)
{
indexp = indexp->next;
*using_index = indexp;
}
else
{
prev->next = indexp->next;
indexp = indexp->next;
}
tmp->next = NULL;
parser_free_tree (parser, tmp);
}
}
}
/* semantic check on using index */
if (using_index != NULL)
{
if (mq_check_using_index (parser, *using_index) != NO_ERROR)
{
return NULL;
}
}
return statement;
}
/*
* mq_translate_local() - recursively expands each query against a view or
* virtual class
* return:
* parser(in):
* statement(in):
* void_arg(in):
* cw(in):
*/
static PT_NODE *
mq_translate_local (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk)
{
int line, column;
PT_NODE *next;
bool aggregate_rewrote_as_derived = false;
if (statement == NULL)
{
return statement;
}
next = statement->next;
statement->next = NULL;
/* try to track original source line and column */
line = statement->line_number;
column = statement->column_number;
switch (statement->node_type)
{
case PT_SELECT:
statement = mq_translate_select (parser, statement);
if (statement)
{
if (pt_has_aggregate (parser, statement) && mq_has_class_methods_corr_subqueries (parser, statement))
{
/* We need to push class methods or correlated subqueries from the select list into the derived table
* because we have no other way of generating correct XASL for correlated subqueries on aggregate
* queries. */
statement = mq_rewrite_aggregate_as_derived (parser, statement);
aggregate_rewrote_as_derived = true;
}
}
break;
case PT_UPDATE:
statement = mq_translate_update (parser, statement);
break;
case PT_INSERT:
statement = mq_translate_insert (parser, statement);
break;
case PT_DELETE:
statement = mq_translate_delete (parser, statement);
break;
case PT_MERGE:
statement = mq_translate_merge (parser, statement);
break;
default:
statement = statement;
break;
}
if (statement)
{
switch (statement->node_type)
{
case PT_SELECT:
statement->info.query.is_subquery = PT_IS_SUBQUERY;
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
statement->info.query.is_subquery = PT_IS_SUBQUERY;
mq_set_union_query (parser, statement->info.query.q.union_.arg1, PT_IS_UNION_SUBQUERY);
mq_set_union_query (parser, statement->info.query.q.union_.arg2, PT_IS_UNION_SUBQUERY);
break;
default:
break;
}
statement->line_number = line;
statement->column_number = column;
/* beware of simply restoring next because the newly rewritten statement can be a list. so we must append next
* to statement. (The number of bugs caused by this multipurpose use of node->next tells us it's not a good
* idea.) */
parser_append_node (next, statement);
/* resolving using index */
if (!pt_has_error (parser))
{
return mq_resolve_n_check_using_index (parser, statement, aggregate_rewrote_as_derived);
}
}
return statement;
}
/*
* mq_check_using_index() - check the using index clause for semantic errors
* return: error code
* parser(in): current parser
* using_index(in): list of PT_NODEs in USING INDEX clause
*/
static int
mq_check_using_index (PARSER_CONTEXT * parser, PT_NODE * using_index)
{
PT_NODE *hint_none = NULL, *hint_all_except = NULL;
PT_NODE *hint_use = NULL, *hint_force = NULL, *hint_ignore = NULL;
bool has_errors = false;
PT_NODE *index_hint = NULL;
PT_NODE *node = NULL, *search_node = NULL;
bool is_hint_class_none = false;
bool is_hint_use = false, is_hint_force = false, is_hint_ignore = false;
/* check for valid using_index node */
if (using_index == NULL)
{
return NO_ERROR;
}
/* Gathering basic information about the index hints */
node = using_index;
while (node != NULL)
{
if (node->etc == (void *) PT_IDX_HINT_NONE)
{
/* USING INDEX NONE node found */
assert (node->info.name.original == NULL && node->info.name.resolved == NULL);
hint_none = node;
}
else if (node->etc == (void *) PT_IDX_HINT_ALL_EXCEPT)
{
hint_all_except = node;
}
else if (node->etc == (void *) PT_IDX_HINT_CLASS_NONE)
{
is_hint_class_none = true;
}
else if (node->etc == (void *) PT_IDX_HINT_USE)
{
/* found USE INDEX idx or USING INDEX idx node */
assert (node->info.name.original != NULL && node->info.name.resolved != NULL);
is_hint_use = true;
if (hint_use == NULL)
{
hint_use = node;
}
}
else if (node->etc == (void *) PT_IDX_HINT_FORCE)
{
/* found FORCE INDEX idx or USING INDEX idx(+) node */
assert (node->info.name.original != NULL && node->info.name.resolved != NULL);
is_hint_force = true;
if (hint_force == NULL)
{
hint_force = node;
}
}
else if (node->etc == (void *) PT_IDX_HINT_IGNORE)
{
/* found IGNORE INDEX idx or USING INDEX idx(-) node */
assert (node->info.name.original != NULL && node->info.name.resolved != NULL);
is_hint_ignore = true;
if (hint_ignore == NULL)
{
hint_ignore = node;
}
}
else
{
/* all hint nodes must have the etc flag set from the grammar */
assert (false);
}
node = node->next;
}
/* check for USING INDEX NONE and {USE|FORCE} INDEX; error if both found */
if (hint_none != NULL)
{
assert (hint_all_except == NULL);
if (hint_use != NULL)
{
index_hint = hint_use;
has_errors = true;
}
else if (hint_force != NULL)
{
index_hint = hint_force;
has_errors = true;
}
if (has_errors)
{
/* {USE|FORCE} INDEX idx ... USING INDEX NONE case was found */
PT_ERRORmf2 (parser, using_index, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INDEX_HINT_CONFLICT,
"using index none", parser_print_tree (parser, index_hint));
return ER_PT_SEMANTIC;
}
}
else if (hint_all_except != NULL && (hint_use != NULL || hint_force != NULL || hint_ignore != NULL))
{
PT_ERRORmf2 (parser, using_index, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INDEX_HINT_CONFLICT,
"USING INDEX ALL EXCEPT", "{USE|FORCE|IGNORE} INDEX");
return ER_PT_SEMANTIC;
}
/*
* USING INDEX t.none is incompatible with {USE|FORCE} INDEX [t.]idx
* Check for USING INDEX class.NONE, class.any-index[(+)] or
* {USE|FORCE} INDEX (class.any-index) ... USING INDEX class.NONE
*/
node = using_index;
while (node != NULL && is_hint_class_none && (is_hint_use || is_hint_force))
{
if (node->info.name.original == NULL && node->info.name.resolved != NULL
&& node->etc == (void *) PT_IDX_HINT_CLASS_NONE)
{
/* search trough all nodes again and check for other index hints on class_name */
search_node = using_index;
while (search_node != NULL)
{
/*
* Case of comparing names after dot(.).
* 1. When comparing owner_name.class_name and class_name.
* class_name used in index_name must be in from. So, only class_name should be compared,
* not owner_name.
* e.g. select c1 from t1 use index (i1) where c1 >= 0 using index t1.none;
* - resolved_name of "use index (i1)" : "u1.t1"
* - resolved_name of "using index t1.none" : "t1"
*/
if (search_node->info.name.original != NULL && search_node->info.name.resolved != NULL
&& (search_node->etc == (void *) PT_IDX_HINT_USE || search_node->etc == (void *) PT_IDX_HINT_FORCE)
&& !pt_user_specified_name_compare (node->info.name.resolved, search_node->info.name.resolved))
{
/* class_name.idx_name and class_name.none found in USE INDEX and/or USING INDEX clauses */
PT_ERRORmf2 (parser, using_index, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INDEX_HINT_CONFLICT,
parser_print_tree (parser, node), parser_print_tree (parser, search_node));
return ER_PT_SEMANTIC;
}
search_node = search_node->next;
}
}
node = node->next;
}
/* no error */
return NO_ERROR;
}
/*
* mq_fetch_subqueries() - ask the schema manager for the cached parser
* containing the compiled subqueries of the class
* return:
* parser(in):
* class(in):
*/
PT_NODE *
mq_fetch_subqueries (PARSER_CONTEXT * parser, PT_NODE * class_)
{
PARSER_CONTEXT *query_cache;
DB_OBJECT *class_object;
if (!class_ || !(class_object = class_->info.name.db_object) || db_is_class (class_object))
{
return NULL;
}
query_cache = sm_virtual_queries (parser, class_object);
if (query_cache && query_cache->view_cache)
{
if (!(query_cache->view_cache->authorization & DB_AUTH_SELECT))
{
PT_ERRORmf (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_SEL_NOT_AUTHORIZED,
db_get_class_name (class_->info.name.db_object));
return NULL;
}
if (parser != NULL && query_cache->error_msgs != NULL)
{
mq_copy_view_error_msgs (parser, query_cache);
}
return query_cache->view_cache->vquery_for_query_in_gdb;
}
return NULL;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* mq_collapse_dot() -
* return: PT_NAME node with the printable form and type of a sub tree
* parser(in):
* tree(in): arg1 should be a name node.
*/
static PT_NODE *
mq_collapse_dot (PARSER_CONTEXT * parser, PT_NODE * tree)
{
PT_NODE *collapse;
PT_NODE *arg1, *arg2;
arg1 = tree->info.dot.arg1;
arg2 = tree->info.dot.arg2;
if (arg1->node_type != PT_NAME || arg2->node_type != PT_NAME)
return tree; /* bail out */
/* this path can be collapsed into a single thing (PT_NAME is used) */
collapse = parser_new_node (parser, PT_NAME);
if (collapse)
{
char *n;
collapse->info.name.spec_id = arg1->info.name.spec_id;
n = pt_append_string (parser, NULL, arg1->info.name.original);
n = pt_append_string (parser, n, ".");
n = pt_append_string (parser, n, arg2->info.name.original);
collapse->info.name.original = n;
collapse->info.name.meta_class = PT_NORMAL;
collapse->info.name.resolved = arg1->info.name.resolved;
collapse->info.name.custom_print = PT_SUPPRESS_QUOTES;
collapse->next = tree->next;
collapse->data_type = parser_copy_tree (parser, arg1->data_type);
collapse->type_enum = tree->type_enum;
tree->next = NULL;
parser_free_tree (parser, tree);
}
return collapse;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* mq_set_types() - sets the type of each item in the select list to
* match the class's attribute type
* return:
* parser(in):
* query_spec(in):
* attributes(in):
* vclass_object(in):
* cascaded_check(in):
*/
static PT_NODE *
mq_set_types (PARSER_CONTEXT * parser, PT_NODE * query_spec, PT_NODE * attributes, DB_OBJECT * vclass_object,
int cascaded_check)
{
PT_NODE *col, *prev_col, *next_col, *new_col;
PT_NODE *attr;
PT_NODE *col_type;
PT_NODE *attr_type;
PT_NODE *attr_class;
PT_NODE *flat = NULL;
if (query_spec == NULL)
{
return NULL;
}
switch (query_spec->node_type)
{
case PT_SELECT:
if (query_spec->info.query.q.select.from != NULL)
{
flat = query_spec->info.query.q.select.from->info.spec.flat_entity_list;
}
else
{
flat = NULL;
}
if (cascaded_check)
{
/* the piecemeal local check option list we have accumulated is now pointless. The user requested an honest
* to god useful check option, instead. */
parser_free_tree (parser, query_spec->info.query.q.select.check_where);
query_spec->info.query.q.select.check_where =
parser_copy_tree_list (parser, query_spec->info.query.q.select.where);
}
while (flat)
{
flat->info.name.virt_object = vclass_object;
flat = flat->next;
}
attr = attributes;
col = query_spec->info.query.q.select.list;
if (PT_IS_VALUE_QUERY (query_spec) && col != NULL && attr != NULL)
{
assert (col->node_type == PT_NODE_LIST);
col = col->info.node_list.list;
/* skip oid */
attr = attr->next;
}
prev_col = NULL;
while (col && attr)
{
/* should check type compatibility here */
if (attr->info.name.meta_class == PT_SHARED)
{
/* this should not get lambda replaced during translation. An easy way to emulate this is to simply
* overwrite the column that would be replacing this. */
next_col = col->next;
col->next = NULL;
parser_free_tree (parser, col);
new_col = parser_copy_tree (parser, attr);
new_col->info.name.db_object = vclass_object;
new_col->next = next_col;
if (prev_col == NULL)
{
query_spec->info.query.q.select.list = new_col;
query_spec->type_enum = new_col->type_enum;
if (query_spec->data_type)
{
parser_free_tree (parser, query_spec->data_type);
}
query_spec->data_type = parser_copy_tree_list (parser, new_col->data_type);
}
else
{
prev_col->next = new_col;
}
col = new_col;
}
else if (col->type_enum == PT_TYPE_NA || col->type_enum == PT_TYPE_NULL)
{
/* These are compatible with anything */
}
else if (attr->type_enum == PT_TYPE_OBJECT)
{
if ((attr_type = attr->data_type))
{
if (attr->info.name.meta_class == PT_OID_ATTR)
{
/* re-classify OID_ATTR as VID_ATTR */
if (col->node_type == PT_NAME)
col->info.name.meta_class = PT_VID_ATTR;
}
/* don't raise an error for the oid placeholder the column may not be an object for non-updatable
* views */
if (!(col_type = col->data_type) || col->type_enum != PT_TYPE_OBJECT)
{
if (attr != attributes)
{
PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_QSPEC_INCOMP_W_ATTR,
attr->info.name.original);
return NULL;
}
}
else
{
/*
* col_type->info.data_type.virt_type_enum
* IS ALREADY SET!. Don't muck with it.
*/
if ((attr_class = attr_type->info.data_type.entity))
{
if (db_is_vclass (attr_class->info.name.db_object) > 0)
{
col_type->info.data_type.virt_object = attr_class->info.name.db_object;
}
}
}
}
}
else if (col->type_enum != attr->type_enum)
{
if (col->node_type == PT_VALUE)
{
(void) pt_coerce_value (parser, col, col, attr->type_enum, attr->data_type);
/* this should also set an error code if it fails */
}
else
{ /* need to CAST */
new_col = pt_type_cast_vclass_query_spec_column (parser, attr, col);
if (new_col != col)
{
if (prev_col == NULL)
{
query_spec->info.query.q.select.list = new_col;
query_spec->type_enum = new_col->type_enum;
if (query_spec->data_type)
{
parser_free_tree (parser, query_spec->data_type);
}
query_spec->data_type = parser_copy_tree_list (parser, new_col->data_type);
}
else
{
prev_col->next = new_col;
}
col = new_col;
}
}
}
/* save previous link */
prev_col = col;
/* advance to next attribute and column */
attr = attr->next;
col = col->next;
}
/* skip hidden column */
while (col)
{
if (col->flag.is_hidden_column)
{
col = col->next;
continue;
}
break;
}
if (col)
{
PT_ERRORmf (parser, query_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_QSPEC_COLS_GT_ATTRS,
db_get_class_name (vclass_object));
return NULL;
}
if (attr)
{
PT_ERRORmf (parser, query_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS,
db_get_class_name (vclass_object));
return NULL;
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
mq_set_types (parser, query_spec->info.query.q.union_.arg1, attributes, vclass_object, cascaded_check);
mq_set_types (parser, query_spec->info.query.q.union_.arg2, attributes, vclass_object, cascaded_check);
break;
default:
/* could flag an error here, this should not happen */
break;
}
return query_spec;
}
/*
* mq_add_dummy_from_pre () - adds a dummy "FROM db-root" to view definitions
* that do not have one.
* Note: This is required so that the view handling code remains
* consistent with the assumption that each SELECT in a view
* has some hidden OID columns.
* This only happens for views or sub-queries of views.
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_add_dummy_from_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE *fake_from;
if (!node)
{
return node;
}
if (node->node_type != PT_SELECT || node->info.query.q.select.from != NULL)
{
return node;
}
fake_from = pt_add_table_name_to_from_list (parser, node, "db_root", NULL, DB_AUTH_NONE);
if (fake_from == NULL)
{
*continue_walk = PT_STOP_WALK;
return NULL;
}
return node;
}
/*
* mq_translate_subqueries() - Translates virtual instance population
* queries of any class
* return: a select or union of selects
* parser(in):
* class_object(in):
* attributes(in):
* authorization(in/out):
*/
static PT_NODE *
mq_translate_subqueries (PARSER_CONTEXT * parser, DB_OBJECT * class_object, PT_NODE * attributes,
DB_AUTH * authorization)
{
DB_QUERY_SPEC *db_query_spec;
PT_NODE **result;
PT_NODE *query_spec;
PT_NODE *statements;
PT_NODE *local_query;
const char *query_spec_string;
int cascaded_check;
int local_check;
if (db_is_class (class_object))
{
return NULL;
}
/* get query spec's */
db_query_spec = db_get_query_specs (class_object);
statements = NULL;
local_query = NULL;
cascaded_check = sm_get_class_flag (class_object, SM_CLASSFLAG_WITHCHECKOPTION);
if (cascaded_check < 0)
{
return NULL;
}
local_check = sm_get_class_flag (class_object, SM_CLASSFLAG_LOCALCHECKOPTION);
if (local_check < 0)
{
return NULL;
}
while (db_query_spec)
{
/* parse and compile the next query spec */
query_spec_string = db_query_spec_string (db_query_spec);
result = parser_parse_string_use_sys_charset (parser, query_spec_string);
/* a system error, that allowed a syntax error to be in a query spec string. May want to augment the error
* messages provided by parser_parse_string. */
if (!result)
{
return NULL;
}
query_spec = *result;
query_spec = parser_walk_tree (parser, query_spec, mq_add_dummy_from_pre, NULL, NULL, NULL);
if (query_spec == NULL)
{
return NULL;
}
parser_walk_tree (parser, query_spec, pt_set_is_view_spec, NULL, NULL, NULL);
/* apply semantic checks */
query_spec = pt_compile (parser, query_spec);
/* a system error, that allowed a semantic error to be in a query spec string. May want to augment the error
* messages provided by parser_parse_string. */
if (!query_spec)
return NULL;
if (local_check && query_spec->node_type == PT_SELECT)
{
/* We have a local check option for a simple select statement. This is the ANSI test case. It does not handle
* a union with local check option. However, updatable unions are a CUBRID extension, so big deal.
*
* * We capture the local where clause before appending the nested views where clauses. */
query_spec->info.query.q.select.check_where =
parser_copy_tree_list (parser, query_spec->info.query.q.select.where);
}
if (authorization)
{
/* if authorizations requested, compute them */
*authorization = (DB_AUTH) (*authorization & mq_compute_query_authorization (query_spec));
}
/* this will recursively expand the query spec into local queries. The mq_push_paths will convert the expression
* as CNF. if subquery is in the expression, it may be copied several times. To avoid repeatedly convert the
* expressions in subqueries and improve performance, we put mq_push_paths as post function. */
local_query = parser_walk_tree (parser, query_spec, NULL, NULL, mq_push_paths, NULL);
local_query = parser_walk_tree (parser, query_spec, NULL, NULL, mq_translate_local, NULL);
local_query = pt_add_row_oid_name (parser, local_query);
mq_set_types (parser, local_query, attributes, class_object, cascaded_check);
/* Reset references to positions in query_spec_string for each node in this tree. These nodes will be used in
* other contexts and these references are meaningless */
local_query = parser_walk_tree (parser, local_query, mq_reset_references_to_query_string, NULL, NULL, NULL);
if (local_query == NULL)
{
return NULL;
}
if (statements == NULL)
{
statements = local_query;
}
else if (local_query)
{
statements = pt_union (parser, statements, local_query);
}
db_query_spec = db_query_spec_next (db_query_spec);
}
return statements;
}
/*
* mq_invert_assign() - Translates invertible expression into an
* assignment expression
* return:
* parser(in):
* attr(in):
* expr(in):
*/
static void
mq_invert_assign (PARSER_CONTEXT * parser, PT_NODE * attr, PT_NODE * &expr, PT_NODE * inverted)
{
PT_NODE *result;
const char *attr_name;
assert (inverted != NULL);
result = parser_new_node (parser, PT_EXPR);
if (result == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return;
}
result->info.expr.op = PT_ASSIGN;
result->etc = attr;
attr_name = attr->info.name.original;
/* need to convert attr to value holder */
attr->node_type = PT_VALUE;
/* make info.value set up properly */
memset (&(attr->info), 0, sizeof (attr->info));
attr->info.value.text = attr_name;
result->info.expr.arg1 = inverted->next; /* name */
inverted->next = NULL;
attr->next = NULL;
result->info.expr.arg2 = inverted; /* right hand side */
expr = result;
}
/*
* mq_invert_subqueries() - Translates invertible subquery expressions into
* an assignment expression
* return:
* parser(in):
* select_statements(in):
* attributes(in):
*/
static void
mq_invert_subqueries (PARSER_CONTEXT * parser, PT_NODE * select_statements, PT_NODE * attributes)
{
PT_NODE **column = NULL;
PT_NODE *attr = NULL;
PT_NODE *column_next = NULL;
PT_NODE *attr_next = NULL;
PT_NODE **column_prev = NULL;
PT_NODE *inverted = NULL;
PT_NODE **head = NULL;
PT_NODE *temp = NULL;
while (select_statements)
{
column = &select_statements->info.query.q.select.list;
attr = parser_copy_tree_list (parser, attributes);
// save the head before deleting a node if necesserary
head = column;
while (attr)
{
column_next = (*column)->next;
attr_next = attr->next;
// try to invert the node
inverted = pt_invert (parser, *column, attr);
// to avoid creating a new "empty" node, we better delete the column from the list
if (inverted == NULL)
{
assert (!pt_has_error (parser));
temp = *column;
temp->next = NULL;
if (column_prev != NULL)
{
// link the previous node to the next node
(*column_prev)->next = column_next;
}
else
{
// move head to the right
head = &column_next;
}
// move forward in list
column = &((*column_prev)->next);
// avoid a mem leak
parser_free_tree (parser, temp);
}
else
{
// we change the column with the assignment
mq_invert_assign (parser, attr, *column, inverted);
if (*column == NULL)
{
break;
}
// set the link of the new node to next
(*column)->next = column_next;
column_prev = column;
// move forward in list
column = &((*column)->next);
}
attr = attr_next;
}
select_statements->info.query.q.select.list = *head;
select_statements = select_statements->next;
}
}
/*
* mq_set_non_updatable_oid() -
* return: none
* parser(in): the parser context used to derive stmt
* stmt(in/out): a SELECT/UNION/DIFFERENCE/INTERSECTION statement
* virt_entity(in):
*/
static void
mq_set_non_updatable_oid (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE * virt_entity)
{
PT_NODE *select_list;
if (!parser || !stmt)
{
return;
}
switch (stmt->node_type)
{
case PT_SELECT:
select_list = stmt->info.query.q.select.list;
if (select_list != NULL && !PT_IS_VALUE_QUERY (stmt))
{
DB_VALUE vid;
select_list->node_type = PT_FUNCTION;
/* make info set up properly */
memset (&(select_list->info), 0, sizeof (select_list->info));
select_list->data_type->info.data_type.entity = NULL;
select_list->data_type->info.data_type.virt_type_enum = PT_TYPE_SEQUENCE;
select_list->type_enum = PT_TYPE_OBJECT;
/* set vclass_name as literal string */
db_make_string (&vid, db_get_class_name (virt_entity->info.name.db_object));
select_list->info.function.arg_list = pt_dbval_to_value (parser, &vid);
select_list->info.function.function_type = F_SEQUENCE;
select_list->data_type->info.data_type.virt_object = virt_entity->info.name.db_object;
pr_clear_value (&vid);
}
break;
case PT_UNION:
case PT_INTERSECTION:
case PT_DIFFERENCE:
mq_set_non_updatable_oid (parser, stmt->info.query.q.union_.arg1, virt_entity);
mq_set_non_updatable_oid (parser, stmt->info.query.q.union_.arg2, virt_entity);
break;
default:
break;
}
}
/*
* mq_check_cycle() -
* return: true if the class object is found in the cycle detection buffer
* fasle if not found, and add the object to the buffer
* class_object(in):
*/
static bool
mq_check_cycle (DB_OBJECT * class_object)
{
unsigned int i, max, enter;
enter = top_cycle % MAX_CYCLE;
max = top_cycle < MAX_CYCLE ? top_cycle : MAX_CYCLE;
for (i = 0; i < max; i++)
{
if (cycle_buffer[i] == class_object)
{
return true;
}
}
/* otherwise increment top cycle and enter object in buffer */
cycle_buffer[enter] = class_object;
top_cycle++;
return false;
}
/*
* mq_free_virtual_query_cache() - Clear parse trees used for view translation,
* and the cached parser
* return: none
* parser(in):
*/
void
mq_free_virtual_query_cache (PARSER_CONTEXT * parser)
{
VIEW_CACHE_INFO *info;
/* symbols is used to hold the virtual query cache */
info = (VIEW_CACHE_INFO *) parser->view_cache;
parser_free_tree (parser, info->attrs);
parser_free_tree (parser, info->vquery_for_query);
parser_free_tree (parser, info->vquery_for_query_in_gdb);
parser_free_tree (parser, info->vquery_for_update);
parser_free_tree (parser, info->vquery_for_update_in_gdb);
parser_free_tree (parser, info->vquery_for_partial_update);
parser_free_tree (parser, info->inverted_vquery_for_update);
parser_free_tree (parser, info->inverted_vquery_for_update_in_gdb);
parser_free_parser (parser);
return;
}
/*
* mq_virtual_queries() - recursively expands each query against a view or
* virtual class
* return:
* class_object(in):
*/
PARSER_CONTEXT *
mq_virtual_queries (DB_OBJECT * class_object)
{
char buf[2000];
const char *cname = db_get_class_name (class_object);
PARSER_CONTEXT *parser = parser_create_parser ();
PT_NODE **statements;
VIEW_CACHE_INFO *symbols;
DB_OBJECT *me = db_get_user ();
DB_OBJECT *owner = db_get_owner (class_object);
if (parser == NULL)
{
return NULL;
}
snprintf (buf, sizeof (buf), "select * from [%s]; ", cname);
statements = parser_parse_string (parser, buf);
parser->view_cache = (VIEW_CACHE_INFO *) parser_alloc (parser, sizeof (VIEW_CACHE_INFO));
symbols = parser->view_cache;
if (symbols == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_alloc");
return NULL;
}
symbols->nested_views = NULL;
if (!ws_is_same_object (owner, me))
{
symbols->authorization = mq_compute_authorization (class_object);
}
else
{
/* no authorization check */
symbols->authorization = DB_AUTH_ALL;
}
if (statements)
{
if (mq_check_cycle (class_object))
{
PT_ERRORmf (parser, statements[0], MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_CYCLIC_QUERY_SPEC, cname);
}
}
if (statements && !pt_has_error (parser))
{
parser->oid_included = PT_INCLUDE_OID_TRUSTME;
statements[0] = pt_compile (parser, statements[0]);
statements[0] = pt_add_row_oid_name (parser, statements[0]);
if (statements[0] && !pt_has_error (parser))
{
symbols->attrs = statements[0]->info.query.q.select.list;
symbols->number_of_attrs = pt_length_of_select_list (symbols->attrs, EXCLUDE_HIDDEN_COLUMNS);
statements[0]->info.query.q.select.list = NULL;
parser_free_tree (parser, statements[0]);
if (!ws_is_same_object (owner, me))
{
/* set user to owner to translate query specification. */
AU_SET_USER (owner);
}
symbols->vquery_for_query =
mq_translate_subqueries (parser, class_object, symbols->attrs, &symbols->authorization);
/* no need to recheck authorizations */
symbols->vquery_for_query_in_gdb = mq_translate_subqueries (parser, class_object, symbols->attrs, NULL);
if (!pt_has_error (parser) && symbols->vquery_for_query)
{
PT_UPDATABILITY updatable = mq_updatable (parser, symbols->vquery_for_query);
if (updatable == PT_PARTIALLY_UPDATABLE)
{
symbols->vquery_for_partial_update = parser_copy_tree_list (parser, symbols->vquery_for_query);
symbols->vquery_for_partial_update = mq_flatten_union (parser, symbols->vquery_for_partial_update);
}
else if (updatable == PT_UPDATABLE)
{
symbols->vquery_for_update = parser_copy_tree_list (parser, symbols->vquery_for_query);
symbols->vquery_for_update = mq_flatten_union (parser, symbols->vquery_for_update);
symbols->vquery_for_update_in_gdb = parser_copy_tree_list (parser, symbols->vquery_for_query_in_gdb);
symbols->vquery_for_update_in_gdb = mq_flatten_union (parser, symbols->vquery_for_update_in_gdb);
symbols->inverted_vquery_for_update =
parser_copy_tree_list (parser, symbols->vquery_for_update_in_gdb);
mq_invert_subqueries (parser, symbols->inverted_vquery_for_update, symbols->attrs);
symbols->inverted_vquery_for_update_in_gdb =
parser_copy_tree_list (parser, symbols->vquery_for_update_in_gdb);
mq_invert_subqueries (parser, symbols->inverted_vquery_for_update_in_gdb, symbols->attrs);
}
if (updatable != PT_UPDATABLE)
{
PT_NODE *virt_class = parser_copy_tree (parser,
symbols->attrs);
if (virt_class == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
virt_class->info.name.db_object = class_object;
mq_set_non_updatable_oid (parser, symbols->vquery_for_query, virt_class);
mq_set_non_updatable_oid (parser, symbols->vquery_for_query_in_gdb, virt_class);
parser_free_tree (parser, virt_class);
}
}
}
}
if (!ws_is_same_object (owner, me))
{
/* set user to me */
AU_SET_USER (me);
}
/* end cycle check */
if (top_cycle > 0)
{
top_cycle--;
cycle_buffer[top_cycle % MAX_CYCLE] = NULL;
}
else
{
top_cycle = 0;
}
return parser;
}
/*
* mq_mark_location() -
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_mark_location (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
short *locp = (short *) arg;
*continue_walk = PT_CONTINUE_WALK;
if (!locp && node->node_type == PT_SELECT)
{
short location = 0;
PT_NODE *spec, *on_cond;
for (spec = node->info.query.q.select.from; spec; spec = spec->next)
{
spec->info.spec.location = location++;
on_cond = spec->info.spec.on_cond;
if (on_cond)
{
switch (spec->info.spec.join_type)
{
case PT_JOIN_INNER:
case PT_JOIN_LEFT_OUTER:
case PT_JOIN_RIGHT_OUTER:
parser_walk_tree (parser, on_cond, mq_mark_location, &(spec->info.spec.location), NULL, NULL);
break;
/* case PT_JOIN_FULL_OUTER: not supported */
case PT_JOIN_NONE:
default:
break;
} /* switch (spec->info.spec.join_type) */
/* ON cond will be moved at optimize_queries */
}
if (spec->info.spec.entity_name)
{
PT_NODE *node = spec->info.spec.entity_name;
if (node->node_type == PT_NAME)
{
node->info.name.location = spec->info.spec.location;
}
else if (node->node_type == PT_SPEC)
{
node->info.spec.location = spec->info.spec.location;
}
else
{
/* dummy else. this case will not happen */
assert (0);
}
}
}
}
else if (node->node_type == PT_SELECT)
{
/* don't walk into subqueries */
*continue_walk = PT_LIST_WALK;
}
else if (locp)
{
if (node->node_type == PT_EXPR)
{
node->info.expr.location = *locp;
}
else if (node->node_type == PT_NAME)
{
node->info.name.location = *locp;
}
else if (node->node_type == PT_VALUE)
{
node->info.value.location = *locp;
}
}
return node;
}
/*
* mq_check_non_updatable_vclass_oid() -
* return:
* parser(in):
* node(in):
* arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_check_non_updatable_vclass_oid (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *dt;
DB_OBJECT *vclass;
bool strict = true, updatable;
if (void_arg != NULL)
{
strict = *((bool *) void_arg);
}
switch (node->node_type)
{
case PT_FUNCTION:
if (node->type_enum == PT_TYPE_OBJECT && (dt = node->data_type) && dt->type_enum == PT_TYPE_OBJECT
&& (vclass = dt->info.data_type.virt_object))
{
/* check for non-updatable vclass oid */
if (strict)
{
updatable = mq_is_updatable_strict (vclass);
}
else
{
updatable = mq_is_updatable (vclass);
}
if (!updatable)
{
/* OID of non-updatable vclass found */
PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NO_VID_FOR_NON_UPDATABLE_VIEW,
/* use function to get name */
db_get_class_name (vclass));
*continue_walk = PT_STOP_WALK;
}
}
break;
default:
break;
}
return node;
}
/*
* mq_check_vclass_for_insert () - checks if view definition is valid for
* INSERT statements (i.e. has only one target)
* return: initial node
* parser(in): parser context
* query_spec(in): view definition
*/
static bool
mq_check_vclass_for_insert (PARSER_CONTEXT * parser, PT_NODE * query_spec)
{
PT_NODE *spec = NULL;
if (query_spec == NULL || query_spec->node_type != PT_SELECT)
{
/* nothing to do */
return true;
}
/* fetch spec; if spec has a "next" it won't be updatable and the statement will be invalidated later */
spec = query_spec->info.query.q.select.from;
if (spec->info.spec.flat_entity_list && spec->info.spec.flat_entity_list->next)
{
PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MULTIPLE_INSERT_TARGETS);
return false;
}
else if (PT_SPEC_IS_DERIVED (spec))
{
return mq_check_vclass_for_insert (parser, spec->info.spec.derived_table);
}
else
{
assert (!PT_SPEC_IS_CTE (spec));
}
/* valid */
return true;
}
/*
* mq_rewrite_upd_del_top_level_specs () - rewrite top-level specs of UPDATE or
* DELETE statements that are spec sets
* or refer views with multiple queries
* return: initial node
* parser(in): parser context
* statement(in): statement
* arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_rewrite_upd_del_top_level_specs (PARSER_CONTEXT * parser, PT_NODE * statement, void *void_arg, int *continue_walk)
{
PT_NODE **spec = NULL;
if (statement == NULL)
{
/* nothing to do */
return NULL;
}
/* mark specs that will be subject to update or delete and retrieve spec list */
switch (statement->node_type)
{
case PT_UPDATE:
pt_mark_spec_list_for_update (parser, statement);
spec = &statement->info.update.spec;
break;
case PT_DELETE:
pt_mark_spec_list_for_delete (parser, statement);
spec = &statement->info.delete_.spec;
break;
case PT_MERGE:
spec = &statement->info.merge.into;
if (statement->info.merge.update.assignment)
{
pt_mark_spec_list_for_update (parser, statement);
if (statement->info.merge.update.has_delete)
{
pt_mark_spec_list_for_delete (parser, statement);
}
}
break;
case PT_INSERT:
/* checks if there is a view or inline view referenced in the odku clause. */
pt_check_odku_refs_view (parser, statement);
/* INSERT does not support rewrites so we must check that no rewrite is needed */
spec = &statement->info.insert.spec;
break;
default:
/* nothing to do */
return statement;
}
while (*spec)
{
/* view definitions for select and for update might look different, so make sure to fetch the correct one */
bool fetch_for_update = ((*spec)->info.spec.flag & PT_SPEC_FLAG_UPDATE)
|| ((*spec)->info.spec.flag & PT_SPEC_FLAG_DELETE) || (statement->node_type == PT_INSERT);
if (fetch_for_update)
{
/* this will fetch a view spec in some illegal cases (e.g. DELETE on a view containing joins); these cases
* will be handled later on */
}
if ((*spec)->info.spec.flat_entity_list)
{
/* fetch entity list */
PT_NODE *subquery = NULL;
PT_NODE *entity = (*spec)->info.spec.flat_entity_list;
/* rewrite if multiple entities */
bool multiple_entity = (entity != NULL && entity->next != NULL);
bool rewrite = false, has_vclass = false;
assert (!PT_SPEC_IS_CTE (*spec) && !PT_SPEC_IS_DERIVED (*spec));
while (entity)
{
if (mq_translatable_class (parser, entity))
{
has_vclass = true;
/* fetch class even if we've decided to rewrite; we need the definition later on */
if (!fetch_for_update)
{
subquery = mq_fetch_subqueries (parser, entity);
}
else
{
subquery = mq_fetch_subqueries_for_update (parser, entity, PT_PARTIAL_SELECT, DB_AUTH_SELECT);
}
if (subquery != NULL && subquery->next != NULL)
{
/* rewrite if multiple queries in view spec */
rewrite = true;
}
}
entity = entity->next;
}
if (multiple_entity && has_vclass)
{
/* if at least one entity is a view, we need to rewrite */
rewrite = true;
}
if (statement->node_type == PT_INSERT)
{
if (rewrite)
{
/* can't rewrite INSERT spec, throw error */
PT_ERRORm (parser, *spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MULTIPLE_INSERT_TARGETS);
return statement;
}
else
{
/* check deeper in view spec; errors will be set in mq_check_vclass_for_insert call, so there is no
* reason to handle return value */
(void) mq_check_vclass_for_insert (parser, subquery);
}
}
if (rewrite)
{
/* rewrite is necessary */
*spec = mq_rewrite_vclass_spec_as_derived (parser, statement, *spec, subquery, false);
}
}
/* next! */
spec = &((*spec)->next);
}
return statement;
}
/*
* mq_translate_helper() - main workhorse for mq_translate
* return:
* parser(in):
* node(in):
*/
static PT_NODE *
mq_translate_helper (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_NODE *next;
int err = NO_ERROR;
bool strict = true;
if (!node)
{
return NULL;
}
/* save and zero link */
next = node->next;
node->next = NULL;
switch (node->node_type)
{
/* only translate translatable statements */
case PT_SELECT:
strict = !PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_NO_STRICT_OID_CHECK);
[[fallthrough]];
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
/* count the number of CTE references, except for the with clause. */
node = parser_walk_tree (parser, node, mq_count_cte_references, NULL, pt_continue_walk, NULL);
node = parser_walk_tree (parser, node, NULL, NULL, mq_rewrite_cte_as_derived, NULL);
/*
* The mq_push_paths will convert the expression as CNF. if subquery is
* in the expression, it may be copied several times. To avoid repeatedly
* convert the expressions in subqueries and improve performance, we
* put mq_push_paths as post function.
*/
node = parser_walk_tree (parser, node, NULL, NULL, mq_push_paths, NULL);
node = parser_walk_tree (parser, node, NULL, NULL, mq_translate_local, NULL);
mq_bump_order_dep_corr_lvl (parser, node);
node = parser_walk_tree (parser, node, mq_mark_location, NULL, NULL, NULL);
node = parser_walk_tree (parser, node, NULL, NULL, mq_check_non_updatable_vclass_oid, &strict);
if (pt_has_error (parser))
{
goto exit_on_error;
}
if (node)
{
node->info.query.is_subquery = (PT_MISC_TYPE) (-1);
if (node->node_type != PT_SELECT)
{
mq_set_union_query (parser, node->info.query.q.union_.arg1, PT_IS_UNION_QUERY);
mq_set_union_query (parser, node->info.query.q.union_.arg2, PT_IS_UNION_QUERY);
}
}
if (node)
{
/* for the optimization of the query includes dblink
* it is better to be written to a subquery */
node = parser_walk_tree (parser, node, NULL, NULL, mq_rewrite_dblink_as_subquery, NULL);
/* mq_optimize works for queries only. Queries generated for update, insert or delete will go thru this path
* when mq_translate is called, so will still get this optimization step applied. */
node = mq_rewrite (parser, node);
/* repeat for constant folding */
if (node)
{
node = pt_semantic_type (parser, node, NULL);
}
}
break;
case PT_INSERT:
case PT_DELETE:
case PT_UPDATE:
case PT_MERGE:
case PT_DO:
node = parser_walk_tree (parser, node, mq_count_cte_references, NULL, pt_continue_walk, NULL);
node = parser_walk_tree (parser, node, NULL, NULL, mq_rewrite_cte_as_derived, NULL);
/*
* The mq_push_paths will convert the expression as CNF. if subquery is
* in the expression, it may be copied several times. To avoid repeatedly
* convert the expressions in subqueries and improve performance, we
* put mq_push_paths as post function.
*/
node = parser_walk_tree (parser, node, NULL, NULL, mq_push_paths, NULL);
node = parser_walk_tree (parser, node, mq_rewrite_upd_del_top_level_specs, NULL, mq_translate_local, NULL);
strict = false; /* no strict OID checking */
node = parser_walk_tree (parser, node, mq_mark_location, NULL, mq_check_non_updatable_vclass_oid, &strict);
if (pt_has_error (parser))
{
goto exit_on_error;
}
if (node)
{
node = mq_rewrite (parser, node);
if (node->node_type == PT_MERGE)
{
mq_auto_param_merge_clauses (parser, node);
}
/* repeat for constant folding */
if (node)
{
node = pt_semantic_type (parser, node, NULL);
}
}
break;
default:
break;
}
/* process FOR UPDATE clause */
err = pt_for_update_prepare_query (parser, node);
if (err != NO_ERROR)
{
return NULL;
}
/* restore link */
if (node)
{
node->next = next;
}
if (pt_has_error (parser))
{
goto exit_on_error;
}
return node;
exit_on_error:
return NULL;
}
/*
* pt_check_for_update_subquery() - check if there is a subquery with
* FOR UPDATE clause.
* return:
* parser(in):
* node(in):
* arg(in/out): an address of an int. 0 for root, 1 if not root and error
* code if a subquery with FOR UPDATE clause was found.
* continue_walk(in):
*/
static PT_NODE *
pt_check_for_update_subquery (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (!*(int *) arg)
{
/* Processed the root node. All remaining PT_SELECT nodes are subqueries */
*(int *) arg = 1;
}
else if (node->node_type == PT_SELECT)
{
if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_FOR_UPDATE))
{
/* found a subquery with FOR UPDATE clause */
PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_USE_FOR_UPDATE_CLAUSE);
*(int *) arg = ER_FAILED;
*continue_walk = PT_STOP_WALK;
return node;
}
}
return node;
}
/*
* pt_check_for_update_clause() - check the query for invalid use of FOR UPDATE
* clause
* return: NO_ERROR or error code
* parser(in):
* query(in): statement to be checked
* root(in): true if this is the main statement and false otherwise.
*
* NOTE: Always call this function with root set to true. false value is used
* internally.
*/
static int
pt_check_for_update_clause (PARSER_CONTEXT * parser, PT_NODE * query, bool root)
{
int error_code = 0;
PT_NODE *spec = NULL, *next = NULL;
if (query == NULL)
{
return NO_ERROR;
}
/* check for subqueries with FOR UPDATE clause */
if (root)
{
next = query->next;
query->next = NULL;
parser_walk_tree (parser, query, pt_check_for_update_subquery, &error_code, NULL, NULL);
query->next = next;
if (error_code < 0)
{
return error_code;
}
}
/* FOR UPDATE is availbale only in SELECT statements */
if (query->node_type != PT_SELECT)
{
if (!root && PT_IS_QUERY (query))
{
PT_ERRORm (parser, query, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_USE_FOR_UPDATE_CLAUSE);
return ER_FAILED;
}
return NO_ERROR;
}
/* Skip check if this is not a query with FOR UPDATE clause */
if (root && !PT_SELECT_INFO_IS_FLAGED (query, PT_SELECT_INFO_FOR_UPDATE))
{
return NO_ERROR;
}
/* check for aggregate functions, GROUP BY and DISTINCT */
if (pt_has_aggregate (parser, query) || pt_is_distinct (query))
{
PT_ERRORm (parser, query, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_USE_FOR_UPDATE_CLAUSE);
return ER_FAILED;
}
/* check derived tables */
for (spec = query->info.query.q.select.from; spec != NULL; spec = spec->next)
{
if ((!root || (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE)) && spec->info.spec.derived_table != NULL)
{
error_code = pt_check_for_update_clause (parser, spec->info.spec.derived_table, false);
if (error_code != NO_ERROR)
{
return error_code;
}
}
}
return NO_ERROR;
}
/*
* pt_for_update_prepare_query_internal() - reflag specs from FOR UPDATE
* including those from subqueries
* return: NO_ERROR or error code
* parser(in):
* query(in/out): query for which the specs are flagged.
*/
static int
pt_for_update_prepare_query_internal (PARSER_CONTEXT * parser, PT_NODE * query)
{
PT_NODE *from = NULL, *spec = NULL;
bool has_for_update = false;
int err = NO_ERROR;
if (query == NULL || query->node_type != PT_SELECT)
{
return NO_ERROR;
}
has_for_update = (PT_SELECT_INFO_IS_FLAGED (query, PT_SELECT_INFO_FOR_UPDATE) ? true : false);
from = query->info.query.q.select.from;
for (spec = from; spec != NULL; spec = spec->next)
{
if (has_for_update && !(spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE))
{
/* skip if the spec is not flagged for FOR UPDATE */
continue;
}
if (spec->info.spec.derived_table != NULL)
{
err = pt_for_update_prepare_query_internal (parser, spec->info.spec.derived_table);
if (err != NO_ERROR)
{
return err;
}
}
else
{
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
}
}
return NO_ERROR;
}
/*
* pt_for_update_prepare_query() - check FOR UPDATE clause and reflag specs from
* FOR UPDATE
* return: returns the modified query.
* parser(in):
* query(in/out): query with FOR UPDATE clause for which the check and spec
* flagging is made.
*/
static int
pt_for_update_prepare_query (PARSER_CONTEXT * parser, PT_NODE * query)
{
int err = NO_ERROR;
if (query == NULL)
{
return NO_ERROR;
}
err = pt_check_for_update_clause (parser, query, true);
if (err != NO_ERROR)
{
return err;
}
if (query->node_type != PT_SELECT || !PT_SELECT_INFO_IS_FLAGED (query, PT_SELECT_INFO_FOR_UPDATE))
{
return NO_ERROR;
}
err = pt_for_update_prepare_query_internal (parser, query);
return err;
}
/*
* mq_translate() - expands each query against a view or virtual class
* return:
* parser(in):
* node(in):
*/
PT_NODE *
mq_translate (PARSER_CONTEXT * parser, PT_NODE * volatile node)
{
volatile PT_NODE *return_node = NULL;
if (!node)
{
return NULL;
}
/* set up an environment for longjump to return to if there is an out of memory error in pt_memory.c. DO NOT RETURN
* unless PT_CLEAR_JMP_ENV is called to clear the environment. */
PT_SET_JMP_ENV (parser);
return_node = mq_translate_helper (parser, node);
PT_CLEAR_JMP_ENV (parser);
return (PT_NODE *) return_node;
}
/*
*
* Function group:
* Functions for the translation of virtual queries
*
*/
/*
* pt_lookup_symbol() -
* return: symbol we are looking for, or NULL if not found
* parser(in):
* attr_list(in): attribute list to look for attr in
* attr(in): attr to look for
*/
static PT_NODE *
mq_lookup_symbol (PARSER_CONTEXT * parser, PT_NODE * attr_list, PT_NODE * attr)
{
PT_NODE *list;
if (!attr || attr->node_type != PT_NAME)
{
PT_INTERNAL_ERROR (parser, "resolution");
return NULL;
}
for (list = attr_list; (list != NULL) && (!pt_name_equal (parser, list, attr)); list = list->next)
{
; /* do nothing */
}
return list;
}
/*
* mq_insert_symbol() - appends the symbol to the entities
* return: none
* parser(in): parser environment
* listhead(in/out): entity_spec to add symbol to
* attr(in): the attribute to add to the symbol table
*/
void
mq_insert_symbol (PARSER_CONTEXT * parser, PT_NODE ** listhead, PT_NODE * attr)
{
PT_NODE *new_node;
if (!attr || attr->node_type != PT_NAME)
{
PT_INTERNAL_ERROR (parser, "translate");
return;
}
/* only insert attributes */
if (attr->info.name.meta_class == PT_PARAMETER)
{
return;
}
new_node = mq_lookup_symbol (parser, *listhead, attr);
if (new_node == NULL)
{
new_node = parser_copy_tree (parser, attr);
*listhead = parser_append_node (new_node, *listhead);
}
}
/*
* mq_generate_name() - generates printable names
* return:
* parser(in):
* root(in):
* version(in):
*/
const char *
mq_generate_name (PARSER_CONTEXT * parser, const char *root, int *version)
{
const char *generatedname;
char temp[20];
(*version)++;
sprintf (temp, "_%d", *version);
/* avoid "stepping" on root */
generatedname = pt_append_string (parser, pt_append_string (parser, NULL, root), temp);
return generatedname;
}
/*
* mq_coerce_resolved() - re-sets PT_NAME node resolution to match
* a new printable name
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_coerce_resolved (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *range = (PT_NODE *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
/* if its not a name, leave it alone */
if (node->node_type == PT_NAME)
{
if (node->info.name.spec_id == range->info.name.spec_id /* same entity spec */
&& node->info.name.resolved /* and has a resolved name, */
&& node->info.name.meta_class != PT_CLASS && node->info.name.meta_class != PT_VCLASS)
{
/* set the attribute resolved name */
node->info.name.resolved = range->info.name.original;
}
/* sub nodes of PT_NAME are not names with range variables */
*continue_walk = PT_LIST_WALK;
}
else if (node->node_type == PT_SPEC && node->info.spec.id == range->info.name.spec_id)
{
PT_NODE *flat = node->info.spec.flat_entity_list;
/* sub nodes of PT_SPEC include flat class lists with range variables. Set them even though they are "class"
* names. */
for (; flat != NULL; flat = flat->next)
{
flat->info.name.resolved = range->info.name.original;
}
}
return node;
}
/*
* mq_set_all_ids() - sets PT_NAME node ids
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_set_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) void_arg;
if (node->node_type == PT_NAME)
{
node->info.name.spec_id = spec->info.spec.id;
node->info.name.resolved = spec->info.spec.range_var->info.name.original;
}
node->spec_ident = spec->info.spec.id;
return node;
}
/*
* mq_reset_all_ids() - re-sets PT_NAME node ids
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_reset_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) void_arg;
if (node->node_type == PT_NAME && node->info.name.spec_id == spec->info.spec.id)
{
node->info.name.spec_id = (UINTPTR) spec;
if (node->info.name.resolved /* has a resolved name */
&& node->info.name.meta_class != PT_CLASS && node->info.name.meta_class != PT_VCLASS)
{
/* set the attribute resolved name */
node->info.name.resolved = spec->info.spec.range_var->info.name.original;
}
}
else if (node->node_type == PT_SPEC && node->info.spec.id == spec->info.spec.id
&& node->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
{
/* fix up pseudo specs, although it probably does not matter */
node->info.spec.id = (UINTPTR) spec;
}
else if (node->node_type == PT_CHECK_OPTION && node->info.check_option.spec_id == spec->info.spec.id)
{
node->info.check_option.spec_id = (UINTPTR) spec;
}
if (node->spec_ident == spec->info.spec.id)
{
node->spec_ident = (UINTPTR) spec;
}
return node;
}
/*
* mq_reset_ids() - re-sets path entities of a spec by removing unreferenced
* paths, reseting ids of remaining paths, and recursing on sub-paths
* return:
* parser(in):
* statement(in):
* spec(in):
*/
PT_NODE *
mq_reset_ids (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
PT_NODE *range;
/* don't mess with pseudo specs */
if (spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
{
return statement;
}
/* skip the spec before assignment */
if (spec->info.spec.id == 0)
{
return statement;
}
/* make sure range var always has same id as spec */
range = spec->info.spec.range_var;
if (range)
{
assert (range->node_type == PT_NAME);
range->info.name.spec_id = spec->info.spec.id;
}
statement = parser_walk_tree (parser, statement, mq_reset_all_ids, spec, NULL, NULL);
/* spec may or may not be part of statement. If it is, this is redundant. If its not, this will reset self
* references, such as in path specs. */
(void) parser_walk_tree (parser, spec, mq_reset_all_ids, spec, NULL, NULL);
/* finally, set spec id */
spec->info.spec.id = (UINTPTR) spec;
return statement;
}
/*
* mq_clear_all_ids() - clear previously resolved PT_NAME node
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_clear_all_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
UINTPTR *spec_id_ptr = (UINTPTR *) void_arg;
if (node->node_type == PT_NAME)
{
if ((spec_id_ptr != NULL && node->info.name.spec_id == (*spec_id_ptr)) || (spec_id_ptr == NULL))
{
node->info.name.spec_id = 0;
}
}
if (pt_is_query (node))
{
*continue_walk = PT_LIST_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return node;
}
/*
* mq_clear_other_ids () - clear ids for all nodes except the ones referencing
* the spec list specified in void_arg
* return : node
* parser (in) : parser context
* node (in) : node to reset
* void_arg (in) : spec list
* continue_walk (in) :
*/
static PT_NODE *
mq_clear_other_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
if (node->node_type == PT_NAME)
{
PT_NODE *filter_spec = (PT_NODE *) void_arg;
while (filter_spec != NULL)
{
if (node->info.name.spec_id == filter_spec->info.spec.id)
{
return node;
}
filter_spec = filter_spec->next;
}
node->info.name.spec_id = 0;
}
return node;
}
/*
* mq_clear_ids () - recursively clear previously resolved PT_NAME nodes
* return:
* parser(in):
* statement(in):
* spec(in):
*/
PT_NODE *
mq_clear_ids (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * spec)
{
node =
parser_walk_tree (parser, node, mq_clear_all_ids, (spec != NULL ? &spec->info.spec.id : NULL), pt_continue_walk,
NULL);
return node;
}
/*
* mq_reset_spec_ids() - resets spec ids for a spec node
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_reset_spec_ids (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = NULL;
switch (node->node_type)
{
case PT_SELECT:
for (spec = node->info.query.q.select.from; spec; spec = spec->next)
{
/* might not be necessary to reset paths and references, but it's a good failsafe */
mq_set_references (parser, node, spec);
}
break;
case PT_UPDATE:
for (spec = node->info.update.spec; spec; spec = spec->next)
{
/* only reset IDs, in case query will be rewritten as SELECT for broker execution */
mq_reset_ids (parser, node, spec);
}
break;
case PT_DELETE:
for (spec = node->info.delete_.spec; spec; spec = spec->next)
{
/* only reset IDs, in case query will be rewritten as SELECT for broker execution */
mq_reset_ids (parser, node, spec);
}
break;
case PT_MERGE:
for (spec = node->info.merge.into; spec; spec = spec->next)
{
/* only reset IDs, in case query will be rewritten as SELECT for broker execution */
mq_reset_ids (parser, node, spec);
}
for (spec = node->info.merge.using_clause; spec; spec = spec->next)
{
/* only reset IDs, in case query will be rewritten as SELECT for broker execution */
mq_reset_ids (parser, node, spec);
}
break;
case PT_INSERT:
if (node->info.insert.odku_assignments)
{
PT_NODE *values = node->info.insert.value_clauses;
PT_NODE *select = values->info.node_list.list;
if (select != NULL && select->node_type == PT_SELECT)
{
PT_NODE *assignments = node->info.insert.odku_assignments;
spec = select->info.query.q.select.from;
for (; spec; spec = spec->next)
{
for (; assignments; assignments = assignments->next)
{
parser_walk_tree (parser, assignments, mq_reset_all_ids, spec, NULL, NULL);
}
}
}
}
break;
default:
break;
}
return (node);
}
/*
* mq_reset_spec_in_method_names() - resets spec id in method name
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*
* NOTE: Currently this function reset spec_id only if the spec_id is
* a name node.
*/
static PT_NODE *
mq_reset_spec_in_method_names (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
if (PT_IS_METHOD (node))
{
PT_NODE *method_name;
method_name = node->info.method_call.method_name;
if (method_name)
{
PT_NODE *spec = (PT_NODE *) method_name->info.name.spec_id;
if (spec && spec->node_type == PT_NAME)
{
method_name->info.name.spec_id = (UINTPTR) method_name;
}
}
}
return (node);
}
/*
* mq_reset_ids_in_statement() - walks the statement and for each spec,
* reset ids that reference that spec
* return: statement having methods ids reseted
* parser(in):
* statement(in):
*/
PT_NODE *
mq_reset_ids_in_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
statement = parser_walk_tree (parser, statement, mq_reset_spec_ids, NULL, NULL, NULL);
return (statement);
}
/*
* mq_reset_ids_in_methods() - walks the statement and for each method reset id
*
* return:
* parser(in):
* statement(in):
*/
PT_NODE *
mq_reset_ids_in_methods (PARSER_CONTEXT * parser, PT_NODE * statement)
{
statement = parser_walk_tree (parser, statement, mq_reset_spec_in_method_names, NULL, NULL, NULL);
return (statement);
}
/*
* mq_get_references_node() - gets referenced PT_NAME nodes
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_get_references_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) void_arg;
if (node->node_type == PT_NAME && node->info.name.spec_id == spec->info.spec.id)
{
node->info.name.spec_id = (UINTPTR) spec;
if (node->info.name.meta_class != PT_METHOD && node->info.name.meta_class != PT_HINT_NAME
&& node->info.name.meta_class != PT_INDEX_NAME)
{
/* filter out method name, hint argument name, index name nodes */
mq_insert_symbol (parser, &spec->info.spec.referenced_attrs, 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. */
node->info.spec.derived_table =
parser_walk_tree (parser, node->info.spec.derived_table, mq_get_references_node, spec, pt_continue_walk, NULL);
node->info.spec.path_entities =
parser_walk_tree (parser, node->info.spec.path_entities, mq_get_references_node, spec, pt_continue_walk, NULL);
node->info.spec.path_conjuncts =
parser_walk_tree (parser, node->info.spec.path_conjuncts, mq_get_references_node, spec, pt_continue_walk, NULL);
node->info.spec.on_cond =
parser_walk_tree (parser, node->info.spec.on_cond, mq_get_references_node, spec, pt_continue_walk, NULL);
/* don't visit any other leaf nodes */
*continue_walk = PT_LIST_WALK;
}
/* 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;
}
if (node->spec_ident == spec->info.spec.id)
{
node->spec_ident = (UINTPTR) spec;
}
return node;
}
/*
* mq_reset_ids_and_references() - re-sets path entities of a spec by
* removing unreferenced paths, reseting ids of remaining paths,
* and recursing on sub-paths
* return:
* parser(in):
* statement(in):
* spec(in):
*/
PT_NODE *
mq_reset_ids_and_references (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
return mq_reset_ids_and_references_helper (parser, statement, spec, true /* default */ );
}
/*
* mq_reset_ids_and_references_helper() -
* return:
* parser(in):
* statement(in):
* spec(in):
* get_spec_referenced_attr(in):
*/
PT_NODE *
mq_reset_ids_and_references_helper (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec,
bool get_spec_referenced_attr)
{
/* don't mess with pseudo specs */
if (spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
{
return statement;
}
statement = mq_reset_ids (parser, statement, spec);
parser_free_tree (parser, spec->info.spec.referenced_attrs);
spec->info.spec.referenced_attrs = NULL;
statement = parser_walk_tree (parser, statement, mq_get_references_node, spec, pt_continue_walk, NULL);
/* spec may or may not be part of statement. If it is, this is redundant. If its not, this will reset catch self
* references, such as in path specs. */
if (get_spec_referenced_attr)
{
(void) parser_walk_tree (parser, spec, mq_get_references_node, spec, pt_continue_walk, NULL);
}
return statement;
}
/*
* mq_get_references() - returns a copy of a list of referenced names for
* the given entity spec
* return:
* parser(in):
* statement(in):
* spec(in):
*/
PT_NODE *
mq_get_references (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
return mq_get_references_helper (parser, statement, spec, true /* default */ );
}
/*
* mq_get_references_helper() -
* return:
* parser(in):
* statement(in):
* spec(in):
* get_spec_referenced_attr(in):
*/
PT_NODE *
mq_get_references_helper (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, bool get_spec_referenced_attr)
{
PT_NODE *references;
statement = mq_reset_ids_and_references_helper (parser, statement, spec, get_spec_referenced_attr);
references = spec->info.spec.referenced_attrs;
spec->info.spec.referenced_attrs = NULL;
return references;
}
/*
* mq_referenced_pre() - looks for a name from a given entity spec
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_referenced_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
EXISTS_INFO *info = (EXISTS_INFO *) void_arg;
PT_NODE *spec = info->spec;
/* don't count self references as being referenced. */
if (node == spec)
{
*continue_walk = PT_LIST_WALK;
return node;
}
if (node->node_type == PT_NAME && node->info.name.spec_id == spec->info.spec.id)
{
node->info.name.spec_id = (UINTPTR) spec;
if (node->info.name.meta_class != PT_VCLASS)
{
info->referenced = 1;
*continue_walk = PT_STOP_WALK;
}
}
return node;
}
/*
* mq_referenced_post() - looks for a name from a given entity spec
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_referenced_post (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
if (*continue_walk != PT_STOP_WALK)
{
*continue_walk = PT_CONTINUE_WALK;
}
return node;
}
/*
* mq_is_referenced() - tests if an entity is referenced in a spec
* return: 1 on referenced
* parser(in):
* statement(in):
* spec(in):
*/
static int
mq_is_referenced (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
EXISTS_INFO info;
info.spec = spec;
info.referenced = 0;
parser_walk_tree (parser, statement, mq_referenced_pre, &info, mq_referenced_post, &info);
return info.referenced;
}
/*
* mq_reset_paths() - re-sets path entities of a spec by removing unreferenced
* paths, reseting ids of remaining paths and recursing on sub-paths
* return:
* parser(in):
* statement(in):
* root_spec(in):
*/
PT_NODE *
mq_reset_paths (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * root_spec)
{
PT_NODE **path_spec_ptr = &root_spec->info.spec.path_entities;
PT_NODE *path_spec = *path_spec_ptr;
for (; path_spec != NULL; path_spec = *path_spec_ptr)
{
if (mq_is_referenced (parser, statement, path_spec))
{
/* keep it if its still referenced */
statement = mq_reset_ids (parser, statement, path_spec);
statement = mq_reset_paths (parser, statement, path_spec);
path_spec_ptr = &path_spec->next;
}
else
{
#if 0
/* its possible inder some perverse conditions for a virtual spec to disappear, while sub paths still apear.
* Hear, we promote the sub-paths to the same level and re-check them all for references. */
parser_append_node (path_spec->info.spec.path_entities, path_spec);
path_spec->info.spec.path_entities = NULL;
#endif /* 0 */
/* remove path spec */
*path_spec_ptr = path_spec->next;
path_spec->next = NULL;
parser_free_tree (parser, path_spec);
}
}
return statement;
}
/*
* mq_set_references_local() - sets the referenced attr list of entity
* specifications and its sub-entities
* return:
* parser(in):
* statement(in):
* spec(in):
*/
static PT_NODE *
mq_set_references_local (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
PT_NODE *path_spec;
parser_free_tree (parser, spec->info.spec.referenced_attrs);
spec->info.spec.referenced_attrs = NULL;
statement = parser_walk_tree (parser, statement, mq_get_references_node, spec, pt_continue_walk, NULL);
path_spec = spec->info.spec.path_entities;
for (; path_spec != NULL; path_spec = path_spec->next)
{
statement = mq_set_references_local (parser, statement, path_spec);
}
return statement;
}
/*
* mq_set_references() - sets the referenced attr list of an entity
* specification and all sub-entities
* return:
* parser(in):
* statement(in):
* spec(in):
*/
PT_NODE *
mq_set_references (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec)
{
/* don't mess with pseudo specs */
if (!spec || spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
{
return statement;
}
statement = mq_reset_ids (parser, statement, spec);
statement = mq_reset_paths (parser, statement, spec);
statement = mq_set_references_local (parser, statement, spec);
return statement;
}
/*
* mq_reset_select_spec_node() - re-sets copied spec symbol table information
* for a select which has just been substituted as a lambda argument in a view
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_reset_select_spec_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_RESET_SELECT_SPEC_INFO *info = (PT_RESET_SELECT_SPEC_INFO *) void_arg;
if (node->node_type == PT_SPEC && node->info.spec.id == info->id)
{
*info->statement = mq_reset_ids_and_references (parser, *info->statement, node);
*info->statement = mq_translate_paths (parser, *info->statement, node);
*info->statement = mq_reset_paths (parser, *info->statement, node);
}
return node;
}
/*
* mq_reset_select_specs() - re-sets spec symbol table information for a select
* which has just been substituted as a lambda argument in a view
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_reset_select_specs (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE **statement = (PT_NODE **) void_arg;
PT_RESET_SELECT_SPEC_INFO info;
PT_NODE *spec;
if (node->node_type == PT_SELECT)
{
spec = node->info.query.q.select.from;
info.statement = statement;
for (; spec != NULL; spec = spec->next)
{
info.id = spec->info.spec.id;
/* now we know which specs must get reset. we need to find each instance of this spec in the statement, and
* reset it. */
*statement = parser_walk_tree (parser, *statement, mq_reset_select_spec_node, &info, NULL, NULL);
}
}
return node;
}
/*
* mq_reset_specs_from_column() - finds every select in column, then resets
* id's and paths from that selects spec
* return:
* parser(in):
* statement(in):
* column(in):
*/
static PT_NODE *
mq_reset_specs_from_column (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * column)
{
parser_walk_tree (parser, column, mq_reset_select_specs, &statement, NULL, NULL);
return statement;
}
/*
* mq_new_spec() - Create a new spec, given a class name
* return:
* parser(in):
* class_name(in):
*/
static PT_NODE *
mq_new_spec (PARSER_CONTEXT * parser, const char *class_name)
{
PT_NODE *class_spec;
PT_FLAT_SPEC_INFO info;
if ((class_spec = parser_new_node (parser, PT_SPEC)) == NULL)
{
return NULL;
}
class_spec->info.spec.id = (UINTPTR) class_spec;
class_spec->info.spec.only_all = PT_ONLY;
class_spec->info.spec.meta_class = PT_META_CLASS;
if ((class_spec->info.spec.entity_name = pt_name (parser, class_name)) == NULL)
{
return NULL;
}
info.spec_parent = NULL;
info.for_update = false;
class_spec = parser_walk_tree (parser, class_spec, pt_flat_spec_pre, &info, pt_continue_walk, NULL);
return class_spec;
}
/*
* mq_replace_name_with_path() - replace them with copies of path supplied,
* ending in name node
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*
* Note:
* ONLY do this for names matching the input expressions spec_id, which
* is passed in in the info structure. Other names may be unrelated names
* from subqueries in the expression being walked
*/
static PT_NODE *
mq_replace_name_with_path (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
REPLACE_NAME_INFO *info = (REPLACE_NAME_INFO *) void_arg;
PT_NODE *path = info->path;
PT_NODE *next;
*continue_walk = PT_CONTINUE_WALK;
if (node->node_type == PT_NAME && node->info.name.spec_id == info->spec_id
&& (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))
{
next = node->next;
if (node->info.name.resolved)
{
/* names appearing in right side of dot expressions should not be replaced. We take advantage of the fact
* that these do not have "resolved" set, to identify those names not to touch. All other names should have
* "resolved" set, and be handled here. */
path = parser_copy_tree (parser, path);
if (path)
{
/* now make this a legitimate path right hand and make it print right, by setting its resolved to NULL. */
node->info.name.resolved = NULL;
path->info.expr.arg2 = node;
path->type_enum = node->type_enum;
parser_free_tree (parser, path->data_type);
path->data_type = parser_copy_tree (parser, node->data_type);
node = path;
node->next = next;
}
}
*continue_walk = PT_LIST_WALK;
}
if (node->node_type == PT_DATA_TYPE)
{
*continue_walk = PT_LIST_WALK;
}
return node;
}
/*
* mq_substitute_path() -
* return:
* parser(in):
* node(in):
* path_info(in):
*/
static PT_NODE *
mq_substitute_path (PARSER_CONTEXT * parser, PT_NODE * node, PATH_LAMBDA_INFO * path_info)
{
PT_NODE *column;
PT_NODE *next;
REPLACE_NAME_INFO info;
PT_NODE *query_spec_column = path_info->lambda_expr;
UINTPTR spec_id = path_info->spec_id;
/* prune other columns and copy */
column = parser_copy_tree (parser, query_spec_column);
if (column == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
if (column->node_type == PT_NAME)
{
if (column->info.name.meta_class == PT_SHARED)
{
PT_NODE *new_spec = mq_new_spec (parser,
db_get_class_name (column->info.name.db_object));
if (new_spec == NULL)
{
PT_INTERNAL_ERROR (parser, "mq_new_spec");
return NULL;
}
path_info->new_specs = parser_append_node (new_spec, path_info->new_specs);
column->info.name.spec_id = new_spec->info.spec.id;
column->next = node->next;
column->line_number = node->line_number;
column->column_number = node->column_number;
node->next = NULL;
parser_free_tree (parser, node);
node = column;
}
else
#if 0
if (PT_IS_OID_NAME (column))
{
/* path collapses a notch! */
next = node->next;
node = node->info.expr.arg1;
node->next = next;
}
else
#endif /* 0 */
{
parser_free_tree (parser, node->info.expr.arg2);
node->info.expr.arg2 = column;
column->info.name.resolved = NULL; /* make it print right */
if (node->data_type)
{
parser_free_tree (parser, node->data_type);
}
node->data_type = parser_copy_tree (parser, column->data_type);
}
}
else
{
next = node->next;
parser_free_tree (parser, node->info.expr.arg2);
node->info.expr.arg2 = NULL;
node->next = NULL;
info.path = node;
info.spec_id = spec_id;
node = parser_walk_tree (parser, column, mq_replace_name_with_path, (void *) &info, pt_continue_walk, NULL);
if (node)
{
node->next = next;
if (node->node_type == PT_EXPR)
{
/* if we replace a path expression with an expression, put parenthesis around it, because we are likely
* IN another expression. If we need to print the outer expression, parenthesis gurantee the proper
* expression precedence. */
node->info.expr.paren_type = 1;
}
}
}
return node;
}
/*
* mq_substitute_path_pre() - tests and substitutes for path expressions
* matching the given name
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_substitute_path_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *arg2;
PT_NODE *next;
PATH_LAMBDA_INFO *info = (PATH_LAMBDA_INFO *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
if (node->node_type == PT_DOT_ && (arg2 = node->info.dot.arg2) && pt_name_equal (parser, arg2, &(info->lambda_name)))
{
/* need to replace node with the converted expression */
node = mq_substitute_path (parser, node, info);
/* no need to revisit these leaves */
*continue_walk = PT_LIST_WALK;
}
else if (node->node_type == PT_NAME)
{
if (pt_name_equal (parser, node, &(info->lambda_name)))
{
/* this is a name reference in a spec somewhere */
next = node->next;
node->next = NULL;
parser_free_tree (parser, node);
node = parser_copy_tree (parser, info->lambda_expr);
if (node == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
node->next = next;
}
/* no need to revisit these leaves */
*continue_walk = PT_LIST_WALK;
}
return node;
}
/*
* mq_path_name_lambda() - Search the tree for path expression right hand sides
* matching the given name, and do path substitution on
* those path expressions with the supplied argument
* return:
* parser(in):
* statement(in):
* lambda_name(in):
* lambda_expr(in):
* spec_id(in):
*/
static PT_NODE *
mq_path_name_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * lambda_name, PT_NODE * lambda_expr,
UINTPTR spec_id)
{
PATH_LAMBDA_INFO info;
/* copy the name because the reference is one of the things that will be replaced. */
info.lambda_name = *lambda_name;
info.lambda_expr = lambda_expr;
info.spec_id = spec_id;
info.new_specs = NULL;
return parser_walk_tree (parser, statement, mq_substitute_path_pre, &info, pt_continue_walk, NULL);
}
/*
* mq_reset_spec_distr_subpath_pre() - moving specs from the sub-path list to
* the immediate path_entities list, and resetting ids in the statement
* return:
* parser(in):
* spec(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_reset_spec_distr_subpath_pre (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg, int *continue_walk)
{
SPEC_RESET_INFO *info = (SPEC_RESET_INFO *) void_arg;
if (spec == info->old_next)
{
*continue_walk = PT_STOP_WALK;
}
else
{
*continue_walk = PT_CONTINUE_WALK;
}
return spec;
}
/*
* mq_reset_spec_distr_subpath_post() -
* return:
* parser(in):
* spec(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_reset_spec_distr_subpath_post (PARSER_CONTEXT * parser, PT_NODE * spec, void *void_arg, int *continue_walk)
{
SPEC_RESET_INFO *info = (SPEC_RESET_INFO *) void_arg;
PT_NODE **sub_paths = info->sub_paths;
PT_NODE *subspec = *sub_paths;
PT_NODE *subspec_term;
PT_NODE *arg1;
*continue_walk = PT_CONTINUE_WALK; /* un-prune other sub-branches */
if (spec != info->old_next && spec->node_type == PT_SPEC)
{
for (; subspec != NULL; subspec = *sub_paths)
{
subspec_term = subspec->info.spec.path_conjuncts;
arg1 = subspec_term->info.expr.arg1;
if ((arg1->node_type == PT_NAME && spec->info.spec.id == arg1->info.name.spec_id)
|| pt_find_id (parser, arg1, spec->info.spec.id))
{
/* a match. link it to this spec path entities */
*sub_paths = subspec->next;
subspec->next = spec->info.spec.path_entities;
spec->info.spec.path_entities = subspec;
}
else
{
/* otherwise advance down the list with no side effects */
sub_paths = &subspec->next;
}
}
/* now that the sub-specs (if any) are attached, we can reset spec_ids and references. */
info->statement = mq_reset_ids_and_references (parser, info->statement, spec);
}
return spec;
}
/*
* mq_path_spec_lambda() - Replace old_spec (virtual) with new_spec (real)
* return:
* parser(in):
* statement(in):
* root_spec(in): points to the spec of the left hand side of the path
* prev_ptr(in): points to the reference to old_spec
* old_spec(out):
* new_spec(in):
*
* Note:
* If the new_spec is a join, this is an error. Only updatable
* new_specs should be candidates. However, previous checks should
* have already caught this.
*
* If the new_spec has path_entities, then the immedieate sub-path entities
* of the old_spec must be distributed amoung the new_spec spec nodes.
*/
static PT_NODE *
mq_path_spec_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * root_spec, PT_NODE ** prev_ptr,
PT_NODE * old_spec, PT_NODE * new_spec)
{
PT_NODE *root_flat;
PT_NODE *old_flat;
PT_NODE *new_flat;
PT_NODE *sub_paths;
root_flat = root_spec->info.spec.flat_entity_list;
if (!root_flat)
{
/* its a derived table */
root_flat = old_spec->info.spec.path_conjuncts->info.expr.arg1->data_type->info.data_type.entity;
}
old_flat = old_spec->info.spec.flat_entity_list;
new_flat = new_spec->info.spec.flat_entity_list;
sub_paths = old_spec->info.spec.path_entities;
old_spec->info.spec.path_entities = NULL;
if (new_spec->next)
{
PT_ERRORmf2 (parser, old_spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VC_COMP_NOT_UPDATABL,
old_flat->info.name.original, new_flat->info.name.original);
}
*prev_ptr = new_spec;
new_spec->next = old_spec->next;
old_spec->next = NULL;
new_spec->info.spec.path_conjuncts = old_spec->info.spec.path_conjuncts;
old_spec->info.spec.path_conjuncts = NULL;
new_spec->line_number = old_spec->line_number;
new_spec->column_number = old_spec->column_number;
if (new_spec->info.spec.path_entities)
{
SPEC_RESET_INFO spec_reset;
/* reset the spec_id's */
spec_reset.statement = statement;
spec_reset.sub_paths = &sub_paths;
spec_reset.old_next = new_spec->next;
new_spec =
parser_walk_tree (parser, new_spec, mq_reset_spec_distr_subpath_pre, &spec_reset,
mq_reset_spec_distr_subpath_post, &spec_reset);
statement = spec_reset.statement;
}
else
{
/* The swap is one for one. All old sub paths must be direct sub-paths. */
new_spec->info.spec.path_entities = sub_paths;
/* reset the spec_id's */
statement = mq_reset_ids_and_references (parser, statement, new_spec);
}
parser_free_tree (parser, old_spec);
return statement;
}
/*
* mq_translate_paths() - translates the composition virtual references to real
* return:
* parser(in):
* statement(in):
* root_spec(in):
*
* Note:
*
* The list of immediate sub-paths must be re-distributed amoung the
* resulting real path specs. In the trivial case in which there is
* a one to one correspondance, this means simply setting the path_entities
* as it was before. Otherwise the name id's of each spec in the immediate
* sub-path must be matched against the n candidate real specs, and appended
* to its path_entities list.
*/
static PT_NODE *
mq_translate_paths (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * root_spec)
{
PT_NODE *references;
PT_NODE *reference_list;
PT_NODE *path_spec;
PT_NODE *next;
PT_NODE *flat;
PT_NODE *join_term;
PT_NODE **prev_ptr;
PT_NODE *real_class;
PT_NODE *expr;
UINTPTR spec_id;
PT_NODE *query_spec;
PT_MISC_TYPE path_type; /* 0, or PT_PATH_INNER */
if (root_spec == NULL)
{
return NULL;
}
prev_ptr = &root_spec->info.spec.path_entities;
path_spec = *prev_ptr;
while (path_spec && statement)
{
flat = path_spec->info.spec.flat_entity_list;
join_term = path_spec->info.spec.path_conjuncts;
if (!join_term)
{
PT_INTERNAL_ERROR (parser, "translate");
}
else if (flat && flat->info.name.meta_class == PT_CLASS /* NOT PT_META_CLASS */
&& (db_is_vclass (flat->info.name.db_object) > 0))
{
next = path_spec->next;
references = mq_get_references (parser, statement, path_spec);
reference_list = references; /* to be freed later */
real_class = join_term->info.expr.arg1->data_type->info.data_type.entity;
path_type = path_spec->info.spec.meta_class;
while (references)
{
expr =
mq_fetch_expression_for_real_class_update (parser, flat->info.name.db_object, references, real_class,
PT_NORMAL_SELECT, DB_AUTH_SELECT, &spec_id);
if (expr)
{
statement = mq_path_name_lambda (parser, statement, references, expr, spec_id);
}
references = references->next;
}
parser_free_tree (parser, reference_list);
query_spec =
mq_fetch_select_for_real_class_update (parser, flat, real_class, PT_NORMAL_SELECT, DB_AUTH_SELECT);
flat = flat->next;
while (flat && !query_spec)
{
query_spec =
mq_fetch_select_for_real_class_update (parser, flat, real_class, PT_NORMAL_SELECT, DB_AUTH_SELECT);
flat = flat->next;
}
/* at this point, if any of the virtual classes had a matching real class_, we will have found it */
if (query_spec)
{
PT_NODE *temp;
PT_NODE *new_spec;
new_spec = parser_copy_tree_list (parser, query_spec->info.query.q.select.from);
/* the following block of code attempts to gurantee that all candidate subclasses are copied to the
* entity list of the path spec we are about to create.
*
* * relational proxies are made an exception, because 1) relational proxies can inherently only refer to
* one table. */
if (db_is_class (real_class->info.name.db_object) > 0)
{
/* find all the rest of the matches */
for (; flat != NULL; flat = flat->next)
{
query_spec =
mq_fetch_select_for_real_class_update (parser, flat, real_class, PT_NORMAL_SELECT,
DB_AUTH_SELECT);
if (query_spec && (temp = query_spec->info.query.q.select.from)
&& (temp = temp->info.spec.flat_entity_list) && (temp = parser_copy_tree_list (parser, temp)))
{
new_spec->info.spec.flat_entity_list =
parser_append_node (temp, new_spec->info.spec.flat_entity_list);
while (temp)
{
temp->info.name.spec_id = new_spec->info.spec.id;
temp = temp->next;
}
}
}
}
statement = mq_path_spec_lambda (parser, statement, root_spec, prev_ptr, path_spec, new_spec);
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
}
path_spec = *prev_ptr; /* this was just over-written */
/* if either the virtual or translated guys is an inner path (selector path) the result must be an inner
* path, as opposed to the usual left join path semantics */
if (path_type == PT_PATH_INNER)
{
path_spec->info.spec.meta_class = PT_PATH_INNER;
}
/* translate virtual sub-paths */
statement = mq_translate_paths (parser, statement, path_spec);
}
prev_ptr = &path_spec->next;
path_spec = *prev_ptr;
}
return statement;
}
/*
* mq_rename_resolved() - re-sets name resolution to of an entity spec
* and a tree to match a new printable name
* return:
* parser(in):
* spec(in):
* statement(in):
* newname(in):
*/
PT_NODE *
mq_rename_resolved (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * statement, const char *newname)
{
if (!spec || !spec->info.spec.range_var || !statement)
{
return statement;
}
spec->info.spec.range_var->info.name.original = newname;
/* this is just to make sure the id is properly set. Its probably not necessary. */
spec->info.spec.range_var->info.name.spec_id = spec->info.spec.id;
statement =
parser_walk_tree (parser, statement, mq_coerce_resolved, spec->info.spec.range_var, pt_continue_walk, NULL);
return statement;
}
/*
* mq_regenerate_if_ambiguous() - regenerate the exposed name
* if ambiguity is detected
* return:
* parser(in):
* spec(in):
* statement(in):
* from(in):
*/
PT_NODE *
mq_regenerate_if_ambiguous (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * statement, PT_NODE * from)
{
const char *newexposedname;
const char *generatedname;
int ambiguous;
int i;
newexposedname = spec->info.spec.range_var->info.name.original;
if (1 < pt_name_occurs_in_from_list (parser, newexposedname, from))
{
/* Ambiguity is detected. rename the newcomer's printable name to fix this. */
i = 0;
ambiguous = true;
while (ambiguous)
{
generatedname = mq_generate_name (parser, newexposedname, &i);
ambiguous = 0 < pt_name_occurs_in_from_list (parser, generatedname, from);
}
/* generatedname is now non-ambiguous */
statement = mq_rename_resolved (parser, spec, statement, generatedname);
}
return statement;
}
/*
* mq_generate_unique() - generates a printable name not found in the name list
* return:
* parser(in):
* name_list(in):
*/
static PT_NODE *
mq_generate_unique (PARSER_CONTEXT * parser, PT_NODE * name_list)
{
int ambiguous = 1;
int i = 0;
PT_NODE *new_name = parser_copy_tree (parser, name_list);
PT_NODE *temp = name_list;
if (new_name == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
while (ambiguous)
{
new_name->info.name.original = mq_generate_name (parser, "a", &i);
temp = name_list;
while (temp && intl_identifier_casecmp (new_name->info.name.original, temp->info.name.original) != 0)
{
temp = temp->next;
}
if (!temp)
{
ambiguous = 0;
}
}
return new_name;
}
/*
* mq_invert_insert_select() - invert sub-query select lists
* return:
* parser(in):
* attr(in):
* subquery(in):
*/
static void
mq_invert_insert_select (PARSER_CONTEXT * parser, PT_NODE * attr, PT_NODE * subquery)
{
PT_NODE **value;
PT_NODE *value_next;
PT_NODE *result;
value = &subquery->info.query.q.select.list;
while (*value)
{
/* ignore the the hidden columns e.g. append when check order by see mq_update_order_by (...) */
if ((*value)->flag.is_hidden_column == 1)
{
value = &(*value)->next;
continue;
}
if (!attr)
{
/* system error, should be caught in semantic pass */
PT_ERRORm (parser, (*value), MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS);
return;
}
value_next = (*value)->next;
(*value)->next = NULL;
(*value) = mq_translate_value (parser, *value);
result = pt_invert (parser, attr, *value);
if (result == NULL)
{
/* error not invertable/updatable */
/* don't want to repeat this error */
if (!pt_has_error (parser))
{
PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, attr));
}
return;
}
if (result->next)
{
parser_free_tree (parser, result->next);
}
result->next = NULL;
(*value) = result; /* the right hand side */
attr = attr->next;
(*value)->next = value_next;
value = &(*value)->next;
}
}
/*
* mq_invert_insert_subquery() - invert sub-query select lists
* return:
* parser(in):
* attr(in):
* subquery(in):
*/
static void
mq_invert_insert_subquery (PARSER_CONTEXT * parser, PT_NODE ** attr, PT_NODE * subquery)
{
PT_NODE *attr_next;
PT_NODE *result;
switch (subquery->node_type)
{
case PT_SELECT:
mq_invert_insert_select (parser, *attr, subquery);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
mq_invert_insert_subquery (parser, attr, subquery->info.query.q.union_.arg1);
if (!pt_has_error (parser))
{
mq_invert_insert_subquery (parser, attr, subquery->info.query.q.union_.arg2);
}
break;
default:
/* should not get here, that is an error! */
/* its almost certainly recoverable, so ignore it */
assert (0);
break;
}
while (*attr && !pt_has_error (parser))
{
attr_next = (*attr)->next;
(*attr)->next = NULL;
pt_find_var (*attr, &result);
if (!result)
{
/* error not invertable/updatable already set */
return;
}
(*attr) = result; /* the name */
(*attr)->next = attr_next;
attr = &(*attr)->next;
}
}
/*
* mq_make_derived_spec() -
* return:
* parser(in):
* node(in):
* subquery(in):
* idx(in):
* spec_ptr(out):
* attr_list_ptr(out):
*/
PT_NODE *
mq_make_derived_spec (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * subquery, int *idx, PT_NODE ** spec_ptr,
PT_NODE ** attr_list_ptr)
{
PT_NODE *range, *spec, *as_attr_list, *col, *tmp;
/* remove unnecessary ORDER BY clause. */
pt_try_remove_order_by (parser, subquery);
/* set line number to range name */
range = pt_name (parser, "av1861");
/* construct new spec */
spec = parser_new_node (parser, PT_SPEC);
if (spec == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
spec->info.spec.derived_table = subquery;
spec->info.spec.derived_table = mq_reset_ids_in_statement (parser, spec->info.spec.derived_table);
spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
spec->info.spec.range_var = range;
spec->info.spec.id = (UINTPTR) spec;
range->info.name.spec_id = (UINTPTR) spec;
/* add new spec to the spec list */
node->info.query.q.select.from = parser_append_node (spec, node->info.query.q.select.from);
/* set spec as unique */
node = mq_regenerate_if_ambiguous (parser, spec, node, node->info.query.q.select.from);
/* construct new attr_list */
spec->info.spec.as_attr_list = as_attr_list = NULL; /* init */
for (col = pt_get_select_list (parser, subquery); col; col = col->next)
{
tmp = pt_name (parser, mq_generate_name (parser, "av", idx));
tmp->info.name.meta_class = PT_NORMAL;
tmp->info.name.resolved = spec->info.spec.range_var->info.name.original;
tmp->info.name.spec_id = spec->info.spec.id;
tmp->type_enum = col->type_enum;
tmp->data_type = parser_copy_tree (parser, col->data_type);
PT_NAME_INFO_SET_FLAG (tmp, PT_NAME_GENERATED_DERIVED_SPEC);
/* keep out hidden columns from derived select list */
if (subquery->info.query.order_by && col->flag.is_hidden_column)
{
col->flag.is_hidden_column = 0;
tmp->flag.is_hidden_column = 0;
spec->info.spec.as_attr_list = parser_append_node (tmp, spec->info.spec.as_attr_list);
}
else
{
spec->info.spec.as_attr_list = parser_append_node (tmp, spec->info.spec.as_attr_list);
as_attr_list = parser_append_node (parser_copy_tree (parser, tmp), as_attr_list);
}
}
/* save spec, attr */
if (spec_ptr)
{
*spec_ptr = spec;
}
if (attr_list_ptr)
{
*attr_list_ptr = as_attr_list;
}
return node;
} /* mq_make_derived_spec */
/*
* mq_class_lambda() - replace class specifiers with their corresponding
* virtual from list
* return:
* parser(in):
* statement(in):
* class(in):
* corresponding_spec(in):
* class_where_part(in):
* class_check_part(in):
* class_group_by_part(in):
* class_having_part(in):
*
* Note:
* A subset of general statements is handled, being
* select - replace the "entity_spec" node in from list
* containing class in its flat_entity_list
* append the where_part, if any.
* update - replace the "entity_spec" node in entity_spec
* if it contains class in its flat_entity_list
* append the where_part, if any.
* insert - replace the "name" node equal to class
* union, difference, intersection
* - the recursive result of this function on both arguments.
*/
PT_NODE *
mq_class_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * class_, PT_NODE * corresponding_spec,
PT_NODE * class_where_part, PT_NODE * class_check_part, PT_NODE * class_group_by_part,
PT_NODE * class_having_part)
{
PT_NODE *spec;
PT_NODE **specptr = NULL;
PT_NODE **where_part = NULL, **where_part_ex = NULL;
PT_NODE **check_where_part = NULL;
PT_NODE *newspec = NULL;
PT_NODE *oldnext = NULL;
PT_NODE *assign, *result;
PT_NODE *attr = NULL, *attr_next = NULL;
PT_NODE **value, *value_next;
PT_NODE *crt_list = NULL, *attr_names = NULL, *attr_names_crt = NULL;
bool build_att_names_list = false, for_update = false;
PT_NODE **lhs, **rhs, *lhs_next, *rhs_next;
const char *newresolved = class_->info.name.resolved;
if (statement == NULL)
{
return NULL;
}
switch (statement->node_type)
{
case PT_SELECT:
statement->info.query.is_subquery = PT_IS_SUBQUERY;
specptr = &statement->info.query.q.select.from;
where_part = &statement->info.query.q.select.where;
check_where_part = &statement->info.query.q.select.check_where;
if (class_group_by_part || class_having_part)
{
/* check for derived */
if (statement->info.query.flag.vspec_as_derived == 1)
{
/* set GROUP BY */
if (class_group_by_part)
{
if (statement->info.query.q.select.group_by)
{
/* this is impossible case. give up */
goto exit_on_error;
}
else
{
statement->info.query.q.select.group_by = parser_copy_tree_list (parser, class_group_by_part);
}
}
/* merge HAVING */
if (class_having_part)
{
PT_NODE **having_part;
having_part = &statement->info.query.q.select.having;
*having_part = parser_append_node (parser_copy_tree_list (parser, class_having_part), *having_part);
}
}
else
{
/* system error */
goto exit_on_error;
}
}
break;
case PT_UPDATE:
specptr = &statement->info.update.spec;
where_part = &statement->info.update.search_cond;
/* Add to statement expressions to check if 'with check option' specified */
check_where_part = NULL;
spec = statement->info.update.spec;
while (spec != NULL && spec->info.spec.id != class_->info.name.spec_id)
{
spec = spec->next;
}
if (spec != NULL)
{
/* Verify if a check_option node already exists for current spec. If so then append condition to existing */
PT_NODE *cw = statement->info.update.check_where;
while (cw != NULL && cw->info.check_option.spec_id != spec->info.spec.id)
{
cw = cw->next;
}
if (cw == NULL)
{
cw = parser_new_node (parser, PT_CHECK_OPTION);
if (cw == NULL)
{
goto exit_on_error;
}
cw->info.check_option.spec_id = corresponding_spec->info.spec.id;
statement->info.update.check_where = parser_append_node (cw, statement->info.update.check_where);
}
check_where_part = &cw->info.check_option.expr;
}
for (assign = statement->info.update.assignment; assign != NULL; assign = assign->next)
{
/* get lhs, rhs */
lhs = &(assign->info.expr.arg1);
rhs = &(assign->info.expr.arg2);
if (PT_IS_N_COLUMN_UPDATE_EXPR (*lhs))
{
/* get lhs element */
lhs = &((*lhs)->info.expr.arg1);
/* get rhs element */
rhs = &((*rhs)->info.query.q.select.list);
}
for (; *lhs && *rhs; *lhs = lhs_next, *rhs = rhs_next)
{
/* cut-off and save next link */
lhs_next = (*lhs)->next;
(*lhs)->next = NULL;
rhs_next = (*rhs)->next;
(*rhs)->next = NULL;
*rhs = mq_translate_value (parser, *rhs);
result = pt_invert (parser, *lhs, *rhs);
if (!result)
{
/* error not invertible/updatable */
PT_ERRORmf (parser, assign, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, *lhs));
goto exit_on_error;
}
if (*lhs)
{
parser_free_tree (parser, *lhs);
}
*lhs = result->next; /* the name */
result->next = NULL;
*rhs = result; /* the right hand side */
lhs = &((*lhs)->next);
rhs = &((*rhs)->next);
}
}
break;
case PT_DELETE:
specptr = &statement->info.delete_.spec;
where_part = &statement->info.delete_.search_cond;
break;
case PT_INSERT:
specptr = &statement->info.insert.spec;
check_where_part = &statement->info.insert.where;
crt_list = statement->info.insert.value_clauses;
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE
|| crt_list->info.node_list.list_type == PT_IS_VALUE)
{
for (; crt_list != NULL; crt_list = crt_list->next)
{
/* Inserting the default values in the original class will "insert" the default view values in the view.
* We don't need to do anything. */
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
{
continue;
}
assert (crt_list->info.node_list.list_type == PT_IS_VALUE);
/* We need to invert expressions now. */
if (attr_names == NULL)
{
/* We'll also build a list of attribute names. */
build_att_names_list = true;
}
else
{
/* The list of attribute names has already been built. */
build_att_names_list = false;
}
attr = statement->info.insert.attr_list;
value = &crt_list->info.node_list.list;
while (*value)
{
if (attr == NULL)
{
/* System error, should have been caught in the semantic pass */
PT_ERRORm (parser, (*value), MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS);
goto exit_on_error;
}
attr_next = attr->next;
attr->next = NULL;
value_next = (*value)->next;
(*value)->next = NULL;
(*value) = mq_translate_value (parser, *value);
result = pt_invert (parser, attr, *value);
if (result == NULL)
{
/* error not invertable/updatable */
PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, attr));
goto exit_on_error;
}
if (build_att_names_list)
{
if (attr_names_crt == NULL)
{
/* This is the first attribute in the name list. */
attr_names_crt = attr_names = result->next;
}
else
{
attr_names_crt->next = result->next;
attr_names_crt = attr_names_crt->next;
}
result->next = NULL;
}
else
{
parser_free_tree (parser, result->next);
result->next = NULL;
}
attr->next = attr_next;
attr = attr->next;
(*value) = result; /* the right hand side */
(*value)->next = value_next;
value = &(*value)->next;
}
}
if (attr_names != NULL)
{
parser_free_tree (parser, statement->info.insert.attr_list);
statement->info.insert.attr_list = attr_names;
attr_names = NULL;
}
}
else if (crt_list->info.node_list.list_type == PT_IS_SUBQUERY)
{
assert (crt_list->next == NULL);
assert (crt_list->info.node_list.list->next == NULL);
mq_invert_insert_subquery (parser, &statement->info.insert.attr_list, crt_list->info.node_list.list);
}
else
{
assert (false);
}
break;
case PT_MERGE:
specptr = &statement->info.merge.into;
where_part = &statement->info.merge.update.search_cond;
where_part_ex = &statement->info.merge.insert.class_where;
/* Add to statement expressions to check if 'with check option' specified */
check_where_part = NULL;
spec = statement->info.merge.into;
if (spec != NULL && spec->info.spec.id == class_->info.name.spec_id)
{
/* Verify if a check_option node already exists for current spec. If so then append condition to existing */
PT_NODE *cw = statement->info.merge.check_where;
while (cw != NULL && cw->info.check_option.spec_id != spec->info.spec.id)
{
cw = cw->next;
}
if (cw == NULL)
{
cw = parser_new_node (parser, PT_CHECK_OPTION);
if (cw == NULL)
{
goto exit_on_error;
}
cw->info.check_option.spec_id = corresponding_spec->info.spec.id;
statement->info.merge.check_where = parser_append_node (cw, statement->info.merge.check_where);
}
check_where_part = &cw->info.check_option.expr;
}
/* check invertible on update assignments */
if (statement->info.merge.update.assignment != NULL)
{
for (assign = statement->info.merge.update.assignment; assign != NULL; assign = assign->next)
{
/* get lhs, rhs */
lhs = &(assign->info.expr.arg1);
rhs = &(assign->info.expr.arg2);
if (PT_IS_N_COLUMN_UPDATE_EXPR (*lhs))
{
/* get lhs element */
lhs = &((*lhs)->info.expr.arg1);
/* get rhs element */
rhs = &((*rhs)->info.query.q.select.list);
}
for (; *lhs && *rhs; *lhs = lhs_next, *rhs = rhs_next)
{
/* cut-off and save next link */
lhs_next = (*lhs)->next;
(*lhs)->next = NULL;
rhs_next = (*rhs)->next;
(*rhs)->next = NULL;
*rhs = mq_translate_value (parser, *rhs);
result = pt_invert (parser, *lhs, *rhs);
if (!result)
{
/* error not invertible/updatable */
PT_ERRORmf (parser, assign, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, *lhs));
goto exit_on_error;
}
if (*lhs)
{
parser_free_tree (parser, *lhs);
}
*lhs = result->next; /* the name */
result->next = NULL;
*rhs = result; /* the right hand side */
lhs = &((*lhs)->next);
rhs = &((*rhs)->next);
}
}
}
/* check insert part attributes */
crt_list = statement->info.merge.insert.value_clauses;
if (crt_list == NULL)
{
break;
}
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE
|| crt_list->info.node_list.list_type == PT_IS_VALUE)
{
for (; crt_list != NULL; crt_list = crt_list->next)
{
/* Inserting the default values in the original class will "insert" the default view values in the view.
* We don't need to do anything. */
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
{
continue;
}
assert (crt_list->info.node_list.list_type == PT_IS_VALUE);
/* We need to invert expressions now. */
if (attr_names == NULL)
{
/* We'll also build a list of attribute names. */
build_att_names_list = true;
}
else
{
/* The list of attribute names has already been built. */
build_att_names_list = false;
}
attr = statement->info.merge.insert.attr_list;
value = &crt_list->info.node_list.list;
while (*value)
{
if (attr == NULL)
{
/* System error, should have been caught in the semantic pass */
PT_ERRORm (parser, (*value), MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS);
break;
}
attr_next = attr->next;
attr->next = NULL;
value_next = (*value)->next;
(*value)->next = NULL;
(*value) = mq_translate_value (parser, *value);
result = pt_invert (parser, attr, *value);
if (result == NULL)
{
/* error not invertable/updatable */
PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, attr));
break;
}
if (build_att_names_list)
{
if (attr_names_crt == NULL)
{
/* This is the first attribute in the name list. */
attr_names_crt = attr_names = result->next;
}
else
{
attr_names_crt->next = result->next;
attr_names_crt = attr_names_crt->next;
}
result->next = NULL;
}
else
{
parser_free_tree (parser, result->next);
result->next = NULL;
}
attr->next = attr_next;
attr = attr->next;
(*value) = result; /* the right hand side */
(*value)->next = value_next;
value = &(*value)->next;
}
}
if (attr_names != NULL)
{
parser_free_tree (parser, statement->info.merge.insert.attr_list);
statement->info.merge.insert.attr_list = attr_names;
attr_names = NULL;
}
}
else
{
assert (false);
}
break;
#if 0 /* this is impossible case */
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
statement->info.query.q.union_.arg1 =
mq_class_lambda (parser, statement->info.query.q.union_.arg1, class_, corresponding_spec, class_where_part,
class_check_part, class_group_by_part, class_having_part);
statement->info.query.q.union_.arg2 =
mq_class_lambda (parser, statement->info.query.q.union_.arg2, class_, corresponding_spec, class_where_part,
class_check_part, class_group_by_part, class_having_part);
break;
#endif /* this is impossible case */
default:
/* system error */
goto exit_on_error;
}
/* check found spec */
spec = pt_find_spec (parser, *specptr, class_);
if (spec == NULL)
{
/* class_'s spec was not found in spec list */
PT_INTERNAL_ERROR (parser, "class spec not found");
goto exit_on_error;
}
if (spec->info.spec.join_type == PT_JOIN_LEFT_OUTER)
{
/* handle is a where parts of view sub-querys */
if (specptr)
{
spec = *specptr;
while (spec && class_->info.name.spec_id != spec->info.spec.id)
{
specptr = &spec->next;
spec = *specptr;
}
if (spec)
{
SPEC_RESET_INFO spec_reset;
PT_NODE *subpaths;
newspec = parser_copy_tree_list (parser, corresponding_spec);
oldnext = spec->next;
spec->next = NULL;
subpaths = spec->info.spec.path_entities;
spec_reset.sub_paths = &subpaths;
spec_reset.statement = statement;
spec_reset.old_next = oldnext;
spec->info.spec.path_entities = NULL;
if (newspec)
{
if (newspec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
{
/* flat_entity_list is needed to gather referenced oids in xasl_generation
* in pt_spec_to_xasl_class_oid_list */
newspec->info.spec.flat_entity_list = spec->info.spec.flat_entity_list;
spec->info.spec.flat_entity_list = NULL;
}
else
{
newspec->info.spec.range_var->info.name.original = spec->info.spec.range_var->info.name.original;
}
newspec->info.spec.location = spec->info.spec.location;
/* move join info */
if (spec->info.spec.join_type != PT_JOIN_NONE)
{
newspec->info.spec.join_type = spec->info.spec.join_type;
newspec->info.spec.on_cond = spec->info.spec.on_cond;
/* handle is a where parts of view sub-querys except for outer join */
if (where_part)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_where_part),
newspec->info.spec.on_cond);
/* class where part of merge insert clause */
if (where_part_ex)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_where_part),
newspec->info.spec.on_cond);
}
}
if (check_where_part)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_check_part),
newspec->info.spec.on_cond);
}
spec->info.spec.on_cond = NULL;
parser_walk_tree (parser, newspec->info.spec.on_cond, mq_mark_location,
&(newspec->info.spec.location), NULL, NULL);
}
}
for_update = (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_FOR_UPDATE)
&& (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE));
parser_free_tree (parser, spec);
if (newspec)
{
*specptr = newspec;
parser_append_node (oldnext, newspec);
newspec =
parser_walk_tree (parser, newspec, mq_reset_spec_distr_subpath_pre, &spec_reset,
mq_reset_spec_distr_subpath_post, &spec_reset);
statement = spec_reset.statement;
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
goto exit_on_error;
}
}
else
{
/* we are doing a null substitution. ie the classes don't match the spec. The "correct translation" is NULL.
* */
goto exit_on_error;
}
}
}
else
{
/* handle is a where parts of view sub-querys except for outer join */
if (where_part)
{
/* force sub expressions to be parenthesized for correct printing. Otherwise, the associativity may be wrong when
* the statement is printed and sent to a local database */
if (class_where_part && class_where_part->node_type == PT_EXPR)
{
class_where_part->info.expr.paren_type = 1;
}
if ((*where_part) && (*where_part)->node_type == PT_EXPR)
{
(*where_part)->info.expr.paren_type = 1;
}
/* Set predicates to be evaluated first */
pt_set_pred_order (parser, class_where_part, 1);
/* The "where clause" is in the form of a list of CNF "and" terms. In order to "and" together the view's "where
* clause" with the statement's, we must maintain this list of terms. Using a 'PT_AND' node here will have the
* effect of losing the "and" terms on the tail of either list. */
*where_part = parser_append_node (parser_copy_tree_list (parser, class_where_part), *where_part);
/* class where part of merge insert clause */
if (where_part_ex)
{
if ((*where_part_ex) && (*where_part_ex)->node_type == PT_EXPR)
{
(*where_part_ex)->info.expr.paren_type = 1;
}
*where_part_ex = parser_append_node (parser_copy_tree_list (parser, class_where_part), *where_part_ex);
}
}
if (check_where_part)
{
if (class_check_part && class_check_part->node_type == PT_EXPR)
{
class_check_part->info.expr.paren_type = 1;
}
if ((*check_where_part) && (*check_where_part)->node_type == PT_EXPR)
{
(*check_where_part)->info.expr.paren_type = 1;
}
*check_where_part = parser_append_node (parser_copy_tree_list (parser, class_check_part), *check_where_part);
}
if (specptr)
{
spec = *specptr;
while (spec && class_->info.name.spec_id != spec->info.spec.id)
{
specptr = &spec->next;
spec = *specptr;
}
if (spec)
{
SPEC_RESET_INFO spec_reset;
PT_NODE *subpaths;
newspec = parser_copy_tree_list (parser, corresponding_spec);
oldnext = spec->next;
spec->next = NULL;
subpaths = spec->info.spec.path_entities;
spec_reset.sub_paths = &subpaths;
spec_reset.statement = statement;
spec_reset.old_next = oldnext;
spec->info.spec.path_entities = NULL;
if (newspec)
{
if (newspec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
{
/* flat_entity_list is needed to gather referenced oids in xasl_generation
* in pt_spec_to_xasl_class_oid_list */
newspec->info.spec.flat_entity_list = spec->info.spec.flat_entity_list;
spec->info.spec.flat_entity_list = NULL;
}
else
{
newspec->info.spec.range_var->info.name.original = spec->info.spec.range_var->info.name.original;
}
newspec->info.spec.location = spec->info.spec.location;
/* move join info */
if (spec->info.spec.join_type != PT_JOIN_NONE)
{
newspec->info.spec.join_type = spec->info.spec.join_type;
newspec->info.spec.on_cond = spec->info.spec.on_cond;
spec->info.spec.on_cond = NULL;
}
}
for_update = (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_FOR_UPDATE)
&& (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE));
parser_free_tree (parser, spec);
if (newspec)
{
*specptr = newspec;
parser_append_node (oldnext, newspec);
newspec =
parser_walk_tree (parser, newspec, mq_reset_spec_distr_subpath_pre, &spec_reset,
mq_reset_spec_distr_subpath_post, &spec_reset);
statement = spec_reset.statement;
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
goto exit_on_error;
}
}
else
{
/* we are doing a null substitution. ie the classes don't match the spec. The "correct translation" is NULL.
* */
goto exit_on_error;
}
}
}
if (statement)
{
/* The spec id's are those copied from the cache. They are unique in this statment tree, but will not be unique
* if this tree is once more translated against the same virtual class_. Now, the newly introduced entity specs,
* are gone through and the id's for each and each name reset again to a new (uncopied) unique number, to
* preserve the uniqueness of the specs. */
for (spec = newspec; spec != NULL; spec = spec->next)
{
if (spec == oldnext)
{
break; /* these are already ok */
}
/* translate virtual sub-paths */
statement = mq_translate_paths (parser, statement, spec);
/* reset ids of path specs, or toss them, as necessary */
statement = mq_reset_paths (parser, statement, spec);
if (for_update)
{
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
}
}
if (newspec)
{
if (!PT_IS_QUERY_NODE_TYPE (statement->node_type))
{
/* PT_INSERT, PT_UPDATE, PT_DELETE */
statement = mq_rename_resolved (parser, newspec, statement, newresolved);
newspec = newspec->next;
}
for (spec = newspec; spec != NULL; spec = spec->next)
{
if (spec == oldnext || statement == NULL)
{
break; /* these are already ok */
}
if (spec->info.spec.range_var->alias_print)
{
char *temp;
temp = pt_append_string (parser, NULL, newresolved);
temp = pt_append_string (parser, temp, ":");
temp = pt_append_string (parser, temp, spec->info.spec.range_var->alias_print);
spec->info.spec.range_var->alias_print = temp;
}
else
{
spec->info.spec.range_var->alias_print = newresolved;
}
statement = mq_regenerate_if_ambiguous (parser, spec, statement, statement->info.query.q.select.from);
}
}
}
return statement;
exit_on_error:
if (attr_names != NULL)
{
parser_free_tree (parser, attr_names);
}
return NULL;
}
/*
* mq_inline_view_lambda() - replace class specifiers with their corresponding
* virtual from list
* return:
* parser(in):
* statement(in):
* class(in):
* corresponding_spec(in):
* class_where_part(in):
* class_check_part(in):
* class_group_by_part(in):
* class_having_part(in):
*
* Note:
* A subset of general statements is handled, being
* select - replace the "entity_spec" node in from list
* containing class in its flat_entity_list
* append the where_part, if any.
* update - replace the "entity_spec" node in entity_spec
* if it contains class in its flat_entity_list
* append the where_part, if any.
* insert - replace the "name" node equal to class
* union, difference, intersection
* - the recursive result of this function on both arguments.
*/
PT_NODE *
mq_inline_view_lambda (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * derived_spec,
PT_NODE * corresponding_spec, PT_NODE * class_where_part, PT_NODE * class_check_part,
PT_NODE * class_group_by_part, PT_NODE * class_having_part)
{
PT_NODE *spec;
PT_NODE **specptr = NULL;
PT_NODE **where_part = NULL, **where_part_ex = NULL;
PT_NODE **check_where_part = NULL;
PT_NODE *newspec = NULL;
PT_NODE *oldnext = NULL;
PT_NODE *assign, *result;
PT_NODE *attr = NULL, *attr_next = NULL;
PT_NODE **value, *value_next;
PT_NODE *crt_list = NULL, *attr_names = NULL, *attr_names_crt = NULL;
bool build_att_names_list = false, for_update = false;
PT_NODE **lhs, **rhs, *lhs_next, *rhs_next;
const char *newresolved = derived_spec->info.spec.range_var->info.name.original;
if (statement == NULL)
{
return NULL;
}
switch (statement->node_type)
{
case PT_SELECT:
statement->info.query.is_subquery = PT_IS_SUBQUERY;
specptr = &statement->info.query.q.select.from;
where_part = &statement->info.query.q.select.where;
check_where_part = &statement->info.query.q.select.check_where;
if (class_group_by_part || class_having_part)
{
/* check for derived */
if (statement->info.query.flag.vspec_as_derived == 1)
{
/* set GROUP BY */
if (class_group_by_part)
{
if (statement->info.query.q.select.group_by)
{
/* this is impossible case. give up */
goto exit_on_error;
}
else
{
statement->info.query.q.select.group_by = parser_copy_tree_list (parser, class_group_by_part);
}
}
/* merge HAVING */
if (class_having_part)
{
PT_NODE **having_part;
having_part = &statement->info.query.q.select.having;
*having_part = parser_append_node (parser_copy_tree_list (parser, class_having_part), *having_part);
}
}
else
{
/* system error */
goto exit_on_error;
}
}
break;
case PT_UPDATE:
specptr = &statement->info.update.spec;
where_part = &statement->info.update.search_cond;
/* Add to statement expressions to check if 'with check option' specified */
check_where_part = NULL;
spec = statement->info.update.spec;
while (spec != NULL && spec->info.spec.id != derived_spec->info.spec.id)
{
spec = spec->next;
}
if (spec != NULL)
{
/* Verify if a check_option node already exists for current spec. If so then append condition to existing */
PT_NODE *cw = statement->info.update.check_where;
while (cw != NULL && cw->info.check_option.spec_id != spec->info.spec.id)
{
cw = cw->next;
}
if (cw == NULL)
{
cw = parser_new_node (parser, PT_CHECK_OPTION);
if (cw == NULL)
{
goto exit_on_error;
}
cw->info.check_option.spec_id = corresponding_spec->info.spec.id;
statement->info.update.check_where = parser_append_node (cw, statement->info.update.check_where);
}
check_where_part = &cw->info.check_option.expr;
}
for (assign = statement->info.update.assignment; assign != NULL; assign = assign->next)
{
/* get lhs, rhs */
lhs = &(assign->info.expr.arg1);
rhs = &(assign->info.expr.arg2);
if (PT_IS_N_COLUMN_UPDATE_EXPR (*lhs))
{
/* get lhs element */
lhs = &((*lhs)->info.expr.arg1);
/* get rhs element */
rhs = &((*rhs)->info.query.q.select.list);
}
for (; *lhs && *rhs; *lhs = lhs_next, *rhs = rhs_next)
{
/* cut-off and save next link */
lhs_next = (*lhs)->next;
(*lhs)->next = NULL;
rhs_next = (*rhs)->next;
(*rhs)->next = NULL;
*rhs = mq_translate_value (parser, *rhs);
result = pt_invert (parser, *lhs, *rhs);
if (!result)
{
/* error not invertible/updatable */
PT_ERRORmf (parser, assign, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, *lhs));
goto exit_on_error;
}
if (*lhs)
{
parser_free_tree (parser, *lhs);
}
*lhs = result->next; /* the name */
result->next = NULL;
*rhs = result; /* the right hand side */
lhs = &((*lhs)->next);
rhs = &((*rhs)->next);
}
}
break;
case PT_DELETE:
specptr = &statement->info.delete_.spec;
where_part = &statement->info.delete_.search_cond;
break;
case PT_INSERT:
specptr = &statement->info.insert.spec;
check_where_part = &statement->info.insert.where;
crt_list = statement->info.insert.value_clauses;
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE
|| crt_list->info.node_list.list_type == PT_IS_VALUE)
{
for (; crt_list != NULL; crt_list = crt_list->next)
{
/* Inserting the default values in the original class will "insert" the default view values in the view.
* We don't need to do anything. */
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
{
continue;
}
assert (crt_list->info.node_list.list_type == PT_IS_VALUE);
/* We need to invert expressions now. */
if (attr_names == NULL)
{
/* We'll also build a list of attribute names. */
build_att_names_list = true;
}
else
{
/* The list of attribute names has already been built. */
build_att_names_list = false;
}
attr = statement->info.insert.attr_list;
value = &crt_list->info.node_list.list;
while (*value)
{
if (attr == NULL)
{
/* System error, should have been caught in the semantic pass */
PT_ERRORm (parser, (*value), MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS);
goto exit_on_error;
}
attr_next = attr->next;
attr->next = NULL;
value_next = (*value)->next;
(*value)->next = NULL;
(*value) = mq_translate_value (parser, *value);
result = pt_invert (parser, attr, *value);
if (result == NULL)
{
/* error not invertable/updatable */
PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, attr));
goto exit_on_error;
}
if (build_att_names_list)
{
if (attr_names_crt == NULL)
{
/* This is the first attribute in the name list. */
attr_names_crt = attr_names = result->next;
}
else
{
attr_names_crt->next = result->next;
attr_names_crt = attr_names_crt->next;
}
result->next = NULL;
}
else
{
parser_free_tree (parser, result->next);
result->next = NULL;
}
attr->next = attr_next;
attr = attr->next;
(*value) = result; /* the right hand side */
(*value)->next = value_next;
value = &(*value)->next;
}
}
if (attr_names != NULL)
{
parser_free_tree (parser, statement->info.insert.attr_list);
statement->info.insert.attr_list = attr_names;
attr_names = NULL;
}
}
else if (crt_list->info.node_list.list_type == PT_IS_SUBQUERY)
{
assert (crt_list->next == NULL);
assert (crt_list->info.node_list.list->next == NULL);
mq_invert_insert_subquery (parser, &statement->info.insert.attr_list, crt_list->info.node_list.list);
}
else
{
assert (false);
}
break;
case PT_MERGE:
specptr = &statement->info.merge.into;
where_part = &statement->info.merge.update.search_cond;
where_part_ex = &statement->info.merge.insert.class_where;
/* Add to statement expressions to check if 'with check option' specified */
check_where_part = NULL;
spec = statement->info.merge.into;
if (spec != NULL && spec->info.spec.id == derived_spec->info.spec.id)
{
/* Verify if a check_option node already exists for current spec. If so then append condition to existing */
PT_NODE *cw = statement->info.merge.check_where;
while (cw != NULL && cw->info.check_option.spec_id != spec->info.spec.id)
{
cw = cw->next;
}
if (cw == NULL)
{
cw = parser_new_node (parser, PT_CHECK_OPTION);
if (cw == NULL)
{
goto exit_on_error;
}
cw->info.check_option.spec_id = corresponding_spec->info.spec.id;
statement->info.merge.check_where = parser_append_node (cw, statement->info.merge.check_where);
}
check_where_part = &cw->info.check_option.expr;
}
/* check invertible on update assignments */
if (statement->info.merge.update.assignment != NULL)
{
for (assign = statement->info.merge.update.assignment; assign != NULL; assign = assign->next)
{
/* get lhs, rhs */
lhs = &(assign->info.expr.arg1);
rhs = &(assign->info.expr.arg2);
if (PT_IS_N_COLUMN_UPDATE_EXPR (*lhs))
{
/* get lhs element */
lhs = &((*lhs)->info.expr.arg1);
/* get rhs element */
rhs = &((*rhs)->info.query.q.select.list);
}
for (; *lhs && *rhs; *lhs = lhs_next, *rhs = rhs_next)
{
/* cut-off and save next link */
lhs_next = (*lhs)->next;
(*lhs)->next = NULL;
rhs_next = (*rhs)->next;
(*rhs)->next = NULL;
*rhs = mq_translate_value (parser, *rhs);
result = pt_invert (parser, *lhs, *rhs);
if (!result)
{
/* error not invertible/updatable */
PT_ERRORmf (parser, assign, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, *lhs));
goto exit_on_error;
}
if (*lhs)
{
parser_free_tree (parser, *lhs);
}
*lhs = result->next; /* the name */
result->next = NULL;
*rhs = result; /* the right hand side */
lhs = &((*lhs)->next);
rhs = &((*rhs)->next);
}
}
}
/* check insert part attributes */
crt_list = statement->info.merge.insert.value_clauses;
if (crt_list == NULL)
{
break;
}
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE
|| crt_list->info.node_list.list_type == PT_IS_VALUE)
{
for (; crt_list != NULL; crt_list = crt_list->next)
{
/* Inserting the default values in the original class will "insert" the default view values in the view.
* We don't need to do anything. */
if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
{
continue;
}
assert (crt_list->info.node_list.list_type == PT_IS_VALUE);
/* We need to invert expressions now. */
if (attr_names == NULL)
{
/* We'll also build a list of attribute names. */
build_att_names_list = true;
}
else
{
/* The list of attribute names has already been built. */
build_att_names_list = false;
}
attr = statement->info.merge.insert.attr_list;
value = &crt_list->info.node_list.list;
while (*value)
{
if (attr == NULL)
{
/* System error, should have been caught in the semantic pass */
PT_ERRORm (parser, (*value), MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ATTRS_GT_QSPEC_COLS);
break;
}
attr_next = attr->next;
attr->next = NULL;
value_next = (*value)->next;
(*value)->next = NULL;
(*value) = mq_translate_value (parser, *value);
result = pt_invert (parser, attr, *value);
if (result == NULL)
{
/* error not invertable/updatable */
PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VASG_TGT_UNINVERTBL,
pt_short_print (parser, attr));
break;
}
if (build_att_names_list)
{
if (attr_names_crt == NULL)
{
/* This is the first attribute in the name list. */
attr_names_crt = attr_names = result->next;
}
else
{
attr_names_crt->next = result->next;
attr_names_crt = attr_names_crt->next;
}
result->next = NULL;
}
else
{
parser_free_tree (parser, result->next);
result->next = NULL;
}
attr->next = attr_next;
attr = attr->next;
(*value) = result; /* the right hand side */
(*value)->next = value_next;
value = &(*value)->next;
}
}
if (attr_names != NULL)
{
parser_free_tree (parser, statement->info.merge.insert.attr_list);
statement->info.merge.insert.attr_list = attr_names;
attr_names = NULL;
}
}
else
{
assert (false);
}
break;
#if 0 /* this is impossible case */
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
statement->info.query.q.union_.arg1 =
mq_class_lambda (parser, statement->info.query.q.union_.arg1, class_, corresponding_spec, class_where_part,
class_check_part, class_group_by_part, class_having_part);
statement->info.query.q.union_.arg2 =
mq_class_lambda (parser, statement->info.query.q.union_.arg2, class_, corresponding_spec, class_where_part,
class_check_part, class_group_by_part, class_having_part);
break;
#endif /* this is impossible case */
default:
/* system error */
goto exit_on_error;
}
if (derived_spec->info.spec.join_type == PT_JOIN_LEFT_OUTER)
{
/* handle is a where parts of view sub-querys */
if (specptr)
{
spec = *specptr;
while (spec && derived_spec->info.spec.id != spec->info.spec.id)
{
specptr = &spec->next;
spec = *specptr;
}
if (spec)
{
SPEC_RESET_INFO spec_reset;
PT_NODE *subpaths;
newspec = parser_copy_tree_list (parser, corresponding_spec);
oldnext = spec->next;
spec->next = NULL;
subpaths = spec->info.spec.path_entities;
spec_reset.sub_paths = &subpaths;
spec_reset.statement = statement;
spec_reset.old_next = oldnext;
spec->info.spec.path_entities = NULL;
if (newspec)
{
if (newspec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
{
/* flat_entity_list is needed to gather referenced oids in xasl_generation
* in pt_spec_to_xasl_class_oid_list */
newspec->info.spec.flat_entity_list = spec->info.spec.flat_entity_list;
spec->info.spec.flat_entity_list = NULL;
}
else
{
newspec->info.spec.range_var->info.name.original = spec->info.spec.range_var->info.name.original;
}
newspec->info.spec.location = spec->info.spec.location;
/* move join info */
if (spec->info.spec.join_type != PT_JOIN_NONE)
{
newspec->info.spec.join_type = spec->info.spec.join_type;
newspec->info.spec.on_cond = spec->info.spec.on_cond;
/* handle is a where parts of view sub-querys except for outer join */
if (where_part)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_where_part),
newspec->info.spec.on_cond);
/* class where part of merge insert clause */
if (where_part_ex)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_where_part),
newspec->info.spec.on_cond);
}
}
if (check_where_part)
{
newspec->info.spec.on_cond =
parser_append_node (parser_copy_tree_list (parser, class_check_part),
newspec->info.spec.on_cond);
}
spec->info.spec.on_cond = NULL;
parser_walk_tree (parser, newspec->info.spec.on_cond, mq_mark_location,
&(newspec->info.spec.location), NULL, NULL);
}
}
for_update = (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_FOR_UPDATE)
&& (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE));
parser_free_tree (parser, spec);
if (newspec)
{
*specptr = newspec;
parser_append_node (oldnext, newspec);
newspec =
parser_walk_tree (parser, newspec, mq_reset_spec_distr_subpath_pre, &spec_reset,
mq_reset_spec_distr_subpath_post, &spec_reset);
statement = spec_reset.statement;
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
goto exit_on_error;
}
}
else
{
/* we are doing a null substitution. ie the classes don't match the spec. The "correct translation" is NULL.
* */
goto exit_on_error;
}
}
}
else
{
/* handle is a where parts of view sub-querys except for outer join */
if (where_part)
{
/* force sub expressions to be parenthesized for correct printing. Otherwise, the associativity may be wrong when
* the statement is printed and sent to a local database */
if (class_where_part && class_where_part->node_type == PT_EXPR)
{
class_where_part->info.expr.paren_type = 1;
}
if ((*where_part) && (*where_part)->node_type == PT_EXPR)
{
(*where_part)->info.expr.paren_type = 1;
}
/* Set predicates to be evaluated first */
pt_set_pred_order (parser, class_where_part, 1);
/* The "where clause" is in the form of a list of CNF "and" terms. In order to "and" together the view's "where
* clause" with the statement's, we must maintain this list of terms. Using a 'PT_AND' node here will have the
* effect of losing the "and" terms on the tail of either list. */
*where_part = parser_append_node (parser_copy_tree_list (parser, class_where_part), *where_part);
/* class where part of merge insert clause */
if (where_part_ex)
{
if ((*where_part_ex) && (*where_part_ex)->node_type == PT_EXPR)
{
(*where_part_ex)->info.expr.paren_type = 1;
}
*where_part_ex = parser_append_node (parser_copy_tree_list (parser, class_where_part), *where_part_ex);
}
}
if (check_where_part)
{
if (class_check_part && class_check_part->node_type == PT_EXPR)
{
class_check_part->info.expr.paren_type = 1;
}
if ((*check_where_part) && (*check_where_part)->node_type == PT_EXPR)
{
(*check_where_part)->info.expr.paren_type = 1;
}
*check_where_part = parser_append_node (parser_copy_tree_list (parser, class_check_part), *check_where_part);
}
if (specptr)
{
spec = *specptr;
while (spec && derived_spec->info.spec.id != spec->info.spec.id)
{
specptr = &spec->next;
spec = *specptr;
}
if (spec)
{
SPEC_RESET_INFO spec_reset;
PT_NODE *subpaths;
newspec = parser_copy_tree_list (parser, corresponding_spec);
oldnext = spec->next;
spec->next = NULL;
subpaths = spec->info.spec.path_entities;
spec_reset.sub_paths = &subpaths;
spec_reset.statement = statement;
spec_reset.old_next = oldnext;
spec->info.spec.path_entities = NULL;
if (newspec)
{
if (newspec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
{
/* flat_entity_list is needed to gather referenced oids in xasl_generation
* in pt_spec_to_xasl_class_oid_list */
newspec->info.spec.flat_entity_list = spec->info.spec.flat_entity_list;
spec->info.spec.flat_entity_list = NULL;
}
else
{
newspec->info.spec.range_var->info.name.original = spec->info.spec.range_var->info.name.original;
}
newspec->info.spec.location = spec->info.spec.location;
/* move join info */
if (spec->info.spec.join_type != PT_JOIN_NONE)
{
newspec->info.spec.join_type = spec->info.spec.join_type;
newspec->info.spec.on_cond = spec->info.spec.on_cond;
spec->info.spec.on_cond = NULL;
}
}
for_update = (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_FOR_UPDATE)
&& (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE));
parser_free_tree (parser, spec);
if (newspec)
{
*specptr = newspec;
parser_append_node (oldnext, newspec);
newspec =
parser_walk_tree (parser, newspec, mq_reset_spec_distr_subpath_pre, &spec_reset,
mq_reset_spec_distr_subpath_post, &spec_reset);
statement = spec_reset.statement;
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
goto exit_on_error;
}
}
else
{
/* we are doing a null substitution. ie the classes don't match the spec. The "correct translation" is NULL.
* */
goto exit_on_error;
}
}
}
if (statement)
{
/* The spec id's are those copied from the cache. They are unique in this statment tree, but will not be unique
* if this tree is once more translated against the same virtual class_. Now, the newly introduced entity specs,
* are gone through and the id's for each and each name reset again to a new (uncopied) unique number, to
* preserve the uniqueness of the specs. */
for (spec = newspec; spec != NULL; spec = spec->next)
{
if (spec == oldnext)
{
break; /* these are already ok */
}
/* translate virtual sub-paths */
statement = mq_translate_paths (parser, statement, spec);
/* reset ids of path specs, or toss them, as necessary */
statement = mq_reset_paths (parser, statement, spec);
if (for_update)
{
spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
}
}
if (newspec)
{
if (!PT_IS_QUERY_NODE_TYPE (statement->node_type))
{
/* PT_INSERT, PT_UPDATE, PT_DELETE */
statement = mq_rename_resolved (parser, newspec, statement, newresolved);
newspec = newspec->next;
}
for (spec = newspec; spec != NULL; spec = spec->next)
{
if (spec == oldnext || statement == NULL)
{
break; /* these are already ok */
}
if (spec->info.spec.range_var->alias_print)
{
char *temp;
temp = pt_append_string (parser, NULL, newresolved);
temp = pt_append_string (parser, temp, ":");
temp = pt_append_string (parser, temp, spec->info.spec.range_var->alias_print);
spec->info.spec.range_var->alias_print = temp;
}
else
{
spec->info.spec.range_var->alias_print = newresolved;
}
statement = mq_regenerate_if_ambiguous (parser, spec, statement, statement->info.query.q.select.from);
}
}
}
return statement;
exit_on_error:
if (attr_names != NULL)
{
parser_free_tree (parser, attr_names);
}
return NULL;
}
/*
* mq_push_arg2() - makes the first item of each top level select into
* path expression with arg2
* return:
* parser(in):
* query(in):
* dot_arg2(in):
*/
static PT_NODE *
mq_push_arg2 (PARSER_CONTEXT * parser, PT_NODE * query, PT_NODE * dot_arg2)
{
PT_NODE *dot;
PT_NODE *spec = NULL;
PT_NODE *new_spec;
PT_NODE *name;
switch (query->node_type)
{
case PT_SELECT:
if (PT_IS_QUERY_NODE_TYPE (query->info.query.q.select.list->node_type))
{
query->info.query.q.select.list = mq_push_arg2 (parser, query->info.query.q.select.list, dot_arg2);
}
else
{
name = query->info.query.q.select.list;
if (name->node_type != PT_NAME)
{
if (name->node_type == PT_DOT_)
{
name = name->info.dot.arg2;
}
else if (name->node_type == PT_METHOD_CALL)
{
name = name->info.method_call.method_name;
}
else
{
name = NULL;
}
}
if (name)
{
spec = pt_find_entity (parser, query->info.query.q.select.from, name->info.name.spec_id);
}
if (spec == NULL)
{
break;
}
dot = parser_copy_tree (parser, dot_arg2);
if (dot == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return NULL;
}
dot->info.dot.arg1 = query->info.query.q.select.list;
query->info.query.q.select.list = dot;
new_spec = pt_insert_entity (parser, dot, spec, NULL);
parser_free_tree (parser, query->data_type);
query->type_enum = dot->type_enum;
query->data_type = parser_copy_tree_list (parser, dot->data_type);
query = mq_translate_paths (parser, query, spec);
query = mq_reset_paths (parser, query, spec);
}
break;
case PT_UNION:
case PT_INTERSECTION:
case PT_DIFFERENCE:
query->info.query.q.union_.arg1 = mq_push_arg2 (parser, query->info.query.q.union_.arg1, dot_arg2);
query->info.query.q.union_.arg2 = mq_push_arg2 (parser, query->info.query.q.union_.arg2, dot_arg2);
parser_free_tree (parser, query->data_type);
query->type_enum = query->info.query.q.union_.arg1->type_enum;
query->data_type = parser_copy_tree_list (parser, query->info.query.q.union_.arg1->data_type);
break;
default:
break;
}
return query;
}
/*
* mq_lambda_node_pre() - creates extra spec frames for each select
* return:
* parser(in):
* tree(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_lambda_node_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
MQ_LAMBDA_ARG *lambda_arg = (MQ_LAMBDA_ARG *) void_arg;
PT_EXTRA_SPECS_FRAME *spec_frame;
if (tree->node_type == PT_SELECT)
{
spec_frame = (PT_EXTRA_SPECS_FRAME *) malloc (sizeof (PT_EXTRA_SPECS_FRAME));
if (spec_frame == NULL)
{
PT_INTERNAL_ERROR (parser, "malloc");
return NULL;
}
spec_frame->next = lambda_arg->spec_frames;
spec_frame->extra_specs = NULL;
lambda_arg->spec_frames = spec_frame;
}
return tree;
} /* mq_lambda_node_pre */
/*
* mq_lambda_node() - applies the lambda test to the node passed to it,
* and conditionally substitutes a copy of its corresponding tree
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_lambda_node (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
MQ_LAMBDA_ARG *lambda_arg = (MQ_LAMBDA_ARG *) void_arg;
PT_NODE *save_node_next, *result, *arg1, *spec;
PT_NODE *dt1, *dt2;
PT_EXTRA_SPECS_FRAME *spec_frame;
PT_NODE *save_data_type;
PT_NODE *name, *tree;
result = node;
switch (node->node_type)
{
case PT_DOT_:
/* Check if the recursive call left an "illegal" path expression */
if ((arg1 = node->info.dot.arg1))
{
save_node_next = node->next;
if (PT_IS_QUERY_NODE_TYPE (arg1->node_type))
{
node->info.dot.arg1 = NULL;
node->next = NULL;
result = mq_push_arg2 (parser, arg1, node);
parser_free_tree (parser, node); /* re-use this memory */
/* could free data_type, and entity_list here too. */
/* if this name was in a name list, keep the list tail */
if (result)
{
result->next = save_node_next;
}
}
else if (arg1->node_type == PT_NAME && PT_IS_OID_NAME (arg1))
{
/* we have an artificial path, from a view that selects an oid, eg create view foo (a) as select x from x
* It would be nice to translate this to just the RHS, but subsequent path translation would have nothing
* to key off of. */
}
else if (PT_IS_NULL_NODE (arg1))
{
/* someone did a select a.b from view, where a is a null the result is also NULL. */
node->info.dot.arg1 = NULL;
node->next = NULL;
result = arg1;
parser_free_tree (parser, node); /* re-use this memory */
/* if this name was in a name list, keep the list tail */
result->next = save_node_next;
}
}
break;
case PT_NAME:
for (name = lambda_arg->name_list, tree = lambda_arg->tree_list; name && tree;
name = name->next, tree = tree->next)
{
/* If the names are equal, substitute new sub tree Here we DON't want to do the usual strict name-datatype
* matching. This is where we project one object attribute as another, so we deliberately allow the loosely
* typed match by nulling the data_type. */
save_data_type = name->data_type; /* save */
name->data_type = NULL;
if (pt_name_equal (parser, node, name))
{
save_node_next = node->next;
node->next = NULL;
result = parser_copy_tree (parser, tree); /* substitute */
/* Keep hidden column information during view translation */
if (result)
{
result->line_number = node->line_number;
result->column_number = node->column_number;
result->flag.is_hidden_column = node->flag.is_hidden_column;
result->buffer_pos = node->buffer_pos;
#if 0
result->info.name.original = node->info.name.original;
#endif /* 0 */
}
/* we may have just copied a whole query, if so, reset its id's */
result = mq_reset_specs_from_column (parser, result, tree);
if (lambda_arg->spec_frames && node->info.name.meta_class == PT_SHARED)
{
PT_NODE *class_spec;
PT_NODE *entity;
/* check for found */
for (class_spec = lambda_arg->spec_frames->extra_specs; class_spec; class_spec = class_spec->next)
{
entity = class_spec->info.spec.entity_name;
if (!pt_user_specified_name_compare (entity->info.name.original, result->info.name.resolved))
{
break; /* found */
}
}
if (!class_spec)
{ /* not found */
class_spec = mq_new_spec (parser, result->info.name.resolved);
if (class_spec == NULL)
{
return NULL;
}
/* add the new spec to the extra_specs */
lambda_arg->spec_frames->extra_specs =
parser_append_node (class_spec, lambda_arg->spec_frames->extra_specs);
}
/* resolve the name node to the new spec */
result->info.name.spec_id = class_spec->info.spec.id;
}
parser_free_tree (parser, node); /* re-use this memory */
result->next = save_node_next;
name->data_type = save_data_type; /* restore */
break; /* exit for-loop */
}
/* name did not match. go ahead */
name->data_type = save_data_type; /* restore */
}
break;
case PT_SELECT:
/* maintain virtual data type information */
if ((dt1 = result->data_type) && result->info.query.q.select.list
&& (dt2 = result->info.query.q.select.list->data_type))
{
parser_free_tree (parser, result->data_type);
result->data_type = parser_copy_tree_list (parser, dt2);
}
/* pop the extra spec frame and add any extra specs to the from list */
spec_frame = lambda_arg->spec_frames;
lambda_arg->spec_frames = lambda_arg->spec_frames->next;
result->info.query.q.select.from = parser_append_node (spec_frame->extra_specs, result->info.query.q.select.from);
/* adding specs may have created ambiguous spec names */
for (spec = spec_frame->extra_specs; spec != NULL; spec = spec->next)
{
result = mq_regenerate_if_ambiguous (parser, spec, result, result->info.query.q.select.from);
}
free_and_init (spec_frame);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
/* maintain virtual data type information */
if ((dt1 = result->data_type) && result->info.query.q.union_.arg1
&& (dt2 = result->info.query.q.union_.arg1->data_type))
{
parser_free_tree (parser, result->data_type);
result->data_type = parser_copy_tree_list (parser, dt2);
}
break;
default:
break;
}
return result;
}
/*
* mq_lambda() - modifies name nodes with copies of a corresponding tree
* return:
* parser(in):
* tree_with_names(in):
* name_node_list(in):
* corresponding_tree_list(in):
*/
PT_NODE *
mq_lambda (PARSER_CONTEXT * parser, PT_NODE * tree_with_names, PT_NODE * name_node_list,
PT_NODE * corresponding_tree_list)
{
MQ_LAMBDA_ARG lambda_arg;
PT_NODE *tree;
PT_NODE *name;
lambda_arg.name_list = name_node_list;
lambda_arg.tree_list = corresponding_tree_list;
lambda_arg.spec_frames = NULL;
for (name = lambda_arg.name_list, tree = lambda_arg.tree_list; name && tree; name = name->next, tree = tree->next)
{
if (tree->node_type == PT_EXPR)
{
/* make sure it will print with proper precedance. we don't want to replace "name" with "1+2" in 4*name, and
* get 4*1+2. It should be 4*(1+2) instead. */
tree->info.expr.paren_type = 1;
}
if (name->node_type != PT_NAME)
{ /* unkonwn error */
tree = tree_with_names;
goto exit_on_error;
}
}
tree = parser_walk_tree (parser, tree_with_names, mq_lambda_node_pre, &lambda_arg, mq_lambda_node, &lambda_arg);
exit_on_error:
return tree;
}
/*
* mq_set_virt_object() - checks and sets name nodes of object type
* virtual object information
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_set_virt_object (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) void_arg;
PT_NODE *dt;
PT_NODE *cls;
if (node->node_type == PT_NAME && node->info.name.spec_id == spec->info.spec.id && (dt = node->data_type)
&& node->type_enum == PT_TYPE_OBJECT && (cls = dt->info.data_type.entity)
/* To distinguish between "V" and "class V" (V is a view) */
&& cls->info.name.meta_class != PT_META_CLASS)
{
if (db_is_vclass (cls->info.name.db_object) > 0)
{
dt->info.data_type.virt_object = cls->info.name.db_object;
if (mq_is_updatable (cls->info.name.db_object))
{
PARSER_CONTEXT *query_cache;
PT_NODE *flat;
flat = mq_fetch_one_real_class_get_cache (cls->info.name.db_object, &query_cache);
if (flat)
{
dt->info.data_type.entity = parser_copy_tree_list (parser, flat);
}
}
else
{
dt->info.data_type.entity = NULL;
}
parser_free_tree (parser, cls);
}
}
return node;
}
/*
* mq_fix_derived() - fixes derived table and checks for virtual object types
* return:
* parser(in):
* select_statement(in):
* spec(in):
*/
static PT_NODE *
mq_fix_derived (PARSER_CONTEXT * parser, PT_NODE * select_statement, PT_NODE * spec)
{
PT_NODE *attr = spec->info.spec.as_attr_list;
PT_NODE *attr_next;
PT_NODE *dt;
PT_NODE *cls;
int had_virtual, any_had_virtual;
any_had_virtual = 0;
while (attr)
{
dt = attr->data_type;
had_virtual = 0;
if (dt && attr->type_enum == PT_TYPE_OBJECT)
{
cls = dt->info.data_type.entity;
while (cls)
{
/* To distinguish between "V" and "class V" (V is a view) */
if (cls->info.name.meta_class != PT_META_CLASS && db_is_vclass (cls->info.name.db_object) > 0)
{
dt->info.data_type.virt_object = cls->info.name.db_object;
had_virtual = 1;
}
cls = cls->next;
}
}
attr_next = attr->next;
if (had_virtual)
{
any_had_virtual = 1;
}
attr = attr_next;
}
mq_reset_ids (parser, select_statement, spec);
if (any_had_virtual)
{
select_statement = parser_walk_tree (parser, select_statement, mq_set_virt_object, spec, NULL, NULL);
select_statement = mq_translate_paths (parser, select_statement, spec);
select_statement = mq_reset_paths (parser, select_statement, spec);
}
return select_statement;
}
/*
* mq_fix_derived_in_union() - fixes the derived tables in queries
* return:
* parser(in):
* statement(in):
* spec_id(in):
*
* Note:
* It performs two functions
* 1) In a given select, the outer level derived table spec
* is not in general the SAME spec being manipulated here.
* This spec is a copy of the outer spec, with the same id.
* Thus, we use the spec_id to find the derived table of interest
* to 'fix up'.
* 2) Since the statement may have been translated to a union,
* there may be multiple derived tables to fix up. This
* recurses for unions to do so.
*/
PT_NODE *
mq_fix_derived_in_union (PARSER_CONTEXT * parser, PT_NODE * statement, UINTPTR spec_id)
{
PT_NODE *spec;
if (statement == NULL)
{
return NULL;
}
switch (statement->node_type)
{
case PT_SELECT:
spec = statement->info.query.q.select.from;
while (spec && spec->info.spec.id != spec_id)
{
spec = spec->next;
}
if (spec)
{
statement = mq_fix_derived (parser, statement, spec);
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
}
break;
case PT_DELETE:
spec = statement->info.delete_.spec;
while (spec && spec->info.spec.id != spec_id)
{
spec = spec->next;
}
if (spec)
{
statement = mq_fix_derived (parser, statement, spec);
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
}
break;
case PT_UPDATE:
spec = statement->info.update.spec;
while (spec && spec->info.spec.id != spec_id)
{
spec = spec->next;
}
if (spec)
{
statement = mq_fix_derived (parser, statement, spec);
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
}
break;
case PT_MERGE:
{
spec = statement->info.merge.into;
spec->next = statement->info.merge.using_clause;
while (spec && spec->info.spec.id != spec_id)
{
spec = spec->next;
}
if (spec)
{
statement = mq_fix_derived (parser, statement, spec);
}
else
{
PT_INTERNAL_ERROR (parser, "translate");
}
if (statement)
{
statement->info.merge.into->next = NULL;
}
}
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
statement->info.query.q.union_.arg1 =
mq_fix_derived_in_union (parser, statement->info.query.q.union_.arg1, spec_id);
statement->info.query.q.union_.arg2 =
mq_fix_derived_in_union (parser, statement->info.query.q.union_.arg2, spec_id);
break;
default:
PT_INTERNAL_ERROR (parser, "translate");
break;
}
return statement;
}
/*
* mq_translate_value() - translate a virtual object to the real object
* return:
* parser(in):
* value(in):
*/
static PT_NODE *
mq_translate_value (PARSER_CONTEXT * parser, PT_NODE * value)
{
PT_NODE *data_type, *class_;
DB_OBJECT *real_object, *real_class;
DB_VALUE *db_value;
if (value->node_type == PT_VALUE && value->type_enum == PT_TYPE_OBJECT && (data_type = value->data_type)
&& (class_ = data_type->info.data_type.entity) && class_->node_type == PT_NAME
&& db_is_vclass (class_->info.name.db_object) > 0)
{
data_type->info.data_type.virt_object = class_->info.name.db_object;
real_object = db_real_instance (value->info.value.data_value.op);
if (real_object)
{
real_class = db_get_class (real_object);
class_->info.name.db_object = db_get_class (real_object);
class_->info.name.original = db_get_class_name (class_->info.name.db_object);
value->info.value.data_value.op = real_object;
db_value = pt_value_to_db (parser, value);
if (db_value)
{
db_make_object (db_value, value->info.value.data_value.op);
}
}
}
return value;
}
/*
* mq_push_dot_in_query() - Generate a new dot expression from the i'th column
* and the name passed in for every select list
* return:
* parser(in):
* query(in):
* i(in):
* name(in):
*/
static void
mq_push_dot_in_query (PARSER_CONTEXT * parser, PT_NODE * query, int i, PT_NODE * name)
{
PT_NODE *col;
PT_NODE *new_col;
PT_NODE *root;
PT_NODE *new_spec;
if (query)
{
switch (query->node_type)
{
case PT_SELECT:
col = query->info.query.q.select.list;
while (i > 0 && col)
{
col = col->next;
i--;
}
if (col && col->node_type == PT_NAME && PT_IS_OID_NAME (col))
{
root = pt_find_entity (parser, query->info.query.q.select.from, col->info.name.spec_id);
new_col = parser_copy_tree (parser, name);
if (new_col == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return;
}
new_col->info.name.spec_id = col->info.name.spec_id;
new_col->info.name.resolved = col->info.name.resolved;
root = pt_find_entity (parser, query->info.query.q.select.from, col->info.name.spec_id);
}
else
{
new_col = parser_new_node (parser, PT_DOT_);
if (new_col == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return;
}
new_col->info.dot.arg1 = parser_copy_tree (parser, col);
if (new_col->info.dot.arg1 == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return;
}
new_col->info.dot.arg2 = parser_copy_tree (parser, name);
if (new_col->info.dot.arg2 == NULL)
{
PT_INTERNAL_ERROR (parser, "parser_copy_tree");
return;
}
new_col->info.dot.arg2->info.name.spec_id = 0;
new_col->info.dot.arg2->info.name.resolved = NULL;
new_col->type_enum = name->type_enum;
new_col->data_type = parser_copy_tree_list (parser, name->data_type);
root = NULL;
if (col == NULL)
{
return;
}
if (col->node_type == PT_NAME)
{
root = pt_find_entity (parser, query->info.query.q.select.from, col->info.name.spec_id);
}
else if (col->node_type == PT_DOT_)
{
root =
pt_find_entity (parser, query->info.query.q.select.from, col->info.dot.arg2->info.name.spec_id);
}
if (root)
{
new_spec = pt_insert_entity (parser, new_col, root, NULL);
if (new_spec)
{
new_col->info.dot.arg2->info.name.spec_id = new_spec->info.spec.id;
}
else
{
/* error is set by pt_insert_entity */
}
}
}
parser_append_node (new_col, col);
break;
case PT_UNION:
case PT_DIFFERENCE:
case PT_INTERSECTION:
mq_push_dot_in_query (parser, query->info.query.q.union_.arg1, i, name);
mq_push_dot_in_query (parser, query->info.query.q.union_.arg2, i, name);
break;
default:
/* should not get here, that is an error! */
/* its almost certainly recoverable, so ignore it */
assert (0);
break;
}
}
}
/*
* mq_clean_dot() -
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in):
*/
static PT_NODE *
mq_clean_dot (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *spec = (PT_NODE *) void_arg;
PT_NODE *temp;
PT_NODE *next;
if (node == NULL)
{
return node;
}
switch (node->node_type)
{
case PT_DOT_:
if (node->info.dot.arg2->info.name.spec_id == spec->info.spec.id)
{
next = node->next;
temp = node->info.dot.arg2;
node->info.dot.arg2 = NULL;
node->next = NULL;
parser_free_tree (parser, node);
node = temp;
node->next = next;
}
break;
default:
break;
}
return node;
}
/*
* mq_push_path() -
* return:
* parser(in):
* statement(in): a select statement needing fixing
* spec(in): the spec of the derived query
* path(in): the path to push inside the spec
*/
PT_NODE *
mq_push_path (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * spec, PT_NODE * path)
{
PT_NODE *cols = spec->info.spec.as_attr_list;
PT_NODE *new_col;
PT_NODE *sub_paths;
PT_NODE *refs, *free_refs;
PT_NODE *join = path->info.spec.path_conjuncts;
int i = pt_find_attribute (parser, join->info.expr.arg1, cols);
refs = mq_get_references (parser, statement, path);
free_refs = refs;
path->info.spec.referenced_attrs = NULL;
if (i >= 0)
{
while (refs)
{
if (!PT_IS_OID_NAME (refs))
{
/* for each referenced attribute, 1) Make a new derived table symbol on referenced and as_attr_lists. 2)
* Create a new path node on each select list made from the referenced name and the column corresponding
* to the join arg1. 3) replace the names in statement corresponding to references with generated name. */
new_col = mq_generate_unique (parser, cols);
if (new_col != NULL)
{
parser_free_tree (parser, new_col->data_type);
new_col->data_type = parser_copy_tree_list (parser, refs->data_type);
new_col->type_enum = refs->type_enum;
parser_append_node (new_col, cols);
mq_push_dot_in_query (parser, spec->info.spec.derived_table, i, refs);
/* not mq_lambda ... */
statement = pt_lambda (parser, statement, refs, new_col);
path = pt_lambda (parser, path, refs, new_col);
}
}
refs = refs->next;
}
}
parser_free_tree (parser, free_refs);
sub_paths = path->info.spec.path_entities;
for (; sub_paths != NULL; sub_paths = sub_paths->next)
{
statement = mq_push_path (parser, statement, spec, sub_paths);
}
statement = parser_walk_tree (parser, statement, mq_clean_dot, spec, NULL, NULL);
return statement;
}
/*
* mq_derived_path() -
* return: derived path spec
* parser(in):
* statement(in): a select statement needing fixing
* path(in): the path to rewrite
*/
PT_NODE *
mq_derived_path (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * path)
{
PT_NODE *join;
PT_NODE *new_spec;
PT_NODE *query;
PT_NODE *temp;
PT_NODE *sub_paths;
PT_NODE *new_sub_path;
new_spec = parser_new_node (parser, PT_SPEC);
if (new_spec == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
query = parser_new_node (parser, PT_SELECT);
if (query == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
path->info.spec.range_var->info.name.resolved = NULL;
if (path->info.spec.entity_name)
{
path->info.spec.entity_name->info.name.resolved = NULL;
}
sub_paths = path->info.spec.path_entities;
path->info.spec.path_entities = NULL;
join = path->info.spec.path_conjuncts;
path->info.spec.path_conjuncts = NULL;
/* move path join term */
new_spec->info.spec.path_conjuncts = join;
new_spec->info.spec.path_entities = sub_paths;
new_spec->info.spec.derived_table_type = PT_IS_SUBQUERY;
new_spec->info.spec.id = path->info.spec.id;
new_spec->info.spec.range_var = parser_copy_tree (parser, path->info.spec.range_var);
statement = mq_reset_ids_and_references (parser, statement, new_spec);
new_spec->info.spec.id = (UINTPTR) new_spec;
new_spec->info.spec.as_attr_list = new_spec->info.spec.referenced_attrs;
new_spec->info.spec.referenced_attrs = NULL;
query->info.query.q.select.from = path;
query->info.query.is_subquery = PT_IS_SUBQUERY;
temp = query->info.query.q.select.list = parser_copy_tree_list (parser, new_spec->info.spec.as_attr_list);
for (; temp != NULL; temp = temp->next)
{
temp->info.name.spec_id = path->info.spec.id;
}
new_spec = parser_walk_tree (parser, new_spec, mq_set_virt_object, new_spec, NULL, NULL);
statement = parser_walk_tree (parser, statement, mq_set_virt_object, new_spec, NULL, NULL);
new_spec->info.spec.derived_table = query;
for (new_spec->info.spec.path_entities = NULL; sub_paths; sub_paths = temp)
{
temp = sub_paths->next;
sub_paths->next = NULL;
new_sub_path = mq_derived_path (parser, statement, sub_paths);
new_spec->info.spec.path_entities = parser_append_node (new_sub_path, new_spec->info.spec.path_entities);
}
return new_spec;
}
/*
* mq_fetch_subqueries_for_update_local() - ask the schema manager for the
* cached parser containing the compiled subqueries of the class_.
* If that is not already cached, the schema manager will call back to
* compute the subqueries
* return:
* parser(in):
* class(in):
* fetch_as(in):
* what_for(in):
* qry_cache(out):
*/
static PT_NODE *
mq_fetch_subqueries_for_update_local (PARSER_CONTEXT * parser, PT_NODE * class_, PT_FETCH_AS fetch_as, DB_AUTH what_for,
PARSER_CONTEXT ** qry_cache)
{
PARSER_CONTEXT *query_cache;
DB_OBJECT *class_object;
if (!class_ || !(class_object = class_->info.name.db_object) || !qry_cache || db_is_class (class_object))
{
return NULL;
}
*qry_cache = query_cache = sm_virtual_queries (parser, class_object);
if (query_cache && query_cache->view_cache)
{
if (!(query_cache->view_cache->authorization & what_for))
{
PT_ERRORmf2 (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON,
get_authorization_name (what_for), db_get_class_name (class_->info.name.db_object));
return NULL;
}
if (parser != NULL && query_cache->error_msgs != NULL)
{
mq_copy_view_error_msgs (parser, query_cache);
}
if (!query_cache->view_cache->vquery_for_update
&& (!query_cache->view_cache->vquery_for_partial_update || (fetch_as != PT_PARTIAL_SELECT)) && parser)
{
if (query_cache->view_cache->has_reuse_oid_table)
{
PT_ERRORmf (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_REUSE_OID_TABLE_NOT_UPDATABLE,
/* use function to get name. class_->info.name.original is not always set. */
db_get_class_name (class_object));
}
else
{
PT_ERRORmf (parser, class_, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VCLASS_NOT_UPDATABLE,
/* use function to get name. class_->info.name.original is not always set. */
db_get_class_name (class_object));
}
}
if (fetch_as == PT_INVERTED_ASSIGNMENTS)
{
return query_cache->view_cache->inverted_vquery_for_update_in_gdb;
}
if (fetch_as == PT_NORMAL_SELECT)
{
return query_cache->view_cache->vquery_for_update_in_gdb;
}
if (fetch_as == PT_PARTIAL_SELECT)
{
if (query_cache->view_cache->vquery_for_update_in_gdb != NULL)
{
return query_cache->view_cache->vquery_for_update_in_gdb;
}
else
{
return query_cache->view_cache->vquery_for_partial_update;
}
}
}
return NULL;
}
/*
* mq_fetch_subqueries_for_update() - just like ..._for_update_local except
* it does not have an output argument for qry_cache
* return:
* parser(in):
* class(in):
* fetch_as(in):
* what_for(in):
*/
PT_NODE *
mq_fetch_subqueries_for_update (PARSER_CONTEXT * parser, PT_NODE * class_, PT_FETCH_AS fetch_as, DB_AUTH what_for)
{
PARSER_CONTEXT *query_cache;
return mq_fetch_subqueries_for_update_local (parser, class_, fetch_as, what_for, &query_cache);
}
/*
* mq_fetch_select_for_real_class_update() - fetch the select statement that
* maps the vclass to the real class
* return:
* parser(in):
* vclass(in):
* real_class(in):
* fetch_as(in):
* what_for(in):
*/
static PT_NODE *
mq_fetch_select_for_real_class_update (PARSER_CONTEXT * parser, PT_NODE * vclass, PT_NODE * real_class,
PT_FETCH_AS fetch_as, DB_AUTH what_for)
{
PT_NODE *select_statements = mq_fetch_subqueries_for_update (parser, vclass, fetch_as, what_for);
PT_NODE *flat;
DB_OBJECT *class_object = NULL;
int error = NO_ERROR;
if (!select_statements)
{
return NULL;
}
if (real_class)
{
class_object = real_class->info.name.db_object;
}
while (select_statements)
{
if (select_statements->info.query.q.select.from)
{
for (flat = select_statements->info.query.q.select.from->info.spec.flat_entity_list; flat; flat = flat->next)
{
if (class_object == flat->info.name.db_object)
{
return select_statements;
}
}
/* class_object might be either a superclass of one of the classes in flat_entity_list for queries selecting
* from a class hierarchy: SELECT * FROM ALL t or a partition of one of the classes in the list: SELECT *
* FROM t if t is a partitioned class. */
for (flat = select_statements->info.query.q.select.from->info.spec.flat_entity_list; flat; flat = flat->next)
{
if (db_is_superclass (class_object, flat->info.name.db_object))
{
return select_statements;
}
else
{
error = db_is_partition (class_object, flat->info.name.db_object);
if (error < 0)
{
PT_ERROR (parser, vclass, er_msg ());
return NULL;
}
else if (error > 0)
{
return select_statements;
}
}
}
}
select_statements = select_statements->next;
}
return NULL;
}
/*
* mq_fetch_expression_for_real_class_update() - fetch the expression statement
* that maps the vclass attribute to the real class
* return:
* parser(in):
* vclass_obj(in):
* attr(in):
* real_class(in):
* fetch_as(in):
* what_for(in):
* spec_id(out): entity spec id of the specification owning the expression
*/
static PT_NODE *
mq_fetch_expression_for_real_class_update (PARSER_CONTEXT * parser, DB_OBJECT * vclass_obj, PT_NODE * attr,
PT_NODE * real_class, PT_FETCH_AS fetch_as, DB_AUTH what_for,
UINTPTR * spec_id)
{
PT_NODE vclass;
PT_NODE *select_statement;
PT_NODE *attr_list;
PT_NODE *select_list;
PT_NODE *spec;
const char *attr_name;
parser_init_node (&vclass, PT_NAME);
vclass.info.name.original = NULL;
vclass.info.name.db_object = vclass_obj;
attr_list = mq_fetch_attributes (parser, &vclass);
select_statement = mq_fetch_select_for_real_class_update (parser, &vclass, real_class, fetch_as, what_for);
if (!select_statement)
{
if (!pt_has_error (parser))
{
const char *real_class_name = "<unknown>";
if (real_class && real_class->info.name.original)
{
real_class_name = real_class->info.name.original;
}
PT_ERRORmf2 (parser, attr, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_VC_COMP_NOT_UPDATABL,
db_get_class_name (vclass_obj), real_class_name);
}
return NULL;
}
if (spec_id)
{
*spec_id = 0;
}
if (!attr || !attr_list || !(select_list = select_statement->info.query.q.select.list)
|| !(attr_name = attr->info.name.original))
{
PT_INTERNAL_ERROR (parser, "translate");
return NULL;
}
for (; attr_list && select_list; attr_list = attr_list->next, select_list = select_list->next)
{
if (intl_identifier_casecmp (attr_name, attr_list->info.name.original) == 0)
{
if (spec_id && (spec = select_statement->info.query.q.select.from))
{
*spec_id = spec->info.spec.id;
}
return select_list;
}
}
if (!pt_has_error (parser))
{
PT_ERRORmf2 (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_HAVE,
db_get_class_name (vclass_obj), attr_name);
}
return NULL;
}
/*
* mq_fetch_attributes() - fetch class's subqueries
* return: PT_NODE list of its attribute names, including oid attr
* parser(in):
* class(in):
*/
PT_NODE *
mq_fetch_attributes (PARSER_CONTEXT * parser, PT_NODE * class_)
{
PARSER_CONTEXT *query_cache;
DB_OBJECT *class_object;
if (!class_ || !(class_object = class_->info.name.db_object) || db_is_class (class_object))
{
return NULL;
}
query_cache = sm_virtual_queries (parser, class_object);
if (query_cache)
{
if (parser != NULL && query_cache->error_msgs != NULL)
{
mq_copy_view_error_msgs (parser, query_cache);
}
if (query_cache->view_cache)
{
return query_cache->view_cache->attrs;
}
}
return NULL;
}
/*
* NAME: mq_set_names_dbobject
*
* This private routine re-sets PT_NAME node resolution to match
* a new printable name (usually, used to resolve ambiguity)
*
* returns: PT_NODE
*
* side effects: none
*
*/
/*
* mq_set_names_dbobject() - re-sets PT_NAME node resolution to match a new
* printable name
* return:
* parser(in):
* node(in):
* void_arg(in):
* continue_walk(in/out):
*/
static PT_NODE *
mq_set_names_dbobject (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
SET_NAMES_INFO *info = (SET_NAMES_INFO *) void_arg;
if (node->node_type == PT_NAME && node->info.name.meta_class != PT_PARAMETER && node->info.name.spec_id == info->id)
{
node->info.name.db_object = info->object;
/* don't walk entity_name_list/flat_entity_spec do walk list especially for method args list for example: set a =
* func(x, y, z) <-- walk into y, z */
*continue_walk = PT_LIST_WALK;
}
if (node->node_type == PT_DATA_TYPE || node->node_type == PT_SPEC)
{
*continue_walk = PT_STOP_WALK;
}
return node;
}
/*
* mq_is_updatable_local() - checks if vclass is updatable
* return: 0 if not, 1 if so
* class_object(in):
* fetch_as(in): fetch mode
*/
static bool
mq_is_updatable_local (DB_OBJECT * class_object, PT_FETCH_AS fetch_as)
{
PT_NODE class_;
PT_NODE *subquery;
PARSER_CONTEXT *parser;
parser = parser_create_parser ();
if (parser == NULL)
{
return false;
}
parser_init_node (&class_, PT_NAME);
class_.info.name.original = NULL;
class_.info.name.db_object = class_object;
subquery = mq_fetch_subqueries_for_update (parser, &class_, fetch_as, DB_AUTH_SELECT);
/* clean up memory */
parser_free_parser (parser);
return (subquery != NULL);
}
/*
* mq_is_updatable() - checks if vclass is at least partially updatable
* return: 0 if not, 1 if so
* class_object(in):
*/
bool
mq_is_updatable (DB_OBJECT * class_object)
{
return mq_is_updatable_local (class_object, PT_PARTIAL_SELECT);
}
/*
* mq_is_updatable_strict() - checks if vclass is strictly updatable
* return: 0 if not, 1 if so
* class_object(in):
*/
bool
mq_is_updatable_strict (DB_OBJECT * class_object)
{
return mq_is_updatable_local (class_object, PT_NORMAL_SELECT);
}
/*
* mq_is_updatable_att() -
* return: true if vmop's att_nam is updatable
* parser(in): the parser context
* vmop(in): vclass object
* att_nam(in): one of vmop's attribute names
* rmop(in): real (base) class object
*/
bool
mq_is_updatable_att (PARSER_CONTEXT * parser, DB_OBJECT * vmop, const char *att_nam, DB_OBJECT * rmop)
{
PT_NODE real, attr, *expr;
parser_init_node (&attr, PT_NAME);
attr.info.name.original = att_nam;
parser_init_node (&real, PT_NAME);
real.info.name.original = NULL;
real.info.name.db_object = rmop;
expr =
mq_fetch_expression_for_real_class_update (parser, vmop, &attr, &real, PT_INVERTED_ASSIGNMENTS, DB_AUTH_SELECT,
NULL);
if (!expr)
{
return false;
}
return expr->info.expr.arg1 && expr->info.expr.arg2;
}
/*
* mq_is_updatable_attribute() -
* return: false if not, true if so
* vclass_object(in):
* attr_name(in):
* real_class_object(in):
*/
bool
mq_is_updatable_attribute (DB_OBJECT * vclass_object, const char *attr_name, DB_OBJECT * real_class_object)
{
PARSER_CONTEXT *parser;
bool rc;
parser = parser_create_parser ();
if (parser == NULL)
{
return false;
}
rc = mq_is_updatable_att (parser, vclass_object, attr_name, real_class_object);
parser_free_parser (parser);
return rc;
}
/*
* mq_evaluate_expression() -
* return: the evaluated expression value, or error
* parser(in):
* expr(in):
* value(in):
* object(in): an object to db_get all names from
* spec_id(in):
*/
int
mq_evaluate_expression (PARSER_CONTEXT * parser, PT_NODE * expr, DB_VALUE * value, DB_OBJECT * object, UINTPTR spec_id)
{
int error = NO_ERROR;
SET_NAMES_INFO info;
info.object = object;
info.id = spec_id;
if (expr)
{
parser_walk_tree (parser, expr, mq_set_names_dbobject, &info, pt_continue_walk, NULL);
pt_evaluate_tree (parser, expr, value, 1);
if (pt_has_error (parser))
{
error = PT_SEMANTIC;
pt_report_to_ersys (parser, (PT_ERROR_TYPE) error);
}
}
else
{
PT_NODE dummy;
dummy.line_number = 0;
dummy.column_number = 0;
PT_ERRORm (parser, &dummy, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NO_EXPR_TO_EVALUATE);
}
if (pt_has_error (parser))
{
error = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
}
return error;
}
/*
* mq_evaluate_expression_having_serial() -
* return:
* parser(in):
* expr(in):
* value(in):
* vals_cnt(in): number of values to return in 'value' parameter
* object(in):
* spec_id(in):
*/
int
mq_evaluate_expression_having_serial (PARSER_CONTEXT * parser, PT_NODE * expr, DB_VALUE * values, int values_count,
DB_OBJECT * object, UINTPTR spec_id)
{
int error = NO_ERROR;
SET_NAMES_INFO info;
info.object = object;
info.id = spec_id;
if (expr)
{
parser_walk_tree (parser, expr, mq_set_names_dbobject, &info, pt_continue_walk, NULL);
pt_evaluate_tree_having_serial (parser, expr, values, values_count);
if (pt_has_error (parser))
{
error = PT_SEMANTIC;
pt_report_to_ersys (parser, (PT_ERROR_TYPE) error);
}
}
else
{
PT_NODE dummy;
dummy.line_number = 0;
dummy.column_number = 0;
PT_ERRORm (parser, &dummy, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NO_EXPR_TO_EVALUATE);
}
if (pt_has_error (parser))
{
error = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
}
return error;
}
/*
* mq_get_attribute() -
* return: NO_ERROR on success, non-zero for ERROR
* vclass_object(in): the "mop" of the virtual instance's vclass
* attr_name(in): the attribute of the virtual instance to updat
* real_class_object(in): the "mop" of the virtual instance's real class
* virtual_value(out): the value gotten from the virtual instance
* real_instance(out): contains real instance of virtual instance
*/
int
mq_get_attribute (DB_OBJECT * vclass_object, const char *attr_name, DB_OBJECT * real_class_object,
DB_VALUE * virtual_value, DB_OBJECT * real_instance)
{
PT_NODE real;
PT_NODE attr;
PARSER_CONTEXT *parser = NULL;
PT_NODE *expr;
int error = NO_ERROR;
UINTPTR spec_id;
int save;
AU_DISABLE (save);
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
AU_ENABLE (save);
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
parser->au_save = save;
parser_init_node (&attr, PT_NAME);
attr.info.name.original = attr_name;
parser_init_node (&real, PT_NAME);
real.info.name.original = NULL;
real.info.name.db_object = real_class_object;
expr =
mq_fetch_expression_for_real_class_update (parser, vclass_object, &attr, &real, PT_NORMAL_SELECT, DB_AUTH_SELECT,
&spec_id);
if (pt_has_error (parser))
{
error = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
}
else
{
error = mq_evaluate_expression (parser, expr, virtual_value, real_instance, spec_id);
}
parser_free_parser (parser);
AU_ENABLE (save);
return error;
}
/*
* mq_oid() -
* return:
* parser(in): the usual context
* spec(in):
*/
PT_NODE *
mq_oid (PARSER_CONTEXT * parser, PT_NODE * spec)
{
PT_NODE *real;
PT_NODE attr;
PT_NODE *expr;
PT_NODE *error_msgs = parser->error_msgs;
int save;
DB_OBJECT *virt_class;
/* DO NOT RETURN FROM WITHIN THE BODY OF THIS PROCEDURE */
AU_DISABLE (save);
parser->au_save = save;
parser_init_node (&attr, PT_NAME);
attr.info.name.original = ""; /* oid's have null string attr name */
real = spec->info.spec.flat_entity_list;
virt_class = real->info.name.virt_object;
parser->error_msgs = NULL;
expr =
mq_fetch_expression_for_real_class_update (parser, virt_class, &attr, real, PT_NORMAL_SELECT, DB_AUTH_ALL, NULL);
/* in case it was NOT updatable just return NULL, no error */
parser_free_tree (parser, parser->error_msgs);
parser->error_msgs = error_msgs;
expr = parser_copy_tree (parser, expr);
expr = parser_walk_tree (parser, expr, mq_set_all_ids, spec, NULL, NULL);
AU_ENABLE (save);
return expr;
}
/*
* mq_update_attribute -
* return: NO_ERROR on success, non-zero for ERROR
* vclass_object(in): the "mop" of the virtual instance's vclass
* attr_name(in): the attribute of the virtual instance to update
* real_class_object(in): the "mop" of the virtual instance's real class
* virtual_value(in): the value to put in the virtual instance
* real_value(out): contains value to set it to
* real_name(out): contains name of real instance attribute to set
* db_auth(in):
*/
int
mq_update_attribute (DB_OBJECT * vclass_object, const char *attr_name, DB_OBJECT * real_class_object,
DB_VALUE * virtual_value, DB_VALUE * real_value, const char **real_name, int db_auth)
{
PT_NODE real;
PT_NODE attr;
/* static */ PARSER_CONTEXT *parser = NULL;
PT_NODE *value_holder;
PT_NODE *expr;
PT_NODE *value;
int error = NO_ERROR;
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
parser_init_node (&attr, PT_NAME);
attr.info.name.original = attr_name;
parser_init_node (&real, PT_NAME);
real.info.name.original = NULL;
real.info.name.db_object = real_class_object;
expr =
mq_fetch_expression_for_real_class_update (parser, vclass_object, &attr, &real, PT_INVERTED_ASSIGNMENTS,
(DB_AUTH) db_auth, NULL);
if (!expr /* SM_NOT_UPDATBLE_ATTRIBUTE */
|| !expr->info.expr.arg1 || !expr->info.expr.arg2 || !expr->etc)
{
error = ER_GENERIC_ERROR;
}
if (error == NO_ERROR && virtual_value != NULL)
{
(*real_name) = expr->info.expr.arg1->info.name.original;
value_holder = (PT_NODE *) expr->etc;
value = pt_dbval_to_value (parser, virtual_value);
if (value)
{
value_holder->info.value.data_value = value->info.value.data_value;
value_holder->info.value.db_value = *virtual_value;
value_holder->info.value.db_value_is_initialized = true;
pt_evaluate_tree (parser, expr->info.expr.arg2, real_value, 1);
parser_free_tree (parser, value);
db_make_null (&value_holder->info.value.db_value);
value_holder->info.value.db_value_is_initialized = false;
/*
* This is a bit of a kludge since there is no way to clean up
* the data_value portion of the info structure. The value_holder
* node now points into the parse tree, but has been allocated by
* a different parser (mq_fetch_expression_for_real_class_update).
* We need to set this pointer to NULL so we won't try to free
* it when cleaning up the parse tree. Setting the "set" pointer
* should be safe for the union.
*/
value_holder->info.value.data_value.set = NULL;
}
}
else if (!pt_has_error (parser))
{
PT_INTERNAL_ERROR (parser, "translate");
}
if (pt_has_error (parser))
{
error = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
}
/* clean up memory */
parser_free_parser (parser);
return error;
}
/*
* mq_fetch_one_real_class_get_cache() -
* return: a convienient real class DB_OBJECT* of an updatable virtual class
* NULL for non-updatable
* vclass_object(in): the "mop" of the virtual class
* query_cache(out): parser holding cached parse trees
*/
static PT_NODE *
mq_fetch_one_real_class_get_cache (DB_OBJECT * vclass_object, PARSER_CONTEXT ** query_cache)
{
PARSER_CONTEXT *parser = NULL;
PT_NODE vclass;
PT_NODE *subquery, *flat = NULL;
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
return NULL;
}
}
parser_init_node (&vclass, PT_NAME);
vclass.info.name.original = NULL;
vclass.info.name.db_object = vclass_object;
subquery = mq_fetch_subqueries_for_update_local (parser, &vclass, PT_NORMAL_SELECT, DB_AUTH_SELECT, query_cache);
if (subquery && subquery->info.query.q.select.from)
{
flat = subquery->info.query.q.select.from->info.spec.flat_entity_list;
}
if (flat == NULL && !pt_has_error (parser))
{
PT_NODE dummy;
dummy.line_number = 0;
dummy.column_number = 0;
PT_ERRORmf (parser, &dummy, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NO_REALCLASS_4_VCLAS,
db_get_class_name (vclass_object));
}
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
}
/* clean up memory */
parser_free_parser (parser);
return flat;
}
/*
* mq_fetch_one_real_class() -
* return: a convienient real class DB_OBJECT* of an updatable virtual class
* NULL for non-updatable
* vclass_object(in): the "mop" of the virtual class
*/
DB_OBJECT *
mq_fetch_one_real_class (DB_OBJECT * vclass_object)
{
PARSER_CONTEXT *query_cache;
PT_NODE *flat;
flat = mq_fetch_one_real_class_get_cache (vclass_object, &query_cache);
if (flat)
{
return flat->info.name.db_object;
}
return NULL;
}
/*
* mq_get_expression() -
* return: NO_ERROR on success, non-zero for ERROR
* object(in): an object to db_get all names from
* expr(in): expression tree
* value(in/out): the evaluated expression value
*/
int
mq_get_expression (DB_OBJECT * object, const char *expr, DB_VALUE * value)
{
PARSER_CONTEXT *parser = NULL;
int error = NO_ERROR;
PT_NODE **statements;
PT_NODE *statement = NULL;
char *buffer;
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
buffer = pt_append_string (parser, NULL, "select ");
buffer = pt_append_string (parser, buffer, expr);
buffer = pt_append_string (parser, buffer, " from ");
buffer = pt_append_string (parser, buffer, db_get_class_name (object));
statements = parser_parse_string_with_escapes (parser, buffer, false);
if (statements)
{
/* exclude from auditing statement */
statement = statements[0];
statement = pt_compile (parser, statement);
}
if (statement && !pt_has_error (parser))
{
error =
mq_evaluate_expression (parser, statement->info.query.q.select.list, value, object,
statement->info.query.q.select.from->info.spec.id);
}
else
{
error = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
}
/* clean up memory */
parser_free_parser (parser);
return error;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* mq_mget_exprs() - bulk db_get_expression of a list of attribute exprs
* for a given set of instances of a class
* return: number of rows evaluated if all OK, -1 otherwise
* objects(in): an array of object instances of a class
* rows(in): number of instances in objects array
* exprs(in): an array of attribute expressions
* cols(in): number of items in exprs array
* qOnErr(in): true if caller wants us to quit on first error
* values(out): destination db_values for attribute expressions
* results(out): array of result codes
* emsg(out): a diagnostic message if an error occurred
*/
static int
mq_mget_exprs (DB_OBJECT ** objects, int rows, char **exprs, int cols, int qOnErr, DB_VALUE * values, int *results,
char *emsg)
{
char *buffer;
DB_ATTDESC **attdesc;
int c, count, err = NO_ERROR, r;
DB_OBJECT *cls;
DB_VALUE *v;
UINTPTR specid;
int siz;
PT_NODE **stmts, *stmt = NULL, *xpr;
PARSER_CONTEXT *parser;
/* make sure we have reasonable arguments */
if (!objects || !(*objects) || (cls = db_get_class (*objects)) == NULL || !exprs || !values || rows <= 0 || cols <= 0)
{
strcpy (emsg, "invalid argument(s) to mq_mget_exprs");
return -1; /* failure */
}
/* create a new parser context */
parser = parser_create_parser ();
if (parser == NULL)
{
return -1;
}
emsg[0] = 0;
/* compose a "select exprs from target_class" */
buffer = pt_append_string (parser, NULL, "select ");
buffer = pt_append_string (parser, buffer, exprs[0]);
for (c = 1; c < cols; c++)
{
buffer = pt_append_string (parser, buffer, ",");
buffer = pt_append_string (parser, buffer, exprs[c]);
}
buffer = pt_append_string (parser, buffer, " from ");
buffer = pt_append_string (parser, buffer, db_get_class_name (cls));
/* compile it */
stmts = parser_parse_string (parser, buffer);
if (stmts)
{
/* exclude from auditing statement */
stmt = stmts[0];
stmt = pt_compile (parser, stmt);
}
if (stmt == NULL || pt_has_error (parser))
{
err = ER_PT_SEMANTIC;
pt_report_to_ersys (parser, PT_SEMANTIC);
count = -1; /* failure */
for (r = 0; r < rows; r++)
{
results[r] = 0;
}
}
else
{
/* partition attribute expressions into names and expressions: simple names will be evaluated via db_dget (fast)
* and expressions will be evaluated via mq_evaluate_expression (slow). */
siz = cols * sizeof (DB_ATTDESC *);
attdesc = (DB_ATTDESC **) parser_alloc (parser, siz);
for (c = 0, xpr = stmt->info.query.q.select.list; c < cols && xpr && (err == NO_ERROR || !qOnErr);
c++, xpr = xpr->next)
{
/* get attribute descriptors for simple names */
if (xpr->node_type == PT_NAME)
{
err = db_get_attribute_descriptor (cls, xpr->info.name.original, 0, 0, &attdesc[c]);
}
}
if (!attdesc || err != NO_ERROR)
{
strcpy (emsg, "mq_mget_exprs fails in getting attribute descriptors");
count = -1; /* failure */
for (r = 0; r < rows; r++)
{
results[r] = 0;
}
}
else
{
/* evaluate attribute expressions and deposit results into values */
count = 0;
specid = stmt->info.query.q.select.from->info.spec.id;
for (r = 0, v = values; r < rows && (err == NO_ERROR || !qOnErr); r++, v = values + (r * cols))
{
for (c = 0, xpr = stmt->info.query.q.select.list; c < cols && xpr && (err == NO_ERROR || !qOnErr);
c++, v++, xpr = xpr->next)
{
/* evaluate using the faster db_dget for simple names and the slower mq_evaluate_expression for
* expressions. */
err =
xpr->node_type == PT_NAME ? db_dget (objects[r], attdesc[c], v) : mq_evaluate_expression (parser,
xpr, v,
objects
[r],
specid);
}
if (err != NO_ERROR)
{
results[r] = 0;
}
else
{
count++;
results[r] = 1;
}
}
}
}
/* deposit any error message into emsg */
if (err != NO_ERROR && !strlen (emsg))
{
strcpy (emsg, db_error_string (3));
}
/* clean up memory */
parser_free_parser (parser);
return count;
}
#endif /* ENABLE_UNUSED_FUNCTION */
/*
* mq_is_real_class_of_vclass() - determine if s_class is one of the real
* classes of the virtual class d_class
* return: 1 if s_class is a real class of the view d_class
* parser(in): the parser context
* s_class(in): a PT_NAME node representing a class_, vclass
* d_class(in): a PT_NAME node representing a view
*/
int
mq_is_real_class_of_vclass (PARSER_CONTEXT * parser, const PT_NODE * s_class, const PT_NODE * d_class)
{
PT_NODE *saved_msgs;
int result;
if (!parser)
{
return 0;
}
saved_msgs = parser->error_msgs;
parser->error_msgs = NULL;
result = (mq_fetch_select_for_real_class_update (parser, (PT_NODE *) d_class, (PT_NODE *) s_class, PT_NORMAL_SELECT,
DB_AUTH_SELECT) != NULL);
if (pt_has_error (parser))
{
parser_free_tree (parser, parser->error_msgs);
}
parser->error_msgs = saved_msgs;
return result;
}
/*
* mq_evaluate_check_option() -
* return: NO_ERROR on success, non-zero for ERROR
* parser(in):
* check_where(in):
* object(in): an object to db_get all names from
* view_class(in):
*/
int
mq_evaluate_check_option (PARSER_CONTEXT * parser, PT_NODE * check_where, DB_OBJECT * object, PT_NODE * view_class)
{
DB_VALUE bool_val;
int error;
db_make_null (&bool_val);
/* evaluate check option */
if (check_where != NULL)
{
for (; check_where != NULL; check_where = check_where->next)
{
error = mq_evaluate_expression (parser, check_where, &bool_val, object, view_class->info.name.spec_id);
if (error < 0)
{
return error;
}
if (db_value_is_null (&bool_val) || db_get_int (&bool_val) == 0)
{
PT_ERRORmf (parser, check_where, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_CHECK_OPTION_EXCEPT,
view_class->info.name.virt_object ? db_get_class_name (view_class->info.name.virt_object) : ""
/* an internal error */ );
/* Report check option error to sys error. */
pt_report_to_ersys (parser, PT_EXECUTION);
return er_errid ();
}
}
}
return NO_ERROR;
}
static const char *
get_authorization_name (DB_AUTH auth)
{
switch (auth)
{
case DB_AUTH_NONE:
return "";
case DB_AUTH_SELECT:
return "SELECT";
case DB_AUTH_INSERT:
return "INSERT";
case DB_AUTH_UPDATE:
return "UPDATE";
case DB_AUTH_DELETE:
return "DELETE";
case DB_AUTH_ALTER:
return "ALTER";
case DB_AUTH_INDEX:
return "INDEX";
case DB_AUTH_EXECUTE:
return "EXECUTE";
case DB_AUTH_REPLACE:
return "REPLACE";
case DB_AUTH_INSERT_UPDATE:
return "INSERT/UPDATE";
case DB_AUTH_UPDATE_DELETE:
return "UPDATE/DELETE";
case DB_AUTH_INSERT_UPDATE_DELETE:
return "INSERT/UPDATE/DELETE";
default:
return "";
}
}
/*
* mq_is_order_dependent_node - determine if node's evaluation result depends
* on tuple order
* return: true if result is dependent of order, false otherwise
* node(in): node to check
*
* NOTE: a node is said to be order dependent if at least one of it's descendant
* nodes is a session variable assignment (eg. @a := expr).
*/
static bool
mq_is_order_dependent_node (PT_NODE * node)
{
bool res = false;
if (node == NULL)
{
return false;
}
/* check expression node */
if (PT_IS_EXPR_NODE (node))
{
if (node->info.expr.op == PT_DEFINE_VARIABLE)
{
/* we found an assignment, no need to look further */
return true;
}
/* recurse arguments */
if (node->info.expr.arg1 != NULL)
{
res = mq_is_order_dependent_node (node->info.expr.arg1) || res;
}
if (node->info.expr.arg2 != NULL)
{
res = mq_is_order_dependent_node (node->info.expr.arg2) || res;
}
if (node->info.expr.arg3 != NULL)
{
res = mq_is_order_dependent_node (node->info.expr.arg3) || res;
}
}
/* check function node */
if (PT_IS_FUNCTION (node))
{
PT_NODE *arg = node->info.function.arg_list;
/* recurse arguments */
while (arg != NULL)
{
res = mq_is_order_dependent_node (arg) || res;
arg = arg->next;
}
}
return res;
}
/*
* mq_mark_order_dependent_nodes - in an order dependent node tree, mark nodes
* that are order dependent
* returns: true if node or one of it's children were marked as order
* dependent, false otherwise
*
* NOTE: this function is called recursively. it will mark session variable
* nodes and all their ancestors up to the root.
*/
static bool
mq_mark_order_dependent_nodes (PT_NODE * node)
{
bool res = false;
if (node == NULL)
{
return false;
}
/* check expression */
if (PT_IS_EXPR_NODE (node))
{
if (node->info.expr.op == PT_DEFINE_VARIABLE || node->info.expr.op == PT_EVALUATE_VARIABLE)
{
/* session variable found */
res = true;
}
/* recurse arguments */
if (node->info.expr.arg1 != NULL)
{
res = mq_mark_order_dependent_nodes (node->info.expr.arg1) || res;
}
if (node->info.expr.arg2 != NULL)
{
res = mq_mark_order_dependent_nodes (node->info.expr.arg2) || res;
}
if (node->info.expr.arg3 != NULL)
{
res = mq_mark_order_dependent_nodes (node->info.expr.arg3) || res;
}
}
/* check functions */
if (PT_IS_FUNCTION (node))
{
PT_NODE *arg = node->info.function.arg_list;
/* recurse arguments */
while (arg != NULL)
{
res = mq_mark_order_dependent_nodes (arg) || res;
arg = arg->next;
}
}
/* set flag */
PT_SET_ORDER_DEPENDENT_FLAG (node, res);
return res;
}
/*
* mq_rewrite_order_dependent_nodes - rewrite order dependent nodes in a tree
* returns: rewritten node or tree
* parser(in): parser context
* node(in): node to rewrite
* select(in): parent SELECT query
* unique(in/out): pointer to an int counter, used for unique name generation
*
* NOTE: this function will take all order independent subtrees and add them to
* the derived table select list, thus making sure the expressions are
* evaluated in the correct context.
*/
static PT_NODE *
mq_rewrite_order_dependent_nodes (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select, int *unique)
{
PT_NODE *attr = NULL, *as_attr = NULL;
PT_NODE *spec = NULL, *dt_query = NULL;
char *name = NULL;
PT_NODE *pt_cur = NULL;
if (node == NULL || select == NULL)
{
/* nothing to do */
return node;
}
if (select->node_type != PT_SELECT)
{
PT_INTERNAL_ERROR (parser, "invalid parent query");
return NULL;
}
/* retrieve derived table spec */
spec = select->info.query.q.select.from;
if (spec == NULL || spec->info.spec.range_var == NULL)
{
PT_INTERNAL_ERROR (parser, "invalid spec");
return NULL;
}
/* retrieve derived table query */
dt_query = spec->info.spec.derived_table;
if (dt_query == NULL || dt_query->node_type != PT_SELECT)
{
PT_INTERNAL_ERROR (parser, "invalid derived table");
return NULL;
}
if (PT_IS_ORDER_DEPENDENT (node))
{
/* node is order dependent */
if (PT_IS_EXPR_NODE (node))
{
/* walk expression arguments */
if (node->info.expr.arg1 != NULL)
{
node->info.expr.arg1 = mq_rewrite_order_dependent_nodes (parser, node->info.expr.arg1, select, unique);
}
if (node->info.expr.arg2 != NULL)
{
node->info.expr.arg2 = mq_rewrite_order_dependent_nodes (parser, node->info.expr.arg2, select, unique);
}
if (node->info.expr.arg3 != NULL)
{
node->info.expr.arg3 = mq_rewrite_order_dependent_nodes (parser, node->info.expr.arg3, select, unique);
}
}
else if (PT_IS_FUNCTION (node))
{
PT_NODE *list = node->info.function.arg_list;
PT_NODE *saved_next = NULL, *prev = NULL, *ret_node;
/* walk function arguments */
while (list != NULL)
{
/* unlink */
saved_next = list->next;
list->next = NULL;
/* rewrite tree */
ret_node = mq_rewrite_order_dependent_nodes (parser, list, select, unique);
if (ret_node == NULL)
{
if (!pt_has_error (parser))
{
/* error should have been set, but making sure */
PT_INTERNAL_ERROR (parser, "node rewrite failed");
}
return NULL;
}
/* relink */
ret_node->next = saved_next;
if (prev != NULL)
{
prev->next = ret_node;
}
/* advance */
prev = ret_node;
list = list->next;
}
}
/* done */
return node;
}
/* we now have an order independent node */
if (node->node_type == PT_VALUE)
{
/* ignore values, no need to move them around */
return node;
}
/* check if the node is in the subquey's select_list. */
if (node->node_type == PT_NAME)
{
for (pt_cur = dt_query->info.query.q.select.list; pt_cur != NULL; pt_cur = pt_cur->next)
{
if (pt_cur->node_type == PT_NAME && pt_name_equal (parser, pt_cur, node) == true)
{
/* the node is in subquery's select_list! Just use it. */
node->info.name.meta_class = PT_MISC_NONE;
node->info.name.resolved = spec->info.spec.range_var->info.name.original;
node->info.name.spec_id = spec->info.spec.id;
node->type_enum = pt_cur->type_enum;
node->data_type = parser_copy_tree (parser, pt_cur->data_type);
node->etc = pt_cur->etc;
/* Whatever it was, now it is visible. */
pt_cur->flag.is_hidden_column = 0;
return node;
}
}
}
/* add node to select list of derived table */
dt_query->info.query.q.select.list = parser_append_node (node, dt_query->info.query.q.select.list);
/* generate unique name for subexpression */
name = (char *) mq_generate_name (parser, "sx", unique);
if (name == NULL)
{
PT_INTERNAL_ERROR (parser, "name generation failed");
return NULL;
}
/* add to as_attr_list of derived table's spec */
as_attr = pt_name (parser, name);
if (as_attr == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
as_attr->info.name.spec_id = spec->info.spec.id;
as_attr->type_enum = node->type_enum;
as_attr->data_type = parser_copy_tree (parser, node->data_type);
as_attr->etc = node->etc;
spec->info.spec.as_attr_list = parser_append_node (as_attr, spec->info.spec.as_attr_list);
/* replace node with reference to derived table field */
attr = pt_name (parser, name);
if (as_attr == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
attr->info.name.resolved = spec->info.spec.range_var->info.name.original;
attr->info.name.spec_id = spec->info.spec.id;
attr->type_enum = node->type_enum;
attr->data_type = parser_copy_tree (parser, node->data_type);
attr->etc = node->etc;
return attr;
}
/*
* mq_rewrite_order_dependent_query - rewrite a query that has order dependent
* nodes in the select list
* returns: rewritten query
* parser(in): parser context
* select(in): SELECT statement node
* unique(in/out): pointer to an int counter, used for unique name generation
*
* EXAMPLE: the function will rewrite the query:
*
* SELECT a, b, @a := @a + (c * 3)
* FROM t ORDER BY a
*
* to the following:
*
* SELECT a, b, @a := @a + expr
* FROM (
* SELECT a, b, (c * 3) AS expr
* FROM t ORDER BY a
* ) dt
*
* thus ensuring that @a is correctly evaluated after sorting
*/
static PT_NODE *
mq_rewrite_order_dependent_query (PARSER_CONTEXT * parser, PT_NODE * select, int *unique)
{
PT_NODE *parent = NULL;
PT_NODE *dt = NULL, *dt_range_var = NULL;
PT_NODE *list = NULL, *list_prev = NULL, *list_next = NULL;
PT_NODE *as_attr = NULL;
PT_NODE *attr = NULL;
int list_pos = 1;
if (select == NULL)
{
/* nothing to do */
return NULL;
}
/* allocate new SELECT node */
parent = parser_new_node (parser, PT_SELECT);
if (parent == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
/* allocate derived table spec node */
dt = parser_new_node (parser, PT_SPEC);
if (dt == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
/* allocate spec's range var (using static name, should not be ambiguous) */
dt_range_var = pt_name (parser, "dt_sort");
if (dt_range_var == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return NULL;
}
/* original select will now be a subquery */
parent->info.query.is_subquery = select->info.query.is_subquery;
select->info.query.is_subquery = PT_IS_SUBQUERY;
/* set up spec */
dt->info.spec.id = (UINTPTR) dt;
dt->info.spec.derived_table = select;
dt->info.spec.derived_table_type = PT_IS_SUBQUERY;
dt->info.spec.range_var = dt_range_var;
dt->info.spec.join_type = PT_JOIN_NONE;
/* new SELECT will query a derived table */
parent->info.query.q.select.from = dt;
parent->info.query.correlation_level = select->info.query.correlation_level;
parent->info.query.scan_op_type = select->info.query.scan_op_type;
parent->info.query.oids_included = select->info.query.oids_included;
parent->type_enum = select->type_enum;
/*
* we now have the original SELECT (with both order dependent and order
* independent nodes in the select list) written as a derived table of an
* empty parent SELECT. first step is to:
* a. add an entry in the spec's as_attr_list for the order independent nodes
* b. add an entry in the parent select list for the order independent nodes
* c. move the order dependent nodes in the parent select list
*/
list = select->info.query.q.select.list;
list_prev = NULL;
list_next = list->next;
list_pos = 1; /* ORDER BY clause indexes position from 1 */
while (list != NULL)
{
if (!PT_IS_ORDER_DEPENDENT (list))
{
char *name = NULL;
/* this node is order independent so it stays in the derived table's select list */
/* generate name */
if (list->node_type == PT_NAME)
{
name = (char *) list->info.name.original;
}
else
{
name = (char *) mq_generate_name (parser, (const char *) "ex", unique);
}
/* add entry in spec's as_attr_list */
as_attr = pt_name (parser, name);
if (as_attr == NULL)
{
PT_ERRORm (parser, select, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
as_attr->info.name.spec_id = dt->info.spec.id;
as_attr->type_enum = list->type_enum;
as_attr->data_type = parser_copy_tree (parser, list->data_type);
as_attr->etc = list->etc;
dt->info.spec.as_attr_list = parser_append_node (as_attr, dt->info.spec.as_attr_list);
if (list->flag.is_hidden_column == 0)
{
/* add entry in new select list */
attr = pt_name (parser, name);
if (attr == NULL)
{
PT_ERRORm (parser, select, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
attr->info.name.resolved = dt_range_var->info.name.original;
attr->info.name.spec_id = dt->info.spec.id;
attr->type_enum = list->type_enum;
attr->data_type = parser_copy_tree (parser, list->data_type);
attr->etc = list->etc;
parent->info.query.q.select.list = parser_append_node (attr, parent->info.query.q.select.list);
}
/* advance in list */
list_prev = list;
list = list_next;
if (list)
{
list_next = list->next;
}
/* advance position in list */
list_pos++;
}
else
{
PT_NODE *sort_spec = select->info.query.order_by;
/* this node is order dependent so it goes in the parent select list */
/* link PREV to NEXT */
if (list_prev != NULL)
{
list_prev->next = list_next;
}
else
{
select->info.query.q.select.list = list_next;
}
/* destroy NEXT link of node */
list->next = NULL;
/* append node to parent select list */
parent->info.query.q.select.list = parser_append_node (list, parent->info.query.q.select.list);
/* adjust ORDER BY nodes */
while (sort_spec && sort_spec->node_type == PT_SORT_SPEC)
{
if (sort_spec->info.sort_spec.pos_descr.pos_no > list_pos)
{
sort_spec->info.sort_spec.pos_descr.pos_no--;
sort_spec->info.sort_spec.expr->info.value.data_value.i--;
}
sort_spec = sort_spec->next;
}
/* advance list and NEXT; prev stays the same */
list = list_next;
if (list)
{
list_next = list->next;
}
}
}
/* second step is to iterate trough the order dependent nodes (which are now in the parent select list) and, for each
* of them, add nodes in the derived table consisting of all order independent subexpressions */
list = parent->info.query.q.select.list;
while (list != NULL)
{
if (PT_IS_ORDER_DEPENDENT (list))
{
/* process subexpressions */
(void) mq_rewrite_order_dependent_nodes (parser, list, parent, unique);
}
/* advance list */
list = list->next;
}
/* mark new SELECT as order dependent so we can bump it's correlation level later on */
PT_SET_ORDER_DEPENDENT_FLAG (parent, true);
return parent;
}
/*
* mq_bump_order_dep_corr_lvl_pre - walk_tree function for bumping correlation
* levels of order dependent SELECTs
* parser(in): parser context
* node(in): node
* arg(in/out): parent node stack
* continue_walk(in/out): walk type
*
* NOTE: for the sake of simplicity, the stack is implemented as a double
* linked list of "PT_EXPR" PT_NODEs, with the following topology:
* n->next: will hold a pointer to the next item in the list
* n->info.expr.arg2: will hold a pointer to the previous item in the list
* n->info.expr.arg1: will hold a pointer to the actual node
*
* NOTE: nodes are pushed in the pre function and popped in the post function
*/
static PT_NODE *
mq_bump_order_dep_corr_lvl_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE **stack = (PT_NODE **) arg;
PT_NODE *stack_head = NULL, *stack_end = NULL;
PT_NODE *stack_item = NULL, *stack_prev = NULL;
if (node == NULL || parser == NULL || stack == NULL || !PT_IS_SELECT (node))
{
return node;
}
/* set up stack pointers */
stack_head = stack_end = *stack;
while (stack_end != NULL && stack_end->next != NULL)
{
stack_end = stack_end->next;
}
/* push node in stack */
stack_item = parser_new_node (parser, PT_EXPR);
if (stack_item == NULL)
{
PT_INTERNAL_ERROR (parser, "allocate new node");
return node;
}
stack_item->next = NULL;
stack_item->info.expr.arg1 = node;
stack_item->info.expr.arg2 = stack_end;
if (stack_end != NULL)
{
/* link to last item */
stack_end->next = stack_item;
stack_end = stack_item;
}
else
{
/* first item */
stack_head = stack_end = stack_item;
*stack = stack_item;
}
/* process node */
if (PT_IS_ORDER_DEPENDENT (node))
{
PT_NODE *item = NULL, *prev = NULL;
int corr_diff = 0;
stack_item = stack_end;
while (stack_item != NULL && stack_item->info.expr.arg2 != NULL)
{
/* due to the walk_tree function, all items in lists are pushed in the stack before any of them is popped;
* here, we skip same-level nodes */
stack_prev = stack_item->info.expr.arg2;
while (stack_prev && stack_prev->info.expr.arg1->next == stack_prev->next->info.expr.arg1)
{
stack_prev = stack_prev->info.expr.arg2;
}
if (stack_prev == NULL)
{
/* stack_item is in top level list; should never get here ... */
assert (false);
break;
}
/* get node and previous node */
item = stack_item->info.expr.arg1;
prev = stack_prev->info.expr.arg1;
corr_diff = item->info.query.correlation_level - prev->info.query.correlation_level;
/* check correlation levels */
if (item != NULL && prev != NULL && corr_diff <= 0 && item->info.query.correlation_level != 0)
{
/* same correlation level or parent has greater correlation level; not acceptable */
corr_diff = -corr_diff + 1;
(void) mq_bump_correlation_level (parser, item, corr_diff, item->info.query.correlation_level);
}
/* peek back in stack list */
stack_item = stack_prev;
}
}
return node;
}
/*
* mq_bump_order_dep_corr_lvl_post - walk_tree post function for bumping
* correlation levels of order dependent
* SELECTs
* parser(in): parser context
* node(in): node
* arg(in/out): parent node stack
* continue_walk(in/out): walk type
*
* NOTE: this function only pops nodes from the stack
*/
static PT_NODE *
mq_bump_order_dep_corr_lvl_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
PT_NODE **stack = (PT_NODE **) arg;
PT_NODE *stack_item = NULL;
if (node == NULL || parser == NULL || stack == NULL || !PT_IS_QUERY (node))
{
return node;
}
/* node is query, pop from stack */
stack_item = *stack;
while (stack_item != NULL && stack_item->next != NULL)
{
stack_item = stack_item->next;
}
if (stack_item != NULL && stack_item->info.expr.arg2)
{
stack_item->info.expr.arg2->next = NULL;
}
else
{
*stack = NULL;
}
if (stack_item != NULL)
{
parser_free_node (parser, stack_item);
}
return node;
}
/*
* mq_bump_order_dep_corr_lvl - bump correlation levels for order dependent
* SELECTs
* parser(in): parser context
* node(in): root node
*/
static void
mq_bump_order_dep_corr_lvl (PARSER_CONTEXT * parser, PT_NODE * node)
{
PT_NODE *stack = NULL;
/* bump order dependent SELECTs */
(void) parser_walk_tree (parser, node, mq_bump_order_dep_corr_lvl_pre, (void *) &stack,
mq_bump_order_dep_corr_lvl_post, (void *) &stack);
}
/*
* mq_reset_references_to_query_string () - reset references to the position
* in the original query string
* parser(in): parser context
* node(in): node
* arg(in/out): parent node stack
* continue_walk(in/out): walk type
*
* NOTE: This function resets the value of line_number, column_number and
* buffer_pos for each node. This is called on the parse trees of translated
* views. Since these values point to the view query and not to the actual
* query that is being executed, they will not hold useful information
*/
static PT_NODE *
mq_reset_references_to_query_string (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
if (node == NULL || parser == NULL)
{
return node;
}
node->line_number = 0;
node->column_number = 0;
node->buffer_pos = -1;
return node;
}
/*
* mq_auto_param_merge_clauses () - auto-parameterize update assignments
* and insert values clause
* return:
* parser(in):
* stmt(in):
*/
static void
mq_auto_param_merge_clauses (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
PT_NODE *values_list, *first, *prev, *p, *save_next;
int i;
/* auto-parameterize update assignments */
qo_auto_parameterize (parser, stmt->info.merge.update.assignment);
/* auto-parameterize insert values clause */
if (stmt->info.merge.insert.value_clauses)
{
p = values_list = stmt->info.merge.insert.value_clauses->info.node_list.list;
first = prev = NULL;
for (i = 0; p != NULL; i++)
{
save_next = p->next;
p->next = NULL;
if (pt_is_const_not_hostvar (p) && !PT_IS_NULL_NODE (p))
{
p = pt_rewrite_to_auto_param (parser, p);
}
if (i == 0)
{
first = p;
}
else
{
prev->next = p;
}
p->next = save_next;
prev = p;
p = p->next;
}
stmt->info.merge.insert.value_clauses->info.node_list.list = first;
}
}
/*
* mq_copy_view_error_msgs () - copy error message from a parser to another
*
* return: void
* parser(in):
* query_cache(in):
*
* Note that view parser will be freed for error cases.
* This means that error message of a view parser should be allocated and copied by the nesting parser.
*/
static void
mq_copy_view_error_msgs (PARSER_CONTEXT * parser, PARSER_CONTEXT * query_cache)
{
PT_NODE *error_msg;
int stmt_no, line_no, col_no;
const char *msg = NULL;
error_msg = pt_get_errors (query_cache);
/* expect callers already confirms it has an error */
assert (error_msg != NULL && error_msg->node_type == PT_ZZ_ERROR_MSG);
error_msg = pt_get_next_error (error_msg, &stmt_no, &line_no, &col_no, &msg);
pt_record_error (parser, stmt_no, line_no, col_no, msg, NULL);
}
/*
* mq_copy_sql_hint () - copy sql hint from src to dest
*
* return: void
* parser(in):
* dest_query(in):
* src_query(in):
*
*/
static void
mq_copy_sql_hint (PARSER_CONTEXT * parser, PT_NODE * dest_query, PT_NODE * src_query)
{
bool is_index_ss, is_index_ls, is_use_hash, is_no_use_hash;
if (dest_query->node_type == PT_SELECT)
{
is_index_ss = dest_query->info.query.q.select.hint & PT_HINT_INDEX_SS;
is_index_ls = dest_query->info.query.q.select.hint & PT_HINT_INDEX_LS;
/* remove some hints if there are multiple tables. */
if (dest_query->info.query.q.select.from->next != NULL)
{
/* ignore ordered hint */
if (src_query->info.query.q.select.hint & PT_HINT_ORDERED)
{
src_query->info.query.q.select.hint &= ~PT_HINT_ORDERED;
}
if (src_query->info.query.q.select.hint & PT_HINT_LEADING
&& dest_query->info.query.q.select.hint & PT_HINT_LEADING)
{
/* ignore leading hint */
src_query->info.query.q.select.hint &= ~PT_HINT_LEADING;
}
}
if (src_query->info.query.q.select.hint & PT_HINT_LEADING)
{
dest_query->info.query.q.select.leading =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.leading),
dest_query->info.query.q.select.leading);
}
/* merge HINT of vclass spec */
dest_query->info.query.q.select.hint =
(PT_HINT_ENUM) (dest_query->info.query.q.select.hint | src_query->info.query.q.select.hint);
dest_query->info.query.q.select.use_nl =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.use_nl),
dest_query->info.query.q.select.use_nl);
dest_query->info.query.q.select.use_idx =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.use_idx),
dest_query->info.query.q.select.use_idx);
if (!is_index_ss || dest_query->info.query.q.select.index_ss != NULL)
{
dest_query->info.query.q.select.index_ss =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.index_ss),
dest_query->info.query.q.select.index_ss);
}
if (!is_index_ls || dest_query->info.query.q.select.index_ls != NULL)
{
dest_query->info.query.q.select.index_ls =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.index_ls),
dest_query->info.query.q.select.index_ls);
}
dest_query->info.query.q.select.use_merge =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.use_merge),
dest_query->info.query.q.select.use_merge);
dest_query->info.query.q.select.no_use_hash =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.no_use_hash),
dest_query->info.query.q.select.no_use_hash);
dest_query->info.query.q.select.use_hash =
parser_append_node (parser_copy_tree_list (parser, src_query->info.query.q.select.use_hash),
dest_query->info.query.q.select.use_hash);
dest_query->info.query.q.select.num_parallel_threads = src_query->info.query.q.select.num_parallel_threads;
}
/* merge USING INDEX clause of vclass spec */
if (src_query->info.query.q.select.using_index)
{
PT_NODE *ui;
ui = parser_copy_tree_list (parser, src_query->info.query.q.select.using_index);
if (dest_query->node_type == PT_SELECT)
{
dest_query->info.query.q.select.using_index =
parser_append_node (ui, dest_query->info.query.q.select.using_index);
}
else if (dest_query->node_type == PT_UPDATE)
{
dest_query->info.update.using_index = parser_append_node (ui, dest_query->info.update.using_index);
}
else if (dest_query->node_type == PT_DELETE)
{
dest_query->info.delete_.using_index = parser_append_node (ui, dest_query->info.delete_.using_index);
}
}
}
/*
* mq_update_analytic_sort_spec_expr() - update PT_VALUE located within the OVER clause of the analytic function.
* return:
* parser(in):
* new_select_list(in):
* old_select_list(in):
*
* NOTE: After calling mq_rewrite_vclass_spec_as_derived() or mq_remove_select_list_for_inline_view(), the order of nodes in the select list may change.
* When analytic functions are included, query results can vary based on the order of nodes in the select list.
* Therefore, it is necessary to update the sort_spec expression of the analytic functions.
*/
static PT_NODE *
mq_update_analytic_sort_spec_expr (PARSER_CONTEXT * parser, PT_NODE * new_select_list, PT_NODE * old_select_list)
{
PT_NODE *partition_by, *order_by;
PT_NODE *col, *link, *order_list, *order, *value;
for (col = new_select_list; col; col = col->next)
{
if (PT_IS_ANALYTIC_NODE (col))
{
partition_by = col->info.function.analytic.partition_by;
order_by = col->info.function.analytic.order_by;
/* link partition and order lists together */
for (link = partition_by; link && link->next; link = link->next)
{
; /* move link to the last node of partition_by list */
}
if (link)
{
order_list = partition_by;
link->next = order_by;
}
else
{
order_list = order_by;
}
for (order = order_list; order; order = order->next)
{
PT_NODE *old_select_node, *new_select_node, *referenced_node;
int old_index, index;
PT_NODE_TYPE type;
if (!PT_IS_VALUE_NODE (order->info.sort_spec.expr))
{
continue;
}
value = order->info.sort_spec.expr;
old_index = value->info.value.data_value.i;
if (old_select_list->type_enum != PT_TYPE_OBJECT)
{
old_index -= 1;
}
old_select_node = pt_get_node_from_list (old_select_list, old_index);
if (old_select_node == NULL)
{
PT_ERRORmf (parser, value, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_RANGE_ERR,
old_index);
return NULL;
}
referenced_node = parser_copy_tree (parser, old_select_node);
if (referenced_node == NULL)
{
PT_ERRORm (parser, old_select_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return NULL;
}
for (new_select_node = new_select_list, index = 1; new_select_node;
new_select_node = new_select_node->next, index++)
{
if (new_select_node->type_enum == PT_TYPE_OBJECT)
{
continue;
}
if (pt_compare_sort_spec_expr (parser, referenced_node, new_select_node))
{
/* match */
break;
}
}
if (new_select_node == NULL)
{
/* referenced_node was not found in select-list */
referenced_node->flag.is_hidden_column = 1;
parser_append_node (referenced_node, new_select_list);
}
value->info.value.data_value.i = index;
order->info.sort_spec.pos_descr.pos_no = index;
}
/* un-link */
if (link)
{
link->next = NULL;
}
}
}
return new_select_list;
}