Showing posts with label database. Show all posts
Showing posts with label database. Show all posts

Tuesday, February 23, 2016

HSQLDB ignores column aliases in select statement by default

Keywords:
HSQL HSQLDB column alias ignored NullPointerException JSTL

Problem:
I can't pin point where this change happened from the HSQLDB changelogs but somewhere between HSQL 1.8.0.7 and 2.2.5, the handling of column aliases has changed. This is particularly problematic if attempting to lookup via alias name in JSTL where failed lookup of results (due to wrong column names) come back as null.

This is a standalone JSP example:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

<sql:setDataSource var="dataSource" 
    url="jdbc:hsqldb:mem:example/aliasprob"
    driver="org.hsqldb.jdbcDriver"/>
<sql:update dataSource="${dataSource}">
    create table if not exists USERS (
        fullname varchar(255), 
        telephone varchar(255)
    );
</sql:update>
<sql:update dataSource="${dataSource}">
    insert into USERS (fullname, telephone) 
    values ('Joe Bloggs', '123-5555');
</sql:update>
<sql:update dataSource="${dataSource}">
    insert into USERS (fullname, telephone) 
    values ('Fred Twitters', '456-5555');
</sql:update>
<sql:query var="users" dataSource="${dataSource}">
    select fullname as "name"
    , telephone as "phone"
    from USERS
</sql:query>

<!DOCTYPE html>
<html>
    <head>
        <title>Alias Problems</title>
    </head>
    <body>
        <p>These are the users:</p>
        <table border="1">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Phone</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach var="row" items="${users.rows}">
                    <tr>
                        <td>${row['name']}</td>
                        <td>${row['phone']}</td>
                    </tr>            
                </c:forEach>
            </tbody>
        </table>        
 </body>
</html>

<sql:update dataSource="${dataSource}">
    drop table USERS;
</sql:update>

With the above test, you get two rows in the result table with empty cells for the values. If the code above was changed to ignore the aliases used in the 'AS' statements and use the underlying column names the data would be visible. If I have code that uses column aliases in this way does the select statement have to be changed to work with HSQLDB?

Solution:
HSQLDB introduced the get_column_name property which "returns the underlying column name" (despite an alias being used) and is true by default. The documentation also states this is for "compatibility with other JDBC driver implementations". I suspect there's some inconsistency with with way the JSTL SQL tags are using ResultSet.getColumnLabel(int) vs ResultSet.getColumnName(int).

When using JSTL, there's no distinction between column labels or names when using the row['column'] syntax so using get_column_name=false in the HSQL connection URL is a quick fix - making the JSTL behaviour at least, consistent with other JDBC drivers.

In the example code above, this would look like:
<sql:setDataSource var="dataSource" 
    url="jdbc:hsqldb:mem:example/aliasprob;get_column_name=false"
    driver="org.hsqldb.jdbcDriver"/>

Friday, October 30, 2015

Database schema naming for Vendor Neutrality

Keywords:
database vendor-agnostic identifier limitations Oracle SQL Server PostgreSQL HSQL MySQL table column

Problem:
It's surprisingly not a common topic of dicussion, but if you want to author database scripts in a way that is vendor-agnostic the consensus seems to be to conform to ANSI SQL. Are there any guides for how to name the schema identifiers to comply with the various vendor's (character limit) limitations?

Solution:
I couldn't find a good cross-vendor summary, so here's an attempt at looking at the important identifier limitations across some vendors.

Vendor Identifier Limit (characters) Notes
TABLE COLUMN CONSTRAINT
Oracle 30 30 30
https://docs.oracle.com/database/121/SQLRF/sql_elements008.htm#SQLRF51129
PostgreSQL 63 63 63
http://www.postgresql.org/docs/current/interactive/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
MS SQL Server 128 128 128
https://msdn.microsoft.com/en-us/library/ms175874.aspx
MySQL 64 64 64
https://dev.mysql.com/doc/refman/5.0/en/identifiers.html
HSQL 128 128 128
http://hsqldb.org/doc/2.0/guide/databaseobjects-chapt.html#dbc_table_creation
DB2 30 * 30 * 18 *
*Limit increased to 128 from v9.5

