-
El término "SQL dinámico" se refiere a los comandos SQL
que se interpretan en tiempo de ejecución.
En esta situación, las sentencias SQL se cargan en una variable del host,
luego se realiza un llamado PREPARE
para decirle al procesador SQL
que compile la sentencia en un comando ejecutable.
-
La siguiente aplicación obtiene los campos nombre y edad
de la tabla estudiantes
según el nombre indicado en la variable sqlnombre.
-
Definimos las variables:
sqlnombre que contiene el nombre a buscar en la tabla,
sqlcommand el cual es la sentencia dinámica a ejecutar,
que contiene el carácter que sirve para indicar
la búsqueda de un campo string,
datastructure contiene la estructura de datos
donde se almacenará el retorno de la sentencia.
sqlselect.rpgle
D sqlnombre S 20A INZ('fsg')
D sqlcommand S 256A INZ('SELECT nombre, edad +
D FROM fluidattacks/ESTUDIANTES +
D WHERE nombre = ')
D VARYING
D quote S 1A INZ('''')
D datastructure DS
D nombre 30A VARYING
D edad 10S 0
-
Asignamos el nombre a la sentencia dinámica:
SELECT nombre, edad FROM estudiantes WHERE nombre = '$nombre' ,
como se muestra a continuación:
C EVAL sqlcommand = sqlcommand +
C quote + sqlnombre + quote
-
Se definen las configuraciones para almacenar la información
y cerrar los recursos:
C/exec sql
C+ SET OPTION
C+ COMMIT = *NONE,
C+ CLOSQLCSR = *ENDMOD
C/end-exec
-
Preparamos la sentencia SQL que proviene de sqlcommand:
C/exec sql
C+ PREPARE SQLSTATEMENT FROM :sqlcommand
C/end-exec
-
Definimos el cursos CURSORSQL,
con el cual podremos recorrer las filas retornadas por la sentencia:
C/exec sql
C+ DECLARE CURSORSQL CURSOR FOR SQLSTATEMENT
C/end-exec
-
Abrimos el cursor:
C/exec sql
C+ OPEN CURSORSQL
C/end-exec
-
Iniciamos la ejecución de la sentencia:
C/exec sql
C+ FETCH NEXT FROM CURSORSQL INTO :datastructure
C/end-exec
-
Iniciamos el ciclo que recorre cada fila
solo si el estado de la sentencia es igual a 00000
(La sentencia fue exitosa y no hubo advertencias),
el estado 02000 es usado cuando no se han encontrado filas.
Imprimimos la estructura:
C DOW SQLSTT = '00000'
C datastructure DSPLY
-
Se obtiene la siguiente fila y la insertamos en la estructura de datos:
C/exec sql
C+ FETCH NEXT FROM CURSORSQL INTO :datastructure
C/end-exec
-
Finalizamos el ciclo y el programa
-
Este tipo de instrucciones son de gran flexibilidad
ya que se puede contener comandos SQL insertados
y cargados por diferentes medios.
Sin embargo, esta flexibilidad puede llevar a problemas de seguridad.
En el ejemplo anterior si la variable sqlnombre contiene datos válidos,
por ejemplo, el nombre fsg (INZ('fsg')),
la sentencia se vería de la siguiente manera:
SELECT nombre, edad FROM estudiantes WHERE nombre = 'fsg'
-
Sin embargo, si la variable contiene otro tipo de información
por ejemplo fsg' OR 1=1-- (INZ('fsg'' OR 1=1--')),
la sentencia generada es:
SELECT nombre, edad FROM estudiantes WHERE nombre = 'fsg' OR 1=1--'
-
En este caso, se recuperan todos los registros,
ya que se ha escrito una tautología.
-
Para evitar este comportamiento es deseable evitar el uso de SQL dinámico.
Si de todas formas es necesario su uso,
se deben utilizar sentencias parametrizadas para evitar la inyección.
-
Se define la nueva sentencia, el carácter ?
es reemplazado al momento de abrir el cursor:
D sqlcommand S 256A INZ('SELECT nombre, edad +
D FROM fluidattacks/ESTUDIANTES +
D WHERE nombre = ? ')
D VARYING
-
Se eliminan las líneas que contienen el siguiente código:
D quote S 1A INZ('''')
C EVAL sqlcommand = sqlcommand +
C quote + sqlnombre + quote
-
Y se modifica la línea que contiene la instrucción
para abrir el cursor por:
C/exec sql
C+ OPEN CURSORSQL USING :sqlnombre
C/end-exec
-
Cada carácter ? en la sentencia
es reemplazado por la variable definida en USING.
-
Si se usa más de una variable, por ejemplo para la sentencia
SELECT nombre, edad FROM estudiantes WHERE nombre = '$nombre' AND edad = '$edad'
el código correspondiente sería:
D sqlcommand S 256A INZ('SELECT nombre, edad +
D FROM fluidattacks/ESTUDIANTES +
D WHERE nombre = ? AND edad = ? ')
D VARYING
-
Y en el cursor:
C/exec sql
C+ OPEN CURSORSQL USING :sqlnombre, :sqledad
C/end-exec