Quel est l'algorithme utilisé par la fonction de hachage ORA?

j'ai trouvé un code dans l'application sur laquelle je travaille qui fait un appel de base de données simplement pour appeler le ORA_HASH fonction ( documentation) sur une chaîne UUID. La raison pour laquelle il fait cela 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.

je voudrais savoir l'algorithme ORA_HASH utilise pour que je puisse le ré-implémenter pour faire un appel de service similaire pour une application qui il n'aura pas accès à une vraie base de données, encore moins à Oracle. J'ai seulement pu trouver ce qui correspond à la documentation de L'API Oracle jusqu'à présent.

Juste pour être super clair: j'ai besoin de cloner ORA_HASH parce que c'est ce qu'un autre système qui est hors de mon contrôle utilise, et j'ai besoin de l'intégrer à ce système. Oui, il serait agréable si pourrait utiliser un vraiment algorithme standard, comme MD5, mais je ne peux pas, à moins que ce soit ce que ORA_HASH est sous les couvertures.

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

14
demandé sur Kaypro II 2017-08-30 00:01:48

2 réponses

un autre système qui semble utiliser ORA_HASH

Eh bien, si elle "semble utiliser" alors il est logique de faire un peu de rétroingénierie et de vérifier ce qui s'appelle exactement et de démonter le code de la fonction.

si vous, cependant, voulez plonger dans les internes D'Oracle alors la suite peut aider.

tout d'abord, vous devez comprendre quel est le nom de la fonction c interne. Pour cela, vous pouvez exécuter certaines exécution de code dans un session. Je n'ai couru ce

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

C'est peut-être le code PL/SQL ainsi, vous avez juste besoin de s'assurer que vous êtes constamment appel ora_hash.

en cours d'exécution

j'ai testé sur Windows et on dirait que c'est ora_hash ...- >evaopn2 () ->evahash () ->...

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

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

jusqu'à présent si bon, nous nous rappelons que nous pouvons utiliser la fonction C externe dans Oracle si nous la construisons dans une bibliothèque (DLL sur Windows).

Par exemple sur mon Win x64 si je change de 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 nombres ne correspond. Quel est donc le problème? ora_hash accepte les paramètres de presque tous les types (par exemple select ora_hash(sys.odcinumberlist(1,2,3)) from dual) alors que la fonction C accepte la valeur comme tableau d'octets. Cela signifie qu'une certaine conversion se produit avant l'appel de fonction. Ainsi, avant d'utiliser la fonction de hachage mentionnée, vous devez comprendre comment la valeur réelle est transformée avant de passer à cette fonction.

vous pouvez procéder à l'ingénierie inverse des binaires Oracle en utilisant les rayons IDA PRO + hex, mais cela peut prendre des jours. Sans parler des détails spécifiques à la plateforme.

Donc, si vous voulez imiter ora_hash, l'option la plus simple serait d'installer Oracle express edition et 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 peut être mappé l'un à l'Autre (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 déballons le paquet de dbms_utility, nous verrons ce qui suit: déclaration

  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);

google pouricd_hash et on peut trouver qu'il est mappé à _psdhsh (https://yurichev.com/blog/50/). Maintenant, il est temps de démonter oracle.exe et l'extrait de code pour _psdhsh. Peut-être que je passerai un peu de temps sur ça l'année prochaine.

15
répondu Dr Y Wit 2017-12-26 19:15:41

cela ne répond pas à la question OP Du Vrai algo derrière ora_hash. Ce n'est qu'un exemple d'utilisation de ora_hash dans pl/sql (répondant au commentaire de @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 en l'utilisant:

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

Sortie Dbms:

2662839991

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

C'est déjà très rapide. 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 essentiellement des lignes de 100k par seconde, Cela inclut tous les commutateurs de contexte qui peuvent vous inquiéter.

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

1
répondu tbone 2017-12-14 19:52:40