miércoles, 30 de abril de 2025

Auditorias de Oracle (DBMS_FGA)

 Objetivos:











 Describir las Auditorias de Oracle.
 Conocer los Procesos ADD_POLICYDROP_POLICY, ENABLE_POLICY y DISABLE_POLICY del paquete DBMS_FGA de SYS.
 Ver ejemplos prácticos.
_______________________________________________________________________
Políticas de Auditoria de Oracle
Oracle ofrece poderosas herramientas para el rastreo de todas las transacciones y/o consultas realizadas a sus tablas, esto se puede lograr con el uso del paquete DBMS_FGA el cual proporciona procesos de seguridad y control que permiten realizar las auditorias de forma precisa y certera. Debe tener pendiente que el uso de dicho paquete requiere los privilegios de administrador, ésto debido que las auditorias permiten el acceso a informaciones sensitivas del entorno del usuario y/o la aplicación del cual opera.

_______________________________________________________________________
Paquete DBMS_FGA
A continuación el detalle de los procesos que veremos en esta publicación:
  • ADD_POLICY: Crea la política de auditoría de acuerdo a los parámetros proporcionados.

Sintaxis:  

SYS.DBMS_FGA.ADD_POLICY(
		object_schema   	IN	VARCHAR2 DEFAULT NULL, 
		object_name     	IN	VARCHAR2,
policy_name IN VARCHAR2,
audit_condition IN VARCHAR2 DEFAULT NULL,
audit_column IN VARCHAR2 DEFAULT NULL,
handler_schema IN VARCHAR2 DEFAULT NULL,
handler_module IN VARCHAR2 DEFAULT NULL,
enable IN BOOLEAN DEFAULT TRUE,
statement_types IN VARCHAR2 DEFAULT SELECT,
audit_trail IN BINARY_INTEGER DEFAULT NULL,
audit_column_opts IN BINARY_INTEGER DEFAULT ANY_COLUMNS,
policy_owner IN VARCHAR2 DEFAULT NULL
);
  • DROP_POLICY: Elimina la política de auditoria especificada.

 Sintaxis:  

SYS.DBMS_FGA.DROP_POLICY(
			   object_schema	IN	VARCHAR2, 
			   object_name		IN	VARCHAR2, 
policy_name IN VARCHAR2
);
  •  ENABLE_POLICY: Habilita la política de auditoria especificada.

 Sintaxis: 

SYS.DBMS_FGA.ENABLE_POLICY(
			   object_schema	IN	VARCHAR2 DEFAULT NULL,
object_name IN VARCHAR2,
policy_name IN VARCHAR2,
enable IN BOOLEAN DEFAULT TRUE
);
  • DISABLE_POLICY: Desactiva/deshabilita la política de auditoria especificada.

  Sintaxis: 

SYS.DBMS_FGA.DISABLE_POLICY(
				object_schema	IN	VARCHAR2,
				object_name	IN	VARCHAR2,
				policy_name	IN	VARCHAR2
			); 
_______________________________________________________________________
Ejemplo Funcional
Para nuestra prueba utilizamos la tabla EMPLOYEES del esquema HR; En caso de no contar con este esquema y/o table puede instalarlo siguiendo las instrucciones aportadas en esta publicación. Adicionalmente creamos la tabla hr.consultas_tab_employees, el procedimiento hr.prc_audit_employees y la política de auditoria.

Script Tabla hr.consultas_tab_employees: 
BEGIN
    EXECUTE IMMEDIATE   'DROP TABLE hr.consultas_tab_employees';

    EXCEPTION
        WHEN OTHERS THEN
            NULL;
END;
/

CREATE TABLE    hr.consultas_tab_employees
(
    usuario_db          VARCHAR2(30)
    , fecha             TIMESTAMP
    , query             CLOB
    , valor_consulta    VARCHAR2(500)
, terminal VARCHAR2(500)
, host VARCHAR2(500)
, ip VARCHAR2(500)
, programa VARCHAR2(500)
, usuario_os VARCHAR2(500)
, sid VARCHAR2(500)
, object_schema VARCHAR2(500)
, object_name VARCHAR2(500)
, policy_name VARCHAR2(500)
);
Script Procedimiento hr.prc_audit_employees: Notar que hacemos usado de la función  SYS_CONTEXT, puede encontrar mas información sobre ella aquí.
CREATE OR   REPLACE PROCEDURE   hr.prc_audit_employees(
                                                        object_schema VARCHAR2
                                                        , object_name VARCHAR2
                                                        , policy_name VARCHAR2
                                                      )  AS

    v_execution_user    VARCHAR2(30);
    v_execution_date    TIMESTAMP;
    v_execution_sql     CLOB;
    v_valor_consulta    VARCHAR2(500);
