JSON SQL APIs en Db2 para z/OS y servicios nativos REST

Nota del editor: Este artículo fue publicado originalmente en inglés. Con la aprobación del autor, el equipo de IDUG ha creado la traducción que a continuación se presenta.

 

Muchos clientes están confundidos sobre las capacidades de Db2 para z/OS con JSON y los servicios nativos de REST. Creen que son lo mismo. De hecho, son completamente diferentes. En este artículo, voy a describir las diferencias y compartir las preguntas que me han hecho muchas veces.

 

Db2 para z/OS, JSON SQL APIs

Db2 para z/OS provee soporte de JSON sobre SQL desde la versión 11. Estos APIs permiten manipular datos JSON que no necesitan estar almacenados dentro de una columna en Db2. Abajo se encontrarán algunos de los APIs más comunes.

- JSON_VAL: Esta función extrae un campo en un documento de JSON y lo convierte a un tipo de datos SQL que especifican como un parámetro. Esta función también permite al usuario crear un índice JSON (utilizando un índice sobre expresión).

- JSON_TABLE: UDF que regresa los elementos del arreglo.

- JSON2BSON y SYSTOOLS.BSON2JSON: Estas 2 UDFS, como los nombres implican, convierten entre JSON y BSON (el formato binario de JSON). Estas funciones son requeridas dado que Db2 almacena datos de JSON en formato BSON dentro de columnas BLOB y muchas de las funciones JSON, como JSON_VAL y JSON_TABLE requieren entradas en formato BSON.

Para ver ejemplos de uso, vean la siguiente liga (en inglés).

https://www.ibm.com/developerworks/data/library/techarticle/dm-1501sql-json-db2/index.html

http://ibm.co/2ejxSDC

Servicios REST nativos en Db2

Db2 para z/OS provee servicios REST nativos como una funcionalidad de la versión 11 posterior a la versión base. Permite a Db2 actuar como un proveedor de servicios RESTful. Los usuarios pueden crear un servicio REST a partir de un comando de SQL (INSERT, SELECT, UPDATE, DELETE, CALL) e invocar dicho servicio vía llamada de POST con una carga de JSON a través de los protocolos http o https. Db2 regresará el resultado de la llamada en formato JSON hacia el programa que invoca.

Más adelante veremos detalles de uso. Por ahora, veamos algunas de las preguntas que he recibido muchas veces.

 

Si almaceno datos JSON dentro de Db2, ¿puedo recuperar estos datos utilizando servicios nativos de Db2?

Claro. Sin embargo, deben prestar atención a los siguientes detalles:

1. Los datos JSON se almacenan en formato BSON dentro de una columna que su aplicación podría no entender, de manera que el comando de SQL que se utiliza para crear el servicio REST necesita llamar la función BSON2JSON para convertir el formato interno de Db2 BSON hacia el tipo JSON de manera que su aplicación pueda interpretarlo.

2. Db2 regresará los datos JSON (que se encuentran dentro de Db2) como un string de caracteres. En otras palabras, la aplicación tendrá que convertir el string en un objeto JavaScript de ser necesario.