https://www-01.ibm.com/support/knowledgecenter/SSEPGG_9.5.0/com.ibm.db2.luw.wn.doc/doc/c0051391.html

If you are concerned with complying with all the above vendors, then the lowest common denominator here is Oracle at 30 characters for table/column & constraints.

Thursday, October 31, 2013

Quick Start to tracing JDBC SQL operations using log4jdbc

Keywords:
database logging show SQL log4jdbc tomcat JNDI log4j trace performance timing

Problem:
Given a complex web application (with many moving parts), there's performance issues that first impressions seem to point at environment-specific database latency. Is there any way to trace/profile/monitor all the SQL statements being made to the database (preferably with timings)?

Solution:
log4jdbc seamlessly lets you configure a logger between your application and the JDBC connection. Documentation on the log4jdbc site is very good and has just about all you need to know, below is a "quick start" for the impatient.

Step 1: Jar File(s)

Drop the log4jdbc4-[version].jar file into [webapp]/WEB-INF/lib.
You'll also need:
  • a supported logging framework (log4j: log4j-[version].jar)
  • the SLF4J API (slf4j-api-[version].jar - already had it)
  • the SLF4J jar to use this logging framework (slf4j-log4j12-[version].jar - already had it)

Step 2: Driver Class and URL

  1. Change the database resource definition to use the driver class net.sf.log4jdbc.DriverSpy ('spy' as it will work out the correct 'real' driver class to use).
  2. Simply prepend jdbc:log4 to the existing URL.

For a PostgreSQL database connection configured as a JNDI/JDBC Resource in Tomcat, this will look like:
    <Resource
            name="jdbc/example"
            type="javax.sql.DataSource"
            factory="org.apache.tomcat.dbcp.dbcp.BasicDataSourceFactory"
            driverClassName="net.sf.log4jdbc.DriverSpy"
            url="jdbc:log4jdbc:postgresql://localhost:5432/example"
            username="postgres" password="***" 
            maxActive="20" maxIdle="10" maxWait="-1" 
            removeAbandoned="true" removeAbandonedTimeout="120" logAbandoned="true"
            auth="Container"
            charset="UTF-8" />

Step 3: Logging Config

There's detailed notes on the five key loggers and an example log4j.properties is available. But if you just want to see SQL + timings, the following will log this all to console:
log4j.logger.jdbc.audit=FATAL,Log4JDBC
log4j.additivity.jdbc.audit=false

log4j.logger.jdbc.resultset=FATAL,Log4JDBC
log4j.additivity.jdbc.resultset=false

log4j.logger.jdbc.sqlonly=FATAL,Log4JDBC
log4j.additivity.jdbc.sqlonly=false

log4j.logger.jdbc.sqltiming=INFO,Log4JDBC
log4j.additivity.jdbc.sqltiming=false

log4j.logger.jdbc.connection=FATAL,Log4JDBC
log4j.additivity.jdbc.connection=false

log4j.appender.Log4JDBC=org.apache.log4j.ConsoleAppender
log4j.appender.Log4JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.Log4JDBC.layout.ConversionPattern=%-5p [%d{DATE} %c]: %m%n


That's it. Start up the application and it should function exactly as it did before, but all SQL operations (made via the JNDI connection pool resource in my example) will be logged in the form:
INFO  [01 Nov 2013 12:00:00,000 jdbc.sqltiming]: SELECT NAME FROM PERSON WHERE ID=54321
{executed in 1 msec}


Notes:

Bonus Step: Trace back to the Application Code

