Ejecutando programas de versiones anteriores: La versión 1.0,
release 4.0 puede ejecutar programas compilados de versiones anteriores
sin necesidad de recompilar los programas fuentes ".sct".
La única diferencia que puede encontrarse, en su caso, será la
referida a su comportamiento, y ello se deberá a la correción
de errores o inclusión de alguna característica nueva.
Compatibilidad hacia atrás: Un programa ".sct" compilado con la versión 1.0, release 4.0 sólo podrá ejecutarse con un Run-Time de versiones anteriores si dicho programa no incluye ninguna de las nuevas funciones añadidas al lenguaje, y que veremos más adelante. Además, no ejecutará correctamente ninguna instrucción "WINDOW ...", "TSQL ...", "PATH WALK ...", "TREE WALK ...", dado que la información que ahora genera el compilador para estas instrucciones es diferente, como se explica a continuación.
Las "Label" que aparecen en una WINDOW FROM SELECT o CURSOR serán sustituidas por los "alias" de las columnas, si los hubiere.
La función OUTPUT, la opción OUTPUT del MENU FORM y
la salida del documentador (tdocu), presentaban los caracteres semigráficos
dependiendo del tipo de terminal al sacar el contenido de una SCREEN.
Esto implicaba que, al ejecutar una de estas opciones o programas en
una pantalla con "set" alternativo para semigráficos
(p. ej.: "vt100"), estos caracteres eran sustituidos por
el carácter correspondiente para este "set". Esto
impedía obtener semigráficos usando este tipo de pantallas.
En la versión 1.0, release 4.0 se ha optado por homogeneizar
esta salida usando siempre el GCS#2 para los caracteres semigráficos.
Los valores asignados a cualquier campo de un FORM en un "query" serán usados para realizar la consulta (antes sólo los que se editaban).
La instrucción NEXT FIELD no funcionaba en algunos casos como:
Estos casos ya funcionan correctamente.
Ya no existe límite en el tamaño de una instrucción SQL ejecutada con TSQL FILE (antes el límite era de 5.120 bytes).
La función "msgtext(num)" admite ahora opcionalmente un segundo parámetro "msgtext(num [, valor])".
Ejemplo:
msgtext(150, "clientes")
− Para el mensaje 150 = "La tabla %s no existe".
− Devuelve: "La tabla clientes no existe".
Sólo se admite uno o ningún parámetro, y en el primer caso el mensaje debe contener un "%s".
Hasta ahora, una vez creada una Base de Datos con transacciones, la única
forma de inhabilitar el manejo de éstas era borrando la entrada "syslog" del
fichero "systables", cerrar la Base de Datos y abrirla de
nuevo.
Para facilitar esta tarea, la release 4.0 admite la sentencia sql:
START DATABASE ... WITH LOG IN"". Al especificar "" como "logfile",
el SQL entiende que se desea finalizar el tratamiento de transacciones.
En los programas de líneas se ha corregido la aparición de éstas en pantalla, mejorando su rapidez. Esto supone además que sólo se ejecutarán las cláusulas ‘AFTER/BEFORE DISPLAY OF lineas’ de las que vayan apareciendo nuevas.
Sólo en el caso del S.O. UNIX, se puede obtener información sobre el optimizador del SQL. Para ello se utiliza la variable de entorno "OUTOPT=filename". El SQL escribirá en el fichero "filename" para cada instruccion:
Sentence:
Frase SQL
Optimization: num Tables <− número de tablas implicadas
Table [tabname] num Ranges <− para la tabla "tabname" se
han
definido "num" rangos
Range [n] : key [start/length]
((s1/l1)(s2/l2)...)
número
de rango \——————−/
descripción
de la clave usada para este rango:
( ) indica
acceso por rowid.
− El orden en que se listan las tablas implicadas en la sentencia SQL (en el caso de usar más de una tabla) es el orden que el optimizador selecciona para acceder a ellas.
− El número de rangos indica el número de porciones de una tabla que son seleccionadas en la cláusula WHERE.
− Para cada rango se especificará, en su caso, la clave por la que se va a acceder a la tabla.
Se puede ejecutar una frase SELECT previamente preparada para comprobar si devuelve o no alguna fila chequeando las variables FOUND y AMBIGUOUS.
Ejemplo:
prepare stmt from "select
* from clientes where cliente = ?"
execute stmt using cli
if found = true then
.....
El error SQL: "El subquery devuelve más de un valor" no se toma ya en cuenta como error para el CTL. Este error se puede controlar con la variable AMBIGUOUS, tanto en la instrucción SELECT del CTL como al ejecutar la instrucción "execute frase_preparada".
Ejemplo:
select c1 from t1 where c1 = 1
{ si devuelve más de una fila }
variable ambiguous -> TRUE
variable found -> TRUE
{ si devuelve una fila }
variable ambiguous -> FALSE
variable found -> TRUE
{ si no devuelve ninguna fila }
variable ambiguous -> FALSE
variable found -> FALSE
NOTA: En versiones anteriores, y para este caso, la variable FOUND valía TRUE sólo si AMBIGUOUS era FALSE.
La cláusula CLEAN de una opción de menú sólo funcionaba para los menús definidos en un pulldown. Esto se ha corregido, de tal forma que al ejecutar una opción de un menú definido con CLEAN su funcionamiento sea:
Se ha modificado la instrucción "PUT EVERY ROW OF ..." cuando la salida es a pantalla, de modo que haga un PAUSE automático cada 24 líneas con el fin de facilitar la lectura del resultado de esta instrucción.
Ya es posible definir LOOKUPs en variables de líneas.
1. Funciones de manejo del directorio de trabajo:
getcwd(): Devuelve el directorio en curso.
chdir(expr): Cambia el directorio en curso, siendo "expr" el nombre del aquel que se desea establecer como nuevo directorio en curso.
2. Funciones de uso del teclado:
dokey(expr): Simula la pulsación de una tecla, cargándola en el buffer del teclado.
— Si "expr" es numérica:
>= 2000: "lastkey" correspondiente
a una acción.
1−256: Carácter ASCII a introducir.
— Si "expr" es char:
− Nombre de una acción ("fcancel", "fquit" ...).
− Si no es ninguna acción, se introduce carácter a carácter
todo el string.
Ejemplo:
call dokey("fleft")
call dokey(3) {‘control-c’}
call dokey(13) {‘RETURN’}
call dokey("freturn") {‘RETURN’}
call dokey("309-4589A") {‘un string’}
keypressed(): Devuelve TRUE si hay un carácter disponible en el buffer del teclado, FALSE en caso contrario. Una vez que devuelve TRUE, la tecla se puede "leer" con READ KEY, PAUSE, o bien será tratada por cualquier elemento del CTL.
Ejemplo:
{ Limpiar buffer de teclado }
while keypressed() = true
read key
3. Funciones de manejo del cursor:
cursorpos(on_off, line, col): Permite encender o apagar el cursor (si lo permite el terminal), así como situar éste en una posición determinada.
4. Funciones de manejo de querys en los Forms:
fquery(): Equivale a la instruccion QUERY, pero sólo devuelve las condiciones SQL que el usuario haya seleccionado.
Ejemplo:
let a = fquery()
valor de a -> "where cliente > 1"
fgetquery(where_clause [, collist]): Equivale a la instrucción GET QUERY WHERE...
Donde:
Devuelve: Número de filas seleccionadas.
Ejemplos:
let num = fgetquery("order
by cliente", "cliente")
let num = fgetquery("where cliente = 1")
let num = fgetquery("where cliente > 2 order by cliente", "cliente")
5. Funciones que devuelven valores internos al programa:
getctlvalue(expr): Devuelve el valor dependiendo del valor de "expr":
expr Devuelve
"version" Versión del ctl. P. ej.: "1.0.4.0".
"o.s." Sistema
operativo. P. ej.: "WINDOWS".
"porting" Indica variación dentro
de un mismo S.O.
"atcol" Columna en la
que arrancó el programa en curso.
"atrow" Fila en la que arrancó el
programa en curso.
"ctlname" Nombre del CTL en curso.
Ejemplo:
if getctlvalue("O.S")
= "MSDOS" then
....
6. Funciones de evaluación:
evalfun(funname [, arg1, .... argn]): Ejecuta la función de nombre "funname" y devuelve su resultado, si lo hubiere.
Donde:
funname: Expresión alfanumérica que indica el nombre de una función de usuario o interna.
Ejemplo:
evalfun("getctlvalue", "version")
equivale a ——> getctlvalue("version")
evalfun(funname(), parm1)
si funname devuelve "fun1"
equivale a ——> fun1(parm1)
evalcond(exp1, oper, exp2, exprtrue, exprfalse): Compara "exp1" con "exp2" según el operador "oper" y devuelve:
"oper" puede ser uno de los siguientes: "=", ">", "<", ">=", "<=", "!=", "<>", "like", "matches"
Ejemplo:
let a = (b, ">", c, "ddd", a)
es equivalente a:
if b > c then
let a = "ddd"
else
let a = a
evalnull(exp1, exprtrue, exprfalse): Devuelve:
7. Funciones de manejo de strings:
strrtrim(char): Devuelve "expr" eliminando blancos por la derecha.
strltrim(char): Devuelve "expr" eliminando blancos por la izquierda.
strtrim(expr): Realiza un "strltrim+strrtrim" y reduce cada grupo de blancos entre palabras a un solo blanco.
strlocate(where, what [, num]): Busca un string en otro. Donde:
Devuelve posición 1 a "n", o 0 si no encuentra.
Ejemplo:
let pos = strlocate("MultiBase TransTOOLs", "OO")
strcount(where, what): Cuenta el número de veces que "what" se encuentra en "where".
Devuelve el número de veces encontradas.
Ejemplo:
let num = strcount("A-3393457332", "33")
strreplace(where, charold, charnew [, num]): Sustituye un valor por otro dentro de un string, siendo "num" el número de sustituciones a efectuar (por defecto asume "todas") y devuelve el resultado de la sustitución siempre que la longitud de "charold" sea igual a la de "charnew", si no es así, devuelve un máximo de 512 caracteres. Si "charnew" es nulo indica que se desea eliminar "charold".
Ejemplo:
let var = strreplace("/disk1/demo", "k1", "k2", 1)
strgetword(where, charseparator [, num [, typesep]]). Devuelve la enésima palabra en un string. Donde:
Devuelve la palabra encontrada, o bien NULL cuando:
Ejemplo:
let a = strgetword ("/tmp ::/usr", ":", 2, false)
En "a" se cargará el valor "/usr".
strnumwords(where, charseparator[, typesep]): Cuenta las palabras en un string.
Devuelve número de palabras.
Ejemplo:
let oldpath = "/path1:/path2:/path3"
let num = strnumwords(oldpath, ":")
for i = num down to 1
let newpath = newpath & strgetword(oldpath, ":", i) & ":"
strrepeat(expr, num): Repite "expr" tantas veces como indique "num". La longitud máxima del resultado es de 512 caracteres.
Ejemplo:
let a = strrepeat("-.", 40)
NOTA: Todas devuelven NULL si hay un error en los parámetros, excepto "strgetword", que también puede significar vacío. Para ello debe comprobarse viendo el número real de palabras con "strnumwors()".
8. Funciones de manejo de ficheros:
nocomment(source, dest): Copia el contenido del fichero "source" al fichero "dest", eliminando las líneas de comentarios (las que comienzan con ‘#’) y devuelve:
Ejemplo:
nocomment("f1.err", "f1.sct")
testfile(file, attri): Comprueba si un fichero es de un determinado tipo o bien si tiene determinados permisos.
Donde:
Se puede especificar cualquier combinacion de éstos.
Devuelve:
Ejemplo:
if testfile("fichero", "rwf")
= true then
...
filesize(file): Devuelve el tamaño, en bytes, del fichero "file", o NULL si éste no existe o hay error de parámetros.
Ejemplo:
if filesize("fichero") > 0
then
....
9. Funciones de manejo de directorios:
opendir(path): Abre un directorio. Devuelve TRUE si el directorio existe y lo puede abrir; FALSE en caso contrario. No podrá existir más de un directorio abierto. En el caso de ejecutar dos opendir seguidas, la segunda ejecutará automáticamente un closedir del primero.
readdir(): Lee fichero a fichero el contenido de un directorio. Devuelve el siguiente fichero o subdirectorio del directorio abierto, o NULL en el caso de estar vacío o después del último.
closedir(): Cierra el directorio abierto.
Ejemplo:
if opendir(dir) = true then begin
forever begin
let fil = readdir()
if fil is null then break
...
...
end
closedir()
end
El entorno de la versión 1.0 release 4.0 de MultiBase para sistema operativo UNIX incorpora las siguientes características:
a) GENERADORES: Los nuevos generadores recogen información del diccionario, sobre todo de aquellos elementos de la integridad referencial (Primary y Foreign Keys). Por tanto, cuando deseemos generar el prototipo de la base de datos, convendría tener bien pensada la integridad entre todas las tablas.
En este nuevo entorno no existe persiana de generadores, sino que la generación se encuentra en el mismo nivel que la modificación, compilación, etc., de módulos.
Generador ENTRADA DE DATOS. El nuevo generador
de entrada de datos contempla la posibilidad de elegir cuáles
son las columnas de las tablas seleccionadas que se van a mantener,
así como su orden de petición y modificación de
su "label". La forma de moverse sobre la ventana de elección
de columnas, etiqueta y orden es mediante las flechas y el <Intro>.
Por defecto, se encuentran seleccionadas todas las columnas de las
tablas elegidas.
Asimismo, se genera un menú básico para el mantenimiento
de la tabla elegida (altas, bajas, modificaciones y consultas). Gracias
a la integridad referencial, contempla que, en caso de no existir referencia
en la tabla referenciada, nos permita dar de alta dicha fila principal
en otro programa de mantenimiento de la tabla referenciada (Primary
key). Por tanto, con esta generación se están enlazando
internamente todos los programas de mantenimiento gracias a la información
generada mediante la integridad referencial. Además, gracias
a la integridad referencial se genera la instrucción WINDOW
de selección en caso de que no tenga atributo LOOKUP en el diccionario.
Por último, además de generar este módulo donde
se incluye el objeto FORM, bien sea de entrada de datos o de líneas,
también se genera una librería, denominada MBLIB, en
la cual se incorporan ciertas funciones estándares que se usan
en todos los módulos de mantenimiento. Esta librería
es la que construye el generador de librerías que comentamos
más adelante.
Generador LÍNEAS. Las opciones de
los generadores de líneas y entrada de datos se encuentran en
el mismo nivel del pulldown, en el de manejo de módulos. La
petición de datos para la generación es similar a la
versión antigua, añadiendo la posibilidad de generar
respecto a columnas de enlace (no sólo entre índices).
Una vez realizado el enlace entre ambas tablas, el interface con el
usuario es como en el generador de Entrada de Datos.
En este tipo de programas se generan automáticamente los menús
que controlan el mantenimiento de cada una de las tablas a enlazar.
Generador de LIBRERÍAS. Mediante
la información correspondiente a la integridad referencial,
el contenido de la librería que se genera automáticamente
es todas las funciones que controlan la falta de integridad de datos
en un mantenimiento de una tabla, permitiendo dar de alta dicha fila
principal en la tabla referenciada (Primary key), mediante la llamada
a un módulo seleccionado por el usuario en las coordenadas especificadas.
Por cada Primary key en la base de datos genera dos funciones en este
módulo.
El control de falta de integridad de datos se realiza en la sección
EDITING del módulo principal después de cada campo con
clave foreign.
Asimismo, se generan otras funciones que son estándares:
mb_temp(pref): Devuelve el nombre de un fichero
compuesto por el PATH de la variable DBTEMP y el prefijo, que se pasa
como parámetro.
str_empty(str): Devuelve TRUE o FALSE si
la expresión pasada como parámetro está o no vacía
o es nula.
str_repeat(c,n): Devuelve la expresión
pasada como parámetro tantas veces repetidas como se indique
en el parámetro "n". Se entrega como ejemplo de una
función recursiva, pero ya está implementada en el CTL.
disp_msg(m): Muestra el mensaje contenido
en el parámetro "m" en una caja, pidiendo confirmación
para continuar el proceso.
col_label(t,c): Devuelve la etiqueta asignada
en diccionario a la columna "c" de la tabla "t".
En caso de no tener asignada ninguna etiqueta devuelve el nombre de
la columna "c".
Generador de Módulos VARIOS. El resultado
de este generador es un módulo que realiza un listado por pantalla
o impresora, según elija el operador. En ejecución, en
caso de elegir la opción de impresora, se formatea la página
de forma dinámica, pidiéndole al operador todos los datos
necesarios.
Simula un "query by form" mediante el objeto Frame y la operación
Query. El interface empleado para la generación de este tipo
de módulos es similar al utilizado por el generador de Entrada
de Datos, es decir, pudiendo elegir las columnas a intervenir en la
consulta, el orden de petición y modificar las etiquetas de
cada una de ellas. El movimiento sobre esta petición de datos
se ha comentado anteriormente en el Generador de Entrada de Datos.
b) MACRO-MOVIMIENTOS: En el nuevo entorno aparece una serie de teclas que provocan un macro-movimiento sobre los menús del pulldown. Por defecto, vienen fijados tres tipos de movimientos, teniendo los puntos de destino que se indican a continuación:
<Ctrl-k>: Elección de un módulo
de entrada de datos.
<Ctrl-l>: Creación, modificación de la estructura de una
tabla.
<Ctrl-p>: Editor de SQL.
Para cambiar estos tres movimientos por otros cualquiera debemos mantener un nuevo fichero que se encuentra en el directorio donde se haya instalado el MultiBase ($TRANSDIR/etc). El fichero se denomina "fastmove", cuyas características se comentan a continuación.
Fichero FASTMOVE. Se trata de un fichero ASCII con formato similar al generado por la instrucción UNLOAD. Este fichero consta de tres campos que pueden ser mantenidos por el administrador en caso de que le interesen otros movimientos:
1. Número: Nivel en el que nos encontramos dentro del pulldown.
0: Primer nivel (pulldown). Los siguientes
niveles son las llamadas a menús.
1: Segundo nivel.
2: Tercer nivel.
2. Acción o acciones a tomar para llegar a la opción deseada. En caso de especificar más de una acción, éstas deben ir separadas por un espacio en blanco. Estas acciones serán las que se pasen como parámetros a la función dokey( ) para que provoque dichos movimientos en el pulldown.
3. Tecla que tendremos que pulsar para provocar el movimiento especificado en el campo 2 de este fichero.
Ejemplo:
0|fquit freturn d m e fuser2|fuser3|
0|fquit freturn b t d fuser2|fuser4|
0|fquit freturn s t |fuser5|
1|fquit fquit freturn d m e fuser2|fuser3|
1|fquit fquit freturn b t d fuser2|fuser4|
1|fquit fquit freturn s t |fuser5|
2|fquit fquit fquit freturn d m e fuser2|fuser3|
2|fquit fquit fquit freturn b t d fuser2|fuser4|
2|fquit fquit fquit freturn s t |fuser5|
c) SELECCIÓN DE OBJETOS: En este nuevo entorno, la selección de objetos (tablas, módulos, programas, etc.) se puede realizar especificando el nombre completo o parte de él empleando los metacaracteres que maneja la cláusula MATCHES (asterisco "*", interrogación "?", corchetes "[ ..]", etc.). En caso de introducir el nombre en blanco aparecerá una ventana de selección del objeto en cuestión.