2017-08-29 6 views
13

J'ai rencontré du code dans l'application sur laquelle je travaille qui fait un appel de base de données simplement pour appeler la fonction ORA_HASH (documentation) sur une chaîne UUID. La raison pour laquelle il fait ceci est qu'il a besoin de la valeur pour faire un appel de service à un autre système qui semble utiliser ORA_HASH pour le partitionnement.Quel est l'algorithme utilisé par la fonction ORA_HASH?

Je voudrais connaître l'algorithme ORA_HASH utilise pour que je puisse le ré-implémenter pour faire un appel de service similaire pour une application qui n'aura pas accès à une vraie base de données, sans parler d'Oracle. J'ai seulement pu trouver ce qui s'apparente à la documentation de l'API Oracle jusqu'à présent.

Juste pour être super clair: je besoin de cloner ORA_HASH parce que ce autre système qui est en dehors de mes utilisations de contrôle, et je dois intégrer à ce système. Oui, ce serait bien si vous pouviez utiliser un algorithme standard vraiment, comme MD5, mais je ne peux pas, sauf si c'est ce que ORA_HASH est sous les couvertures.

Les réponses ou les commentaires qui proposent l'utilisation d'un algorithme de hachage en plus de ORA_HASH ne sont pas utiles. Cette question est spécifiquement sur ORA_HASH, pas de hachage ou de partitionnement en général.

+0

Si vous souhaitez ré-implémenter un appel pour une application qui n'a pas accès à une base de données réelle, pourquoi avez-vous besoin de ré-implémenter 'ORA_HASH'? Quelle est la magie à ce sujet? Écrivez votre propre fonction de hachage simple sans référence à 'ORA_HASH'. – mathguy

+0

Les mots clés ici sont "faire un appel de service à un autre système." Si j'utilise un algorithme différent, j'obtiendrai une valeur de hachage différente et l'appel ne fonctionnera pas. –

+0

Oh ... je vois; le "autre système" qui "semble utiliser' ORA_HASH' "n'est pas sous votre contrôle. Mon point était, utilisez la même fonction que vous créez dans les deux; mais si vous ne contrôlez qu'un côté, je vois pourquoi vous en avez besoin. – mathguy

Répondre

1

Ceci ne répond pas à la question OP de l'algo réel derrière ora_hash. Ceci est juste un exemple d'utilisation ora_hash en pl/sql (répondant commentaire @JonHeller):

La fonction:

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. 

Et l'utiliser:

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

DBM Sortie:

2662839991 

Vous pouvez également jouer avec RESULT_CACHE ou d'autres techniques pour essayer d'accélérer les choses encore plus.

C'est très rapide déjà. Par exemple, appeler la fonction 1 million de fois sur une grande table:

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. 

donc en gros 100k lignes par seconde, qui comprend tout contexte vous passe peut-être inquiet.

Si vous avez besoin de reproduire ORA_HASH en raison de la performance, je suggère que votre goulot d'étranglement de performance peut être ailleurs.

+0

Le produit de masquage que j'utilise déjà le fait. Le contexte bascule du "select ora_hash ... from dual" est horriblement lent. Ce serait beaucoup plus rapide si le code pouvait exécuter 'return ora_hash ...' mais cela n'est pas autorisé. C'est pourquoi connaître l'algorithme ORA_HASH pourrait être utile car nous pourrions potentiellement le recréer en PL/SQL pur. –

+0

@JonHeller J'ai mis à jour ma réponse. C'est très rapide en fait. – tbone

+0

Malheureusement, notre produit de masquage de données appelle ORA_HASH plusieurs fois pour chaque colonne de chaque ligne, et nous avons des milliards de lignes à traiter. Une implémentation alternative serait donc utile. –

12

un autre système qui semble utiliser ORA_HASH

Eh bien, si elle « semble utiliser » alors il est logique de faire un peu d'ingénierie inverse et vérifier ce qu'on appelle exactement et démonter code du fonction.

