oracle algorithm hash plsql database-partitioning

oracle - ¿Cuál es el algoritmo utilizado por la función ORA_HASH?



algorithm plsql (2)

He encontrado un código en la aplicación en la que estoy trabajando que hace una llamada a la base de datos simplemente para llamar a la función ORA_HASH ( ORA_HASH ) en una cadena UUID. La razón por la que está haciendo esto es que necesita el valor para realizar una llamada de servicio a otro sistema que parece usar ORA_HASH para la partición.

Me gustaría saber qué algoritmo usa ORA_HASH para poder volver a implementarlo y hacer una llamada de servicio similar para una aplicación que no tendrá acceso a una base de datos real, y mucho menos a Oracle. Solo he podido encontrar lo que equivale a la documentación de la API de Oracle hasta el momento.

Solo para ser súper claro: necesito clonar ORA_HASH porque eso es lo que otro sistema que está fuera de mi control usa, y necesito integrarme con ese sistema. Sí, sería bueno si pudiera usar un algoritmo realmente estándar, como MD5, pero no puedo, a menos que eso sea lo que ORA_HASH está debajo de las cubiertas.

Las respuestas o comentarios que proponen el uso de un algoritmo hash además de ORA_HASH no son útiles. Esta pregunta es específicamente acerca de ORA_HASH , no hash o partición en general.


Otro sistema que parece utilizar ORA_HASH.

Bueno, si "parece que se usa", entonces tiene sentido hacer un poco de ingeniería inversa y verificar qué se llama exactamente y desensamblar el código de la función.

Si, sin embargo, desea sumergirse en los aspectos internos de Oracle, seguirlo puede ayudar.

En primer lugar, debes averiguar cómo se llama la función C interna. Para hacerlo, puede ejecutar un código de ejecución larga en una sesión. Yo corri esto

select avg(ora_hash(rownum)) id from (select rownum from dual connect by rownum <= 1e4), (select rownum from dual connect by rownum <= 1e4);

También puede ser código PL / SQL, solo debes asegurarte de llamar constantemente a ora_hash.

Mientras se esta ejecutando

Probé en Windows y parece que ora_hash es ...-> evaopn2 () -> evahash () -> ...

Ahora vamos a google para evahash. Tuvimos mucha suerte porque hay un archivo de encabezado en el sitio oficial https://oss.oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash.h con enlace a evahash.

Y, finalmente, hay una página con el código C real http://burtleburtle.net/bob/hash/evahash.html

Hasta ahora, bien, recordamos que podemos usar la función C externa en Oracle si la construimos en la biblioteca (DLL en Windows).

Por ejemplo, en mi Win x64 si cambio la firma de función a

extern "C" ub4 hash( ub1 *k, ub4 length, ub4 initval)

Se puede ejecutar con éxito desde Oracle. Pero, como puede ver, la firma difiere un poco de ora_hash en Oracle. Esta función acepta el valor, su longitud e initval (puede ser semilla), mientras que la firma en Oracle es ora_hash (expr, max_bucket, seed_value).

Tratemos de probar Oracle

SQL> select ora_hash(utl_raw.cast_to_raw(''0''), power(2, 32) - 1, 0) oh1, 2 ora_hash(''0'', power(2, 32) - 1, 0) oh2, 3 ora_hash(0, power(2, 32) - 1, 0) oh3, 4 ora_hash(chr(0), power(2, 32) - 1, 0) oh4 5 from dual; OH1 OH2 OH3 OH4 ---------- ---------- ---------- ---------- 3517341953 3517341953 1475158189 4056412421

do

int main() { ub1 ta[] = {0}; ub1* t = ta; cout << hash(t, 1, 0) << endl; ub1 ta0[] = {''0''}; ub1* t0 = ta0; cout << hash(t0, 1, 0) << endl; return 0; } 1843378377 4052366646

