8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-24 18:43:02 +01:00
firebird-mirror/src/qli/mov.cpp

1609 lines
33 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: QLI Access Method
* MODULE: mov.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): ______________________________________.
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../qli/dtr.h"
#include "../jrd/jrd_time.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/intl.h"
#include "../qli/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/gdsassert.h"
2001-05-23 15:26:42 +02:00
#include "../qli/mov_proto.h"
static void date_error(const TEXT*, const USHORT);
2001-05-23 15:26:42 +02:00
static void timestamp_to_text(SLONG[2], DSC *);
static void sql_time_to_text(ULONG[1], DSC *);
static void sql_date_to_text(SLONG[1], DSC *);
static void mover_error(int, USHORT, USHORT);
2003-10-08 10:42:48 +02:00
static void now_to_date(const tm*, SLONG[2]);
static void numeric_to_text(const dsc*, dsc*);
static void string_to_date(const TEXT*, USHORT, SLONG[2]);
2001-05-23 15:26:42 +02:00
static void string_to_time(TEXT *, USHORT, SLONG[2]);
static TEXT *type_name(USHORT);
2001-05-23 15:26:42 +02:00
#define LETTER(c) (c >= 'A' && c <= 'Z')
#define DIGIT(c) (c >= '0' && c <= '9')
#define TODAY "TODAY"
#define NOW "NOW"
#define TOMORROW "TOMORROW"
#define YESTERDAY "YESTERDAY"
#define PRECISION 10000
static const TEXT *months[] = {
2001-05-23 15:26:42 +02:00
"JANUARY",
"FEBRUARY",
"MARCH",
"APRIL",
"MAY",
"JUNE",
"JULY",
"AUGUST",
"SEPTEMBER",
"OCTOBER",
"NOVEMBER",
"DECEMBER",
0
};
struct dtypes_t {
2001-05-23 15:26:42 +02:00
USHORT type;
TEXT *description;
};
2001-05-23 15:26:42 +02:00
2003-09-23 12:13:39 +02:00
static const dtypes_t dtypes_table[] = {
{ dtype_null, "NULL" },
{ dtype_text, "character string" },
{ dtype_cstring, "character string" },
{ dtype_varying, "varying string" },
{ dtype_packed, "packed decimal" },
{ dtype_byte, "byte integer" },
{ dtype_short, "short integer" },
{ dtype_long, "long integer" },
{ dtype_quad, "quadword integer" },
{ dtype_real, "single precision floating" },
{ dtype_double, "double precision floating" },
{ dtype_d_float, "double precision floating" },
{ dtype_timestamp, "date" },
{ dtype_sql_date, "SQL date" },
{ dtype_sql_time, "SQL time" },
{ dtype_blob, "blob" },
{ 0, 0 }
2001-05-23 15:26:42 +02:00
};
int MOVQ_compare(const dsc* arg1, const dsc* arg2)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ c o m p a r e
*
**************************************
*
* Functional description
* Compare two descriptors. Return (-1, 0, 1) if a<b, a=b, or a>b.
*
**************************************/
SLONG date[2];
/* Handle the simple (matched) ones first */
if (arg1->dsc_dtype == arg2->dsc_dtype &&
arg1->dsc_scale == arg2->dsc_scale)
{
const UCHAR* p1 = arg1->dsc_address;
const UCHAR* p2 = arg2->dsc_address;
2001-05-23 15:26:42 +02:00
switch (arg1->dsc_dtype) {
case dtype_short:
if (*(SSHORT *) p1 == *(SSHORT *) p2)
return 0;
if (*(SSHORT *) p1 > *(SSHORT *) p2)
return 1;
return -1;
case dtype_long:
case dtype_sql_date:
case dtype_sql_time:
if (*(SLONG *) p1 == *(SLONG *) p2)
return 0;
if (*(SLONG *) p1 > *(SLONG *) p2)
return 1;
return -1;
case dtype_timestamp:
case dtype_quad:
if (((SLONG *) p1)[0] < ((SLONG *) p2)[0])
return -1;
if (((SLONG *) p1)[0] > ((SLONG *) p2)[0])
return 1;
if (((ULONG *) p1)[1] < ((ULONG *) p2)[1])
return -1;
if (((ULONG *) p1)[1] > ((ULONG *) p2)[1])
return 1;
return 0;
case dtype_real:
if (*(float *) p1 == *(float *) p2)
return 0;
if (*(float *) p1 > *(float *) p2)
return 1;
return -1;
case dtype_double:
if (*(double *) p1 == *(double *) p2)
return 0;
if (*(double *) p1 > *(double *) p2)
return 1;
return -1;
case dtype_text:
{
SSHORT length;
if (arg1->dsc_length >= arg2->dsc_length) {
if (length = arg2->dsc_length)
do {
if (*p1++ != *p2++)
if (p1[-1] > p2[-1])
return 1;
else
return -1;
} while (--length);
if (length = arg1->dsc_length - arg2->dsc_length)
do {
if (*p1++ != ' ')
if (p1[-1] > ' ')
return 1;
else
return -1;
} while (--length);
return 0;
}
if (length = arg1->dsc_length)
do {
2001-05-23 15:26:42 +02:00
if (*p1++ != *p2++)
if (p1[-1] > p2[-1])
return 1;
else
return -1;
} while (--length);
length = arg2->dsc_length - arg1->dsc_length;
do {
if (*p2++ != ' ')
if (' ' > p2[-1])
2001-05-23 15:26:42 +02:00
return 1;
else
return -1;
} while (--length);
return 0;
}
2001-05-23 15:26:42 +02:00
}
}
// Handle mixed string comparisons
2001-05-23 15:26:42 +02:00
if (arg1->dsc_dtype <= dtype_varying && arg2->dsc_dtype <= dtype_varying) {
const UCHAR* p1;
const UCHAR* p2;
SSHORT length = MOVQ_get_string(arg1, (TEXT**) &p1, 0, 0);
SSHORT length2 = MOVQ_get_string(arg2, (TEXT**) &p2, 0, 0);
SSHORT fill = length - length2;
2001-05-23 15:26:42 +02:00
if (length >= length2) {
if (length2)
do {
2001-05-23 15:26:42 +02:00
if (*p1++ != *p2++)
if (p1[-1] > p2[-1])
return 1;
else
return -1;
} while (--length2);
2001-05-23 15:26:42 +02:00
if (fill > 0)
do {
2001-05-23 15:26:42 +02:00
if (*p1++ != ' ')
if (p1[-1] > ' ')
return 1;
else
return -1;
} while (--fill);
2001-05-23 15:26:42 +02:00
return 0;
}
if (length) {
do {
2001-05-23 15:26:42 +02:00
if (*p1++ != *p2++)
if (p1[-1] > p2[-1])
return 1;
else
return -1;
} while (--length);
}
do {
2001-05-23 15:26:42 +02:00
if (*p2++ != ' ')
if (' ' > p2[-1])
return 1;
else
return -1;
} while (++fill);
2001-05-23 15:26:42 +02:00
return 0;
}
// Handle hetergeneous compares
2001-05-23 15:26:42 +02:00
if (arg1->dsc_dtype < arg2->dsc_dtype)
return (-MOVQ_compare(arg2, arg1));
dsc desc;
2001-05-23 15:26:42 +02:00
switch (arg1->dsc_dtype) {
case dtype_timestamp:
desc.dsc_dtype = dtype_timestamp;
desc.dsc_length = sizeof(date);
desc.dsc_scale = 0;
desc.dsc_address = (UCHAR *) date;
desc.dsc_sub_type = 0;
MOVQ_move(arg2, &desc);
return MOVQ_compare(arg1, &desc);
case dtype_sql_time:
desc.dsc_dtype = dtype_sql_time;
desc.dsc_length = sizeof(date[0]);
desc.dsc_scale = 0;
desc.dsc_address = (UCHAR *) date;
desc.dsc_sub_type = 0;
MOVQ_move(arg2, &desc);
return MOVQ_compare(arg1, &desc);
case dtype_sql_date:
desc.dsc_dtype = dtype_sql_date;
desc.dsc_length = sizeof(date[0]);
desc.dsc_scale = 0;
desc.dsc_address = (UCHAR *) date;
desc.dsc_sub_type = 0;
MOVQ_move(arg2, &desc);
return MOVQ_compare(arg1, &desc);
case dtype_short:
case dtype_long:
{
const SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
2001-05-23 15:26:42 +02:00
const SLONG temp1 = MOVQ_get_long(arg1, scale);
const SLONG temp2 = MOVQ_get_long(arg2, scale);
2001-05-23 15:26:42 +02:00
if (temp1 == temp2)
return 0;
if (temp1 > temp2)
return 1;
return -1;
}
case dtype_real:
{
const float temp1 = MOVQ_get_double(arg1);
const float temp2 = MOVQ_get_double(arg2);
2001-05-23 15:26:42 +02:00
if (temp1 == temp2)
return 0;
if (temp1 > temp2)
return 1;
return -1;
}
case dtype_double:
{
const double temp1 = MOVQ_get_double(arg1);
const double temp2 = MOVQ_get_double(arg2);
2001-05-23 15:26:42 +02:00
if (temp1 == temp2)
return 0;
if (temp1 > temp2)
return 1;
return -1;
}
case dtype_blob:
IBERROR(48); // Msg 48 Blob conversion is not supported
2001-05-23 15:26:42 +02:00
default:
mover_error(410, arg1->dsc_dtype, arg2->dsc_dtype);
}
2002-06-29 10:49:39 +02:00
return -1;
2001-05-23 15:26:42 +02:00
}
double MOVQ_date_to_double(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ 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;
// If the input descriptor is not in date form, convert it.
2001-05-23 15:26:42 +02:00
if (desc->dsc_dtype == dtype_timestamp)
date = (SLONG *) desc->dsc_address;
else {
dsc temp_desc;
2001-05-23 15:26:42 +02:00
temp_desc.dsc_dtype = dtype_timestamp;
temp_desc.dsc_length = sizeof(temp);
temp_desc.dsc_scale = 0;
temp_desc.dsc_sub_type = 0;
date = temp;
temp_desc.dsc_address = (UCHAR *) date;
MOVQ_move(desc, &temp_desc);
}
return date[0] + (double) date[1] / (24. * 60. * 60. * PRECISION);
}
int MOVQ_decompose(const TEXT* string, USHORT length, SLONG* return_value)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ d e c o m p o s e
*
**************************************
*
* Functional description
* Decompose a numeric string in mantissa and exponent.
*
**************************************/
SSHORT scale = 0;
SLONG value = 0;
2001-05-23 15:26:42 +02:00
SCHAR temp[128];
bool sign = false;
bool fraction = false;
2001-05-23 15:26:42 +02:00
const TEXT* p = string;
const TEXT* const end = p + length;
for (; p < end; p++) {
2001-05-23 15:26:42 +02:00
if (*p == ',')
continue;
else if (DIGIT(*p)) {
value = value * 10 + *p - '0';
if (fraction)
--scale;
}
else if (*p == '.')
if (fraction) {
MOVQ_terminate(string, temp, length, sizeof(temp));
ERRQ_error(411, temp, NULL, NULL, NULL, NULL);
}
else
fraction = true;
2001-05-23 15:26:42 +02:00
else if (*p == '-' && !value && !sign)
sign = true;
2001-05-23 15:26:42 +02:00
else if (*p == '+' && !value && !sign)
sign = false;
2001-05-23 15:26:42 +02:00
else if (*p == 'e' || *p == 'E')
break;
else if (*p != ' ') {
MOVQ_terminate(string, temp, length, sizeof(temp));
ERRQ_error(411, temp, NULL, NULL, NULL, NULL);
}
}
if (sign)
value = -value;
/* If there's still something left, there must be an explicit
exponent */
if (p < end) {
SSHORT exp = 0;
sign = false;
2001-05-23 15:26:42 +02:00
for (p++; p < end; p++) {
if (DIGIT(*p))
exp = exp * 10 + *p - '0';
else if (*p == '-' && !exp)
sign = true;
2001-05-23 15:26:42 +02:00
else if (*p == '+' && !exp && !sign)
continue;
else if (*p != ' ') {
MOVQ_terminate(string, temp, length, sizeof(temp));
ERRQ_error(411, temp, NULL, NULL, NULL, NULL);
}
}
if (sign)
scale -= exp;
else
scale += exp;
}
*return_value = value;
return scale;
}
void MOVQ_double_to_date( double real, SLONG fixed[2])
{
/**************************************
*
* M O V Q _ 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] = real;
fixed[1] = (real - fixed[0]) * 24. * 60. * 60. * PRECISION;
}
void MOVQ_fast(const SCHAR* from,
SCHAR* to,
ULONG length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ f a s t
*
**************************************
*
* Functional description
* Move a byte string as fast as possible.
*
**************************************/
if (length)
do {
2001-05-23 15:26:42 +02:00
*to++ = *from++;
} while (--length);
2001-05-23 15:26:42 +02:00
}
double MOVQ_get_double(const dsc* desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ g e t _ d o u b l e
*
**************************************
*
* Functional description
* Convert something arbitrary to a
* double_precision representation.
2001-05-23 15:26:42 +02:00
*
**************************************/
double value;
SSHORT scale, fraction, sign, exp, length;
TEXT *p, *end;
switch (desc->dsc_dtype) {
case dtype_short:
value = *((SSHORT *) desc->dsc_address);
break;
case dtype_long:
value = *((SLONG *) desc->dsc_address);
break;
case dtype_real:
return *((float *) desc->dsc_address);
case dtype_double:
return *((double *) desc->dsc_address);
case dtype_varying:
case dtype_cstring:
case dtype_text:
length = MOVQ_get_string(desc, &p, 0, 0);
scale = fraction = sign = value = 0;
for (end = p + length; p < end; p++) {
if (*p == ',')
continue;
else if (DIGIT(*p)) {
value = value * 10. + (*p - '0');
if (fraction)
scale++;
}
else if (*p == '.')
if (fraction)
IBERROR(52); // Msg 52 conversion error
2001-05-23 15:26:42 +02:00
else
fraction = 1;
else if (!value && *p == '-')
sign = 1;
else if (!value && *p == '+')
continue;
else if (*p == 'e' || *p == 'E')
break;
else if (*p != ' ')
IBERROR(53); // Msg 53 conversion error
2001-05-23 15:26:42 +02:00
}
if (sign)
value = -value;
/* If there's still something left, there must be an explicit
exponent */
if (p < end) {
sign = exp = 0;
for (p++; p < end; p++) {
if (DIGIT(*p))
exp = exp * 10 + *p - '0';
else if (*p == '-' && !exp)
sign = TRUE;
else if (*p == '+' && !exp);
else if (*p != ' ')
IBERROR(54); // Msg 54 conversion error
2001-05-23 15:26:42 +02:00
}
if (sign)
scale += exp;
else
scale -= exp;
}
if (scale > 0)
do {
2001-05-23 15:26:42 +02:00
value /= 10.;
} while (--scale);
2001-05-23 15:26:42 +02:00
else if (scale)
do {
2001-05-23 15:26:42 +02:00
value *= 10.;
} while (++scale);
2001-05-23 15:26:42 +02:00
return value;
default:
mover_error(410, desc->dsc_dtype, dtype_double);
}
// Last, but not least, adjust for scale
2001-05-23 15:26:42 +02:00
if ((scale = desc->dsc_scale) == 0)
return value;
if (scale > 0)
do {
2001-05-23 15:26:42 +02:00
value *= 10.;
} while (--scale);
2001-05-23 15:26:42 +02:00
else
do {
2001-05-23 15:26:42 +02:00
value /= 10.;
} while (++scale);
2001-05-23 15:26:42 +02:00
return value;
}
SLONG MOVQ_get_long(const dsc* desc, SSHORT scale)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ g e t _ l o n g
*
**************************************
*
* Functional description
* Convert something arbitrary to a long (32 bit) integer of given
* scale.
*
**************************************/
SLONG value;
double d;
SSHORT length;
2001-05-23 15:26:42 +02:00
scale -= (SSHORT) desc->dsc_scale;
TEXT* p = (TEXT *) desc->dsc_address;
2001-05-23 15:26:42 +02:00
switch (desc->dsc_dtype) {
case dtype_short:
value = *((SSHORT *) p);
break;
case dtype_long:
value = *((SLONG *) p);
break;
case dtype_real:
d = *((float *) p);
if (scale > 0)
do {
2001-05-23 15:26:42 +02:00
d /= 10.;
} while (--scale);
2001-05-23 15:26:42 +02:00
else if (scale < 0)
do {
2001-05-23 15:26:42 +02:00
d *= 10.;
} while (++scale);
2001-05-23 15:26:42 +02:00
if (d > 0)
d += 0.5;
else
d -= 0.5;
return (SLONG) d;
case dtype_double:
d = *((double *) p);
if (scale > 0)
do {
2001-05-23 15:26:42 +02:00
d /= 10.;
} while (--scale);
2001-05-23 15:26:42 +02:00
else if (scale < 0)
do {
2001-05-23 15:26:42 +02:00
d *= 10.;
} while (++scale);
2001-05-23 15:26:42 +02:00
if (d > 0)
d += 0.5;
else
d -= 0.5;
return (SLONG) d;
case dtype_varying:
case dtype_cstring:
case dtype_text:
length = MOVQ_get_string(desc, &p, 0, 0);
scale -= MOVQ_decompose(p, length, &value);
break;
default:
mover_error(410, desc->dsc_dtype, dtype_long);
}
// Last, but not least, adjust for scale
2001-05-23 15:26:42 +02:00
if (scale == 0)
return value;
if (scale > 0) {
if ((desc->dsc_dtype == dtype_short)
|| (desc->dsc_dtype == dtype_long)) {
SSHORT fraction = 0;
2001-05-23 15:26:42 +02:00
do {
if (scale == 1)
fraction = value % 10;
value /= 10;
} while (--scale);
if (fraction > 4)
value++;
}
else
do {
2001-05-23 15:26:42 +02:00
value /= 10;
} while (--scale);
2001-05-23 15:26:42 +02:00
}
else
do {
2001-05-23 15:26:42 +02:00
value *= 10;
} while (++scale);
2001-05-23 15:26:42 +02:00
return value;
}
int MOVQ_get_string(const dsc* desc, TEXT** address, vary* temp, USHORT length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ g e t _ s t r i n g
*
**************************************
*
* Functional description
* Get address and length of string, converting the value to
* string, if necessary. The caller must provide a sufficiently
* large temporary. The address of the resultant string is returned
* by reference. Get_string returns the length of the string.
*
* Note: If the descriptor is known to be a string type, the third
* argument (temp buffer) may be omitted.
*
**************************************/
/* If the value is already a string (fixed or varying), just return
the address and length. */
if (desc->dsc_dtype == dtype_text) {
*address = (TEXT *) desc->dsc_address;
return desc->dsc_length;
}
// Perhaps it a "C" type string?
2001-05-23 15:26:42 +02:00
if (desc->dsc_dtype == dtype_cstring) {
*address = (TEXT *) desc->dsc_address;
2001-07-12 07:46:06 +02:00
return MIN(strlen((char*)desc->dsc_address), desc->dsc_length - 1);
2001-05-23 15:26:42 +02:00
}
// No luck -- convert value to varying string.
2001-05-23 15:26:42 +02:00
if (desc->dsc_dtype == dtype_varying) {
vary* varying = (vary*) desc->dsc_address;
2001-05-23 15:26:42 +02:00
*address = varying->vary_string;
return varying->vary_length;
}
dsc temp_desc;
2001-05-23 15:26:42 +02:00
temp_desc.dsc_length = length;
temp_desc.dsc_address = (UCHAR *) temp;
temp_desc.dsc_scale = 0;
temp_desc.dsc_dtype = dtype_varying;
temp_desc.dsc_sub_type = ttype_ascii;
MOVQ_move(desc, &temp_desc);
*address = temp->vary_string;
return temp->vary_length;
}
void MOVQ_move(const dsc* from, dsc* to)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ m o v e
*
**************************************
*
* Functional description
* Move (and possible convert) something to something else.
*
**************************************/
SSHORT fill;
2001-05-23 15:26:42 +02:00
SLONG l;
UCHAR *ptr;
USHORT length = from->dsc_length;
UCHAR* p = to->dsc_address;
const UCHAR* q = from->dsc_address;
2001-05-23 15:26:42 +02:00
/* If the datatypes and lengths are identical, just move the
stuff byte by byte. Although this may seem slower than
2001-05-23 15:26:42 +02:00
optimal, it would cost more to find the fast move than the
fast move would gain. */
if (DSC_EQUIV(from, to))
/*
if (((ALT_DSC*) from)->dsc_combined_type == ((ALT_DSC*) to)->dsc_combined_type)
*/
{
if (length)
do {
2001-05-23 15:26:42 +02:00
*p++ = *q++;
} while (--length);
2001-05-23 15:26:42 +02:00
return;
}
/* Do data type by data type conversions. Not all are supported,
and some will drop out for additional handling. */
switch (to->dsc_dtype) {
case dtype_timestamp:
switch (from->dsc_dtype) {
case dtype_varying:
case dtype_cstring:
case dtype_text:
2001-07-12 07:46:06 +02:00
length = MOVQ_get_string(from, (TEXT**) &ptr, 0, 0);
string_to_date((TEXT*) ptr, length, (long int*)to->dsc_address);
2001-05-23 15:26:42 +02:00
return;
case dtype_sql_date:
((SLONG *) to->dsc_address)[0] = *(SLONG *) from->dsc_address;
((SLONG *) to->dsc_address)[1] = 0;
return;
case dtype_sql_time:
((SLONG *) to->dsc_address)[0] = 0;
((SLONG *) to->dsc_address)[1] = *(SLONG *) from->dsc_address;
return;
}
break;
case dtype_sql_date:
switch (from->dsc_dtype) {
case dtype_varying:
case dtype_cstring:
case dtype_text:
{
SLONG date[2];
2001-07-12 07:46:06 +02:00
length = MOVQ_get_string(from, (TEXT**)&ptr, 0, 0);
string_to_date((TEXT*) ptr, length, (long int*)date);
2001-05-23 15:26:42 +02:00
((SLONG *) to->dsc_address)[0] = date[0];
}
return;
case dtype_timestamp:
((SLONG *) to->dsc_address)[0] = ((SLONG *) from->dsc_address)[0];
return;
case dtype_sql_time:
// Error situation
2001-05-23 15:26:42 +02:00
break;
}
break;
case dtype_sql_time:
switch (from->dsc_dtype) {
case dtype_varying:
case dtype_cstring:
case dtype_text:
{
SLONG date[2];
2001-07-12 07:46:06 +02:00
length = MOVQ_get_string(from, (TEXT**)&ptr, 0, 0);
string_to_time((TEXT*) ptr, length, (long int*) date);
2001-05-23 15:26:42 +02:00
((SLONG *) to->dsc_address)[0] = date[1];
}
return;
case dtype_timestamp:
((SLONG *) to->dsc_address)[0] = ((SLONG *) from->dsc_address)[1];
return;
case dtype_sql_date:
// Error situation
2001-05-23 15:26:42 +02:00
break;
}
break;
case dtype_text:
case dtype_cstring:
case dtype_varying:
switch (from->dsc_dtype) {
case dtype_varying:
case dtype_cstring:
case dtype_text:
2001-07-12 07:46:06 +02:00
length = MOVQ_get_string(from, (TEXT**)&ptr, 0, 0);
2001-05-23 15:26:42 +02:00
q = ptr;
switch (to->dsc_dtype) {
case dtype_text:
length = MIN(length, to->dsc_length);
fill = to->dsc_length - length;
if (length)
do {
2001-05-23 15:26:42 +02:00
*p++ = *q++;
} while (--length);
2001-05-23 15:26:42 +02:00
if (fill > 0)
do {
2001-05-23 15:26:42 +02:00
*p++ = ' ';
} while (--fill);
2001-05-23 15:26:42 +02:00
return;
case dtype_cstring:
length = MIN(length, to->dsc_length - 1);
if (length)
do {
2001-05-23 15:26:42 +02:00
*p++ = *q++;
} while (--length);
2001-05-23 15:26:42 +02:00
*p = 0;
return;
case dtype_varying:
length = MIN(length, to->dsc_length - sizeof(SSHORT));
((vary*) p)->vary_length = length;
p = (UCHAR*) ((vary*) p)->vary_string;
2001-05-23 15:26:42 +02:00
if (length)
do {
2001-05-23 15:26:42 +02:00
*p++ = *q++;
} while (--length);
2001-05-23 15:26:42 +02:00
return;
}
case dtype_short:
case dtype_long:
case dtype_real:
case dtype_double:
numeric_to_text(from, to);
return;
case dtype_sql_date:
2001-07-12 07:46:06 +02:00
sql_date_to_text((long int*) from->dsc_address, to);
2001-05-23 15:26:42 +02:00
return;
case dtype_sql_time:
2001-07-12 07:46:06 +02:00
sql_time_to_text((ULONG*) from->dsc_address, to);
2001-05-23 15:26:42 +02:00
return;
case dtype_timestamp:
2001-07-12 07:46:06 +02:00
timestamp_to_text((long int*) from->dsc_address, to);
2001-05-23 15:26:42 +02:00
return;
}
break;
case dtype_blob:
if (from->dsc_dtype == dtype_quad) {
((SLONG *) p)[0] = ((SLONG *) q)[0];
((SLONG *) p)[1] = ((SLONG *) q)[1];
return;
}
break;
case dtype_quad:
if (from->dsc_dtype == dtype_blob) {
((SLONG *) p)[0] = ((SLONG *) q)[0];
((SLONG *) p)[1] = ((SLONG *) q)[1];
return;
}
break;
case dtype_short:
*(SSHORT *) p = l = MOVQ_get_long(from, to->dsc_scale);
if (*(SSHORT *) p != l)
IBERROR(14); // Msg14 integer overflow
2001-05-23 15:26:42 +02:00
return;
case dtype_long:
*(SLONG *) p = MOVQ_get_long(from, to->dsc_scale);
return;
case dtype_real:
*(float *) p = MOVQ_get_double(from);
return;
case dtype_double:
*(double *) p = MOVQ_get_double(from);
return;
}
if (to->dsc_dtype == dtype_blob || from->dsc_dtype == dtype_blob)
IBERROR(55); // Msg 55 Blob conversion is not supported
2001-05-23 15:26:42 +02:00
mover_error(410, from->dsc_dtype, to->dsc_dtype);
}
void MOVQ_terminate(const SCHAR* from,
SCHAR* to,
USHORT length,
USHORT max_length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M O V Q _ t e r m i n a t e
*
**************************************
*
* Functional description
* Null-terminate a possibly non-
* null-terminated string with max
* buffer room.
*
**************************************/
fb_assert(max_length != 0);
2001-05-23 15:26:42 +02:00
if (length) {
length = MIN(length, max_length - 1);
do {
2001-05-23 15:26:42 +02:00
*to++ = *from++;
} while (--length);
2001-05-23 15:26:42 +02:00
*to++ = '\0';
}
else {
// It seems like the correct condition uses --max_length instead
2001-05-23 15:26:42 +02:00
while (max_length-- && (*to++ = *from++));
*--to = '\0';
}
}
static void date_error(const TEXT* string, const USHORT length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* d a t e _ e r r o r
*
**************************************
*
* Functional description
* A date conversion error occurred. Complain.
*
**************************************/
SCHAR temp[128];
MOVQ_terminate(string, temp, length, sizeof(temp));
ERRQ_error(56, temp, NULL, NULL, NULL, NULL);
// Msg 56 Error converting string \"%s\" to date
2001-05-23 15:26:42 +02:00
}
static void sql_date_to_text( SLONG date[1], DSC * to)
{
/**************************************
*
* s q l _ d a t e _ t o _ t e x t
*
**************************************
*
* Functional description
* Convert date to text.
*
**************************************/
2003-09-23 12:13:39 +02:00
tm times;
2001-05-23 15:26:42 +02:00
SLONG date2[2];
2002-06-29 10:49:39 +02:00
date2[0] = date[0];
2001-05-23 15:26:42 +02:00
date2[1] = 0;
isc_decode_date((ISC_QUAD*) date2, &times);
TEXT temp[35];
2001-05-23 15:26:42 +02:00
sprintf(temp, "%2d-%.3s-%04d", times.tm_mday,
months[times.tm_mon], times.tm_year + 1900);
TEXT* p;
2001-05-23 15:26:42 +02:00
for (p = temp; *p; p++);
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_length = p - temp;
desc.dsc_address = (UCHAR *) temp;
desc.dsc_dtype = dtype_text;
desc.dsc_scale = 0;
desc.dsc_sub_type = ttype_ascii;
MOVQ_move(&desc, to);
}
static void sql_time_to_text( ULONG date[1], DSC * to)
{
/**************************************
*
* s q l _ t i m e _ t o _ t e x t
*
**************************************
*
* Functional description
* Convert sql time to text.
*
**************************************/
2003-09-23 12:13:39 +02:00
tm times;
2001-05-23 15:26:42 +02:00
SLONG date2[2];
date2[0] = 0;
2002-06-29 10:49:39 +02:00
date2[1] = date[0];
2001-05-23 15:26:42 +02:00
isc_decode_date((ISC_QUAD*) date2, &times);
2001-05-23 15:26:42 +02:00
TEXT temp[35];
sprintf(temp, " %2d:%.2d:%.2d.%.4"SLONGFORMAT, times.tm_hour, times.tm_min,
2001-05-23 15:26:42 +02:00
times.tm_sec, date2[1] % PRECISION);
TEXT* p;
2001-05-23 15:26:42 +02:00
for (p = temp; *p; p++);
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_length = p - temp;
desc.dsc_address = (UCHAR *) temp;
desc.dsc_dtype = dtype_text;
desc.dsc_scale = 0;
desc.dsc_sub_type = ttype_ascii;
MOVQ_move(&desc, to);
}
static void timestamp_to_text( SLONG date[2], DSC * to)
{
/**************************************
*
* t i m e s t a m p _ t o _ t e x t
*
**************************************
*
* Functional description
* Convert date to text.
*
**************************************/
2003-09-23 12:13:39 +02:00
tm times;
isc_decode_date((ISC_QUAD*)date, &times);
2001-05-23 15:26:42 +02:00
TEXT temp[35];
2001-05-23 15:26:42 +02:00
sprintf(temp, "%2d-%.3s-%04d", times.tm_mday,
months[times.tm_mon], times.tm_year + 1900);
if (times.tm_hour || times.tm_min || times.tm_sec || date[1]) {
TEXT time[15];
sprintf(time, " %2d:%.2d:%.2d.%.4"SLONGFORMAT, times.tm_hour, times.tm_min,
2001-05-23 15:26:42 +02:00
times.tm_sec, date[1] % PRECISION);
strcat(temp, time);
}
TEXT* p;
2001-05-23 15:26:42 +02:00
for (p = temp; *p; p++);
dsc desc;
2001-05-23 15:26:42 +02:00
desc.dsc_length = p - temp;
desc.dsc_address = (UCHAR *) temp;
desc.dsc_dtype = dtype_text;
desc.dsc_scale = 0;
desc.dsc_sub_type = ttype_ascii;
MOVQ_move(&desc, to);
}
static void mover_error( int pattern, USHORT in_type, USHORT out_type)
{
/**************************************
*
* m o v e r _ e r r o r
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Return a reasonable error for
* unreasonable conversions and
* comparisons.
*
**************************************/
TEXT in_name[25], out_name[25], msg_unknown[40];
2001-05-23 15:26:42 +02:00
ERRQ_msg_get(504, msg_unknown); // Msg504 unknown datatype %d
2001-05-23 15:26:42 +02:00
TEXT* in = type_name(in_type);
if (!in) {
2001-05-23 15:26:42 +02:00
in = in_name;
sprintf(in_name, msg_unknown, in_type);
}
TEXT* out = type_name(out_type);
if (!out) {
2001-05-23 15:26:42 +02:00
out = out_name;
sprintf(out, msg_unknown, out_type);
}
ERRQ_error(pattern, (TEXT *) in, (TEXT *) out, NULL, NULL, NULL);
}
2003-10-08 10:42:48 +02:00
static void now_to_date(const tm* time, SLONG date[2])
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n o w _ t o _ d a t e
*
**************************************
*
* Functional description
* Convert "now" (or best guess) to
* a date attempting to get millisecond
* precision. This unfortunately means
* that we use one routine for VAX, one
* for Apollo, and a third for Unix.
*
**************************************/
isc_encode_date(time, (ISC_QUAD*)date);
2001-05-23 15:26:42 +02:00
}
static void numeric_to_text(const dsc* from, dsc* to)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n u m e r i c _ t o _ t e x t
*
**************************************
*
* Functional description
* Convert your basic number to nice, formatted text.
*
**************************************/
/* Save (or compute) scale of source. Then convert source to ordinary
longword. */
SSHORT scale = from->dsc_scale;
2001-05-23 15:26:42 +02:00
SSHORT pad = 0, decimal = 0;
2001-05-23 15:26:42 +02:00
if (scale > 0)
pad = scale;
else if (scale < 0)
decimal = 1;
SLONG n;
dsc intermediate;
2001-05-23 15:26:42 +02:00
intermediate.dsc_dtype = dtype_long;
intermediate.dsc_length = sizeof(SLONG);
intermediate.dsc_scale = scale;
intermediate.dsc_sub_type = 0;
intermediate.dsc_address = (UCHAR *) &n;
2001-05-23 15:26:42 +02:00
MOVQ_move(from, &intermediate);
// Check for negation, then convert the number to a string of digits
2001-05-23 15:26:42 +02:00
SSHORT neg = 0;
2001-05-23 15:26:42 +02:00
if (n < 0) {
neg = 1;
n = -n;
}
TEXT temp[32];
TEXT* p = temp;
2001-05-23 15:26:42 +02:00
do {
*p++ = n % 10 + '0';
n /= 10;
} while (n);
/* Compute the total length off the field formatted. Make sure it
fits. Keep in mind that routine handles both string and varying
string fields. */
SSHORT l = p - temp;
const SSHORT length = l + neg + decimal + pad;
2001-05-23 15:26:42 +02:00
if ((to->dsc_dtype == dtype_text && length > to->dsc_length) ||
(to->dsc_dtype == dtype_cstring && length >= to->dsc_length) ||
(to->dsc_dtype == dtype_varying
&& length > to->dsc_length - sizeof(SSHORT)))
{
IBERROR(57); // Msg 57 overflow during conversion
}
2001-05-23 15:26:42 +02:00
// Hopefully a cstring never reached this point.
TEXT* q =
2001-05-23 15:26:42 +02:00
(TEXT *) ((to->dsc_dtype == dtype_text) ? to->dsc_address : to->
dsc_address + sizeof(SSHORT));
// If negative, put in minus sign
2001-05-23 15:26:42 +02:00
if (neg)
*q++ = '-';
/* If a decimal point is required, do the formatting. Otherwise just
copy number */
if (scale < 0) {
if ((l += scale) > 0)
do {
2001-05-23 15:26:42 +02:00
*q++ = *--p;
} while (--l);
2001-05-23 15:26:42 +02:00
*q++ = '.';
do {
2001-05-23 15:26:42 +02:00
*q++ = *--p;
} while (++scale);
2001-05-23 15:26:42 +02:00
}
else
do {
2001-05-23 15:26:42 +02:00
*q++ = *--p;
} while (--l);
2001-05-23 15:26:42 +02:00
// If padding is required, do it now.
2001-05-23 15:26:42 +02:00
if (pad)
do {
2001-05-23 15:26:42 +02:00
*q++ = '0';
} while (--pad);
2001-05-23 15:26:42 +02:00
/* Finish up by padding (if fixed) or computing the actual length
(varying string) */
if (to->dsc_dtype == dtype_text) {
if (l = to->dsc_length - length)
do {
2001-05-23 15:26:42 +02:00
*q++ = ' ';
} while (--l);
2001-05-23 15:26:42 +02:00
return;
}
if (to->dsc_dtype == dtype_cstring) {
*q = 0;
return;
}
*(SSHORT *) (to->dsc_address) =
(UCHAR *) q - to->dsc_address - sizeof(SSHORT);
}
static void string_to_date(const TEXT* string, USHORT length, SLONG date[2])
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t r i n g _ t o _ d a t e
*
**************************************
*
* Functional description
* Convert an arbitrary string to a date.
*
**************************************/
if (!length) {
date[0] = date[1] = 0;
return;
}
const TEXT* p = string;
const TEXT* const end = p + length;
2001-05-23 15:26:42 +02:00
USHORT month_position = 0;
2001-05-23 15:26:42 +02:00
2003-10-08 10:42:48 +02:00
const time_t clock = time(0);
tm* today = localtime(&clock);
2001-05-23 15:26:42 +02:00
USHORT i;
USHORT components[7];
2001-05-23 15:26:42 +02:00
for (i = 0; i < 7; i++)
components[i] = 0;
// Parse components
TEXT temp[15];
USHORT n, precision;
bool year = false;
2001-05-23 15:26:42 +02:00
for (i = 0; i < 7; i++) {
/* Skip leading blanks. If we run out of characters, we're done
with parse. */
while (p < end && *p == ' ')
p++;
if (p == end)
break;
// Handle digit or character strings
2001-05-23 15:26:42 +02:00
TEXT c = UPPER(*p);
2001-05-23 15:26:42 +02:00
if (DIGIT(c)) {
precision = n = 0;
while (p < end && DIGIT(*p)) {
n = n * 10 + *p++ - '0';
precision++;
}
if (i == 2)
year = true;
2001-05-23 15:26:42 +02:00
}
else if (LETTER(c)) {
TEXT* t = temp;
2001-05-23 15:26:42 +02:00
while (p < end && LETTER(c)) {
c = UPPER(*p);
if (!LETTER(c))
break;
*t++ = c;
p++;
}
*t = 0;
const TEXT** month_ptr = months;
2003-09-10 19:52:12 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
if (!*month_ptr) {
while (++p < end)
if (*p != ' ' && *p != '\t' && *p != 0)
date_error(string, length);
if (strcmp(temp, NOW) == 0) {
now_to_date(today, date);
return;
}
today->tm_hour = today->tm_min = today->tm_sec = 0;
isc_encode_date(today, (ISC_QUAD*)date);
2001-05-23 15:26:42 +02:00
if (strcmp(temp, TODAY) == 0)
return;
if (strcmp(temp, TOMORROW) == 0) {
++date[0];
return;
}
if (strcmp(temp, YESTERDAY) == 0) {
--date[0];
return;
}
date_error(string, length);
}
t = temp;
for (const TEXT* m = *month_ptr++; *t && *t == *m; t++, m++);
2001-05-23 15:26:42 +02:00
if (!*t)
break;
}
n = month_ptr - months;
month_position = i;
}
else {
date_error(string, length);
return;
}
components[i] = n;
while (p < end && *p == ' ')
p++;
if (*p == '/' || *p == '-' || *p == ',' || *p == ':') {
p++;
continue;
}
if (*p == '.') {
if (!month_position && i < 2)
month_position = 1;
p++;
continue;
}
}
// Slide things into day, month, year form
2001-05-23 15:26:42 +02:00
tm times;
2001-05-23 15:26:42 +02:00
if (month_position) {
times.tm_mon = components[1];
times.tm_mday = components[0];
}
else {
times.tm_mon = components[0];
times.tm_mday = components[1];
}
// Handle defaulting of year
2001-05-23 15:26:42 +02:00
if (((times.tm_year = components[2]) == 0) && !year)
times.tm_year = today->tm_year + 1900;
else if (times.tm_year < 100) {
if (times.tm_year < (today->tm_year - 50) % 100)
times.tm_year += 2000;
else
times.tm_year += 1900;
}
times.tm_year -= 1900;
times.tm_mon -= 1;
times.tm_hour = components[3];
times.tm_min = components[4];
times.tm_sec = components[5];
// convert day/month/year to Julian and validate result
2001-05-23 15:26:42 +02:00
isc_encode_date(&times, (ISC_QUAD*)date);
tm times2;
isc_decode_date((ISC_QUAD*)date, &times2);
2001-05-23 15:26:42 +02:00
if (times.tm_year != times2.tm_year ||
times.tm_mon != times2.tm_mon || times.tm_mday != times2.tm_mday)
date_error(string, length);
while (precision++ < 4)
components[6] *= 10;
date[1] += components[6];
}
static void string_to_time( TEXT * string, USHORT length, SLONG date[2])
{
/**************************************
*
* s t r i n g _ t o _ t i m e
*
**************************************
*
* Functional description
* Convert an arbitrary string to a t i m e.
*
**************************************/
if (!length) {
date[0] = date[1] = 0;
return;
}
const TEXT* p = string;
const TEXT* const end = p + length;
2001-05-23 15:26:42 +02:00
2003-10-08 10:42:48 +02:00
const time_t clock = time(0);
const tm* today = localtime(&clock);
2001-05-23 15:26:42 +02:00
USHORT i;
USHORT components[7];
2001-05-23 15:26:42 +02:00
for (i = 0; i < 7; i++)
components[i] = 0;
// Parse components
TEXT temp[15];
USHORT n, precision;
2001-05-23 15:26:42 +02:00
for (i = 3; i < 7; i++) {
/* Skip leading blanks. If we run out of characters, we're done
with parse. */
while (p < end && *p == ' ')
p++;
if (p == end)
break;
// Handle digit or character strings
2001-05-23 15:26:42 +02:00
TEXT c = UPPER(*p);
2001-05-23 15:26:42 +02:00
if (DIGIT(c)) {
precision = n = 0;
while (p < end && DIGIT(*p)) {
n = n * 10 + *p++ - '0';
precision++;
}
}
else if (LETTER(c)) {
TEXT* t = temp;
2001-05-23 15:26:42 +02:00
while (p < end && LETTER(c)) {
c = UPPER(*p);
if (!LETTER(c))
break;
*t++ = c;
p++;
}
*t = 0;
while (++p < end)
if (*p != ' ' && *p != '\t' && *p != 0)
date_error(string, length);
if (strcmp(temp, NOW) == 0) {
now_to_date(today, date);
return;
}
date_error(string, length);
}
else {
date_error(string, length);
return;
}
components[i] = n;
while (p < end && *p == ' ')
p++;
if (*p == '/' || *p == '-' || *p == ',' || *p == ':' || *p == '.') {
p++;
continue;
}
}
tm times;
2001-05-23 15:26:42 +02:00
times.tm_hour = components[3];
times.tm_min = components[4];
times.tm_sec = components[5];
// convert day/month/year to Julian and validate result
2001-05-23 15:26:42 +02:00
isc_encode_date(&times, (ISC_QUAD*)date);
2001-05-23 15:26:42 +02:00
while (precision++ < 4)
components[6] *= 10;
date[1] += components[6];
}
static TEXT *type_name( USHORT dtype)
{
/**************************************
*
* t y p e _ n a m e
*
**************************************
*
* Functional description
* Return the name of a data type.
*
**************************************/
for (const dtypes_t* names = dtypes_table; names->description; names++)
2001-05-23 15:26:42 +02:00
if (names->type == dtype)
return names->description;
return NULL;
}