use of org.datanucleus.store.rdbms.sql.SelectStatement in project datanucleus-rdbms by datanucleus.
the class RDBMSStoreHelper method getClassNameForIdUsingUnion.
/**
* Utility that does a union candidate query for the specified candidate(s) and subclasses
* and returns the class name of the instance that has the specified identity (if any).
* @param storeMgr RDBMS StoreManager
* @param ec execution context
* @param id The id
* @param rootCmds Metadata for the classes at the root
* @return Name of the class with this identity (or null if none found)
*/
public static String getClassNameForIdUsingUnion(RDBMSStoreManager storeMgr, ExecutionContext ec, Object id, List<AbstractClassMetaData> rootCmds) {
// Check for input error
if (rootCmds == null || rootCmds.isEmpty() || id == null) {
return null;
}
SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
// Form a query UNIONing all possible root candidates (and their subclasses)
Iterator<AbstractClassMetaData> rootCmdIter = rootCmds.iterator();
// Metadata for sample class in the tree so we can check if needs locking
AbstractClassMetaData sampleCmd = null;
SelectStatement sqlStmtMain = null;
while (rootCmdIter.hasNext()) {
AbstractClassMetaData rootCmd = rootCmdIter.next();
DatastoreClass rootTbl = storeMgr.getDatastoreClass(rootCmd.getFullClassName(), clr);
InheritanceMetaData rootInhmd = rootCmd.getBaseAbstractClassMetaData().getInheritanceMetaData();
if (rootInhmd.getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
// COMPLETE TABLE so use one branch of UNION for each possible class
if (rootTbl != null) {
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, clr.classForName(rootCmd.getFullClassName()), false, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
if (sqlStmtMain == null) {
sampleCmd = rootCmd;
sqlStmtMain = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
} else {
SelectStatement sqlStmt = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);
sqlStmtMain.union(sqlStmt);
}
}
Collection<String> rootSubclassNames = storeMgr.getSubClassesForClass(rootCmd.getFullClassName(), true, clr);
for (String rootSubclassName : rootSubclassNames) {
AbstractClassMetaData rootSubclassCmd = storeMgr.getMetaDataManager().getMetaDataForClass(rootSubclassName, clr);
DatastoreClass rootSubclassTbl = storeMgr.getDatastoreClass(rootSubclassCmd.getFullClassName(), clr);
if (rootSubclassTbl != null) {
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, clr.classForName(rootSubclassCmd.getFullClassName()), false, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
if (sqlStmtMain == null) {
sampleCmd = rootSubclassCmd;
sqlStmtMain = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
} else {
SelectStatement sqlStmt = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);
sqlStmtMain.union(sqlStmt);
}
}
}
continue;
}
if (rootTbl == null) {
// Class must be using "subclass-table" (no table of its own) so find where it is
AbstractClassMetaData[] subcmds = storeMgr.getClassesManagingTableForClass(rootCmd, clr);
if (subcmds == null || subcmds.length == 0) {
// No table for this class so ignore
} else {
for (int i = 0; i < subcmds.length; i++) {
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, clr.classForName(subcmds[i].getFullClassName()), true, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
if (sqlStmtMain == null) {
sampleCmd = subcmds[i];
sqlStmtMain = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
} else {
SelectStatement sqlStmt = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);
sqlStmtMain.union(sqlStmt);
}
}
}
} else {
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, clr.classForName(rootCmd.getFullClassName()), true, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
if (sqlStmtMain == null) {
sampleCmd = rootCmd;
sqlStmtMain = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmtMain.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmtMain, sqlStmtMain.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmtMain, idParamMapping, id, "ID");
sqlStmtMain.whereAnd(fieldExpr.eq(fieldVal), true);
} else {
SelectStatement sqlStmt = stmtGen.getStatement(ec);
// WHERE (object id) = ?
JavaTypeMapping idMapping = sqlStmt.getPrimaryTable().getTable().getIdMapping();
JavaTypeMapping idParamMapping = new PersistableIdMapping((PersistableMapping) idMapping);
SQLExpression fieldExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), idMapping);
SQLExpression fieldVal = exprFactory.newLiteralParameter(sqlStmt, idParamMapping, id, "ID");
sqlStmt.whereAnd(fieldExpr.eq(fieldVal), true);
sqlStmtMain.union(sqlStmt);
}
}
}
// Perform the query
if (sqlStmtMain != null) {
try {
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
if (sampleCmd != null && ec.getSerializeReadForClass(sampleCmd.getFullClassName())) {
sqlStmtMain.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
}
try {
PreparedStatement ps = SQLStatementHelper.getPreparedStatementForSQLStatement(sqlStmtMain, ec, mconn, null, null);
String statement = sqlStmtMain.getSQLText().toSQL();
try {
ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, statement, ps);
try {
while (rs.next()) {
try {
return rs.getString(UnionStatementGenerator.DN_TYPE_COLUMN).trim();
} catch (SQLException sqle) {
}
}
} finally {
rs.close();
}
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException sqe) {
NucleusLogger.DATASTORE.error("Exception with UNION statement", sqe);
throw new NucleusDataStoreException(sqe.toString());
}
}
return null;
}
use of org.datanucleus.store.rdbms.sql.SelectStatement 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");
}
}
}
}
use of org.datanucleus.store.rdbms.sql.SelectStatement in project datanucleus-rdbms by datanucleus.
the class QueryToSQLMapper method compileResult.
/**
* Method to compile the result clause of the query into the SQLStatement.
* Note that this also compiles queries of the candidate (no specified result), selecting the candidate field(s).
* @param stmt SELECT statement
*/
protected void compileResult(SelectStatement stmt) {
compileComponent = CompilationComponent.RESULT;
boolean unionsPresent = stmt.getNumberOfUnions() > 0;
// TODO Cater for more expression types where we have UNIONs and select each UNION separately
if (compilation.getExprResult() != null) {
// Select any result expressions
Expression[] resultExprs = compilation.getExprResult();
for (int i = 0; i < resultExprs.length; i++) {
String alias = resultExprs[i].getAlias();
if (alias != null && resultAliases == null) {
resultAliases = new HashSet<>();
}
if (resultExprs[i] instanceof InvokeExpression || resultExprs[i] instanceof ParameterExpression || resultExprs[i] instanceof Literal) {
// Process expressions that need no special treatment
if (resultExprs[i] instanceof InvokeExpression) {
processInvokeExpression((InvokeExpression) resultExprs[i]);
} else if (resultExprs[i] instanceof ParameterExpression) {
// Second argument : parameters are literals in result
processParameterExpression((ParameterExpression) resultExprs[i], true);
} else {
processLiteral((Literal) resultExprs[i]);
}
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
} else if (resultExprs[i] instanceof PrimaryExpression) {
PrimaryExpression primExpr = (PrimaryExpression) resultExprs[i];
if (primExpr.getId().equals(candidateAlias)) {
// "this", so select fetch plan fields
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
StatementClassMapping map = new StatementClassMapping(candidateCmd.getFullClassName(), null);
SQLStatementHelper.selectFetchPlanOfCandidateInStatement(stmt, map, candidateCmd, fetchPlan, 1);
resultDefinition.addMappingForResultExpression(i, map);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
StatementClassMapping dummyClsMapping = new StatementClassMapping(candidateCmd.getFullClassName(), null);
SQLStatementHelper.selectFetchPlanOfCandidateInStatement(unionStmt, dummyClsMapping, candidateCmd, fetchPlan, 1);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
} else {
processPrimaryExpression(primExpr);
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
if (primExpr.getId().endsWith("#KEY") || primExpr.getId().endsWith("#VALUE")) {
// JPQL KEY(map) or VALUE(map), so select FetchPlan fields where persistable
if (sqlExpr.getJavaTypeMapping() instanceof PersistableMapping) {
// Method returns persistable object, so select the FetchPlan
String selectedType = ((PersistableMapping) sqlExpr.getJavaTypeMapping()).getType();
AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(selectedType, clr);
FetchPlanForClass fpForCmd = fetchPlan.getFetchPlanForClass(selectedCmd);
int[] membersToSelect = fpForCmd.getMemberNumbers();
ClassTable selectedTable = (ClassTable) sqlExpr.getSQLTable().getTable();
StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
if (selectedCmd.getIdentityType() == IdentityType.DATASTORE) {
int[] cols = stmt.select(sqlExpr.getSQLTable(), selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false), alias);
StatementMappingIndex idx = new StatementMappingIndex(selectedTable.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
idx.setColumnPositions(cols);
map.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), idx);
}
// Select the FetchPlan members
for (int memberToSelect : membersToSelect) {
AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
// TODO Arbitrary penultimate argument
SQLStatementHelper.selectMemberOfSourceInStatement(stmt, map, fetchPlan, sqlExpr.getSQLTable(), selMmd, clr, 1, null);
}
resultDefinition.addMappingForResultExpression(i, map);
continue;
} else if (sqlExpr.getJavaTypeMapping() instanceof EmbeddedMapping) {
// Method returns embedded object, so select the FetchPlan
EmbeddedMapping embMapping = (EmbeddedMapping) sqlExpr.getJavaTypeMapping();
AbstractClassMetaData selectedCmd = ec.getMetaDataManager().getMetaDataForClass(embMapping.getType(), clr);
// Select the FetchPlan members
StatementClassMapping map = new StatementClassMapping(selectedCmd.getFullClassName(), null);
int[] membersToSelect = fetchPlan.getFetchPlanForClass(selectedCmd).getMemberNumbers();
for (int memberToSelect : membersToSelect) {
AbstractMemberMetaData selMmd = selectedCmd.getMetaDataForManagedMemberAtAbsolutePosition(memberToSelect);
JavaTypeMapping selMapping = embMapping.getJavaTypeMapping(selMmd.getName());
if (selMapping.includeInFetchStatement()) {
int[] cols = stmt.select(sqlExpr.getSQLTable(), selMapping, alias);
StatementMappingIndex idx = new StatementMappingIndex(selMapping);
idx.setColumnPositions(cols);
map.addMappingForMember(memberToSelect, idx);
}
}
resultDefinition.addMappingForResultExpression(i, map);
continue;
}
}
// TODO If the user selects an alias here that is joined, should maybe respect FetchPlan for that (like above for candidate)
// TODO Cater for use of UNIONs (e.g "complete-table" inheritance) where mapping is different in other UNION
// The difficulty here is that processPrimaryExpression makes use of the sqlTableByPrimary lookup, which is based on the primary statement, so
// it still doesn't find the right table/mapping for the UNIONed statements.
int[] cols = null;
if (sqlExpr instanceof SQLLiteral) {
cols = stmt.select(sqlExpr, alias);
} else {
cols = stmt.select(sqlExpr.getSQLTable(), sqlExpr.getJavaTypeMapping(), alias);
}
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
}
} else if (resultExprs[i] instanceof VariableExpression) {
// Subquery?
processVariableExpression((VariableExpression) resultExprs[i]);
SQLExpression sqlExpr = stack.pop();
validateExpressionForResult(sqlExpr);
if (sqlExpr instanceof UnboundExpression) {
// Variable wasn't bound in the compilation so far, so handle as cross-join
processUnboundExpression((UnboundExpression) sqlExpr);
sqlExpr = stack.pop();
NucleusLogger.QUERY.debug("QueryToSQL.exprResult variable was still unbound, so binding via cross-join");
}
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
} else if (resultExprs[i] instanceof TypeExpression) {
// TYPE(identification_variable | single_valued_path_expr | input_parameter)
TypeExpression typeExpr = (TypeExpression) resultExprs[i];
Expression containedExpr = typeExpr.getContainedExpression();
if (containedExpr instanceof PrimaryExpression) {
processPrimaryExpression((PrimaryExpression) containedExpr);
SQLExpression sqlExpr = stack.pop();
JavaTypeMapping discrimMapping = sqlExpr.getSQLTable().getTable().getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true);
if (discrimMapping == null) {
// TODO If we have a UNIONED primary expression then it would be possible to just select the DN_TYPE from the particular union
throw new NucleusException("Result has call to " + typeExpr + " but contained expression has no discriminator. Not supported");
}
int[] cols = stmt.select(sqlExpr.getSQLTable(), discrimMapping, null, true);
StatementMappingIndex idx = new StatementMappingIndex(discrimMapping);
idx.setColumnPositions(cols);
resultDefinition.addMappingForResultExpression(i, idx);
} else {
throw new NucleusException("Result has call to " + typeExpr + " but contained expression not supported");
}
} else if (resultExprs[i] instanceof CreatorExpression) {
processCreatorExpression((CreatorExpression) resultExprs[i]);
NewObjectExpression sqlExpr = (NewObjectExpression) stack.pop();
StatementNewObjectMapping stmtMap = getStatementMappingForNewObjectExpression(sqlExpr, stmt);
resultDefinition.addMappingForResultExpression(i, stmtMap);
} else if (resultExprs[i] instanceof DyadicExpression || resultExprs[i] instanceof CaseExpression) {
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
resultExprs[i].evaluate(this);
SQLExpression sqlExpr = stack.pop();
int[] cols = stmt.select(sqlExpr, alias);
StatementMappingIndex idx = new StatementMappingIndex(sqlExpr.getJavaTypeMapping());
idx.setColumnPositions(cols);
if (alias != null) {
resultAliases.add(alias);
idx.setColumnAlias(alias);
}
resultDefinition.addMappingForResultExpression(i, idx);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
resultExprs[i].evaluate(this);
sqlExpr = stack.pop();
unionStmt.select(sqlExpr, alias);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
} else {
throw new NucleusException("Dont currently support result clause containing expression of type " + resultExprs[i]);
}
}
if (stmt.getNumberOfSelects() == 0) {
// Nothing selected so likely the user had some "new MyClass()" expression, so select "1"
stmt.select(exprFactory.newLiteral(stmt, storeMgr.getMappingManager().getMapping(Integer.class), 1), null);
}
} else {
// Select of the candidate (no result)
if (candidateCmd.getIdentityType() == IdentityType.NONDURABLE) {
// Nondurable identity cases have no "id" for later fetching so get all fields now
if (NucleusLogger.QUERY.isDebugEnabled()) {
NucleusLogger.QUERY.debug(Localiser.msg("052520", candidateCmd.getFullClassName()));
}
fetchPlan.setGroup("all");
}
if (subclasses) {
// TODO Check for special case of candidate+subclasses stores in same table with discriminator, so select FetchGroup of subclasses also
}
int maxFetchDepth = fetchPlan.getMaxFetchDepth();
if (maxFetchDepth < 0) {
NucleusLogger.QUERY.debug("No limit specified on query fetch so limiting to 3 levels from candidate. " + "Specify the '" + PropertyNames.PROPERTY_MAX_FETCH_DEPTH + "' to override this");
// TODO Arbitrary
maxFetchDepth = 3;
}
// This means that we then cater for a UNION using a different column name than the primary statement
if (unionsPresent) {
// Process the first union separately (in case they have TYPE/instanceof) and then handle remaining UNIONs below
stmt.setAllowUnions(false);
}
selectFetchPlanForCandidate(stmt, resultDefinitionForClass, maxFetchDepth);
if (unionsPresent) {
// Process remaining UNIONs. Assumed that we have the same result mapping as the first UNION otherwise SQL wouldn't work anyway
stmt.setAllowUnions(true);
List<SelectStatement> unionStmts = stmt.getUnions();
SelectStatement originalStmt = stmt;
for (SelectStatement unionStmt : unionStmts) {
this.stmt = unionStmt;
unionStmt.setQueryGenerator(this);
unionStmt.setAllowUnions(false);
// We don't want to overwrite anything in the root StatementClassMapping
StatementClassMapping dummyResClsMapping = new StatementClassMapping();
selectFetchPlanForCandidate(unionStmt, dummyResClsMapping, maxFetchDepth);
unionStmt.setQueryGenerator(null);
unionStmt.setAllowUnions(true);
}
this.stmt = originalStmt;
}
}
compileComponent = null;
}
use of org.datanucleus.store.rdbms.sql.SelectStatement in project datanucleus-rdbms by datanucleus.
the class RDBMSQueryUtils method getStatementForCandidates.
/**
* Method to return a statement selecting the candidate table(s) required to cover all possible types for this candidates inheritance strategy.
* @param storeMgr RDBMS StoreManager
* @param parentStmt Parent statement (if there is one)
* @param cmd Metadata for the class
* @param clsMapping Mapping for the results of the statement
* @param ec ExecutionContext
* @param candidateCls Candidate class
* @param subclasses Whether to create a statement for subclasses of the candidate too
* @param result The result clause
* @param candidateAlias alias for the candidate (if any)
* @param candidateTableGroupName TableGroup name for the candidate (if any)
* @param options Any options for the statement for getting candidates. See SelectStatementGenerator for some options.
* @return The SQLStatement
* @throws NucleusException if there are no tables for concrete classes in this query (hence would return null)
*/
public static SelectStatement getStatementForCandidates(RDBMSStoreManager storeMgr, SQLStatement parentStmt, AbstractClassMetaData cmd, StatementClassMapping clsMapping, ExecutionContext ec, Class candidateCls, boolean subclasses, String result, String candidateAlias, String candidateTableGroupName, Set<String> options) {
SelectStatement stmt = null;
DatastoreIdentifier candidateAliasId = null;
if (candidateAlias != null) {
candidateAliasId = storeMgr.getIdentifierFactory().newTableIdentifier(candidateAlias);
}
ClassLoaderResolver clr = ec.getClassLoaderResolver();
List<DatastoreClass> candidateTables = new ArrayList<>();
if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
if (candidateTable != null) {
candidateTables.add(candidateTable);
}
if (subclasses) {
Collection<String> subclassNames = storeMgr.getSubClassesForClass(cmd.getFullClassName(), subclasses, clr);
if (subclassNames != null) {
Iterator<String> subclassIter = subclassNames.iterator();
while (subclassIter.hasNext()) {
String subclassName = subclassIter.next();
DatastoreClass tbl = storeMgr.getDatastoreClass(subclassName, clr);
if (tbl != null) {
candidateTables.add(tbl);
}
}
}
}
Iterator<DatastoreClass> iter = candidateTables.iterator();
int maxClassNameLength = cmd.getFullClassName().length();
while (iter.hasNext()) {
DatastoreClass cls = iter.next();
String className = cls.getType();
if (className.length() > maxClassNameLength) {
maxClassNameLength = className.length();
}
}
iter = candidateTables.iterator();
while (iter.hasNext()) {
DatastoreClass cls = iter.next();
SelectStatement tblStmt = new SelectStatement(parentStmt, storeMgr, cls, candidateAliasId, candidateTableGroupName);
tblStmt.setClassLoaderResolver(clr);
tblStmt.setCandidateClassName(cls.getType());
// Add SELECT of dummy column accessible as "DN_TYPE" containing the classname
JavaTypeMapping m = storeMgr.getMappingManager().getMapping(String.class);
String nuctypeName = cls.getType();
if (maxClassNameLength > nuctypeName.length()) {
nuctypeName = StringUtils.leftAlignedPaddedString(nuctypeName, maxClassNameLength);
}
StringLiteral lit = new StringLiteral(tblStmt, m, nuctypeName, null);
tblStmt.select(lit, UnionStatementGenerator.DN_TYPE_COLUMN);
if (stmt == null) {
stmt = tblStmt;
} else {
stmt.union(tblStmt);
}
}
if (clsMapping != null) {
clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
}
} else {
// "new-table", "superclass-table", "subclass-table"
List<Class> candidateClasses = new ArrayList<>();
if (ClassUtils.isReferenceType(candidateCls)) {
// Persistent interface, so find all persistent implementations
String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(candidateCls.getName(), clr);
for (int i = 0; i < clsNames.length; i++) {
Class cls = clr.classForName(clsNames[i]);
DatastoreClass table = storeMgr.getDatastoreClass(clsNames[i], clr);
candidateClasses.add(cls);
candidateTables.add(table);
AbstractClassMetaData implCmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(cls, clr);
if (implCmd.getIdentityType() != cmd.getIdentityType()) {
throw new NucleusUserException("You are querying an interface (" + cmd.getFullClassName() + ") " + "yet one of its implementations (" + implCmd.getFullClassName() + ") uses a different identity type!");
} else if (cmd.getIdentityType() == IdentityType.APPLICATION) {
if (cmd.getPKMemberPositions().length != implCmd.getPKMemberPositions().length) {
throw new NucleusUserException("You are querying an interface (" + cmd.getFullClassName() + ") " + "yet one of its implementations (" + implCmd.getFullClassName() + ") has a different number of PK members!");
}
}
}
} else {
DatastoreClass candidateTable = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
if (candidateTable != null) {
// Candidate has own table
candidateClasses.add(candidateCls);
candidateTables.add(candidateTable);
} else {
// Candidate stored in subclass tables
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(cmd, clr);
if (cmds != null && cmds.length > 0) {
for (int i = 0; i < cmds.length; i++) {
DatastoreClass table = storeMgr.getDatastoreClass(cmds[i].getFullClassName(), clr);
Class cls = clr.classForName(cmds[i].getFullClassName());
candidateClasses.add(cls);
candidateTables.add(table);
}
} else {
throw new UnsupportedOperationException("No tables for query of " + cmd.getFullClassName());
}
}
}
for (int i = 0; i < candidateTables.size(); i++) {
DatastoreClass tbl = candidateTables.get(i);
Class cls = candidateClasses.get(i);
SelectStatementGenerator stmtGen = null;
if (tbl.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true) != null || QueryUtils.resultHasOnlyAggregates(result)) {
// Either has a discriminator, or only selecting aggregates so need single select
// TODO Add option to omit discriminator restriction
stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
stmtGen.setOption(SelectStatementGenerator.OPTION_RESTRICT_DISCRIM);
if (options != null) {
for (String option : options) {
stmtGen.setOption(option);
}
}
} else {
// No discriminator, so try to identify using UNIONs (hopefully one per class)
stmtGen = new UnionStatementGenerator(storeMgr, clr, cls, subclasses, candidateAliasId, candidateTableGroupName);
if (options != null) {
for (String option : options) {
stmtGen.setOption(option);
}
}
if (result == null) {
// Returning one row per candidate so include distinguisher column
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
if (clsMapping != null) {
clsMapping.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
}
}
}
stmtGen.setParentStatement(parentStmt);
SelectStatement tblStmt = stmtGen.getStatement(ec);
if (stmt == null) {
stmt = tblStmt;
} else {
stmt.union(tblStmt);
}
}
}
return stmt;
}
use of org.datanucleus.store.rdbms.sql.SelectStatement in project datanucleus-rdbms by datanucleus.
the class FKSetStore method getIteratorStatement.
/**
* Method to return the SQLStatement and mapping for an iterator for this backing store.
* Create a statement of the form
* <pre>
* SELECT ELEM_COLS
* FROM ELEM_TBL
* [WHERE]
* [ELEM_TBL.OWNER_ID = {value}] [AND]
* [ELEM_TBL.DISCRIM = {discrimValue}]
* [ORDER BY {orderClause}]
* </pre>
* @param ec ExecutionContext
* @param fp FetchPlan to use in determining which fields of element to select
* @param addRestrictionOnOwner Whether to restrict to a particular owner (otherwise functions as bulk fetch for many owners).
* @return The SQLStatement and its associated StatementClassMapping
*/
public IteratorStatement getIteratorStatement(ExecutionContext ec, FetchPlan fp, boolean addRestrictionOnOwner) {
SelectStatement sqlStmt = null;
SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
StatementClassMapping iteratorMappingClass = new StatementClassMapping();
if (elementInfo[0].getDatastoreClass().getDiscriminatorMetaData() != null && elementInfo[0].getDatastoreClass().getDiscriminatorMetaData().getStrategy() != DiscriminatorStrategy.NONE) {
// TODO Only caters for one elementInfo, but with subclass-table we can have multiple
String elementType = ownerMemberMetaData.getCollection().getElementType();
if (ClassUtils.isReferenceType(clr.classForName(elementType))) {
String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
Class[] cls = new Class[clsNames.length];
for (int i = 0; i < clsNames.length; i++) {
cls[i] = clr.classForName(clsNames[i]);
}
sqlStmt = new DiscriminatorStatementGenerator(storeMgr, clr, cls, true, null, null).getStatement(ec);
} else {
sqlStmt = new DiscriminatorStatementGenerator(storeMgr, clr, clr.classForName(elementInfo[0].getClassName()), true, null, null).getStatement(ec);
}
iterateUsingDiscriminator = true;
// TODO Cater for having all possible subclasses stored in the same table (so we can select their fields too)
// String[] elemSubclasses = op.getExecutionContext().getMetaDataManager().getSubclassesForClass(emd.getFullClassName(), false);
// NucleusLogger.GENERAL.info(">> FKSetStore.iter iterMapDef=" + iteratorMappingDef + " table=" + sqlStmt.getPrimaryTable() +
// " emd=" + emd.getFullClassName() + " elem.subclasses=" + StringUtils.objectArrayToString(elemSubclasses));
// Select the required fields (of the element class)
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, iteratorMappingClass, fp, sqlStmt.getPrimaryTable(), elementCmd, fp.getMaxFetchDepth());
} else {
boolean selectFetchPlan = true;
Class elementTypeCls = clr.classForName(elementType);
if (elementTypeCls.isInterface() && elementInfo.length > 1) {
// Multiple implementations of an interface, so assume the FetchPlan differs between implementation
selectFetchPlan = false;
}
// TODO This only works if the different elementInfos have the same number of PK fields (otherwise get SQL error in UNION)
for (int i = 0; i < elementInfo.length; i++) {
final Class elementCls = clr.classForName(this.elementInfo[i].getClassName());
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, elementCls, true, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
iteratorMappingClass.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
SelectStatement subStmt = stmtGen.getStatement(ec);
if (selectFetchPlan) {
// Select the FetchPlan fields (of the element class)
if (sqlStmt == null) {
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(subStmt, iteratorMappingClass, fp, subStmt.getPrimaryTable(), elementInfo[i].getAbstractClassMetaData(), fp.getMaxFetchDepth());
} else {
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(subStmt, null, fp, subStmt.getPrimaryTable(), elementInfo[i].getAbstractClassMetaData(), fp.getMaxFetchDepth());
}
} else {
// Select the candidate id of the element class only
if (sqlStmt == null) {
SQLStatementHelper.selectIdentityOfCandidateInStatement(subStmt, iteratorMappingClass, elementInfo[i].getAbstractClassMetaData());
} else {
SQLStatementHelper.selectIdentityOfCandidateInStatement(subStmt, null, elementInfo[i].getAbstractClassMetaData());
}
}
if (sqlStmt == null) {
sqlStmt = subStmt;
} else {
sqlStmt.union(subStmt);
}
}
if (sqlStmt == null) {
throw new NucleusException("Unable to generate iterator statement for field " + getOwnerMemberMetaData().getFullFieldName());
}
}
if (addRestrictionOnOwner) {
// Apply condition to filter by owner
// TODO If ownerMapping is not for containerTable then do JOIN to ownerTable in the FROM clause (or find if already done)
SQLTable ownerSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), ownerMapping);
SQLExpression ownerExpr = exprFactory.newExpression(sqlStmt, ownerSqlTbl, ownerMapping);
SQLExpression ownerVal = exprFactory.newLiteralParameter(sqlStmt, ownerMapping, null, "OWNER");
sqlStmt.whereAnd(ownerExpr.eq(ownerVal), true);
}
if (relationDiscriminatorMapping != null) {
// Apply condition on distinguisher field to filter by distinguisher (when present)
SQLTable distSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), relationDiscriminatorMapping);
SQLExpression distExpr = exprFactory.newExpression(sqlStmt, distSqlTbl, relationDiscriminatorMapping);
SQLExpression distVal = exprFactory.newLiteral(sqlStmt, relationDiscriminatorMapping, relationDiscriminatorValue);
sqlStmt.whereAnd(distExpr.eq(distVal), true);
}
if (orderMapping != null) {
// Order by the ordering column, when present
SQLTable orderSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression[] orderExprs = new SQLExpression[orderMapping.getNumberOfDatastoreMappings()];
boolean[] descendingOrder = new boolean[orderMapping.getNumberOfDatastoreMappings()];
orderExprs[0] = exprFactory.newExpression(sqlStmt, orderSqlTbl, orderMapping);
sqlStmt.setOrdering(orderExprs, descendingOrder);
}
return new IteratorStatement(this, sqlStmt, iteratorMappingClass);
}
Aggregations