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.
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.
<script> Ax.db.execute(`CREATE TABLE IF NOT EXISTS excel( b_name VARCHAR(40), b_type VARCHAR(80), b_size INTEGER, b_data BLOB )`); 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 ); let rs = Ax.db.executeQuery("SELECT * FROM excel") console.log(rs); rs.close(); Ax.db.execute(`DROP TABLE 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.
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.
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
<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.
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.
<script> Ax.db.execute(`CREATE PROCEDURE IF NOT EXISTS splRowType(value INTEGER) RETURNING ROW(a iNTEGER, b SMALLINT) RETURN ROW(value * 2, value / 2); END PROCEDURE`); 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]); Ax.db.execute(`DROP PROCEDURE splRowType`); </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.
<script> Ax.db.execute(`CREATE PROCEDURE IF NOT EXISTS splRowType(value INTEGER) RETURNING ROW(a iNTEGER, b SMALLINT) RETURN ROW(value * 2, value / 2); END PROCEDURE`); // 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); Ax.db.execute(`DROP PROCEDURE splRowType`); </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
<script> Ax.db.execute(`CREATE PROCEDURE IF NOT EXISTS splRowType(value INTEGER) RETURNING ROW(a iNTEGER, b SMALLINT) RETURN ROW(value * 2, value / 2); END PROCEDURE`); 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); Ax.db.execute(`DROP PROCEDURE splRowType`); </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.
<script> Ax.db.execute(`CREATE PROCEDURE IF NOT EXISTS splRowType(value INTEGER) RETURNING ROW(a iNTEGER, b SMALLINT) RETURN ROW(value * 2, value / 2); END PROCEDURE`); var rs = Ax.db.executeQuery(` select mytab.retcol.* from systables, TABLE(splRowType(10)) as mytab(retcol) WHERE tabid < 10`); console.log(rs); rs.close(); Ax.db.execute(`DROP PROCEDURE splRowType`); </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:
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.
<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.
<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.
<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>
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.
BSON
database types in:
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
.
<script> Ax.db.execute(`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) );`); Ax.db.execute(`INSERT INTO cars VALUES (1, "Chevrolet Chevelle Malibu", 18, 307, 8, 130, '{audio:{model:"Sony",watts:40},phone:true,colors:["red","blue"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (2, "Buick Skylark 320", 15, 350, 8, 165, null);`); Ax.db.execute(`INSERT INTO cars VALUES (3, "Plymouth Satellite", 18, 318, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (4, "AMC Rebel SST", 16, 304, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (5, "Ford Torino", 17, 302, 8, 140, '{audio:{model:"Toshiba",watts:30},phone:true,colors:["black", "blue", "silver"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (6, "Ford Galaxie 500", 15, 429, 8, 198, null);`); Ax.db.execute(`INSERT INTO cars VALUES (7, "Chevrolet Impala", 14, 454, 8, 220, '{audio:{model:"Bose",watts:80},phone:true,colors:["black", "yellow", "red"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (8, "Plymouth Fury iii", 14, 440, 8, 215, null);`); 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")); } Ax.db.execute(`DROP TABLE cars`); </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
.
<script> // Warning: BSON is not yet supported. Ax.db.execute('DROP TABLE IF EXISTS cars'); Ax.db.execute(`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) );`); Ax.db.execute(`INSERT INTO cars VALUES (1, "Chevrolet Chevelle Malibu", 18, 307, 8, 130, '{audio:{model:"Sony",watts:40},phone:true,colors:["red","blue"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (2, "Buick Skylark 320", 15, 350, 8, 165, null);`); Ax.db.execute(`INSERT INTO cars VALUES (3, "Plymouth Satellite", 18, 318, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (4, "AMC Rebel SST", 16, 304, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (5, "Ford Torino", 17, 302, 8, 140, '{audio:{model:"Toshiba",watts:30},phone:true,colors:["black", "blue", "silver"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (6, "Ford Galaxie 500", 15, 429, 8, 198, null);`); Ax.db.execute(`INSERT INTO cars VALUES (7, "Chevrolet Impala", 14, 454, 8, 220, '{audio:{model:"Bose",watts:80},phone:true,colors:["black", "yellow", "red"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (8, "Plymouth Fury iii", 14, 440, 8, 215, null);`); 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()); Ax.db.execute(`DROP TABLE cars`); </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
.
<script> // Warning: BSON is not yet supported. Ax.db.execute('DROP TABLE IF EXISTS cars'); Ax.db.execute(`CREATE TABLE IF NOT EXISTS 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) );`); Ax.db.execute('DELETE FROM cars'); Ax.db.execute(`INSERT INTO cars VALUES (1, "Chevrolet Chevelle Malibu", 18, 307, 8, 130, '{audio:{model:"Sony",watts:40},phone:true,colors:["red","blue"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (2, "Buick Skylark 320", 15, 350, 8, 165, null);`); Ax.db.execute(`INSERT INTO cars VALUES (3, "Plymouth Satellite", 18, 318, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (4, "AMC Rebel SST", 16, 304, 8, 150, null);`); Ax.db.execute(`INSERT INTO cars VALUES (5, "Ford Torino", 17, 302, 8, 140, '{audio:{model:"Toshiba",watts:30},phone:true,colors:["black", "blue", "silver"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (6, "Ford Galaxie 500", 15, 429, 8, 198, null);`); Ax.db.execute(`INSERT INTO cars VALUES (7, "Chevrolet Impala", 14, 454, 8, 220, '{audio:{model:"Bose",watts:80},phone:true,colors:["black", "yellow", "red"]}'::JSON);`); Ax.db.execute(`INSERT INTO cars VALUES (8, "Plymouth Fury iii", 14, 440, 8, 215, null);`); var row1 = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 1").toOne(); 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(); console.log(row2); Ax.db.execute(`DROP TABLE cars`); </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}