v_terminal_name VARCHAR2(500);
v_host_name VARCHAR2(500);
v_ip_address VARCHAR2(500);
v_program_name VARCHAR2(500);
v_os_user VARCHAR2(500);
v_sid_user VARCHAR2(500);
BEGIN v_execution_user := USER; v_execution_date := SYSTIMESTAMP; v_execution_sql := SYS_CONTEXT('USERENV','CURRENT_SQL'); v_valor_consulta := SYS_CONTEXT('USERENV','CURRENT_BIND');
v_terminal_name := SYS_CONTEXT('USERENV','TERMINAL');
v_host_name := SYS_CONTEXT('USERENV','HOST');
v_ip_address := SYS_CONTEXT('USERENV','IP_ADDRESS');
v_program_name := SYS_CONTEXT('USERENV','MODULE');
v_os_user := SYS_CONTEXT('USERENV','OS_USER');
v_sid_user := SYS_CONTEXT('USERENV','SID');
INSERT INTO hr.consultas_tab_employees ( usuario_db , fecha , query , valor_consulta , terminal , host , ip , programa , usuario_os , sid , object_schema , object_name , policy_name ) VALUES ( v_execution_user --usuario_db VARCHAR2(30) , v_execution_date --fecha TIMESTAMP , v_execution_sql --query CLOB , v_valor_consulta --valor_consulta VARCHAR2(500) , v_terminal_name --terminal VARCHAR2(500) , v_host_name --host VARCHAR2(500) , v_ip_address --ip VARCHAR2(500) , v_program_name --programa VARCHAR2(500) , v_os_user --usuario_os VARCHAR2(500) , v_sid_user --sid VARCHAR2(500) , object_schema --object_schema VARCHAR2(500) , object_name --object_name VARCHAR2(500) , policy_name --policy_name VARCHAR2(500) ); END prc_audit_employees; /
Script Auditoria AUDIT_EMPLOYEES: 
SET SERVEROUTPUT ON
BEGIN
    SYS.DBMS_FGA.DROP_POLICY(
             --Argument Name       Value               Type       In/Out
             --------------------  ----------------- ------
             object_schema    =>   'HR'              --VARCHAR2   IN
             ,object_name     =>   'EMPLOYEES'       --VARCHAR2   IN
             ,policy_name     =>   'AUDIT_EMPLOYEES' --VARCHAR2   IN
        );

    EXCEPTION
        WHEN OTHERS THEN
            SYS.DBMS_OUTPUT.PUT_LINE(
                                        'Auditoria AUDIT_EMPLOYEES  no existe!'
                                    );
END;
/

BEGIN
    SYS.DBMS_FGA.ADD_POLICY(
         --Argument Name        Value                           Type      In/Out
         ---------------------- -----------------------       ------
         object_schema     =>   'HR'                          --VARCHAR2  IN
         ,object_name      =>   'EMPLOYEES'                   --VARCHAR2  IN
         ,policy_name      =>   'AUDIT_EMPLOYEES'             --VARCHAR2  IN
         ,audit_condition  =>   'EMPLOYEE_ID IS NOT NULL'     --VARCHAR2  IN
         ,audit_column     =>   'EMPLOYEE_ID'                 --VARCHAR2  IN
         ,handler_schema   =>   'HR'                          --VARCHAR2  IN
         ,handler_module   =>   'PRC_AUDIT_EMPLOYEES'         --VARCHAR2  IN
         ,enable           =>   TRUE                          --VARCHAR2  IN
         ,statement_types  =>   'SELECT,INSERT,UPDATE,DELETE' --VARCHAR2  IN
        );

    SYS.DBMS_OUTPUT.PUT_LINE(
                                'Auditoria AUDIT_EMPLOYEES creada con éxito.'
                            );

    EXCEPTION
        WHEN OTHERS THEN
            SYS.DBMS_OUTPUT.PUT_LINE(
                        'Error creando auditoria AUDIT_EMPLOYEES. Detalle: '
						||SQLERRM
                    );
END;
/

Tener presente que los anteriores Scripts fueron ejecutados con el usuario administrador SYSTEM. Ahora conectado desde con el usuario HR ejecutamos la siguiente consulta, notar el uso de la variable de entorno :emp para que se nos pida el valor a consultar:














Consultamos la tabla hr.consultas_tab_employees conectados con el usuario SYSTEM.








Nuevamente con HR insertados un registro en la tabla hr.employees:

Reconsultamos la tabla hr.consultas_tab_employees conectados con el usuario SYSTEM.








Como ultima prueba nos conectamos con HR mediante SQL PLUS y realizamos una consulta del nuevo empleado:














De igual forma verificamos con SYSTEM la anterior consulta en la tabla hr.consultas_tab_employees. Notar como en el campo programa aparece SQL*Plus.







