Modern databases support complex data types. We will focus specially in BLOB, ROW and JSON data types.

1 BLOB data type

An SQL BLOB is a built-in type that stores a Binary Large Object as a column value in a row of a database table.

Let's see how to store a BLOB in a table. To do that, we simply create a table to store Excel files.

Copy
CREATE TABLE IF NOT EXISTS excel(
   b_name VARCHAR(40),
   b_type VARCHAR(80),
   b_size INTEGER,
   b_data BLOB
);

1.1 Writing a BLOB

Now, we will create an Excel file using Ax.ms.Excel library and will insert into a table as a BLOB object.

Copy
<script>
   var wb = new Ax.ms.Excel("Acme Sales");
   // fill excel with values...
   wb.createSheet("Sales");
   
   // Convert to blob
   var blob = wb.toBlob();
    Ax.db.execute("INSERT INTO excel (b_name, b_type, b_size, b_data) VALUES (?, ?, ?, ?)",
       blob.getFileName(),
       blob.getContentType(),
       blob.length(),
       blob
    );

    console.log(Ax.db.executeQuery("SELECT * FROM excel"));
</script>
+----------------------------------------+----------------------------------------+----------+--------+
|b_name                                  |b_type                                  |b_size    |b_data  |
+----------------------------------------+----------------------------------------+----------+--------+
|Acme Sales                              |application/vnd.ms-excel                |        -1|[BINARY]|
+----------------------------------------+----------------------------------------+----------+--------+

1.2 Reading a BLOB

TO DO

This section is incomplete and will be concluded as soon as possible.

2 ROW data type

A ROW data type is a user-defined type containing an ordered sequence of named (or unnamed) fields each with an associated data type.

Some applications can deal with ROW data types. For example, dbstudio UI interface can handle graphics produced by ROW types. For example, a bar pie can be rendered as a graphics cell using.

Copy
SELECT tabname, ROW('pie', 'a', 55, 'b', 62, 'c', 33, 'd', 35, 'e', 99, 'f', 55, 'g', 20 ) 
   FROM systables

Now, the question is how to map values from a ROW type to Javascript. Let's see a simple procedure that takes one value as input and returns two values from it.

2.1 Row returned from select

A ROW type can also be returned from a stored procedure.

Copy
SELECT tabname[1,20], ROW(tabid, npused) as info 
  FROM systables
 WHERE tabid = 1;

TO DO

This section is incomplete and will be concluded as soon as possible.

TO DO

This section is incomplete and will be concluded as soon as possible.

2.1.1 Selecting data

Copy
<script>
    var row = Ax.db.executeQuery(`
    SELECT tabname[1,20], ROW(tabid, npused) as info 
     FROM systables 
    WHERE tabid = 1
    `).toOne();
    
    console.log(row);
    console.log("tabid  = " + row.info[0]);
    console.log("npused = " + row.info[1]);
    return row;
</script>
{tabname=systables, info=[1, 7.0]}
tabid  = 1
npused = 7

2.2 Row returned from procedure

A ROW type can also be returned from a stored procedure. In the following example we create a procedure that returns a ROW with two named values.

Copy
CREATE PROCEDURE IF NOT EXISTS splRowType(value INTEGER)
	RETURNING ROW(a iNTEGER, b SMALLINT)
	RETURN ROW(value * 2, value / 2);
END PROCEDURE;

-- Execute the procedure
EXECUTE PROCEDURE splRowType(10);

-- Execute the procedure as column (executed on every row)
SELECT tabname[1,20], splRowType(10)
 FROM systables 
WHERE tabid = 1;

-- Execute the procedure as derived table (executed once)
SELECT tabname[1,20], rtable.*  
  FROM systables, TABLE(splRowType(10)) rtable
 WHERE tabid = 1;

TO DO

This section is incomplete and will be concluded as soon as possible.

TO DO

This section is incomplete and will be concluded as soon as possible.

2.2.1 Selecting data

The ROW type is mapped to java.sql.Types.Struct. Its attibutes are retrieved as JSON array. To access the JSON element you should give them an appropiate name like in the following example.

Copy
<script>
    var row = Ax.db.executeQuery(`
    SELECT tabname[1,20], splRowType(10) as retval
     FROM systables 
    WHERE tabid = 1
    `).toOne();
    
    console.log(row);
    console.log("1st = " + row.retval[0]);
    console.log("2nd = " + row.retval[1]);
