2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* PROGRAM: JRD Access Method
|
2003-10-08 10:42:48 +02:00
|
|
|
* MODULE: cvt2.cpp
|
2001-05-23 15:26:42 +02:00
|
|
|
* DESCRIPTION: Data mover and converter and comparator, etc.
|
|
|
|
* Routines used ONLY within engine.
|
|
|
|
*
|
|
|
|
* 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-06-29 15:03:13 +02:00
|
|
|
*
|
|
|
|
* 2001.6.18 Claudio Valderrama: Implement comparison on blobs and blobs against
|
|
|
|
* other datatypes by request from Ann Harrison.
|
2001-05-23 15:26:42 +02:00
|
|
|
*/
|
|
|
|
|
2001-07-29 19:42:23 +02:00
|
|
|
#include "firebird.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include <string.h>
|
2010-10-13 12:39:52 +02:00
|
|
|
#include "../common/common.h"
|
2004-04-29 19:48:39 +02:00
|
|
|
#include "../jrd/ibase.h"
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/val.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../common/quad.h"
|
2003-11-11 13:19:20 +01:00
|
|
|
#include "gen/iberror.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/intl.h"
|
2010-10-13 12:39:52 +02:00
|
|
|
#include "../common/gdsassert.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/cvt_proto.h"
|
|
|
|
#include "../jrd/cvt2_proto.h"
|
2008-07-10 17:57:33 +02:00
|
|
|
#include "../common/cvt.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
#include "../jrd/err_proto.h"
|
|
|
|
#include "../jrd/intl_proto.h"
|
2002-06-04 21:56:16 +02:00
|
|
|
#include "../jrd/intl_classes.h"
|
2010-10-12 10:02:57 +02:00
|
|
|
#include "../yvalve/gds_proto.h"
|
2009-08-23 11:50:47 +02:00
|
|
|
// CVC: I needed them here.
|
2002-06-29 15:03:13 +02:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/blb_proto.h"
|
|
|
|
#include "../jrd/tra.h"
|
|
|
|
#include "../jrd/req.h"
|
2005-01-25 07:38:58 +01:00
|
|
|
#include "../jrd/constants.h"
|
2005-01-26 09:03:01 +01:00
|
|
|
#include "../common/utils_proto.h"
|
2009-04-29 16:00:32 +02:00
|
|
|
#include "../common/classes/VaryStr.h"
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-03-20 15:57:40 +01:00
|
|
|
using namespace Jrd;
|
2008-07-10 17:57:33 +02:00
|
|
|
using namespace Firebird;
|
2004-03-20 15:57:40 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
/* The original order of dsc_type values corresponded to the priority
|
|
|
|
of conversion (that is, always convert the lesser to the greater
|
|
|
|
type.) Introduction of dtype_int64 breaks that assumption: its
|
|
|
|
position on the scale should be between dtype_long and dtype_real, but
|
|
|
|
the types are integers, and dtype_quad occupies the only available
|
|
|
|
place. Renumbering all the higher-numbered types would be a major
|
|
|
|
ODS change and a fundamental discomfort
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
This table permits us to put the entries in the right order for
|
|
|
|
comparison purpose, even though isc_int64 had to get number 19, which
|
|
|
|
is otherwise too high.
|
|
|
|
|
|
|
|
This table is used in CVT2_compare, is indexed by dsc_dtype, and
|
|
|
|
returns the relative priority of types for use when different types
|
|
|
|
are compared.
|
|
|
|
*/
|
2009-01-14 09:22:32 +01:00
|
|
|
static const BYTE compare_priority[] =
|
|
|
|
{
|
2009-08-23 11:50:47 +02:00
|
|
|
dtype_unknown, // dtype_unknown through dtype_varying
|
|
|
|
dtype_text, // have their natural values stored
|
|
|
|
dtype_cstring, // in the table.
|
2001-05-23 15:26:42 +02:00
|
|
|
dtype_varying,
|
2009-08-23 11:50:47 +02:00
|
|
|
0, 0, // dtypes and 4, 5 are unused.
|
|
|
|
dtype_packed, // packed through long also have
|
|
|
|
dtype_byte, // their natural values in the table
|
2001-05-23 15:26:42 +02:00
|
|
|
dtype_short,
|
|
|
|
dtype_long,
|
2009-08-23 11:50:47 +02:00
|
|
|
dtype_quad + 1, // quad through array all move up
|
|
|
|
dtype_real + 1, // by one to make room for int64
|
|
|
|
dtype_double + 1, // at its proper place in the table.
|
2001-05-23 15:26:42 +02:00
|
|
|
dtype_d_float + 1,
|
|
|
|
dtype_sql_date + 1,
|
|
|
|
dtype_sql_time + 1,
|
|
|
|
dtype_timestamp + 1,
|
|
|
|
dtype_blob + 1,
|
|
|
|
dtype_array + 1,
|
2009-08-23 11:50:47 +02:00
|
|
|
dtype_long + 1, // int64 goes right after long
|
2010-12-18 03:17:06 +01:00
|
|
|
dtype_dbkey, // compares with nothing except itself
|
|
|
|
dtype_boolean // compares with nothing except itself
|
2009-07-13 12:00:43 +02:00
|
|
|
};
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-05 10:02:33 +01:00
|
|
|
|
2008-07-10 17:57:33 +02:00
|
|
|
SSHORT CVT2_compare(const dsc* arg1, const dsc* arg2)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* C V T 2 _ c o m p a r e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Compare two descriptors. Return (-1, 0, 1) if a<b, a=b, or a>b.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-03-11 06:04:26 +01:00
|
|
|
thread_db* tdbb = NULL;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
// AB: Maybe we need a other error-message, but at least throw
|
2003-09-03 23:18:27 +02:00
|
|
|
// a message when 1 or both input paramters are empty.
|
|
|
|
if (!arg1 || !arg2) {
|
|
|
|
BUGCHECK(189); // msg 189 comparison not supported for specified data types.
|
|
|
|
}
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Handle the simple (matched) ones first
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-08 06:59:06 +01:00
|
|
|
if (arg1->dsc_dtype == arg2->dsc_dtype && arg1->dsc_scale == arg2->dsc_scale)
|
2003-11-05 10:02:33 +01:00
|
|
|
{
|
|
|
|
const UCHAR* p1 = arg1->dsc_address;
|
|
|
|
const UCHAR* p2 = arg2->dsc_address;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
switch (arg1->dsc_dtype)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_short:
|
|
|
|
if (*(SSHORT *) p1 == *(SSHORT *) p2)
|
|
|
|
return 0;
|
|
|
|
if (*(SSHORT *) p1 > *(SSHORT *) p2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case dtype_sql_time:
|
|
|
|
if (*(ULONG *) p1 == *(ULONG *) p2)
|
|
|
|
return 0;
|
|
|
|
if (*(ULONG *) p1 > *(ULONG *) p2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case dtype_long:
|
|
|
|
case dtype_sql_date:
|
|
|
|
if (*(SLONG *) p1 == *(SLONG *) p2)
|
|
|
|
return 0;
|
|
|
|
if (*(SLONG *) p1 > *(SLONG *) p2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case dtype_quad:
|
|
|
|
return QUAD_COMPARE(*(SQUAD *) p1, *(SQUAD *) p2);
|
|
|
|
|
|
|
|
case dtype_int64:
|
|
|
|
if (*(SINT64 *) p1 == *(SINT64 *) p2)
|
|
|
|
return 0;
|
|
|
|
if (*(SINT64 *) p1 > *(SINT64 *) p2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
|
2009-07-09 16:04:42 +02:00
|
|
|
case dtype_dbkey:
|
|
|
|
{
|
2009-07-13 12:00:43 +02:00
|
|
|
// keep old ttype_binary compare rules
|
|
|
|
USHORT l = MIN(arg1->dsc_length, arg2->dsc_length);
|
|
|
|
SSHORT rc = memcmp(p1, p2, l);
|
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
return rc;
|
|
|
|
}
|
2009-07-15 11:31:41 +02:00
|
|
|
return (arg1->dsc_length > l) ? 1 : (arg2->dsc_length > l) ? -1 : 0;
|
2009-07-09 16:04:42 +02:00
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_timestamp:
|
|
|
|
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 DEFAULT_DOUBLE:
|
|
|
|
if (*(double *) p1 == *(double *) p2)
|
|
|
|
return 0;
|
|
|
|
if (*(double *) p1 > *(double *) p2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
|
2010-12-18 03:17:06 +01:00
|
|
|
case dtype_boolean:
|
|
|
|
return *p1 == *p2 ? 0 : *p1 < *p2 ? -1 : 1;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_text:
|
|
|
|
case dtype_varying:
|
|
|
|
case dtype_cstring:
|
|
|
|
case dtype_array:
|
|
|
|
case dtype_blob:
|
2009-08-23 11:50:47 +02:00
|
|
|
// Special processing below
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-08-23 11:50:47 +02:00
|
|
|
// the two arguments have identical dtype and scale, but the
|
|
|
|
// dtype is not one of your defined types!
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(FALSE);
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
} // switch on dtype
|
|
|
|
} // if dtypes and scales are equal
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Handle mixed string comparisons
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-08 06:59:06 +01:00
|
|
|
if (arg1->dsc_dtype <= dtype_varying && arg2->dsc_dtype <= dtype_varying)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
/*
|
|
|
|
* For the sake of optimization, we call INTL_compare
|
|
|
|
* only when we cannot just do byte-by-byte compare.
|
|
|
|
* We can do a local compare here, if
|
|
|
|
* (a) one of the arguments is charset ttype_binary
|
|
|
|
* OR (b) both of the arguments are char set ttype_none
|
|
|
|
* OR (c) both of the arguments are char set ttype_ascii
|
|
|
|
* If any argument is ttype_dynamic, we must see the
|
|
|
|
* charset of the attachment.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SET_TDBB(tdbb);
|
2005-11-08 06:59:06 +01:00
|
|
|
CHARSET_ID charset1 = INTL_TTYPE(arg1);
|
|
|
|
if (charset1 == ttype_dynamic)
|
|
|
|
charset1 = INTL_charset(tdbb, charset1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-08 06:59:06 +01:00
|
|
|
CHARSET_ID charset2 = INTL_TTYPE(arg2);
|
|
|
|
if (charset2 == ttype_dynamic)
|
|
|
|
charset2 = INTL_charset(tdbb, charset2);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if ((IS_INTL_DATA(arg1) || IS_INTL_DATA(arg2)) &&
|
|
|
|
(charset1 != ttype_binary) &&
|
|
|
|
(charset2 != ttype_binary) &&
|
|
|
|
((charset1 != ttype_ascii) ||
|
|
|
|
(charset2 != ttype_ascii)) &&
|
2005-11-08 06:59:06 +01:00
|
|
|
((charset1 != ttype_none) || (charset2 != ttype_none)))
|
|
|
|
{
|
2008-07-10 17:57:33 +02:00
|
|
|
return INTL_compare(tdbb, arg1, arg2, ERR_post);
|
2005-11-08 06:59:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2003-11-05 10:02:33 +01:00
|
|
|
UCHAR* p1 = NULL;
|
|
|
|
UCHAR* p2 = NULL;
|
2005-11-08 06:59:06 +01:00
|
|
|
USHORT t1, t2; // unused later
|
2010-01-23 15:14:16 +01:00
|
|
|
USHORT length = CVT_get_string_ptr(arg1, &t1, &p1, NULL, 0);
|
|
|
|
USHORT length2 = CVT_get_string_ptr(arg2, &t2, &p2, NULL, 0);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-11-08 06:59:06 +01:00
|
|
|
int fill = length - length2;
|
|
|
|
const UCHAR pad = charset1 == ttype_binary || charset2 == ttype_binary ? '\0' : ' ';
|
2008-01-16 09:31:31 +01:00
|
|
|
|
|
|
|
if (length >= length2)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (length2)
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
do
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*p1++ != *p2++)
|
2008-01-16 09:31:31 +01:00
|
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
|
|
} while (--length2);
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (fill > 0)
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
do
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2005-11-08 06:59:06 +01:00
|
|
|
if (*p1++ != pad)
|
2008-01-16 09:31:31 +01:00
|
|
|
return (p1[-1] > pad) ? 1 : -1;
|
|
|
|
} while (--fill);
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
if (length)
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
do
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
if (*p1++ != *p2++)
|
2008-01-16 09:31:31 +01:00
|
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
|
|
} while (--length);
|
|
|
|
}
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
do
|
2008-01-16 09:31:31 +01:00
|
|
|
{
|
2005-11-08 06:59:06 +01:00
|
|
|
if (*p2++ != pad)
|
2008-01-16 09:31:31 +01:00
|
|
|
return (pad > p2[-1]) ? 1 : -1;
|
|
|
|
} while (++fill);
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Handle heterogeneous compares
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
if (compare_priority[arg1->dsc_dtype] < compare_priority[arg2->dsc_dtype])
|
2008-07-10 17:57:33 +02:00
|
|
|
return -CVT2_compare(arg2, arg1);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// At this point, the type of arg1 is guaranteed to be "greater than" arg2,
|
|
|
|
// in the sense that it is the preferred type for comparing the two.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2001-12-24 03:51:06 +01:00
|
|
|
switch (arg1->dsc_dtype)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_timestamp:
|
2005-11-08 06:59:06 +01:00
|
|
|
{
|
|
|
|
DSC desc;
|
|
|
|
MOVE_CLEAR(&desc, sizeof(desc));
|
|
|
|
desc.dsc_dtype = dtype_timestamp;
|
2009-11-23 06:39:35 +01:00
|
|
|
SLONG datetime[2];
|
|
|
|
desc.dsc_length = sizeof(datetime);
|
|
|
|
desc.dsc_address = (UCHAR*) datetime;
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(arg2, &desc);
|
|
|
|
return CVT2_compare(arg1, &desc);
|
2005-11-08 06:59:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case dtype_sql_time:
|
2005-11-08 06:59:06 +01:00
|
|
|
{
|
|
|
|
DSC desc;
|
|
|
|
MOVE_CLEAR(&desc, sizeof(desc));
|
|
|
|
desc.dsc_dtype = dtype_sql_time;
|
2009-11-23 06:39:35 +01:00
|
|
|
SLONG atime;
|
|
|
|
desc.dsc_length = sizeof(atime);
|
|
|
|
desc.dsc_address = (UCHAR*) &atime;
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(arg2, &desc);
|
|
|
|
return CVT2_compare(arg1, &desc);
|
2005-11-08 06:59:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case dtype_sql_date:
|
2005-11-08 06:59:06 +01:00
|
|
|
{
|
|
|
|
DSC desc;
|
|
|
|
MOVE_CLEAR(&desc, sizeof(desc));
|
|
|
|
desc.dsc_dtype = dtype_sql_date;
|
2009-11-23 06:39:35 +01:00
|
|
|
SLONG date;
|
|
|
|
desc.dsc_length = sizeof(date);
|
|
|
|
desc.dsc_address = (UCHAR*) &date;
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(arg2, &desc);
|
|
|
|
return CVT2_compare(arg1, &desc);
|
2005-11-08 06:59:06 +01:00
|
|
|
}
|
2001-05-23 15:26:42 +02:00
|
|
|
|
|
|
|
case dtype_short:
|
|
|
|
{
|
|
|
|
SSHORT scale;
|
|
|
|
if (arg2->dsc_dtype > dtype_varying)
|
|
|
|
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
|
|
|
|
else
|
|
|
|
scale = arg1->dsc_scale;
|
2008-07-10 17:57:33 +02:00
|
|
|
const SLONG temp1 = CVT_get_long(arg1, scale, ERR_post);
|
|
|
|
const SLONG temp2 = CVT_get_long(arg2, scale, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (temp1 == temp2)
|
|
|
|
return 0;
|
|
|
|
if (temp1 > temp2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
case dtype_long:
|
2009-08-23 11:50:47 +02:00
|
|
|
// Since longs may overflow when scaled, use int64 instead
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_int64:
|
|
|
|
{
|
|
|
|
SSHORT scale;
|
|
|
|
if (arg2->dsc_dtype > dtype_varying)
|
|
|
|
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
|
|
|
|
else
|
|
|
|
scale = arg1->dsc_scale;
|
2008-07-10 17:57:33 +02:00
|
|
|
const SINT64 temp1 = CVT_get_int64(arg1, scale, ERR_post);
|
|
|
|
const SINT64 temp2 = CVT_get_int64(arg2, scale, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (temp1 == temp2)
|
|
|
|
return 0;
|
|
|
|
if (temp1 > temp2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
case dtype_quad:
|
|
|
|
{
|
|
|
|
SSHORT scale;
|
|
|
|
if (arg2->dsc_dtype > dtype_varying)
|
|
|
|
scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
|
|
|
|
else
|
|
|
|
scale = arg1->dsc_scale;
|
2008-07-10 17:57:33 +02:00
|
|
|
const SQUAD temp1 = CVT_get_quad(arg1, scale, ERR_post);
|
|
|
|
const SQUAD temp2 = CVT_get_quad(arg2, scale, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
return QUAD_COMPARE(temp1, temp2);
|
|
|
|
}
|
|
|
|
|
|
|
|
case dtype_real:
|
|
|
|
{
|
2008-07-10 17:57:33 +02:00
|
|
|
const float temp1 = (float) CVT_get_double(arg1, ERR_post);
|
|
|
|
const float temp2 = (float) CVT_get_double(arg2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (temp1 == temp2)
|
|
|
|
return 0;
|
|
|
|
if (temp1 > temp2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
case dtype_double:
|
|
|
|
{
|
2008-07-10 17:57:33 +02:00
|
|
|
const double temp1 = CVT_get_double(arg1, ERR_post);
|
|
|
|
const double temp2 = CVT_get_double(arg2, ERR_post);
|
2001-05-23 15:26:42 +02:00
|
|
|
if (temp1 == temp2)
|
|
|
|
return 0;
|
|
|
|
if (temp1 > temp2)
|
|
|
|
return 1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
case dtype_blob:
|
2008-07-10 17:57:33 +02:00
|
|
|
return CVT2_blob_compare(arg1, arg2);
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
case dtype_array:
|
2008-07-10 17:57:33 +02:00
|
|
|
ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_blobnotsup) << "compare");
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
|
2009-07-09 16:04:42 +02:00
|
|
|
case dtype_dbkey:
|
2011-11-05 19:07:04 +01:00
|
|
|
if (arg2->isText())
|
2009-07-09 16:04:42 +02:00
|
|
|
{
|
|
|
|
UCHAR* p = NULL;
|
|
|
|
USHORT t; // unused later
|
2010-01-23 15:14:16 +01:00
|
|
|
USHORT length = CVT_get_string_ptr(arg2, &t, &p, NULL, 0);
|
2009-07-13 12:00:43 +02:00
|
|
|
|
2009-07-09 16:04:42 +02:00
|
|
|
USHORT l = MIN(arg1->dsc_length, length);
|
|
|
|
SSHORT rc = memcmp(arg1->dsc_address, p, l);
|
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
return rc;
|
|
|
|
}
|
2009-07-14 10:24:59 +02:00
|
|
|
return (arg1->dsc_length > l) ? 1 : (length > l) ? -1 : 0;
|
2009-07-09 16:04:42 +02:00
|
|
|
}
|
|
|
|
ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_random) << "DB_KEY compare");
|
|
|
|
break;
|
|
|
|
|
2010-12-18 03:17:06 +01:00
|
|
|
case dtype_boolean:
|
|
|
|
ERR_post(Arg::Gds(isc_invalid_boolean_usage));
|
|
|
|
break;
|
|
|
|
|
2001-05-23 15:26:42 +02:00
|
|
|
default:
|
2009-08-23 11:50:47 +02:00
|
|
|
BUGCHECK(189); // msg 189 comparison not supported for specified data types
|
2001-05-23 15:26:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-10 17:57:33 +02:00
|
|
|
SSHORT CVT2_blob_compare(const dsc* arg1, const dsc* arg2)
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
|
|
|
* C V T 2 _ b l o b _ c o m p a r e
|
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
* Compare two blobs. Return (-1, 0, 1) if a<b, a=b, or a>b.
|
|
|
|
* Alternatively, it will try to compare a blob against a string;
|
|
|
|
* in this case, the string should be the second argument.
|
|
|
|
* CVC: Ann Harrison asked for this function to make comparisons more
|
|
|
|
* complete in the engine.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-02-20 07:43:27 +01:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
SLONG l1, l2;
|
2005-11-08 06:59:06 +01:00
|
|
|
USHORT ttype2;
|
2002-06-29 15:03:13 +02:00
|
|
|
SSHORT ret_val = 0;
|
|
|
|
|
2004-03-11 06:04:26 +01:00
|
|
|
thread_db* tdbb = NULL;
|
2002-06-29 15:03:13 +02:00
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// DEV_BLKCHK (node, type_nod);
|
2002-06-29 15:03:13 +02:00
|
|
|
|
|
|
|
if (arg1->dsc_dtype != dtype_blob)
|
2008-07-10 17:57:33 +02:00
|
|
|
ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_datnotsup));
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-11-08 06:59:06 +01:00
|
|
|
USHORT ttype1;
|
2005-05-28 00:45:31 +02:00
|
|
|
if (arg1->dsc_sub_type == isc_blob_text)
|
2009-08-23 11:50:47 +02:00
|
|
|
ttype1 = arg1->dsc_blob_ttype(); // Load blob character set and collation
|
2005-05-28 00:45:31 +02:00
|
|
|
else
|
|
|
|
ttype1 = ttype_binary;
|
|
|
|
|
2005-06-14 05:16:54 +02:00
|
|
|
TextType* obj1 = INTL_texttype_lookup(tdbb, ttype1);
|
2005-05-28 00:45:31 +02:00
|
|
|
ttype1 = obj1->getType();
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Is arg2 a blob?
|
2002-06-29 15:03:13 +02:00
|
|
|
if (arg2->dsc_dtype == dtype_blob)
|
|
|
|
{
|
2009-08-23 11:50:47 +02:00
|
|
|
// Same blob id address?
|
2002-06-29 15:03:13 +02:00
|
|
|
if (arg1->dsc_address == arg2->dsc_address)
|
|
|
|
return 0;
|
2009-01-14 09:22:32 +01:00
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Second test for blob id, checking relation and slot.
|
2009-01-14 09:22:32 +01:00
|
|
|
const bid* bid1 = (bid*) arg1->dsc_address;
|
|
|
|
const bid* bid2 = (bid*) arg2->dsc_address;
|
|
|
|
if (*bid1 == *bid2)
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
2009-01-14 09:22:32 +01:00
|
|
|
return 0;
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2004-04-29 19:48:39 +02:00
|
|
|
if (arg2->dsc_sub_type == isc_blob_text)
|
2009-08-23 11:50:47 +02:00
|
|
|
ttype2 = arg2->dsc_blob_ttype(); // Load blob character set and collation
|
2002-06-29 15:03:13 +02:00
|
|
|
else
|
2005-05-28 00:45:31 +02:00
|
|
|
ttype2 = ttype_binary;
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-06-14 05:16:54 +02:00
|
|
|
TextType* obj2 = INTL_texttype_lookup(tdbb, ttype2);
|
2005-05-28 00:45:31 +02:00
|
|
|
ttype2 = obj2->getType();
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
if (ttype1 == ttype_binary || ttype2 == ttype_binary)
|
|
|
|
ttype1 = ttype2 = ttype_binary;
|
|
|
|
else if (ttype1 == ttype_none || ttype2 == ttype_none)
|
|
|
|
ttype1 = ttype2 = ttype_none;
|
2004-02-20 07:43:27 +01:00
|
|
|
|
2005-06-14 05:16:54 +02:00
|
|
|
obj1 = INTL_texttype_lookup(tdbb, ttype1);
|
|
|
|
obj2 = INTL_texttype_lookup(tdbb, ttype2);
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
CharSet* charSet1 = obj1->getCharSet();
|
|
|
|
CharSet* charSet2 = obj2->getCharSet();
|
|
|
|
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer1;
|
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer2;
|
|
|
|
fb_assert(BUFFER_LARGE % 4 == 0); // 4 is our maximum character length
|
|
|
|
|
|
|
|
UCHAR bpb[] = {isc_bpb_version1,
|
|
|
|
isc_bpb_source_type, 1, isc_blob_text, isc_bpb_source_interp, 1, 0,
|
|
|
|
isc_bpb_target_type, 1, isc_blob_text, isc_bpb_target_interp, 1, 0};
|
|
|
|
USHORT bpbLength = 0;
|
|
|
|
|
|
|
|
if (arg1->dsc_sub_type == isc_blob_text && arg2->dsc_sub_type == isc_blob_text)
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
bpb[6] = arg2->dsc_scale; // source charset
|
|
|
|
bpb[12] = arg1->dsc_scale; // destination charset
|
|
|
|
bpbLength = sizeof(bpb);
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
|
|
|
|
2012-02-15 04:34:21 +01:00
|
|
|
blb* blob1 = blb::open(tdbb, tdbb->getRequest()->req_transaction, (bid*) arg1->dsc_address);
|
|
|
|
blb* blob2 = blb::open2(tdbb, tdbb->getRequest()->req_transaction, (bid*) arg2->dsc_address, bpbLength, bpb);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (charSet1->isMultiByte())
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
buffer1.getBuffer(blob1->blb_length);
|
2005-06-08 13:10:13 +02:00
|
|
|
buffer2.getBuffer(blob2->blb_length / charSet2->minBytesPerChar() * charSet1->maxBytesPerChar());
|
2003-11-04 00:59:24 +01:00
|
|
|
}
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
while (ret_val == 0 && !(blob1->blb_flags & BLB_eof) && !(blob2->blb_flags & BLB_eof))
|
2003-11-04 00:59:24 +01:00
|
|
|
{
|
2012-02-07 04:17:52 +01:00
|
|
|
l1 = blob1->BLB_get_data(tdbb, buffer1.begin(), buffer1.getCapacity(), false);
|
|
|
|
l2 = blob2->BLB_get_data(tdbb, buffer2.begin(), buffer2.getCapacity(), false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
ret_val = obj1->compare(l1, buffer1.begin(), l2, buffer2.begin());
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (ret_val == 0)
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if ((blob1->blb_flags & BLB_eof) == BLB_eof)
|
|
|
|
l1 = 0;
|
|
|
|
|
|
|
|
if ((blob2->blb_flags & BLB_eof) == BLB_eof)
|
|
|
|
l2 = 0;
|
|
|
|
|
|
|
|
while (ret_val == 0 &&
|
|
|
|
!((blob1->blb_flags & BLB_eof) == BLB_eof &&
|
|
|
|
(blob2->blb_flags & BLB_eof) == BLB_eof))
|
2002-06-29 15:03:13 +02:00
|
|
|
{
|
2005-05-28 00:45:31 +02:00
|
|
|
if (!(blob1->blb_flags & BLB_eof))
|
2012-02-07 04:17:52 +01:00
|
|
|
l1 = blob1->BLB_get_data(tdbb, buffer1.begin(), buffer1.getCapacity(), false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (!(blob2->blb_flags & BLB_eof))
|
2012-02-07 04:17:52 +01:00
|
|
|
l2 = blob2->BLB_get_data(tdbb, buffer2.begin(), buffer2.getCapacity(), false);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
ret_val = obj1->compare(l1, buffer1.begin(), l2, buffer2.begin());
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2012-02-07 04:17:52 +01:00
|
|
|
blob1->BLB_close(tdbb);
|
|
|
|
blob2->BLB_close(tdbb);
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
|
|
|
else if (arg2->dsc_dtype == dtype_array)
|
2009-08-23 11:50:47 +02:00
|
|
|
{
|
|
|
|
// We do not accept arrays for now. Maybe InternalArrayDesc in the future.
|
2008-07-10 17:57:33 +02:00
|
|
|
ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_datnotsup));
|
2009-08-23 11:50:47 +02:00
|
|
|
}
|
2002-06-29 15:03:13 +02:00
|
|
|
else
|
|
|
|
{
|
2009-08-23 11:50:47 +02:00
|
|
|
// The second parameter should be a string.
|
2002-06-29 15:03:13 +02:00
|
|
|
if (arg2->dsc_dtype <= dtype_varying)
|
|
|
|
{
|
2005-09-04 17:28:09 +02:00
|
|
|
if ((ttype2 = arg2->dsc_ttype()) != ttype_binary)
|
2005-05-28 00:45:31 +02:00
|
|
|
ttype2 = ttype1;
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
else
|
|
|
|
ttype2 = ttype1;
|
|
|
|
|
|
|
|
if (ttype1 == ttype_binary || ttype2 == ttype_binary)
|
|
|
|
ttype1 = ttype2 = ttype_binary;
|
|
|
|
else if (ttype1 == ttype_none || ttype2 == ttype_none)
|
|
|
|
ttype1 = ttype2 = ttype_none;
|
|
|
|
|
2005-06-14 05:16:54 +02:00
|
|
|
obj1 = INTL_texttype_lookup(tdbb, ttype1);
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-05-28 00:45:31 +02:00
|
|
|
CharSet* charSet1 = obj1->getCharSet();
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2005-05-12 20:28:04 +02:00
|
|
|
Firebird::HalfStaticArray<UCHAR, BUFFER_LARGE> buffer1;
|
2005-05-28 00:45:31 +02:00
|
|
|
UCHAR* p;
|
|
|
|
MoveBuffer temp_str;
|
|
|
|
|
2008-07-10 17:57:33 +02:00
|
|
|
l2 = CVT2_make_string2(arg2, ttype1, &p, temp_str);
|
2002-06-29 15:03:13 +02:00
|
|
|
|
2012-02-15 04:34:21 +01:00
|
|
|
blb* blob1 = blb::open(tdbb, tdbb->getRequest()->req_transaction, (bid*) arg1->dsc_address);
|
2005-05-28 00:45:31 +02:00
|
|
|
|
|
|
|
if (charSet1->isMultiByte())
|
|
|
|
buffer1.getBuffer(blob1->blb_length);
|
|
|
|
else
|
|
|
|
buffer1.getBuffer(l2);
|
|
|
|
|
2012-02-07 04:17:52 +01:00
|
|
|
l1 = blob1->BLB_get_data(tdbb, buffer1.begin(), buffer1.getCapacity(), false);
|
2005-05-28 00:45:31 +02:00
|
|
|
ret_val = obj1->compare(l1, buffer1.begin(), l2, p);
|
|
|
|
|
|
|
|
while (ret_val == 0 && (blob1->blb_flags & BLB_eof) != BLB_eof)
|
|
|
|
{
|
2012-02-07 04:17:52 +01:00
|
|
|
l1 = blob1->BLB_get_data(tdbb, buffer1.begin(), buffer1.getCapacity(), false);
|
2005-05-28 00:45:31 +02:00
|
|
|
ret_val = obj1->compare(l1, buffer1.begin(), 0, p);
|
|
|
|
}
|
|
|
|
|
2012-02-07 04:17:52 +01:00
|
|
|
blob1->BLB_close(tdbb);
|
2002-06-29 15:03:13 +02:00
|
|
|
}
|
2005-05-28 00:45:31 +02:00
|
|
|
|
2002-06-29 15:03:13 +02:00
|
|
|
return ret_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Jrd::MoveBuffer& temp)
|
2001-05-23 15:26:42 +02:00
|
|
|
{
|
|
|
|
/**************************************
|
|
|
|
*
|
2009-05-02 16:56:43 +02:00
|
|
|
* C V T 2 _ m a k e _ s t r i n g 2
|
2001-05-23 15:26:42 +02:00
|
|
|
*
|
|
|
|
**************************************
|
|
|
|
*
|
|
|
|
* Functional description
|
|
|
|
*
|
|
|
|
* Convert the data from the desc to a string in the specified interp.
|
|
|
|
* The pointer to this string is returned in address.
|
|
|
|
*
|
|
|
|
**************************************/
|
2004-02-20 07:43:27 +01:00
|
|
|
UCHAR* from_buf;
|
2001-05-23 15:26:42 +02:00
|
|
|
USHORT from_len;
|
|
|
|
USHORT from_interp;
|
|
|
|
|
2003-11-04 00:59:24 +01:00
|
|
|
fb_assert(desc != NULL);
|
|
|
|
fb_assert(address != NULL);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
switch (desc->dsc_dtype)
|
|
|
|
{
|
|
|
|
case dtype_text:
|
2001-05-23 15:26:42 +02:00
|
|
|
from_buf = desc->dsc_address;
|
|
|
|
from_len = desc->dsc_length;
|
|
|
|
from_interp = INTL_TTYPE(desc);
|
2009-01-14 09:22:32 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
case dtype_cstring:
|
2001-05-23 15:26:42 +02:00
|
|
|
from_buf = desc->dsc_address;
|
2009-01-14 09:22:32 +01:00
|
|
|
from_len = MIN(strlen((char *) desc->dsc_address), (unsigned) (desc->dsc_length - 1));
|
2001-05-23 15:26:42 +02:00
|
|
|
from_interp = INTL_TTYPE(desc);
|
2009-01-14 09:22:32 +01:00
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
case dtype_varying:
|
|
|
|
{
|
|
|
|
vary* varying = (vary*) desc->dsc_address;
|
|
|
|
from_buf = reinterpret_cast<UCHAR*>(varying->vary_string);
|
|
|
|
from_len = MIN(varying->vary_length, (USHORT) (desc->dsc_length - sizeof(SSHORT)));
|
|
|
|
from_interp = INTL_TTYPE(desc);
|
|
|
|
}
|
|
|
|
break;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2011-11-05 19:07:04 +01:00
|
|
|
if (desc->isText())
|
2009-01-14 09:22:32 +01:00
|
|
|
{
|
2011-11-05 19:07:04 +01:00
|
|
|
if (from_interp == to_interp || to_interp == ttype_none || to_interp == ttype_binary)
|
2009-08-23 11:50:47 +02:00
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*address = from_buf;
|
|
|
|
return from_len;
|
|
|
|
}
|
|
|
|
|
2004-05-22 16:28:54 +02:00
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
2005-06-14 05:16:54 +02:00
|
|
|
const USHORT cs1 = INTL_charset(tdbb, to_interp);
|
|
|
|
const USHORT cs2 = INTL_charset(tdbb, from_interp);
|
2009-08-23 11:50:47 +02:00
|
|
|
if (cs1 == cs2)
|
|
|
|
{
|
2001-05-23 15:26:42 +02:00
|
|
|
*address = from_buf;
|
|
|
|
return from_len;
|
|
|
|
}
|
2008-01-16 09:31:31 +01:00
|
|
|
|
2009-01-14 09:22:32 +01:00
|
|
|
USHORT length = INTL_convert_bytes(tdbb, cs1, NULL, 0, cs2, from_buf, from_len, ERR_post);
|
2008-01-16 09:31:31 +01:00
|
|
|
UCHAR* tempptr = temp.getBuffer(length);
|
2009-01-14 09:22:32 +01:00
|
|
|
length = INTL_convert_bytes(tdbb, cs1, tempptr, length, cs2, from_buf, from_len, ERR_post);
|
2008-01-16 09:31:31 +01:00
|
|
|
*address = tempptr;
|
2009-10-21 02:42:38 +02:00
|
|
|
temp.resize(length);
|
2008-01-16 09:31:31 +01:00
|
|
|
return length;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|
|
|
|
|
2009-08-23 11:50:47 +02:00
|
|
|
// Not string data, then -- convert value to varying string.
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2004-02-20 07:43:27 +01:00
|
|
|
dsc temp_desc;
|
2001-05-23 15:26:42 +02:00
|
|
|
MOVE_CLEAR(&temp_desc, sizeof(temp_desc));
|
2005-05-12 20:28:04 +02:00
|
|
|
temp_desc.dsc_length = temp.getCapacity();
|
|
|
|
temp_desc.dsc_address = temp.getBuffer(temp_desc.dsc_length);
|
|
|
|
vary* vtmp = reinterpret_cast<vary*>(temp_desc.dsc_address);
|
2001-05-23 15:26:42 +02:00
|
|
|
temp_desc.dsc_dtype = dtype_varying;
|
2009-11-28 20:39:23 +01:00
|
|
|
temp_desc.setTextType(to_interp);
|
2008-07-10 17:57:33 +02:00
|
|
|
CVT_move(desc, &temp_desc);
|
2005-05-12 20:28:04 +02:00
|
|
|
*address = reinterpret_cast<UCHAR*>(vtmp->vary_string);
|
2001-05-23 15:26:42 +02:00
|
|
|
|
2005-05-12 20:28:04 +02:00
|
|
|
return vtmp->vary_length;
|
2001-05-23 15:26:42 +02:00
|
|
|
}
|