Toutefois, si vous souhaitez vous familiariser avec les composants internes d'Oracle, il peut être utile de suivre ce qui suit. Tout d'abord, vous devez déterminer quelle fonction C interne est appelée. Pour ce faire, vous pouvez exécuter du code long en une seule session. J'ai couru cette

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

Il peut être un code PL/SQL ainsi, il vous suffit de vous assurer que vous appelez constamment ora_hash.

Alors qu'il est en cours d'exécution

J'ai testé sur Windows et ressemble à ça ora_hash est ...-> evaopn2() ->evahash() -> ...

Maintenant, nous allons google pour evahash. Nous avons eu beaucoup de chance car il y a un fichier d'en-tête sur le site officiel https://oss.oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash.h avec un lien vers evahash.

Et enfin il y a la page avec le code réel C http://burtleburtle.net/bob/hash/evahash.html

Jusqu'à présent, si bien, nous nous souvenons que nous pouvons utiliser la fonction C externe dans Oracle si nous construisons dans la bibliothèque (DLL sous Windows).

Par exemple sur mon Win x64 si je change la signature de fonction

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

il peut être exécuté avec succès à partir d'Oracle. Mais, comme vous le voyez, la signature diffère un peu de ora_hash dans Oracle. Cette fonction accepte la valeur, sa longueur et initval (peut-être seed) alors que la signature dans Oracle est ora_hash (expr, max_bucket, seed_value).

Essayons de tester 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 

C

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 

Aucun des numéros matchs. Alors, quel est le problème? ora_hash accepte les paramètres de presque n'importe quel type (par exemple select ora_hash(sys.odcinumberlist(1,2,3)) from dual) alors que la fonction C accepte la valeur en tant que tableau d'octets. Cela signifie qu'une certaine conversion se produit avant l'appel de fonction. Ainsi, avant d'utiliser la fonction de hachage C mentionnée, vous devez déterminer comment la valeur réelle est transformée avant de passer à elle.

Vous pouvez procéder à la rétro-ingénierie des binaires Oracle à l'aide des rayons hexadécimaux IDA PRO +, mais cela peut prendre plusieurs jours. Sans oublier les détails spécifiques à la plate-forme. Donc, si vous voulez imiter ora_hash, l'option la plus simple serait d'installer Oracle Express Edition et de l'utiliser pour appeler ora_hash.

J'espère que c'était intéressant. Bonne chance.

Mise à jour

ora_hash et dbms_utility.get_hash_value peuvent être mis en correspondance les uns aux autres (voir 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 nous UnWrap corps de paquet de DBMS_UTILITY, nous verrons déclaration suivante

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

et

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

Nous allons google pour icd_hash et nous pouvons trouver qu'il est mappé à _psdhsh (https://yurichev.com/blog/50/). Maintenant, il est temps de désassembler oracle.exe et d'extraire le code pour _psdhsh. Je vais peut-être passer du temps sur l'année prochaine.

+0

Je crois que je pourrais utiliser cela pour construire une réponse un peu plus rapidement. Il doit y avoir un format que vous pouvez transmettre qui ne se transforme pas beaucoup. –

+0

@DylanB Allez-y. Si cela prend plus de temps que la période de la prime, ne vous inquiétez pas, je vais créer une autre prime de 500 points et de l'attribuer à vous ou à quelqu'un d'autre qui la construit en premier. (En supposant que le code soit en PL/SQL ou appelable à partir de PL/SQL et correspond à ORA_HASH.) –

+0

@Dylan B, je crois que ce serait suffisant si vous comprenez au moins comment cela fonctionne pour les paramètres varchar2. J'ai joué avec des valeurs d'un octet pour le rendre aussi simple que possible et pour éviter les problèmes avec big-endian/little-endian et d'autres choses. Oracle peut ajouter un octet "service" à la valeur ou inverser les bits pour tous les octets de n'importe quoi. Cela peut être quelque chose de plus complexe et il est extrêmement difficile de le comprendre sans l'ingénierie inverse des binaires Oracle. –