If your issue is working what part of the application code triggered the execution of a given SQL statement, a neat feature of the log4jdbc loggers is including the class & method-name of the code that invoked the JDBC driver. As there may be a few layers between your code and the JDBC driver (hibernate > application server connection pool, etc) you can tell it what level you what to capture from.
  1. change log level to DEBUG
    log4j.logger.jdbc.sqltiming=DEBUG,Log4JDBC
  2. set the system property log4jdbc.debug.stack.prefix
    -Dlog4jdbc.debug.stack.prefix=com.example
Log statements will then be in the form:
DEBUG [01 Nov 2013 12:30:00,000 jdbc.sqltiming]:  com.example.PersonDAO.findPerson(PersonDAO.java:111)
2. SELECT NAME FROM PERSON WHERE ID=54321
{executed in 53 msec}

NB: this extra logging adds a noticeable overhead to the database usage (and timings).



Wednesday, July 25, 2012

Managing Oracle JDBC Memory Usage - Executive Summary

Keywords:
Oracle JDBC memory leak usage java.lang.OutOfMemoryError fetchSize white paper summary 10g 11.2

Problem:
So there appears to be memory issues with your system. Oracle JDBC drivers seem like the prime candidate (based on the heap dumps). The Oracle JDBC Memory Management white paper says as much:
The Oracle JDBC drivers can use large amounts of memory. This is a conscious design choice, to trade off large memory use for improved performance. For the most part and for most users this has proved to be a good choice. Some users have experienced problems with the amount of memory the JDBC drivers use.

The white paper is only a dozen or so pages, but it seems each JDBC version introduces new connection properties or changes the meaning of previously used properties. Is there an executive-summary/cheat-sheet/quick-reference that would essentially say what you can do for a given version?

Solution:
There doesn't seem to be, but I'll have a go ...

First, some summary points:
  • From 9i to 10g performance of the JDBC drivers has been improved "... on average about 30% faster".
  • This was achieved by a greater use a caches. The justification being "memory is relatively cheap".
  • In large scale applications with complex (and batch) data usage, there seem to be two key caches that may cause memory usage issues - the "Implicit Statement Cache" and the "Internal Buffer Cache".
  • Tuning the caches can be done via connection properties (all of which can also be defined as system "-D" properties).
    Tuning should be done in consideration of:
    • the table design (column types and sizes)
    • the query design (columns needed / batches)
    • the fetch size. Setting this incorrectly will definitely cause OutOfMemoryErrors - as seen in JCR-2892.

Now, for the connection properties:

Property

Default

Notes

Applicable for Version

10.0.2.4

11.1.0.6.0

11.1.0.7.0

11.2

oracle.jdbc.freeMemoryOnEnterImplicitCache

false

When true row-data buffers are cleared when a PreparedStatement is cached (in the "Implicit Statement Cache").

Yes (new)

Ignored
(from 11.1.0.6.0 onwards, buffers are put in a new "Internal Buffer Cache")

oracle.jdbc.maxCachedBufferSize

Integer.MAX_VALUE
(2,147,483,647 ~ 2Gb)


Sets an upper limit on the "Internal Buffer Cache".
Look out for the change in meaning from 11.1.0.7 to 11.2 - though if you use a value >30 in 11.2 it will revert to treating it as an integer. Oracle recommends: "... start with 18. If you have to set the value to less than 16, you probably need more memory."
Each connection will (may?) have its own buffer cache. So in a connection pool setup multiply the pool-size by the maxCachedBufferSize.

N/A (no "Internal Buffer Cache")

No (no way to set the size)

Yes
Set as an integer value.
102400 ~ 100Kb

Yes
Set as a log2 value.
18 = 2^18 = 262,144 ~ 256Kb

oracle.jdbc.useThreadLocalBufferCache

false

By storing the buffer cache as a TreadLocal instead of on the Connection you'll save memory if (and only if) there are less Threads than Connections. Avoid if using code that uses Connections across Threads.

N/A

N/A

Yes (new)

Yes