Para nuestras pruebas creamos una tabla donde almacenamos las informaciones que entendemos importantes, de igual forma Oracle guarda un registro por cada auditoria realiza en la tabla sys.dba_fga_audit_trail, puede también hacer uso de ella:






Espero este contenido les haya sido de utilidad, si tienen preguntas o dudas no duden en dejárnoslas saber.


Fuentes:

https://docs.oracle.com/cd/B10500_01/appdev.920/a96612/d_fga2.htm#1002217
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_FGA.html






domingo, 27 de abril de 2025

Manejando las Transacciones Bloqueadas

Objetivos:

 Describir las Secciones de Oracle.
 Consultar las secciones activas.
 Conocer los metodos para terminar y/o desconectar las sesiones activas.
 Ver y terminar los bloqueos de Transacciones.






_______________________________________________________________________
Las Sesiones de Oracle.

Una sesión no es más que la conexión entre una aplicación X y la Base de Datos Oracle con sus respectivos objetos.

Puede consultar y obtener información de las sesiones de Oracle mediante las vistas V$SESSION GV$SESSION. Estas vistas contienen entre otros, los campos sid, serial#, inst_id útiles para dropear las secciones.

SELECT
        vs.sid
        , vs.serial#
        , vs.*
FROM    sys.v$session vs
;

SELECT
        gvs.sid
        , gvs.serial#
        , gvs.inst_id
        , gvs.*
FROM    sys.gv$session gvs
;

_______________________________________________________________________
Los Bloqueos de Oracle.

Oracle ofrece varias vistas que permiten consultar los bloqueos de transacciones sobre los distintos objetos de base de datos. Una de ellas es la vista V$LOCK, esta muestras los bloqueos activos en la base de datos sin muchos detalles del objeto bloqueado.

A continuación un ejemplo de la utilidad de la vista V$LOCK:

    1. Conectado con HR ejecutamos la consulta:
SELECT
        employee_id
        , first_name
        , last_name
        , email
FROM    hr.employees
WHERE   employee_id =   100
;








    2. Actualizamos el campo email, no ejecutamos COMMIT alguno y re ejecutamos la anterior consulta:
UPDATE  hr.employees
    SET email   =   email||'@company.com'
WHERE   employee_id =   100
;






    3. Conectado con un segundo usuario (SYSTEM en mi caso) ejecutamos los mismos script anteriores:





















/*Debido a que el usuario HR no ejecutó COMMIT luego del UPDATE, esa transacción está pendiente y ningun otro usuario puede ver los cambios.*/

    4. Con el usuario SYSTEM tratamos de actualizar el mismo campo email para el empleado 100 y notamos como la transacción no se completa debido al bloqueo del registro:














    5. Ahora conectado con un tercel usuario (SYS en mi caso) ejecutamos la siguiente consulta:

SELECT
        (
            SELECT  s.username
            FROM    sys.v$session s
            WHERE   s.sid=bker.sid
        )           AS  usuario_bloqueador,
        bker.SID    AS  sid_bloqueador,
        (
            SELECT  s.username
            FROM    sys.v$session s
            WHERE   s.sid=bked.sid
        )           AS  usuario_bloqueado,
        bked.SID    AS  sid_bloqueado
FROM
        sys.v$lock bker
        JOIN    sys.v$lock bked
            ON  (
                bker.id1 = bked.id1
                AND     bker.id2 = bked.id2
                )
WHERE
        bker.block = 1
AND     bked.request > 0
;





_______________________________________________________________________
La Vista V$LOCKED_OBJECT
La vista V$LOCKED_OBJECT permite ver las transacciones bloqueadas en la Base de Datos con más detalle, permitiendo conocer el objeto bloqueado.

La siguiente consulta muestra las mismas sesiones bloqueadas con el objeto que contiene el bloqueo:

SELECT
        o.owner||'.'||o.object_name     AS  objeto
        ,o.object_type                  AS  tipo_bojeto
        ,s.username                     AS  usuario_conexion
        ,s.sid
        ,s.serial#
        ,s.status
        , DECODE(s.status, 'INACTIVE', 'BLOQUEADOR', 'ACTIVE', 'BLOQUEDADO', 'DESCONOCIDO') AS  DESR_USUARIO
FROM
        sys.gv$locked_object bo
        JOIN    sys.gv$session s
            ON  ( s.sid = bo.session_id)
        JOIN    sys.dba_objects o
            ON  (bo.object_id    =   o.object_id
                    AND bo.inst_id      =   s.inst_id)
;






_______________________________________________________________________
Eliminando Bloqueos de Base Datos
Oracle ofrece varios métodos para desbloquear una transacción. Puede utilizar las sentencias:

    ALTER SYSTEM KILL SESSION 'SID, SERIAL#' [IMMEDIATE];

