wake-up-neo.net

Welchen Algorithmus verwendet die ORA_HASH-Funktion?

In der Anwendung, an der ich gerade arbeite, bin ich auf etwas Code gestoßen, der einen Datenbankaufruf lediglich zum Aufrufen der Funktion ORA_HASH ( documentation ) einer UUID-Zeichenfolge ausführt. Der Grund dafür ist, dass es den Wert benötigt, um einen Service-Aufruf an ein anderes System zu tätigen, das anscheinend ORA_HASH für die Partitionierung verwendet.

Ich würde gerne wissen, welchen Algorithmus ORA_HASH verwendet, damit ich ihn erneut implementieren kann, um einen ähnlichen Service-Aufruf für eine Anwendung durchzuführen, die keinen Zugriff auf eine echte Datenbank hat, ganz zu schweigen von Oracle. Ich konnte bisher nur herausfinden, was der Oracle API-Dokumentation entspricht.

Nur um ganz klar zu sein: Ich muss ORA_HASH klonen, weil dies ein anderes System ist, das außerhalb meiner Kontrolle liegt, und ich muss in dieses System integrieren. Ja, es wäre schön, wenn Sie einen wirklich-Standardalgorithmus wie MD5 verwenden könnten, aber ich kann es nicht, es sei denn, ORA_HASH steht unter der Decke. 

Antworten oder Kommentare, die die Verwendung eines Hash-Algorithmus neben ORA_HASH vorschlagen, sind nicht hilfreich. Diese Frage bezieht sich speziell auf ORA_HASH, nicht generell auf Hashing oder Partitionierung.

16
Kaypro II

ein anderes System, das scheinbar ORA_HASH verwendet

Wenn es "scheinbar zu verwenden" ist, 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 das Folgende hilfreich sein.

Zunächst müssen Sie herausfinden, wie die interne C-Funktion ..__ benannt wird. Dazu können Sie in einer Sitzung lang laufenden Code 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 sich auch um PL/SQL-Code handeln. Sie müssen lediglich sicherstellen, dass Sie ora_hash ständig aufrufen.

Während es läuft

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

Lassen Sie uns nun auf evahash gehen. Wir hatten großes Glück, weil es auf der offiziellen Site https://oss.Oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash) eine Header-Datei gibt. 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, erinnern wir uns daran, dass wir externe C-Funktionen in Oracle verwenden können, wenn wir sie in eine Bibliothek (DLL unter Windows) integrieren.

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

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

sie kann erfolgreich von Oracle aus ausgeführt werden . Wie Sie jedoch sehen, unterscheidet sich die Signatur in Oracle etwas von ora_hash. Diese Funktion akzeptiert value, seine Länge und initval (möglicherweise Seed), während die Signatur in Oracle ora_hash (expr, max_bucket, seed_value) ist.

Versuchen wir 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 mit ..__ überein. Worin liegt das Problem? Ora_hash akzeptiert Parameter fast jedes Typs (beispielsweise select ora_hash(sys.odcinumberlist(1,2,3)) from dual), während die C-Funktion Werte als Byte-Array akzeptiert. Dies bedeutet, dass einige Konvertierungen vor dem Funktionsaufruf .. stattfinden. Bevor Sie die erwähnte C-Hash-Funktion verwenden, müssen Sie herausfinden, wie der tatsächliche Wert transformiert wird, bevor Sie ihn übergeben.

Sie können mit dem Reverse-Engineering von Oracle-Binärdateien mit IDA PRO + -Hex-Strahlen fortfahren. Dies kann jedoch Tage dauern. Plattformspezifische Details ganz zu schweigen.

Wenn Sie ora_hash imitieren möchten, wäre die einfachste Option die Installation der Oracle Express Edition und die Verwendung von ora_hash.

Ich hoffe das war interessant. Viel Glück.

Update

ora_hash und dbms_utility.get_hash_value können einander zugeordnet 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 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 google nach icd_hash suchen, und wir können feststellen, dass es _psdhsh ( https://yurichev.com/blog/50/ ) zugeordnet ist. Jetzt ist es Zeit, Oracle.exe zu demontieren und Code für _psdhsh daraus zu extrahieren. Vielleicht werde ich nächstes Jahr etwas Zeit damit verbringen.

19
Dr Y Wit

Dies beantwortet nicht die OP-Frage nach dem eigentlichen Algorithmus hinter ora_hash. Dies ist nur ein Beispiel für die Verwendung von ora_hash in pl/sql (Antwort auf @JonHeller-Kommentar):

Die Funktion:

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.

Und es benutzen:

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-Ausgabe:

2662839991

Sie können auch mit RESULT_CACHE oder anderen Techniken herumspielen, um die Dinge noch schneller zu machen.

Es ist schon sehr schnell. Beispiel: Aufruf der Funktion 1 Million Mal für eine große Tabelle:

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.

Im Grunde also 100.000 Zeilen pro Sekunde, das schließt alle Kontextwechsel ein, um die Sie sich Sorgen machen müssen. 

Wenn Sie ORA_HASH wegen der Leistung reproduzieren müssen, würde ich vorschlagen, dass Ihr Leistungsengpass möglicherweise an anderer Stelle liegt.

1
tbone