Etiqueta

Mostrando entradas con la etiqueta #SQL. Mostrar todas las entradas
Mostrando entradas con la etiqueta #SQL. Mostrar todas las entradas

08 octubre 2014

Creación automática de tablas en el servidor

En algunos casos, puede tener sentido la ejecución de un proceso, que cree las tablas que falten en el servidor automáticamente tras iniciar la sesión con la base de datos, la intención sería, causar que las tablas necesarias para el correcto funcionamiento de la aplicación existan. Dicho proceso podría lanzarse cuando la aplicación es ejecutada por primera vez, cuando se entrega una nueva versión de la base de datos, cuando se cambie a un nuevo servidor de base de datos o a una base de datos diferente.

Puesto que estamos haciendo uso de clases "schema" durante el desarrollo de nuestra aplicación, es posible compararlas con las tablas correspondientes en el servidor, para saber si existen realmente y en caso necesario crearlas.

Partiendo de la base de que tenemos una variable "task" denominada "tSessObj" con una conexión válida a una base de datos. Crearemos un "statementobject" que más tarde utilizaremos para enviar las sentencias SQL necesarias a la base de datos, lo haremos sobre la variable local "lStatObj" (de tipo "Object" y sin subtipo).

Do tSessObj.$newstatement(#CT) Returns lStatObj

La variable "Object" "lStatObj" ahora dispone de los métodos y propiedades necesarios y que pueden ser localizados mediante el "Interface Manager" (opción disponible, mediante pulsar el botón derecho del ratón sobre la variable) si lo deseamos.

A continuación, utilizaríamos el código que mostramos más abajo para obtener las tablas alojadas en el servidor de base de datos, recogiéndolas sobre la variable de tipo lista "lDBTableList", tal como sigue:

Do lStatObj.$tables(kStatementServerTable)

Tenga en cuenta que sólo podrá ejecutar éste código mientras disponga de una instancia activa, es decir, mientras exista una conexión válida con la base de datos. Por lo que, si desea depurar el código, deberá marcar un punto de interrupción en el lugar que desee de la "Startup_Task", para cargar sobre la lista el resultado del método anterior, escriba:

Do lStatObj.$fetch(lDBTableList,kFetchAll)

El segundo paso será crear otra lista, que contenga las clases "schema" existentes en nuestra librería. Para ello usaremos también dos variables locales que la definirán, tal y como sigue:

Do lTablesForAppList.$define(lTableName,lSchemaClassName)
Do $schemas.$appendlist(lTablesForAppList,$ref.$servertablename,$ref.$name)

Ahora tendremos que ejecutar un bucle "For" que ira recorriendo la lista de clases "schema" y creando las nuevas tablas en el servidor según sea necesario:


For lTablesForAppList.$line from 1 to lTablesForAppList.$linecount() step 1
   Do lTablesForAppList.$loadcols()
   ; carga la línea actual en los campos correspondientes lTableName y lSchemaClassName
   Do lDBTableList.$search(lDBTableList.TableOrViewName=lTableName,kTrue,kFalse,kFalse,kFalse)
   If not(lDBTableList.$line) ;; cuando no existe la tabla
      Do lRow.$definefromsqlclass(lSchemaClassName)
      ; define una variable local de tipo "row" con el nombre del "schema" que se acaba de cargar
      Do lRow.$sessionobject.$assign(tSessObj) ;; ¡importante! hace que $createnames funcionen correctamente
      ; crea la nueva tabla en el servidor:
      Do lStatObj.$execdirect(con('CREATE TABLE ',lRow.$servertablenames(),'
(',lRow.$createnames(),') ')) Returns lStatus
      If not(lStatus)
         Breakpoint -> implementar aquí el controlador de errores
      End If
   End If
End For

Naturalmente este código deberá ejecutarse después de abrir la sesión con la base de datos sobre el método $construct de la "Startup_Task", cómo puede observar, se comprueba si existe la tabla en el servidor en correspondencia con lo definido en su clase "schema" y si no existe será automáticamente creada.

14 agosto 2014

SQL en modo multitarea y multi-hilo (Parte 8 de 8)

Eliminación de procesos en ejecución


Durante el tiempo que un “Interface Object” esté operativo será posible invocar sus métodos $init() y $start(), aún en el caso de que el “Worker Object” esté ejecutando una tarea anterior. En este caso, será su propiedad $waitforcomplete quien determine si el proceso en ejecución debe continuar hasta completarse o no, informando al “Interface Object” de su finalización.

Si $waitforcomplete es kFalse, el proceso en ejecución será desligado del “Interface Object”, tal y como si estuviera a punto de quedar huérfano, sin embargo, lo que sucede, es que se crea un nuevo objeto “Worker Delegate” que será utilizado para la ejecución de la nueva tarea, con la capacidad necesaria para informar  al “Interface Object” de su finalización.

Si $waitforcomplete es kTrue, El “Worker Main” o objeto “worker” en uso, devolverá un error al “Interface Object” ante cualquier intento de volver a utilizarlo, mientras sucede que aún está en ejecución su “Worker Delegate” correspondiente. En estos casos, los objetos “worker” no pueden ser re-utilizados hasta que se produce el correspondiente $completed()/$cancelled(), será su propiedad $state quién podrá indicar su finalización.

Para entender mejor su funcionamiento, podríamos decir que un objeto “worker” con su propiedad $waitforcomplete fijada como kFalse, se convierte a todos los efectos en una especie de “dispara y olvídate”, lo cual causa que, por ejemplo, una sucesión de sentencias INSERT, UPDATE o DELETE sean remitidas al servidor de forma continua y en hilos de ejecución separados desde un único objeto.


Cancelación de procesos huérfanos


Si no se indica lo contrario (por defecto), a los subprocesos que han quedado huérfanos se les permite llegar hasta su terminación. No obstante, en algunos casos puede ser preferible emitir una solicitud para su cancelación, ($cancelled) especialmente si se estaba ejecutando una sentencia SELECT, ya que sabemos que no podremos recuperar su resultado por haber quedado desligado de su “Interface Object”. Esto se logra mediante fijar la propiedad $cancelifrunning a kTrue, antes de que el objeto “worker” sea reutilizado o destruido.

La propiedad $cancelifrunning es por defecto kFalse, por lo tanto, los subprocesos que han quedado huérfanos son ejecutados por completo antes de desecharse sus resultados y ser destruidos por el objeto “Thread Timer”.

12 agosto 2014

SQL en modo multitarea y multi-hilo (Parte 7 de 8)

Procesos “worker” huérfanos


Al lanzarse un $start(), el “Worker Delegate” se encargará de que se complete la tarea antes de invocar a los métodos del “Interface Object” $completed() o $cancelled(). Si se trata de un $run(), el “worker” será bloqueado para prevenir su cancelación, por lo que el método $completed() siempre será invocado.

Sin embargo, en caso de un $start(), el “Interface Object” puede quedar legítimamente desligado (huérfano) del “worker”, de lo contrario correría el riesgo de ser destruido antes de terminar la tarea que se le ha encargado ejecutar como subproceso. En éste momento, podemos decir que el subproceso no posee objeto alguno, se ha quedado huérfano y, por tanto, no es posible la invocación de los métodos $completed()/$cancelled(). En éste caso tanto los resultados como la información sobre posibles errores se descartarán.


 Proceso “Worker Delegate” huérfano. El “Interface Object” puede quedar desligado de su “Delegate”, y por lo tanto apuntar ahora hacia un nuevo “Worker Delegate”.

Tras la destrucción de un “Interface Object”, se transfiere la propiedad del “Worker Delegate” al “Thread Timer”. Un sólo objeto “Thread Timer” lleva acabo la supervisión de todos los objetos “Worker” de un determinado tipo, encargándose de eliminar los que hayan podido quedar huérfanos una vez concluidas sus tareas.

Un solo objeto “Thread Timer” supervisa todos los “Worker Delegates” de un determinado tipo. Un “Interface Object” sólo puede iniciar y recibir resultados de un único “Worker Delegate”, aunque pueden coexistir múltiples “Interface Objects”

01 agosto 2014

SQL en modo multitarea y multi-hilo (Parte 6 de 8)

Funcionamiento de los objetos “SQL worker”


El objeto “worker” puede ser representado mediante tres sub-objetos:

  • Interface Object
    Es el objeto no-visual estándar Omnis que proporciona los métodos y propiedades ya descritos anteriormente.

  • Worker Delegate Object
    Es el encargado de crear y ejecutar el subproceso en segundo plano. El “Worker Delegate” realiza el trabajo real del objeto “worker”, invocando al “Interface Object” cada vez que concluye.

  • Thread Timer Object
    Declarado estáticamente, cada “Worker Delegate” “suscribe” (por decirlo así) al ejecutarse ($start) un objeto “Timer Thread” (control multi-hilo). Todos los subprocesos poseen algún tipo particular de “Thread Timer”, el cual es responsable de que se vuelva a invocar su correspondiente “Interface Object”, tras concluir su ejecución.


Detrás de cada objeto “SQL Worker”, se esconde un “Worker Delegate” y un “Thread Timer”

30 julio 2014

SQL en modo multitarea y multi-hilo (Parte 5 de 8)

Ejecución de un “worker”


El método $start() causa que la tarea especificada para el “worker” sea ejecutada como un subproceso en segundo plano. Por lo tanto no se produce pausa o espera alguna, la aplicación continua con su ejecución. Observe el siguiente ejemplo, el cual presupone que disponemos de la variable “iWorkerObj” correctamente definida, tal y como se puede observar a continuación…

;; iWorkerObj es una variable de tipo Object definida bajo instancia
Calculate Params as row(
iSQLText,'192.168.0.10’,iUser,iPassword,iDBName)
Do Params.$redefine(query,hostname,username,password,database)
Do iWorkerObj.$init(Params)
Do iWorkerObj.$start() Returns #F

…el método $run() (proporcionado sólo con fines de depuración y pruebas) es análogo a $start(), pero no debe ser usado en modo de explotación, ya que el “worker” es ejecutado sobre un único hilo quedando bloqueado hasta que la tarea asignada concluye.

Una vez iniciado el objeto “worker” puede ser ejecutado varias veces si así se desea, siempre y cuando el objeto-sesión suministrado siga estando disponible o bien el grupo de sesiones (sesión-pool) disponga de una o más sesiones libres de uso y las credenciales de inicio de sesión sigan siendo válidas. Cualquiera de las variables vinculadas (bind) ya suministradas, serán reutilizadas cada vez que se ejecute el “worker”.

Si se produce un error durante la ejecución de un $init(), $start() o $run(), se enviará un mensaje de error mediante las propiedades del objeto denominadas $códigoerror y $erroreext.

Procesado de los resultados de un “worker”


Una vez concluida la tarea asignada al “worker”, se invocará uno de los dos métodos de devolución disponibles:

  • $completed()
    Este método es invocado junto con un parámetro de tipo “row” que contiene dos columnas: “Results”: con una lista de una sola columna con cero o más conjuntos de resultados SQL y “Errors”: una lista de dos columnas con los valores ErrorCode y ErrorMsg.

  • $cancelled()
    Este método es invocado (sin paso de parámetros) y se produce cuando el usuario ejecuta un $cancel() sobre el objeto “worker” mientras está en ejecución. Los resultados pendientes son descartados.

A menudo sucede que una misma librería contiene objetos “worker” de diferentes tipos, cada uno de ellos con una tarea específica (sentencia SQL) e iniciados en modo asíncrono. Por lo tanto, la responsabilidad sobre el tratamiento de los resultados depende del uso que demos a los métodos indicados anteriormente, es en ellos donde se podrá obtener información sobre los resultados y ponerlos a disposición de la aplicación. A modo de ejemplo, describimos un posible uso del método $completed().

Calculate List as pRow.Results.1.1 ;;extrae el primer conjunto de resultados
Calculate Errors as pRow.Errors ;;extrae la lista con los posibles errores
If Errors.$linecount()>0
    Do method reportError(Errors)
Else
    Do method updateWindow(List) ;; procesa resultados
End If
Calculate iThreadCount as $cinst.$threadCount ;; obtiene el número de hilos en proceso


En caso de que los resultados devueltos por $completed() deban ser mostrados sobre una ventana, necesitaremos dibujar de nuevo su instancia (la de la ventana) y si se trata de un “remote-form”, el cliente deberá contactarse de nuevo con el servidor para su actualización.

Estados de un “worker”


Para consultar el estado en que se pueda encontrar un objeto “SQL worker” deberemos hacer uso de su propiedad $state. Que podrá informar de los siguientes:

  • kWorkerStateCancelled – El “worker” ha sido cancelado.
  • kWorkerStateClear – El “worker” se ha iniciado.
  • kWorkerStateComplete – El “worker” ha concluido.
  • kWorkerStateError – Se ha producido un error (ver $errortext).
  • kWorkerStateRunning – El “worker” está actualmente en ejecución.

Ejemplo de uso:

If iWorkerObj.$state=kWorkerStateRunning&iWorkerObj.$waitforcomplete=kTrue
     Calculate iMesg as 'En ejecución (esperando su conclusión)'
     Quit method
End If



21 julio 2014

SQL en modo multitarea y multi-hilo (Parte 4 de 8)

Iniciación de un objeto “worker”


El objeto “worker” deberá ser iniciado para un hilo, antes de ser ejecutado. Lo haremos mediante el suministro de una sentencia SQL y sus variables vinculadas (bind) que precise. Los datos necesarios para el inicio de una sesión con la base de datos o el nombre del grupo de sesiones (si estamos usando una pila de sesiones) también podrán ser suministrados durante el proceso de iniciación.

El parámetro de inicialización deberá ser suministrado con el método $init(), por medio de una variable de tipo “row”, que contendrá los valores para cada uno de sus atributos. El nombre de cada atributo corresponderá con el de cada una de sus columnas. A continuación, mostramos los que serán reconocidos por el objeto “worker”: (sensible al uso de mayúsculas y minúsculas)

Atributo Descripción
session Un objeto-sesión activo (session object) o referencia a un objeto-sesión. Deberá corresponder a una conexión en uso y utilizable.
poolname El nombre de un grupo de sesión existente. El “worker” tomará un objeto-sesión del grupo indicado y devolverá el control.
hostname Nombre host/IP del servidor de base de datos.
database Nombre de la base de datos a utilizar al inicio de sesión.
username Nombre del usuario a utilizar para el inicio de sesión.
password Contraseña del usuario para el inicio de sesión.
query Sentencia SQL que deberá ser ejecutada por el “worker”.
bindvars Es una lista con los valores de las variables vinculadas (bind). Las variables “bind” son leídas según el orden de las columnas. Si la lista contiene varias filas, la consulta será ejecutada para cada una de las filas.

Si se proporciona el atributo “session”, el resto de atributos de inicio de sesión, es decir, el “hostname”, “username” y “password” serán ignorados, ya que se presupone que el objeto-sesión está en estado utilizable. Nota importante: Si se usa de éste modo, la sesión deberá ser considerada como reservada para su uso exclusivo con “worker”. Cualquier intento de compartir un objeto-sesión que está siendo utilizado por un “worker”, que (dicho sea de paso) cuenta con su propio hilo de ejecución, los resultados serán impredecibles.

Al igual que en el caso del atributo “session”, los parámetros de inicio de sesión indicados también serán ignorados si se suministra el atributo “poolname”. En este modo, el “worker” intentará obtener una sesión del grupo de sesiones referenciado, liberándolo de nuevo tras su ejecución. En el caso de aportarse ambos atributos, (“sesión” y “poolname”) se ignorará el “poolname”.

Cuando no se aportan ninguno de ambos atributos, (“sesión” y “poolname”), se crea en ese instante un objeto-sesión interno. En éste caso deberán indicarse las credenciales para el inicio de la sesión, tales como el nombre de host/IP, nombre de usuario y su contraseña. Tenga en cuenta que, aunque estos atributos serán leídos al ejecutarse el método $init(), el “worker” no intentará iniciar la sesión hasta que los métodos $run() o $start() sean invocados. Éste modo de uso, causa que la sesión sea activada/desactivada automáticamente, cada vez que la tarea o “worker” se completa o bien es cancelada. En caso de que necesite modificar uno o más atributos de la sesión, antes de ejecutar el método $run() o $start(), podrá hacerlo mediante obtener una referencia al objeto-sesión al que pertenece el “worker”, usando para ello el método $sessionref(), por ejemplo, si quisiéramos modificar el puerto asignado, podríamos hacer lo siguiente:

Do iWorkerObj.$sessionref(lObjRef) Returns #F
Do lObjRef.$port.$assign(5435)


La texto SQL suministrada a través del atributo “query” podrá contener cualquier sentencia SQL, pero puesto que se trata de aprovechar la capacidad del multiproceso y multi-hilo, se espera que se trate de una declaración que normalmente le lleva una apreciable cantidad de tiempo al servidor para su ejecución, por ejemplo; una SELECT, UPDATE o DELETE con muchas filas y/o tablas implicadas. El texto de la “query” también podrá contener una o más variables vinculadas, especificadas en la forma ya conocida @[...].

Los valores de las variables vinculadas deberán suministrarse por medio del atributo “bindvars”. La lista es guardada por medio del método $init() y leida cuando se ejecuta la tarea o “worker”. En el caso de que la lista contenga varias filas, el “worker” volverá a ejecutar la sentencia SQL suministrada para cada una de las filas, pasando los valores de cada variable vinculada, en correspondencia con el orden de sus columnas.

Tenga en cuenta que el “worker” no tendrá en cuenta los nombres de las variables especificadas en el texto SQL, a la hora de establecer la correspondencia con sus valores, ya que ésta será establecida de acuerdo a su posición y no según sus nombres.

SQL en modo multitarea y multi-hilo (Parte 3 de 8)

Creación de objetos “SQL Worker”


Los objetos “worker” son creados como sub-clases (de tipo “SQL Worker”) de una clase objeto Omnis (Omnis object class). Por ejemplo, si queremos usarlo contra una base de datos Oracle, seleccionaríamos desde el cuadro de diálogo “Select Object”, el valor pertinente para su propiedad $superclase en: .ORACLE8DAM.Worker Objects\OracleWorker.
Naturalmente lo que queremos es poder acceder a las diferentes funciones del objeto “worker” desde el código de nuestra aplicación, para ello deberemos crear una o más variables de tipo objeto en el ámbito de instancia, fijando el objeto “worker” creado, como sub-tipo de dichas variables.

15 julio 2014

SQL en modo multitarea y multi-hilo (Parte 2 de 8)

Métodos de los objetos “SQL Worker”
Método Descripción
$init() $init(ParamRow). Inicia o restablece un objeto “worker” preparándolo para realizar una tarea.*
$start() Ejecuta una tarea o “worker” como un subproceso (ejecución en segundo plano, tenga en cuenta que el programa continuará su ejecución no se producirá ningún tipo de bloqueo o detención)*
$run() Ejecuta una tarea en modo “único-hilo” (se detienen todos los procesos a la espera de una respuesta). Este método se aporta sólo con la finalidad de que pueda realizar pruebas de ejecución.*
$cancel() Cancela una tarea o “worker”, que está siendo ejecutada como subproceso (en segundo plano).*
$sessionref() $sessionref(ObjectRef). Devuelve la referencia del objeto de sesión, al que pertenece el “worker” en uso.*
$completed() Es ejecutado por el propio “worker”, tras concluirse su ejecución.
$cancelled() Es invocado en el caso de que se produzca la cancelación de un “worker” que se estaba ejecutándose en segundo plano.
*Si es ejecutado con éxito devolverá kTrue o en caso contrario kFalse.


Propiedades de los objetos “SQL Worker”
Propiedad Descripción
$cancelifrunning Si es kFalse (por defecto), se concluirá la ejecución de los “worker” que hayan podido quedar huérfanos. Si es kTrue, se informará sobre su estado.
$waitforcomplete Si es kTrue (por defecto), el “Interface Object” se mantiene a la espera de que el “worker” en ejecución termine, antes de habilitarse para la ejecución de otro. Si es kFalse, el “worker” en ejecución es liberado, permitiéndose la ejecución de otro en su lugar.
$state Devuelve el estado actual del “worker”, el cual podrá ser: kWorkerStateCancelled, kWorkerStateClear, kWorkerStateComplete, kWorkerStateError o kWorkerStateRunning.
$errorcode Contiene el código de error. (Si se produce).
$errortext Contiene el texto del error. (Si se produce).
$threadcount Contiene el número de subprocesos “worker” que están actualmente bajo supervisión del “Thread Timer Object”.  (Control multi-hilo)

14 julio 2014

SQL en modo multitarea y multi-hilo (Parte 1 de 8)

Omnis Studio 6 permite el lanzamiento de sentencias SELECT como subprocesos independientes y desligados del hilo principal, hasta que el servidor ha procesado los resultados. Para habilitar esta funcionalidad, los DAM’s posibilitan ahora la creación de “SQL Workers”, denominación otorgada a las instancias creadas con éste propósito mediante el uso de variables objeto (SQL) y disponibles con los nuevos DAM’s para Oracle, ODBC, JDBC, MySQL, PostgreSQL, DB2, Sybase, y SQLite.

Los objetos “SQL Worker” pueden ser (por ejemplo) asignados a los diferentes campos de tipo lista usados en un formulario y que (nótese) serán cargados en modo asíncrono, lo que podría ser usado para conseguir acelerar en gran manera la carga de datos en un formulario, ya que además se hace uso de la característica añadida del multi-hilo (procesamiento en paralelo de sentencia SQL) junto con la capacidad multitarea aportada por los “SQL Workers”.

¿Cómo funciona?

Los objetos “SQL Worker” contienen los siguientes métodos principales:
  • $init()
  • Inicia o restablece un objeto “worker” dejándolo listo para realizar su tarea

  • $start()
  • Ejecuta una tarea o “worker” como un subproceso (ejecución en segundo plano, tenga en cuenta que el programa continuará su ejecución no se producirá ningún tipo de bloqueo o detención)

  • $cancel()
  • Cancela una tarea o “worker”, que está siendo ejecutada como subproceso (en segundo plano)

Esto es sólo una descripción general, por supuesto contamos con una serie de propiedades que nos permiten (por ejemplo) descartar una tarea en ejecución para hacer uso de otra en su lugar, cancelar tareas que se hayan podido convertir en “huérfanas” o simplemente informaros del estado en que se encuentre una determinada tarea o “worker”.

Los objetos “worker” son creados como sub-clases (tipo “SQL Worker”) de una clase objeto Omnis. Después deberemos iniciar el objeto con una sentencia SQL y sus variables vinculadas (bind) que precise. Los datos necesarios para el inicio de una sesión con la base de datos o el nombre del grupo de sesiones (si estamos usando una pila de sesiones) también podrán ser suministrados durante el proceso de iniciación.

Para la instanciación de un subproceso “SQL Worker” se usa el método $start(). Al concluirse su ejecución, se invocará automáticamente al método $completed() o al $cancelled(), al que se pasará el conjunto de resultados o bien la información del error producido.