use of org.datanucleus.store.query.compiler.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileFilter.
/**
* Method to compile the WHERE clause of the query into the SQLStatement.
*/
protected void compileFilter() {
if (compilation.getExprFilter() != null) {
// Apply the filter to the SQLStatement
compileComponent = CompilationComponent.FILTER;
if (QueryUtils.expressionHasOrOperator(compilation.getExprFilter())) {
compileProperties.put("Filter.OR", true);
}
if (QueryUtils.expressionHasNotOperator(compilation.getExprFilter())) {
// Really we want to know if there is a NOT contains, but just check NOT for now
compileProperties.put("Filter.NOT", true);
}
if (stmt instanceof SelectStatement && ((SelectStatement) stmt).getNumberOfUnions() > 0) {
// Process each UNIONed statement separately TODO This is only necessary when the filter contains things like "instanceof"/"TYPE" so maybe detect that
List<SelectStatement> unionStmts = ((SelectStatement) stmt).getUnions();
// a). Main SelectStatement, disable unions while we process it
SQLStatement originalStmt = stmt;
((SelectStatement) stmt).setAllowUnions(false);
SQLExpression filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
BooleanExpression filterExpr = getBooleanExpressionForUseInFilter((BooleanExpression) filterSqlExpr);
stmt.whereAnd(filterExpr, true);
((SelectStatement) stmt).setAllowUnions(true);
// b). UNIONed SelectStatements
for (SelectStatement unionStmt : unionStmts) {
stmt = unionStmt;
stmt.setQueryGenerator(this);
filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
filterExpr = getBooleanExpressionForUseInFilter((BooleanExpression) filterSqlExpr);
stmt.whereAnd(filterExpr, true);
stmt.setQueryGenerator(null);
}
stmt = originalStmt;
} else {
SQLExpression filterSqlExpr = (SQLExpression) compilation.getExprFilter().evaluate(this);
if (!(filterSqlExpr instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("Filter compiles to something that is not a boolean expression. Kindly fix your query : " + filterSqlExpr);
}
BooleanExpression filterExpr = (BooleanExpression) filterSqlExpr;
filterExpr = getBooleanExpressionForUseInFilter(filterExpr);
stmt.whereAnd(filterExpr, true);
}
compileComponent = null;
}
}
use of org.datanucleus.store.query.compiler.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method processCaseExpression.
protected Object processCaseExpression(CaseExpression expr, SQLExpression typeExpr) {
processingCase = true;
try {
List<ExpressionPair> conditions = expr.getConditions();
Iterator<ExpressionPair> whenExprIter = conditions.iterator();
SQLExpression[] whenSqlExprs = new SQLExpression[conditions.size()];
SQLExpression[] actionSqlExprs = new SQLExpression[conditions.size()];
boolean numericCase = false;
boolean booleanCase = false;
boolean stringCase = false;
boolean typeSet = false;
if (typeExpr != null) {
if (typeExpr instanceof NumericExpression) {
numericCase = true;
typeSet = true;
} else if (typeExpr instanceof BooleanExpression) {
booleanCase = true;
typeSet = true;
} else if (typeExpr instanceof StringExpression) {
stringCase = true;
typeSet = true;
}
}
int i = 0;
while (whenExprIter.hasNext()) {
ExpressionPair pair = whenExprIter.next();
Expression whenExpr = pair.getWhenExpression();
whenExpr.evaluate(this);
whenSqlExprs[i] = stack.pop();
if (!(whenSqlExprs[i] instanceof BooleanExpression)) {
throw new QueryCompilerSyntaxException("IF/ELSE conditional expression should return boolean but doesn't : " + expr);
}
Expression actionExpr = pair.getActionExpression();
actionExpr.evaluate(this);
actionSqlExprs[i] = stack.pop();
if (!typeSet) {
if (actionSqlExprs[i] instanceof NumericExpression) {
numericCase = true;
typeSet = true;
} else if (actionSqlExprs[i] instanceof BooleanExpression) {
booleanCase = true;
typeSet = true;
} else if (actionSqlExprs[i] instanceof StringExpression) {
stringCase = true;
typeSet = true;
}
}
i++;
}
Expression elseExpr = expr.getElseExpression();
elseExpr.evaluate(this);
SQLExpression elseActionSqlExpr = stack.pop();
// Check that all action sql expressions are consistent
for (int j = 1; j < actionSqlExprs.length; j++) {
if (!checkCaseExpressionsConsistent(actionSqlExprs[0], actionSqlExprs[j])) {
throw new QueryCompilerSyntaxException("IF/ELSE action expression " + actionSqlExprs[j] + " is of different type to first action " + actionSqlExprs[0] + " - must be consistent");
}
}
if (!checkCaseExpressionsConsistent(actionSqlExprs[0], elseActionSqlExpr)) {
throw new QueryCompilerSyntaxException("IF/ELSE action expression " + elseActionSqlExpr + " is of different type to first action " + actionSqlExprs[0] + " - must be consistent");
}
SQLExpression caseSqlExpr = null;
if (numericCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseNumericExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else if (booleanCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseBooleanExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else if (stringCase) {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseStringExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
} else {
caseSqlExpr = new org.datanucleus.store.rdbms.sql.expression.CaseExpression(whenSqlExprs, actionSqlExprs, elseActionSqlExpr);
}
stack.push(caseSqlExpr);
return caseSqlExpr;
} finally {
processingCase = false;
}
}
use of org.datanucleus.store.query.compiler.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method processParameterExpression.
/**
* Method to process a parameter expression.
* The optional argument controls whether we should create this as a parameter or as a literal (i.e the param value is known etc).
* If the parameter doesn't have its value defined then returns ParameterLiteral otherwise we get an XXXLiteral of the (declared) type of the parameter
* @param expr The ParameterExpression
* @param asLiteral Whether to create a SQLLiteral rather than a parameter literal
* @return The processed expression
*/
protected Object processParameterExpression(ParameterExpression expr, boolean asLiteral) {
if (compileComponent == CompilationComponent.ORDERING || compileComponent == CompilationComponent.RESULT) {
// All JDBC drivers I know don't allow parameters in the order-by, or update clause
// Note that we also don't allow parameters in result clause since SQLStatement squashes all SELECT expression to a String so losing info about params
asLiteral = true;
} else if (compileComponent == CompilationComponent.UPDATE && processingCase && !storeMgr.getDatastoreAdapter().supportsOption(DatastoreAdapter.PARAMETER_IN_CASE_IN_UPDATE_CLAUSE)) {
// This database doesn't support parameters within a CASE expression in the UPDATE clause, so process as a literal
asLiteral = true;
}
if (expr.getPosition() >= 0) {
if (paramNameByPosition == null) {
paramNameByPosition = new HashMap<>();
}
paramNameByPosition.put(Integer.valueOf(expr.getPosition()), expr.getId());
}
// Find the parameter value if supplied
Object paramValue = null;
boolean paramValueSet = false;
if (parameters != null && parameters.size() > 0) {
// Check if the parameter has a value
if (parameters.containsKey(expr.getId())) {
// Named parameter
paramValue = parameters.get(expr.getId());
paramValueSet = true;
} else if (parameterValueByName != null && parameterValueByName.containsKey(expr.getId())) {
// Positional parameter, but already encountered
paramValue = parameterValueByName.get(expr.getId());
paramValueSet = true;
} else {
// Positional parameter, not yet encountered
int position = positionalParamNumber;
if (positionalParamNumber < 0) {
position = 0;
}
if (parameters.containsKey(Integer.valueOf(position))) {
paramValue = parameters.get(Integer.valueOf(position));
paramValueSet = true;
positionalParamNumber = position + 1;
if (parameterValueByName == null) {
parameterValueByName = new HashMap<>();
}
parameterValueByName.put(expr.getId(), paramValue);
}
}
}
// Find the type to use for the parameter
JavaTypeMapping m = paramMappingForName.get(expr.getId());
if (m == null) {
// Try to determine from provided parameter value or from symbol table (declared type)
if (paramValue != null) {
if (!storeMgr.getMetaDataManager().isClassPersistable(paramValue.getClass().getName()) && !paramValue.getClass().isArray() && !paramValue.getClass().isInterface() && !Collection.class.isAssignableFrom(paramValue.getClass()) && !Map.class.isAssignableFrom(paramValue.getClass()) && !storeMgr.getNucleusContext().getTypeManager().isSupportedSecondClassType(paramValue.getClass().getName())) {
// Test for this being the "id" of a persistable object
// Persistable/array/interface/collection/map/simple cannot be an object "id"
String className = storeMgr.getClassNameForObjectID(paramValue, clr, ec);
if (className != null) {
// Identity for persistable class
AbstractClassMetaData cmd = storeMgr.getMetaDataManager().getMetaDataForClass(className, clr);
if (cmd.getIdentityType() == IdentityType.APPLICATION) {
Class cls = clr.classForName(className);
m = exprFactory.getMappingForType(cls, false);
m = new PersistableIdMapping((PersistableMapping) m);
}
}
}
if (m == null) {
// Use the type of the input parameter value
try {
m = exprFactory.getMappingForType(paramValue.getClass(), false);
if (m instanceof TypeConverterMapping && expr.getSymbol().getValueType() != null && expr.getSymbol().getValueType() != m.getJavaType()) {
// This is because if we have a parameter of type "ZoneInfo" it needs to use TimeZone since we have the TypeConverter for that
try {
m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
} catch (NucleusUserException nue) {
}
}
} catch (NucleusUserException nue) {
// Maybe it needs a TypeConverter so try with the (declared) symbol type of this parameter
m = exprFactory.getMappingForType(expr.getSymbol().getValueType(), false);
}
}
if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
if (!QueryUtils.queryParameterTypesAreCompatible(expr.getSymbol().getValueType(), paramValue.getClass())) {
throw new QueryCompilerSyntaxException(Localiser.msg("021118", expr.getId(), expr.getSymbol().getValueType().getName(), paramValue.getClass().getName()));
}
if (expr.getSymbol().getValueType() != paramValue.getClass()) {
// Mark as not precompilable since the supplied type implies a subclass of the declared type
setNotPrecompilable();
}
}
} else if (expr.getSymbol() != null && expr.getSymbol().getValueType() != null) {
Class valueType = expr.getSymbol().getValueType();
if (!paramValueSet) {
if (valueType.isInterface()) {
// Special case where we have an interface parameter (not set), and don't know the type, so we pick the first implementation just to get something that works
// This is recompiled when the parameter is provided so is just for use in "compile()"
String[] implNames = storeMgr.getMetaDataManager().getClassesImplementingInterface(valueType.getName(), clr);
if (implNames != null && implNames.length > 0) {
valueType = clr.classForName(implNames[0]);
setNotPrecompilable();
}
}
}
// Use the declared type of the parameter (explicit params)
m = exprFactory.getMappingForType(valueType, false);
}
}
if (asLiteral && m != null && !m.representableAsStringLiteralInStatement()) {
// Must keep this as a parameter since its String form is no good in statements
asLiteral = false;
}
if (asLiteral) {
// Parameter being represented as a literal (for whatever reason), so no longer precompilable
if (isPrecompilable()) {
NucleusLogger.QUERY.debug("Parameter " + expr + " is being resolved as a literal, so the query is no longer precompilable");
}
setNotPrecompilable();
} else if (paramValue == null && expr.getSymbol() != null) {
if (isPrecompilable()) {
NucleusLogger.QUERY.debug("Parameter " + expr + " is set to null so this has to be resolved as a NullLiteral, and the query is no longer precompilable");
}
setNotPrecompilable();
}
// Create the SQLExpression for this parameter, either as value-literal or as parameter-literal
SQLExpression sqlExpr = null;
if (paramValueSet && paramValue == null && options.contains(OPTION_NULL_PARAM_USE_IS_NULL)) {
// Value is set to null, but we enforce a NullLiteral for the case of null comparisons e.g we don't want "field = ?", but instead "field IS NULL"
sqlExpr = exprFactory.newLiteral(stmt, null, null);
} else if (asLiteral) {
// Create a value-literal as requested
sqlExpr = exprFactory.newLiteral(stmt, m, paramValue);
} else {
// Create a parameter-literal with it tied to the parameter name for later replacement in the statement
sqlExpr = exprFactory.newLiteralParameter(stmt, m, paramValue, expr.getId());
if (sqlExpr instanceof ParameterLiteral) {
((ParameterLiteral) sqlExpr).setName(expr.getId());
}
if (expressionForParameter == null) {
expressionForParameter = new HashMap<>();
}
expressionForParameter.put(expr.getId(), sqlExpr);
paramMappingForName.put(expr.getId(), m);
}
stack.push(sqlExpr);
return sqlExpr;
}
use of org.datanucleus.store.query.compiler.QueryCompilerSyntaxException in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compile.
/**
* Method to update the supplied SQLStatement with the components of the specified query.
* During the compilation process this updates the SQLStatement "compileComponent" to the
* component of the query being compiled.
*/
public void compile() {
if (NucleusLogger.QUERY.isDebugEnabled() && parentMapper == null) {
// Give debug output of compilation
StringBuilder str = new StringBuilder("JoinType : navigation(default=");
str.append(defaultJoinType != null ? defaultJoinType : "(using nullability)");
str.append(", filter=");
str.append(defaultJoinTypeFilter != null ? defaultJoinTypeFilter : "(using nullability)");
str.append(")");
if (extensionsByName != null) {
Iterator<Map.Entry<String, Object>> extensionsIter = extensionsByName.entrySet().iterator();
while (extensionsIter.hasNext()) {
Map.Entry<String, Object> entry = extensionsIter.next();
String key = entry.getKey();
if (key.startsWith("datanucleus.query.jdoql.") && key.endsWith(".join")) {
// Alias join definition
String alias = key.substring("datanucleus.query.jdoql.".length(), key.lastIndexOf(".join"));
str.append(", ").append(alias).append("=").append(entry.getValue());
}
}
}
NucleusLogger.QUERY.debug("Compile of " + compilation.getQueryLanguage() + " into SQL - " + str);
}
compileFrom();
compileFilter();
if (stmt instanceof UpdateStatement) {
compileUpdate((UpdateStatement) stmt);
} else if (stmt instanceof SelectStatement) {
SelectStatement selectStmt = (SelectStatement) stmt;
// the datastore doesn't allow select of some field types when used with DISTINCT
if (compilation.getResultDistinct()) {
selectStmt.setDistinct(true);
} else if (!options.contains(OPTION_EXPLICIT_JOINS) && compilation.getExprResult() == null) {
// Joins are made implicitly and no result so set distinct based on whether joining to other table groups
if (selectStmt.getNumberOfTableGroups() > 1) {
// Queries against an extent always consider only distinct candidate instances, regardless of whether distinct is specified (JDO spec)
if (!options.contains(OPTION_NON_DISTINCT_IMPLICIT_JOINS)) {
// If user can guarantee distinct w/ query no reason to take performance hit of distinct clause
selectStmt.setDistinct(true);
}
}
}
compileResult(selectStmt);
compileGrouping(selectStmt);
compileHaving(selectStmt);
compileOrdering(selectStmt);
}
// Check for variables that haven't been bound to the query (declared but not used)
for (String symbol : compilation.getSymbolTable().getSymbolNames()) {
Symbol sym = compilation.getSymbolTable().getSymbol(symbol);
if (sym.getType() == Symbol.VARIABLE) {
if (compilation.getCompilationForSubquery(sym.getQualifiedName()) == null && !hasSQLTableMappingForAlias(sym.getQualifiedName())) {
// Variable not a subquery, nor had its table allocated
throw new QueryCompilerSyntaxException("Query has variable \"" + sym.getQualifiedName() + "\" which is not bound to the query");
}
}
}
}
Aggregations