File execute_schema.c¶
File List > cubrid > src > query > execute_schema.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.
*
*/
/*
* execute_schema.c
*/
#ident "$Id$"
#include "config.h"
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>
#include "authenticate.h"
#include "error_manager.h"
#include "parser.h"
#include "parser_message.h"
#include "db.h"
#include "dbi.h"
#include "semantic_check.h"
#include "execute_schema.h"
#include "execute_statement.h"
#include "schema_manager.h"
#include "transaction_cl.h"
#include "system_parameter.h"
#if defined(WINDOWS)
#include "misc_string.h"
#endif /* WINDOWS */
#include "semantic_check.h"
#include "xasl_generation.h"
#include "memory_alloc.h"
#include "schema_system_catalog_constants.h"
#include "transform.h"
#include "set_object.h"
#include "object_accessor.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "memory_hash.h"
#include "locator_cl.h"
#include "network_interface_cl.h"
#include "view_transform.h"
#include "xasl_to_stream.h"
#include "parser_support.h"
#include "dbtype.h"
#include "jsp_cl.h"
#include "msgcat_glossary.hpp"
#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1) ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */
#define UNIQUE_SAVEPOINT_ADD_ATTR_MTHD "aDDaTTRmTHD"
#define UNIQUE_SAVEPOINT_CREATE_ENTITY "cREATEeNTITY"
#define UNIQUE_SAVEPOINT_DROP_ENTITY "dROPeNTITY"
#define UNIQUE_SAVEPOINT_REPLACE_VIEW "rEPlACE"
#define UNIQUE_SAVEPOINT_RENAME "rENAME"
#define UNIQUE_SAVEPOINT_MULTIPLE_ALTER "mULTIPLEaLTER"
#define UNIQUE_SAVEPOINT_TRUNCATE "tRUnCATE"
#define UNIQUE_SAVEPOINT_CHANGE_ATTR "cHANGEaTTR"
#define UNIQUE_SAVEPOINT_ALTER_INDEX "aLTERiNDEX"
#define UNIQUE_SAVEPOINT_CHANGE_DEF_COLL "cHANGEdEFAULTcOLL"
#define UNIQUE_SAVEPOINT_CHANGE_TBL_COMMENT "cHANGEtBLcOMMENT"
#define UNIQUE_SAVEPOINT_CHANGE_COLUMN_COMMENT "cHANGEcOLUMNcOMMENT"
#define UNIQUE_SAVEPOINT_CREATE_USER_ENTITY "cREATEuSEReNTITY"
#define UNIQUE_SAVEPOINT_DROP_USER_ENTITY "dROPuSEReNTITY"
#define UNIQUE_SAVEPOINT_ALTER_USER_ENTITY "aLTERuSEReNTITY"
#define UNIQUE_SAVEPOINT_GRANT_USER "gRANTuSER"
#define UNIQUE_SAVEPOINT_REVOKE_USER "rEVOKEuSER"
#define QUERY_MAX_SIZE 1024 * 1024
#define MAX_FILTER_PREDICATE_STRING_LENGTH (1073741823)
#define MAX_FUNCTION_EXPRESSION_STRING_LENGTH 1024
typedef enum
{
DO_INDEX_CREATE, DO_INDEX_DROP
} DO_INDEX;
typedef enum
{
SM_ATTR_CHG_NOT_NEEDED = 0,
SM_ATTR_CHG_ONLY_SCHEMA = 1,
SM_ATTR_CHG_WITH_ROW_UPDATE = 2,
SM_ATTR_CHG_BEST_EFFORT = 3 /* same as SM_ATTR_CHG_WITH_ROW_UPDATE, but there is a significant chance that the
* operation will fail */
} SM_ATTR_CHG_SOL;
/* The ATT_CHG_XXX enum bit flags describe the status of an attribute specific
* property (sm_attr_properties_chg). Each property is initialized with
* 'ATT_CHG_PROPERTY_NOT_CHECKED' value, and keeps it until is marked as
* checked (by setting to zero) and then set corresponding bits.
* '_OLD' and '_NEW' flags track simple presence of property in the attribute
* existing schema and new definition, while the upper values flags are set
* upon more cross-checkings. Some flags applies only to certain properties
* (like '.._TYPE_..' for domain of attribute)
* !! Values in enum should be kept in this order as some internal checks
* rely on the order !!*/
enum
{
/* property present in existing schema */
ATT_CHG_PROPERTY_PRESENT_OLD = 0x1,
/* property present in new attribute definition */
ATT_CHG_PROPERTY_PRESENT_NEW = 0x2,
/* present in OLD , lost in NEW */
ATT_CHG_PROPERTY_LOST = 0x4,
/* not present in OLD , gained in NEW */
ATT_CHG_PROPERTY_GAINED = 0x8,
/* property is not changed (not present in both current schema or new defition or present in both but not affected in
* any way) */
ATT_CHG_PROPERTY_UNCHANGED = 0x10,
/* property is changed (i.e.: both present in old an new , but different) */
ATT_CHG_PROPERTY_DIFF = 0x20,
/* type : precision increase : varchar(2) -> varchar (10) */
ATT_CHG_TYPE_PREC_INCR = 0x100,
/* type : for COLLECTION or OBJECT (Class) type : the new SET is more general (the new OBJECT is a super-class) */
ATT_CHG_TYPE_SET_CLS_COMPAT = 0x200,
/* type : upgrade : int -> bigint */
ATT_CHG_TYPE_UPGRADE = 0x400,
/* type : changed, but needs checking if new domain supports all existing values , i.e : int -> char(3) */
ATT_CHG_TYPE_NEED_ROW_CHECK = 0x800,
/* type : pseudo-upgrade : datetime -> time: this is succesful as a cast, but may fail due to unique constraint */
ATT_CHG_TYPE_PSEUDO_UPGRADE = 0x1000,
/* type : not supported with existing configuration */
ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG = 0x2000,
/* type : upgrade : not supported */
ATT_CHG_TYPE_NOT_SUPPORTED = 0x4000,
/* property was not checked needs to be the highest value in enum */
ATT_CHG_PROPERTY_NOT_CHECKED = 0x10000
};
/* Enum to access array from 'sm_attr_properties_chg' struct
*/
enum
{
P_NAME = 0, /* name of attribute */
P_NOT_NULL, /* constraint NOT NULL */
P_DEFAULT_VALUE, /* DEFAULT VALUE of attribute */
P_ON_UPDATE_EXPR, /* ON UPADTE default of attribute */
P_CONSTR_CHECK, /* constraint CHECK */
P_DEFFERABLE, /* DEFFERABLE */
P_ORDER, /* ORDERING definition */
P_AUTO_INCR, /* has AUTO INCREMENT */
P_CONSTR_FK, /* constraint FOREIGN KEY */
P_S_CONSTR_PK, /* constraint PRIMARY KEY only on one single column : the checked attribute */
P_M_CONSTR_PK, /* constraint PRIMARY KEY on more columns, including checked attribute */
P_S_CONSTR_UNI, /* constraint UNIQUE only on one single column : the checked attribute */
P_M_CONSTR_UNI, /* constraint UNIQUE on more columns, including checked attribute */
P_CONSTR_NON_UNI, /* has a non-unique index defined on it, should apply only for existing schema (as you
* cannot add a non-index with ALTER CHANGE) */
P_PREFIX_INDEX, /* has a prefix index defined on it, should apply only for existing schema (as you
* cannot add a prefix index with ALTER CHANGE) */
P_TYPE, /* type (domain) change */
P_IS_PARTITION_COL, /* class has partitions */
P_COMMENT, /* has comment */
NUM_ATT_CHG_PROP
};
/* sm_attr_properties_chg :
* structure used for checking existing attribute definition (from schema)
* and new attribute definition
* Array is accessed using enum values define above.
*/
typedef struct sm_attr_properties_chg SM_ATTR_PROP_CHG;
struct sm_attr_properties_chg
{
int p[NUM_ATT_CHG_PROP]; /* 'change' property */
SM_CONSTRAINT_INFO *constr_info;
SM_CONSTRAINT_INFO *new_constr_info;
int att_id;
SM_NAME_SPACE name_space; /* class, shared or normal attribute */
SM_NAME_SPACE new_name_space; /* class, shared or normal attribute */
bool class_has_subclass; /* if class is part of a hierarchy and if it has subclasses */
};
/* sm_partition_alter_info :
* helper object for altering partitioning
*/
typedef struct sm_partition_alter_info SM_PARTITION_ALTER_INFO;
struct sm_partition_alter_info
{
MOP root_op; /* MOP of the root class */
DB_CTMPL *root_tmpl; /* template of the root class */
char keycol[DB_MAX_IDENTIFIER_LENGTH]; /* partition key column */
char **promoted_names; /* promoted partition names */
int promoted_count; /* number of promoted partition */
};
/* part_class_info :
* structure used for creating partitions for a class
*/
typedef struct part_class_info PART_CLASS_INFO;
struct part_class_info
{
char *pname; /* partition name */
DB_CTMPL *temp; /* partition class template for */
DB_OBJECT *obj; /* partition class obj */
PART_CLASS_INFO *next;
};
/* db_value_slist :
* partition range definition list
*/
typedef struct db_value_slist DB_VALUE_SLIST;
struct db_value_slist
{
struct db_value_slist *next;
SM_PARTITION *partition; /* partition info */
MOP class_obj; /* class object */
DB_VALUE *min; /* min range for partition */
DB_VALUE *max; /* max range for partition */
};
static int drop_class_name (const char *name, bool is_cascade_constraints);
static int do_alter_one_clause_with_template (PARSER_CONTEXT * parser, PT_NODE * alter);
static int lob_process_dir_add_attr (SM_CLASS * class_, int old_att_count);
static int lob_process_dir_drop_attr (SM_CLASS * class_, const char *attr_name);
static int do_alter_clause_rename_entity (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_clause_add_index (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_clause_drop_index (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_change_auto_increment (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_rename_internal (const char *const old_name, const char *const new_name);
static DB_CONSTRAINT_TYPE get_reverse_unique_index_type (const bool is_reverse, const bool is_unique);
static int create_or_drop_index_helper (PARSER_CONTEXT * parser, const char *const constraint_name,
const bool is_reverse, const bool is_unique,
const PT_INDEX_INFO * idx_info, DB_OBJECT * const obj, DO_INDEX do_index);
static int update_locksets_for_multiple_rename (const char *class_name, int *num_mops, MOP * mop_set, int *num_names,
char **name_set, bool error_on_misssing_class);
static int acquire_locks_for_multiple_rename (const PT_NODE * statement);
static int validate_attribute_domain (PARSER_CONTEXT * parser, PT_NODE * attribute, const bool check_zero_precision);
static SM_FOREIGN_KEY_ACTION map_pt_to_sm_action (const PT_MISC_TYPE action);
static int add_foreign_key (DB_CTMPL * ctemplate, const PT_NODE * cnstr, const char **att_names);
static int add_union_query (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * query);
static int add_query_to_virtual_class (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * queries);
static int do_copy_indexes (PARSER_CONTEXT * parser, MOP classmop, SM_CLASS * src_class);
static int do_recreate_renamed_class_indexes (const PARSER_CONTEXT * parser, const char *const old_class_name,
const char *const class_name);
static int do_alter_clause_change_attribute (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_change_owner (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_change_default_cs_coll (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_change_tbl_comment (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_alter_change_col_comment (PARSER_CONTEXT * const parser, PT_NODE * const alter);
static int do_change_att_schema_only (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attribute,
PT_NODE * old_name_node, PT_NODE * constraints, SM_ATTR_PROP_CHG * attr_chg_prop,
SM_ATTR_CHG_SOL * change_mode);
static int build_attr_change_map (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attr_def,
PT_NODE * attr_old_name, PT_NODE * constraints,
SM_ATTR_PROP_CHG * attr_chg_properties);
static int build_att_type_change_map (TP_DOMAIN * curr_domain, TP_DOMAIN * req_domain,
SM_ATTR_PROP_CHG * attr_chg_properties);
static int build_att_coll_change_map (TP_DOMAIN * curr_domain, TP_DOMAIN * req_domain,
SM_ATTR_PROP_CHG * attr_chg_properties);
static int check_att_chg_allowed (const char *att_name, const PT_TYPE_ENUM t, const SM_ATTR_PROP_CHG * attr_chg_prop,
SM_ATTR_CHG_SOL chg_how, bool log_error_allowed, bool * new_attempt);
static bool is_att_property_structure_checked (const SM_ATTR_PROP_CHG * attr_chg_properties);
static bool is_att_change_needed (const SM_ATTR_PROP_CHG * attr_chg_properties);
static void reset_att_property_structure (SM_ATTR_PROP_CHG * attr_chg_properties);
static bool is_att_prop_set (const int prop, const int value);
static int get_att_order_from_def (PT_NODE * attribute, bool * ord_first, const char **ord_after_name);
static int check_default_on_update_clause (PARSER_CONTEXT * parser, PT_NODE * attribute);
static int get_att_default_from_def (PARSER_CONTEXT * parser, PT_NODE * attribute, DB_VALUE ** default_value,
const char *classname);
static int do_update_new_notnull_cols_without_default (PARSER_CONTEXT * parser, PT_NODE * alter, MOP class_mop);
static int do_update_new_cols_with_default_expression (PARSER_CONTEXT * parser, PT_NODE * alter, MOP class_mop);
static int do_run_update_query_for_new_notnull_fields (PARSER_CONTEXT * parser, PT_NODE * alter, PT_NODE * attr_list,
int attr_count, MOP class_mop);
static bool is_attribute_primary_key (const char *class_name, const char *attr_name);
static const char *get_hard_default_for_type (PT_TYPE_ENUM type);
static int do_run_upgrade_instances_domain (PARSER_CONTEXT * parser, OID * p_class_oid, int att_id);
static int do_drop_att_constraints (MOP class_mop, SM_CONSTRAINT_INFO * constr_info_list);
static int do_recreate_att_constraints (MOP class_mop, SM_CONSTRAINT_INFO * constr_info_list);
static int check_change_attribute (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attribute,
PT_NODE * old_name_node, PT_NODE ** pointer_constraints,
SM_ATTR_PROP_CHG * attr_chg_prop, SM_ATTR_CHG_SOL * change_mode);
static int check_change_class_collation (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_ALTER_INFO * alter,
bool * need_update, int *collation_id);
static int sort_constr_info_list (SM_CONSTRAINT_INFO ** source);
static int save_constraint_info_from_pt_node (SM_CONSTRAINT_INFO ** save_info, const PT_NODE * const pt_constr);
static int do_run_update_query_for_class (char *query, MOP class_mop, int *row_count);
static SM_FUNCTION_INFO *pt_node_to_function_index (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * sort_spec,
DO_INDEX do_index);
static int do_create_partition_constraints (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_create_partition_constraint (PT_NODE * alter, SM_CLASS * root_class, SM_CLASS_CONSTRAINT * constraint,
SM_PARTITION_ALTER_INFO * pinfo);
static int do_alter_partitioning_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_alter_partitioning_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_create_partition (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_remove_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_remove_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_coalesce_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_coalesce_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_reorganize_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_reorganize_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_promote_partition_list (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
static int do_promote_partition_by_name (const char *class_name, const char *part_num, char **partition_name);
static int do_promote_partition (SM_CLASS * class_);
#if defined (ENABLE_UNUSED_FUNCTION)
static int do_analyze_partition (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo);
#endif
static int do_redistribute_partitions_data (const char *class_name, const char *keyname, char **promoted,
int promoted_count, PT_ALTER_CODE alter_op, bool should_update,
bool should_insert);
static SM_FUNCTION_INFO *compile_partition_expression (PARSER_CONTEXT * parser, PT_NODE * entity_name, PT_NODE * pinfo);
static PT_NODE *replace_names_alter_chg_attr (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
int *continue_walk);
static PT_NODE *pt_replace_names_index_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
int *continue_walk);
static int adjust_partition_range (DB_OBJLIST * objs);
static int adjust_partition_size (MOP class_, DB_CTMPL * tmpl);
static const char *get_attr_name (PT_NODE * attribute);
static int do_add_attribute (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * atts, PT_NODE * constraints,
bool error_on_not_normal);
static int do_add_attribute_from_select_column (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, DB_QUERY_TYPE * column);
static DB_QUERY_TYPE *query_get_column_with_name (DB_QUERY_TYPE * query_columns, const char *name);
static PT_NODE *get_attribute_with_name (PT_NODE * atts, const char *name);
static PT_NODE *create_select_to_insert_into (PARSER_CONTEXT * parser, const char *class_name, PT_NODE * create_select,
PT_CREATE_SELECT_ACTION create_select_action,
DB_QUERY_TYPE * query_columns);
static int execute_create_select_query (PARSER_CONTEXT * parser, const char *const class_name, PT_NODE * create_select,
PT_CREATE_SELECT_ACTION create_select_action, DB_QUERY_TYPE * query_columns,
PT_NODE * flagged_statement);
static int do_set_auto_increment (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const char *attr_name,
PT_NODE * attribute, SM_ATTRIBUTE ** attr);
static int do_find_auto_increment_serial (MOP * auto_increment_obj, const char *class_name, const char *attr_name);
static int do_check_fk_constraints_internal (DB_CTMPL * ctemplate, PT_NODE * constraints, bool is_partitioned);
static int get_index_type_qualifiers (MOP obj, bool * is_reverse, bool * is_unique, const char *index_name);
static SM_PARTITION *pt_node_to_partition_info (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * entity_name,
char *class_name, char *partition_name, DB_VALUE * minval);
static int do_save_all_indexes (MOP classmop, SM_CONSTRAINT_INFO ** saved_index_info_listpp);
static int do_drop_saved_indexes (MOP classmop, SM_CONSTRAINT_INFO * index_save_info);
static int do_recreate_saved_indexes (MOP classmop, SM_CONSTRAINT_INFO * index_save_info);
static int do_alter_index_status (PARSER_CONTEXT * parser, const PT_NODE * statement);
int ib_thread_count = 0;
/*
* Function Group :
* DO functions for alter statement
*
*/
/*
* do_alter_one_clause_with_template() - Executes the operations required by a
* single ALTER clause.
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a single clause of an ALTER statement. Not
* all possible clauses are handled by this function; see the
* note below and the do_alter() function.
*
* Note: This function handles clauses that require class template operations.
* It always calls dbt_edit_class(). Other ALTER clauses might have
* dedicated processing functions. See do_alter() for details.
*/
static int
do_alter_one_clause_with_template (PARSER_CONTEXT * parser, PT_NODE * alter)
{
const char *entity_name, *new_query;
const char *attr_name, *mthd_name, *mthd_file, *attr_mthd_name;
const char *new_name, *old_name, *domain;
DB_CTMPL *ctemplate = NULL;
DB_OBJECT *vclass, *sup_class;
int error = NO_ERROR;
DB_ATTRIBUTE *found_attr, *def_attr;
DB_METHOD *found_mthd;
TP_DOMAIN *def_domain;
DB_VALUE src_val, dest_val;
DB_TYPE db_desired_type;
int query_no, class_attr;
PT_NODE *vlist, *p, *n, *d;
PT_NODE *node, *nodelist;
PT_NODE *data_type, *data_default, *path;
PT_NODE *slist;
PT_TYPE_ENUM pt_desired_type;
PT_NODE *temp_val, *def_val, *initial_def_val = NULL;
#if 0
HFID *hfid;
#endif
SM_PARTITION_ALTER_INFO pinfo;
bool partition_savepoint = false;
const PT_ALTER_CODE alter_code = alter->info.alter.code;
#if defined (ENABLE_RENAME_CONSTRAINT)
SM_CONSTRAINT_FAMILY constraint_family;
#endif
unsigned int save_custom;
PT_NODE *super_node = NULL;
MOP super_class;
DB_DEFAULT_EXPR default_expr;
entity_name = alter->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
ERROR1 (error, ER_UNEXPECTED, "Expecting a class or virtual class name.");
return error;
}
vclass = db_find_class (entity_name);
if (vclass == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
db_make_null (&src_val);
db_make_null (&dest_val);
ctemplate = dbt_edit_class (vclass);
if (ctemplate == NULL)
{
/* when dbt_edit_class fails (e.g. because the server unilaterally aborts us), we must record the associated
* error message into the parser. Otherwise, we may get a confusing error msg of the form: "so_and_so is not a
* class". */
pt_record_error (parser, parser->statement_number - 1, alter->line_number, alter->column_number, er_msg (), NULL);
return er_errid ();
}
switch (alter_code)
{
case PT_ADD_QUERY:
error = do_add_queries (parser, ctemplate, alter->info.alter.alter_clause.query.query);
if (error != NO_ERROR)
{
break;
}
/* set vclass comment if it exists */
if (alter->info.alter.alter_clause.query.view_comment != NULL)
{
error = do_alter_change_tbl_comment (parser, alter);
}
break;
case PT_DROP_QUERY:
vlist = alter->info.alter.alter_clause.query.query_no_list;
if (vlist == NULL)
{
error = dbt_drop_query_spec (ctemplate, 1);
}
else if (vlist->next == NULL)
{ /* ie, only one element in list */
error = dbt_drop_query_spec (ctemplate, vlist->info.value.data_value.i);
}
else
{
slist = pt_sort_in_desc_order (vlist);
for (; slist; slist = slist->next)
{
error = dbt_drop_query_spec (ctemplate, slist->info.value.data_value.i);
if (error != NO_ERROR)
{
break;
}
}
}
break;
case PT_MODIFY_QUERY:
if (alter->info.alter.alter_clause.query.query_no_list)
{
query_no = alter->info.alter.alter_clause.query.query_no_list->info.value.data_value.i;
}
else
{
query_no = 1;
}
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
new_query = parser_print_tree_with_quotes (parser, alter->info.alter.alter_clause.query.query);
parser->custom_print = save_custom;
error = dbt_change_query_spec (ctemplate, new_query, query_no);
if (error != NO_ERROR)
{
break;
}
/* set vclass comment if it exists */
if (alter->info.alter.alter_clause.query.view_comment != NULL)
{
error = do_alter_change_tbl_comment (parser, alter);
}
break;
case PT_ADD_ATTR_MTHD:
#if 0
/* we currently core dump when adding a unique constraint at the same time as an attribute, whether the unique
* constraint is on the new attribute or another. Therefore we temporarily disallow adding a unique constraint
* and an attribute in the same alter statement if the class has or has had any instances. Note that we should be
* checking for instances in the entire subhierarchy, not just the current class. */
if ((hfid = sm_get_ch_heap (vclass)) && !HFID_IS_NULL (hfid)
&& alter->info.alter.alter_clause.attr_mthd.attr_def_list)
{
for (p = alter->info.alter.constraint_list; p != NULL; p = p->next)
{
if (p->info.constraint.type == PT_CONSTRAIN_UNIQUE)
{
error = ER_DO_ALTER_ADD_WITH_UNIQUE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
(void) dbt_abort_class (ctemplate);
return error;
}
}
PT_END;
}
#endif
{
SM_CLASS *class_ = ctemplate->current;
SM_ATTRIBUTE *attr;
int new_att_count = 0;
int old_att_count = class_->att_count;
int type_id = 0;
int lob_attrid_arr_length = 0;
int *lob_alloc_attrid_arr = NULL;
int lob_local_attrid_arr[2];
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
if (error == NO_ERROR)
{
error =
do_add_attributes (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.attr_def_list,
alter->info.alter.constraint_list, NULL);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
vclass = dbt_finish_class (ctemplate);
if (vclass == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
ctemplate = dbt_edit_class (vclass);
if (ctemplate == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
error = do_add_constraints (ctemplate, alter->info.alter.constraint_list);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
error = do_check_fk_constraints (ctemplate, alter->info.alter.constraint_list);
if (error != NO_ERROR)
{
(void) dbt_abort_class (ctemplate);
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
if (alter->info.alter.alter_clause.attr_mthd.mthd_def_list != NULL)
{
error = do_add_methods (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.mthd_def_list);
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
if (alter->info.alter.alter_clause.attr_mthd.mthd_file_list != NULL)
{
error =
do_add_method_files (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.mthd_file_list);
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
assert (alter->info.alter.create_index == NULL);
error = lob_process_dir_add_attr (ctemplate->current, old_att_count);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
return error;
}
}
break;
}
case PT_RESET_QUERY:
{
DB_ATTRIBUTE *cur_attr = db_get_attributes (vclass);
assert (db_get_subclasses (vclass) == NULL);
assert (db_get_superclasses (vclass) == NULL);
/* drop all attributes */
while (cur_attr)
{
assert (cur_attr->header.name != NULL);
error = dbt_drop_attribute (ctemplate, cur_attr->header.name);
if (error != NO_ERROR)
{
goto reset_query_error;
}
cur_attr = db_attribute_next (cur_attr);
}
/* also drop any query specs there may have been */
error = dbt_reset_query_spec (ctemplate);
if (error != NO_ERROR)
{
goto reset_query_error;
}
/* add the new attributes */
error =
do_add_attributes (parser, ctemplate, alter->info.alter.alter_clause.query.attr_def_list,
alter->info.alter.constraint_list, NULL);
if (error != NO_ERROR)
{
goto reset_query_error;
}
/* and add the single query spec we allow */
error = do_add_queries (parser, ctemplate, alter->info.alter.alter_clause.query.query);
if (error != NO_ERROR)
{
goto reset_query_error;
}
/* set vclass comment if it exists */
if (alter->info.alter.alter_clause.query.view_comment != NULL)
{
error = do_alter_change_tbl_comment (parser, alter);
}
if (error != NO_ERROR)
{
goto reset_query_error;
}
break;
reset_query_error:
dbt_abort_class (ctemplate);
return error;
}
break;
case PT_DROP_ATTR_MTHD:
attr_mthd_name = "";
p = alter->info.alter.alter_clause.attr_mthd.attr_mthd_name_list;
for (; p && p->node_type == PT_NAME; p = p->next)
{
attr_mthd_name = p->info.name.original;
if (p->info.name.meta_class == PT_META_ATTR)
{
found_attr = db_get_class_attribute (vclass, attr_mthd_name);
if (found_attr)
{
error = dbt_drop_class_attribute (ctemplate, attr_mthd_name);
}
else
{
found_mthd = db_get_class_method (vclass, attr_mthd_name);
if (found_mthd)
{
error = dbt_drop_class_method (ctemplate, attr_mthd_name);
}
}
}
else
{
found_attr = db_get_attribute (vclass, attr_mthd_name);
if (found_attr)
{
error = dbt_drop_attribute (ctemplate, attr_mthd_name);
}
else
{
found_mthd = db_get_method (vclass, attr_mthd_name);
if (found_mthd)
{
error = dbt_drop_method (ctemplate, attr_mthd_name);
}
}
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
}
p = alter->info.alter.alter_clause.attr_mthd.mthd_file_list;
for (;
p && p->node_type == PT_FILE_PATH && (path = p->info.file_path.string) != NULL && path->node_type == PT_VALUE
&& (path->type_enum == PT_TYPE_VARCHAR || path->type_enum == PT_TYPE_CHAR); p = p->next)
{
mthd_file = (char *) path->info.value.data_value.str->bytes;
error = dbt_drop_method_file (ctemplate, mthd_file);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
}
error = lob_process_dir_drop_attr (ctemplate->current, (char *) attr_mthd_name);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
break;
case PT_MODIFY_ATTR_MTHD:
p = alter->info.alter.alter_clause.attr_mthd.attr_def_list;
for (; p && p->node_type == PT_ATTR_DEF; p = p->next)
{
attr_name = p->info.attr_def.attr_name->info.name.original;
if (p->info.attr_def.attr_type == PT_META_ATTR)
{
class_attr = 1;
}
else
{
class_attr = 0;
}
data_type = p->data_type;
domain = pt_node_to_db_domain_name (p);
error = dbt_change_domain (ctemplate, attr_name, class_attr, domain);
if (data_type && pt_is_set_type (p))
{
nodelist = data_type->data_type;
for (node = nodelist; node != NULL; node = node->next)
{
domain = pt_data_type_to_db_domain_name (node);
error = dbt_add_set_attribute_domain (ctemplate, attr_name, class_attr, domain);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
}
}
data_default = p->info.attr_def.data_default;
if (data_default != NULL && data_default->node_type == PT_DATA_DEFAULT)
{
pt_desired_type = p->type_enum;
if (pt_desired_type == PT_TYPE_BLOB || pt_desired_type == PT_TYPE_CLOB)
{
error = ER_INTERFACE_NOT_SUPPORTED_OPERATION;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
break;
}
/* try to coerce the default value into the attribute's type */
d = data_default->info.data_default.default_value;
d = pt_semantic_check (parser, d);
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
dbt_abort_class (ctemplate);
return er_errid ();
}
if (d != NULL)
{
error = pt_coerce_value (parser, d, d, pt_desired_type, p->data_type);
if (error != NO_ERROR)
{
break;
}
}
pt_evaluate_tree (parser, d, &dest_val, 1);
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
dbt_abort_class (ctemplate);
return er_errid ();
}
error = dbt_change_default (ctemplate, attr_name, class_attr, &dest_val);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
}
}
/* the order in which methods are defined will change; currently there's no way around this problem. */
p = alter->info.alter.alter_clause.attr_mthd.mthd_def_list;
for (; p && p->node_type == PT_METHOD_DEF; p = p->next)
{
mthd_name = p->info.method_def.method_name->info.name.original;
error = dbt_drop_method (ctemplate, mthd_name);
if (error == NO_ERROR)
{
error = do_add_methods (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.mthd_def_list);
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
}
break;
case PT_ADD_SUPCLASS:
/* here we need to check if the super classes are partitioned, as it is not allowed to inherit from partition
* tables. */
assert (error == NO_ERROR);
super_node = alter->info.alter.super.sup_class_list;
while (super_node)
{
super_class = db_find_class (super_node->info.name.original);
if (super_class == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
}
else
{
error = sm_is_partitioned_class (super_class);
if (error > 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INHERIT_FROM_PARTITION_TABLE, 0);
error = er_errid ();
}
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
super_node = super_node->next;
}
error = do_add_supers (parser, ctemplate, alter->info.alter.super.sup_class_list);
break;
case PT_DROP_SUPCLASS:
for (p = alter->info.alter.super.sup_class_list; p && p->node_type == PT_NAME; p = p->next)
{
sup_class = db_find_class (p->info.name.original);
if (sup_class == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = dbt_drop_super (ctemplate, sup_class);
}
if (error != NO_ERROR)
{
(void) dbt_abort_class (ctemplate);
return error;
}
}
break;
case PT_DROP_RESOLUTION:
for (p = alter->info.alter.super.resolution_list; p && p->node_type == PT_RESOLUTION; p = p->next)
{
sup_class = db_find_class (p->info.resolution.of_sup_class_name->info.name.original);
attr_mthd_name = p->info.resolution.attr_mthd_name->info.name.original;
error = dbt_drop_resolution (ctemplate, sup_class, attr_mthd_name);
if (error != NO_ERROR)
{
(void) dbt_abort_class (ctemplate);
return error;
}
}
break;
case PT_MODIFY_DEFAULT:
case PT_ALTER_DEFAULT:
n = alter->info.alter.alter_clause.ch_attr_def.attr_name_list;
d = alter->info.alter.alter_clause.ch_attr_def.data_default_list;
for (; n && d; n = n->next, d = d->next)
{
/* try to coerce the default value into the attribute's type */
d = pt_semantic_check (parser, d);
if (d == NULL)
{
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
}
else
{
error = ER_GENERIC_ERROR;
}
break;
}
attr_name = n->info.name.original;
if (n->info.name.meta_class == PT_META_ATTR)
{
def_attr = db_get_class_attribute (vclass, attr_name);
}
else
{
def_attr = db_get_attribute (vclass, attr_name);
}
if (!def_attr || !(def_domain = db_attribute_domain (def_attr)))
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
break;
}
db_desired_type = TP_DOMAIN_TYPE (def_domain);
pt_desired_type = (PT_TYPE_ENUM) pt_db_to_type_enum (db_desired_type);
data_type = pt_domain_to_data_type (parser, def_domain);
if (data_type == NULL)
{
PT_ERRORm (parser, n, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
error = ER_FAILED;
break;
}
def_val = d->info.data_default.default_value;
if (d->info.data_default.default_expr_type == DB_DEFAULT_NONE)
{
initial_def_val = parser_copy_tree (parser, def_val);
if (initial_def_val == NULL)
{
error = ER_FAILED;
break;
}
error = pt_coerce_value_for_default_value (parser, def_val, def_val, pt_desired_type, data_type,
d->info.data_default.default_expr_type, true);
if (error != NO_ERROR)
{
if (pt_has_error (parser))
{
/* forget previous one to set the better error */
pt_reset_error (parser);
}
if (error == ER_IT_DATA_OVERFLOW)
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OVERFLOW_COERCING_TO,
pt_short_print (parser, initial_def_val), pt_short_print (parser, data_type));
}
else
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, initial_def_val), pt_short_print (parser, data_type));
}
parser_free_tree (parser, data_type);
break;
}
pt_evaluate_tree (parser, d->info.data_default.default_value, &src_val, 1);
/* Fix CUBRIDSUS-8035. FOR Primary Key situation, we will throw another ERROR in function
* dbt_change_default, so I excluded it from here. */
if (DB_IS_NULL (&src_val) && (def_attr->flags & SM_ATTFLAG_NON_NULL)
&& !(def_attr->flags & SM_ATTFLAG_PRIMARY_KEY))
{
db_value_clear (&src_val);
parser_free_tree (parser, data_type);
ERROR1 (error, ER_CANNOT_HAVE_NOTNULL_DEFAULT_NULL, attr_name);
break;
}
if (n->info.name.meta_class == PT_META_ATTR)
{
error = dbt_change_default (ctemplate, attr_name, 1, &src_val);
}
else
{
error = dbt_change_default (ctemplate, attr_name, 0, &src_val);
}
}
else
{
def_val = pt_semantic_type (parser, def_val, NULL);
if (pt_has_error (parser) || def_val == NULL)
{
parser_free_tree (parser, data_type);
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
break;
}
pt_evaluate_tree_having_serial (parser, def_val, &src_val, 1);
if (pt_has_error (parser))
{
parser_free_tree (parser, data_type);
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
break;
}
temp_val = pt_dbval_to_value (parser, &src_val);
if (temp_val == NULL)
{
parser_free_tree (parser, data_type);
db_value_clear (&src_val);
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
break;
}
error = pt_coerce_value_for_default_value (parser, temp_val, temp_val, pt_desired_type, data_type,
d->info.data_default.default_expr_type, true);
db_value_clear (&src_val);
temp_val->info.value.db_value_is_in_workspace = 0;
parser_free_node (parser, temp_val);
if (error != NO_ERROR)
{
if (pt_has_error (parser))
{
/* forget previous one to set the better error */
pt_reset_error (parser);
}
if (error == ER_IT_DATA_OVERFLOW)
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OVERFLOW_COERCING_TO,
pt_short_print (parser, def_val), pt_short_print (parser, data_type));
}
else
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, def_val), pt_short_print (parser, data_type));
}
parser_free_tree (parser, data_type);
break;
}
pt_get_default_expression_from_data_default_node (parser, d, &default_expr);
smt_set_attribute_default (ctemplate, attr_name, 0, &src_val, &default_expr);
}
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
break;
}
if (error != NO_ERROR)
{
break;
}
parser_free_tree (parser, data_type);
pr_clear_value (&src_val);
pr_clear_value (&dest_val);
}
break;
/* If merely renaming resolution, will be done after switch statement */
case PT_RENAME_RESOLUTION:
break;
case PT_RENAME_ATTR_MTHD:
if (alter->info.alter.alter_clause.rename.old_name)
{
old_name = alter->info.alter.alter_clause.rename.old_name->info.name.original;
}
else
{
old_name = NULL;
}
new_name = alter->info.alter.alter_clause.rename.new_name->info.name.original;
if (alter->info.alter.alter_clause.rename.meta == PT_META_ATTR)
{
class_attr = 1;
}
else
{
class_attr = 0;
}
switch (alter->info.alter.alter_clause.rename.element_type)
{
case PT_ATTRIBUTE:
case PT_METHOD:
error = dbt_rename (ctemplate, old_name, class_attr, new_name);
break;
case PT_FUNCTION_RENAME:
mthd_name = alter->info.alter.alter_clause.rename.mthd_name->info.name.original;
error = dbt_change_method_implementation (ctemplate, mthd_name, class_attr, new_name);
break;
/* the following case is not yet supported, but hey, when it is, there'll code for it :-) */
/* There's code now. this drops the old file name and puts the new file name in its place., I took out the
* class_attr, since for our purpose we don't need it */
case PT_FILE_RENAME:
{
PT_NODE *old_name_node, *new_name_node;
old_name_node = alter->info.alter.alter_clause.rename.old_name;
new_name_node = alter->info.alter.alter_clause.rename.new_name;
old_name = (char *) old_name_node->info.file_path.string->info.value.data_value.str->bytes;
new_name = (char *) new_name_node->info.file_path.string->info.value.data_value.str->bytes;
error = dbt_rename_method_file (ctemplate, old_name, new_name);
}
break;
default:
/* actually, it means that a wrong thing is being renamed, and is really an error condition. */
assert (false);
break;
}
break;
case PT_DROP_CONSTRAINT:
case PT_DROP_FK_CLAUSE:
case PT_DROP_PRIMARY_CLAUSE:
{
SM_CLASS_CONSTRAINT *cons = NULL;
const char *constraint_name = NULL;
if (alter_code == PT_DROP_PRIMARY_CLAUSE)
{
assert (alter->info.alter.constraint_list == NULL);
cons = classobj_find_class_primary_key (ctemplate->current);
if (cons != NULL)
{
assert (cons->type == SM_CONSTRAINT_PRIMARY_KEY);
constraint_name = cons->name;
}
else
{
/* We set a name to print the error message. */
constraint_name = "primary key";
}
}
else
{
assert (alter->info.alter.constraint_list->next == NULL);
assert (alter->info.alter.constraint_list->node_type == PT_NAME);
constraint_name = alter->info.alter.constraint_list->info.name.original;
assert (constraint_name != NULL);
cons = classobj_find_class_index (ctemplate->current, constraint_name);
}
if (cons != NULL)
{
const DB_CONSTRAINT_TYPE constraint_type = db_constraint_type (cons);
if (alter_code == PT_DROP_FK_CLAUSE && constraint_type != DB_CONSTRAINT_FOREIGN_KEY)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE, 1, constraint_name);
error = er_errid ();
}
else
{
if (alter_code == PT_DROP_FK_CLAUSE && prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL)
{
/* We warn the user that dropping a foreign key behaves differently in CUBRID (the associated index
* is also dropped while MySQL's associated index is kept and only the foreign key constraint is
* dropped). This difference is not important enough to be an error but a warning or a notification
* might help. */
er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_SM_FK_MYSQL_DIFFERENT, 0);
}
error = dbt_drop_constraint (ctemplate, constraint_type, constraint_name, NULL, 0);
}
}
else
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_SM_CONSTRAINT_NOT_FOUND, 1, constraint_name);
error = er_errid ();
}
}
break;
case PT_APPLY_PARTITION:
case PT_REMOVE_PARTITION:
case PT_ADD_PARTITION:
case PT_ADD_HASHPARTITION:
case PT_COALESCE_PARTITION:
case PT_REORG_PARTITION:
case PT_DROP_PARTITION:
case PT_ANALYZE_PARTITION:
case PT_PROMOTE_PARTITION:
/* initialize partition alteration context */
pinfo.promoted_count = 0;
pinfo.promoted_names = NULL;
pinfo.root_tmpl = ctemplate;
pinfo.root_op = vclass;
pinfo.keycol[0] = 0;
error = tran_system_savepoint (UNIQUE_PARTITION_SAVEPOINT_ALTER);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
partition_savepoint = true;
error = do_alter_partitioning_pre (parser, alter, &pinfo);
break;
#if defined (ENABLE_RENAME_CONSTRAINT)
case PT_RENAME_CONSTRAINT:
case PT_RENAME_INDEX:
old_name = alter->info.alter.alter_clause.rename.old_name->info.name.original;
new_name = alter->info.alter.alter_clause.rename.new_name->info.name.original;
if (alter->info.alter.alter_clause.rename.element_type == PT_CONSTRAINT_NAME)
{
constraint_family = SM_CONSTRAINT_NAME;
}
else /* if (alter->info.alter.alter_clause. rename.element_type == PT_INDEX_NAME) */
{
constraint_family = SM_INDEX_NAME;
}
error = smt_rename_constraint (ctemplate, old_name, new_name, constraint_family);
break;
#endif
default:
assert (false);
dbt_abort_class (ctemplate);
return error;
}
if (initial_def_val != NULL)
{
parser_free_tree (parser, initial_def_val);
}
/* Process resolution list if appropriate */
if (error == NO_ERROR)
{
if (alter->info.alter.super.resolution_list != NULL && alter->info.alter.code != PT_DROP_RESOLUTION)
{
error = do_add_resolutions (parser, ctemplate, alter->info.alter.super.resolution_list);
}
}
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
if (partition_savepoint)
{
goto alter_partition_fail;
}
return error;
}
vclass = dbt_finish_class (ctemplate);
/* the dbt_finish_class() failed, the template was not freed */
if (vclass == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
dbt_abort_class (ctemplate);
if (partition_savepoint)
{
goto alter_partition_fail;
}
return error;
}
/* If we have an ADD COLUMN x NOT NULL without a default value, the existing rows will be filled with NULL for the
* new column by default. For compatibility with MySQL, we can auto-fill some column types with "hard defaults", like
* 0 for integer types. THIS CAN TAKE A LONG TIME (it runs an UPDATE), and can be turned off by setting
* "add_col_not_null_no_default_behavior" to "cubrid". The parameter is true by default. */
if (alter_code == PT_ADD_ATTR_MTHD)
{
error = do_update_new_notnull_cols_without_default (parser, alter, vclass);
if (error != NO_ERROR)
{
if (error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
}
return error;
}
/*
* if we ADD COLUMN with DEFAULT expression the existing rows will be filled with default expression for the
* new column. That's because we need to set the column value right now (the default expression may be date/time).
*/
error = do_update_new_cols_with_default_expression (parser, alter, vclass);
if (error != NO_ERROR)
{
if (error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ADD_ATTR_MTHD);
}
return error;
}
}
switch (alter_code)
{
case PT_APPLY_PARTITION:
case PT_REMOVE_PARTITION:
case PT_ADD_PARTITION:
case PT_ADD_HASHPARTITION:
case PT_COALESCE_PARTITION:
case PT_REORG_PARTITION:
case PT_DROP_PARTITION:
case PT_ANALYZE_PARTITION:
case PT_PROMOTE_PARTITION:
/* root class template has been edited and finished, update the references in the pinfo object */
pinfo.root_op = vclass;
pinfo.root_tmpl = NULL;
error = do_alter_partitioning_post (parser, alter, &pinfo);
if (pinfo.promoted_names != NULL)
{
/* cleanup promoted names if any */
int i;
for (i = 0; i < pinfo.promoted_count; i++)
{
free_and_init (pinfo.promoted_names[i]);
}
free_and_init (pinfo.promoted_names);
}
if (error != NO_ERROR && error != ER_LK_UNILATERALLY_ABORTED)
{
goto alter_partition_fail;
}
break;
default:
break;
}
return error;
alter_partition_fail:
if (partition_savepoint && error != NO_ERROR && error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_PARTITION_SAVEPOINT_ALTER);
}
return error;
}
/*
* lob_process_dir_add_attr() - This function is called during the execution of
* ALTER TABLE ... ADD COLUMN. If the newly added column is a LOB type,
* it constructs an attribute ID array and triggers the creation of the
* corresponding LOB directory.
* return: Error code
* class_(in): Class information
* old_att_count(in): Number of attributes before adding new attributes
*/
static int
lob_process_dir_add_attr (SM_CLASS * class_, int old_att_count)
{
SM_ATTRIBUTE *attr;
HFID lob_hfid;
int lob_attrid_arr_length = 0;
int *lob_alloc_attrid_arr = NULL;
int lob_local_attrid_arr[2];
int *lob_attrid_arr = NULL;
int error = NO_ERROR;
assert (class_ != NULL);
assert (old_att_count >= 0);
for (int i = old_att_count; i < class_->att_count; i++)
{
attr = &class_->attributes[i];
if (TP_IS_LOB_TYPE (attr->type->id))
{
lob_attrid_arr_length++;
}
}
if (lob_attrid_arr_length == 0)
{
goto end;
}
else if (lob_attrid_arr_length <= 2)
{
lob_attrid_arr = lob_local_attrid_arr;
}
else
{
lob_alloc_attrid_arr = (int *) malloc (sizeof (int) * lob_attrid_arr_length);
if (lob_alloc_attrid_arr == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (int) * lob_attrid_arr_length);
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto end;
}
lob_attrid_arr = lob_alloc_attrid_arr;
}
for (int i = old_att_count, index = 0; i < class_->att_count; i++)
{
attr = &class_->attributes[i];
if (TP_IS_LOB_TYPE (attr->type->id))
{
lob_attrid_arr[index++] = attr->id;
}
}
lob_hfid = class_->header.ch_heap;
error = locator_lob_create_or_remove_dir (NULL, &lob_hfid, lob_attrid_arr, lob_attrid_arr_length);
if (error != NO_ERROR)
{
goto end;
}
end:
free (lob_alloc_attrid_arr);
return error;
}
/*
* lob_process_dir_drop_attr() - This function is called during the execution of
* ALTER TABLE ... DROP COLUMN. If the column being dropped is a LOB type,
* it removes the corresponding LOB directory.
* return: Error code
* class_(in): Class information
* attr_name(in): Name of the attribute to be dropped
*/
static int
lob_process_dir_drop_attr (SM_CLASS * class_, const char *attr_name)
{
SM_ATTRIBUTE attr;
int error = NO_ERROR;
assert (class_ != NULL);
assert (attr_name != NULL);
for (int i = 0; i < class_->att_count; i++)
{
attr = class_->attributes[i];
if (strcmp (attr.header.name, attr_name) == 0)
{
if (TP_IS_LOB_TYPE (attr.type->id))
{
HFID lob_hfid = class_->header.ch_heap;
int lob_attrid_arr[1] = { attr.id };
error = locator_lob_create_or_remove_dir (&lob_hfid, NULL, lob_attrid_arr, 1);
if (error != NO_ERROR)
{
return error;
}
}
break;
}
}
return error;
}
/*
* do_alter_clause_rename_entity() - Executes an ALTER TABLE RENAME TO clause
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_RENAME_ENTITY clause potentially
* followed by the rest of the clauses in the ALTER
* statement.
* Note: The clauses following the PT_RENAME_ENTITY clause will be updated to
* the new name of the class.
*/
static int
do_alter_clause_rename_entity (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error_code = NO_ERROR;
const PT_ALTER_CODE alter_code = alter->info.alter.code;
const char *const old_name = alter->info.alter.entity_name->info.name.original;
const char *const new_name = alter->info.alter.alter_clause.rename.new_name->info.name.original;
PT_NODE *tmp_clause = NULL;
assert (alter_code == PT_RENAME_ENTITY);
assert (alter->info.alter.super.resolution_list == NULL);
error_code = do_rename_internal (old_name, new_name);
if (error_code != NO_ERROR)
{
goto error_exit;
}
error_code = do_recreate_renamed_class_indexes (parser, old_name, new_name);
if (error_code != NO_ERROR)
{
goto error_exit;
}
/* We now need to update the current name of the class for the rest of the ALTER clauses. */
for (tmp_clause = alter->next; tmp_clause != NULL; tmp_clause = tmp_clause->next)
{
parser_free_tree (parser, tmp_clause->info.alter.entity_name);
tmp_clause->info.alter.entity_name = parser_copy_tree (parser, alter->info.alter.alter_clause.rename.new_name);
if (tmp_clause->info.alter.entity_name == NULL)
{
error_code = ER_FAILED;
goto error_exit;
}
}
return error_code;
error_exit:
return error_code;
}
/*
* do_alter_clause_add_index() - Executes an ALTER TABLE ADD INDEX clause
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_ADD_INDEX_CLAUSE clause potentially
* followed by the rest of the clauses in the ALTER
* statement.
* Note: The clauses following the PT_ADD_INDEX_CLAUSE clause are not
* affected in any way.
*/
static int
do_alter_clause_add_index (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error = NO_ERROR;
PT_NODE *create_index = NULL;
assert (alter->info.alter.create_index != NULL);
assert (alter->info.alter.constraint_list == NULL);
assert (alter->info.alter.alter_clause.attr_mthd.attr_def_list == NULL);
assert (alter->info.alter.alter_clause.attr_mthd.mthd_def_list == NULL);
assert (alter->info.alter.alter_clause.attr_mthd.mthd_file_list == NULL);
create_index = alter->info.alter.create_index;
for (; create_index != NULL; create_index = create_index->next)
{
error = do_create_index (parser, create_index);
if (error != NO_ERROR)
{
break;
}
}
return error;
}
/*
* do_alter_clause_drop_index() - Executes an ALTER TABLE DROP INDEX clause
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_DROP_INDEX_CLAUSE clause potentially
* followed by the rest of the clauses in the ALTER
* statement.
* Note: The clauses following the PT_DROP_INDEX_CLAUSE clause are not
* affected in any way.
*/
static int
do_alter_clause_drop_index (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error_code = NO_ERROR;
const PT_ALTER_CODE alter_code = alter->info.alter.code;
DB_OBJECT *obj = NULL;
DB_CONSTRAINT_TYPE index_type;
bool is_reverse;
bool is_unique;
assert (alter_code == PT_DROP_INDEX_CLAUSE);
assert (alter->info.alter.constraint_list != NULL);
assert (alter->info.alter.constraint_list->next == NULL);
assert (alter->info.alter.constraint_list->node_type == PT_NAME);
index_type =
get_reverse_unique_index_type (alter->info.alter.alter_clause.index.reverse,
alter->info.alter.alter_clause.index.unique);
obj = db_find_class (alter->info.alter.entity_name->info.name.original);
if (obj == NULL)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
return error_code;
}
if (index_type == DB_CONSTRAINT_INDEX)
{
error_code =
get_index_type_qualifiers (obj, &is_reverse, &is_unique, alter->info.alter.constraint_list->info.name.original);
if (error_code != NO_ERROR)
{
return error_code;
}
}
else
{
is_reverse = alter->info.alter.alter_clause.index.reverse;
is_unique = alter->info.alter.alter_clause.index.unique;
}
error_code =
create_or_drop_index_helper (parser, alter->info.alter.constraint_list->info.name.original, is_reverse, is_unique,
NULL, obj, DO_INDEX_DROP);
return error_code;
}
/*
* do_alter_change_auto_increment() - Executes an
* ALTER TABLE ... AUTO_INCREMENT = x statement.
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_CHANGE_AUTO_INCREMENT clause.
*/
static int
do_alter_change_auto_increment (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
const char *entity_name = NULL;
DB_OBJECT *class_obj = NULL;
DB_ATTRIBUTE *cur_attr = NULL;
MOP ai_serial = NULL;
int error = NO_ERROR;
int au_save = 0;
entity_name = alter->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
ERROR1 (error, ER_UNEXPECTED, "Expecting a class name.");
goto change_ai_error;
}
class_obj = db_find_class (entity_name);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto change_ai_error;
}
cur_attr = db_get_attributes (class_obj);
/* find the attribute that has auto_increment */
for (cur_attr = db_get_attributes (class_obj); cur_attr != NULL; cur_attr = db_attribute_next (cur_attr))
{
if (cur_attr->auto_increment == NULL)
{
continue;
}
ai_serial = cur_attr->auto_increment;
break;
}
if (ai_serial == NULL)
{
/* we ought to have exactly ONE proper attribute with auto increment */
ERROR0 (error, ER_AUTO_INCREMENT_SINGLE_COL_AMBIGUITY);
goto change_ai_error;
}
AU_DISABLE (au_save);
error =
do_change_auto_increment_serial (parser, ai_serial, alter->info.alter.alter_clause.auto_increment.start_value);
AU_ENABLE (au_save);
return error;
change_ai_error:
return error;
}
/*
* do_alter() -
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of an alter statement
*/
int
do_alter (PARSER_CONTEXT * parser, PT_NODE * alter)
{
int error_code = NO_ERROR;
PT_NODE *crt_clause = NULL;
bool do_semantic_checks = false;
bool do_rollback = false;
CHECK_MODIFICATION_ERROR ();
/* Multiple alter operations in a single statement need to be atomic. */
error_code = tran_system_savepoint (UNIQUE_SAVEPOINT_MULTIPLE_ALTER);
if (error_code != NO_ERROR)
{
goto error_exit;
}
do_rollback = true;
for (crt_clause = alter; crt_clause != NULL; crt_clause = crt_clause->next)
{
PT_NODE *const save_next = crt_clause->next;
const PT_ALTER_CODE alter_code = crt_clause->info.alter.code;
/* The first ALTER clause has already been checked, we call the semantic check starting with the second clause. */
if (do_semantic_checks)
{
PT_NODE *crt_result = NULL;
crt_clause->next = NULL;
crt_result = pt_compile (parser, crt_clause);
crt_clause->next = save_next;
if (!crt_result || pt_has_error (parser))
{
pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, crt_clause);
error_code = er_errid ();
goto error_exit;
}
assert (crt_result == crt_clause);
}
switch (alter_code)
{
case PT_RENAME_ENTITY:
error_code = do_alter_clause_rename_entity (parser, crt_clause);
break;
case PT_ADD_INDEX_CLAUSE:
error_code = do_alter_clause_add_index (parser, crt_clause);
break;
case PT_DROP_INDEX_CLAUSE:
error_code = do_alter_clause_drop_index (parser, crt_clause);
break;
case PT_CHANGE_AUTO_INCREMENT:
error_code = do_alter_change_auto_increment (parser, crt_clause);
break;
case PT_CHANGE_ATTR:
error_code = do_alter_clause_change_attribute (parser, crt_clause);
break;
case PT_CHANGE_OWNER:
error_code = do_alter_change_owner (parser, crt_clause);
break;
case PT_CHANGE_COLLATION:
error_code = do_alter_change_default_cs_coll (parser, crt_clause);
break;
case PT_CHANGE_TABLE_COMMENT:
error_code = do_alter_change_tbl_comment (parser, crt_clause);
break;
case PT_CHANGE_COLUMN_COMMENT:
error_code = do_alter_change_col_comment (parser, crt_clause);
break;
default:
/* This code might not correctly handle a list of ALTER clauses so we keep crt_clause->next to NULL during
* its execution just to be on the safe side. */
crt_clause->next = NULL;
error_code = do_alter_one_clause_with_template (parser, crt_clause);
crt_clause->next = save_next;
}
if (error_code != NO_ERROR)
{
goto error_exit;
}
do_semantic_checks = true;
}
return error_code;
error_exit:
if (do_rollback && error_code != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_MULTIPLE_ALTER);
}
return error_code;
}
/*
* Function Group :
* DO functions for user management
*
*/
#define IS_NAME(n) ((n)->node_type == PT_NAME)
#define IS_STRING(n) ((n)->node_type == PT_VALUE && \
((n)->type_enum == PT_TYPE_VARCHAR || (n)->type_enum == PT_TYPE_CHAR))
#define GET_NAME(n) ((char *) (n)->info.name.original)
#define GET_STRING(n) ((char *) (n)->info.value.data_value.str->bytes)
/*
* do_grant() - Grants priviledges
* return: Error code if grant fails
* parser(in): Parser context
* statement(in): Parse tree of a grant statement
*/
int
do_grant (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
PT_NODE *user, *user_list;
DB_OBJECT *user_obj, *class_mop;
PT_NODE *auth_cmd_list, *auth_list, *auth;
DB_AUTH db_auth;
PT_NODE *spec_list;
PT_NODE *entity_list, *entity;
int grant_option;
bool set_savepoint = false;
CHECK_MODIFICATION_ERROR ();
user_list = statement->info.grant.user_list;
auth_cmd_list = statement->info.grant.auth_cmd_list;
spec_list = statement->info.grant.spec_list;
if (statement->info.grant.grant_option == PT_GRANT_OPTION)
{
grant_option = true;
}
else
{
grant_option = false;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_GRANT_USER);
if (error != NO_ERROR)
{
return error;
}
set_savepoint = true;
for (user = user_list; user != NULL; user = user->next)
{
user_obj = db_find_user (user->info.name.original);
if (user_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
auth_list = auth_cmd_list;
for (auth = auth_list; auth != NULL; auth = auth->next)
{
db_auth = pt_auth_to_db_auth (auth);
if (auth->info.auth_cmd.auth_cmd == PT_EXECUTE_PROCEDURE_PRIV)
{
// NOTE: db_auth is always DB_AUTH_EXECUTE
assert (db_auth == DB_AUTH_EXECUTE);
PT_NODE *p_list = spec_list;
for (PT_NODE * procs = p_list; procs != NULL; procs = procs->next)
{
// [TODO] Resovle user schema name, built-in package name
const char *proc_name = procs->info.name.original;
MOP proc_mop = jsp_find_stored_procedure (proc_name, DB_AUTH_NONE);
if (proc_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
error = db_grant_object (DB_OBJECT_PROCEDURE, user_obj, proc_mop, db_auth, grant_option);
if (error != NO_ERROR)
{
goto end;
}
}
}
else
{
for (PT_NODE * spec = spec_list; spec != NULL; spec = spec->next)
{
entity_list = spec->info.spec.flat_entity_list;
for (entity = entity_list; entity != NULL; entity = entity->next)
{
class_mop = db_find_class (entity->info.name.original);
if (class_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
error = db_grant_object (DB_OBJECT_CLASS, user_obj, class_mop, db_auth, grant_option);
if (error != NO_ERROR)
{
goto end;
}
}
}
}
}
}
end:
if (set_savepoint && error != NO_ERROR && !ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_GRANT_USER);
}
return error;
}
/*
* do_revoke() - Revokes priviledges
* return: Error code if revoke fails
* parser(in): Parser context
* statement(in): Parse tree of a revoke statement
*/
int
do_revoke (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
PT_NODE *user, *user_list;
DB_OBJECT *user_obj, *class_mop;
PT_NODE *auth_cmd_list, *auth_list, *auth;
DB_AUTH db_auth;
PT_NODE *spec_list, *s_list, *spec;
PT_NODE *entity_list, *entity;
bool set_savepoint = false;
CHECK_MODIFICATION_ERROR ();
user_list = statement->info.revoke.user_list;
auth_cmd_list = statement->info.revoke.auth_cmd_list;
spec_list = statement->info.revoke.spec_list;
error = tran_system_savepoint (UNIQUE_SAVEPOINT_REVOKE_USER);
if (error != NO_ERROR)
{
return error;
}
set_savepoint = true;
for (user = user_list; user != NULL; user = user->next)
{
user_obj = db_find_user (user->info.name.original);
if (user_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
auth_list = auth_cmd_list;
for (auth = auth_list; auth != NULL; auth = auth->next)
{
db_auth = pt_auth_to_db_auth (auth);
if (auth->info.auth_cmd.auth_cmd == PT_EXECUTE_PROCEDURE_PRIV)
{
// NOTE: db_auth is always DB_AUTH_EXECUTE
assert (db_auth == DB_AUTH_EXECUTE);
PT_NODE *p_list = spec_list;
for (PT_NODE * procs = p_list; procs != NULL; procs = procs->next)
{
// [TODO] Resovle user schema name, built-in package name
const char *proc_name = procs->info.name.original;
MOP proc_mop = jsp_find_stored_procedure (proc_name, DB_AUTH_NONE);
if (proc_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
// TODO: In CBRD-24912, GRANT/REVOKE for stored procedure is implemented, the following will be processed properly
error = db_revoke_object (DB_OBJECT_PROCEDURE, user_obj, proc_mop, db_auth);
if (error != NO_ERROR)
{
goto end;
}
}
}
else
{
for (PT_NODE * spec = spec_list; spec != NULL; spec = spec->next)
{
entity_list = spec->info.spec.flat_entity_list;
for (entity = entity_list; entity != NULL; entity = entity->next)
{
class_mop = db_find_class (entity->info.name.original);
if (class_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
error = db_revoke_object (DB_OBJECT_CLASS, user_obj, class_mop, db_auth);
if (error != NO_ERROR)
{
goto end;
}
}
}
}
}
}
end:
if (set_savepoint && error != NO_ERROR && !ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_REVOKE_USER);
}
return error;
}
/*
* do_create_user() - Create a user
* return: Error code if creation fails
* parser(in): Parser context
* statement(in): Parse tree of a create user statement
*/
int
do_create_user (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *user, *group, *member;
int exists;
PT_NODE *node, *node2;
const char *user_name, *password, *comment;
const char *group_name, *member_name;
bool set_savepoint = false;
CHECK_MODIFICATION_ERROR ();
if (parser == NULL || statement == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
node = statement->info.create_user.user_name;
if (node == NULL || node->node_type != PT_NAME || node->info.name.original == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_MISSING_OR_INVALID_USER, 0);
return ER_AU_MISSING_OR_INVALID_USER;
}
user_name = node->info.name.original;
if (user_name == NULL)
{
error = ER_AU_INVALID_USER;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, "");
return error;
}
/* first, check if user_name is in group or member clause */
for (node = statement->info.create_user.groups; node != NULL; node = node->next)
{
if (node == NULL || node->node_type != PT_NAME || node->info.name.original == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_MISSING_OR_INVALID_USER, 0);
return ER_AU_MISSING_OR_INVALID_USER;
}
group_name = node->info.name.original;
if (intl_identifier_casecmp (user_name, group_name) == 0)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_MEMBER_CAUSES_CYCLES, 0);
return ER_AU_MEMBER_CAUSES_CYCLES;
}
}
for (node = statement->info.create_user.members; node != NULL; node = node->next)
{
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (member_name == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
if (intl_identifier_casecmp (user_name, member_name) == 0
|| intl_identifier_casecmp (member_name, AU_PUBLIC_USER_NAME) == 0)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_MEMBER_CAUSES_CYCLES, 0);
return ER_AU_MEMBER_CAUSES_CYCLES;
}
}
/* second, check if group name is in member clause */
for (node = statement->info.create_user.groups; node != NULL; node = node->next)
{
group_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (group_name == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
for (node2 = statement->info.create_user.members; node2 != NULL; node2 = node2->next)
{
member_name = (node2 && IS_NAME (node2)) ? GET_NAME (node2) : NULL;
if (member_name == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
if (intl_identifier_casecmp (group_name, member_name) == 0)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_AU_MEMBER_CAUSES_CYCLES, 0);
return ER_AU_MEMBER_CAUSES_CYCLES;
}
}
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CREATE_USER_ENTITY);
if (error != NO_ERROR)
{
return error;
}
set_savepoint = true;
exists = 0;
user = db_add_user (user_name, &exists);
if (user == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
else if (exists)
{
error = ER_AU_USER_EXISTS;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, user_name);
goto end;
}
/* Now treats optional password, group, member and comment of the created user */
/* password */
node = statement->info.create_user.password;
password = (node && IS_STRING (node)) ? GET_STRING (node) : NULL;
if (password != NULL)
{
error = au_set_password_encrypt (user, password);
if (error != NO_ERROR)
{
goto end;
}
}
/* group */
node = statement->info.create_user.groups;
group_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (group_name != NULL)
{
do
{
group = db_find_user (group_name);
if (group == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = db_add_member (group, user);
}
if (error != NO_ERROR)
{
goto end;
}
node = node->next;
group_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
}
while (group_name != NULL);
}
/* member */
node = statement->info.create_user.members;
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (member_name != NULL)
{
do
{
member = db_find_user (member_name);
if (member == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = db_add_member (user, member);
}
if (error != NO_ERROR)
{
goto end;
}
node = node->next;
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
}
while (member_name != NULL);
}
/* comment */
node = statement->info.create_user.comment;
if (node != NULL)
{
assert (node->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (node);
error = au_set_user_comment (user, comment);
if (error != NO_ERROR)
{
goto end;
}
}
// for syncronizing created_time and updated_time
error = au_set_user_timestamps (user);
if (error != NO_ERROR)
{
goto end;
}
end:
if (set_savepoint && error != NO_ERROR && !ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CREATE_USER_ENTITY);
}
return error;
}
/*
* do_drop_user() - Drop the user
* return: Error code if dropping fails
* parser(in): Parser context
* statement(in): Parse tree of a drop user statement
*/
int
do_drop_user (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *user = NULL;
PT_NODE *node;
const char *user_name;
bool set_savepoint = false;
CHECK_MODIFICATION_ERROR ();
if (parser == NULL || statement == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
node = statement->info.create_user.user_name;
user_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (user_name == NULL)
{
error = ER_AU_INVALID_USER;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, "");
return error;
}
error = db_find_user_to_drop (user_name, &user);
if (error != NO_ERROR)
{
return error;
}
assert (user != NULL);
error = tran_system_savepoint (UNIQUE_SAVEPOINT_DROP_USER_ENTITY);
if (error != NO_ERROR)
{
return error;
}
set_savepoint = true;
error = db_drop_user (user);
if (set_savepoint && error != NO_ERROR && !ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_DROP_USER_ENTITY);
}
return error;
}
/*
* do_alter_user() - Change the user's password
* return: Error code if alter fails
* parser(in): Parser context
* statement(in): Parse tree of an alter statement
*/
int
do_alter_user (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *user, *member;
PT_NODE *node;
const PT_ALTER_CODE alter_user_code = statement->info.alter_user.code;
const char *user_name, *password, *comment;
const char *member_name;
bool set_savepoint = false;
CHECK_MODIFICATION_ERROR ();
if (parser == NULL || statement == NULL)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
return ER_OBJ_INVALID_ARGUMENTS;
}
node = statement->info.alter_user.user_name;
user_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (user_name == NULL)
{
error = ER_AU_INVALID_USER;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, "");
return error;
}
user = db_find_user (user_name);
if (user == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
return error;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_USER_ENTITY);
if (error != NO_ERROR)
{
return error;
}
set_savepoint = true;
/*
* here, both password and comment are optional,
* either password or comment shall exist,
* csql_grammar denies the error case with the missing of both.
*/
/* password */
node = statement->info.alter_user.password;
if (node != NULL)
{
password = IS_STRING (node) ? GET_STRING (node) : NULL;
error = au_set_password_encrypt (user, password);
if (error != NO_ERROR)
{
goto end;
}
}
/* member */
node = statement->info.alter_user.members;
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
if (member_name != NULL)
{
if (!ws_is_same_object (user, Au_user) && !au_is_dba_group_member (Au_user))
{
error = ER_AU_NOT_OWNER;
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 1, MSGCAT_GET_GLOSSARY_MSG (MSGCAT_GLOSSARY_CLASS));
goto end;
}
switch (alter_user_code)
{
case PT_ADD_MEMBERS:
do
{
member = db_find_user (member_name);
if (member == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = db_add_member (user, member);
}
if (error != NO_ERROR)
{
goto end;
}
node = node->next;
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
}
while (member_name != NULL);
break;
case PT_DROP_MEMBERS:
do
{
member = db_find_user (member_name);
if (member == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = db_drop_member (user, member);
}
if (error != NO_ERROR)
{
goto end;
}
node = node->next;
member_name = (node && IS_NAME (node)) ? GET_NAME (node) : NULL;
}
while (member_name != NULL);
break;
default:
/*
* code shall be one of the above 2 types, otherwise it's an error.
*/
assert (false);
break;
}
}
/* comment */
node = statement->info.alter_user.comment;
if (node != NULL)
{
assert (node->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (node);
error = au_set_user_comment (user, comment);
if (error != NO_ERROR)
{
goto end;
}
}
/*
* Timestamp already updated during add/drop members (member_name != NULL)
* in db_add_member() or db_drop_member(); update only for password or comment changes.
*/
if (statement->info.alter_user.members == NULL)
{
error = au_update_user_timestamp (user);
if (error != NO_ERROR)
{
goto end;
}
}
end:
if (set_savepoint && error != NO_ERROR && !ER_IS_ABORTED_DUE_TO_DEADLOCK (error))
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_USER_ENTITY);
}
return error;
}
/*
* Function Group :
* Code for dropping a Classes by Parse Tree descriptions.
*
*/
/*
* drop_class_name() - This static routine drops a class by name.
* return: Error code
* name(in): Class name to drop
* is_cascade_constraints(in): whether drop relative FK constraints
*/
static int
drop_class_name (const char *name, bool is_cascade_constraints)
{
DB_OBJECT *class_mop;
class_mop = db_find_class (name);
if (class_mop)
{
return db_drop_class_ex (class_mop, is_cascade_constraints);
}
else
{
/* if class is null, return the global error. */
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
/*
* do_drop() - Drops a vclass, class
* return: Error code if a vclass is not deleted.
* parser(in): Parser context
* statement(in/out): Parse tree of a drop statement
*/
int
do_drop (PARSER_CONTEXT * parser, PT_NODE * statement)
{
int error = NO_ERROR;
PT_NODE *entity_spec_list, *entity_spec;
PT_NODE *entity;
PT_NODE *entity_list;
CHECK_MODIFICATION_ERROR ();
/* partitioned sub-class check */
entity_spec_list = statement->info.drop.spec_list;
for (entity_spec = entity_spec_list; entity_spec != NULL; entity_spec = entity_spec->next)
{
entity_list = entity_spec->info.spec.flat_entity_list;
for (entity = entity_list; entity != NULL; entity = entity->next)
{
if (do_is_partitioned_subclass (NULL, entity->info.name.original, NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_PARTITION_REQUEST, 0);
return er_errid ();
}
}
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_DROP_ENTITY);
if (error != NO_ERROR)
{
return error;
}
entity_spec_list = statement->info.drop.spec_list;
for (entity_spec = entity_spec_list; entity_spec != NULL; entity_spec = entity_spec->next)
{
entity_list = entity_spec->info.spec.flat_entity_list;
for (entity = entity_list; entity != NULL; entity = entity->next)
{
error = drop_class_name (entity->info.name.original, statement->info.drop.is_cascade_constraints);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
return error;
error_exit:
if (error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_DROP_ENTITY);
}
return error;
}
/*
* update_locksets_for_multiple_rename() - Adds a class name to one of the two
* sets: either names to be reserved
* or classes to be locked
* return: Error code
* class_name(in): A class name involved in a rename operation
* num_mops(in/out): The number of MOPs
* mop_set(in/out): The MOPs to lock before the rename operation
* num_names(in/out): The number of class names
* name_set(in/out): The class names to reserve before the rename operation
* error_on_misssing_class(in/out): Whether to return an error if a class
* with the given class_name is not found
*/
int
update_locksets_for_multiple_rename (const char *class_name, int *num_mops, MOP * mop_set, int *num_names,
char **name_set, bool error_on_misssing_class)
{
DB_OBJECT *class_mop = NULL;
char realname[SM_MAX_IDENTIFIER_LENGTH];
int i = 0;
sm_downcase_name (class_name, realname, SM_MAX_IDENTIFIER_LENGTH);
class_mop = db_find_class (realname);
if (class_mop == NULL && error_on_misssing_class)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (class_mop != NULL)
{
/* Classes that exist are locked. */
/* Duplicates are harmless; they are handled by locator_fetch_set () anyway. */
mop_set[*num_mops] = class_mop;
++(*num_mops);
}
else
{
/* Class names that don't yet exist are reserved. */
for (i = 0; i < *num_names; ++i)
{
if (intl_identifier_casecmp (name_set[i], realname) == 0)
{
/* The class name is used more than once, we ignore its current occurence. */
return NO_ERROR;
}
}
name_set[*num_names] = strdup (realname);
if (name_set[*num_names] == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(strlen (realname) + 1) * sizeof (char));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
++(*num_names);
}
return NO_ERROR;
}
/*
* acquire_locks_for_multiple_rename() - Performs the necessary locking for an
* atomic multiple rename operation
* return: Error code
* statement(in): Parse tree of a rename statement
*
* Note: We need to lock all the classes and vclasses involved in the rename
* operation. When doing multiple renames we preventively lock all the
* names involved in the rename operation. For statements such as:
* RENAME A to tmp, B to A, tmp to B;
* "A" and "B" will be exclusively locked (locator_fetch_set ())
* and the name "tmp" will be reserved for renaming operations
* (locator_reserve_class_names ()).
*/
int
acquire_locks_for_multiple_rename (const PT_NODE * statement)
{
int error = NO_ERROR;
const PT_NODE *current_rename = NULL;
int num_rename = 0;
int num_mops = 0;
MOP *mop_set = NULL;
int num_names = 0;
char **name_set = NULL;
OID *oid_set = NULL;
MOBJ fetch_result = NULL;
LC_FIND_CLASSNAME reserve_result = LC_CLASSNAME_ERROR;
int i = 0;
num_rename = pt_length_of_list (statement);
mop_set = (MOP *) malloc (2 * num_rename * sizeof (MOP));
if (mop_set == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, 2 * num_rename * sizeof (MOP));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
num_mops = 0;
name_set = (char **) malloc (2 * num_rename * sizeof (char *));
if (name_set == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, 2 * num_rename * sizeof (char *));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
num_names = 0;
for (current_rename = statement; current_rename != NULL; current_rename = current_rename->next)
{
const bool is_first_rename = current_rename == statement ? true : false;
const char *old_name = current_rename->info.rename.old_name->info.name.original;
const char *new_name = current_rename->info.rename.new_name->info.name.original;
bool found = false;
for (i = 0; i < num_names; i++)
{
/*
* Check if old_name to be changed next is in the list of new_name that has been changed before.
* - e.g. rename table a as b, B as c, C as a;
* 1. dba.b vs dba.B
* 2. dba.b vs dba.C, dba.c vs dba.C
*
* ERROR: Unknown class "b".
*
* Changed comparison function to pt_str_compare() function to be case insensitive.
*/
if (pt_str_compare (name_set[i], old_name, CASE_INSENSITIVE) == 0)
{
found = true;
break;
}
}
if (!found)
{
error = update_locksets_for_multiple_rename (old_name, &num_mops, mop_set, &num_names, name_set, true);
if (error != NO_ERROR)
{
goto error_exit;
}
if (is_first_rename)
{
/* We have made sure the first class to be renamed can be locked. */
assert (num_mops == 1);
}
}
error = update_locksets_for_multiple_rename (new_name, &num_mops, mop_set, &num_names, name_set, false);
if (error != NO_ERROR)
{
goto error_exit;
}
if (is_first_rename && num_names != 1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_LC_CLASSNAME_EXIST, 1, new_name);
error = ER_LC_CLASSNAME_EXIST;
goto error_exit;
}
/* We have made sure the first name to be used can be reserved. */
}
assert (num_mops != 0 && num_names != 0);
fetch_result = locator_fetch_set (num_mops, mop_set, DB_FETCH_WRITE, DB_FETCH_WRITE, 1);
if (fetch_result == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_GET_LOCK, 0);
error = ER_CANNOT_GET_LOCK;
goto error_exit;
}
oid_set = (OID *) malloc (num_names * sizeof (OID));
if (oid_set == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, num_names * sizeof (OID));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
for (i = 0; i < num_names; ++i)
{
/* Each reserved name will point to the OID of the first class to be renamed. This is ok as the associated
* transient table entries will only be used for the multiple rename operation. */
COPY_OID (&oid_set[i], ws_oid (mop_set[0]));
}
reserve_result = locator_reserve_class_names (num_names, (const char **) name_set, oid_set);
if (reserve_result != LC_CLASSNAME_RESERVED)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_GET_LOCK, 0);
error = ER_CANNOT_GET_LOCK;
goto error_exit;
}
error_exit:
if (oid_set != NULL)
{
assert (num_names > 0);
free_and_init (oid_set);
}
if (name_set != NULL)
{
for (i = 0; i < num_names; ++i)
{
assert (name_set[i] != NULL);
free_and_init (name_set[i]);
}
free_and_init (name_set);
}
if (mop_set != NULL)
{
free_and_init (mop_set);
}
return error;
}
/*
* do_rename() - Renames several vclasses or classes
* return: Error code
* parser(in): Parser context
* statement(in): Parse tree of a rename statement
*/
int
do_rename (PARSER_CONTEXT * parser, PT_NODE * statement)
{
int error = NO_ERROR;
const PT_NODE *current_rename = NULL;
CHECK_MODIFICATION_ERROR ();
/* Renaming operations in a single statement need to be atomic. */
error = tran_system_savepoint (UNIQUE_SAVEPOINT_RENAME);
if (error != NO_ERROR)
{
return error;
}
if (statement->next != NULL)
{
error = acquire_locks_for_multiple_rename (statement);
if (error != NO_ERROR)
{
return error;
}
}
for (current_rename = statement; current_rename != NULL; current_rename = current_rename->next)
{
const char *old_name = current_rename->info.rename.old_name->info.name.original;
const char *new_name = current_rename->info.rename.new_name->info.name.original;
/* We cannot change the schema of a class by using synonym names. */
if (db_find_synonym (old_name) != NULL)
{
PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, old_name);
goto error_exit;
}
else
{
/* db_find_synonym () == NULL */
ASSERT_ERROR_AND_SET (error);
if (er_errid () == ER_SYNONYM_NOT_EXIST)
{
er_clear ();
error = NO_ERROR;
}
else
{
goto error_exit;
}
}
const char *old_qualifier_name = pt_get_qualifier_name (parser, current_rename->info.rename.old_name);
const char *new_qualifier_name = pt_get_qualifier_name (parser, current_rename->info.rename.new_name);
if (old_qualifier_name && new_qualifier_name
&& intl_identifier_casecmp (old_qualifier_name, new_qualifier_name) != 0)
{
ERROR_SET_ERROR (error, ER_SM_RENAME_CANT_ALTER_OWNER);
goto error_exit;
}
error = do_rename_internal (old_name, new_name);
if (error != NO_ERROR)
{
goto error_exit;
}
error = do_recreate_renamed_class_indexes (parser, old_name, new_name);
if (error != NO_ERROR)
{
goto error_exit;
}
}
return error;
error_exit:
if (error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_RENAME);
}
return error;
}
static int
do_rename_internal (const char *const old_name, const char *const new_name)
{
DB_OBJECT *old_class = NULL;
if (do_is_partitioned_subclass (NULL, old_name, NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_PARTITION_REQUEST, 0);
return er_errid ();
}
old_class = db_find_class (old_name);
if (old_class == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
return db_rename_class (old_class, new_name);
}
/*
* Function Group :
* Parse tree to index commands translation.
*
*/
static DB_CONSTRAINT_TYPE
get_reverse_unique_index_type (const bool is_reverse, const bool is_unique)
{
if (is_unique)
{
return is_reverse ? DB_CONSTRAINT_REVERSE_UNIQUE : DB_CONSTRAINT_UNIQUE;
}
else
{
return is_reverse ? DB_CONSTRAINT_REVERSE_INDEX : DB_CONSTRAINT_INDEX;
}
}
/*
* create_or_drop_index_helper()
* return: Error code
* parser(in): Parser context
* constraint_name(in): If NULL the default constraint name is used;
* column_names must be non-NULL in this case.
* is_reverse(in):
* is_unique(in):
* idx_info(in): NULL if dropping a index
* obj(in): Class object
* do_index(in) : The operation to be performed (creating or dropping)
*/
static int
create_or_drop_index_helper (PARSER_CONTEXT * parser, const char *const constraint_name, const bool is_reverse,
const bool is_unique, const PT_INDEX_INFO * idx_info, DB_OBJECT * const obj,
DO_INDEX do_index)
{
int error = NO_ERROR;
int nnames = 0;
DB_CONSTRAINT_TYPE ctype = DB_CONSTRAINT_NONE;
char **attnames = NULL;
int *asc_desc = NULL;
int *attrs_prefix_length = NULL;
char *cname = NULL;
bool free_packing_buff = false;
PRED_EXPR_WITH_CONTEXT *filter_predicate = NULL;
SM_PREDICATE_INFO pred_index_info = { NULL, NULL, 0, NULL, 0 };
SM_PREDICATE_INFO *p_pred_index_info = NULL;
SM_FUNCTION_INFO *func_index_info = NULL;
int is_partition = DB_NOT_PARTITIONED_CLASS;
error = sm_partitioned_class_type (obj, &is_partition, NULL, NULL);
if (error != NO_ERROR)
{
return error;
}
if (is_partition == DB_PARTITION_CLASS)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, 0);
return ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
}
ctype = get_reverse_unique_index_type (is_reverse, is_unique);
char *attname_tmp = NULL;
if (do_index != DO_INDEX_CREATE)
{
assert (constraint_name != NULL);
nnames = 0;
attnames = &attname_tmp;
attnames[0] = NULL;
}
else
{
assert (idx_info);
nnames = pt_length_of_list (idx_info->column_names);
if (nnames == 1 && idx_info->prefix_length)
{
if (idx_info->column_names->info.sort_spec.expr)
{
char const *colname = idx_info->column_names->info.sort_spec.expr->info.name.original;
if (colname && (sm_att_unique_constrained (obj, colname) || sm_att_fk_constrained (obj, colname)))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_INDEX_PREFIX_LENGTH_ON_UNIQUE_FOREIGN, 0);
return ER_SM_INDEX_PREFIX_LENGTH_ON_UNIQUE_FOREIGN;
}
}
}
bool has_deduplicate_key_col = false;
// Class or shared attributes are not considered. These are not indexed columns.
// Also, The prefix index is also not supported.(The prefix index will be deprecated.)
if (ctype == DB_CONSTRAINT_INDEX || ctype == DB_CONSTRAINT_REVERSE_INDEX)
{
int param_dedup_level = prm_get_integer_value (PRM_ID_DEDUPLICATE_KEY_LEVEL);
if (param_dedup_level == DEDUPLICATE_ABSOLUTE_DISABLE)
{
((PT_INDEX_INFO *) idx_info)->deduplicate_level = DEDUPLICATE_KEY_LEVEL_OFF;
}
else
{
if (idx_info->deduplicate_level == DEDUPLICATE_OPTION_AUTO)
{
PT_INDEX_INFO *t_info = (PT_INDEX_INFO *) idx_info;
t_info->deduplicate_level = param_dedup_level;
}
if ((idx_info->deduplicate_level != DEDUPLICATE_KEY_LEVEL_OFF) && (idx_info->prefix_length == NULL))
{
has_deduplicate_key_col = true;
nnames++;
}
}
}
attnames = (char **) malloc ((nnames + 1) * sizeof (const char *));
if (attnames == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (nnames + 1) * sizeof (const char *));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
asc_desc = (int *) malloc ((nnames) * sizeof (int));
if (asc_desc == NULL)
{
free_and_init (attnames);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, nnames * sizeof (int));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
attrs_prefix_length = (int *) malloc ((nnames) * sizeof (int));
if (attrs_prefix_length == NULL)
{
free_and_init (attnames);
free_and_init (asc_desc);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, nnames * sizeof (int));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
int i = 0;
const PT_NODE *c = idx_info->column_names;
while (c != NULL)
{
asc_desc[i] = c->info.sort_spec.asc_or_desc == PT_ASC ? 0 : 1;
/* column name node */
attnames[i] = (char *) c->info.sort_spec.expr->info.name.original;
attrs_prefix_length[i] = -1;
i++;
c = c->next;
}
if (has_deduplicate_key_col)
{
nnames--; // get count of real columns, except hidden column
}
attnames[i] = NULL;
if (nnames == 1 && idx_info->prefix_length)
{
attrs_prefix_length[0] = idx_info->prefix_length->info.value.data_value.i;
}
if (idx_info->function_expr)
{
pt_enter_packing_buf ();
free_packing_buff = true;
func_index_info =
pt_node_to_function_index (parser, idx_info->indexed_class, idx_info->function_expr, do_index);
if (func_index_info == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_FUNCTION_INFO));
error = ER_FAILED;
goto end;
}
else
{
func_index_info->col_id = idx_info->func_pos;
func_index_info->attr_index_start = nnames - idx_info->func_no_args;
}
}
if (has_deduplicate_key_col)
{
SM_CLASS *class_ = NULL;
assert ((ctype == DB_CONSTRAINT_INDEX) || (ctype == DB_CONSTRAINT_REVERSE_INDEX));
error = au_fetch_class (obj, &class_, AU_FETCH_READ, AU_INDEX);
if (error != NO_ERROR)
{
goto end;
}
if (class_->constraints == NULL
|| !classobj_check_attr_in_unique_constraint (class_->constraints, attnames, func_index_info))
{
dk_create_index_level_adjust (idx_info, attnames, asc_desc, attrs_prefix_length, func_index_info,
nnames, DB_IS_CONSTRAINT_REVERSE_INDEX_FAMILY (ctype));
}
}
}
cname = sm_produce_constraint_name (sm_get_ch_name (obj), ctype, (const char **) attnames, asc_desc, constraint_name);
if (cname == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else if (do_index == DO_INDEX_CREATE)
{
if (idx_info->where)
{
PARSER_VARCHAR *filter_expr = NULL;
unsigned int save_custom;
/* free at parser_free_parser */
/* make sure paren_type is 0 so parenthesis are not printed */
idx_info->where->info.expr.paren_type = 0;
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
filter_expr = pt_print_bytes ((PARSER_CONTEXT *) parser, (PT_NODE *) idx_info->where);
parser->custom_print = save_custom;
if (filter_expr)
{
pred_index_info.pred_string = (char *) filter_expr->bytes;
if (strlen (pred_index_info.pred_string) > MAX_FILTER_PREDICATE_STRING_LENGTH)
{
error = ER_SM_INVALID_FILTER_PREDICATE_LENGTH;
PT_ERRORmf ((PARSER_CONTEXT *) parser, idx_info->where, MSGCAT_SET_ERROR,
-(ER_SM_INVALID_FILTER_PREDICATE_LENGTH), MAX_FILTER_PREDICATE_STRING_LENGTH);
goto end;
}
}
pt_enter_packing_buf ();
free_packing_buff = true;
filter_predicate =
pt_to_pred_with_context ((PARSER_CONTEXT *) parser, (PT_NODE *) idx_info->where,
(PT_NODE *) idx_info->indexed_class);
if (filter_predicate)
{
error =
xts_map_filter_pred_to_stream (filter_predicate, &(pred_index_info.pred_stream),
&(pred_index_info.pred_stream_size));
if (error != NO_ERROR)
{
PT_ERRORm ((PARSER_CONTEXT *) parser, idx_info->where, MSGCAT_SET_PARSER_RUNTIME,
MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
goto end;
}
pred_index_info.att_ids = filter_predicate->attrids_pred;
pred_index_info.num_attrs = filter_predicate->num_attrs_pred;
p_pred_index_info = &pred_index_info;
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
}
assert (idx_info->index_status != SM_NO_INDEX);
const char *comment_str = NULL;
if (idx_info->comment != NULL)
{
assert (idx_info->comment->node_type == PT_VALUE);
comment_str = (char *) PT_VALUE_GET_BYTES (idx_info->comment);
}
error = sm_add_constraint (obj, ctype, cname, (const char **) attnames, asc_desc, attrs_prefix_length, false,
p_pred_index_info, func_index_info, comment_str, idx_info->index_status);
}
else
{
assert (do_index == DO_INDEX_DROP);
bool mysql_index_name = false;
if (prm_get_integer_value (PRM_ID_COMPAT_MODE) == COMPAT_MYSQL && ctype == DB_CONSTRAINT_INDEX)
{
mysql_index_name = true;
}
error = sm_drop_constraint (obj, ctype, cname, (const char **) attnames, false, mysql_index_name);
}
end:
/* free function index info */
if (func_index_info)
{
sm_free_function_index_info (func_index_info);
db_ws_free (func_index_info);
func_index_info = NULL;
}
/* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
if (pred_index_info.pred_stream)
{
free_and_init (pred_index_info.pred_stream);
}
if (free_packing_buff)
{
/* mark the end of another level of xasl packing */
pt_exit_packing_buf ();
}
if (attnames != &attname_tmp)
{
free_and_init (attnames);
}
free_and_init (asc_desc);
if (attrs_prefix_length)
{
free_and_init (attrs_prefix_length);
}
if (cname != NULL)
{
free_and_init (cname);
}
return error;
}
/*
* do_create_index() - Creates an index
* return: Error code if it fails
* parser(in): Parser context
* statement(in) : Parse tree of a create index statement
*/
int
do_create_index (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
PT_NODE *cls;
DB_OBJECT *obj;
const char *index_name = NULL;
int error = NO_ERROR;
CHECK_MODIFICATION_ERROR ();
/* class should be already available */
assert (statement->info.index.indexed_class);
cls = statement->info.index.indexed_class->info.spec.entity_name;
obj = db_find_class (cls->info.name.original);
if (obj == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
if (statement->info.index.index_status == SM_ONLINE_INDEX_BUILDING_IN_PROGRESS)
{
ib_thread_count = statement->info.index.ib_threads;
}
error = create_or_drop_index_helper (parser, index_name, statement->info.index.reverse, statement->info.index.unique,
&statement->info.index, obj, DO_INDEX_CREATE);
return error;
}
/*
* do_drop_index() - Drops an index on a class.
* return: Error code if it fails
* parser(in) : Parser context
* statement(in): Parse tree of a drop index statement
*/
int
do_drop_index (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
PT_NODE *cls = NULL;
DB_OBJECT *obj = NULL;
const char *index_name = NULL;
int error_code = NO_ERROR;
const char *class_name = NULL;
DB_CONSTRAINT_TYPE index_type;
bool is_reverse;
bool is_unique;
CHECK_MODIFICATION_ERROR ();
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
if (index_name == NULL)
{
error_code = ER_SM_INVALID_DEF_CONSTRAINT_NAME_PARAMS;
return error_code;
}
if (statement->info.index.indexed_class)
{
cls = statement->info.index.indexed_class->info.spec.flat_entity_list;
}
assert (cls != NULL);
index_type = get_reverse_unique_index_type (statement->info.index.reverse, statement->info.index.unique);
class_name = cls->info.name.resolved;
obj = db_find_class (class_name);
if (obj == NULL)
{
assert (er_errid () != NO_ERROR);
error_code = er_errid ();
return error_code;
}
if (index_type == DB_CONSTRAINT_INDEX)
{
error_code = get_index_type_qualifiers (obj, &is_reverse, &is_unique, index_name);
if (error_code != NO_ERROR)
{
return error_code;
}
}
else
{
is_reverse = statement->info.index.reverse;
is_unique = statement->info.index.unique;
}
error_code = create_or_drop_index_helper (parser, index_name, is_reverse, is_unique, NULL, obj, DO_INDEX_DROP);
return error_code;
}
/*
* do_alter_index_rebuild() - Alters an index on a class (drop and create).
* INDEX REBUILD statement ignores any type of the
* qualifier, column, and filter predicate (filtered
* index). The purpose of this feature is that
* reconstructing the corrupted index or improving
* the efficiency of indexes. For the backward
* compatibility, this function supports the
* previous grammar.
* return: Error code if it fails
* parser(in): Parser context
* statement(in): Parse tree of a alter index statement
*/
static int
do_alter_index_rebuild (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *obj;
PT_NODE *cls = NULL;
int i, nnames;
DB_CONSTRAINT_TYPE ctype, original_ctype;
char **attnames = NULL;
int *asc_desc = NULL;
int *attrs_prefix_length = NULL;
SM_CLASS *smcls;
SM_CLASS_CONSTRAINT *idx = NULL;
SM_ATTRIBUTE **attp;
const char *index_name = NULL;
bool free_pred_string = false;
bool free_packing_buff = false;
SM_FUNCTION_INFO *func_index_info = NULL;
SM_PREDICATE_INFO pred_index_info = { NULL, NULL, 0, NULL, 0 };
SM_PREDICATE_INFO *p_pred_index_info = NULL;
const char *class_name = NULL;
const char *comment_str = NULL;
bool do_rollback = false;
SM_INDEX_STATUS saved_index_status = SM_NORMAL_INDEX;
/* TODO refactor this code, the code in create_or_drop_index_helper and the code in do_drop_index in order to remove
* duplicate code */
CHECK_MODIFICATION_ERROR ();
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
assert (index_name != NULL);
if (statement->info.index.indexed_class)
{
cls = statement->info.index.indexed_class->info.spec.flat_entity_list;
}
assert (cls != NULL);
class_name = cls->info.name.resolved;
obj = db_find_class (class_name);
if (obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
if (au_fetch_class (obj, &smcls, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
idx = classobj_find_class_index (smcls, index_name);
if (idx == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_NO_INDEX, 1, index_name);
error = ER_SM_NO_INDEX;
goto error_exit;
}
saved_index_status = idx->index_status;
if (statement->info.index.comment != NULL)
{
assert (statement->info.index.comment->node_type == PT_VALUE);
comment_str = (char *) PT_VALUE_GET_BYTES (statement->info.index.comment);
}
/* check the index type */
ctype = get_reverse_unique_index_type (statement->info.index.reverse, statement->info.index.unique);
original_ctype = db_constraint_type (idx);
if (ctype != original_ctype)
{
er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE, 1, index_name);
}
/* get attributes of the index */
attp = idx->attributes;
if (attp == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ATTRIBUTE, 1, "unknown");
error = ER_OBJ_INVALID_ATTRIBUTE;
goto error_exit;
}
nnames = 0;
while (*attp++)
{
nnames++;
}
attnames = (char **) malloc ((nnames + 1) * sizeof (const char *));
if (attnames == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (nnames + 1) * sizeof (const char *));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
for (i = 0, attp = idx->attributes; *attp; i++, attp++)
{
attnames[i] = strdup ((*attp)->header.name);
if (attnames[i] == NULL)
{
int j;
for (j = 0; j < i; ++j)
{
free_and_init (attnames[j]);
}
free_and_init (attnames);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(strlen ((*attp)->header.name) + 1) * sizeof (char));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
}
attnames[i] = NULL;
if (idx->asc_desc)
{
asc_desc = (int *) malloc ((nnames) * sizeof (int));
if (asc_desc == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, nnames * sizeof (int));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
for (i = 0; i < nnames; i++)
{
asc_desc[i] = idx->asc_desc[i];
}
}
if (original_ctype == DB_CONSTRAINT_INDEX)
{
assert (idx->attrs_prefix_length);
attrs_prefix_length = (int *) malloc ((nnames) * sizeof (int));
if (attrs_prefix_length == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, nnames * sizeof (int));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
for (i = 0; i < nnames; i++)
{
attrs_prefix_length[i] = idx->attrs_prefix_length[i];
}
}
if (idx->filter_predicate)
{
int pred_str_len;
assert (idx->filter_predicate->pred_string != NULL && idx->filter_predicate->pred_stream != NULL);
pred_str_len = strlen (idx->filter_predicate->pred_string);
pred_index_info.pred_string = strdup (idx->filter_predicate->pred_string);
if (pred_index_info.pred_string == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(strlen (idx->filter_predicate->pred_string) + 1) * sizeof (char));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
free_pred_string = true;
pred_index_info.pred_stream = (char *) malloc (idx->filter_predicate->pred_stream_size * sizeof (char));
if (pred_index_info.pred_stream == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
idx->filter_predicate->pred_stream_size * sizeof (char));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
memcpy (pred_index_info.pred_stream, idx->filter_predicate->pred_stream, idx->filter_predicate->pred_stream_size);
pred_index_info.pred_stream_size = idx->filter_predicate->pred_stream_size;
if (idx->filter_predicate->num_attrs == 0)
{
pred_index_info.att_ids = NULL;
}
else
{
pred_index_info.att_ids = (int *) calloc (idx->filter_predicate->num_attrs, sizeof (int));
if (pred_index_info.att_ids == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
idx->filter_predicate->num_attrs * sizeof (int));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
for (i = 0; i < idx->filter_predicate->num_attrs; i++)
{
pred_index_info.att_ids[i] = idx->filter_predicate->att_ids[i];
}
}
pred_index_info.num_attrs = idx->filter_predicate->num_attrs;
p_pred_index_info = &pred_index_info;
}
if (idx->func_index_info)
{
func_index_info = (SM_FUNCTION_INFO *) db_ws_alloc (sizeof (SM_FUNCTION_INFO));
if (func_index_info == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_exit;
}
func_index_info->fi_domain = tp_domain_copy (idx->func_index_info->fi_domain, true);
func_index_info->expr_str = strdup (idx->func_index_info->expr_str);
if (func_index_info->expr_str == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1,
(strlen (idx->func_index_info->expr_str) + 1) * sizeof (char));
goto error_exit;
}
func_index_info->expr_stream = (char *) calloc (idx->func_index_info->expr_stream_size, sizeof (char));
if (func_index_info->expr_stream == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, idx->func_index_info->expr_stream_size * sizeof (char));
goto error_exit;
}
memcpy (func_index_info->expr_stream, idx->func_index_info->expr_stream, idx->func_index_info->expr_stream_size);
func_index_info->expr_stream_size = idx->func_index_info->expr_stream_size;
func_index_info->col_id = idx->func_index_info->col_id;
func_index_info->attr_index_start = idx->func_index_info->attr_index_start;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback = true;
error = sm_drop_constraint (obj, original_ctype, index_name, (const char **) attnames, false, false);
if (error != NO_ERROR)
{
goto error_exit;
}
error =
sm_add_constraint (obj, original_ctype, index_name, (const char **) attnames, asc_desc, attrs_prefix_length, false,
p_pred_index_info, func_index_info, comment_str, saved_index_status);
if (error != NO_ERROR)
{
goto error_exit;
}
end:
if (func_index_info)
{
sm_free_function_index_info (func_index_info);
db_ws_free (func_index_info);
func_index_info = NULL;
}
if (pred_index_info.pred_stream != NULL)
{
free_and_init (pred_index_info.pred_stream);
}
if (free_pred_string)
{
free_and_init (pred_index_info.pred_string);
/* free allocated attribute ids */
if (pred_index_info.att_ids != NULL)
{
free_and_init (pred_index_info.att_ids);
}
}
if (free_packing_buff)
{
/* mark the end of another level of xasl packing */
pt_exit_packing_buf ();
}
if (attnames)
{
for (i = 0; attnames[i]; i++)
{
free_and_init (attnames[i]);
}
free_and_init (attnames);
}
if (asc_desc)
{
free_and_init (asc_desc);
}
if (attrs_prefix_length)
{
free_and_init (attrs_prefix_length);
}
return error;
error_exit:
if (do_rollback == true)
{
if (do_rollback && error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
}
}
error = (error == NO_ERROR && (error = er_errid ()) == NO_ERROR) ? ER_FAILED : error;
goto end;
}
#if defined (ENABLE_RENAME_CONSTRAINT)
/*
* do_alter_index_rename() - renames an index on a class.
* return: Error code if it fails
* parser(in): Parser context
* statement(in): Parse tree of a alter index statement
*/
static int
do_alter_index_rename (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *obj;
PT_NODE *cls = NULL;
SM_TEMPLATE *ctemplate = NULL;
const char *class_name = NULL;
const char *index_name = NULL;
const char *new_index_name = NULL;
const char *comment = NULL;
bool do_rollback = false;
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
new_index_name = statement->info.index.new_name ? statement->info.index.new_name->info.name.original : NULL;
if (index_name == NULL || new_index_name == NULL)
{
goto error_exit;
}
if (statement->info.index.comment != NULL)
{
assert (statement->info.index.comment->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (statement->info.index.comment);
}
cls = statement->info.index.indexed_class ? statement->info.index.indexed_class->info.spec.flat_entity_list : NULL;
if (cls == NULL)
{
goto error_exit;
}
class_name = cls->info.name.resolved;
obj = db_find_class (class_name);
if (obj == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
goto error_exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback = true;
ctemplate = smt_edit_class_mop (obj, AU_INDEX);
if (ctemplate == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
assert (error != NO_ERROR);
goto error_exit;
}
error = smt_rename_constraint (ctemplate, index_name, new_index_name, SM_INDEX_NAME);
if (error != NO_ERROR)
{
goto error_exit;
}
if (comment != NULL)
{
error = smt_change_constraint_comment (ctemplate, new_index_name, comment);
if (error != NO_ERROR)
{
goto error_exit;
}
}
/* classobj_free_template() is included in sm_update_class() */
error = sm_update_class (ctemplate, NULL);
if (error != NO_ERROR)
{
/* Even though sm_update() did not return NO_ERROR, ctemplate is already freed */
ctemplate = NULL;
goto error_exit;
}
end:
return error;
error_exit:
if (ctemplate != NULL)
{
/* smt_quit() always returns NO_ERROR */
smt_quit (ctemplate);
}
if (do_rollback == true)
{
if (do_rollback && error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
}
}
error = (error == NO_ERROR && (error = er_errid ()) == NO_ERROR) ? ER_FAILED : error;
goto end;
}
#endif
/*
* do_alter_index_comment() - alter an index comment on a class.
* return: Error code if it fails
* parser(in): Parser context
* statement(in): Parse tree of a alter index statement
*/
static int
do_alter_index_comment (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *obj;
PT_NODE *cls = NULL;
SM_TEMPLATE *ctemplate = NULL;
const char *class_name = NULL;
const char *index_name = NULL;
const char *comment = NULL;
bool do_rollback = false;
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
if (index_name == NULL)
{
goto error_exit;
}
if (statement->info.index.comment != NULL)
{
assert (statement->info.index.comment->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (statement->info.index.comment);
}
cls = statement->info.index.indexed_class ? statement->info.index.indexed_class->info.spec.flat_entity_list : NULL;
if (cls == NULL)
{
goto error_exit;
}
class_name = cls->info.name.resolved;
obj = db_find_class (class_name);
if (obj == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
goto error_exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback = true;
ctemplate = smt_edit_class_mop (obj, AU_INDEX);
if (ctemplate == NULL)
{
error = er_errid ();
assert (error != NO_ERROR);
goto error_exit;
}
error = smt_change_constraint_comment (ctemplate, index_name, comment);
if (error != NO_ERROR)
{
goto error_exit;
}
/* classobj_free_template() is included in sm_update_class() */
error = sm_update_class (ctemplate, NULL);
if (error != NO_ERROR)
{
/* Even though sm_update() did not return NO_ERROR, ctemplate is already freed */
ctemplate = NULL;
goto error_exit;
}
end:
return error;
error_exit:
if (ctemplate != NULL)
{
/* smt_quit() always returns NO_ERROR */
smt_quit (ctemplate);
}
if (do_rollback == true)
{
if (do_rollback && error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
}
}
error = (error == NO_ERROR && (error = er_errid ()) == NO_ERROR) ? ER_FAILED : error;
goto end;
}
/*
* do_alter_index() - Alters an index on a class.
* return: Error code if it fails
* parser(in): Parser context
* statement(in): Parse tree of a alter index statement
*/
int
do_alter_index (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
CHECK_MODIFICATION_ERROR ();
if (statement->info.index.code == PT_REBUILD_INDEX)
{
error = do_alter_index_rebuild (parser, statement);
}
#if defined (ENABLE_RENAME_CONSTRAINT)
else if (statement->info.index.code == PT_RENAME_INDEX)
{
error = do_alter_index_rename (parser, statement);
}
#endif
else if (statement->info.index.code == PT_CHANGE_INDEX_COMMENT)
{
error = do_alter_index_comment (parser, statement);
}
else if (statement->info.index.code == PT_CHANGE_INDEX_STATUS)
{
error = do_alter_index_status (parser, statement);
}
else
{
return ER_FAILED;
}
return error;
}
/*
* do_create_partition() - Creates partitions
* return: Error code if partitions are not created
* parser(in): Parser context
* alter(in): The parse tree of a create class
* pinfo(in): partition alter context
*
* Note:
*/
static int
do_create_partition (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
int error;
PT_NODE *alter_info, *hash_parts, *newparts, *hashtail;
PT_NODE *parts, *parts_save, *fmin;
PT_NODE *parttemp, *entity_name = NULL;
PART_CLASS_INFO pci = { NULL, NULL, NULL, NULL };
PART_CLASS_INFO *newpci, *wpci;
char class_name[DB_MAX_IDENTIFIER_LENGTH];
DB_VALUE *minval, *parts_val, *fmin_val, partsize;
int part_cnt = 0, part_add = -1;
size_t buf_size;
SM_CLASS *smclass;
bool reuse_oid = false;
TDE_ALGORITHM tde_algo = TDE_ALGORITHM_NONE;
CHECK_MODIFICATION_ERROR ();
alter_info = hash_parts = newparts = hashtail = NULL;
parts = parts_save = fmin = NULL;
if (alter->node_type == PT_ALTER)
{
alter_info = alter->info.alter.alter_clause.partition.info;
if (alter->info.alter.code == PT_ADD_PARTITION || alter->info.alter.code == PT_REORG_PARTITION)
{
parts = alter->info.alter.alter_clause.partition.parts;
part_add = parts->info.parts.type;
}
else if (alter->info.alter.code == PT_ADD_HASHPARTITION)
{
part_add = PT_PARTITION_HASH;
}
entity_name = alter->info.alter.entity_name;
intl_identifier_lower ((char *) entity_name->info.name.original, class_name);
}
else if (alter->node_type == PT_CREATE_ENTITY)
{
entity_name = alter->info.create_entity.entity_name;
alter_info = alter->info.create_entity.partition_info;
intl_identifier_lower ((char *) entity_name->info.name.original, class_name);
}
else
{
return NO_ERROR;
}
if (part_add == -1)
{ /* create or apply partition */
if (!alter_info)
{
return NO_ERROR;
}
parts = alter_info->info.partition.parts;
}
parts_save = parts;
parttemp = parser_new_node (parser, PT_CREATE_ENTITY);
if (parttemp == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
error = au_fetch_class (pinfo->root_op, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
/* If the current class is part of a hierarchy and the class is
* not partitioned, end this as we do not allow partitions on hierarchies.
*/
if (smclass->partition == NULL && (smclass->users != NULL || smclass->inheritance != NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SM_NO_PARTITION_ON_HIERARCHIES, 0);
error = ER_SM_NO_PARTITION_ON_HIERARCHIES;
goto end_create;
}
reuse_oid = (smclass->flags & SM_CLASSFLAG_REUSE_OID) ? true : false;
tde_algo = (TDE_ALGORITHM) smclass->tde_algorithm;
parttemp->info.create_entity.entity_type = PT_CLASS;
parttemp->info.create_entity.entity_name = parser_new_node (parser, PT_NAME);
parttemp->info.create_entity.supclass_list = parser_new_node (parser, PT_NAME);
if (parttemp->info.create_entity.entity_name == NULL || parttemp->info.create_entity.supclass_list == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
parttemp->info.create_entity.supclass_list->info.name.db_object = pinfo->root_op;
error = NO_ERROR;
if (part_add == PT_PARTITION_HASH
|| (alter_info && alter_info->node_type != PT_VALUE && alter_info->info.partition.type == PT_PARTITION_HASH))
{
int pi, org_hashsize, new_hashsize;
hash_parts = parser_new_node (parser, PT_PARTS);
if (hash_parts == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
hash_parts->info.parts.name = parser_new_node (parser, PT_NAME);
if (hash_parts->info.parts.name == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
hash_parts->info.parts.type = PT_PARTITION_HASH;
if (part_add == PT_PARTITION_HASH)
{
org_hashsize = do_get_partition_size (pinfo->root_op);
if (org_hashsize < 0)
{
error = org_hashsize;
goto end_create;
}
new_hashsize = alter->info.alter.alter_clause.partition.size->info.value.data_value.i;
}
else
{
org_hashsize = 0;
new_hashsize = alter_info->info.partition.hashsize->info.value.data_value.i;
}
for (pi = 0; pi < new_hashsize; pi++)
{
newpci = (PART_CLASS_INFO *) malloc (sizeof (PART_CLASS_INFO));
if (newpci == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PART_CLASS_INFO));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto end_create;
}
memset (newpci, 0x0, sizeof (PART_CLASS_INFO));
newpci->next = pci.next;
pci.next = newpci;
buf_size = strlen (class_name) + 5 + 13;
newpci->pname = (char *) malloc (buf_size);
if (newpci->pname == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto end_create;
}
sprintf (newpci->pname, "%s" PARTITIONED_SUB_CLASS_TAG "p%d", class_name, pi + org_hashsize);
if (strlen (newpci->pname) >= PARTITION_VARCHAR_LEN)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end_create;
}
newpci->temp = dbt_create_class (newpci->pname);
if (newpci->temp == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
parttemp->info.create_entity.entity_name->info.name.original = newpci->pname;
parttemp->info.create_entity.supclass_list->info.name.original = class_name;
error = do_create_local (parser, newpci->temp, parttemp, NULL);
if (error != NO_ERROR)
{
dbt_abort_class (newpci->temp);
goto end_create;
}
newpci->temp->partition_parent_atts = smclass->attributes;
hash_parts->info.parts.name->info.name.original =
strstr (newpci->pname, PARTITIONED_SUB_CLASS_TAG) + strlen (PARTITIONED_SUB_CLASS_TAG);
hash_parts->info.parts.values = NULL;
newpci->temp->partition =
pt_node_to_partition_info (parser, hash_parts, NULL, class_name, newpci->pname, NULL);
if (newpci->temp->partition == NULL)
{
error = er_errid ();
dbt_abort_class (newpci->temp);
goto end_create;
}
newpci->obj = dbt_finish_class (newpci->temp);
if (newpci->obj == NULL)
{
dbt_abort_class (newpci->temp);
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
else if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
{
/* The class doesn't exist. We are creating the class. */
er_clear ();
}
if (reuse_oid)
{
error = sm_set_class_flag (newpci->obj, SM_CLASSFLAG_REUSE_OID, 1);
if (error != NO_ERROR)
{
goto end_create;
}
}
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = sm_set_class_tde_algorithm (newpci->obj, tde_algo);
if (error != NO_ERROR)
{
goto end_create;
}
}
if (locator_create_heap_if_needed (newpci->obj, reuse_oid) == NULL
|| locator_flush_class (newpci->obj) != NO_ERROR)
{
error = (er_errid () != NO_ERROR) ? er_errid () : ER_FAILED;
goto end_create;
}
if (part_add == PT_PARTITION_HASH)
{
hash_parts->next = NULL;
hash_parts->info.parts.name->info.name.db_object = newpci->obj;
newparts = parser_copy_tree (parser, hash_parts);
if (alter->info.alter.alter_clause.partition.parts == NULL)
{
alter->info.alter.alter_clause.partition.parts = newparts;
}
else
{
if (hashtail != NULL)
{
hashtail->next = newparts;
}
}
hashtail = newparts;
}
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = file_apply_tde_to_class_files (&newpci->obj->oid_info.oid);
if (error != NO_ERROR)
{
goto end_create;
}
}
error = NO_ERROR;
}
}
else
{ /* RANGE or LIST */
char *part_name;
for (; parts; parts = parts->next, part_cnt++)
{
newpci = (PART_CLASS_INFO *) malloc (sizeof (PART_CLASS_INFO));
if (newpci == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (PART_CLASS_INFO));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto end_create;
}
memset (newpci, 0x0, sizeof (PART_CLASS_INFO));
newpci->next = pci.next;
pci.next = newpci;
part_name = (char *) parts->info.parts.name->info.name.original;
buf_size = strlen (class_name) + 5 + 1 + strlen (part_name);
newpci->pname = (char *) malloc (buf_size);
if (newpci->pname == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto end_create;
}
sprintf (newpci->pname, "%s" PARTITIONED_SUB_CLASS_TAG "%s", class_name, part_name);
if (strlen (newpci->pname) >= PARTITION_VARCHAR_LEN)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end_create;
}
if (alter->info.alter.code == PT_REORG_PARTITION && parts->flag.partition_pruned)
{ /* reused partition */
newpci->obj = ws_find_class (newpci->pname);
if (newpci->obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
newpci->temp = dbt_edit_class (newpci->obj);
if (newpci->temp == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
newpci->temp->partition =
pt_node_to_partition_info (parser, parts, NULL, class_name, newpci->pname, NULL);
if (newpci->temp->partition == NULL)
{
error = er_errid ();
dbt_abort_class (newpci->temp);
goto end_create;
}
newpci->obj = dbt_finish_class (newpci->temp);
if (newpci->obj == NULL)
{
dbt_abort_class (newpci->temp);
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
continue;
}
newpci->temp = dbt_create_class (newpci->pname);
if (newpci->temp == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
parttemp->info.create_entity.entity_name->info.name.original = newpci->pname;
parttemp->info.create_entity.supclass_list->info.name.original = class_name;
error = do_create_local (parser, newpci->temp, parttemp, NULL);
if (error != NO_ERROR)
{
dbt_abort_class (newpci->temp);
goto end_create;
}
newpci->temp->partition_parent_atts = smclass->attributes;
/* RANGE-MIN VALUE search */
minval = NULL;
if ((alter_info && alter_info->node_type != PT_VALUE && alter_info->info.partition.type == PT_PARTITION_RANGE)
|| part_add == PT_PARTITION_RANGE)
{
parts_val = pt_value_to_db (parser, parts->info.parts.values);
for (fmin = parts_save; fmin; fmin = fmin->next)
{
if (fmin == parts)
{
continue;
}
if (fmin->info.parts.values == NULL)
{
continue; /* RANGE-MAXVALUE */
}
fmin_val = pt_value_to_db (parser, fmin->info.parts.values);
if (fmin_val == NULL)
{
continue;
}
if (parts->info.parts.values == NULL || db_value_compare (parts_val, fmin_val) == DB_GT)
{
if (minval == NULL)
{
minval = fmin_val;
}
else
{
if (db_value_compare (minval, fmin_val) == DB_LT)
{
minval = fmin_val;
}
}
}
}
}
if (part_add == PT_PARTITION_RANGE && minval == NULL && alter_info && alter_info->node_type == PT_VALUE)
{
/* set in pt_check_alter_partition */
minval = pt_value_to_db (parser, alter_info);
}
newpci->temp->partition = pt_node_to_partition_info (parser, parts, NULL, class_name, newpci->pname, minval);
if (newpci->temp->partition == NULL)
{
error = er_errid ();
dbt_abort_class (newpci->temp);
goto end_create;
}
newpci->obj = dbt_finish_class (newpci->temp);
if (newpci->obj == NULL)
{
dbt_abort_class (newpci->temp);
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
else if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
{
/* The class doesn't exist. We are creating the class. */
er_clear ();
}
parts->info.parts.name->info.name.db_object = newpci->obj;
sm_set_class_collation (newpci->obj, smclass->collation_id);
if (reuse_oid)
{
error = sm_set_class_flag (newpci->obj, SM_CLASSFLAG_REUSE_OID, 1);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
}
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = sm_set_class_tde_algorithm (newpci->obj, tde_algo);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
}
if (locator_create_heap_if_needed (newpci->obj, reuse_oid) == NULL
|| locator_flush_class (newpci->obj) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = file_apply_tde_to_class_files (&newpci->obj->oid_info.oid);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end_create;
}
}
error = NO_ERROR;
}
}
if (part_add != -1)
{
/* partition size update */
error = adjust_partition_size (pinfo->root_op, pinfo->root_tmpl);
if (error != NO_ERROR)
{
goto end_create;
}
if (alter->info.alter.code == PT_REORG_PARTITION && part_add == PT_PARTITION_RANGE)
{
error = au_fetch_class (pinfo->root_op, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto end_create;
}
error = adjust_partition_range (smclass->users);
if (error != NO_ERROR)
{
goto end_create;
}
}
}
else
{
/* set parent's partition info */
DB_CTMPL *root_tmpl = NULL;
bool abort_template = false;
db_make_int (&partsize, part_cnt);
if (pinfo->root_tmpl != NULL)
{
root_tmpl = pinfo->root_tmpl;
}
else
{
root_tmpl = dbt_edit_class (pinfo->root_op);
if (root_tmpl == NULL)
{
error = er_errid ();
goto end_create;
}
abort_template = true;
}
root_tmpl->partition =
pt_node_to_partition_info (parser, alter_info, entity_name, class_name, class_name, &partsize);
if (root_tmpl->partition == NULL)
{
error = er_errid ();
if (abort_template == true)
{
dbt_abort_class (root_tmpl);
}
goto end_create;
}
if (abort_template == true)
{
if (dbt_finish_class (root_tmpl) == NULL)
{
dbt_abort_class (root_tmpl);
error = er_errid ();
goto end_create;
}
}
}
end_create:
for (wpci = pci.next; wpci;)
{
if (wpci->pname)
{
free_and_init (wpci->pname);
}
newpci = wpci;
wpci = wpci->next;
free_and_init (newpci);
}
if (parttemp != NULL)
{
parser_free_tree (parser, parttemp);
}
if (error != NO_ERROR)
{
return error;
}
assert (er_errid_if_has_error () == NO_ERROR);
return NO_ERROR;
}
/*
* compile_partition_expression () - compile the partition expression and
* serialize it to a stream
*
* return : serialized expression or NULL
* parser (in) : parser context
* entity_name (in) : the name of the partitioned table
* pinfo (in) : partition information node
*
*/
static SM_FUNCTION_INFO *
compile_partition_expression (PARSER_CONTEXT * parser, PT_NODE * entity_name, PT_NODE * pinfo)
{
PT_NODE *spec = NULL, *expr = NULL;
SM_FUNCTION_INFO *part_expr = NULL;
if (pinfo->node_type != PT_PARTITION)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
spec = pt_entity (parser, entity_name, NULL, NULL);
if (spec == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, sizeof (PT_NODE));
return NULL;
}
/* perform semantic check on the expression */
expr = pinfo->info.partition.expr;
mq_clear_ids (parser, expr, NULL);
if (pt_semantic_quick_check_node (parser, &spec, &expr) == NULL)
{
PT_ERRORm (parser, expr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
return NULL;
}
/* pack the partition expression */
pt_enter_packing_buf ();
part_expr = pt_node_to_function_index (parser, spec, expr, DO_INDEX_CREATE);
pt_exit_packing_buf ();
parser_free_node (parser, spec);
return part_expr;
}
/*
* do_check_partitioned_class() - Checks partitioned class
* return: Error code if check_map or keyattr is checked
* classop(in): MOP of class
* class_map(in/out): Checking method(CHECK_PARTITION_NONE, _PARTITION_PARENT,
* _PARTITION_SUBS)
* keyattr(in): Partition key attribute to check
*
* Note:
*/
int
do_check_partitioned_class (DB_OBJECT * classop, int check_map, char *keyattr)
{
int error = NO_ERROR;
int is_partition = 0;
char attr_name[DB_MAX_IDENTIFIER_LENGTH + 1];
if (classop == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, 0);
return ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
}
error = sm_partitioned_class_type (classop, &is_partition, (keyattr) ? attr_name : NULL, NULL);
if (error != NO_ERROR)
{
return error;
}
if (is_partition > 0)
{
if (((check_map & CHECK_PARTITION_PARENT) && is_partition == 1)
|| ((check_map & CHECK_PARTITION_SUBS) && is_partition == 2))
{
error = ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
}
else if (keyattr)
{
if (intl_identifier_casecmp (keyattr, attr_name) == 0)
{
error = ER_NOT_ALLOWED_ACCESS_TO_PARTITION;
}
}
if (error != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, 0);
}
}
return error;
}
/*
* do_get_partition_parent () -
* return: NO_ERROR or error code
* classop(in): MOP of class
* parentop(out): MOP of the parent of the sub-partition or NULL if not a
* sub-partition
*/
int
do_get_partition_parent (DB_OBJECT * const classop, MOP * const parentop)
{
int error = NO_ERROR;
int au_save = 0;
SM_CLASS *smclass = NULL;
if (classop == NULL)
{
assert_release (classop != NULL);
return ER_FAILED;
}
if (parentop == NULL || *parentop != NULL)
{
assert_release (parentop == NULL || *parentop != NULL);
return ER_FAILED;
}
*parentop = NULL;
AU_DISABLE (au_save);
error = au_fetch_class (classop, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto error_exit;
}
if (smclass->partition == NULL)
{
/* not a partitioned class */
goto end;
}
if (smclass->inheritance == NULL || smclass->users != NULL)
{
/* this is the partitioned table, not a partition */
goto end;
}
if (smclass->inheritance->next != NULL)
{
assert (false);
goto error_exit;
}
*parentop = smclass->inheritance->op;
end:
AU_ENABLE (au_save);
smclass = NULL;
return error;
error_exit:
AU_ENABLE (au_save);
smclass = NULL;
*parentop = NULL;
return error;
}
/*
* do_is_partitioned_subclass() -
* return: 1 if success, else error code
* is_partitioned(in/out):
* classname(in):
* keyattr(in):
*
* Note:
*/
int
do_is_partitioned_subclass (int *is_partitioned, const char *classname, char *keyattr)
{
MOP classop;
SM_CLASS *smclass;
DB_VALUE attrname;
int ret = 0;
if (!classname)
{
return 0;
}
if (is_partitioned)
{
*is_partitioned = 0;
}
classop = db_find_class (classname);
if (classop == NULL)
{
return 0;
}
if (au_fetch_class (classop, &smclass, AU_FETCH_READ, AU_SELECT) != NO_ERROR || !smclass->partition)
{
return 0;
}
if (smclass->partition->pname != NULL)
{
ret = 1; /* partitioned sub-class */
}
else
{
if (is_partitioned)
{
*is_partitioned = 1;
}
if (keyattr)
{
const char *p = NULL;
keyattr[0] = 0;
if (set_get_element_nocopy (smclass->partition->values, 0, &attrname) == NO_ERROR && !DB_IS_NULL (&attrname)
&& (p = db_get_string (&attrname)))
{
strncpy (keyattr, p, DB_MAX_IDENTIFIER_LENGTH);
if (strlen (p) < DB_MAX_IDENTIFIER_LENGTH)
{
keyattr[strlen (p)] = 0;
}
else
{
keyattr[DB_MAX_IDENTIFIER_LENGTH] = 0;
}
}
}
}
return ret;
}
/*
* do_drop_partition() -
* return: Error code
* class(in):
* drop_sub_flag(in):
* is_cascade_constraints(in):
*
* Note:
*/
int
do_drop_partitioned_class (MOP class_, int drop_sub_flag, bool is_cascade_constraints)
{
DB_OBJLIST *objs;
SM_CLASS *smclass, *subclass;
MOP delobj;
int error = NO_ERROR;
CHECK_MODIFICATION_ERROR ();
if (!class_)
{
return ER_FAILED;
}
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto fail_return;
}
if (!smclass->partition)
{
goto fail_return;
}
if (smclass->users == NULL)
{
/* this is a partition, not the partitioned table */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_NOT_ALLOWED_ACCESS_TO_PARTITION, 0);
error = ER_FAILED;
goto fail_return;
}
for (objs = smclass->users; objs;)
{
error = au_fetch_class (objs->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto fail_return;
}
if (subclass->partition)
{
delobj = objs->op;
objs = objs->next;
if (drop_sub_flag)
{
error = sm_delete_class_mop (delobj, is_cascade_constraints);
if (error != NO_ERROR)
{
goto fail_return;
}
}
}
else
{
objs = objs->next;
}
}
error = NO_ERROR;
fail_return:
return error;
}
/*
* partition_rename() -
* return: Error code
* old_class(in):
* newname(in):
*
* Note:
*/
int
do_rename_partition (MOP old_class, const char *newname)
{
DB_OBJLIST *objs;
SM_CLASS *smclass, *subclass;
int newlen;
int error;
char new_subname[PARTITION_VARCHAR_LEN + 1], *ptr;
char expr[DB_MAX_PARTITION_EXPR_LENGTH + 1] = { '\0' };
char *expr_ptr = NULL;
if (!old_class || !newname)
{
return ER_FAILED;
}
newlen = strlen (newname);
error = au_fetch_class (old_class, &smclass, AU_FETCH_UPDATE, AU_ALTER);
if (error != NO_ERROR)
{
goto end_rename;
}
/* The pexpr format is defined in the function pt_node_to_partition_info. */
strncpy (expr, smclass->partition->expr, DB_MAX_PARTITION_EXPR_LENGTH);
expr[DB_MAX_PARTITION_EXPR_LENGTH] = '\0';
expr_ptr = strchr (expr, ']'); // skip select list
expr_ptr = strchr (expr_ptr + 1, '['); // find table name
sprintf (expr_ptr, "[%s]", newname);
ws_free_string (smclass->partition->expr);
smclass->partition->expr = ws_copy_string (expr);
sm_flush_objects (old_class);
for (objs = smclass->users; objs; objs = objs->next)
{
error = au_fetch_class (objs->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto end_rename;
}
if (subclass->partition)
{
ptr = strstr ((char *) sm_ch_name ((MOBJ) subclass), PARTITIONED_SUB_CLASS_TAG);
if (ptr == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
error = ER_PARTITION_WORK_FAILED;
goto end_rename;
}
if ((newlen + strlen (ptr)) >= PARTITION_VARCHAR_LEN)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
error = ER_PARTITION_WORK_FAILED;
goto end_rename;
}
sprintf (new_subname, "%s%s", newname, ptr);
error = sm_rename_class (objs->op, new_subname);
if (error != NO_ERROR)
{
break;
}
}
}
end_rename:
return error;
}
/*
* do_redistribute_partitions_data() -
* return: error code or NO_ERROR
* classname(in):
* keyname(in):
* promoted(in):
* promoted_count(in):
* alter_op(in):
* should_update(in):
* should_insert(in):
* Note:
*/
static int
do_redistribute_partitions_data (const char *classname, const char *keyname, char **promoted, int promoted_count,
PT_ALTER_CODE alter_op, bool should_update, bool should_insert)
{
int error = NO_ERROR;
DB_QUERY_RESULT *query_result;
DB_QUERY_ERROR query_error;
char *query_buf;
size_t query_size;
int i = 0;
MOP subclass_mop, class_mop;
OID *partitions = NULL;
SM_CONSTRAINT_INFO *index_save_info = NULL;
if (should_update)
{
query_size = 0;
query_size += 7; /* 'UPDATE ' */
query_size += 28; // ' /*+ NO_SUPPLEMENTAL_LOG */ '
query_size += strlen (classname) + 2;
query_size += 5; /* ' SET ' */
query_size += strlen (keyname) * 2 + 6; /* [keyname]=[keyname]; */
query_buf = (char *) malloc (query_size + 1);
if (query_buf == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_size + 1);
return ER_FAILED;
}
sprintf (query_buf, "UPDATE /*+ NO_SUPPLEMENTAL_LOG */ [%s] SET [%s]=[%s];", classname, keyname, keyname);
error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
if (error >= 0)
{
error = NO_ERROR;
db_query_end (query_result);
}
free_and_init (query_buf);
if (error < 0)
{
return error;
}
}
if (should_insert)
{
class_mop = sm_find_class (classname);
if (class_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
if (alter_op != PT_REORG_PARTITION)
{
error = do_save_all_indexes (class_mop, &index_save_info);
if (error != NO_ERROR)
{
goto exit;
}
error = do_drop_saved_indexes (class_mop, index_save_info);
if (error != NO_ERROR)
{
goto exit;
}
}
partitions = (OID *) malloc (promoted_count * sizeof (OID));
if (partitions == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, promoted_count * sizeof (OID));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit;
}
for (i = 0; i < promoted_count; i++)
{
subclass_mop = sm_find_class (promoted[i]);
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
COPY_OID (&partitions[i], &subclass_mop->oid_info.oid);
}
error = locator_redistribute_partition_data (&class_mop->oid_info.oid, promoted_count, partitions);
if (error != NO_ERROR)
{
goto exit;
}
if (alter_op != PT_REORG_PARTITION)
{
error = do_recreate_saved_indexes (class_mop, index_save_info);
}
}
exit:
if (partitions != NULL)
{
free_and_init (partitions);
}
if (index_save_info != NULL)
{
sm_free_constraint_info (&index_save_info);
}
return error;
}
static int
do_set_auto_increment (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const char *attr_name, PT_NODE * attribute,
SM_ATTRIBUTE ** attr)
{
SM_ATTRIBUTE *ctmpl_attrs = ctemplate->attributes;
int error = NO_ERROR;
MOP auto_increment_obj = NULL;
assert (attribute->info.attr_def.attr_type != PT_META_ATTR && attribute->info.attr_def.attr_type != PT_SHARED);
assert (attribute->info.attr_def.auto_increment != NULL);
assert (attr != NULL);
if (*attr == NULL)
{
error = smt_find_attribute (ctemplate, attr_name, 0, attr);
}
if (error != NO_ERROR)
{
return error;
}
while (ctmpl_attrs != NULL)
{
if (ctmpl_attrs->auto_increment == NULL)
{
ctmpl_attrs = (SM_ATTRIBUTE *) ctmpl_attrs->header.next;
continue;
}
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AUTO_INCREMENT_SINGLE_COL_ONLY, 0);
return ER_AUTO_INCREMENT_SINGLE_COL_ONLY;
}
error = do_create_auto_increment_serial (parser, &auto_increment_obj, ctemplate->name, attribute);
if (error == NO_ERROR)
{
(*attr)->auto_increment = auto_increment_obj;
(*attr)->flags |= SM_ATTFLAG_AUTO_INCREMENT;
}
return error;
}
/*
* do_find_auto_increment_serial() -
* return: Error code
* auto_increment_obj(out):
* class_name(in):
* atrr_name(in):
*
* Note:
*/
static int
do_find_auto_increment_serial (MOP * auto_increment_obj, const char *class_name, const char *attr_name)
{
MOP serial_class = NULL;
char *serial_name = NULL;
size_t serial_name_size;
DB_IDENTIFIER serial_obj_id;
int error = NO_ERROR;
assert (class_name != NULL && attr_name != NULL);
*auto_increment_obj = NULL;
serial_class = sm_find_class (CT_SERIAL_NAME);
if (serial_class == NULL)
{
error = ER_QPROC_DB_SERIAL_NOT_FOUND;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto end;
}
serial_name_size = strlen (class_name) + strlen (attr_name) + AUTO_INCREMENT_SERIAL_NAME_EXTRA_LENGTH + 1;
serial_name = (char *) malloc (serial_name_size);
if (serial_name == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, serial_name_size);
goto end;
}
SET_AUTO_INCREMENT_SERIAL_NAME (serial_name, class_name, attr_name);
*auto_increment_obj = do_get_serial_obj_id (&serial_obj_id, serial_class, serial_name);
if (*auto_increment_obj == NULL)
{
error = ER_QPROC_SERIAL_NOT_FOUND;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, serial_name);
goto end;
}
end:
if (serial_name != NULL)
{
free_and_init (serial_name);
}
return error;
}
/*
* adjust_partition_range() -
* return: Error code
* objs(in):
*
* Note:
*/
static int
adjust_partition_range (DB_OBJLIST * objs)
{
DB_OBJLIST *subs;
SM_CLASS *subclass;
DB_VALUE minval, maxval, seqval, *wrtval;
int error = NO_ERROR;
char check_flag = 1;
DB_VALUE_SLIST *ranges = NULL, *rfind, *new_range, *prev_range;
DB_COLLECTION *dbc = NULL;
SM_TEMPLATE *tmpl = NULL;
db_make_null (&minval);
db_make_null (&maxval);
for (subs = objs; subs; subs = subs->next)
{
error = au_fetch_class (subs->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
break;
}
if (!subclass->partition)
{
continue;
}
if (check_flag)
{ /* RANGE check */
if (subclass->partition->partition_type != PT_PARTITION_RANGE)
{
break;
}
check_flag = 0;
}
if (subclass->partition->expr != NULL)
{
continue; /* reorg deleted partition */
}
error = set_get_element_nocopy (subclass->partition->values, 0, &minval);
if (error != NO_ERROR)
{
break;
}
error = set_get_element_nocopy (subclass->partition->values, 1, &maxval);
if (error != NO_ERROR)
{
break;
}
if ((new_range = (DB_VALUE_SLIST *) malloc (sizeof (DB_VALUE_SLIST))) == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, sizeof (DB_VALUE_SLIST));
break;
}
new_range->partition = subclass->partition;
new_range->class_obj = subs->op;
new_range->min = db_value_copy (&minval);
new_range->max = db_value_copy (&maxval);
new_range->next = NULL;
if (ranges == NULL)
{
ranges = new_range;
}
else
{ /* sort ranges */
for (rfind = ranges, prev_range = NULL; rfind; rfind = rfind->next)
{
if (DB_IS_NULL (rfind->max) || db_value_compare (rfind->max, new_range->max) == DB_GT)
{
if (prev_range == NULL)
{
new_range->next = ranges;
ranges = new_range;
}
else
{
new_range->next = prev_range->next;
prev_range->next = new_range;
}
break;
}
prev_range = rfind;
}
if (rfind == NULL)
{
prev_range->next = new_range;
}
}
}
for (rfind = ranges, prev_range = NULL; rfind; rfind = rfind->next)
{
wrtval = NULL;
if (prev_range == NULL)
{ /* Min value of first range is low infinite */
if (!DB_IS_NULL (rfind->min))
{
db_make_null (&minval);
wrtval = &minval;
}
}
else
{
if (db_value_compare (prev_range->max, rfind->min) != DB_EQ)
{
wrtval = prev_range->max;
}
}
if (wrtval != NULL)
{ /* adjust min value of range */
dbc = set_create_sequence (0);
if (dbc != NULL)
{
set_add_element (dbc, wrtval);
set_add_element (dbc, rfind->max);
db_make_sequence (&seqval, dbc);
tmpl = dbt_edit_class (rfind->class_obj);
if (tmpl == NULL)
{
set_free (dbc);
error = ER_FAILED;
break;
}
if (tmpl->partition->values != NULL)
{
/* free previous set */
set_free (tmpl->partition->values);
tmpl->partition->values = NULL;
}
tmpl->partition->values = db_seq_copy (dbc);
if (tmpl->partition->values == NULL)
{
set_free (dbc);
dbt_abort_class (tmpl);
error = ER_FAILED;
break;
}
if (dbt_finish_class (tmpl) == NULL)
{
set_free (dbc);
dbt_abort_class (tmpl);
error = ER_FAILED;
break;
}
set_free (dbc);
}
if (error != NO_ERROR)
{
break;
}
}
prev_range = rfind;
}
for (rfind = ranges; rfind;)
{
db_value_free (rfind->min);
db_value_free (rfind->max);
prev_range = rfind->next;
free_and_init (rfind);
rfind = prev_range;
}
return error;
}
/*
* adjust_partition_size() -
* return: Error code
* class(in):
* tmpl (in): partitioned class template
*
* Note:
*/
static int
adjust_partition_size (MOP class_, DB_CTMPL * tmpl)
{
int error = NO_ERROR;
SM_CLASS *smclass, *subclass;
DB_VALUE psize;
DB_OBJLIST *subs;
int partcnt;
if (class_ == NULL)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (smclass->partition == NULL || tmpl == NULL)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
db_make_null (&psize);
for (subs = smclass->users, partcnt = 0; subs; subs = subs->next)
{
error = au_fetch_class (subs->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (!subclass->partition)
{
continue;
}
partcnt++;
}
error = set_get_element_nocopy (tmpl->partition->values, 1, &psize);
if (error != NO_ERROR)
{
return error;
}
if (psize.data.i != partcnt)
{
psize.data.i = partcnt;
error = set_put_element (tmpl->partition->values, 1, &psize);
if (error != NO_ERROR)
{
return error;
}
}
return NO_ERROR;
}
/*
* do_get_partition_size() -
* return: Size if success, else error code
* class(in):
*
* Note:
*/
int
do_get_partition_size (MOP class_)
{
int error = NO_ERROR;
SM_CLASS *smclass;
DB_VALUE psize;
if (class_ == NULL)
{
return ER_FAILED;
}
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (!smclass->partition)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
db_make_null (&psize);
error = set_get_element_nocopy (smclass->partition->values, 1, &psize);
if (error != NO_ERROR)
{
return error;
}
error = psize.data.i;
if (error == 0)
{
error = ER_FAILED;
}
return error;
}
/*
* do_get_partition_keycol() -
* return: Error code
* keycol(out):
* class(in):
*
* Note:
*/
int
do_get_partition_keycol (char *keycol, MOP class_)
{
int error = NO_ERROR;
SM_CLASS *smclass;
DB_VALUE keyname;
const char *keyname_str;
if (class_ == NULL || keycol == NULL)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
*keycol = '\0';
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (!smclass->partition)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
db_make_null (&keyname);
error = set_get_element_nocopy (smclass->partition->values, 0, &keyname);
if (error != NO_ERROR)
{
return error;
}
if (DB_IS_NULL (&keyname))
{
return error;
}
keyname_str = db_get_string (&keyname);
strncpy (keycol, keyname_str, DB_MAX_IDENTIFIER_LENGTH);
error = NO_ERROR;
return error;
}
/*
* do_drop_partition_list() -
* return: Error code
* class(in):
* name_list(in):
* tmpl (in): partitioned class template
*
* Note:
*/
int
do_drop_partition_list (MOP class_, PT_NODE * name_list, DB_CTMPL * tmpl)
{
PT_NODE *names;
int error = NO_ERROR;
char subclass_name[DB_MAX_IDENTIFIER_LENGTH];
SM_CLASS *smclass, *subclass;
MOP classcata;
OID *partitions = NULL;
int no_partitions = 0;
int i;
if (class_ == NULL || name_list == NULL)
{
return ER_FAILED;
}
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto exit;
}
if (smclass->partition == NULL)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto exit;
}
for (names = name_list; names; names = names->next)
{
no_partitions++;
}
partitions = (OID *) malloc (no_partitions * sizeof (OID));
if (partitions == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, no_partitions * sizeof (OID));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit;
}
for (names = name_list, i = 0; names; names = names->next, i++)
{
sprintf (subclass_name, "%s" PARTITIONED_SUB_CLASS_TAG "%s", sm_ch_name ((MOBJ) smclass),
names->info.name.original);
classcata = sm_find_class (subclass_name);
if (classcata == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
COPY_OID (&partitions[i], &classcata->oid_info.oid);
}
for (names = name_list, i = 0; names; names = names->next, i++)
{
sprintf (subclass_name, "%s" PARTITIONED_SUB_CLASS_TAG "%s", smclass->header.ch_name, names->info.name.original);
classcata = sm_find_class (subclass_name);
if (classcata == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
COPY_OID (&partitions[i], &classcata->oid_info.oid);
error = au_fetch_class (classcata, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto exit;
}
if (subclass->partition)
{
error = sm_delete_class_mop (classcata, false);
if (error != NO_ERROR)
{
goto exit;
}
}
else
{
error = ER_PARTITION_NOT_EXIST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto exit;
}
}
error = adjust_partition_range (smclass->users);
if (error != NO_ERROR)
{
goto exit;
}
error = adjust_partition_size (class_, tmpl);
if (error != NO_ERROR)
{
goto exit;
}
error = NO_ERROR;
exit:
if (partitions != NULL)
{
free_and_init (partitions);
}
return error;
}
/*
* do_create_partition_constraints () - copy indexes from the root table to
* partitions
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : alter node
* pinfo (in) : partition alter context
*
* Note: At the moment the following constraints are added to partitions:
* - SM_CONSTRAINT_INDEX
* - SM_CONSTRAINT_REVERSE_INDEX
*/
static int
do_create_partition_constraints (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
SM_CLASS *smclass = NULL;
SM_CLASS_CONSTRAINT *cons = NULL;
int error = NO_ERROR;
/* sanity check */
assert (parser != NULL && alter != NULL && pinfo != NULL);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
smclass = sm_get_class_with_statistics (pinfo->root_op);
if (smclass == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
for (cons = smclass->constraints; cons != NULL; cons = cons->next)
{
if (cons->type != SM_CONSTRAINT_INDEX && cons->type != SM_CONSTRAINT_REVERSE_INDEX)
{
continue;
}
error = do_create_partition_constraint (alter, smclass, cons, pinfo);
if (error != NO_ERROR)
{
return error;
}
}
return error;
}
/*
* do_create_partition_constraint () - copy a constraint from the root class
* to the new partitions
* return : error code or NO_ERROR
* alter (in) : alter node
* root_class (in) : root class
* constraint (in) : root constraint
*/
static int
do_create_partition_constraint (PT_NODE * alter, SM_CLASS * root_class, SM_CLASS_CONSTRAINT * constraint,
SM_PARTITION_ALTER_INFO * pinfo)
{
int error = NO_ERROR, i = 0;
char **namep = NULL, **attrnames = NULL;
int *asc_desc = NULL;
SM_CLASS *subclass = NULL;
SM_ATTRIBUTE **attp = NULL;
TP_DOMAIN *key_type = NULL;
DB_OBJLIST *objs = NULL;
PT_ALTER_CODE alter_op;
PT_NODE *parts;
SM_FUNCTION_INFO *new_func_index_info = NULL;
alter_op = alter->info.alter.code;
attp = constraint->attributes;
i = 0;
/* count attributes in constraint */
while (*attp)
{
attp++;
i++;
}
if (i == 0)
{
assert (false);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
error = ER_FAILED;
goto cleanup;
}
namep = (char **) malloc ((i + 1) * sizeof (char *));
if (namep == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (i + 1) * sizeof (char *));
error = ER_FAILED;
goto cleanup;
}
asc_desc = (int *) malloc (i * sizeof (int));
if (asc_desc == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, i * sizeof (int *));
error = ER_FAILED;
goto cleanup;
}
attp = constraint->attributes;
attrnames = namep;
key_type = classobj_find_cons_index2_col_type_list (constraint, &pinfo->root_op->oid_info.oid);
if (key_type == NULL)
{
if ((error = er_errid ()) == NO_ERROR)
{
/* set an error if none was set yet */
error = ER_PARTITION_WORK_FAILED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
}
goto cleanup;
}
i = 0;
while (*attp && key_type)
{
*attrnames = (char *) (*attp)->header.name;
attrnames++;
asc_desc[i] = 0; /* guess as Asc */
if (DB_IS_CONSTRAINT_REVERSE_INDEX_FAMILY (constraint->type) || key_type->is_desc)
{
asc_desc[i] = 1; /* Desc */
}
i++;
attp++;
key_type = key_type->next;
}
*attrnames = NULL;
if (alter_op == PT_ADD_PARTITION || alter_op == PT_REORG_PARTITION || alter_op == PT_ADD_HASHPARTITION)
{
/* only create constraint for new partitions */
parts = alter->info.alter.alter_clause.partition.parts;
for (; parts; parts = parts->next)
{
MOP subclass_op = parts->info.parts.name->info.name.db_object;
if (alter_op == PT_REORG_PARTITION && parts->flag.partition_pruned)
{
continue; /* reused partition */
}
error = au_fetch_class (subclass_op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto cleanup;
}
if (subclass->partition == NULL)
{
assert (false);
error = ER_FAILED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
goto cleanup;
}
if (constraint->func_index_info)
{
error = sm_save_function_index_info (&new_func_index_info, constraint->func_index_info);
if (error != NO_ERROR)
{
goto cleanup;
}
error =
do_recreate_func_index_constr (NULL, NULL, new_func_index_info, NULL, sm_ch_name ((MOBJ) root_class),
sm_ch_name ((MOBJ) subclass));
if (error != NO_ERROR)
{
goto cleanup;
}
}
else
{
new_func_index_info = NULL;
}
error =
sm_add_constraint (subclass_op, db_constraint_type (constraint), constraint->name, (const char **) namep,
asc_desc, constraint->attrs_prefix_length, false, constraint->filter_predicate,
new_func_index_info, constraint->comment, constraint->index_status);
if (error != NO_ERROR)
{
goto cleanup;
}
if (new_func_index_info)
{
sm_free_function_index_info (new_func_index_info);
free_and_init (new_func_index_info);
}
}
}
else
{
for (objs = root_class->users; objs; objs = objs->next)
{
error = au_fetch_class (objs->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto cleanup;
}
if (subclass->partition == NULL)
{
/* not partitioned */
continue;
}
if (constraint->func_index_info)
{
error = sm_save_function_index_info (&new_func_index_info, constraint->func_index_info);
if (error != NO_ERROR)
{
goto cleanup;
}
error =
do_recreate_func_index_constr (NULL, NULL, new_func_index_info, NULL, sm_ch_name ((MOBJ) root_class),
sm_ch_name ((MOBJ) subclass));
if (error != NO_ERROR)
{
goto cleanup;
}
}
else
{
new_func_index_info = NULL;
}
error =
sm_add_constraint (objs->op, db_constraint_type (constraint), constraint->name, (const char **) namep,
asc_desc, constraint->attrs_prefix_length, false, constraint->filter_predicate,
new_func_index_info, constraint->comment, constraint->index_status);
if (error != NO_ERROR)
{
goto cleanup;
}
if (new_func_index_info)
{
sm_free_function_index_info (new_func_index_info);
free_and_init (new_func_index_info);
}
}
}
cleanup:
if (namep != NULL)
{
free_and_init (namep);
}
if (asc_desc != NULL)
{
free_and_init (asc_desc);
}
if (new_func_index_info)
{
sm_free_function_index_info (new_func_index_info);
free_and_init (new_func_index_info);
}
return error;
}
/*
* do_alter_partitioning_pre () - perform partition manipulation
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : partition alter context
*
* Note: Altering a partitioned class is an action that that involves two
* steps:
* 1. modifying the schema of the partitioned class
* 2. redistributing the data to the new schema
* The "pre" action is the action responsible for schema modification.
* We must ensure that the result of this action is a valid schema because
* the redistributing action can only be performed on a valid schema.
* The pre action performs the following actions:
* PT_APPLY_PARTITION
* PT_ADD_PARTITION
* PT_ADD_HASHPARTITION
* - create [new] partitions and update the root class with this info
* PT_REMOVE_PARTITION
* - promote all partitions from the schema to normal classes
* PT_COALESCE_PARTITION
* - promote partitions which will be dropped
* PT_REORG_PARTITION
* - promote partitions which will be reorganized
* PT_ANALYZE_PARTITION
* - N/A
* PT_DROP_PARTITION
* - drop partitions
* PT_PROMOTE_PARTITION:
* - promote partitions
*/
static int
do_alter_partitioning_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
PT_ALTER_CODE alter_op;
int error = NO_ERROR;
const char *entity_name = NULL;
/* sanity check */
assert (parser != NULL && alter != NULL && pinfo != NULL);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
assert (pinfo->root_op != NULL && pinfo->root_tmpl != NULL);
entity_name = alter->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
ERROR1 (error, ER_UNEXPECTED, "Expecting a class or virtual class name.");
return ER_FAILED;
}
alter_op = alter->info.alter.code;
if (alter_op != PT_ANALYZE_PARTITION && alter_op != PT_ADD_PARTITION)
{
/* Check to see if the root class is referenced by foreign keys. If the class is referenced by a foreign key, we
* only allow PT_ADD_PARTITION and PT_ANALYZE_PARTITION alter operations. All other alter operations will
* probably move data through different classes which is hard to track and can cause the foreign key constraint
* to be violated. */
SM_CLASS *root_class = NULL;
SM_CLASS_CONSTRAINT *pk;
error = au_fetch_class (pinfo->root_op, &root_class, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
pk = classobj_find_cons_primary_key (root_class->constraints);
if (pk != NULL && pk->fk_info != NULL && classobj_is_pk_referred (pinfo->root_op, pk->fk_info, true, NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ALTER_PARTITIONS_FK_NOT_ALLOWED, 0);
return ER_FAILED;
}
}
if (alter_op != PT_APPLY_PARTITION)
{
/* get partition key, we're going to need it later on */
error = do_get_partition_keycol (pinfo->keycol, pinfo->root_op);
if (error != NO_ERROR)
{
return error;
}
}
switch (alter_op)
{
case PT_APPLY_PARTITION:
{
PT_NODE *pt_key_col = NULL;
const char *key_col_name = NULL;
SM_ATTRIBUTE *attr = NULL;
error = do_check_fk_constraints_internal (pinfo->root_tmpl, alter->info.alter.constraint_list, true);
if (error != NO_ERROR)
{
return error;
}
/* Set SM_ATTFLAG_PARTITION_KEY on the partitioning key. Notice that at this point, we have access to the root
* class template which is being edited. The calling function will call finish on this template and we will
* create the actual partitions on the "post" action of the alter statement. */
pt_key_col = alter->info.alter.alter_clause.partition.info->info.partition.keycol;
key_col_name = pt_key_col->info.name.original;
attr = pinfo->root_tmpl->attributes;
while (attr != NULL)
{
if (SM_COMPARE_NAMES (key_col_name, attr->header.name) == 0)
{
attr->flags |= SM_ATTFLAG_PARTITION_KEY;
break;
}
attr = (SM_ATTRIBUTE *) attr->header.next;
}
break;
}
case PT_ADD_PARTITION:
case PT_ADD_HASHPARTITION:
/* create new partitions */
error = do_create_partition (parser, alter, pinfo);
if (error != NO_ERROR)
{
return error;
}
error = do_check_fk_constraints (pinfo->root_tmpl, alter->info.alter.constraint_list);
break;
case PT_REMOVE_PARTITION:
error = do_remove_partition_pre (parser, alter, pinfo);
break;
case PT_COALESCE_PARTITION:
error = do_coalesce_partition_pre (parser, alter, pinfo);
break;
case PT_REORG_PARTITION:
error = do_reorganize_partition_pre (parser, alter, pinfo);
break;
case PT_ANALYZE_PARTITION:
break;
case PT_DROP_PARTITION:
error =
do_drop_partition_list (pinfo->root_op, alter->info.alter.alter_clause.partition.name_list, pinfo->root_tmpl);
break;
case PT_PROMOTE_PARTITION:
error = do_promote_partition_list (parser, alter, pinfo);
break;
default:
error = ER_FAILED;
break;
}
return error;
}
/*
* do_alter_partitioning_post () - redistribute data for partition
* manipulation
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : partition alter context
*
* Note: Altering a partitioned class is an action that that involves two
* steps:
* 1. modifying the schema of the partitioned class
* 2. redistributing the data to the new schema
* The "post" action is the action responsible for redistributing data.
* The post action performs the following actions:
* PT_APPLY_PARTITION
* PT_ADD_HASHPARTITION
* - redistribute data from the root table [and the old partitions] to the
* new partitioning schema by performing "UPDATE t SET * = *"
* PT_REMOVE_PARTITION
* PT_COALESCE_PARTITION
* PT_REORG_PARTITION
* - redistribute data from the partitions promoted in the pre action to
* the new schema (using INSERT SELECT)and drop the promoted partitions
* PT_ANALYZE_PARTITION
* - update statistics on partitions
* PT_ADD_PARTITION
* PT_DROP_PARTITION
* PT_PROMOTE_PARTITION:
* - N/A
*/
static int
do_alter_partitioning_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
PT_ALTER_CODE alter_op;
int error = NO_ERROR;
const char *entity_name = NULL;
assert (parser != NULL && alter != NULL && pinfo != NULL);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
assert (alter != NULL);
assert (alter->node_type == PT_ALTER);
alter_op = alter->info.alter.code;
entity_name = alter->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
ERROR1 (error, ER_UNEXPECTED, "Expecting a class or virtual class name.");
return ER_FAILED;
}
switch (alter_op)
{
case PT_APPLY_PARTITION:
error = do_create_partition (parser, alter, pinfo);
if (error != NO_ERROR)
{
return error;
}
error = do_get_partition_keycol (pinfo->keycol, pinfo->root_op);
if (error != NO_ERROR)
{
return error;
}
[[fallthrough]];
case PT_ADD_HASHPARTITION:
error = do_redistribute_partitions_data (entity_name, pinfo->keycol, NULL, 0, PT_ADD_HASHPARTITION, true, false);
break;
case PT_COALESCE_PARTITION:
error = do_coalesce_partition_post (parser, alter, pinfo);
break;
case PT_REORG_PARTITION:
error = do_reorganize_partition_post (parser, alter, pinfo);
break;
case PT_REMOVE_PARTITION:
error = do_remove_partition_post (parser, alter, pinfo);
break;
case PT_ANALYZE_PARTITION:
case PT_ADD_PARTITION:
case PT_DROP_PARTITION:
case PT_PROMOTE_PARTITION:
/* nothing to do */
break;
default:
error = NO_ERROR;
break;
}
if (error != NO_ERROR)
{
return error;
}
/* if we created new partitions, we need to propagate indexes here */
switch (alter_op)
{
case PT_APPLY_PARTITION:
case PT_ADD_PARTITION:
case PT_REORG_PARTITION:
case PT_ADD_HASHPARTITION:
error = do_create_partition_constraints (parser, alter, pinfo);
break;
default:
break;
}
return error;
}
/*
* do_remove_partition_pre () - perform schema actions for partition removing
*
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "pre" action for removing partitions from a class is to promote
* all partitions to stand alone tables.
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_remove_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
SM_CLASS *class_ = NULL, *subclass = NULL;
DB_OBJLIST *obj = NULL, *obj_next = NULL;
int error;
char **names = NULL;
int names_count = 0, allocated = 0, i = 0;
/* sanity checks */
assert (parser && alter && pinfo);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
assert (alter->node_type == PT_ALTER);
error = au_fetch_class (pinfo->root_op, &class_, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (class_->partition == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
return ER_FAILED;
}
/* preallocate 10 elements for the names array */
names = (char **) malloc (10 * sizeof (char *));
if (names == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, 10 * sizeof (char *));
return ER_FAILED;
}
allocated = 10;
names_count = 0;
obj = class_->users;
while (obj != NULL)
{
/* save next because obj will be modified by the promote call below */
obj_next = obj->next;
error = au_fetch_class (obj->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto error_return;
}
if (subclass->partition == NULL)
{
/* not a partition */
obj = obj_next;
continue;
}
if (names_count >= allocated - 1)
{
/* If the names array is to small to accept a new element, reallocate it */
char **buf = (char **) realloc (names, (allocated + 10) * sizeof (char *));
if (buf == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
((allocated + 10) * sizeof (char *)));
error = ER_FAILED;
goto error_return;
}
names = buf;
allocated += 10;
}
names[names_count] = strdup (sm_ch_name ((MOBJ) subclass));
if (names[names_count] == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
((strlen (sm_ch_name ((MOBJ) subclass)) + 1) * sizeof (char)));
error = ER_FAILED;
goto error_return;
}
names_count++;
error = do_promote_partition (subclass);
if (error != NO_ERROR)
{
goto error_return;
}
obj = obj_next;
}
/* keep the promoted names in the partition alter context */
pinfo->promoted_names = names;
pinfo->promoted_count = names_count;
classobj_free_partition_info (pinfo->root_tmpl->partition);
pinfo->root_tmpl->partition = NULL;
return NO_ERROR;
error_return:
if (names != NULL)
{
for (i = 0; i < names_count; i++)
{
free_and_init (names[i]);
}
free_and_init (names);
}
return error;
}
/*
* do_remove_partition_post () - redistribute data from removed partitions
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "post" action for removing partitions from a class is to
* redistribute data from promoted partitions in pre action to the root
* table and drop the promoted partitions
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_remove_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
int error = NO_ERROR, i = 0;
const char *root_name = NULL;
MOP subclass_mop = NULL;
/* sanity checks */
assert (parser && alter && pinfo);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
/* At this point, the root class of the partitioned table has been modified not to be partitioned anymore and the all
* the promoted partition names are stored in pinfo->promoted_names. step 1: do an INSERT ... SELECT to move all data
* from promoted classes into the root class step 2: drop promoted classes; */
root_name = alter->info.alter.entity_name->info.name.original;
error =
do_redistribute_partitions_data (root_name, pinfo->keycol, pinfo->promoted_names, pinfo->promoted_count,
alter->info.alter.code, false, true);
if (error != NO_ERROR)
{
return error;
}
for (i = 0; i < pinfo->promoted_count; i++)
{
subclass_mop = sm_find_class (pinfo->promoted_names[i]);
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
return error;
}
error = sm_delete_class_mop (subclass_mop, false);
if (error != NO_ERROR)
{
return error;
}
}
return error;
}
/*
* do_coalesce_partition_pre () - perform schema actions for partition
* coalescing
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "pre" action for coalescing partitions from a class is to
* promote the number of partitions requested by the user to stand alone
* tables. This request is only valid for HASH partitions. For this type
* of partitioning, the name of the partition classes is given
* automatically by CUBRID and we will promote the last created ones.
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_coalesce_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
SM_CLASS *class_ = NULL, *subclass = NULL;
MOP subclass_op = NULL;
int error;
char **names = NULL;
int names_count = 0, i = 0;
int coalesce_count = 0, partitions_count = 0;
OID *partitions = NULL;
/* sanity checks */
assert (parser && alter && pinfo);
assert (parser && alter && pinfo);
assert (alter->node_type == PT_ALTER);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
partitions_count = do_get_partition_size (pinfo->root_op);
if (partitions_count < 0)
{
return ER_FAILED;
}
else if (partitions_count == 0)
{
/* cannot coalesce partitions of a class which is not partitioned */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
return ER_FAILED;
}
coalesce_count = alter->info.alter.alter_clause.partition.size->info.value.data_value.i;
if (coalesce_count >= partitions_count)
{
/* cannot coalesce partitions of a class which is not partitioned */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
return ER_FAILED;
}
error = au_fetch_class (pinfo->root_op, &class_, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (class_->partition == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_WORK_FAILED, 0);
return ER_FAILED;
}
/* Promote the last (partition_count - coalesce_count) partitions */
names = (char **) malloc (coalesce_count * sizeof (char *));
if (names == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, coalesce_count * sizeof (char *));
return ER_FAILED;
}
partitions = (OID *) malloc (partitions_count * sizeof (OID));
if (partitions == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, partitions_count * sizeof (OID));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto error_return;
}
for (i = partitions_count - 1, names_count = 0; i >= partitions_count - coalesce_count; i--)
{
names[names_count] = (char *) malloc (DB_MAX_IDENTIFIER_LENGTH + 1);
if (names[names_count] == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(size_t) (DB_MAX_IDENTIFIER_LENGTH + 1));
error = ER_FAILED;
goto error_return;
}
sprintf (names[names_count], "%s" PARTITIONED_SUB_CLASS_TAG "p%d", sm_ch_name ((MOBJ) class_), i);
subclass_op = sm_find_class (names[names_count]);
if (subclass_op == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_return;
}
error = au_fetch_class (subclass_op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto error_return;
}
COPY_OID (&partitions[names_count], &subclass_op->oid_info.oid);
names_count++;
error = do_promote_partition (subclass);
if (error != NO_ERROR)
{
goto error_return;
}
}
error = adjust_partition_size (pinfo->root_op, pinfo->root_tmpl);
if (error != NO_ERROR)
{
goto error_return;
}
/* keep the promoted names in the partition alter context */
pinfo->promoted_names = names;
pinfo->promoted_count = names_count;
if (partitions != NULL)
{
free_and_init (partitions);
}
return NO_ERROR;
error_return:
if (names != NULL)
{
for (i = 0; i < names_count; i++)
{
free_and_init (names[i]);
}
free_and_init (names);
}
if (partitions != NULL)
{
free_and_init (partitions);
}
return error;
}
/*
* do_coalesce_partition_post () - redistribute data from removed partitions
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "post" action for coalescing partitions from a class is to
* redistribute data from partitions promoted in pre action to the root
* table and drop the promoted partitions
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_coalesce_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
int error = NO_ERROR, i = 0;
const char *root_name = NULL;
MOP subclass_mop = NULL;
OID *partitions = NULL;
SM_CLASS *smclass = NULL;
MOP class_ = NULL;
/* sanity checks */
assert (parser && alter && pinfo);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
/* At this point, the root class of the partitioned table has been modified and contains only the final partitions.
* The promoted partition names are stored in pinfo->promoted_names. step 1: redistribute data step 2: drop promoted
* classes; */
root_name = alter->info.alter.entity_name->info.name.original;
class_ = sm_find_class (root_name);
if (class_ == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto exit;
}
error =
do_redistribute_partitions_data (root_name, pinfo->keycol, pinfo->promoted_names, pinfo->promoted_count,
alter->info.alter.code, true, true);
if (error != NO_ERROR)
{
goto exit;
}
if (pinfo->promoted_count < 1)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto exit;
}
partitions = (OID *) malloc (pinfo->promoted_count * sizeof (OID));
if (partitions == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, pinfo->promoted_count * sizeof (OID));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto exit;
}
for (i = 0; i < pinfo->promoted_count; i++)
{
subclass_mop = sm_find_class (pinfo->promoted_names[i]);
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
COPY_OID (&partitions[i], &subclass_mop->oid_info.oid);
}
for (i = 0; i < pinfo->promoted_count; i++)
{
subclass_mop = sm_find_class (pinfo->promoted_names[i]);
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = sm_delete_class_mop (subclass_mop, false);
if (error != NO_ERROR)
{
goto exit;
}
}
exit:
if (partitions != NULL)
{
free_and_init (partitions);
}
return error;
}
/*
* do_reorganize_partition_pre () - perform schema actions for partition
* reorganizing
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "pre" action for reorganizing partitions from a class is to
* promote the partitions that will be reorganized to alone tables and
* to create the new partitions.
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_reorganize_partition_pre (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
PT_NODE *old_part = NULL, *new_part = NULL;
int error = NO_ERROR;
char **names = NULL;
int names_count = 0, allocated = 0, i;
const char *old_name = NULL, *new_name = NULL, *class_name = NULL;
bool found = false;
/* sanity checks */
assert (parser && alter && pinfo);
assert (alter->node_type == PT_ALTER);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
class_name = alter->info.alter.entity_name->info.name.original;
old_part = alter->info.alter.alter_clause.partition.name_list;
new_part = alter->info.alter.alter_clause.partition.parts;
/* Reorganize partitions might mean that we're dropping some of the curent partitions and changing some others to
* contain a new range/list We only want to promote partitions which will be deleted, not the ones which will be
* changed. */
while (old_part != NULL)
{
/* search old_part through new_part */
found = false;
old_name = old_part->info.name.original;
while (new_part != NULL)
{
new_name = new_part->info.parts.name->info.name.original;
if (intl_identifier_casecmp (old_name, new_name) == 0)
{
found = true;
break;
}
new_part = new_part->next;
}
new_part = alter->info.alter.alter_clause.partition.parts;
if (!found)
{
/* we will drop this partition so we have to promote it first */
if (names == NULL)
{
/* allocate */
names = (char **) malloc (10 * sizeof (char *));
if (names == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, 10 * sizeof (char *));
error = ER_FAILED;
goto error_return;
}
allocated = 10;
}
else if (names_count >= allocated - 1)
{
/* need to reallocate */
char **new_buf = (char **) realloc (names, 10 * sizeof (char *));
if (new_buf == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, 10 * sizeof (char *));
error = ER_FAILED;
goto error_return;
}
names = new_buf;
allocated += 10;
}
error = do_promote_partition_by_name (class_name, old_name, &names[names_count]);
if (error != NO_ERROR)
{
goto error_return;
}
names_count++;
}
old_part = old_part->next;
}
/* create new partitions */
error = do_create_partition (parser, alter, pinfo);
if (error != NO_ERROR)
{
goto error_return;
}
pinfo->promoted_names = names;
pinfo->promoted_count = names_count;
return NO_ERROR;
error_return:
if (names != NULL)
{
for (i = 0; i < names_count; i++)
{
free_and_init (names[i]);
}
free_and_init (names);
}
return error;
}
/*
* do_reorganize_partition_post () - redistribute data from removed partitions
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : the alter node
* pinfo (in/out) : the partition context for the alter operation
*
* Note: The "post" action for reorganizing partitions from a class is to
* redistribute data from promoted partitions in pre action to the new
* partitions and drop the promoted ones.
* See notes for do_alter_partitioning_pre, do_alter_partitioning_post
* to understand the nature of post/pre actions
*/
static int
do_reorganize_partition_post (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
int error = NO_ERROR, i = 0;
const char *root_name = NULL;
MOP subclass_mop = NULL;
PT_NODE *name = NULL;
bool insert = false, update = false;
/* sanity checks */
assert (parser && alter && pinfo);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
/* At this point, the root class of the partitioned table has been modified to the new partitioning schema. We might
* have promoted some partitions and also changed other partitions to a new range or list. We have to run an INSERT
* ... SELECT for the promoted partitions and an UPDATE for the ones that we only changed the schema (in order to
* redistribute the data) */
root_name = alter->info.alter.entity_name->info.name.original;
if (pinfo->promoted_names != NULL)
{
/* we have at least one promoted partition */
insert = true;
}
name = alter->info.alter.alter_clause.partition.parts;
while (name)
{
if (name->flag.partition_pruned != 0)
{
/* at least one partitions has been changed */
update = true;
break;
}
name = name->next;
}
error =
do_redistribute_partitions_data (root_name, pinfo->keycol, pinfo->promoted_names, pinfo->promoted_count,
alter->info.alter.code, update, insert);
if (error != NO_ERROR)
{
return error;
}
for (i = 0; i < pinfo->promoted_count; i++)
{
subclass_mop = sm_find_class (pinfo->promoted_names[i]);
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
return error;
}
error = sm_delete_class_mop (subclass_mop, false);
if (error != NO_ERROR)
{
return error;
}
}
return NO_ERROR;
}
#if defined (ENABLE_UNUSED_FUNCTION)
/*
* do_analyze_partition () - update statistics on partitions
* return : error code or NO_ERROR
* parser (in) : parser context
* alter (in) : alter node
*/
static int
do_analyze_partition (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
PT_NODE *name = NULL;
int error = NO_ERROR;
/* sanity check */
assert (parser != NULL && alter != NULL);
CHECK_2ARGS_ERROR (parser, alter);
name = alter->info.alter.alter_clause.partition.name_list;
if (name != NULL)
{
/* update statistics for given names */
while (name)
{
assert (name->info.name.db_object != NULL);
error = sm_update_statistics (name->info.name.db_object, STATS_WITH_SAMPLING);
if (error != NO_ERROR)
{
return error;
}
name = name->next;
}
}
else
{
/* update statistics on all partitions */
SM_CLASS *class_ = NULL, *subclass = NULL;
DB_OBJLIST *obj = NULL;
error = au_fetch_class (pinfo->root_op, &class_, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
error = sm_update_statistics (pinfo->root_op, STATS_WITH_SAMPLING);
if (error != NO_ERROR)
{
return error;
}
for (obj = class_->users; obj != NULL; obj = obj->next)
{
error = au_fetch_class (obj->op, &subclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (subclass->partition == NULL)
{
/* not partitioned */
continue;
}
error = sm_update_statistics (obj->op, STATS_WITH_SAMPLING);
if (error != NO_ERROR)
{
return error;
}
}
}
return error;
}
#endif
/*
* do_promote_partition_list () -
* return: error code or NO_ERROR
* class_(in) : root class mop
* name_list(in): partition names
*
* Note: Currently, only the "local" indexes are promoted. After this
* operation, global indexes (like unique indexes and primary keys)
* will not be automatically created on the promoted table
*/
static int
do_promote_partition_list (PARSER_CONTEXT * parser, PT_NODE * alter, SM_PARTITION_ALTER_INFO * pinfo)
{
int error = NO_ERROR;
char subclass_name[DB_MAX_IDENTIFIER_LENGTH];
SM_CLASS *smclass = NULL, *smsubclass = NULL;
MOP subclass = NULL;
PT_NODE *name = NULL;
PT_NODE *name_list = NULL;
DB_OBJLIST *obj = NULL;
int promoted_count = 0, partitions_count = 0;
OID *partitions = NULL;
/* sanity check */
assert (parser != NULL && alter != NULL && pinfo != NULL);
CHECK_3ARGS_ERROR (parser, alter, pinfo);
name_list = alter->info.alter.alter_clause.partition.name_list;
if (name_list == NULL)
{
/* nothing to do */
return NO_ERROR;
}
if (pinfo->root_op == NULL)
{
assert (false);
return ER_FAILED;
}
error = au_fetch_class (pinfo->root_op, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (!smclass->partition)
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
/* count the total number of partitions */
partitions_count = 0;
for (obj = smclass->users; obj != NULL; obj = obj->next)
{
error = au_fetch_class (obj->op, &smsubclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (!smsubclass->partition)
{
continue;
}
partitions_count++;
}
partitions = (OID *) malloc (partitions_count * sizeof (OID));
if (partitions == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, partitions_count * sizeof (OID));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
promoted_count = 0;
for (name = name_list; name != NULL; name = name->next)
{
sprintf (subclass_name, "%s" PARTITIONED_SUB_CLASS_TAG "%s", sm_ch_name ((MOBJ) smclass),
name->info.name.original);
/* Before promoting, make sure to recreate filter and function indexes because the expression used in these
* indexes depends on the partitioned class name, not on the partition name */
error = do_recreate_renamed_class_indexes (parser, sm_ch_name ((MOBJ) smclass), subclass_name);
if (error != NO_ERROR)
{
goto exit;
}
subclass = sm_find_class (subclass_name);
if (subclass == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = au_fetch_class (subclass, &smsubclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto exit;
}
error = do_promote_partition (smsubclass);
if (error != NO_ERROR)
{
goto exit;
}
COPY_OID (&partitions[promoted_count], &subclass->oid_info.oid);
promoted_count++;
}
assert (partitions_count >= promoted_count);
if (partitions_count - promoted_count == 0)
{
/* All partitions were promoted so mark this class as not being partitioned. Through the pinfo object, we have
* access to the root class template on which an edit operation was started. We can use it to perform the update. */
assert (pinfo->root_tmpl != NULL);
classobj_free_partition_info (pinfo->root_tmpl->partition);
pinfo->root_tmpl->partition = NULL;
}
else
{
/* smclass might not be valid here, we need to re fetch it */
error = au_fetch_class (pinfo->root_op, &smclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
goto exit;
}
error = adjust_partition_range (smclass->users);
if (error != NO_ERROR)
{
goto exit;
}
error = adjust_partition_size (pinfo->root_op, pinfo->root_tmpl);
if (error != NO_ERROR)
{
goto exit;
}
}
exit:
if (partitions != NULL)
{
free_and_init (partitions);
}
return error;
}
/*
* do_promote_partition_by_name () - promote a partition
* return : error code or NO_ERROR
* class_name (in) : root class name
* part_num (in) : partition number/indicator
* partition_name (in/out) : full partition name
*/
static int
do_promote_partition_by_name (const char *class_name, const char *part_num, char **partition_name)
{
int error = NO_ERROR;
MOP subclass = NULL;
SM_CLASS *smsmclass = NULL;
char name[DB_MAX_IDENTIFIER_LENGTH];
assert (class_name != NULL && part_num != NULL);
CHECK_2ARGS_ERROR (class_name, part_num);
sprintf (name, "%s" PARTITIONED_SUB_CLASS_TAG "%s", class_name, part_num);
subclass = sm_find_class (name);
if (subclass == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
error = au_fetch_class (subclass, &smsmclass, AU_FETCH_READ, AU_SELECT);
if (error != NO_ERROR)
{
return error;
}
error = do_promote_partition (smsmclass);
if (error != NO_ERROR)
{
return error;
}
if (partition_name != NULL)
{
*partition_name = strdup (name);
if (*partition_name == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (strlen (name) + 1) * sizeof (char));
return ER_FAILED;
}
}
return NO_ERROR;
}
/*
* do_promote_partition () - promote a partition
* return : error code or NO_ERROR
* class_ (in) : class to promote
*/
static int
do_promote_partition (SM_CLASS * class_)
{
MOP subclass_mop = NULL;
int error = NO_ERROR;
SM_CLASS *current = NULL;
DB_CTMPL *ctemplate = NULL;
SM_ATTRIBUTE *smattr = NULL;
bool has_pk = false;
CHECK_1ARG_ERROR (class_);
if (class_->partition == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_NOT_EXIST, 0);
return ER_PARTITION_NOT_EXIST;
}
subclass_mop = sm_find_class (sm_ch_name ((MOBJ) class_));
if (subclass_mop == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
ctemplate = dbt_edit_class (subclass_mop);
if (ctemplate == NULL)
{
return ER_FAILED;
}
current = ctemplate->current;
assert (current != NULL);
/* Copy attributes in the order in which they were defined in order to preserve the order from the root class */
error = classobj_copy_attlist (current->ordered_attributes, NULL, 1, &ctemplate->attributes);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
for (smattr = ctemplate->attributes; smattr != NULL; smattr = (SM_ATTRIBUTE *) smattr->header.next)
{
/* make attributes point to the subclass, not the parent */
smattr->class_mop = subclass_mop;
}
error = classobj_copy_attlist (current->class_attributes, NULL, 0, &ctemplate->class_attributes);
if (error != NO_ERROR)
{
dbt_abort_class (ctemplate);
return error;
}
for (smattr = ctemplate->class_attributes; smattr != NULL; smattr = (SM_ATTRIBUTE *) smattr->header.next)
{
/* make attributes point to the subclass, not the parent */
smattr->class_mop = subclass_mop;
}
/* Make sure we do not copy anything that actually belongs to the root class (the class to which this partition
* belongs to). This includes: auto_increment flags, unique indexes, primary keys, and foreign keys */
for (smattr = ctemplate->attributes; smattr != NULL; smattr = (SM_ATTRIBUTE *) smattr->header.next)
{
/* reset flags that belong to the root partitioned table */
smattr->auto_increment = NULL;
smattr->flags &= ~(SM_ATTFLAG_AUTO_INCREMENT);
if ((smattr->flags & SM_ATTFLAG_PRIMARY_KEY) != 0)
{
smattr->flags &= ~(SM_ATTFLAG_PRIMARY_KEY);
smattr->flags &= ~(SM_ATTFLAG_NON_NULL);
has_pk = true;
}
smattr->flags &= ~(SM_ATTFLAG_UNIQUE);
smattr->flags &= ~(SM_ATTFLAG_REVERSE_UNIQUE);
smattr->flags &= ~(SM_ATTFLAG_FOREIGN_KEY);
smattr->flags &= ~(SM_ATTFLAG_PARTITION_KEY);
}
ctemplate->inheritance = NULL;
ctemplate->methods = NULL;
ctemplate->resolutions = NULL;
ctemplate->class_methods = NULL;
ctemplate->class_resolutions = NULL;
ctemplate->method_files = NULL;
ctemplate->loader_commands = NULL;
ctemplate->query_spec = NULL;
ctemplate->instance_attributes = NULL;
ctemplate->shared_attributes = NULL;
ctemplate->partition_parent_atts = NULL;
ctemplate->triggers = NULL;
classobj_free_partition_info (ctemplate->partition);
ctemplate->partition = NULL;
if (ctemplate->properties != NULL)
{
if (has_pk)
{
classobj_drop_prop (ctemplate->properties, SM_PROPERTY_PRIMARY_KEY);
classobj_drop_prop (ctemplate->properties, SM_PROPERTY_NOT_NULL);
}
classobj_drop_prop (ctemplate->properties, SM_PROPERTY_FOREIGN_KEY);
classobj_drop_prop (ctemplate->properties, SM_PROPERTY_REVERSE_UNIQUE);
classobj_drop_prop (ctemplate->properties, SM_PROPERTY_UNIQUE);
}
if (dbt_finish_class (ctemplate) == NULL)
{
dbt_abort_class (ctemplate);
assert (er_errid () != NO_ERROR);
error = er_errid ();
if (error == NO_ERROR)
{
error = ER_FAILED;
}
return error;
}
return error;
}
/*
* validate_attribute_domain() - This checks an attribute to make sure
* that it makes sense
* return: Error code
* parser(in): Parser context
* attribute(in): Parse tree of an attribute or method
* check_zero_precision(in): Do not permit zero precision if true
*
* Note: Error reporting system
*/
static int
validate_attribute_domain (PARSER_CONTEXT * parser, PT_NODE * attribute, const bool check_zero_precision)
{
int error = NO_ERROR;
if (attribute == NULL)
{
pt_record_error (parser, parser->statement_number, __LINE__, 0, "system error - NULL attribute node", NULL);
}
else
{
if (attribute->type_enum == PT_TYPE_NONE)
{
pt_record_error (parser, parser->statement_number, attribute->line_number, attribute->column_number,
"system error - attribute type not set", NULL);
}
else
{
PT_NODE *dtyp = attribute->data_type;
if (dtyp)
{
int p = attribute->data_type->info.data_type.precision;
switch (attribute->type_enum)
{
case PT_TYPE_FLOAT:
case PT_TYPE_DOUBLE:
if (p != DB_DEFAULT_PRECISION && (p < 0 || p > DB_MAX_NUMERIC_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_NUMERIC_PRECISION);
}
break;
case PT_TYPE_NUMERIC:
if (p != DB_DEFAULT_PRECISION
&& (p < 0 || (p == 0 && check_zero_precision) || p > DB_MAX_NUMERIC_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_NUMERIC_PRECISION);
}
break;
case PT_TYPE_BIT:
if (p != DB_DEFAULT_PRECISION
&& (p < 0 || (p == 0 && check_zero_precision) || p > DB_MAX_BIT_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_BIT_PRECISION);
}
break;
case PT_TYPE_VARBIT:
if (p != DB_DEFAULT_PRECISION
&& (p < 0 || (p == 0 && check_zero_precision) || p > DB_MAX_VARBIT_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_VARBIT_PRECISION);
}
break;
case PT_TYPE_CHAR:
if (p != DB_DEFAULT_PRECISION
&& (p < 0 || (p == 0 && check_zero_precision) || p > DB_MAX_CHAR_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_CHAR_PRECISION);
}
break;
case PT_TYPE_VARCHAR:
if (p != DB_DEFAULT_PRECISION
&& (p < 0 || (p == 0 && check_zero_precision) || p > DB_MAX_VARCHAR_PRECISION))
{
PT_ERRORmf3 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INV_PREC, p, 0,
DB_MAX_VARCHAR_PRECISION);
}
break;
case PT_TYPE_SET:
case PT_TYPE_MULTISET:
case PT_TYPE_SEQUENCE:
{
PT_NODE *elem;
for (elem = dtyp; elem != NULL; elem = elem->next)
{
if (PT_IS_LOB_TYPE (elem->type_enum))
{
PT_ERRORmf2 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC,
MSGCAT_SEMANTIC_INVALID_SET_ELEMENT, pt_show_type_enum (attribute->type_enum),
pt_show_type_enum (elem->type_enum));
break;
}
}
}
break;
case PT_TYPE_ENUMERATION:
(void) pt_check_enum_data_type (parser, dtyp);
break;
default:
break;
}
}
}
}
if (error == NO_ERROR)
{
if (pt_has_error (parser))
{
error = ER_PT_SEMANTIC;
}
}
return error;
}
static const char *
get_attr_name (PT_NODE * attribute)
{
/* First try the derived name and then the original name. For example: create view a_view as select a av1, a av2, b
* bv from a_tbl; */
return (attribute->info.attr_def.attr_name->alias_print
? attribute->info.attr_def.attr_name->alias_print : attribute->info.attr_def.attr_name->info.name.original);
}
/*
* do_add_attribute() - Adds an attribute to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* attribute(in/out): Attribute to add
* constraints(in/out): the constraints of the class
* error_on_not_normal(in): whether to flag an error on class and shared attributes or not
*
* Note : The class object is modified
*/
static int
do_add_attribute (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attribute, PT_NODE * constraints,
bool error_on_not_normal)
{
const char *attr_name = NULL;
int meta, shared;
DB_VALUE stack_value;
DB_VALUE *default_value = &stack_value;
DB_DEFAULT_EXPR_TYPE on_update_expr = DB_DEFAULT_NONE;
int error = NO_ERROR;
TP_DOMAIN *attr_db_domain;
MOP auto_increment_obj = NULL;
SM_ATTRIBUTE *att = NULL;
SM_NAME_SPACE name_space = ID_NULL;
bool add_first = false;
const char *add_after_attr = NULL;
PT_NODE *cnstr, *pk_attr, *comment;
DB_DEFAULT_EXPR default_expr;
PARSER_VARCHAR *comment_str = NULL;
db_make_null (&stack_value);
attr_name = get_attr_name (attribute);
meta = (attribute->info.attr_def.attr_type == PT_META_ATTR);
shared = (attribute->info.attr_def.attr_type == PT_SHARED);
if (error_on_not_normal && attribute->info.attr_def.attr_type != PT_NORMAL)
{
ERROR1 (error, ER_SM_ONLY_NORMAL_ATTRIBUTES, attr_name);
goto error_exit;
}
/* set default codeset and collation for attribute if none is specified */
if (ctemplate->current != NULL && attribute->node_type == PT_ATTR_DEF && PT_HAS_COLLATION (attribute->type_enum))
{
pt_attr_check_default_cs_coll (parser, attribute, -1, ctemplate->current->collation_id);
}
if (validate_attribute_domain (parser, attribute, smt_get_class_type (ctemplate) == SM_CLASS_CT ? true : false))
{
/* validate_attribute_domain() is assumed to issue whatever messages are pertinent. */
error = ER_GENERIC_ERROR;
goto error_exit;
}
assert (default_value == &stack_value);
error = get_att_default_from_def (parser, attribute, &default_value, ctemplate->name);
if (error != NO_ERROR)
{
goto error_exit;
}
if (default_value && DB_IS_NULL (default_value))
{
/* don't allow a default value of NULL for NOT NULL constrained columns */
if (attribute->info.attr_def.constrain_not_null)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_HAVE_NOTNULL_DEFAULT_NULL, 1, attr_name);
error = ER_CANNOT_HAVE_NOTNULL_DEFAULT_NULL;
goto error_exit;
}
/* don't allow a default value of NULL in new PK constraint */
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
if (cnstr->info.constraint.type == PT_CONSTRAIN_PRIMARY_KEY)
{
break;
}
}
if (cnstr != NULL)
{
for (pk_attr = cnstr->info.constraint.un.primary_key.attrs; pk_attr != NULL; pk_attr = pk_attr->next)
{
if (intl_identifier_casecmp (pk_attr->info.name.original, attr_name) == 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_HAVE_PK_DEFAULT_NULL, 1, attr_name);
error = ER_CANNOT_HAVE_PK_DEFAULT_NULL;
goto error_exit;
}
}
}
}
attr_db_domain = pt_node_to_db_domain (parser, attribute, ctemplate->name);
if (attr_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
error = check_default_on_update_clause (parser, attribute);
if (error != NO_ERROR)
{
goto error_exit;
}
error = get_att_order_from_def (attribute, &add_first, &add_after_attr);
if (error != NO_ERROR)
{
goto error_exit;
}
if (meta)
{
name_space = ID_CLASS_ATTRIBUTE;
}
else if (shared)
{
name_space = ID_SHARED_ATTRIBUTE;
}
else
{
name_space = ID_ATTRIBUTE;
}
on_update_expr = attribute->info.attr_def.on_update;
pt_get_default_expression_from_data_default_node (parser, attribute->info.attr_def.data_default, &default_expr);
default_value = &stack_value;
error = smt_add_attribute_w_dflt_w_order (ctemplate, attr_name, NULL, attr_db_domain, default_value, name_space,
add_first, add_after_attr, &default_expr, &on_update_expr, NULL);
db_value_clear (&stack_value);
/* Does the attribute belong to a NON_NULL constraint? */
if (error == NO_ERROR)
{
if (attribute->info.attr_def.constrain_not_null)
{
error = dbt_constrain_non_null (ctemplate, attr_name, meta, 1);
}
}
/* create & set auto_increment attribute's serial object */
if (error == NO_ERROR && !meta && !shared)
{
if (attribute->info.attr_def.auto_increment)
{
error = do_set_auto_increment (parser, ctemplate, attr_name, attribute, &att);
}
}
comment = attribute->info.attr_def.comment;
if (error == NO_ERROR && comment != NULL)
{
/* skip finding attribute if att is already available */
if (att == NULL)
{
error = smt_find_attribute (ctemplate, attr_name, 0, &att);
}
if (error == NO_ERROR)
{
assert (comment->node_type == PT_VALUE);
comment_str = comment->info.value.data_value.str;
att->comment = ws_copy_string ((char *) pt_get_varchar_bytes (comment_str));
if (att->comment == NULL)
{
error = (er_errid () != NO_ERROR) ? er_errid () : ER_FAILED;
goto error_exit;
}
}
}
return error;
error_exit:
db_value_clear (&stack_value);
return error;
}
/*
* do_add_attribute_from_select_column() - Adds an attribute to a class object
* return: Error code
* ctemplate(in/out): Class template
* column(in): Attribute to add, as specified by a SELECT column in a
* CREATE ... AS SELECT statement. The original SELECT column's
* NOT NULL and default value need to be copied.
*
* Note : The class object is modified
*/
static int
do_add_attribute_from_select_column (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, DB_QUERY_TYPE * column)
{
DB_VALUE default_value;
int error = NO_ERROR;
const char *attr_name;
MOP class_obj = NULL;
DB_DEFAULT_EXPR *default_expr = NULL;
DB_DEFAULT_EXPR_TYPE *on_update_default_expr = NULL;
db_make_null (&default_value);
if (column == NULL || column->domain == NULL)
{
error = ER_FAILED;
goto error_exit;
}
if (column->domain->type->id == DB_TYPE_NULL)
{
error = ER_CREATE_AS_SELECT_NULL_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
goto error_exit;
}
else if (TP_IS_SET_TYPE (column->domain->type->id))
{
TP_DOMAIN *elem;
for (elem = column->domain->setdomain; elem != NULL; elem = elem->next)
{
if (TP_DOMAIN_TYPE (elem) == DB_TYPE_BLOB || TP_DOMAIN_TYPE (elem) == DB_TYPE_CLOB)
{
PT_TYPE_ENUM elem_type, set_type;
elem_type = pt_db_to_type_enum (TP_DOMAIN_TYPE (elem));
set_type = pt_db_to_type_enum (column->domain->type->id);
PT_ERRORmf2 (parser, NULL, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_SET_ELEMENT,
pt_show_type_enum (set_type), pt_show_type_enum (elem_type));
error = ER_PT_SEMANTIC;
goto error_exit;
}
}
}
attr_name = db_query_format_name (column);
if (column->spec_name != NULL)
{
class_obj = sm_find_class (column->spec_name);
if (class_obj == NULL)
{
goto error_exit;
}
error = sm_att_default_value (class_obj, column->attr_name, &default_value, &default_expr,
&on_update_default_expr);
if (error != NO_ERROR)
{
goto error_exit;
}
}
error = smt_add_attribute_w_dflt (ctemplate, attr_name, NULL, column->domain, &default_value, ID_ATTRIBUTE,
default_expr, on_update_default_expr, NULL);
if (error != NO_ERROR)
{
goto error_exit;
}
if (class_obj != NULL)
{
if (sm_att_constrained (class_obj, column->attr_name, SM_ATTFLAG_NON_NULL))
{
error = dbt_constrain_non_null (ctemplate, attr_name, 0, 1);
}
}
return error;
error_exit:
db_value_clear (&default_value);
return error;
}
static DB_QUERY_TYPE *
query_get_column_with_name (DB_QUERY_TYPE * query_columns, const char *name)
{
DB_QUERY_TYPE *column;
char real_name[SM_MAX_IDENTIFIER_LENGTH];
char column_name[SM_MAX_IDENTIFIER_LENGTH];
if (query_columns == NULL)
{
return NULL;
}
sm_downcase_name (name, real_name, SM_MAX_IDENTIFIER_LENGTH);
for (column = query_columns; column != NULL; column = db_query_format_next (column))
{
sm_downcase_name (db_query_format_name (column), column_name, SM_MAX_IDENTIFIER_LENGTH);
if (intl_identifier_casecmp (real_name, column_name) == 0)
{
return column;
}
}
return NULL;
}
static PT_NODE *
get_attribute_with_name (PT_NODE * atts, const char *name)
{
PT_NODE *crt_attr;
char real_name[SM_MAX_IDENTIFIER_LENGTH];
char attribute_name[SM_MAX_IDENTIFIER_LENGTH];
if (atts == NULL)
{
return NULL;
}
sm_downcase_name (name, real_name, SM_MAX_IDENTIFIER_LENGTH);
for (crt_attr = atts; crt_attr != NULL; crt_attr = crt_attr->next)
{
sm_downcase_name (get_attr_name (crt_attr), attribute_name, SM_MAX_IDENTIFIER_LENGTH);
if (intl_identifier_casecmp (real_name, attribute_name) == 0)
{
return crt_attr;
}
}
return NULL;
}
/*
* do_add_attributes() - Adds attributes to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* atts(in/out): Attributes to add
* constraints(in/out): the constraints of the class
* create_select_columns(in): the column list of a select for
* CREATE ... AS SELECT statements
*
* Note : The class object is modified
*/
int
do_add_attributes (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * atts, PT_NODE * constraints,
DB_QUERY_TYPE * create_select_columns)
{
PT_NODE *crt_attr;
DB_QUERY_TYPE *column;
int error = NO_ERROR;
crt_attr = atts;
while (crt_attr)
{
const char *const attr_name = get_attr_name (crt_attr);
if (query_get_column_with_name (create_select_columns, attr_name) == NULL)
{
error = do_add_attribute (parser, ctemplate, crt_attr, constraints, false);
if (error != NO_ERROR)
{
return error;
}
}
crt_attr = crt_attr->next;
}
for (column = create_select_columns; column != NULL; column = db_query_format_next (column))
{
const char *const col_name = db_query_format_name (column);
crt_attr = get_attribute_with_name (atts, col_name);
if (crt_attr != NULL)
{
error = do_add_attribute (parser, ctemplate, crt_attr, constraints, true);
if (error != NO_ERROR)
{
return error;
}
}
else
{
error = do_add_attribute_from_select_column (parser, ctemplate, column);
if (error != NO_ERROR)
{
return error;
}
}
}
return error;
}
/*
* map_pt_to_sm_action() -
* return: SM_FOREIGN_KEY_ACTION
* action(in): Action to map
*
* Note:
*/
static SM_FOREIGN_KEY_ACTION
map_pt_to_sm_action (const PT_MISC_TYPE action)
{
switch (action)
{
case PT_RULE_CASCADE:
return SM_FOREIGN_KEY_CASCADE;
case PT_RULE_RESTRICT:
return SM_FOREIGN_KEY_RESTRICT;
case PT_RULE_NO_ACTION:
return SM_FOREIGN_KEY_NO_ACTION;
case PT_RULE_SET_NULL:
return SM_FOREIGN_KEY_SET_NULL;
default:
break;
}
return SM_FOREIGN_KEY_NO_ACTION;
}
/*
* add_foreign_key() -
* return: Error code
* ctemplate(in/out): Class template
* cnstr(in): Constraint name
* att_names(in): Key attribute names
*
* Note:
*/
static int
add_foreign_key (DB_CTMPL * ctemplate, const PT_NODE * cnstr, const char **att_names)
{
PT_FOREIGN_KEY_INFO *fk_info;
const char *constraint_name = NULL;
char **ref_attrs = NULL;
int i, n_atts, n_ref_atts;
PT_NODE *p;
int error = NO_ERROR;
const char *comment = NULL;
size_t buf_size;
fk_info = (PT_FOREIGN_KEY_INFO *) (&cnstr->info.constraint.un.foreign_key);
n_atts = pt_length_of_list (fk_info->attrs);
i = 0;
for (p = fk_info->attrs; p; p = p->next)
{
att_names[i++] = p->info.name.original;
}
int param_dedup_level = prm_get_integer_value (PRM_ID_DEDUPLICATE_KEY_LEVEL);
if (param_dedup_level == DEDUPLICATE_ABSOLUTE_DISABLE)
{
fk_info->deduplicate_level = DEDUPLICATE_KEY_LEVEL_OFF;
}
else
{
if (fk_info->deduplicate_level == DEDUPLICATE_OPTION_AUTO)
{
fk_info->deduplicate_level = param_dedup_level;
}
if (fk_info->deduplicate_level != DEDUPLICATE_KEY_LEVEL_OFF)
{
SM_CLASS *class_ = NULL;
SM_CLASS_CONSTRAINT *free_cons = NULL;
SM_CLASS_CONSTRAINT *check_cons;
if (ctemplate->op != NULL)
{
error = au_fetch_class (ctemplate->op, &class_, AU_FETCH_READ, AU_INDEX);
if (error != NO_ERROR)
{
return error;
}
check_cons = class_->constraints;
}
else
{
error = classobj_make_class_constraints (ctemplate->properties, ctemplate->attributes, &check_cons);
if (error != NO_ERROR)
{
return error;
}
free_cons = check_cons;
}
att_names[i] = NULL;
if (check_cons == NULL || !classobj_check_attr_in_unique_constraint (check_cons, (char **) att_names, NULL))
{
// adjust for FK: add deduplicate_key_attr column
att_names[i++] = dk_get_deduplicate_key_attr_name (fk_info->deduplicate_level);
}
if (free_cons != NULL)
{
classobj_free_class_constraints (free_cons);
}
}
}
att_names[i] = NULL;
if (fk_info->referenced_attrs != NULL)
{
n_ref_atts = pt_length_of_list (fk_info->referenced_attrs);
buf_size = (n_ref_atts + 1) * sizeof (char *);
ref_attrs = (char **) malloc (buf_size);
if (ref_attrs == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
return error;
}
i = 0;
for (p = fk_info->referenced_attrs; p; p = p->next)
{
ref_attrs[i++] = (char *) p->info.name.original;
}
ref_attrs[i] = NULL;
}
/* Get the constraint name (if supplied) */
if (cnstr->info.constraint.name)
{
constraint_name = cnstr->info.constraint.name->info.name.original;
}
if (cnstr->info.constraint.comment != NULL)
{
assert (cnstr->info.constraint.comment->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (cnstr->info.constraint.comment);
}
error =
dbt_add_foreign_key (ctemplate, constraint_name, att_names, fk_info->referenced_class->info.name.original,
(const char **) ref_attrs, map_pt_to_sm_action (fk_info->delete_action),
map_pt_to_sm_action (fk_info->update_action), comment);
free_and_init (ref_attrs);
return error;
}
/*
* do_add_constraints() - This extern routine adds constraints
* to a class object.
* return: Error code
* ctemplate(in/out): Class template
* constraints(in): Constraints to add
*
* Note : Class object is modified
*/
int
do_add_constraints (DB_CTMPL * ctemplate, PT_NODE * constraints)
{
int error = NO_ERROR;
PT_NODE *cnstr;
int max_attrs = 0;
char **att_names = NULL;
size_t buf_size;
/* Find the size of the largest UNIQUE constraint list and allocate a character array large enough to contain it. */
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
if (cnstr->info.constraint.type == PT_CONSTRAIN_UNIQUE)
{
max_attrs = MAX (max_attrs, pt_length_of_list (cnstr->info.constraint.un.unique.attrs));
}
if (cnstr->info.constraint.type == PT_CONSTRAIN_PRIMARY_KEY)
{
max_attrs = MAX (max_attrs, pt_length_of_list (cnstr->info.constraint.un.primary_key.attrs));
}
if (cnstr->info.constraint.type == PT_CONSTRAIN_FOREIGN_KEY)
{
max_attrs = MAX (max_attrs, pt_length_of_list (cnstr->info.constraint.un.foreign_key.attrs));
}
}
if (max_attrs > 0)
{
// If there is an FK, one more space is allocated in advance because deduplicate_key_attr information will be added.
// max_attrs + [ deduplicate_key_attr ] + NULL
buf_size = (max_attrs + 2) * sizeof (char *);
att_names = (char **) malloc (buf_size);
if (att_names == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
error = ER_OUT_OF_VIRTUAL_MEMORY;
}
else
{
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
if (cnstr->info.constraint.type == PT_CONSTRAIN_UNIQUE)
{
PT_NODE *p;
int i, n_atts;
int class_attributes = 0;
char *constraint_name = NULL;
DB_CONSTRAINT_TYPE constraint_type = DB_CONSTRAINT_UNIQUE;
int *asc_desc = NULL;
char *comment = NULL;
n_atts = pt_length_of_list (cnstr->info.constraint.un.unique.attrs);
asc_desc = (int *) malloc (n_atts * sizeof (int));
if (asc_desc == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (n_atts * sizeof (int)));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto constraint_error;
}
if (PT_NAME_INFO_IS_FLAGED (cnstr->info.constraint.un.unique.attrs, PT_NAME_INFO_DESC))
{
constraint_type = DB_CONSTRAINT_REVERSE_UNIQUE;
}
i = 0;
for (p = cnstr->info.constraint.un.unique.attrs; p; p = p->next)
{
asc_desc[i] = PT_NAME_INFO_IS_FLAGED (p, PT_NAME_INFO_DESC) ? 1 : 0;
att_names[i++] = (char *) p->info.name.original;
/* Determine if the unique constraint is being applied to class or normal attributes. The way
* the parser currently works, all multi-column constraints will be on normal attributes and it's
* therefore impossible for a constraint to contain both class and normal attributes. */
if (p->info.name.meta_class == PT_META_ATTR)
{
class_attributes = 1;
}
/* We keep DB_CONSTRAINT_REVERSE_UNIQUE only if all columns are marked as DESC. */
if (!PT_NAME_INFO_IS_FLAGED (p, PT_NAME_INFO_DESC))
{
constraint_type = DB_CONSTRAINT_UNIQUE;
}
}
att_names[i] = NULL;
/* Get the constraint name (if supplied) */
if (cnstr->info.constraint.name)
{
constraint_name = (char *) cnstr->info.constraint.name->info.name.original;
}
constraint_name =
sm_produce_constraint_name_tmpl (ctemplate, constraint_type, (const char **) att_names, asc_desc,
constraint_name);
if (constraint_name == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
free_and_init (asc_desc);
goto constraint_error;
}
if (cnstr->info.constraint.comment != NULL)
{
assert (cnstr->info.constraint.comment->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (cnstr->info.constraint.comment);
}
error = smt_add_constraint (ctemplate, constraint_type, constraint_name, (const char **) att_names,
asc_desc, NULL, class_attributes, NULL, NULL, NULL, comment,
SM_NORMAL_INDEX);
free_and_init (constraint_name);
free_and_init (asc_desc);
if (error != NO_ERROR)
{
goto constraint_error;
}
}
else if (cnstr->info.constraint.type == PT_CONSTRAIN_PRIMARY_KEY)
{
PT_NODE *p;
int i, n_atts;
int class_attributes = 0;
char *constraint_name = NULL;
int *asc_desc = NULL;
char *comment = NULL;
n_atts = pt_length_of_list (cnstr->info.constraint.un.primary_key.attrs);
asc_desc = (int *) malloc (n_atts * sizeof (int));
if (asc_desc == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (n_atts * sizeof (int)));
error = ER_OUT_OF_VIRTUAL_MEMORY;
goto constraint_error;
}
i = 0;
for (p = cnstr->info.constraint.un.primary_key.attrs; p; p = p->next)
{
asc_desc[i] = PT_NAME_INFO_IS_FLAGED (p, PT_NAME_INFO_DESC) ? 1 : 0;
att_names[i++] = (char *) p->info.name.original;
/* Determine if the unique constraint is being applied to class or normal attributes. The way
* the parser currently works, all multi-column constraints will be on normal attributes and it's
* therefore impossible for a constraint to contain both class and normal attributes. */
if (p->info.name.meta_class == PT_META_ATTR)
{
class_attributes = 1;
}
}
att_names[i] = NULL;
/* Get the constraint name (if supplied) */
if (cnstr->info.constraint.name)
{
constraint_name = (char *) cnstr->info.constraint.name->info.name.original;
}
constraint_name =
sm_produce_constraint_name_tmpl (ctemplate, DB_CONSTRAINT_PRIMARY_KEY, (const char **) att_names,
asc_desc, constraint_name);
if (constraint_name == NULL)
{
free_and_init (asc_desc);
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto constraint_error;
}
if (cnstr->info.constraint.comment != NULL)
{
assert (cnstr->info.constraint.comment->node_type == PT_VALUE);
comment = (char *) PT_VALUE_GET_BYTES (cnstr->info.constraint.comment);
}
error = smt_add_constraint (ctemplate, DB_CONSTRAINT_PRIMARY_KEY, constraint_name,
(const char **) att_names, asc_desc, NULL, class_attributes, NULL, NULL,
NULL, comment, SM_NORMAL_INDEX);
free_and_init (constraint_name);
free_and_init (asc_desc);
if (error != NO_ERROR)
{
goto constraint_error;
}
}
else if (cnstr->info.constraint.type == PT_CONSTRAIN_FOREIGN_KEY)
{
error = add_foreign_key (ctemplate, cnstr, (const char **) att_names);
if (error != NO_ERROR)
{
goto constraint_error;
}
}
}
free_and_init (att_names);
}
}
return (error);
/* error handler */
constraint_error:
if (att_names)
{
free_and_init (att_names);
}
return (error);
}
/*
* do_check_fk_constraints_internal () - Checks that foreign key constraints
* are consistent with the schema.
* The routine only works when a new class is created or when it is altered
* with a single change; it might not work in the future if a class will be
* altered with multiple changes in a single call.
*
* Currently the following checks are performed:
* SET NULL referential actions must not contradict the attributes' domains
* (the attributes cannot have a NOT NULL constraint as they cannot be
* NULL).
*
* SET NULL actions are not yet supported on partitioned tables.
*
* In the future the function should also check for foreign keys that have
* cascading referential actions and either represent cycles in the schema or
* represent "race" updates (the same attribute can be affected on two
* separate cascading action paths; the results are undefined).
*
* return: Error code
* ctemplate(in/out): Class template
* constraints(in): List of all the class constraints that have been added.
* Currently the function does not support checking for
* consistency when NOT NULL constraints are added.
*
* Note : The class object is not modified
*/
static int
do_check_fk_constraints_internal (DB_CTMPL * ctemplate, PT_NODE * constraints, bool is_partitioned)
{
int error = NO_ERROR;
PT_NODE *cnstr;
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
PT_NODE *attr;
PT_FOREIGN_KEY_INFO *fk_info;
if (cnstr->info.constraint.type != PT_CONSTRAIN_FOREIGN_KEY)
{
continue;
}
fk_info = (PT_FOREIGN_KEY_INFO *) (&cnstr->info.constraint.un.foreign_key);
if (is_partitioned && (fk_info->delete_action == PT_RULE_SET_NULL || fk_info->update_action == PT_RULE_SET_NULL))
{
PT_NODE *const constr_name = cnstr->info.constraint.name;
ERROR2 (error, ER_FK_CANT_ON_PARTITION, constr_name ? constr_name->info.name.original : "", ctemplate->name);
goto error_exit;
}
for (attr = fk_info->attrs; attr; attr = attr->next)
{
const char *const att_name = attr->info.name.original;
SM_ATTRIBUTE *attp = NULL;
error = smt_find_attribute (ctemplate, att_name, 0, &attp);
if (error != NO_ERROR)
{
goto error_exit;
}
/* FK cannot be defined on shared attribute. */
if (db_attribute_is_shared (attp))
{
PT_NODE *const constr_name = cnstr->info.constraint.name;
ERROR2 (error, ER_FK_CANT_ON_SHARED_ATTRIBUTE, constr_name ? constr_name->info.name.original : "",
att_name);
goto error_exit;
}
if ((fk_info->delete_action == PT_RULE_SET_NULL || fk_info->update_action == PT_RULE_SET_NULL)
&& db_attribute_is_non_null (attp))
{
PT_NODE *const constr_name = cnstr->info.constraint.name;
ERROR2 (error, ER_FK_MUST_NOT_BE_NOT_NULL, constr_name ? constr_name->info.name.original : "", att_name);
goto error_exit;
}
}
}
if (ctemplate->current != NULL)
{
SM_CLASS_CONSTRAINT *c;
for (c = ctemplate->current->constraints; c != NULL; c = c->next)
{
SM_ATTRIBUTE **attribute_p = NULL;
if (c->type != SM_CONSTRAINT_FOREIGN_KEY)
{
continue;
}
if (is_partitioned
&& (c->fk_info->delete_action == SM_FOREIGN_KEY_SET_NULL
|| c->fk_info->update_action == SM_FOREIGN_KEY_SET_NULL))
{
ERROR2 (error, ER_FK_CANT_ON_PARTITION, c->name ? c->name : "", ctemplate->name);
goto error_exit;
}
if (c->fk_info->delete_action != SM_FOREIGN_KEY_SET_NULL
&& c->fk_info->update_action != SM_FOREIGN_KEY_SET_NULL)
{
continue;
}
for (attribute_p = c->attributes; *attribute_p; ++attribute_p)
{
const char *const att_name = (*attribute_p)->header.name;
SM_ATTRIBUTE *attp = NULL;
smt_find_attribute (ctemplate, att_name, 0, &attp);
if (db_attribute_is_non_null (attp))
{
ERROR2 (error, ER_FK_MUST_NOT_BE_NOT_NULL, c->name ? c->name : "", att_name);
goto error_exit;
}
}
}
}
return error;
error_exit:
return error;
}
/*
* do_check_fk_constraints() - Checks that foreign key constraints are
* consistent with the schema.
* return: Error code
* ctemplate(in/out): Class template
* constraints(in): List of all the class constraints that have been added.
* Currently the function does not support checking for
* consistency when NOT NULL constraints are added.
* Note : The class object is not modified
*/
int
do_check_fk_constraints (DB_CTMPL * ctemplate, PT_NODE * constraints)
{
bool is_partitioned = false;
if (ctemplate == NULL)
{
assert_release (ctemplate != NULL);
return ER_FAILED;
}
if (ctemplate->partition != NULL)
{
is_partitioned = true;
}
return do_check_fk_constraints_internal (ctemplate, constraints, is_partitioned);
}
/*
* do_add_methods() - Adds methods to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* methods(in): Methods to add
*
* Note : Class object is modified
*/
int
do_add_methods (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * methods)
{
const char *method_name, *method_impl;
PT_NODE *args_list, *type, *type_list;
PT_NODE *data_type;
int arg_num;
int is_meta;
int error = NO_ERROR;
TP_DOMAIN *arg_db_domain;
/* add each method listed in the class definition */
while (methods && (error == NO_ERROR))
{
method_name = methods->info.method_def.method_name->info.name.original;
if (methods->info.method_def.function_name != NULL)
{
method_impl = methods->info.method_def.function_name->info.name.original;
}
else
{
method_impl = NULL;
}
if (methods->info.method_def.mthd_type == PT_META_ATTR)
{
error = dbt_add_class_method (ctemplate, method_name, method_impl);
}
else
{
error = dbt_add_method (ctemplate, method_name, method_impl);
}
if (error != NO_ERROR)
{
return error;
}
/* if the result of the method is declared, then add it! */
arg_num = 0;
is_meta = methods->info.method_def.mthd_type == PT_META_ATTR;
if (methods->type_enum != PT_TYPE_NONE)
{
if (PT_IS_COLLECTION_TYPE (methods->type_enum))
{
arg_db_domain = pt_node_to_db_domain (parser, methods, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error = smt_assign_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
type_list = methods->data_type;
for (type = type_list; type != NULL; type = type->next)
{
arg_db_domain = pt_data_type_to_db_domain (parser, type, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error =
smt_add_set_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
}
}
else
{
if (validate_attribute_domain (parser, methods, false))
{
return ER_GENERIC_ERROR;
}
arg_db_domain = pt_node_to_db_domain (parser, methods, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error = smt_assign_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
}
}
/* add each argument of the method that is declared. */
args_list = methods->info.method_def.method_args_list;
for (data_type = args_list; data_type != NULL; data_type = data_type->next)
{
arg_num++;
if (PT_IS_COLLECTION_TYPE (data_type->type_enum))
{
arg_db_domain = pt_data_type_to_db_domain (parser, data_type, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error = smt_assign_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
type_list = data_type->data_type;
for (type = type_list; type != NULL; type = type->next)
{
arg_db_domain = pt_data_type_to_db_domain (parser, type, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error =
smt_add_set_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
}
}
else
{
if (validate_attribute_domain (parser, data_type, false))
{
return ER_GENERIC_ERROR;
}
arg_db_domain = pt_node_to_db_domain (parser, data_type, ctemplate->name);
if (arg_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
error = smt_assign_argument_domain (ctemplate, method_name, is_meta, NULL, arg_num, NULL, arg_db_domain);
if (error != NO_ERROR)
{
return error;
}
}
}
methods = methods->next;
}
return (error);
}
/*
* do_add_method_files() - Adds method files to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* method_files(in): Method files to add
*
* Note : Class object is modified
*/
int
do_add_method_files (const PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * method_files)
{
const char *method_file_name;
int error = NO_ERROR;
PT_NODE *path, *mf;
/* add each method_file listed in the class definition */
for (mf = method_files; mf && error == NO_ERROR; mf = mf->next)
{
if (mf->node_type == PT_FILE_PATH && (path = mf->info.file_path.string) != NULL && path->node_type == PT_VALUE
&& (path->type_enum == PT_TYPE_VARCHAR || path->type_enum == PT_TYPE_CHAR))
{
method_file_name = (char *) path->info.value.data_value.str->bytes;
error = dbt_add_method_file (ctemplate, method_file_name);
}
else
{
break;
}
}
return (error);
}
/*
* do_add_supers() - Adds super-classes to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* supers(in): Superclasses to add
*
* Note : Class object is modified
*/
int
do_add_supers (const PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * supers)
{
MOP super_class;
int error = NO_ERROR;
/* Add each superclass listed in the class definition. Each superclass must already exist inthe database before it
* can be added. */
while (supers && (error == NO_ERROR))
{
super_class = db_find_class (supers->info.name.original);
if (super_class == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
}
else
{
error = dbt_add_super (ctemplate, super_class);
}
supers = supers->next;
}
return (error);
}
/*
* do_add_resolutions() - Adds resolutions to a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* supers(in): Resolution to add
*
* Note : Class object is modified
*/
int
do_add_resolutions (const PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * resolution)
{
int error = NO_ERROR;
DB_OBJECT *resolution_super_mop;
const char *resolution_attr_mthd_name, *resolution_as_attr_mthd_name;
/* add each conflict resolution listed in the class definition */
while (resolution && (error == NO_ERROR))
{
resolution_super_mop = db_find_class (resolution->info.resolution.of_sup_class_name->info.name.original);
if (resolution_super_mop == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
break;
}
resolution_attr_mthd_name = resolution->info.resolution.attr_mthd_name->info.name.original;
if (resolution->info.resolution.as_attr_mthd_name == NULL)
{
resolution_as_attr_mthd_name = NULL;
}
else
{
resolution_as_attr_mthd_name = resolution->info.resolution.as_attr_mthd_name->info.name.original;
}
if (resolution->info.resolution.attr_type == PT_META_ATTR)
{
error =
dbt_add_class_resolution (ctemplate, resolution_super_mop, resolution_attr_mthd_name,
resolution_as_attr_mthd_name);
}
else
{
error =
dbt_add_resolution (ctemplate, resolution_super_mop, resolution_attr_mthd_name,
resolution_as_attr_mthd_name);
}
resolution = resolution->next;
}
return (error);
}
/*
* add_query_to_virtual_class() - Adds a query to a virtual class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* supers(in): Queries to add
*
* Note : Class object is modified
*/
static int
add_query_to_virtual_class (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * queries)
{
const char *query;
int error = NO_ERROR;
unsigned int save_custom;
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
query = parser_print_tree_with_quotes (parser, queries);
parser->custom_print = save_custom;
error = dbt_add_query_spec (ctemplate, query);
return (error);
}
/*
* add_union_query() - Adds a query to a virtual class object.
* If the query is a union all query
* without limit and order_by, it is
* divided into its component queries
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* supers(in): Union queries to add
*
* Note : class object modified
*/
static int
add_union_query (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * query)
{
int error = NO_ERROR;
/* Add each query listed in the virtual class definition. */
if (query->node_type == PT_UNION && query->info.query.all_distinct == PT_ALL && query->info.query.limit == NULL
&& query->info.query.order_by == NULL)
{
error = add_union_query (parser, ctemplate, query->info.query.q.union_.arg1);
if (error == NO_ERROR)
{
error = add_union_query (parser, ctemplate, query->info.query.q.union_.arg2);
}
}
else
{
error = add_query_to_virtual_class (parser, ctemplate, query);
}
return (error);
}
/*
* do_add_queries() - Adds a list of queries to a virtual class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* queries(in): Queries to add
*
* Note : Class object is modified
*/
int
do_add_queries (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, const PT_NODE * queries)
{
int error = NO_ERROR;
while (queries && (error == NO_ERROR))
{
error = add_union_query (parser, ctemplate, queries);
queries = queries->next;
}
return (error);
}
/*
* do_set_object_id() - Sets the object_id for a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* queries(in): Object ids to set
*
* Note : Class object is modified
*/
int
do_set_object_id (const PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * object_id_list)
{
int error = NO_ERROR;
PT_NODE *object_id;
int total_ids = 0;
DB_NAMELIST *id_list = NULL;
const char *att_name;
object_id = object_id_list;
while (object_id)
{
att_name = object_id->info.name.original;
if (att_name)
{
(void) db_namelist_append (&id_list, att_name);
}
++total_ids;
object_id = object_id->next;
}
if (total_ids == 0)
{
if (id_list)
{
db_namelist_free (id_list);
}
return (error);
}
error = dbt_set_object_id (ctemplate, id_list);
db_namelist_free (id_list);
return (error);
}
/*
* do_create_local() - Creates a new class or vclass
* return: Error code if the class/vclass is not created
* parser(in): Parser context
* ctemplate(in/out): Class template
* pt_node(in): Parse tree of a create class/vclass
*/
int
do_create_local (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * pt_node,
DB_QUERY_TYPE * create_select_columns)
{
int error = NO_ERROR;
/* create a MOP for the ctemplate, extracting its name from the parse tree. */
error =
do_add_attributes (parser, ctemplate, pt_node->info.create_entity.attr_def_list,
pt_node->info.create_entity.constraint_list, create_select_columns);
if (error != NO_ERROR)
{
return error;
}
error = do_add_attributes (parser, ctemplate, pt_node->info.create_entity.class_attr_def_list, NULL, NULL);
if (error != NO_ERROR)
{
return error;
}
error = do_add_constraints (ctemplate, pt_node->info.create_entity.constraint_list);
if (error != NO_ERROR)
{
return error;
}
error = do_check_fk_constraints (ctemplate, pt_node->info.create_entity.constraint_list);
if (error != NO_ERROR)
{
return error;
}
error = do_add_methods (parser, ctemplate, pt_node->info.create_entity.method_def_list);
if (error != NO_ERROR)
{
return error;
}
error = do_add_method_files (parser, ctemplate, pt_node->info.create_entity.method_file_list);
if (error != NO_ERROR)
{
return error;
}
error = do_add_resolutions (parser, ctemplate, pt_node->info.create_entity.resolution_list);
if (error != NO_ERROR)
{
return error;
}
error = do_add_supers (parser, ctemplate, pt_node->info.create_entity.supclass_list);
if (error != NO_ERROR)
{
return error;
}
error = do_add_queries (parser, ctemplate, pt_node->info.create_entity.as_query_list);
if (error != NO_ERROR)
{
return error;
}
error = do_set_object_id (parser, ctemplate, pt_node->info.create_entity.object_id_list);
if (error != NO_ERROR)
{
return error;
}
if (pt_node->info.create_entity.partition_info)
{
/* set partitioning key flag on the attribute */
PT_NODE *pt_key_col = pt_node->info.create_entity.partition_info->info.partition.keycol;
const char *key_col_name = pt_key_col->info.name.original;
SM_ATTRIBUTE *attr = ctemplate->attributes;
while (attr != NULL)
{
if (SM_COMPARE_NAMES (key_col_name, attr->header.name) == 0)
{
attr->flags |= SM_ATTFLAG_PARTITION_KEY;
break;
}
attr = (SM_ATTRIBUTE *) attr->header.next;
}
}
return (error);
}
/*
* create_select_to_insert_into() - An "INSERT INTO ... SELECT" statement is
* built from a simple SELECT statement to be
* used for "CREATE ... AS SELECT" execution
* return: The INSERT statement or NULL on error
* parser(in): Parser context
* class_name(in): the name of the table created by the CREATE statement
* create_select(in): the select statement parse tree
* query_columns(in): the columns of the select statement
*/
static PT_NODE *
create_select_to_insert_into (PARSER_CONTEXT * parser, const char *class_name, PT_NODE * create_select,
PT_CREATE_SELECT_ACTION create_select_action, DB_QUERY_TYPE * query_columns)
{
PT_NODE *ins = NULL;
PT_NODE *ocs = NULL;
PT_NODE *nls = NULL;
DB_QUERY_TYPE *column = NULL;
char real_name[SM_MAX_IDENTIFIER_LENGTH] = { 0 };
PT_NODE *name = NULL;
/* TODO The generated nodes have incorrect line and column information. */
ins = parser_new_node (parser, PT_INSERT);
if (ins == NULL)
{
goto error_exit;
}
if (create_select_action == PT_CREATE_SELECT_REPLACE)
{
ins->info.insert.do_replace = true;
}
else
{
/* PT_CREATE_SELECT_IGNORE is not yet implemented */
assert (create_select_action == PT_CREATE_SELECT_NO_ACTION);
}
ins->info.insert.spec = ocs = parser_new_node (parser, PT_SPEC);
if (ocs == NULL)
{
goto error_exit;
}
ocs->info.spec.only_all = PT_ONLY;
ocs->info.spec.meta_class = PT_CLASS;
ocs->info.spec.entity_name = pt_name (parser, class_name);
if (ocs->info.spec.entity_name == NULL)
{
goto error_exit;
}
for (column = query_columns; column != NULL; column = db_query_format_next (column))
{
sm_downcase_name (db_query_format_name (column), real_name, SM_MAX_IDENTIFIER_LENGTH);
name = pt_name (parser, real_name);
if (name == NULL)
{
goto error_exit;
}
ins->info.insert.attr_list = parser_append_node (name, ins->info.insert.attr_list);
}
ins->info.insert.value_clauses = nls = pt_node_list (parser, PT_IS_SUBQUERY, create_select);
if (nls == NULL)
{
goto error_exit;
}
return ins;
error_exit:
parser_free_tree (parser, ins);
return NULL;
}
/*
* execute_create_select_query() - Executes an "INSERT INTO ... SELECT"
* statement built from a SELECT statement to
* be used for "CREATE ... AS SELECT"
* execution
* return: NO_ERROR on success, non-zero for ERROR
* parser(in): Parser context
* class_name(in): the name of the table created by the CREATE statement
* create_select(in): the select statement parse tree
* query_columns(in): the columns of the select statement
* flagged_statement(in): a node to copy the special statement flags from;
* flags such as recompile will be used for the
* INSERT statement
*/
static int
execute_create_select_query (PARSER_CONTEXT * parser, const char *const class_name, PT_NODE * create_select,
PT_CREATE_SELECT_ACTION create_select_action, DB_QUERY_TYPE * query_columns,
PT_NODE * flagged_statement)
{
PT_NODE *insert_into = NULL;
PT_NODE *create_select_copy = parser_copy_tree (parser, create_select);
int error = NO_ERROR;
if (create_select_copy == NULL)
{
error = ER_FAILED;
goto error_exit;
}
insert_into =
create_select_to_insert_into (parser, class_name, create_select_copy, create_select_action, query_columns);
if (insert_into == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
pt_copy_statement_flags (flagged_statement, insert_into);
create_select_copy = NULL;
insert_into = pt_compile (parser, insert_into);
if (!insert_into || pt_has_error (parser))
{
pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, insert_into);
error = er_errid ();
goto error_exit;
}
insert_into = mq_translate (parser, insert_into);
if (!insert_into || pt_has_error (parser))
{
pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, insert_into);
error = er_errid ();
goto error_exit;
}
error = do_statement (parser, insert_into);
pt_free_statement_xasl_id (insert_into);
if (error < 0)
{
goto error_exit;
}
else
{
error = 0;
}
parser_free_tree (parser, insert_into);
insert_into = NULL;
return error;
error_exit:
if (create_select_copy != NULL)
{
parser_free_tree (parser, create_select_copy);
create_select_copy = NULL;
}
if (insert_into != NULL)
{
parser_free_tree (parser, insert_into);
insert_into = NULL;
}
return error;
}
/*
* do_create_entity() - Creates a new class/vclass
* return: Error code if the class/vclass is not created
* parser(in): Parser context
* node(in/out): Parse tree of a create class/vclass
*/
int
do_create_entity (PARSER_CONTEXT * parser, PT_NODE * node)
{
int error = NO_ERROR;
DB_CTMPL *ctemplate = NULL;
DB_OBJECT *class_obj = NULL;
const char *class_name = NULL;
const char *create_like = NULL;
SM_CLASS *source_class = NULL;
PT_NODE *create_select = NULL;
PT_NODE *create_index = NULL;
DB_QUERY_TYPE *query_columns = NULL;
PT_NODE *tbl_opt = NULL;
bool found_reuse_oid_option = false, reuse_oid = false;
bool do_rollback_on_error = false;
bool do_abort_class_on_error = false;
bool do_flush_class_mop = false;
int charset = LANG_SYS_CODESET;
int collation_id = LANG_SYS_COLLATION;
PARSER_VARCHAR *comment = NULL;
PT_NODE *tbl_opt_charset, *tbl_opt_coll, *cs_node, *coll_node;
PT_NODE *tbl_opt_comment, *comment_node, *super_node;
PT_NODE *tbl_opt_encrypt, *encrypt_node;
const char *comment_str = NULL;
MOP super_class = NULL;
int tde_algo_opt = -1;
TDE_ALGORITHM tde_algo = TDE_ALGORITHM_NONE;
CHECK_MODIFICATION_ERROR ();
tbl_opt_charset = tbl_opt_coll = cs_node = coll_node = NULL;
tbl_opt_comment = comment_node = NULL;
tbl_opt_encrypt = encrypt_node = NULL;
class_name = node->info.create_entity.entity_name->info.name.original;
if (node->info.create_entity.create_like != NULL)
{
create_like = node->info.create_entity.create_like->info.name.original;
}
create_select = node->info.create_entity.create_select;
if (create_select != NULL)
{
DB_QUERY_TYPE *column;
error = pt_get_select_query_columns (parser, create_select, &query_columns);
if (error != NO_ERROR)
{
goto error_exit;
}
/* check for mis-creating string type with -1 precision */
for (column = query_columns; column != NULL; column = db_query_format_next (column))
{
if (column->domain == NULL)
{
/*
* this might be from dblink which has errors for column definition
* the error code is not need to set at this point
* because the error code is already set from dblink
*/
error = ER_FAILED;
goto error_exit;
}
if (column->domain->type->id == DB_TYPE_VARCHAR)
{
if (column->domain->precision == DB_DEFAULT_PRECISION)
{
column->domain->precision = DB_MAX_VARCHAR_PRECISION;
}
}
}
}
assert (!(create_like != NULL && create_select != NULL));
switch (node->info.create_entity.entity_type)
{
case PT_CLASS:
if (node->info.create_entity.if_not_exists == 1 && db_find_class (class_name))
{
goto error_exit;
}
/* here we need to check if the super classes are partitioned, as it is not allowed to inherit from partition
* tables. */
super_node = node->info.create_entity.supclass_list;
while (super_node)
{
super_class = db_find_class (super_node->info.name.original);
if (super_class == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
else
{
error = sm_is_partitioned_class (super_class);
if (error < 0)
{
goto error_exit;
}
if (error > 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INHERIT_FROM_PARTITION_TABLE, 0);
error = er_errid ();
goto error_exit;
}
}
super_node = super_node->next;
}
for (tbl_opt = node->info.create_entity.table_option_list; tbl_opt != NULL; tbl_opt = tbl_opt->next)
{
assert (tbl_opt->node_type == PT_TABLE_OPTION);
switch (tbl_opt->info.table_option.option)
{
case PT_TABLE_OPTION_REUSE_OID:
found_reuse_oid_option = true;
reuse_oid = true;
break;
case PT_TABLE_OPTION_ENCRYPT:
tbl_opt_encrypt = tbl_opt;
break;
case PT_TABLE_OPTION_DONT_REUSE_OID:
found_reuse_oid_option = true;
reuse_oid = false;
break;
case PT_TABLE_OPTION_CHARSET:
tbl_opt_charset = tbl_opt;
break;
case PT_TABLE_OPTION_COLLATION:
tbl_opt_coll = tbl_opt;
break;
case PT_TABLE_OPTION_COMMENT:
tbl_opt_comment = tbl_opt;
break;
default:
break;
}
}
if (tbl_opt_encrypt)
{
int tde_loaded = 0;
(void) tde_is_loaded (&tde_loaded);
if (!tde_loaded)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TDE_CIPHER_IS_NOT_LOADED, 0);
error = er_errid ();
goto error_exit;
}
}
/* get default value of reuse_oid from system parameter, if don't use table option related reuse_oid */
if (!found_reuse_oid_option)
{
reuse_oid = prm_get_bool_value (PRM_ID_TB_DEFAULT_REUSE_OID);
}
/* validate charset and collation options, if any */
cs_node = (tbl_opt_charset) ? tbl_opt_charset->info.table_option.val : NULL;
coll_node = (tbl_opt_coll) ? tbl_opt_coll->info.table_option.val : NULL;
charset = LANG_SYS_CODESET;
collation_id = LANG_SYS_COLLATION;
if (cs_node != NULL || coll_node != NULL)
{
error = pt_check_grammar_charset_collation (parser, cs_node, coll_node, &charset, &collation_id);
if (error != NO_ERROR)
{
goto error_exit;
}
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CREATE_ENTITY);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback_on_error = true;
if (create_like)
{
ctemplate = dbt_copy_class (class_name, create_like, &source_class);
}
else
{
ctemplate = dbt_create_class (class_name);
}
break;
case PT_VCLASS:
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CREATE_ENTITY);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback_on_error = true;
if (node->info.create_entity.or_replace && db_find_class (class_name))
{
/* drop existing view */
if (do_is_partitioned_subclass (NULL, class_name, NULL))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_PARTITION_REQUEST, 0);
error = er_errid ();
goto error_exit;
}
error = drop_class_name (class_name, false);
if (error != NO_ERROR)
{
goto error_exit;
}
/* Quick fix: create a savepoint to mark deleted transient state. */
/* NOTE: Without this savepoint, creating view will completely replace deleted classname. */
error = tran_system_savepoint (UNIQUE_SAVEPOINT_REPLACE_VIEW);
if (error != NO_ERROR)
{
goto error_exit;
}
}
ctemplate = dbt_create_vclass (class_name);
break;
default:
error = ER_GENERIC_ERROR; /* a system error */
break;
}
if (ctemplate == NULL)
{
if (error == NO_ERROR)
{
error = er_errid ();
}
assert (error != NO_ERROR);
goto error_exit;
}
do_abort_class_on_error = true;
if (create_like != NULL)
{
/* Nothing left to do, but get the collation from the source class. */
collation_id = source_class->collation_id;
}
else
{
error = do_create_local (parser, ctemplate, node, query_columns);
}
if (error != NO_ERROR)
{
goto error_exit;
}
class_obj = dbt_finish_class (ctemplate);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error_exit;
}
if (er_errid () == ER_LC_UNKNOWN_CLASSNAME && er_get_severity () == ER_WARNING_SEVERITY)
{
/* Because the class is still inexistent, normally, here we will have to encounter some errors/warnings like
* ER_LC_UNKNOWN_CLASSNAME which is unuseful for current context indeed and may disturb other subsequent
* routines. Thus, we can/should clear the errors safely. */
er_clear ();
}
do_abort_class_on_error = false;
ctemplate = NULL;
switch (node->info.create_entity.entity_type)
{
case PT_VCLASS:
if (node->info.create_entity.with_check_option == PT_CASCADED)
{
error = sm_set_class_flag (class_obj, SM_CLASSFLAG_WITHCHECKOPTION, 1);
}
else if (node->info.create_entity.with_check_option == PT_LOCAL)
{
error = sm_set_class_flag (class_obj, SM_CLASSFLAG_LOCALCHECKOPTION, 1);
}
comment_node = node->info.create_entity.vclass_comment;
if (comment_node != NULL)
{
assert (comment_node->node_type == PT_VALUE);
comment_str = (char *) comment_node->info.value.data_value.str->bytes;
error = sm_set_class_comment (class_obj, comment_str);
if (error == NO_ERROR)
{
do_flush_class_mop = true;
}
}
break;
case PT_CLASS:
if (create_like)
{
assert (source_class != NULL);
if (!reuse_oid && (source_class->flags & SM_CLASSFLAG_REUSE_OID))
{
reuse_oid = true;
}
tde_algo = (TDE_ALGORITHM) source_class->tde_algorithm;
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = sm_set_class_tde_algorithm (class_obj, tde_algo);
if (error != NO_ERROR)
{
break;
}
do_flush_class_mop = true;
}
if (source_class->comment)
{
error = sm_set_class_comment (class_obj, source_class->comment);
if (error != NO_ERROR)
{
break;
}
}
}
if (locator_create_heap_if_needed (class_obj, reuse_oid) == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
break;
}
if (reuse_oid)
{
error = sm_set_class_flag (class_obj, SM_CLASSFLAG_REUSE_OID, 1);
if (error == NO_ERROR)
{
/* Need to flush class mop in order to reflect reuse_oid flag into catalog table. Without flushing it,
* catalog information is incorrect under non-autocommit mode. */
do_flush_class_mop = true;
}
}
if (tbl_opt_encrypt)
{
encrypt_node = tbl_opt_encrypt->info.table_option.val;
assert (encrypt_node != NULL && encrypt_node->node_type == PT_VALUE
&& encrypt_node->type_enum == PT_TYPE_INTEGER);
tde_algo_opt = encrypt_node->info.value.data_value.i;
/*
* -1 means using deafult encryption algorithm.
* Other values but -1, TDE_ALGORITHM_AES, TDE_ALGORITHM_ARIA has been denied by parser.
*/
if (tde_algo_opt == -1)
{
tde_algo = (TDE_ALGORITHM) prm_get_integer_value (PRM_ID_TDE_DEFAULT_ALGORITHM);
}
else
{
tde_algo = (TDE_ALGORITHM) tde_algo_opt;
}
error = sm_set_class_tde_algorithm (class_obj, tde_algo);
if (error != NO_ERROR)
{
break;
}
do_flush_class_mop = true;
}
error = sm_set_class_collation (class_obj, collation_id);
if (error != NO_ERROR)
{
break;
}
if (tbl_opt_comment)
{
comment_node = tbl_opt_comment->info.table_option.val;
assert (comment_node != NULL && comment_node->node_type == PT_VALUE);
comment = comment_node->info.value.data_value.str;
error = sm_set_class_comment (class_obj, (char *) pt_get_varchar_bytes (comment));
if (error == NO_ERROR)
{
do_flush_class_mop = true;
}
}
break;
default:
break;
}
if (db_get_client_type () == DB_CLIENT_TYPE_ADMIN_CSQL_REBUILD_CATALOG)
{
if (sm_check_system_class_by_name (class_name))
{
sm_mark_system_class (class_obj, 1);
}
}
if (do_flush_class_mop == true)
{
assert (error == NO_ERROR);
assert (class_obj != NULL);
error = locator_flush_class (class_obj);
}
if (error != NO_ERROR)
{
goto error_exit;
}
if (node->info.create_entity.partition_info != NULL)
{
SM_PARTITION_ALTER_INFO info;
info.keycol[0] = 0;
info.promoted_count = 0;
info.promoted_names = NULL;
info.root_tmpl = NULL;
info.root_op = class_obj;
error = do_create_partition (parser, node, &info);
if (error != NO_ERROR)
{
if (error == ER_LK_UNILATERALLY_ABORTED)
{
do_rollback_on_error = false;
}
goto error_exit;
}
}
if (create_like)
{
error = do_copy_indexes (parser, class_obj, source_class);
if (error != NO_ERROR)
{
goto error_exit;
}
}
if (create_select)
{
error =
execute_create_select_query (parser, class_name, create_select, node->info.create_entity.create_select_action,
query_columns, node);
if (error != NO_ERROR)
{
goto error_exit;
}
db_free_query_format (query_columns);
query_columns = NULL;
}
assert (query_columns == NULL);
for (create_index = node->info.create_entity.create_index; create_index != NULL; create_index = create_index->next)
{
PT_NODE *save_next = NULL;
create_index->info.index.indexed_class = pt_entity (parser, node->info.create_entity.entity_name, NULL, NULL);
if (create_index->info.index.indexed_class == NULL)
{
error = ER_FAILED;
goto error_exit;
}
save_next = create_index->next;
create_index->next = NULL;
pt_semantic_check (parser, create_index);
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto error_exit;
}
create_index->next = save_next;
error = do_create_index (parser, create_index);
if (error != NO_ERROR)
{
goto error_exit;
}
}
if (tde_algo != TDE_ALGORITHM_NONE)
{
error = file_apply_tde_to_class_files (&class_obj->oid_info.oid);
if (error != NO_ERROR)
{
goto error_exit;
}
}
return error;
error_exit:
if (query_columns != NULL)
{
db_free_query_format (query_columns);
query_columns = NULL;
}
if (do_abort_class_on_error)
{
(void) dbt_abort_class (ctemplate);
}
if (do_rollback_on_error && error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CREATE_ENTITY);
}
return error;
}
/*
* do_recreate_renamed_class_indexes() - Recreate indexes of previously
* renamed class
* return: NO_ERROR on success, non-zero for ERROR
* parser(in): Parser context
* old_class_name(in): the old name of the renamed class
* class_name(in): the new name of the renamed class
*
* Note : This function must be called after class renaming.
* Currently, only filter and function index are recreated since
* their expression must be modified and recompiled.
*/
static int
do_recreate_renamed_class_indexes (const PARSER_CONTEXT * parser, const char *const old_class_name,
const char *const class_name)
{
int error = NO_ERROR;
SM_CLASS_CONSTRAINT *c = NULL;
SM_CONSTRAINT_INFO *index_save_info = NULL, *saved = NULL;
SM_CLASS *class_ = NULL;
DB_OBJECT *classmop = NULL;
assert (parser != NULL && old_class_name != NULL && class_name != NULL);
classmop = db_find_class (class_name);
if (classmop == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (au_fetch_class (classmop, &class_, AU_FETCH_READ, DB_AUTH_SELECT) != NO_ERROR)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
if (class_->constraints == NULL)
{
/* no constraints, nothing to do */
return NO_ERROR;
}
/* We can come here by renaming a view. In general, views have no constraints. If there are no constraints,
* NO_ERROR is returned before. However, views can have shared columns with a not null constraint.
* Renaming does not affect the not null constraint. So, even if the view has a not null constraint,
* change it so that an error does not occur here. I do not think there is a view with a constraint
* other than a not null constraint. Change the return value from ER_OBJ_NOT_A_CLASS to NO_ERROR. (In CBRD-24435) */
if (class_->class_type != SM_CLASS_CT)
{
return NO_ERROR;
}
for (c = class_->constraints; c; c = c->next)
{
if (c->type != SM_CONSTRAINT_INDEX && c->type != SM_CONSTRAINT_REVERSE_INDEX && c->type != SM_CONSTRAINT_UNIQUE
&& c->type != SM_CONSTRAINT_REVERSE_UNIQUE)
{
continue;
}
if (c->func_index_info || c->filter_predicate)
{
/* save constraints */
error = sm_save_constraint_info (&index_save_info, c);
if (error == NO_ERROR)
{
assert (index_save_info != NULL);
saved = index_save_info;
while (saved->next)
{
saved = saved->next;
}
if (c->func_index_info)
{
/* recompile function index expression */
error =
do_recreate_func_index_constr ((PARSER_CONTEXT *) parser, saved, NULL, NULL, old_class_name,
sm_ch_name ((MOBJ) class_));
if (error != NO_ERROR)
{
goto error_exit;
}
}
else
{
/* recompile filter index expression */
error =
do_recreate_filter_index_constr ((PARSER_CONTEXT *) parser, saved->filter_predicate, NULL,
old_class_name, sm_ch_name ((MOBJ) class_));
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
}
}
/* drop indexes */
for (saved = index_save_info; saved != NULL; saved = saved->next)
{
if (SM_IS_CONSTRAINT_UNIQUE_FAMILY ((SM_CONSTRAINT_TYPE) saved->constraint_type))
{
error =
sm_drop_constraint (classmop, saved->constraint_type, saved->name, (const char **) saved->att_names, false,
false);
if (error != NO_ERROR)
{
goto error_exit;
}
}
else
{
error = sm_drop_index (classmop, saved->name);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
/* add indexes */
for (saved = index_save_info; saved != NULL; saved = saved->next)
{
error = sm_add_constraint (classmop, saved->constraint_type, saved->name, (const char **) saved->att_names,
saved->asc_desc, saved->prefix_length, false, saved->filter_predicate,
saved->func_index_info, saved->comment, saved->index_status);
if (error != NO_ERROR)
{
goto error_exit;
}
}
if (index_save_info != NULL)
{
sm_free_constraint_info (&index_save_info);
}
return NO_ERROR;
error_exit:
if (index_save_info != NULL)
{
sm_free_constraint_info (&index_save_info);
}
return error;
}
/*
* do_copy_indexes() - Copies all the indexes of a given class to another
* class.
* return: NO_ERROR on success, non-zero for ERROR
* parser(in): Parser context
* classmop(in): the class to copy the indexes to
* class_(in): the class to copy the indexes from
*/
static int
do_copy_indexes (PARSER_CONTEXT * parser, MOP classmop, SM_CLASS * src_class)
{
int error = NO_ERROR;
const char **att_names = NULL;
SM_CLASS_CONSTRAINT *c;
char *new_cons_name = NULL;
SM_CONSTRAINT_INFO *index_save_info = NULL;
DB_CONSTRAINT_TYPE constraint_type;
int free_constraint = 0;
const char *class_name = NULL;
assert (src_class != NULL);
if (src_class->constraints == NULL)
{
return NO_ERROR;
}
for (c = src_class->constraints; c; c = c->next)
{
if (c->type != SM_CONSTRAINT_INDEX && c->type != SM_CONSTRAINT_REVERSE_INDEX)
{
/* These should have been copied already. */
continue;
}
att_names = classobj_point_at_att_names (c, NULL);
if (att_names == NULL)
{
assert (er_errid () != NO_ERROR);
return er_errid ();
}
constraint_type = db_constraint_type (c);
new_cons_name = (char *) c->name;
if (c->func_index_info || c->filter_predicate)
{
/* we need to recompile the expression need for function index */
error = sm_save_constraint_info (&index_save_info, c);
if (error != NO_ERROR)
{
goto exit_on_error;
}
free_constraint = 1;
class_name = sm_get_ch_name (classmop);
if (class_name == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit_on_error;
}
if (c->func_index_info)
{
error =
do_recreate_func_index_constr (parser, index_save_info, NULL, NULL, sm_ch_name ((MOBJ) src_class),
class_name);
}
else
{
/* filter index predicate available */
error =
do_recreate_filter_index_constr (parser, index_save_info->filter_predicate, NULL,
sm_ch_name ((MOBJ) src_class), class_name);
}
if (error != NO_ERROR)
{
goto exit_on_error;
}
}
if (c->func_index_info || c->filter_predicate)
{
error = sm_add_constraint (classmop, constraint_type, new_cons_name, att_names, index_save_info->asc_desc,
index_save_info->prefix_length, false, index_save_info->filter_predicate,
index_save_info->func_index_info, index_save_info->comment,
index_save_info->index_status);
}
else
{
error =
sm_add_constraint (classmop, constraint_type, new_cons_name, att_names, c->asc_desc, c->attrs_prefix_length,
false, c->filter_predicate, c->func_index_info, c->comment, c->index_status);
}
if (error != NO_ERROR)
{
goto exit_on_error;
}
free_and_init (att_names);
if (new_cons_name != NULL && new_cons_name != c->name)
{
free_and_init (new_cons_name);
}
if (free_constraint)
{
sm_free_constraint_info (&index_save_info);
}
free_constraint = 0;
}
return error;
exit_on_error:
free_and_init (att_names);
if (new_cons_name != NULL && new_cons_name != c->name)
{
free_and_init (new_cons_name);
}
if (free_constraint)
{
sm_free_constraint_info (&index_save_info);
}
return error;
}
/*
* Function Group :
* Code for truncating Classes by Parse Tree descriptions.
*
*/
static int truncate_class_name (const char *name, const bool is_cascade);
/*
* truncate_class_name() - This static routine truncates a class by name.
* return: Error code
* name(in): Class name to truncate
* is_cascade(in): whether to truncate cascade FK-referring classes
*/
static int
truncate_class_name (const char *name, const bool is_cascade)
{
DB_OBJECT *class_mop;
class_mop = db_find_class (name);
if (class_mop)
{
return db_truncate_class (class_mop, is_cascade);
}
else
{
/* if class is null, return the global error. */
assert (er_errid () != NO_ERROR);
return er_errid ();
}
}
/*
* do_truncate() - Truncates a class
* return: Error code if truncation fails.
* parser(in): Parser context
* statement(in/out): Parse tree of the statement
*/
int
do_truncate (PARSER_CONTEXT * parser, PT_NODE * statement)
{
int error = NO_ERROR;
PT_NODE *entity_spec = NULL;
PT_NODE *entity = NULL;
PT_NODE *entity_list = NULL;
bool is_cascade = false;
CHECK_MODIFICATION_ERROR ();
is_cascade = statement->info.truncate.is_cascade;
entity_spec = statement->info.truncate.spec;
if (entity_spec == NULL)
{
return NO_ERROR;
}
entity_list = entity_spec->info.spec.flat_entity_list;
for (entity = entity_list; entity != NULL; entity = entity->next)
{
/* partitioned sub-class check */
if (do_is_partitioned_subclass (NULL, entity->info.name.original, NULL))
{
error = ER_INVALID_PARTITION_REQUEST;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_TRUNCATE);
if (error != NO_ERROR)
{
return error;
}
for (entity = entity_list; entity != NULL; entity = entity->next)
{
error = truncate_class_name (entity->info.name.original, is_cascade);
if (error != NO_ERROR)
{
if (error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_TRUNCATE);
}
return error;
}
}
return error;
}
/*
* do_alter_clause_change_attribute() - Executes an ALTER CHANGE or
* ALTER MODIFY clause
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_RENAME_ENTITY clause potentially
* followed by the rest of the clauses in the ALTER
* statement.
*/
static int
do_alter_clause_change_attribute (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error = NO_ERROR;
const PT_ALTER_CODE alter_code = alter->info.alter.code;
const char *entity_name = NULL;
DB_OBJECT *class_obj = NULL;
DB_CTMPL *ctemplate = NULL;
SM_ATTR_CHG_SOL change_mode = SM_ATTR_CHG_ONLY_SCHEMA;
SM_ATTR_PROP_CHG attr_chg_prop;
bool tran_saved = false;
MOP class_mop = NULL;
OID *usr_oid_array = NULL;
int user_count = 0;
int i;
bool has_partitions = false;
bool is_srv_update_needed = false;
OID class_oid;
int att_id = -1;
assert (alter_code == PT_CHANGE_ATTR);
assert (alter->info.alter.super.resolution_list == NULL);
OID_SET_NULL (&class_oid);
reset_att_property_structure (&attr_chg_prop);
entity_name = alter->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Expecting a class or virtual class name.");
goto exit;
}
class_obj = db_find_class (entity_name);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = locator_flush_class (class_obj);
if (error != NO_ERROR)
{
/* don't overwrite error */
goto exit;
}
/* force exclusive lock on class, even though it should have been already acquired */
if (locator_fetch_class (class_obj, DB_FETCH_WRITE) == NULL)
{
error = ER_FAILED;
goto exit;
}
ctemplate = dbt_edit_class (class_obj);
if (ctemplate == NULL)
{
/* when dbt_edit_class fails (e.g. because the server unilaterally aborts us), we must record the associated
* error message into the parser. Otherwise, we may get a confusing error msg of the form: "so_and_so is not a
* class". */
pt_record_error (parser, parser->statement_number - 1, alter->line_number, alter->column_number, er_msg (), NULL);
error = er_errid ();
goto exit;
}
/* this ALTER CHANGE syntax supports only one attribute change per ALTER clause */
assert (alter->info.alter.alter_clause.attr_mthd.mthd_def_list == NULL);
assert (alter->info.alter.alter_clause.attr_mthd.attr_def_list->next == NULL);
error =
check_change_attribute (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.attr_def_list,
alter->info.alter.alter_clause.attr_mthd.attr_old_name,
&(alter->info.alter.constraint_list), &attr_chg_prop, &change_mode);
if (error != NO_ERROR)
{
goto exit;
}
if (change_mode == SM_ATTR_CHG_NOT_NEEDED)
{
/* nothing to do */
goto exit;
}
if (ctemplate->current->users != NULL && ctemplate->partition != NULL)
{
DB_OBJLIST *user_list = NULL;
user_count = ws_list_length ((DB_LIST *) ctemplate->current->users);
usr_oid_array = (OID *) calloc (user_count, sizeof (OID));
if (usr_oid_array == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, user_count * sizeof (OID));
goto exit;
}
for (i = 0, user_list = ctemplate->current->users; i < user_count && user_list != NULL;
i++, user_list = user_list->next)
{
/* copy partition class OID for later use */
COPY_OID (&(usr_oid_array[i]), &(user_list->op->oid_info.oid));
/* force exclusive lock on class, even though it should have been already acquired */
if (locator_fetch_class (user_list->op, DB_FETCH_WRITE) == NULL)
{
error = ER_FAILED;
goto exit;
}
}
has_partitions = true;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_ATTR);
if (error != NO_ERROR)
{
goto exit;
}
tran_saved = true;
error =
do_change_att_schema_only (parser, ctemplate, alter->info.alter.alter_clause.attr_mthd.attr_def_list,
alter->info.alter.alter_clause.attr_mthd.attr_old_name,
alter->info.alter.constraint_list, &attr_chg_prop, &change_mode);
if (error != NO_ERROR)
{
goto exit;
}
/* save class MOP */
class_mop = ctemplate->op;
/* check foreign key constraints */
error = do_check_fk_constraints (ctemplate, alter->info.alter.constraint_list);
if (error != NO_ERROR)
{
goto exit;
}
is_srv_update_needed = ((change_mode == SM_ATTR_CHG_WITH_ROW_UPDATE || change_mode == SM_ATTR_CHG_BEST_EFFORT)
&& attr_chg_prop.name_space == ID_ATTRIBUTE) ? true : false;
if (is_srv_update_needed)
{
COPY_OID (&class_oid, &(ctemplate->op->oid_info.oid));
att_id = attr_chg_prop.att_id;
}
/* force schema update to server */
class_obj = dbt_finish_class (ctemplate);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
/* set NULL, avoid 'abort_class' in case of error */
ctemplate = NULL;
if (attr_chg_prop.constr_info != NULL)
{
SM_CONSTRAINT_INFO *saved_constr = NULL;
for (saved_constr = attr_chg_prop.constr_info; saved_constr != NULL; saved_constr = saved_constr->next)
{
if (saved_constr->func_index_info || saved_constr->filter_predicate)
{
if (saved_constr->func_index_info)
{
error = do_recreate_func_index_constr (parser, saved_constr, NULL, alter, NULL, NULL);
if (error != NO_ERROR)
{
goto exit;
}
}
if (saved_constr->filter_predicate)
{
error = do_recreate_filter_index_constr (parser, saved_constr->filter_predicate, alter, NULL, NULL);
if (error != NO_ERROR)
{
goto exit;
}
}
if (!is_srv_update_needed)
{
const char *att_names[2];
PT_NODE *att_old_name = alter->info.alter.alter_clause.attr_mthd.attr_old_name;
if (att_old_name != NULL)
{
assert (att_old_name->node_type == PT_NAME);
att_names[0] = att_old_name->info.name.original;
att_names[1] = NULL;
assert (alter->info.alter.alter_clause.attr_mthd.attr_old_name->node_type == PT_NAME);
error =
sm_drop_constraint (class_mop, saved_constr->constraint_type, saved_constr->name, att_names,
false, false);
if (error != NO_ERROR)
{
goto exit;
}
error = sm_add_constraint (class_mop, saved_constr->constraint_type, saved_constr->name,
(const char **) saved_constr->att_names, saved_constr->asc_desc,
saved_constr->prefix_length, false, saved_constr->filter_predicate,
saved_constr->func_index_info, saved_constr->comment,
saved_constr->index_status);
if (error != NO_ERROR)
{
goto exit;
}
}
}
}
}
}
if (is_srv_update_needed || is_att_prop_set (attr_chg_prop.p[P_TYPE], ATT_CHG_TYPE_PREC_INCR))
{
error = do_drop_att_constraints (class_mop, attr_chg_prop.constr_info);
if (error != NO_ERROR)
{
goto exit;
}
/* perform UPDATE or each row */
if (is_srv_update_needed)
{
assert (att_id >= 0);
assert (!OID_ISNULL (&class_oid));
if (has_partitions)
{
assert (user_count > 0);
assert (usr_oid_array != NULL);
for (i = 0; i < user_count; i++)
{
error = do_run_upgrade_instances_domain (parser, &(usr_oid_array[i]), att_id);
if (error != NO_ERROR)
{
goto exit;
}
}
}
else
{
error = do_run_upgrade_instances_domain (parser, &class_oid, att_id);
if (error != NO_ERROR)
{
goto exit;
}
}
}
error = sort_constr_info_list (&(attr_chg_prop.constr_info));
if (error != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "Sorting constraints failed.");
goto exit;
}
error = do_recreate_att_constraints (class_mop, attr_chg_prop.constr_info);
if (error != NO_ERROR)
{
goto exit;
}
}
else
{
assert (change_mode == SM_ATTR_CHG_ONLY_SCHEMA);
}
/* create any new constraints: */
if (attr_chg_prop.new_constr_info != NULL)
{
SM_CONSTRAINT_INFO *ci = NULL;
error = sort_constr_info_list (&(attr_chg_prop.new_constr_info));
if (error != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UNEXPECTED, 1, "Sorting constraints failed.");
goto exit;
}
/* add new constraints */
for (ci = attr_chg_prop.new_constr_info; ci != NULL; ci = ci->next)
{
if (ci->constraint_type == DB_CONSTRAINT_NOT_NULL)
{
const char *att_name = *(ci->att_names);
if (alter->info.alter.hint & PT_HINT_SKIP_UPDATE_NULL)
{
error = db_add_constraint (class_mop, ci->constraint_type, NULL, (const char **) ci->att_names, 0);
}
else if (!prm_get_bool_value (PRM_ID_ALTER_TABLE_CHANGE_TYPE_STRICT))
{
char query[SM_MAX_IDENTIFIER_LENGTH * 4 + 36] = { 0 };
const char *class_name = NULL;
const char *hard_default =
get_hard_default_for_type (alter->info.alter.alter_clause.attr_mthd.attr_def_list->type_enum);
int update_rows_count = 0;
class_name = db_get_class_name (class_mop);
if (class_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Cannot get class name of mop.");
goto exit;
}
assert (class_name != NULL && att_name != NULL && hard_default != NULL);
snprintf (query, SM_MAX_IDENTIFIER_LENGTH * 4 + 30, "UPDATE [%s] SET [%s]=%s WHERE [%s] IS NULL",
class_name, att_name, hard_default, att_name);
error = do_run_update_query_for_class (query, class_mop, &update_rows_count);
if (error != NO_ERROR)
{
goto exit;
}
if (update_rows_count > 0)
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_ALTER_CHANGE_ADD_NOT_NULL_SET_HARD_DEFAULT, 0);
}
error = db_add_constraint (class_mop, ci->constraint_type, NULL, (const char **) ci->att_names, 0);
}
else
{
error = db_constrain_non_null (class_mop, *(ci->att_names), 0, 1);
}
if (error != NO_ERROR)
{
goto exit;
}
}
else
{
assert (ci->constraint_type == DB_CONSTRAINT_UNIQUE || ci->constraint_type == DB_CONSTRAINT_PRIMARY_KEY);
error = db_add_constraint (class_mop, ci->constraint_type, NULL, (const char **) ci->att_names, 0);
}
if (error != NO_ERROR)
{
goto exit;
}
}
}
exit:
if (ctemplate != NULL)
{
dbt_abort_class (ctemplate);
ctemplate = NULL;
}
if (error != NO_ERROR && tran_saved && error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_ATTR);
}
if (attr_chg_prop.constr_info != NULL)
{
sm_free_constraint_info (&(attr_chg_prop.constr_info));
}
if (attr_chg_prop.new_constr_info != NULL)
{
sm_free_constraint_info (&(attr_chg_prop.new_constr_info));
}
if (usr_oid_array != NULL)
{
free_and_init (usr_oid_array);
}
return error;
}
/*
* do_alter_change_owner() - change the owner of a class/vclass
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_CHANGE_OWNER claus
*/
static int
do_alter_change_owner (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error = NO_ERROR;
MOP class_mop, user_mop;
PT_NODE *class_, *user;
SM_CLASS *sm_class = NULL;
assert (alter != NULL);
class_ = alter->info.alter.entity_name;
assert (class_ != NULL);
user = alter->info.alter.alter_clause.user.user_name;
assert (user != NULL);
class_mop = sm_find_class (class_->info.name.original);
if (class_mop == NULL)
{
ASSERT_ERROR_AND_SET (error);
return error;
}
error = au_fetch_class_force (class_mop, &sm_class, AU_FETCH_UPDATE);
if (error != NO_ERROR)
{
ASSERT_ERROR_AND_SET (error);
return error;
}
/* To change the owner of a system class is not allowed. */
if (sm_issystem (sm_class))
{
ERROR_SET_ERROR_1ARG (error, ER_AU_CANT_ALTER_OWNER_OF_SYSTEM_CLASS, "");
return error;
}
user_mop = au_find_user (user->info.name.original);
if (user_mop == NULL)
{
ASSERT_ERROR_AND_SET (error);
return error;
}
error = au_change_class_owner (class_mop, user_mop);
return error;
}
/*
* do_alter_change_default_cs_coll() - change the default collation of a
* class/vclass
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_CHANGE_OWNER claus
*/
static int
do_alter_change_default_cs_coll (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error = NO_ERROR;
const char *entity_name = NULL;
DB_OBJECT *class_obj = NULL;
DB_CTMPL *ctemplate = NULL;
PT_ALTER_INFO *alter_info;
bool tran_saved = false;
MOP class_mop = NULL;
OID class_oid;
bool is_chg_needed = false;
int i, collation_id = -1, is_partition = -1;
MOP *sub_partitions = NULL;
alter_info = &(alter->info.alter);
assert (alter_info->code == PT_CHANGE_COLLATION);
OID_SET_NULL (&class_oid);
entity_name = alter_info->entity_name->info.name.original;
if (entity_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Expecting a class or virtual class name.");
goto exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_DEF_COLL);
if (error != NO_ERROR)
{
goto exit;
}
tran_saved = true;
class_obj = db_find_class (entity_name);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = locator_flush_class (class_obj);
if (error != NO_ERROR)
{
/* don't overwrite error */
goto exit;
}
/* get exclusive lock on class */
if (locator_fetch_class (class_obj, DB_FETCH_WRITE) == NULL)
{
error = ER_FAILED;
goto exit;
}
ctemplate = dbt_edit_class (class_obj);
if (ctemplate == NULL)
{
/* when dbt_edit_class fails (e.g. because the server unilaterally aborts us), we must record the associated
* error message into the parser. Otherwise, we may get a confusing error msg of the form: "so_and_so is not a
* class". */
pt_record_error (parser, parser->statement_number - 1, alter->line_number, alter->column_number, er_msg (), NULL);
error = er_errid ();
goto exit;
}
error = check_change_class_collation (parser, ctemplate, alter_info, &is_chg_needed, &collation_id);
if (error != NO_ERROR)
{
goto exit;
}
if (!is_chg_needed)
{
/* nothing to do */
goto exit;
}
class_mop = ctemplate->op;
error = sm_set_class_collation (class_mop, collation_id);
if (error != NO_ERROR)
{
goto exit;
}
error = sm_partitioned_class_type (class_mop, &is_partition, NULL, &sub_partitions);
if (error != NO_ERROR)
{
goto exit;
}
if (is_partition == DB_PARTITIONED_CLASS)
{
for (i = 0; sub_partitions[i]; i++)
{
error = sm_set_class_collation (sub_partitions[i], collation_id);
if (error != NO_ERROR)
{
break;
}
}
if (error != NO_ERROR)
{
goto exit;
}
}
/* force schema update to server */
class_obj = dbt_finish_class (ctemplate);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
/* set NULL, avoid 'abort_class' in case of error */
ctemplate = NULL;
exit:
if (ctemplate != NULL)
{
dbt_abort_class (ctemplate);
ctemplate = NULL;
}
if (sub_partitions)
{
free_and_init (sub_partitions);
}
if (error != NO_ERROR && tran_saved && error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_ATTR);
}
return error;
}
/*
* do_alter_change_tbl_comment() - change the table comment
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_CHANGE_TABLE_COMMENT clause
*/
static int
do_alter_change_tbl_comment (PARSER_CONTEXT * const parser, PT_NODE * const alter)
{
int error = NO_ERROR;
const char *entity_name = NULL;
DB_OBJECT *class_obj = NULL;
DB_CTMPL *ctemplate = NULL;
PT_ALTER_INFO *alter_info;
bool tran_saved = false;
MOP class_mop = NULL;
PT_NODE *comment_node = NULL;
PARSER_VARCHAR *comment = NULL;
alter_info = &(alter->info.alter);
if (alter_info->code == PT_CHANGE_TABLE_COMMENT)
{
comment_node = alter_info->alter_clause.comment.tbl_comment;
}
else if (alter_info->code == PT_RESET_QUERY || alter_info->code == PT_ADD_QUERY
|| alter_info->code == PT_MODIFY_QUERY)
{
comment_node = alter_info->alter_clause.query.view_comment;
}
else
{
/*
* code shall be one of the above 4 types, otherwise it's an error.
*/
assert (0);
}
assert (comment_node != NULL && comment_node->node_type == PT_VALUE);
entity_name = alter_info->entity_name->info.name.original;
if (entity_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Expecting a class or virtual class name.");
goto exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_TBL_COMMENT);
if (error != NO_ERROR)
{
goto exit;
}
tran_saved = true;
class_obj = db_find_class (entity_name);
if (class_obj == NULL)
{
error = er_errid ();
goto exit;
}
error = locator_flush_class (class_obj);
if (error != NO_ERROR)
{
/* don't overwrite error */
goto exit;
}
/* get exclusive lock on class */
if (locator_fetch_class (class_obj, DB_FETCH_WRITE) == NULL)
{
error = ER_FAILED;
goto exit;
}
ctemplate = dbt_edit_class (class_obj);
if (ctemplate == NULL)
{
/* when dbt_edit_class fails (e.g. because the server unilaterally aborts us), we must record the associated
* error message into the parser. Otherwise, we may get a confusing error msg of the form: "so_and_so is not a
* class". */
pt_record_error (parser, parser->statement_number - 1, alter->line_number, alter->column_number, er_msg (), NULL);
error = er_errid ();
goto exit;
}
class_mop = ctemplate->op;
comment = comment_node->info.value.data_value.str;
error = sm_set_class_comment (class_mop, (char *) pt_get_varchar_bytes (comment));
if (error != NO_ERROR)
{
goto exit;
}
/* force schema update to server */
class_obj = dbt_finish_class (ctemplate);
if (class_obj == NULL)
{
error = er_errid ();
goto exit;
}
/* set NULL, avoid 'abort_class' in case of error */
ctemplate = NULL;
exit:
if (ctemplate != NULL)
{
dbt_abort_class (ctemplate);
ctemplate = NULL;
}
if (error != NO_ERROR && tran_saved && error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_TBL_COMMENT);
}
return error;
}
/*
* do_alter_change_col_comment() - change the column comment
* return: Error code
* parser(in): Parser context
* alter(in/out): Parse tree of a PT_CHANGE_COLUMN_COMMENT clause
*/
static int
do_alter_change_col_comment (PARSER_CONTEXT * const parser, PT_NODE * const alter_node)
{
int error = NO_ERROR;
int meta = 0, shared = 0;
SM_ATTRIBUTE *found_attr = NULL;
SM_NAME_SPACE name_space = ID_NULL;
const PT_ALTER_CODE alter_code = alter_node->info.alter.code;
const char *entity_name = NULL;
PT_NODE *attr_node = NULL;
const char *attr_name = NULL;
PT_NODE *comment_node = NULL;
PARSER_VARCHAR *comment_str = NULL;
DB_OBJECT *class_obj = NULL;
DB_CTMPL *ctemplate = NULL;
MOP class_mop = NULL;
OID class_oid;
bool tran_saved = false;
assert (alter_code == PT_CHANGE_COLUMN_COMMENT);
OID_SET_NULL (&class_oid);
entity_name = alter_node->info.alter.entity_name->info.name.original;
if (entity_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Expecting a class or virtual class name.");
goto exit;
}
class_obj = db_find_class (entity_name);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = locator_flush_class (class_obj);
if (error != NO_ERROR)
{
/* don't overwrite error */
goto exit;
}
/* force exclusive lock on class, even though it should have been already acquired */
if (locator_fetch_class (class_obj, DB_FETCH_WRITE) == NULL)
{
error = ER_FAILED;
goto exit;
}
ctemplate = dbt_edit_class (class_obj);
if (ctemplate == NULL)
{
/* when dbt_edit_class fails (e.g. because the server unilaterally aborts us), we must record the associated
* error message into the parser. Otherwise, we may get a confusing error msg of the form: "so_and_so is not a
* class". */
pt_record_error (parser, parser->statement_number - 1, alter_node->line_number, alter_node->column_number,
er_msg (), NULL);
error = er_errid ();
goto exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_COLUMN_COMMENT);
if (error != NO_ERROR)
{
goto exit;
}
tran_saved = true;
attr_node = alter_node->info.alter.alter_clause.attr_mthd.attr_def_list;
while (attr_node != NULL)
{
attr_name = get_attr_name (attr_node);
comment_node = attr_node->info.attr_def.comment;
assert (comment_node != NULL);
assert (comment_node->node_type == PT_VALUE);
meta = (attr_node->info.attr_def.attr_type == PT_META_ATTR);
shared = (attr_node->info.attr_def.attr_type == PT_SHARED);
name_space = (meta) ? ID_CLASS_ATTRIBUTE : ((shared) ? ID_SHARED_ATTRIBUTE : ID_ATTRIBUTE);
/* get the attribute structure */
error = smt_find_attribute (ctemplate, attr_name, (name_space == ID_CLASS_ATTRIBUTE) ? 1 : 0, &found_attr);
if (error != NO_ERROR)
{
goto exit;
}
assert (found_attr != NULL);
/* comment */
comment_str = comment_node->info.value.data_value.str;
ws_free_string_and_init (found_attr->comment);
found_attr->comment = ws_copy_string ((char *) pt_get_varchar_bytes (comment_str));
if (found_attr->comment == NULL && comment_str != NULL)
{
error = (er_errid () != NO_ERROR) ? er_errid () : ER_FAILED;
goto exit;
}
attr_node = attr_node->next;
}
/* save class MOP */
class_mop = ctemplate->op;
/* force schema update to server */
class_obj = dbt_finish_class (ctemplate);
if (class_obj == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
/* set NULL, avoid 'abort_class' in case of error */
ctemplate = NULL;
exit:
if (ctemplate != NULL)
{
dbt_abort_class (ctemplate);
}
if (error != NO_ERROR && tran_saved && error != ER_LK_UNILATERALLY_ABORTED)
{
(void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_CHANGE_COLUMN_COMMENT);
}
return error;
}
/*
* do_change_att_schema_only() - Change an attribute of a class object
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* attribute(in/out): Attribute to add
*/
static int
do_change_att_schema_only (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attribute, PT_NODE * old_name_node,
PT_NODE * constraints, SM_ATTR_PROP_CHG * attr_chg_prop, SM_ATTR_CHG_SOL * change_mode)
{
DB_VALUE stack_value;
TP_DOMAIN *attr_db_domain = NULL;
DB_VALUE *new_default = NULL;
DB_VALUE *default_value = &stack_value;
SM_ATTRIBUTE *found_att = NULL;
int error = NO_ERROR;
bool change_first = false;
const char *change_after_attr = NULL;
const char *old_name = NULL;
const char *new_name = NULL;
const char *attr_name = NULL;
PARSER_VARCHAR *comment_str = NULL;
DB_DEFAULT_EXPR new_default_expr;
PT_NODE *comment = NULL;
assert (attr_chg_prop != NULL);
assert (change_mode != NULL);
assert (attribute->node_type == PT_ATTR_DEF);
db_make_null (&stack_value);
attr_name = get_attr_name (attribute);
/* get new name */
if (old_name_node != NULL)
{
assert (old_name_node->node_type == PT_NAME);
old_name = old_name_node->info.name.original;
assert (old_name != NULL);
/* attr_name is supplied using the ATTR_DEF node and it means: for MODIFY syntax : current and unchanged name
* (attr_name) for CHANGE syntax : new name of the attribute (new_name) */
if (is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_DIFF))
{
new_name = attr_name;
attr_name = old_name;
}
else
{
attr_name = old_name;
new_name = NULL;
}
}
if (validate_attribute_domain (parser, attribute, smt_get_class_type (ctemplate) == SM_CLASS_CT ? true : false))
{
/* validate_attribute_domain() is assumed to issue whatever messages are pertinent. */
error = ER_FAILED;
goto exit;
}
if (*change_mode == SM_ATTR_CHG_ONLY_SCHEMA)
{
if (attr_chg_prop->name_space == ID_ATTRIBUTE)
{
assert (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_UNCHANGED)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_SET_CLS_COMPAT)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PREC_INCR));
}
else
{
assert (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE || attr_chg_prop->name_space == ID_SHARED_ATTRIBUTE);
assert (!is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG)
&& !is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NOT_SUPPORTED));
}
}
else if (*change_mode == SM_ATTR_CHG_WITH_ROW_UPDATE)
{
assert (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_UPGRADE));
}
else
{
assert (*change_mode == SM_ATTR_CHG_BEST_EFFORT);
/* this mode is needed when: - a type change other than UPGRADE */
assert (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NEED_ROW_CHECK)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PSEUDO_UPGRADE));
}
error = check_default_on_update_clause (parser, attribute);
if (error != NO_ERROR)
{
goto exit;
}
/* default value: for CLASS and SHARED attributes this changes the value itself of the atribute */
error = get_att_default_from_def (parser, attribute, &default_value, NULL);
if (error != NO_ERROR)
{
goto exit;
}
/* default_value is either NULL or pointing to address of stack_value */
assert (default_value == NULL || default_value == &stack_value);
new_default = default_value;
pt_get_default_expression_from_data_default_node (parser, attribute->info.attr_def.data_default, &new_default_expr);
attr_db_domain = pt_node_to_db_domain (parser, attribute, ctemplate->name);
if (attr_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = get_att_order_from_def (attribute, &change_first, &change_after_attr);
if (error != NO_ERROR)
{
goto exit;
}
error = smt_change_attribute_w_dflt_w_order (ctemplate, attr_name, new_name, NULL, attr_db_domain,
attr_chg_prop->name_space, new_default, &new_default_expr,
attribute->info.attr_def.on_update, change_first, change_after_attr,
&found_att);
if (error != NO_ERROR)
{
goto exit;
}
if (found_att == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Attribute not found.");
goto exit;
}
/* if it is an auto_increment column, check its domain */
if (found_att->auto_increment != NULL)
{
PT_NODE *att;
const char *att_name;
switch (attribute->type_enum)
{
case PT_TYPE_INTEGER:
case PT_TYPE_BIGINT:
case PT_TYPE_SMALLINT:
break;
case PT_TYPE_NUMERIC:
if (attribute->data_type->info.data_type.dec_precision == 0)
{
break;
}
[[fallthrough]];
default:
att = attribute->info.attr_def.attr_name;
att_name = att->info.name.original;
PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_AUTO_INCREMENT_DOMAIN, att_name);
error = ER_PT_SEMANTIC;
goto exit;
}
}
if (is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_DIFF))
{
assert (new_name != NULL);
attr_name = new_name;
}
/* save attribute id */
attr_chg_prop->att_id = found_att->id;
if (attr_chg_prop->name_space != ID_ATTRIBUTE)
{
assert (error == NO_ERROR);
goto exit;
}
/* processing only for normal attributes */
/* DEFAULT value */
if (is_att_prop_set (attr_chg_prop->p[P_DEFAULT_VALUE], ATT_CHG_PROPERTY_LOST))
{
pr_clear_value (&(found_att->default_value.value));
classobj_initialize_default_expr (&found_att->default_value.default_expr);
if (found_att->properties != NULL)
{
classobj_drop_prop (found_att->properties, "default_expr");
}
}
/* on update expression */
if (is_att_prop_set (attr_chg_prop->p[P_ON_UPDATE_EXPR], ATT_CHG_PROPERTY_LOST))
{
found_att->on_update_default_expr = DB_DEFAULT_NONE;
if (found_att->properties != NULL)
{
classobj_drop_prop (found_att->properties, "update_default");
}
}
/* add or drop NOT NULL constraint */
if (is_att_prop_set (attr_chg_prop->p[P_NOT_NULL], ATT_CHG_PROPERTY_GAINED))
{
assert (attribute->info.attr_def.constrain_not_null != 0);
/* constraint is added later when new constraints are created */
}
else if (is_att_prop_set (attr_chg_prop->p[P_NOT_NULL], ATT_CHG_PROPERTY_LOST))
{
error =
dbt_constrain_non_null (ctemplate, attr_name, (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE) ? 1 : 0, 0);
}
/* delete or (re-)create auto_increment attribute's serial object */
if (is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_DIFF)
|| is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_LOST))
{
/* delete current serial */
int save;
OID *oidp, serial_obj_id;
const char *name = found_att->header.name;
if (is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_DIFF))
{
name = old_name;
assert (name != NULL);
}
OID_SET_NULL (&serial_obj_id);
if (found_att->auto_increment == NULL)
{
char auto_increment_name[AUTO_INCREMENT_SERIAL_NAME_MAX_LENGTH];
MOP serial_class_mop, serial_mop;
serial_class_mop = sm_find_class (CT_SERIAL_NAME);
SET_AUTO_INCREMENT_SERIAL_NAME (auto_increment_name, ctemplate->name, name);
serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class_mop, auto_increment_name);
found_att->auto_increment = serial_mop;
}
assert_release (found_att->auto_increment);
error = au_check_serial_authorization (found_att->auto_increment);
if (error != NO_ERROR)
{
goto exit;
}
if (OID_ISNULL (&serial_obj_id))
{
oidp = ws_identifier (found_att->auto_increment);
COPY_OID (&serial_obj_id, oidp);
}
AU_DISABLE (save);
error = obj_delete (found_att->auto_increment);
AU_ENABLE (save);
if (error != NO_ERROR)
{
goto exit;
}
(void) serial_decache (&serial_obj_id);
found_att->flags &= ~(SM_ATTFLAG_AUTO_INCREMENT);
found_att->auto_increment = NULL;
}
/* create or re-create serial with new properties */
if (is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_DIFF)
|| is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_GAINED))
{
error = do_set_auto_increment (parser, ctemplate, attr_name, attribute, &found_att);
if (error != NO_ERROR)
{
goto exit;
}
}
/* the serial property has not changed, we are only dealing with renaming */
if (is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_DIFF)
&& is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_PRESENT_OLD)
&& !is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_DIFF)
&& !is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_LOST)
&& !is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_GAINED))
{
OID serial_obj_id;
OID_SET_NULL (&serial_obj_id);
if (found_att->auto_increment == NULL)
{
char auto_increment_name[AUTO_INCREMENT_SERIAL_NAME_MAX_LENGTH];
MOP serial_class_mop, serial_mop;
serial_class_mop = sm_find_class (CT_SERIAL_NAME);
SET_AUTO_INCREMENT_SERIAL_NAME (auto_increment_name, ctemplate->name, old_name);
serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class_mop, auto_increment_name);
found_att->auto_increment = serial_mop;
}
assert_release (found_att->auto_increment);
error = do_update_auto_increment_serial_on_rename (found_att->auto_increment, ctemplate->name, new_name);
if (error != NO_ERROR)
{
goto exit;
}
}
/* attribute type changed, and auto_increment is set to use(unchanged), update max_val in _db_serial according to new
* type */
if (is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_PRESENT_OLD | ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
MOP auto_increment_obj = NULL;
assert_release (found_att->auto_increment != NULL);
error = do_update_maxvalue_of_auto_increment_serial (parser, &auto_increment_obj, ctemplate->name, attribute);
if (error == NO_ERROR)
{
assert_release (auto_increment_obj != NULL);
if (found_att != NULL)
{
found_att->auto_increment = auto_increment_obj;
found_att->flags |= SM_ATTFLAG_AUTO_INCREMENT;
}
}
else
{
goto exit;
}
}
if (is_att_prop_set (attr_chg_prop->p[P_COMMENT], ATT_CHG_PROPERTY_DIFF))
{
/* free old before assign new */
if (found_att->comment)
{
ws_free_string (found_att->comment);
}
comment = attribute->info.attr_def.comment;
assert (comment != NULL && comment->node_type == PT_VALUE);
comment_str = comment->info.value.data_value.str;
found_att->comment = ws_copy_string ((char *) pt_get_varchar_bytes (comment_str));
if (found_att->comment == NULL)
{
error = (er_errid () != NO_ERROR) ? er_errid () : ER_FAILED;
goto exit;
}
else if (!found_att->comment[0]) /* empty string */
{
ws_free_string (found_att->comment);
found_att->comment = NULL;
}
}
else if (is_att_prop_set (attr_chg_prop->p[P_COMMENT], ATT_CHG_PROPERTY_LOST))
{
if (found_att->comment != NULL)
{
ws_free_string (found_att->comment);
found_att->comment = NULL;
}
}
assert (attr_chg_prop->name_space == ID_ATTRIBUTE);
exit:
db_value_clear (&stack_value);
return error;
}
/*
* build_attr_change_map() - This builds a map of changes on the attribute
* return: Error code
* parser(in): Parser context
* ctemplate(in): Class template
* attr_def(in): New attribute definition (PT_NODE : PT_ATTR_DEF)
* attr_old_name(in): Old name of attribute (PT_NODE : PT_NAME)
* constraints(in): New constraints for class template
* (PT_NODE : PT_CONSTRAINT)
* attr_chg_properties(out): map of attribute changes to build
*
*/
static int
build_attr_change_map (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attr_def, PT_NODE * attr_old_name,
PT_NODE * constraints, SM_ATTR_PROP_CHG * attr_chg_properties)
{
TP_DOMAIN *attr_db_domain = NULL;
SM_ATTRIBUTE *att = NULL;
SM_CLASS_CONSTRAINT *sm_cls_constr = NULL;
PT_NODE *cnstr = NULL;
const char *attr_name = NULL;
const char *old_name = NULL;
const char *new_name = NULL;
int error = NO_ERROR;
PT_NODE *comment;
attr_name = get_attr_name (attr_def);
/* attribute name */
attr_chg_properties->p[P_NAME] = 0;
attr_chg_properties->p[P_NAME] |= ATT_CHG_PROPERTY_PRESENT_OLD;
if (attr_old_name != NULL)
{
assert (attr_old_name->node_type == PT_NAME);
old_name = attr_old_name->info.name.original;
assert (old_name != NULL);
/* attr_name is supplied using the ATTR_DEF node and it means: for MODIFY syntax : current and unchanged node
* (attr_name) for CHANGE syntax : new name of the attribute (new_name) */
new_name = attr_name;
attr_name = old_name;
attr_chg_properties->p[P_NAME] |= ATT_CHG_PROPERTY_PRESENT_NEW;
if (intl_identifier_casecmp (attr_name, new_name) == 0)
{
attr_chg_properties->p[P_NAME] |= ATT_CHG_PROPERTY_UNCHANGED;
}
else
{
attr_chg_properties->p[P_NAME] |= ATT_CHG_PROPERTY_DIFF;
}
}
else
{
attr_chg_properties->p[P_NAME] |= ATT_CHG_PROPERTY_UNCHANGED;
}
/* at this point, attr_name is the current name of the attribute, new_name is either the desired new name or NULL, if
* name change is not requested */
/* get the attribute structure */
error =
smt_find_attribute (ctemplate, attr_name, (attr_chg_properties->name_space == ID_CLASS_ATTRIBUTE) ? 1 : 0, &att);
if (error != NO_ERROR)
{
return error;
}
assert (att != NULL);
attr_chg_properties->name_space = att->header.name_space;
if (attr_def->info.attr_def.attr_type == PT_NORMAL)
{
attr_chg_properties->new_name_space = ID_ATTRIBUTE;
}
else if (attr_def->info.attr_def.attr_type == PT_SHARED)
{
attr_chg_properties->new_name_space = ID_SHARED_ATTRIBUTE;
}
if (attr_def->info.attr_def.data_default != NULL)
{
if (attr_def->info.attr_def.data_default->info.data_default.shared == PT_SHARED)
{
attr_chg_properties->new_name_space = ID_SHARED_ATTRIBUTE;
}
}
/* DEFAULT value */
attr_chg_properties->p[P_DEFAULT_VALUE] = 0;
if (attr_def->info.attr_def.data_default != NULL)
{
attr_chg_properties->p[P_DEFAULT_VALUE] |= ATT_CHG_PROPERTY_PRESENT_NEW;
}
if (!DB_IS_NULL (&(att->default_value.original_value)) || !DB_IS_NULL (&(att->default_value.value))
|| att->default_value.default_expr.default_expr_type != DB_DEFAULT_NONE)
{
attr_chg_properties->p[P_DEFAULT_VALUE] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* ON UPDATE expr */
attr_chg_properties->p[P_ON_UPDATE_EXPR] = 0;
if (attr_def->info.attr_def.on_update != DB_DEFAULT_NONE)
{
attr_chg_properties->p[P_ON_UPDATE_EXPR] |= ATT_CHG_PROPERTY_PRESENT_NEW;
}
if (att->on_update_default_expr != DB_DEFAULT_NONE)
{
attr_chg_properties->p[P_ON_UPDATE_EXPR] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* DEFFERABLE : not supported, just mark as checked */
attr_chg_properties->p[P_DEFFERABLE] = 0;
/* ORDERING */
attr_chg_properties->p[P_ORDER] = 0;
if (attr_def->info.attr_def.ordering_info != NULL)
{
attr_chg_properties->p[P_ORDER] |= ATT_CHG_PROPERTY_PRESENT_NEW;
}
/* AUTO INCREMENT */
attr_chg_properties->p[P_AUTO_INCR] = 0;
if (attr_def->info.attr_def.auto_increment != NULL)
{
attr_chg_properties->p[P_AUTO_INCR] |= ATT_CHG_PROPERTY_PRESENT_NEW;
}
if (att->flags & SM_ATTFLAG_AUTO_INCREMENT)
{
attr_chg_properties->p[P_AUTO_INCR] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/*
* DEFAULT value and AUTO INCREMENT cannot be defined for the same column.
* Therefore, it will change to the property on which the last MODIFY statement was executed.
*/
if (attr_def->info.attr_def.data_default != NULL)
{
if (att->flags & SM_ATTFLAG_AUTO_INCREMENT)
{
attr_chg_properties->p[P_AUTO_INCR] |= ATT_CHG_PROPERTY_LOST;
}
}
else if (attr_def->info.attr_def.auto_increment != NULL)
{
if ((!DB_IS_NULL (&(att->default_value.original_value)) || !DB_IS_NULL (&(att->default_value.value))
|| att->default_value.default_expr.default_expr_type != DB_DEFAULT_NONE))
{
attr_chg_properties->p[P_DEFAULT_VALUE] |= ATT_CHG_PROPERTY_LOST;
}
}
/* existing FOREIGN KEY (referencing) */
attr_chg_properties->p[P_CONSTR_FK] = 0;
if (att->flags & SM_ATTFLAG_FOREIGN_KEY)
{
attr_chg_properties->p[P_CONSTR_FK] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* existing PRIMARY KEY: mark as checked */
attr_chg_properties->p[P_S_CONSTR_PK] = 0;
attr_chg_properties->p[P_M_CONSTR_PK] = 0;
/* existing non-unique INDEX ? */
attr_chg_properties->p[P_CONSTR_NON_UNI] = 0;
if (att->flags & SM_ATTFLAG_INDEX)
{
attr_chg_properties->p[P_CONSTR_NON_UNI] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* prefix index */
attr_chg_properties->p[P_PREFIX_INDEX] = 0;
/* constraint : NOT NULL */
attr_chg_properties->p[P_NOT_NULL] = 0;
if (att->flags & SM_ATTFLAG_NON_NULL)
{
attr_chg_properties->p[P_NOT_NULL] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* constraint CHECK : not supported, just mark as checked */
attr_chg_properties->p[P_CONSTR_CHECK] = 0;
/* check for existing constraints: FK referenced, unique, non-unique idx */
if (ctemplate->current != NULL)
{
const char *attr_name_to_check = attr_name;
attr_chg_properties->p[P_S_CONSTR_UNI] = 0;
attr_chg_properties->p[P_M_CONSTR_UNI] = 0;
for (sm_cls_constr = ctemplate->current->constraints; sm_cls_constr != NULL; sm_cls_constr = sm_cls_constr->next)
{
/* check if attribute is contained in this constraint */
SM_ATTRIBUTE **sm_constr_attr = sm_cls_constr->attributes;
int nb_att_in_constr = 0;
int attr_name_found_at = -1;
while (*sm_constr_attr != NULL)
{
if ((*sm_constr_attr)->header.name != NULL
&& (*sm_constr_attr)->header.name_space == att->header.name_space
&& !intl_identifier_casecmp ((*sm_constr_attr)->header.name, attr_name_to_check))
{
attr_name_found_at = nb_att_in_constr;
}
sm_constr_attr++;
nb_att_in_constr++;
}
if (attr_name_found_at != -1)
{
bool save_constr = false;
/* referenced FK */
if (sm_cls_constr->fk_info != NULL)
{
assert (sm_cls_constr->fk_info->name != NULL);
attr_chg_properties->p[P_CONSTR_FK] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
/* PRIMARY KEY */
if (sm_cls_constr->type == SM_CONSTRAINT_PRIMARY_KEY)
{
assert (nb_att_in_constr >= 1);
if (nb_att_in_constr >= 2)
{
attr_chg_properties->p[P_M_CONSTR_PK] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
else
{
attr_chg_properties->p[P_S_CONSTR_PK] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
attr_chg_properties->p[P_NOT_NULL] |= ATT_CHG_PROPERTY_PRESENT_NEW;
save_constr = true;
}
/* non-unique index */
else if (sm_cls_constr->type == SM_CONSTRAINT_INDEX || sm_cls_constr->type == SM_CONSTRAINT_REVERSE_INDEX)
{
assert (nb_att_in_constr >= 1);
attr_chg_properties->p[P_CONSTR_NON_UNI] |= ATT_CHG_PROPERTY_PRESENT_OLD;
save_constr = true;
if (sm_cls_constr->attrs_prefix_length != NULL
&& sm_cls_constr->attrs_prefix_length[attr_name_found_at] != -1)
{
attr_chg_properties->p[P_PREFIX_INDEX] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
}
/* UNIQUE */
else if (sm_cls_constr->type == SM_CONSTRAINT_UNIQUE
|| sm_cls_constr->type == SM_CONSTRAINT_REVERSE_UNIQUE)
{
assert (nb_att_in_constr >= 1);
if (nb_att_in_constr >= 2)
{
attr_chg_properties->p[P_M_CONSTR_UNI] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
else
{
attr_chg_properties->p[P_S_CONSTR_UNI] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
save_constr = true;
}
if (save_constr)
{
assert (attr_chg_properties->name_space == ID_ATTRIBUTE);
error = sm_save_constraint_info (&(attr_chg_properties->constr_info), sm_cls_constr);
if (error != NO_ERROR)
{
return error;
}
}
}
}
}
else
{
error = ER_OBJ_TEMPLATE_INTERNAL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
/* attribute is saved in constraints info with the old name; replace all occurences with the new name; constraints
* names are not adjusted to reflect new attribute name, but are kept with the old name, reason: MySQL compatibility */
if ((new_name != NULL) && (attr_name != NULL) && (attr_chg_properties->constr_info != NULL)
&& (intl_identifier_casecmp (new_name, attr_name) != 0))
{
SM_CONSTRAINT_INFO *saved_constr = NULL;
for (saved_constr = attr_chg_properties->constr_info; saved_constr != NULL; saved_constr = saved_constr->next)
{
char **c_name = NULL;
for (c_name = saved_constr->att_names; *c_name != NULL; ++c_name)
{
if (intl_identifier_casecmp (attr_name, *c_name) == 0)
{
free_and_init (*c_name);
*c_name = strdup (new_name);
if (*c_name == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
(strlen (new_name) + 1) * sizeof (char));
return ER_OUT_OF_VIRTUAL_MEMORY;
}
}
}
}
}
/* check for constraints in the new attribute definition */
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
PT_NODE *constr_att = NULL;
PT_NODE *constr_att_list = NULL;
bool save_pt_costraint = false;
int chg_prop_idx = NUM_ATT_CHG_PROP;
const char *attr_name_to_check = attr_name;
if (is_att_prop_set (attr_chg_properties->p[P_NAME], ATT_CHG_PROPERTY_DIFF))
{
attr_name_to_check = new_name;
}
assert (cnstr->node_type == PT_CONSTRAINT);
switch (cnstr->info.constraint.type)
{
case PT_CONSTRAIN_FOREIGN_KEY:
constr_att_list = cnstr->info.constraint.un.foreign_key.attrs;
chg_prop_idx = P_CONSTR_FK;
break;
case PT_CONSTRAIN_PRIMARY_KEY:
constr_att_list = cnstr->info.constraint.un.primary_key.attrs;
chg_prop_idx = P_S_CONSTR_PK;
save_pt_costraint = true;
break;
case PT_CONSTRAIN_UNIQUE:
constr_att_list = cnstr->info.constraint.un.unique.attrs;
chg_prop_idx = P_S_CONSTR_UNI;
save_pt_costraint = true;
break;
case PT_CONSTRAIN_NULL:
attr_chg_properties->p[P_NOT_NULL] = ATT_CHG_PROPERTY_LOST;
[[fallthrough]];
case PT_CONSTRAIN_NOT_NULL:
constr_att_list = cnstr->info.constraint.un.not_null.attr;
chg_prop_idx = P_NOT_NULL;
save_pt_costraint = true;
break;
case PT_CONSTRAIN_CHECK:
/* not supported, just mark as 'PRESENT' */
assert (false);
attr_chg_properties->p[P_CONSTR_CHECK] |= ATT_CHG_PROPERTY_PRESENT_NEW;
continue;
default:
assert (false);
}
for (constr_att = constr_att_list; constr_att != NULL; constr_att = constr_att->next)
{
assert (constr_att->node_type == PT_NAME);
if (intl_identifier_casecmp (attr_name_to_check, constr_att->info.name.original) == 0)
{
if (chg_prop_idx >= NUM_ATT_CHG_PROP)
{
continue;
}
assert (chg_prop_idx < NUM_ATT_CHG_PROP);
assert (chg_prop_idx >= 0);
/* save new constraint only if it is not already present in current template */
if (save_pt_costraint
&& !is_att_prop_set (attr_chg_properties->p[chg_prop_idx], ATT_CHG_PROPERTY_PRESENT_OLD))
{
error = save_constraint_info_from_pt_node (&(attr_chg_properties->new_constr_info), cnstr);
if (error != NO_ERROR)
{
return error;
}
}
attr_chg_properties->p[chg_prop_idx] |= ATT_CHG_PROPERTY_PRESENT_NEW;
break;
}
}
}
/* partitions: */
attr_chg_properties->p[P_IS_PARTITION_COL] = 0;
if (ctemplate->partition)
{
char keycol[DB_MAX_IDENTIFIER_LENGTH] = { 0 };
assert (attr_chg_properties->name_space == ID_ATTRIBUTE);
error = do_get_partition_keycol (keycol, ctemplate->op);
if (error != NO_ERROR)
{
return error;
}
if (intl_identifier_casecmp (keycol, attr_name) == 0)
{
attr_chg_properties->p[P_IS_PARTITION_COL] |= ATT_CHG_PROPERTY_PRESENT_OLD;
}
}
/* DOMAIN */
attr_db_domain = pt_node_to_db_domain (parser, attr_def, ctemplate->name);
if (attr_db_domain == NULL)
{
assert (er_errid () != NO_ERROR);
return (er_errid ());
}
attr_chg_properties->p[P_TYPE] = 0;
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_PRESENT_NEW;
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_PRESENT_OLD;
/* consolidate properties : */
{
int i = 0;
for (i = 0; i < NUM_ATT_CHG_PROP; i++)
{
int *const p = &(attr_chg_properties->p[i]);
if (*p & ATT_CHG_PROPERTY_LOST)
{
continue;
}
if (*p & ATT_CHG_PROPERTY_PRESENT_OLD)
{
if (*p & ATT_CHG_PROPERTY_PRESENT_NEW)
{
*p |= ATT_CHG_PROPERTY_UNCHANGED;
}
else
{
*p |= ATT_CHG_PROPERTY_UNCHANGED;
}
}
else
{
if (*p & ATT_CHG_PROPERTY_PRESENT_NEW)
{
*p |= ATT_CHG_PROPERTY_GAINED;
}
else
{
*p |= ATT_CHG_PROPERTY_UNCHANGED;
}
}
if (is_att_prop_set (*p, ATT_CHG_PROPERTY_DIFF) && is_att_prop_set (*p, ATT_CHG_PROPERTY_UNCHANGED))
{
/* remove UNCHANGED flag if DIFF flag was already set */
*p &= ~ATT_CHG_PROPERTY_UNCHANGED;
}
}
}
/* special case : TYPE */
if (tp_domain_match (attr_db_domain, att->domain, TP_EXACT_MATCH) != 0)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_UNCHANGED;
}
else
{
assert (attr_db_domain->type != NULL);
/* remove "UNCHANGED" flag */
attr_chg_properties->p[P_TYPE] &= ~ATT_CHG_PROPERTY_UNCHANGED;
if (TP_DOMAIN_TYPE (attr_db_domain) == TP_DOMAIN_TYPE (att->domain)
&& TP_IS_CHAR_BIT_TYPE (TP_DOMAIN_TYPE (attr_db_domain)))
{
if (tp_domain_match (attr_db_domain, att->domain, TP_STR_MATCH) != 0)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PREC_INCR;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_DIFF;
if (attr_db_domain->precision >= att->domain->precision)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
(void) build_att_coll_change_map (att->domain, attr_db_domain, attr_chg_properties);
}
else
{
assert (attr_db_domain->precision < att->domain->precision
|| (TP_DOMAIN_COLLATION (attr_db_domain) != TP_DOMAIN_COLLATION (att->domain)));
if (QSTR_IS_FIXED_LENGTH (TP_DOMAIN_TYPE (attr_db_domain))
&& prm_get_bool_value (PRM_ID_ALTER_TABLE_CHANGE_TYPE_STRICT) == true)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
(void) build_att_coll_change_map (att->domain, attr_db_domain, attr_chg_properties);
}
}
}
}
else if (TP_DOMAIN_TYPE (attr_db_domain) == TP_DOMAIN_TYPE (att->domain)
&& TP_DOMAIN_TYPE (attr_db_domain) == DB_TYPE_NUMERIC)
{
if (attr_db_domain->scale == att->domain->scale && attr_db_domain->precision > att->domain->precision)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PREC_INCR;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
}
else if (TP_IS_SET_TYPE (TP_DOMAIN_TYPE (attr_db_domain)) && TP_IS_SET_TYPE (TP_DOMAIN_TYPE (att->domain)))
{
if (tp_domain_compatible (att->domain, attr_db_domain) != 0)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_SET_CLS_COMPAT;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_DIFF;
}
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_PROPERTY_DIFF;
error = build_att_type_change_map (att->domain, attr_db_domain, attr_chg_properties);
if (error != NO_ERROR)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
return error;
}
}
}
tp_domain_free (attr_db_domain);
/* special case : AUTO INCREMENT if start value specified, we create a new serial or keep the old one Be mysql
* compatible, see CUBRIDSUS-6441 */
if (is_att_prop_set (attr_chg_properties->p[P_AUTO_INCR], ATT_CHG_PROPERTY_PRESENT_OLD | ATT_CHG_PROPERTY_PRESENT_NEW)
&& attr_def->info.attr_def.auto_increment->info.auto_increment.start_val != NULL)
{
attr_chg_properties->p[P_AUTO_INCR] |= ATT_CHG_PROPERTY_DIFF;
/* remove "UNCHANGED" flag */
attr_chg_properties->p[P_AUTO_INCR] &= ~ATT_CHG_PROPERTY_UNCHANGED;
}
/* special case : DEFAULT */
if (is_att_prop_set (attr_chg_properties->p[P_DEFAULT_VALUE],
(ATT_CHG_PROPERTY_PRESENT_OLD | ATT_CHG_PROPERTY_PRESENT_NEW)))
{
attr_chg_properties->p[P_DEFAULT_VALUE] |= ATT_CHG_PROPERTY_DIFF;
/* remove "UNCHANGED" flag */
attr_chg_properties->p[P_DEFAULT_VALUE] &= ~ATT_CHG_PROPERTY_UNCHANGED;
}
/* special case : UNIQUE on multiple columns */
if (is_att_prop_set (attr_chg_properties->p[P_M_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_OLD))
{
if (is_att_prop_set (attr_chg_properties->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
attr_chg_properties->p[P_M_CONSTR_UNI] |= ATT_CHG_PROPERTY_DIFF;
/* remove "UNCHANGED" flag */
attr_chg_properties->p[P_M_CONSTR_UNI] &= ~ATT_CHG_PROPERTY_UNCHANGED;
}
else
{
attr_chg_properties->p[P_M_CONSTR_UNI] |= ATT_CHG_PROPERTY_UNCHANGED;
}
}
/* comment */
attr_chg_properties->p[P_COMMENT] = 0;
comment = attr_def->info.attr_def.comment;
if (comment != NULL)
{
assert (comment->node_type == PT_VALUE);
if (comment->info.value.data_value.str != NULL)
{
attr_chg_properties->p[P_COMMENT] |= ATT_CHG_PROPERTY_DIFF;
/* remove "LOST" flag */
attr_chg_properties->p[P_COMMENT] &= ~ATT_CHG_PROPERTY_LOST;
}
}
else
{
attr_chg_properties->p[P_COMMENT] |= ATT_CHG_PROPERTY_UNCHANGED;
}
return error;
}
/*
* build_att_type_change_map() - This checks the attribute type change
*
* return: Error code
* parser(in): Parser context
* curr_domain(in): Current domain of the atribute
* req_domain(in): Requested (new) domain of the attribute
* attr_chg_properties(out): structure summarizing the changed properties
* of attribute
*/
static int
build_att_type_change_map (TP_DOMAIN * curr_domain, TP_DOMAIN * req_domain, SM_ATTR_PROP_CHG * attr_chg_properties)
{
int error = NO_ERROR;
const int MIN_DIGITS_FOR_INTEGER = TP_INTEGER_PRECISION;
const int MIN_DIGITS_FOR_SHORT = TP_SMALLINT_PRECISION;
const int MIN_DIGITS_FOR_BIGINT = TP_BIGINT_PRECISION;
const int MIN_CHARS_FOR_TIME = TP_TIME_AS_CHAR_LENGTH;
const int MIN_CHARS_FOR_DATE = TP_DATE_AS_CHAR_LENGTH;
const int MIN_CHARS_FOR_DATETIME = TP_DATETIME_AS_CHAR_LENGTH;
const int MIN_CHARS_FOR_DATETIMETZ = TP_DATETIMETZ_AS_CHAR_LENGTH;
const int MIN_CHARS_FOR_TIMESTAMP = TP_TIMESTAMP_AS_CHAR_LENGTH;
const int MIN_CHARS_FOR_TIMESTAMPTZ = TP_TIMESTAMPTZ_AS_CHAR_LENGTH;
DB_TYPE current_type = TP_DOMAIN_TYPE (curr_domain);
DB_TYPE new_type = TP_DOMAIN_TYPE (req_domain);
int req_prec = req_domain->precision;
int req_scale = req_domain->scale;
int cur_prec = curr_domain->precision;
int cur_scale = curr_domain->scale;
bool is_req_max_prec = false;
/* check if maximum precision was requested for new domain */
if (new_type == DB_TYPE_VARCHAR)
{
if (req_prec == DB_MAX_VARCHAR_PRECISION)
{
is_req_max_prec = true;
}
else if (req_prec == TP_FLOATING_PRECISION_VALUE)
{
req_prec = DB_MAX_VARCHAR_PRECISION;
is_req_max_prec = true;
}
else
{
assert (req_prec >= 0);
}
}
else
{
assert (is_req_max_prec == false);
}
switch (current_type)
{
case DB_TYPE_SHORT:
switch (new_type)
{
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_NUMERIC:
if (req_prec - req_scale >= MIN_DIGITS_FOR_SHORT)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_DIGITS_FOR_SHORT + 1)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_INTEGER:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_FLOAT:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_BIGINT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_NUMERIC:
if (req_prec - req_scale >= MIN_DIGITS_FOR_INTEGER)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_DIGITS_FOR_INTEGER + 1)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_BIGINT:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_NUMERIC:
if (req_prec - req_scale >= MIN_DIGITS_FOR_BIGINT)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_DIGITS_FOR_BIGINT + 1)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_NUMERIC:
switch (new_type)
{
case DB_TYPE_SHORT:
if ((cur_prec < MIN_DIGITS_FOR_SHORT) && (cur_scale == 0))
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_INTEGER:
if ((cur_prec < MIN_DIGITS_FOR_INTEGER) && (cur_scale == 0))
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_BIGINT:
if ((cur_prec < MIN_DIGITS_FOR_BIGINT) && (cur_scale == 0))
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= cur_prec + 2)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_FLOAT:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_CHAR:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_VARCHAR:
if (is_req_max_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_DOUBLE:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_CHAR:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_VARCHAR:
if (is_req_max_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_MONETARY:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_MONETARY:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_CHAR:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_VARCHAR:
if (is_req_max_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
case DB_TYPE_DOUBLE:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_TIME:
switch (new_type)
{
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_TIME)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_DATE:
switch (new_type)
{
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_DATE)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_DATETIME:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_DATETIME)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_DATETIMETZ:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_TIMESTAMP:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_TIMESTAMPTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_DATETIMETZ)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_DATETIMELTZ:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_DATETIMETZ)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_TIMESTAMP:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_TIMESTAMP)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_TIMESTAMPTZ:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMELTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_DATETIMETZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_TIMESTAMPTZ)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_TIMESTAMPLTZ:
switch (new_type)
{
case DB_TYPE_TIME:
case DB_TYPE_DATE:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
break;
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
break;
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
if (req_prec >= MIN_CHARS_FOR_TIMESTAMPTZ)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
break;
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_CHAR:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
case DB_TYPE_DATE:
case DB_TYPE_TIME:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_VARCHAR:
if (req_prec >= cur_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
if (prm_get_bool_value (PRM_ID_ALTER_TABLE_CHANGE_TYPE_STRICT) == true)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
}
break;
default:
assert (new_type != DB_TYPE_CHAR);
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_VARCHAR:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_MONETARY:
case DB_TYPE_DATE:
case DB_TYPE_TIME:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
case DB_TYPE_ENUMERATION:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
case DB_TYPE_CHAR:
if (req_prec >= cur_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
default:
assert (new_type != DB_TYPE_VARCHAR);
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_BIT:
switch (new_type)
{
case DB_TYPE_VARBIT:
if (req_prec >= cur_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
if (prm_get_bool_value (PRM_ID_ALTER_TABLE_CHANGE_TYPE_STRICT) == true)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
}
break;
default:
assert (new_type != DB_TYPE_BIT);
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_VARBIT:
switch (new_type)
{
case DB_TYPE_BIT:
if (req_prec >= cur_prec)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_UPGRADE;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
}
break;
default:
assert (new_type != DB_TYPE_VARBIT);
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
case DB_TYPE_OBJECT:
if (new_type != DB_TYPE_OBJECT)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
else
{
assert (db_is_class (curr_domain->class_mop) != 0);
assert (db_is_class (req_domain->class_mop) != 0);
if (req_domain->class_mop != curr_domain->class_mop)
{
if (db_is_subclass (curr_domain->class_mop, req_domain->class_mop) <= 0)
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
else
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_SET_CLS_COMPAT;
}
}
else
{
/* same OBJECT, should have been checked earlier */
assert (false);
attr_chg_properties->p[P_TYPE] &= ~ATT_CHG_PROPERTY_DIFF;
}
}
break;
case DB_TYPE_ENUMERATION:
switch (new_type)
{
case DB_TYPE_SHORT:
case DB_TYPE_INTEGER:
case DB_TYPE_BIGINT:
case DB_TYPE_NUMERIC:
case DB_TYPE_FLOAT:
case DB_TYPE_DOUBLE:
case DB_TYPE_CHAR:
case DB_TYPE_VARCHAR:
case DB_TYPE_MONETARY:
case DB_TYPE_ENUMERATION:
case DB_TYPE_DATE:
case DB_TYPE_DATETIME:
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIME:
case DB_TYPE_TIMESTAMP:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NEED_ROW_CHECK;
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
break;
default:
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
break;
}
(void) build_att_coll_change_map (curr_domain, req_domain, attr_chg_properties);
return error;
}
/*
* build_att_coll_change_map() - This checks the attribute collation and
* codeset change
*
* return: Error code
* parser(in): Parser context
* curr_domain(in): Current domain of the attribute
* req_domain(in): Requested (new) domain of the attribute
* attr_chg_properties(out): structure summarizing the changed properties
* of attribute
*/
static int
build_att_coll_change_map (TP_DOMAIN * curr_domain, TP_DOMAIN * req_domain, SM_ATTR_PROP_CHG * attr_chg_properties)
{
/* check collation change */
if (TP_TYPE_HAS_COLLATION (TP_DOMAIN_TYPE (curr_domain)) && TP_TYPE_HAS_COLLATION (TP_DOMAIN_TYPE (req_domain)))
{
const int curr_coll_id = TP_DOMAIN_COLLATION (curr_domain);
const int req_coll_id = TP_DOMAIN_COLLATION (req_domain);
const INTL_CODESET curr_cs = TP_DOMAIN_CODESET (curr_domain);
const INTL_CODESET req_cs = TP_DOMAIN_CODESET (req_domain);
if (curr_coll_id != req_coll_id)
{
if (!INTL_CAN_COERCE_CS (curr_cs, req_cs))
{
/* change of codeset not supported */
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
else
{
/* change of collation allowed : requires index recreation */
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_PSEUDO_UPGRADE;
}
if (is_att_prop_set (attr_chg_properties->p[P_PREFIX_INDEX], ATT_CHG_PROPERTY_PRESENT_OLD))
{
/* check if new collation has expansions */
LANG_COLLATION *lc = lang_get_collation (req_coll_id);
assert (lc != NULL);
if (!(lc->options.allow_prefix_index))
{
attr_chg_properties->p[P_TYPE] |= ATT_CHG_TYPE_NOT_SUPPORTED;
}
}
}
else
{
/* collation and codeset unchanged */
assert (curr_cs == req_cs);
}
}
return NO_ERROR;
}
/*
* check_att_chg_allowed() - This checks if the attribute change is possible,
* if not it sets an appropiate error
*
* return: NO_ERROR if changed allowed, an error code if change not allowed
* att_name(in): name of attribute (to display in error messages)
* t(in): new type (parse tree namespace)
* attr_chg_properties(in): structure summarizing the changed properties of
* attribute
* chg_how(in): the strategy for which the check is requested
* log_error_allowed(in): log the error if any
* new_attempt(out): is set to false if a new attempt with different
* 'chg_how' argument may produce a positive result
*
* Note : this function may be called several times, each time esacalating
* the 'chg_how' mode parameter; the caller should ensure that only
* the last call allows also to log an error, by setting the
* 'log_error_allowed' argument.
* The caller should also check the 'new_attempt' value before trying
* a new 'chg_how' argument.
* All error codes set in this function must correspond to messages
* with one argument, otherwise additional processing must be done
* before tracing the error.
*/
static int
check_att_chg_allowed (const char *att_name, const PT_TYPE_ENUM t, const SM_ATTR_PROP_CHG * attr_chg_prop,
SM_ATTR_CHG_SOL chg_how, bool log_error_allowed, bool * new_attempt)
{
int error = NO_ERROR;
/* these are error codes issued by ALTER CHANGE which map on other exising ALTER CHANGE error messages; they are kept
* with different names for better differentiation between error contexts */
const int ER_ALTER_CHANGE_TYPE_WITH_NON_UNIQUE = ER_ALTER_CHANGE_TYPE_WITH_INDEX;
const int ER_ALTER_CHANGE_TYPE_WITH_M_UNIQUE = ER_ALTER_CHANGE_TYPE_WITH_INDEX;
const int ER_ALTER_CHANGE_TYPE_WITH_S_UNIQUE = ER_ALTER_CHANGE_TYPE_WITH_INDEX;
const int ER_ALTER_CHANGE_TYPE_WITH_PK = ER_ALTER_CHANGE_TYPE_WITH_INDEX;
/* by default we advise new attempt */
*new_attempt = true;
/* partitions not allowed : this check (by value instead of bit) ensures that the column doesn't have partitions in
* current schema and in new definition */
if (attr_chg_prop->p[P_IS_PARTITION_COL] != ATT_CHG_PROPERTY_UNCHANGED)
{
error = ER_ALTER_CHANGE_PARTITIONS;
*new_attempt = false;
goto not_allowed;
}
/* foreign key not allowed : this check (by value instead of bit) ensures that the column doesn't have a foreign key
* in current schema and in new definition */
if (attr_chg_prop->p[P_CONSTR_FK] != ATT_CHG_PROPERTY_UNCHANGED)
{
error = ER_ALTER_CHANGE_FK;
*new_attempt = false;
goto not_allowed;
}
if ((attr_chg_prop->name_space == ID_SHARED_ATTRIBUTE && attr_chg_prop->new_name_space == ID_ATTRIBUTE)
|| (attr_chg_prop->name_space == ID_ATTRIBUTE && attr_chg_prop->new_name_space == ID_SHARED_ATTRIBUTE))
{
error = ER_ALTER_CHANGE_ATTR_TO_FROM_SHARED_NOT_ALLOWED;
*new_attempt = false;
goto not_allowed;
}
/* unique key : drop is allowed */
/* unique key : gaining UK is matter of adding a new constraint */
/* primary key : drop is allowed */
/* primary key : gaining PK is matter of adding a new constraint */
/* NOT NULL : gaining is not always allowed */
if (is_att_prop_set (attr_chg_prop->p[P_NOT_NULL], ATT_CHG_PROPERTY_GAINED))
{
if (t == PT_TYPE_BLOB || t == PT_TYPE_CLOB)
{
error = ER_SM_NOT_NULL_NOT_ALLOWED;
*new_attempt = false;
goto not_allowed;
}
if (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE)
{
error = ER_SM_INVALID_CONSTRAINT;
*new_attempt = false;
goto not_allowed;
}
if (prm_get_bool_value (PRM_ID_ALTER_TABLE_CHANGE_TYPE_STRICT) == false)
{
/* in permissive mode, we may have to convert existent NULL values to hard- defaults, so make sure the hard
* default type exists */
if (get_hard_default_for_type (t) == NULL)
{
error = ER_ALTER_CHANGE_HARD_DEFAULT_NOT_EXIST;
*new_attempt = false;
goto not_allowed;
}
}
/* gaining NOT NULL is matter of adding a new constraint */
}
/* check type changes and ... */
/* check if AUTO_INCR is gained: */
if (is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_GAINED))
{
if (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE || attr_chg_prop->name_space == ID_SHARED_ATTRIBUTE)
{
error = ER_SM_INVALID_CONSTRAINT;
*new_attempt = false;
goto not_allowed;
}
if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
if (chg_how == SM_ATTR_CHG_ONLY_SCHEMA)
{
error = ER_ALTER_CHANGE_TYPE_WITH_AUTO_INCR;
goto not_allowed;
}
}
}
/* check type change */
if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NOT_SUPPORTED))
{
error = ER_ALTER_CHANGE_TYPE_NOT_SUPP;
*new_attempt = false;
goto not_allowed;
}
else if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NOT_SUPPORTED_WITH_CFG))
{
error = ER_ALTER_CHANGE_TYPE_UPGRADE_CFG;
*new_attempt = false;
goto not_allowed;
}
else if (chg_how == SM_ATTR_CHG_ONLY_SCHEMA)
{
if (attr_chg_prop->name_space != ID_ATTRIBUTE)
{
/* allow any type change (except when not supported by config) for class and shared attributes */
assert (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE || attr_chg_prop->name_space == ID_SHARED_ATTRIBUTE);
assert (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_UNCHANGED)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NEED_ROW_CHECK)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PSEUDO_UPGRADE)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_UPGRADE)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PREC_INCR)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_SET_CLS_COMPAT));
}
else
{
if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NEED_ROW_CHECK)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PSEUDO_UPGRADE))
{
error = ER_ALTER_CHANGE_TYPE_NEED_ROW_CHECK;
goto not_allowed;
}
else if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_UPGRADE))
{
error = ER_ALTER_CHANGE_TYPE_UPGRADE_CFG;
goto not_allowed;
}
else if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF)
&& !(is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PREC_INCR)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_SET_CLS_COMPAT)))
{
error = ER_ALTER_CHANGE_TYPE_NOT_SUPP;
goto not_allowed;
}
}
}
else if (chg_how == SM_ATTR_CHG_WITH_ROW_UPDATE)
{
assert (attr_chg_prop->name_space == ID_ATTRIBUTE);
if (is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_NEED_ROW_CHECK)
|| is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_TYPE_PSEUDO_UPGRADE))
{
error = ER_ALTER_CHANGE_TYPE_NEED_ROW_CHECK;
goto not_allowed;
}
}
else
{
assert (attr_chg_prop->name_space == ID_ATTRIBUTE);
/* allow any change that is not "NOT_SUPPORTED" */
assert (chg_how == SM_ATTR_CHG_BEST_EFFORT);
}
/* these constraints are not allowed under a "schema only" change: */
if (chg_how == SM_ATTR_CHG_ONLY_SCHEMA)
{
/* CLASS and SHARED attribute are incompatible with UNIQUE, PK */
if (attr_chg_prop->name_space == ID_CLASS_ATTRIBUTE || attr_chg_prop->name_space == ID_SHARED_ATTRIBUTE)
{
if (is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_NEW)
|| is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_NEW))
{
error = ER_SM_INVALID_CONSTRAINT;
*new_attempt = false;
goto not_allowed;
}
}
/* cannot keep UNIQUE constr if type is changed */
if (is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_UNI],
ATT_CHG_PROPERTY_PRESENT_OLD | ATT_CHG_PROPERTY_PRESENT_NEW)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
error = ER_ALTER_CHANGE_TYPE_WITH_S_UNIQUE;
goto not_allowed;
}
if (is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_OLD)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
error = ER_ALTER_CHANGE_TYPE_WITH_M_UNIQUE;
goto not_allowed;
}
/* primary key not allowed to be kept when type changes: */
if (is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD | ATT_CHG_PROPERTY_PRESENT_NEW)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
error = ER_ALTER_CHANGE_TYPE_WITH_PK;
goto not_allowed;
}
/* non-unique index not allowed when type changes: */
if (is_att_prop_set (attr_chg_prop->p[P_CONSTR_NON_UNI], ATT_CHG_PROPERTY_PRESENT_OLD)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_DIFF))
{
error = ER_ALTER_CHANGE_TYPE_WITH_NON_UNIQUE;
goto not_allowed;
}
}
/* we should not have multiple primary keys defined */
assert ((is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD))
? (is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD) ? false : true) : true);
/* ALTER .. CHANGE <attribute> syntax should not allow to define PK on multiple rows */
assert (!is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_NEW));
/* check if multiple primary keys after new definition */
if ((is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_NEW))
&& (is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_NEW)))
{
error = ER_ALTER_CHANGE_MULTIPLE_PK;
*new_attempt = false;
goto not_allowed;
}
/* check if class has subclasses: */
if (attr_chg_prop->class_has_subclass
&& !(is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_ORDER], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_TYPE], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_NOT_NULL], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_CONSTR_CHECK], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_DEFFERABLE], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_AUTO_INCR], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_UNCHANGED)
&& is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_UNI], ATT_CHG_PROPERTY_UNCHANGED)))
{
/* allowed changes for class with sub-classes is for DEFAULT value */
error = ER_ALTER_CHANGE_CLASS_HIERARCHY;
*new_attempt = false;
goto not_allowed;
}
return NO_ERROR;
not_allowed:
if (log_error_allowed || !(*new_attempt))
{
if (error == ER_SM_NOT_NULL_NOT_ALLOWED || error == ER_SM_INVALID_CONSTRAINT)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, pt_show_type_enum (t));
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, att_name);
}
}
return error;
}
/*
* is_att_property_structure_checked() - Checks all properties from the
* attribute change properties structure
*
* return : true, if all properties are marked as checked, false otherwise
* attr_chg_properties(in): structure summarizing the changed properties of
* attribute
*
*/
static bool
is_att_property_structure_checked (const SM_ATTR_PROP_CHG * attr_chg_properties)
{
int i = 0;
for (i = 0; i < NUM_ATT_CHG_PROP; i++)
{
if (attr_chg_properties->p[i] >= ATT_CHG_PROPERTY_NOT_CHECKED)
{
return false;
}
}
return true;
}
/*
* is_att_change_needed() - Checks all properties from the attribute change
* properties structure and decides if the schema
* update is necessary
*
* return : true, if schema change is needed, false otherwise
* attr_chg_properties(in): structure summarizing the changed properties of
* attribute
*
*/
static bool
is_att_change_needed (const SM_ATTR_PROP_CHG * attr_chg_properties)
{
int i = 0;
for (i = 0; i < NUM_ATT_CHG_PROP; i++)
{
if (attr_chg_properties->p[i] >= ATT_CHG_PROPERTY_DIFF)
{
return true;
}
if (!is_att_prop_set (attr_chg_properties->p[i], ATT_CHG_PROPERTY_UNCHANGED))
{
return true;
}
}
return false;
}
/*
* is_att_prop_set() - Checks that the properties has the flag set
*
* return : true, if property has the value flag set, false otherwise
* prop(in): property
* value(in): value
*
*/
static bool
is_att_prop_set (const int prop, const int value)
{
return ((prop & value) == value);
}
/*
* reset_att_property_structure() - Resets the attribute change properties
* structure, so that all properties are
* marked as 'unchecked'
*
* attr_chg_properties(in): structure summarizing the changed properties of
* attribute
*
*/
static void
reset_att_property_structure (SM_ATTR_PROP_CHG * attr_chg_properties)
{
int i = 0;
assert (sizeof (attr_chg_properties->p) / sizeof (int) == NUM_ATT_CHG_PROP);
for (i = 0; i < NUM_ATT_CHG_PROP; i++)
{
attr_chg_properties->p[i] = ATT_CHG_PROPERTY_NOT_CHECKED;
}
attr_chg_properties->constr_info = NULL;
attr_chg_properties->new_constr_info = NULL;
attr_chg_properties->att_id = -1;
attr_chg_properties->name_space = ID_NULL;
attr_chg_properties->new_name_space = ID_NULL;
attr_chg_properties->class_has_subclass = false;
}
/*
* get_att_order_from_def() - Retrieves the order properties (first,
* after name) from the attribute definition node
*
* return : NO_ERROR, if success; error code otherwise
* attribute(in): attribute definition node (PT_ATTR_DEF)
* ord_first(out): true if definition contains 'FIRST' specification, false
* otherwise
* ord_after_name(out): name of column 'AFTER <col_name>'
*
*/
static int
get_att_order_from_def (PT_NODE * attribute, bool * ord_first, const char **ord_after_name)
{
PT_NODE *ordering_info = NULL;
assert (attribute->node_type == PT_ATTR_DEF);
ordering_info = attribute->info.attr_def.ordering_info;
if (ordering_info != NULL)
{
assert (ordering_info->node_type == PT_ATTR_ORDERING);
*ord_first = ordering_info->info.attr_ordering.first;
if (ordering_info->info.attr_ordering.after != NULL)
{
PT_NODE *const after_name = ordering_info->info.attr_ordering.after;
assert (after_name->node_type == PT_NAME);
*ord_after_name = after_name->info.name.original;
assert (*ord_first == false);
}
else
{
*ord_after_name = NULL;
/*
* If we have no "AFTER name" then this must have been a "FIRST"
* token
*/
assert (*ord_first == true);
}
}
else
{
*ord_first = false;
*ord_after_name = NULL;
}
return NO_ERROR;
}
static int
check_default_on_update_clause (PARSER_CONTEXT * parser, PT_NODE * attribute)
{
int error = NO_ERROR;
PT_TYPE_ENUM desired_type = attribute->type_enum;
DB_DEFAULT_EXPR_TYPE on_update_expr_type = attribute->info.attr_def.on_update;
PT_NODE *temp_ptval = NULL;
if (on_update_expr_type == DB_DEFAULT_NONE)
{
return error;
}
PT_OP_TYPE op = pt_op_type_from_default_expr_type (on_update_expr_type);
PT_NODE *on_update_default_expr = parser_make_expression (parser, op, NULL, NULL, NULL);
if (on_update_default_expr == NULL)
{
PT_ERRORm (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
return ER_FAILED;
}
on_update_default_expr = pt_semantic_type (parser, on_update_default_expr, NULL);
if (on_update_default_expr == NULL)
{
return ER_FAILED;
}
on_update_default_expr = pt_semantic_check (parser, on_update_default_expr);
if (on_update_default_expr == NULL)
{
return ER_FAILED;
}
on_update_default_expr->buffer_pos = attribute->buffer_pos;
on_update_default_expr->line_number = attribute->line_number;
on_update_default_expr->column_number = attribute->column_number;
DB_VALUE on_update_val;
db_make_null (&on_update_val);
pt_evaluate_tree_having_serial (parser, on_update_default_expr, &on_update_val, 1);
temp_ptval = pt_dbval_to_value (parser, &on_update_val);
if (temp_ptval == NULL)
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
pr_clear_value (&on_update_val);
if (on_update_default_expr != NULL)
{
parser_free_node (parser, on_update_default_expr);
}
return error;
}
error = pt_coerce_value_for_default_value (parser, temp_ptval, temp_ptval, desired_type, attribute->data_type,
on_update_expr_type, true);
if (pt_has_error (parser))
{
/* forget previous one to set the better error */
pt_reset_error (parser);
}
if (error != NO_ERROR)
{
const char *data_type_print;
if (attribute->data_type != NULL)
{
data_type_print = pt_short_print (parser, attribute->data_type);
}
else
{
data_type_print = pt_show_type_enum ((PT_TYPE_ENUM) desired_type);
}
if (error == ER_IT_DATA_OVERFLOW)
{
PT_ERRORmf2 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OVERFLOW_COERCING_TO,
pt_short_print (parser, on_update_default_expr), data_type_print);
}
else
{
PT_ERRORmf2 (parser, attribute, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, on_update_default_expr), data_type_print);
}
}
pr_clear_value (&on_update_val);
if (temp_ptval != NULL)
{
parser_free_node (parser, temp_ptval);
}
if (on_update_default_expr != NULL)
{
parser_free_node (parser, on_update_default_expr);
}
return error;
}
/*
* get_att_default_from_def() - Retrieves the default value property from the
* attribute definition node
*
* return : NO_ERROR, if success; error code otherwise
* parser(in): parser context
* attribute(in): attribute definition node (PT_ATTR_DEF)
* default_value(in/out): default value; this must be initially passed as
* pointer to an allocated DB_VALUE; it is returned
* as NULL if a DEFAULT is not specified for the
* attribute, otherwise the DEFAULT value is returned
* (the initially passed value is used for storage)
* classname(in): If part of create class statement, this argument will be
* the name of the class. We want to avoid fetching it since
* it doesn't exist yet.
*
*/
static int
get_att_default_from_def (PARSER_CONTEXT * parser, PT_NODE * attribute, DB_VALUE ** default_value,
const char *classname)
{
int error = NO_ERROR;
PT_NODE *def_val = NULL, *initial_def_val = NULL;
DB_DEFAULT_EXPR_TYPE def_expr_type;
PT_TYPE_ENUM desired_type = attribute->type_enum;
bool has_self_ref = false;
const char *data_type_print;
assert (attribute->node_type == PT_ATTR_DEF);
if (attribute->info.attr_def.data_default == NULL)
{
*default_value = NULL;
return NO_ERROR;
}
def_expr_type = attribute->info.attr_def.data_default->info.data_default.default_expr_type;
def_val = attribute->info.attr_def.data_default->info.data_default.default_value;
def_val = pt_semantic_check (parser, def_val);
if (pt_has_error (parser) || def_val == NULL)
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
if (classname != NULL && attribute->data_type != NULL)
{
PT_NODE *dt = NULL;
for (dt = attribute->data_type; dt != NULL; dt = dt->next)
{
if (dt->info.data_type.entity != NULL && dt->info.data_type.entity->node_type == PT_NAME
&& pt_user_specified_name_compare (dt->info.data_type.entity->info.name.original, classname) == 0)
{
has_self_ref = true;
break;
}
}
}
initial_def_val = parser_copy_tree (parser, def_val);
if (initial_def_val == NULL)
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
if (has_self_ref)
{
/* We are creating a new class, and expected domain of default value has a self reference. Class cannot be
* resolved yet, since it doesn't exist. It is only reserved and it has a temporary OID. Thus, we need to handle
* it here and avoid fetching the object (which will hit assert due to temporary OID). We can only accept a NULL
* default value, or if the expected type is a collection (that contains self references too), we can only accept
* an empty set. */
DB_VALUE *value;
if (desired_type != PT_TYPE_OBJECT && !PT_IS_COLLECTION_TYPE (desired_type))
{
/* Should we even be here? */
PT_INTERNAL_ERROR (parser, "Self referencing attribute unexpected type.");
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
/* Desired type either PT_TYPE_OBJECT or collection type. */
/* We allow default value if: 1. Not a default expression. 2. Value is NULL or value is empty set and collection
* type is expected. */
value = &def_val->info.value.db_value;
if (def_expr_type == DB_DEFAULT_NONE
&& (db_value_is_null (value)
|| (desired_type != PT_TYPE_OBJECT && TP_IS_SET_TYPE (value->domain.general_info.type)
&& value->data.set->set->size == 0)))
{
/* We can accept the default value. */
pt_evaluate_tree (parser, def_val, *default_value, 1);
}
else
{
/* Cannot coerce. */
if (desired_type == PT_TYPE_OBJECT)
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, initial_def_val), classname);
}
else
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, initial_def_val), pt_show_type_enum (desired_type));
}
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
}
else
{
/* try to coerce the default value into the attribute type */
if (def_expr_type == DB_DEFAULT_NONE)
{
error = pt_coerce_value_for_default_value (parser, def_val, def_val, desired_type, attribute->data_type,
def_expr_type, true);
if (error != NO_ERROR)
{
goto exit_on_coerce_error;
}
}
else
{
DB_VALUE src;
PT_NODE *temp_val;
db_make_null (&src);
def_val = pt_semantic_type (parser, def_val, NULL);
if (pt_has_error (parser) || def_val == NULL)
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
pt_evaluate_tree_having_serial (parser, def_val, &src, 1);
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
temp_val = pt_dbval_to_value (parser, &src);
if (temp_val == NULL)
{
db_value_clear (&src);
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
error = pt_coerce_value_for_default_value (parser, temp_val, temp_val, desired_type, attribute->data_type,
def_expr_type, true);
db_value_clear (&src);
temp_val->info.value.db_value_is_in_workspace = 0;
parser_free_node (parser, temp_val);
if (error != NO_ERROR)
{
goto exit_on_coerce_error;
}
}
if (def_expr_type == DB_DEFAULT_NONE)
{
pt_evaluate_tree (parser, def_val, *default_value, 1);
}
else
{
*default_value = NULL;
}
if (pt_has_error (parser))
{
pt_report_to_ersys (parser, PT_SEMANTIC);
error = er_errid ();
goto exit;
}
}
exit:
if (initial_def_val != NULL)
{
parser_free_tree (parser, initial_def_val);
}
return error;
exit_on_coerce_error:
if (pt_has_error (parser))
{
/* forget previous one to set the better error */
pt_reset_error (parser);
}
if (attribute->data_type != NULL)
{
data_type_print = pt_short_print (parser, attribute->data_type);
}
else
{
data_type_print = pt_show_type_enum ((PT_TYPE_ENUM) desired_type);
}
if (error == ER_IT_DATA_OVERFLOW)
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OVERFLOW_COERCING_TO,
pt_short_print (parser, initial_def_val), data_type_print);
}
else
{
PT_ERRORmf2 (parser, def_val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
pt_short_print (parser, initial_def_val), data_type_print);
}
if (initial_def_val != NULL)
{
parser_free_tree (parser, initial_def_val);
}
return error;
}
/*
* get_hard_default_for_type() - Get a hard-coded default value for the given
* type, or NULL if there is no such value.
*
* Note: the default is returned as a string, to be used in building queries.
*
* return : pointer to a static char array or NULL
* type(in): the type, as stored in the parse tree
*
*/
static const char *
get_hard_default_for_type (PT_TYPE_ENUM type)
{
static const char *zero = "0";
static const char *empty_str = "''";
static const char *empty_bit = "b'0'";
static const char *empty_date = "DATE '01/01/0001'";
static const char *empty_time = "TIME '00:00'";
static const char *empty_datetime = "DATETIME '01/01/0001 00:00'";
static const char *empty_dt_tz = "DATETIMETZ '01/01/0001 00:00 +00:00'";
static const char *empty_dt_ltz = "DATETIMELTZ '01/01/0001 00:00 +00:00'";
static const char *empty_json = "null";
/* TODO : use db_value_domain_default instead, but make sure that db_value_domain_default is not using NULL DB_VALUE
* as default for any type */
/* Timestamp is interpreted as local and converted internally to UTC, so hard default value of Timestamp set to '1'
* (Unix epoch time + 1). (0 means zero date) */
static const char *empty_timestamp = "1";
static const char *empty_set = "{}";
switch (type)
{
case PT_TYPE_INTEGER:
case PT_TYPE_SMALLINT:
case PT_TYPE_MONETARY:
case PT_TYPE_NUMERIC:
case PT_TYPE_BIGINT:
case PT_TYPE_FLOAT:
case PT_TYPE_DOUBLE:
return zero;
case PT_TYPE_TIMESTAMP:
case PT_TYPE_TIMESTAMPLTZ:
case PT_TYPE_TIMESTAMPTZ:
return empty_timestamp;
case PT_TYPE_DATE:
return empty_date;
case PT_TYPE_TIME:
return empty_time;
case PT_TYPE_DATETIME:
return empty_datetime;
case PT_TYPE_DATETIMELTZ:
return empty_dt_ltz;
case PT_TYPE_DATETIMETZ:
return empty_dt_tz;
case PT_TYPE_CHAR:
case PT_TYPE_VARCHAR:
return empty_str;
case PT_TYPE_SET:
case PT_TYPE_MULTISET:
case PT_TYPE_SEQUENCE:
return empty_set;
case PT_TYPE_BIT:
case PT_TYPE_VARBIT:
return empty_bit;
case PT_TYPE_LOGICAL:
case PT_TYPE_NONE:
case PT_TYPE_MAYBE:
case PT_TYPE_NA:
case PT_TYPE_NULL:
case PT_TYPE_STAR:
case PT_TYPE_OBJECT:
case PT_TYPE_MIDXKEY:
case PT_TYPE_COMPOUND:
case PT_TYPE_RESULTSET:
case PT_TYPE_BLOB:
case PT_TYPE_CLOB:
case PT_TYPE_ELO:
return NULL;
case PT_TYPE_JSON:
return empty_json;
default:
return NULL;
}
}
/*
* do_run_update_query_for_new_notnull_fields() - worker function for
* do_update_new_notnull_cols_without_default().
* It creates a complex UPDATE query and runs it.
*/
static int
do_run_update_query_for_new_notnull_fields (PARSER_CONTEXT * parser, PT_NODE * alter, PT_NODE * attr_list,
int attr_count, MOP class_mop)
{
char *query, *q;
int query_len, remaining, n;
PT_NODE *attr;
bool first = true;
int error = NO_ERROR;
int row_count = 0;
assert (parser && alter && attr_list);
assert (attr_count > 0);
/* Allocate enough for each attribute's name, its default value, and for the "UPDATE table_name" part of the query.
* 42 is more than the maximum length of any default value for an attribute, including three spaces, the coma sign
* and an equal. And size of 28 is added for NO_SUPPLEMENTAL_LOG hint. */
query_len = remaining = (attr_count + 1) * (DB_MAX_IDENTIFIER_LENGTH + 42 + 28);
if (query_len > QUERY_MAX_SIZE)
{
ERROR1 (error, ER_UNEXPECTED, "Too many attributes.");
return error;
}
q = query = (char *) malloc (query_len + 1);
if (query == NULL)
{
ERROR1 (error, ER_OUT_OF_VIRTUAL_MEMORY, (size_t) (query_len + 1));
return error;
}
query[0] = 0;
/* Using UPDATE ALL to update the current class and all its children. */
n =
snprintf (q, remaining, "UPDATE /*+ NO_SUPPLEMENTAL_LOG */ ALL [%s] SET ",
alter->info.alter.entity_name->info.name.original);
if (n < 0)
{
ERROR1 (error, ER_UNEXPECTED, "Building UPDATE statement failed.");
goto end;
}
remaining -= n;
q += n;
for (attr = attr_list; attr != NULL; attr = attr->next)
{
const char *sep = first ? "" : ", ";
const char *hard_default = get_hard_default_for_type (attr->type_enum);
n = snprintf (q, remaining, "%s[%s] = %s", sep, attr->info.attr_def.attr_name->info.name.original, hard_default);
if (n < 0)
{
ERROR1 (error, ER_UNEXPECTED, "Building UPDATE statement failed.");
goto end;
}
remaining -= n;
q += n;
first = false;
}
/* Now just RUN thew query */
error = do_run_update_query_for_class (query, class_mop, &row_count);
end:
if (query)
{
free_and_init (query);
}
return error;
}
/*
* do_run_update_query_for_new_default_expression_fields() - worker function for
* do_update_new_cols_with_default_expression().
*
* parser(in): parser
* alter(in): alter node
* attr_list(in) attribute nodes list
* attr_count(int): count attribute nodes
* class_mop(in): class mop
*
* Note : It creates a complex UPDATE query and runs it.
*/
static int
do_run_update_query_for_new_default_expression_fields (PARSER_CONTEXT * parser, PT_NODE * alter, PT_NODE * attr_list,
int attr_count, MOP class_mop)
{
char *query, *q;
int query_len, remaining, n;
PT_NODE *attr;
bool first = true;
int error = NO_ERROR;
int row_count = 0;
assert (parser && alter && attr_list);
assert (attr_count > 0);
/* Allocate enough for each attribute's name, its default value, and for the "UPDATE table_name" part of the query.
* 100 is more than the maximum length of any default value for an attribute, including three spaces, the comma sign
* and an equal. */
query_len = remaining = (attr_count + 1) * (DB_MAX_IDENTIFIER_LENGTH + 100);
if (query_len > QUERY_MAX_SIZE)
{
ERROR1 (error, ER_UNEXPECTED, "Too many attributes.");
return error;
}
q = query = (char *) malloc (query_len + 1);
if (query == NULL)
{
ERROR1 (error, ER_OUT_OF_VIRTUAL_MEMORY, (size_t) (query_len + 1));
return error;
}
query[0] = 0;
/* Using UPDATE ALL to update the current class and all its children. */
n =
snprintf (q, remaining, "UPDATE /*+ NO_SUPPLEMENTAL_LOG */ ALL [%s] SET ",
alter->info.alter.entity_name->info.name.original);
if (n < 0)
{
ERROR1 (error, ER_UNEXPECTED, "Building UPDATE statement failed.");
goto end;
}
remaining -= n;
q += n;
for (attr = attr_list; attr != NULL; attr = attr->next)
{
const char *sep = first ? "" : ", ";
char *data_default;
data_default = parser_print_tree (parser, attr->info.attr_def.data_default->info.data_default.default_value);
if (data_default == NULL)
{
continue;
}
n = snprintf (q, remaining, "%s[%s] = %s", sep, attr->info.attr_def.attr_name->info.name.original, data_default);
if (n < 0)
{
ERROR1 (error, ER_UNEXPECTED, "Building UPDATE statement failed.");
goto end;
}
remaining -= n;
q += n;
first = false;
}
/* Now just RUN the query */
error = do_run_update_query_for_class (query, class_mop, &row_count);
end:
if (query)
{
free_and_init (query);
}
return error;
}
/*
* is_attribute_primary_key() - Returns true if the attribute given is part
* of the primary key of the table.
*
*
* return : true or false
* class_name(in): the class name
* attr_name(in): the attribute name
*
*/
static bool
is_attribute_primary_key (const char *class_name, const char *attr_name)
{
DB_ATTRIBUTE *db_att = NULL;
if (class_name == NULL || attr_name == NULL)
{
return false;
}
db_att = db_get_attribute_by_name (class_name, attr_name);
if (db_att && db_attribute_is_primary_key (db_att))
{
return true;
}
return false;
}
/*
* do_update_new_notnull_cols_without_default()
* Populates the newly added columns with hard-coded defaults.
*
* Used only on ALTER TABLE ... ADD COLUMN, and only AFTER the operation has
* been performed (i.e. the columns have been added to the schema, even
* though the transaction has not been committed).
*
* IF the clause has added columns that:
* 1. have no default value AND
* 2a. have the NOT NULL constraint OR
* 2b. are part of the PRIMARY KEY
* THEN try to fill them with a hard-coded default (zero, empty string etc.)
*
* This is done in MySQL compatibility mode, to ensure consistency: otherwise
* columns with the NOT NULL constraint would have ended up being filled
* with NULL as a default.
*
* NOTE: there are types (such as OBJECT) that do not have a "zero"-like
* value, and if we encounter one of these, we block the entire operation.
*
* return: Error code if operation fails or if one of the attributes to add
* is of type OBJECT, with NOT NULL and no default value.
* parser(in): Parser context
* alter(in): Parse tree of the statement
*/
static int
do_update_new_notnull_cols_without_default (PARSER_CONTEXT * parser, PT_NODE * alter, MOP class_mop)
{
PT_NODE *relevant_attrs = NULL;
int error = NO_ERROR;
int attr_count = 0;
PT_NODE *attr = NULL;
PT_NODE *save = NULL;
PT_NODE *copy = NULL;
assert (alter->node_type == PT_ALTER);
assert (alter->info.alter.code == PT_ADD_ATTR_MTHD);
/* Look for attributes that: have NOT NULL, do not have a DEFAULT and their type has a "hard" default. Also look for
* attributes that are primary keys Throw an error for types that do not have a hard default (like objects). */
for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
{
const bool is_not_null = (attr->info.attr_def.constrain_not_null != 0);
const bool has_default = (attr->info.attr_def.data_default != NULL);
const bool is_pri_key = is_attribute_primary_key (alter->info.alter.entity_name->info.name.original,
attr->info.attr_def.attr_name->info.name.original);
if (has_default)
{
continue;
}
if (!is_not_null && !is_pri_key)
{
continue;
}
if (!db_class_has_instance (class_mop))
{
continue;
}
if (!prm_get_bool_value (PRM_ID_ADD_COLUMN_UPDATE_HARD_DEFAULT))
{
ERROR1 (error, ER_SM_ATTR_NOT_NULL, attr->info.attr_def.attr_name->info.name.original);
goto end;
}
if (get_hard_default_for_type (attr->type_enum) == NULL)
{
ERROR1 (error, ER_NOTNULL_ON_TYPE_WITHOUT_DEFAULT_VALUE, pt_show_type_enum (attr->type_enum));
goto end;
}
/* now we have an interesting node. Copy it in our list. */
attr_count++;
save = attr->next;
attr->next = NULL;
copy = parser_copy_tree (parser, attr);
if (copy == NULL)
{
attr->next = save;
ERROR0 (error, ER_OUT_OF_VIRTUAL_MEMORY);
parser_free_tree (parser, relevant_attrs);
goto end;
}
relevant_attrs = parser_append_node (copy, relevant_attrs);
attr->next = save;
}
if (relevant_attrs == NULL)
{
/* no interesting attribute found, just leave */
goto end;
}
/* RUN an UPDATE query comprising all the attributes */
error = do_run_update_query_for_new_notnull_fields (parser, alter, relevant_attrs, attr_count, class_mop);
if (error != NO_ERROR)
{
goto end;
}
end:
if (relevant_attrs != NULL)
{
parser_free_tree (parser, relevant_attrs);
}
return error;
}
/*
* do_update_new_cols_with_default_expression() : Populates the newly added columns with default expression
*
* return: error code
*
* parser(in): Parser context
* alter(in): Parse tree of the statement
*
* Note: Used only on ALTER TABLE ... ADD COLUMN, and only AFTER the operation has been performed (i.e. the columns
* have been added to the schema, even though the transaction has not been committed).
*
*/
static int
do_update_new_cols_with_default_expression (PARSER_CONTEXT * parser, PT_NODE * alter, MOP class_mop)
{
PT_NODE *relevant_attrs = NULL;
int error = NO_ERROR;
int attr_count = 0;
PT_NODE *pt_data_default = NULL;
PT_NODE *attr = NULL;
PT_NODE *save = NULL;
PT_NODE *copy = NULL;
DB_DEFAULT_EXPR default_expr;
assert (alter->node_type == PT_ALTER);
assert (alter->info.alter.code == PT_ADD_ATTR_MTHD);
/* Look for attributes that have a DEFAULT expression. */
for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
{
pt_data_default = attr->info.attr_def.data_default;
if (pt_data_default == NULL)
{
/* don't have default clause */
continue;
}
pt_get_default_expression_from_data_default_node (parser, pt_data_default, &default_expr);
if (default_expr.default_expr_type == DB_DEFAULT_NONE)
{
/* don't have default expression */
continue;
}
if (!db_is_class (class_mop) || !db_class_has_instance (class_mop))
{
continue;
}
/* now we have an interesting node. Copy it in our list. */
attr_count++;
save = attr->next;
attr->next = NULL;
copy = parser_copy_tree (parser, attr);
if (copy == NULL)
{
attr->next = save;
ERROR0 (error, ER_OUT_OF_VIRTUAL_MEMORY);
parser_free_tree (parser, relevant_attrs);
goto end;
}
relevant_attrs = parser_append_node (copy, relevant_attrs);
attr->next = save;
}
if (relevant_attrs == NULL)
{
/* no interesting attribute found, just leave */
goto end;
}
/* RUN an UPDATE query comprising all the attributes */
error = do_run_update_query_for_new_default_expression_fields (parser, alter, relevant_attrs, attr_count, class_mop);
if (error != NO_ERROR)
{
goto end;
}
end:
if (relevant_attrs != NULL)
{
parser_free_tree (parser, relevant_attrs);
}
return error;
}
/*
* do_run_upgrade_instances_domain() - proxy function for server function
* 'xlocator_upgrade_instances_domain'
*
* parser(in):
* p_class_oid(in): class OID
* att_id(in): constraint list
*/
static int
do_run_upgrade_instances_domain (PARSER_CONTEXT * parser, OID * p_class_oid, int att_id)
{
int error = NO_ERROR;
assert (parser != NULL);
assert (p_class_oid != NULL);
assert (att_id >= 0);
error = locator_upgrade_instances_domain (p_class_oid, att_id);
return error;
}
/*
* do_drop_att_constraints() - drops constraints in list associated with a
* class
* class_mop(in): class object
* constr_info_list(in): constraint list
*
* Note: Warning : Only non-unique, unique, and primary constraints are
* handled; FOREIGN KEY constraints are not supported
*/
static int
do_drop_att_constraints (MOP class_mop, SM_CONSTRAINT_INFO * constr_info_list)
{
int error = NO_ERROR;
SM_CONSTRAINT_INFO *constr;
for (constr = constr_info_list; constr != NULL; constr = constr->next)
{
if (SM_IS_CONSTRAINT_UNIQUE_FAMILY ((SM_CONSTRAINT_TYPE) constr->constraint_type))
{
error =
sm_drop_constraint (class_mop, constr->constraint_type, constr->name, (const char **) constr->att_names, 0,
false);
if (error != NO_ERROR)
{
goto error_exit;
}
}
else if (constr->constraint_type == DB_CONSTRAINT_INDEX || constr->constraint_type == DB_CONSTRAINT_REVERSE_INDEX)
{
error = sm_drop_index (class_mop, constr->name);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
error_exit:
return error;
}
/*
* do_recreate_att_constraints() - (re-)creates constraints in list associated
* with a class
* class_mop(in): class object
* constr_info_list(in): constraint list
*
* Note: Warning : Only non-unique, unique, and primary constraints are
* handled; FOREIGN KEY constraints are not supported
*/
static int
do_recreate_att_constraints (MOP class_mop, SM_CONSTRAINT_INFO * constr_info_list)
{
int error = NO_ERROR;
SM_CONSTRAINT_INFO *constr;
for (constr = constr_info_list; constr != NULL; constr = constr->next)
{
if (SM_IS_CONSTRAINT_INDEX_FAMILY ((SM_CONSTRAINT_TYPE) constr->constraint_type))
{
error =
sm_add_constraint (class_mop, constr->constraint_type, constr->name, (const char **) constr->att_names,
constr->asc_desc, constr->prefix_length, false, constr->filter_predicate,
constr->func_index_info, constr->comment, constr->index_status);
if (error != NO_ERROR)
{
goto error_exit;
}
}
}
error_exit:
return error;
}
/*
* check_change_attribute() - Checks if an attribute change attribute is
* possible, in the context of the requested
* change mode
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* attribute(in/out): Attribute to add
*/
static int
check_change_attribute (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_NODE * attribute, PT_NODE * old_name_node,
PT_NODE ** pointer_constraints, SM_ATTR_PROP_CHG * attr_chg_prop, SM_ATTR_CHG_SOL * change_mode)
{
SM_NAME_SPACE name_space = ID_NULL;
int meta = 0, shared = 0;
int error = NO_ERROR;
const char *old_name = NULL;
const char *attr_name = NULL;
bool new_attempt = true;
DB_VALUE def_value;
DB_VALUE *ptr_def = &def_value;
PT_NODE *cnstr;
PT_NODE *not_null_node = NULL, *pk_node = NULL, *previous_node = NULL, *tmp_node = NULL;
PT_NODE *constraints = *pointer_constraints;
assert (attr_chg_prop != NULL);
assert (change_mode != NULL);
assert (attribute->node_type == PT_ATTR_DEF);
*change_mode = SM_ATTR_CHG_ONLY_SCHEMA;
db_make_null (&def_value);
attr_name = get_attr_name (attribute);
meta = (attribute->info.attr_def.attr_type == PT_META_ATTR);
shared = (attribute->info.attr_def.attr_type == PT_SHARED);
name_space = (meta) ? ID_CLASS_ATTRIBUTE : ((shared) ? ID_SHARED_ATTRIBUTE : ID_ATTRIBUTE);
attr_chg_prop->name_space = name_space;
/* check if class has subclasses : 'users' of class may be subclass, but also partitions of class */
if (ctemplate->current->users != NULL && ctemplate->partition == NULL)
{
attr_chg_prop->class_has_subclass = true;
}
error = check_default_on_update_clause (parser, attribute);
if (error != NO_ERROR)
{
goto exit;
}
error = get_att_default_from_def (parser, attribute, &ptr_def, NULL);
if (error != NO_ERROR)
{
goto exit;
}
/* ptr_def is either NULL or pointing to address of def_value */
assert (ptr_def == NULL || ptr_def == &def_value);
if (ptr_def && DB_IS_NULL (ptr_def)
&& attribute->info.attr_def.data_default->info.data_default.default_expr_type == DB_DEFAULT_NONE)
{
for (cnstr = constraints; cnstr != NULL; cnstr = cnstr->next)
{
if (cnstr->info.constraint.type == PT_CONSTRAIN_NOT_NULL)
{
/* don't allow a default value of NULL for NOT NULL constrained columns */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_HAVE_NOTNULL_DEFAULT_NULL, 1, attr_name);
error = ER_CANNOT_HAVE_NOTNULL_DEFAULT_NULL;
goto exit;
}
else if (cnstr->info.constraint.type == PT_CONSTRAIN_PRIMARY_KEY)
{
/* don't allow a default value of NULL in new PK constraint */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_HAVE_PK_DEFAULT_NULL, 1, attr_name);
error = ER_CANNOT_HAVE_PK_DEFAULT_NULL;
goto exit;
}
}
}
/* Try to find out the combined constraints definition of NOT_NULL and PK */
for (cnstr = constraints; cnstr != NULL; tmp_node = cnstr, cnstr = cnstr->next)
{
if (cnstr->info.constraint.type == PT_CONSTRAIN_NOT_NULL)
{
not_null_node = cnstr;
previous_node = tmp_node;
}
else if (cnstr->info.constraint.type == PT_CONSTRAIN_PRIMARY_KEY)
{
pk_node = cnstr;
}
if (not_null_node != NULL && pk_node != NULL)
{
break;
}
}
/* Exclude/remove the duplicated NOT_NULL constraint which would be implicitly defined by PK. */
if (not_null_node != NULL && pk_node != NULL)
{
if (previous_node == NULL)
{
/* At the head of the list */
constraints = not_null_node->next;
*pointer_constraints = constraints;
}
else
{
previous_node->next = not_null_node->next;
}
not_null_node->next = NULL;
parser_free_node (parser, not_null_node);
}
error = build_attr_change_map (parser, ctemplate, attribute, old_name_node, constraints, attr_chg_prop);
if (error != NO_ERROR)
{
goto exit;
}
if (!is_att_property_structure_checked (attr_chg_prop))
{
assert (false);
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Not all properties are checked.");
goto exit;
}
/* get new name */
if (old_name_node != NULL)
{
assert (old_name_node->node_type == PT_NAME);
old_name = old_name_node->info.name.original;
assert (old_name != NULL);
/* attr_name is supplied using the ATTR_DEF node and it means: for MODIFY syntax : current and unchanged name
* (attr_name) for CHANGE syntax : new name of the attribute (new_name) */
if (is_att_prop_set (attr_chg_prop->p[P_NAME], ATT_CHG_PROPERTY_DIFF))
{
attr_name = old_name;
}
else
{
attr_name = old_name;
}
}
if (!is_att_change_needed (attr_chg_prop))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_ALTER_CHANGE_WARN_NO_CHANGE, 1, attr_name);
error = NO_ERROR;
/* just a warning : nothing to do */
*change_mode = SM_ATTR_CHG_NOT_NEEDED;
goto exit;
}
/* check if domain type is indexable : for constraints that may be acquired with ALTER.. CHANGE, check both if the
* constraint is present in either old or new schema; if constraint cannot be acquired with CHANGE, check only if it
* is present with old schema */
/* TODO : this should be done at semantic check for all attribute definition nodes (including at table creation) */
if (is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_NEW)
|| is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_PK], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_NEW)
|| is_att_prop_set (attr_chg_prop->p[P_S_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_M_CONSTR_UNI], ATT_CHG_PROPERTY_PRESENT_OLD)
|| is_att_prop_set (attr_chg_prop->p[P_CONSTR_NON_UNI], ATT_CHG_PROPERTY_PRESENT_OLD))
{
if (!tp_valid_indextype (pt_type_enum_to_db (attribute->type_enum)))
{
error = ER_SM_INVALID_INDEX_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, pt_type_enum_to_db_domain_name (attribute->type_enum));
goto exit;
}
}
/* check if attribute change is allowed : */
error = check_att_chg_allowed (attr_name, attribute->type_enum, attr_chg_prop, *change_mode, false, &new_attempt);
if (error != NO_ERROR && new_attempt)
{
*change_mode = SM_ATTR_CHG_WITH_ROW_UPDATE;
error = check_att_chg_allowed (attr_name, attribute->type_enum, attr_chg_prop, *change_mode, false, &new_attempt);
if (error != NO_ERROR && new_attempt)
{
*change_mode = SM_ATTR_CHG_BEST_EFFORT;
error =
check_att_chg_allowed (attr_name, attribute->type_enum, attr_chg_prop, *change_mode, true, &new_attempt);
if (error != NO_ERROR)
{
goto exit;
}
}
}
exit:
db_value_clear (&def_value);
return error;
}
/*
* check_change_class_collation() - Checks if it is necessary to update the
* default collation of a class
* return: Error code
* parser(in): Parser context
* ctemplate(in/out): Class template
* alter(in): Node containing desired collation and codeset
*/
static int
check_change_class_collation (PARSER_CONTEXT * parser, DB_CTMPL * ctemplate, PT_ALTER_INFO * alter, bool * need_update,
int *collation_id)
{
int error = NO_ERROR;
int cs = -1, coll_id = -1;
assert (ctemplate->current != NULL);
*need_update = false;
cs = alter->alter_clause.collation.charset;
coll_id = alter->alter_clause.collation.collation_id;
/* check if codeset, collation or both are set, and check if it is necessary to update the default collation of the
* class */
if (coll_id != -1)
{
if (ctemplate->current->collation_id != coll_id)
{
*need_update = true;
}
else
{
*need_update = false;
}
*collation_id = coll_id;
}
else
{
LANG_COLLATION *lc;
assert (cs != -1);
lc = lang_get_collation (ctemplate->current->collation_id);
assert (lc != NULL);
if ((lc->codeset != cs) || (LANG_GET_BINARY_COLLATION (cs) != ctemplate->current->collation_id))
{
*need_update = true;
*collation_id = LANG_GET_BINARY_COLLATION (cs);
}
}
return error;
}
/*
* sort_constr_info_list - sorts the list of constraints in the order:
* - non-unique indexes
* - unique indexes
* - primary keys
* - foreign key constraints
* return: none
* source(in/out): list to sort
*/
static int
sort_constr_info_list (SM_CONSTRAINT_INFO ** orig_list)
{
int error = NO_ERROR;
SM_CONSTRAINT_INFO *sorted, *next, *prev, *ins, *found, *constr;
int constr_order[7] = { 0 };
assert (orig_list != NULL);
if (*orig_list == NULL)
{
return error;
}
/* TODO change this to compile-time asserts when we have such a mechanism. */
assert (DB_CONSTRAINT_UNIQUE == 0);
assert (DB_CONSTRAINT_FOREIGN_KEY == 6);
constr_order[DB_CONSTRAINT_UNIQUE] = 2;
constr_order[DB_CONSTRAINT_INDEX] = 0;
constr_order[DB_CONSTRAINT_NOT_NULL] = 6;
constr_order[DB_CONSTRAINT_REVERSE_UNIQUE] = 2;
constr_order[DB_CONSTRAINT_REVERSE_INDEX] = 0;
constr_order[DB_CONSTRAINT_PRIMARY_KEY] = 4;
constr_order[DB_CONSTRAINT_FOREIGN_KEY] = 5;
sorted = NULL;
for (constr = *orig_list, next = NULL; constr != NULL; constr = next)
{
next = constr->next;
for (ins = sorted, prev = NULL, found = NULL; ins != NULL && found == NULL; ins = ins->next)
{
if (constr->constraint_type < 0 || constr->constraint_type > DB_CONSTRAINT_FOREIGN_KEY
|| ins->constraint_type < 0 || ins->constraint_type > DB_CONSTRAINT_FOREIGN_KEY)
{
assert (false);
return ER_UNEXPECTED;
}
if (constr_order[constr->constraint_type] < constr_order[ins->constraint_type])
{
found = ins;
}
else
{
prev = ins;
}
}
constr->next = found;
if (prev == NULL)
{
sorted = constr;
}
else
{
prev->next = constr;
}
}
*orig_list = sorted;
return error;
}
/*
* save_constraint_info_from_pt_node() - Saves the information necessary to
* create a constraint from a PT_CONSTRAINT_INFO node
*
* return: NO_ERROR on success, non-zero for ERROR
* save_info(in/out): The information saved
* pt_constr(in): The constraint node to be saved
*
* Note :this function handles only constraints for single
* attributes : PT_CONSTRAIN_NOT_NULL, PT_CONSTRAIN_UNIQUE,
* PT_CONSTRAIN_PRIMARY_KEY.
* Foreign keys, indexes on multiple columns are not supported and also
* 'prefix_length' and ASC/DESC info is not supported.
* It process only one node; the 'next' PT_NODE is ignored.
*/
static int
save_constraint_info_from_pt_node (SM_CONSTRAINT_INFO ** save_info, const PT_NODE * const pt_constr)
{
int error_code = NO_ERROR;
SM_CONSTRAINT_INFO *new_constraint = NULL;
PT_NODE *constr_att_name = NULL;
assert (pt_constr->node_type == PT_CONSTRAINT);
new_constraint = (SM_CONSTRAINT_INFO *) calloc (1, sizeof (SM_CONSTRAINT_INFO));
if (new_constraint == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, sizeof (SM_CONSTRAINT_INFO));
goto error_exit;
}
/* set NULL, expect to generate constraint name */
new_constraint->name = NULL;
switch (pt_constr->info.constraint.type)
{
case PT_CONSTRAIN_PRIMARY_KEY:
constr_att_name = pt_constr->info.constraint.un.primary_key.attrs;
new_constraint->constraint_type = DB_CONSTRAINT_PRIMARY_KEY;
break;
case PT_CONSTRAIN_UNIQUE:
constr_att_name = pt_constr->info.constraint.un.unique.attrs;
new_constraint->constraint_type = DB_CONSTRAINT_UNIQUE;
break;
case PT_CONSTRAIN_NOT_NULL:
constr_att_name = pt_constr->info.constraint.un.not_null.attr;
new_constraint->constraint_type = DB_CONSTRAINT_NOT_NULL;
break;
default:
assert (false);
}
if (constr_att_name->next != NULL)
{
error_code = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, "Constraint name should be the last attribute.");
goto error_exit;
}
new_constraint->att_names = (char **) calloc (1 + 1, sizeof (char *));
if (new_constraint->att_names == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, (1 + 1) * sizeof (char *));
goto error_exit;
}
assert (constr_att_name->info.name.original != NULL);
new_constraint->att_names[0] = strdup (constr_att_name->info.name.original);
if (new_constraint->att_names[0] == NULL)
{
error_code = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1,
(size_t) (strlen (constr_att_name->info.name.original) + 1));
goto error_exit;
}
new_constraint->att_names[1] = NULL;
assert (new_constraint->next == NULL);
while ((*save_info) != NULL)
{
save_info = &((*save_info)->next);
}
*save_info = new_constraint;
return error_code;
error_exit:
if (new_constraint != NULL)
{
sm_free_constraint_info (&new_constraint);
}
return error_code;
}
/*
* do_check_rows_for_null() - checks if a column has NULL values
* return: NO_ERROR or error code
*
* class_mop(in): class to check
* att_name(in): name of column to check
* has_nulls(out): true if column has rows with NULL
*
*/
int
do_check_rows_for_null (MOP class_mop, const char *att_name, bool * has_nulls)
{
int error = NO_ERROR;
int n = 0;
int stmt_id = 0;
DB_SESSION *session = NULL;
DB_QUERY_RESULT *result = NULL;
const char *class_name = NULL;
char query[2 * SM_MAX_IDENTIFIER_LENGTH + 50] = { 0 };
DB_VALUE count;
assert (class_mop != NULL);
assert (att_name != NULL);
assert (has_nulls != NULL);
*has_nulls = false;
db_make_null (&count);
class_name = db_get_class_name (class_mop);
if (class_name == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Cannot get class name of mop.");
goto end;
}
n =
snprintf (query, sizeof (query) / sizeof (char), "SELECT count(*) FROM [%s] WHERE [%s] IS NULL", class_name,
att_name);
if (n < 0 || (n == sizeof (query) / sizeof (char)))
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Building SELECT statement failed.");
goto end;
}
/* RUN the query */
session = db_open_buffer (query);
if (session == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
if (db_get_errors (session) || db_statement_count (session) != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
stmt_id = db_compile_statement (session);
if (stmt_id != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
error = db_execute_statement_local (session, stmt_id, &result);
if (error < 0)
{
goto end;
}
if (result == NULL)
{
error = ER_UNEXPECTED;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, "Executing internal query failed.");
goto end;
}
error = db_query_first_tuple (result);
if (error != NO_ERROR)
{
goto end;
}
assert (result->query_type->db_type == DB_TYPE_BIGINT);
error = db_query_set_copy_tplvalue (result, 0 /* peek */ );
if (error != NO_ERROR)
{
goto end;
}
error = db_query_get_tuple_value (result, 0, &count);
if (error != NO_ERROR)
{
goto end;
}
assert (!DB_IS_NULL (&count));
assert (DB_VALUE_DOMAIN_TYPE (&count) == DB_TYPE_BIGINT);
if (db_get_bigint (&count) > 0)
{
*has_nulls = true;
}
end:
if (result != NULL)
{
db_query_end (result);
}
if (session != NULL)
{
db_close_session (session);
}
db_value_clear (&count);
return error;
}
/*
* do_run_update_query_for_class() - runs an UPDATE query.
* return: NO_ERROR or error code
*
* query(in): query statement
* class_mop(in): class to check
* suppress_replication(in): force suppress replication
* row_count(out): count of updated rows
*
*/
static int
do_run_update_query_for_class (char *query, MOP class_mop, int *row_count)
{
int error = NO_ERROR;
DB_SESSION *session = NULL;
int stmt_id = 0;
bool save_tr_state = tr_get_execution_state ();
bool check_tr_state = false;
assert (query != NULL);
assert (class_mop != NULL);
assert (row_count != NULL);
*row_count = -1;
lang_set_parser_use_client_charset (false);
session = db_open_buffer (query);
if (session == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
if (db_get_errors (session) || db_statement_count (session) != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
stmt_id = db_compile_statement (session);
if (stmt_id != 1)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto end;
}
/*
* We are going to perform an UPDATE on the table. We need to disable
* the triggers because these are not UPDATES that the user required
* explicitly.
*/
check_tr_state = tr_set_execution_state (false);
assert (check_tr_state == save_tr_state);
error = db_execute_statement_local (session, stmt_id, NULL);
if (error < 0)
{
goto end;
}
error = NO_ERROR;
/* Invalidate the XASL cache by using the touch function */
assert (class_mop);
error = sm_touch_class (class_mop);
if (error != NO_ERROR)
{
goto end;
}
*row_count = db_get_parser (session)->execution_values.row_count;
end:
lang_set_parser_use_client_charset (true);
if (session != NULL)
{
db_free_query (session);
db_close_session (session);
}
tr_set_execution_state (save_tr_state);
return error;
}
/*
* pt_node_to_function_index () - extracts function index information
* based on the given expression
* parser(in): parser context
* spec(in): class spec
* expr(in): expression used with function index
* return: pointer to SM_FUNCTION_INFO structure, containing the
* function index information
*/
static SM_FUNCTION_INFO *
pt_node_to_function_index (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * node, DO_INDEX do_index)
{
SM_FUNCTION_INFO *func_index_info;
FUNC_PRED *func_pred;
PT_NODE *expr = NULL;
char *expr_str = NULL;
TP_DOMAIN *d = NULL;
unsigned int save_custom;
int error = NO_ERROR;
if (node->node_type == PT_SORT_SPEC)
{
expr = node->info.sort_spec.expr;
}
else
{
expr = node;
}
func_index_info = (SM_FUNCTION_INFO *) db_ws_alloc (sizeof (SM_FUNCTION_INFO));
if (func_index_info == NULL)
{
return NULL;
}
memset (func_index_info, 0, sizeof (SM_FUNCTION_INFO));
if (expr->data_type)
{
d = pt_data_type_to_db_domain (parser, expr->data_type, NULL);
}
else
{
d = pt_type_enum_to_db_domain (expr->type_enum);
}
if (node->node_type == PT_SORT_SPEC)
{
d->is_desc = (node->info.sort_spec.asc_or_desc == PT_ASC ? 0 : 1);
}
d = tp_domain_cache (d);
func_index_info->fi_domain = d;
assert (func_index_info->fi_domain != NULL);
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
expr_str = parser_print_tree_with_quotes (parser, expr);
parser->custom_print = save_custom;
assert (expr_str != NULL);
if (expr_str && (strlen (expr_str) >= MAX_FUNCTION_EXPRESSION_STRING_LENGTH))
{
error = ER_SM_INVALID_FILTER_PREDICATE_LENGTH;
PT_ERRORmf ((PARSER_CONTEXT *) parser, node, MSGCAT_SET_ERROR, -(ER_SM_INVALID_FUNCTION_EXPRESSION_LENGTH),
MAX_FUNCTION_EXPRESSION_STRING_LENGTH);
goto error_exit;
}
func_index_info->expr_str = strdup (expr_str);
if (func_index_info->expr_str == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) strlen (expr_str));
goto error_exit;
}
func_index_info->expr_stream = NULL;
func_index_info->expr_stream_size = -1;
if (do_index == DO_INDEX_CREATE)
{
func_pred = pt_to_func_pred (parser, spec, expr);
if (func_pred)
{
xts_map_func_pred_to_stream (func_pred, &func_index_info->expr_stream, &func_index_info->expr_stream_size);
}
else
{
goto error_exit;
}
}
return func_index_info;
error_exit:
if (func_index_info != NULL)
{
sm_free_function_index_info (func_index_info);
db_ws_free (func_index_info);
}
return NULL;
}
/*
* do_recreate_func_index_constr () - rebuilds the function index expression
* parser(in): parser context
* constr(in): constraint info, must be a function index
* func_index_info(in): function index info
* alter (in): information regarding changes made by an ALTER statement
* src_cls_name (in): current table name holding the constraint
* new_cls_name (in): new table name holding the constraint
* (when CREATE TABLE ... LIKE statement is used)
* return: NO_ERROR or error code
*
*/
int
do_recreate_func_index_constr (PARSER_CONTEXT * parser, SM_CONSTRAINT_INFO * constr, SM_FUNCTION_INFO * func_index_info,
PT_NODE * alter, const char *src_cls_name, const char *new_cls_name)
{
PT_NODE **stmt;
PT_NODE *expr;
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
SM_FUNCTION_INFO *fi_info_ws = NULL;
int error = NO_ERROR;
const char *class_name = NULL;
char *query_str = NULL;
size_t query_str_len = 0;
int saved_func_index_pos = -1, saved_attr_index_start = -1;
bool free_packing_buff = false;
bool free_parser = false;
SM_FUNCTION_INFO *fi_info = NULL;
if (constr)
{
fi_info = constr->func_index_info;
}
else
{
fi_info = func_index_info;
}
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
error = ER_FAILED;
goto error;
}
free_parser = true;
}
if (alter && alter->node_type == PT_ALTER)
{
/* rebuilding the index due to ALTER CHANGE statement */
if (alter->info.alter.entity_name)
{
class_name = alter->info.alter.entity_name->info.name.original;
}
}
else
{
/* rebuilding the index due to CREATE TABLE ... LIKE statement */
if (src_cls_name)
{
class_name = src_cls_name;
}
}
if (class_name == NULL)
{
error = ER_FAILED;
goto error;
}
query_str_len = strlen (fi_info->expr_str) + strlen (class_name) + 7 /* strlen("SELECT ") */ +
6 /* strlen(" FROM ") */ +
2 /* [] */ +
1 /* terminating null */ ;
query_str = (char *) malloc (query_str_len);
if (query_str == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_str_len);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
snprintf (query_str, query_str_len, "SELECT %s FROM [%s]", fi_info->expr_str, class_name);
stmt = parser_parse_string_use_sys_charset (parser, query_str);
if (stmt == NULL || *stmt == NULL || pt_has_error (parser))
{
error = ER_FAILED;
goto error;
}
expr = (*stmt)->info.query.q.select.list;
if (alter)
{
(void) parser_walk_tree (parser, expr, replace_names_alter_chg_attr, alter, NULL, NULL);
}
else
{
PT_NODE *new_node = pt_name (parser, new_cls_name);
PT_NODE *old_name = (*stmt)->info.query.q.select.from->info.spec.entity_name;
if (!old_name)
{
error = ER_FAILED;
goto error;
}
if (new_node)
{
new_node->next = old_name->next;
old_name->next = NULL;
parser_free_tree (parser, old_name);
(*stmt)->info.query.q.select.from->info.spec.entity_name = new_node;
}
(void) parser_walk_tree (parser, expr, pt_replace_names_index_expr, (void *) new_cls_name, NULL, NULL);
}
*stmt = pt_resolve_names (parser, *stmt, &sc_info);
if (*stmt != NULL && !pt_has_error (parser))
{
*stmt = pt_semantic_type (parser, *stmt, &sc_info);
}
else
{
error = ER_FAILED;
goto error;
}
if (*stmt != NULL && !pt_has_error (parser))
{
expr = (*stmt)->info.query.q.select.list;
if (expr && !pt_is_function_index_expr (parser, expr, true))
{
error = ER_FAILED;
goto error;
}
}
else
{
error = ER_FAILED;
goto error;
}
saved_func_index_pos = fi_info->col_id;
saved_attr_index_start = fi_info->attr_index_start;
if (constr)
{
/* free previous function index info */
sm_free_function_index_info (fi_info);
free_and_init (fi_info);
}
else
{
sm_free_function_index_info (fi_info);
}
pt_enter_packing_buf ();
free_packing_buff = true;
fi_info_ws = pt_node_to_function_index (parser, (*stmt)->info.query.q.select.from, expr, DO_INDEX_CREATE);
if (fi_info_ws == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, sizeof (SM_FUNCTION_INFO));
goto error;
}
if (constr)
{
/* original function index uses WS storage, switch to normal heap storage */
fi_info = (SM_FUNCTION_INFO *) malloc (sizeof (SM_FUNCTION_INFO));
if (fi_info == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, sizeof (SM_FUNCTION_INFO));
db_ws_free (fi_info_ws);
goto error;
}
}
memcpy (fi_info, fi_info_ws, sizeof (SM_FUNCTION_INFO));
db_ws_free (fi_info_ws);
fi_info->col_id = saved_func_index_pos;
fi_info->attr_index_start = saved_attr_index_start;
if (constr)
{
/* restore original values */
constr->func_index_info = fi_info;
}
error:
if (free_packing_buff)
{
pt_exit_packing_buf ();
}
if (free_parser)
{
parser_free_parser (parser);
}
if (query_str)
{
free_and_init (query_str);
}
return error;
}
/*
* do_recreate_filter_index_constr () - rebuilds the filter index expression
* parser(in): parser context
* filter_index_info(in): filter index information
* alter (in): information regarding changes made by an ALTER statement
* src_cls_name (in): current table name holding the constraint
* new_cls_name (in): new table name holding the constraint
* (when CREATE TABLE ... LIKE statement is used)
* return: NO_ERROR or error code
*
*/
int
do_recreate_filter_index_constr (PARSER_CONTEXT * parser, SM_PREDICATE_INFO * filter_index_info, PT_NODE * alter,
const char *src_cls_name, const char *new_cls_name)
{
PT_NODE **stmt;
PT_NODE *where_predicate;
SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
PARSER_VARCHAR *filter_expr = NULL;
PRED_EXPR_WITH_CONTEXT *filter_predicate;
int error;
const char *class_name = NULL;
char *query_str = NULL;
size_t query_str_len = 0;
char *pred_str = NULL;
int pred_str_len = 0;
bool free_packing_buff = false;
SM_PREDICATE_INFO new_pred = { NULL, NULL, 0, NULL, 0 };
bool free_parser = false;
unsigned int save_custom;
if (parser == NULL)
{
parser = parser_create_parser ();
if (parser == NULL)
{
error = ER_FAILED;
goto error;
}
free_parser = true;
}
if (alter && alter->node_type == PT_ALTER)
{
/* rebuilding the index due to ALTER CHANGE statement */
if (alter->info.alter.entity_name)
{
class_name = alter->info.alter.entity_name->info.name.original;
}
}
else
{
/* rebuilding the index due to CREATE TABLE ... LIKE statement */
if (src_cls_name)
{
class_name = src_cls_name;
}
}
if (class_name == NULL)
{
error = ER_FAILED;
goto error;
}
query_str_len = strlen (filter_index_info->pred_string) + strlen (class_name) + 9 /* strlen("SELECT 1 ") */ +
6 /* strlen(" FROM ") */ +
2 /* [] */ +
7 /* strlen(" WHERE " */ +
1 /* terminating null */ ;
query_str = (char *) malloc (query_str_len);
if (query_str == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, query_str_len);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
snprintf (query_str, query_str_len, "SELECT 1 FROM [%s] WHERE %s", class_name, filter_index_info->pred_string);
stmt = parser_parse_string_use_sys_charset (parser, query_str);
if (stmt == NULL || *stmt == NULL || pt_has_error (parser))
{
error = ER_FAILED;
goto error;
}
where_predicate = (*stmt)->info.query.q.select.where;
if (alter)
{
(void) parser_walk_tree (parser, where_predicate, replace_names_alter_chg_attr, alter, NULL, NULL);
}
else
{
PT_NODE *new_node = pt_name (parser, new_cls_name);
PT_NODE *old_name = (*stmt)->info.query.q.select.from->info.spec.entity_name;
if (!old_name)
{
error = ER_FAILED;
goto error;
}
if (new_node)
{
new_node->next = old_name->next;
old_name->next = NULL;
parser_free_tree (parser, old_name);
(*stmt)->info.query.q.select.from->info.spec.entity_name = new_node;
}
(void) parser_walk_tree (parser, where_predicate, pt_replace_names_index_expr, (void *) new_cls_name, NULL, NULL);
}
*stmt = pt_resolve_names (parser, *stmt, &sc_info);
if (*stmt != NULL && !pt_has_error (parser))
{
*stmt = pt_semantic_type (parser, *stmt, &sc_info);
}
else
{
error = ER_FAILED;
goto error;
}
if (*stmt == NULL || pt_has_error (parser))
{
error = ER_FAILED;
goto error;
}
/* make sure paren_type is 0 so parenthesis are not printed */
where_predicate->info.expr.paren_type = 0;
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
filter_expr = pt_print_bytes (parser, where_predicate);
parser->custom_print = save_custom;
if (filter_expr)
{
pred_str = (char *) filter_expr->bytes;
pred_str_len = strlen (pred_str);
new_pred.pred_string = (char *) calloc (pred_str_len + 1, sizeof (char));
if (new_pred.pred_string == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, (pred_str_len + 1) * sizeof (char));
goto error;
}
memcpy (new_pred.pred_string, pred_str, pred_str_len);
if (strlen (new_pred.pred_string) > MAX_FILTER_PREDICATE_STRING_LENGTH)
{
PT_ERRORmf (parser, where_predicate, MSGCAT_SET_ERROR, -(ER_SM_INVALID_FILTER_PREDICATE_LENGTH),
MAX_FILTER_PREDICATE_STRING_LENGTH);
error = ER_FAILED;
goto error;
}
}
pt_enter_packing_buf ();
free_packing_buff = true;
filter_predicate = pt_to_pred_with_context (parser, where_predicate, (*stmt)->info.query.q.select.from);
if (filter_predicate)
{
error = xts_map_filter_pred_to_stream (filter_predicate, &new_pred.pred_stream, &new_pred.pred_stream_size);
if (error != NO_ERROR)
{
PT_ERRORm (parser, where_predicate, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
error = ER_FAILED;
goto error;
}
}
else
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto error;
}
if (filter_predicate->attrids_pred)
{
int i;
assert (filter_predicate->num_attrs_pred > 0);
new_pred.att_ids = (int *) calloc (filter_predicate->num_attrs_pred, sizeof (int));
if (new_pred.att_ids == NULL)
{
PT_ERRORm (parser, where_predicate, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
error = ER_FAILED;
goto error;
}
for (i = 0; i < filter_predicate->num_attrs_pred; i++)
{
new_pred.att_ids[i] = filter_predicate->attrids_pred[i];
}
}
else
{
new_pred.att_ids = NULL;
}
new_pred.num_attrs = filter_predicate->num_attrs_pred;
if (filter_index_info->pred_string)
{
free_and_init (filter_index_info->pred_string);
}
if (filter_index_info->pred_stream)
{
free_and_init (filter_index_info->pred_stream);
}
if (filter_index_info->att_ids)
{
free_and_init (filter_index_info->att_ids);
}
filter_index_info->pred_string = new_pred.pred_string;
filter_index_info->pred_stream = new_pred.pred_stream;
filter_index_info->pred_stream_size = new_pred.pred_stream_size;
filter_index_info->att_ids = new_pred.att_ids;
filter_index_info->num_attrs = new_pred.num_attrs;
if (free_parser)
{
parser_free_parser (parser);
}
if (query_str)
{
free_and_init (query_str);
}
pt_exit_packing_buf ();
return NO_ERROR;
error:
if (free_parser)
{
parser_free_parser (parser);
}
if (query_str)
{
free_and_init (query_str);
}
if (new_pred.pred_string)
{
free_and_init (new_pred.pred_string);
}
if (new_pred.pred_stream)
{
free_and_init (new_pred.pred_stream);
}
if (new_pred.att_ids)
{
free_and_init (new_pred.att_ids);
}
if (free_packing_buff)
{
pt_exit_packing_buf ();
}
return error;
}
/*
* replace_names_alter_chg_attr() - Replaces the attribute name in a given
* expression, based on the changes imposed
* the ALTER CHANGE statement
* return: PT_NODE pointer
* parser(in): Parser context
* node(in):
* void_arg(in):
* continue_walk(in):
*
* Note:
*/
static PT_NODE *
replace_names_alter_chg_attr (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
PT_NODE *alter = (PT_NODE *) void_arg;
PT_NODE *old_name = NULL;
const char *new_name = NULL;
assert (alter->node_type == PT_ALTER);
if (alter->info.alter.alter_clause.attr_mthd.attr_def_list)
{
new_name = get_attr_name (alter->info.alter.alter_clause.attr_mthd.attr_def_list);
}
old_name = alter->info.alter.alter_clause.attr_mthd.attr_old_name;
if (old_name == NULL || new_name == NULL)
{
*continue_walk = PT_STOP_WALK;
return node;
}
*continue_walk = PT_CONTINUE_WALK;
if (node->node_type == PT_DOT_)
{
if (PT_IS_NAME_NODE (node->info.dot.arg2))
{
PT_NODE *new_node = NULL;
if (intl_identifier_casecmp (node->info.dot.arg2->info.name.original, old_name->info.name.original) == 0)
{
new_node = pt_name (parser, new_name);
}
else
{
new_node = pt_name (parser, node->info.dot.arg2->info.name.original);
}
if (new_node)
{
new_node->next = node->next;
node->next = NULL;
parser_free_tree (parser, node);
node = new_node;
}
}
}
return node;
}
/*
* pt_replace_names_index_expr () - Replaces the table name in a given
* expression, based on the name required
* when copying a function/filter index on
* CREATE TABLE ... LIKE or when adding an
* function/filter index to partitions of a class.
*
* return: PT_NODE pointer
* parser(in): Parser context
* node(in):
* void_arg(in):
* continue_walk(in):
*
* Note:
*/
static PT_NODE *
pt_replace_names_index_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
const char *new_name = (char *) void_arg;
*continue_walk = PT_CONTINUE_WALK;
if (node->node_type == PT_DOT_)
{
if (PT_IS_NAME_NODE (node->info.dot.arg1))
{
PT_NODE *new_node = pt_name (parser, new_name);
PT_NODE *dot_arg = node->info.dot.arg1;
if (new_node)
{
new_node->next = dot_arg->next;
dot_arg->next = NULL;
parser_free_tree (parser, dot_arg);
node->info.dot.arg1 = new_node;
}
}
}
return node;
}
/*
* get_index_type_qualifiers() - get qualifiers of the index type that
* matches the index name.
* return: NO_ERROR or error code
* obj(in): Memory Object Pointer
* is_reverse(out): TRUE if the index type has the reverse feature.
* is_unique(out): TRUE if the index type has the unique feature.
* index_name(in): the name of index
*
* note:
* Only index types that satisfy the SM_IS_INDEX_FAMILY
* condition will be searched for.
*/
static int
get_index_type_qualifiers (MOP obj, bool * is_reverse, bool * is_unique, const char *index_name)
{
int error_code = NO_ERROR;
SM_CLASS_CONSTRAINT *sm_all_constraints = NULL;
SM_CLASS_CONSTRAINT *sm_constraint = NULL;
if (obj == NULL)
{
error_code = ER_FAILED;
return error_code;
}
sm_all_constraints = sm_class_constraints (obj);
sm_constraint = classobj_find_constraint_by_name (sm_all_constraints, index_name);
if (sm_all_constraints == NULL || sm_constraint == NULL)
{
error_code = ER_SM_NO_INDEX;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, index_name);
return error_code;
}
if (!SM_IS_INDEX_FAMILY (sm_constraint->type))
{
error_code = ER_SM_CONSTRAINT_HAS_DIFFERENT_TYPE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, index_name);
return error_code;
}
switch (sm_constraint->type)
{
case SM_CONSTRAINT_INDEX:
*is_reverse = false;
*is_unique = false;
break;
case SM_CONSTRAINT_UNIQUE:
*is_reverse = false;
*is_unique = true;
break;
case SM_CONSTRAINT_REVERSE_INDEX:
*is_reverse = true;
*is_unique = false;
break;
case SM_CONSTRAINT_REVERSE_UNIQUE:
*is_reverse = true;
*is_unique = true;
break;
default:
break;
}
return error_code;
}
/*
* pt_node_to_partition_info() -
* return: Error code
* parser(in): Parser context
* node(in): Parser tree of an partition class
* entity_name(in):
* class_name(in): class name
* partition_name(in): partition name
* minval(in):
*
* Note:
*/
static SM_PARTITION *
pt_node_to_partition_info (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * entity_name, char *class_name,
char *partition_name, DB_VALUE * minval)
{
DB_VALUE val, *ptval, *hashsize;
PT_NODE *parts;
char *query, *query_str = NULL, *p;
DB_COLLECTION *dbc = NULL;
size_t buf_size;
SM_PARTITION *partition = NULL;
partition = classobj_make_partition_info ();
if (partition == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (SM_PARTITION));
return NULL;
}
if (node->node_type == PT_PARTITION)
{
partition->pname = NULL;
}
else
{
partition->pname = ws_copy_string ((char *) node->info.parts.name->info.name.original);
if (partition->pname == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
strlen ((char *) node->info.parts.name->info.name.original));
return NULL;
}
}
if (node->node_type == PT_PARTITION)
{
partition->partition_type = node->info.partition.type;
partition->class_partition_type = DB_PARTITIONED_CLASS;
}
else
{
partition->partition_type = node->info.parts.type;
partition->class_partition_type = DB_PARTITION_CLASS;
}
if (node->node_type == PT_PARTITION)
{
unsigned int save_custom;
save_custom = parser->custom_print;
parser->custom_print |= PT_CHARSET_COLLATE_FULL;
query = parser_print_tree_with_quotes (parser, node->info.partition.expr);
parser->custom_print = save_custom;
if (query == NULL)
{
goto fail_return;
}
buf_size = strlen (query) + strlen (class_name) + 7 /* strlen("SELECT ") */ +
6 /* strlen(" FROM ") */ +
2 /* [] */ +
1 /* terminating null */ ;
if (buf_size > DB_MAX_PARTITION_EXPR_LENGTH)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PARTITION_EXPRESSION_TOO_LONG, DB_MAX_PARTITION_EXPR_LENGTH);
goto fail_return;
}
query_str = (char *) malloc (buf_size);
if (query_str == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, buf_size);
goto fail_return;
}
sprintf (query_str, "SELECT %s FROM [%s]", query, class_name);
partition->expr = ws_copy_string (query_str);
if (partition->expr == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, strlen (query_str));
goto fail_return;
}
}
else
{
partition->expr = NULL;
}
if (query_str)
{
free_and_init (query_str);
}
dbc = set_create_sequence (0);
if (dbc == NULL)
{
goto fail_return;
}
if (node->node_type == PT_PARTITION)
{
DB_VALUE expr;
SM_FUNCTION_INFO *part_expr = NULL;
p = (char *) node->info.partition.keycol->info.name.original;
db_make_varchar (&val, PARTITION_VARCHAR_LEN, p, strlen (p), LANG_SYS_CODESET, LANG_SYS_COLLATION);
set_add_element (dbc, &val);
if (node->info.partition.type == PT_PARTITION_HASH)
{
hashsize = pt_value_to_db (parser, node->info.partition.hashsize);
set_add_element (dbc, hashsize);
}
else
{
set_add_element (dbc, minval);
}
/* Now compile the partition expression and add it to the end of the collection */
part_expr = compile_partition_expression (parser, entity_name, node);
if (part_expr == NULL)
{
if (pt_has_error (parser))
{
pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, node);
}
goto fail_return;
}
db_make_char (&expr, part_expr->expr_stream_size, part_expr->expr_stream, part_expr->expr_stream_size,
LANG_SYS_CODESET, LANG_SYS_COLLATION);
set_add_element (dbc, &expr);
/* Notice that we're not calling pr_clear_value on expr here because memory allocated for expr_stream is
* deallocated by 'sm_free_function_index_info' */
sm_free_function_index_info (part_expr);
db_ws_free (part_expr);
part_expr = NULL;
}
else
{
if (node->info.parts.type == PT_PARTITION_RANGE)
{
if (minval == NULL)
{
db_make_null (&val);
set_add_element (dbc, &val);
}
else
{
set_add_element (dbc, minval);
}
}
if (node->info.parts.values == NULL)
{ /* RANGE-MAXVALUE */
db_make_null (&val);
set_add_element (dbc, &val);
}
else
{
for (parts = node->info.parts.values; parts; parts = parts->next)
{
ptval = pt_value_to_db (parser, parts);
if (ptval == NULL)
{
goto fail_return;
}
set_add_element (dbc, ptval);
}
}
}
partition->values = db_seq_copy (dbc);
if (node->node_type != PT_PARTITION)
{
if (node->info.parts.comment != NULL)
{
assert (node->info.parts.comment->node_type == PT_VALUE);
p = (char *) node->info.parts.comment->info.value.data_value.str->bytes;
partition->comment = ws_copy_string (p);
if (partition->comment == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, strlen (p));
goto fail_return;
}
}
}
set_free (dbc);
return partition;
fail_return:
if (dbc != NULL)
{
set_free (dbc);
}
classobj_free_partition_info (partition);
if (query_str != NULL)
{
free_and_init (query_str);
}
assert (er_errid () != NO_ERROR);
return NULL;
}
/*
* do_save_all_indexes () - Save all constraint info for a given class
* return: error code
* classmop(in):
* saved_index_info_listpp(out):
*
*/
static int
do_save_all_indexes (MOP classmop, SM_CONSTRAINT_INFO ** saved_index_info_listpp)
{
int error = NO_ERROR;
SM_CLASS_CONSTRAINT *c = NULL;
SM_CONSTRAINT_INFO *index_save_info = NULL, *saved = NULL;
SM_CLASS *class_ = NULL;
assert (classmop != NULL);
assert (saved_index_info_listpp != NULL);
*saved_index_info_listpp = NULL;
error = au_fetch_class (classmop, &class_, AU_FETCH_READ, DB_AUTH_SELECT);
if (error != NO_ERROR)
{
return error;
}
if (class_->constraints == NULL)
{
/* no constraints, nothing to do */
return NO_ERROR;
}
if (class_->class_type != SM_CLASS_CT)
{
return NO_ERROR;
}
for (c = class_->constraints; c; c = c->next)
{
if ((DB_CONSTRAINT_TYPE) c->type == DB_CONSTRAINT_NOT_NULL)
{
continue;
}
/* save constraints */
error = sm_save_constraint_info (&index_save_info, c);
if (error == NO_ERROR)
{
assert (index_save_info != NULL);
saved = index_save_info;
while (saved->next)
{
saved = saved->next;
}
}
else
{
return error;
}
}
*saved_index_info_listpp = index_save_info;
return NO_ERROR;
}
/*
* do_drop_saved_indexes () - drop all indexes from a constraint info list
* return: error
* classmop (in) :
* index_save_info :
*/
static int
do_drop_saved_indexes (MOP classmop, SM_CONSTRAINT_INFO * index_save_info)
{
int error = NO_ERROR;
SM_CONSTRAINT_INFO *saved = NULL;
for (saved = index_save_info; saved != NULL; saved = saved->next)
{
if (SM_IS_CONSTRAINT_EXCEPT_INDEX_FAMILY ((SM_CONSTRAINT_TYPE) saved->constraint_type))
{
error =
sm_drop_constraint (classmop, saved->constraint_type, saved->name, (const char **) saved->att_names, false,
false);
if (error != NO_ERROR)
{
return error;
}
}
else
{
error = sm_drop_index (classmop, saved->name);
if (error != NO_ERROR)
{
return error;
}
}
}
return NO_ERROR;
}
/*
* do_recreate_saved_indexes () - recreate all indexes from a constraint
* info list
* return: error
* classmop (in) :
* index_save_info :
*/
static int
do_recreate_saved_indexes (MOP classmop, SM_CONSTRAINT_INFO * index_save_info)
{
int error = NO_ERROR;
SM_CONSTRAINT_INFO *saved = NULL;
for (saved = index_save_info; saved != NULL; saved = saved->next)
{
if (SM_IS_CONSTRAINT_INDEX_FAMILY ((SM_CONSTRAINT_TYPE) saved->constraint_type))
{
error = sm_add_constraint (classmop, saved->constraint_type, saved->name, (const char **) saved->att_names,
saved->asc_desc, saved->prefix_length, false, saved->filter_predicate,
saved->func_index_info, saved->comment, saved->index_status);
if (error != NO_ERROR)
{
return error;
}
}
}
return NO_ERROR;
}
int
do_alter_index_status (PARSER_CONTEXT * parser, const PT_NODE * statement)
{
int error = NO_ERROR;
DB_OBJECT *obj;
PT_NODE *cls = NULL;
SM_TEMPLATE *ctemplate = NULL;
const char *class_name = NULL;
const char *index_name = NULL;
SM_INDEX_STATUS index_status;
bool do_rollback = false;
index_name = statement->info.index.index_name ? statement->info.index.index_name->info.name.original : NULL;
if (index_name == NULL)
{
goto error_exit;
}
index_status = (SM_INDEX_STATUS) statement->info.index.index_status;
cls = statement->info.index.indexed_class ? statement->info.index.indexed_class->info.spec.flat_entity_list : NULL;
if (cls == NULL)
{
goto error_exit;
}
class_name = cls->info.name.resolved;
obj = db_find_class (class_name);
if (obj == NULL)
{
ASSERT_ERROR_AND_SET (error);
goto error_exit;
}
error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
if (error != NO_ERROR)
{
goto error_exit;
}
do_rollback = true;
ctemplate = smt_edit_class_mop (obj, AU_INDEX);
if (ctemplate == NULL)
{
ASSERT_ERROR_AND_SET (error);
goto error_exit;
}
error = smt_change_constraint_status (ctemplate, index_name, index_status);
if (error != NO_ERROR)
{
goto error_exit;
}
/* classobj_free_template() is included in sm_update_class() */
error = sm_update_class (ctemplate, NULL);
if (error != NO_ERROR)
{
/* Even though sm_update() did not return NO_ERROR, ctemplate is already freed */
ctemplate = NULL;
goto error_exit;
}
end:
return error;
error_exit:
if (ctemplate != NULL)
{
/* smt_quit() always returns NO_ERROR */
smt_quit (ctemplate);
}
if (do_rollback == true)
{
if (do_rollback && error != ER_LK_UNILATERALLY_ABORTED)
{
tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_INDEX);
}
}
error = (error == NO_ERROR && (error = er_errid ()) == NO_ERROR) ? ER_FAILED : error;
goto end;
}
int
ib_get_thread_count ()
{
return ib_thread_count;
}