Skip to content

Commit

Permalink
Jakarta Persistence 3.2 new feature - JPQL functions LEFT(), RIGHT() (#…
Browse files Browse the repository at this point in the history
…1933)

This change contains following changes:

New JPQL String functions LEFT(), RIGHT() for return leftmost, rightmost number of character from string passed as a first function argument
Tests are available in the JPQL parser and JPA JSE test module
EclipseLink issue is described at #1885 and Jakarta Persistence specification request at the jakartaee/persistence#396
This feature is not directly functional on Apache Derby Database and Oracle Database, but for a booth platforms is implemented overridden operator based on SUBSTR(...) SQL function. See org.eclipse.persistence.platform.database.DerbyPlatform#derbyLeftOperator | derbyRightOperator
and
org.eclipse.persistence.platform.database.OraclePlatform#oracleLeft | oracleRight .

LEFT seemed to be in collision with LEFT JOIN and similar, but after code inspection of usage org.eclipse.persistence.jpa.jpql.parser.Expression.LEFT it never happens.
Main meaning if org.eclipse.persistence.jpa.jpql.parser.Expression.LEFT is now as LEFT() function identifier and in some particular cases String constant "LEFT" behind org.eclipse.persistence.jpa.jpql.parser.Expression.LEFT is used to parse JOINs.

Signed-off-by: Radek Felcman <radek.felcman@oracle.com>
  • Loading branch information
rfelcman committed Aug 29, 2023
1 parent 7dfeab5 commit abe7bf9
Show file tree
Hide file tree
Showing 49 changed files with 1,915 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3064,6 +3064,37 @@ public Expression regexp(Expression regexp) {
return anOperator.expressionFor(this, regexp);
}

/**
* PUBLIC:
* Function return a given number of characters starting at the
* left of a string. Equivalent to the Sybase LEFT function
* <p>Example:
* <blockquote><pre>
* EclipseLink: employee.get("name").left(2)
* Java: NA
* SQL: LEFT(name, 2)
* </pre></blockquote>
*/
public Expression left(int characters) {
return left(Integer.valueOf(characters));
}

/**
* PUBLIC:
* Function return a given number of characters starting at the
* left of a string. Equivalent to the Sybase LEFT function
* <p>Example:
* <blockquote><pre>
* EclipseLink: employee.get("name").left(2)
* Java: NA
* SQL: LEFT(name, 2)
* </pre></blockquote>
*/
public Expression left(Object characters) {
ExpressionOperator anOperator = getOperator(ExpressionOperator.Left);
return anOperator.expressionFor(this, characters);
}

/**
* PUBLIC:
* Return an expression that compares if the receivers value is like other value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ public class ExpressionOperator implements Serializable {
public static final int Reverse = 99;
public static final int Replicate = 100;
public static final int Right = 101;
public static final int Left = 153;
public static final int Locate = 112;
public static final int Locate2 = 113;
public static final int ToChar = 114;
Expand Down Expand Up @@ -1650,6 +1651,7 @@ public static Map<Integer, String> initializePlatformOperatorNames() {
platformOperatorNames.put(Reverse, "Reverse");
platformOperatorNames.put(Replicate, "Replicate");
platformOperatorNames.put(Right, "Right");
platformOperatorNames.put(Left, "Left");
platformOperatorNames.put(Locate, "Locate");
platformOperatorNames.put(Locate2, "Locate");
platformOperatorNames.put(ToNumber, "ToNumber");
Expand Down Expand Up @@ -1754,6 +1756,7 @@ public static Map<String, Integer> initializePlatformOperatorSelectors() {
platformOperatorNames.put("Reverse", Reverse);
platformOperatorNames.put("Replicate", Replicate);
platformOperatorNames.put("Right", Right);
platformOperatorNames.put("Left", Left);
platformOperatorNames.put("Locate", Locate);
platformOperatorNames.put("ToNumber", ToNumber);
platformOperatorNames.put("ToChar", ToChar);
Expand Down Expand Up @@ -2001,6 +2004,14 @@ public static ExpressionOperator regexp() {
return result;
}

/**
* INTERNAL:
* Build operator.
*/
public static ExpressionOperator left() {
return simpleTwoArgumentFunction(Left, "LEFT");
}