En el siguiente ejemplo, crearemos una tabla para almacenar datos del empleado en formato JSON (Nota: Utilizaré el carácter # como fin de SQL).

 

DROP TABLE EMPLOYEE#
CREATE TABLE EMPLOYEE(id INT, name BLOB)#

 

INSERT INTO EMPLOYEE VALUES(1, SYSTOOLS.JSON2BSON('{ “name”: “Jane”, “age”:18, “isManager”: false}'))#
INSERT INTO EMPLOYEE VALUES(2, SYSTOOLS.JSON2BSON('{ "name": "John", "age":58, "isManager": true}'))#

 

Para crear un servicio REST que ejecute SELECT sobre el empleado utilizando su ID, podemos enviar una llamada POST hacia Db2. Nota: En el siguiente ejemplo, dtec207.vmec.svl.ibm.com es el host y 446 es el número de puerto.

POST http://dtec207.vmec.svl.ibm.com:446/services/DB2ServiceManager
     {
          "requestType": "createService",
          "sqlStmt": "SELECT SYSTOOLS.BSON2JSON(name) FROM EMPLOYEE WHERE ID= ?",
          "serviceName": "selectNameFromId",
          "description": "Select name from id"
     }

 

El URL del servicio será "http://dtec207.vmec.svl.ibm.com:446/services/SYSIBMSERVICE/selectNameFromId"

Para obtener más información sobre este servicio (por ejemplo, entradas y salidas), pueden enviar una petición GET hacia el URL
http://dtec207.vmec.svl.ibm.com:446/services/SYSIBMSERVICE/selectNameFromId

Y verán la siguiente salida:

RequestSchema”: {
      …
      "properties": {
        "P1": {
          "type": ["null", "integer"],
          "multipleOf": 1,
          "minimum": -2147483648,
          "maximum": 2147483647,
          "description": "Nullable INTEGER"
        }
      },
…
    },
    "ResponseSchema": {
      "$schema": "http://json-schema.org/draft-04/schema#",
      "type": "object",
      "properties": {
        "ResultSet Output": {
          "type": "array",
          "items": {
             "type": "object",
            "properties": {
              "C1": {
                "type": ["null", "string"],
                "maxLength": 16777216,
                "description": "Nullable CLOB(16777216)"
              }
            },
            "required": ["C1"],

Pueden observar la información sobre los parámetros de entrada y salida en los campos RequestSchema y RespondeSchema.

Abajo encontrarán cómo pueden invocar dicho servicio desde una aplicación node.js. Estamos utilizando una aplicación node.js como ejemplo, pero la misma lógica aplica a otros lenguajes de programación.

var Client = require('node-rest-client').Client;
var client = new Client();
 
//create a base64 encoding of userid:password , need to fill out with actual userid and password
var userpw = "userid:password";
var buffer = new Buffer.from(userpw);
var userPwBase64 = buffer.toString('base64');
 
var args = {
    data: { "P1": 1 },
    headers: {  "Content-Type": "application/json",
                "Authorization": "Basic " + userPwBase64,
               "Accept": "application/json"     }
};
 
client.post("http://dtec207.vmec.svl.ibm.com:446/services/SYSIBMSERVICE/selectNameFromId", args, function (data, response) {
    console.log(data);
    var ResultSetOutput = data["ResultSet Output"];
    var nameInfo = ResultSetOutput[0].C1;
     
    console.log(nameInfo);//print: {"name":"Jane","age":18,"isManager":false}
    console.log(nameInfo.name); //print: undefined (incorrect)
    console.log(JSON.parse(nameInfo).name); //print: Jane (correct)
   });

 

Del ejemplo de arriba, nameInfo.name regresará el valor “undefined”. En este caso, se requiere usar JSON.parse() para convertir el string a un objeto JavaScript.

 

¿Puedo hacer insert multi-row utilizando servicios nativos de Db2?

En este momento, los servicios nativos de Db2 no soportan el tipo de array. En otras palabras, el parámetro de entrada para un servicio nativo de Db2 no puede ser del tipo array.

Como tal vez ya estén enterados, el tipo array permite realizar un INSERT multi-row utilizando la función UNNEST. Por ejemplo:

DROP TABLE RESULTTABLE#
CREATE TABLE RESULTTABLE(name varchar(20))#
 
DROP TYPE namesArray#
CREATE TYPE namesArray AS VARCHAR(12) ARRAY[3]#
 
--Following can be inside a SQLPL:
DECLARE names namesArray;
SET names=ARRAY['Jane','Tom','Michael'];
INSERT INTO RESULTTABLE
  (
    SELECT *
    FROM UNNEST(names)
   );

 

Una vez ejecutado, la tabla RESULTTABLE tendrá los registros “Jane”, “Tom” y “Michael”.

Dado que los servicios nativos REST de Db2 no soportan el tipo array, ¿existe alguna manera sencilla de realizar un INSERT multi-row utilizando servicios REST? Sí, podemos utilizar los Db2 JSON SQL APIs para compensar sobre esta limitante.

Paso 1: Crear un SQLPL para tomar CLOB (o VARCHAR) como parámetro.

CREATE PROCEDURE JANEPROC(IN iJSON CLOB(16M))
DETERMINISTIC
LANGUAGE SQLP1: BEGIN 
      INSERT INTO RESULTTABLE  ( 
          SELECT X.VALUE  
          FROM       
          TABLE(SYSTOOLS.JSON_TABLE(
               SYSTOOLS.JSON2BSON(iJSON), 'names', 's:20')) X  
       );
END P1 #

Más adelante se describirán los detalles.

Paso 2: Crear un servicio nativo REST para invocar el SQLPL creado en el paso anterior.

Crear un servicio REST llamado callSQLPL con el siguiente CALL:
CALL JANEPROC(?)

Este servicio REST invocará el SQLPL creado anteriormente.

Paso 3: Invocar el servicio REST creado en el paso 2

POST http://dtec207.vmec.svl.ibm.com:446/services/SYSIBMSERVICE/callSQLPL
Con el siguiente parámetro de entrada*
{
    "P1": "{                         
              \"names\": [\"Jane\",
                        \"Tom\",
                        \"Michael\"
                      ]
             }"
}

 

Dentro del cuerpo del SQLPL en el paso 1, primero convertimos el parámetro de entrada iJSON hacia BSON utilizando JSON2BSON. Después usamos JSON_TABLE para extraer los elementos del arreglo de “names”. Finalmente, insertamos cada elemento del arreglo dentro RESULTTABLE utilizando INSERT from SELECT. Utilizando esta alternativa, podemos invocar un servicio nativo REST que realiza un INSERT multi-row.

NOTA: Necesitan “escapar” el parámetro de entrara para que el servicio REST funcione apropiadamente.

Resumen

JSON es uno de los formatos de intercambio más comúnmente utilizados mientras que REST es una de las arquitecturas más utilizadas en las aplicaciones modernas. Desde la perspectiva de Db2, son un poco diferente (ya que tienen diferentes pre-requisitos), pero están relacionados en la forma de utilizarlos (esto es, las entradas y salidas de un servicio nativo REST de Db2 están en formato JSON). Sin importar cómo se usan estas capacidades, Db2 para z/OS provee soporte para ambos. Personalmente, veo cómo es que juegan un papel importante para modernizar las aplicaciones de Db2 para z/OS.

 

Recent Stories
DB2 FOR Z/OS PCTFREE FOR UPDATE FOR PERFORMANCE

IBM DB2 EN LA NUBE DE AMAZON AWS

JSON SQL APIs en Db2 para z/OS y servicios nativos REST