Query a database from JavaScript is easy using Axional library. When performing a query, database returns an SQL ResultSet.

A SQL result set is a set of rows from a database, as well as metadata about the query such as the column names, and the types and sizes of each column. Depending on the database system, the number of rows in the result set may or may not be known. Usually, this number is not known up front because the result set is built on-the-fly.

A result set is effectively a table. The ORDER BY clause can be used in a query to impose a certain sort condition on the rows. Without that clause, there is no guarantee whatsoever on the order in which the rows are returned.

Specific wrappers around database ResultSet and in memory ResultSet are provided as built-in Javascript objects. You can read more about how to handle ResultSet objects in the Ax.rs package.

The database object provides basic methods to perform query execution.

Return Method Description
JSResultSet executeQuery(String sql, Object... args) Executes a query.
Object executeGet(String sql, Object... args) Executes a single row and single column query. Better use executeGetXXX for the appropiate number type to avoid script engine side effects.
int executeGetInt(String sql, Object... args) Executes a single row and single column query
long executeGetLong(String sql, Object... args) Executes a single row and single column query
double executeGetDouble(String sql, Object... args) Executes a single row and single column query
JSResultSet executeScriptOrQuery(String sql, Object... args) Executes a script or query
JSSQLCA execute(String sql, Object... args) Execute an SQL statement that may return a ResultSet or not depending statement type

1 Writing a statement

You can write a statement as literal string is a sequence of bytes or characters, enclosed within either two single quotes (' ') or two double quotes (" ").

1.1 Passing arguments

All methods admit variable number of arguments to match SQL statement place holders.

Copy
<script>
    var rs = Ax.db.executeQuery(
        "SELECT tabname, created FROM systables WHERE tabid = ?", 
        1
    );
    console.log(rs);
</script>
+----------------------------------------+----------+
|tabname                                 |created   |
+----------------------------------------+----------+
|systables                               |2018-05-17|
+----------------------------------------+----------+

1.2 Using template literals

Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 specification.

Template literals are enclosed by the back-tick (` `) (grave accent) character instead of double or single quotes. Template literals can contain placeholders. These are indicated by the dollar sign and curly braces (${expression}). The expressions in the placeholders and the text between them get passed to a function.

Copy
<script>
    var tabid = 1;
    var rs = Ax.db.executeQuery(
        `SELECT tabname, created FROM systables WHERE tabid = ${tabid}`
    );
    console.log(rs);
</script>
+----------------------------------------+----------+
|tabname                                 |created   |
+----------------------------------------+----------+
|systables                               |2018-05-17|
+----------------------------------------+----------+

1.3 Multiline strings

Any newline characters inserted in the source are part of the template literal. This has a great advantage for readability when writing large multi line SQL statements.

Copy
<script>
    var tabid = 1;
    var rs = Ax.db.executeQuery(`
        SELECT tabname, created 
          FROM systables 
         WHERE tabid = ${tabid}
`
    );
    console.log(rs);
</script>

2 Quering a database

You have several methods to query a database witch are more simple than their Java synonyms.

2.1 executeQuery

The executeQuery statements executes the SQL query in this PreparedStatement object and returns the ResultSet object generated by the query.

Copy
<script>
    return Ax.db.executeQuery(
        "SELECT tabid, tabname, nrows, created FROM systables WHERE tabname='systables'"
    );
</script>

data

+----------+----------------------------------------+----------------+----------+
|tabid     |tabname                                 |nrows           |created   |
|serial    |varchar                                 |float           |date      |
+----------+----------------------------------------+----------------+----------+
|         1|systables                               |          89.000|2018-04-08|
+----------+----------------------------------------+----------------+----------+

2.1.1 Iterating results

The ResulSet object is iterable. So we can use a for loop to traverse it. On each iteration, the returned object is a JSON object with row data.

Copy
<script>
    var rs = Ax.db.executeQuery(
        "SELECT tabid, tabname, nrows, created FROM systables WHERE tabname='systables'"
    );
    for (var row of rs) {
        console.log(row.tabid + "," + row.tabname);
    }
</script>

data

1,systables

2.1.2 Get current Row Number in a Resultset iteration

The ResulSet object is iterable. In each iteration an internal row counter is incremente, so you can know how many rows are you currently retrieved from the Resultset. You can access this counter by callint getRow() method..

