VoltDB provides many built-in functions for use in SQL statements that are described in an appendix to the Using VoltDB manual. These built-in functions perform a variety of tasks, such as data conversion, string matching, geometric calculations, and so on.
However, not all possible functions are built in and there may be cases where you have an application-specific function that needs to be performed repeatedly. Rather than duplicating the code to perform the function each time you need it, VoltDB lets you create and declare your own functions that can be invoked directly from within SQL queries and data manipulation statements just like built-in functions.
There are three steps to creating a user-defined function:
Write the code to perform the function as a Java method.
Load the Java class that includes the user-defined function into the database.
Declare the function using the CREATE FUNCTION statement, associating it to the Java class and method.
The following sections describe how to perform each of these tasks, as well as how to invoke the function in SQL statements once it has been declared.
You write user-defined functions as Java methods. If you are creating multiple user-defined functions, you can put them all in a single Java class or in separate Java classes. Whichever is most convenient for you and the management of your code base.
The number and datatypes of the method's parameters define the number and types of the function's arguments. For
example, if you declare the method as having two parameters, a Java int
and String
,
the function will have two arguments, a VoltDB INTEGER and a VARCHAR. Similarly, the datatype of the method itself
determines the datatype that the function returns.
Because user-defined functions are executable within SQL statements and stored procedures, the methods must obey the same rules concerning determinism as stored procedures. That is, avoid any actions that introduce values that may vary from one system to another, such as system time, random number generation, or I/O with indeterminate results. See the section on determinism in the Using VoltDB manual for details.
For example, say you need to convert distances from imperial or US units to metric measurements. You might define your
function with two arguments: a floating-point value representing the measurement and a string unit identifying the units
(such as "feet", "yards", or "miles"). So your Java source code would need to declare the method as accepting two
parameters: a double
and a String
. It should also be declared as returning a
double
value.
package myapp.sql.functions; import org.voltdb.*; public class Conversion { public double us2metric( double value, String units ) throws VoltAbortException {
Note the method is declared as throwing a VoltAbortException. This is useful for error handling. By throwing a VoltAbortException, it is possible for the function to handle errors gracefully and notify the VoltDB runtime to rollback the current transaction. For example, the first step in the method might be to validate the input and make sure the units argument has a known value:
units = units.toUpperCase().trim(); if (!units.equals("FEET") && !units.equals("YARDS") && !units.equals("MILES") ) throw new VoltAbortException("Unrecognized selector.");
The bulk of the method will focus on performing the actual task the function is designed for. The key point is to make
sure it returns the appropriate datatype object that correlates to the VoltDB datatype you want the function to return in
the SQL statement. In the previous example, the method is declared as returning a double
, which matches
the VoltDB FLOAT type. See the appendix on Datatype compatibility in the Using VoltDB manual for details on the mapping of
Java and VoltDB datatypes. But, in brief, the mapping of Java to VoltDB datatypes is as follows:
byte or Byte → TINYINT |
short or Short → SMALLINT |
int or Integer → INTEGER |
long or Long → BIGINT |
double or Double → FLOAT |
BigDecimal → DECIMAL |
String → VARCHAR |
byte[] or Byte[] → VARBINARY |
You can define parameters for VoltDB-specific datatypes by using the object types included in the VoltDB Java packages. For example:
org.voltdb.types.GeographyValue → GEOGRAPHY |
org.voltdb.types.GeographyPointValue → GEOGRAPHY_POINT |
org.voltdb.types.TimestampType → TIMESTAMP |