/**
* INTERNAL:
* Create the LIKE operator with ESCAPE.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,10 +458,12 @@ protected void initializePlatformOperators() {
addOperator(ExpressionOperator.initcap());
addOperator(ExpressionOperator.instring());
addOperator(ExpressionOperator.soundex());
addOperator(ExpressionOperator.left());
addOperator(ExpressionOperator.leftPad());
addOperator(ExpressionOperator.leftTrim());
addOperator(ExpressionOperator.leftTrim2());
addOperator(ExpressionOperator.replace());
addOperator(ExpressionOperator.right());
addOperator(ExpressionOperator.rightPad());
addOperator(ExpressionOperator.rightTrim());
addOperator(ExpressionOperator.rightTrim2());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import org.eclipse.persistence.jpa.jpql.parser.Join;
import org.eclipse.persistence.jpa.jpql.parser.KeyExpression;
import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression;
import org.eclipse.persistence.jpa.jpql.parser.LeftExpression;
import org.eclipse.persistence.jpa.jpql.parser.LengthExpression;
import org.eclipse.persistence.jpa.jpql.parser.LikeExpression;
import org.eclipse.persistence.jpa.jpql.parser.LocalDateTime;
Expand Down Expand Up @@ -120,6 +121,7 @@
import org.eclipse.persistence.jpa.jpql.parser.RegexpExpression;
import org.eclipse.persistence.jpa.jpql.parser.ReplaceExpression;
import org.eclipse.persistence.jpa.jpql.parser.ResultVariable;
import org.eclipse.persistence.jpa.jpql.parser.RightExpression;
import org.eclipse.persistence.jpa.jpql.parser.SelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SimpleFromClause;
Expand Down Expand Up @@ -1276,6 +1278,24 @@ else if (keyword == KeywordExpression.TRUE) {
queryExpression = new ConstantExpression(value, queryExpression);
}

@Override
public void visit(LeftExpression expression) {

// Create the first expression
expression.getFirstExpression().accept(this);
Expression firstExpression = queryExpression;

// Create the second expression
expression.getSecondExpression().accept(this);
Expression secondExpression = queryExpression;

// Now create the LEFT expression
queryExpression = firstExpression.left(secondExpression);

// Set the expression type
type[0] = String.class;
}

@Override
public void visit(LengthExpression expression) {

Expand Down Expand Up @@ -1790,6 +1810,24 @@ public void visit(ResultVariable expression) {
// Note: The type will be calculated when traversing the select expression
}

@Override
public void visit(RightExpression expression) {

// Create the first expression
expression.getFirstExpression().accept(this);
Expression firstExpression = queryExpression;

// Create the second expression
expression.getSecondExpression().accept(this);
Expression secondExpression = queryExpression;

// Now create the RIGHT expression
queryExpression = firstExpression.right(secondExpression);

// Set the expression type
type[0] = String.class;
}

@Override
public void visit(SelectClause expression) {
// Nothing to do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.eclipse.persistence.jpa.jpql.parser.Join;
import org.eclipse.persistence.jpa.jpql.parser.KeyExpression;
import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression;
import org.eclipse.persistence.jpa.jpql.parser.LeftExpression;
import org.eclipse.persistence.jpa.jpql.parser.LengthExpression;
import org.eclipse.persistence.jpa.jpql.parser.LocateExpression;
import org.eclipse.persistence.jpa.jpql.parser.LowerExpression;
Expand All @@ -60,6 +61,7 @@
import org.eclipse.persistence.jpa.jpql.parser.ObjectExpression;
import org.eclipse.persistence.jpa.jpql.parser.ReplaceExpression;
import org.eclipse.persistence.jpa.jpql.parser.ResultVariable;
import org.eclipse.persistence.jpa.jpql.parser.RightExpression;
import org.eclipse.persistence.jpa.jpql.parser.SelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SimpleSelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SizeExpression;
Expand Down Expand Up @@ -377,6 +379,12 @@ public void visit(KeywordExpression expression) {
addAttribute("CONSTANT", queryExpression);
}

@Override
public void visit(LeftExpression expression) {
Expression queryExpression = queryContext.buildExpression(expression, type);
addAttribute(ExpressionTools.EMPTY_STRING, queryExpression, type[0]);
}

@Override
public void visit(LengthExpression expression) {
Expression queryExpression = queryContext.buildExpression(expression, type);
Expand Down Expand Up @@ -511,6 +519,12 @@ public void visit(ResultVariable expression) {
resultVariable = null;
}

@Override
public void visit(RightExpression expression) {
Expression queryExpression = queryContext.buildExpression(expression, type);
addAttribute(ExpressionTools.EMPTY_STRING, queryExpression, type[0]);
}

@Override
public void visit(SelectClause expression) {
visitAbstractSelectClause(expression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.eclipse.persistence.jpa.jpql.parser.Join;
import org.eclipse.persistence.jpa.jpql.parser.KeyExpression;
import org.eclipse.persistence.jpa.jpql.parser.KeywordExpression;
import org.eclipse.persistence.jpa.jpql.parser.LeftExpression;
import org.eclipse.persistence.jpa.jpql.parser.LengthExpression;
import org.eclipse.persistence.jpa.jpql.parser.LikeExpression;
import org.eclipse.persistence.jpa.jpql.parser.LocalDateTime;
Expand Down Expand Up @@ -105,6 +106,7 @@
import org.eclipse.persistence.jpa.jpql.parser.RegexpExpression;
import org.eclipse.persistence.jpa.jpql.parser.ReplaceExpression;
import org.eclipse.persistence.jpa.jpql.parser.ResultVariable;
import org.eclipse.persistence.jpa.jpql.parser.RightExpression;
import org.eclipse.persistence.jpa.jpql.parser.SelectClause;
import org.eclipse.persistence.jpa.jpql.parser.SelectStatement;
import org.eclipse.persistence.jpa.jpql.parser.SimpleFromClause;
Expand Down Expand Up @@ -862,6 +864,11 @@ public void visit(KeywordExpression expression) {
}
}

@Override
public void visit(LeftExpression expression) {
type = String.class;
}

@Override
public void visit(LengthExpression expression) {
type = Integer.class;
Expand Down Expand Up @@ -1081,6 +1088,11 @@ public void visit(ResultVariable expression) {
expression.getSelectExpression().accept(this);
}

@Override
public void visit(RightExpression expression) {
type = String.class;
}

@Override
public void visit(SelectClause expression) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ protected void initializePlatformOperators() {
addOperator(ExpressionOperator.simpleFunction(ExpressionOperator.ToNumber, "DOUBLE"));
addOperator(derbyExtractOperator());
addOperator(derbyPowerOperator());
addOperator(derbyLeftOperator());
addOperator(derbyRightOperator());

addOperator(avgOperator());
addOperator(sumOperator());
Expand Down Expand Up @@ -486,6 +488,48 @@ private static ExpressionOperator derbyPowerOperator() {
return operator;
}

/**
* INTERNAL:
* Derby equivalent to LEFT e.g. LEFT(dname, 5) is SUBSTR e.g. SUBSTR(dname, 1, 5).
*/
protected static ExpressionOperator derbyLeftOperator() {
ExpressionOperator exOperator = new ExpressionOperator();
exOperator.setType(ExpressionOperator.FunctionOperator);
exOperator.setSelector(ExpressionOperator.Left);
List<String> v = new ArrayList<>(3);
v.add("SUBSTR(");
v.add(", 1, ");
v.add(")");
exOperator.printsAs(v);
exOperator.bePrefix();
int[] indices = { 0, 1 };
exOperator.setArgumentIndices(indices);
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
return exOperator;
}