oracle.jdbc.implicitStatementCacheSize

0

An initial size for the "Implicit Statement Cache". But it doesn't seem that setting it to 0 or -1 will disable it, so it can perhaps be ignored for memory management issues - it may improve performance.

N/A

N/A

N/A

Yes (new)

Friday, April 20, 2012

Running Native SQL deletes or updates via Hibernate

Keywords:
hibernate manual native SQL update delete bulkUpdate child table spring

Problem:
Probably not the best idea but for performance, limitations in hibernate mappings or errors in the objects you find it'd be much easier to just run some manual SQL. Can you do this via hibernate?

Solution:
Yes you can, hibernate call this "Native SQL". This is fairly well document, but I noticed its missing an example of doing a manual update/delete - where the result is not going to give you a list() of anything.

For Query you make via createSQLQuery(...), use executeUpdate() which returns the number of rows effected.

Here an example (using the CATS/DOGS table names from the hibernate document):
Query deleteQuery = session.createSQLQuery(
    "delete from CATS "
    + "where DOG_ID in ( "
        + "select d.ID from DOGS d "
        + "where d.STATUS = ?)");
deleteQuery.setString(0, "VICIOUS");
int updated = deleteQuery.executeUpdate();

If you're integrating with hibernate via spring, the same can be done via the template-callback mechanism (where your DAO extends org.springframework.orm.hibernate3.support.HibernateDaoSupport):
Integer deletedData = (Integer)getHibernateTemplate().execute(new HibernateCallback () {
    public Object doInHibernate(Session session) throws HibernateException, SQLException {
        // delete the data
        Query deleteQuery = session.createSQLQuery(
            "delete from CATS "
            + "where DOG_ID in ( "
                + "select d.ID from DOGS d "
                + "where d.STATUS = ?)");
        deleteQuery.setString(0, "VICIOUS");
        int updated = deleteQuery.executeUpdate();
    }
});
if (log.isDebugEnabled()) {
    log.debug("rows deleted from CATS: " + deletedData);
}

Notes:
If you're wondering if you can avoid hard coding the table references by asking hibernate for the table-name mapping, this does seem possible - Get the table name from the model in Hibernate - but this approach doesn't seem to be "public" so may disappear.


Thursday, August 25, 2011

Beware: an empty string in Oracle is NULL

Keywords:
empty string '' CLOB varchar varchar2 text null isnull nvl NullPointerException JDBC

Problem:
There's code that is (seemingly) working with writing strings to CLOB columns and with the code from a previous post (Convert Oracle CLOBs to String) the reading of strings from the CLOB columns is working ok too ... until we get to empty strings - could it be that something is converting '' to NULL?

Here's a test case:
create table test_clobtext(

id number
, text clob
);
insert into test_clobtext values (1, 'some clob text');
insert into test_clobtext values (2, '');
select id, text from test_clobtext;

You get:
        ID TEXT

---------- ----------------
1 some clob text
2

What's the value in the 2nd row? You can use the NVL() (which is just like ISNULL()):

select id, NVL(text,'IT IS A NULL') as text from test_clobtext;

Shock, horror, this is the result:
        ID TEXT

---------- ----------------
1 some clob text
2 IT IS A NULL

... and because I'm still in disbelief:

select id, NVL(text,'IT IS A NULL') as text from test_clobtext
where text IS NULL;

This is definitely the result:
        ID TEXT

---------- ----------------
2 IT IS A NULL


So it's something to do with CLOBs? No, changing the text column to a varchar or varchar2 and you will get the same result! Is this right?

