Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Welchen Algorithmus verwendet die ORA_HASH-Funktion?

Nun, wenn es "zu verwenden scheint", dann ist es sinnvoll, ein wenig Reverse Engineering durchzuführen und zu überprüfen, was genau aufgerufen wird, und den Code der Funktion zu zerlegen.

Wenn Sie jedoch in die Interna von Oracle eintauchen möchten, kann Folgendes hilfreich sein.

Zunächst einmal müssen Sie herausfinden, welche interne C-Funktion aufgerufen wird. Dazu können Sie langlaufenden Code in einer Sitzung ausführen. Ich habe dies ausgeführt

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

Es kann auch PL/SQL-Code sein, Sie müssen nur sicherstellen, dass Sie ständig ora_hash aufrufen.

Während es läuft

Ich habe es unter Windows getestet und es sieht so aus, als ob ora_hash ...->evaopn2()->evahash() ist ->...

Lassen Sie uns jetzt nach evahash googeln. Wir hatten großes Glück, denn es gibt eine Header-Datei auf der offiziellen Seite https://oss.oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash.h mit Link zu evahash.

Und schließlich gibt es eine Seite mit aktuellem C-Code http://burtleburtle.net/bob/hash/ evahash.html

Soweit so gut, wir erinnern uns, dass wir externe C-Funktionen in Oracle verwenden können, wenn wir sie in die Bibliothek (DLL unter Windows) einbauen.

Zum Beispiel auf meinem Win x64, wenn ich die Funktionssignatur in

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

Es kann erfolgreich von Oracle ausgeführt werden. Aber wie Sie sehen, unterscheidet sich die Signatur ein wenig von ora_hash in Oracle. Diese Funktion akzeptiert Wert, ihre Länge und Initialisierung (kann Seed sein), während die Signatur in Oracle ora_hash(expr, max_bucket, seed_value) ist.

Lassen Sie uns versuchen, Oracle zu testen

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

Keine der Zahlen stimmt überein. Was ist also das Problem? ora_hash akzeptiert Parameter fast jeden Typs (zum Beispiel select ora_hash(sys.odcinumberlist(1,2,3)) from dual ), während die C-Funktion den Wert als Array von Bytes akzeptiert. Dies bedeutet, dass vor dem Funktionsaufruf eine gewisse Konvertierung stattfindet. Bevor Sie die erwähnte C-Hash-Funktion verwenden, müssen Sie also herausfinden, wie der tatsächliche Wert umgewandelt wird, bevor er an ihn übergeben wird.

Sie können mit dem Reverse Engineering von Oracle-Binärdateien mit IDA PRO + Hex-Rays fortfahren, aber das kann Tage dauern. Ganz zu schweigen von plattformspezifischen Details.

Wenn Sie also ora_hash imitieren möchten, wäre die einfachste Option, die Oracle Express Edition zu installieren und sie zum Aufrufen von ora_hash zu verwenden.

Ich hoffe, das war interessant. Viel Glück.

Aktualisieren

ora_hash und dbms_utility.get_hash_value können aufeinander abgebildet werden (siehe 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

Wenn wir den Paketkörper von dbms_utility auspacken, sehen wir die folgende Deklaration

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

und

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

Lassen Sie uns nach icd_hash googeln und wir können feststellen, dass es _psdhsh zugeordnet ist (https://yurichev.com/blog/50/ ). Jetzt ist es an der Zeit, oracle.exe zu disassemblieren und den Code für _psdhsh zu extrahieren davon. Vielleicht verbringe ich nächstes Jahr etwas Zeit damit.