/**
* INTERNAL:
* Derby equivalent to RIGHT e.g. RIGHT(dname, 5) is SUBSTR e.g. SUBSTR(dname, LENGTH(dname) +1 - 5, 5).
*/
protected static ExpressionOperator derbyRightOperator() {
ExpressionOperator exOperator = new ExpressionOperator();
exOperator.setType(ExpressionOperator.FunctionOperator);
exOperator.setSelector(ExpressionOperator.Right);
List<String> v = new ArrayList<>(3);
v.add("SUBSTR(");
v.add(", LENGTH(");
v.add(") + 1 - ");
v.add(", ");
v.add(")");
exOperator.printsAs(v);
exOperator.bePrefix();
int[] indices = { 0, 0, 1, 1 };
exOperator.setArgumentIndices(indices);
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
return exOperator;
}

// Derby does not suport native EXTRACT at all. There are specific functions for most of the date/time parts.
// QUARTER part needs to be calculated from MONTH value.
// SECOND does not return fraction part and there is no fraction specific function available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,8 @@ protected void initializePlatformOperators() {
addOperator(regexpOperator());
addOperator(exceptOperator());
addOperator(oracleExtractOperator());
addOperator(oracleLeft());
addOperator(oracleRight());
}

/**
Expand Down Expand Up @@ -725,6 +727,46 @@ protected static ExpressionOperator oracleDateName() {
return exOperator;
}

/**
* INTERNAL:
* Oracle equivalent to LEFT e.g. LEFT(dname, 5) is SUBSTR e.g. SUBSTR(dname, 1, 5).
*/
protected static ExpressionOperator oracleLeft() {
ExpressionOperator exOperator = new ExpressionOperator();
exOperator.setType(ExpressionOperator.FunctionOperator);
exOperator.setSelector(ExpressionOperator.Left);
List<String> v = new ArrayList<>(3);
v.add("SUBSTR(");
v.add(", 1, ");
v.add(")");
exOperator.printsAs(v);
exOperator.bePrefix();
int[] indices = { 0, 1 };
exOperator.setArgumentIndices(indices);
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
return exOperator;
}

/**
* INTERNAL:
* Oracle equivalent to RIGHT e.g. RIGHT(dname, 5) is SUBSTR e.g. SUBSTR(dname, -5).
*/
protected static ExpressionOperator oracleRight() {
ExpressionOperator exOperator = new ExpressionOperator();
exOperator.setType(ExpressionOperator.FunctionOperator);
exOperator.setSelector(ExpressionOperator.Right);
List<String> v = new ArrayList<>(3);
v.add("SUBSTR(");
v.add(", -");
v.add(")");
exOperator.printsAs(v);
exOperator.bePrefix();
int[] indices = { 0, 1 };
exOperator.setArgumentIndices(indices);
exOperator.setNodeClass(ClassConstants.FunctionExpression_Class);
return exOperator;
}

// Oracle EXTRACT operator needs emulation of QUARTER and WEEK date/time parts.
private static final class OracleExtractOperator extends ExtractOperator {

Expand Down

0 comments on commit abe7bf9

Please sign in to comment.