Solution:
This is apparently a well known issue (that I've only just stumbled across). A discussion is on stackoverflow: Why does Oracle treat empty string as NULL which links to more details information on ask-tom: Strings of Zero Length Not Equivalent To NULL.

It would seem that there are some scenarios where it won't be NULL but I can't reproduce this - changing the test case to have text as a char(1) still gives me NULL for the column.

The bottom line is if you're working with strings/text in a Oracle database you must expect and handle NULL values coming back - there will be no way to distinguish between whether what was originally stored was actually a NULL or an empty string ('').

Notes:
If you're dealing with CLOB columns you do have the option of storing (vendor specific) empty_clob() where you do want to distinguish between the cell being set to empty from it not being set at all (ie NULL). This post "An Empty Clob is not NULL" is a good discussion.



Monday, June 20, 2011

Convert Oracle CLOBs to String in JSTL and tag file

Keywords:
java.lang.ClassCastException oracle.sql.CLOB cast java.lang.String CLOB jstl tag requestScope requestContext pageContext jspContext

Problem:
It's annoying when SQL that works for other vendors fails for a specific one ... in this case a "text" column in a schema is defined as "clob" in the corresponding oracle schema. Problem is that this is not necessarily equivalent - especially when querying the data. This is even more complex when the SQL is in JSTL. So with the JSTL code (where textValue is a CLOB):

<sql:query var="data" >
select id,
textValue
from example
where id=?
<sql:param value="${param['id']}"/>
</sql:query>
<c:forEach items="${data.rows}" var="row">
<c:out value="${row.textValue}"/><br/>
</c:forEach>


You get the result:

oracle.sql.CLOB@e645e0
oracle.sql.CLOB@1f58913
oracle.sql.CLOB@fa6b82
...


Or if you try to use the textValue in something expecting a string, you'll get:
java.lang.ClassCastException: oracle.sql.CLOB cannot be cast to java.lang.String


How do you turn a Clob to a String without filling the JSP with vendor-specific code (leaving out the argument for not having SQL in the JSP for now)?

Solution:
Great discussion of this very issue is on the OTN Forum: JSP and CLOB. It essentially involves putting the Clob to String code in a scriptlet. To keep this vendor-neutral and take some of the "ugliness" out of the JSP I'd opt for putting this code in a tag file and stick to referencing just the java.sql.* interfaces.

Step 1: create a /WEB-INF/tags/to-string.tag tag file


(Or in a subfolder - the path must start with /WEB-INF/tags/.. if using the tagdir approach).

This takes the CLOB (or other) column value and sets it back in the request context as a String.

<%--
Can turn a CLOB to String for Oracle schema
--%>
<%@ tag body-content="empty" %>
<%@ attribute name="var" required="true" type="java.lang.String" %>
<%@ attribute name="value" required="true" type="java.lang.Object" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ tag import="java.sql.*" %>
<%@ tag import="javax.servlet.jsp.*" %>
<%
String strValue = null;
if (value == null) {
strValue = ""; // NB: oracle empty string is null
} else if (value instanceof Clob) {
Clob clob = (Clob)value;
long size = clob.length();
strValue = clob.getSubString(1, (int)size);
} else {
strValue = value.toString();
}
jspContext.setAttribute(var, strValue, PageContext.REQUEST_SCOPE);
%>


In this tag file, var is the name of the variable to define in the requestScope. Note how this is done by referencing the jspContext variable.

Step 2: Use the to-string tag for your text, clob or Other columns


This involves first defining the new taglib (putting all .tag files in tagdir in the JSP scope using the "eg" prefix in this example) and then simply using the eg:to-string tag to put the string-value of the column in a "local" requestScope variable.

<%@ taglib prefix="eg" tagdir="/WEB-INF/tags" %>
<sql:query var="data" >
select id,
textValue
from example
where id=?
<sql:param value="${param['id']}"/>
</sql:query>
<c:forEach items="${data.rows}" var="row">
<eg:to-string var="textValue" value="${row.textValue}"/>
<c:out value="${textValue}"/><br/>
</c:forEach>


Friday, January 28, 2011

Is the IBM DB2 UDB service not running? Can't make JDBC Type 4 connections

Keywords:
IBM DB2 v8 UDB Universal Driver TCP/IP which port windows service JDBC Type 4

Problem:
Attempting to make a JDBC Type 4 connection (ie pure java talking TCP/IP, no native code) to the DB2 server gives me:

com.ibm.db2.jcc.b.SqlException: IO Exception opening socket to server <myservername> on port 50000.
The DB2 Server may be down.


The DB2 server (running on Windows) is definitely running. In DB2 "Control Center" the "instance" ("DB2" the default name?) is definitely started. Looking at the local TCP ports being listened via netstat -abno there's no 50000 or anything close. So either the service that accepts the Type-4 JDBC connections (UDB) is not running or it's listening on a different port. How do you check?

Solution:
I couldn't find any mention of this in searching (though I did find "DB2 Version 8 Connectivity Cheat Sheet" which is good reference for DB2 generally), but by accident I stumbled on "Setup communications..." on right clicking the "DB2" instance in DB2 Control Center. From here the rest is straight forward:

  1. so, right click the "DB2" instance and select "Setup communications..."

  2. check TCP/IP

  3. click the Properties button and then just click the Default button to get default values

    • Note the port number: 50000 by default

  4. after clicking OK from the Properties and the communications dialog you'll have to restart the instance

    • right click the "DB2" instance and select Stop and then Start



Now when you check the open ports via netstat -abno you should hopefully see:

TCP 0.0.0.0:50000 0.0.0.0:0 LISTENING 3012
[db2syscs.exe]


To recap the IBM DB2 Universal Driver Type 4 (thin) connection details:
Driver Class:com.ibm.db2.jcc.DB2Driver
URL:jdbc:db2://<host>[:<port>]/<database_name>
eg:jdbc:db2://myservername:50000/MYDATABASE
Driver Class:com.ibm.db2.jcc.DB2Driver
Jar file(s):db2jcc.jar & db2jcc_license_cu.jar

Wednesday, August 18, 2010

Type coercion in JSTL - for sql:param

Keywords:
jstl sql integer string type coercion postgres serial operator does not exist: bigint = character varying

Problem:
After upgrading from PostgreSQL 8.0 to 8.4 the following JSTL that queries a table by a passed in "ID":
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${param['ID']}"/>
</sql:query>

Fails with the exception:
javax.servlet.jsp.JspException:
    select ExampleName as "name"
    from ExampleTable
    where ExampleId = ?
    order by ExampleName ASC

: ERROR: operator does not exist: bigint = character varying
        at org.apache.taglibs.standard.tag.common.sql.QueryTagSupport.doEndTag(QueryTagSupport.java:220)
        ....
Caused by: java.sql.SQLException: ERROR: operator does not exist: bigint = character varying
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1471)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1256)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:175)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:389)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:330)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:240)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
        at org.apache.taglibs.standard.tag.common.sql.QueryTagSupport.doEndTag(QueryTagSupport.java:215)


