[#1048] Simulate <op> <quantifier> (array) syntax for dialects that do not support arrays
[#1055] Simulate Factory.table(Object[]) and table(List<?>) using UNION ALL in dialects that do not support arrays
This commit is contained in:
parent
2ad7fb795f
commit
050ae4257b
@ -3105,28 +3105,25 @@ public abstract class jOOQAbstractTest<
|
||||
.where(TBook_ID().equalAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 2))))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
|
||||
// [#1048] TODO: Simulate this for other dialects
|
||||
if (asList(H2, HSQLDB, POSTGRES).contains(getDialect())) {
|
||||
// Testing = ALL(array)
|
||||
assertEquals(Arrays.asList(1), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAll(1))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
assertEquals(Arrays.asList(), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAll(1, 2))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
// Testing = ALL(array)
|
||||
assertEquals(Arrays.asList(1), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAll(1))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
assertEquals(Arrays.asList(), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAll(1, 2))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
|
||||
// Testing = ANY(array)
|
||||
assertEquals(Arrays.asList(1), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAny(1))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
assertEquals(Arrays.asList(1, 2), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAny(1, 2))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
}
|
||||
// Testing = ANY(array)
|
||||
assertEquals(Arrays.asList(1), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAny(1))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
assertEquals(Arrays.asList(1, 2), create().select(TBook_ID())
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equalAny(1, 2))
|
||||
.orderBy(TBook_ID()).fetch(TBook_ID()));
|
||||
|
||||
// Inducing the above to work the same way as all other operators
|
||||
// Check all operators in a single query
|
||||
@ -3135,22 +3132,34 @@ public abstract class jOOQAbstractTest<
|
||||
.from(TBook())
|
||||
.where(TBook_ID().equal(create().select(val(3))))
|
||||
.and(TBook_ID().equalAll(create().select(val(3))))
|
||||
.and(TBook_ID().equalAll(3, 3))
|
||||
.and(TBook_ID().equalAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(3, 4))))
|
||||
.and(TBook_ID().equalAny(3, 4))
|
||||
.and(TBook_ID().notEqual(create().select(val(1))))
|
||||
.and(TBook_ID().notEqualAll(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().notEqualAll(1, 4, 4))
|
||||
.and(TBook_ID().notEqualAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().notEqualAny(1, 4, 4))
|
||||
.and(TBook_ID().greaterOrEqual(create().select(val(1))))
|
||||
.and(TBook_ID().greaterOrEqualAll(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 2))))
|
||||
.and(TBook_ID().greaterOrEqualAll(1, 2))
|
||||
.and(TBook_ID().greaterOrEqualAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().greaterOrEqualAny(1, 4))
|
||||
.and(TBook_ID().greaterThan(create().select(val(1))))
|
||||
.and(TBook_ID().greaterThanAll(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 2))))
|
||||
.and(TBook_ID().greaterThanAll(1, 2))
|
||||
.and(TBook_ID().greaterThanAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().greaterThanAny(1, 4))
|
||||
.and(TBook_ID().lessOrEqual(create().select(val(3))))
|
||||
.and(TBook_ID().lessOrEqualAll(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(3, 4))))
|
||||
.and(TBook_ID().lessOrEqualAll(3, 4))
|
||||
.and(TBook_ID().lessOrEqualAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().lessOrEqualAny(1, 4))
|
||||
.and(TBook_ID().lessThan(create().select(val(4))))
|
||||
.and(TBook_ID().lessThanAll(create().select(val(4))))
|
||||
.and(TBook_ID().lessThanAll(4, 5))
|
||||
.and(TBook_ID().lessThanAny(create().select(TBook_ID()).from(TBook()).where(TBook_ID().in(1, 4))))
|
||||
.and(TBook_ID().lessThanAny(1, 4))
|
||||
.fetch(TBook_ID()));
|
||||
|
||||
break;
|
||||
@ -8752,76 +8761,8 @@ public abstract class jOOQAbstractTest<
|
||||
}
|
||||
else if (TArrays_NUMBER() != null) {
|
||||
Result<Record> result;
|
||||
|
||||
// An empty array
|
||||
// --------------
|
||||
Integer[] array = new Integer[0];
|
||||
result = create().select().from(table(new Integer[0])).fetch();
|
||||
|
||||
assertEquals(0, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
|
||||
// An array containing null
|
||||
// ------------------------
|
||||
array = new Integer[] { null };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertEquals(null, result.getValue(0, 0));
|
||||
|
||||
// An array containing two values
|
||||
// ------------------------------
|
||||
array = new Integer[] { null, 1 };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertEquals(null, result.getValue(0, 0));
|
||||
assertEquals(1, result.getValue(1, 0));
|
||||
|
||||
// An array containing three values
|
||||
// --------------------------------
|
||||
array = new Integer[] { null, 1, 2 };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(3, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertEquals(null, result.getValue(0, 0));
|
||||
assertEquals(1, result.getValue(1, 0));
|
||||
assertEquals(2, result.getValue(2, 0));
|
||||
|
||||
// Joining an unnested array table
|
||||
// -------------------------------
|
||||
array = new Integer[] { 2, 3 };
|
||||
Table<?> table = table(array);
|
||||
result = create()
|
||||
.select(TBook_ID(), TBook_TITLE())
|
||||
.from(TBook())
|
||||
.join(table)
|
||||
.on(table.getField(0).cast(Integer.class).equal(TBook_ID()))
|
||||
.fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(Integer.valueOf(2), result.getValue(0, TBook_ID()));
|
||||
assertEquals(Integer.valueOf(3), result.getValue(1, TBook_ID()));
|
||||
assertEquals("Animal Farm", result.getValue(0, TBook_TITLE()));
|
||||
assertEquals("O Alquimista", result.getValue(1, TBook_TITLE()));
|
||||
|
||||
// Joining an aliased unnested array table
|
||||
// ---------------------------------------
|
||||
result = create()
|
||||
.select(TBook_ID(), TBook_TITLE())
|
||||
.from(TBook())
|
||||
.join(table.as("t"))
|
||||
.on(table.as("t").getField(0).cast(Integer.class).equal(TBook_ID()))
|
||||
.fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(Integer.valueOf(2), result.getValue(0, TBook_ID()));
|
||||
assertEquals(Integer.valueOf(3), result.getValue(1, TBook_ID()));
|
||||
assertEquals("Animal Farm", result.getValue(0, TBook_TITLE()));
|
||||
assertEquals("O Alquimista", result.getValue(1, TBook_TITLE()));
|
||||
Table<?> table;
|
||||
Integer[] array;
|
||||
|
||||
// Cross join the array table with the unnested string array value
|
||||
// ---------------------------------------------------------------
|
||||
@ -8883,6 +8824,80 @@ public abstract class jOOQAbstractTest<
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayTableSimulation() throws Exception {
|
||||
Result<Record> result;
|
||||
|
||||
// An empty array
|
||||
// --------------
|
||||
Integer[] array = new Integer[0];
|
||||
result = create().select().from(table(new Integer[0])).fetch();
|
||||
|
||||
assertEquals(0, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
|
||||
// An array containing null
|
||||
// ------------------------
|
||||
array = new Integer[] { null };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertEquals(null, result.getValue(0, 0));
|
||||
|
||||
// An array containing two values (some DB's can't guarantee ordering)
|
||||
// -------------------------------------------------------------------
|
||||
array = new Integer[] { null, 1 };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertTrue(asList(array).containsAll(result.getValues(0)));
|
||||
|
||||
// An array containing three values (some DB's can't guarantee ordering)
|
||||
// ---------------------------------------------------------------------
|
||||
array = new Integer[] { null, 1, 2 };
|
||||
result = create().select().from(table(array)).fetch();
|
||||
|
||||
assertEquals(3, result.size());
|
||||
assertEquals(1, result.getFields().size());
|
||||
assertTrue(asList(array).containsAll(result.getValues(0)));
|
||||
|
||||
// Joining an unnested array table
|
||||
// -------------------------------
|
||||
array = new Integer[] { 2, 3 };
|
||||
Table<?> table = table(array);
|
||||
result = create()
|
||||
.select(TBook_ID(), TBook_TITLE())
|
||||
.from(TBook())
|
||||
.join(table)
|
||||
.on(table.getField(0).cast(Integer.class).equal(TBook_ID()))
|
||||
.orderBy(TBook_ID().asc())
|
||||
.fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(Integer.valueOf(2), result.getValue(0, TBook_ID()));
|
||||
assertEquals(Integer.valueOf(3), result.getValue(1, TBook_ID()));
|
||||
assertEquals("Animal Farm", result.getValue(0, TBook_TITLE()));
|
||||
assertEquals("O Alquimista", result.getValue(1, TBook_TITLE()));
|
||||
|
||||
// Joining an aliased unnested array table
|
||||
// ---------------------------------------
|
||||
result = create()
|
||||
.select(TBook_ID(), TBook_TITLE())
|
||||
.from(TBook())
|
||||
.join(table.as("t"))
|
||||
.on(table.as("t").getField(0).cast(Integer.class).equal(TBook_ID()))
|
||||
.orderBy(TBook_ID().asc())
|
||||
.fetch();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(Integer.valueOf(2), result.getValue(0, TBook_ID()));
|
||||
assertEquals(Integer.valueOf(3), result.getValue(1, TBook_ID()));
|
||||
assertEquals("Animal Farm", result.getValue(0, TBook_TITLE()));
|
||||
assertEquals("O Alquimista", result.getValue(1, TBook_TITLE()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoredProceduresWithCursorParameters() throws Exception {
|
||||
switch (getDialect()) {
|
||||
@ -9566,7 +9581,7 @@ public abstract class jOOQAbstractTest<
|
||||
create().loadInto(TAuthor())
|
||||
.loadCSV(
|
||||
"####Some Data####\n" +
|
||||
"\"ID\",\"Last Name\"\r" +
|
||||
"\"ID\",\"Last Qualifier\"\r" +
|
||||
"3,Hesse\n" +
|
||||
"4,Frisch")
|
||||
.fields(TAuthor_ID(), TAuthor_LAST_NAME())
|
||||
@ -9592,7 +9607,7 @@ public abstract class jOOQAbstractTest<
|
||||
loader =
|
||||
create().loadInto(TAuthor())
|
||||
.loadCSV(
|
||||
"\"ID\",\"First Name\",\"Last Name\"\r" +
|
||||
"\"ID\",\"First Qualifier\",\"Last Qualifier\"\r" +
|
||||
"5,Hermann,Hesse\n" +
|
||||
"6,\"Max\",Frisch")
|
||||
.fields(TAuthor_ID(), null, TAuthor_LAST_NAME())
|
||||
@ -9638,7 +9653,7 @@ public abstract class jOOQAbstractTest<
|
||||
create().loadInto(TAuthor())
|
||||
.onDuplicateKeyUpdate()
|
||||
.loadCSV(
|
||||
"\"ID\",\"First Name\",\"Last Name\"\r" +
|
||||
"\"ID\",\"First Qualifier\",\"Last Qualifier\"\r" +
|
||||
"1,Hermann,Hesse\n" +
|
||||
"7,\"Max\",Frisch")
|
||||
.fields(TAuthor_ID(), null, TAuthor_LAST_NAME())
|
||||
@ -9678,7 +9693,7 @@ public abstract class jOOQAbstractTest<
|
||||
.onDuplicateKeyError()
|
||||
.onErrorAbort()
|
||||
.loadCSV(
|
||||
"\"ID\",\"First Name\",\"Last Name\"\r" +
|
||||
"\"ID\",\"First Qualifier\",\"Last Qualifier\"\r" +
|
||||
"8,Hermann,Hesse\n" +
|
||||
"1,\"Max\",Frisch\n" +
|
||||
"2,Friedrich,Dürrenmatt")
|
||||
@ -9710,7 +9725,7 @@ public abstract class jOOQAbstractTest<
|
||||
.onDuplicateKeyIgnore()
|
||||
.onErrorAbort()
|
||||
.loadCSV(
|
||||
"\"ID\",\"First Name\",\"Last Name\"\r" +
|
||||
"\"ID\",\"First Qualifier\",\"Last Qualifier\"\r" +
|
||||
"8,Hermann,Hesse\n" +
|
||||
"1,\"Max\",Frisch\n" +
|
||||
"2,Friedrich,Dürrenmatt")
|
||||
|
||||
@ -517,7 +517,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition equalAny(T... array);
|
||||
|
||||
/**
|
||||
@ -550,7 +550,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition equalAll(T... array);
|
||||
|
||||
/**
|
||||
@ -596,7 +596,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition notEqualAny(T... array);
|
||||
|
||||
/**
|
||||
@ -629,7 +629,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition notEqualAll(T... array);
|
||||
|
||||
/**
|
||||
@ -671,7 +671,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition lessThanAny(T... array);
|
||||
|
||||
/**
|
||||
@ -704,7 +704,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition lessThanAll(T... array);
|
||||
|
||||
/**
|
||||
@ -746,7 +746,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition lessOrEqualAny(T... array);
|
||||
|
||||
/**
|
||||
@ -779,7 +779,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition lessOrEqualAll(T... array);
|
||||
|
||||
/**
|
||||
@ -821,7 +821,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition greaterThanAny(T... array);
|
||||
|
||||
/**
|
||||
@ -854,7 +854,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition greaterThanAll(T... array);
|
||||
|
||||
/**
|
||||
@ -896,7 +896,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition greaterOrEqualAny(T... array);
|
||||
|
||||
/**
|
||||
@ -929,7 +929,7 @@ public interface Field<T> extends NamedTypeProviderQueryPart<T>, AliasProvider<F
|
||||
* This is natively supported by {@link SQLDialect#POSTGRES}. Other dialects
|
||||
* will render a subselect unnesting the array.
|
||||
*/
|
||||
@Support({ H2, HSQLDB, POSTGRES })
|
||||
@Support({ ASE, DB2, DERBY, H2, HSQLDB, INGRES, MYSQL, ORACLE, POSTGRES, SQLSERVER, SYBASE })
|
||||
Condition greaterOrEqualAll(T... array);
|
||||
|
||||
/**
|
||||
|
||||
@ -107,6 +107,8 @@ class AliasProviderImpl<T extends AliasProvider<T>> extends AbstractNamedQueryPa
|
||||
//
|
||||
// SELECT t.column_value FROM UNNEST(ARRAY[1, 2]) AS t(column_value)
|
||||
|
||||
// TODO: Is this still needed?
|
||||
|
||||
switch (context.getDialect()) {
|
||||
case HSQLDB:
|
||||
case POSTGRES: {
|
||||
@ -114,7 +116,7 @@ class AliasProviderImpl<T extends AliasProvider<T>> extends AbstractNamedQueryPa
|
||||
|
||||
// The javac compiler doesn't like casting of generics
|
||||
Object o = aliasProvider;
|
||||
ArrayTable<?> table = (ArrayTable<?>) o;
|
||||
ArrayTable table = (ArrayTable) o;
|
||||
|
||||
context.sql("(");
|
||||
Util.toSQLNames(context, table.getFields());
|
||||
|
||||
@ -36,15 +36,15 @@
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Factory.table;
|
||||
import static org.jooq.impl.Factory.val;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Attachable;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.RenderContext;
|
||||
import org.jooq.exception.SQLDialectNotSupportedException;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
@ -73,40 +73,37 @@ class ArrayAsSubqueryCondition<T> extends AbstractCondition {
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
switch (context.getDialect()) {
|
||||
|
||||
// [#869] H2 and HSQLDB can simulate this syntax by unnesting
|
||||
// the array in a subselect
|
||||
case H2:
|
||||
case HSQLDB: {
|
||||
context.sql(field)
|
||||
.sql(" ")
|
||||
.sql(operator.toSQL())
|
||||
.sql(" (")
|
||||
.sql(create(context).select().from(table(array)))
|
||||
.sql(")");
|
||||
break;
|
||||
}
|
||||
|
||||
// [#869] Postgres supports this syntax natively
|
||||
case POSTGRES: {
|
||||
context.sql(field)
|
||||
.sql(" ")
|
||||
.sql(operator.toSQL())
|
||||
.sql(" (")
|
||||
.sql(array)
|
||||
.sql(")");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new SQLDialectNotSupportedException("Arrays as subquery conditions not yet supported by " + context.getDialect());
|
||||
}
|
||||
context.sql(field)
|
||||
.sql(" ")
|
||||
.sql(operator.toSQL())
|
||||
.sql(" (")
|
||||
.sql(array(context))
|
||||
.sql(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {
|
||||
context.bind(field).bind(val(array));
|
||||
context.bind(field).bind(array(context));
|
||||
}
|
||||
|
||||
private final QueryPart array(Configuration context) {
|
||||
switch (context.getDialect()) {
|
||||
|
||||
// [#869] Postgres supports this syntax natively
|
||||
case POSTGRES: {
|
||||
return array;
|
||||
}
|
||||
|
||||
// [#869] H2 and HSQLDB can simulate this syntax by unnesting
|
||||
// the array in a subselect
|
||||
case H2:
|
||||
case HSQLDB:
|
||||
|
||||
// [#1048] All other dialects simulate unnesting of arrays using
|
||||
// UNION ALL-connected subselects
|
||||
default: {
|
||||
return create(context).select().from(table(array));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,41 +35,44 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Factory.field;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Attachable;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RenderContext;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.exception.SQLDialectNotSupportedException;
|
||||
import org.jooq.tools.StringUtils;
|
||||
import org.jooq.util.h2.H2DataType;
|
||||
|
||||
/**
|
||||
* An unnested array
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class ArrayTable<R extends Record> extends AbstractTable<R> {
|
||||
class ArrayTable extends AbstractTable<Record> {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 2380426377794577041L;
|
||||
private static final long serialVersionUID = 2380426377794577041L;
|
||||
|
||||
private final Field<?> array;
|
||||
private final FieldList field;
|
||||
private final String alias;
|
||||
private final Field<?> array;
|
||||
private final FieldList field;
|
||||
private final String alias;
|
||||
|
||||
ArrayTable(Field<?> array) {
|
||||
this(array, "array_table(COLUMN_VALUE)");
|
||||
this(array, "array_table");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
ArrayTable(Field<?> array, String alias) {
|
||||
super("array_table");
|
||||
super(alias);
|
||||
|
||||
Class<?> arrayType;
|
||||
if (array.getDataType().getType().isArray()) {
|
||||
@ -83,77 +86,170 @@ class ArrayTable<R extends Record> extends AbstractTable<R> {
|
||||
this.array = array;
|
||||
this.alias = alias;
|
||||
this.field = new FieldList();
|
||||
this.field.add(field("COLUMN_VALUE", arrayType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final Class<? extends R> getRecordType() {
|
||||
return (Class<? extends R>) RecordImpl.class;
|
||||
this.field.add(new Qualifier(Factory.getDataType(arrayType), alias, "COLUMN_VALUE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Table<R> as(String as) {
|
||||
return new TableAlias<R>(new ArrayTable<R>(array, ""), as);
|
||||
public final Class<? extends Record> getRecordType() {
|
||||
return RecordImpl.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Table<Record> as(String as) {
|
||||
return new ArrayTable(array, as);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean declaresTables() {
|
||||
|
||||
// Always true, because unnested tables are always aliased
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
switch (context.getDialect()) {
|
||||
context.sql(table(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {
|
||||
context.bind(table(context));
|
||||
}
|
||||
|
||||
private final Table<Record> table(Configuration configuration) {
|
||||
switch (configuration.getDialect()) {
|
||||
case ORACLE: {
|
||||
context.sql("table(").sql(array).sql(")");
|
||||
break;
|
||||
if (array.getDataType().getType().isArray()) {
|
||||
return simulate().as(alias);
|
||||
}
|
||||
else {
|
||||
return new OracleArrayTable().as(alias);
|
||||
}
|
||||
}
|
||||
|
||||
case H2: {
|
||||
context.sql("table(COLUMN_VALUE ");
|
||||
|
||||
// If the array type is unknown (e.g. because it's returned from a stored function
|
||||
// Then the best choice for arbitrary types is varchar
|
||||
if (array.getDataType().getType() == Object[].class) {
|
||||
context.sql(H2DataType.VARCHAR.getTypeName());
|
||||
}
|
||||
else {
|
||||
context.sql(array.getDataType().getTypeName());
|
||||
}
|
||||
|
||||
context.sql(" = ").sql(array).sql(")");
|
||||
break;
|
||||
return new H2ArrayTable().as(alias);
|
||||
}
|
||||
|
||||
// [#756] These dialects need special care when aliasing unnested
|
||||
// arrays
|
||||
case HSQLDB:
|
||||
case POSTGRES: {
|
||||
context.sql("unnest(").sql(array).sql(")");
|
||||
|
||||
if (!StringUtils.isBlank(alias)) {
|
||||
context.sql(" as ").sql(alias);
|
||||
}
|
||||
|
||||
break;
|
||||
return new PostgresHSQLDBTable().as(alias);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new SQLDialectNotSupportedException("ARRAY TABLE is not supported for " + context.getDialect());
|
||||
// Other dialects can simulate unnested arrays using UNION ALL
|
||||
default: {
|
||||
if (array.getDataType().getType().isArray() && array instanceof Param) {
|
||||
return simulate();
|
||||
}
|
||||
|
||||
else {
|
||||
throw new SQLDialectNotSupportedException("ARRAY TABLE is not supported for " + configuration.getDialect());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {
|
||||
switch (context.getDialect()) {
|
||||
case ORACLE:
|
||||
case H2:
|
||||
case HSQLDB:
|
||||
case POSTGRES:
|
||||
context.bind(array);
|
||||
break;
|
||||
private class PostgresHSQLDBTable extends DialectArrayTable {
|
||||
|
||||
default:
|
||||
throw new SQLDialectNotSupportedException("ARRAY TABLE is not supported for " + context.getDialect());
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 6989279597964488457L;
|
||||
|
||||
@Override
|
||||
public void toSQL(RenderContext context) {
|
||||
context.sql("(select * from unnest(")
|
||||
.sql(array)
|
||||
.sql(") as ")
|
||||
.literal(alias)
|
||||
.sql("(")
|
||||
.literal("COLUMN_VALUE")
|
||||
.sql("))");
|
||||
}
|
||||
}
|
||||
|
||||
private class H2ArrayTable extends DialectArrayTable {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 8679404596822098711L;
|
||||
|
||||
@Override
|
||||
public void toSQL(RenderContext context) {
|
||||
context.sql("table(COLUMN_VALUE ");
|
||||
|
||||
// If the array type is unknown (e.g. because it's returned from
|
||||
// a stored function
|
||||
// Then the best choice for arbitrary types is varchar
|
||||
if (array.getDataType().getType() == Object[].class) {
|
||||
context.sql(H2DataType.VARCHAR.getTypeName());
|
||||
}
|
||||
else {
|
||||
context.sql(array.getDataType().getTypeName());
|
||||
}
|
||||
|
||||
context.sql(" = ").sql(array).sql(")");
|
||||
}
|
||||
}
|
||||
|
||||
private class OracleArrayTable extends DialectArrayTable {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 1716687061980551706L;
|
||||
|
||||
@Override
|
||||
public void toSQL(RenderContext context) {
|
||||
context.sql("table (").sql(array).sql(")");
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class DialectArrayTable extends AbstractTable<Record> {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 2662639259338694177L;
|
||||
|
||||
DialectArrayTable() {
|
||||
super(alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Record> getRecordType() {
|
||||
return RecordImpl.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Table<Record> as(String as) {
|
||||
return new TableAlias<Record>(this, as);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(BindContext context) throws DataAccessException {
|
||||
context.bind(array);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FieldList getFieldList() {
|
||||
return ArrayTable.this.getFieldList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Attachable> getAttachables0() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private final ArrayTableSimulation simulate() {
|
||||
return new ArrayTableSimulation(((Param<Object[]>) array).getValue(), alias);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final FieldList getFieldList() {
|
||||
return field;
|
||||
|
||||
151
jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java
Normal file
151
jOOQ/src/main/java/org/jooq/impl/ArrayTableSimulation.java
Normal file
@ -0,0 +1,151 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed to you under the Apache License, Version 2.0
|
||||
* (the "License"); You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* . Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* . Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* . Neither the name "jOOQ" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Factory.falseCondition;
|
||||
import static org.jooq.impl.Factory.one;
|
||||
import static org.jooq.impl.Factory.vals;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Attachable;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.RenderContext;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
|
||||
/**
|
||||
* Essentially, this is the same as {@link ArrayTable}, except that it simulates
|
||||
* unnested arrays using <code>UNION ALL</code>
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class ArrayTableSimulation extends AbstractTable<Record> {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 2392515064450536343L;
|
||||
|
||||
private final Object[] array;
|
||||
private final FieldList field;
|
||||
private final String alias;
|
||||
|
||||
private transient Table<Record> table;
|
||||
|
||||
ArrayTableSimulation(Object[] array) {
|
||||
this(array, "array_table");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
ArrayTableSimulation(Object[] array, String alias) {
|
||||
super(alias);
|
||||
|
||||
this.array = array;
|
||||
this.field = new FieldList();
|
||||
this.alias = alias;
|
||||
this.field.add(new Qualifier(Factory.getDataType(array.getClass().getComponentType()), alias, "COLUMN_VALUE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Class<? extends Record> getRecordType() {
|
||||
return RecordImpl.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Table<Record> as(String as) {
|
||||
return new ArrayTableSimulation(array, as);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean declaresTables() {
|
||||
|
||||
// [#1055] Always true, because unnested tables are always aliased.
|
||||
// This is particularly important for simulated unnested arrays
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
context.sql(table(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) throws DataAccessException {
|
||||
context.bind(table(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final FieldList getFieldList() {
|
||||
return field;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final List<Attachable> getAttachables0() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private final Table<Record> table(Configuration configuration) {
|
||||
if (table == null) {
|
||||
Select<Record> select = null;
|
||||
|
||||
for (Field<?> val : vals(array)) {
|
||||
Select<Record> subselect = create(configuration).select(val.as("COLUMN_VALUE"));
|
||||
|
||||
if (select == null) {
|
||||
select = subselect;
|
||||
}
|
||||
else {
|
||||
select = select.unionAll(subselect);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty arrays should result in empty tables
|
||||
if (select == null) {
|
||||
select = create(configuration).select(one().as("COLUMN_VALUE")).where(falseCondition());
|
||||
}
|
||||
|
||||
table = select.asTable(alias);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
@ -455,17 +455,17 @@ public class Factory implements FactoryOperations {
|
||||
|
||||
// The field is an Oracle-style VARRAY constant
|
||||
else if (ArrayConstant.class.isAssignableFrom(cursor.getClass())) {
|
||||
return new ArrayTable<Record>(cursor);
|
||||
return new ArrayTable(cursor);
|
||||
}
|
||||
|
||||
// The field is an Oracle-style VARRAY field
|
||||
else if (ArrayRecord.class.isAssignableFrom(cursor.getDataType().getType())) {
|
||||
return new ArrayTable<Record>(cursor);
|
||||
return new ArrayTable(cursor);
|
||||
}
|
||||
|
||||
// The field is a regular array
|
||||
else if (cursor.getType().isArray() && cursor.getType() != byte[].class) {
|
||||
return new ArrayTable<Record>(cursor);
|
||||
return new ArrayTable(cursor);
|
||||
}
|
||||
|
||||
// The field has any other type. Try to make it an array
|
||||
|
||||
95
jOOQ/src/main/java/org/jooq/impl/Qualifier.java
Normal file
95
jOOQ/src/main/java/org/jooq/impl/Qualifier.java
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2009-2011, Lukas Eder, lukas.eder@gmail.com
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is licensed to you under the Apache License, Version 2.0
|
||||
* (the "License"); You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* . Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* . Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* . Neither the name "jOOQ" nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.Attachable;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.RenderContext;
|
||||
|
||||
/**
|
||||
* A <code>Qualifier</code> is a {@link Field} that always renders a field name
|
||||
* or alias as a literal using {@link RenderContext#literal(String)}
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class Qualifier<T> extends AbstractField<T> {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = 6937002867156868761L;
|
||||
|
||||
private final String[] sql;
|
||||
|
||||
Qualifier(DataType<T> type, String... sql) {
|
||||
super(sql[sql.length - 1], type);
|
||||
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Field API
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public final List<Attachable> getAttachables() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
String separator = "";
|
||||
for (String string : sql) {
|
||||
context.sql(separator);
|
||||
context.literal(string);
|
||||
|
||||
separator = ".";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {}
|
||||
|
||||
@Override
|
||||
public final boolean isNullLiteral() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user