/* * 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/ExprNodes.h" #include "../jrd/ibase.h" #include "../jrd/Attachment.h" #include "../jrd/RecordSourceNodes.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/met_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 Firebird; static void assign_field_length(dsql_fld*, USHORT); static void post_607(const Arg::StatusVector& v); ///const int DEFAULT_BLOB_SEGMENT_SIZE = 80; // bytes // Determine whether ids or names should be referenced when generating blr for fields and relations. bool DDL_ids(const DsqlCompilerScratch* scratch) { return !(scratch->flags & DsqlCompilerScratch::FLAG_DDL); } void DDL_resolve_intl_type(DsqlCompilerScratch* dsqlScratch, dsql_fld* field, const MetaName& collation_name, bool modifying) { /************************************** * * D D L _ r e s o l v e _ i n t l _ t y p e * ************************************** * * 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 length 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->typeOfName.hasData()) { if (field->typeOfTable.hasData()) { dsql_rel* relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, field->typeOfTable.c_str()); const dsql_fld* fld = NULL; if (relation) { const MetaName fieldName(field->typeOfName); for (fld = relation->rel_fields; fld; fld = fld->fld_next) { if (fieldName == fld->fld_name) { field->dimensions = fld->dimensions; field->fieldSource = fld->fieldSource; field->length = fld->length; field->scale = fld->scale; field->subType = fld->subType; field->charSetId = fld->charSetId; field->collationId = fld->collationId; field->charLength = fld->charLength; field->flags = fld->flags; field->dtype = fld->dtype; field->segLength = fld->segLength; 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->typeOfName) << field->typeOfTable); } } else { if (!METD_get_domain(dsqlScratch->getTransaction(), field, field->typeOfName)) { // Specified domain or source field does not exist post_607(Arg::Gds(isc_dsql_domain_not_found) << Arg::Str(field->typeOfName)); } } if (field->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->dtype <= dtype_any_text || (field->dtype == dtype_blob && field->subType == isc_blob_text)) { field->charSet = METD_get_charset_name(dsqlScratch->getTransaction(), field->charSetId); } } if ((field->dtype > dtype_any_text) && field->dtype != dtype_blob) { if (field->charSet.hasData() || collation_name.hasData() || (field->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->dtype == dtype_blob) { if (field->subTypeName.hasData()) { SSHORT blob_sub_type; if (!METD_get_type(dsqlScratch->getTransaction(), field->subTypeName, "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(field->subTypeName)); } field->subType = blob_sub_type; } if (field->subType > 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->charSet.hasData() && (field->subType == isc_blob_untyped)) field->subType = isc_blob_text; if (field->charSet.hasData() && (field->subType != 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.hasData() && (field->subType != 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->subType != isc_blob_text) return; } if (field->charSetId != 0 && collation_name.isEmpty()) { // This field has already been resolved once, and the collation // hasn't changed. Therefore, no need to do it again. return; } if (modifying) { 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 == dsqlScratch->relation || !dsqlScratch->relation); break; } afield = afield->fld_next; } if (afield) { field->charSetId = afield->charSetId; bpc = METD_get_charset_bpc(dsqlScratch->getTransaction(), field->charSetId); field->collationId = afield->collationId; field->textType = afield->textType; if (afield->flags & FLD_national) field->flags |= FLD_national; else field->flags &= ~FLD_national; assign_field_length(field, bpc); return; } } if (!(field->charSet.hasData() || field->charSetId || // set if a domain (field->flags & FLD_national))) { // Attach the database default character set, if not otherwise specified MetaName defaultCharSet; if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL) defaultCharSet = METD_get_default_charset(dsqlScratch->getTransaction()); else { USHORT charSet = dsqlScratch->getAttachment()->dbb_attachment->att_charset; if (charSet != CS_NONE) defaultCharSet = METD_get_charset_name(dsqlScratch->getTransaction(), charSet); } if (defaultCharSet.hasData()) field->charSet = defaultCharSet; 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->textType = 0; if (collation_name.isEmpty()) return; } } MetaName charset_name; if (field->flags & FLD_national) charset_name = NATIONAL_CHARACTER_SET; else if (field->charSet.hasData()) charset_name = field->charSet; // Find an intlsym for any specified character set name & collation name const dsql_intlsym* resolved_type = NULL; if (charset_name.hasData()) { const dsql_intlsym* resolved_charset = METD_get_charset(dsqlScratch->getTransaction(), (USHORT) charset_name.length(), charset_name.c_str()); // 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->charSetId = resolved_charset->intlsym_charset_id; resolved_type = resolved_charset; } if (collation_name.hasData()) { const dsql_intlsym* resolved_collation = METD_get_collation(dsqlScratch->getTransaction(), collation_name, field->charSetId); if (!resolved_collation) { MetaName charSetName; if (charset_name.hasData()) charSetName = charset_name; else { charSetName = METD_get_charset_name(dsqlScratch->getTransaction(), field->charSetId); } // Specified collation not found ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << ///Arg::Gds(isc_dsql_datatype_err) << // (too large status vector) Arg::Gds(isc_collation_not_found) << collation_name << 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->charSetId != resolved_type->intlsym_charset_id) && (field->charSetId != ttype_dynamic)) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_datatype_err) << Arg::Gds(isc_collation_not_for_charset) << collation_name); } field->explicitCollation = true; } assign_field_length(field, resolved_type->intlsym_bytes_per_char); field->textType = resolved_type->intlsym_ttype; field->charSetId = resolved_type->intlsym_charset_id; field->collationId = 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->charLength) { ULONG field_length = (ULONG) bytes_per_char * field->charLength; if (field->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->length = (USHORT) field_length; } } // 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); }