La anterior sentencia revierte las transacciones en curso, libera los bloqueos y recupera parcialmente los recursos utilizados; si no incluimos la clausula IMMEDIATE la sesión es marcada como 'marked for kill' para que ésta se elimine tan pronto como le sea posible, teniendo en cuenta que dicho proceso puede tomar varios minutos en completarse. 

    ALTER SYSTEM DISCONNECT SESSION 'SID, SERIAL#' [POST_TRANSACTION IMMEDIATE];

La pasada sentencia es un método alternativo para eliminar transacciones bloqueadas. Mientras que KILL SESSION solicita a la sesión que se auto elimine automáticamente, DISCONNECT SESSION termina el proceso del servidor dedicado. Lo que es igual a terminar el proceso desde el sistema operativo.

La cláusula POST_TRANSACTION espera a que se completen las transacciones en curso para luego desconectar la sesión, la cláusula IMMEDIATE desconecta la sesión y las transacciones en curso se revierten inmediatamente.

Tener pendiente que en la sintaxis [ ]  indica que ambas cláusulas son opcionales, pero esto no es así, se deben especificar una o ambas, de lo contrario recibiría un error. Si ambas cláusulas son incluidas IMMEDIATE seria ignorada.

ALTER SYSTEM KILL SESSION '1473, 24750' IMMEDIATE;

ALTER SYSTEM DISCONNECT SESSION '1473, 24750' IMMEDIATE;
/*Las dos sentencias anteriores muestran un ejemplo de como terminar la sesión bloqueadora.*/


DECLARE

    CURSOR  cur_sessions    IS
        SELECT
                vs.sid
                , vs.serial#
        FROM    sys.v$session vs
        WHERE   sid <>   SYS_CONTEXT('USERENV', 'SID')
        ;

BEGIN

    FOR i   IN  cur_sessions    LOOP
    BEGIN
        EXECUTE IMMEDIATE 'ALTER SYSTEM KILL SESSION '''||i.sid||', '||i.serial#||''' IMMEDIATE';

    EXCEPTION
    WHEN OTHERS THEN
        BEGIN
            EXECUTE IMMEDIATE 'ALTER SYSTEM DISCONNECT SESSION '''||i.sid||', '||i.serial#||''' IMMEDIATE';

        EXCEPTION
            WHEN OTHERSTHEN
                NULL;
        END;
    END;
    END LOOP;

END;
/*El bloque PL/SQL anterior consulta todas las sesiones de base de datos diferentes a la sesión actual y utiliza las sentencias KILL SESSION y DISCONNECT SESSION para eliminarlas o desconectarlas .*/


Fuentes 
https://docs.oracle.com/cd/B14099_19/web.1012/b15901/sessions001.htm
https://dbaora.com/kill-session-in-oracle/
https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-LOCK.html
https://oracle-base.com/articles/misc/killing-oracle-sessions

sábado, 19 de abril de 2025

Función LISTAGG Oracle

 Objetivo:





 Conocer la función LISTAGG.
 Ver algunos ejemplos útiles.
____________________________________________________________

Descripción
La función agregada de Oracle LISTAGG opera sobre una lista de registros y retorna en una sola línea todos los valores separados por un delimitador especificado. En esencia es usada para denormalizar datos de varios registros en un registro único. 
Sintaxis
LISTAGG(measure_expr [, 'delimiter'] ON OVERFLOW ERROR)
  WITHIN GROUP (order_by_clause) [OVER query_partition_clause]

En la Sintaxis:
1. measure_expr es la columna o expresión que se desea concatenar.
Tener en cuenta que los valores NULOS son ignorados.
2. delimiter único es el carácter separador. Este parámetro es opcional, 
3. order_by_clause establece el orden que se mostrará la lista de valores.
4. query_partition_clause devuelve todas las filas y duplica el resultado 
de LISTAGG para cada fila de la partición.
Ejemplos
A continuación algunas consultas que muestran el uso de la función LISTAGG, para ellos usamos las tablas del esquema HR.

SELECT
        e.department_id             AS  departamento
        ,    COUNT(e.first_name)    AS  cantidad_empleados
        , LISTAGG(
                    e.first_name,
                    ', '
                ) WITHIN GROUP( ORDER BY e.first_name)                   AS  empleados_x_dept
FROM    hr.employees    e
GROUP BY e.department_id
ORDER BY cantidad_empleados  DESC;

/*La consulta retorna el listado de todos los empleados agrupados por el departamento al que pertenecen*/
La Cláusula OVERFLOW