Copy
<script>
	const rs = Ax.db.executeQuery(`
 		<select first='10'>
 			<columns>tabname</columns>
 			<from table='systables'/>
 		</select>`);

    var currentRow = 0;
    for (var systables of rs) {
        console.log("ROW = " + rs.getRow() + " TABNAME=" + systables.tabname);
    }
</script>

Console result

ROW = 1 TABNAME=systables
ROW = 2 TABNAME=syscolumns
ROW = 3 TABNAME=sysindices
ROW = 4 TABNAME=systabauth
ROW = 5 TABNAME=syscolauth
ROW = 6 TABNAME=sysviews
ROW = 7 TABNAME=sysusers
ROW = 8 TABNAME=sysdepend
ROW = 9 TABNAME=syssynonyms
ROW = 10 TABNAME=syssyntable

2.2 executeGet

The method executeGet is simplified query for single row / column values. For example, to get the count(*) of some table we have several options.

  1. Using executeQuery and iterating to first row to convert it to JSON.

    Copy
    <script>
        var rs = Ax.db.executeQuery("SELECT COUNT(*) as count FROM systables");
        var count;
        for (var row of rs) {
            count = row.count;
        }
        console.log(count);
    </script>
    88
  2. Using executeQuery and toOne function to convert directly to a JSON object.

    Copy
    <script>
        var row = Ax.db.executeQuery("SELECT COUNT(*) as count FROM systables").toOne();
        count = row.count;
        console.log(count);
    </script>
    88
  3. Using the executeGet method is quite simple.

    Copy
    <script>
        var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables");
        console.log(count);
    </script>
    88

Notice that in methods 1 and 2 we need to provide an alias for column name.

2.2.1 The data type returned

executeGet returns the native databse type and may cause some undesired side effects. In some databases (Informix for example) the COUNT(*) returns a BigDecimal. This may cause a fail when comparing the returned value with a given value depending the script engine.

The problem may be related when the value returned is BigDecimal or BigInteger. Nashorn compares a BigDecimal or BigInteger with a javascript number but Graal will not.

Copy

Nashorn script engine

<script type="nashorn">
    var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables WHERE tabid = -1");
    console.log(count);
    console.log(count == 0);
</script>
0
true
Copy

Graal script engine

<script type="graal">
    var count = Ax.db.executeGet("SELECT COUNT(*) FROM systables WHERE tabid = -1");
    console.log(count);
    console.log(count == 0);
</script>
0
false

Use executeGetInt, executeGetLong or executegetDouble to get the appropiate data type to avoid script engine side effects.

2.3 executeScriptOrQuery

The executeScriptOrQuery statements executes the XSQL code in this PreparedStatement object and returns one ResultSet object.

  1. Calling one xsql script without named columns.

    Copy
    <script>
        var message = "Hello world";
        
        const rsXsql = Ax.db.executeScriptOrQuery(`
            <call>
                <args>
                    <arg>${message}</arg>
                </args>
               <![CDATA[
                <xsql-script>
                    <args>
                        <arg name='param1' type='string' />
                    </args>
                    <body>
                        <return>
                            <param1 />
                        </return>
                    </body>
                </xsql-script>]]>       
            </call>
        `);
        
        // Get return data 
        for (const row of rsXsql) {
            console.log(row['(constant_1)']);
        }     
    </script>
    {(constant_1)=Hello world}
    Hello world
  2. Calling one xsql script with named columns.

    Copy
    <script>
        const rsXsql = Ax.db.executeScriptOrQuery(`
            <call into='col1'>
                <args>
                    <arg>Hello world</arg>
                </args>
               <![CDATA[
                <xsql-script>
                    <args>
                        <arg name='param1' type='string' />
                    </args>
                    <body>
                        <return>
                            <param1 />
                        </return>
                    </body>
                </xsql-script>]]>       
            </call>
        `);
        
        // Get return data 
        for (const row of rsXsql) {
            console.log(row.col1);
        }     
    </script>
    {col1=Hello world}
    Hello world

3 Executing statements

The execute method executes the SQL statement in a PreparedStatement object, which may be any kind of SQL statement. Some prepared statements return multiple results; the execute method handles these complex statements as well as the simpler form of statements handled by the methods executeQuery.

