Oracle представляет числа в своем собственном внутреннем формате, который можно увидеть с помощью функции dump()
в Oracle.Например,
SELECT dump(123) FROM dual;
Typ=2 Len=3: 194,2,24
Итак, чтобы хэшировать число в Python и получить тот же результат, что и в Oracle, вам нужно преобразовать число Python в набор байтов:так же, как Oracle делает это во внутренних органах.
Хороший анализ внутренней логики, используемой Oracle, можно найти здесь .Это верно с одним незначительным упущением, связанным с завершающими отрицательными числами.Кроме того, он написан с точки зрения декодирования номера Oracle из его байтов.В нашем случае нам нужно кодировать номер Oracle в его внутренний байтовый формат.Тем не менее, я широко использовал его при формировании этого ответа.
В приведенном ниже коде показана функция Python, to_oracle_number()
, которая будет возвращать массив целых чисел, имеющий такое же байтовое представление числа, которое рассчитывает база данных Oracle.,Он должен обрабатывать любое число (положительное, отрицательное, дробное, ноль и т. Д.).
Код в самом низу также показывает, как вызывать эту функцию и хэшировать ее результаты, чтобы получить то же значение хеш-функции, которое было вычислено вбаза данных Oracle, которая, как мне кажется, является основой вашего вопроса.
ПРИМЕЧАНИЕ. Функция ожидает, что число, которое вы хотите преобразовать, будет передано в виде строки, чтобы избежать потери точности.
import math
import decimal
import hashlib
def to_oracle_number( nstr ):
# define number n that we want to convert
n = decimal.Decimal(nstr)
# compute exponent (base 100) and convert to Oracle byte along with sign
#print (abs(n))
l_exp = 0
l_len = 0
l_abs_n = abs(n)
if l_abs_n != 0:
l_exp = math.floor(math.log(l_abs_n,100))
# Oracle adds 1 to all bytes when encoding
l_exp = l_exp + 1
# Oracle adds 64 to exponent whe encoding
l_exp = l_exp + 64
if n < 0:
# take 1's complement of exponent so far (bitwise xor)
l_exp = (l_exp ^ 127)
if n >= 0:
# add sign bit. zero is considered positive.
l_exp = l_exp + 128
l_bytes = []
l_bytes.append(l_exp)
l_len = l_len + 1 # exponent and sign take 1 byte
l_whole_part = str(int(l_abs_n))
# make sure there is an even number of digits in the whole part
if len(l_whole_part) % 2 == 1:
l_whole_part = '0' + l_whole_part
# get the fractional digits, so if 0.01234, just 01234
l_frac_part = str(l_abs_n - int(l_abs_n))[2:]
# make sure there is an even number of digits in the fractional part
if len(l_frac_part) % 2 == 1:
l_frac_part = l_frac_part + '0'
l_mantissa = l_whole_part + l_frac_part
# chop off leading 00 pairs
while l_mantissa[0:2] == '00':
l_mantissa = l_mantissa[2:]
# chop off trailing 00 pairs
while l_mantissa[-2:] == '00':
l_mantissa = l_mantissa[:-2]
# compute number of 2-character chunks
l_chunk_count = int(len(l_mantissa) / 2)
l_chunks = '';
for i in range(0, l_chunk_count):
l_chunk = int(l_mantissa[i*2:i*2+2])
if n < 0:
# for negative numbers, we subtract from 100
l_chunk = 100-l_chunk
# Oracle adds 1 to all bytes
l_chunk = l_chunk + 1
# Add the chunk to our answer
l_chunks = l_chunks + ',' + str(l_chunk)
l_bytes.append(l_chunk)
l_len = l_len + 1 # we have computed one more byte
#print (str(i) + ':' + str(l_chunk))
if n < 0 and l_len < 21:
# terminating negative numbers always end in byte 102 (do not know why)
l_chunks = l_chunks + ',102'
l_bytes.append(102)
l_len = l_len + 1
l_computed_dump = 'Typ=2 Len=' + str(l_len) + ': ' + str(l_exp) + l_chunks
print (l_computed_dump)
print (l_bytes)
return l_bytes
# test it
m = hashlib.sha256()
b = bytes(to_oracle_number('123')) # pass a string so no precision errors
m.update(b)
print(m.hexdigest().upper())
ВЫХОД
Typ=2 Len=3: 194,2,24
[194, 2, 24]
A0740C0829EC3314E5318E1F060266479AA31F8BBBC1868DA42B9E608F52A09F