blob: 15ac6fbc0628f227d4f77e1bd8711538d729ad3f [file] [log] [blame]
/*
* Copyright (c) 2006, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation
//
package org.eclipse.persistence.jpa.jpql.parser;
import org.eclipse.persistence.jpa.jpql.EclipseLinkVersion;
import org.eclipse.persistence.jpa.jpql.JPAVersion;
import org.eclipse.persistence.jpa.jpql.parser.FunctionExpressionFactory.ParameterCount;
import static org.eclipse.persistence.jpa.jpql.parser.Expression.*;
/**
* <p>This {@link JPQLGrammar} provides support for parsing JPQL queries defined in <a
* href="http://jcp.org/en/jsr/detail?id=317">JSR-338 - Java Persistence 2.1</a> and the additional
* support provided by EclipseLink 2.4.</p>
*
* The BNFs of the additional support are the following:
*
* <pre><code> select_statement ::= select_clause from_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause] {union_clause}*
*
* union_clause ::= { UNION | INTERSECT | EXCEPT} [ALL] subquery
*
* from_clause ::= FROM identification_variable_declaration {, {identification_variable_declaration |
* collection_member_declaration |
* (subquery) |
* table_variable_declaration }}*
*
* range_variable_declaration ::= root_object [AS] identification_variable
*
* root_object ::= abstract_schema_name | fully_qualified_class_name
*
* table_variable_declaration ::= table_expression [AS] identification_variable
*
* join ::= join_spec { abstract_schema_name | join_association_path_expression } [AS] identification_variable [join_condition]
*
* functions_returning_datetime ::= cast_expression |
* extract_expression |
* ...
*
* functions_returning_string ::= cast_expression |
* extract_expression |
* ...
*
* functions_returning_numeric ::= cast_expression |
* extract_expression |
* ...
*
* simple_cond_expression ::= regexp_expression |
* ...
*
* function_expression ::= { FUNC | FUNCTION | OPERATOR | SQL | COLUMN } (string_literal {, function_arg}*)
*
* regexp_expression ::= string_expression REGEXP pattern_value
*
* extract_expression ::= EXTRACT(date_part_literal [FROM] scalar_expression)
*
* table_expression ::= TABLE(string_literal)
*
* date_part_literal ::= { MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER |
* YEAR | SECOND_MICROSECOND | MINUTE_MICROSECOND | MINUTE_SECOND |
* HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE | DAY_MICROSECOND |
* DAY_SECOND | DAY_MINUTE | DAY_HOUR | YEAR_MONTH, etc }
*
* cast_expression ::= CAST(scalar_expression [AS] database_type)
*
* database_type ::= data_type_literal [( [numeric_literal [, numeric_literal]] )]
*
* data_type_literal ::= [CHAR, VARCHAR, NUMERIC, INTEGER, DATE, TIME, TIMESTAMP, etc]</code></pre>
*
* <p>Provisional API: This interface is part of an interim API that is still under development and
* expected to change significantly before reaching stability. It is available at this early stage
* to solicit feedback from pioneering adopters on the understanding that any code that uses this
* API will almost certainly be broken (repeatedly) as the API evolves.</p>
*
* @version 2.5
* @since 2.4
* @author Pascal Filion
*/
@SuppressWarnings("nls")
public final class EclipseLinkJPQLGrammar2_4 extends AbstractJPQLGrammar {
/**
* The singleton instance of this {@link EclipseLinkJPQLGrammar2_4}.
*/
private static final JPQLGrammar INSTANCE = new EclipseLinkJPQLGrammar2_4();
/**
* The EclipseLink version, which is 2.4.
*/
public static final EclipseLinkVersion VERSION = EclipseLinkVersion.VERSION_2_4;
/**
* Creates a new <code>EclipseLinkJPQLGrammar2_4</code>.
*/
public EclipseLinkJPQLGrammar2_4() {
super();
}
/**
* Creates a new <code>EclipseLinkJPQLGrammar2_4</code>.
*
* @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without
* instantiating the base {@link JPQLGrammar}
*/
private EclipseLinkJPQLGrammar2_4(AbstractJPQLGrammar jpqlGrammar) {
super(jpqlGrammar);
}
/**
* Extends the given {@link JPQLGrammar} with the information of this one without instantiating
* the base {@link JPQLGrammar}.
*
* @param jpqlGrammar The {@link JPQLGrammar} to extend with the content of this one without
* instantiating the base {@link JPQLGrammar}
*/
public static void extend(AbstractJPQLGrammar jpqlGrammar) {
new EclipseLinkJPQLGrammar2_4(jpqlGrammar);
}
/**
* Returns the singleton instance of this class.
*
* @return The singleton instance of {@link EclipseLinkJPQLGrammar2_4}
*/
public static JPQLGrammar instance() {
return INSTANCE;
}
@Override
protected JPQLGrammar buildBaseGrammar() {
// First build the JPQL 2.1 grammar
JPQLGrammar2_1 jpqlGrammar = new JPQLGrammar2_1();
// Extend it by adding the EclipseLink 2.0 additional support
EclipseLinkJPQLGrammar2_0.extend(jpqlGrammar);
// Extend it by adding the EclipseLink 2.1 additional support
EclipseLinkJPQLGrammar2_1.extend(jpqlGrammar);
return jpqlGrammar;
}
@Override
public JPAVersion getJPAVersion() {
return JPAVersion.VERSION_2_1;
}
@Override
public String getProvider() {
return DefaultEclipseLinkJPQLGrammar.PROVIDER_NAME;
}
@Override
public String getProviderVersion() {
return VERSION.getVersion();
}
@Override
protected void initializeBNFs() {
registerBNF(new CastExpressionBNF());
registerBNF(new DatabaseTypeQueryBNF());
registerBNF(new ExtractExpressionBNF());
registerBNF(new InternalColumnExpressionBNF());
registerBNF(new RegexpExpressionBNF());
registerBNF(new TableExpressionBNF());
registerBNF(new TableVariableDeclarationBNF());
registerBNF(new UnionClauseBNF());
// This is required to properly validate an entity name used as a join association path
addChildBNF(JoinAssociationPathExpressionBNF.ID, AbstractSchemaNameBNF.ID);
// Override (internal) simple_select_expression to add support for result variable
registerBNF(new SimpleResultVariableBNF());
// Note: This should only support SQL expression
addChildBNF(SimpleConditionalExpressionBNF.ID, FunctionExpressionBNF.ID);
// CAST
addChildBNF(FunctionsReturningDatetimeBNF.ID, CastExpressionBNF.ID);
addChildBNF(FunctionsReturningNumericsBNF.ID, CastExpressionBNF.ID);
addChildBNF(FunctionsReturningStringsBNF.ID, CastExpressionBNF.ID);
// EXTRACT
addChildBNF(FunctionsReturningDatetimeBNF.ID, ExtractExpressionBNF.ID);
addChildBNF(FunctionsReturningNumericsBNF.ID, ExtractExpressionBNF.ID);
addChildBNF(FunctionsReturningStringsBNF.ID, ExtractExpressionBNF.ID);
// REGEXP
addChildBNF(SimpleConditionalExpressionBNF.ID, RegexpExpressionBNF.ID);
// Add subquery support to RangeDeclarationBNF
addChildBNF(RangeDeclarationBNF.ID, SubqueryBNF.ID);
// Add table declaration to the FROM clause's declaration
addChildBNF(InternalFromClauseBNF.ID, TableVariableDeclarationBNF.ID);
addChildBNF(InternalSimpleFromClauseBNF.ID, TableVariableDeclarationBNF.ID);
// Trickle down the handling of a sub expression (subquery) to RangeVariableDeclaration
// and in order to keep the hierarchy intact, otherwise the default behavior would be
// FromClause would parse the subquery immediately.
//
// FromClause
// |- IdentificationVariableDeclaration
// |- RangeVariableDeclaration [(subquery) AS identification_variable]
// |- SubExpression
// |- SimpleSelectStatement
setHandleSubExpression(InternalFromClauseBNF.ID, true);
setHandleSubExpression(InternalSimpleFromClauseBNF.ID, true);
setHandleSubExpression(IdentificationVariableDeclarationBNF.ID, true);
setHandleSubExpression(RangeVariableDeclarationBNF.ID, true);
}
@Override
protected void initializeExpressionFactories() {
registerFactory(new CastExpressionFactory());
registerFactory(new DatabaseTypeFactory());
registerFactory(new ExtractExpressionFactory());
registerFactory(new JoinCollectionValuedPathExpressionFactory());
registerFactory(new OnClauseFactory());
registerFactory(new RegexpExpressionFactory());
registerFactory(new TableExpressionFactory());
registerFactory(new TableVariableDeclarationFactory());
registerFactory(new UnionClauseFactory());
// Add a new FunctionExpression for 'COLUMN' since it has different rules
FunctionExpressionFactory columnExpressionFactory = new FunctionExpressionFactory(COLUMN, COLUMN);
columnExpressionFactory.setParameterCount(ParameterCount.ONE);
columnExpressionFactory.setParameterQueryBNFId(InternalColumnExpressionBNF.ID);
registerFactory(columnExpressionFactory);
// Add COLUMN ExpressionFactory to FunctionExpressionBNF
addChildFactory(FunctionExpressionBNF.ID, COLUMN);
// Change the fallback ExpressionFactory to add support for an abstract schema name
// as a valid join association path expression
setFallbackExpressionFactoryId(JoinAssociationPathExpressionBNF.ID, JoinCollectionValuedPathExpressionFactory.ID);
}
@Override
protected void initializeIdentifiers() {
// Expand FunctionExpression to support 'FUNCTION', 'OPERATOR' and 'SQL'
addIdentifiers(FunctionExpressionFactory.ID, FUNCTION, OPERATOR, SQL);
registerIdentifierRole(CAST, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2)
registerIdentifierRole(COLUMN, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2)
registerIdentifierRole(EXCEPT, IdentifierRole.CLAUSE);
registerIdentifierRole(EXTRACT, IdentifierRole.FUNCTION); // EXTRACT(x FROM y)
registerIdentifierRole(FUNCTION, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2)
registerIdentifierRole(INTERSECT, IdentifierRole.CLAUSE);
registerIdentifierRole(NULLS_FIRST, IdentifierRole.COMPLEMENT);
registerIdentifierRole(NULLS_LAST, IdentifierRole.COMPLEMENT);
registerIdentifierRole(ON, IdentifierRole.CLAUSE); // ON x
registerIdentifierRole(OPERATOR, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2)
registerIdentifierRole(REGEXP, IdentifierRole.COMPOUND_FUNCTION); // x REGEXP y
registerIdentifierRole(SQL, IdentifierRole.FUNCTION); // FUNCTION(n, x1, ..., x2)
registerIdentifierRole(TABLE, IdentifierRole.FUNCTION); // TABLE('TABLE_NAME')
registerIdentifierRole(UNION, IdentifierRole.CLAUSE);
registerIdentifierVersion(CAST, JPAVersion.VERSION_2_1);
registerIdentifierVersion(COLUMN, JPAVersion.VERSION_2_1);
registerIdentifierVersion(EXCEPT, JPAVersion.VERSION_2_1);
registerIdentifierVersion(EXTRACT, JPAVersion.VERSION_2_1);
registerIdentifierVersion(FUNCTION, JPAVersion.VERSION_2_1);
registerIdentifierVersion(INTERSECT, JPAVersion.VERSION_2_1);
registerIdentifierVersion(NULLS_FIRST, JPAVersion.VERSION_2_1);
registerIdentifierVersion(NULLS_LAST, JPAVersion.VERSION_2_1);
registerIdentifierVersion(ON, JPAVersion.VERSION_2_1);
registerIdentifierVersion(OPERATOR, JPAVersion.VERSION_2_1);
registerIdentifierVersion(REGEXP, JPAVersion.VERSION_2_1);
registerIdentifierVersion(SQL, JPAVersion.VERSION_2_1);
registerIdentifierVersion(TABLE, JPAVersion.VERSION_2_1);
registerIdentifierVersion(UNION, JPAVersion.VERSION_2_1);
// Partial identifiers
registerIdentifierRole("NULLS", IdentifierRole.CLAUSE); // Part of NULLS FIRST, NULLS LAST
registerIdentifierRole("FIRST", IdentifierRole.CLAUSE); // Part of NULLS FIRST
registerIdentifierRole("LAST", IdentifierRole.CLAUSE); // Part of NULLS LAST
}
@Override
public String toString() {
return "EclipseLink 2.4";
}
}