Why are there data type errors all of a sudden? ... and how do you fix it?

Solution:
There is normally Type Coercion for EL expressions but it's a bit vague for sql:param. Ideally it should coerce the param into the type required but this would require knowing the schema & the SQL being executed. So looking at the source for QueryTagSupport it will just call setObject using the default type that was supplied in the param.

Contrary to the sql:param documentation the value does not have to be a String.

To get the right type into sql:param use type coercion in EL before the param gets the value. To coerce a String to Integer, you could multiply by 1. For example:
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${param['ID']*1}"/>
</sql:query>

Or in two steps:
<c:set var="exampleId" value="${param['ID']*1}"/>
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${exampleId}"/>
</sql:query>


Why did this come up after a PostgreSQL upgrade? It seems something to do with different handling of the SERIAL data type which is now compiled to it's actual representation of integer with sequence rather than leaving it as it's "notational convenience" name. Perhaps the PostgreSQL JDBC will coerce a String to a serial but not an integer?

It's hard to say the above work around is best practice but it will be harmless for databases that handle coercion at the JDBC level and necessary for those that don't.

Thursday, February 04, 2010

HSQL random function - calling scalar functions via JDBC

Keywords:
HSQL JDBC select random scalar built-in function rand fn

Problem:
I want to demstrate database interaction but the data doesn't really matter in this case. HSQL is ideal because you can set a connection to in-memory database .. but there's no data. Random would do, is there a random function in HSQL? Where is the function reference? Is it called rand(), random() or other?