La cláusula ON OVERFLOW permite controlar la ocurrencia de la excepción ORA-01489 cuando la cadena retornada supera el limite máximo del tipo de datos retornado. Con esta evitamos el error y en su lugar podemos truncar la cadena retornada.

SELECT
        empleo
        , COUNT(1)  AS  cantidad
        , LISTAGG(detalle_empleado, ';' ON OVERFLOW TRUNCATE '**')
            WITHIN GROUP (ORDER BY empleo)   AS detalle_empleado
FROM
(
SELECT
        j.job_title     AS  empleo,
        '|Código: '          ||e.employee_id||'|'||
        'Nombre: '          ||e.first_name||' '||e.last_name||'|'||
        'Correo: '          ||e.email||'|'||
        'Telefono: '        ||e.phone_number||'|'||
        'Salario: '         ||e.salary||'|'||
        'Departamento: '    ||d.department_name||'|'||
        'Dirección: '       ||l.street_address||', '||l.city||', '||
        l.state_province||', '||l.postal_code||', '||c.country_name
        AS  detalle_empleado
FROM
        hr.employees e
        LEFT    JOIN    hr.departments d
            ON  (d.department_id    =   e.department_id)
        LEFT    JOIN    hr.jobs j
            ON  (j.job_id    =   e.job_id)
        LEFT    JOIN    hr.locations l
            ON  (l.location_id    =   d.location_id)
        LEFT    JOIN    hr.countries c
            ON  (c.country_id   =   l.country_id)
)
GROUP  BY   empleo
ORDER BY    cantidad DESC
;
/*En la anterior consulta unimos distintas tablas para traer las informaciones relativas a los empleados; Notar el uso de la cláusula ON OVERFLOW TRUNCATE '**'; de omitirla se generaría la mencionada excepcion ORA-01489*/


Fuentes
https://www.techonthenet.com/oracle/functions/listagg.php
https://www.oracletutorial.com/oracle-aggregate-functions/oracle-listagg/

viernes, 18 de abril de 2025

Instalación del Esquema HR en Oracle 19C

 Objetivo:




 Instalar el esquema HR de forma manual en la versión 19C de la Base de Datos Oracle.
____________________________________________________________

Introducción
La versión 19C de la Base de Datos Oracle no instala por defecto el esquema HR y sus respectivos objetos, por suerte existe una forma muy fácil de instalarla y aquí te la mostramos.


Esquema HR
Este esquema resulta ser una herramienta util para aprender y practicar las distintas sentencias SQL y PL/SQL. Este contiene tablas, vistas, triggers, constrains etc.

La siguiente imagen muestra la estructura de sus tablas:

Instalación Esquema HR
Abrimos el Command Prompt de Windows, escribimos sqlplus y presionamos la tecla Enter y nos conectamos con SYSTEM:


Luego ejecutamos el Script Esquema_HR.sql, el cual puede ser descargado mediante este LINK de Google Drive:


Yo instalé el script via SQL PLUS pero ustedes puedes ejecutarlo con la herramienta que entiendan, de igual forma si tienen preguntas o dudas pueden dejarme saber por la via que entiendan,



domingo, 24 de junio de 2018

Escribiendo Código Flexible en Oracle Forms

Objetivos:



 Describir un Código Flexible.
 Indicar las ventajas de usar variables de sistema.
 Identificar los built-ins útiles en la codificación flexible.
 Escribir código dinámico mediante la referencia a objetos:
--Por ID interno.
--Indirectamente.
_________________________________________________________________________
¿Qué es el Código Flexible?
Código Flexible: es todo código que puede ser reutilizado en distintos escenarios. El código flexible a menudo es un código genérico que puede usar en cualquier módulo Form en una aplicación. Por lo general, incluye el uso de variables del sistema en lugar de nombres de objetos codificados.

¿Por qué escribir código flexible?
Escribir código flexible te brinda las siguientes ventajas:
 Es reutilizable.
 Es genérico.
 Evita nombres de objetos codificados.
 Es más fácil de mantener.
 Aumenta la productividad.
______________________________________________________________________________________
Uso de  Variables de Sistema para el Contexto Actual.
Variables del sistema para localizar el foco de entrada actual:
Variable de Sistema
Funcción
CURSOR_BLOCK
Contiene el Bloque que tiene el foco de entrada.
CURSOR_RECORD
Contiene el registro que tiene el foco de entrada.
CURSOR_ITEM
Contiene el Item y Bloque que tiene el foco de entrada.
CURSOR_VALUE
Contiene el valor del Item que tiene el foco de entrada.
Ejemplo:
IF :SYSTEM.CURSOR_BLOCK = 'ORDERS' THEN
GO_BLOCK('ORDER_ITEMS');
ELSIF :SYSTEM.CURSOR_BLOCK = 'ORDER_ITEMS' THEN
  GO_BLOCK('INVENTORIES');
