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
Si está en Windows, puede usar ostackprof de TANEL PODER ( https://blog.tanelpoder.com/2008/10/31/advanced-oracle-troubleshooting-guide-part-9-process-stack-profiling-from-sqlplus-using-ostackprof/ )
Si está en * nix, puede utilizar dtrace ( http://www.oracle.com/technetwork/articles/servers-storage-dev/dtrace-on-linux-1956556.html ), Flame Graph (escenario de uso https://blog.dbi-services.com/oracle-database-multilingual-engine-mle/ )
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.