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

537 lines
15 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
2002-10-29 17:27:47 +01:00
* MODULE: cvt.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Data mover and converter and comparator, etc.
*
* 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): ______________________________________.
2002-02-16 04:05:21 +01:00
*
2002-02-16 06:06:17 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete ports:
* - DELTA and IMP
2002-02-16 04:05:21 +01:00
*
2002-06-29 15:03:13 +02:00
* 2001.6.16 Claudio Valderrama: Wiped out the leading space in
* cast(float_expr as char(n)) in dialect 1, reported in SF.
* 2001.11.19 Claudio Valderrama: integer_to_text() should use the
* source descriptor "from" to call conversion_error.
2002-10-29 03:45:09 +01:00
*
* 2002.10.28 Sean Leyne - Completed removal of obsolete "DGUX" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../jrd/val.h"
#include "gen/iberror.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/intl.h"
#include "../jrd/gdsassert.h"
#include "../jrd/cvt_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/intl_proto.h"
#include "../common/classes/timestamp.h"
#include "../common/cvt.h"
2001-05-23 15:26:42 +02:00
2001-07-12 07:46:06 +02:00
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
2002-06-04 21:56:16 +02:00
#include "../jrd/intl_classes.h"
2001-05-23 15:26:42 +02:00
#ifdef sun
#define DBL_MAX_10_EXP 308
#endif
#ifdef SCO_UNIX
#define DBL_MAX_10_EXP 308
#endif
#ifndef DBL_MAX_10_EXP
#include <float.h>
#endif
/* ERR_post is used to flag whether we were called from mov.cpp or
2001-05-23 15:26:42 +02:00
anywhere else CVT is used from (by comparing with param err) */
/* normally the following two definitions are part of limits.h
but due to a compiler bug on Apollo casting LONG_MIN to be a
double, these have to be defined as double numbers...
Also, since SunOS4.0 doesn't want to play with the rest of
the ANSI world, these definitions have to be included explicitly.
So, instead of some including <limits.h> and others using these
definitions, just always use these definitions (huh?) */
#define LONG_MIN_real -2147483648. /* min decimal value of an "SLONG" */
#define LONG_MAX_real 2147483647. /* max decimal value of an "SLONG" */
#define LONG_MIN_int -2147483648 /* min integer value of an "SLONG" */
#define LONG_MAX_int 2147483647 /* max integer value of an "SLONG" */
/* It turns out to be tricky to write the INT64 versions of those constant in
a way that will do the right thing on all platforms. Here we go. */
#define LONG_MAX_int64 ((SINT64)2147483647) /* max int64 value of an SLONG */
2004-12-08 06:58:41 +01:00
#define LONG_MIN_int64 (-LONG_MAX_int64 - 1) /* min int64 value of an SLONG */
2001-05-23 15:26:42 +02:00
#define FLOAT_MAX 3.4e38 /* max float (32 bit) value */
#define LETTER7(c) ((c) >= 'A' && (c) <= 'Z')
#define DIGIT(c) ((c) >= '0' && (c) <= '9')
#define ABSOLUT(x) ((x) < 0 ? -(x) : (x))
/* The expressions for SHORT_LIMIT, LONG_LIMIT, INT64_LIMIT and
* QUAD_LIMIT return the largest value that permit you to multiply by
* 10 without getting an overflow. The right operand of the << is two
* less than the number of bits in the type: one bit is for the sign,
* and the other is because we divide by 5, rather than 10. */
#define SHORT_LIMIT ((1 << 14) / 5)
#define LONG_LIMIT ((1L << 30) / 5)
/* NOTE: The syntax for the below line may need modification to ensure
* the result of 1 << 62 is a quad
*/
#define INT64_LIMIT ((((SINT64) 1) << 62) / 5)
#define NUMERIC_LIMIT (INT64_LIMIT)
#define TODAY "TODAY"
#define NOW "NOW"
#define TOMORROW "TOMORROW"
#define YESTERDAY "YESTERDAY"
#define CVT_FAILURE_SPACE 128
2004-11-24 10:22:07 +01:00
#define CVT_COPY_BUFF(from, to, len) \
2008-01-16 09:31:31 +01:00
{if (len) {memcpy(to, from, len); from += len; to += len; len = 0;} }
2001-05-23 15:26:42 +02:00
enum EXPECT_DATETIME {
2001-05-23 15:26:42 +02:00
expect_timestamp,
expect_sql_date,
expect_sql_time
2004-05-24 19:31:47 +02:00
};
2001-05-23 15:26:42 +02:00
static const double eps_double = 1e-14;
static const double eps_float = 1e-5;
using namespace Jrd;
using namespace Firebird;
2001-05-23 15:26:42 +02:00
double CVT_date_to_double(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ d a t e _ t o _ d o u b l e
*
**************************************
*
* Functional description
* Convert a date to double precision for
* date arithmetic routines.
*
**************************************/
SLONG temp[2], *date;
2008-01-16 09:31:31 +01:00
// If the input descriptor is not in date form, convert it.
2001-05-23 15:26:42 +02:00
2008-01-16 09:31:31 +01:00
switch (desc->dsc_dtype)
{
case dtype_timestamp:
2003-10-05 09:04:56 +02:00
date = (SLONG*) desc->dsc_address;
2008-01-16 09:31:31 +01:00
break;
case dtype_sql_time:
// Temporarily convert the time to a timestamp for conversion
2001-05-23 15:26:42 +02:00
date = temp;
date[0] = 0;
2003-10-05 09:04:56 +02:00
date[1] = *(SLONG*) desc->dsc_address;
2008-01-16 09:31:31 +01:00
break;
case dtype_sql_date:
// Temporarily convert the date to a timestamp for conversion
2001-05-23 15:26:42 +02:00
date = temp;
2003-10-05 09:04:56 +02:00
date[0] = *(SLONG*) desc->dsc_address;
2001-05-23 15:26:42 +02:00
date[1] = 0;
2008-01-16 09:31:31 +01:00
break;
default:
{
// Unknown type - most likely a string. Try to convert it to a
// timestamp -- or die trying (reporting an error).
// Don't worry about users putting a TIME or DATE here - this
// conversion is occuring in really flexible spots - we don't
// want to overdo it.
dsc temp_desc;
MOVE_CLEAR(&temp_desc, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_timestamp;
temp_desc.dsc_length = sizeof(temp);
date = temp;
temp_desc.dsc_address = (UCHAR*) date;
CVT_move(desc, &temp_desc);
2008-01-16 09:31:31 +01:00
}
2001-05-23 15:26:42 +02:00
}
/* Instead of returning the calculated double value in the return
statement, am assigning the value to a local volatile double
variable and returning that. This is to prevent a specific kind of
precision error caused on Intel platforms (SCO and Linux) due
to FPU register being 80 bits long and double being 64 bits long */
2003-12-22 11:00:59 +01:00
volatile double retval;
2001-05-23 15:26:42 +02:00
retval =
date[0] +
(double) date[1] / (24. * 60. * 60. * ISC_TIME_SECONDS_PRECISION);
return retval;
}
void CVT_double_to_date(double real, SLONG fixed[2])
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ d o u b l e _ t o _ d a t e
*
**************************************
*
* Functional description
* Convert a double precision representation of a date
* to a fixed point representation. Double is used for
* date arithmetic.
*
**************************************/
fixed[0] = (SLONG) real;
fixed[1] =
(SLONG) ((real - fixed[0]) * 24. * 60. * 60. *
ISC_TIME_SECONDS_PRECISION);
}
2003-10-16 10:51:06 +02:00
UCHAR CVT_get_numeric(const UCHAR* string,
const USHORT length,
SSHORT* scale, double* ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ g e t _ n u m e r i c
*
**************************************
*
* Functional description
* Convert a numeric literal (string) to its binary value.
*
* If the literal contains an exponent or is too large to fit
* in an int64, return a double, else if the literal is too
* large to fit in a long, return an int64, else return a long.
*
* The return value from the function is set to dtype_double,
* dtype_int64, or dtype_long depending on the conversion performed.
* The binary value (long, int64, or double) is stored at the
* address given by ptr.
*
*
**************************************/
2003-10-05 09:04:56 +02:00
dsc desc;
2001-05-23 15:26:42 +02:00
MOVE_CLEAR(&desc, sizeof(desc));
desc.dsc_dtype = dtype_text;
desc.dsc_ttype() = ttype_ascii;
2001-05-23 15:26:42 +02:00
desc.dsc_length = length;
2003-10-16 10:51:06 +02:00
desc.dsc_address = const_cast<UCHAR*>(string);
// The above line allows the assignment, but "string" is treated as const
// for all the purposes here.
2001-05-23 15:26:42 +02:00
2003-10-05 09:04:56 +02:00
SINT64 value = 0;
SSHORT local_scale = 0, sign = 0;
bool digit_seen = false, fraction = false;
2001-05-23 15:26:42 +02:00
2003-10-05 09:04:56 +02:00
const UCHAR* p = string;
const UCHAR* const end = p + length;
for (; p < end; p++) {
2001-05-23 15:26:42 +02:00
if (DIGIT(*p)) {
2003-10-05 09:04:56 +02:00
digit_seen = true;
2001-05-23 15:26:42 +02:00
/* Before computing the next value, make sure there will be
no overflow. Trying to detect overflow after the fact is
tricky: the value doesn't always become negative after an
overflow! */
if (value >= NUMERIC_LIMIT) {
/* possibility of an overflow */
if (value > NUMERIC_LIMIT)
break;
2008-01-16 09:31:31 +01:00
if ((*p > '8' && sign == -1) || (*p > '7' && sign != -1))
2001-05-23 15:26:42 +02:00
break;
}
/* Force the subtraction to be performed before the addition,
thus preventing a possible signed arithmetic overflow. */
value = value * 10 + (*p - '0');
if (fraction)
--local_scale;
}
else if (*p == '.') {
if (fraction)
CVT_conversion_error(&desc, ERR_post);
2001-05-23 15:26:42 +02:00
else
2003-10-05 09:04:56 +02:00
fraction = true;
2001-05-23 15:26:42 +02:00
}
else if (*p == '-' && !digit_seen && !sign && !fraction)
sign = -1;
else if (*p == '+' && !digit_seen && !sign && !fraction)
sign = 1;
else if (*p == 'e' || *p == 'E')
break;
else if (*p != ' ')
CVT_conversion_error(&desc, ERR_post);
2001-05-23 15:26:42 +02:00
}
if (!digit_seen)
CVT_conversion_error(&desc, ERR_post);
2001-05-23 15:26:42 +02:00
if ((p < end) || /* there is an exponent */
2003-12-22 11:00:59 +01:00
((value < 0) && (sign != -1))) /* MAX_SINT64+1 wrapped around */
{
2001-05-23 15:26:42 +02:00
/* convert to double */
*ptr = CVT_get_double(&desc, ERR_post);
2001-05-23 15:26:42 +02:00
return dtype_double;
}
*scale = local_scale;
/* The literal has already been converted to a 64-bit integer: return
a long if the value fits into a long, else return an int64. */
if ((value <= LONG_MAX_int64) && (value >= 0)) {
*(SLONG *) ptr = (SLONG) ((sign == -1) ? -value : value);
return dtype_long;
}
2008-01-16 09:31:31 +01:00
if ((sign == -1) && (-value == LONG_MIN_int64)) {
2001-05-23 15:26:42 +02:00
*(SLONG *) ptr = LONG_MIN;
return dtype_long;
}
2008-01-16 09:31:31 +01:00
/* Either MAX_SLONG < value <= MAX_SINT64, or
((value == MIN_SINT64) && (sign == -1)).
In the first case, the number can be negated, while in the second
negating the value will not change it on a 2s-complement system. */
*(SINT64 *) ptr = ((sign == -1) ? -value : value);
return dtype_int64;
2001-05-23 15:26:42 +02:00
}
GDS_DATE CVT_get_sql_date(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ g e t _ s q l _ d a t e
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL date value
*
**************************************/
if (desc->dsc_dtype == dtype_sql_date)
return *((GDS_DATE *) desc->dsc_address);
2003-12-22 11:00:59 +01:00
DSC temp_desc;
GDS_DATE value;
2001-05-23 15:26:42 +02:00
memset(&temp_desc, 0, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_sql_date;
2003-10-05 09:04:56 +02:00
temp_desc.dsc_address = (UCHAR *) &value;
CVT_move(desc, &temp_desc);
2001-05-23 15:26:42 +02:00
return value;
}
GDS_TIME CVT_get_sql_time(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ g e t _ s q l _ t i m e
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL time value
*
**************************************/
if (desc->dsc_dtype == dtype_sql_time)
return *((GDS_TIME *) desc->dsc_address);
2003-12-22 11:00:59 +01:00
DSC temp_desc;
GDS_TIME value;
2001-05-23 15:26:42 +02:00
memset(&temp_desc, 0, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_sql_time;
2003-10-05 09:04:56 +02:00
temp_desc.dsc_address = (UCHAR *) &value;
CVT_move(desc, &temp_desc);
2001-05-23 15:26:42 +02:00
return value;
}
GDS_TIMESTAMP CVT_get_timestamp(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ g e t _ t i m e s t a m p
*
**************************************
*
* Functional description
* Convert something arbitrary to a SQL timestamp
*
**************************************/
if (desc->dsc_dtype == dtype_timestamp)
return *((GDS_TIMESTAMP *) desc->dsc_address);
2003-12-22 11:00:59 +01:00
DSC temp_desc;
GDS_TIMESTAMP value;
2001-05-23 15:26:42 +02:00
memset(&temp_desc, 0, sizeof(temp_desc));
temp_desc.dsc_dtype = dtype_timestamp;
2003-10-05 09:04:56 +02:00
temp_desc.dsc_address = (UCHAR *) &value;
CVT_move(desc, &temp_desc);
2001-05-23 15:26:42 +02:00
return value;
}
static bool transliterate(const dsc* from, dsc* to, CHARSET_ID& charset2, ErrorFunction err)
2001-05-23 15:26:42 +02:00
{
UCHAR* p = to->dsc_address;
const UCHAR* q = from->dsc_address;
CHARSET_ID charset1;
if (INTL_TTYPE(from) == ttype_dynamic)
charset1 = INTL_charset(NULL, INTL_TTYPE(from));
else
charset1 = INTL_TTYPE(from);
if (INTL_TTYPE(to) == ttype_dynamic)
charset2 = INTL_charset(NULL, INTL_TTYPE(to));
else
charset2 = INTL_TTYPE(to);
/* The charset[12] can be ttype_dynamic only if we are
outside the engine. Within the engine INTL_charset
would have set it to the ttype of the attachment */
if ((charset1 != charset2) &&
(charset2 != ttype_none) &&
(charset1 != ttype_binary) &&
(charset2 != ttype_binary) &&
(charset1 != ttype_dynamic) && (charset2 != ttype_dynamic))
{
INTL_convert_string(to, from, err);
return true;
2001-05-23 15:26:42 +02:00
}
return false;
}
2001-05-23 15:26:42 +02:00
static CharSet* getToCharset(CHARSET_ID charset2)
{
return charset2 == ttype_dynamic || charset2 == CS_METADATA ? NULL :
INTL_charset_lookup(NULL, charset2);
2001-05-23 15:26:42 +02:00
}
static void validateData(CharSet* toCharSet, SLONG length, const UCHAR* q, ErrorFunction err)
2001-05-23 15:26:42 +02:00
{
if (toCharSet && !toCharSet->wellFormed(length, q))
err(Arg::Gds(isc_malformed_string));
2001-05-23 15:26:42 +02:00
}
static void validateLength(CharSet* toCharSet, SLONG toLength, UCHAR* start, const USHORT to_size, ErrorFunction err)
2001-05-23 15:26:42 +02:00
{
if (toCharSet)
{
Jrd::thread_db* tdbb = NULL;
SET_TDBB(tdbb);
2001-05-23 15:26:42 +02:00
if (toCharSet->isMultiByte() &&
!(toCharSet->getFlags() & CHARSET_LEGACY_SEMANTICS) &&
toLength != 31 && // allow non CHARSET_LEGACY_SEMANTICS to be used as connection charset
toCharSet->length(toLength, start, false) > to_size / toCharSet->maxBytesPerChar())
{
err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_transliteration_failed));
2003-12-22 11:00:59 +01:00
}
}
2001-05-23 15:26:42 +02:00
}
static CHARSET_ID getChid(const dsc* to)
2001-05-23 15:26:42 +02:00
{
if (INTL_TTYPE(to) == ttype_dynamic)
return INTL_charset(NULL, INTL_TTYPE(to));
return INTL_TTYPE(to);
2001-05-23 15:26:42 +02:00
}
static SLONG getCurDate()
2001-05-23 15:26:42 +02:00
{
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest())
2003-10-05 09:04:56 +02:00
{
fb_assert(!tdbb->getRequest()->req_timestamp.isEmpty());
return tdbb->getRequest()->req_timestamp.value().timestamp_date;
2003-10-05 09:04:56 +02:00
}
2001-05-23 15:26:42 +02:00
return Firebird::TimeStamp::getCurrentTimeStamp().value().timestamp_date;
2001-05-23 15:26:42 +02:00
}
static void isVersion4(bool& v4)
2001-05-23 15:26:42 +02:00
{
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
if (tdbb && (tdbb->getType() == ThreadData::tddDBB) && tdbb->getRequest())
2002-06-29 15:03:13 +02:00
{
v4 = tdbb->getRequest()->req_flags & req_blr_version4 ? true : false;
2002-06-29 15:03:13 +02:00
}
2001-05-23 15:26:42 +02:00
}
static Callbacks toEngine = {transliterate, getChid, ERR_post, getToCharset,
validateData, validateLength, getCurDate, isVersion4};
2001-05-23 15:26:42 +02:00
void CVT_move(const dsc* from, dsc* to)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* C V T _ m o v e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Move (and possible convert) something to something else.
2001-05-23 15:26:42 +02:00
*
**************************************/
CVT_move_common(from, to, &toEngine);
}