</script>
{tabname=systables, retval=[20, 5]}
1st = 20
2nd = 5

2.3 Materialize row type as columns

Some times, we may want to materialize a row type attributes as named columns. To do that, we need to operate in a memory resultset using the cols function.

Copy
<script>
    
    // 1. Query calls a SPL function on each row that returns a 
    //    ROW type as 'retval' containing two columns
    //
    var rs = Ax.db.executeQuery(`
    SELECT tabname[1,20], splRowType(10) as retval
      FROM systables
     WHERE tabid < 10
    `).toMemory();
    
    // 2. Materialize (add) a column named "mul" with value of
    // the first column of the row type.
    //
    rs.cols().add("mul", Ax.sql.Types.INTEGER, r => {
        // return the first element of the row
        return r.getObject("retval")[0];
    });
    console.log(rs);
</script>
+-----------+------+---+
|tabname    |retval|mul|
+-----------+------+---+
|systables  |[20,5]| 20|
|syscolumns |[20,5]| 20|
|sysindices |[20,5]| 20|
|systabauth |[20,5]| 20|
|syscolauth |[20,5]| 20|
|sysviews   |[20,5]| 20|
|sysusers   |[20,5]| 20|
|sysdepend  |[20,5]| 20|
|syssynonyms|[20,5]| 20|
+-----------+------+---+

Multiple columns can be converted in a single operation

Copy
<script>
    var rs = Ax.db.executeQuery(`
    SELECT tabname[1,20], splRowType(10) as retval
      FROM systables
     WHERE tabid < 10
    `).toMemory();
    
    rs.cols().add(["mul", "div"], [Ax.sql.Types.INTEGER, Ax.sql.Types.INTEGER], r => {
        // return the row (it's an array of it's attributes)
        return r.getObject("retval");
    });
    rs.cols().drop("retval");
    console.log(rs);
</script>
+-----------+---+---+
|tabname    |mul|div|
+-----------+---+---+
|systables  | 20|  5|
|syscolumns | 20|  5|
|sysindices | 20|  5|
|systabauth | 20|  5|
|syscolauth | 20|  5|
|sysviews   | 20|  5|
|sysusers   | 20|  5|
|sysdepend  | 20|  5|
|syssynonyms| 20|  5|
+-----------+---+---+

Even this is not javscript related, We include next example to show how you can materialize row types into columns using plain SQL instructions.

Copy
<script>
    var rs = Ax.db.executeQuery(`
select ctercero.codigo, mytab.retcol.*
  from systables,
       TABLE(splRowType(10)) as mytab(retcol)
 WHERE tabid &lt; 10`);
 
    console.log(rs);
</script>

3 MULTISET data type

You can use a Collection Subquery to create a MULTISET collection from the results of a subquery. This syntax is an extension to the ANSI/ISO standard for SQL.

A collection subquery returns a multiset of unnamed ROW data types. The fields of this ROW type are elements in the projection list of the subquery. Examples that follow access the tables and the ROW types that these statements define:

Copy
CREATE ROW TYPE rt1 (a INT);
CREATE ROW TYPE rt2 (x int, y rt1);
CREATE TABLE tab1 (col1 rt1, col2 rt2);
CREATE TABLE tab2 OF TYPE rt1;
CREATE TABLE tab3 (a ROW(x INT));
Collection Subquery Resulting Collections
MULTISET (SELECT * FROM tab1)... MULTISET(ROW(col1 rt1, col2 rt2))
MULTISET (SELECT col2.y FROM tab1)... MULTISET(ROW(y rt1))
MULTISET (SELECT * FROM tab2)... MULTISET(ROW(a int))
MULTISET(SELECT p FROM tab2 p)... MULTISET(ROW(p rt1))
MULTISET (SELECT * FROM tab3)... MULTISET(ROW(a ROW(x int)))

3.1 MULTISET from Javascript

Multiset is returned as an Array of arrays. In the follwing example we generate a list of tables with their column names as a MULTISET.

Copy
<script>
    var rs = Ax.db.executeQuery(`
        SELECT a.tabid, a.tabname,
               MULTISET (SELECT colname FROM syscolumns WHERE tabid = a.tabid) AS colnames
          FROM systables a
         WHERE tabid < 5
    `);
    console.log(rs);

