8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 16:03:03 +01:00

Support for different hashes algorithms in HASH system function.

This commit is contained in:
Adriano dos Santos Fernandes 2017-07-21 15:24:01 +00:00
parent 987e85f676
commit 3a1a9b9d48
15 changed files with 131 additions and 18 deletions

View File

@ -68,6 +68,10 @@
* [CORE-5064](http://tracker.firebirdsql.org/browse/CORE-5064): Add datatypes (VAR)BINARY(n) and BINARY VARYING(n) as aliases for (VAR)CHAR(n) CHARACTER SET OCTETS
Contributor(s): Dimitry Sibiryakov
* [CORE-4436](http://tracker.firebirdsql.org/browse/CORE-4436): Support for different hash algorithms in HASH system function
Reference(s): [doc/sql.extensions/README.builtin_functions.txt](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.builtin_functions.txt)
Contributor(s): Adriano dos Santos Fernandes
* [CORE-2557](http://tracker.firebirdsql.org/browse/CORE-2557): Grants on MON$ tables
Contributor(s): Alex Peshkoff

View File

@ -473,13 +473,21 @@ HASH
----
Function:
Returns a HASH of a string.
Returns a HASH of a string using a specified algorithm.
Format:
HASH( <string> )
HASH( <string> [ USING <algorithm> ] )
algorithm ::= { MD5 | SHA1 | SHA256 | SHA512 }
Important:
- The syntax without USING is very discouraged and maintained for backward compatibility.
It returns a 64 bit integer and produces very bad hashes that easily result in collisions.
- The syntax with USING is introduced in FB 4.0 and returns VARCHAR strings with OCTETS charset.
Example:
select hash(x) from y;
select hash(x using sha256) from y;
----

View File

@ -1714,6 +1714,8 @@ C --
PARAMETER (GDS__subfunc_not_impl = 335545150)
INTEGER*4 GDS__subproc_not_impl
PARAMETER (GDS__subproc_not_impl = 335545151)
INTEGER*4 GDS__sysf_invalid_hash_algorithm
PARAMETER (GDS__sysf_invalid_hash_algorithm = 335545152)
INTEGER*4 GDS__gfix_db_name
PARAMETER (GDS__gfix_db_name = 335740929)
INTEGER*4 GDS__gfix_invalid_sw

View File

@ -1709,6 +1709,8 @@ const
gds_subfunc_not_impl = 335545150;
isc_subproc_not_impl = 335545151;
gds_subproc_not_impl = 335545151;
isc_sysf_invalid_hash_algorithm = 335545152;
gds_sysf_invalid_hash_algorithm = 335545152;
isc_gfix_db_name = 335740929;
gds_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;

View File

@ -7709,7 +7709,6 @@ system_function_std_syntax
| EXP
| FLOOR
| GEN_UUID
| HASH
| LEFT
| LN
| LOG
@ -7769,6 +7768,14 @@ system_function_special_syntax
newNode<ValueListNode>(MAKE_const_slong($3))->add($5)->add($7));
$$->dsqlSpecialSyntax = true;
}
| HASH '(' value ')'
{ $$ = newNode<SysFuncCallNode>(*$1, newNode<ValueListNode>($3)); }
| HASH '(' value USING valid_symbol_name ')'
{
$$ = newNode<SysFuncCallNode>(*$1,
newNode<ValueListNode>($3)->add(MAKE_str_constant(newIntlString($5->c_str()), CS_ASCII)));
$$->dsqlSpecialSyntax = true;
}
| OVERLAY '(' value PLACING value FROM value FOR value ')'
{
$$ = newNode<SysFuncCallNode>(*$1,

View File

@ -853,6 +853,7 @@ static const struct {
{"subproc_defvaldecl", 335545149},
{"subfunc_not_impl", 335545150},
{"subproc_not_impl", 335545151},
{"sysf_invalid_hash_algorithm", 335545152},
{"gfix_db_name", 335740929},
{"gfix_invalid_sw", 335740930},
{"gfix_incmp_sw", 335740932},

View File

@ -887,6 +887,7 @@ const ISC_STATUS isc_subfunc_defvaldecl = 335545148L;
const ISC_STATUS isc_subproc_defvaldecl = 335545149L;
const ISC_STATUS isc_subfunc_not_impl = 335545150L;
const ISC_STATUS isc_subproc_not_impl = 335545151L;
const ISC_STATUS isc_sysf_invalid_hash_algorithm = 335545152L;
const ISC_STATUS isc_gfix_db_name = 335740929L;
const ISC_STATUS isc_gfix_invalid_sw = 335740930L;
const ISC_STATUS isc_gfix_incmp_sw = 335740932L;
@ -1361,7 +1362,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L;
const ISC_STATUS isc_trace_switch_param_miss = 337182758L;
const ISC_STATUS isc_trace_param_act_notcompat = 337182759L;
const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L;
const ISC_STATUS isc_err_max = 1305;
const ISC_STATUS isc_err_max = 1306;
#else /* c definitions */
@ -2218,6 +2219,7 @@ const ISC_STATUS isc_err_max = 1305;
#define isc_subproc_defvaldecl 335545149L
#define isc_subfunc_not_impl 335545150L
#define isc_subproc_not_impl 335545151L
#define isc_sysf_invalid_hash_algorithm 335545152L
#define isc_gfix_db_name 335740929L
#define isc_gfix_invalid_sw 335740930L
#define isc_gfix_incmp_sw 335740932L
@ -2692,7 +2694,7 @@ const ISC_STATUS isc_err_max = 1305;
#define isc_trace_switch_param_miss 337182758L
#define isc_trace_param_act_notcompat 337182759L
#define isc_trace_mandatory_switch_miss 337182760L
#define isc_err_max 1305
#define isc_err_max 1306
#endif

View File

@ -856,6 +856,7 @@ Data source : @4"}, /* eds_statement */
{335545149, "Default values for parameters are not allowed in definition of the previously declared sub-procedure @1"}, /* subproc_defvaldecl */
{335545150, "Sub-function @1 was declared but not implemented"}, /* subfunc_not_impl */
{335545151, "Sub-procedure @1 was declared but not implemented"}, /* subproc_not_impl */
{335545152, "Invalid HASH algorithm @1"}, /* sysf_invalid_hash_algorithm */
{335740929, "data base file name (@1) already given"}, /* gfix_db_name */
{335740930, "invalid switch @1"}, /* gfix_invalid_sw */
{335740932, "incompatible switch combination"}, /* gfix_incmp_sw */

View File

@ -852,6 +852,7 @@ static const struct {
{335545149, -901}, /* 829 subproc_defvaldecl */
{335545150, -901}, /* 830 subfunc_not_impl */
{335545151, -901}, /* 831 subproc_not_impl */
{335545152, -901}, /* 832 sysf_invalid_hash_algorithm */
{335740929, -901}, /* 1 gfix_db_name */
{335740930, -901}, /* 2 gfix_invalid_sw */
{335740932, -901}, /* 4 gfix_incmp_sw */

View File

@ -852,6 +852,7 @@ static const struct {
{335545149, "42000"}, // 829 subproc_defvaldecl
{335545150, "42000"}, // 830 subfunc_not_impl
{335545151, "42000"}, // 831 subproc_not_impl
{335545152, "42000"}, // 832 sysf_invalid_hash_algorithm
{335740929, "00000"}, // 1 gfix_db_name
{335740930, "00000"}, // 2 gfix_invalid_sw
{335740932, "00000"}, // 4 gfix_incmp_sw

View File

@ -53,6 +53,7 @@
#include "../jrd/trace/TraceObjects.h"
#include "../jrd/Collation.h"
#include "../common/classes/FpeControl.h"
#include <functional>
#include <math.h>
using namespace Firebird;
@ -101,6 +102,37 @@ enum TrigonFunction
};
struct HashAlgorithmDescriptor
{
const char* name;
USHORT length;
std::function<HashContext* (MemoryPool&)> create;
static const HashAlgorithmDescriptor* find(const char* name);
};
static const HashAlgorithmDescriptor hashAlgorithmDescriptors[] = {
{"MD5", 16, [](MemoryPool& pool) { return FB_NEW_POOL(pool) Md5HashContext(pool); }},
{"SHA1", 20, [](MemoryPool& pool) { return FB_NEW_POOL(pool) Sha1HashContext(pool); }},
{"SHA256", 32, [](MemoryPool& pool) { return FB_NEW_POOL(pool) Sha256HashContext(pool); }},
{"SHA512", 64, [](MemoryPool& pool) { return FB_NEW_POOL(pool) Sha512HashContext(pool); }}
};
const HashAlgorithmDescriptor* HashAlgorithmDescriptor::find(const char* name)
{
unsigned count = FB_NELEM(hashAlgorithmDescriptors);
for (unsigned i = 0; i < count; ++i)
{
if (strcmp(name, hashAlgorithmDescriptors[i].name) == 0)
return &hashAlgorithmDescriptors[i];
}
status_exception::raise(Arg::Gds(isc_sysf_invalid_hash_algorithm) << name);
return nullptr;
}
// constants
const int oneDay = 86400;
@ -145,6 +177,7 @@ void makeBinShift(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, d
void makeCeilFloor(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeHash(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeMod(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
void makeOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args);
@ -901,6 +934,27 @@ void makeGetSetContext(DataTypeUtilBase* /*dataTypeUtil*/, const SysFunction* fu
}
void makeHash(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result,
int argsCount, const dsc** args)
{
fb_assert(argsCount >= function->minArgCount);
if (argsCount == 1)
makeInt64Result(dataTypeUtil, function, result, argsCount, args);
else if (argsCount >= 2)
{
if (!args[1]->dsc_address || !args[1]->isText()) // not a constant
status_exception::raise(Arg::Gds(isc_sysf_invalid_hash_algorithm) << "<not a string constant>");
MetaName algorithmName;
MOV_get_metaname(JRD_get_thread_data(), args[1], algorithmName);
result->makeVarying(HashAlgorithmDescriptor::find(algorithmName.c_str())->length, ttype_binary);
result->setNullable(args[0]->isNullable());
}
}
void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result,
int argsCount, const dsc** args)
{
@ -2640,7 +2694,7 @@ dsc* evlSetContext(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
impure_value* impure)
{
fb_assert(args.getCount() == 1);
fb_assert(args.getCount() >= 1);
jrd_req* request = tdbb->getRequest();
@ -2648,8 +2702,27 @@ dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
if (request->req_flags & req_null) // return NULL if value is NULL
return NULL;
WeakHashContext hashContext;
impure->vlu_misc.vlu_int64 = 0;
AutoPtr<HashContext> hashContext;
MemoryPool& pool = *request->req_pool;
if (args.getCount() >= 2)
{
const dsc* algorithmDesc = EVL_expr(tdbb, request, args[1]);
if (request->req_flags & req_null) // return NULL if algorithm is NULL
return NULL;
if (!algorithmDesc->isText())
status_exception::raise(Arg::Gds(isc_sysf_invalid_hash_algorithm) << "<not a string constant>");
MetaName algorithmName;
MOV_get_metaname(tdbb, algorithmDesc, algorithmName);
hashContext.reset(HashAlgorithmDescriptor::find(algorithmName.c_str())->create(pool));
}
else
{
hashContext.reset(FB_NEW_POOL(pool) WeakHashContext());
impure->vlu_misc.vlu_int64 = 0;
}
if (value->isBlob())
{
@ -2660,7 +2733,7 @@ dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
while (!(blob->blb_flags & BLB_eof))
{
const ULONG length = blob->BLB_get_data(tdbb, buffer, sizeof(buffer), false);
hashContext.update(buffer, length);
hashContext->update(buffer, length);
}
blob->BLB_close(tdbb);
@ -2670,17 +2743,26 @@ dsc* evlHash(thread_db* tdbb, const SysFunction*, const NestValueArray& args,
UCHAR* address;
MoveBuffer buffer;
const ULONG length = MOV_make_string2(tdbb, value, value->getTextType(), &address, buffer, false);
hashContext.update(address, length);
hashContext->update(address, length);
}
HashContext::Buffer resultBuffer;
hashContext.finish(resultBuffer);
hashContext->finish(resultBuffer);
fb_assert(resultBuffer.getCount() == sizeof(SINT64));
memcpy(&impure->vlu_misc.vlu_int64, resultBuffer.begin(), sizeof(SINT64));
if (args.getCount() >= 2)
{
dsc result;
result.makeText(resultBuffer.getCount(), ttype_binary, resultBuffer.begin());
EVL_make_value(tdbb, &result, impure);
}
else
{
fb_assert(resultBuffer.getCount() == sizeof(SINT64));
memcpy(&impure->vlu_misc.vlu_int64, resultBuffer.begin(), sizeof(SINT64));
// make descriptor for return value
impure->vlu_desc.makeInt64(0, &impure->vlu_misc.vlu_int64);
// make descriptor for return value
impure->vlu_desc.makeInt64(0, &impure->vlu_misc.vlu_int64);
}
return &impure->vlu_desc;
}
@ -4262,7 +4344,7 @@ const SysFunction SysFunction::functions[] =
{"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL},
{"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
{"GEN_UUID", 0, 0, NULL, makeUuid, evlGenUuid, NULL},
{"HASH", 1, 1, NULL, makeInt64Result, evlHash, NULL},
{"HASH", 1, 2, NULL, makeHash, evlHash, NULL},
{"LEFT", 2, 2, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
{"LN", 1, 1, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
{"LOG", 2, 2, setParamsDblDec, makeDblDecResult, evlLog, NULL},

View File

@ -176,7 +176,7 @@ void MOV_get_metaname(Jrd::thread_db* tdbb, const dsc* desc, MetaName& name)
fb_assert(length && ptr);
fb_assert(length <= MAX_SQL_IDENTIFIER_LEN);
fb_assert(ttype == ttype_metadata);
fb_assert(ttype == ttype_ascii || ttype == ttype_metadata);
name.assign(reinterpret_cast<char*>(ptr), length);
}

View File

@ -1,7 +1,7 @@
/* MAX_NUMBER is the next number to be used, always one more than the highest message number. */
set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?);
--
('2017-07-10 12:27:00', 'JRD', 0, 832)
('2017-07-24 12:10:00', 'JRD', 0, 833)
('2015-03-17 18:33:00', 'QLI', 1, 533)
('2015-01-07 18:01:51', 'GFIX', 3, 134)
('1996-11-07 13:39:40', 'GPRE', 4, 1)

View File

@ -939,6 +939,7 @@ Data source : @4', NULL, NULL)
('subproc_defvaldecl', NULL, 'StmtNodes.cpp', NULL, 0, 829, NULL, 'Default values for parameters are not allowed in definition of the previously declared sub-procedure @1', NULL, NULL);
('subfunc_not_impl', NULL, 'StmtNodes.cpp', NULL, 0, 830, NULL, 'Sub-function @1 was declared but not implemented', NULL, NULL);
('subproc_not_impl', NULL, 'StmtNodes.cpp', NULL, 0, 831, NULL, 'Sub-procedure @1 was declared but not implemented', NULL, NULL);
('sysf_invalid_hash_algorithm', NULL, 'SysFunction.cpp', NULL, 0, 832, NULL, 'Invalid HASH algorithm @1', NULL, NULL);
-- QLI
(NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL);
(NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL);

View File

@ -838,6 +838,7 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA
(-901, '42', '000', 0, 829, 'subproc_defvaldecl', NULL, NULL)
(-901, '42', '000', 0, 830, 'subfunc_not_impl', NULL, NULL)
(-901, '42', '000', 0, 831, 'subproc_not_impl', NULL, NULL)
(-901, '42', '000', 0, 832, 'sysf_invalid_hash_algorithm', NULL, NULL)
-- GFIX
(-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL)
(-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL)