[#3015] Add support for HSQLDB user-defined aggregate functions

This commit is contained in:
Lukas Eder 2014-02-07 15:02:57 +01:00
parent db23122bc8
commit dcc98506b9
8 changed files with 126 additions and 49 deletions

View File

@ -41,6 +41,7 @@
package org.jooq.util.hsqldb;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.nvl;
import static org.jooq.util.hsqldb.information_schema.Tables.CHECK_CONSTRAINTS;
import static org.jooq.util.hsqldb.information_schema.Tables.ELEMENT_TYPES;
@ -316,7 +317,8 @@ public class HSQLDBDatabase extends AbstractDatabase {
ROUTINES.SPECIFIC_NAME,
nvl(ELEMENT_TYPES.COLLECTION_TYPE_IDENTIFIER, ROUTINES.DATA_TYPE).as("datatype"),
ROUTINES.NUMERIC_PRECISION,
ROUTINES.NUMERIC_SCALE)
ROUTINES.NUMERIC_SCALE,
field(ROUTINES.ROUTINE_DEFINITION.likeRegex(".*(?i:(\\w+\\s+)+aggregate\\s+function).*")).as("aggregate"))
.from(ROUTINES)
.leftOuterJoin(ELEMENT_TYPES)
.on(ROUTINES.ROUTINE_SCHEMA.equal(ELEMENT_TYPES.OBJECT_SCHEMA))
@ -334,7 +336,8 @@ public class HSQLDBDatabase extends AbstractDatabase {
record.getValue(ROUTINES.SPECIFIC_NAME),
record.getValue("datatype", String.class),
record.getValue(ROUTINES.NUMERIC_PRECISION),
record.getValue(ROUTINES.NUMERIC_SCALE)));
record.getValue(ROUTINES.NUMERIC_SCALE),
record.getValue("aggregate", boolean.class)));
}
return result;

View File

