Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Maneira mais rápida de calcular o hash de uma tabela inteira


Em primeiro lugar, acho que a maneira de abordar "administradores desonestos" é com uma combinação da trilha de auditoria da Oracle e Cofre de banco de dados recursos.

Dito isto, aqui está o que eu poderia tentar:

1) Crie uma função de agregação ODCI personalizada para calcular um hash de várias linhas como uma agregação.2) Crie um VIRTUAL NOT NULL coluna na tabela que era um hash SHA de todas as colunas na tabela - ou todas as que você se preocupa em proteger. Você manteria isso o tempo todo - basicamente trocando alguns insert/update/delete desempenho em troca para poder calcular hashes mais rapidamente.3) Crie um índice não exclusivo nessa coluna virtual4) SELECT my_aggregate_hash_function(virtual_hash_column) FROM my_table para obter os resultados.

Aqui está o código:

Crie uma função agregada para calcular um hash SHA em várias linhas

CREATE OR REPLACE TYPE matt_hash_aggregate_impl AS OBJECT
(
  hash_value RAW(32000),
  CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT,  
-- Called to initialize a new aggregation context
-- For analytic functions, the aggregation context of the *previous* window is passed in, so we only need to adjust as needed instead 
-- of creating the new aggregation context from scratch
  STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER,
-- Called when a new data point is added to an aggregation context  
  MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER,
-- Called to return the computed aggragate from an aggregation context
  MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER,
-- Called to merge to two aggregation contexts into one (e.g., merging results of parallel slaves) 
  MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER,
  -- ODCIAggregateDelete
  MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER  
);

/

CREATE OR REPLACE TYPE BODY matt_hash_aggregate_impl IS

CONSTRUCTOR FUNCTION matt_hash_aggregate_impl(SELF IN OUT NOCOPY matt_hash_aggregate_impl ) RETURN SELF AS RESULT IS
BEGIN
  SELF.hash_value := null;
  RETURN;
END;


STATIC FUNCTION ODCIAggregateInitialize (sctx IN OUT matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
  sctx := matt_hash_aggregate_impl ();
  RETURN ODCIConst.Success;
END;


MEMBER FUNCTION ODCIAggregateIterate (self IN OUT matt_hash_aggregate_impl, value IN raw ) RETURN NUMBER IS
BEGIN
  IF self.hash_value IS NULL THEN
    self.hash_value := dbms_crypto.hash(value, dbms_crypto.hash_sh1);
  ELSE 
      self.hash_value := dbms_crypto.hash(self.hash_value || value, dbms_crypto.hash_sh1);
  END IF;
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateTerminate (self IN matt_hash_aggregate_impl, returnValue OUT raw, flags IN NUMBER) RETURN NUMBER IS
BEGIN
  returnValue := dbms_crypto.hash(self.hash_value,dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

MEMBER FUNCTION ODCIAggregateMerge (self IN OUT matt_hash_aggregate_impl, ctx2 IN matt_hash_aggregate_impl) RETURN NUMBER IS
BEGIN
    self.hash_value := dbms_crypto.hash(self.hash_value || ctx2.hash_value, dbms_crypto.hash_sh1);
  RETURN ODCIConst.Success;
END;

-- ODCIAggregateDelete
MEMBER FUNCTION ODCIAggregateDelete(self IN OUT matt_hash_aggregate_impl, value raw) RETURN NUMBER IS
BEGIN
  raise_application_error(-20001, 'Invalid operation -- hash aggregate function does not support windowing!');
END;  

END;
/

CREATE OR REPLACE FUNCTION matt_hash_aggregate ( input raw) RETURN raw
PARALLEL_ENABLE AGGREGATE USING matt_hash_aggregate_impl;
/

Crie uma tabela de teste para trabalhar (você pula isso, pois tem sua tabela real)

create table mattmsi as select * from mtl_system_items where rownum <= 200000;

Crie um hash de coluna virtual dos dados de cada linha. Certifique-se de que é NOT NULL

alter table mattmsi add compliance_hash generated always as ( dbms_crypto.hash(to_clob(inventory_item_id || segment1 || last_update_date || created_by || description), 3 /*dbms_crypto.hash_sh1*/) ) VIRTUAL not null ;

Cria um índice na coluna virtual; desta forma você pode calcular seu hash com uma varredura completa do índice estreito em vez de uma varredura completa da tabela de gordura

create index msi_compliance_hash_n1 on mattmsi (compliance_hash);  

Junte tudo para calcular seu hash

SELECT matt_hash_aggregate(compliance_hash) from (select compliance_hash from mattmsi order by compliance_hash);

Alguns comentários:
  1. Acho importante usar um hash para calcular a agregação (em vez de simplesmente fazer um SUM() sobre os hashes em nível de linha, porque um invasor pode forjar a soma correta com muita facilidade.
  2. Acho que você não pode (facilmente?) usar consulta paralela porque é importante que as linhas sejam alimentadas para a função de agregação em uma ordem consistente, caso contrário o valor do hash será alterado.