ELSIF :SYSTEM.CURSOR_BLOCK = 'INVENTORIES' THEN
  GO_BLOCK('ORDERS');
END IF;
/*El ejemplo anterior muestra un código que podría colocarse en un Trigger When-Button-Pressed para permitir a los usuarios navegar a otro bloque en el Form. Verifica el nombre del bloque actual, luego navega a otro en función del resultado.*/

Nota: asegúrese de establecer la propiedad Mouse Navigate del botón en No; de lo contrario, SYSTEM.CURSOR_BLOCK siempre será el bloque en el que se encuentra el botón.

Variables del sistema para localizar el foco del Trigger.
System Variable
Function
TRIGGER_BLOCK
El bloque en el que estaba el foco de entrada cuando el Trigger se disparó inicialmente.
TRIGGER_RECORD
El número del registro que Forms está procesando.
TRIGGER_ITEM
El bloque y item en el que estaba el foco de entrada cuando el Trigger se disparó inicialmente.
El Uso de las Variables de Enfoque del Trigger.
Las variables para ubicar el foco del trigger son útiles para navegar de regreso al destino inicial una vez que se completa la ejecución del trigger . Por ejemplo, puede que durante la ejecución del trigger se requiere navegar a otros bloques, registros o items y así realizar acciones sobre ellos, y que después de la ejecución de dicho proceso se desee que el cursor retorne en la misma instancia inicial. Debido a que la navegación en el trigger se produce detrás de escenas, el usuario ni siquiera se dará cuenta.
Nota: la mejor manera de aprender sobre las variables del sistema es observar sus valores cuando se está ejecutando un Form. Puede examinar las variables del sistema utilizando el Depurador (Debugger).

Variables de sistema para determinar el estado actual del Form.
También puede usar las siguientes variables de sistema para realizar acciones de acuerdo al valor que estas contengan:
SYSTEM.RECORD_STATUS
SYSTEM.BLOCK_STATUS

SYSTEM.FORM_STATUS
Ejemplo:
BEGIN
ENTER;
IF :SYSTEM.BLOCK_STATUS = 'CHANGED' THEN
COMMIT_FORM;
END IF;
CLEAR_BLOCK;
END;
/*En el ejemplo anterior se valida si existen modificaciones en el bloque actual, si las hay se procede a guardar dichas modificaciones (COMMIT_FORM), posteriormente se procede a limpiar dicho bloque (CLEAR_BLOCK).*/
_________________________________________________________________________
El Uso Built-ins para codificación flexible.
Algunos de los Built-ins de Forms Builder proporcionan el mismo tipo de información que proporcionan las variables de sistema.

Algunos Built-ins:
 GET_APPLICATION_PROPERTY
 GET_FORM_PROPERTY
 GET_BLOCK_PROPERTY
 GET_RELATION_PROPERTY
 GET_RECORD_PROPERTY
 GET_ITEM_PROPERTY
 GET_ITEM_INSTANCE_PROPERTY
 GET_LOV_PROPERTY
 GET_RADIO_BUTTON_PROPERTY
 GET_MENU_ITEM_PROPERTY
 GET_CANVAS_PROPERTY
 GET_TAB_PAGE_PROPERTY
 GET_VIEW_PROPERTY
 GET_WINDOW_PROPERTY
 SET_APPLICATION_PROPERTY
 SET_FORM_PROPERTY
 SET_BLOCK_PROPERTY
 SET_RELATION_PROPERTY
 SET_RECORD_PROPERTY
 SET_ITEM_PROPERTY
 SET_ITEM_INSTANCE_PROPERTY
 SET_LOV_PROPERTY
 SET_RADIO_BUTTON_PROPERTY
 SET_MENU_ITEM_PROPERTY
 SET_CANVAS_PROPERTY
 SET_TAB_PAGE_PROPERTY
 SET_VIEW_PROPERTY
 SET_WINDOW_PROPERTY

Algunos Explicaciones:
 GET_APPLICATION_PROPERTY  devuelve información sobre la aplicación Forms actual.

Ejemplo:
BEGIN
:GLOBAL.username := GET_APPLICATION_PROPERTY(USERNAME);
:GLOBAL.o_sys := GET_APPLICATION_PROPERTY(OPERATING_SYSTEM);
END;
/*El ejemplo anterior captura el nombre de usuario y la información del sistema operativo.*/

Nota: GET_APPLICATION_PROPERTY devuelve información sobre la aplicación Forms que se ejecuta en el nivel medio (Servidor Forms). Si necesita información sobre la máquina cliente, puede usar los JavaBean.

 GET_BLOCK_PROPERTY devuelve información sobre un bloque específico.

