mirror of https://github.com/apache/openjpa.git
OPENJPA-488, OPENJPA-489, OPENJPA-490, OPENJPA-491, OPENJPA-492
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@612345 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
72f7d7ce43
commit
8c4cf9511e
|
@ -26,6 +26,8 @@ import org.apache.openjpa.jdbc.kernel.exps.FilterValue;
|
||||||
public abstract class AbstractDB2Dictionary
|
public abstract class AbstractDB2Dictionary
|
||||||
extends DBDictionary {
|
extends DBDictionary {
|
||||||
|
|
||||||
|
public int varcharCastLength = 1000;
|
||||||
|
|
||||||
public AbstractDB2Dictionary() {
|
public AbstractDB2Dictionary() {
|
||||||
numericTypeName = "DOUBLE";
|
numericTypeName = "DOUBLE";
|
||||||
bitTypeName = "SMALLINT";
|
bitTypeName = "SMALLINT";
|
||||||
|
@ -36,11 +38,13 @@ public abstract class AbstractDB2Dictionary
|
||||||
|
|
||||||
// DB2-based databases have restrictions on having uncast parameters
|
// DB2-based databases have restrictions on having uncast parameters
|
||||||
// in string functions
|
// in string functions
|
||||||
toUpperCaseFunction = "UPPER(CAST({0} AS VARCHAR(1000)))";
|
toUpperCaseFunction = "UPPER(CAST({0} AS VARCHAR(" + varcharCastLength
|
||||||
toLowerCaseFunction = "LOWER(CAST({0} AS VARCHAR(1000)))";
|
+ ")))";
|
||||||
|
toLowerCaseFunction = "LOWER(CAST({0} AS VARCHAR(" + varcharCastLength
|
||||||
|
+ ")))";
|
||||||
stringLengthFunction = "LENGTH({0})";
|
stringLengthFunction = "LENGTH({0})";
|
||||||
concatenateFunction = "(CAST({0} AS VARCHAR(1000)))||"
|
concatenateFunction = "(CAST({0} AS VARCHAR(" + varcharCastLength
|
||||||
+ "(CAST({1} AS VARCHAR(1000)))";
|
+ ")))||(CAST({1} AS VARCHAR(1000)))";
|
||||||
|
|
||||||
trimLeadingFunction = "LTRIM({0})";
|
trimLeadingFunction = "LTRIM({0})";
|
||||||
trimTrailingFunction = "RTRIM({0})";
|
trimTrailingFunction = "RTRIM({0})";
|
||||||
|
@ -74,9 +78,11 @@ public abstract class AbstractDB2Dictionary
|
||||||
FilterValue start) {
|
FilterValue start) {
|
||||||
buf.append("(LOCATE(CAST((");
|
buf.append("(LOCATE(CAST((");
|
||||||
find.appendTo(buf);
|
find.appendTo(buf);
|
||||||
buf.append(") AS VARCHAR(1000)), CAST((");
|
buf.append(") AS VARCHAR(").append(Integer.toString(varcharCastLength))
|
||||||
|
.append(")), CAST((");
|
||||||
str.appendTo(buf);
|
str.appendTo(buf);
|
||||||
buf.append(") AS VARCHAR(1000))");
|
buf.append(") AS VARCHAR(").append(Integer.toString(varcharCastLength))
|
||||||
|
.append("))");
|
||||||
if (start != null) {
|
if (start != null) {
|
||||||
buf.append(", CAST((");
|
buf.append(", CAST((");
|
||||||
start.appendTo(buf);
|
start.appendTo(buf);
|
||||||
|
@ -89,15 +95,30 @@ public abstract class AbstractDB2Dictionary
|
||||||
FilterValue end) {
|
FilterValue end) {
|
||||||
buf.append("SUBSTR(CAST((");
|
buf.append("SUBSTR(CAST((");
|
||||||
str.appendTo(buf);
|
str.appendTo(buf);
|
||||||
buf.append(") AS VARCHAR(1000)), CAST((");
|
buf.append(") AS VARCHAR(").append(Integer.toString(varcharCastLength))
|
||||||
start.appendTo(buf);
|
.append(")), ");
|
||||||
buf.append(") AS INTEGER) + 1");
|
if (start.getValue() instanceof Number) {
|
||||||
if (end != null) {
|
long startLong = toLong(start);
|
||||||
buf.append(", CAST((");
|
buf.append(Long.toString(startLong + 1));
|
||||||
end.appendTo(buf);
|
} else {
|
||||||
buf.append(") AS INTEGER) - CAST((");
|
buf.append("CAST((");
|
||||||
start.appendTo(buf);
|
start.appendTo(buf);
|
||||||
buf.append(") AS INTEGER)");
|
buf.append(") AS INTEGER) + 1");
|
||||||
|
}
|
||||||
|
if (end != null) {
|
||||||
|
buf.append(", ");
|
||||||
|
if (start.getValue() instanceof Number
|
||||||
|
&& end.getValue() instanceof Number) {
|
||||||
|
long startLong = toLong(start);
|
||||||
|
long endLong = toLong(end);
|
||||||
|
buf.append(Long.toString(endLong - startLong));
|
||||||
|
} else {
|
||||||
|
buf.append("CAST((");
|
||||||
|
end.appendTo(buf);
|
||||||
|
buf.append(") AS INTEGER) - CAST((");
|
||||||
|
start.appendTo(buf);
|
||||||
|
buf.append(") AS INTEGER)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf.append(")");
|
buf.append(")");
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,23 +117,23 @@ public abstract class AbstractSQLServerDictionary
|
||||||
|
|
||||||
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
||||||
FilterValue end) {
|
FilterValue end) {
|
||||||
buf.append("SUBSTRING(");
|
if (end != null)
|
||||||
str.appendTo(buf);
|
super.substring(buf, str, start, end);
|
||||||
buf.append(", ");
|
else {
|
||||||
start.appendTo(buf);
|
// ### it would be good to change this logic as in DBDictionary to
|
||||||
buf.append(" + 1, ");
|
// ### simplify the generated SQL
|
||||||
if (end != null) {
|
buf.append("SUBSTRING(");
|
||||||
buf.append("(");
|
str.appendTo(buf);
|
||||||
end.appendTo(buf);
|
buf.append(", ");
|
||||||
buf.append(")");
|
start.appendTo(buf);
|
||||||
} else {
|
buf.append(" + 1, ");
|
||||||
buf.append("LEN(");
|
buf.append("LEN(");
|
||||||
str.appendTo(buf);
|
str.appendTo(buf);
|
||||||
buf.append(")");
|
buf.append(")");
|
||||||
|
buf.append(" - (");
|
||||||
|
start.appendTo(buf);
|
||||||
|
buf.append("))");
|
||||||
}
|
}
|
||||||
buf.append(" - (");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append("))");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
|
public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
|
||||||
|
|
|
@ -63,6 +63,8 @@ public class AccessDictionary
|
||||||
supportsForeignKeys = false;
|
supportsForeignKeys = false;
|
||||||
supportsDeferredConstraints = false;
|
supportsDeferredConstraints = false;
|
||||||
maxIndexesPerTable = 32;
|
maxIndexesPerTable = 32;
|
||||||
|
|
||||||
|
substringFunctionName = "MID";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLong(PreparedStatement stmnt, int idx, long val, Column col)
|
public void setLong(PreparedStatement stmnt, int idx, long val, Column col)
|
||||||
|
@ -75,22 +77,5 @@ public class AccessDictionary
|
||||||
else
|
else
|
||||||
stmnt.setDouble(idx, val);
|
stmnt.setDouble(idx, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
|
||||||
FilterValue end) {
|
|
||||||
buf.append("MID(");
|
|
||||||
str.appendTo(buf);
|
|
||||||
buf.append(", (");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(" + 1)");
|
|
||||||
if (end != null) {
|
|
||||||
buf.append(", (");
|
|
||||||
end.appendTo(buf);
|
|
||||||
buf.append(" - ");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ import org.apache.openjpa.jdbc.schema.Table;
|
||||||
import org.apache.openjpa.jdbc.schema.Unique;
|
import org.apache.openjpa.jdbc.schema.Unique;
|
||||||
import org.apache.openjpa.kernel.Filters;
|
import org.apache.openjpa.kernel.Filters;
|
||||||
import org.apache.openjpa.kernel.exps.Path;
|
import org.apache.openjpa.kernel.exps.Path;
|
||||||
|
import org.apache.openjpa.kernel.exps.Literal;
|
||||||
import org.apache.openjpa.lib.conf.Configurable;
|
import org.apache.openjpa.lib.conf.Configurable;
|
||||||
import org.apache.openjpa.lib.conf.Configuration;
|
import org.apache.openjpa.lib.conf.Configuration;
|
||||||
import org.apache.openjpa.lib.jdbc.ConnectionDecorator;
|
import org.apache.openjpa.lib.jdbc.ConnectionDecorator;
|
||||||
|
@ -2494,21 +2495,38 @@ public class DBDictionary
|
||||||
*/
|
*/
|
||||||
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
||||||
FilterValue end) {
|
FilterValue end) {
|
||||||
buf.append(substringFunctionName).append("((");
|
buf.append(substringFunctionName).append("(");
|
||||||
str.appendTo(buf);
|
str.appendTo(buf);
|
||||||
buf.append("), (");
|
buf.append(", ");
|
||||||
start.appendTo(buf);
|
if (start instanceof Number) {
|
||||||
buf.append(" + 1)");
|
long startLong = toLong(start);
|
||||||
if (end != null) {
|
buf.append(Long.toString(startLong + 1));
|
||||||
buf.append(", (");
|
} else {
|
||||||
end.appendTo(buf);
|
buf.append("(");
|
||||||
buf.append(" - (");
|
|
||||||
start.appendTo(buf);
|
start.appendTo(buf);
|
||||||
buf.append("))");
|
buf.append(" + 1)");
|
||||||
|
}
|
||||||
|
if (end != null) {
|
||||||
|
buf.append(", ");
|
||||||
|
if (start.getValue() instanceof Number
|
||||||
|
&& end.getValue() instanceof Number) {
|
||||||
|
long startLong = toLong(start);
|
||||||
|
long endLong = toLong(end);
|
||||||
|
buf.append(Long.toString(endLong - startLong));
|
||||||
|
} else {
|
||||||
|
end.appendTo(buf);
|
||||||
|
buf.append(" - (");
|
||||||
|
start.appendTo(buf);
|
||||||
|
buf.append(")");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buf.append(")");
|
buf.append(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long toLong(FilterValue litValue) {
|
||||||
|
return ((Number) litValue.getValue()).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke this database's indexOf function.
|
* Invoke this database's indexOf function.
|
||||||
*
|
*
|
||||||
|
|
|
@ -193,23 +193,6 @@ public class H2Dictionary extends DBDictionary {
|
||||||
buf.append(" OFFSET ").appendValue(start);
|
buf.append(" OFFSET ").appendValue(start);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
|
||||||
FilterValue end) {
|
|
||||||
buf.append("SUBSTR(");
|
|
||||||
str.appendTo(buf);
|
|
||||||
buf.append(", (");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(" + 1)");
|
|
||||||
if (end != null) {
|
|
||||||
buf.append(", (");
|
|
||||||
end.appendTo(buf);
|
|
||||||
buf.append(" - ");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
|
public void indexOf(SQLBuffer buf, FilterValue str, FilterValue find,
|
||||||
FilterValue start) {
|
FilterValue start) {
|
||||||
buf.append("(POSITION(");
|
buf.append("(POSITION(");
|
||||||
|
|
|
@ -157,6 +157,8 @@ public class OracleDictionary
|
||||||
"LONG", "MAXEXTENTS", "MINUS", "MODE", "NOAUDIT", "NOCOMPRESS",
|
"LONG", "MAXEXTENTS", "MINUS", "MODE", "NOAUDIT", "NOCOMPRESS",
|
||||||
"NOWAIT", "OFFLINE", "ONLINE", "PCTFREE", "ROW",
|
"NOWAIT", "OFFLINE", "ONLINE", "PCTFREE", "ROW",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
substringFunctionName = "SUBSTR";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endConfiguration() {
|
public void endConfiguration() {
|
||||||
|
@ -427,23 +429,6 @@ public class OracleDictionary
|
||||||
return select;
|
return select;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void substring(SQLBuffer buf, FilterValue str, FilterValue start,
|
|
||||||
FilterValue end) {
|
|
||||||
buf.append("SUBSTR(");
|
|
||||||
str.appendTo(buf);
|
|
||||||
buf.append(", (");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(" + 1)");
|
|
||||||
if (end != null) {
|
|
||||||
buf.append(", (");
|
|
||||||
end.appendTo(buf);
|
|
||||||
buf.append(" - ");
|
|
||||||
start.appendTo(buf);
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
buf.append(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setString(PreparedStatement stmnt, int idx, String val,
|
public void setString(PreparedStatement stmnt, int idx, String val,
|
||||||
Column col)
|
Column col)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
|
|
|
@ -334,7 +334,8 @@ public interface ExpressionFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a value representing the {@link String#substring} function on
|
* Return a value representing the {@link String#substring} function on
|
||||||
* the given target with the given args.
|
* the given target with the given args. As with {@link String#substring},
|
||||||
|
* the start index is zero-based, and the second argument is the end index.
|
||||||
*/
|
*/
|
||||||
public Value substring(Value str, Value args);
|
public Value substring(Value str, Value args);
|
||||||
|
|
||||||
|
|
|
@ -977,21 +977,28 @@ public class JPQLExpressionBuilder
|
||||||
// arg2 is the end index): we perform the translation by
|
// arg2 is the end index): we perform the translation by
|
||||||
// adding one to the first argument, and then adding the
|
// adding one to the first argument, and then adding the
|
||||||
// first argument to the second argument to get the endIndex
|
// first argument to the second argument to get the endIndex
|
||||||
//
|
Value start;
|
||||||
// ### we could get rid of some messy expressions by checking for
|
Value end;
|
||||||
// the common case where the arguments are specified as
|
if (val2 instanceof Literal && val3 instanceof Literal) {
|
||||||
// a literal, in which case we could just do the calculations
|
// optimize SQL for the common case of two literals
|
||||||
// in memory; otherwise we wind up with ugly looking SQL like:
|
long jpqlStart = ((Number) ((Literal) val2).getValue())
|
||||||
// SELECT ... FROM ... t1
|
.longValue();
|
||||||
// (SUBSTRING(t1.ASTR, (? - ?) + 1, (? + (? - ?)) - ((? - ?))) = ?)
|
long length = ((Number) ((Literal) val3).getValue())
|
||||||
// [params=(long) 2, (int) 1, (long) 2, (long) 2, (int) 1,
|
.longValue();
|
||||||
// (long) 2, (int) 1, (String) oo
|
start = factory.newLiteral(new Long(jpqlStart - 1),
|
||||||
return factory.substring(val1, factory.newArgumentList
|
Literal.TYPE_NUMBER);
|
||||||
(factory.subtract(val2, factory.newLiteral
|
long endIndex = length + (jpqlStart - 1);
|
||||||
(Numbers.valueOf(1), Literal.TYPE_NUMBER)),
|
end = factory.newLiteral(new Long(endIndex),
|
||||||
(factory.add(val3,
|
Literal.TYPE_NUMBER);
|
||||||
(factory.subtract(val2, factory.newLiteral
|
} else {
|
||||||
(Numbers.valueOf(1), Literal.TYPE_NUMBER)))))));
|
start = factory.subtract(val2, factory.newLiteral
|
||||||
|
(Numbers.valueOf(1), Literal.TYPE_NUMBER));
|
||||||
|
end = factory.add(val3,
|
||||||
|
(factory.subtract(val2, factory.newLiteral
|
||||||
|
(Numbers.valueOf(1), Literal.TYPE_NUMBER))));
|
||||||
|
}
|
||||||
|
return factory.substring(val1, factory.newArgumentList(
|
||||||
|
start, end));
|
||||||
|
|
||||||
case JJTLOCATE:
|
case JJTLOCATE:
|
||||||
// as with SUBSTRING (above), the semantics for LOCATE differ
|
// as with SUBSTRING (above), the semantics for LOCATE differ
|
||||||
|
@ -1067,6 +1074,15 @@ public class JPQLExpressionBuilder
|
||||||
case JJTCURRENTTIMESTAMP:
|
case JJTCURRENTTIMESTAMP:
|
||||||
return factory.getCurrentTimestamp();
|
return factory.getCurrentTimestamp();
|
||||||
|
|
||||||
|
case JJTSELECTEXTENSION:
|
||||||
|
return eval(node.children[0]);
|
||||||
|
|
||||||
|
case JJTGROUPBYEXTENSION:
|
||||||
|
return eval(node.children[0]);
|
||||||
|
|
||||||
|
case JJTORDERBYEXTENSION:
|
||||||
|
return eval(node.children[0]);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw parseException(EX_FATAL, "bad-tree",
|
throw parseException(EX_FATAL, "bad-tree",
|
||||||
new Object[]{ node }, null);
|
new Object[]{ node }, null);
|
||||||
|
@ -1669,9 +1685,6 @@ public class JPQLExpressionBuilder
|
||||||
// parser may sometimes (unfortunately) throw
|
// parser may sometimes (unfortunately) throw
|
||||||
throw new UserException(_loc.get("parse-error",
|
throw new UserException(_loc.get("parse-error",
|
||||||
new Object[]{ e.toString(), jpql }));
|
new Object[]{ e.toString(), jpql }));
|
||||||
} catch (ParseException e) {
|
|
||||||
throw new UserException(_loc.get("parse-error",
|
|
||||||
new Object[]{ e.toString(), jpql }), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ import java.io.*;
|
||||||
public class JPQL
|
public class JPQL
|
||||||
{
|
{
|
||||||
String jpql;
|
String jpql;
|
||||||
|
boolean extensionsEnabled = true;
|
||||||
|
|
||||||
|
|
||||||
public JPQL (String jpql)
|
public JPQL (String jpql)
|
||||||
|
@ -498,6 +499,13 @@ void select_expression() #SELECTEXPRESSION : { }
|
||||||
| identification_variable()
|
| identification_variable()
|
||||||
| (<OBJECT> "(" identification_variable() ")")
|
| (<OBJECT> "(" identification_variable() ")")
|
||||||
| constructor_expression()
|
| constructor_expression()
|
||||||
|
| select_extension()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void select_extension() #SELECTEXTENSION(extensionsEnabled) : { }
|
||||||
|
{
|
||||||
|
scalar_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -506,12 +514,12 @@ void subselect_expressions() #SELECTEXPRESSIONS : { }
|
||||||
subselect_expression() (<COMMA> subselect_expression())*
|
subselect_expression() (<COMMA> subselect_expression())*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void subselect_expression() #SELECTEXPRESSION : { }
|
void subselect_expression() #SELECTEXPRESSION : { }
|
||||||
{
|
{
|
||||||
LOOKAHEAD(path()) path()
|
LOOKAHEAD(path()) path()
|
||||||
| aggregate_select_expression()
|
| aggregate_select_expression()
|
||||||
| LOOKAHEAD(1) identification_variable()
|
| LOOKAHEAD(1) identification_variable()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -615,7 +623,13 @@ void groupby_clause() #GROUPBY : { }
|
||||||
|
|
||||||
void groupby_item() : { }
|
void groupby_item() : { }
|
||||||
{
|
{
|
||||||
LOOKAHEAD(path()) path() | identification_variable()
|
LOOKAHEAD(path()) path() | identification_variable() | groupby_extension()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void groupby_extension() #GROUPBYEXTENSION(extensionsEnabled) : { }
|
||||||
|
{
|
||||||
|
scalar_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -841,6 +855,12 @@ void datetime_comp() : { }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scalar_function() : { }
|
||||||
|
{
|
||||||
|
functions_returning_numerics()
|
||||||
|
| functions_returning_datetime()
|
||||||
|
| functions_returning_strings()
|
||||||
|
}
|
||||||
|
|
||||||
void arithmetic_value() : { }
|
void arithmetic_value() : { }
|
||||||
{
|
{
|
||||||
|
@ -1073,7 +1093,14 @@ void orderby_clause() #ORDERBY : { }
|
||||||
|
|
||||||
void orderby_item() #ORDERBYITEM : { }
|
void orderby_item() #ORDERBYITEM : { }
|
||||||
{
|
{
|
||||||
path() [ <ASC> #ASCENDING | <DESC> #DESCENDING ]
|
(LOOKAHEAD(path()) path() | orderby_extension())
|
||||||
|
[ <ASC> #ASCENDING | <DESC> #DESCENDING ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void orderby_extension() #ORDERBYEXTENSION(extensionsEnabled) : { }
|
||||||
|
{
|
||||||
|
aggregate_select_expression()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.jdbc.query;
|
||||||
|
|
||||||
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.query.GroupingTestCase;
|
||||||
|
|
||||||
|
public class TestJDBCGrouping extends GroupingTestCase {
|
||||||
|
|
||||||
|
protected void prepareQuery(Query q) {
|
||||||
|
// nothing to do for JDBC case
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
package org.apache.openjpa.persistence.query;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMTestCase;
|
||||||
|
import org.apache.openjpa.persistence.simple.AllFieldTypes;
|
||||||
|
import org.apache.openjpa.persistence.ArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Tests grouping and having capabilities.</p>
|
||||||
|
*
|
||||||
|
* @author Abe White
|
||||||
|
*/
|
||||||
|
public abstract class GroupingTestCase
|
||||||
|
extends SingleEMTestCase {
|
||||||
|
|
||||||
|
protected abstract void prepareQuery(Query q);
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp(AllFieldTypes.class, CLEAR_TABLES, "openjpa.Log", "SQL=TRACE");
|
||||||
|
|
||||||
|
AllFieldTypes pc1 = new AllFieldTypes();
|
||||||
|
AllFieldTypes pc2 = new AllFieldTypes();
|
||||||
|
AllFieldTypes pc3 = new AllFieldTypes();
|
||||||
|
AllFieldTypes pc4 = new AllFieldTypes();
|
||||||
|
|
||||||
|
// pc1 and pc2, pc3 and pc4 grouped on intField, shortField
|
||||||
|
pc1.setIntField(1);
|
||||||
|
pc1.setShortField((short) -1);
|
||||||
|
pc2.setIntField(1);
|
||||||
|
pc2.setShortField((short) -1);
|
||||||
|
pc3.setIntField(2);
|
||||||
|
pc3.setShortField((short) -2);
|
||||||
|
pc4.setIntField(2);
|
||||||
|
pc4.setShortField((short) -2);
|
||||||
|
|
||||||
|
// pc1 and pc2 grouped on stringField
|
||||||
|
pc1.setStringField("abc");
|
||||||
|
pc2.setStringField("acd");
|
||||||
|
pc3.setStringField("def");
|
||||||
|
pc4.setStringField("efg");
|
||||||
|
|
||||||
|
// pc2 and pc3 grouped on byteField
|
||||||
|
pc2.setByteField((byte) 1);
|
||||||
|
pc3.setByteField((byte) 1);
|
||||||
|
pc1.setByteField((byte) 0);
|
||||||
|
pc4.setByteField((byte) 2);
|
||||||
|
|
||||||
|
// longField is unique id
|
||||||
|
pc1.setLongField(1L);
|
||||||
|
pc2.setLongField(2L);
|
||||||
|
pc3.setLongField(3L);
|
||||||
|
pc4.setLongField(4L);
|
||||||
|
|
||||||
|
// set up some relations
|
||||||
|
pc1.setSelfOneOne(pc4);
|
||||||
|
pc2.setSelfOneOne(pc3);
|
||||||
|
pc3.setSelfOneOne(pc2);
|
||||||
|
pc4.setSelfOneOne(pc1);
|
||||||
|
|
||||||
|
// if variable testing, set up some 1-Ms instead of the 1-1s above
|
||||||
|
if (getName().startsWith("testVariable")) {
|
||||||
|
pc1.setSelfOneOne(pc1);
|
||||||
|
pc2.setSelfOneOne(pc1);
|
||||||
|
pc1.getSelfOneMany().add(pc1);
|
||||||
|
pc1.getSelfOneMany().add(pc2);
|
||||||
|
|
||||||
|
pc3.setSelfOneOne(pc3);
|
||||||
|
pc4.setSelfOneOne(pc3);
|
||||||
|
pc3.getSelfOneMany().add(pc3);
|
||||||
|
pc3.getSelfOneMany().add(pc4);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist(pc1);
|
||||||
|
em.persist(pc2);
|
||||||
|
em.persist(pc3);
|
||||||
|
em.persist(pc4);
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimpleGroup() {
|
||||||
|
Query q = em.createQuery("select o.intField from AllFieldTypes o " +
|
||||||
|
"group by o.intField order by o.intField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Integer(1), itr.next());
|
||||||
|
assertEquals(new Integer(2), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOrderByAggregate() {
|
||||||
|
// this is an extension of JPQL
|
||||||
|
Query q = em.createQuery("select sum(o.shortField) " +
|
||||||
|
"from AllFieldTypes o"
|
||||||
|
+ " group by o.intField order by sum(o.shortField) asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
// this might fail in MySQL
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Long(-4), itr.next());
|
||||||
|
assertEquals(new Long(-2), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCompoundGroupSame() {
|
||||||
|
Query q = em.createQuery("select o.intField from AllFieldTypes o " +
|
||||||
|
"group by o.intField, o.shortField order by o.shortField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Integer(2), itr.next());
|
||||||
|
assertEquals(new Integer(1), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCompoundGroupDifferent() {
|
||||||
|
Query q = em.createQuery("select o.intField from AllFieldTypes o " +
|
||||||
|
"group by o.intField, o.byteField order by o.intField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(4, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Integer(1), itr.next());
|
||||||
|
assertEquals(new Integer(1), itr.next());
|
||||||
|
assertEquals(new Integer(2), itr.next());
|
||||||
|
assertEquals(new Integer(2), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDifferentGroupLengths() {
|
||||||
|
Query q = em.createQuery("select o.byteField from AllFieldTypes o"
|
||||||
|
+ " group by o.byteField order by o.byteField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(3, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals((byte) 0, itr.next());
|
||||||
|
assertEquals((byte) 1, itr.next());
|
||||||
|
assertEquals((byte) 2, itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGroupRelationField() {
|
||||||
|
Query q = em.createQuery("select o.selfOneOne.intField " +
|
||||||
|
"from AllFieldTypes o group by o.selfOneOne.intField " +
|
||||||
|
"order by o.selfOneOne.intField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Integer(1), itr.next());
|
||||||
|
assertEquals(new Integer(2), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubstringInGroupBy() {
|
||||||
|
// this is an extension of JPQL
|
||||||
|
Query q = em.createQuery("select substring(o.stringField, 1, 1), " +
|
||||||
|
"count(o) from AllFieldTypes o " +
|
||||||
|
"group by substring(o.stringField, 1, 1)");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(3, res.size());
|
||||||
|
|
||||||
|
q = em.createQuery("select substring(o.stringField, 1, 2), count(o) " +
|
||||||
|
"from AllFieldTypes o group by substring(o.stringField, 1, 2)");
|
||||||
|
prepareQuery(q);
|
||||||
|
res = q.getResultList();
|
||||||
|
assertEquals(4, res.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGroupedAggregate() {
|
||||||
|
Query q = em.createQuery("select count(o) from AllFieldTypes o " +
|
||||||
|
"group by o.byteField order by o.byteField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(3, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Long(1), itr.next());
|
||||||
|
assertEquals(new Long(2), itr.next());
|
||||||
|
assertEquals(new Long(1), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGroupedRelationAggregate() {
|
||||||
|
Query q = em.createQuery("select count(o), max(o.selfOneOne.longField)"
|
||||||
|
+ " from AllFieldTypes o group by o.intField"
|
||||||
|
+ " order by o.intField asc");
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
Object[] o = (Object[]) itr.next();
|
||||||
|
assertEquals(new Long(2), o[0]);
|
||||||
|
assertEquals(new Long(4), o[1]);
|
||||||
|
o = (Object[]) itr.next();
|
||||||
|
assertEquals(new Long(2), o[0]);
|
||||||
|
assertEquals(new Long(2), o[1]);
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGroupedMixedProjection() {
|
||||||
|
Query q = em.createQuery("select count(o), o.shortField " +
|
||||||
|
"from AllFieldTypes o group by o.intField, o.shortField " +
|
||||||
|
"order by o.intField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
Object[] o = (Object[]) itr.next();
|
||||||
|
assertEquals(new Long(2), o[0]);
|
||||||
|
assertEquals(new Short((short) -1), o[1]);
|
||||||
|
o = (Object[]) itr.next();
|
||||||
|
assertEquals(new Long(2), o[0]);
|
||||||
|
assertEquals(new Short((short) -2), o[1]);
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSimpleHaving() {
|
||||||
|
Query q = em.createQuery("select o.intField from AllFieldTypes o " +
|
||||||
|
"group by o.intField having o.intField < 2");
|
||||||
|
prepareQuery(q);
|
||||||
|
assertEquals(new Integer(1), q.getSingleResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAggregateHaving() {
|
||||||
|
Query q = em.createQuery("select o.byteField from AllFieldTypes o " +
|
||||||
|
"group by o.byteField having count(o) > 1");
|
||||||
|
prepareQuery(q);
|
||||||
|
assertEquals(new Byte((byte) 1), q.getSingleResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMixedHaving() {
|
||||||
|
Query q = em.createQuery("select o.byteField from AllFieldTypes o " +
|
||||||
|
"group by o.byteField having count(o) > 1 or o.byteField = 0 " +
|
||||||
|
"order by o.byteField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Byte((byte) 0), itr.next());
|
||||||
|
assertEquals(new Byte((byte) 1), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testVariableGroup() {
|
||||||
|
Query q = em.createQuery("select max(other.longField) " +
|
||||||
|
"from AllFieldTypes o, AllFieldTypes other " +
|
||||||
|
"where other member of o.selfOneMany " +
|
||||||
|
"group by other.intField order by other.intField asc");
|
||||||
|
prepareQuery(q);
|
||||||
|
List res = q.getResultList();
|
||||||
|
assertEquals(2, res.size());
|
||||||
|
Iterator itr = res.iterator();
|
||||||
|
assertEquals(new Long(2), itr.next());
|
||||||
|
assertEquals(new Long(4), itr.next());
|
||||||
|
assertTrue(!itr.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testVariableHaving() {
|
||||||
|
Query q = em.createQuery("select max(o.longField), other.byteField " +
|
||||||
|
"from AllFieldTypes o, AllFieldTypes other " +
|
||||||
|
"where other member of o.selfOneMany " +
|
||||||
|
"group by other.byteField having sum(other.intField) = 2");
|
||||||
|
prepareQuery(q);
|
||||||
|
assertEquals(new Long(3), ((Object[])q.getSingleResult())[0]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ public class TestQueryPagination
|
||||||
extends SQLListenerTestCase {
|
extends SQLListenerTestCase {
|
||||||
|
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
setUp(SimpleEntity.class, CLEAR_TABLES, "openjpa.Log", "SQL=TRACE");
|
setUp(SimpleEntity.class, CLEAR_TABLES);
|
||||||
|
|
||||||
EntityManager em = emf.createEntityManager();
|
EntityManager em = emf.createEntityManager();
|
||||||
em.getTransaction().begin();
|
em.getTransaction().begin();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.openjpa.persistence.query;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.test.SingleEMTestCase;
|
||||||
|
|
||||||
|
public class TestSubstring extends SingleEMTestCase {
|
||||||
|
|
||||||
|
public void setUp() {
|
||||||
|
super.setUp(SimpleEntity.class, CLEAR_TABLES, "openjpa.Log", "SQL=TRACE");
|
||||||
|
|
||||||
|
EntityManager em = emf.createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist(new SimpleEntity("foo", "bar"));
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSingleCharacterSubstringInWhere() {
|
||||||
|
assertEquals((long) 1, em.createQuery("select count(o) from simple o " +
|
||||||
|
"where substring(o.value, 1, 1) = 'b'").getSingleResult());
|
||||||
|
assertEquals((long) 1, em.createQuery("select count(o) from simple o " +
|
||||||
|
"where substring(o.value, 2, 1) = 'a'").getSingleResult());
|
||||||
|
assertEquals((long) 1, em.createQuery("select count(o) from simple o " +
|
||||||
|
"where substring(o.value, 3, 1) = 'r'").getSingleResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiCharacterSubstringInWhere() {
|
||||||
|
assertEquals((long) 1, em.createQuery("select count(o) from simple o " +
|
||||||
|
"where substring(o.value, 1, 2) = 'ba'").getSingleResult());
|
||||||
|
assertEquals((long) 1, em.createQuery("select count(o) from simple o " +
|
||||||
|
"where substring(o.value, 2, 2) = 'ar'").getSingleResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubstringInSelect() {
|
||||||
|
assertEquals("b", em.createQuery("select substring(o.value, 1, 1) " +
|
||||||
|
"from simple o").getSingleResult());
|
||||||
|
assertEquals("a", em.createQuery("select substring(o.value, 2, 1) " +
|
||||||
|
"from simple o").getSingleResult());
|
||||||
|
assertEquals("r", em.createQuery("select substring(o.value, 3, 1) " +
|
||||||
|
"from simple o").getSingleResult());
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,14 +25,18 @@ import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Version;
|
import javax.persistence.Version;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
import org.apache.openjpa.persistence.PersistentCollection;
|
import org.apache.openjpa.persistence.PersistentCollection;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class AllFieldTypes {
|
public class AllFieldTypes {
|
||||||
|
|
||||||
public static enum EnumType {Value1, Value2};
|
public static enum EnumType {Value1, Value2};
|
||||||
|
|
||||||
// @Basic types
|
// @Basic types
|
||||||
|
@ -74,6 +78,12 @@ public class AllFieldTypes {
|
||||||
@PersistentCollection
|
@PersistentCollection
|
||||||
private int[] arrayOfInts;
|
private int[] arrayOfInts;
|
||||||
|
|
||||||
|
// one-to-one and one-to-many relations to self
|
||||||
|
@OneToOne
|
||||||
|
private AllFieldTypes selfOneOne;
|
||||||
|
@OneToMany
|
||||||
|
private List<AllFieldTypes> selfOneMany = new ArrayList<AllFieldTypes>();
|
||||||
|
|
||||||
public void setShortField(short shortField) {
|
public void setShortField(short shortField) {
|
||||||
this.shortField = shortField;
|
this.shortField = shortField;
|
||||||
}
|
}
|
||||||
|
@ -338,5 +348,20 @@ public class AllFieldTypes {
|
||||||
wShortField = shortField;
|
wShortField = shortField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AllFieldTypes getSelfOneOne() {
|
||||||
|
return selfOneOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelfOneOne(AllFieldTypes selfOneOne) {
|
||||||
|
this.selfOneOne = selfOneOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AllFieldTypes> getSelfOneMany() {
|
||||||
|
return selfOneMany;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelfOneMany(List<AllFieldTypes> selfOneMany) {
|
||||||
|
this.selfOneMany = selfOneMany;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue