CREATE TYPE metric_meter AS DOUBLE;
CREATE TYPE imperial_foot AS DOUBLE;
final
modifier) and
INSTANTIABLE or NOT INSTANTIABLE (as with the Java
abstract
modifier). Here's the classic OO example:
CREATE TYPE Shape2D
NOT INSTANTIABLE
NOT FINAL;
CREATE TYPE Circle UNDER Shape2D
AS(
radius DOUBLE DEFAULT 1
)
INSTANTIABLE
FINAL;
CREATE TYPE Rectangle UNDER Shape2D
NOT INSTANTIABLE
NOT FINAL;
CREATE TYPE Square UNDER Rectangle
AS(
side_length DOUBLE DEFAULT 1
)
INSTANTIABLE
FINAL;
CREATE TYPE Oblong UNDER Rectangle
AS(
width DOUBLE DEFAULT 1,
length DOUBLE DEFAULT 2
)
INSTANTIABLE
NOT FINAL;
Attributes can have default values, but not constraints (meaning you
can't even declare an attribute as NOT NULL). It's not even possible
to use domains to work around this, since domains can only be based on
builtin types. However, it is possible to use column-level
constraints or validation code in a constructor instead.
CREATE TABLE PolkaDots(
dot_id INT NOT NULL PRIMARY KEY,
Circle dot NOT NULL CHECK ((dot.radius > 0) IS TRUE),
center_x DOUBLE NOT NULL,
center_y DOUBLE NOT NULL);
Note that the NOT NULL constraint on dot applies to the Circle object
as a whole (not to its radius attribute, which is validated by the
CHECK clause). Attempting to reference an attribute of a null type
instance results in NULL (not an exception as you might expect).
CALL
statement. Procedures are not associated with a
particular type, and cannot be used in SQL row expressions. An SQL-invoked
procedure is similar to a C++ free function with void return type.
CREATE PROCEDURE raise_annual_salary(IN raise_percentage DOUBLE)
LANGUAGE SQL
MODIFIES SQL DATA
BEGIN
UPDATE emps SET salary = salary + salary*raise_percentage;
END;
CALL raise_annual_salary(0.10);
CREATE FUNCTION sqr(x DOUBLE)
RETURNS DOUBLE
LANGUAGE SQL
CONTAINS SQL
RETURN(x*x);
SELECT sum(3.1415927*sqr(d.dot.radius)) as total_area FROM PolkaDots d;
CREATE TYPE Shape2D
NOT INSTANTIABLE
NOT FINAL
INSTANCE METHOD area() RETURNS DOUBLE CONTAINS SQL;
CREATE TYPE Circle UNDER Shape2D
AS(
radius DOUBLE DEFAULT 1
)
INSTANTIABLE
FINAL
OVERRIDING INSTANCE METHOD area() RETURNS DOUBLE CONTAINS SQL;
CREATE TYPE Rectangle UNDER Shape2D
NOT INSTANTIABLE
NOT FINAL
OVERRIDING INSTANCE METHOD area() RETURNS DOUBLE CONTAINS SQL
INSTANCE METHOD rect_width() RETURNS DOUBLE CONTAINS SQL
INSTANCE METHOD rect_length() RETURNS DOUBLE CONTAINS SQL;
CREATE TYPE Square UNDER Rectangle
AS(
side_length DOUBLE DEFAULT 1
)
INSTANTIABLE
FINAL
OVERRIDING INSTANCE METHOD rect_width() RETURNS DOUBLE CONTAINS SQL;
OVERRIDING INSTANCE METHOD rect_length() RETURNS DOUBLE CONTAINS SQL;
CREATE TYPE Oblong UNDER Rectangle
AS(
width DOUBLE DEFAULT 1,
length DOUBLE DEFAULT 2
)
INSTANTIABLE
NOT FINAL
OVERRIDING INSTANCE METHOD rect_width() RETURNS DOUBLE CONTAINS SQL
OVERRIDING INSTANCE METHOD rect_length() RETURNS DOUBLE CONTAINS SQL;
CREATE INSTANCE METHOD area()
RETURNS DOUBLE
FOR Circle
RETURN 3.1415927*sqr(radius);
CREATE INSTANCE METHOD area()
RETURNS DOUBLE
FOR Rectangle
RETURN rect_width*rect_length;
CREATE INSTANCE METHOD rect_width()
RETURNS DOUBLE
FOR Oblong
RETURN width;
CREATE INSTANCE METHOD rect_length()
RETURNS DOUBLE
FOR Oblong
RETURN length;
CREATE INSTANCE METHOD rect_width()
RETURNS DOUBLE
FOR Square
RETURN side_length;
CREATE INSTANCE METHOD rect_length()
RETURNS DOUBLE
FOR Square
RETURN side_length;
SELECT d.dot.area FROM PolkaDots d;
SELECT d.dot.area() FROM PolkaDots d;
INSERT INTO PolkaDots VALUES(1,NEW CIRCLE(),0,0);
NEW
expression, which (when given arguments) combines
the system-defined constructor function with the appropriate
user-defined constructor method:
CREATE TYPE Circle UNDER Shape2D
AS(
radius DOUBLE DEFAULT 1
)
INSTANTIABLE
FINAL
CONSTRUCTOR METHOD Circle(radius DOUBLE) RETURNS Circle
SELF AS RESULT
CONTAINS SQL;
CREATE CONSTRUCTOR METHOD Circle(radius DOUBLE)
FOR Circle
RETURNS Circle
BEGIN
SET SELF.radius = radius;
RETURN SELF;
END;
INSERT INTO PolkaDots VALUES(2,NEW CIRCLE(20.5),10,100);
(Note that the SQL SELF
keyword serves the same purpose
as the this
keyword in Java.)
SELECT d.dot.radius() FROM PolkaDots d;
SELECT d.dot.radius FROM PolkaDots d;
SELECT d.dot.radius(d.dot.radius*2) FROM PolkaDots d;
Traditional attribute lvalue semantics are also supported for updates:
UPDATE PolkaDots SET dot.radius = dot.radius*2;
This is equivalent to:
UPDATE PolkaDots SET dot = dot.radius(dot.radius*2);
LANGUAGE
: this specifies the implementation language.
The examples above all used SQL
, but later we'll see some
examples which specify JAVA
instead.
PARAMETER STYLE
: this can be one of {SQL |
GENERAL | JAVA}
and controls how routine parameter lists are
mapped into other languages for external routines.
SPECIFIC name
: an additional name by which
an overloaded routine is known in the catalog. This allows a routine
to be referenced by specific name without having to include its full
signature.
[NOT] DETERMINISTIC
: specifies whether the routine
always produces the same result given the same inputs and persistent state.
[NOT] DYNAMIC_FUNCTION
: specifies whether it is safe
to cache query plans referring to the routine; default is false (NOT
DYNAMIC_FUNCTION), indicating that plan caching is safe. An example
of a DYNAMIC_FUNCTION would be an external value lookup routine
(similar to the CURRENT_DATE expression) which should be re-evaluated
for each query execution, but not for each row. Note that this is a
Farrago-specific extension (not defined by SQL:2003).
{NO SQL | CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA
}
: constrains the usage of SQL within the implementation of
the routine.
DYNAMIC RESULT SETS
: for procedures, whether result
sets are returned when the procedure is invoked. Procedural result
sets are not covered in this document.
[ RETURNS NULL | CALLED ] ON NULL INPUT
: for functions,
whether the routine is actually called if one of its arguments is
NULL. RETURNS NULL
forces the executor to short-circuit
the function invocation, supplying NULL for its result.
{ IN | OUT | INOUT
}
. Parameter names are optional (but still recommended) when
an external routine is specified, since external routine lookup is by
type signature. Parameters do not take default values.
instanceof
keyword is the
IS [NOT] OF
syntax:
SELECT p.name
FROM ParkingLots p
WHERE p.geometry IS OF (Square,Circle);
Casting is sometimes required to get the right version of a method due
to overloading or overriding. The normal SQL CAST
keyword is not used for this purpose. Instead, the syntax for
downcasting uses the TREAT
keyword:
SELECT TREAT(p.geometry AS Square).side_length
FROM ParkingLots p
WHERE p.geometry IS OF (Square);
For upcasting, the TREAT keyword is omitted:
SELECT generate_label(d.dot AS Shape)
FROM PolkaDots p;
If the generate_label
function were overloaded with
different versions for Circles and for generic Shapes, then the query
above will invoke the generic Shape version. For the gory details on
routine resolution, consult the standard.
Besides explicit type hierarchy casting, SQL:2003 also provides for
CREATE CAST
statement.
These are relevant for structured types; for distinct types, cast
functions to and from builtin types can be referenced as part of the
type definition.
CREATE ORDERING
statement.
CREATE TRANSFORM
statement, which is unnecessary for
Java; JDBC already defines the SQLData interface for
this purpose.
CAST FROM
clause (mostly useful with external routines
where the routine wasn't defined with SQL in mind).
RESULT
clause; I
haven't decoded this part of the standard yet.
ALTER TYPE
statement, which allows for
RESTRICT
semantics
(no CASCADE
).
Procedures, functions, and types may be defined in any schema, but methods must be defined in the same schema as the associated type.
SQL:2003 introduces the notion of a SQL-path, which is used to
resolve unqualified references to procedures, functions, and types
(similar to the way the PATH
environment variable is used
to resolve executable names). Each schema has a SQL-path associated
with it; objects defined within that schema use that path implicitly.
Sessions also have their own SQL-paths used for dynamic SQL.
As with table references in views, unqualified routine references are
"frozen" at the time the invoking code is validated so that subsequent
path changes do not affect them. However, the situation is a little
more complicated in the case of late binding; instead of freezing on a
single routine, the system freezes a candidate set with final matching
performed at execution time. In the examples above, an invocation of
shape.area()
does not know until execution whether a
particular shape is a Rectangle or a Circle.
Defining a routine with a Java implementation is a multi-step process:
class MathFuncs {
public static double sqr(double d)
{
return d*d;
}
}
CALL
the system-defined
SQLJ.INSTALL_JAR
routine to install the JAR into the
DBMS. (The last parameter, which controls whether deployment descriptors
are used, is currently ignored since these aren't supported yet.)
CALL SQLJ.INSTALL_JAR('file:/home/jvs/MathFuncs.jar','MathFuncsJavaLib', 0);
CREATE {FUNCTION|PROCEDURE}
statement
referencing the routine's Java implementation in the installed JAR.
CREATE FUNCTION sqr(d DOUBLE)
RETURNS DOUBLE
NO SQL
EXTERNAL NAME 'MathFuncsLib:MathFuncs.sqr'
LANGUAGE JAVA
PARAMETER STYLE JAVA;
CREATE FUNCTION sqr(d DOUBLE)
RETURNS DOUBLE
NO SQL
INLINE
LANGUAGE JAVA
PARAMETER STYLE JAVA
{
return d*d;
}
;
The schema named SQLJ
is a special schema maintained by
the system (just like INFORMATION_SCHEMA
). It contains
other procedures for JAR maintenance (REPLACE_JAR, REMOVE_JAR,
ALTER_JAVA_PATH
). Besides Java classes, JAR files can also
contain deployment descriptors such as the DDL for creating the SQL
routines and granting access to them. In a way this is the inverse
of the inline routine definition mentioned above: instead of defining
everything in SQL, define everything in the JAR, and then issue a
single CALL
from SQL to deploy it.
public/private/protected
of Java and C++. However, the
standard (and more powerful) GRANT/REVOKE mechanism can be used at the
method level instead.
When external routines execute SQL statements, the authorization with
which they run can be controlled via the EXTERNAL
SECURITY
clause on the routine definition:
EXTERNAL SECURITY DEFINER
: the routine executes with
the privileges of the user who created the routine.
EXTERNAL SECURITY INVOKER
: the routine executes with
the privileges of the user who invoked the routine.
EXTERNAL SECURITY IMPLEMENTATION DEFINED
: the system
decides (may be any user at all!).