Optimización SQL en Oracle. En venta, próximamente!
El misterioso caso del error de «privilegios insuficientes» en la creación de vista materializada dentro de un procedimiento PL/SQL.
Recuerdo aquella vez que me pidieron crear un usuario para una aplicación en desarrollo. El usuario debía ser capaz de crear vistas y vistas materializadas, de modo que usé la siguiente sintaxis.
SQL> create user desarrollo identified by desarrollo;
User created.
SQL> grant connect, resource, create view, create materialized view to desarrollo;
Grant succeeded.
De este modo, creía yo, garantizaba que el usuario podría tener ese privilegio de forma explícita, y no mediante un rol, y así descartaba errores que podrían producirse por la no herencia de privilegios a través de roles en el uso de PL/SQL.
No obstante, el usuario vino a mi mesa a decirme: «No puedo crear vistas materializadas. Privilegios insuficientes».
-¿Cómo es posible? – pregunté sorprendido. – Te aseguro que el usuario tiene privilegios para crearlas.
De modo que abrí una consola de sqlplus y ejecuté:
SQL> create materialized view test as select * from dual;
Materialized view created.
– ¿Ves? El usuario tiene privilegios.
– Ya, pero es que yo lo ejecuto dentro de un procedimiento PL/SQL, mediante el comando EXECUTE IMMEDIATE. – contestó.
– Vamos a probar.
SQL> drop materialized view test;
Materialized view dropped.
SQL> begin
2 execute immediate ‘create materialized view test as select * from dual’;
3 end;
4 /
PL/SQL procedure successfully completed.
SQL> desc test
Name Null? Type
—————————————– ——– —————————-
DUMMY VARCHAR2(1)
«La query que tumba la máquina».
Hace unos años publiqué un artículo llamado «PL/SQL y ejecuciones en host» en el que describía el paso a paso para poder, desde PL/SQL, ejecutar código en el sistema operativo.
Oracle no permite que los procedimientos y funciones puedan acceder al host, pero sí permite llamadas a funciones externas implementadas con C o PASCAL, y redireccionadas como librerías mediante un objeto library.
Mi intención inicial fue la de crear un procedimiento PL/SQL que realizara un backup en caliente del servidor, realizase un export, import, o cualquier otra invocación a un ejecutable residente en el sistema operativo.
Hoy he visto una configuración similar en una base de datos en un entorno de producción, que realizan la misma implementación pero mediante una función.
create or replace
FUNCTION sysrun (syscomm IN VARCHAR2)
RETURN BINARY_INTEGER
AS LANGUAGE C
NAME «sysrun»
LIBRARY shell_lib
PARAMETERS(syscomm string);
Broadcast message from root (Thu Jan 27 13:16:34 2011):
The system is going down for reboot NOW!
SYSTEM.SYSRUN('SUDOREBOOT')
---------------------------
0
Encriptación de datos desde aplicación.
En todos los ejemplos que he encontrado sobre encriptación y desencriptación de datos en Oracle, siempre se usan procedimientos PL/SQL para establecer la seguridad en la base de datos. No he encontrado un sólo ejemplo que permita hacer un insert «encriptado» y una consulta «desencriptada».
Imaginando el siguiente escenario: Cada usuario tiene una «palabra secreta» para desencriptar su propia información. En la base de datos todo se registra encriptado.
Para ello, Oracle ofrece dos paquetes:
– DBMS_OBFUSCATION_TOOLKIT. A partir de Oracle8i, que soporta encriptación DES y triple DES (Data Encription Standard), y con ciertas limitaciones (por ejemplo, los datos a encriptar han de ser un múltiplo de 8 bytes).
– DBMS_CRYPTO. A partir de Oracle10g. Soporta más formas de encriptación, como la AES (Advanced Encription Standard), que sustituye el anterior DES y no hay limitación con el número de carácteres.
Para mas información, la documentación de Oracle ofrece esta comparativa de funcionalidades.
El siguiente ejemplo muestra la encriptación de la palabra «SECRETO» (8 bytes) y genera un error al intentar encriptar «SECRETITOS!» (11 bytes)
SQL> select DBMS_OBFUSCATION_TOOLKIT.
DBMS_OBFUSCATION_TOOLKIT.
——————————
lr??
SQL> select DBMS_OBFUSCATION_TOOLKIT.
select DBMS_OBFUSCATION_TOOLKIT.
*
ERROR at line 1:
ORA-28232: invalid input length for obfuscation toolkit
ORA-06512: at «SYS.DBMS_OBFUSCATION_TOOLKIT_
ORA-06512: at «SYS.DBMS_OBFUSCATION_TOOLKIT»
El error ORA-28232 corresponde a la longitud inadecuada de la cadena a encriptar. ‘SECRETITOS!» tiene 11 carácteres y el paquete está limitado a múltiplos de 8 bytes. Por ejemplo, el número de una tarjeta de crédito.
SQL> select DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>’1234567812345678′,
2 key_string=>’clavedesencript’)
3 from dual;
DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(INPUT_STRING=>’1234567812345678′,KEY_STRING=
——————————————————————————–
}??X??
De modo que la desencriptación funciona de igual modo, usando la función DESDecrypt
SQL> select DBMS_OBFUSCATION_TOOLKIT.
2 input_string=>DBMS_
3 key_string=>’CLAVE_BUENA’) from dual;
DBMS_OBFUSCATION_TOOLKIT.
——————————
SECRETO!
SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>’1111222233334444′,key_string=>’CLAVE_BUENA’),
3 key_string=>’CLAVE_BUENA’) from dual;
DBMS_OBFUSCATION_TOOLKIT.DESDECRYPT(INPUT_STRING=>DBMS_OBFUSCATION_TOOLKIT.DESEN
——————————————————————————–
1111222233334444
y si se utiliza una clave distinta, la información no se desencriptará adecuadamente.
SQL> select DBMS_OBFUSCATION_TOOLKIT.
2 input_string=>DBMS_
3 key_string=>’CLAVE_ERRONEA’) from dual;
DBMS_OBFUSCATION_TOOLKIT.
——————————
???! k
…ó producirá un error.
SQL> select DBMS_OBFUSCATION_TOOLKIT.DESDecrypt(
2 input_string=>DBMS_OBFUSCATION_TOOLKIT.DESEncrypt(input_string=>’1111222233334444′,key_string=>’CLAVE_BUENA’),
3 key_string=>’CLAVE_MALA’) from dual;
ERROR:
ORA-29275: partial multibyte character
no rows selected
El siguiente ejemplo muestra la encriptación de la palabra «SECRETITOS!» (11 bytes) usando una suite de encriptación que ya viene implementada. En concreto es la DES_CBC_PCKS5, que contiene encriptación DES, encadenamiento de cifrado de bloques y modificadores de relleno PCKS5.
Es preciso, para invocar correctamente a este paquete, realizar una conversión a RAW de las cadenas a encriptar. He utilizado para ello el paquete UTL_RAW y la función UTL_I18N.STRING_TO_RAW.
SQL> select DBMS_CRYPTO.ENCRYPT(src => UTL_I18N.STRING_TO_RAW (‘SECRETITOS!’, ‘AL32UTF8’),
2 typ => 4353,
3 key => UTL_I18N.STRING_TO_RAW (‘clavedesencript’, ‘AL32UTF8’)
4 )
5 from dual;
DBMS_CRYPTO.ENCRYPT(SRC=>UTL_
——————————
1BA7F933C2CAD0C7F4FDA685775BE0
Y la desencriptación de la información, con la función DECRYPT.
SQL> select UTL_RAW.cast_to_varchar2(
2 DBMS_CRYPTO.DECRYPT(
3 DBMS_CRYPTO.ENCRYPT(src => UTL_I18N.STRING_TO_RAW (‘SECRETITOS!’, ‘AL32UTF8’),
4
5
6
7 typ => 4353,
8 key => UTL_I18N.STRING_TO_RAW (‘clavedesdecript’, ‘AL32UTF8’)
9 )
10 )
11 from dual;
UTL_RAW.CAST_TO_VARCHAR2(DBMS_
——————————
SECRETITOS!
Para más información sobre las múltiples formas de encriptación y uso de claves, lo mejor es consultar la documentación del paquete DBMS_CRYPTO.
Acceso a una bbdd MSAccess desde Oracle.
Esta funcionalidad permite configurar una entrada DNS de ODBC como si se tratara de un servicio Oracle cualquiera. De este modo, mediante un dblink, un usuario podría acceder a una tabla de una base de datos access (o SQLServer o MySQL, por ejemplo) desde una query o desde PL/SQL.
La arquitectura de servicios heterogeneos se basa en la siguiente configuración:
Se especifica una entrada de TNS en el fichero tnsnames.ora que resuelva una conexión al host, puerto y nombre de servicio determinado, con el parámetro HS=OK (Oracle Heterogeneous Services).
Por otro lado, el listener destino tiene definido para ese servicio la invocación a un programa llamado «hsodbc» que, mediante un fichero de parámetros llamado init
De este modo, y creando un dblink, cualquier usuario podría hacer una select que cruzara datos de su esquema Oracle y de una bbdd Access.
PASOS A SEGUIR
1.- Instalar en el diccionario los servicios heterogeneos.
2.- Configurar el tnsnames.ora
3.- Configurar el listener.
4.- Comprobar que el listener y el tnsnames.ora funcionan correctamente.
5.- Configurar el ODBC para la bbdd access.
6.- Crear el DBLINK hacia la bbdd access.
7.- Crear el fichero init
8.- Ejecutar una consulta mediante ese dblink.
1.- Instalar en el diccionario los servicios heterogeneos
@?/rdbms/admin/caths.sql
NOTA: puede que estas vistas estén instaladas ya en la bbdd.
2.- Configurar el tnsnames.ora
bbdd_access =
(DESCRIPTION=
(ADDRESS= (PROTOCOL=tcp)
(HOST=192.168.2.4)
(PORT=1521)
)
(CONNECT_DATA =
(SERVICE_NAME=odbc_access)
)
(HS = OK)
)
3.- Configurar el listener. (la entrada en negrita es la que hay que añadir)…
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.2.4)(PORT = 1521))
)
)
)
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = D:oracleora92)
(PROGRAM = extproc)
)
(SID_DESC =
(GLOBAL_DBNAME = orcl)
(ORACLE_HOME = D:oracleora92)
(SID_NAME = orcl)
)
(SID_DESC =
(ORACLE_HOME = D:oracleora92)
(SID_NAME = odbc_access)
(PROGRAM = hsodbc)
)
)
4.- Comprobar que el listener y el tnsnames.ora funciona correctamente.
C:Documents and SettingsAdministrador>tnsping bbdd_access
TNS Ping Utility for 32-bit Windows: Version 9.2.0.1.0 – Production on 08-SEP-2006 18:19:42
Copyright (c) 1997 Oracle Corporation. All rights reserved.
Archivos de parßmetros utilizados:
D:oracleora92networkadmin
Adaptador TNSNAMES utilizado para resolver el alias
Attempting to contact (DESCRIPTION= (ADDRESS= (PROTOCOL=tcp) (HOST=192.168.2.4) (PORT=1521)) (CONNECT_DATA = (SERVICE_NAME=odbc_access)) (HS = OK))
Realizado correctamente (50 mseg)
5.- Configurar el ODBC para la bbdd access.
La base de datos access utilizada en el ejemplo tiene una única tabla con cuatro filas de ejemplo…
create table ejemplo
(id counter,
valor text)
desde ahí se crea el driver odbc_access vinculado a la base de datos que uso: «test_hs.mdb» con esta tabla de ejemplo.
NOTA: esta bbdd access tiene password «tesths»
6.- Crear el DBLINK hacia la bbdd access.
SQL> CREATE DATABASE LINK bbdd_access
2 CONNECT TO ADMIN IDENTIFIED BY TESTHS
3 USING ‘bbdd_access’;
Enlace con la base de datos creado.
7.- Crear el fichero init
— FICHERO initODBC_ACCESS.ora
#
# HS init parameters
#
HS_FDS_CONNECT_INFO = odbc_access
HS_FDS_TRACE_LEVEL = 0
HS_FDS_TRACE_FILE_NAME = odbc_access.trc
HS_AUTOREGISTER = TRUE
8.- Lanzar la query via ese dblink
SQL> column «valor» format a40
SQL> set pages 1000
SQL> set lines 120
SQL> select * from ejemplo@bbdd_access;
id valor
———- ——————————
1 dato1
2 dato2
3 dato3
4 dato4
Funciones Analíticas.
Al detalle. Una consulta del tipo «Datos de cliente con la fecha del primer contrato, fecha de la primera cancelación de contrato, fecha del último contrato contratado, fecha de…» suele consultarse con una subconsulta para cada «fecha de…».
Éste ejemplo, o el típico «Los tres contratos más recientes, las cinco últimas cancelaciones, etc.» siempre hacen que los programadores realicen una subconsulta por cada una de las condiciones… y otra y otra y al final el rendimiento se incrementa tanto de consultar varias veces la misma tabla.
…evidentemente, la consulta SQL se ha hecho tan vasta que resulta muy complicado mantenerla.
Para esta casuística, las funciones analíticas se aplican a un subconjunto de registros, por lo que Oracle, para gestionarlo correctamente, crea una ventana SQL intermedia para reagrupar una y otra vez los resultados de una consulta. Así, dado el anterior ejemplo, Oracle tomaría todos los contratos de ese cliente y los agruparía para cada columna de resultados: el primer contrato contratado, el primer cancelado, el último contrato de alta, etc. sin necesidad de consultar una y otra vez la tabla de contratos.
Las funciones analíticas tienen la siguiente sintaxis (no es la sintaxis completa).
FUNCIÓN_ANALITICA(campo)
OVER (PARTITION BY campo_agr1, campo_agr2
ORDER BY campo_ord1 NULLS LAST)
Un ejemplo de su uso sería, por ejemplo, intentar corregir esta consulta:
SELECT a.ID_FACTURA,
a.FALINEA_AUX – b.minCount + 1 ID_FALINEA,
a.ID_CLIENT,
a.ID_COMPTEFACT,
a.PRODUCT_ID,
a.ID_PRCATPRODUCTE,
a.DS_PRNUMSERVEI,
a.ID_FACONCEPTE,
a.DT_FAFACTURACIO,
a.NUM_FAIMPORTCONCEPTE,
a.PRODUCT_LABEL,
a.DT_MOVIMENT,
a.FG_TIPUSOPERACIO,
a.asset_id,
a.PRODUCT_ATTR_VALUE
FROM vw_ci_linia_factura_tmp a,
(select t.id_factura,
t.dt_fafacturacio,
min(t.falinea_aux) minCount
from vw_ci_linia_factura_tmp t
group by t.id_factura,
t.dt_fafacturacio
) b
WHERE a.id_factura = b.id_factura
ORDER BY a.id_factura, a.FALINEA_AUX – b.minCount + 1 ASC;
No es necesario. Los costes de ejecución se reducen a la mitad.
SELECT a.ID_FACTURA,
a.FALINEA_AUX – min(falinea_aux) over
(partition by id_factura, dt_fafacturacio) +1 ID_FALINEA,
a.ID_CLIENT,
a.ID_COMPTEFACT,
a.PRODUCT_ID,
a.ID_PRCATPRODUCTE,
a.DS_PRNUMSERVEI,
a.ID_FACONCEPTE,
a.DT_FAFACTURACIO,
a.NUM_FAIMPORTCONCEPTE,
a.PRODUCT_LABEL,
a.DT_MOVIMENT,
a.FG_TIPUSOPERACIO,
a.asset_id,
a.PRODUCT_ATTR_VALUE
FROM sta_vw_ci_linia_factura_tmp a
ORDER BY 1,2 ASC;
Hablemos de Constraints.
– Primary key
– Foreign key
– Unique key
– Not Null
– Check
Las constraints tienen, básicamente, tres propósitos:
1.- Forzar integridad. Una constraint con estado ENABLED evita que, sobre las tablas, puedan modificarse valores de forma que no se satisfaga la regla de la constraint.
2.- Validar la información. Una constraint con estado DISABLED VALIDATE realiza una validación sobre los datos, certificando que la información contenida en las tablas satisface la regla de la constraint.
3.- Asumir la validez de la información. Una constraint con estado RELY no verifica la información, ni fuerza la integridad para las modificaciones y asume que los datos introducidos satisfacen la regla de la constraint.
Las constraints habilitadas cumplen el propósito 1. Ningún dato introducido o modificado viola la integridad de la regla de la constraint. De este modo, sobre una primary key ENABLED no se permite un insert sobre una fila ya existente. No obstante, si la constraint está en el estado ENABLED NOVALIDATE, no se permitirán cambios que violen la regla, pero no se verificarán que los datos existentes la cumplan.
Las constraints, por tanto, pueden estar en estado VALIDATE o NOVALIDATE independientemente de si están ENABLED o DISABLED.
Las constraints con VALIDATE cumplen el propósito 2: La información existente está validada. No obstante, si la constraint está deshabilitada con DISABLED VALIDATE, no se permitirán cambios sobre las columnas afectadas, ya que aunque la constraint no está habilitada, certifica que los datos existentes son válidos y cumplen con la regla de la constraint.
Las constraints, por tanto, pueden estar en estado RELY o NORELY (también conocido como BELIEF) cuando las constraints están en estado DISABLED NOVALIDATE.
Las constraints en ese estado cumplen con el propósito 3: Asumen que la información es correcta y aportan al optimizador información válida para el uso de vistas materializadas, optimización de queries, etc. De este modo, por ejemplo, al optimizador le puede resultar mucho más fácil encontrar tablas FACT o DIMENSION por las constraints de foreign key entre las tablas.
SQL> create table valores(
2 id number,
3 valor varchar2(50),
4 descrip varchar2(300),
5 CONSTRAINT PK_VALORES PRIMARY KEY (id) deferrable initially immediate);
Tabla creada.
SQL> alter table valores disable primary key;
Tabla modificada.
SQL> insert into valores values (1,’ELEMENTO UNO’,’Primer elemento, tabla vacía’);
1 fila creada.
SQL> insert into valores values (1,’ELEMENTO UNO»’,’Primer elemento, repetido’);
1 fila creada.
SQL> insert into valores values (1,’ELEMENTO UNO»»’,’Primer elemento, otra vez repetido’);
1 fila creada.
SQL> alter table valores enable novalidate primary key;
Tabla modificada.
SQL> insert into valores values (1,’ELEMENTO UNO»»»’,’Primer elemento, violando PK, repetido’);
insert into valores values (1,’ELEMENTO UNO»»»’,’Primer elemento, violando PK, repetido’)
*
ERROR en lÝnea 1:
ORA-00001: restricci¾n ·nica (DW_VIAJES.PK_VALORES) violada
SQL> select * from valores;
ID VALOR DESCRIP
———- —————————————- —————————————-
1 ELEMENTO UNO Primer elemento, tabla vacía
1 ELEMENTO UNO’ Primer elemento, repetido
1 ELEMENTO UNO» Primer elemento, otra vez repetido
SQL> truncate table valores;
Tabla truncada.
SQL> alter table valores disable primary key;
Tabla modificada.
SQL> insert into valores values (1,’ELEMENTO UNO’,’Primer elemento, tabla vacía’);
1 fila creada.
SQL> insert into valores values (2,’ELEMENTO DOS’,’Segundo elemento, ID distinto.’);
1 fila creada.
SQL> insert into valores values (3,’ELEMENTO TRES’,’Tercer elemento, ID distinto.’);
1 fila creada.
SQL> alter table valores disable validate primary key;
Tabla modificada.
SQL> insert into valores values (3,’ELEMENTO TRES’,’Tercer elemento, ID distinto.’);
insert into valores values (3,’ELEMENTO TRES’,’Tercer elemento, ID distinto.’)
*
ERROR en lÝnea 1:
ORA-25128: No se puede insertar/actualizar/suprimir en la tabla con la restricci¾n (DW_VIAJES.PK_VALORES) desactivada y validada
SQL> insert into valores values (4,’ELEMENTO CUATRO’,’Cuarto elemento, ID distinto.’);
insert into valores values (4,’ELEMENTO CUATRO’,’Cuarto elemento, ID distinto.’)
*
ERROR en lÝnea 1:
ORA-25128: No se puede insertar/actualizar/suprimir en la tabla con la restricci¾n (DW_VIAJES.PK_VALORES) desactivada y validada
SQL> create table master (id number primary key);
Tabla creada.
SQL> create table detail (id number primary key, valor varchar2(50),
2 master_id number constraint fk_master_id references master(id));
Tabla creada.
SQL> insert into master select rownum from user_tables;
40 filas creadas.
SQL> alter table detail disable constraint fk_master_id;
Tabla modificada.
SQL> insert into detail select rownum, object_name, mod(rownum,45)+1 from user_objects;
68 filas creadas.
SQL> alter table detail enable constraint fk_master_id exceptions into exceptions;
alter table detail enable constraint fk_master_id exceptions into exceptions
*
ERROR en línea 1:
ORA-02298: no se puede validar (ORADBA.FK_MASTER_ID) – claves principales no encontradas
SQL> select count(*) from exceptions;
COUNT(*)
———-
5
SQL> select * from exceptions;
ROW_ID OWNER TABLE_NAME CONSTRAINT
—————— —————————— —————————— ——————
AAAY/nAAJAAAd7KAAn ORADBA DETAIL FK_MASTER_ID
AAAY/nAAJAAAd7KAAo ORADBA DETAIL FK_MASTER_ID
AAAY/nAAJAAAd7KAAp ORADBA DETAIL FK_MASTER_ID
AAAY/nAAJAAAd7KAAq ORADBA DETAIL FK_MASTER_ID
AAAY/nAAJAAAd7KAAr ORADBA DETAIL FK_MASTER_ID
Uso de logminer.
– Usuario borra una fila.
– Por accidente.
– No sabe de qué fila se trata, pero ‘algo’ ha dicho «registro borrado».
– (…borrado y validado).
– Ni idea de qué datos tenía la fila.
– Ni él, ni nadie.
– Desde ese momento, la fila X ha desaparecido sin rastro.
Con variaciones, éste caso es bastante frecuente, y si el usuario que ha borrado el registro es lo suficientemente rápido, existe una posibilidad de recuperar los datos perdidos.
Generalmente, todas las transacciones convencionales procesan la información pasando por las áreas de redo log. En estos archivos está toda la información correspondiente al nuevo cambio. Oracle basa toda su actividad en el buen funcionamiento del registro de redo log y, por tanto, en esta área únicamente almacena los datos físicos elementales para rehacer la operación. Por tanto, no se almacena información sobre los valores anteriores.
Lo cual sí sucede en el área de rollback, y ¡la gestión de rollback también pasa por redolog!.
Oracle dispone de un paquete, el LOG MINER que permite rastrear los redo logs (online y archivados) junto con la información de rollback disponible también en éstos. Una vez ejecutado el paquete que crea el fichero de diccionario de objetos, Oracle es capaz de interpretar esos redologs y mostrar información tan válida como el SQL generado y el SQL inverso para «reparar» la acción.
SQL> connect / as sysdba
Conectado.
SQL> show parameters utl
NAME TYPE VALUE
———————————— ———– ——————————
utl_file_dir string c:oraclefiles
SQL> exec DBMS_LOGMNR_D.BUILD( DICTIONARY_FILENAME =>’dictionary.ora’, DICTIONARY_LOCATION => ‘c:oraclefiles’);
Procedimiento PL/SQL terminado correctamente.
SQL> select member from v$logfile;
MEMBER
——————————————————————————–
C:ORACLEORADATAORCLREDO04A.LOG
C:ORACLEORADATAORCLREDO04B.LOG
C:ORACLEORADATAORCLREDO05A.LOG
C:ORACLEORADATAORCLREDO05B.LOG
C:ORACLEORADATAORCLREDO06A.LOG
C:ORACLEORADATAORCLREDO06B.LOG
6 filas seleccionadas.
SQL> exec DBMS_LOGMNR.add_logfile(‘C:ORACLEORADATAORCLREDO04A.LOG’);
Procedimiento PL/SQL terminado correctamente.
SQL> exec DBMS_LOGMNR.add_logfile(‘C:ORACLEORADATAORCLREDO05A.LOG’);
Procedimiento PL/SQL terminado correctamente.
SQL> exec DBMS_LOGMNR.add_logfile(‘C:ORACLEORADATAORCLREDO06A.LOG’);
Procedimiento PL/SQL terminado correctamente.
SQL> EXECUTE DBMS_LOGMNR.START_LOGMNR(DICTFILENAME =>’c:oraclefilesdictionary.ora’);
Procedimiento PL/SQL terminado correctamente.
SQL> set pages 100
SQL> set lines 120
SQL> column sql_redo format a50
SQL> column sql_undo format a50
SQL> select sql_redo, sql_undo from v$logmnr_contents where rownum<10;
SQL_REDO SQL_UNDO
————————————————– ————————————————–
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘170,01’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘170,01’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘170,01′,’1′,’170,01′,’1 170,01’ and «Importe AVG» = ‘170,01’ and «Ciudad»
70,01′,’170,01′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52149′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52149’ and ROWID = ‘AAALV
mAAPAAAAn8AAA’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘112,95’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘112,95’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘112,95′,’1′,’112,95′,’1 112,95’ and «Importe AVG» = ‘112,95’ and «Ciudad»
12,95′,’112,95′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52206′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52206’ and ROWID = ‘AAALV
mAAPAAAAn8AAB’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘123,29’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘123,29’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘123,29′,’1′,’123,29′,’1 123,29’ and «Importe AVG» = ‘123,29’ and «Ciudad»
23,29′,’123,29′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52331′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52331’ and ROWID = ‘AAALV
mAAPAAAAn8AAC’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘178,55’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘178,55’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘178,55′,’1′,’178,55′,’1 178,55’ and «Importe AVG» = ‘178,55’ and «Ciudad»
78,55′,’178,55′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52419′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52419’ and ROWID = ‘AAALV
mAAPAAAAn8AAD’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘107,36’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘107,36’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘107,36′,’1′,’107,36′,’1 107,36’ and «Importe AVG» = ‘107,36’ and «Ciudad»
07,36′,’107,36′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52488′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52488’ and ROWID = ‘AAALV
mAAPAAAAn8AAE’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘120,5’ and «Importe COUNT» = ‘1’ a
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» nd «Importe MAX» = ‘120,5’ and «Importe MIN» = ’12
,»Detalle Vuelo») values (‘120,5′,’1′,’120,5′,’120 0,5’ and «Importe AVG» = ‘120,5’ and «Ciudad» = ‘M
,5′,’120,5′,’Madeira’,’Portugal’,’Mayores de 50 a± adeira’ and «Pais» = ‘Portugal’ and «Rango Edad» =
os’,’Mujer’,’VUELO52561′); ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and «De
talle Vuelo» = ‘VUELO52561’ and ROWID = ‘AAALVmAAP
AAAAn8AAF’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘109,01’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘109,01’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘109,01′,’1′,’109,01′,’1 109,01’ and «Importe AVG» = ‘109,01’ and «Ciudad»
09,01′,’109,01′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52690′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52690’ and ROWID = ‘AAALV
mAAPAAAAn8AAG’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘174,91’ and «Importe COUNT» = ‘1’
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» and «Importe MAX» = ‘174,91’ and «Importe MIN» = ‘
,»Detalle Vuelo») values (‘174,91′,’1′,’174,91′,’1 174,91’ and «Importe AVG» = ‘174,91’ and «Ciudad»
74,91′,’174,91′,’Madeira’,’Portugal’,’Mayores de 5 = ‘Madeira’ and «Pais» = ‘Portugal’ and «Rango Eda
0 a±os’,’Mujer’,’VUELO52693′); d» = ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and
«Detalle Vuelo» = ‘VUELO52693’ and ROWID = ‘AAALV
mAAPAAAAn8AAH’;
insert into «DISCOVERER9I».»EUL5_MV101651″(«Import delete from «DISCOVERER9I».»EUL5_MV101651″ where «
e SUM»,»Importe COUNT»,»Importe MAX»,»Importe MIN» Importe SUM» = ‘131,5’ and «Importe COUNT» = ‘1’ a
,»Importe AVG»,»Ciudad»,»Pais»,»Rango Edad»,»Sexo» nd «Importe MAX» = ‘131,5’ and «Importe MIN» = ’13
,»Detalle Vuelo») values (‘131,5′,’1′,’131,5′,’131 1,5’ and «Importe AVG» = ‘131,5’ and «Ciudad» = ‘M
,5′,’131,5′,’Madeira’,’Portugal’,’Mayores de 50 a± adeira’ and «Pais» = ‘Portugal’ and «Rango Edad» =
os’,’Mujer’,’VUELO52951′); ‘Mayores de 50 a±os’ and «Sexo» = ‘Mujer’ and «De
talle Vuelo» = ‘VUELO52951’ and ROWID = ‘AAALVmAAP
AAAAn8AAI’;
9 filas seleccionadas.
Tremendas artimañas
Parece que TOAD, de Quest, es una herramienta demasiado atractiva para algunos. Incluso ofreciendo alternativas gratuítas al respecto.
Tras comprobar que seguían utilizando TOAD, incluso contra las bases de datos de producción, decidimos implementar el siguiente trigger after logon:
create or replace trigger logon_program after logon on database
declare r_session v$session%rowtype;
begin
— Tomamos info de la sesion que se conecta
select * into r_session
from v$session
where audsid=userenv(‘SESSIONID’);
if ((upper(r_session.program) like ‘%T%O%A%D%’)
or (upper(r_session.program) like ‘%TOAD%’))
then
raise_application_error(-20002,’Hacking detected. Application not supported.’);
end if;
end;
/
No obstante, hay gente que se sigue resignando a usar sustitutivos a TOAD, y recurren a estratégias tan imaginativas como renombrar el ejecutable TOAD.EXE por SQLPLUSW.EXE
Vamos, casi ni se nota!
SQL> select sid, serial#, program, module, osuser from v$session;
SID SERIAL# PROGRAM MODULE OSUSER
———- ———- —————————— ——————————————— ————–
1 1 ORACLE.EXE SYSTEM
2 1 ORACLE.EXE SYSTEM
3 1 ORACLE.EXE SYSTEM
4 1 ORACLE.EXE SYSTEM
5 1 ORACLE.EXE SYSTEM
6 1 ORACLE.EXE SYSTEM
7 1 ORACLE.EXE SYSTEM
8 1 ORACLE.EXE SYSTEM
10 1293 jrew.exe jrew.exe bbdd
11 333 sqlplus.exe sqlplus.exe bbdd
* 13 583 sqlplusw.exe T.O.A.D. slaborda
14 2 JDBC Thin Client OWB Runtime Service – Infrastructure SYSTEM
* 17 1091 sqlplusw.exe T.O.A.D. slaborda
18 1006 OraEdit.exe OraEdit.exe bbdd
19 385 QueryReporter.exe PL/SQL Developer bbdd
* 22 819 sqlplusw.exe T.O.A.D. slaborda
23 30 PLSQLDev.exe PL/SQL Developer bbdd
37 75 jrew.exe jrew.exe fdomingues
* 44 21 FCBARCELONA5-REALMADRID0.exe T.O.A.D. slaborda
53 196 JDBC Thin Client JDBC Thin Client fdomingues
61 413 JDBC Thin Client JDBC Thin Client fdomingues
* 62 50 FCBARCELONA5-REALMADRID0.exe T.O.A.D. slaborda
22 filas seleccionadas.
Impresionante…