diff --git a/doc/sql.extensions/README.set_bind.md b/doc/sql.extensions/README.set_bind.md index 64aa458d49..d682bfea62 100644 --- a/doc/sql.extensions/README.set_bind.md +++ b/doc/sql.extensions/README.set_bind.md @@ -11,15 +11,30 @@ ### Syntax is: ```sql -SET BIND OF type1 TO type2; +SET BIND OF type-from TO { type-to | LEGACY }; SET BIND OF type NATIVE; ``` ### Description: -Makes it possible to define rules of describing types of returned to the client columns in non-standard way - `type1` is replaced with `type2`, automatic data coercion takes place. +Makes it possible to define rules of describing types of returned to the client columns in non-standard way - +`type-from` is replaced with `type-to`, automatic data coercion takes place. + +Except SQL-statement there are two more ways to specify data coercion - tag `isc_dpb_bind` in DPB +and `SetBind` parameter in firebird.conf & databases.conf. The later the rule is introduced (.conf->DPB->SQL) +the higher priotiy it has. I.e. one can override any preconfigured settings from SQL statement. + +When incomplete type definition is used (i.e. `CHAR` instead `CHAR(n)`) in left part of `SET BIND` coercion +will take place for all `CHAR` columns, not only default `CHAR(1)`. +When incomplete type definiton is used in right side of the statement (TO part) firebird engine will define missing +details about that type automatically based on source column. + +Special `TO` part format `LEGACY` is used when datatype, missing in previous FB version, should be represented in +a way, understandable by old client software (may be with some data losses). For example, `NUMERIC(38)` in legacy +form will be represented as `NUMERIC(18)`. + +Setting `NATIVE` means `type` will be used as if there were no previous rules for it. -When incomplete type definition is used (i.e. `CHAR` instead `CHAR(n)`) in left part of `SET BIND` coercion will take place for all `CHAR` columns, not only default `CHAR(1)`. When incomplete type definiton is used in right side of the statement firebird engine will define missing parts automatically based on source column. ### Samples: diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 9ce4ed9091..335e593826 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -8361,23 +8361,12 @@ void SetDecFloatTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/, jrd_t //-------------------- -void CoercionRule::setRule(TypeClause* from, TypeClause *to) -{ - static const USHORT FROM_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale; - static const USHORT TO_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale | FLD_legacy; - - fromMask = from->flags & FROM_MASK; - DsqlDescMaker::fromField(&fromDsc, from); - - toMask = to->flags & TO_MASK; - DsqlDescMaker::fromField(&toDsc, to); -} - - SessionManagementNode* SetBindNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { + static const USHORT NON_FIELD_MASK = FLD_legacy | FLD_native; + from->resolve(dsqlScratch); - if (!(to->flags & FLD_legacy)) + if (!(to->flags & NON_FIELD_MASK)) to->resolve(dsqlScratch); return SessionManagementNode::dsqlPass(dsqlScratch); diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index a5c4dc6b0d..3064feaf8b 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -329,7 +329,8 @@ enum fld_flags_vals { FLD_has_len = 16, FLD_has_chset = 32, FLD_has_scale = 64, - FLD_legacy = 128 + FLD_legacy = 128, + FLD_native = 256 }; //! Stored Procedure block diff --git a/src/dsql/parse.y b/src/dsql/parse.y index a4a31a6d71..2912950783 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -5287,8 +5287,8 @@ set_decfloat_traps set_bind : SET BIND OF set_bind_from { $$ = newNode(); $$->from = $4; } - TO set_bind_to - { $$ = $5; $$->to = $7; } + set_bind_to + { $$ = $5; $$->to = $6; } ; %type set_bind_from @@ -5298,15 +5298,20 @@ set_bind_from %type set_bind_to set_bind_to - : simple_type + : TO simple_type { - $$ = $1; + $$ = $2; } - | LEGACY + | TO LEGACY { $$ = newNode(); $$->flags = FLD_legacy; } + | NATIVE + { + $$ = newNode(); + $$->flags = FLD_native; + } ; %type decfloat_traps_list_opt() diff --git a/src/jrd/Coercion.cpp b/src/jrd/Coercion.cpp index 99e749cb55..c8e88836ab 100644 --- a/src/jrd/Coercion.cpp +++ b/src/jrd/Coercion.cpp @@ -29,11 +29,15 @@ #include "../jrd/Coercion.h" #include "../dsql/dsql.h" +#include "../dsql/make_proto.h" #include "../jrd/align.h" using namespace Jrd; using namespace Firebird; +static const USHORT FROM_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale; +static const USHORT TO_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale | FLD_legacy | FLD_native; + bool CoercionArray::coerce(dsc* d) const { // move down through array to ensure correct order: newer rule overrides older one @@ -46,7 +50,16 @@ bool CoercionArray::coerce(dsc* d) const return false; } -bool CoercionRule::coerce(dsc* d) const +void CoercionRule::setRule(TypeClause* from, TypeClause *to) +{ + fromMask = from->flags & FROM_MASK; + DsqlDescMaker::fromField(&fromDsc, from); + + toMask = to->flags & TO_MASK; + DsqlDescMaker::fromField(&toDsc, to); +} + +bool CoercionRule::match(dsc* d) const { bool found = false; @@ -75,14 +88,24 @@ bool CoercionRule::coerce(dsc* d) const } } - if (!found) + return found; +} + +bool CoercionRule::coerce(dsc* d) const +{ + // check does descriptor match FROM clause + if (! match(d)) return false; - // now define output descriptor - // first of all process LEGACY case + // native binding - do not touch descriptor at all + if (toMask & FLD_native) + return true; + + // process legacy case if (toMask & FLD_legacy) { - found = true; + bool found = true; + switch(d->dsc_dtype) { case dtype_dec64: diff --git a/src/jrd/Coercion.h b/src/jrd/Coercion.h index 14b15f5453..5e6b28efc2 100644 --- a/src/jrd/Coercion.h +++ b/src/jrd/Coercion.h @@ -50,6 +50,7 @@ public: void setRule(TypeClause* from, TypeClause *to); bool coerce(dsc* d) const; + bool match(dsc* d) const; private: dsc fromDsc, toDsc;