The execute statement returns a SQLCA object.

3.1 SQLCA object

SQLCA is called as a SQL communication Area. SQLCA will have all the information like return code, error id, error details, serial information, etc after database statement execution.

3.1.1 Rows affected count

The count property stores the number of rows affected by an INSERT, DELETE or UPDATE operation. This information can also be retrieved by calling the getCount() method on SQLCA class.

Copy
<script>
    Ax.db.execute("CREATE TABLE IF NOT EXISTS test1 (a VARCHAR(3), b SMALLINT)");
    Ax.db.execute("INSERT INTO test1 VALUES('a', 1)").getCount();
    Ax.db.execute("INSERT INTO test1 VALUES('b', 2)").getCount();
    Ax.db.execute("INSERT INTO test1 VALUES('c', 3)").getCount();
    var updated = Ax.db.execute("UPDATE test1 SET b = b + 1").getCount();
    console.log(updated + " rows updated");
</script>
3 rows updated

3.1.2 Last serial inserted

The serial property stores the serial value stored in a serial column from last INSERT executed. This information can also be retrieved by calling the getSerial() method on SQLCA class.

Copy
<script>
    Ax.db.execute("CREATE TABLE IF NOT EXISTS test2 (a SERIAL, b CHAR(3))");
    console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'a')").getSerial());
    console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'b')").getSerial());
    console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'c')").getSerial());
</script>
1
2
3

3.1.3 Time of statement

You can obtain the time in milliseconds of last operation accessing time member on SQLCA object.

Copy
<script>
    Ax.db.execute("CREATE TEMP TABLE IF NOT EXISTS test2 (a SERIAL, b CHAR(3))");
    console.log(Ax.db.execute("INSERT INTO test2 VALUES(0, 'a')").time);
</script>
0

4 Select statement

The select statement may produce two types of return values:

  • A ResultSet, when number of rows are undetermined.
  • A JSON object, when one and only one row is returned.

4.1 Getting a ResultSet

The response for a database query is a SQL ResultSet is a set of rows, as well as metadata about the query such as the column names, and the types and sizes of each column.

A ResultSet is effectively a table. The ORDER BY clause can be used in a query to impose a certain sort condition on the rows. Without that clause, there is no guarantee whatsoever on the order in which the rows are returned.

The ResultSet object implements the Iterable interface so it can be interated using Javascript for ... of statement.

Copy
<script>
    var rs_cars = Ax.db.executeQuery("SELECT * FROM cars");
    for (var row of rs_cars) {
        console.log(row);
    }
</script>
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}
{cylynders=null, acceleration=11, seqno=3, horsepower=150, mpg=18, year=70, origin=US, weight=3436, model=plymouth satellite, displacement=318}
{cylynders=null, acceleration=12, seqno=4, horsepower=150, mpg=16, year=70, origin=US, weight=3433, model=amc rebel sst, displacement=304}

4.2 Getting one JSON object

Some times we want a single object so we would like to avoid the need to iterate the ResultSet to get it.

Usually, using toOne() ,ethod is enough as it returns a ResultSetRowMap class that is almost similar to a JSON Object, but if you require a string JSON representation (for example to return it into a REST API), you can convert it by using toJSON() method.

Copy
var row = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 2").toOne();
console.log(typeof(row));
console.log(row);
var json = row.toJSON();
console.log(typeof(json));
console.log(json);
object
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}
string
{cylynders=null, acceleration=11.5, seqno=2, horsepower=165, mpg=15, year=70, origin=US, weight=3693, model=buick skylark 320, displacement=350}

If SQL statement returns more than one row it will fail with an SQLException as only one row is expected.

4.2.1 Ensuring row exists

If no row exists an empty JSON object is returned. But in many cases we would like to stop an application if some required data is missing.

You can use the setRequired(message) function to fire an exception is no row is found.

Copy
<script>
    var row = Ax.db.executeQuery("SELECT * FROM cars WHERE seqno = 99").toOne().setRequired("No car found");
</script>
java.sql.SQLException: No car found

4.3 Getting a single value

To get a single column like when we want to select a COUNT(*) we can use a fastest way avoiding either the need of a ResultSet nor the JSON object. We will get only a single value from a single row.

