-
-
Notifications
You must be signed in to change notification settings - Fork 267
Expand file tree
/
Copy pathDataTypeUtil.cpp
More file actions
417 lines (342 loc) · 11.5 KB
/
DataTypeUtil.cpp
File metadata and controls
417 lines (342 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
/*
* PROGRAM:
* MODULE: DataTypeUtil.cpp
* DESCRIPTION: Data Type Utility functions
*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Adriano dos Santos Fernandes
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2006 Adriano dos Santos Fernandes <adrianosf@uol.com.br>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../jrd/DataTypeUtil.h"
#include "../jrd/SysFunction.h"
#include "../jrd/align.h"
#include "../common/cvt.h"
#include "../common/dsc.h"
#include "../jrd/intl.h"
#include "../common/dsc_proto.h"
#include "../jrd/intl_proto.h"
#include "../common/gdsassert.h"
using namespace Firebird;
SSHORT DataTypeUtilBase::getResultBlobSubType(const dsc* value1, const dsc* value2)
{
const SSHORT subType1 = value1->getBlobSubType();
const SSHORT subType2 = value2->getBlobSubType();
if (value1->isUnknown())
return subType2;
if (value2->isUnknown())
return subType1;
if (subType2 == isc_blob_untyped) // binary
return subType2;
return subType1;
}
TTypeId DataTypeUtilBase::getResultTextType(const dsc* value1, const dsc* value2)
{
const auto cs1 = value1->getCharSet();
const auto cs2 = value2->getCharSet();
const auto ttype1 = value1->getTextType();
const auto ttype2 = value2->getTextType();
if (cs1 == CS_NONE || cs2 == CS_BINARY)
return ttype2;
if (cs1 == CS_ASCII && cs2 != CS_NONE)
return ttype2;
return ttype1;
}
// This function is made to determine a output descriptor from a given list
// of expressions according to the latest SQL-standard that was available.
// (ISO/ANSI SQL:200n WG3:DRS-013 H2-2002-358 August, 2002).
//
// The output type is figured out as based on this order:
// 1) If any datatype is blob, returns blob;
// 2) If any datatype is a) varying or b) any text/cstring and another datatype, returns varying;
// 3) If any datatype is text or cstring, returns text;
// 4) If any datatype is approximate numeric then each datatype in the list shall be numeric
// (otherwise an error is thrown), returns approximate numeric;
// 5) If all datatypes are exact numeric, returns exact numeric with the maximum scale and the
// maximum precision used.
// 6) If any datatype is a date/time/timestamp then each datatype in the list shall be the same
// date/time/timestamp (otherwise an error is thrown), returns a date/time/timestamp.
//
// If a blob is returned, and there is a binary blob in the list, a binary blob is returned.
//
// If a blob/text is returned, the returned charset is figured out as based on this order:
// 1) If there is a OCTETS blob/string, returns OCTETS;
// 2) If there is a non-(NONE/ASCII) blob/string, returns it charset;
// 3) If there is a ASCII blob/string, a numeric or a date/time/timestamp, returns ASCII;
// 4) Otherwise, returns NONE.
void DataTypeUtilBase::makeFromList(dsc* result, const char* expressionName, int argsCount,
const dsc** args)
{
result->clear();
bool allNulls = true;
bool nullable = false;
bool anyVarying = false;
bool anyBlobOrText = false;
for (const dsc** p = args; p < args + argsCount; ++p)
{
const dsc* arg = *p;
allNulls &= arg->isNull();
// Ignore NULL and parameter value from walking.
if (arg->isNull() || arg->isUnknown())
{
nullable = true;
continue;
}
nullable |= arg->isNullable();
anyVarying |= arg->dsc_dtype != dtype_text;
if (makeBlobOrText(result, arg, false))
anyBlobOrText = true;
else if (DTYPE_IS_NUMERIC(arg->dsc_dtype))
{
if (result->isUnknown() || DTYPE_IS_NUMERIC(result->dsc_dtype))
{
if (!arg->isExact() && result->isExact())
{
*result = *arg;
result->dsc_scale = 0; // clear it (for dialect 1)
}
else if (result->isUnknown() || result->isExact() || !arg->isExact())
{
result->dsc_dtype = MAX(result->dsc_dtype, arg->dsc_dtype);
result->dsc_length = MAX(result->dsc_length, arg->dsc_length);
result->dsc_scale = MIN(result->dsc_scale, arg->dsc_scale); // scale is negative
result->dsc_sub_type = MAX(result->dsc_sub_type, arg->dsc_sub_type);
}
}
else
makeBlobOrText(result, arg, true);
}
else if (DTYPE_IS_DATE(arg->dsc_dtype))
{
if (result->isUnknown())
*result = *arg;
else if (result->dsc_dtype != arg->dsc_dtype)
{
UCHAR low = MIN(result->dsc_dtype, arg->dsc_dtype);
UCHAR high = MAX(result->dsc_dtype, arg->dsc_dtype);
if (low == dtype_sql_time && high == dtype_sql_time_tz)
{
result->dsc_dtype = dtype_sql_time_tz;
result->dsc_length = sizeof(ISC_TIME_TZ);
}
else if (low == dtype_timestamp && high == dtype_timestamp_tz)
{
result->dsc_dtype = dtype_timestamp_tz;
result->dsc_length = sizeof(ISC_TIMESTAMP_TZ);
}
else
makeBlobOrText(result, arg, true);
}
}
else if (arg->dsc_dtype == dtype_boolean)
{
if (result->isUnknown())
*result = *arg;
else if (result->dsc_dtype != arg->dsc_dtype)
makeBlobOrText(result, arg, true);
}
else // we don't support this datatype here
{
// Unknown datatype
status_exception::raise(Arg::Gds(isc_sqlerr) << Arg::Num(-804) <<
Arg::Gds(isc_dsql_datatype_err));
}
}
// If we didn't have any blob or text but return a blob or text, it means we have incomparable
// types like date and time without a blob or string.
if (!anyBlobOrText && (result->isText() || result->isBlob()))
{
// Datatypes @1are not comparable in expression @2
status_exception::raise(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
Arg::Gds(isc_dsql_datatypes_not_comparable) << Arg::Str("") <<
Arg::Str(expressionName));
}
if (allNulls)
result->makeNullString();
result->setNullable(nullable);
// We'll return a string...
if (result->isText())
{
// So convert its character length to max. byte length of the destination charset.
const ULONG len = convertLength(result->dsc_length, CS_ASCII, result->getCharSet());
if (anyVarying)
result->dsc_dtype = dtype_varying;
result->dsc_length = fixLength(result, len);
if (anyVarying)
result->dsc_length += sizeof(USHORT);
}
}
ULONG DataTypeUtilBase::convertLength(ULONG len, CSetId srcCharSet, CSetId dstCharSet)
{
if (dstCharSet == CS_NONE || dstCharSet == CS_BINARY)
return len;
const ULONG srcBPC = maxBytesPerChar(srcCharSet);
const ULONG dstBPC = maxBytesPerChar(dstCharSet);
return (ROUNDUP(len, srcBPC) / srcBPC) * dstBPC;
}
ULONG DataTypeUtilBase::convertLength(const dsc* src, const dsc* dst)
{
fb_assert(dst->isText());
if (src->dsc_dtype == dtype_dbkey)
{
return src->dsc_length;
}
return convertLength(src->getStringLength(), src->getCharSet(), dst->getCharSet());
}
ULONG DataTypeUtilBase::fixLength(const dsc* desc, ULONG length)
{
const UCHAR bpc = maxBytesPerChar(desc->getCharSet());
USHORT overhead = 0;
if (desc->dsc_dtype == dtype_varying)
overhead = sizeof(USHORT);
else if (desc->dsc_dtype == dtype_cstring)
overhead = sizeof(UCHAR);
return MIN(((MAX_STR_SIZE - overhead) / bpc) * bpc, length);
}
void DataTypeUtilBase::makeConcatenate(dsc* result, const dsc* value1, const dsc* value2)
{
result->clear();
if (value1->isNull() && value2->isNull())
{
result->makeNullString();
return;
}
if (value1->dsc_dtype == dtype_dbkey && value2->dsc_dtype == dtype_dbkey)
{
result->dsc_dtype = dtype_dbkey;
result->dsc_length = value1->dsc_length + value2->dsc_length;
}
else if (value1->isBlob() || value2->isBlob())
{
result->dsc_dtype = dtype_blob;
result->dsc_length = sizeof(ISC_QUAD);
result->setBlobSubType(getResultBlobSubType(value1, value2));
result->setTextType(getResultTextType(value1, value2));
}
else
{
result->dsc_dtype = dtype_varying;
result->setTextType(getResultTextType(value1, value2));
ULONG length = fixLength(result,
convertLength(value1, result) + convertLength(value2, result));
result->dsc_length = length + static_cast<USHORT>(sizeof(USHORT));
}
result->setNullable(value1->isNullable() || value2->isNullable());
}
void DataTypeUtilBase::makeSubstr(dsc* result, const dsc* value, const dsc* offset, const dsc* length)
{
result->clear();
if (value->isNull())
{
result->makeNullString();
return;
}
if (value->isBlob())
{
result->dsc_dtype = dtype_blob;
result->dsc_length = sizeof(ISC_QUAD);
result->setBlobSubType(value->getBlobSubType());
}
else
{
// Beware that JRD treats substring() always as returning CHAR
// instead of VARCHAR for historical reasons.
result->dsc_dtype = dtype_varying;
}
result->setTextType(value->isText() || value->isBlob() ? value->getTextType() : TTypeId(CS_ASCII));
result->setNullable(value->isNullable() ||
(offset && offset->isNullable()) ||
(length && length->isNullable()));
if (result->isText())
{
ULONG len = convertLength(value, result);
if (length && length->dsc_address) // constant
{
SLONG constant = CVT_get_long(length, 0, JRD_get_thread_data()->getAttachment()->att_dec_status, ERR_post);
fb_assert(constant >= 0);
len = MIN(len, MIN(MAX_STR_SIZE, ULONG(constant)) * maxBytesPerChar(result->getCharSet()));
}
result->dsc_length = fixLength(result, len) + static_cast<USHORT>(sizeof(USHORT));
}
}
bool DataTypeUtilBase::makeBlobOrText(dsc* result, const dsc* arg, bool force)
{
if (arg->isBlob() || result->isBlob())
{
result->makeBlob(getResultBlobSubType(result, arg), getResultTextType(result, arg));
return true;
}
if (force || arg->isText() || result->isText())
{
USHORT argLen = convertLength(arg->getStringLength(), arg->getCharSet(), CS_ASCII);
USHORT resultLen = result->getStringLength();
result->makeText(MAX(argLen, resultLen), getResultTextType(result, arg));
return true;
}
return false;
}
namespace Jrd {
UCHAR DataTypeUtil::maxBytesPerChar(CSetId charSet)
{
return INTL_charset_lookup(tdbb, charSet)->maxBytesPerChar();
}
USHORT DataTypeUtil::getDialect() const
{
return (tdbb->getDatabase()->dbb_flags & DBB_DB_SQL_dialect_3) ? 3 : 1;
}
// Returns false if conversion is not needed.
bool DataTypeUtil::convertToUTF8(const string& src, string& dst, CSetId charset, ErrorFunction err)
{
thread_db* tdbb = JRD_get_thread_data();
if (charset == CS_dynamic)
{
fb_assert(tdbb->getAttachment());
charset = tdbb->getAttachment()->att_charset;
}
if (charset == CS_UTF8 || charset == CS_UNICODE_FSS)
return false;
// We throw a status_exception exception to catch it and check charset again.
// If charset is NONE, we re-throw the exception through err().
try
{
DataTypeUtil dtUtil(tdbb);
ULONG length = dtUtil.convertLength(src.length(), charset, CS_UTF8);
length = INTL_convert_bytes(tdbb,
CS_UTF8, (UCHAR*) dst.getBuffer(length), length,
charset, (const BYTE*) src.begin(), src.length(),
status_exception::raise);
dst.resize(length);
}
catch (const status_exception& ex)
{
const Arg::StatusVector v(ex);
if (charset == CS_NONE)
{
const FB_SIZE_T length = src.length();
const char* s = src.c_str();
char* p = dst.getBuffer(length);
for (const char* end = src.end(); s < end; ++p, ++s)
*p = (*s < ASCII_SPACE ? '?' : *s);
}
else
err(v);
}
return true;
}
} // namespace Jrd