Ejemplo: 
...GET_BLOCK_PROPERTY('blockname',top_record)...
/*Este ejemplo muestra como determinar el registro actual que está visible en la primera línea (superior) de un bloque.*/

 GET_ITEM_PROPERTY devuelve información sobre un item especificado.

Ejemplo: 
DECLARE cv_name varchar2(30);
BEGIN
cv_name := GET_ITEM_PROPERTY(:SYSTEM.CURSOR_ITEM,item_canvas);
...
/*Este ejemplo muestra como determinar el Canvas que contiene el Item donde radica el foco de entrada.*/


SET_ITEM_INSTANCE_PROPERTY modifica la instancia del item en cuestión cambiando la propiedad especificada.

Ejemplo: 
BEGIN
SET_ITEM_INSTANCE_PROPERTY(:SYSTEM.CURSOR_ITEM, VISUAL_ATTRIBUTE, CURRENT_RECORD, 'VA_CURR');
END;
/*El anterior ejemplo establece el atributo visual en VA_CURR para el registro actual del item en cuestión.*/

• SET_MENU_ITEM_PROPERTY modifica las propiedades dadas de un elemento de menú.

Ejemplo: 
SET_MENU_ITEM_PROPERTY ('FILE.SAVE', ENABLED, PROPERTY_TRUE);
/*El ejemplo muestra como habilitar el Item SAVE.*/
.
SET_TAB_PAGE_PROPERTY establece las propiedades de la página de pestañas de la página de lienzo de pestañas especificada.

Ejemplo:
DECLARE
tbpg_id  TAB_PAGE;
BEGIN
tbpg_id := FIND_TAB_PAGE('tab_page_1');
IF GET_TAB_PAGE_PROPERTY(tbpg_id, enabled) = 'FALSE' THEN
SET_TAB_PAGE_PROPERTY(tbpg_id, enabled,property_true);
END IF;
END;
/*El anterior ejemplo evalúa el estado de la pestaña 'tab_page_1' y en caso de estar inhabilitada procede a habilitarla.*/
_________________________________________________________________________
Haciendo Referencia a Objetos por el ID Interno.
Forms Builder asigna un ID a cada objeto creado. El ID de objeto es un valor interno que nunca se muestra. Puede obtener la ID de un objeto mediante el uso de los built-ins FIND_objectFIND_object requiere un nombre de objeto que recibe como parámetro.

Los valores de retorno de FIND_object (los ID de objeto) son de un tipo específico. Los tipos de ID de objeto están predefinidos en Forms Builder. Hay un tipo diferente para cada objeto.

¿Por Qué usar Identificadores de Objetos?
• Mejora del rendimiento (Forms busca el objeto una sola vez al llama al built-in FIND_object para obtener el ID. Cuando se refiere a un objeto por nombre en un trigger, Forms debe buscar la ID del objeto cada vez).
• Permite escribir un código más genérico y a la vez más dinámico.
• Permite probar si existe un objeto (usando la función ID_NULL y FIND_object).

Tipos de Objetos FIND_object: 
Object Class
Subprogram
Return Type
Alertas
FIND_ALERT
ALERT
Bloques
FIND_BLOCK
BLOCK
Canvas
FIND_CANVAS
CANVAS
Editores
FIND_EDITOR
EDITOR
Forms
FIND_FORM
FORMMODULE
Items
FIND_ITEM
ITEM
LOVs
FIND_LOV
LOV
Relaciones
FIND_RELATION
RELATION
Vistas
FIND_VIEW
VIEWPORT
Windows
FIND_WINDOW
WINDOW
Las Variables para Identificadores de Objetos.
Para usar un ID de objeto, primero debe asignarlo a una variable. La variable debe ser declarada del mismo tipo que la ID del objeto.

El siguiente ejemplo usa el buil-int FIND_ITEM para asignar el ID del item que tiene el foco de entrada a la variable id_var.
DECLARE 
 
id_var item;0
BEGIN
 
id_var := FIND_ITEM(:SYSTEM.CURSOR_ITEM);
. . .
END;

Una vez que asigna un ID de objeto a una variable en un trigger o unidad de programa PL/SQL, puede usar esa variable para hacer referencia al objeto, en lugar de referirse al objeto por su nombre.

Los ejemplos siguientes muestran que puede pasar tanto el nombre de item como su ID al built-in  SET_ITEM_PROPERTY. Las siguientes llamadas son lógicamente equivalentes:
SET_ITEM_PROPERTY('ORDERS.order_id',position,50,35);

SET_ITEM_PROPERTY(id_var,position,50,35);
Puede usar identificadores de objetos o nombres de objetos en la misma lista de argumentos, siempre que cada argumento individual se refiera a un objeto distinto.

