<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Chapter 8. SQL-Invoked Routines</title> <link href="../docbook.css" type="text/css" rel="stylesheet"> <meta content="DocBook XSL-NS Stylesheets V1.76.1" name="generator"> <meta name="keywords" content="Hsqldb, HyperSQL, SQL"> <meta name="keywords" content="Hsqldb, HyperSQL, Database, JDBC, Java"> <link rel="home" href="index.html" title="HyperSQL User Guide"> <link rel="up" href="index.html" title="HyperSQL User Guide"> <link rel="prev" href="dataaccess-chapt.html" title="Chapter 7. Data Access and Change"> <link rel="next" href="triggers-chapt.html" title="Chapter 9. Triggers"> </head> <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"> <div class="navheader"> <table summary="Navigation header" width="100%"> <tr> <td align="left" width="30%"><a accesskey="p" href="dataaccess-chapt.html"><img src="../images/db/prev.png" alt="Prev"></a> </td><td align="center" width="40%" style="font-weight:bold;">Chapter 8. SQL-Invoked Routines</td><td align="right" width="30%"> <a accesskey="n" href="triggers-chapt.html"><img src="../images/db/next.png" alt="Next"></a></td> </tr> <tr> <td valign="top" align="left" width="30%">Chapter 7. Data Access and Change </td><td align="center" width="40%"><a accesskey="h" href="index.html"><img src="../images/db/home.png" alt="Home"></a></td><td valign="top" align="right" width="30%"> Chapter 9. Triggers</td> </tr> </table> </div> <HR> <div class="chapter" title="Chapter 8. SQL-Invoked Routines"> <div class="titlepage"> <div> <div> <h2 class="title"> <a name="sqlroutines-chapt"></a>Chapter 8. SQL-Invoked Routines</h2> </div> <div> <div class="authorgroup"> <div class="author"> <h3 class="author"> <span class="firstname">Fred</span> <span class="surname">Toussi</span> </h3> <div class="affiliation"> <span class="orgname">The HSQL Development Group<br> </span> </div> </div> </div> </div> <div> <p class="releaseinfo">$Revision: 5706 $</p> </div> <div> <div class="legalnotice" title="Legal Notice"> <a name="N12FBB"></a> <p>Copyright 2010-2017 Fred Toussi. Permission is granted to distribute this document without any alteration under the terms of the HSQLDB license. Additional permission is granted to the HSQL Development Group to distribute this document with or without alterations under the terms of the HSQLDB license.</p> </div> </div> <div> <p class="pubdate">2017-04-09 14:14:09-0400</p> </div> </div> </div> <div class="toc"> <p> <b>Table of Contents</b> </p> <dl> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_routine_definition">Routine Definition</a></span> </dt> <dd> <dl> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_routine_characteristics">Routine Characteristics</a></span> </dt> </dl> </dd> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_routines">SQL Language Routines (PSM)</a></span> </dt> <dd> <dl> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_advantages">Advantages and Disadvantages</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_statements">Routine Statements</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_compound">Compound Statement</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_table_vars">Table Variables</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_vars">Variables</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_cursors">Cursors</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_handlers">Handlers</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_assignment">Assignment Statement</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_select_single">Select Statement : Single Row</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_formal_parameters">Formal Parameters</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_iterated_statements">Iterated Statements</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_for_statement">Iterated FOR Statement</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_conditional">Conditional Statements</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_return_statement">Return Statement</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_control_statements">Control Statements</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_exceptions">Raising Exceptions</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_routine_polymorphism">Routine Polymorphism</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_returning_data">Returning Data From Procedures</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_recursive_routines">Recursive Routines</a></span> </dt> </dl> </dd> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_routines">Java Language Routines (SQL/JRT)</a></span> </dt> <dd> <dl> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_polymorphis">Polymorphism</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_procedures">Java Language Procedures</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_static_methods">Java Static Methods</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_legacy">Legacy Support</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_access_control">Securing Access to Classes and Routines</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#N1367A">Warning</a></span> </dt> </dl> </dd> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_aggregate_functions">User-Defined Aggregate Functions</a></span> </dt> <dd> <dl> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_aggregate_function_definition">Definition of Aggregate Functions</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_psm_aggregate_functions">SQL PSM Aggregate Functions</a></span> </dt> <dt> <span class="section"><a href="sqlroutines-chapt.html#src_jrt_aggregate_functions">Java Aggregate Functions</a></span> </dt> </dl> </dd> </dl> </div> <p>SQL-invoked routines are functions and procedures called from SQL. HyperSQL 2.4 supports routines conforming to two parts of the SQL Standard. Routines written in the SQL language are supported in conformance to SQL/PSM (Persistent Stored Modules) specification. Routines written in Java are supported in broad conformance to SQL/JRT specification. In addition, HyperSQL's previous non-standard support for calling Java routines without prior method definition is retained and enhanced in the latest version by extending the SQL/JRT specification.</p> <p>HyperSQL also supports user-defined aggregate functions written in the SQL language or Java. This feature is an extension to the SQL Standard.</p> <p>SQL-invoked routines are schema objects. Naming and referencing follows conventions common to all schema objects. The same routine name can be defined in two different schemas and used with schema-qualified references.</p> <p>A routine is either a procedure or a function.</p> <p>A function:</p> <div class="itemizedlist"> <ul class="itemizedlist" type="disc"> <li class="listitem"> <p>is defined with CREATE FUNCTION</p> </li> <li class="listitem"> <p>always returns a single value or a single table</p> </li> <li class="listitem"> <p>does not modify the data in the database</p> </li> <li class="listitem"> <p>is used as part of an SQL statement such as a SELECT statement</p> </li> <li class="listitem"> <p>can have parameters</p> </li> <li class="listitem"> <p>can be polymorphic</p> </li> </ul> </div> <p>A procedure:</p> <div class="itemizedlist"> <ul class="itemizedlist" type="disc"> <li class="listitem"> <p>is defined with CREATE PROCEDURE</p> </li> <li class="listitem"> <p>can return zero to multiple values or result sets</p> </li> <li class="listitem"> <p>can modify the data in the database</p> </li> <li class="listitem"> <p>is called separately, using the CALL statement</p> </li> <li class="listitem"> <p>can have parameters</p> </li> <li class="listitem"> <p>can be polymorphic</p> </li> </ul> </div> <p>Definition of routine signature and characteristics, name resolution and invocation are all implemented uniformly for routines written in SQL or Java.</p> <p>Access to routines can be granted to users with GRANT EXECUTE or GRANT ALL. For example <code class="literal">GRANT EXECUTE ON myroutine TO PUBLIC</code>.</p> <div class="section" title="Routine Definition"> <div class="titlepage"> <div> <div> <h2 class="title" style="clear: both"> <a name="src_routine_definition"></a>Routine Definition</h2> </div> </div> </div> <p>SQL-Invoked Routines, whether PSM or JRT, are defined using a SQL statement with the same syntax. The part that is different is the <routine body> which consists of SQL statements in PSM routines or a reference to a Java method in JRT routines.</p> <p>Details of Routine definition are discussed in this section. You may start by reading the next two sections which provide several examples before reading this section for the details.</p> <p>Routine definition has several mandatory or optional clauses. The complete BNF supported by HyperSQL and the remaining clauses are documented in this section.</p> <a name="N13001" class="indexterm"></a> <p> <span class="bold"><strong>CREATE FUNCTION</strong></span> </p> <a name="N1300A" class="indexterm"></a> <p> <span class="bold"><strong>CREATE PROCEDURE</strong></span> </p> <p> <span class="emphasis"><em>routine definition</em></span> </p> <p>Routine definition is similar for procedures and functions. A function definition has the mandatory <code class="literal"><returns clause></code> which is discussed later. The description given so far covers the essential elements of the specification with the BNF given below.</p> <p> <code class="literal"><schema procedure> ::= CREATE PROCEDURE <schema qualified routine name> <SQL parameter declaration list> <routine characteristics> <routine body></code> </p> <p> <code class="literal"><schema function> ::= CREATE FUNCTION <schema qualified routine name> <SQL parameter declaration list> <returns clause> <routine characteristics> <routine body></code> </p> <p>Parameter declaration list has been described above. For SQL/JRT routines, the <code class="literal"><SQL parameter name></code> is optional while for SQL/PSM routines, it is required. If the <code class="literal"><parameter mode></code> of a parameter is OUT or INOUT, it must be specified. The BNF is given below:</p> <p> <code class="literal"><SQL parameter declaration list> ::= <left paren> [ <SQL parameter declaration> [ { <comma> <SQL parameter declaration> }... ] ] <right paren></code> </p> <p> <code class="literal"><SQL parameter declaration> ::= [ <parameter mode> ] [ <SQL parameter name> ] <parameter type></code> </p> <p> <code class="literal"><parameter mode> ::= IN | OUT | INOUT</code> </p> <p> <code class="literal"><parameter type> ::= <data type></code> </p> <p>Return Value and Table Functions</p> <a name="N13037" class="indexterm"></a> <p> <span class="bold"><strong>RETURNS</strong></span> </p> <p> <span class="emphasis"><em>returns clause</em></span> </p> <p>The <code class="literal"><returns clause></code> specifies the type of the return value of a function (not a procedure). For all SQL/PSM functions and ordinary SQL/JRT functions, this is simply a type definition which can be a built-in type, a DOMAIN type or a DISTINCT type, or alternatively, a TABLE definition. For example, RETURNS INTEGER.</p> <p>For a SQL/JRT function, it is possible to define a <code class="literal"><returns table type></code> for a Java method that returns a <code class="classname">java.sql.ResultSet</code> object. Such SQL/JRT functions are called <em class="glossterm">table functions</em>. Table functions are used differently from normal functions. A table function can be used in an SQL query expression exactly where a normal table or view is allowed. At the time of invocation, the Java method is called and the returned ResultSet is transformed into an SQL table. The column types of the declared TABLE must match those of the ResultSet, otherwise an exception is raised at the time of invocation.</p> <p>If a <code class="literal"><returns table type></code> is defined for an SQL/PSM function, the following expression is used inside the function to return a table: <code class="literal">RETURN TABLE ( <query expression> );</code> In the example blow, a table with two columns is returned.</p> <pre class="programlisting"> RETURN TABLE ( SELECT a, b FROM atable WHERE e = 10 );</pre> <p>Functions that return a table are designed to be used in SELECT statements using the TABLE keyword to form a joined table.</p> <p>When a JDBC <code class="classname">CallableStatement</code> is used to CALL the function, the table returned from the function call is returned and can be accessed with the <code class="methodname">getResultSet()</code> method of the <code class="classname">CallableStatement</code>.</p> <p> <code class="literal"><returns clause> ::= RETURNS <returns type></code> </p> <p> <code class="literal"><returns type> ::= <returns data type> | <returns table type></code> </p> <p> <code class="literal"><returns table type> ::= TABLE <table function column list></code> </p> <p> <code class="literal"><table function column list> ::= <left paren> <table function column list element> [ { <comma> <table function column list element> } ... ] <right paren></code> </p> <p> <code class="literal"><table function column list element> ::= <column name> <data type></code> </p> <p> <code class="literal"><returns data type> ::= <data type></code> </p> <a name="N1307C" class="indexterm"></a> <p> <span class="bold"><strong>routine body</strong></span> </p> <p> <span class="emphasis"><em>routine body</em></span> </p> <p>Routine body is either one or more SQL statements or a Java reference. The user that defines the routine by issuing the CREATE FUNCTION or CREATE SCHEMA command must have the relevant access rights to all tables, sequences, routines, etc. that are accessed by the routine. If another user is given EXECUTE privilege on the routine, then there are two possibilities, depending on the <code class="literal"><rights clause></code>. This clause refers to the access rights that are checked when a routine is invoked. The default is <code class="literal">SQL SECURITY DEFINER</code>, which means access rights of the definer are used; therefore no extra checks are performed when the other user invokes the routine. The alternative <code class="literal">SQL SECURITY INVOKER</code> means access rights on all the database objects referenced by the routine are checked for the invoker. This alternative is not supported by HyperSQL.</p> <p> <code class="literal"><routine body> ::= <SQL routine spec> | <external body reference></code> </p> <p> <code class="literal"><SQL routine spec> ::= [ <rights clause> ] <SQL routine body></code> </p> <p> <code class="literal"><rights clause> ::= SQL SECURITY INVOKER | SQL SECURITY DEFINER</code> </p> <a name="N1309C" class="indexterm"></a> <p> <span class="bold"><strong>SQL routine body</strong></span> </p> <p> <span class="emphasis"><em>SQL routine body</em></span> </p> <p>The routine body of a an SQL routine consists of an statement.</p> <p> <code class="literal"><SQL routine body> ::= <SQL procedure statement></code> </p> <a name="N130AD" class="indexterm"></a> <p> <span class="bold"><strong>EXTERNAL NAME</strong></span> </p> <p> <span class="emphasis"><em>external body reference</em></span> </p> <p>External name specifies the qualified name of the Java method associated with this routine. HyperSQL 2.3 only supports Java methods within the classpath. The <code class="literal"><external Java reference string></code> is a quoted string which starts with CLASSPATH: and is followed by the Java package, class and method names separated with dots. HyperSQL does not currently support the optional <code class="literal"><Java parameter declaration list></code>.</p> <p> <code class="literal"><external body reference> ::= EXTERNAL NAME <external Java reference string></code> </p> <p> <code class="literal"><external Java reference string> ::= <jar and class name> <period> <Java method name> [ <Java parameter declaration list> ]</code> </p> <div class="section" title="Routine Characteristics"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_routine_characteristics"></a>Routine Characteristics</h3> </div> </div> </div> <p>The <code class="literal"><routine characteristics></code> clause covers several sub-clauses</p> <p> <code class="literal"><routine characteristics> ::= [ <routine characteristic>... ]</code> </p> <p> <code class="literal"><routine characteristic> ::= <language clause> | <parameter style clause> | SPECIFIC <specific name> | <deterministic characteristic> | <SQL-data access indication> | <null-call clause> | <returned result sets characteristic> | <savepoint level indication></code> </p> <a name="N130D6" class="indexterm"></a> <p> <span class="bold"><strong>LANGUAGE</strong></span> </p> <p> <span class="emphasis"><em>language clause</em></span> </p> <p>The <code class="literal"><language clause></code> refers to the language in which the routine body is written. It is either SQL or Java. The default is SQL, so JAVA must be specified for SQL/JRT routines.</p> <p> <code class="literal"><language clause> ::= LANGUAGE <language name></code> </p> <p> <code class="literal"><language name> ::= SQL | JAVA</code> </p> <p>The parameter style is not allowed for SQL routines. It is optional for Java routines and, in HyperSQL, the only value allowed is JAVA.</p> <p> <code class="literal"><parameter style> ::= JAVA</code> </p> <a name="N130F2" class="indexterm"></a> <p> <span class="bold"><strong>SPECIFIC NAME</strong></span> </p> <p> <span class="emphasis"><em>specific name</em></span> </p> <p>The <code class="literal">SPECIFIC <specific name></code> clause is optional but the engine will creates an automatic name if it is not present. When there are several versions of the same routine, the <code class="literal"><specific name></code> is used in schema manipulation statements to drop or alter a specific version. The <code class="literal"><specific name></code> is a user-defined name. It applies to both functions and procedures. In the examples below, a specific name is specified for each function.</p> <pre class="programlisting"> CREATE FUNCTION an_hour_before_or_now(t TIMESTAMP) RETURNS TIMESTAMP NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA SPECIFIC an_hour_before_or_now_with_timestamp EXTERNAL NAME 'CLASSPATH:org.npo.lib.nowLessAnHour' CREATE FUNCTION an_hour_before_max (e_type INT) RETURNS TIMESTAMP SPECIFIC an_hour_before_max_with_int RETURN (SELECT MAX(event_time) FROM atable WHERE event_type = e_type) - 1 HOUR </pre> <a name="N1310B" class="indexterm"></a> <p> <span class="bold"><strong>DETERMINISTIC</strong></span> </p> <p> <span class="emphasis"><em>deterministic characteristic</em></span> </p> <p>The <code class="literal"><deterministic characteristic></code> clause indicates that a routine is deterministic or not. Deterministic means the routine does not reference random values, external variables, or time of invocation. The default is <code class="literal">NOT DETERMINISTIC</code>. It is essential to declare this characteristics correctly for an SQL/JRT routine, as the engine does not know the contents of the Java code, which could include calls to methods returning random or time sensitive values.</p> <p> <code class="literal"><deterministic characteristic> ::= DETERMINISTIC | NOT DETERMINISTIC</code> </p> <a name="N13122" class="indexterm"></a> <p> <span class="bold"><strong>SQL DATA access</strong></span> </p> <p> <span class="emphasis"><em>SQL DATA access characteristic</em></span> </p> <p>The <code class="literal"><SQL-data access indication></code> clause indicates the extent to which a routine interacts with the database or the data stored in the database tables in different schemas (SQL DATA).</p> <p>NO SQL means no SQL command is issued in the routine body and can be used only for SQL/JRT functions.</p> <p> <code class="literal">CONTAINS SQL</code> means some SQL commands are used, but they do not read or modify the SQL data. <code class="literal">READS SQL DATA</code> and <code class="literal">MODIFIES SQL DATA</code> are self explanatory.</p> <p>A <code class="literal">CREATE PROCEDURE</code> definition can use <code class="literal">MODIFIES SQL DATA</code>. This is not allowed in <code class="literal">CREATE FUNCTION</code>. Note that a PROCEDURE or a FUNCTION may have internal tables or return a table which is populated by the routine's statements. These tables are not considered SQL DATA, therefore there is no need to specify <code class="literal">MODIFIES SQL DATA</code> for such routines.</p> <p> <code class="literal"><SQL-data access indication> ::= NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA</code> </p> <a name="N13150" class="indexterm"></a> <p> <span class="bold"><strong>NULL INPUT</strong></span> </p> <p> <span class="emphasis"><em>null call clause</em></span> </p> <p>Null Arguments</p> <p>The <code class="literal"><null-call clause></code> is used only for functions. If a function returns NULL when any of the calling arguments is null, then by specifying <code class="literal">RETURNS NULL ON NULL INPUT</code>, calls to the function are known to be redundant and do not take place when an argument is null. This simplifies the coding of the SQL/JRT Java methods and improves performance at the same time.</p> <p> <code class="literal"><null-call clause> ::= RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT</code> </p> <a name="N13169" class="indexterm"></a> <p> <span class="bold"><strong>SAVEPOINT LEVEL</strong></span> </p> <p> <span class="emphasis"><em>transaction impact</em></span> </p> <p>The <code class="literal"><savepoint level indication></code> is used only for procedures and refers to the visibility of existing savepoints within the body of the procedure. If <code class="literal">NEW SAVEPOINT LEVEL</code> is specified, savepoints that have been declared prior to calling the procedure become invisible within the body of the procedure. HyperSQL’s implementation accepts only <code class="literal">NEW SAVEPOINT LEVEL</code>.</p> <p> <code class="literal"><savepoint level indication> ::= NEW SAVEPOINT LEVEL | OLD SAVEPOINT LEVEL</code> </p> <a name="N13183" class="indexterm"></a> <p> <span class="bold"><strong>DYNAMIC RESULT SETS</strong></span> </p> <p> <span class="emphasis"><em>returned result sets characteristic</em></span> </p> <p>The <code class="literal"><returned result sets characteristic></code> is used with SQL/PSM and SQL/JRT procedures (not with functions). The maximum number of result sets that a procedure may return can be specified with the clause below. The default is zero. If you want your procedure to return result sets, you must specify the maximum number of result sets that your procedure may return. Details are discussed in the next sections.</p> <p> <code class="literal"><returned result sets characteristic> ::= DYNAMIC RESULT SETS <maximum returned result sets></code> </p> </div> </div> <div class="section" title="SQL Language Routines (PSM)"> <div class="titlepage"> <div> <div> <h2 class="title" style="clear: both"> <a name="src_psm_routines"></a>SQL Language Routines (PSM)</h2> </div> </div> </div> <p>The PSM (Persistent Stored Module) specification extends the SQL language with structures and control statements such as conditional and loop statements. Both SQL Function and SQL procedure bodies use the same syntax, with minor exceptions.</p> <p>The routine body is a SQL statement. In its simplest form, the body is a single SQL statement. A simple example of a function is given below:</p> <pre class="programlisting"> CREATE FUNCTION an_hour_before (t TIMESTAMP) RETURNS TIMESTAMP RETURN t - 1 HOUR </pre> <p>An example of the use of the function in an SQL statement is given below:</p> <pre class="programlisting"> SELECT an_hour_before(event_timestamp) AS notification_timestamp, event_name FROM events;</pre> <p>A simple example of a procedure is given below:</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(firstname VARCHAR(50), lastname VARCHAR(50)) MODIFIES SQL DATA INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP) </pre> <p>The procedure inserts a row into an existing table with the definition given below:</p> <pre class="programlisting"> CREATE TABLE customers(id INTEGER GENERATED BY DEFAULT AS IDENTITY, firstname VARCHAR(50), lastname VARCHAR(50), added TIMESTAMP);</pre> <p>An example of the use of the procedure is given below:</p> <pre class="programlisting"> CALL new_customer('JOHN', 'SMITH');</pre> <p>The routine body is often a compound statement. A compound statement can contain one or more SQL statements, which can include control statements, as well as nested compound statements.</p> <p>Please note carefully the use of <code class="literal"><semicolon></code>, which is required at the end of some statements but not accepted at the end of others.</p> <div class="section" title="Advantages and Disadvantages"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_advantages"></a>Advantages and Disadvantages</h3> </div> </div> </div> <p>SQL Language Routines (PSM) have certain advantages over Java Language Routines (SQL/JRT) and a couple of disadvantages.</p> <div class="itemizedlist"> <ul class="itemizedlist" type="disc"> <li class="listitem"> <p>SQL language routines (PSM) do not rely on custom Java classes to be present on the classpath. The databases that use them are therefore more portable.</p> </li> <li class="listitem"> <p>For a routine that accesses SQL DATA, all the SQL statements in an SQL routine are known and monitored by the engine. The engine will not allow a table, routine or sequence that is referenced in an SQL routine to be dropped, or its structure modified in a way that will break the routine execution. The engine does not keep this information about a Java routine.</p> </li> <li class="listitem"> <p>Because the statements in an SQL routine are known to the engine, the execution of an SQL routine locks all the database objects it needs to access before the actual execution. With Java routines, locks are obtained during execution and this may cause additional delays in multi threaded access to the database.</p> </li> <li class="listitem"> <p>For routines that do not access SQL DATA, Java routines (SQL/JRT) may be faster if they perform extensive calculations.</p> </li> <li class="listitem"> <p>Only Java routines can access external programs and resources directly.</p> </li> </ul> </div> </div> <div class="section" title="Routine Statements"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_statements"></a>Routine Statements</h3> </div> </div> </div> <p>The following SQL Statements can be used only in routines. These statements are covered in this section.</p> <p> <code class="literal"><handler declaration></code> </p> <p> <code class="literal"><table variable declaration></code> </p> <p> <code class="literal"><variable declaration></code> </p> <p> <code class="literal"><declare cursor></code> </p> <p> <code class="literal"><assignment statement></code> </p> <p> <code class="literal"><compound statement></code> </p> <p> <code class="literal"><case statement></code> </p> <p> <code class="literal"><if statement></code> </p> <p> <code class="literal"><while statement></code> </p> <p> <code class="literal"><repeat statement></code> </p> <p> <code class="literal"><for statement></code> </p> <p> <code class="literal"><loop statement></code> </p> <p> <code class="literal"><iterate statement</code> </p> <p> <code class="literal"><leave statement></code> </p> <p> <code class="literal"><signal statement></code> </p> <p> <code class="literal"><resignal statement></code> </p> <p> <code class="literal"><return statement></code> </p> <p> <code class="literal"><select statement: single row></code> </p> <p> <code class="literal"><open statement></code> </p> <p>The following SQL Statements can be used in procedures but not in generally in functions (they can be used in functions only to change the data in a local table variable) . These statements are covered in other chapters of this Guide.</p> <p> <code class="literal"><call statement></code> </p> <p> <code class="literal"><delete statement></code> </p> <p> <code class="literal"><insert statement></code> </p> <p> <code class="literal"><update statement></code> </p> <p> <code class="literal"><merge statement></code> </p> <p>Transaction statements such as COMMIT and ROLLBACK are not allowed in the body of a function or procedure. When the session is in auto-commit mode, the commit takes place after the execution of the whole procedure has been completed. No commit is performed during the execution.</p> <p>As shown in the examples below, the formal parameters and the variables of the routine can be used in statements, similar to the way a column reference is used.</p> </div> <div class="section" title="Compound Statement"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_compound"></a>Compound Statement</h3> </div> </div> </div> <p>A compound statement is enclosed in a BEGIN / END block with optional labels. It can contain one or more <code class="literal"><SQL variable declaration></code>, <code class="literal"><declare cursor></code> or <code class="literal"><handler declaration></code> before at least one SQL statement. The BNF is given below:</p> <p> <code class="literal"><compound statement> ::= [ <beginning label> <colon> ] BEGIN [[NOT] ATOMIC]</code> </p> <p> <code class="literal">[{<SQL variable declaration> <semicolon>} ...]</code> </p> <p> <code class="literal">[{<declare cursor> <semicolon>} ...]</code> </p> <p> <code class="literal">[{<handler declaration> <semicolon>}...] </code> </p> <p> <code class="literal">{<SQL procedure statement> <semicolon>} ... </code> </p> <p> <code class="literal">END [ <ending label> ]</code> </p> <p>An example of a simple compound statement body is given below. It performs the common task of inserting related data into two table. The IDENTITY value that is automatically inserted in the first table is retrieved using the IDENTITY() function and inserted into the second table. Other examples show more complex compound statements.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(firstname VARCHAR(50), lastname VARCHAR(50), address VARCHAR(100)) MODIFIES SQL DATA BEGIN ATOMIC INSERT INTO customers VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); INSERT INTO addresses VALUES (DEFAULT, IDENTITY(), address); END </pre> </div> <div class="section" title="Table Variables"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_table_vars"></a>Table Variables</h3> </div> </div> </div> <p>A <code class="literal"><table variable declaration></code> defines the name and columns of a local table, that can be used in the routine body. The table cannot have constraints. Table variable declarations are made before scalar variable declarations.</p> <pre class="programlisting"> BEGIN ATOMIC DECLARE TABLE temp_table (col_a INT, col_b VARCHAR(20); DECLARE temp_id INTEGER; -- more statements END </pre> </div> <div class="section" title="Variables"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_vars"></a>Variables</h3> </div> </div> </div> <p>A <code class="literal"><variable declaration></code> defines the name and data type of the variable and, optionally, its default value. In the next example, a variable is used to hold the IDENTITY value. In addition, the formal parameters of the procedure are identified as input parameters with the use of the optional IN keyword. This procedure does exactly the same job as the procedure in the previous example.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(IN firstname VARCHAR(50), IN lastname VARCHAR(50), IN address VARCHAR(100)) MODIFIES SQL DATA BEGIN ATOMIC DECLARE temp_id INTEGER; INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); SET temp_id = IDENTITY(); INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address); END </pre> <p>The BNF for variable declaration is given below:</p> <a name="N1325F" class="indexterm"></a> <p> <span class="bold"><strong>DECLARE variable</strong></span> </p> <p> <span class="emphasis"><em>SQL variable declaration</em></span> </p> <p> <code class="literal"><SQL variable declaration> ::= DECLARE <variable name list> <data type> [DEFAULT <default value>]</code> </p> <p> <code class="literal"><variable name list> ::= <variable name> [ { <comma> <variable name> }... ]</code> </p> <p>Examples of variable declaration are given below. Note that in a DECLARE statement with multiple comma-separated variable names, the type and the default value applies to all the variables in the list:</p> <pre class="programlisting"> BEGIN ATOMIC DECLARE temp_zero DATE; DECLARE temp_one, temp_two INTEGER DEFAULT 2; DECLARE temp_three VARCHAR(20) DEFAULT 'no name'; -- more statements ... SET temp_zero = DATE '2010-03-18'; SET temp_two = 5; -- more statements ... END</pre> </div> <div class="section" title="Cursors"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_cursors"></a>Cursors</h3> </div> </div> </div> <p>A <code class="literal"><declare cursor></code> statement is used to declare a SELECT statement. The current usage of this statement in HyperSQL 2.3 is exclusively to return a result set from a procedure. The result set is returned to the JDBC <code class="classname">CallableStatement</code> object that calls the procedure. The <code class="methodname">getResultSet()</code> method of <code class="classname">CallableStatement</code> is then used to retrieve the JDBC ResultSet.</p> <p>In the <code class="literal"><routine definition></code>, the <code class="literal">DYNAMIC RESULT SETS</code> clause must be used to specify a value above zero. The <code class="literal">DECLARE CURSOR</code> statement is used after any variable declaration in compound statement block. The SELECT statement should be followed with FOR READ ONLY to avoid possible error messages. The <code class="literal"><open statement></code> is then executed for the cursor at the point where the result set should be populated.</p> <p>After the procedure is executed with a JDBC <code class="methodname">CallableStatement execute()</code> method, all the result sets that were opened are returned to the JDBC <code class="classname">CallableStatement</code>.</p> <p>Calling <code class="methodname">getResultSet()</code> will return the first ResultSet. When there are multiple result sets, the <code class="methodname">getMoreResults()</code> method of the Callable statement is called to move to the next ResultSet, before <code class="methodname">getResultSet()</code> is called to return the next ResultSet. See the <a class="link" href="dataaccess-chapt.html" title="Chapter 7. Data Access and Change">Data Access and Change</a> chapter on the syntax for declaring the cursor.</p> <pre class="programlisting"> BEGIN ATOMIC DECLARE temp_zero DATE; DECLARE result CURSOR WITH RETURN FOR SELECT * FROM INFORMATION_SCHEMA.TABLES FOR READ ONLY; -- more statements ... OPEN result; END </pre> </div> <div class="section" title="Handlers"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_handlers"></a>Handlers</h3> </div> </div> </div> <p>A <code class="literal"><handler declaration></code> defines the course of action when an exception or warning is raised during the execution of the compound statement. A compound statement may have one or more handler declarations. These handlers become active when code execution enters the compound statement block and remain active in any sub-block and statement within the block. The handlers become inactive when code execution leaves the block.</p> <p>In the previous example of the <code class="literal">new_customer</code> procedure, if an exception is thrown during the execution of either SQL statement, the execution of the compound statement is terminated and the exception is propagated and thrown by the CALL statement for the procedure. All changes made by the procedure are rolled back.</p> <p>A handler declaration can resolve the thrown exception within the compound statement without propagating it, and allow the execution of the compound statement to continue.</p> <p>In the example below, the <code class="literal">UNDO</code> handler declaration catches any exception that is thrown during the execution of the compound statement inside the <code class="literal">BEGIN ... END</code> block. As it is an <code class="literal">UNDO</code> handler, all the changes to data performed within the compound statement ( <code class="literal">BEGIN ... END</code> block) are rolled back. The procedure then returns without throwing an exception.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(IN firstname VARCHAR(50), IN lastname VARCHAR(50), IN address VARCHAR(100)) MODIFIES SQL DATA label_one: BEGIN ATOMIC DECLARE temp_id INTEGER; DECLARE UNDO HANDLER FOR SQLEXCEPTION; INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); SET temp_id = IDENTITY(); INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address); END </pre> <p>Other types of hander are <code class="literal">CONTINUE</code> and <code class="literal">EXIT</code> handlers. A <code class="literal">CONTINUE</code> handler ignores any exception and proceeds to the next statement in the block. An <code class="literal">EXIT</code> handler terminates execution without undoing the data changes performed by the previous (successful) statements.</p> <p>The conditions can be general conditions, or specific conditions.</p> <p>Among general conditions that can be specified, <code class="literal">SQLEXCEPTION</code> covers all exceptions, <code class="literal">SQLWARNING</code> covers all warnings, while <code class="literal">NOT FOUND</code> covers the not-found condition, which is raised when a DELETE, UPDATE, INSERT or MERGE statement completes without actually affecting any row.</p> <p>Alternatively, one or more specific conditions can be specified (separated with commas) which apply to specific exceptions or warnings or classes or exceptions or warnings. A specific condition is specified with <code class="literal">SQLSTATE <value></code>, for example <code class="literal">SQLSTATE 'W_01003'</code> specifies the warning raised after a SQL statement is executed which contains an aggregate function which encounters a null value during execution. An example is given below which activates the handler when either of the two warnings is raised:</p> <pre class="programlisting"> DECLARE UNDO HANDLER FOR SQLSTATE 'W_01003', 'W_01004';</pre> <p>The BNF for <code class="literal"><handler declaration></code> is given below:</p> <a name="N132F8" class="indexterm"></a> <p> <span class="bold"><strong>DECLARE HANDLER</strong></span> </p> <p> <span class="emphasis"><em>declare handler statement</em></span> </p> <p> <code class="literal"><handler declaration> ::= DECLARE {UNDO | CONTINUE | EXIT} HANDLER FOR {SQLEXCEPTION | SQLWARNING | NOT FOUND} | { SQLSTATE <state value> [, ...]} [<SQL procedure statement>];</code> </p> <p>A handler declaration may specify an <code class="literal"><SQL procedure statement></code> to be performed when the handler is activated. In the example below the handler performs the <code class="literal">UNDO</code> as in the previous example then inserts the (invalid) data into a separate table.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(IN firstname VARCHAR(50), IN lastname VARCHAR(50), IN address VARCHAR(100)) MODIFIES SQL DATA label_one: BEGIN ATOMIC DECLARE temp_id INTEGER; DECLARE UNDO HANDLER FOR SQLEXCEPTION INSERT INTO invalid_customers VALUES(firstanme, lastname, address); -- last statement is part of the handler; it is called if the next statements throw an exception INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); SET temp_id = IDENTITY(); INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address); END </pre> <p>The <code class="literal"><SQL procedure statement></code> in the handler declaration is required by the SQL Standard but is optional in HyperSQL. If the execution of the <code class="literal"><SQL procedure statement></code> specified in the handler declaration throws an exception itself, then it is handled by the handlers that are currently active at an enclosing (outer) <code class="literal">BEGIN ... END</code> block. The <code class="literal"><SQL procedure statement></code> can itself be a compound statement with its own handlers.</p> <p>When a handler handles an exception condition such as the general <code class="literal">SQLEXCEPTION</code> or some specific <code class="literal">SQLSTATE</code>, any changes made by the statement that caused the exception will be rolled back. For example, execution of a single update statement that modifies several rows will not change any row if an exception occurs during the update of one of the rows. The handler action affects the changes made by statements that were executed successfully before the exception occured.</p> <p>Actions performed by different types of handler are listed below:</p> <div class="itemizedlist"> <ul class="itemizedlist" type="disc"> <li class="listitem"> <p>An <code class="literal">UNDO</code> handler rolls back all the data changes within the <code class="literal">BEGIN ... END</code> block which contains the handler declaration. The execution of the <code class="literal">BEGIN ... END</code> block is considered complete. If an <code class="literal"><SQL procedure statement></code> is specified, it is executed after the roll back.</p> </li> <li class="listitem"> <p>A <code class="literal">CONTINUE</code> handler does not roll back the data changes. It continues execution as if the last statement was successful. If an <code class="literal"><SQL procedure statement></code> is specified, it is executed before continuing execution.</p> </li> <li class="listitem"> <p>An <code class="literal">EXIT</code> handler does not roll back the data changes. It aborts the execution of the <code class="literal">BEGIN ... END</code> block which contains the handler declaration. The execution of the <code class="literal">BEGIN ... END</code> block is considered complete, but unlike the <code class="literal">UNDO</code> handler the actions are not rolled back. If an <code class="literal"><SQL procedure statement></code> is specified, it is executed before aborting.</p> </li> </ul> </div> </div> <div class="section" title="Assignment Statement"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_assignment"></a>Assignment Statement</h3> </div> </div> </div> <p>The SET statement is used for assignment. It can be used flexibly with rows or single values. The BNF is given below:</p> <p> <code class="literal"><assignment statement> ::= <singleton variable assignment> | <multiple variable assignment></code> </p> <p> <code class="literal"><singleton variable assignment> ::= SET <assignment target> <equals operator> <assignment source></code> </p> <p> <code class="literal"><multiple variable assignment> ::= SET (<variable or parameter>, ...) = <row value expression></code> </p> <p>In the example below, the result of the SELECT is assigned to two OUT or INOUT arguments. The SELECT must return one row. If it returns more than one, an exception is raised. If it returns no row, no change is made to ARG1 and ARG2.</p> <pre class="programlisting"> SET (arg1, arg2) = (SELECT col1, col2 FROM atable WHERE id = 10);</pre> <p>In the example below, the result of a function call is assigned to VAR1.</p> <pre class="programlisting"> SET var1 = SQRT(var2);</pre> </div> <div class="section" title="Select Statement : Single Row"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_select_single"></a>Select Statement : Single Row</h3> </div> </div> </div> <p>A special form of SELECT can also be used for assigning values from a query to one or more arguments or variables. This works similar to a SET statement that has a SELECT statement as the source.</p> <a name="N13371" class="indexterm"></a> <p> <span class="bold"><strong>SELECT : SINGLE ROW</strong></span> </p> <p> <span class="emphasis"><em>select statement: single row</em></span> </p> <p> <code class="literal"><select statement: single row> ::= SELECT [ <set quantifier> ] <select list> INTO <select target list> <table expression></code> </p> <p> <code class="literal"><select target list> ::= <target specification> [ { <comma> <target specification> }... ]</code> </p> <p>Retrieve values from a specified row of a table and assign the fields to the specified targets. The example below has an identical effect to the example of SET statement given above.</p> <pre class="programlisting">SELECT col1, col2 INTO arg1, arg2 FROM atable WHERE id = 10;</pre> </div> <div class="section" title="Formal Parameters"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_formal_parameters"></a>Formal Parameters</h3> </div> </div> </div> <p>Each parameter of a procedure can be defined as IN, OUT or INOUT. An IN parameter is an input to the procedure and is passed by value. The value cannot be modified inside the procedure body. An OUT parameter is a reference for output. An INOUT parameter is a reference for both input and output. An OUT or INOUT parameter argument is passed by reference, therefore only a dynamic parameter argument or a variable within an enclosing procedure can be passed for it. The assignment statement is used to assign a value to an OUT or INOUT parameter.</p> <p>In the example below, the procedure is declared with an OUT parameter. It assigns the auto-generated IDENTITY value from the INSERT statement to the OUT argument.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(OUT newid INT, IN firstname VARCHAR(50), IN lastname VARCHAR(50), IN address VARCHAR(100)) MODIFIES SQL DATA BEGIN ATOMIC DECLARE temp_id INTEGER; INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); SET temp_id = IDENTITY(); INSERT INTO ADDRESSES VALUES (DEFAULT, temp_id, address); SET newid = temp_id; END </pre> <p>In the SQL session, or in the body of another stored procedure, a variable must be assigned to the OUT parameter. After the procedure call, this variable will hold the new identity value that was generated inside the procedure. If the procedure is called directly, using the JDBC CallableStatement interface, then the value of the first, OUT argument can be retrieved with a call to <code class="literal">getInt(1)</code>after calling the execute() method.</p> <p>In the example below, a session variable, <code class="literal">the_new_id</code> is declared. After the call to <code class="literal">new_customer</code>, the value for the identity is stored in <code class="literal">the_new_id</code> variable. This is returned via the next CALL statement. Alternatively, <code class="literal">the_new_id</code> can be used as an argument to another CALL statement.</p> <pre class="programlisting"> DECLARE the_new_id INT DEFAULT NULL; CALL new_customer(the_new_id, 'John', 'Smith', '10 Parliament Square'); CALL the_new_id; </pre> </div> <div class="section" title="Iterated Statements"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_iterated_statements"></a>Iterated Statements</h3> </div> </div> </div> <p>Various iterated statements can be used in routines. In these statements, the <code class="literal"><SQL statement list></code> consists of one or more SQL statements. The <code class="literal"><search condition></code> can be any valid SQL expression of BOOLEAN type.</p> <a name="N133B2" class="indexterm"></a> <p> <span class="bold"><strong>LOOP</strong></span> </p> <p> <span class="emphasis"><em>loop statement</em></span> </p> <p> <code class="literal"><loop statement> ::= [ <beginning label> <colon> ] LOOP <SQL statement list> END LOOP [ <ending label> ]</code> </p> <a name="N133C1" class="indexterm"></a> <p> <span class="bold"><strong>WHILE</strong></span> </p> <p> <span class="emphasis"><em>while statement</em></span> </p> <p> <code class="literal"><while statement> ::= [ <beginning label> <colon> ] WHILE <search condition> DO <SQL statement list> END WHILE [ <ending label> ]</code> </p> <a name="N133D0" class="indexterm"></a> <p> <span class="bold"><strong>REPEAT</strong></span> </p> <p> <span class="emphasis"><em>repeat statement</em></span> </p> <p> <code class="literal"><repeat statement> ::= [ <beginning label> <colon> ]</code> </p> <p> <code class="literal">REPEAT <SQL statement list> UNTIL <search condition> END REPEAT [ <ending label></code> </p> <p>In the example below, a multiple rows are inserted into a table in a WHILE loop:</p> <pre class="programlisting"> loop_label: WHILE my_var > 0 DO INSERT INTO CUSTOMERS VALUES (DEFAULT, my_var); SET my_var = my_var - 1; IF my_var = 10 THEN SET my_var = 8; END IF; IF my_var = 22 THEN LEAVE loop_label; END IF; END WHILE loop_label; </pre> </div> <div class="section" title="Iterated FOR Statement"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_for_statement"></a>Iterated FOR Statement</h3> </div> </div> </div> <p>The <code class="literal"><for statement></code> is similar to other iterated statement, but it is always used with a cursor declaration to iterate over the rows of the result set of the cursor and perform operations using the values of each row.</p> <a name="N133EF" class="indexterm"></a> <p> <span class="bold"><strong>FOR</strong></span> </p> <p> <span class="emphasis"><em>for statement</em></span> </p> <p> <code class="literal"><for statement> ::= [ <beginning label> <colon> ] FOR <query expression> DO <SQL statement list> END FOR [ <ending label> ]</code> </p> <p>The <query expression> is a SELECT statement. When the FOR statement is executed, the query expression is executed first and the result set is formed. Then for each row of the result set, the <code class="literal"><SQL statement list></code> is executed. What is special about the FOR statement is that all the columns of the current row can be accessed by name in the statements in the <code class="literal"><SQL statement list></code>. The columns are read only and cannot be updated. For example, if the column names for the select statement are ID, FIRSTNAME, LASTNAME, then these can be accessed as a variable name. The column names must be unique and not equivalent to any parameter or variable name in scope.</p> <p>The FOR statement is useful for computing values over multiple rows of the result set, or for calling a procedure for some row of the result set.</p> <p>In the example below, the procedure uses a FOR statement to iterate over the rows for a customer with lastname equal to lastname_p. No action is performed for the first row, but for all the subsequent rows, the row is deleted from the table.</p> <p>Note the following: The result set for the SELECT statement is built only once, before processing the statements inside the FOR block begins. For all the rows of the SELECT statement apart from the first row, the row is deleted from the customer table. The WHERE condition uses the automatic variable id, which holds the customer.id value for the current row of the result set, to delete the row. The procedure updates the val_p argument and when it returns, the val_p represents the total count of rows with the given lastname before the duplicates were deleted.</p> <pre class="programlisting"> CREATE PROCEDURE test_proc(INOUT val_p INT, IN lastname_p VARCHAR(20)) MODIFIES SQL DATA BEGIN ATOMIC SET val_p = 0; for_label: FOR SELECT * FROM customer WHERE lastname = lastname_p DO IF val_p > 0 THEN DELETE FROM customer WHERE customer.id = id; END IF; SET val_p = val_p + 1; END FOR for_label; END </pre> </div> <div class="section" title="Conditional Statements"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_conditional"></a>Conditional Statements</h3> </div> </div> </div> <p>There are two types of CASE ... WHEN statement and the IF ... THEN statement.</p> <a name="N13414" class="indexterm"></a> <p> <span class="bold"><strong>CASE WHEN</strong></span> </p> <p> <span class="emphasis"><em>case when statement</em></span> </p> <p>The simple case statement uses a <code class="literal"><case operand></code> as the predicand of one or more predicates. For the right part of each predicate, it specifies one or more SQL statements to execute if the predicate evaluates TRUE. If the ELSE clause is not specified, at least one of the search conditions must be true, otherwise an exception is raised.</p> <p> <code class="literal"><simple case statement> ::= CASE <case operand> <simple case statement when clause>... [ <case statement else clause> ] END CASE</code> </p> <p> <code class="literal"><simple case statement when clause> ::= WHEN <when operand list> THEN <SQL statement list></code> </p> <p> <code class="literal"><case statement else clause> ::= ELSE <SQL statement list></code> </p> <p>A skeletal example is given below. The variable var_one is first tested for equality with 22 or 23 and if the test evaluates to TRUE, then the INSERT statement is performed and the statement ends. If the test does not evaluate to TRUE, the next condition test, which is an IN predicate, is performed with var_one and so on. The statement after the ELSE clause is performed if none the previous tests returns TRUE.</p> <pre class="programlisting">CASE var_one WHEN 22, 23 THEN INSERT INTO t_one ...; WHEN IN (2, 4, 5) THEN DELETE FROM t_one WHERE ...; ELSE UPDATE t_one ...; END CASE </pre> <p>The searched case statement uses one or more search conditions, and for each search condition, it specifies one or more SQL statements to execute if the search condition evaluates TRUE. An exception is raised if there is no ELSE clause and none of the search conditions evaluates TRUE.</p> <p> <code class="literal"><searched case statement> ::= CASE <searched case statement when clause>... [ <case statement else clause> ] END CASE</code> </p> <p> <code class="literal"><searched case statement when clause> ::= WHEN <search condition> THEN <SQL statement list></code> </p> <p>The example below is partly a rewrite of the previous example, but a new condition is added:</p> <pre class="programlisting"> CASE WHEN var_one = 22 OR var_one = 23 THEN INSERT INTO t_one ...; WHEN var_one IN (2, 4, 5) THEN DELETE FROM t_one WHERE ...; WHEN var_two IS NULL THEN UPDATE t_one ...; ELSE UPDATE t_one ...; END CASE </pre> <a name="N1343E" class="indexterm"></a> <p> <span class="bold"><strong>IF</strong></span> </p> <p> <span class="emphasis"><em>if statement</em></span> </p> <p>The if statement is very similar to the searched case statement. The difference is that no exception is raised if there is no ELSE clause and no search condition evaluates TRUE.</p> <p> <code class="literal"><if statement> ::= IF <search condition> <if statement then clause> [ <if statement elseif clause>... ] [ <if statement else clause> ] END IF</code> </p> <p> <code class="literal"><if statement then clause> ::= THEN <SQL statement list></code> </p> <p> <code class="literal"><if statement elseif clause> ::= ELSEIF <search condition> THEN <SQL statement list></code> </p> <p> <code class="literal"><if statement else clause> ::= ELSE <SQL statement list></code> </p> </div> <div class="section" title="Return Statement"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_return_statement"></a>Return Statement</h3> </div> </div> </div> <p>The RETURN statement is required and used only in functions. The body of a function is either a RETURN statement, or a compound statement that contains a RETURN statement.</p> <p>The return value of a FUNCTION can be assigned to a variable, or used inside an SQL statement.</p> <p>An SQL/PSM function or an SQL/JRT function can return a single result when the function is defined as RETURNS TABLE ( .. )</p> <p>To return a table from a SELECT statement, you should use a return statement such as RETURN TABLE( SELECT ...) in an SQL/PSM function. For an SQL/JRT function, the Java method should return a JDBCResultSet instance.</p> <p>To call a function from JDBC, use a <code class="classname">java.sql.CallableStatement</code> instance. The <code class="literal">getResultSet()</code> call can be used to access the ResultSet returned from a function that returns a result set. If the function returns a scalar value, the returned result has a single column and a single row which contains the scalar returned value.</p> <a name="N1346C" class="indexterm"></a> <p> <span class="bold"><strong>RETURN</strong></span> </p> <p> <span class="emphasis"><em>return statement</em></span> </p> <p> <code class="literal"><return statement> ::= RETURN <return value></code> </p> <p> <code class="literal"><return value> ::= <value expression> | NULL</code> </p> <p>Return a value from an SQL function. If the function is defined as RETURNS TABLE, then the value is a TABLE expression such as RETURN TABLE(SELECT ...) otherwise, the value expression can be any scalar expression. In the examples below, the same function is written with or without a BEGIN END block. In both versions, the RETURN value is a scalar expression.</p> <pre class="programlisting"> CREATE FUNCTION an_hour_before_max (e_type INT) RETURNS TIMESTAMP RETURN (SELECT MAX(event_time) FROM atable WHERE event_type = e_type) - 1 HOUR CREATE FUNCTION an_hour_before_max (e_type INT) RETURNS TIMESTAMP BEGIN ATOMIC DECLARE max_event TIMESTAMP; SET max_event = SELECT MAX(event_time) FROM atable WHERE event_type = e_type; RETURN max_event - 1 HOUR; END </pre> </div> <div class="section" title="Control Statements"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_control_statements"></a>Control Statements</h3> </div> </div> </div> <p>In addition to the RETURN statement, the following statements can be used in specific contexts.</p> <p>ITERATE STATEMENT</p> <p>The ITERATE statement can be used to cause the next iteration of a labelled iterated statement (a WHILE, REPEAT or LOOP statement). It is similar to the "continue" statement in C and Java.</p> <p> <code class="literal"><iterate statement> ::= ITERATE <statement label></code> </p> <p>LEAVE STATEMENT</p> <p>The LEAVE statement can be used to leave a labelled block. When used in an iterated statement, it is similar to the "break" statement is C and Java. But it can be used in compound statements as well.</p> <p> <code class="literal"><leave statement> ::= LEAVE <statement label></code> </p> </div> <div class="section" title="Raising Exceptions"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_exceptions"></a>Raising Exceptions</h3> </div> </div> </div> <p>Signal and Resignal Statements allow the routine to throw an exception. If used with the IF or CASE conditions, the exception is thrown conditionally.</p> <a name="N1349C" class="indexterm"></a> <p> <span class="bold"><strong>SIGNAL</strong></span> </p> <p> <span class="emphasis"><em>signal statement</em></span> </p> <p>The SIGNAL statement is used to throw an exception (or force an exception). When invoked, any exception handler for the given exception is in turn invoked. If there is no handler, the exception is propagated to the enclosing context. In its simplest form, when there is no exception handler for the given exception, routine execution is halted, any change of data is rolled back and the routine throws the exception. By default, the message for the exception is taken from the predefined exception message for the specified SQLSTATE. A custom message can be specified with the optional SET clause.</p> <p> <code class="literal"><signal statement> ::= SIGNAL SQLSTATE <state value> [ SET MESSAGE_TEXT = <character string literal> ] </code> </p> <a name="N134AD" class="indexterm"></a> <p> <span class="bold"><strong>RESIGNAL</strong></span> </p> <p> <span class="emphasis"><em>resignal statement</em></span> </p> <p>The RESIGNAL statement is used to throw an exception from an exception handler's <code class="literal"><SQL procedure statement></code>, in effect propagating the exception to the enclosing context without further action by the currently active handlers. By default, the message for the exception is taken from the predefined exception message for the specified SQLSTATE. A custom message can be specified with the optional SET clause.</p> <p> <code class="literal"><resignal statement> ::= RESIGNAL SQLSTATE <state value> [ SET MESSAGE_TEXT = <character string literal> ]</code> </p> </div> <div class="section" title="Routine Polymorphism"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_routine_polymorphism"></a>Routine Polymorphism</h3> </div> </div> </div> <p>More than one version of a routine can be created.</p> <p>For procedures, the different versions must have different parameter counts. When the procedure is called, the parameter count determines which version is called.</p> <p>For functions, the different versions can have the same or different parameter counts. When the parameter count of two versions of a function is the same, the type of parameters must be different. When the function is called, the best matching version of the function is used, according to both the parameter count and parameter types. The return type of different versions of a function can be the same or different.</p> <p>Two versions of an overloaded function are given below. One version accepts TIMESTAMP while the other accepts TIME arguments.</p> <pre class="programlisting"> CREATE FUNCTION an_hour_before_or_now(t TIMESTAMP) RETURNS TIMESTAMP IF t > CURRENT_TIMESTAMP THEN RETURN CURRENT_TIMESTAMP; ELSE RETURN t - 1 HOUR; END IF CREATE FUNCTION an_hour_before_or_now(t TIME) RETURNS TIME CASE t WHEN > CURRENT_TIME THEN RETURN CURRENT_TIME; WHEN >= TIME'01:00:00' THEN RETURN t - 1 HOUR; ELSE RETURN CURRENT_TIME; END CASE </pre> <p>It is perfectly possible to have different versions of the routine as SQL/JRT or SQL/PSM routines.</p> </div> <div class="section" title="Returning Data From Procedures"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_returning_data"></a>Returning Data From Procedures</h3> </div> </div> </div> <p>The OUT or INOUT parameters of a PROCEDURE are used to assign simple values to dynamic parameters or to variables in the calling context.</p> <p>According to the Standard, an SQL/PSM or SQL/JRT procedure may also return result sets to the calling context. These result sets are dynamic in the sense that a procedure may return a different number of result sets or none at all in different invocations. The SQL Standard uses a mechanism called CURSORS for accessing and modifying rows of a result set one by one. This mechanism is necessary when the database is accessed from an external application program. The JDBC ResultSet interface allows this method of access from Java programs and is supported by HyperSQL.</p> <p>HyperSQL support this method of returning single or multiple result sets from SQL/PSM procedures only via the JDBC CallableStatement interface. Cursors are declared and opened within the body of the procedure. No further operation is performed on the cursors within the procedure. When the execution of the procedure is complete, the cursors become available as Java ResultSet objects via the CallableStatement instance that called the SQL/PSM procedure.</p> <p>The JDBC CallableStatement class is used with the SQL statement <code class="literal">CALL <routine name> ( <argument 1>, ... )</code> to call procedures (also to call functions). After the call to execute(), the <code class="literal">getXXX()</code> methods can be used to retrieve INOUT or OUT arguments after the call. The <code class="methodname">getMoreResults()</code> method and the <code class="methodname">getResultSet()</code> method can be used to access the ResultSet(s) returned by a procedure that returns one or more results. If the procedure returns more than one result set, the <code class="methodname">getMoreResults()</code> call moves to the next result.</p> <p>In the example below, the procedure inserts a row into the customer table. It then performs the SELECT statement to return the latest inserted row as a result set. Therefore the definition includes the <code class="code">DYNAMIC RESULT SETS 1</code> clause. You must specify correctly the maximum number of result sets that the procedure may return.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(firstname VARCHAR(50), lastname VARCHAR(50)) MODIFIES SQL DATA DYNAMIC RESULT SETS 1 BEGIN ATOMIC DECLARE result CURSOR FOR SELECT * FROM CUSTOMERS WHERE ID = IDENTITY(); INSERT INTO CUSTOMERS VALUES (DEFAULT, firstname, lastname, CURRENT_TIMESTAMP); OPEN result; END </pre> <p>The above procedure is called in Java using a CallableStatement</p> <pre class="programlisting"> Connection conn = ...; CallableStatement call = conn.prepareCall("call new_customer(?, ?)"); call.setString(1, "Paul"); call.setString(2, "Smith"); call.execute(); if (call.getMoreResults()) ResultSet result = call.getResultSet(); </pre> <p>In the example below a procedure has one IN argument and two OUT arguments. The JDBC CallableStatement is used to retrieve the values returned in the OUT arguments.</p> <pre class="programlisting"> CREATE PROCEDURE get_customer(IN id INT, OUT firstname VARCHAR(50), OUT lastname VARCHAR(50)) READS SQL DATA BEGIN ATOMIC -- this statement uses the id to get firstname and lastname SELECT first_name, last_name INTO firstname, lastname FROM customers WHERE cust_id = id; END Connection conn = ...; CallableStatement call = conn.prepareCall("call get_customer(?, ?, ?)"); call.setInt(1, 121); // only the IN (or INOUT) arguments should be set before the call call.execute(); String firstname = call.getString(2); // the OUT (or INOUT) arguments are retrieved after the call String lastname = call.getString(3); </pre> <p>SQL/JRT procedures are discussed in the Java Language Procedures section below. Those routines are called exactly the same way as SQL/PSM procedures, using the JDBC CallableStatement interface.</p> <p>It is also possible to use a JDBC Statement or PreparedStatement object to call a procedure if the procedure arguments are constant. If the procedure returns one or more result sets, the <code class="methodname">Statement.getMoreResults()</code> method should be called before retrieving the ResultSet.</p> <p>An SQL/JRT or SQL/PSM funtion (as opposed to procedure) returns either a value or a table in a ResultSet. Functions are called from JDBC similar to procedures, but with functions, the <code class="methodname">getMoreResuls()</code> method should not be called at all. The <code class="methodname">getResulSet()</code> method is called after calling the <code class="methodname">execute()</code> method.</p> </div> <div class="section" title="Recursive Routines"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_recursive_routines"></a>Recursive Routines</h3> </div> </div> </div> <p>Routines can be recursive. Recursive functions are often functions that return arrays or tables. To create a recursive routine, the routine definition must be created first with a dummy body. Then the ALTER ROUTINE statement is used to define the routine body.</p> <p>In the example below, the table contains a tree of rows each with a parent. The routine returns an array containing the id list of all the direct and indirect children of the given parent. The routine appends the array variable id_list with the id of each direct child and for each child appends the array with the id array of its children by calling the routine recursively.</p> <p>The routine can be used in a SELECT statement as the example shows.</p> <pre class="programlisting"> CREATE TABLE ptree (pid INT, id INT); INSERT INTO ptree VALUES (NULL, 1) ,(1,2), (1,3),(2,4),(4,5),(3,6),(3,7); -- the function is created and always throws an exception when used CREATE FUNCTION child_arr(p_pid INT) RETURNS INT ARRAY SPECIFIC child_arr_one READS SQL DATA SIGNAL SQLSTATE '45000' -- the actual body of the function is defined, replacing the statement that throws the exception ALTER SPECIFIC ROUTINE child_arr_one BEGIN ATOMIC DECLARE id_list INT ARRAY DEFAULT ARRAY[]; for_loop: FOR SELECT id FROM ptree WHERE pid = p_pid DO SET id_list[CARDINALITY(id_list) + 1] = id; SET id_list = id_list || child_arr(id); END FOR for_loop; RETURN id_list; END -- the function can now be used in SQL statements SELECT * FROM TABLE(child_arr(2)) </pre> <p>In the next example, a table with two columns is returned instead of an array. In this example, a local table variable is declared and filled with the children and the children's children.</p> <pre class="programlisting"> CREATE FUNCTION child_table(p_pid INT) RETURNS TABLE(r_pid INT, r_id INT) SPECIFIC child_table_one READS SQL DATA SIGNAL SQLSTATE '45000' ALTER SPECIFIC ROUTINE child_table_one BEGIN ATOMIC DECLARE TABLE child_tree (pid INT, id INT); for_loop: FOR SELECT pid, id FROM ptree WHERE pid = p_pid DO INSERT INTO child_tree VALUES pid, id; INSERT INTO child_tree SELECT r_pid, r_id FROM TABLE(child_table(id)); END FOR for_loop; RETURN TABLE(SELECT * FROM child_tree); END SELECT * FROM TABLE(child_table(1)) </pre> <p>Infinite recursion is not possible as the routine is terminated when a given depth is reached.</p> </div> </div> <div class="section" title="Java Language Routines (SQL/JRT)"> <div class="titlepage"> <div> <div> <h2 class="title" style="clear: both"> <a name="src_jrt_routines"></a>Java Language Routines (SQL/JRT)</h2> </div> </div> </div> <p>The general features of SQL-Invoked Routines are shared between PSM and JRT routines. These features are covered in the previous section. This section deals with specific aspects of JRT routines.</p> <p>The body of a Java language routine is a static method of a Java class, specified with a fully qualified method name in the routine definition. A simple CREATE FUNCTION example is given below, which defines the function to call the <code class="literal">java.lang.Math.sinh(double d)</code> Java method. The function can be called in SQL statements just like any built-in function.</p> <pre class="programlisting"> CREATE FUNCTION sinh(v DOUBLE) RETURNS DOUBLE LANGUAGE JAVA DETERMINISTIC NO SQL EXTERNAL NAME 'CLASSPATH:java.lang.Math.sinh' SELECT sinh(doublecolumn) FROM mytable </pre> <p>In the example below, the static method named <code class="methodname">toZeroPaddedString</code> is specified to be called when the function is invoked.</p> <pre class="programlisting"> CREATE FUNCTION zero_pad(x BIGINT, digits INT, maxsize INT) RETURNS CHAR VARYING(100) LANGUAGE JAVA DETERMINISTIC NO SQL EXTERNAL NAME 'CLASSPATH:org.hsqldb.lib.StringUtil.toZeroPaddedString' </pre> <p>The signature of the Java method (used in the Java code but not in SQL code to create the function) is given below:</p> <pre class="programlisting"> public static String toZeroPaddedString(long value, int precision, int maxSize)</pre> <p>The parameter and return types of the SQL routine definition must match those of the Java method according to the table below:</p> <div class="informaltable"> <table cellspacing="0" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "> <colgroup> <col> <col> </colgroup> <tbody> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>SMALLINT </p> </td><td style="border-bottom: 0.5pt solid ; "> <p>short or Short</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>INT</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>int or Integer</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>BIGINT</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>long or Long</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>NUMERIC or DECIMAL</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>BigDecimal</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>FLOAT or DOUBLE</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>double or Double</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>CHAR or VARCHAR</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>String</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>DATE</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>java.sql.Date</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>TIME</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>java.sql.Time</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">TIME WITH TIME ZONE</td><td style="border-bottom: 0.5pt solid ; ">java.time.OffsetTime</td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>TIMESTAMP</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>java.sql.Timestamp</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">TIMSTAMP WITH TIME ZONE</td><td style="border-bottom: 0.5pt solid ; ">java.time.OffsetDateTime</td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">INTERVAL MONTH</td><td style="border-bottom: 0.5pt solid ; ">java.time.Period</td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">INTERVAL SECOND</td><td style="border-bottom: 0.5pt solid ; ">java.time.Duration</td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>BINARY</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>byte[]</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">VARBINARY</td><td style="border-bottom: 0.5pt solid ; ">byte[]</td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; "> <p>BOOLEAN</p> </td><td style="border-bottom: 0.5pt solid ; "> <p>boolean or Boolean</p> </td> </tr> <tr> <td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; ">ARRAY of any type</td><td style="border-bottom: 0.5pt solid ; ">java.sql.Array</td> </tr> <tr> <td style="border-right: 0.5pt solid ; "> <p>TABLE</p> </td><td style=""> <p>java.sql.ResultSet</p> </td> </tr> </tbody> </table> </div> <p>For OUT and INOUT parameters of procedures Java arrays of the type given in the table above should be used as parameters For example if the OUT parameter is defined as VARCHAR(10), it matches a Java parameter type defined as <code class="classname">String[]</code>.</p> <p>If the specified Java method is not found or its parameters and return types do not match the definition, an exception is raised. If more than one version of the Java method exist, then the one with matching parameter and return types is found and registered. If two “equivalent” methods exist, the first one is registered. (This situation arises only when a parameter is a primitive in one version and an Object in another version, e.g. <code class="classname">long</code> and <code class="classname">java.lang.Long</code>.).</p> <p>When the Java method of an SQL/JRT routine returns a value, it should be within the size and precision limits defined in the return type of the SQL-invoked routine, otherwise an exception is raised. Any difference in numeric scale is ignored and corrected. For example, in the above example, the <code class="literal">RETURNS CHAR VARYING(100)</code> clause limits the length of the strings returned from the Java method to 100. But if the number of digits after the decimal point (scale) of a returned BigDecimal value is larger than the scale specified in the RETURNS clause, the decimal fraction is silently truncated and no exception of warning is raised.</p> <p>When the function is specified as RETURNS TABLE(...) the static Java method should return a JDBCResultSet instance. For an example of how to construct a <code class="classname">JDBCResultSet</code> for this purpose, see the source code for the <code class="classname">org.hsqldb.jdbc.JDBCArray</code> class.</p> <div class="section" title="Polymorphism"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_polymorphis"></a>Polymorphism</h3> </div> </div> </div> <p>If two versions of the same SQL invoked routine with different parameter types are required, they can be defined to point to the same method name or different method names, or even methods in different classes. In the example below, the first two definitions refer to the same method name in the same class. In the Java class, the two static methods are defined with corresponding method signatures.</p> <p>In the third example, the Java function returns a result set and the SQL declaration includes RETURNS TABLE.</p> <pre class="programlisting"> CREATE FUNCTION an_hour_before_or_now(t TIME) RETURNS TIME NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'CLASSPATH:org.npo.lib.nowLessAnHour' CREATE FUNCTION an_hour_before_or_now(t TIMESTAMP) RETURNS TIMESTAMP NO SQL LANGUAGE JAVA PARAMETER STYLE JAVA EXTERNAL NAME 'CLASSPATH:org.npo.lib.nowLessAnHour' CREATE FUNCTION testquery(i INTEGER) RETURNS TABLE(n VARCHAR(20), i INT) READS SQL DATA LANGUAGE JAVA EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.TestJavaFunctions.getQueryResult' </pre> <p>In the Java class the definitions are as follows. Note the definition of the <code class="methodname">getQueryResult()</code> method begins with a <code class="classname">java.sql.Connection</code> parameter. This parameter is ignored when choosing the Java method. The parameter is used to pass the current JDBC connection to the Java method.</p> <pre class="programlisting"> public static java.sql.Time nowLessAnHour(java.sql.Time value) { ... } public static java.sql.Timestamp nowLessAnHour(java.sql.Timestamp value) ... } public static ResultSet getQueryResult(Connection connection, int i) throws SQLException { Statement st = connection.createStatement(); return st.executeQuery("SELECT * FROM T WHERE I < " + i); } </pre> </div> <div class="section" title="Java Language Procedures"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_procedures"></a>Java Language Procedures</h3> </div> </div> </div> <p>Java procedures are defined similarly to functions. The differences are:</p> <div class="itemizedlist"> <ul class="itemizedlist" type="disc"> <li class="listitem"> <p>The return type of the Java static method must be void.</p> </li> <li class="listitem"> <p>If a parameter is defined as OUT or INOUT, the corresponding Java static method parameter must be defined as an array of the JDBC non-primitive type.</p> </li> <li class="listitem"> <p>When the Java static method is invoked, the OUT and INOUT arguments are passed to the Java method as a single-element array.</p> </li> <li class="listitem"> <p>The static method can modify the OUT or INOUT argument by assigning a value to the sole element of the argument array.</p> </li> <li class="listitem"> <p>A procedure can return one or more result sets. These are instantiated as JDBC ResultSet objects by the Java static and returned in array arguments of the method. The signature of the Java method for a procedure that has N declared parameters and returns M result sets has the following pattern. The N parameters corresponding to the signature of the declared SQL procedure are defined first, followed by M parameters as ResultSet arrays.</p> <p>When the SQL procedure is executed, the Java method is called with single element array arguments passed for OUT and INOUT SQL parameters, and single element arrays of ResultSet for the returned ResultSet objects. The Java method may call the <code class="methodname">execute()</code> or <code class="methodname">executeQuery()</code> methods of JDBC Statement or PreparedStatement objects that are declared within the method and assign the ResultSet objects to the first element of each ResultSet[] argument. For the returned ResultSet objects, the Java method should not call the methods of <code class="classname">java.sql.ResultSet</code> before returning.</p> <p> <code class="literal">void methodName(<arg1>, ... <argN>, ResultSet[] r1, ..., ResultSet[] rM)</code> </p> </li> <li class="listitem"> <p>If the procedure contains SQL statements, only statements for data access and manipulation are allowed. The Java method should not perform commit or rollback. The SQL statements should not change the session settings and should not include statements that create or alter tables or other database objects. These rules are generally enforced by the engine, but additional enforcement may be added in future versions</p> </li> </ul> </div> <p>An example of a procedure definition, together with its Java signature, is given below. This procedure is the SQL/JRT version of the example discussed above for SQL/PSM.</p> <pre class="programlisting"> CREATE PROCEDURE get_customer(IN id INT, OUT firstname VARCHAR(50), OUT lastname VARCHAR(50)) READS SQL DATA LANGUAGE JAVA EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.Test01.getCustomerProcedure' public static void getCustomerProcedure(int id, String[] firstn, String[] lastn) throws java.sql.SQLException { firstn[0] = somevalue; // parameter out value is assigned lastn[0] = somevalue; // parameter out value is assigned } </pre> <p>In the next example a procedure is defined to return a result set. The signature of the Java method is also given. The Java method assigns a ResultSet object to the zero element of the result parameter. The result parameter is always the last one and is declared after the normal IN and OUT parameters.</p> <pre class="programlisting"> CREATE PROCEDURE new_customer(firstname VARCHAR(50), lastname VARCHAR(50)) MODIFIES SQL DATA LANGUAGE JAVA DYNAMIC RESULT SETS 1 EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.Test01.newCustomerProcedure' public static void newCustomerProcedure(String firstn, String lastn, ResultSet[] result) throws java.sql.SQLException { result[0] = someresultset; // dynamic result set is assigned } </pre> <p>You may want to return your own data in the ResultSet that is returned from an SQL/JRT procedure or function. The <code class="classname">org.hsqldb.jdbc.JDBCResultSet</code> has two static factory methods that return instances of the <code class="classname">JDBCResultSetBasic</code> class. Refer to the source code to see how you can use this class in your Java static methods. You can use the <code class="classname">org.hsqldb.jdbc.JDBCArrayBasic</code> class to create a JDBC Array in your Java static method. This class also includes code to construct a <code class="classname">JDBCResultSetBasic</code> instance.</p> <p>Java language procedures SQL/JRT are used in an identical manner to SQL/PSM routines. See the section under SQL/PSM routines, Returning Data From Procedures, on how to use the JDBC CallableStatement interface to call the procedure and to get the OUT and INOUT arguments and to use the ResultSet objects returned by the procedure.</p> </div> <div class="section" title="Java Static Methods"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_static_methods"></a>Java Static Methods</h3> </div> </div> </div> <p>The static methods that are used for procedures and functions must be declared in a public class. The methods must be declared as public static. For functions, the method return type must be one of the JDBC supported types. The IN parameters of the method must be declared as one of the supported types. The OUT and INOUT parameters must be declared as Java arrays of supported types. If the SQL definition of a function includes RETURNS NULL ON NULL INPUT, then the IN parameters of the Java static function can be int or long primitives, otherwise, they must be Integer or Long. The declared Java arrays for OUT and INOUT parameters for SQL INTEGER or BIGINT must be Integer[] or Long[] respectively.</p> <p>If the SQL definition of the routine includes NO SQL, then no JDBC method call is allowed to execute in the method body. Otherwise, a JDBC Connection can be used within the Java method to access the database. If the definition includes CONTAINS SQL, then no table data can be read. If the definition includes READS SQL DATA, then no table data can be modified. If the definition includes MODIFIES SQL DATA, then data can be modified. In all modes, it is not allowed to execute DDL statements that change the schema definition.</p> <p>It is possible to use DECLARE LOCAL TEMPORARY TABLE in a Java method, as this is in the session scope.</p> <p>There are two ways to use the JDBC Connection object.</p> <div class="orderedlist"> <ol class="orderedlist" type="1"> <li class="listitem"> <p>Define the Java method with a Connection parameter as the first parameter. This parameter is "hidden" and only visible to the engine. The rest of the parameters, if any, are used to choose the method according to the required types of parameters.</p> </li> <li class="listitem"> <p>Use the SQL/JRT Standard <code class="literal">"jdbc:default:connection"</code> method. With this approach, the Java method does not include a Connection parameter. In the method body, the connection is established with a method call to DriverManager, as in the example below:</p> <p> <code class="literal">Connection con = DriverManager.getConnection("jdbc:default:connection");</code> </p> </li> </ol> </div> <p>Both methods return a connection that is based on the current session. This connection has some extra properties, for example, the Close() method does not actually close it.</p> <p>An example of an SQL PROCEDURE with its Java method definition is given below. The CREATE PROCEDURE statement is the same with or without the Connection parameter:</p> <pre class="programlisting"> CREATE PROCEDURE proc1(IN P1 INT, IN P2 INT, OUT P3 INT) SPECIFIC P2 LANGUAGE JAVA DETERMINISTIC MODIFIES SQL DATA EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.TestStoredProcedure.procTest2'"); </pre> <p>In the first example, the <code class="literal">"jdbc:default:connection"</code> method is used. In the second example, a connection parameter is used</p> <pre class="programlisting"> public static void procTest2(int p1, int p2, Integer[] p3) throws java.sql.SQLException { Connection conn = DriverManager.getConnection("jdbc:default:connection"); java.sql.Statement stmt = conn.createStatement(); stmt.execute("INSERT INTO MYTABLE VALUES(" + p1 + ",'test1')"); stmt.execute("INSERT INTO MYTABLE VALUES(" + p2 + ",'test2')"); java.sql.ResultSet rs = stmt.executeQuery("select * from MYTABLE"); java.sql.ResultSetMetaData meta = rs.getMetaData(); int cols = meta.getColumnCount(); p3[0] = Integer.valueOf(cols); rs.close(); stmt.close(); } // alternative declaration with Connection parameter // public static void procTest2(Connection conn, int p1, int p2, // Integer[] p3) throws java.sql.SQLException { </pre> <p>When the stored procedure is called by the user's program, the value of the OUT parameter can be read after the call.</p> <pre class="programlisting"> // a CallableStatement is used to prepare the call // the OUT parameter contains the return value CallableStatement c = conn.prepareCall("call proc1(1,2,?)"); c.execute(); int value = c.getInt(1); </pre> </div> <div class="section" title="Legacy Support"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_legacy"></a>Legacy Support</h3> </div> </div> </div> <p>The legacy HyperSQL statement, <code class="literal">CREATE ALIAS <name> FOR <fully qualified Java method name></code> is no longer supported directly. It is supported when importing databases and translates to a special <code class="literal">CREATE FUNCTION <name></code> statement that creates the function in the PUBLIC schema.</p> <p>The direct use of a Java method as a function is still supported but deprecated. It is internally translated to a special <code class="literal">CREATE FUNCTION</code> statement where the name of the function is the double quoted, fully qualified name of the Java method used.</p> </div> <div class="section" title="Securing Access to Classes and Routines"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_access_control"></a>Securing Access to Classes and Routines</h3> </div> </div> </div> <p>By default, the static methods of any class that is on the classpath are available to be used. This can compromise security in some systems. The optional Java system property <code class="literal">hsqldb.method_class_names</code> allows preventing access to classes other than <code class="literal">java.lang.Math</code> or specifying a semicolon-separated list of allowed classes. A property value that ends with .* is treated as a wild card and allows access to all class or method names formed by substitution of the * (asterisk).</p> <p>In the example below, the property has been included as an argument to the Java command.</p> <pre class="programlisting"> java -Dhsqldb.method_class_names="org.me.MyClass;org.you.YourClass;org.you.lib.*" [the rest of the command line] </pre> <p>The above example allows access to the methods in the two classes: <code class="classname">org.me.MyClass</code> and <code class="classname">org.you.YourClass</code> together with all the classes in the <code class="classname">org.you.lib</code> package. Note that if the property is not defined, no access control is performed at this level.</p> <p>The user who creates a Java routine must have the relevant access privileges on the tables that are used inside the Java method.</p> <p>Once the routine has been defined, the normal database access control applies to its user. The routine can be executed only by those users who have been granted EXECUTE privileges on it. Access to routines can be granted to users with GRANT EXECUTE or GRANT ALL. For example <code class="literal">GRANT EXECUTE ON myroutine TO PUBLIC</code>.</p> </div> <div class="section" title="Warning"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="N1367A"></a>Warning</h3> </div> </div> </div> <p>The definition of SQL/JRT routines referencing the user's Java static methods is stored in the .script file of the database.</p> <p>If the database is opened in a Java environment that does not have access to the referenced Java static methods on its classpath, the SQL/JRT routines are not created when the database is opened. When the database is closed, the routine definitions are lost.</p> <p>There is a workaround to prevent opening the database when the static method are not on the classpath. You can create an SQL/PSM procedure which calls all the SQL/JRT functions and procedures in your database. The calls should have the necessary dummy arguments. This procedure will fail to be created when the referenced methods are not accessible and will return "Error in script file". There is no need ever to execute the procedure. However, to avoid accidental use, you can ensure that it does not execute the SQL/JRT routines by adding a line such as <code class="literal">IF TRUE THEN SIGNAL SQLSTATE '45000';</code> before any references to the SQL/JRT routines.</p> </div> </div> <div class="section" title="User-Defined Aggregate Functions"> <div class="titlepage"> <div> <div> <h2 class="title" style="clear: both"> <a name="src_aggregate_functions"></a>User-Defined Aggregate Functions</h2> </div> </div> </div> <p>HyperSQL adds an extension to the SQL Standard to allow user-defined aggregate functions. A user-defined aggregate function has a single parameter when it is used in SQL statements. Unlike the predefined aggregate functions, the keyword DISTINCT cannot be used when a user-defined aggregate function is invoked. Like all user-defined functions, an aggregate function belongs to a schema and can be polymorphic (with multiple function definitions with the same name but different parameter types).</p> <p>A user-defined aggregate function can be used in SQL statements where a predefined aggregate function is allowed.</p> <div class="section" title="Definition of Aggregate Functions"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_aggregate_function_definition"></a>Definition of Aggregate Functions</h3> </div> </div> </div> <p>An aggregate function is always defined with 4 parameters. The first parameter is the parameter that is used when the function is invoked in SQL statements, the rest of the parameter are invisible to the invoking SQL statement. The type of the first parameter is user defined. The type of the second parameter must be BOOLEAN. The third and fourth parameters have user-defined types and must be defined as INOUT parameters. The defined return type of the function determines the type of the value returned when the function is invoked.</p> <a name="N13694" class="indexterm"></a> <p> <span class="bold"><strong>CREATE AGGREGATE FUNCTION</strong></span> </p> <p> <span class="emphasis"><em>user defined aggregate function definition</em></span> </p> <p>Aggregate function definition is similar to normal function definition and has the mandatory <code class="literal"><returns clause></code>. The BNF is given below.</p> <p> <code class="literal"><user defined aggregate function> ::= CREATE AGGREGATE FUNCTION <schema qualified routine name> <SQL aggregate parameter declaration list> <returns clause> <routine characteristics> <routine body></code> </p> <p>The parameter declaration list BNF is given below. The type of the first parameter is used when the function is invoked as part of an SQL statement. When multiple versions of a function are required, each version will have the first parameter of a different type.</p> <p> <code class="literal"><SQL aggregate declaration list> ::= <left paren> [IN] [ <SQL parameter name> ] <parameter type> <comma> [IN] [ <SQL parameter name> ] BOOLEAN <comma> INOUT [ <SQL parameter name> ] <parameter type> <comma> INOUT [ <SQL parameter name> ] <parameter type> <right paren></code> </p> <p>The return type is user defined. This is the type of the resulting value when the function is called. Usually an aggregate function is defined with CONTAINS SQL, as it normally does not read the data in database tables, but it is possible to define the function with READS SQL DATA and access the database tables.</p> <p>When a SQL statement that uses the aggregate function is executed, HyperSQL invokes the aggregate function, with all the arguments set, once per each row in order to compute the values. Finally, it invokes the function once more to return the final result.</p> <p>In the computation phase, the first argument is the value of the user argument as specified in the SQL statement, computed for the current row. The second argument is the boolean FALSE. The third and fourth argument values can have any type and are initially null, but they can be updated in the body of the function during each invocation. The third and fourth arguments act as registers and hold their values between invocations. The return value of the function is ignored during the computation phase (when the second parameter is FALSE).</p> <p>After the computation phase, the function is invoked once more to get the final result. In this invocation, the first argument is NULL and the second argument is boolean TRUE. The third and fourth arguments hold the values they held at the end of the last invocation. The value returned by the function in this invocation is used as the result of the aggregate function computation in the invoking SQL statement. In SQL queries with GROUP BY, the call sequence is repeated separately for each separate group.</p> </div> <div class="section" title="SQL PSM Aggregate Functions"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_psm_aggregate_functions"></a>SQL PSM Aggregate Functions</h3> </div> </div> </div> <p>The example below features a user-defined version of the Standard <code class="literal">AVG(<value expression>)</code> aggregate function for INTEGER input and output types. This function behaves differently from the Standard AVG function as it returns 0 when all the input values are null.</p> <pre class="programlisting"> CREATE AGGREGATE FUNCTION udavg(IN x INTEGER, IN flag BOOLEAN, INOUT addup BIGINT, INOUT counter INT) RETURNS INTEGER CONTAINS SQL BEGIN ATOMIC IF flag THEN RETURN addup / counter; ELSE SET counter = COALESCE(counter, 0) + 1; SET addup = COALESCE(addup, 0) + COALESCE(x, 0); RETURN NULL; END IF; END </pre> <p>The user-defined aggregate function is used in a select statement in the example below. Only the first parameter is visible and utilised in the select statement.</p> <pre class="programlisting"> SELECT udavg(id) FROM customers GROUP BY lastname;</pre> <p>In the example below, the function returns an array that contains all the values passed for the aggregated column. For use with longer arrays, you can optimise the function by defining a larger array in the first iteration, and using the TRIM_ARRAY function on the RETURN to cut the array to size. This function is similar to the built-in ARRAY_AGG function</p> <pre class="programlisting"> CREATE AGGREGATE FUNCTION array_aggregate(IN val VARCHAR(100), IN flag boolean, INOUT buffer VARCHAR(100) ARRAY, INOUT counter INT) RETURNS VARCHAR(100) ARRAY CONTAINS SQL BEGIN ATOMIC IF flag THEN RETURN buffer; ELSE IF val IS NULL THEN RETURN NULL; END IF; IF counter IS NULL THEN SET counter = 0; END IF; SET counter = counter + 1; IF counter = 1 THEN SET buffer = ARRAY[val]; ELSE SET buffer[counter] = val; END IF; RETURN NULL; END IF; END </pre> <p>The tables and data for the select statement below are created with the DatabaseManager or DatabaseManagerSwing GUI apps. (You can find the SQL in the TestSelf.txt file in the zip). Part of the output is shown. Each row of the output includes an array containing the values for the invoices for each customer.</p> <pre class="programlisting"> SELECT ID, FIRSTNAME, LASTNAME, ARRAY_AGGREGATE(CAST(INVOICE.TOTAL AS VARCHAR(100))) FROM customer JOIN INVOICE ON ID =CUSTOMERID GROUP BY ID, FIRSTNAME, LASTNAME 11 Susanne Karsen ARRAY['3988.20'] 12 John Peterson ARRAY['2903.10','4382.10','4139.70','3316.50'] 13 Michael Clancy ARRAY['6525.30'] 14 James King ARRAY['3665.40','905.10','498.00'] 18 Sylvia Clancy ARRAY['634.20','4883.10'] 20 Bob Clancy ARRAY['3414.60','744.60'] </pre> <p>In the example below, the function returns a string that contains the comma-separated list of all the values passed for the aggregated column. This function is similar to the built in GROUP_CONCAT function.</p> <pre class="programlisting"> CREATE AGGREGATE FUNCTION group_concatenate (IN val VARCHAR(100), IN flag BOOLEAN, INOUT buffer VARCHAR(1000), INOUT counter INT) RETURNS VARCHAR(1000) CONTAINS SQL BEGIN ATOMIC IF FLAG THEN RETURN BUFFER; ELSE IF val IS NULL THEN RETURN NULL; END IF; IF buffer IS NULL THEN SET BUFFER = ''; END IF; IF counter IS NULL THEN SET COUNTER = 0; END IF; IF counter > 0 THEN SET buffer = buffer || ','; END IF; SET buffer = buffer + val; SET counter = counter + 1; RETURN NULL; END IF; END </pre> <p>The same tables and data as for the previous example is used. Part of the output is shown. Each row of the output is a comma-separated list of names.</p> <pre class="programlisting"> SELECT group_concatenate(firstname || ' ' || lastname) FROM customer GROUP BY lastname Laura Steel,John Steel,John Steel,Robert Steel Robert King,Robert King,James King,George King,Julia King,George King Robert Sommer,Janet Sommer Michael Smith,Anne Smith,Andrew Smith Bill Fuller,Anne Fuller Laura White,Sylvia White Susanne Clancy,Michael Clancy,Sylvia Clancy,Bob Clancy,Susanne Clancy,John Clancy </pre> </div> <div class="section" title="Java Aggregate Functions"> <div class="titlepage"> <div> <div> <h3 class="title"> <a name="src_jrt_aggregate_functions"></a>Java Aggregate Functions</h3> </div> </div> </div> <p>A Java aggregate function is defined similarly to PSM functions, apart from the routine body, which is defined as <code class="literal">EXTERNAL NAME ...</code> The Java function signature must follow the rules for both nullable and INOUT parameters, therefore:</p> <p>No argument is defined as a primitive or primitive array type. This allows nulls to be passed to the function. The second and third arguments must be defined as arrays of the JDBC non-primitive types listed in the table in the previous section.</p> <p>In the example below, a user-defined aggregate function for geometric mean is defined.</p> <pre class="programlisting"> CREATE AGGREGATE FUNCTION geometric_mean(IN val DOUBLE, IN flag BOOLEAN, INOUT register DOUBLE, INOUT counter INT) RETURNS DOUBLE NO SQL LANGUAGE JAVA EXTERNAL NAME 'CLASSPATH:org.hsqldb.test.Test01.geometricMean' </pre> <p>The Java function definition is given below:</p> <pre class="programlisting"> public static Double geometricMean(Double in, Boolean flag, Double[] register, Integer[] counter) { if (flag) { if (register[0] == null) { return null; } double a = register[0].doubleValue(); double b = 1 / (double) counter[0]; return Double.valueOf(java.lang.Math.pow(a, b)); } if (in == null) { return null; } if (in.doubleValue() == 0) { return null; } if (register[0] == null) { register[0] = in; counter[0] = Integer.valueOf(1); } else { register[0] = Double.valueOf(register[0].doubleValue() * in.doubleValue()); counter[0] = Integer.valueOf(counter[0].intValue() + 1); } return null; } </pre> <p>In a select statement, the function is used exactly like the built-in aggregate functions:</p> <pre class="programlisting"> SELECT geometric_mean(age) FROM FROM customer </pre> </div> </div> </div> <HR xmlns:xi="http://www.w3.org/2001/XInclude"> <P xmlns:xi="http://www.w3.org/2001/XInclude" class="svnrev">$Revision: 5675 $</P> <div class="navfooter"> <hr> <table summary="Navigation footer" width="100%"> <tr> <td align="left" width="40%"><a accesskey="p" href="dataaccess-chapt.html"><img src="../images/db/prev.png" alt="Prev"></a> </td><td align="center" width="20%"> </td><td align="right" width="40%"> <a accesskey="n" href="triggers-chapt.html"><img src="../images/db/next.png" alt="Next"></a></td> </tr> <tr> <td valign="top" align="left" width="40%">Chapter 7. Data Access and Change </td><td align="center" width="20%"><a accesskey="h" href="index.html"><img src="../images/db/home.png" alt="Home"></a></td><td valign="top" align="right" width="40%"> Chapter 9. Triggers</td> </tr> </table> </div> </body> </html>