Copy
<script>
    var count = Ax.db.executeGet("SELECT COUNT(*) FROM cars");
    console.log("count is " + count);
</script>
count is 3

If SQL statement contains more than one column or returns more than one row it will fail with an SQLException.

4.4 Escape strings to prevent SQL Injection

To avoid SQL Injection if you don't use prepared statements and embed variable content directly inside SQL Statement, you should escape this string to avoid SQL Injection.

Copy
<script>
    var str_tabname = "' OR 1=1; --";
    
    var sqlstmt = `SELECT * FROM systables WHERE tabname = '${Ax.db.escape(str_tabname)}'`;
    
    var tab = Ax.db.executeQuery(sqlstmt).toOne();
    console.log(tab);
</script>
{}

5 Database ResultSets

You can perform database querys by using the executeQuery method. This method returns a ResultSet object.

A ResultSet object maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The next method moves the cursor to the next row, and because it returns false when there are no more rows in the ResultSet object, it can be used in a while loop to iterate through the result set.

ResultSet object is iterable so it can be travesed using the for each statement. On each iteration, next row fetched is returned as a JSON object.

5.1 ResultSet close

Database resultsets consume database resources while open. Application will automatically take care of ResultSet close when:

  • Affer a call to next() with no results, if ResultSet is forward only it will be automatically closed.
  • Affer ResultSet iteration, if ResultSet is forward only it will be automatically closed.
  • At the end of the script, all ResultSet still open will be closed. A warning message will be shown in server console.

Even so, you should better take care of closing a ResultSet when it's no loger used.

Copy
<script>
    var rs = Ax.db.executeQuery(`SELECT tabname FROM systables`);
    for (var syst of rs) {
        console.log("ROW=" + rs.getRow() + " TABNAME=" + syst.tabname);
        if (rs.getRow() == 3) {
            // Breaking cursor iteration.
            // ResultSet is not going to be consumed, so we need to close it explicitely.
            rs.close();
            break;
        }
    }
</script>

Not closing open resultset statements can also derive into error "Can not re-use statement while cursor open". Ensure you close all resultset statements after using them.

Copy
var rs_cvenfach = Ax.db.executeQuery(`select * from cvenfach`);

for (var cvenfach of rs_cvenfach) {
    var rs_cvenfacl = Ax.db.executeQuery(`SELECT * FROM cvenfacl WHERE facidx = ?`, cvenfach.facidx);
    for (var cvenfacl of rs_cvenfacl) {
        // Exit on first row to force not consuming all cursor statement
        break;
    }
    
    // rs_cvenfacl is not fully consumed, so we need to close it explicitly
    // to avoid "Can not re-use statement while cursor open" error
    rs_cvenfacl.close();
}
rs_cvenfach.close();

5.2 ResultSet holdabiliy

Result set holdability is the ability to specify whether cursors (or a result set as java.sql.ResultSet objects) should be held open or closed at the end of a transaction. A holdable cursor, or result set, is one that does not automatically close when the transaction that contains the cursor is committed.

By default javascript ResultSets are holdable, so they survive a transaction commit or rollback.

5.3 ResultSetRowMap object

When iterating a ResultSet you will get a map of type ResultSetRowMap. This object has some extensions that are very useful.

ResultSetRowMap
Return Method Description
String toJSON() Returns the JSON representation of this object.
boolean hasChanged() Returns true if map has been changed.
ResultSetRowMap getChanged() Returns a new ResultSetRowMap with values that have changed since last call.
ResultSetRowMap setNotFound(String msg) Throws an exception with given message it map contains no data (isNotFound()). Otherwise returns the map itself
boolean isNotFound() Returns true if map contains no data as a result of a query with no results. Map may still contain all column keys but all are null.

5.3.1 ResultSet iteration

During iteration, the object returned is a map of type ResultSetRowMap

Copy
<script>
    var rs = Ax.db.executeQuery("SELECT tabname, created FROM systables WHERE tabid < 3");
    for each (var row in rs) {
        // row is a JSON object with database row data
        console.log(row)
    }
</script>
{
  "tabname": "systables",
  "created": "2020-05-08"
}
{
  "tabname": "syscolumns",
  "created": "2020-05-08"
}

5.3.2 Returning a single row object

