Está en la página 1de 16

Información del entorno IBM i, AS/400, AIX, Windows, Linux y Unix

Año 35 - Noviembre 2021 Nº 343 Precio: 7 Euros

COLABORACIONES

El otoño trae nuevas características de RPG


Cuando me senté por primera vez a referencia a una de las mejoras de las agregadas este año, tanto en los anuncios
escribir este informe, mi enfoque estaba en mejoras de primavera, ¡rápidamente de abril como en septiembre. Será en dos
presentarles las nuevas características de descubrí que nunca las había escrito ni partes. Esta primera parte cubrirá las
RPG que se agregaron junto con la última publicado! características orientadas a la matriz.
actualización tecnológica de septiembre ¡Oooopppps! Como resultado, este
(TR). Sin embargo, cuando traté de hacer artículo cubrirá todas las mejoras de RPG Sigue en página 6

Criterios alternativos de selección SUMARIO


de filas de SQL
Colaboración
Después de escribir el artículo Guru: Alternate SQL Row-Selection Criteria Revisited,
me tomé en serio el uso de SQL dinámico en mis programas RPG en lugar de habilitar y Criterios alternativos de selección
deshabilitar expresiones lógicas en la cláusula WHERE. Estoy reviviendo los días en que de filas de SQL revisitados, revisitados 2
OPNQRYF era mi mejor amigo, tratando de hacer que los apóstrofos vayan bien con la
concatenación de cadenas. Sigue en página 2 El otoño trae nuevas características
de RPG (¡y también lo hizo
la primavera!) 6
¿Qué es el encarpetado constante
¿Qué es el encarpetado constante
y porqué? y por qué debería preocuparme
El encarpetado constante es una técnica de optimización del compilador, mediante la por ello?
cual el compilador reemplaza los cálculos que involucran constantes con los valores de
resultado cuando es posible. El plegado constante es común en los compiladores RPG salida rápida y práctica 13
modernos, y de acuerdo con la referencia RPG, el compilador RPG utiliza esta técnica.
(Consulte la documentación de las funciones %div y %rem, por ejemplo.)
Pero Ud. y yo no escribimos compiladores. Escribimos aplicaciones de negocio. ¿Por
qué entonces deberíamos preocuparnos por el encarpetado constante? Esa es una
pregunta que vale la pena reflexionar. Sigue en página 10

RPG salida rápida y práctica


¿Es una de esas personas que prosperan en la complejidad? ¿Te encanta la burocracia
y el papeleo? Si es así, estás leyendo el boletín equivocado. Me gusta que todo sea lo
más sencillo posible. Hoy tengo una rutina simple que me ahorra tiempo. Espero que os
sea de utilidad.
Mi pequeña rutina me permite imprimir cualquier cosa con el mínimo esfuerzo.
Puedo imprimir variables de caracteres, variables numéricas, estructuras de datos, los
resultados de expresiones y cualquier otra cosa que se pueda asignar a una variable de
caracteres. Lo desarrollé porque necesitaba una forma rápida de agregar salida a los
programas RPG sin el esfuerzo de escribir especificaciones DDS y O.
Sigue en página 13
COLABORACIONES

Criterios alternativos de
selección de filas de SQL
revisitados, revisitados
Después de escribir el artículo Guru: Alternate SQL select;
Row-Selection Criteria Revisited, me tomé en serio el uso when SelectByClass = '1';
de SQL dinámico en mis programas RPG en lugar de exec sql open Inp using :iClass;
habilitar y deshabilitar expresiones lógicas en la cláusula when SelectByZip = '1';
WHERE. Estoy reviviendo los días en que OPNQRYF era exec sql open Inp using :iFromZip,
mi mejor amigo, tratando de hacer que los apóstrofos :iThruZip;
vayan bien con la concatenación de cadenas. other;
exec sql open Inp;
En general, estoy contento con el cambio. Cuando he endsl;
convertido la versión cargada de conmutadores a SQL
dinámico, he visto una mejora notable del rendimiento en Tuve que usar múltiples instrucciones abiertas para
algunos casos, y al menos un ligero rendimiento en la acomodar diferentes de marcadores de parámetros, los
mayoría de los casos. En algunos casos, no hay ninguna signos de interrogación que se usan en SQL dinámico
diferencia, aunque Visual Explain muestra menos pasos como marcadores de posición para los valores de los
para recuperar los datos. parámetros. Cuantas más opciones le des al usuario, más
declaraciones abiertas terminarás teniendo que usar.
Una cosa es una molestia especialmente aburrida, a
saber, tener que codificar múltiples instrucciones de Sin embargo, IBM ideó cuidadosamente una forma de
cursor OPEN para acomodar múltiples listas de usar una instrucción abierta independientemente del
parámetros. Si miras el artículo antes mencionado, verás número de marcadores de parámetros en una cadena de
esto en el último fragmento de código: consulta. Es fácil y me gusta mucho.

