/* * PROGRAM: QLI Access Method * MODULE: mov.cpp * 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" #include #include #include "../qli/dtr.h" #include "../common/classes/timestamp.h" #include "../jrd/intl.h" #include "../qli/err_proto.h" #include "../yvalve/gds_proto.h" #include "../common/gdsassert.h" #include "../qli/mov_proto.h" #include "../common/utils_proto.h" using MsgFormat::SafeArg; static void date_error(const TEXT*, const USHORT); static double double_from_text(const dsc* desc); static void sql_date_to_text(const SLONG[1], DSC*); static void sql_time_to_text(const ULONG[1], DSC*); static void timestamp_to_text(const SLONG[2], DSC*); static void mover_error(int, USHORT, USHORT); 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]); static void string_to_time(const TEXT*, USHORT, SLONG[2]); static const TEXT* type_name(USHORT); #define LETTER(c) (c >= 'A' && c <= 'Z') #define DIGIT(c) (c >= '0' && c <= '9') const char* const TODAY = "TODAY"; const char* const NOW = "NOW"; const char* const TOMORROW = "TOMORROW"; const char* const YESTERDAY = "YESTERDAY"; const int PRECISION = 10000; struct dtypes_t { USHORT type; const TEXT* description; }; static const dtypes_t dtypes_table[] = { { dtype_unknown, "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" }, { dtype_int64, "big integer" }, { 0, 0 } }; int MOVQ_compare(const dsc* arg1, const dsc* arg2) { /************************************** * * M O V Q _ c o m p a r e * ************************************** * * Functional description * Compare two descriptors. Return (-1, 0, 1) if ab. * **************************************/ // 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; 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_int64: 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++) return (p1[-1] > p2[-1]) ? 1 : -1; } while (--length); if (length = arg1->dsc_length - arg2->dsc_length) do { if (*p1++ != ' ') return (p1[-1] > ' ') ? 1 : -1; } while (--length); return 0; } if (length = arg1->dsc_length) do { if (*p1++ != *p2++) return (p1[-1] > p2[-1]) ? 1 : -1; } while (--length); length = arg2->dsc_length - arg1->dsc_length; do { if (*p2++ != ' ') return (' ' > p2[-1]) ? 1 : -1; } while (--length); return 0; } } } // Handle mixed string comparisons if (arg1->dsc_dtype <= dtype_varying && arg2->dsc_dtype <= dtype_varying) { const TEXT* p1; const TEXT* p2; SSHORT length = MOVQ_get_string(arg1, &p1, 0, 0); SSHORT length2 = MOVQ_get_string(arg2, &p2, 0, 0); SSHORT fill = length - length2; if (length >= length2) { if (length2) do { if (*p1++ != *p2++) return (p1[-1] > p2[-1]) ? 1 : -1; } while (--length2); if (fill > 0) do { if (*p1++ != ' ') return (p1[-1] > ' ') ? 1 : -1; } while (--fill); return 0; } if (length) { do { if (*p1++ != *p2++) return (p1[-1] > p2[-1]) ? 1 : -1; } while (--length); } do { if (*p2++ != ' ') return (' ' > p2[-1]) ? 1 : -1; } while (++fill); return 0; } // Handle hetergeneous compares if (arg1->dsc_dtype < arg2->dsc_dtype) return (-MOVQ_compare(arg2, arg1)); dsc desc; SLONG date[2]; 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); const SLONG temp1 = MOVQ_get_long(arg1, scale); const SLONG temp2 = MOVQ_get_long(arg2, scale); 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); 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); if (temp1 == temp2) return 0; if (temp1 > temp2) return 1; return -1; } case dtype_blob: IBERROR(48); // Msg 48 Blob conversion is not supported default: mover_error(410, arg1->dsc_dtype, arg2->dsc_dtype); } return -1; } double MOVQ_date_to_double(const dsc* desc) { /************************************** * * 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. if (desc->dsc_dtype == dtype_timestamp) date = (SLONG *) desc->dsc_address; else { dsc temp_desc; 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; QLI_validate_desc(temp_desc); 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) { /************************************** * * 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; SCHAR temp[128]; bool sign = false; bool fraction = false; const TEXT* p = string; const TEXT* const end = p + length; for (; p < end; p++) { if (*p == ',') continue; 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); } else fraction = true; } else if (*p == '-' && !value && !sign) sign = true; else if (*p == '+' && !value && !sign) sign = false; else if (*p == 'e' || *p == 'E') break; else if (*p != ' ') { MOVQ_terminate(string, temp, length, sizeof(temp)); ERRQ_error(411, temp); } } if (sign) value = -value; // If there's still something left, there must be an explicit exponent if (p < end) { SSHORT exp = 0; sign = false; for (p++; p < end; p++) { if (DIGIT(*p)) exp = exp * 10 + *p - '0'; else if (*p == '-' && !exp) sign = true; else if (*p == '+' && !exp && !sign) continue; else if (*p != ' ') { MOVQ_terminate(string, temp, length, sizeof(temp)); ERRQ_error(411, temp); } } 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] = static_cast(real); fixed[1] = static_cast((real - fixed[0]) * 24. * 60. * 60. * PRECISION); } double MOVQ_get_double(const dsc* desc) { /************************************** * * M O V Q _ g e t _ d o u b l e * ************************************** * * Functional description * Convert something arbitrary to a * double_precision representation. * **************************************/ double value = 0; switch (desc->dsc_dtype) { case dtype_short: value = *((SSHORT*) desc->dsc_address); break; case dtype_long: value = *((SLONG*) desc->dsc_address); break; case dtype_int64: value = *((SINT64*) 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: return double_from_text(desc); default: mover_error(410, desc->dsc_dtype, dtype_double); } // Last, but not least, adjust for scale int scale = desc->dsc_scale; if (scale == 0) return value; if (scale > 0) do { value *= 10.; } while (--scale); else do { value /= 10.; } while (++scale); return value; } SLONG MOVQ_get_long(const dsc* desc, SSHORT scale) { /************************************** * * 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; scale -= (SSHORT) desc->dsc_scale; const TEXT* p = (TEXT*) desc->dsc_address; switch (desc->dsc_dtype) { case dtype_short: value = *((SSHORT*) p); break; case dtype_int64: value = *((SINT64*) p); break; case dtype_long: value = *((SLONG*) p); break; case dtype_real: d = *((float*) p); if (scale > 0) do { d /= 10.; } while (--scale); else if (scale < 0) do { d *= 10.; } while (++scale); if (d > 0) d += 0.5; else d -= 0.5; return (SLONG) d; case dtype_double: d = *((double*) p); if (scale > 0) do { d /= 10.; } while (--scale); else if (scale < 0) do { d *= 10.; } while (++scale); if (d > 0) d += 0.5; else d -= 0.5; return (SLONG) d; case dtype_varying: case dtype_cstring: case dtype_text: { const SSHORT 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 if (scale == 0) return value; if (scale > 0) { if ((desc->dsc_dtype == dtype_short) || (desc->dsc_dtype == dtype_long)) { int fraction = 0; do { if (scale == 1) fraction = value % 10; value /= 10; } while (--scale); if (fraction > 4) value++; } else do { value /= 10; } while (--scale); } else do { value *= 10; } while (++scale); return value; } int MOVQ_get_string(const dsc* desc, const TEXT** address, vary* temp, USHORT length) { /************************************** * * 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? if (desc->dsc_dtype == dtype_cstring) { *address = (TEXT*) desc->dsc_address; return MIN(static_cast(strlen((char*)desc->dsc_address)), desc->dsc_length - 1); } // No luck -- convert value to varying string. if (desc->dsc_dtype == dtype_varying) { vary* varying = (vary*) desc->dsc_address; *address = varying->vary_string; return varying->vary_length; } dsc temp_desc; 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; QLI_validate_desc(temp_desc); MOVQ_move(desc, &temp_desc); *address = temp->vary_string; return temp->vary_length; } void MOVQ_move(const dsc* from, dsc* to) { /************************************** * * M O V Q _ m o v e * ************************************** * * Functional description * Move (and possible convert) something to something else. * **************************************/ USHORT length = from->dsc_length; UCHAR* p = to->dsc_address; const UCHAR* q = from->dsc_address; // If the datatypes and lengths are identical, just move the // stuff byte by byte. Although this may seem slower than // optimal, it would cost more to find the fast move than the // fast move would gain. // if (((ALT_DSC*) from)->dsc_combined_type == ((ALT_DSC*) to)->dsc_combined_type) if (DSC_EQUIV(from, to, false)) { if (length) memcpy(p, q, length); return; } const TEXT* ptr; // 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: length = MOVQ_get_string(from, &ptr, 0, 0); string_to_date((TEXT*) ptr, length, (SLONG*) to->dsc_address); 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]; length = MOVQ_get_string(from, &ptr, 0, 0); string_to_date((TEXT*) ptr, length, (SLONG*) date); ((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 break; } break; case dtype_sql_time: switch (from->dsc_dtype) { case dtype_varying: case dtype_cstring: case dtype_text: { SLONG date[2]; length = MOVQ_get_string(from, &ptr, 0, 0); string_to_time((TEXT*) ptr, length, (SLONG*) date); ((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 break; } break; case dtype_text: case dtype_cstring: case dtype_varying: switch (from->dsc_dtype) { case dtype_varying: case dtype_cstring: case dtype_text: { length = MOVQ_get_string(from, &ptr, 0, 0); const TEXT* s = ptr; switch (to->dsc_dtype) { case dtype_text: { length = MIN(length, to->dsc_length); SSHORT fill = to->dsc_length - length; if (length) memcpy(p, s, length); if (fill > 0) memset(p + length, ' ', fill); return; } case dtype_cstring: length = MIN(length, to->dsc_length - 1); if (length) memcpy(p, s, length); p[length] = 0; return; case dtype_varying: { length = MIN(length, to->dsc_length - sizeof(SSHORT)); vary* avary = reinterpret_cast(p); avary->vary_length = length; if (length) memcpy(avary->vary_string, s, length); } return; } } case dtype_int64: case dtype_short: case dtype_long: case dtype_real: case dtype_double: numeric_to_text(from, to); return; case dtype_sql_date: sql_date_to_text((SLONG*) from->dsc_address, to); return; case dtype_sql_time: sql_time_to_text((ULONG*) from->dsc_address, to); return; case dtype_timestamp: timestamp_to_text((SLONG*) from->dsc_address, to); 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: { const SLONG l = MOVQ_get_long(from, to->dsc_scale); *(SSHORT*) p = l; if (*(SSHORT*) p != l) IBERROR(14); // Msg14 integer overflow return; } case dtype_long: *(SLONG*) p = MOVQ_get_long(from, to->dsc_scale); return; case dtype_int64: *(SINT64*) 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 mover_error(410, from->dsc_dtype, to->dsc_dtype); } void MOVQ_terminate(const SCHAR* from, SCHAR* to, USHORT length, USHORT max_length) { /************************************** * * 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); if (length) { length = MIN(length, max_length - 1); memcpy(to, from, length); to[length] = '\0'; } else fb_utils::copy_terminate(to, from, max_length); } static void date_error(const TEXT* string, const USHORT length) { /************************************** * * 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); // Msg 56 Error converting string \"%s\" to date } static double double_from_text(const dsc* desc) { const TEXT* p; const SSHORT length = MOVQ_get_string(desc, &p, 0, 0); int scale = 0; bool fraction = false, sign = false; double value = 0; const TEXT* const end = p + length; for (; p < end; p++) { if (*p == ',') continue; if (DIGIT(*p)) { value = value * 10. + (*p - '0'); if (fraction) scale++; } else if (*p == '.') { if (fraction) IBERROR(52); // Msg 52 conversion error else fraction = true; } else if (!value && *p == '-') sign = true; else if (!value && *p == '+') continue; else if (*p == 'e' || *p == 'E') break; else if (*p != ' ') IBERROR(53); // Msg 53 conversion error } if (sign) value = -value; // If there's still something left, there must be an explicit exponent if (p < end) { sign = false; int 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 } if (sign) scale += exp; else scale -= exp; } if (scale > 0) do { value /= 10.; } while (--scale); else if (scale) do { value *= 10.; } while (++scale); return value; } static void sql_date_to_text( const SLONG date[1], DSC* to) { /************************************** * * s q l _ d a t e _ t o _ t e x t * ************************************** * * Functional description * Convert date to text. * **************************************/ tm times; SLONG date2[2]; date2[0] = date[0]; date2[1] = 0; isc_decode_date((const ISC_QUAD*) date2, ×); TEXT temp[35]; sprintf(temp, "%2d-%.3s-%04d", times.tm_mday, FB_LONG_MONTHS_UPPER[times.tm_mon], times.tm_year + 1900); const TEXT* p = temp; while (*p) ++p; dsc desc; 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; QLI_validate_desc(desc); MOVQ_move(&desc, to); } static void sql_time_to_text( const 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. * **************************************/ tm times; SLONG date2[2]; date2[0] = 0; date2[1] = date[0]; isc_decode_date((const ISC_QUAD*) date2, ×); TEXT temp[35]; sprintf(temp, " %2d:%.2d:%.2d.%.4" SLONGFORMAT, times.tm_hour, times.tm_min, times.tm_sec, date2[1] % PRECISION); const TEXT* p = temp; while (*p) ++p; dsc desc; 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; QLI_validate_desc(desc); MOVQ_move(&desc, to); } static void timestamp_to_text( const 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. * **************************************/ tm times; isc_decode_date((const ISC_QUAD*) date, ×); TEXT temp[35]; sprintf(temp, "%2d-%.3s-%04d", times.tm_mday, FB_LONG_MONTHS_UPPER[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, times.tm_sec, date[1] % PRECISION); strcat(temp, time); } const TEXT* p = temp; while (*p) ++p; dsc desc; 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; QLI_validate_desc(desc); 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 * ************************************** * * Functional description * Return a reasonable error for * unreasonable conversions and * comparisons. * **************************************/ TEXT in_name[25], out_name[25], msg_unknown[40]; ERRQ_msg_get(504, msg_unknown, sizeof(msg_unknown)); // Msg504 unknown datatype %d const TEXT* in = type_name(in_type); if (!in) { in = in_name; sprintf(in_name, msg_unknown, in_type); } const TEXT* out = type_name(out_type); if (!out) { out = out_name; sprintf(out_name, msg_unknown, out_type); } ERRQ_error(pattern, SafeArg() << in << out); } static void now_to_date(const tm* time, SLONG date[2]) { /************************************** * * 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); } static void numeric_to_text(const dsc* from, dsc* to) { /************************************** * * 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; SSHORT pad = 0, decimal = 0; if (scale > 0) pad = scale; else if (scale < 0) decimal = 1; SLONG n; dsc intermediate; intermediate.dsc_dtype = dtype_long; intermediate.dsc_length = sizeof(SLONG); intermediate.dsc_scale = scale; intermediate.dsc_sub_type = 0; intermediate.dsc_address = (UCHAR*) &n; QLI_validate_desc(intermediate); MOVQ_move(from, &intermediate); // Check for negation, then convert the number to a string of digits SSHORT neg = 0; if (n < 0) { neg = 1; n = -n; } TEXT temp[32]; TEXT* p = temp; 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 FB_SIZE_T length = l + neg + decimal + pad; 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 } // Hopefully a cstring never reached this point. TEXT* q = (TEXT*) ((to->dsc_dtype == dtype_text) ? to->dsc_address : to->dsc_address + sizeof(SSHORT)); // If negative, put in minus sign if (neg) *q++ = '-'; // If a decimal point is required, do the formatting. Otherwise just // copy number if (scale < 0) { if ((l += scale) > 0) do { *q++ = *--p; } while (--l); *q++ = '.'; do { *q++ = *--p; } while (++scale); } else do { *q++ = *--p; } while (--l); // If padding is required, do it now. if (pad) do { *q++ = '0'; } while (--pad); // 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 { *q++ = ' '; } while (--l); return; } if (to->dsc_dtype == dtype_cstring) { *q = 0; return; } *(SSHORT*) (to->dsc_address) = static_cast((UCHAR*) q - to->dsc_address - sizeof(SSHORT)); } static void string_to_date(const TEXT* string, USHORT length, SLONG date[2]) { /************************************** * * 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; USHORT month_position = 0; const time_t clock = time(0); tm* today = localtime(&clock); USHORT components[7]; for (int i = 0; i < 7; i++) components[i] = 0; // Parse components TEXT temp[15]; USHORT n, precision; bool year = false; for (int 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 TEXT c = UPPER(*p); if (DIGIT(c)) { precision = n = 0; while (p < end && DIGIT(*p)) { n = n * 10 + *p++ - '0'; precision++; } if (i == 2) year = true; } else if (LETTER(c)) { TEXT* t = temp; while (p < end && LETTER(c)) { c = UPPER(*p); if (!LETTER(c)) break; *t++ = c; p++; } *t = 0; const TEXT* const* month_ptr = FB_LONG_MONTHS_UPPER; while (true) { 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); 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++); if (!*t) break; } n = month_ptr - FB_LONG_MONTHS_UPPER; month_position = i; } else { date_error(string, length); return; } components[i] = n; while (p < end && *p == ' ') p++; switch (*p) { case '.': if (!month_position && i < 2) month_position = 1; // fall into case '/': case '-': case ',': case ':': p++; continue; } } // Slide things into day, month, year form tm times; 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 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 isc_encode_date(×, (ISC_QUAD*)date); tm times2; isc_decode_date((ISC_QUAD*)date, ×2); 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(const 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 time. * **************************************/ if (!length) { date[0] = date[1] = 0; return; } const TEXT* p = string; const TEXT* const end = p + length; const time_t clock = time(0); const tm* today = localtime(&clock); int i; USHORT components[7]; for (i = 0; i < 7; i++) components[i] = 0; // Parse components TEXT temp[15]; USHORT n, precision; 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 TEXT c = UPPER(*p); if (DIGIT(c)) { precision = n = 0; while (p < end && DIGIT(*p)) { n = n * 10 + *p++ - '0'; precision++; } } else if (LETTER(c)) { TEXT* t = temp; 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; times.tm_hour = components[3]; times.tm_min = components[4]; times.tm_sec = components[5]; // convert day/month/year to Julian and validate result isc_encode_date(×, (ISC_QUAD*)date); while (precision++ < 4) components[6] *= 10; date[1] += components[6]; } static const 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++) { if (names->type == dtype) return names->description; } return NULL; }