From 4be766b8c8e42a4dfe8a2a6f74e85804de88d153 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes Date: Tue, 11 Jul 2017 13:01:38 +0000 Subject: [PATCH] Improvement CORE-5380 - Allow subroutines to call others subroutines and themself recursively. --- CHANGELOG.md | 4 + doc/sql.extensions/README.subroutines.txt | 79 +++++- lang_helpers/gds_codes.ftn | 16 ++ lang_helpers/gds_codes.pas | 16 ++ src/dsql/DsqlCompilerScratch.cpp | 75 ++++++ src/dsql/DsqlCompilerScratch.h | 50 +--- src/dsql/ExprNodes.cpp | 13 +- src/dsql/PackageNodes.epp | 172 +------------ src/dsql/StmtNodes.cpp | 301 +++++++++++++++------- src/dsql/StmtNodes.h | 12 + src/dsql/dsql.h | 190 ++++++++++++++ src/dsql/parse-conflicts.txt | 2 +- src/dsql/parse.y | 134 +++++++--- src/dsql/pass1.cpp | 10 +- src/include/gen/codetext.h | 8 + src/include/gen/iberror.h | 20 +- src/include/gen/msgs.h | 12 +- src/include/gen/sql_code.h | 8 + src/include/gen/sql_state.h | 8 + src/jrd/RecordSourceNodes.cpp | 10 +- src/jrd/exe.h | 4 +- src/msgs/facilities2.sql | 2 +- src/msgs/messages2.sql | 12 +- src/msgs/system_errors2.sql | 8 + 24 files changed, 807 insertions(+), 359 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index def6b77e5e..0c6f262c6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,10 @@ Reference(s): [doc/sql.extensions/README.identity_columns](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.identity_columns.txt) Contributor(s): Adriano dos Santos Fernandes +* [CORE-5380](http://tracker.firebirdsql.org/browse/CORE-5380): Allow subroutines to call others subroutines and themself recursively + Reference(s): [doc/sql.extensions/README.subroutines.txt](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.subroutines.txt) + Contributor(s): Adriano dos Santos Fernandes + * [CORE-5119](http://tracker.firebirdsql.org/browse/CORE-5119): Support autocommit mode in SET TRANSACTION statement Contributor(s): Dmitry Yemanov diff --git a/doc/sql.extensions/README.subroutines.txt b/doc/sql.extensions/README.subroutines.txt index 7077d9242b..5de6db5494 100644 --- a/doc/sql.extensions/README.subroutines.txt +++ b/doc/sql.extensions/README.subroutines.txt @@ -7,7 +7,7 @@ Author: Description: Support for PSQL subroutines (functions and procedures) inside functions, procedures, triggers - and EXECUTE BLOCK. Subroutines are declared in the main routine and may be used from there. + and EXECUTE BLOCK. Subroutines are declared in the main routine and may be used from there or others subroutines. Syntax: ::= @@ -15,7 +15,21 @@ Syntax: | DECLARE [VARIABLE] CURSOR FOR (); | - DECLARE FUNCTION RETURNS + + | + + + ::= + DECLARE FUNCTION [ () ] + RETURNS + [ [ NOT ] DETERMINISTIC ] ; + | + DECLARE PROCEDURE [ () ] [ RETURNS () ] ; + + ::= + DECLARE FUNCTION [ () ] + RETURNS + [ [ NOT ] DETERMINISTIC ] AS ... BEGIN @@ -32,8 +46,11 @@ Syntax: Limitations: 1) Subroutines may not be nested in another subroutine. They are only supported in the main routine. - 2) Currently, a subroutine may not directly access or use variables, cursors or another - subroutines of the main statements. This may be allowed in the future. + 2) Currently, a subroutine may not directly access or use variables or cursors of the + main statements. This may be allowed in the future. + +Notes: + 1) Starting in FB 4, subroutines may be recursive or call others subroutines. Examples: set term !; @@ -89,3 +106,57 @@ Examples: end! select func1(5, 6) from rdb$database! + + + -- 3) Recursive sub-function in EXECUTE BLOCK. + + execute block returns (i integer, o integer) + as + -- Recursive function without forward declaration. + declare function fibonacci(n integer) returns integer + as + begin + if (n = 0 or n = 1) then + return n; + else + return fibonacci(n - 1) + fibonacci(n - 2); + end + begin + i = 0; + + while (i < 10) + do + begin + o = fibonacci(i); + suspend; + i = i + 1; + end + end! + + + -- 4) Example with forward declaration and parameter with default values. + + execute block returns (o integer) + as + -- Forward declaration of P1. + declare procedure p1(i integer = 1) returns (o integer); + + -- Forward declaration of P2. + declare procedure p2(i integer) returns (o integer); + + -- Implementation of P1 should not re-declare parameter default value. + declare procedure p1(i integer) returns (o integer) + as + begin + execute procedure p2(i) returning_values o; + end + + declare procedure p2(i integer) returns (o integer) + as + begin + o = i; + end + begin + execute procedure p1 returning_values o; + suspend; + end! diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index e570469fa0..6c6301d9d9 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1698,6 +1698,22 @@ C -- PARAMETER (GDS__decfloat_overflow = 335545142) INTEGER*4 GDS__decfloat_underflow PARAMETER (GDS__decfloat_underflow = 335545143) + INTEGER*4 GDS__subfunc_notdef + PARAMETER (GDS__subfunc_notdef = 335545144) + INTEGER*4 GDS__subproc_notdef + PARAMETER (GDS__subproc_notdef = 335545145) + INTEGER*4 GDS__subfunc_signat + PARAMETER (GDS__subfunc_signat = 335545146) + INTEGER*4 GDS__subproc_signat + PARAMETER (GDS__subproc_signat = 335545147) + INTEGER*4 GDS__subfunc_defvaldecl + PARAMETER (GDS__subfunc_defvaldecl = 335545148) + INTEGER*4 GDS__subproc_defvaldecl + PARAMETER (GDS__subproc_defvaldecl = 335545149) + INTEGER*4 GDS__subfunc_not_impl + PARAMETER (GDS__subfunc_not_impl = 335545150) + INTEGER*4 GDS__subproc_not_impl + PARAMETER (GDS__subproc_not_impl = 335545151) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index b2a8f54aca..ca96b073d1 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1693,6 +1693,22 @@ const gds_decfloat_overflow = 335545142; isc_decfloat_underflow = 335545143; gds_decfloat_underflow = 335545143; + isc_subfunc_notdef = 335545144; + gds_subfunc_notdef = 335545144; + isc_subproc_notdef = 335545145; + gds_subproc_notdef = 335545145; + isc_subfunc_signat = 335545146; + gds_subfunc_signat = 335545146; + isc_subproc_signat = 335545147; + gds_subproc_signat = 335545147; + isc_subfunc_defvaldecl = 335545148; + gds_subfunc_defvaldecl = 335545148; + isc_subproc_defvaldecl = 335545149; + gds_subproc_defvaldecl = 335545149; + isc_subfunc_not_impl = 335545150; + gds_subfunc_not_impl = 335545150; + isc_subproc_not_impl = 335545151; + gds_subproc_not_impl = 335545151; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index 7d6800e56f..c91e8d1cbd 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -320,6 +320,37 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT else fb_assert(false); } + + if (!(flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE)) + { + // Check not implemented sub-functions. + + GenericMap >::ConstAccessor funcAccessor(&subFunctions); + + for (bool found = funcAccessor.getFirst(); found; found = funcAccessor.getNext()) + { + if (!funcAccessor.current()->second->dsqlBlock) + { + status_exception::raise( + Arg::Gds(isc_subfunc_not_impl) << + funcAccessor.current()->first.c_str()); + } + } + + // Check not implemented sub-procedures. + + GenericMap >::ConstAccessor procAccessor(&subProcedures); + + for (bool found = procAccessor.getFirst(); found; found = procAccessor.getNext()) + { + if (!procAccessor.current()->second->dsqlBlock) + { + status_exception::raise( + Arg::Gds(isc_subproc_not_impl) << + procAccessor.current()->first.c_str()); + } + } + } } // Write out local variable field data type. @@ -586,6 +617,50 @@ void DsqlCompilerScratch::checkUnusedCTEs() const } } +DeclareSubFuncNode* DsqlCompilerScratch::getSubFunction(const Firebird::MetaName& name) +{ + DeclareSubFuncNode* subFunc = NULL; + subFunctions.get(name, subFunc); + + if (!subFunc && mainScratch) + subFunc = mainScratch->getSubFunction(name); + + return subFunc; +} + +void DsqlCompilerScratch::putSubFunction(DeclareSubFuncNode* subFunc, bool replace) +{ + if (!replace && subFunctions.exist(subFunc->name)) + { + status_exception::raise( + Arg::Gds(isc_dsql_duplicate_spec) << subFunc->name); + } + + subFunctions.put(subFunc->name, subFunc); +} + +DeclareSubProcNode* DsqlCompilerScratch::getSubProcedure(const Firebird::MetaName& name) +{ + DeclareSubProcNode* subProc = NULL; + subProcedures.get(name, subProc); + + if (!subProc && mainScratch) + subProc = mainScratch->getSubProcedure(name); + + return subProc; +} + +void DsqlCompilerScratch::putSubProcedure(DeclareSubProcNode* subProc, bool replace) +{ + if (!replace && subProcedures.exist(subProc->name)) + { + status_exception::raise( + Arg::Gds(isc_dsql_duplicate_spec) << subProc->name); + } + + subProcedures.put(subProc->name, subProc); +} + // Process derived table which can be recursive CTE. // If it is non-recursive return input node unchanged. // If it is recursive return new derived table which is an union of union of anchor (non-recursive) diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index c6755ef637..a8c91b243c 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -70,7 +70,7 @@ public: public: DsqlCompilerScratch(MemoryPool& p, dsql_dbb* aDbb, jrd_tra* aTransaction, - DsqlCompiledStatement* aStatement) + DsqlCompiledStatement* aStatement, DsqlCompilerScratch* aMainScratch = NULL) : BlrDebugWriter(p), dbb(aDbb), transaction(aTransaction), @@ -79,7 +79,6 @@ public: nestingLevel(0), ports(p), relation(NULL), - procedure(NULL), mainContext(p), context(&mainContext), unionContext(p), @@ -115,6 +114,7 @@ public: ctes(p), cteAliases(p), psql(false), + mainScratch(aMainScratch), subFunctions(p), subProcedures(p) { @@ -247,43 +247,11 @@ public: bool isPsql() const { return psql; } void setPsql(bool value) { psql = value; } - dsql_udf* getSubFunction(const Firebird::MetaName& name) - { - dsql_udf* subFunc = NULL; - subFunctions.get(name, subFunc); - return subFunc; - } + DeclareSubFuncNode* getSubFunction(const Firebird::MetaName& name); + void putSubFunction(DeclareSubFuncNode* subFunc, bool replace = false); - void putSubFunction(dsql_udf* subFunc) - { - if (subFunctions.exist(subFunc->udf_name.identifier)) - { - using namespace Firebird; - status_exception::raise( - Arg::Gds(isc_dsql_duplicate_spec) << subFunc->udf_name.identifier); - } - - subFunctions.put(subFunc->udf_name.identifier, subFunc); - } - - dsql_prc* getSubProcedure(const Firebird::MetaName& name) - { - dsql_prc* subProc = NULL; - subProcedures.get(name, subProc); - return subProc; - } - - void putSubProcedure(dsql_prc* subProc) - { - if (subProcedures.exist(subProc->prc_name.identifier)) - { - using namespace Firebird; - status_exception::raise( - Arg::Gds(isc_dsql_duplicate_spec) << subProc->prc_name.identifier); - } - - subProcedures.put(subProc->prc_name.identifier, subProc); - } + DeclareSubProcNode* getSubProcedure(const Firebird::MetaName& name); + void putSubProcedure(DeclareSubProcNode* subProc, bool replace = false); private: SelectExprNode* pass1RecursiveCte(SelectExprNode* input); @@ -300,7 +268,6 @@ public: unsigned nestingLevel; // begin...end nesting level Firebird::Array ports; // Port messages dsql_rel* relation; // relation created by this request (for DDL) - dsql_prc* procedure; // procedure created by this request (for DDL) DsqlContextStack mainContext; DsqlContextStack* context; DsqlContextStack unionContext; // Save contexts for views of unions @@ -340,8 +307,9 @@ private: Firebird::HalfStaticArray ctes; // common table expressions Firebird::HalfStaticArray cteAliases; // CTE aliases in recursive members bool psql; - Firebird::GenericMap > subFunctions; - Firebird::GenericMap > subProcedures; + DsqlCompilerScratch* mainScratch; + Firebird::GenericMap > subFunctions; + Firebird::GenericMap > subProcedures; }; class PsqlChanger diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index b182d78b0f..a80ea8f7fb 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -11589,8 +11589,12 @@ DmlNode* UdfCallNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* if (blrOp == blr_subfunc) { DeclareSubFuncNode* declareNode; - if (csb->subFunctions.get(name.identifier, declareNode)) - node->function = declareNode->routine; + + for (auto curCsb = csb; curCsb && !node->function; curCsb = curCsb->mainCsb) + { + if (curCsb->subFunctions.get(name.identifier, declareNode)) + node->function = declareNode->routine; + } } Function* function = node->function; @@ -12023,7 +12027,10 @@ ValueExprNode* UdfCallNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) doDsqlPass(dsqlScratch, args)); if (name.package.isEmpty()) - node->dsqlFunction = dsqlScratch->getSubFunction(name.identifier); + { + DeclareSubFuncNode* subFunction = dsqlScratch->getSubFunction(name.identifier); + node->dsqlFunction = subFunction ? subFunction->dsqlFunction : NULL; + } if (!node->dsqlFunction) node->dsqlFunction = METD_get_function(dsqlScratch->getTransaction(), dsqlScratch, name); diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 75daf789c6..da8eb31080 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -51,164 +51,6 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { - struct ParameterInfo - { - explicit ParameterInfo(MemoryPool& p) - : type(0), - number(0), - name(p), - fieldSource(p), - fieldName(p), - relationName(p), - mechanism(0) - { - defaultSource.clear(); - defaultValue.clear(); - } - - ParameterInfo(MemoryPool& p, const ParameterInfo& o) - : type(o.type), - number(o.number), - name(p, o.name), - fieldSource(p, o.fieldSource), - fieldName(p, o.fieldName), - relationName(p, o.relationName), - collationId(o.collationId), - nullFlag(o.nullFlag), - mechanism(o.mechanism), - fieldLength(o.fieldLength), - fieldScale(o.fieldScale), - fieldType(o.fieldType), - fieldSubType(o.fieldSubType), - fieldSegmentLength(o.fieldSegmentLength), - fieldNullFlag(o.fieldNullFlag), - fieldCharLength(o.fieldCharLength), - fieldCollationId(o.fieldCollationId), - fieldCharSetId(o.fieldCharSetId), - fieldPrecision(o.fieldPrecision), - defaultSource(o.defaultSource), - defaultValue(o.defaultValue) - { - } - - SSHORT type; - SSHORT number; - MetaName name; - MetaName fieldSource; - MetaName fieldName; - MetaName relationName; - Nullable collationId; - Nullable nullFlag; - SSHORT mechanism; - Nullable fieldLength; - Nullable fieldScale; - Nullable fieldType; - Nullable fieldSubType; - Nullable fieldSegmentLength; - Nullable fieldNullFlag; - Nullable fieldCharLength; - Nullable fieldCollationId; - Nullable fieldCharSetId; - Nullable fieldPrecision; - - // Not compared - bid defaultSource; - bid defaultValue; - - bool operator >(const ParameterInfo& o) const - { - return type > o.type || (type == o.type && number > o.number); - } - - bool operator ==(const ParameterInfo& o) const - { - return type == o.type && number == o.number && name == o.name && - (fieldSource == o.fieldSource || - (fb_utils::implicit_domain(fieldSource.c_str()) && - fb_utils::implicit_domain(o.fieldSource.c_str()))) && - fieldName == o.fieldName && relationName == o.relationName && - collationId == o.collationId && nullFlag == o.nullFlag && - mechanism == o.mechanism && fieldLength == o.fieldLength && - fieldScale == o.fieldScale && fieldType == o.fieldType && - fieldSubType == o.fieldSubType && fieldSegmentLength == o.fieldSegmentLength && - fieldNullFlag == o.fieldNullFlag && fieldCharLength == o.fieldCharLength && - fieldCollationId == o.fieldCollationId && fieldCharSetId == o.fieldCharSetId && - fieldPrecision == o.fieldPrecision; - } - - bool operator !=(const ParameterInfo& o) const - { - return !(*this == o); - } - }; - - struct Signature - { - Signature(MemoryPool& p, const MetaName& aName) - : name(p, aName), - parameters(p), - defined(false) - { - } - - explicit Signature(const MetaName& aName) - : name(aName), - parameters(*getDefaultMemoryPool()), - defined(false) - { - } - - explicit Signature(MemoryPool& p) - : name(p), - parameters(p), - defined(false) - { - } - - Signature(MemoryPool& p, const Signature& o) - : name(p, o.name), - parameters(p), - defined(o.defined) - { - for (SortedObjectsArray::const_iterator i = o.parameters.begin(); - i != o.parameters.end(); ++i) - { - parameters.add(*i); - } - } - - bool operator >(const Signature& o) const - { - return name > o.name; - } - - bool operator ==(const Signature& o) const - { - if (name != o.name || parameters.getCount() != o.parameters.getCount()) - return false; - - for (SortedObjectsArray::const_iterator i = parameters.begin(), - j = o.parameters.begin(); - i != parameters.end(); ++i, ++j) - { - if (*i != *j) - return false; - } - - return true; - } - - bool operator !=(const Signature& o) const - { - return !(*this == o); - } - - MetaName name; - SortedObjectsArray parameters; - bool defined; - }; - - // Return function and procedure names (in the user charset) and optionally its details for a // given package. void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const MetaName& metaName, @@ -234,7 +76,7 @@ namespace ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE { - ParameterInfo parameter(*getDefaultMemoryPool()); + SignatureParameter parameter(*getDefaultMemoryPool()); parameter.number = ARG.RDB$ARGUMENT_POSITION; parameter.name = ARG.RDB$ARGUMENT_NAME; @@ -271,11 +113,6 @@ namespace if (!FLD.RDB$FIELD_PRECISION.NULL) parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - if (!ARG.RDB$DEFAULT_SOURCE.NULL) - parameter.defaultSource = ARG.RDB$DEFAULT_SOURCE; - if (!ARG.RDB$DEFAULT_VALUE.NULL) - parameter.defaultValue = ARG.RDB$DEFAULT_VALUE; - function.parameters.add(parameter); } END_FOR @@ -304,7 +141,7 @@ namespace PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE { - ParameterInfo parameter(*getDefaultMemoryPool()); + SignatureParameter parameter(*getDefaultMemoryPool()); parameter.type = PRM.RDB$PARAMETER_TYPE; parameter.number = PRM.RDB$PARAMETER_NUMBER; parameter.name = PRM.RDB$PARAMETER_NAME; @@ -341,11 +178,6 @@ namespace if (!FLD.RDB$FIELD_PRECISION.NULL) parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - if (!PRM.RDB$DEFAULT_SOURCE.NULL) - parameter.defaultSource = PRM.RDB$DEFAULT_SOURCE; - if (!PRM.RDB$DEFAULT_VALUE.NULL) - parameter.defaultValue = PRM.RDB$DEFAULT_VALUE; - procedure.parameters.add(parameter); } END_FOR diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 6f8ea0cbae..7532a42a19 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -1402,7 +1402,8 @@ DmlNode* DeclareSubFuncNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerSc { // scope CompilerScratch* const subCsb = node->subCsb = - FB_NEW_POOL(csb->csb_pool) CompilerScratch(csb->csb_pool); + FB_NEW_POOL(csb->csb_pool) CompilerScratch(csb->csb_pool, csb); + subCsb->csb_g_flags |= csb_subroutine | (csb->csb_g_flags & csb_get_dependencies); subCsb->csb_blr_reader = csb->csb_blr_reader; @@ -1520,6 +1521,97 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE) ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << "nested sub function"); + DeclareSubFuncNode* prevDecl = dsqlScratch->getSubFunction(name); + bool implemetingForward = prevDecl && !prevDecl->dsqlBlock && dsqlBlock; + + dsqlFunction = implemetingForward ? prevDecl->dsqlFunction : FB_NEW_POOL(pool) dsql_udf(pool); + + dsqlFunction->udf_flags = UDF_subfunc; + dsqlFunction->udf_name.identifier = name; + + fb_assert(dsqlReturns.getCount() == 1); + const TypeClause* returnType = dsqlReturns[0]->type; + + dsqlFunction->udf_dtype = returnType->dtype; + dsqlFunction->udf_scale = returnType->scale; + dsqlFunction->udf_sub_type = returnType->subType; + dsqlFunction->udf_length = returnType->length; + dsqlFunction->udf_character_set_id = returnType->charSetId; + + if (dsqlDeterministic) + dsqlSignature.flags |= Signature::FLAG_DETERMINISTIC; + + SignatureParameter sigRet(pool); + sigRet.type = 1; + sigRet.number = -1; + sigRet.fromType(returnType); + dsqlSignature.parameters.add(sigRet); + + Array >& paramArray = dsqlParameters; + bool defaultFound = false; + + for (NestConst* i = paramArray.begin(); i != paramArray.end(); ++i) + { + ParameterClause* param = *i; + const unsigned paramIndex = i - paramArray.begin(); + + SignatureParameter sigParam(pool); + sigParam.type = 0; + sigParam.number = (SSHORT) dsqlSignature.parameters.getCount(); + sigParam.name = param->name; + sigParam.fromType(param->type); + dsqlSignature.parameters.add(sigParam); + + if (!implemetingForward) + { + // ASF: dsqlFunction->udf_arguments is only checked for its count for now. + dsqlFunction->udf_arguments.add(dsc()); + } + + if (param->defaultClause) + { + if (prevDecl) + { + status_exception::raise( + Arg::Gds(isc_subfunc_defvaldecl) << + name.c_str()); + } + + defaultFound = true; + + if (!implemetingForward && dsqlFunction->udf_def_count == 0) + dsqlFunction->udf_def_count = paramArray.end() - i; + } + else + { + if (defaultFound) + { + // Parameter without default value after parameters with default. + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << + Arg::Gds(isc_bad_default_value) << + Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last")); + } + + if (prevDecl && paramIndex < prevDecl->dsqlParameters.getCount()) + param->defaultClause = prevDecl->dsqlParameters[paramIndex]->defaultClause; + } + } + + if (!implemetingForward) + dsqlScratch->putSubFunction(this); + else if (dsqlSignature != prevDecl->dsqlSignature) + { + status_exception::raise( + Arg::Gds(isc_subfunc_signat) << + name.c_str()); + } + + if (!dsqlBlock) // forward decl + return this; + + if (prevDecl) + dsqlScratch->putSubFunction(this, true); + DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) @@ -1535,58 +1627,23 @@ DeclareSubFuncNode* DeclareSubFuncNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setType(DsqlCompiledStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, - dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement); + dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); blockScratch->clientDialect = dsqlScratch->clientDialect; - blockScratch->flags |= DsqlCompilerScratch::FLAG_FUNCTION | DsqlCompilerScratch::FLAG_SUB_ROUTINE; - blockScratch->flags |= dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL; + blockScratch->flags |= + DsqlCompilerScratch::FLAG_FUNCTION | + DsqlCompilerScratch::FLAG_SUB_ROUTINE | + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL); dsqlBlock = dsqlBlock->dsqlPass(blockScratch); - dsqlFunction = FB_NEW_POOL(pool) dsql_udf(pool); - dsqlFunction->udf_flags = UDF_subfunc; - dsqlFunction->udf_name.identifier = name; - - fb_assert(dsqlBlock->returns.getCount() == 1); - const TypeClause* returnType = dsqlBlock->returns[0]->type; - dsqlFunction->udf_dtype = returnType->dtype; - dsqlFunction->udf_scale = returnType->scale; - dsqlFunction->udf_sub_type = returnType->subType; - dsqlFunction->udf_length = returnType->length; - dsqlFunction->udf_character_set_id = returnType->charSetId; - - const Array >& paramArray = dsqlBlock->parameters; - bool defaultFound = false; - - for (const NestConst* i = paramArray.begin(); i != paramArray.end(); ++i) - { - // ASF: dsqlFunction->udf_arguments is only checked for its count for now. - dsqlFunction->udf_arguments.add(dsc()); - - const ParameterClause* param = *i; - - if (param->defaultClause) - { - defaultFound = true; - - if (dsqlFunction->udf_def_count == 0) - dsqlFunction->udf_def_count = paramArray.end() - i; - } - else if (defaultFound) - { - // Parameter without default value after parameters with default. - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << - Arg::Gds(isc_bad_default_value) << - Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last")); - } - } - - dsqlScratch->putSubFunction(dsqlFunction); - return this; } void DeclareSubFuncNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + if (!dsqlBlock) // forward decl + return; + GEN_request(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subfunc_decl); @@ -1677,7 +1734,8 @@ DmlNode* DeclareSubProcNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerSc { // scope CompilerScratch* const subCsb = node->subCsb = - FB_NEW_POOL(csb->csb_pool) CompilerScratch(csb->csb_pool); + FB_NEW_POOL(csb->csb_pool) CompilerScratch(csb->csb_pool, csb); + subCsb->csb_g_flags |= csb_subroutine | (csb->csb_g_flags & csb_get_dependencies); subCsb->csb_blr_reader = csb->csb_blr_reader; @@ -1803,6 +1861,100 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_SUB_ROUTINE) ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << "nested sub procedure"); + DeclareSubProcNode* prevDecl = dsqlScratch->getSubProcedure(name); + bool implemetingForward = prevDecl && !prevDecl->dsqlBlock && dsqlBlock; + + dsqlProcedure = implemetingForward ? prevDecl->dsqlProcedure : FB_NEW_POOL(pool) dsql_prc(pool); + + dsqlProcedure->prc_flags = PRC_subproc; + dsqlProcedure->prc_name.identifier = name; + dsqlProcedure->prc_in_count = USHORT(dsqlParameters.getCount()); + dsqlProcedure->prc_out_count = USHORT(dsqlReturns.getCount()); + + if (dsqlParameters.hasData()) + { + Array >& paramArray = dsqlParameters; + bool defaultFound = false; + + dsqlProcedure->prc_inputs = paramArray.front()->type; + + for (NestConst* i = paramArray.begin(); i != paramArray.end(); ++i) + { + ParameterClause* param = *i; + const unsigned paramIndex = i - paramArray.begin(); + + SignatureParameter sigParam(pool); + sigParam.type = 0; // input + sigParam.number = (SSHORT) dsqlSignature.parameters.getCount(); + sigParam.name = param->name; + sigParam.fromType(param->type); + dsqlSignature.parameters.add(sigParam); + + if (param->defaultClause) + { + if (prevDecl) + { + status_exception::raise( + Arg::Gds(isc_subproc_defvaldecl) << + name.c_str()); + } + + defaultFound = true; + + if (!implemetingForward && dsqlProcedure->prc_def_count == 0) + dsqlProcedure->prc_def_count = paramArray.end() - i; + } + else + { + if (defaultFound) + { + // Parameter without default value after parameters with default. + ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << + Arg::Gds(isc_bad_default_value) << + Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last")); + } + + if (prevDecl && paramIndex < prevDecl->dsqlParameters.getCount()) + param->defaultClause = prevDecl->dsqlParameters[paramIndex]->defaultClause; + } + } + } + + if (dsqlReturns.hasData()) + { + Array >& paramArray = dsqlReturns; + + dsqlProcedure->prc_outputs = paramArray.front()->type; + + for (NestConst* i = paramArray.begin(); i != paramArray.end(); ++i) + { + ParameterClause* param = *i; + const unsigned paramIndex = i - paramArray.begin(); + + SignatureParameter sigParam(pool); + sigParam.type = 1; // output + sigParam.number = (SSHORT) dsqlSignature.parameters.getCount(); + sigParam.name = param->name; + sigParam.fromType(param->type); + dsqlSignature.parameters.add(sigParam); + } + } + + if (!implemetingForward) + dsqlScratch->putSubProcedure(this); + else if (dsqlSignature != prevDecl->dsqlSignature) + { + status_exception::raise( + Arg::Gds(isc_subproc_signat) << + name.c_str()); + } + + if (!dsqlBlock) // forward decl + return this; + + if (prevDecl) + dsqlScratch->putSubProcedure(this, true); + DsqlCompiledStatement* statement = FB_NEW_POOL(pool) DsqlCompiledStatement(pool); if (dsqlScratch->clientDialect > SQL_DIALECT_V5) @@ -1818,54 +1970,21 @@ DeclareSubProcNode* DeclareSubProcNode::dsqlPass(DsqlCompilerScratch* dsqlScratc statement->setType(DsqlCompiledStatement::TYPE_SELECT); blockScratch = FB_NEW_POOL(pool) DsqlCompilerScratch(pool, - dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement); + dsqlScratch->getAttachment(), dsqlScratch->getTransaction(), statement, dsqlScratch); blockScratch->clientDialect = dsqlScratch->clientDialect; blockScratch->flags |= DsqlCompilerScratch::FLAG_PROCEDURE | DsqlCompilerScratch::FLAG_SUB_ROUTINE; blockScratch->flags |= dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL; dsqlBlock = dsqlBlock->dsqlPass(blockScratch); - dsqlProcedure = FB_NEW_POOL(pool) dsql_prc(pool); - dsqlProcedure->prc_flags = PRC_subproc; - dsqlProcedure->prc_name.identifier = name; - dsqlProcedure->prc_in_count = USHORT(dsqlBlock->parameters.getCount()); - dsqlProcedure->prc_out_count = USHORT(dsqlBlock->returns.getCount()); - - if (dsqlBlock->parameters.hasData()) - { - Array >& paramArray = dsqlBlock->parameters; - - dsqlProcedure->prc_inputs = paramArray.front()->type; - - for (const NestConst* i = paramArray.begin(); i != paramArray.end(); ++i) - { - const ParameterClause* param = *i; - - if (param->defaultClause) - { - if (dsqlProcedure->prc_def_count == 0) - dsqlProcedure->prc_def_count = paramArray.end() - i; - } - else if (dsqlProcedure->prc_def_count != 0) - { - // Parameter without default value after parameters with default. - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << - Arg::Gds(isc_bad_default_value) << - Arg::Gds(isc_invalid_clause) << Arg::Str("defaults must be last")); - } - } - } - - if (dsqlBlock->returns.hasData()) - dsqlProcedure->prc_outputs = dsqlBlock->returns.front()->type; - - dsqlScratch->putSubProcedure(dsqlProcedure); - return this; } void DeclareSubProcNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + if (!dsqlBlock) // forward decl + return; + GEN_request(blockScratch, dsqlBlock); dsqlScratch->appendUChar(blr_subproc_decl); @@ -2636,8 +2755,12 @@ DmlNode* ExecProcedureNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScr if (blrOp == blr_exec_subproc) { DeclareSubProcNode* declareNode; - if (csb->subProcedures.get(name.identifier, declareNode)) - procedure = declareNode->routine; + + for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb) + { + if (curCsb->subProcedures.get(name.identifier, declareNode)) + procedure = declareNode->routine; + } } else procedure = MET_lookup_procedure(tdbb, name, false); @@ -2669,7 +2792,10 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) dsql_prc* procedure = NULL; if (dsqlName.package.isEmpty()) - procedure = dsqlScratch->getSubProcedure(dsqlName.identifier); + { + DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(dsqlName.identifier); + procedure = subProcedure ? subProcedure->dsqlProcedure : NULL; + } if (!procedure) procedure = METD_get_procedure(dsqlScratch->getTransaction(), dsqlScratch, dsqlName); @@ -2683,10 +2809,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } if (!dsqlScratch->isPsql()) - { - dsqlScratch->procedure = procedure; dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_EXEC_PROCEDURE); - } ExecProcedureNode* node = FB_NEW_POOL(getPool()) ExecProcedureNode(getPool(), dsqlName); node->dsqlProcedure = procedure; @@ -2740,7 +2863,7 @@ ExecProcedureNode* ExecProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Arg::Gds(isc_random) << Arg::Str("RETURNING_VALUES")); } - node->outputSources = explodeOutputs(dsqlScratch, dsqlScratch->procedure); + node->outputSources = explodeOutputs(dsqlScratch, procedure); } if (node->outputSources) diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index c15fc55e07..398eba0b0f 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -420,6 +420,9 @@ public: : TypedNode(pool), name(pool, aName), dsqlDeterministic(false), + dsqlParameters(pool), + dsqlReturns(pool), + dsqlSignature(pool, aName), dsqlBlock(NULL), blockScratch(NULL), dsqlFunction(NULL), @@ -451,6 +454,9 @@ private: public: Firebird::MetaName name; bool dsqlDeterministic; + Firebird::Array > dsqlParameters; + Firebird::Array > dsqlReturns; + Signature dsqlSignature; NestConst dsqlBlock; DsqlCompilerScratch* blockScratch; dsql_udf* dsqlFunction; @@ -467,6 +473,9 @@ public: explicit DeclareSubProcNode(MemoryPool& pool, const Firebird::MetaName& aName) : TypedNode(pool), name(pool, aName), + dsqlParameters(pool), + dsqlReturns(pool), + dsqlSignature(pool, aName), dsqlBlock(NULL), blockScratch(NULL), dsqlProcedure(NULL), @@ -497,6 +506,9 @@ private: public: Firebird::MetaName name; + Firebird::Array > dsqlParameters; + Firebird::Array > dsqlReturns; + Signature dsqlSignature; NestConst dsqlBlock; DsqlCompilerScratch* blockScratch; dsql_prc* dsqlProcedure; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 21ce846320..13135db2d0 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -1000,6 +1000,196 @@ private: int scale; }; +struct SignatureParameter +{ + explicit SignatureParameter(MemoryPool& p) + : type(0), + number(0), + name(p), + fieldSource(p), + fieldName(p), + relationName(p), + charSetName(p), + collationName(p), + subTypeName(p), + mechanism(0) + { + } + + SignatureParameter(MemoryPool& p, const SignatureParameter& o) + : type(o.type), + number(o.number), + name(p, o.name), + fieldSource(p, o.fieldSource), + fieldName(p, o.fieldName), + relationName(p, o.relationName), + charSetName(p, o.charSetName), + collationName(p, o.collationName), + subTypeName(p, o.subTypeName), + collationId(o.collationId), + nullFlag(o.nullFlag), + mechanism(o.mechanism), + fieldLength(o.fieldLength), + fieldScale(o.fieldScale), + fieldType(o.fieldType), + fieldSubType(o.fieldSubType), + fieldSegmentLength(o.fieldSegmentLength), + fieldNullFlag(o.fieldNullFlag), + fieldCharLength(o.fieldCharLength), + fieldCollationId(o.fieldCollationId), + fieldCharSetId(o.fieldCharSetId), + fieldPrecision(o.fieldPrecision) + { + } + + void fromType(const TypeClause* type) + { + fieldType = type->dtype; + fieldScale = type->scale; + subTypeName = type->subTypeName; + fieldSubType = type->subType; + fieldLength = type->length; + fieldCharLength = type->charLength; + charSetName = type->charSet; + fieldCharSetId = type->charSetId; + collationName = type->collate; + fieldCollationId = type->collationId; + fieldSource = type->fieldSource; + fieldName = type->typeOfName; + relationName = type->typeOfTable; + fieldSegmentLength = type->segLength; + fieldPrecision = type->precision; + nullFlag = (SSHORT) type->notNull; + mechanism = (SSHORT) type->fullDomain; + } + + SSHORT type; + SSHORT number; + Firebird::MetaName name; + Firebird::MetaName fieldSource; + Firebird::MetaName fieldName; + Firebird::MetaName relationName; + Firebird::MetaName charSetName; + Firebird::MetaName collationName; + Firebird::MetaName subTypeName; + Nullable collationId; + Nullable nullFlag; + SSHORT mechanism; + Nullable fieldLength; + Nullable fieldScale; + Nullable fieldType; + Nullable fieldSubType; + Nullable fieldSegmentLength; + Nullable fieldNullFlag; + Nullable fieldCharLength; + Nullable fieldCollationId; + Nullable fieldCharSetId; + Nullable fieldPrecision; + + bool operator >(const SignatureParameter& o) const + { + return type > o.type || (type == o.type && number > o.number); + } + + bool operator ==(const SignatureParameter& o) const + { + return type == o.type && number == o.number && name == o.name && + (fieldSource == o.fieldSource || + (fb_utils::implicit_domain(fieldSource.c_str()) && + fb_utils::implicit_domain(o.fieldSource.c_str()))) && + fieldName == o.fieldName && relationName == o.relationName && + collationId == o.collationId && nullFlag == o.nullFlag && + mechanism == o.mechanism && fieldLength == o.fieldLength && + fieldScale == o.fieldScale && fieldType == o.fieldType && + fieldSubType == o.fieldSubType && fieldSegmentLength == o.fieldSegmentLength && + fieldNullFlag == o.fieldNullFlag && fieldCharLength == o.fieldCharLength && + charSetName == o.charSetName && collationName == o.collationName && subTypeName == o.subTypeName && + fieldCollationId == o.fieldCollationId && fieldCharSetId == o.fieldCharSetId && + fieldPrecision == o.fieldPrecision; + } + + bool operator !=(const SignatureParameter& o) const + { + return !(*this == o); + } +}; + +struct Signature +{ + const static unsigned FLAG_DETERMINISTIC = 0x01; + + Signature(MemoryPool& p, const Firebird::MetaName& aName) + : name(p, aName), + parameters(p), + flags(0), + defined(false) + { + } + + explicit Signature(const Firebird::MetaName& aName) + : name(aName), + parameters(*getDefaultMemoryPool()), + flags(0), + defined(false) + { + } + + explicit Signature(MemoryPool& p) + : name(p), + parameters(p), + flags(0), + defined(false) + { + } + + Signature(MemoryPool& p, const Signature& o) + : name(p, o.name), + parameters(p), + flags(0), + defined(o.defined) + { + for (Firebird::SortedObjectsArray::const_iterator i = o.parameters.begin(); + i != o.parameters.end(); + ++i) + { + parameters.add(*i); + } + } + + bool operator >(const Signature& o) const + { + return name > o.name; + } + + bool operator ==(const Signature& o) const + { + if (name != o.name || flags != o.flags || parameters.getCount() != o.parameters.getCount()) + return false; + + for (Firebird::SortedObjectsArray::const_iterator i = parameters.begin(), + j = o.parameters.begin(); + i != parameters.end(); + ++i, ++j) + { + if (*i != *j) + return false; + } + + return true; + } + + bool operator !=(const Signature& o) const + { + return !(*this == o); + } + + Firebird::MetaName name; + Firebird::SortedObjectsArray parameters; + unsigned flags; + bool defined; +}; + + } // namespace /*! \var unsigned DSQL_debug diff --git a/src/dsql/parse-conflicts.txt b/src/dsql/parse-conflicts.txt index 9a7308c038..86df312516 100644 --- a/src/dsql/parse-conflicts.txt +++ b/src/dsql/parse-conflicts.txt @@ -1 +1 @@ -46 shift/reduce conflicts, 17 reduce/reduce conflicts. +54 shift/reduce conflicts, 17 reduce/reduce conflicts. diff --git a/src/dsql/parse.y b/src/dsql/parse.y index fec83c58ae..2c52a669b9 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -2539,7 +2539,7 @@ procedure_clause %type psql_procedure_clause psql_procedure_clause - : procedure_clause_start sql_security_clause AS local_declaration_list full_proc_block + : procedure_clause_start sql_security_clause AS local_declarations_opt full_proc_block { $$ = $1; $$->ssDefiner = $2; @@ -2670,7 +2670,7 @@ function_clause %type psql_function_clause psql_function_clause - : function_clause_start sql_security_clause AS local_declaration_list full_proc_block + : function_clause_start sql_security_clause AS local_declarations_opt full_proc_block { $$ = $1; $$->ssDefiner = $2; @@ -2869,73 +2869,131 @@ package_body_item ; -%type local_declaration_list -local_declaration_list - : /* nothing */ { $$ = NULL; } - | local_declarations +%type local_declarations_opt +local_declarations_opt + : local_forward_declarations_opt local_nonforward_declarations_opt + { + CompoundStmtNode* forward = $1; + CompoundStmtNode* nonForward = $2; + + if (!forward) + $$ = nonForward; + else + { + if (nonForward) + forward->statements.add(nonForward->statements.begin(), nonForward->statements.getCount()); + + $$ = forward; + } + } ; -%type local_declarations -local_declarations - : local_declaration +%type local_forward_declarations_opt +local_forward_declarations_opt + : /* nothing */ { $$ = NULL; } + | local_forward_declarations + ; + +%type local_forward_declarations +local_forward_declarations + : local_forward_declaration { $$ = newNode(); $$->statements.add($1); } - | local_declarations local_declaration + | local_forward_declarations local_forward_declaration { $1->statements.add($2); $$ = $1; } ; -%type local_declaration -local_declaration +%type local_forward_declaration +local_forward_declaration + : local_declaration_subproc_start ';' { $$ = $1; } + | local_declaration_subfunc_start ';' { $$ = $1; } + ; + +%type local_nonforward_declarations_opt +local_nonforward_declarations_opt + : /* nothing */ { $$ = NULL; } + | local_nonforward_declarations + ; + +%type local_nonforward_declarations +local_nonforward_declarations + : local_nonforward_declaration + { + $$ = newNode(); + $$->statements.add($1); + } + | local_nonforward_declarations local_nonforward_declaration + { + $1->statements.add($2); + $$ = $1; + } + ; + +%type local_nonforward_declaration +local_nonforward_declaration : DECLARE var_decl_opt local_declaration_item ';' { $$ = $3; $$->line = YYPOSNARG(1).firstLine; $$->column = YYPOSNARG(1).firstColumn; } - | DECLARE PROCEDURE symbol_procedure_name - { $$ = newNode(); } - input_parameters(NOTRIAL(&$4->parameters)) - output_parameters(NOTRIAL(&$4->returns)) AS - local_declaration_list - full_proc_block + | local_declaration_subproc_start AS local_declarations_opt full_proc_block { - DeclareSubProcNode* node = newNode(*$3); - node->dsqlBlock = $4; - node->dsqlBlock->localDeclList = $8; - node->dsqlBlock->body = $9; + DeclareSubProcNode* node = $1; + node->dsqlBlock = newNode(); + node->dsqlBlock->parameters = node->dsqlParameters; + node->dsqlBlock->returns = node->dsqlReturns; + node->dsqlBlock->localDeclList = $3; + node->dsqlBlock->body = $4; for (FB_SIZE_T i = 0; i < node->dsqlBlock->parameters.getCount(); ++i) node->dsqlBlock->parameters[i]->parameterExpr = make_parameter(); $$ = node; } - | DECLARE FUNCTION symbol_UDF_name - { $$ = newNode(); } - input_parameters(NOTRIAL(&$4->parameters)) - RETURNS domain_or_non_array_type collate_clause deterministic_opt AS - local_declaration_list - full_proc_block + | local_declaration_subfunc_start AS local_declarations_opt full_proc_block { - DeclareSubFuncNode* node = newNode(*$3); - node->dsqlDeterministic = $9; - node->dsqlBlock = $4; - node->dsqlBlock->localDeclList = $11; - node->dsqlBlock->body = $12; + DeclareSubFuncNode* node = $1; + node->dsqlBlock = newNode(); + node->dsqlBlock->parameters = node->dsqlParameters; + node->dsqlBlock->returns = node->dsqlReturns; + node->dsqlBlock->localDeclList = $3; + node->dsqlBlock->body = $4; for (FB_SIZE_T i = 0; i < node->dsqlBlock->parameters.getCount(); ++i) node->dsqlBlock->parameters[i]->parameterExpr = make_parameter(); - node->dsqlBlock->returns.add(newNode($7, optName($8))); - $$ = node; } ; +%type local_declaration_subproc_start +local_declaration_subproc_start + : DECLARE PROCEDURE symbol_procedure_name + { $$ = newNode(NOTRIAL(*$3)); } + input_parameters(NOTRIAL(&$4->dsqlParameters)) + output_parameters(NOTRIAL(&$4->dsqlReturns)) + { $$ = $4; } + ; + +%type local_declaration_subfunc_start +local_declaration_subfunc_start + : DECLARE FUNCTION symbol_UDF_name + { $$ = newNode(NOTRIAL(*$3)); } + input_parameters(NOTRIAL(&$4->dsqlParameters)) + RETURNS domain_or_non_array_type collate_clause deterministic_opt + { + $$ = $4; + $$->dsqlReturns.add(newNode($7, optName($8))); + $$->dsqlDeterministic = $9; + } + ; + %type local_declaration_item local_declaration_item : var_declaration_item @@ -3526,7 +3584,7 @@ exec_block { $$ = newNode(); } block_input_params(NOTRIAL(&$3->parameters)) output_parameters(NOTRIAL(&$3->returns)) AS - local_declaration_list + local_declarations_opt full_proc_block { ExecBlockNode* node = $3; @@ -3597,7 +3655,7 @@ check_opt %type trigger_clause trigger_clause - : create_trigger_start trg_sql_security_clause AS local_declaration_list full_proc_block + : create_trigger_start trg_sql_security_clause AS local_declarations_opt full_proc_block { $$ = $1; $$->ssDefiner = $2; @@ -4244,7 +4302,7 @@ crypt_key_clause($alterDatabaseNode) %type alter_trigger_clause alter_trigger_clause : symbol_trigger_name trigger_active trigger_type_opt trigger_position trg_sql_security_clause - AS local_declaration_list full_proc_block + AS local_declarations_opt full_proc_block { $$ = newNode(*$1); $$->alter = true; diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 74ed71b620..bfc9738d02 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -367,7 +367,10 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* else if (procNode && (procNode->dsqlName.package.hasData() || procNode->sourceList)) { if (procNode->dsqlName.package.isEmpty()) - procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); + { + DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); + procedure = subProcedure ? subProcedure->dsqlProcedure : NULL; + } if (!procedure) { @@ -390,7 +393,10 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* else { if (procNode && procNode->dsqlName.package.isEmpty()) - procedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); + { + DeclareSubProcNode* subProcedure = dsqlScratch->getSubProcedure(procNode->dsqlName.identifier); + procedure = subProcedure ? subProcedure->dsqlProcedure : NULL; + } if (!procedure) relation = METD_get_relation(dsqlScratch->getTransaction(), dsqlScratch, relation_name); diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 6d1069a761..93df2212fa 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -845,6 +845,14 @@ static const struct { {"decfloat_invalid_operation", 335545141}, {"decfloat_overflow", 335545142}, {"decfloat_underflow", 335545143}, + {"subfunc_notdef", 335545144}, + {"subproc_notdef", 335545145}, + {"subfunc_signat", 335545146}, + {"subproc_signat", 335545147}, + {"subfunc_defvaldecl", 335545148}, + {"subproc_defvaldecl", 335545149}, + {"subfunc_not_impl", 335545150}, + {"subproc_not_impl", 335545151}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 142dd0db2a..dbfa5d8837 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -879,6 +879,14 @@ const ISC_STATUS isc_decfloat_inexact_result = 335545140L; const ISC_STATUS isc_decfloat_invalid_operation = 335545141L; const ISC_STATUS isc_decfloat_overflow = 335545142L; const ISC_STATUS isc_decfloat_underflow = 335545143L; +const ISC_STATUS isc_subfunc_notdef = 335545144L; +const ISC_STATUS isc_subproc_notdef = 335545145L; +const ISC_STATUS isc_subfunc_signat = 335545146L; +const ISC_STATUS isc_subproc_signat = 335545147L; +const ISC_STATUS isc_subfunc_defvaldecl = 335545148L; +const ISC_STATUS isc_subproc_defvaldecl = 335545149L; +const ISC_STATUS isc_subfunc_not_impl = 335545150L; +const ISC_STATUS isc_subproc_not_impl = 335545151L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1353,7 +1361,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L; const ISC_STATUS isc_trace_switch_param_miss = 337182758L; const ISC_STATUS isc_trace_param_act_notcompat = 337182759L; const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L; -const ISC_STATUS isc_err_max = 1297; +const ISC_STATUS isc_err_max = 1305; #else /* c definitions */ @@ -2202,6 +2210,14 @@ const ISC_STATUS isc_err_max = 1297; #define isc_decfloat_invalid_operation 335545141L #define isc_decfloat_overflow 335545142L #define isc_decfloat_underflow 335545143L +#define isc_subfunc_notdef 335545144L +#define isc_subproc_notdef 335545145L +#define isc_subfunc_signat 335545146L +#define isc_subproc_signat 335545147L +#define isc_subfunc_defvaldecl 335545148L +#define isc_subproc_defvaldecl 335545149L +#define isc_subfunc_not_impl 335545150L +#define isc_subproc_not_impl 335545151L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2676,7 +2692,7 @@ const ISC_STATUS isc_err_max = 1297; #define isc_trace_switch_param_miss 337182758L #define isc_trace_param_act_notcompat 337182759L #define isc_trace_mandatory_switch_miss 337182760L -#define isc_err_max 1297 +#define isc_err_max 1305 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 6fe2b426d3..c0fe687d81 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -848,6 +848,14 @@ Data source : @4"}, /* eds_statement */ {335545141, "Decimal float invalid operation. An indeterminant error occurred during an operation."}, /* decfloat_invalid_operation */ {335545142, "Decimal float overflow. The exponent of a result is greater than the magnitude allowed."}, /* decfloat_overflow */ {335545143, "Decimal float underflow. The exponent of a result is less than the magnitude allowed."}, /* decfloat_underflow */ + {335545144, "Sub-function @1 has not been defined"}, /* subfunc_notdef */ + {335545145, "Sub-procedure @1 has not been defined"}, /* subproc_notdef */ + {335545146, "Sub-function @1 has a signature mismatch with its forward declaration"}, /* subfunc_signat */ + {335545147, "Sub-procedure @1 has a signature mismatch with its forward declaration"}, /* subproc_signat */ + {335545148, "Default values for parameters are not allowed in definition of the previously declared sub-function @1"}, /* subfunc_defvaldecl */ + {335545149, "Default values for parameters are not allowed in definition of the previously declared sub-procedure @1"}, /* subproc_defvaldecl */ + {335545150, "Sub-function @1 was declared but not implemented"}, /* subfunc_not_impl */ + {335545151, "Sub-procedure @1 was declared but not implemented"}, /* subproc_not_impl */ {335740929, "data base file name (@1) already given"}, /* gfix_db_name */ {335740930, "invalid switch @1"}, /* gfix_invalid_sw */ {335740932, "incompatible switch combination"}, /* gfix_incmp_sw */ @@ -976,7 +984,7 @@ Data source : @4"}, /* eds_statement */ {336068872, "Procedure @1 has not been defined on the package body @2"}, /* dyn_procnotdef_package */ {336068873, "Function @1 has a signature mismatch on package body @2"}, /* dyn_funcsignat_package */ {336068874, "Procedure @1 has a signature mismatch on package body @2"}, /* dyn_procsignat_package */ - {336068875, "Default values for parameters are allowed only in declaration of packaged procedure @1.@2"}, /* dyn_defvaldecl_package_proc */ + {336068875, "Default values for parameters are not allowed in the definition of a previously declared packaged procedure @1.@2"}, /* dyn_defvaldecl_package_proc */ {336068877, "Package body @1 already exists"}, /* dyn_package_body_exists */ {336068878, "Invalid DDL statement for function @1"}, /* dyn_invalid_ddl_func */ {336068879, "Cannot alter new style function @1 with ALTER EXTERNAL FUNCTION. Use ALTER FUNCTION instead."}, /* dyn_newfc_oldsyntax */ @@ -990,7 +998,7 @@ Data source : @4"}, /* eds_statement */ {336068895, "System @1 @2 cannot be modified"}, /* dyn_cant_modify_sysobj */ {336068896, "INCREMENT BY 0 is an illegal option for sequence @1"}, /* dyn_cant_use_zero_increment */ {336068897, "Can't use @1 in FOREIGN KEY constraint"}, /* dyn_cant_use_in_foreignkey */ - {336068898, "Default values for parameters are allowed only in declaration of packaged function @1.@2"}, /* dyn_defvaldecl_package_func */ + {336068898, "Default values for parameters are not allowed in the definition of a previously declared packaged function @1.@2"}, /* dyn_defvaldecl_package_func */ {336068904, "INCREMENT BY 0 is an illegal option for identity column @1 of table @2"}, /* dyn_cant_use_zero_inc_ident */ {336330753, "found unknown switch"}, /* gbak_unknown_switch */ {336330754, "page size parameter missing"}, /* gbak_page_size_missing */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index fd926c2db2..62a5c81d5c 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -844,6 +844,14 @@ static const struct { {335545141, -901}, /* 821 decfloat_invalid_operation */ {335545142, -901}, /* 822 decfloat_overflow */ {335545143, -901}, /* 823 decfloat_underflow */ + {335545144, -901}, /* 824 subfunc_notdef */ + {335545145, -901}, /* 825 subproc_notdef */ + {335545146, -901}, /* 826 subfunc_signat */ + {335545147, -901}, /* 827 subproc_signat */ + {335545148, -901}, /* 828 subfunc_defvaldecl */ + {335545149, -901}, /* 829 subproc_defvaldecl */ + {335545150, -901}, /* 830 subfunc_not_impl */ + {335545151, -901}, /* 831 subproc_not_impl */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 17bd675509..d66f3ac9dc 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -844,6 +844,14 @@ static const struct { {335545141, "22000"}, // 821 decfloat_invalid_operation {335545142, "22003"}, // 822 decfloat_overflow {335545143, "22003"}, // 823 decfloat_underflow + {335545144, "42000"}, // 824 subfunc_notdef + {335545145, "42000"}, // 825 subproc_notdef + {335545146, "42000"}, // 826 subfunc_signat + {335545147, "42000"}, // 827 subproc_signat + {335545148, "42000"}, // 828 subfunc_defvaldecl + {335545149, "42000"}, // 829 subproc_defvaldecl + {335545150, "42000"}, // 830 subfunc_not_impl + {335545151, "42000"}, // 831 subproc_not_impl {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 9ecb738b8d..7aa18d2404 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -910,9 +910,13 @@ ProcedureSourceNode* ProcedureSourceNode::parse(thread_db* tdbb, CompilerScratch if (blrOp == blr_subproc) { - DeclareSubProcNode* node; - if (csb->subProcedures.get(name.identifier, node)) - procedure = node->routine; + DeclareSubProcNode* declareNode; + + for (auto curCsb = csb; curCsb && !procedure; curCsb = curCsb->mainCsb) + { + if (curCsb->subProcedures.get(name.identifier, declareNode)) + procedure = declareNode->routine; + } } else procedure = MET_lookup_procedure(tdbb, name, false); diff --git a/src/jrd/exe.h b/src/jrd/exe.h index f288e44848..bc6f3b5d5f 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -427,7 +427,7 @@ public: SLONG subNumber; }; - explicit CompilerScratch(MemoryPool& p) + explicit CompilerScratch(MemoryPool& p, CompilerScratch* aMainCsb = NULL) : /*csb_node(0), csb_variables(0), csb_dependencies(0), @@ -439,6 +439,7 @@ public: #ifdef CMP_DEBUG csb_dump(p), #endif + mainCsb(aMainCsb), csb_external(p), csb_access(p), csb_resources(p), @@ -486,6 +487,7 @@ public: Firebird::string csb_dump; #endif + CompilerScratch* mainCsb; Firebird::BlrReader csb_blr_reader; DmlNode* csb_node; ExternalAccessList csb_external; // Access to outside procedures/triggers to be checked diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index fe9b3794e2..567ad4b2e5 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2017-05-23 16:08:00', 'JRD', 0, 824) +('2017-07-10 12:27:00', 'JRD', 0, 832) ('2015-03-17 18:33:00', 'QLI', 1, 533) ('2015-01-07 18:01:51', 'GFIX', 3, 134) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 1cc8b62e52..a0d2c88796 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -931,6 +931,14 @@ Data source : @4', NULL, NULL) ('decfloat_invalid_operation', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 821, NULL, 'Decimal float invalid operation. An indeterminant error occurred during an operation.', NULL, NULL); ('decfloat_overflow', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 822, NULL, 'Decimal float overflow. The exponent of a result is greater than the magnitude allowed.', NULL, NULL); ('decfloat_underflow', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 823, NULL, 'Decimal float underflow. The exponent of a result is less than the magnitude allowed.', NULL, NULL); +('subfunc_notdef', NULL, 'StmtNodes.cpp', NULL, 0, 824, NULL, 'Sub-function @1 has not been defined', NULL, NULL); +('subproc_notdef', NULL, 'StmtNodes.cpp', NULL, 0, 825, NULL, 'Sub-procedure @1 has not been defined', NULL, NULL); +('subfunc_signat', NULL, 'StmtNodes.cpp', NULL, 0, 826, NULL, 'Sub-function @1 has a signature mismatch with its forward declaration', NULL, NULL); +('subproc_signat', NULL, 'StmtNodes.cpp', NULL, 0, 827, NULL, 'Sub-procedure @1 has a signature mismatch with its forward declaration', NULL, NULL); +('subfunc_defvaldecl', NULL, 'StmtNodes.cpp', NULL, 0, 828, NULL, 'Default values for parameters are not allowed in definition of the previously declared sub-function @1', NULL, NULL); +('subproc_defvaldecl', NULL, 'StmtNodes.cpp', NULL, 0, 829, NULL, 'Default values for parameters are not allowed in definition of the previously declared sub-procedure @1', NULL, NULL); +('subfunc_not_impl', NULL, 'StmtNodes.cpp', NULL, 0, 830, NULL, 'Sub-function @1 was declared but not implemented', NULL, NULL); +('subproc_not_impl', NULL, 'StmtNodes.cpp', NULL, 0, 831, NULL, 'Sub-procedure @1 was declared but not implemented', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); @@ -1981,7 +1989,7 @@ COMMIT WORK; ('dyn_procnotdef_package', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 264, NULL, 'Procedure @1 has not been defined on the package body @2', NULL, NULL); ('dyn_funcsignat_package', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 265, NULL, 'Function @1 has a signature mismatch on package body @2', NULL, NULL); ('dyn_procsignat_package', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 266, NULL, 'Procedure @1 has a signature mismatch on package body @2', NULL, NULL); -('dyn_defvaldecl_package_proc', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 267, NULL, 'Default values for parameters are allowed only in declaration of packaged procedure @1.@2', NULL, NULL); +('dyn_defvaldecl_package_proc', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 267, NULL, 'Default values for parameters are not allowed in the definition of a previously declared packaged procedure @1.@2', NULL, NULL); ('dyn_dup_function', 'DYN_define_function', 'dyn_def.epp', NULL, 8, 268, NULL, 'Function @1 already exists', NULL, NULL); ('dyn_package_body_exists', NULL, 'DdlNodes.epp/PackageNodes.epp', NULL, 8, 269, NULL, 'Package body @1 already exists', NULL, NULL); ('dyn_invalid_ddl_func', 'CreateAlterFunctionNode::compile', 'DdlNodes.epp', NULL, 8, 270, NULL, 'Invalid DDL statement for function @1', NULL, NULL); @@ -2004,7 +2012,7 @@ COMMIT WORK; ('dyn_cant_modify_sysobj', NULL, 'DdlNodes.epp', NULL, 8, 287, NULL, 'System @1 @2 cannot be modified', NULL, 'Ex: System generator rdb$... cannot be modified'); ('dyn_cant_use_zero_increment', NULL, 'DdlNodes.epp', NULL, 8, 288, NULL, 'INCREMENT BY 0 is an illegal option for sequence @1', NULL, NULL); ('dyn_cant_use_in_foreignkey', NULL, 'DdlNodes.epp', NULL, 8, 289, NULL, 'Can''t use @1 in FOREIGN KEY constraint', NULL, NULL); -('dyn_defvaldecl_package_func', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 290, NULL, 'Default values for parameters are allowed only in declaration of packaged function @1.@2', NULL, NULL); +('dyn_defvaldecl_package_func', 'CreatePackageBodyNode::execute', 'PackageNodes.epp', NULL, 8, 290, NULL, 'Default values for parameters are not allowed in the definition of a previously declared packaged function @1.@2', NULL, NULL); ('dyn_create_user_no_password', 'CreateAlterUserNode', 'DdlNodes.epp', NULL, 8, 291, NULL, 'Password must be specified when creating user', NULL, NULL); ('dyn_cyclic_role', 'GrantRevokeNode::grantRevoke', 'DdlNodes.epp', NULL, 8, 292, NULL, 'role @1 can not be granted to role @2', NULL, NULL); (NULL, 'CreateAlterRoleNode::execute', 'DdlNodes.epp', NULL, 8, 293, NULL, 'DROP SYSTEM PRIVILEGES should not be used in CREATE ROLE operator', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index d8a425cd72..648e2ec1fb 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -830,6 +830,14 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-901, '22', '000', 0, 821, 'decfloat_invalid_operation', NULL, NULL) (-901, '22', '003', 0, 822, 'decfloat_overflow', NULL, NULL) (-901, '22', '003', 0, 823, 'decfloat_underflow', NULL, NULL) +(-901, '42', '000', 0, 824, 'subfunc_notdef', NULL, NULL) +(-901, '42', '000', 0, 825, 'subproc_notdef', NULL, NULL) +(-901, '42', '000', 0, 826, 'subfunc_signat', NULL, NULL) +(-901, '42', '000', 0, 827, 'subproc_signat', NULL, NULL) +(-901, '42', '000', 0, 828, 'subfunc_defvaldecl', NULL, NULL) +(-901, '42', '000', 0, 829, 'subproc_defvaldecl', NULL, NULL) +(-901, '42', '000', 0, 830, 'subfunc_not_impl', NULL, NULL) +(-901, '42', '000', 0, 831, 'subproc_not_impl', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)