8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 10:43:03 +01:00
firebird-mirror/src/jrd/Coercion.cpp

354 lines
8.6 KiB
C++

/*
* PROGRAM: JRD access method
* MODULE: Coercion.cpp
* DESCRIPTION: Automatically coercing user datatypes
*
* The contents of this file are subject to the Initial
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* 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 Alex Peshkov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2019 Alex Peshkov <peshkoff@mail.ru>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
*/
#include "firebird.h"
#include "../jrd/Coercion.h"
#include "../jrd/cvt_proto.h"
#include "../dsql/dsql.h"
#include "../dsql/make_proto.h"
#include "../jrd/align.h"
#include "../jrd/DataTypeUtil.h"
using namespace Jrd;
using namespace Firebird;
static const USHORT FROM_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale |
FLD_has_sub | FLD_has_prec;
static const USHORT TO_MASK = FLD_has_len | FLD_has_chset | FLD_has_scale |
FLD_legacy | FLD_native | FLD_has_sub | FLD_has_prec | FLD_extended;
bool CoercionArray::coerce(thread_db* tdbb, dsc* d, unsigned startItem) const
{
// move down through array to ensure correct order: newer rule overrides older one
for (unsigned n = getCount(); n-- > startItem; )
{
if (getElement(n).coerce(tdbb, d))
return true;
}
return false;
}
void CoercionArray::setRule(const TypeClause* from, const TypeClause *to)
{
CoercionRule newRule;
newRule.setRule(from, to);
for (unsigned n = 0; n < getCount(); ++n)
{
if (getElement(n) == newRule)
{
remove(n);
break;
}
}
add(newRule);
}
void CoercionRule::raiseError()
{
// Do not use ERR_post here - old error should be overwritten
(Arg::Gds(isc_bind_convert) << fromDsc.typeToText() << toDsc.typeToText()).raise();
}
void CoercionRule::setRule(const TypeClause* from, const TypeClause *to)
{
fromMask = from->flags & FROM_MASK;
DsqlDescMaker::fromField(&fromDsc, from);
toMask = to->flags & TO_MASK;
DsqlDescMaker::fromField(&toDsc, to);
// Check for datatype compatibility
// No checks for special case
if (toMask & (FLD_native | FLD_legacy))
return;
// Extending timezone info
if (toMask & FLD_extended)
{
if (fromDsc.isDateTimeTz())
return;
raiseError();
}
// Exceptions - enable blob2blob & blob2string casts
if ((toDsc.dsc_dtype == dtype_blob && fromDsc.isText()) ||
(fromDsc.dsc_dtype == dtype_blob && toDsc.isText()) ||
(toDsc.isBlob() && fromDsc.isBlob()))
{
return;
}
// Disable the rest of casts with blobs
if (toDsc.isBlob() || fromDsc.isBlob())
raiseError();
// Generic check
const unsigned DATASIZE = 256;
UCHAR buf[DATASIZE * 2 + FB_ALIGNMENT];
memset(buf, 0, sizeof buf);
toDsc.dsc_address = FB_ALIGN(buf, FB_ALIGNMENT);
if (! (toMask & FLD_has_len))
{
toDsc.dsc_length = DATASIZE - 2;
}
fromDsc.dsc_address = toDsc.dsc_address + DATASIZE;
try
{
CVT_move(&fromDsc, &toDsc, 0);
}
catch(const Exception&)
{
raiseError();
}
}
dsc* CoercionRule::makeLegacy(USHORT mask)
{
toMask = FLD_legacy;
fromMask = mask;
return &fromDsc;
}
bool CoercionRule::operator==(const CoercionRule& rule) const
{
if (fromMask != rule.fromMask)
return false;
return match(&rule.fromDsc);
}
bool CoercionRule::match(const dsc* d) const
{
// check for exact match (taking flags into an account)
if ((d->dsc_dtype == fromDsc.dsc_dtype) &&
((d->dsc_length == fromDsc.dsc_length) || (!(fromMask & FLD_has_len))) &&
((d->getCharSet() == fromDsc.getCharSet()) || (!(fromMask & FLD_has_chset))) &&
((d->getSubType() == fromDsc.getSubType()) || (!(fromMask & FLD_has_sub))) &&
((d->dsc_scale == fromDsc.dsc_scale) || (!(fromMask & FLD_has_scale))))
{
return true;
}
// check for inexact datatype match when FLD_has_prec is not set
if (!(fromMask & FLD_has_prec))
{
switch(fromDsc.dsc_dtype)
{
case dtype_dec64:
case dtype_dec128:
if (DTYPE_IS_DECFLOAT(d->dsc_dtype))
return true;
break;
case dtype_short:
case dtype_long:
case dtype_int64:
case dtype_int128:
if (d->isExact() && (fromMask & FLD_has_sub) && (d->dsc_sub_type == fromDsc.dsc_sub_type))
return true;
break;
case dtype_timestamp_tz:
case dtype_sql_time_tz:
if (d->isDateTimeTz())
return true;
break;
}
}
return false;
}
static const USHORT COMPATIBLE_TEXT = 1;
static const USHORT COMPATIBLE_INT = 2;
static const USHORT subTypeCompatibility[DTYPE_TYPE_MAX] =
{
0, // dtype_unknown
COMPATIBLE_TEXT, // dtype_text
0, // dtype_cstring
COMPATIBLE_TEXT, // dtype_varying
0,
0,
0, // dtype_packed
0, // dtype_byte
COMPATIBLE_INT, // dtype_short -32768
COMPATIBLE_INT, // dtype_long -2147483648
0, // dtype_quad -9223372036854775808
0, // dtype_real -1.23456789e+12
0, // dtype_double -1.2345678901234567e+123
0, // dtype_d_float (believed to have this range) -1.2345678901234567e+123
0, // dtype_sql_date YYYY-MM-DD
0, // dtype_sql_time HH:MM:SS.MMMM
0, // dtype_timestamp YYYY-MM-DD HH:MM:SS.MMMM
0, // dtype_blob FFFF:FFFF
0, // dtype_array FFFF:FFFF
COMPATIBLE_INT, // dtype_int64 -9223372036854775808
0, // dtype_dbkey
0, // dtype_boolean
0, // dtype_dec64 1 + 1 + 1 + 1 + 16(34) + 3(4)
0, // dtype_dec128 +- . e +- coeff + exp
COMPATIBLE_INT, // dtype_int128
0, // dtype_sql_time_tz HH:MM:SS.MMMM +NN:NN
0, // dtype_timestamp_tz YYYY-MM-DD HH:MM:SS.MMMM +NN:NN
0, // dtype_ex_time_tz HH:MM:SS.MMMM +NN:NN
0, // dtype_ex_timestamp_tz YYYY-MM-DD HH:MM:SS.MMMM +NN:NN
};
bool CoercionRule::coerce(thread_db* tdbb, dsc* d) const
{
// check does descriptor match FROM clause
if (! match(d))
return false;
// native binding - do not touch descriptor at all
if (toMask & FLD_native)
return true;
// process legacy case
if (toMask & FLD_legacy)
{
bool found = true;
switch(d->dsc_dtype)
{
case dtype_dec64:
case dtype_dec128:
d->dsc_dtype = dtype_double;
d->dsc_length = sizeof(double);
break;
case dtype_sql_time_tz:
d->dsc_dtype = dtype_sql_time;
d->dsc_length = sizeof(ISC_TIME);
break;
case dtype_timestamp_tz:
d->dsc_dtype = dtype_timestamp;
d->dsc_length = sizeof(ISC_TIMESTAMP);
break;
case dtype_int128:
d->dsc_dtype = dtype_int64;
d->dsc_length = sizeof(SINT64);
break;
case dtype_boolean:
d->dsc_dtype = dtype_text;
d->dsc_length = 5;
break;
default:
found = false;
break;
}
return found;
}
// extending time zone
if (toMask & FLD_extended)
{
bool found = true;
switch(d->dsc_dtype)
{
case dtype_timestamp_tz:
d->dsc_dtype = dtype_ex_timestamp_tz;
d->dsc_length = sizeof(ISC_TIMESTAMP_TZ_EX);
break;
case dtype_sql_time_tz:
d->dsc_dtype = dtype_ex_time_tz;
d->dsc_length = sizeof(ISC_TIME_TZ_EX);
break;
default:
found = false;
break;
}
return found;
}
// final pass - order is important
const auto srcCharSet = d->getCharSet();
// scale
if (toMask & FLD_has_scale)
d->dsc_scale = toDsc.dsc_scale;
else if (!(DTYPE_IS_EXACT(d->dsc_dtype) && DTYPE_IS_EXACT(toDsc.dsc_dtype)))
d->dsc_scale = 0;
// subtype
if (toMask & FLD_has_sub ||
d->dsc_dtype >= DTYPE_TYPE_MAX || toDsc.dsc_dtype >= DTYPE_TYPE_MAX ||
subTypeCompatibility[d->dsc_dtype] == 0 ||
subTypeCompatibility[d->dsc_dtype] != subTypeCompatibility[toDsc.dsc_dtype])
{
d->dsc_sub_type = toDsc.dsc_sub_type;
}
// length
if (toMask & FLD_has_len)
d->dsc_length = toDsc.dsc_length;
// type
if (toMask & FLD_has_prec ||
subTypeCompatibility[d->dsc_dtype] != COMPATIBLE_INT ||
subTypeCompatibility[toDsc.dsc_dtype] != COMPATIBLE_INT)
{
if (!type_lengths[toDsc.dsc_dtype])
{
fb_assert(toDsc.isText());
d->dsc_length = d->getStringLength();
}
else
d->dsc_length = type_lengths[toDsc.dsc_dtype];
d->dsc_dtype = toDsc.dsc_dtype;
}
// charset
if (toMask & FLD_has_chset)
d->setTextType(toDsc.getTextType());
if (d->isText())
d->dsc_length = DataTypeUtil(tdbb).convertLength(d->dsc_length, srcCharSet, toDsc.getCharSet());
// varchar length
if (d->dsc_dtype == dtype_varying)
d->dsc_length += sizeof(USHORT);
// subtype - special processing for BLOBs
if (toMask & FLD_has_sub)
d->setBlobSubType(toDsc.getBlobSubType());
return true;
}