Sin embargo, no puede usar un ID de objeto y un nombre de objeto para formar un nombre compuesto (nombre_de_bloque.nombre_de_elemento). La siguiente llamada es ilegal:
GO_ITEM(block_id.'item_name');
Nota: Es recomendable usar los built-ins FIND_object solo cuando se refiera a un objeto más de una vez en el mismo Trigger o unidad de programa PL/SQL.
_________________________________________________________________________
ID de Objetos fuera del Bloque PL/SQL Inicial.
Hemos visto como hacer referencia a los ID de objeto dentro de un trigger o unidad de programa mediante variables PL/SQL. Puede hacer referencia a estas variables PL/SQL solo en el bloque PL/SQL actual; sin embargo, puede aumentar el alcance de un ID de objeto.

Para hacer referencia a una ID de objeto fuera del bloque PL/SQL inicial, debe convertir el ID a un formato numérico utilizando una extensión .id para su variable PL/SQL declarada, y luego asignarla a una variable global.

Ejemplo:
En el siguiente ejemplo se asigna la ID del objeto a una variable local de PL/SQL (item_var) inicialmente, luego a una variable global (global.item):
DECLARE
item_var item;
BEGIN
item_var := FIND_ITEM(:SYSTEM.CURSOR_ITEM);
:GLOBAL.item := item_var.id;
END;

Ahora puede pasar la variable global dentro de la aplicación. Para poder reutilizar la ID del objeto, debe volver a convertirla a su tipo de datos original.

Ejemplo:
El siguiente ejemplo muestra la conversión de la variable global a su tipo original de datos variables PL/SQL:
DECLARE
item_var item;
BEGIN
item_var.id := TO_NUMBER(:GLOBAL.item);
GO_ITEM(item_var);
END;
_________________________________________________________________________
Haciendo Referencia a Items Indirectamente.
Al hacer referencia indirectamente a los item, puede escribir un código más genérico y reutilizable. Usando variables en lugar de nombres de item reales, puede escribir una unidad de programa PL/SQL para usar cualquier item cuyo nombre esté asignado a la variable indicada.

Puede hacer referencia indirecta a los item con los built-ins NAME_IN y COPY.

Nota: utilice referencias indirectas cuando cree procedimientos y funciones en un módulo de librería, ya que las referencias directas no se pueden resolver.

Usando el Built-in NAME_IN.
La función NAME_IN devuelve el valor contenido de la variable indicada. Las siguientes sentencias son equivalentes. La primera usa una referencia directa a customer.name, mientras que la segunda usa una referencia indirecta:
IF :CUSTOMERS.cust_last_name = 'Welles'...
En una librería, puede evitar esta referencia directa al usar:
IF NAME_IN('CUSTOMERS.cust_last_name') = 'Welles'...
El valor de retorno de NAME_IN siempre es una cadena de caracteres. Para usar NAME_IN para un item tipo fecha o numérico, convierta la cadena al tipo de datos deseado con la función de conversión adecuada. Por ejemplo:
date_var := TO_DATE(NAME_IN('ORDERS.order_date'));

Usando el Built-in COPY.
El built-in COPY  asigna un valor indicado a una variable o item especifico. A diferencia de la sentencia de asignación estándar de PL/SQL, usar COPY le permite hacer referencia indirectamente al item cuyo valor se está configurando.
Ejemplo:
COPY('Welles','CUSTOMERS.cust_last_name');

Usando NAME_IN y COPY.
Utilice los built-ins COPY y NAME_IN para asignar indirectamente un valor a un item cuyo nombre se almacena en una variable global.
Ejemplo:
COPY('Welles',NAME_IN('global.customer_name_item'));
_________________________________________________________________________
Resumen.
En esta publicación, debes haber aprendido que:
 El código flexible es un código genérico reutilizable que puede usar en cualquier módulo Forms de una aplicación.
 Con las variables del sistema, puede:
---Realizar acciones condicionalmente en función de la ubicación actual (SYSTEM.CURSOR_ [RECORD | ITEM | BLOCK]).
---Utilizar el valor de un item sin conocer su nombre (SYSTEM.CURSOR_VALUE).
---Navegar a la ubicación inicial después de que se complete la ejecución de un trigger: (SYSTEM.TRIGGER_ [RECORD ITEM BLOCK]).
---Realizar acciones condicionalmente en función del estado del COMMITSYSTEM. [RECORD BLOCK| FORM] _STATUS.
 Los built-ins [GET | SET] _ <object> _PROPERTY son útil en la codificación flexible.
 El código que hace referencia a los objetos es más eficiente y genérico:
---Por ID interno: use FIND_ <object> built-ins.
---Indirectamente: usando lost built-ins COPY y NAME_IN incorporados.
_________________________________________________________________________
Fuente: Oracle Forms Developer 10g: Build Internet Applications.