mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 16:43:03 +01:00
Feature #6798 - Add built-in functions UNICODE_CHAR and UNICODE_VAL to convert between Unicode code point and character.
This commit is contained in:
parent
d0eac02bb9
commit
3b372197e4
@ -721,22 +721,22 @@ Notes:
|
||||
1) If the first argument (relation) is a string expression or literal, then
|
||||
it's treated as a relation name and the engine searches for the
|
||||
corresponding relation ID. The search is case-sensitive.
|
||||
In the case of string literal, relation ID is evaluated at prepare time.
|
||||
In the case of expression, relation ID is evaluated at execution time.
|
||||
In the case of string literal, relation ID is evaluated at prepare time.
|
||||
In the case of expression, relation ID is evaluated at execution time.
|
||||
If the relation couldn't be found, then isc_relnotdef error is raised.
|
||||
2) If the first argument (relation) is a numeric expression or literal, then
|
||||
2) If the first argument (relation) is a numeric expression or literal, then
|
||||
it's treated as a relation ID and used "as is", without verification
|
||||
against existing relations.
|
||||
If the argument value is negative or greater than the maximum allowed
|
||||
relation ID (65535 currently), then NULL is returned.
|
||||
3) Second argument (recnum) represents an absolute record number in relation
|
||||
3) Second argument (recnum) represents an absolute record number in relation
|
||||
(if the next arguments -- dpnum and ppnum -- are missing), or a record
|
||||
number relative to the first record, specified by the next arguments.
|
||||
4) Third argument (dpnum) is a logical number of data page in relation (if
|
||||
4) Third argument (dpnum) is a logical number of data page in relation (if
|
||||
the next argument -- ppnum -- is missing), or number of data page
|
||||
relative to the first data page addressed by the given ppnum.
|
||||
5) Forth argument (ppnum) is a logical number of pointer page in relation.
|
||||
6) All numbers are zero-based.
|
||||
6) All numbers are zero-based.
|
||||
Maximum allowed value for dpnum and ppnum is 2^32 (4294967296).
|
||||
If dpnum is specified, then recnum could be negative.
|
||||
If dpnum is missing and recnum is negative then NULL is returned.
|
||||
@ -763,7 +763,7 @@ Examples:
|
||||
where rdb$db_key >= make_dbkey(6, 0, 0)
|
||||
and rdb$db_key < make_dbkey(6, 0, 1)
|
||||
|
||||
4) Select all records that physically reside at first data page of 6th pointer
|
||||
4) Select all records that physically reside at first data page of 6th pointer
|
||||
page at relation
|
||||
|
||||
select * from SOMETABLE
|
||||
@ -1255,6 +1255,41 @@ Example:
|
||||
3) select trunc(987.65, 1), trunc(987.65, -1) from rdb$database; -- returns 987.60, 980.00
|
||||
|
||||
|
||||
------------
|
||||
UNICODE_CHAR
|
||||
------------
|
||||
|
||||
Function:
|
||||
Returns the UNICODE character with the specified code point.
|
||||
|
||||
Format:
|
||||
UNICODE_CHAR( <number> )
|
||||
|
||||
Notes:
|
||||
Argument to UNICODE_CHAR must be a valid UTF-32 code point not in the range of
|
||||
high/low surrogates (0xD800 to 0xDFFF). Otherwise it throws an error.
|
||||
|
||||
Example:
|
||||
select unicode_char(x) from y;
|
||||
|
||||
|
||||
-----------
|
||||
UNICODE_VAL
|
||||
-----------
|
||||
|
||||
Function:
|
||||
Returns the UTF-32 code point of the first character of the specified string.
|
||||
|
||||
Format:
|
||||
UNICODE_VAL( <string> )
|
||||
|
||||
Notes:
|
||||
Returns 0 if the string is empty.
|
||||
|
||||
Example:
|
||||
select unicode_val(x) from y;
|
||||
|
||||
|
||||
------------
|
||||
UUID_TO_CHAR
|
||||
------------
|
||||
|
@ -507,6 +507,8 @@ static const TOK tokens[] =
|
||||
{TOK_UNBOUNDED, "UNBOUNDED", false},
|
||||
{TOK_UNCOMMITTED, "UNCOMMITTED", true},
|
||||
{TOK_UNDO, "UNDO", true},
|
||||
{TOK_UNICODE_CHAR, "UNICODE_CHAR", true},
|
||||
{TOK_UNICODE_VAL, "UNICODE_VAL", true},
|
||||
{TOK_UNION, "UNION", false},
|
||||
{TOK_UNIQUE, "UNIQUE", false},
|
||||
{TOK_UNKNOWN, "UNKNOWN", false},
|
||||
|
@ -677,6 +677,11 @@ using namespace Firebird;
|
||||
%token <metaNamePtr> CLEAR
|
||||
%token <metaNamePtr> OLDEST
|
||||
|
||||
// tokens added for Firebird 5.0
|
||||
|
||||
%token <metaNamePtr> UNICODE_CHAR
|
||||
%token <metaNamePtr> UNICODE_VAL
|
||||
|
||||
// precedence declarations for expression evaluation
|
||||
|
||||
%left OR
|
||||
@ -8134,6 +8139,8 @@ system_function_std_syntax
|
||||
| TAN
|
||||
| TANH
|
||||
| TRUNC
|
||||
| UNICODE_CHAR
|
||||
| UNICODE_VAL
|
||||
| UUID_TO_CHAR
|
||||
| QUANTIZE
|
||||
| TOTALORDER
|
||||
@ -9040,6 +9047,8 @@ non_reserved_word
|
||||
| TOTALORDER
|
||||
| TRAPS
|
||||
| ZONE
|
||||
| UNICODE_CHAR // added in FB 5.0
|
||||
| UNICODE_VAL
|
||||
;
|
||||
|
||||
%%
|
||||
|
@ -237,6 +237,7 @@ void setParamsRsaEncrypt(DataTypeUtilBase*, const SysFunction*, int argsCount, d
|
||||
void setParamsRsaPublic(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args);
|
||||
void setParamsRsaSign(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args);
|
||||
void setParamsRsaVerify(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args);
|
||||
void setParamsUnicodeVal(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
|
||||
void setParamsUuidToChar(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args);
|
||||
|
||||
// generic make functions
|
||||
@ -280,6 +281,7 @@ void makeRsaPrivate(DataTypeUtilBase* dataTypeUtil, const SysFunction* function,
|
||||
void makeRsaPublic(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
void makeRsaSign(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
void makeTrunc(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
void makeUnicodeChar(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
void makeUuid(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
void makeUuidToChar(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
|
||||
|
||||
@ -338,6 +340,8 @@ dsc* evlSign(thread_db* tdbb, const SysFunction* function, const NestValueArray&
|
||||
dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
dsc* evlUnicodeChar(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure);
|
||||
|
||||
|
||||
@ -645,6 +649,13 @@ void setParamsDateDiff(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc
|
||||
}
|
||||
|
||||
|
||||
void setParamsUnicodeVal(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args)
|
||||
{
|
||||
if (argsCount >= 1 && args[0]->isUnknown())
|
||||
args[0]->makeText(4, CS_UTF8);
|
||||
}
|
||||
|
||||
|
||||
void setParamVarying(dsc* param, USHORT textType, bool condition)
|
||||
{
|
||||
if (!param)
|
||||
@ -1734,6 +1745,24 @@ void makeTrunc(DataTypeUtilBase*, const SysFunction* function, dsc* result,
|
||||
}
|
||||
|
||||
|
||||
void makeUnicodeChar(DataTypeUtilBase*, const SysFunction* function, dsc* result,
|
||||
int argsCount, const dsc** args)
|
||||
{
|
||||
fb_assert(argsCount == function->minArgCount);
|
||||
|
||||
const dsc* value = args[0];
|
||||
|
||||
if (value->isNull())
|
||||
{
|
||||
result->makeNullString();
|
||||
return;
|
||||
}
|
||||
|
||||
result->makeText(4, ttype_utf8);
|
||||
result->setNullable(value->isNullable());
|
||||
}
|
||||
|
||||
|
||||
void makeUuid(DataTypeUtilBase*, const SysFunction* function, dsc* result,
|
||||
int argsCount, const dsc** args)
|
||||
{
|
||||
@ -6331,6 +6360,75 @@ dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction*, const NestValueArra
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
dsc* evlUnicodeChar(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
|
||||
impure_value* impure)
|
||||
{
|
||||
fb_assert(args.getCount() == 1);
|
||||
|
||||
jrd_req* request = tdbb->getRequest();
|
||||
|
||||
const dsc* value = EVL_expr(tdbb, request, args[0]);
|
||||
if (request->req_flags & req_null) // return NULL if value is NULL
|
||||
return NULL;
|
||||
|
||||
const UChar32 code = MOV_get_long(tdbb, value, 0);
|
||||
|
||||
if (U8_LENGTH(code) == 0)
|
||||
status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_malformed_string));
|
||||
|
||||
UCHAR buffer[4];
|
||||
int len = 0;
|
||||
U8_APPEND_UNSAFE(buffer, len, code);
|
||||
|
||||
dsc result;
|
||||
result.makeText(len, ttype_utf8, buffer);
|
||||
EVL_make_value(tdbb, &result, impure);
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
|
||||
dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
|
||||
impure_value* impure)
|
||||
{
|
||||
fb_assert(args.getCount() == 1);
|
||||
|
||||
jrd_req* request = tdbb->getRequest();
|
||||
|
||||
const dsc* value = EVL_expr(tdbb, request, args[0]);
|
||||
if (request->req_flags & req_null) // return NULL if value is NULL
|
||||
return NULL;
|
||||
|
||||
MoveBuffer buffer;
|
||||
UCHAR* str;
|
||||
int len = MOV_make_string2(tdbb, value, CS_UTF8, &str, buffer);
|
||||
|
||||
USHORT dst[2];
|
||||
USHORT errCode = 0;
|
||||
ULONG errPosition;
|
||||
ULONG dstLen = UnicodeUtil::utf8ToUtf16(len, str, sizeof(dst), dst, &errCode, &errPosition);
|
||||
|
||||
if (errCode != 0 && errCode != CS_TRUNCATION_ERROR)
|
||||
status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_transliteration_failed));
|
||||
|
||||
if (dstLen == 0)
|
||||
impure->vlu_misc.vlu_long = 0;
|
||||
else if (dstLen == 2 || !U_IS_SURROGATE(dst[0]))
|
||||
impure->vlu_misc.vlu_long = dst[0];
|
||||
else if (dstLen == 4 && U16_IS_LEAD(dst[0]) && U16_IS_TRAIL(dst[1]))
|
||||
impure->vlu_misc.vlu_long = U16_GET_SUPPLEMENTARY(dst[0], dst[1]);
|
||||
else
|
||||
{
|
||||
fb_assert(false);
|
||||
impure->vlu_misc.vlu_long = 0;
|
||||
}
|
||||
|
||||
impure->vlu_desc.makeLong(0, &impure->vlu_misc.vlu_long);
|
||||
|
||||
return &impure->vlu_desc;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
@ -6417,6 +6515,8 @@ const SysFunction SysFunction::functions[] =
|
||||
{"TANH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
|
||||
{"TOTALORDER", 2, 2, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd},
|
||||
{"TRUNC", 1, 2, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
|
||||
{"UNICODE_CHAR", 1, 1, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL},
|
||||
{"UNICODE_VAL", 1, 1, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL},
|
||||
{"UUID_TO_CHAR", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
|
||||
{"", 0, 0, NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user