</script>
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|tabid     |tabname     |colnames                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|         1|systables   |['[''tabname'']','[''owner'']','[''partnum'']','[''tabid'']','[''rowsize'']','[''ncols'']','[''nindexes'']','[''nrows'']','[''created'']','[''version'']','[''tabtype'']','[''locklevel'']','[''npused'']','[''fextsize'']','[''nextsize'']','[''flags'']','[''site'']','[''dbname'']','[''type_xid'']','[''am_id'']','[''pagesize'']','[''ustlowts'']','[''secpolicyid'']','[''protgranularity'']','[''statchange'']','[''statlevel'']']                                                                                                                                                                                                                                                                                                                                                                                       |
|         2|syscolumns  |['[''colname'']','[''tabid'']','[''colno'']','[''coltype'']','[''collength'']','[''colmin'']','[''colmax'']','[''extended_id'']','[''seclabelid'']','[''colattr'']']                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
|         3|sysindices  |['[''idxname'']','[''owner'']','[''tabid'']','[''idxtype'']','[''clustered'']','[''levels'']','[''leaves'']','[''nunique'']','[''clust'']','[''nrows'']','[''indexkeys'']','[''amid'']','[''amparam'']','[''collation'']','[''pagesize'']','[''nhashcols'']','[''nbuckets'']','[''ustlowts'']','[''ustbuildduration'']','[''nupdates'']','[''ndeletes'']','[''ninserts'']','[''fextsize'']','[''nextsize'']','[''indexattr'']','[''jparam'']']                                                                                                                                                                                                                                                                                                                                                                                  |
|         4|systabauth  |['[''grantor'']','[''grantee'']','[''tabid'']','[''tabauth'']']                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

You can access each element of the MULTISET as a regular javascript array.

Copy
<script>
   var rs = Ax.db.executeQuery(`
    SELECT a.tabname,
           MULTISET (SELECT colname FROM syscolumns WHERE tabid = a.tabid) AS colnames
      FROM systables a
     WHERE tabid < 5
    `);
    
    for (var row of rs) {
        console.log(row.colnames);
        console.log(row.colnames[0]);
        console.log(row.colnames[0][0]);
    }
</script>
[[[tabname], [owner], [partnum], [tabid], [rowsize], [ncols], [nindexes], [nrows], [created], [version], [tabtype], [locklevel], [npused], [fextsize], [nextsize], [flags], [site], [dbname], [type_xid], [am_id], [pagesize], [ustlowts], [secpolicyid], [protgranularity], [statchange], [statlevel]]]
[[tabname]]
tabname
[[[colname], [tabid], [colno], [coltype], [collength], [colmin], [colmax], [extended_id], [seclabelid], [colattr]]]
[[colname]]
colname
[[[idxname], [owner], [tabid], [idxtype], [clustered], [levels], [leaves], [nunique], [clust], [nrows], [indexkeys], [amid], [amparam], [collation], [pagesize], [nhashcols], [nbuckets], [ustlowts], [ustbuildduration], [nupdates], [ndeletes], [ninserts], [fextsize], [nextsize], [indexattr], [jparam]]]
[[idxname]]
idxname
[[[grantor], [grantee], [tabid], [tabauth]]]
[[grantor]]
grantor

3.2 Sorting a multiset

MULTISET statements can not contain the ORDER BY clause. The MULTISET is thus returned in the natural or undetermined order. You can create a VIEW to solve the problem. In that case the MULTISET statement will use the VIEW and it's ORDER BY clause.

Another option cam be sort the multiset on each row using the ResultSetRowMap sort function.

Copy
<script>
    var rs = Ax.db.executeQuery(`
        SELECT systables.tabname, 
            MULTISET(SELECT colno, colname FROM syscolumns WHERE syscolumns.tabid = systables.tabid) columns
            FROM systables
            WHERE tabid < 4
	`).toMemory();
	
	// Iterate each row and sort columns by colname (notice that column index starts at 0)
	rs.forEach(row => {
		row.sort("columns", 1);
	});
	
	// We have put the RS into memory to allow it start back from the beginning
	console.log(rs);
</script>
Notice that we use a memory ResultSet for interation as we need to process the resultse twice. We can not use a scrollable cursor (via executeScrollableQuery) cause Informix scrollable cursors can not handle multisets.
You will get the following exception if you try to use a multiset with scroll