ATTITUDES Nº 343 2 Noviembre 2021


COLABORACIONES

Una sola instrucción OPEN toma el lugar de las tres En este ejemplo, he definido dos variables indicadoras:
declaraciones OPEN que se muestran arriba. una para la clase y otra para los dos campos de código
postal.
exec sql open Inp using subset
:iClass :Indic_Class, Cada variable indicadora debe establecerse en uno de
:iFromZip :Indic_Zip, dos valores. Cero significa que la variable debe ser
:iThruZip :Indic_Zip; utilizada y (-7) que la variable debe ser ignorada. Definí
dos constantes numéricas en un intento de hacer que el
Observe la palabra SUBSET después de USING. Esto código sea más comprensible.
le indica a SQL que puede o no usar todos los parámetros.
De hecho, puede no usar ninguno de ellos en absoluto. En La otra cosa que debe hacer es asegurarse de que las
este caso, tengo tres marcadores de parámetros potenciales variables indicadoras sean consistentes con la cadena de
en la consulta para representar la clase, desde el código consulta.
postal y hasta el código postal.
Stmt = 'select cusnbr, cusname,
Después de cada parámetro, coloqué una variable cuszip, cusclass from customers';
indicadora para decirle a SQL si ese parámetro se está
utilizando o no. Una variable indicadora es un valor select;
entero de cinco dígitos. when iClass <> *blanks;
Stmt += ' where cusclass = ?';
dcl-s Indic_Zip int(5) Indic_Class = Assigned;
inz(Unassigned); when iFromZip <> *blanks;
dcl-s Indic_Class int(5) Stmt += ' where cuszip between ?
inz(Unassigned); and ?';
Indic_Zip = Assigned;
dcl-c Assigned const(*zero); endsl;
dcl-c Unassigned const(-7);
En este ejemplo, permito que el usuario recupere todas

ATTITUDES Nº 343 3 Noviembre 2021


COLABORACIONES

las filas, solo las filas de una determinada clase o todas las endif;
filas dentro de un rango de códigos postales, if iThruZip <> *blanks
and iThruZip <> iFromZip;
Por supuesto, no hay ninguna razón por la que no Where += (And + ' (cuszip
pueda permitir que el usuario seleccione tanto la clase between ? and ?)');
como el rango de códigos postales. Indic_Thru_Zip = Assigned;
else;
dcl-s Indic_From_Zip int(5) Where += (And + ' (cuszip =
inz(Unassigned); ?)');
dcl-s Indic_Thru_Zip int(5) endif;
inz(Unassigned); endif;
dcl-s Indic_Class int(5)
inz(Unassigned); if Where <> *blanks;
Stmt += ' Where ' + Where;
dcl-c Assigned const(*zero); endif;
dcl-c Unassigned const(-7);

dcl-s Stmt varchar( exec sql open Inp using subset


512); :iClass :Indic_Class,
dcl-s Where varchar( :iFromZip
128); :Indic_From_Zip,
dcl-s And char ( :iThruZip
5); :Indic_Thru_Zip;

Stmt = 'select cusnbr, cusname, Podría evitar la molestia de múltiples aperturas


cuszip, cusclass from customers'; concatenando los valores en las cadenas de comandos, lo
que eliminaría la necesidad de marcadores de parámetros.
if iClass <> *blanks; La probabilidad de que uno de mis programas sea dirigido
Where = ' (cusclass = ?) '; para la inyección SQL es escasa. Por otra parte, hay una
Indic_Class = Assigned; gran cantidad de código antiguo que todavía se está
endif; ejecutando y no muestra signos de desaparecer pronto.

