When comparing XSQL
with Java, one could imagine a script as a class and a function as a class method.
Functions and scripts both share the same pattern, accept arguments, and can return values.
- Scripts are what we call functions which are an entry point and, as such, can be executed from a loader. Scripts are invoked by a loader, or by another script or function, using the <call> tag.
- Functions are what we call blocks of code declared within a script or function. As such, they are invisible outside the script or function which declares them. Functions declare new tags during their life cycle, and are invoked using the tag which corresponds to their name.
New in v2019.1
As of Axional Studio
version 2019.1, the <call> tag not only executes xsql-script
but also can
execute javascript and R scripts. See the
JS and R scripts section
1 Scripts
A script-type function resides in one of two places: an XML file whose name is the function identifier, or a database dictionary. In the second case, the ID code is a name. Scripts are evaluated by a loader.
- The command line loader can execute scripts based on files or stored in databases.
- The HTTP loader for WebApp, WebOS or SOAP applications can execute scripts stored in databases or embedded, but cannot reference files.
Script-type functions take the following form:
<xsql-script> <args> <arg name='p_arg1' /> <arg name='p_arg2' /> <arg name='p_argN' ... /> <args> <body> <return>...</return> </body> </xsql-script>
1.1 Scripts in Files
During the development of tests or scripts for batch execution, a text or XML editor can be used
to write XSQL
code. The standard is for files to end in a .xml extension.
Scripts stored in files are processed by the file loader, and calls which affect other scripts (call) are resolved as filenames under the same directory as the initial script.
In this way:
- The file calc0.xml is executed from the command loader.
- The script calc0 calls another script named calc1.
- The loader resolves the call by loading a calc1.xml file.
1.2 Scripts in Dictionaries
Scripts are often stored in dictionaries, which are assigned to a database for data mining. In such cases, the loader utilizes the database used to execute the application, resolving scripts by exploring their dictionaries by order of prevalence (from the most specific to least specific dictionary, or in other words, by dictionary priority level).
1.3 Embedded Scripts
Axional Studio
applications are primarily based on SQL objects
.
These generally include access to SQL tables; as such, the main body of the object
is an SQL statement. The application output (result) is a selected set of data.
In some cases where complex processes are required, the object can simply perform a call to a script catalogued in the database dictionary. The object can also directly execute embedded code.
An embedded script is defined as a script whose code is declared within a <call>, via a CDATA container.
<call> <args> <arg>Hello</arg> </args> <![CDATA[ <xsql-script> <args> <arg name='text' /> </args> <body> <println><text/></println> </body> </xsql-script> ]]> </call>
Hello
An embedded call cannot have an attribute name
A common programming error is to write a call which contains code embedded in the tag itself, and also possesses the attribute 'name'. If this occurs, the system sends an exception because the two cases (embedded code and calls to another XSQL Script program) are incompatible.
1.4 Calling JS and R scripts
From Axional Studio
version 2019.1 it's possible to invoke javascript
and R
scripts.
When the <call>
attribute
2 Functions
Functions are similar to scripts, except for the fact that they must be declared within scripts. One could imagine a function as a subroutine, visible only within the scope of the routine which declared it.
Functions have names, which are used to instance a tag. This tag appears during the life cycle of the function, as if it were a function of the language itself.
The following example shows how to write a distance function within a script:
declare distance=sqrt((x2−x1)^2+(y2−y1)^2)
<xsql-script name='main'> <body> <!-- Declares the distance function which installs a new tag in XSQL language--> <function name='distance'> <args> <arg name='x1' /> <arg name='y1' /> <arg name='x2' /> <arg name='y2' /> </args> <body> <return> <math.sqrt> <add> <math.pow> <sub> <x2/> <x1/> </sub> <number>2</number> </math.pow> <math.pow> <sub> <y2/> <y1/> </sub> <number>2</number> </math.pow> </add> </math.sqrt> </return> </body> </function> <set name='x1'>10.0</set> <set name='y1'>10.0</set> <set name='x2'>50.0</set> <set name='y2'>50.0</set> <println>Distance between 2 points = <distance><x1/><y1/><x2/><y2/></distance></println> </body> </xsql-script>
Distance between 2 points = 56.568542494923804
Once the function has been declared, the language has a new tag available, called distance, which requires four arguments.
3 Arguments
Scripts and functions can receive arguments. Arguments, if required, are declared within the args tag which must precede the body of the script or function. Each argument is declared within the arg tag and requires a name which declares the function's input variable.
The following example shows a function which receives three distinct types of arguments.
<xsql-script> <body> <function name='f1'> <args> <arg name='a' /> <arg name='b' /> <arg name='c' /> </args> <body> <println>a = <a/></println> <println>b = <b/></println> <println>c = <b/></println> </body> </function> <f1> <number>10</number> <number>3.14</number> <string>hello</string> </f1> </body> </xsql-script>
a = 10
b = 3.14
c = 3.14
3.1 Command Line Loader Arguments
In theory, any script can be the entry point for a call from the command line loader (or an HTTP request). However, just like in a Java or C program, only string types can initially be passed to a main routine.
<xsql-script name='main'> <args> <arg name='p_name'/> </args> <body> <println>Hello <p_name/>!</println> </body> </xsql-script>
$ ws-dbscript -file test.xml -args "p_name=lisa"
Hello lisa!
The script's name attribute only serves indicative purposes (debugging, logging). This is because the loader determines the file or object to execute based on the filename or object name recorded, when it is located in a database.
3.1.1 Type Conversion
As has been indicated, arguments received from external sources in the main function must be string type.
<xsql-script name='main'> <args> <arg name='x' /> <arg name='y' /> </args> <body> <println>The argument x is <x/>, and its type is <variable.typeof><x/></variable.typeof></println> <println>The argument y es <y/>, and its type is <variable.typeof><x/></variable.typeof></println> </body> </xsql-script>
$ ws-dbscript -file test.xml -args "x=1,y=2"
The argument x is 1 and its type is String.
The argument y is 2 and its type is String.
Nonetheless, routines can force the conversion of string type to other data types. These conversions are limited to numerical types, dates, or timestamps. a los tipos numéricos y fechas o timestamps.
<xsql-script name='main'> <args> <arg name='x' type='double' /> <arg name='y' type='integer' /> </args> <body> <println>The argument x is <x/>, and its type is <variable.typeof><x/></variable.typeof></println> <println>The argument y is <y/>, and its type is <variable.typeof><x/></variable.typeof></println> </body> </xsql-script>
$ ws-dbscript -file test.xml -args "x=1,y=2"
The argument x is 1.0 and its type is Double.
The argument y is 2 and its type is Integer.
Conversion of Numerical Types
This language performs automatic conversions wherever possible. For example, it would be viable to pass a smaller numerical type to a larger numerical type, or an integer type to a floating type. However, any conversion which may involve conversion loss is illegal. Therefore, it can be useful to define argument types correctly in mathematical functions, as a verification measure. The same is true of other types of routines, in which we would expect objects of a particular special type.
Conversions of Numerical Types | |||
---|---|---|---|
Source type | Destination type | Possible | Observations |
string | integer, long, float, double, decimal | Acceptable as long as the string can be parsed to the destination type. | |
smallint | integer, long, float, double, decimal | To higher type | |
integer | long, float, double, decimal | To higher type | |
integer | smallint | If the integer is in the range from 32767 to -32768. | |
long | float, double, decimal | To higher type | |
long | smallint | If the long is in the range from 32767 to -32768. | |
long | integer | If the long is in the range from 2147483647 to -2147483648. | |
float | smallint, integer, long | Error | |
float | double, decimal | To higher type | |
double | smallint, integer, long | Error | |
double | float | May require rounding and loss of precision. | |
double | decimal | To higher type | |
decimal | smallint, integer, long | Error | |
decimal | float, double | May require rounding and loss of precision. |
Date Conversion
TO DO
This section is incomplete and will be concluded as soon as possible.3.2 Form Query Arguments
The applications in Axional Studio
, which are commonly referred to as SQL objects
,
include the ability to ask the user for variables in a form. The object
can utilize these variables to execute a script.
The SQL objects query processor sends input values formatted for use in the database agent where the query will be executed. This is especially critical if the values are dates.
For example, if a date such as August 1st, 2006 is sent using an Informix agent, it will be sent in MDY format (8,1,2006). In Oracle, the function TO_DATE() will be used, and so on. On the other hand, if the execution is from the command line, "01-08-2006" will be sent as an argument.
A routine's argument can be wrapped in an expression container. The tag <expression> allows the value indicated to be resolved as an expression of the database agent, then converted to the required data type.
The <call> tag can be used to invoke a script from an XML object. Variables will often be passed using the reference $VARIABLE. If it is known that an argument is a date or unit of time, the correct procedure is to use <expression> encapsulation. This tag allows data to be converted from database-native SQL format to the data type expected by the script processor.
<call name='test'> <arg name='fecha'><expression>$FECHA</expression></arg> </call>
4 Internal Functions
A script can contain an arbitrary number of added functions. Each added function can contain a new set of added functions at once. Each added function is visible within the scope of the predecessor function.
In the following example, f1
contains the function f2
, which in turn contains the function
f3
, and so on in succession until f5
.
-
f2
is visible tof1
. -
f3
is visible tof2
. -
f4
is visible tof3
. -
f5
is visible tof4
.
In the following example, the execution of functions follows this order:
f1->f2->f3->f4->f5
<xsql-script> <body> <function name='f1'> <body> <println>f1</println> <function name='f2'> <body> <println>f2</println> <function name='f3'> <body> <println>f3</println> <function name='f4'> <body> <println>f4</println> <function name='f5'> <body> <println>f5</println> </body> </function> <f5/> </body> </function> <f4/> </body> </function> <f3/> </body> </function> <f2/> </body> </function> <f1/> </body> </xsql-script>
f1
f2
f3
f4
f5
5 Return Values
XSQL
does not declare a function's return value, and as such
the return value is resolved in execution time. In general, although
XSQL
grammar does not prohibit it, it is not correct to write functions with conditional
return values.
In other words, if we establish that a function will or will not return arguments, we must determine that all function output is symmetrical with regards to return values.
5.1 No Values Returned
Functions which do not return any values can be equated to a Java method's void declaration. A function which does not return values cannot be used as an expression or assigned to a variable.
5.2 One Value Returned
Normally, functions will return only one value of any type. If the type is numerical or boolean, the function could be used as an expression.
5.3 Returning Multiple Values (into)
Scripts or functions can return multiple values. In such cases, the resulting value is an array of objects.
The attribute into can be used to specify returned variables for each element of the array, both when performing a <call> to a script and when using the declared function.
<xsql-script> <body> <function name='local_1'> <args> <!-- args --> </args> <body> <!-- code --> <return> <number>10</number> <number>20</number> </return> </body> </function> <!-- place 10 into a 20 into b --> <local_1 into='a, b' /> <!-- place Object[] {10,20] into c --> <set name='c'><local_1/></set> <println>a=<a/></println> <println>b=<b/></println> <println>c type = <variable.typeof><c /></variable.typeof></println> </body> </xsql-script>
a=10
b=20
c type = [Ljava.lang.Object;
5.4 DataSource Return
In some cases, a script can be used to generate a data file (a PDF or image, for example) returned as output. This returned value may be the same value sent to a web client. However, how can users specify the data type of the response?
To handle these cases, the return clause can be used with type attributes (which indicate the Content Type) and name attributes (which indicate the filename). In this case, the script would return an object with the following type: javax.activation.DataSource.
<xsql-script> <body> <return type='application/pdf' name='factura.pdf'> <fop.form code='cvenfach' cond="cvenfach.facidx = 135" > <vars> </vars> </fop.form> </return> </body> </xsql-script>
If the previous code is executed in Axional DBStudio
, a PDF (DataSource) will be returned.
On the other hand, if type and name attributes are removed, a string
with a reference to the PDF file will be returned.
5.5 Returning Values to the Loader
Typically, a script is invoked internally from an application. However, in some cases a script can be invoked from outside the system, or individually. For example, a script could be invoked from:
- The command line interpreter.
- The database management tool
Axional DBStudio
. - A SOAP petition.
In the first two cases, interpreters convert the returned value to a readable format. In the case of SOAP petitions, the returned value is serialized according to preferred SOAP type.
Let's take the following script as an example:
<xsql-script> <body> <return> <number>10</number> <number>20</number> </return> </body> </xsql-script>
The command line interpreter will show:
ws-dbscript -file test.xml
Running script................: test
Program returned..............: {(constant_1)=10, (constant_2)=20}
Execution completed...........: 0.016 secs.
Axional DBStudio
will show:

6 Cache
In specific cases, a function within a process is considered invariant for a set of input data. The attribute cache='true' can be defined to indicate that a function is invariant.
Cache functions always return the same output when given the same input. In other words, the same result is always returned when the same arguments invoke it.
The following examples show two scripts. The first can be catalogued as f_sum.xml, an invariant script.
<xsql-script> <args> <arg name='sum1'/> <arg name='sum2'/> </args> <body> <println>Sum of <sum1 /> and <sum2 /></println> <return><add><sum1/><sum2/></add></return> </body> </xsql-script>
The second script can be catalogued as test.xml
<xsql-script> <body> <call name='f_sum' cache='true'> <number>4</number> <number>5</number> </call> <call name='f_sum' cache='true'> <number>4</number> <number>5</number> </call> </body> </xsql-script>
Sum of 4 and 5
Note that although the call to f_sum is executed twice, the message only appears once.
Normally, the cache flag would be used to implement functions with database access considered invariant for a given process cycle. In such cases, the optimization factor can significantly reduce processing time.
The cache is localized to the context of a script execution, and as such, cannot be shared between processes.
Note
The user should be certain that the code to be cached is invariant. If not, unexpected results will be returned. If the user does not fully comprehend the effect this option will have, we recommend they observe simple examples to learn beforehand.
7 Stack
A script possesses an internal stack. The stack allows us to store values and later recover them. The stack is localized to the scope of a routine or function's execution context. In other words, each script or function has its own stack, whose duration is the scope of the execution context. The stack is built using a last-in-first-out (LIFO) stack of objects.
7.1 Push
Stores an object in the execution context's stack.
<push>
<var /> !
</push>
Arguments | |||||
---|---|---|---|---|---|
Name | Type | Required | Unique | Nullable | Description |
Evar |
Returns | |
---|---|
Type | Description |
Object | The object deposited in the stack. |
Declare and initialize a string-type variable, then deposit in the routine's execution stack.
<xsql-script name='push_test'> <body> <set name='a'><number>10</number></set> <push><a/></push> </body> </xsql-script>
7.2 Pop
The <pop> tag eliminates the last object saved in the stack and returns its value.
<pop name='name'/>
Attributes | |||||
---|---|---|---|---|---|
Name | Type | Required | Default | Description | |
Aname | Name of the new variable created with the returned value. |
Returns | |
---|---|
Type | Description |
Object | The last object deposited in the stack using the push function. |
Initialize the execution stack with two values and obtain them using the pop tag.
<xsql-script name='pop_test'> <body> <set name='a'><number>10</number></set> <set name='b'><number>2</number></set> <push><b/></push> <push><a/></push> <!-- [2,10] are in the stack. --> <pop name='c' /> <println><c/></println> <!-- 10 --> <!-- Now the value 2 is all that remains in the stack. --> <pop name='d' /> <println><d/></println> <!-- 2 --> </body> </xsql-script>
7.3 Peek
The <peek> tag returns the last object saved in the stack without removing it.
<peek name='name'/>
Attributes | |||||
---|---|---|---|---|---|
Name | Type | Required | Default | Description | |
Aname | String | Name of the new variable created with the returned value. |
Returns | |
---|---|
Type | Description |
Object | The last object deposited in the stack using the push function. |
Initialize the execution stack with two values and obtain the value using the peek function.
<xsql-script name='pop_test'> <body> <set name='a'><number>10</number></set> <set name='b'><number>2</number></set> <push><b/></push> <push><a/></push> <!-- [2,10] are in the stack. --> <peek name='c' /> <println><c/></println> <!-- 10 --> <!-- [2,10] both remain in the stack. --> <peek name='d' /> <println><d/></println> <!-- 10 --> </body> </xsql-script>