Solution:
Short answer is it's called "rand". So the SQL would be:
CALL rand()
With JDBC this is:
PreparedStatement stmt = connection.prepareStatement("CALL rand()");
stmt.execute();
... and I can't find a complete function reference for HSQL.


Digging a bit further there is JDBC convention for calling scalar/built-in function in a vendor independent way - See JDBC 2.0: Escape Syntax and Scalar Functions. This involves the use of the "fn" keyword and braces - so the syntax is:
{fn <function()>}
Whether the JDBC driver recognises the syntax and maps it correctly to the underlying function ("rand" is called "random" in postgres for example) is up to the vendor. I can confirm that HSQL seems to handle most numeric, string and date functions (as mentioned above, can't find doco for this. The derby JDBC function reference is a reasonably good overview). So the vendor agnostic random call becomes:
PreparedStatement stmt = connection.prepareStatement("CALL {fn rand()}");



Notes:
What I could find was documentation of HSQL "Java Stored Procedures". This effectively lets you call any public static java method (from a class in the classpath) right there in the SQL statement. So, yet another (non-JDBC standard) way of making the random call would be:
CALL "java.lang.Math.random"()
... or doing more work in the SQL:
CALL "java.lang.Math.floor"("java.lang.Math.random"() * 100)
Pretty cool, I must think of a use for this :)

Wednesday, April 09, 2008

Call a stored procedure from a JSP with JSTL

Keywords:
Call execute SQL stored procedure JSP JSTL tag library

Problem:
The JSTL SQL tag library is a useful way of getting a rapid prototype going - it's all in a plain text file and will get compile on the fly. Examples I've seen show SELECT, UPDATE, INSERT and DELETE(s) ... can a stored procedure be run?

Solution:
The short answer is yes, the key thing is to know if the stored procedure is returning a result set or not as you have two tags available:

  • <sql:query> expects a ResultSet

  • <sql:update> does not expect a ResultSet - will throw an error if gets one. You do have access to an Integer result - eg rows updated.

For example, calling a stored procedure that returns a result set:
<sql:setDataSource var="myDataSource" dataSource="jdbc/myjndiref"/>
<sql:query var="examples" dataSource="${myDataSource}">
    exec ExampleProcLoadExample ?
    <sql:param value="${exampleId}"/>
</sql:query>
<c:choose>
    <c:when test="${fn:length(examples.rows) == 0}">
        <%-- no rows returned ! --%>
    </c:when>
    <c:otherwise>
        <c:set var="example" value="${examples.rows[0]}"/>
        <%-- got your object, can access columns with '.' notation --%>
    </c:otherwise>
</c:choose>


For example, calling a stored procedure that performs an 'update' (or insert/delete) returning number of rows updated:
<sql:setDataSource var="myDataSource" dataSource="jdbc/myjndiref"/>
<sql:update var="updateCount" dataSource="${myDataSource}">
    exec ExampleProcRemoveExample ?
    <sql:param value="${exampleId}"/>
</sql:update>
<c:choose>
    <c:when test="${updateCount le 0}">
        <%-- no rows updated ! --%>
    </c:when>
    <c:otherwise>
        <%-- some row(s) has been updated --%>
        <c:out value="${updateCount} row(s) have been updated"/>
    </c:otherwise>
</c:choose>


Notes:
When calling stored procedures you're getting into RDBMS vendor specific territory. Using the recommended JDBC drivers from the DB vendor for the DB version in use may make some of this work more smoothly.