java.sql.SQLException: Scroll cursor can't select collection columns.

4 BSON data type

Lets create a simple table with a JSON column. In the example, we have a cars table with some characteristics. We have a JSON column to store car equipment.

You can read more about Informix BSON database types in:
Copy
CREATE TABLE cars(
    seqno           INT NOT NULL,
    car             CHAR(25),
    mpg             FLOAT,
    cylynders       INT,
    displacement    FLOAT,
    horsepower      INT,
    equipment       BSON, -- BINARY JSON UDT TYPE

    PRIMARY KEY (seqno)
);

INSERT INTO cars VALUES (1, "Chevrolet Chevelle Malibu", 18, 307, 8, 130, '{audio:{model:"Sony",watts:40},phone:true,colors:["red","blue"]}'::JSON);
INSERT INTO cars VALUES (2, "Buick Skylark 320",         15, 350, 8, 165, null);
INSERT INTO cars VALUES (3, "Plymouth Satellite",        18, 318, 8, 150, null);
INSERT INTO cars VALUES (4, "AMC Rebel SST",             16, 304, 8, 150, null);
INSERT INTO cars VALUES (5, "Ford Torino",               17, 302, 8, 140, '{audio:{model:"Toshiba",watts:30},phone:true,colors:["black", "blue", "silver"]}'::JSON);
INSERT INTO cars VALUES (6, "Ford Galaxie 500",          15, 429, 8, 198, null);
INSERT INTO cars VALUES (7, "Chevrolet Impala",          14, 454, 8, 220, '{audio:{model:"Bose",watts:80},phone:true,colors:["black", "yellow", "red"]}'::JSON);
INSERT INTO cars VALUES (8, "Plymouth Fury iii",         14, 440, 8, 215, null);

TO DO

This section is incomplete and will be concluded as soon as possible.

TO DO

This section is incomplete and will be concluded as soon as possible.

4.1 Selecting data

When processing data returned from a database query, each row iterated is converted to a JSON object. And JSON column is also converted from BSON to JSON.

Copy
<script>
    var rs = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 5");
    for (var row of rs) {
       console.log(row);
       console.log("Car name     =" + row.car);
       console.log("Car colors   =" + (row.equipment ? row.equipment.get("colors") : "n/a"));
    }
</script>
{cylynders=302, seqno=5, horsepower=140, car=Ford Torino, mpg=17, equipment={audio={model=Toshiba, watts=30.0}, phone=true, colors=[black, blue, silver]}, displacement=8}
Car name     =Ford Torino
Car colors   =[black, blue, silver]

4.2 Inserting data

Let's see how to insert a row including a BSON column from JavaScript.

Copy
<script>

    var row = {};
    row.seqno = 9;
    row.car = "Pontiac Catalina";
    row.mpg = 9;
    row.cylynders = 8;
    row.displacement = 455;
    row.horsepower = 225;
    row.equipment = {audio:{model:"Sony",watts:40},phone:true,colors:["red","blue"]} ;
    
    Ax.db.insert("cars", row);
    console.log(Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 9").toOne());

</script>
{cylynders=8, seqno=9, horsepower=225, car=Pontiac Catalina, mpg=9, equipment={audio={model=Sony, watts=40.0}, phone=true, colors=[red, blue]}, displacement=455}

4.3 Updating data

Let's see how to update columns including a BSON column from JavaScript.

Copy
<script>
    var row1 = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 9").toOne("Row not found");
    console.log(row1);
    row1.horsepower = 250;
    row1.equipment.phone = false;
    row1.equipment.turbo = "optional";
    var count = Ax.db.update("cars", row1);
    console.log(count);
    
    var row2 = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 9").toOne("Row not found");
    console.log(row2);
</script>
{cylynders=8, seqno=9, horsepower=225, car=Pontiac Catalina, mpg=9, equipment={audio={model=Sony, watts=40.0, phone=true, colors=[red, blue]}}, displacement=455}
{serial=0, warnings=null, count=1, resultset=false, time=1, type=UPDATE}
{cylynders=8, seqno=9, horsepower=250, car=Pontiac Catalina, mpg=9, equipment={audio={model=Sony, watts=40.0, phone=true, colors=[red, blue]}, phone=false, turbo=optional}, displacement=455}