@ -40,7 +40,9 @@
*/
package org.jooq.util.hsqldb;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.nvl;
import static org.jooq.impl.DSL.val;
import static org.jooq.util.hsqldb.information_schema.Tables.ELEMENT_TYPES;
import static org.jooq.util.hsqldb.information_schema.Tables.PARAMETERS;
import static org.jooq.util.hsqldb.information_schema.Tables.ROUTINES;
@ -69,7 +71,11 @@ public class HSQLDBRoutineDefinition extends AbstractRoutineDefinition {
private final String specificName; // internal name for the function used by HSQLDB
public HSQLDBRoutineDefinition(SchemaDefinition schema, String name, String specificName, String dataType, Number precision, Number scale) {
super(schema, null, name, null, null);
this(schema, name, specificName, dataType, precision, scale, false);
}
public HSQLDBRoutineDefinition(SchemaDefinition schema, String name, String specificName, String dataType, Number precision, Number scale, boolean aggregate) {
super(schema, null, name, null, null, aggregate);
if (!StringUtils.isBlank(dataType)) {
DataTypeDefinition type = new DefaultDataTypeDefinition(
@ -109,6 +115,10 @@ public class HSQLDBRoutineDefinition extends AbstractRoutineDefinition {
.and(PARAMETERS.DTD_IDENTIFIER.equal(ELEMENT_TYPES.COLLECTION_TYPE_IDENTIFIER))
.where(PARAMETERS.SPECIFIC_SCHEMA.equal(getSchema().getName()))
.and(PARAMETERS.SPECIFIC_NAME.equal(this.specificName))
// [#3015] HSQLDB user-defined AGGREGATE functions have four parameters, but only one
// is relevant to client code
.and(condition(val(!isAggregate())).or(PARAMETERS.ORDINAL_POSITION.eq(1L)))
.orderBy(PARAMETERS.ORDINAL_POSITION.asc()).fetch();
for (Record record : result) {

View File

@ -59,6 +59,7 @@ import java.util.UUID;
import junit.framework.Assert;
import org.jooq.AggregateFunction;
// ...
import org.jooq.Configuration;
import org.jooq.DAO;
@ -667,6 +668,10 @@ public abstract class BaseTest<
return delegate.TIdentityPK_VAL();
}
protected AggregateFunction<Integer> secondMax(Field<Integer> val) {
return delegate.secondMax(val);
}
protected Field<? extends Number> FAuthorExistsField(String authorName) {
return delegate.FAuthorExistsField(authorName);
}

View File

@ -67,6 +67,7 @@ import java.sql.Date;
import java.sql.Timestamp;
import java.util.UUID;
import org.jooq.AggregateFunction;
// ...
import org.jooq.DSLContext;
import org.jooq.DataType;
@ -651,6 +652,12 @@ public class HSQLDBTest extends jOOQAbstractTest<
return T_IDENTITY_PK.VAL;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected AggregateFunction secondMax(Field val) {
return Routines.secondMax(val);
}
@Override
protected Field<? extends Number> FAuthorExistsField(String authorName) {
return Routines.fAuthorExists(authorName);

View File

@ -62,7 +62,6 @@ xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@ -110,6 +109,7 @@ xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxx
xxxxxx xxxxxxxxxxxxxxxxxxxx
@ -740,6 +740,12 @@ xxxxxx xxxxx xxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxxx
xxxxxx xxxxx
x
xxxxxxxxxxxxxxxxxxx xxxxxxxxxxx xxxxxxxxxxx xx
xxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx xxxx x
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxx
x
xxxxxxxxx
xxxxxxxxx xxxxxxx xxxxxxx xxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx x
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@ -1375,42 +1381,6 @@ xxxxxx xxxxx xxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x
xxxxx
xxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx xxxxxxxxx x
xx xxxxx xxx xxxxxxxxxxx xx xxx xxxxxxxxx xxxxxxxx
xxxxxxxxxxxxx xxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx xxx xxxxxxxxx
xx xxxxx xxx xxxxxxxxxxx xx xxx xxxxxxxxxx xxxxxxxx
xxxxxxxxxxxxx xxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx xx xx xxx xxxxxxxxx
xx xxxxxxx xxxxx xx xxxxx xxxxxxxxxx xxx xxxxxxxxx xxxxxxxx xxxxxx xxx
xxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxx xxxxxxx x
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx xxx xxxxxxxxx
x
xxxxx
xxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxx xxxxxxxxx x
xxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx

View File

@ -106,6 +106,7 @@ import java.sql.Date;
import java.util.Arrays;
import java.util.List;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Name;
import org.jooq.Record;
@ -122,6 +123,7 @@ import org.jooq.TableRecord;
import org.jooq.UpdatableRecord;
import org.jooq.WindowDefinition;
import org.jooq.WindowSpecification;
import org.jooq.impl.DSL;
import org.jooq.test.BaseTest;
import org.jooq.test.jOOQAbstractTest;
@ -161,17 +163,63 @@ extends BaseTest<A, AP, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, UU, I, IPK, T7
}
@Test
public void testAggregateFunctions() throws Exception {
public void testUserDefinedAggregateFunctions() throws Exception {
if (secondMax(null) == null) {
log.info("SKIPPING", "User-defined aggregate function tests");
return;
}
// Standard aggregate functions, available in all dialects:
// --------------------------------------------------------
Field<BigDecimal> median = median(TBook_ID());
// Check the correctness of the aggregate function
List<Integer> result1 =
create().select(secondMax(TBook_ID()))
.from(TBook())
.groupBy(TBook_AUTHOR_ID())
.orderBy(TBook_AUTHOR_ID().asc())
.fetch(0, Integer.class);
// Some dialects don't support a median function or a simulation thereof
// Use AVG instead, as in this example the values of MEDIAN and AVG
// are the same
switch (dialect().family()) {
/* [pro] xx
assertEquals(asList(1, 3), result1);
/* [pro] xx
xxxxxx xxxxxxxxxxxxxxxxxxxx x
xxxx xxxxxxx x
xx xxxxx xxx xxxxxxxxxxx xx xxx xxxxxxxxxx xxxxxxxx
xxxxxxxxxxxxx xxxxxxx x
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx xx xx xxx xxxxxxxxx
xx xxxxxxx xxxxx xx xxxxx xxxxxxxxxx xxx xxxxxxxxx xxxxxxxx xxxxxx xxx
xxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxx xxxxxxx x
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxx xxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxx xxx xxxxxxxxx
xxxxxx
x
x
xx xxxxx xx
x
xxxxx
xxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxx xxxxxxxxx x
xx xxxxxxxx xxxxxxxxx xxxxxxxxxx xxxxxxxxx xx xxx xxxxxxxxx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxx xxxxxx x xxxxxxxxxxxxxxxxxxx
xx xxxx xxxxxxxx xxxxx xxxxxxx x xxxxxx xxxxxxxx xx x xxxxxxxxxx xxxxxxx
xx xxx xxx xxxxxxxx xx xx xxxx xxxxxxx xxx xxxxxx xx xxxxxx xxx xxx
xx xxx xxx xxxx
xxxxxx xxxxxxxxxxxxxxxxxxxx x
xx xxxxx xx
xxxx xxxx
xxxx xxxxxxx
xxxx xxxx

View File

@ -22,6 +22,7 @@ DROP FUNCTION IF EXISTS f2502_2/
DROP PROCEDURE IF EXISTS p2502/
DROP FUNCTION IF EXISTS f2515/
DROP FUNCTION IF EXISTS f2515_/
DROP FUNCTION IF EXISTS second_max/
DROP VIEW IF EXISTS v_author/
DROP VIEW IF EXISTS v_book/
@ -541,3 +542,27 @@ BEGIN ATOMIC
SET io2 = i2;
END
/
CREATE AGGREGATE FUNCTION second_max(
IN val INTEGER,
IN flag BOOLEAN,
INOUT highest INTEGER,
INOUT second_highest INTEGER
)
RETURNS INTEGER
CONTAINS SQL
BEGIN ATOMIC
DECLARE temp INTEGER;
IF flag THEN
RETURN second_highest;
ELSE
SET temp = highest;
SET highest = GREATEST(COALESCE(highest, -2147483648), val);
SET second_highest = CASE WHEN temp < highest THEN temp ELSE second_highest END;
SET temp = GREATEST(COALESCE(second_highest, -2147483648), val);
SET second_highest = CASE WHEN temp < highest THEN temp ELSE second_highest END;
END IF;
END
/

View File

@ -72,6 +72,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.jooq.AggregateFunction;
// ...
import org.jooq.DAO;
import org.jooq.DSLContext;
@ -915,6 +916,9 @@ public abstract class jOOQAbstractTest<
protected abstract TableField<IPK, Integer> TIdentityPK_ID();
protected abstract TableField<IPK, Integer> TIdentityPK_VAL();
protected <N extends Number> AggregateFunction<N> secondMax(Field<N> val) {
return null;
}
protected abstract Field<? extends Number> FAuthorExistsField(String authorName);
protected abstract Field<? extends Number> FOneField();
protected abstract Field<? extends Number> FNumberField(Number n);
@ -2081,6 +2085,11 @@ public abstract class jOOQAbstractTest<
new FunctionTests(this).testBitwiseOperations();
}
@Test
public void testUserDefinedAggregateFunctions() throws Exception {
new AggregateWindowFunctionTests(this).testUserDefinedAggregateFunctions();
}
@Test
public void testAggregateFunctions() throws Exception {
new AggregateWindowFunctionTests(this).testAggregateFunctions();