- Notifications
You must be signed in to change notification settings - Fork 234
/
Copy pathDataTypeUtil.cpp
405 lines (332 loc) · 11.2 KB
/
DataTypeUtil.cpp
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
/*
* 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"
usingnamespaceFirebird;
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;
}
USHORT DataTypeUtilBase::getResultTextType(const dsc* value1, const dsc* value2)
{
const USHORT cs1 = value1->getCharSet();
const USHORT cs2 = value2->getCharSet();
const USHORT ttype1 = value1->getTextType();
const USHORT 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.
voidDataTypeUtilBase::makeFromList(dsc* result, constchar* 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;
elseif (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)
}
elseif (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);
}
elseif (DTYPE_IS_DATE(arg->dsc_dtype))
{
if (result->isUnknown())
*result = *arg;
elseif (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);
}
elseif (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);
}
}
elseif (arg->dsc_dtype == dtype_boolean)
{
if (result->isUnknown())
*result = *arg;
elseif (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, USHORT srcCharSet, USHORT dstCharSet)
{
if (dstCharSet == CS_NONE || dstCharSet == CS_BINARY)
return len;
return (len / maxBytesPerChar(srcCharSet)) * maxBytesPerChar(dstCharSet);
}
ULONG DataTypeUtilBase::convertLength(const dsc* src, const dsc* dst)
{
fb_assert(dst->isText());
if (src->dsc_dtype == dtype_dbkey)
{
return src->dsc_length;
}
returnconvertLength(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);
elseif (desc->dsc_dtype == dtype_cstring)
overhead = sizeof(UCHAR);
returnMIN(((MAX_STR_SIZE - overhead) / bpc) * bpc, length);
}
voidDataTypeUtilBase::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;
}
elseif (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());
}
voidDataTypeUtilBase::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() : 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));
}
}
boolDataTypeUtilBase::makeBlobOrText(dsc* result, const dsc* arg, bool force)
{
if (arg->isBlob() || result->isBlob())
{
result->makeBlob(getResultBlobSubType(result, arg), getResultTextType(result, arg));
returntrue;
}
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));
returntrue;
}
returnfalse;
}
namespaceJrd {
UCHAR DataTypeUtil::maxBytesPerChar(UCHAR charSet)
{
returnINTL_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.
boolDataTypeUtil::convertToUTF8(const string& src, string& dst, CHARSET_ID 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)
returnfalse;
if (charset == CS_NONE)
{
const FB_SIZE_T length = src.length();
constchar* s = src.c_str();
char* p = dst.getBuffer(length);
for (constchar* end = src.end(); s < end; ++p, ++s)
*p = (*s < 0 ? '?' : *s);
}
else// charset != CS_UTF8
{
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(),
err);
dst.resize(length);
}
returntrue;
}
} // namespace Jrd