if iFromZip <> *blanks; Además, perdería la ventaja de preparar una declaración


Indic_From_Zip = Assigned; una vez, y luego abrirla con diferentes valores según sea
if Where <> *blanks; necesario.
And = ' and ';

ATTITUDES Nº 343 4 Noviembre 2021


COLABORACIONES

El otoño trae nuevas características


de RPG (¡y también lo hizo la primavera!)
Cuando me senté por primera vez a escribir este MEJORAS EN EL ARREGLO DE DISCOS
informe, mi enfoque estaba en presentarles las nuevas Comencemos con %SPLIT, que fue parte de las
características de RPG que se agregaron junto con la última adiciones de primavera. A primera vista, puede preguntarse
actualización tecnológica de septiembre (TR). Sin embargo, por qué incluyo esto como una función de matriz. Lo
cuando traté de hacer referencia a una de las mejoras de las cuento porque produce una matriz a partir de la entrada de
mejoras de primavera, ¡rápidamente descubrí que nunca las texto. Por ejemplo, si tengo una variable textString que
había escrito ni publicado! contiene una oración simple que quiero dividir en palabras
individuales, puedo codificar esto:
¡Oooopppps! Como resultado, este artículo cubrirá
todas las mejoras de RPG agregadas este año, tanto en los dcl-s textString varchar(50)
anuncios de abril como en septiembre. Será en dos partes. inz('The quick brown fox jumps over
Esta primera parte cubrirá las características orientadas a la the lazy dog');
matriz. dcl-s words varchar(20) dim(*Auto:
20);
La gama de opciones de procesamiento de matrices de
RPG se ha expandido significativamente a lo largo de los words = %Split( textString );
años y la mayoría de nosotros realmente no las usamos lo
suficiente. La segunda parte cubrirá las otras características El BIF "divide" la entrada en subcadenas utilizando el
nuevas en las mejoras de este año, que incluyen otros separador predeterminado de un espacio. Como resultado,
nuevos BIF y una nueva adición útil a las instalaciones de el elemento 1 de las palabras de la matriz ahora contiene
depuración de RPG. "El", el elemento 2 "rápido", y así sucesivamente.

ATTITUDES Nº 343 6 Noviembre 2021


COLABORACIONES

Si lo que necesita es dividir una cadena CSV, puede que estamos hablando, se puede usar en cualquier lugar
hacerlo con %SPLIT utilizando el segundo parámetro donde se pueda usar una variable del mismo tipo.
opcional que puede contener uno o más caracteres Entonces, por ejemplo, podría simplemente usar %SPLIT
separadores. De esta manera: como argumento para el código operacional FOR-EACH
en lugar de tener que definir una matriz dentro del
words = %Split( csvString: ',' ); // programa.
Just split at commas f
words = %Split( textString: '.,!? ' or-each word in %Split( textString
); // Split on any major punctuation );
// Process the word ...
Una advertencia aquí. %SPLIT omite los separadores EndFor;
consecutivos. La mayoría de las veces eso es algo bueno,
pero puede presentar un problema en algunas %MINARR Y %MAXARR
circunstancias. Por ejemplo, si estuviera procesando un Cuando %MIN y %MAX se introdujeron por primera
archivo CSV que contiene la cadena ABC,,XYZ usando vez, me alegré de verlos, pero pensé en ese momento que
CPYFRMIMPF, resultaría en tres campos. "ABC", en también sería bueno tener una variante que funcionara en
blanco, y "XYZ". Sin embargo, %SPLIT solo produciría matrices en lugar de una lista de valores. El problema era
dos elementos de matriz porque se omitiría la segunda que en su encarnación original estos BIFs devolvían el
coma. Por lo tanto, en estas circunstancias se puede valor real y cuando se trataba de una matriz uno realmente
requerir algún procesamiento previo de la entrada. querría el índice del valor. Si no está familiarizado con
estos BIF, puede encontrar los conceptos básicos
El segundo ejemplo muestra cómo podría dividir una documentación de IBM.
oración de tal manera que se ignoren la mayoría de los
signos de puntuación. Observe que incluí un espacio en %MINARR y %MAXARR llenan ese vacío.
blanco al final de la cadena para que los espacios contaran %MINARR devuelve el índice del valor más bajo de la
como separador. Si también quisiera ignorar las comillas, matriz y %MAXARR devuelve el índice del más alto.
etc., simplemente las agregaría a la lista de separadores. Estos BIFs se pueden aplicar a una matriz convencional o
a una matriz DS.
No olvide que, debido a que se trata de un BIF de l0
Esta última capacidad es particularmente útil.

ATTITUDES Nº 342 7 Octubre 2021


COLABORACIONES

Supongamos que tiene un número de vendedor y un Esta compatibilidad se proporciona mediante


volumen de ventas en una matriz DS. Para identificar el %FIELDS para enumerar los campos clave que se
peor desempeño, puede aplicar %MINARR al valor de utilizarán en su secuencia de prioridades. En el siguiente
ventas y usar el índice resultante para acceder al número ejemplo estoy ordenando la lista de animales por peso en
y nombre del vendedor. Así: tipo (es decir, gato, perro, etc.). En otras palabras, todos
los gatos se agruparán y secuenciarán en orden de su
dcl-ds salesData qualified peso.
dim(*Auto : 100); // Maximum 100
salesmen dcl-ds myAnimals qualified dim(99);
salesRep# char(5); ID int(10);
name varchar(30); type varchar(16);
salesToDate packed(9:2); name varchar(16);
end-ds; weight packed(7:2);
end-ds;
index = %minArr( salesData(*). ...
salesToDate ); SortA myAnimals %Fields( type:
Dsply ( 'Rep #' + salesData(index). weight );
salesRep# + ' Name: '
+ salesData(index).name ); FINALIZANDO
Como puede ver, hay algunas características
Puede limitar el alcance de la búsqueda especificando agradables aquí que pueden hacer que nuestras vidas de
el número de elemento más alto para buscar a través del programación sean un poco más fáciles. ¡El RPG sigue
tercer parámetro. El valor predeterminado es buscar en creciendo! Antes de probar estas nuevas funciones, no
toda la matriz. Como se muestra en estos ejemplos, en olvide verificar que tenga instalados los PTF necesarios.
estos días uso matrices automáticas para la mayor parte Puede encontrar los detalles aquí para los TIR de
de mi trabajo, por lo que nunca tengo que preocuparme primavera y otoño: https://ibm.biz/rpg_cafe
por limitar el alcance del BIF, ya que RPG lo hace
automáticamente por mí. Si aún no está en V7.4, la única
forma de limitar el alcance de la búsqueda es especificando
el número de elemento más alto para buscar en el tercer
parámetro.

En el ejemplo siguiente, asumo que la variable


activeItems contiene el número real de elementos de la
matriz invoiceList y estoy intentando localizar la factura
con el valor más alto. En caso de que se pregunte sobre el
segundo parámetro, se puede usar para especificar el
índice inicial si lo desea. Como era de esperar, el valor
predeterminado es 1 cuando solo se proporciona un solo
parámetro.

index = %maxArr( invoiceList(*).


invoiceTotal : 1 : activeItems );
MEJORA DE SORTA
Hace algún tiempo, SORTA se mejoró para permitir la
clasificación de matrices DS. Por útil que fuera, se
limitaba a ordenar en un solo campo clave. Esto significaba
que cada vez que necesitaba un orden de múltiples claves
todavía tenía que recurrir al uso de la función C qsort().
No es un problema para mí, ya que lo he estado usando
durante años, sino un obstáculo potencial para aquellos
que vienen después de mí y necesitan mantener mi
código. Con el nuevo soporte, hay muy pocas ocasiones
en las que necesite usar qsort() ya que RPG ahora admite
directamente la clasificación de múltiples claves.

ATTITUDES Nº 343 8 Noviembre 2021


COLABORACIONES

¿Qué es el encarpetado constante


y por qué debería preocuparme por ello?
El encarpetado constante es una técnica de en un campo guardado, tal vez para verificar si hay una
optimización del compilador, mediante la cual el interrupción de control. ¿Qué podría salir mal con este
compilador reemplaza los cálculos que involucran código? Simplemente esto: si alguien aumenta el tamaño
constantes con los valores de resultado cuando es posible. del campo de número de cuenta del cliente, CUSTSV ya
El plegado constante es común en los compiladores no se establecerá en el valor adecuado. Lo que es peor, el
modernos, y de acuerdo con la referencia RPG, el error no aparecerá hasta que a un cliente se le asigne el
compilador RPG utiliza esta técnica. (Consulte la número de cuenta 100000 o superior, lo que puede ser
documentación de las funciones %div y %rem, por mucho tiempo después del cambio de programa.
ejemplo.)
Así que la maravillosa gente de IBM decidió dejarnos
Pero Ud. y yo no escribimos compiladores. Escribimos hacer esto en su lugar:
aplicaciones de negocio. ¿Por qué entonces deberíamos C *LIKE DEFN CUSTNR
preocuparnos por el encarpetado constante? Esa es una CUSTSV
pregunta que vale la pena reflexionar. C MOVE CUSTNR
Considere cómo solía tener que escribir RPG en la CUSTSV
Edad Media.
Se elimina una posible fuente de error. Estaba
C MOVE CUSTNR asombrado cuando IBM agregó el código de operación
CUSTSV 50 *LIKE DEFN al compilador RPG con el que estaba
trabajando en ese momento.
Aquí estoy copiando el número de cuenta del cliente

ATTITUDES Nº 343 10 Noviembre 2021


COLABORACIONES

Hoy en día hacemos lo mismo con las especificaciones el caso. En su lugar, tenemos que usar el método indirecto
de definición. para declarar dos constantes con nombre. Descubrí esto
de la manera difícil, a pesar de que este comportamiento
dcl-s Cust_Account_Save like(Cust_
está documentado en la referencia de RPG. Evidentemente
Account);
tengo cosas más importantes que hacer que leer manuales.
O al menos deberíamos. He visto mucho código
donde el programador podría haber usado la palabra clave Estas funciones también son útiles al cambiar el tipo
LIKE y no lo hizo, y no estoy hablando de código de datos. Por ejemplo, supongamos que quiero una
antiguo. variable varchar que contenga la misma cantidad de datos
que un campo de caracteres de longitud fija. No puedo
La palabra clave LIKE tiene un segundo argumento usar la palabra clave LIKE, pero puedo hacer esto:
que aumenta o disminuye el tamaño definido de una
variable. Por ejemplo: dcl-s Address char(20);
dcl-s AddrMod
dcl-s Limit packed (7: 2);
varchar(%size(Address));
dcl-s NewLimit like(Limit: +2);
NewLimit se define como de nueve dígitos de largo, Algunos compiladores son más generosos que el
dos de los cuales son posiciones decimales. compilador RPG cuando se trata del uso de constantes.
Supongamos que necesito que una variable sea el doble
IBM también nos proporciona tres funciones de larga que un campo de base de datos. Tal vez voy a
integradas que recuperan información sobre las almacenar la representación hexadecimal del campo en la
definiciones de datos: %SIZE, %LEN y %DECPOS. variable. Tal vez voy a reemplazar cada apóstrofo con dos
Estas maravillosas funciones abren aún más posibilidades apóstrofos. Algunos compiladores permitirían algo como
para que el compilador utilice el encarpetado constante. esto:
Al igual que la palabra clave LIKE, están infrautilizados
y no se aprecian. dcl-s Dirección char(20);
dcl-s AddrMod char(%len(Address)* 2);
Supongamos que necesito recuperar un número
decimal de una cadena de caracteres. La función %DEC No así el compilador RPG. En cambio, tienes que
lleva a cabo dicha tarea fácilmente, pero tiene que saber encontrar otra manera, como este método, que considero
cómo definir el valor resultante. Podría hacer esto: un truco.
dcl-s Limit packed (7: 2);
Limit = %dec (TextValue: 7: 2); dcl-s Address char(20);
dcl-ds AddrMod;
Pero este es mejor: *n like(Address);
dcl-s Limit packed (7: 2); *n like(Address);
end-ds AddrMod;
dcl-c Length const( %Len (Limit) )
dcl-c DecPos const( %DecPos (Limit) ); Cuanto más podamos usar la palabra clave LIKE y las
funciones integradas de definición de datos, más
Limit = %dec (TextValue: Length: DecPos); posibilidades tiene el compilador de usar el encarpetado
constante. Pero, ¿por qué deberíamos preocuparnos por
Si la definición de LIMIT cambia alguna vez, este ello? ¿Porque necesitamos código objeto optimizado? Esa
código toma el cambio con calma. es una buena razón, pero aquí hay una aún mejor: porque
el encarpetado constante hace que los programas sean
Es una peculiaridad, a mi modo de ver, que no menos propensos a romperse cuando los modificamos.
podamos colocar las llamadas a las funciones %LEN y Ud. y yo tenemos cosas más importantes que hacer que
%DECPOS directamente en la función %DEC, pero tal es buscar errores.

ATTITUDES Nº 343 11 Noviembre 2021


COLABORACIONES

RPG salida rápida y práctica


¿Es una de esas personas que prosperan en la Aquí está el código fuente.
complejidad? ¿Te encanta la burocracia y el papeleo? Si
es así, estás leyendo el boletín equivocado. Me gusta que Fqsysprt o f 132 printer
todo sea lo más sencillo posible. Hoy tengo una rutina usropn
simple que me ahorra tiempo. Espero que os sea de
utilidad. D writeln pr
D inString 132a
Mi pequeña rutina me permite imprimir cualquier const
cosa con el mínimo esfuerzo. Puedo imprimir variables de
caracteres, variables numéricas, estructuras de datos, los P writeln b
resultados de expresiones y cualquier otra cosa que se D pi
pueda asignar a una variable de caracteres. Lo desarrollé D inString 132a
porque necesitaba una forma rápida de agregar salida a const
los programas RPG sin el esfuerzo de escribir // locals
especificaciones DDS y O. D ReportLine ds 132
/free
Usé varios nombres para esta y otras rutinas similares, ReportLine = inString;
pero el nombre en el que finalmente me decidí es Writeln, write qsysprt ReportLine;
pronunciado "write line". Si sabes algo sobre Pascal (el /end-free
lenguaje de programación, no el científico), sabes que P e
tomé el nombre del procedimiento de salida de flujo
primario de Pascal. No hay mucho que hacer, ¿eh? Utilice cualquier
archivo de impresora descrito por el programa que desee.

ATTITUDES Nº 343 13 Noviembre 2021


COLABORACIONES

Utilicé QSYSPRT como ilustración. FmtStr(zSqlData.CompanyName) + Sep +


FmtStr(zSqlData.CompanyCityState) +
Writeln imprime cualquier cosa que le arrojes, dentro Sep +
de lo razonable. Estos son algunos ejemplos: FmtZip(zSqlData.CompanyZip));

1. Imprima una variable de caracteres. ¿POR QUÉ HA SIDO ÚTIL WRITELN?

if ErrorMessage <> *blanks; 1. Es rápido. No tengo que escribir especificaciones


writeln(ErrorMessage); DDS u O
endif;
2. Se descarta fácilmente. Podría usar writeln durante
2. Imprima carácter literal. el desarrollo o la depuración, luego eliminarlo del
programa o subordinarlo a una directiva de compilador
writeln('** End **'); deshabilitada antes de instalarlo en producción. Como
tengo tan poco tiempo invertido en las llamadas a mi
3. Imprima un carácter numérico. rutina, no siento que haya perdido el tiempo.

writeln('valuelen=' + %char(vallen)); 3. Se reemplaza fácilmente. A veces uso writeln


mientras desarrollo para concentrarme en la lógica de un
4. Imprima una expresión. programa. Cuando tengo la lógica funcionando de la
manera que quiero, diriendo mi atención al formato, lo
writeln(%trim(zSqlData.SerialNbr) que significa que mi DDS tiende a salir de la manera que
+ Sep + FmtDate(zSqlData.InstallDate) quiero sin mucha experimentación y revisión.
+ Sep +
%trimr(zSqlData.PartNbr) + Sep + 4. Es bueno para la salida de formato libre. Incluso he
FmtStr(zSqlData.PartDesc) + Sep + usado esta rutina para crear archivos CSV.
FmtDate(zSqlData.EntryDate)
+ Sep + Eso debería ser suficiente para ponerte en marcha, y
%trim(zSqlData.Model) + Sep + con suerte lo suficiente como para abrirte el apetito.

ATTITUDES Nº 32 15 Octubre 2021

También podría gustarte