Oracle
 sql >> Base de données >  >> RDS >> Oracle

Quel est l'algorithme utilisé par la fonction ORA_HASH ?

Eh bien, s'il "semble utiliser", il est logique de faire un peu de rétro-ingénierie et de vérifier exactement ce qu'on appelle et de désassembler le code de la fonction.

Toutefois, si vous souhaitez vous plonger dans les composants internes d'Oracle, ce qui suit peut vous aider.

Tout d'abord, vous devez comprendre comment s'appelle la fonction C interne. Pour ce faire, vous pouvez exécuter du code long en une seule session. J'ai exécuté ceci

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 également s'agir de code PL/SQL, il vous suffit de vous assurer que vous appelez constamment ora_hash.

Pendant qu'il tourne

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

Maintenant, cherchons evahash sur Google. Nous avons eu beaucoup de chance car il existe 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 lien vers evahash.

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

Jusqu'ici tout va bien, nous nous souvenons que nous pouvons utiliser la fonction C externe dans Oracle si nous la créons dans une bibliothèque (DLL sous Windows).

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

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 une graine) tandis 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.Alors quel est le problème ?ora_hash accepte des paramètres de presque tous les types (par exemple, select ora_hash(sys.odcinumberlist(1,2,3)) from dual ) tandis que la fonction C accepte la valeur sous forme de tableau d'octets. Cela signifie qu'une certaine conversion se produit avant l'appel de la fonction.Ainsi, avant d'utiliser la fonction de hachage C mentionnée, vous devez comprendre comment la valeur réelle est transformée avant de lui passer.

Vous pouvez procéder à l'ingénierie inverse des binaires Oracle en utilisant les rayons hexadécimaux IDA PRO +, mais cela peut prendre des jours. Sans parler des 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.

Mettre à jour

ora_hash et dbms_utility.get_hash_value peuvent être mappés 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 corps du package de dbms_utility, nous verrons la 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);

Cherchons icd_hash sur Google et nous pouvons trouver qu'il est mappé sur _psdhsh (https://yurichev.com/blog/50/ ). Il est maintenant temps de désassembler oracle.exe et d'extraire le code pour _psdhsh à partir de cela. Peut-être que j'y consacrerai du temps l'année prochaine.