ResultSet obtained by executing Ax.db.executeQuery provides some methods to obtain a single row or single value from cursor returned.

Depending method used, it can control if only one row is selected if at least one row is selected or return first row selected.

All this functions return an ResultSetRowMap object with row data and implements isNotFound() method to know if some data has been returned by ResultSet or not

Return single row

The method toOne() ensures data contains 0 or 1 row only. If more that one rows are returned, the statement will fail with an exception.

Copy
<script>
    var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname = ?`, 'systables').toOne();
    console.log("notfound:" + m_systables.isNotFound());
    var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname = ?`, 'XXX').toOne();
    console.log("notfound:" + m_systables.isNotFound());
</script>
notfound:false
notfound:true

Notice that the JavaScript object returned (a Map) contains an special function to determine is row has been found. And that when no data is found, the object contains all columns with null values.

Return first Row

The method firstOne() returns first row of ResultSet, whenever number of rows is contained by ResultSet.

Copy
<script>
    var m_systables = Ax.db.executeQuery(`SELECT * FROM systables where tabname like 'sys%'`).firstOne();
    console.log("data:" + m_systables);
    console.log("notfound:" + m_systables.isNotFound());
</script>
notfound:false
notfound:true

Notice that the JavaScript object returned (a Map) contains an special function to determine is row has been found. And that when no data is found, the object contains all columns with null values.

Automatic fail if notfound

In some cases, we need a database rows exists to continue application. If row does not exists we should stop application as it's a failure. We can automatically setup this controls by using setRequired(message) method.

For example, if you some data is required and application can't continue without it, you can use this method to prevent that situations.

Copy
<script>
    var m_tabname = "UndefinedTable";

    // This call will fail with given message if no data is found
    var m_row = Ax.db
    .executeQuery('SELECT * FROM systables WHERE tabname = ?', m_tabname)
    .toOne()
    .setRequired(`Table ${m_tabname} not found.`);
</script>
java.sql.SQLException: Table UndefinedTable not found.
	at deister.axional.server.jdbc.resultset.iterator.ResultSetRowMap.setRequired(ResultSetRowMap.java:172)
	...
Notice that required parameter is a Javascript template (``) expression so variables can be replaced into text.

5.3.3 Determine values have changed

You can select a tuple from a database using a ResultSetRowMap, update some values and automatically determine the values changed using getChanged() to generate a new map with them to be used to perform a database update.

Copy
<script>

    Ax.db.execute("DROP TABLE IF EXISTS t1");
    Ax.db.execute("CREATE TABLE t1(a smallint, b varchar(10), c integer)");
    Ax.db.execute("INSERT INTO t1 VALUES(1, 'Animal', 5000)");
    Ax.db.execute("INSERT INTO t1 VALUES(2, 'Vegetal', 6000)");
    
    var row = Ax.db.executeQuery("SELECT * FROM t1 WHERE a = 1").toOne();
    console.log("--- ROW ---");
    console.log(row);
    
    console.log("--- ROW CHANGED ---");
    row["c"] = 9000;
    console.log(row.hasChanged());
    console.log(row.getChanged());
    
    console.log("--- ROW UPDATE ---");
    Ax.db.update("t1", row.getChanged(), "a=1");

    var rs = Ax.db.executeQuery("SELECT * FROM t1 ORDER BY a");
    console.log(rs);
    rs.close();
</script>
--- ROW ---
{
  "a": 1,
  "b": "Animal",
  "c": 5000
}
--- ROW CHANGED ---
true
{
  "c": 9000
}
+--------+-----------+----------+
|t1      |t1         |t1        |
|a       |b          |c         |
|smallint|varchar(10)|int       |
+--------+-----------+----------+
|1       |Animal     |9000      |
|2       |Vegetal    |6000      |
+--------+-----------+----------+

5.4 Getting Rows from ResultSet

The easiest way to get a row from a ResultSet is to get a ResultsetRowMap object.

5.4.1 Traversing using next

As we have seen, a ResultSet is converted to ResultsetRowMap on every iteration using toRow.

Copy
<script>
var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables");
if (rs.next()) {
   console.log(rs.toRow());
}
rs.close();
</script>
{count=92}

5.4.2 Traversing using iterator

Using an iterator is more easy. The variable row is a ResultsetRowMap.