Ninguno de los números coincide. Entonces, ¿cuál es el problema? ora_hash acepta parámetros de casi cualquier tipo (por ejemplo, select ora_hash(sys.odcinumberlist(1,2,3)) from dual ) mientras que la función C acepta el valor como una matriz de bytes. Esto significa que alguna conversión ocurre antes de la llamada a la función. Por lo tanto, antes de usar la función hash C mencionada, debes averiguar cómo se transforma el valor real antes de pasarlo.

Puede proceder con la ingeniería inversa de los binarios de Oracle utilizando rayos hexadecimales IDA PRO +, pero esto puede llevar días. Sin mencionar los detalles específicos de la plataforma.

Entonces, si desea imitar ora_hash, la opción más sencilla sería instalar Oracle Express Edition y usarlo para llamar a ora_hash.

Espero que haya sido interesante. Buena suerte.

Actualizar

ora_hash y dbms_utility.get_hash_value se pueden asignar entre sí (consulte https://jonathanlewis.wordpress.com/2009/11/21/ora_hash-function/ )

SQL> select dbms_utility.get_hash_value(''0'', 0 + 1, 1e6 + 1) ha1, 2 ora_hash(''0'', 1e6, 0) + 1 ha2 3 from dual; HA1 HA2 ---------- ---------- 338437 338437

Si desenvolvemos el cuerpo del paquete dbms_utility veremos la siguiente declaración

function get_hash_value(name varchar2, base number, hash_size number) return number is begin return(icd_hash(name, base, hash_size)); end;

y

function icd_hash(name varchar2, base binary_integer, hash_size binary_integer) return binary_integer; pragma interface(c, icd_hash);

Busquemos en Google para icd_hash y podemos encontrar que está asignado a _psdhsh ( https://yurichev.com/blog/50/ ). Ahora es el momento de desmontar oracle.exe y extraer el código para _psdhsh de él. Tal vez voy a pasar algo de tiempo en este próximo año.


Esto no responde a la pregunta OP del algo real detrás de ora_hash. Este es solo un ejemplo del uso de ora_hash en pl / sql (respondiendo al comentario de @JonHeller):

La función:

SQL> create or replace function get_ora_hash(i_str in varchar2, i_max_bucket in number default 4294967295, i_seed number default 0) return number deterministic parallel_enable as rv number:= 0; begin select ORA_HASH(i_str, i_max_bucket, i_seed) into rv from dual; return rv; end; Function created.

Y usándolo:

SQL> declare l_val number; begin l_val := get_ora_hash(''test''); dbms_output.put_line(l_val); end; PL/SQL procedure successfully completed.

Dbms de salida:

2662839991

También puedes jugar con RESULT_CACHE u otras técnicas para intentar acelerar aún más las cosas.

Es muy rápido ya. Por ejemplo, llamando a la función 1 millón de veces en una tabla grande:

SQL> set serveroutput on SQL> declare l_val number; l_start_dte timestamp; l_end_dte timestamp; l_interval INTERVAL DAY(9) TO SECOND(9); l_cnt number := 0; begin l_start_dte:= systimestamp; --for rec in (select object_name from dba_objects) for rec in (select name from my_big_table where rownum <= 1000000) loop l_cnt := l_cnt + 1; l_val := get_ora_hash(rec.name); end loop; l_end_dte:= systimestamp; l_interval := l_end_dte - l_start_dte; dbms_output.put_line(''Rows processed: '' || l_cnt || '', Start: '' || l_start_dte || '', End: '' || l_end_dte || '', Interval: '' || l_interval); end; Rows processed: 1000000, Start: 14-DEC-17 02.48.31.138212 PM, End: 14-DEC-17 02.48.41.148884 PM, Interval: +000000000 00:00:10.010672000 PL/SQL procedure successfully completed.

Básicamente, 100k filas por segundo, eso incluye cualquier cambio de contexto que le pueda preocupar.

Si necesita reproducir ORA_HASH debido al rendimiento, sugeriría que su cuello de botella de rendimiento puede estar en otra parte.