Oracle
 sql >> Database >  >> RDS >> Oracle

Qual è l'algoritmo utilizzato dalla funzione ORA_HASH?

Bene, se "sembra usare", allora ha senso fare un po' di reverse engineering e controllare cosa si chiama esattamente e smontare il codice della funzione.

Se, tuttavia, desideri approfondire gli aspetti interni di Oracle, seguire la procedura potrebbe essere d'aiuto.

Prima di tutto, devi capire quale funzione C interna viene chiamata. Per farlo puoi eseguire del codice di lunga durata in una sessione. L'ho eseguito

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

Può essere anche codice PL/SQL, devi solo assicurarti di chiamare costantemente ora_hash.

Mentre è in esecuzione

Ho provato su Windows e sembra che ora_hash sia ...->evaopn2()->evahash() ->...

Ora cerchiamo su Google evahash. Siamo stati estremamente fortunati perché c'è un file di intestazione sul sito ufficiale https://oss.oracle.com/projects/ocfs-tools/src/branches/new-dir-format/libocfs/Linux/inc/ocfshash.h con link a evahash.

E infine c'è una pagina con il codice C effettivo http://burtleburtle.net/bob/hash/ evahash.html

Fin qui tutto bene, ricordiamo che possiamo usare la funzione C esterna in Oracle se la incorporiamo nella libreria (DLL su Windows).

Ad esempio sul mio Win x64 se cambio la firma della funzione in

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

può essere eseguito con successo da Oracle. Ma, come vedi, la firma differisce leggermente da ora_hash in Oracle. Questa funzione accetta il valore, la sua lunghezza e initval (può essere seed) mentre la firma in Oracle è ora_hash(expr, max_bucket, seed_value).

Proviamo a testareOracle

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

Nessuno dei numeri corrisponde. Allora qual è il problema?ora_hash accetta parametri di quasi tutti i tipi (ad esempio select ora_hash(sys.odcinumberlist(1,2,3)) from dual ) mentre la funzione C accetta il valore come matrice di byte. Ciò significa che alcune conversioni avvengono prima della chiamata della funzione. Quindi, prima di utilizzare la funzione hash C menzionata, devi capire come viene trasformato il valore effettivo prima di passarlo.

Puoi procedere con il reverse engineering dei binari Oracle utilizzando IDA PRO + raggi esadecimali, ma ciò potrebbe richiedere giorni. Per non parlare dei dettagli specifici della piattaforma.

Quindi, se vuoi imitare ora_hash, l'opzione più semplice sarebbe installare Oracle Express Edition e usarla per chiamare ora_hash.

Spero sia stato interessante. Buona fortuna.

Aggiorna

ora_hash e dbms_utility.get_hash_value possono essere mappati tra loro (vedi 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

Se scartiamo il corpo del pacchetto di dbms_utility vedremo la seguente dichiarazione

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

e

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

Cerchiamo su Google icd_hash e possiamo scoprire che è mappato su _psdhsh (https://yurichev.com/blog/50/ ). Ora è il momento di disassemblare oracle.exe ed estrarre il codice per _psdhsh da. Forse ci dedicherò un po' di tempo l'anno prossimo.