Copy
<script>
var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables");
for (var row of rs) {
   console.log(row);
}
</script>
{count=92}

5.4.3 Converting to single row

If we know a query will return only one value we can avoid loop iteration and use direct row to ResultsetRowMap conversion.

Copy
<script>
    var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables");
    console.log(rs.toOne());
    rs.close();
</script>
{count=92}
This operation will fail if there are more that one row.

6 ResultSet to HTML

To convert a resultset to HTML there are methods:

Return Method Description
String {resultset}.toHTML() Returns a string representation a conversion of resultset to HTML code

toHTML() method requires rewindable cursor, so you're required to convert resultset to memory resultset before using this method.

        function xx() {
            ...
            var rs = Ax.db.executeQuery(`SELECT FIRST 2 * FROM systables`).toMemory();

            console.log(rs.toHTML(options => {
                options.setEmitFullHTML(false);
                //options.setEmitHeadHTML(true);
                //options.setEmitBodyHTML(true);
                options.setEmitGlobalStyles(false);
                options.setEmitGroupStyles(false);
            }));
        }
    

7 ResultSet to JSON

There are multiple methods to convert a ResultSet to native Javascript representation:

Return Method Description
String {resultset}.asJSON() Returns a string representation of a conversion of the resultset to JSON Object
Array {resultset}.toJSONArray() Returns the resultset as a Native JSON Object (Array of objects).
Array {resultset}.toArray() Returns one Array of Arrays containing resultset row values
Object {resultset}.toJSON() Returns a JSON Object with an internal declaration of resultset structure and data.

All this methods do not close the resultset. Avoid return rs.asJSON(), rs.toJSONArray(), etc. in functions. Instead, get result into a variable, close resultset and then return variable.

        function xx() {
            ...
            var result = rs.toJSON();
            rs.close();
            return result;
        }
    

7.1 Convert to ResultSet Representation: toJSON()

To obtain an String with Resultset Representation in JSON, you can use toJSON() method of JSResultSet Class

Copy
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");
console.log(rs.toJSON());
rs.close();
{
  "rows": [
    [
      1,
      "systables"
    ],
    [
      2,
      "syscolumns"
    ],
    [
      3,
      "sysindices"
    ],
  ],
  "cols": [
    {
      "columnName": "tabid",
      "columnType": 4,
      "columnIndex": 1,
      "precision": 10,
      "scale": 0,
      "tableName": "systables",
      "schemaName": "",
      "catalogName": "",
      "columnClassName": "java.lang.Integer",
      "columnLabel": "tabid",
      "columnTypeName": "serial",
      "columnDisplaySize": 10,
      "isAutoIncrement": true,
      "isCaseSensitive": false,
      "isCurrency": false,
      "isReadOnly": false,
      "isSearchable": true,
      "isSigned": false,
      "isWritable": true,
      "isDefinitelyWritable": true,
      "isNullable": 0,
    },
    {
      "columnName": "tabname",
      "columnType": 12,
      "columnIndex": 2,
      "precision": 128,
      "scale": 0,
      "tableName": "systables",
      "schemaName": "",
...
    }
  ],
  "hasMore": false,
  "title": null,
  "attributes": null,
  "hasMore": false
}

7.2 Convert to String in JSON Representation: asJSON()

Method asJSON() Convert a Resultset to a String with a JSON Object representation.

