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:

Copy
<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.

Loading...

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).

Loading...

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.

Copy
<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:

Copy

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.

Functions should always be declared before being used.

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.

Copy
<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.

Copy
<xsql-script name='main'>
    <args>
        <arg name='p_name'/>
    </args>
    <body>
        <println>Hello <p_name/>!</println>
    </body>
</xsql-script>
Copy
$ 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.

Copy
<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>
Copy
$ 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.

Copy
<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>
Copy
$ 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.

ADD FORM

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.

EMBEBED SCRIPT
Copy
<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 to f1.
  • f3 is visible to f2.
  • f4 is visible to f3.
  • f5 is visible to f4.

In the following example, the execution of functions follows this order: f1->f2->f3->f4->f5

Copy
<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.

Copy
<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.

Copy
<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:

Copy
<xsql-script>
    <body>

        <return>
            <number>10</number>
            <number>20</number>
        </return>

    </body>
</xsql-script>

The command line interpreter will show:

Copy
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.

Local functions invoked using the function name itself do not accept caching. In other words, caching is only applied for calls to external functions.

The following examples show two scripts. The first can be catalogued as f_sum.xml, an invariant script.

Copy
<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

Copy
<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>
Example

Declare and initialize a string-type variable, then deposit in the routine's execution stack.

Copy
<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'/>
Example

Initialize the execution stack with two values and obtain them using the pop tag.

Copy
<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'/>
Example

Initialize the execution stack with two values and obtain the value using the peek function.

Copy
<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>