/* * PROGRAM: Dynamic SQL runtime support * MODULE: ddl.cpp * DESCRIPTION: Utilities for generating ddl * * The contents of this file are subject to the Interbase Public * License Version 1.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.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash, * caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET; * * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE" * conditionals, as the engine now fully supports * readonly databases. * December 2001 Mike Nordell - Attempt to make it C++ * * 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash, * caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET; * 2001.5.29 Claudio Valderrama: Check for view v/s relation in DROP * command will stop a user that uses DROP VIEW and drops a table by * accident and vice-versa. * 2001.5.30 Claudio Valderrama: alter column should use 1..N for the * position argument since the call comes from SQL DDL. * 2001.6.27 Claudio Valderrama: DDL_resolve_intl_type() was adding 2 to the * length of varchars instead of just checking that len+2<=MAX_COLUMN_SIZE. * It required a minor change to put_field() where it was decremented, too. * 2001.6.27 Claudio Valderrama: Finally stop users from invoking the same option * several times when altering a domain. Specially dangerous with text data types. * Ex: alter domain d type char(5) type varchar(5) default 'x' default 'y'; * Bear in mind that if DYN functions are addressed directly, this protection * becomes a moot point. * 2001.6.30 Claudio Valderrama: revert changes from 2001.6.26 because the code * is called from several places and there are more functions, even in metd.c, * playing the same nonsense game with the field's length, so it needs more * careful examination. For now, the new checks in DYN_MOD should catch most anomalies. * 2001.7.3 Claudio Valderrama: fix Firebird Bug #223059 with mismatch between number * of declared fields for a VIEW and effective fields in the SELECT statement. * 2001.07.22 Claudio Valderrama: minor fixes and improvements. * 2001.08.18 Claudio Valderrama: RECREATE PROCEDURE. * 2001.10.01 Claudio Valderrama: modify_privilege() should recognize that a ROLE can * now be made an explicit grantee. * 2001.10.08 Claudio Valderrama: implement fb_sysflag enum values for autogenerated * non-system triggers so DFW can recognize them easily. * 2001.10.26 Claudio Valderrama: added a call to the new METD_drop_function() * in DDL_execute() so the metadata cache for udfs can be refreshed. * 2001.12.06 Claudio Valderrama: DDL_resolve_intl_type should calculate field length * 2002.08.04 Claudio Valderrama: allow declaring and defining variables at the same time * 2002.08.04 Dmitry Yemanov: ALTER VIEW * 2002.08.31 Dmitry Yemanov: allowed user-defined index names for PK/FK/UK constraints * 2002.09.01 Dmitry Yemanov: RECREATE VIEW * 2002.09.12 Nickolay Samofatov: fixed cached metadata errors * 2004.01.16 Vlad Horsun: added support for default parameters and * EXECUTE BLOCK statement * Adriano dos Santos Fernandes */ #include "firebird.h" #include "dyn_consts.h" #include #include #include "../jrd/SysFunction.h" #include "../common/classes/MetaName.h" #include "../dsql/dsql.h" #include "../dsql/node.h" #include "../dsql/ExprNodes.h" #include "../jrd/ibase.h" #include "../jrd/Attachment.h" #include "../jrd/intl.h" #include "../jrd/intl_classes.h" #include "../jrd/jrd.h" #include "../jrd/flags.h" #include "../jrd/constants.h" #include "../dsql/errd_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/gen_proto.h" #include "../dsql/make_proto.h" #include "../dsql/metd_proto.h" #include "../dsql/pass1_proto.h" #include "../dsql/utld_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/dyn_proto.h" #include "../jrd/met_proto.h" #include "../jrd/thread_proto.h" #include "../yvalve/gds_proto.h" #include "../jrd/jrd_proto.h" #include "../jrd/vio_proto.h" #include "../yvalve/why_proto.h" #include "../common/utils_proto.h" #include "../dsql/DdlNodes.h" #include "../dsql/DSqlDataTypeUtil.h" #include "../common/StatusArg.h" #ifdef DSQL_DEBUG #include "../common/prett_proto.h" #endif using namespace Jrd; using namespace Dsql; using namespace Firebird; static void assign_field_length(dsql_fld*, USHORT); static void define_computed(DsqlCompilerScratch*, dsql_nod*, dsql_fld*, dsql_nod*); static void define_database(DsqlCompilerScratch*); static void define_filter(DsqlCompilerScratch*); static SSHORT getBlobFilterSubType(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node); static void define_role(DsqlCompilerScratch*); static void define_index(DsqlCompilerScratch*); static void define_shadow(DsqlCompilerScratch*); static void define_udf(DsqlCompilerScratch*); static void generate_dyn(DsqlCompilerScratch*, dsql_nod*); static void grant_revoke(DsqlCompilerScratch*); static void modify_database(DsqlCompilerScratch*); static void modify_index(DsqlCompilerScratch*); static void modify_privilege(DsqlCompilerScratch* dsqlScratch, NOD_TYPE type, SSHORT option, const UCHAR* privs, const dsql_nod* table, const dsql_nod* user, const dsql_nod* grantor, const dsql_str* field_name); static char modify_privileges(DsqlCompilerScratch*, NOD_TYPE, SSHORT, const dsql_nod*, const dsql_nod*, const dsql_nod*, const dsql_nod*); static void modify_udf(DsqlCompilerScratch*); static void modify_map(DsqlCompilerScratch*); static void process_role_nm_list(DsqlCompilerScratch*, SSHORT, const dsql_nod*, const dsql_nod*, NOD_TYPE, const dsql_nod*); static void put_field(DsqlCompilerScratch*, dsql_fld*, bool); static void set_statistics(DsqlCompilerScratch*); static void define_user(DsqlCompilerScratch*, UCHAR); static void put_grantor(DsqlCompilerScratch* dsqlScratch, const dsql_nod* grantor); static void post_607(const Arg::StatusVector& v); static void put_user_grant(DsqlCompilerScratch* dsqlScratch, const dsql_nod* user); const int DEFAULT_BLOB_SEGMENT_SIZE = 80; // bytes void DDL_execute(dsql_req* request) { /************************************** * * D D L _ e x e c u t e * ************************************** * * Functional description * Call access method layered service DYN * to interpret dyn string and perform * metadata updates. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); const DsqlCompiledStatement* statement = request->getStatement(); #ifdef DSQL_DEBUG if (DSQL_debug & 4) { dsql_trace("Output DYN string for DDL:"); PRETTY_print_dyn(statement->getDdlData().begin(), gds__trace_printer, NULL, 0); } #endif // for delete & modify, get rid of the cached relation metadata const dsql_str* string = NULL; SYM_TYPE sym_type; const NOD_TYPE type = statement->getDdlNode()->nod_type; switch (type) { case nod_del_udf: case nod_mod_udf: // Signal UDF for obsolescence string = (dsql_str*) statement->getDdlNode()->nod_arg[e_udf_name]; sym_type = SYM_udf; METD_drop_function(request->getTransaction(), QualifiedName(string->str_data, "")); break; } if (string) MET_dsql_cache_release(tdbb, sym_type, string->str_data); if (type == nod_class_stmtnode) { fb_utils::init_status(tdbb->tdbb_status_vector); // Do the same as DYN_ddl does. // run all statements under savepoint control { // scope AutoSavePoint savePoint(tdbb, request->req_transaction); DdlNode* ddlNode = reinterpret_cast(statement->getDdlNode()->nod_arg[0]); ddlNode->executeDdl(tdbb, statement->getDdlScratch(), request->req_transaction); savePoint.release(); // everything is ok } } else { fb_assert(statement->getDdlData().getCount() <= MAX_ULONG); DYN_ddl(request->req_transaction, statement->getDdlData().getCount(), statement->getDdlData().begin(), *statement->getSqlText()); } JRD_autocommit_ddl(tdbb, request->req_transaction); } void DDL_generate(DsqlCompilerScratch* dsqlScratch, dsql_nod* node) { /************************************** * * D D L _ g e n e r a t e * ************************************** * * Functional description * Generate the DYN string for a * metadata update. Done during the * prepare phase. * **************************************/ if (dsqlScratch->getAttachment()->dbb_read_only) { ERRD_post(Arg::Gds(isc_read_only_database)); return; } dsqlScratch->getStatement()->setDdlNode(node); if (node->nod_type != nod_class_stmtnode) dsqlScratch->appendUChar(isc_dyn_version_1); generate_dyn(dsqlScratch, node); if (node->nod_type != nod_class_stmtnode) { dsqlScratch->appendUChar(isc_dyn_eoc); // Store DYN data in the statement. dsqlScratch->getStatement()->setDdlData(dsqlScratch->getBlrData()); } } // // Determine whether ids or names should be referenced // when generating blr for fields and relations. // bool DDL_ids(const DsqlCompilerScratch* scratch) { return !scratch->getStatement()->getDdlNode(); } // // See the next function for description. This is only a // wrapper that sets the last parameter to false to indicate // we are creating a field, not modifying one. // void DDL_resolve_intl_type(DsqlCompilerScratch* dsqlScratch, dsql_fld* field, const dsql_str* collation_name) { DDL_resolve_intl_type2(dsqlScratch, field, collation_name, false); } void DDL_resolve_intl_type2(DsqlCompilerScratch* dsqlScratch, dsql_fld* field, const dsql_str* collation_name, bool modifying) { /************************************** * * D D L _ r e s o l v e _ i n t l _ t y p e 2 * ************************************** * * Function * If the field is defined with a character set or collation, * resolve the names to a subtype now. * * Also resolve the field length & whatnot. * * If the field is being created, it will pick the db-wide charset * and collation if not specified. If the field is being modified, * since we don't allow changes to those attributes, we'll go and * calculate the correct old lenth from the field itself so DYN * can validate the change properly. * * For International text fields, this is a good time to calculate * their actual size - when declared they were declared in * lengths of CHARACTERs, not BYTES. * **************************************/ if (field->fld_type_of_name.hasData()) { if (field->fld_type_of_table) { dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, field->fld_type_of_table->str_data); const dsql_fld* fld = NULL; if (relation) { const MetaName fieldName(field->fld_type_of_name); for (fld = relation->rel_fields; fld; fld = fld->fld_next) { if (fieldName == fld->fld_name) { field->fld_dimensions = fld->fld_dimensions; field->fld_source = fld->fld_source; field->fld_length = fld->fld_length; field->fld_scale = fld->fld_scale; field->fld_sub_type = fld->fld_sub_type; field->fld_character_set_id = fld->fld_character_set_id; field->fld_collation_id = fld->fld_collation_id; field->fld_character_length = fld->fld_character_length; field->fld_flags = fld->fld_flags; field->fld_dtype = fld->fld_dtype; field->fld_seg_length = fld->fld_seg_length; break; } } } if (!fld) { // column @1 does not exist in table/view @2 post_607(Arg::Gds(isc_dyn_column_does_not_exist) << Arg::Str(field->fld_type_of_name) << Arg::Str(field->fld_type_of_table->str_data)); } } else { if (!METD_get_domain(dsqlScratch->getTransaction(), field, field->fld_type_of_name.c_str())) { // Specified domain or source field does not exist post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(field->fld_type_of_name)); } } if (field->fld_dimensions != 0) { ERRD_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << Arg::Str("Usage of domain or TYPE OF COLUMN of array type in PSQL")); } } if ((field->fld_dtype > dtype_any_text) && field->fld_dtype != dtype_blob) { if (field->fld_character_set || collation_name || field->fld_flags & FLD_national) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_requires_text)); } return; } if (field->fld_dtype == dtype_blob) { if (field->fld_sub_type_name) { SSHORT blob_sub_type; if (!METD_get_type(dsqlScratch->getTransaction(), reinterpret_cast(field->fld_sub_type_name), "RDB$FIELD_SUB_TYPE", &blob_sub_type)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_dsql_blob_type_unknown) << Arg::Str(((dsql_str*) field->fld_sub_type_name)->str_data)); } field->fld_sub_type = blob_sub_type; } if (field->fld_sub_type > isc_blob_text) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_subtype_for_internal_use)); } if (field->fld_character_set && (field->fld_sub_type == isc_blob_untyped)) field->fld_sub_type = isc_blob_text; if (field->fld_character_set && (field->fld_sub_type != isc_blob_text)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_requires_text)); } if (collation_name && (field->fld_sub_type != isc_blob_text)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_requires_text)); } if (field->fld_sub_type != isc_blob_text) return; } if (field->fld_character_set_id != 0 && !collation_name) { // This field has already been resolved once, and the collation // hasn't changed. Therefore, no need to do it again. return; } if (modifying) { #ifdef DEV_BUILD const dsql_rel* relation = dsqlScratch->relation; #endif const dsql_fld* afield = field->fld_next; USHORT bpc = 0; while (afield) { // The first test is redundant. if (afield != field && afield->fld_relation && afield->fld_name == field->fld_name) { fb_assert(afield->fld_relation == relation || !relation); break; } afield = afield->fld_next; } if (afield) { field->fld_character_set_id = afield->fld_character_set_id; bpc = METD_get_charset_bpc(dsqlScratch->getTransaction(), field->fld_character_set_id); field->fld_collation_id = afield->fld_collation_id; field->fld_ttype = afield->fld_ttype; if (afield->fld_flags & FLD_national) field->fld_flags |= FLD_national; else field->fld_flags &= ~FLD_national; assign_field_length (field, bpc); return; } } if (!(field->fld_character_set || field->fld_character_set_id || // set if a domain (field->fld_flags & FLD_national))) { // Attach the database default character set, if not otherwise specified const dsql_str* dfl_charset = NULL; if (dsqlScratch->getStatement()->getDdlNode() || (dsqlScratch->flags & ( DsqlCompilerScratch::FLAG_FUNCTION | DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_TRIGGER))) { dfl_charset = METD_get_default_charset(dsqlScratch->getTransaction()); } else { USHORT charSet = dsqlScratch->getAttachment()->dbb_attachment->att_charset; if (charSet != CS_NONE) { MetaName charSetName = METD_get_charset_name(dsqlScratch->getTransaction(), charSet); dfl_charset = MAKE_string(charSetName.c_str(), charSetName.length()); } } if (dfl_charset) field->fld_character_set = (dsql_nod*) dfl_charset; else { // If field is not specified with NATIONAL, or CHARACTER SET // treat it as a single-byte-per-character field of character set NONE. assign_field_length(field, 1); field->fld_ttype = 0; if (!collation_name) return; } } const char* charset_name = NULL; if (field->fld_flags & FLD_national) charset_name = NATIONAL_CHARACTER_SET; else if (field->fld_character_set) charset_name = ((dsql_str*) field->fld_character_set)->str_data; // Find an intlsym for any specified character set name & collation name const dsql_intlsym* resolved_type = NULL; if (charset_name) { const dsql_intlsym* resolved_charset = METD_get_charset(dsqlScratch->getTransaction(), (USHORT) strlen(charset_name), charset_name); // Error code -204 (IBM's DB2 manual) is close enough if (!resolved_charset) { // specified character set not found ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_charset_not_found) << Arg::Str(charset_name)); } field->fld_character_set_id = resolved_charset->intlsym_charset_id; resolved_type = resolved_charset; } if (collation_name) { const dsql_intlsym* resolved_collation = METD_get_collation(dsqlScratch->getTransaction(), collation_name->str_data, field->fld_character_set_id); if (!resolved_collation) { MetaName charSetName; if (charset_name) charSetName = charset_name; else { charSetName = METD_get_charset_name(dsqlScratch->getTransaction(), field->fld_character_set_id); } // Specified collation not found ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_not_found) << Arg::Str(collation_name->str_data) << Arg::Str(charSetName)); } // If both specified, must be for same character set // A "literal constant" must be handled (charset as ttype_dynamic) resolved_type = resolved_collation; if ((field->fld_character_set_id != resolved_type->intlsym_charset_id) && (field->fld_character_set_id != ttype_dynamic)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_not_for_charset) << Arg::Str(collation_name->str_data)); } field->fld_explicit_collation = true; } assign_field_length (field, resolved_type->intlsym_bytes_per_char); field->fld_ttype = resolved_type->intlsym_ttype; field->fld_character_set_id = resolved_type->intlsym_charset_id; field->fld_collation_id = resolved_type->intlsym_collate_id; } static void assign_field_length(dsql_fld* field, USHORT bytes_per_char) { /************************************** * * a s s i g n _ f i e l d _ l e n g t h * ************************************** * * Function * We'll see if the field's length fits in the maximum * allowed field, including charset and space for varchars. * Either we raise an error or assign the field's length. * If the charlen comes as zero, we do nothing, although we * know that DYN, MET and DFW will blindly set field length * to zero if they don't catch charlen or another condition. * **************************************/ if (field->fld_character_length) { ULONG field_length = (ULONG) bytes_per_char * field->fld_character_length; if (field->fld_dtype == dtype_varying) { field_length += sizeof(USHORT); } if (field_length > MAX_COLUMN_SIZE) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_imp_exc) << Arg::Gds(isc_field_name) << Arg::Str(field->fld_name)); } field->fld_length = (USHORT) field_length; } } static void define_computed(DsqlCompilerScratch* dsqlScratch, dsql_nod* relation_node, dsql_fld* field, dsql_nod* node) { /************************************** * * d e f i n e _ c o m p u t e d * ************************************** * * Function * Create the ddl to define a computed field * or an expression index. * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); dsql_nod* const saved_ddl_node = statement->getDdlNode(); statement->setDdlNode(node); // Get the table node & set up correct context DDL_reset_context_stack(dsqlScratch); dsc save_desc; // Save the size of the field if it is specified save_desc.dsc_dtype = 0; if (field && field->fld_dtype) { fb_assert(field->fld_dtype <= MAX_UCHAR); save_desc.dsc_dtype = (UCHAR) field->fld_dtype; save_desc.dsc_length = field->fld_length; fb_assert(field->fld_scale <= MAX_SCHAR); save_desc.dsc_scale = (SCHAR) field->fld_scale; save_desc.dsc_sub_type = field->fld_sub_type; field->fld_dtype = 0; field->fld_length = 0; field->fld_scale = 0; field->fld_sub_type = 0; } PASS1_make_context(dsqlScratch, relation_node); dsql_nod* input = PASS1_node(dsqlScratch, node->nod_arg[e_cmp_expr]); // try to calculate size of the computed field. The calculated size // may be ignored, but it will catch self references dsc desc; MAKE_desc(dsqlScratch, &desc, input); // generate the blr expression dsqlScratch->beginBlr(isc_dyn_fld_computed_blr); GEN_hidden_variables(dsqlScratch, true); GEN_expr(dsqlScratch, input); dsqlScratch->endBlr(); if (save_desc.dsc_dtype) { // restore the field size/type overrides field->fld_dtype = save_desc.dsc_dtype; field->fld_length = save_desc.dsc_length; field->fld_scale = save_desc.dsc_scale; if (field->fld_dtype <= dtype_any_text) { field->fld_character_set_id = DSC_GET_CHARSET(&save_desc); field->fld_collation_id= DSC_GET_COLLATE(&save_desc); } else field->fld_sub_type = save_desc.dsc_sub_type; } else if (field) { // use size calculated field->fld_dtype = desc.dsc_dtype; field->fld_length = desc.dsc_length; field->fld_scale = desc.dsc_scale; if (field->fld_dtype <= dtype_any_text) { field->fld_character_set_id = DSC_GET_CHARSET(&desc); field->fld_collation_id= DSC_GET_COLLATE(&desc); } else field->fld_sub_type = desc.dsc_sub_type; } statement->setType(DsqlCompiledStatement::TYPE_DDL); statement->setDdlNode(saved_ddl_node); DDL_reset_context_stack(dsqlScratch); // generate the source text const dsql_str* source = (dsql_str*) node->nod_arg[e_cmp_text]; fb_assert(source->str_length <= MAX_USHORT); dsqlScratch->appendString(isc_dyn_fld_computed_source, source->str_data, (USHORT) source->str_length); } static void define_database(DsqlCompilerScratch* dsqlScratch) { /************************************** * * d e f i n e _ d a t a b a s e * ************************************** * * Function * Create a database. Assumes that * database is created elsewhere with * initial options. Modify the * database using DYN to add the remaining * options. * **************************************/ SLONG start = 0; DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* ddl_node = statement->getDdlNode(); dsqlScratch->appendUChar(isc_dyn_mod_database); // dsqlScratch->appendNumber(isc_dyn_rel_sql_protection, 1); const dsql_nod* elements = ddl_node->nod_arg[e_database_initial_desc]; if (elements) { const dsql_nod* const* ptr = elements->nod_arg; for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ptr++) { const dsql_nod* element = *ptr; switch (element->nod_type) { case nod_file_length: start = (IPTR) element->nod_arg[0] + 1; break; default: break; } } } const dsql_str* name; const dsql_fil* file; elements = ddl_node->nod_arg[e_database_rem_desc]; if (elements) { const dsql_nod* const* ptr = elements->nod_arg; for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ptr++) { const dsql_nod* element = *ptr; switch (element->nod_type) { case nod_difference_file: dsqlScratch->appendNullString(isc_dyn_def_difference, ((dsql_str*) element->nod_arg[0])->str_data); break; case nod_file_desc: file = (dsql_fil*) element->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_def_file, file->fil_name->str_data); start = MAX(start, file->fil_start); dsqlScratch->appendFileStart(start); dsqlScratch->appendFileLength(file->fil_length); dsqlScratch->appendUChar(isc_dyn_end); start += file->fil_length; break; case nod_dfl_charset: name = (dsql_str*) element->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_fld_character_set_name, name->str_data); break; case nod_dfl_collate: name = (dsql_str*) element->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_fld_collation, name->str_data); break; default: break; } } } dsqlScratch->appendUChar(isc_dyn_end); } static SSHORT getBlobFilterSubType(DsqlCompilerScratch* dsqlScratch, const dsql_nod* node) { /******************************************* * * g e t B l o b F i l t e r S u b T y p e * ******************************************* * * Function * get sub_type value from LiteralNode. * **************************************/ const LiteralNode* literal = ExprNode::as(node); fb_assert(literal); switch (literal->litDesc.dsc_dtype) { case dtype_long: return (SSHORT) literal->getSlong(); case dtype_text: break; default: fb_assert(false); return 0; } // fall thru for dtype_text const dsql_str* type_name = reinterpret_cast(node->nod_arg[0]); SSHORT blob_sub_type; if (!METD_get_type(dsqlScratch->getTransaction(), type_name, "RDB$FIELD_SUB_TYPE", &blob_sub_type)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_dsql_blob_type_unknown) << Arg::Str(type_name->str_data)); } return blob_sub_type; } static void define_filter(DsqlCompilerScratch* dsqlScratch) { /************************************** * * d e f i n e _ f i l t e r * ************************************** * * Function * define a filter to the database. * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* filter_node = statement->getDdlNode(); const dsql_nod* const* ptr = filter_node->nod_arg; dsqlScratch->appendNullString(isc_dyn_def_filter, ((dsql_str*) (ptr[e_filter_name]))->str_data); dsqlScratch->appendNumber(isc_dyn_filter_in_subtype, getBlobFilterSubType(dsqlScratch, ptr[e_filter_in_type])); dsqlScratch->appendNumber(isc_dyn_filter_out_subtype, getBlobFilterSubType(dsqlScratch, ptr[e_filter_out_type])); dsqlScratch->appendNullString(isc_dyn_func_entry_point, ((dsql_str*) (ptr[e_filter_entry_pt]))->str_data); dsqlScratch->appendNullString(isc_dyn_func_module_name, ((dsql_str*) (ptr[e_filter_module]))->str_data); dsqlScratch->appendUChar(isc_dyn_end); } static void define_index(DsqlCompilerScratch* dsqlScratch) { /************************************** * * d e f i n e _ i n d e x * ************************************** * * Function * Generate ddl to create an index. * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); dsqlScratch->appendUChar(isc_dyn_begin); const dsql_nod* ddl_node = statement->getDdlNode(); dsql_nod* relation_node = (dsql_nod*) ddl_node->nod_arg[e_idx_table]; const dsql_str* relation_name = (dsql_str*) relation_node->nod_arg[e_rln_name]; dsql_nod* field_list = ddl_node->nod_arg[e_idx_fields]; const dsql_str* index_name = (dsql_str*) ddl_node->nod_arg[e_idx_name]; dsqlScratch->appendNullString(isc_dyn_def_idx, index_name->str_data); dsqlScratch->appendNullString(isc_dyn_rel_name, relation_name->str_data); // go through the fields list, making an index segment for each field, // unless we have a computation, in which case generate an expression index if (field_list->nod_type == nod_list) { const dsql_nod* const* ptr = field_list->nod_arg; const dsql_nod* const* const end = ptr + field_list->nod_count; for (; ptr < end; ptr++) dsqlScratch->appendNullString(isc_dyn_fld_name, ((dsql_str*) (*ptr)->nod_arg[1])->str_data); } else if (field_list->nod_type == nod_def_computed) define_computed(dsqlScratch, relation_node, NULL, field_list); // check for a unique index if (ddl_node->nod_arg[e_idx_unique]) { dsqlScratch->appendNumber(isc_dyn_idx_unique, 1); } if (ddl_node->nod_arg[e_idx_asc_dsc]) { dsqlScratch->appendNumber(isc_dyn_idx_type, 1); } dsqlScratch->appendUChar(isc_dyn_end); // of define index dsqlScratch->appendUChar(isc_dyn_end); // of begin } // ****************************** // d e f i n e _ r o l e // ****************************** // Create a SQL role. // static void define_role(DsqlCompilerScratch* dsqlScratch) { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_str* role_name = (dsql_str*) statement->getDdlNode()->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_def_sql_role, role_name->str_data); dsqlScratch->appendUChar(isc_dyn_end); } // // create a shadow for the database // static void define_shadow(DsqlCompilerScratch* dsqlScratch) { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* shadow_node = statement->getDdlNode(); const dsql_nod* const* ptr = shadow_node->nod_arg; if (!ptr[e_shadow_number]) { post_607(Arg::Gds(isc_dsql_shadow_number_err)); } dsqlScratch->appendNumber(isc_dyn_def_shadow, (SSHORT)(IPTR) (ptr[e_shadow_number])); dsqlScratch->appendNullString(isc_dyn_def_file, ((dsql_str*) (ptr[e_shadow_name]))->str_data); dsqlScratch->appendNumber(isc_dyn_shadow_man_auto, (SSHORT) ExprNode::as(ptr[e_shadow_man_auto])->getSlong()); dsqlScratch->appendNumber(isc_dyn_shadow_conditional, (SSHORT) ExprNode::as(ptr[e_shadow_conditional])->getSlong()); dsqlScratch->appendFileStart(0); SLONG length = (IPTR) ptr[e_shadow_length]; dsqlScratch->appendFileLength(length); dsqlScratch->appendUChar(isc_dyn_end); const dsql_nod* elements = ptr[e_shadow_sec_files]; if (elements) { ptr = elements->nod_arg; for (const dsql_nod* const* const end = ptr + elements->nod_count; ptr < end; ++ptr) { const dsql_nod* element = *ptr; const dsql_fil* file = (dsql_fil*) element->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_def_file, file->fil_name->str_data); if (!length && !file->fil_start) { // Preceding file did not specify length, so %s must include starting page number post_607(Arg::Gds(isc_dsql_file_length_err) << Arg::Str(file->fil_name->str_data)); } const SLONG start = file->fil_start; dsqlScratch->appendFileStart(start); length = file->fil_length; dsqlScratch->appendFileLength(length); dsqlScratch->appendUChar(isc_dyn_end); } } dsqlScratch->appendUChar(isc_dyn_end); } static void define_udf(DsqlCompilerScratch* dsqlScratch) { /************************************** * * d e f i n e _ u d f * ************************************** * * Function * define a udf to the database. * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); SSHORT position, blob_position = -1; dsql_nod* udf_node = statement->getDdlNode(); dsql_nod* arguments = udf_node->nod_arg[e_udf_args]; dsql_nod** ptr = udf_node->nod_arg; const char* udf_name = ((dsql_str*) (ptr[e_udf_name]))->str_data; const dsql_str* func_entry_point_name = reinterpret_cast(ptr[e_udf_entry_pt]); const dsql_str* func_module_name = reinterpret_cast(ptr[e_udf_module]); dsqlScratch->appendNullString(isc_dyn_def_function, udf_name); dsqlScratch->appendNullString(isc_dyn_func_entry_point, func_entry_point_name->str_data); if (func_module_name) dsqlScratch->appendNullString(isc_dyn_func_module_name, func_module_name->str_data); dsql_nod** ret_val_ptr = ptr[e_udf_return_value]->nod_arg; dsql_fld* field = (dsql_fld*) ret_val_ptr[0]; if (field) { // CVC: This is case of "returns [by value|reference]" // Some data types can not be returned as value if (((int) ExprNode::as(ret_val_ptr[1])->getSlong() == FUN_value) && (field->fld_dtype == dtype_text || field->fld_dtype == dtype_varying || field->fld_dtype == dtype_cstring || field->fld_dtype == dtype_blob || field->fld_dtype == dtype_timestamp)) { // Return mode by value not allowed for this data type post_607(Arg::Gds(isc_return_mode_err)); } // For functions returning a blob, coerce return argument position to // be the last parameter. if (field->fld_dtype == dtype_blob) { blob_position = arguments ? arguments->nod_count + 1 : 1; if (blob_position > MAX_UDF_ARGUMENTS) { // External functions can not have more than 10 parameters // Or 9 if the function returns a BLOB post_607(Arg::Gds(isc_extern_func_err)); } dsqlScratch->appendNumber(isc_dyn_func_return_argument, blob_position); } else { dsqlScratch->appendNumber(isc_dyn_func_return_argument, (SSHORT) 0); } position = 0; } else { // CVC: This is case of "returns parameter " position = (SSHORT) ExprNode::as(ret_val_ptr[1])->getSlong(); // Function modifies an argument whose value is the function return value if (!arguments || position > arguments->nod_count || position < 1) { post_607(Arg::Gds(isc_dsql_udf_return_pos_err) << //gds__extern_func_err, Arg::Num(arguments ? arguments->nod_count : 0)); // CVC: We should devise new msg "position should be between 1 and #params"; // here it is: dsql_udf_return_pos_err // External functions can not have more than 10 parameters // Not strictly correct -- return position error } // We'll verify that SCALAR_ARRAY can't be used as a return type. // The support for SCALAR_ARRAY is only for input parameters. const dsql_nod* ret_arg = arguments->nod_arg[position - 1]; const dsql_nod* const* param_node = ret_arg->nod_arg; if (param_node[e_udf_param_type]) { const SSHORT arg_mechanism = (SSHORT) ExprNode::as(param_node[e_udf_param_type])->getSlong(); if (arg_mechanism == FUN_scalar_array) post_607(Arg::Gds(isc_random) << Arg::Str("BY SCALAR_ARRAY can't be used as a return parameter")); } dsqlScratch->appendNumber(isc_dyn_func_return_argument, position); position = 1; } // Now define all the arguments if (!position) { // CVC: This is case of "returns [by value|reference]" if (field->fld_dtype == dtype_blob) { // CVC: I need to test returning blobs by descriptor before allowing the // change there. For now, I ignore the return type specification. const bool free_it = ((SSHORT) ExprNode::as(ret_val_ptr[1])->getSlong() < 0); dsqlScratch->appendNumber(isc_dyn_def_function_arg, blob_position); dsqlScratch->appendNumber(isc_dyn_func_mechanism, (SSHORT)(SLONG) ((free_it ? -1 : 1) * FUN_blob_struct)); // if we have the free_it set then the blob has to be freed on return } else { dsqlScratch->appendNumber(isc_dyn_def_function_arg, (SSHORT) 0); dsqlScratch->appendNumber(isc_dyn_func_mechanism, (SSHORT) ExprNode::as(ret_val_ptr[1])->getSlong()); } dsqlScratch->appendNullString(isc_dyn_function_name, udf_name); DDL_resolve_intl_type(dsqlScratch, field, NULL); put_field(dsqlScratch, field, true); dsqlScratch->appendUChar(isc_dyn_end); position = 1; } fb_assert(position == 1); // CVC: This for all params, including the case of "returns parameter " if (arguments) { ptr = arguments->nod_arg; for (const dsql_nod* const* const end = ptr + arguments->nod_count; ptr < end; ptr++, position++) { if (position > MAX_UDF_ARGUMENTS) { // External functions can not have more than 10 parameters post_607(Arg::Gds(isc_extern_func_err)); } // field = (dsql_fld*) *ptr; dsql_nod** param_node = (*ptr)->nod_arg; field = (dsql_fld*) param_node[e_udf_param_field]; dsqlScratch->appendNumber(isc_dyn_def_function_arg, position); if (param_node[e_udf_param_type]) { const SSHORT arg_mechanism = (SSHORT) ExprNode::as(param_node[e_udf_param_type])->getSlong(); dsqlScratch->appendNumber(isc_dyn_func_mechanism, arg_mechanism); } else if (field->fld_dtype == dtype_blob) { dsqlScratch->appendNumber(isc_dyn_func_mechanism, (SSHORT) FUN_blob_struct); } else { dsqlScratch->appendNumber(isc_dyn_func_mechanism, (SSHORT) FUN_reference); } dsqlScratch->appendNullString(isc_dyn_function_name, udf_name); DDL_resolve_intl_type(dsqlScratch, field, NULL); put_field(dsqlScratch, field, true); dsqlScratch->appendUChar(isc_dyn_end); } } dsqlScratch->appendUChar(isc_dyn_end); } static void generate_dyn(DsqlCompilerScratch* dsqlScratch, dsql_nod* node) { /************************************** * * g e n e r a t e _ d y n * ************************************** * * Functional description * Switch off the type of node to generate a * DYN string. * **************************************/ const dsql_str* string; switch (node->nod_type) { case nod_def_index: define_index(dsqlScratch); break; case nod_del_index: string = (dsql_str*) node->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_delete_idx, string->str_data); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_del_role: string = (dsql_str*) node->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_del_sql_role, string->str_data); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_grant: case nod_revoke: grant_revoke(dsqlScratch); break; case nod_def_role: define_role(dsqlScratch); break; case nod_def_filter: define_filter(dsqlScratch); break; case nod_del_generator: string = (dsql_str*) node->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_delete_generator, string->str_data); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_del_filter: string = (dsql_str*) node->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_delete_filter, string->str_data); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_def_udf: define_udf(dsqlScratch); break; case nod_del_udf: string = (dsql_str*) node->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_delete_function, string->str_data); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_def_shadow: define_shadow(dsqlScratch); break; case nod_del_shadow: dsqlScratch->appendNumber(isc_dyn_delete_shadow, (SSHORT) (IPTR) node->nod_arg[0]); dsqlScratch->appendUChar(isc_dyn_end); break; case nod_mod_database: modify_database(dsqlScratch); break; case nod_def_database: define_database(dsqlScratch); break; case nod_mod_index: modify_index(dsqlScratch); break; case nod_set_statistics: set_statistics(dsqlScratch); break; case nod_mod_udf: modify_udf(dsqlScratch); break; case nod_mod_role: modify_map(dsqlScratch); break; case nod_add_user: define_user(dsqlScratch, isc_dyn_user_add); break; case nod_mod_user: define_user(dsqlScratch, isc_dyn_user_mod); break; case nod_del_user: define_user(dsqlScratch, isc_dyn_user_del); break; default: // CVC: Shouldn't we complain here? break; } } static void grant_revoke(DsqlCompilerScratch* dsqlScratch) { /************************************** * * g r a n t _ r e v o k e * ************************************** * * Functional description * Build DYN string for GRANT/REVOKE statements * **************************************/ const dsql_nod* const* uptr; const dsql_nod* const* uend; SSHORT option = 0; // no grant/admin option DsqlCompiledStatement* statement = dsqlScratch->getStatement(); dsql_nod* ddl_node = statement->getDdlNode(); const dsql_nod* privs = ddl_node->nod_arg[e_grant_privs]; const dsql_nod* table = ddl_node->nod_arg[e_grant_table]; if ((ddl_node->nod_type == nod_revoke) && !privs && !table) // ALL ON ALL { dsqlScratch->appendUChar(isc_dyn_begin); const dsql_nod* users = ddl_node->nod_arg[e_grant_users]; uend = users->nod_arg + users->nod_count; for (uptr = users->nod_arg; uptr < uend; ++uptr) { dsqlScratch->appendUChar(isc_dyn_revoke_all); put_user_grant(dsqlScratch, *uptr); dsqlScratch->appendUChar(isc_dyn_end); } dsqlScratch->appendUChar(isc_dyn_end); return; } bool process_grant_role = false; if (privs->nod_arg[0] != NULL) { if (privs->nod_arg[0]->nod_type == nod_role_name) { process_grant_role = true; } } dsqlScratch->appendUChar(isc_dyn_begin); if (!process_grant_role) { const dsql_nod* users = ddl_node->nod_arg[e_grant_users]; if (ddl_node->nod_arg[e_grant_grant]) { option = 1; // with grant option } uend = users->nod_arg + users->nod_count; for (uptr = users->nod_arg; uptr < uend; ++uptr) { modify_privileges(dsqlScratch, ddl_node->nod_type, option, privs, table, *uptr, ddl_node->nod_arg[e_grant_grantor]); } } else { const dsql_nod* role_list = ddl_node->nod_arg[0]; const dsql_nod* users = ddl_node->nod_arg[1]; if (ddl_node->nod_arg[3]) { option = 2; // with admin option } const dsql_nod* const* role_end = role_list->nod_arg + role_list->nod_count; for (const dsql_nod* const* role_ptr = role_list->nod_arg; role_ptr < role_end; ++role_ptr) { uend = users->nod_arg + users->nod_count; for (uptr = users->nod_arg; uptr < uend; ++uptr) { process_role_nm_list(dsqlScratch, option, *uptr, *role_ptr, ddl_node->nod_type, ddl_node->nod_arg[e_grant_grantor]); } } } dsqlScratch->appendUChar(isc_dyn_end); } static void modify_database( DsqlCompilerScratch* dsqlScratch) { /************************************** * * m o d i f y _ d a t a b a s e * ************************************** * * Function * Modify a database. * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* ddl_node = statement->getDdlNode(); dsqlScratch->appendUChar(isc_dyn_mod_database); // dsqlScratch->appendNumber(isc_dyn_rel_sql_protection, 1); bool drop_difference = false; const dsql_nod* elements = ddl_node->nod_arg[e_adb_all]; const dsql_nod* const* end = elements->nod_arg + elements->nod_count; const dsql_nod* const* ptr; for (ptr = elements->nod_arg; ptr < end; ptr++) { const dsql_nod* element = *ptr; if (element->nod_type == nod_drop_difference) drop_difference = true; } if (drop_difference) dsqlScratch->appendUChar(isc_dyn_drop_difference); SLONG start = 0; elements = ddl_node->nod_arg[e_adb_all]; end = elements->nod_arg + elements->nod_count; for (ptr = elements->nod_arg; ptr < end; ptr++) { const dsql_fil* file; const dsql_nod* element = *ptr; switch (element->nod_type) { case nod_file_desc: file = (dsql_fil*) element->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_def_file, file->fil_name->str_data); start = MAX(start, file->fil_start); dsqlScratch->appendFileStart(start); dsqlScratch->appendFileLength(file->fil_length); dsqlScratch->appendUChar(isc_dyn_end); start += file->fil_length; break; case nod_difference_file: dsqlScratch->appendNullString(isc_dyn_def_difference, ((dsql_str*) element->nod_arg[0])->str_data); break; case nod_begin_backup: dsqlScratch->appendUChar(isc_dyn_begin_backup); break; case nod_end_backup: dsqlScratch->appendUChar(isc_dyn_end_backup); break; case nod_dfl_charset: dsqlScratch->appendNullString(isc_dyn_fld_character_set_name, ((dsql_str*) element->nod_arg[0])->str_data); break; default: break; } } dsqlScratch->appendUChar(isc_dyn_end); } static void modify_index( DsqlCompilerScratch* dsqlScratch) { /************************************** * * m o d i f y _ i n d e x * ************************************** * * Function * Alter an index (only active or inactive for now) * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); dsql_nod* ddl_node = statement->getDdlNode(); dsql_nod* index_node = ddl_node->nod_arg[e_alt_index]; const dsql_str* index_name = (dsql_str*) index_node->nod_arg[e_alt_idx_name]; dsqlScratch->appendNullString(isc_dyn_mod_idx, index_name->str_data); if (index_node->nod_type == nod_idx_active) dsqlScratch->appendNumber(isc_dyn_idx_inactive, 0); else if (index_node->nod_type == nod_idx_inactive) dsqlScratch->appendNumber(isc_dyn_idx_inactive, 1); dsqlScratch->appendUChar(isc_dyn_end); } static void put_user_grant(DsqlCompilerScratch* dsqlScratch, const dsql_nod* user) { /************************************** * * p u t _ u s e r _ g r a n t * ************************************** * * Functional description * Stuff a user/role/obj option in grant/revoke * **************************************/ const dsql_str* name = (dsql_str*) user->nod_arg[0]; switch (user->nod_type) { case nod_user_group: // GRANT priv ON tbl TO GROUP unix_group dsqlScratch->appendNullString(isc_dyn_grant_user_group, name->str_data); break; case nod_user_name: if (user->nod_count == 2) dsqlScratch->appendNullString(isc_dyn_grant_user_explicit, name->str_data); else dsqlScratch->appendNullString(isc_dyn_grant_user, name->str_data); break; case nod_package_obj: dsqlScratch->appendNullString(isc_dyn_grant_package, name->str_data); break; case nod_proc_obj: dsqlScratch->appendNullString(isc_dyn_grant_proc, name->str_data); break; case nod_func_obj: dsqlScratch->appendNullString(isc_dyn_grant_func, name->str_data); break; case nod_trig_obj: dsqlScratch->appendNullString(isc_dyn_grant_trig, name->str_data); break; case nod_view_obj: dsqlScratch->appendNullString(isc_dyn_grant_view, name->str_data); break; case nod_role_name: dsqlScratch->appendNullString(isc_dyn_grant_role, name->str_data); break; default: // CVC: Here we should complain: DYN doesn't check parameters // and it will write trash in rdb$user_privileges. We probably // should complain in most cases when "name" is blank, too. break; } } static void modify_privilege(DsqlCompilerScratch* dsqlScratch, NOD_TYPE type, SSHORT option, const UCHAR* privs, const dsql_nod* table, const dsql_nod* user, const dsql_nod* grantor, const dsql_str* field_name) { /************************************** * * m o d i f y _ p r i v i l e g e * ************************************** * * Functional description * Stuff a single grant/revoke verb and all its options. * **************************************/ if (type == nod_grant) dsqlScratch->appendUChar(isc_dyn_grant); else dsqlScratch->appendUChar(isc_dyn_revoke); // stuff the privileges string SSHORT priv_count = 0; dsqlScratch->appendUShort(0); for (; *privs; privs++) { priv_count++; dsqlScratch->appendUChar(*privs); } UCHAR* dynsave = dsqlScratch->getBlrData().end(); for (SSHORT i = priv_count + 2; i; i--) --dynsave; *dynsave++ = (UCHAR) priv_count; *dynsave = (UCHAR) (priv_count >> 8); UCHAR dynVerb = 0; switch (table->nod_type) { case nod_procedure_name: dynVerb = isc_dyn_prc_name; break; case nod_function_name: dynVerb = isc_dyn_fun_name; break; case nod_package_name: dynVerb = isc_dyn_pkg_name; break; default: dynVerb = isc_dyn_rel_name; } const dsql_str* name = (dsql_str*) table->nod_arg[0]; dsqlScratch->appendNullString(dynVerb, name->str_data); put_user_grant(dsqlScratch, user); if (field_name) dsqlScratch->appendNullString(isc_dyn_fld_name, field_name->str_data); if (option) dsqlScratch->appendNumber(isc_dyn_grant_options, option); put_grantor(dsqlScratch, grantor); dsqlScratch->appendUChar(isc_dyn_end); } static char modify_privileges(DsqlCompilerScratch* dsqlScratch, NOD_TYPE type, SSHORT option, const dsql_nod* privs, const dsql_nod* table, const dsql_nod* user, const dsql_nod* grantor) { /************************************** * * m o d i f y _ p r i v i l e g e s * ************************************** * * Functional description * Return a char indicating the privilege to be modified * **************************************/ char privileges[10]; const char* p = 0; char* q; const dsql_nod* fields; const dsql_nod* const* ptr; const dsql_nod* const* end; switch (privs->nod_type) { case nod_all: p = "A"; break; case nod_select: return 'S'; case nod_execute: return 'X'; case nod_insert: return 'I'; case nod_references: case nod_update: p = (privs->nod_type == nod_references) ? "R" : "U"; fields = privs->nod_arg[0]; if (!fields) { return *p; } for (ptr = fields->nod_arg, end = ptr + fields->nod_count; ptr < end; ptr++) { modify_privilege(dsqlScratch, type, option, reinterpret_cast(p), table, user, grantor, reinterpret_cast((*ptr)->nod_arg[1])); } return 0; case nod_delete: return 'D'; case nod_list: p = q = privileges; for (ptr = privs->nod_arg, end = ptr + privs->nod_count; ptr < end; ptr++) { *q = modify_privileges(dsqlScratch, type, option, *ptr, table, user, grantor); if (*q) { q++; } } *q = 0; break; default: break; } if (*p) { modify_privilege(dsqlScratch, type, option, reinterpret_cast(p), table, user, grantor, 0); } return 0; } // ******************* // m o d i f y _ u d f // ******************* // Allow the user to change the entry point or module name. // Useful when there are dependencies on the udf, so it cannot be dropped. static void modify_udf(DsqlCompilerScratch* dsqlScratch) { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* node = statement->getDdlNode(); fb_assert(node->nod_type == nod_mod_udf); const dsql_str* obj_name = (dsql_str*) node->nod_arg[e_mod_udf_name]; if (!node->nod_arg[e_mod_udf_entry_pt] && !node->nod_arg[e_mod_udf_module]) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << // Unexpected end of command Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) << Arg::Num(node->nod_column + obj_name->str_length)); // + strlen("FUNCTION") } dsqlScratch->appendNullString(isc_dyn_mod_function, obj_name->str_data); const dsql_str* entry_point_name = (dsql_str*) node->nod_arg[e_mod_udf_entry_pt]; if (entry_point_name) dsqlScratch->appendNullString(isc_dyn_func_entry_point, entry_point_name->str_data); const dsql_str* module_name = (dsql_str*) node->nod_arg[e_mod_udf_module]; if (module_name) dsqlScratch->appendNullString(isc_dyn_func_module_name, module_name->str_data); dsqlScratch->appendUChar(isc_dyn_end); } // ******************* // m o d i f y _ m a p // ******************* // Allow the user to establish/drop the mapping between OS security object and the role static void modify_map(DsqlCompilerScratch* dsqlScratch) { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* node = statement->getDdlNode(); fb_assert(node->nod_type == nod_mod_role); const dsql_str* ds = (dsql_str*) node->nod_arg[e_mod_role_os_name]; fb_assert(ds || ExprNode::as(node->nod_arg[e_mod_role_action])->getSlong() == isc_dyn_automap_role || ExprNode::as(node->nod_arg[e_mod_role_action])->getSlong() == isc_dyn_autounmap_role); dsqlScratch->appendNullString(isc_dyn_mapping, ds ? ds->str_data : ""); ds = (dsql_str*) node->nod_arg[e_mod_role_db_name]; fb_assert(ds); dsqlScratch->appendNullString( ExprNode::as(node->nod_arg[e_mod_role_action])->getSlong(), ds->str_data); dsqlScratch->appendUChar(isc_dyn_end); } // ********************* // d e f i n e _ u s e r // ********************* // Support SQL operator create/alter/drop user static void define_user(DsqlCompilerScratch* dsqlScratch, UCHAR op) { DsqlCompiledStatement* statement = dsqlScratch->getStatement(); dsqlScratch->appendUChar(isc_dyn_user); const dsql_nod* node = statement->getDdlNode(); int argCount = 0; for (int i = 0; i < node->nod_count; ++i) { const dsql_str* ds = (dsql_str*) node->nod_arg[i]; if (! ds) { if (i == e_user_name || (i == e_user_passwd && op == isc_dyn_user_add)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << // Unexpected end of command Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) << Arg::Num(node->nod_column)); } continue; } ++argCount; switch (i) { case e_user_name: dsqlScratch->appendNullString(op, ds->str_data); break; case e_user_passwd: dsqlScratch->appendNullString(isc_dyn_user_passwd, ds->str_data); break; case e_user_first: dsqlScratch->appendNullString(isc_dyn_user_first, ds->str_data); break; case e_user_middle: dsqlScratch->appendNullString(isc_dyn_user_middle, ds->str_data); break; case e_user_last: dsqlScratch->appendNullString(isc_dyn_user_last, ds->str_data); break; case e_user_admin: dsqlScratch->appendNullString(isc_dyn_user_admin, ds->str_data); break; } } if (argCount < 2 && op != isc_dyn_user_del) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << // Unexpected end of command Arg::Gds(isc_command_end_err2) << Arg::Num(node->nod_line) << Arg::Num(node->nod_column)); } dsqlScratch->appendUChar(isc_user_end); dsqlScratch->appendUChar(isc_dyn_end); } static void process_role_nm_list(DsqlCompilerScratch* dsqlScratch, SSHORT option, const dsql_nod* user_ptr, const dsql_nod* role_ptr, NOD_TYPE type, const dsql_nod* grantor) { /************************************** * * p r o c e s s _ r o l e _ n m _ l i s t * ************************************** * * Functional description * Build req_blr for grant & revoke role stmt * **************************************/ if (type == nod_grant) dsqlScratch->appendUChar(isc_dyn_grant); else dsqlScratch->appendUChar(isc_dyn_revoke); dsqlScratch->appendUShort(1); dsqlScratch->appendUChar('M'); const dsql_str* role_nm = (dsql_str*) role_ptr->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_sql_role_name, role_nm->str_data); const dsql_str* user_nm = (dsql_str*) user_ptr->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_grant_user, user_nm->str_data); if (option) { dsqlScratch->appendNumber(isc_dyn_grant_admin_options, option); } put_grantor(dsqlScratch, grantor); dsqlScratch->appendUChar(isc_dyn_end); } static void put_grantor(DsqlCompilerScratch* dsqlScratch, const dsql_nod* grantor) { /************************************** * * p u t _ g r a n t o r * ************************************** * * Function * Write out grantor for grant / revoke. * **************************************/ if (grantor) { fb_assert(grantor->nod_type == nod_user_name); const dsql_str* name = (const dsql_str*) grantor->nod_arg[0]; dsqlScratch->appendNullString(isc_dyn_grant_grantor, name->str_data); } } static void put_field( DsqlCompilerScratch* dsqlScratch, dsql_fld* field, bool udf_flag) { /************************************** * * p u t _ f i e l d * ************************************** * * Function * Emit dyn which describes a field data type. * This field could be a column, a procedure input, * or a procedure output. * **************************************/ if (field->fld_not_nullable) dsqlScratch->appendUChar(isc_dyn_fld_not_null); if (field->fld_type_of_name.hasData()) { if (field->fld_source.hasData()) { dsqlScratch->appendString(isc_dyn_fld_source, field->fld_source); dsqlScratch->appendString(isc_dyn_fld_name, field->fld_type_of_name); dsqlScratch->appendNullString(isc_dyn_rel_name, field->fld_type_of_table->str_data); } else dsqlScratch->appendString(isc_dyn_fld_source, field->fld_type_of_name); if (field->fld_explicit_collation) dsqlScratch->appendNumber(isc_dyn_fld_collation, field->fld_collation_id); return; } dsqlScratch->appendNumber(isc_dyn_fld_type, blr_dtypes[field->fld_dtype]); if (field->fld_dtype == dtype_blob) { dsqlScratch->appendNumber(isc_dyn_fld_sub_type, field->fld_sub_type); dsqlScratch->appendNumber(isc_dyn_fld_scale, 0); if (!udf_flag) { if (!field->fld_seg_length) { field->fld_seg_length = DEFAULT_BLOB_SEGMENT_SIZE; } dsqlScratch->appendNumber(isc_dyn_fld_segment_length, field->fld_seg_length); } else { dsqlScratch->appendNumber(isc_dyn_fld_length, sizeof(ISC_QUAD)); } if (field->fld_sub_type == isc_blob_text) { dsqlScratch->appendNumber(isc_dyn_fld_character_set, field->fld_character_set_id); dsqlScratch->appendNumber(isc_dyn_fld_collation, field->fld_collation_id); } } else if (field->fld_dtype <= dtype_any_text) { dsqlScratch->appendNumber(isc_dyn_fld_sub_type, field->fld_sub_type); dsqlScratch->appendNumber(isc_dyn_fld_scale, 0); if (field->fld_dtype == dtype_varying) { // CVC: Fix the assertion fb_assert((field->fld_length) <= MAX_SSHORT); dsqlScratch->appendNumber(isc_dyn_fld_length, (SSHORT) (field->fld_length - sizeof(USHORT))); } else { dsqlScratch->appendNumber(isc_dyn_fld_length, field->fld_length); } dsqlScratch->appendNumber(isc_dyn_fld_char_length, field->fld_character_length); dsqlScratch->appendNumber(isc_dyn_fld_character_set, field->fld_character_set_id); if (!udf_flag) dsqlScratch->appendNumber(isc_dyn_fld_collation, field->fld_collation_id); } else { dsqlScratch->appendNumber(isc_dyn_fld_scale, field->fld_scale); dsqlScratch->appendNumber(isc_dyn_fld_length, field->fld_length); if (DTYPE_IS_EXACT(field->fld_dtype)) { dsqlScratch->appendNumber(isc_dyn_fld_precision, field->fld_precision); dsqlScratch->appendNumber(isc_dyn_fld_sub_type, field->fld_sub_type); } } } void DDL_reset_context_stack(DsqlCompilerScratch* dsqlScratch) { /************************************** * * D D L _ r e s e t _ c o n t e x t _ s t a c k * ************************************** * * Function * Get rid of any predefined contexts created * for a view or trigger definition. * Also reset hidden variables. * **************************************/ dsqlScratch->context->clear(); dsqlScratch->contextNumber = 0; dsqlScratch->derivedContextNumber = 0; dsqlScratch->hiddenVarsNumber = 0; dsqlScratch->hiddenVars.clear(); } static void set_statistics(DsqlCompilerScratch* dsqlScratch) { /************************************** * * s e t _ s t a t i s t i c s * ************************************** * * Function * Alter an index/.. statistics * **************************************/ DsqlCompiledStatement* statement = dsqlScratch->getStatement(); const dsql_nod* ddl_node = statement->getDdlNode(); const dsql_str* index_name = (dsql_str*) ddl_node->nod_arg[e_stat_name]; dsqlScratch->appendNullString(isc_dyn_mod_idx, index_name->str_data); dsqlScratch->appendUChar(isc_dyn_idx_statistic); dsqlScratch->appendUChar(isc_dyn_end); } // post very often used error - avoid code duplication static void post_607(const Arg::StatusVector& v) { Arg::Gds err(isc_sqlerr); err << Arg::Num(-607) << Arg::Gds(isc_dsql_command_err); err.append(v); ERRD_post(err); }