Copy
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");
var js = rs.asJSON();
console.log(typeof(js);
console.log(js);
rs.close();
string
[
  {
    "tabid": 1,
    "tabname": "systables"
  },
  {
    "tabid": 2,
    "tabname": "syscolumns"
  },
  {
    "tabid": 3,
    "tabname": "sysindices"
  }
]

7.3 Convert to native JSON Object (Array of objects)

Method toJSONArray() Convert a Resultset to a native JSON Object.

Copy
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");
var js = rs.toJSONArray();
console.log(typeof(js);
console.log(js);
rs.close();
object
[{
  "tabid": 1,
  "tabname": "systables"
}, {
  "tabid": 2,
  "tabname": "syscolumns"
}, {
  "tabid": 3,
  "tabname": "sysindices"
}]

7.4 Convert to Java (Array of Arrays)

Method toArray() Convert a Resultset to a Java Array Object.

Copy
var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");
var js = rs.toArray();
console.log(typeof(js));
console.log(js);
for (r = 0; r < js.length; r++) {
    console.log(`Row ${r}`);
    for (var c = 0; c < js[r].length; c++) {
        console.log(js[r][c]);
    }
}
rs.close();
object
[[Ljava.lang.Object;@34e04854, [Ljava.lang.Object;@53159100, [Ljava.lang.Object;@4c34c1ac]
Row 0
1
systables
Row 1
2
syscolumns
Row 2
3
sysindices

8 ResultSet to PDF

Every ResultSet can be converted automatically to Pdf and processed as an stream easily by using toPDF() method.

Copy
var rs = Ax.db.executeQuery("SELECT COUNT(*) count FROM systables");
new Ax.io.File("/tmp/resultset.pdf").write(rs.toPDF());
rs.close();

9 ResultSet to XML

Copy
<script>
    var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");
    var xml = "<sqlresponse>\n<rowset>\n";
    for (let row of rs) {
        xml += " <r>\n";
        for (obj in row) {
            xml += "  <c>" + row[obj] + "</c>\n";
        }
        xml += " </r>\n";
    }
    xml += "</rowset>\n</sqlresponse>";

    console.log(xml);
</script>
<sqlresponse>
<rowset>
 <r>
  <c>1</c>
  <c>systables</c>
 </r>
 <r>
  <c>2</c>
  <c>syscolumns</c>
 </r>
 <r>
  <c>3</c>
  <c>sysindices</c>
 </r>
</rowset>
</sqlresponse>

10 ResultSet to CSV

Copy
<script>
    var rs = Ax.db.executeQuery("SELECT FIRST 3 tabid, tabname FROM systables");

    var blob = new Ax.sql.Blob("csv_resultset.csv");
        new Ax.rs.Writer(rs).csv(options => {
            options.setResource(blob);
        });

    console.log(blob.getContent());
</script>
tabid,tabname
1,systables
2,syscolumns
3,sysindices

11 Memory ResultSets

You can generate virtual in memory resultsets and return them to client as as database resultset. This kind of resultset are very useful to perform data transformation prior returning them to client applications.

In the following example a ResultSet with two columns is build and returned to client.

Copy
<script>
function fibonacci(num) {
   if (num <= 1) 
      return 1;
   return fibonacci(num - 1) + fibonacci(num - 2);
}

var rs = new Ax.rs.Reader().memory(options => {
	options.setColumnNames([ "number", "fibonacci" ]);
	options.setColumnTypes([ Ax.sql.Types.INTEGER, Ax.sql.Types.DOUBLE ]);
});

for (var idx = 1; idx < 10; idx++) 
   rs.rows().add([idx, fibonacci(idx)]);
console.log(rs);

// Iterate Memory cursor
for each (var row in rs) {
  console.log(row['number'] + " " + row['fibonacci']);
}

return rs;
</script>
+-------+---------+
|number |fibonacci|
|double |decimal  |
+-------+---------+
|  1.000|    1.000|
|  2.000|    2.000|
|  3.000|    3.000|
|  4.000|    5.000|
|  5.000|    8.000|
|  6.000|   13.000|
|  7.000|   21.000|
|  8.000|   34.000|
|  9.000|   55.000|
| 10.000|   89.000|
+-------+---------+

1 1
2 2
3 3
4 5
5 8
6 13
7 21
8 34
9 55

12 Data mapping

12.1 Java to Javascript

Objects returned from database are the correspoding Java objects for each column data type. Javascript will work natively for String, integers and floating point numbers. And will use the Java wrapping for Date, Decimal and Blob data types as they have no corresponding Javascript equivalent.

Java Javascript
Map JSON
List Array
Boolean Native boolean
Integer Native number
Long Native number
Float Native number
Double Native number
String Native string
Date wrapper of Date
Decimal wrapper of Decimal
Blob wrapper of Blob

12.2 Javascript to Java

Native objects passed form Javascript to Java are converted to Java types. For example, you can pass a Javascript native date as parameter for database date.

Javascript Java
JSON object Map
Native Array java.util.List
Native Date java.util.Date
Native Number java.lang.Double
Native String java.lang.String

12.3 API

Error

Ax.db.ResultSet: not found