use of org.datanucleus.exceptions.NucleusUserException in project datanucleus-rdbms by datanucleus.
the class SQLQuery method performExecute.
/**
* Execute the query and return the result.
* For a SELECT query this will be the QueryResult.
* For an UPDATE/DELETE it will be the row count for the update statement.
* @param parameters the Map containing all of the parameters (positional parameters) (not null)
* @return the result of the query
*/
protected Object performExecute(Map parameters) {
if (parameters.size() != (parameterNames != null ? parameterNames.length : 0)) {
throw new NucleusUserException(Localiser.msg("059019", (parameterNames != null) ? "" + parameterNames.length : 0, "" + parameters.size()));
}
if (type == QueryType.BULK_DELETE || type == QueryType.BULK_UPDATE) {
// Update/Delete statement (INSERT/UPDATE/DELETE/MERGE)
try {
RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, compiledSQL, false);
try {
// Set the values of any parameters
for (int i = 0; i < parameters.size(); i++) {
ps.setObject((i + 1), parameters.get(Integer.valueOf(i + 1)));
}
// Execute the update statement
int[] rcs = sqlControl.executeStatementUpdate(ec, mconn, compiledSQL, ps, true);
// Return a single Long with the number of records updated
return Long.valueOf(rcs[0]);
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException e) {
throw new NucleusDataStoreException(Localiser.msg("059025", compiledSQL), e);
}
} else if (type == QueryType.SELECT) {
// Query statement (SELECT, stored-procedure)
AbstractRDBMSQueryResult qr = null;
try {
RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
PreparedStatement ps = RDBMSQueryUtils.getPreparedStatementForQuery(mconn, compiledSQL, this);
try {
// Set the values of any parameters
for (int i = 0; i < parameters.size(); i++) {
ps.setObject((i + 1), parameters.get(Integer.valueOf(i + 1)));
}
// Apply any user-specified constraints over timeouts and ResultSet
RDBMSQueryUtils.prepareStatementForExecution(ps, this, true);
ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, compiledSQL, ps);
try {
// Generate a ResultObjectFactory
ResultObjectFactory rof = null;
if (resultMetaData != null) {
// Each row of the ResultSet is defined by MetaData
rof = new ResultMetaDataROF(ec, rs, ignoreCache, resultMetaData);
} else if (resultClass != null || candidateClass == null) {
// Each row of the ResultSet is either an instance of resultClass, or Object[]
rof = RDBMSQueryUtils.getResultObjectFactoryForNoCandidateClass(ec, rs, resultClass);
} else {
// Each row of the ResultSet is an instance of the candidate class
rof = getResultObjectFactoryForCandidateClass(rs);
}
// Return the associated type of results depending on whether scrollable or not
qr = RDBMSQueryUtils.getQueryResultForQuery(this, rof, rs, null);
qr.initialise();
final QueryResult qr1 = qr;
final ManagedConnection mconn1 = mconn;
mconn.addListener(new ManagedConnectionResourceListener() {
public void transactionFlushed() {
}
public void transactionPreClose() {
// Disconnect the query from this ManagedConnection (read in unread rows etc)
qr1.disconnect();
}
public void managedConnectionPreClose() {
if (!ec.getTransaction().isActive()) {
// Disconnect the query from this ManagedConnection (read in unread rows etc)
qr1.disconnect();
}
}
public void managedConnectionPostClose() {
}
public void resourcePostClose() {
mconn1.removeListener(this);
}
});
} finally {
if (qr == null) {
rs.close();
}
}
} catch (QueryInterruptedException qie) {
// Execution was cancelled so cancel the PreparedStatement
ps.cancel();
throw qie;
} finally {
if (qr == null) {
sqlControl.closeStatement(mconn, ps);
}
}
} finally {
mconn.release();
}
} catch (SQLException e) {
throw new NucleusDataStoreException(Localiser.msg("059025", compiledSQL), e);
}
return qr;
} else {
// 'Other' statement (manually invoked stored-procedure?, CREATE?, DROP?, or similar)
try {
RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
PreparedStatement ps = RDBMSQueryUtils.getPreparedStatementForQuery(mconn, compiledSQL, this);
try {
// Set the values of any parameters
for (int i = 0; i < parameters.size(); i++) {
ps.setObject((i + 1), parameters.get(Integer.valueOf(i + 1)));
}
// Apply any user-specified constraints over timeouts etc
RDBMSQueryUtils.prepareStatementForExecution(ps, this, false);
sqlControl.executeStatement(ec, mconn, compiledSQL, ps);
} catch (QueryInterruptedException qie) {
// Execution was cancelled so cancel the PreparedStatement
ps.cancel();
throw qie;
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException e) {
throw new NucleusDataStoreException(Localiser.msg("059025", compiledSQL), e);
}
return true;
}
}
use of org.datanucleus.exceptions.NucleusUserException in project datanucleus-rdbms by datanucleus.
the class SQLQuery method prepareForExecution.
/**
* Method to process the input parameters preparing the statement and parameters for execution.
* The parameters returned are ready for execution. Compiles the query, and updates the
* "compiledSQL" and "parameterNames".
* Supports positional parameters, numbered parameters (?1, ?2), and named parameters (:p1, :p3).
* If using named parameters then the keys of the Map must align to the names in the SQL.
* If using numbered/positional parameters then the keys of the Map must be Integer and align with the
* parameter numbers/positions.
* @param executeParameters The input parameters map
* @return Map of parameters for execution
*/
protected Map prepareForExecution(Map executeParameters) {
Map params = new HashMap();
if (implicitParameters != null) {
// Add any implicit parameters defined via the API
params.putAll(implicitParameters);
}
if (executeParameters != null) {
// Add any parameters defined at execute()
params.putAll(executeParameters);
}
compileInternal(executeParameters);
// Clear the parameterNames that are set in compile since we assign ours using the parameterMap passed in
List paramNames = new ArrayList();
// Build up list of expected parameters (in the order the query needs them)
// Allow for positional parameters ('?'), numbered parameters ("?1") or named parameters (":myParam")
Collection expectedParams = new ArrayList();
boolean complete = false;
int charPos = 0;
char[] statement = compiledSQL.toCharArray();
StringBuilder paramName = null;
int paramPos = 0;
boolean colonParam = true;
StringBuilder runtimeJdbcText = new StringBuilder();
while (!complete) {
char c = statement[charPos];
boolean endOfParam = false;
if (c == '?') {
// New positional/numbered parameter
colonParam = false;
paramPos++;
paramName = new StringBuilder();
} else if (c == ':') {
// New named parameter
if (charPos > 0) {
char prev = statement[charPos - 1];
if (Character.isLetterOrDigit(prev)) {
// Some valid SQL can include colon, so ignore if the part just before is alphanumeric
} else {
colonParam = true;
paramPos++;
paramName = new StringBuilder();
}
} else {
colonParam = true;
paramPos++;
paramName = new StringBuilder();
}
} else {
if (paramName != null) {
if (Character.isLetterOrDigit(c)) {
// Allow param names to include alphnumeric
paramName.append(c);
} else {
endOfParam = true;
}
}
}
if (paramName != null) {
if (endOfParam) {
// Replace the param by "?" in the runtime SQL
runtimeJdbcText.append('?');
runtimeJdbcText.append(c);
}
} else {
runtimeJdbcText.append(c);
}
charPos++;
complete = (charPos == compiledSQL.length());
if (complete && paramName != null && !endOfParam) {
runtimeJdbcText.append('?');
}
if (paramName != null && (complete || endOfParam)) {
// Process the parameter
if (paramName.length() > 0) {
// Named/Numbered parameter
if (colonParam) {
expectedParams.add(paramName.toString());
} else {
try {
Integer num = Integer.valueOf(paramName.toString());
expectedParams.add(num);
} catch (NumberFormatException nfe) {
throw new NucleusUserException("SQL query " + inputSQL + " contains an invalid parameter specification " + paramName.toString());
}
}
} else {
if (!colonParam) {
// Positional parameter
expectedParams.add(Integer.valueOf(paramPos));
} else {
// Just a colon so ignore it
}
}
paramName = null;
}
}
// Update the SQL that JDBC will receive to just have ? for a parameter
compiledSQL = runtimeJdbcText.toString();
if (expectedParams.size() > 0 && params.isEmpty()) {
// We expect some parameters yet the user gives us none!
throw new NucleusUserException(Localiser.msg("059028", inputSQL, "" + expectedParams.size()));
}
// Build a Map of params with keys 1, 2, 3, etc representing the position in the runtime JDBC SQL
Map executeMap = new HashMap();
// Cycle through the expected params
paramPos = 1;
for (Object expectedParam : expectedParams) {
if (!params.containsKey(expectedParam)) {
// Expected parameter is not provided
throw new NucleusUserException(Localiser.msg("059031", "" + expectedParam, inputSQL));
}
executeMap.put(Integer.valueOf(paramPos), params.get(expectedParam));
paramNames.add("" + paramPos);
paramPos++;
}
parameterNames = (String[]) paramNames.toArray(new String[paramNames.size()]);
return executeMap;
}
use of org.datanucleus.exceptions.NucleusUserException in project datanucleus-rdbms by datanucleus.
the class SQLQuery method getResultObjectFactoryForCandidateClass.
/**
* Method to generate a ResultObjectFactory for converting rows of the provided ResultSet into instances of the candidate class.
* Populates "stmtMappings".
* @param rs The ResultSet
* @return The ResultObjectFactory
* @throws SQLException Thrown if an error occurs processing the ResultSet
*/
protected ResultObjectFactory getResultObjectFactoryForCandidateClass(ResultSet rs) throws SQLException {
ClassLoaderResolver clr = ec.getClassLoaderResolver();
RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
// Create an index listing for ALL (fetchable) fields in the result class.
final AbstractClassMetaData candidateCmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
int fieldCount = candidateCmd.getNoOfManagedMembers() + candidateCmd.getNoOfInheritedManagedMembers();
// Map of field numbers keyed by the column name
Map columnFieldNumberMap = new HashMap();
stmtMappings = new StatementMappingIndex[fieldCount];
DatastoreClass tbl = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
for (int fieldNumber = 0; fieldNumber < fieldCount; ++fieldNumber) {
AbstractMemberMetaData mmd = candidateCmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
String fieldName = mmd.getName();
Class fieldType = mmd.getType();
JavaTypeMapping m = null;
if (mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) {
if (tbl != null) {
// Get the field mapping from the candidate table
m = tbl.getMemberMapping(mmd);
} else {
// Fall back to generating a mapping for this type - does this ever happen?
m = storeMgr.getMappingManager().getMappingWithDatastoreMapping(fieldType, false, false, clr);
}
if (m.includeInFetchStatement()) {
// Set mapping for this field since it can potentially be returned from a fetch
String columnName = null;
if (mmd.getColumnMetaData() != null && mmd.getColumnMetaData().length > 0) {
for (int colNum = 0; colNum < mmd.getColumnMetaData().length; colNum++) {
columnName = mmd.getColumnMetaData()[colNum].getName();
columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
}
} else {
columnName = storeMgr.getIdentifierFactory().newColumnIdentifier(fieldName, ec.getNucleusContext().getTypeManager().isDefaultEmbeddedType(fieldType), FieldRole.ROLE_NONE, false).getName();
columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
}
} else {
// Don't put anything in this position (field has no column in the result set)
}
} else {
// Don't put anything in this position (field has no column in the result set)
}
stmtMappings[fieldNumber] = new StatementMappingIndex(m);
}
if (columnFieldNumberMap.size() == 0) {
// None of the fields in the class have columns in the datastore table!
throw new NucleusUserException(Localiser.msg("059030", candidateClass.getName())).setFatal();
}
// Generate id column field information for later checking the id is present
DatastoreClass table = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
if (table == null) {
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(candidateCmd, clr);
if (cmds != null && cmds.length == 1) {
table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
} else {
throw new NucleusUserException("SQL query specified with class " + candidateClass.getName() + " but this doesn't have its own table, or is mapped to multiple tables. Unsupported");
}
}
PersistableMapping idMapping = (PersistableMapping) table.getIdMapping();
String[] idColNames = new String[idMapping.getNumberOfDatastoreMappings()];
for (int i = 0; i < idMapping.getNumberOfDatastoreMappings(); i++) {
idColNames[i] = idMapping.getDatastoreMapping(i).getColumn().getIdentifier().toString();
}
// Generate discriminator information for later checking it is present
JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
String discrimColName = discrimMapping != null ? discrimMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
// Generate version information for later checking it is present
JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
String versionColName = versionMapping != null ? versionMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
// Go through the fields of the ResultSet and map to the required fields in the candidate
ResultSetMetaData rsmd = rs.getMetaData();
// TODO We put nothing in this, so what is it for?!
HashSet remainingColumnNames = new HashSet(columnFieldNumberMap.size());
int colCount = rsmd.getColumnCount();
int[] datastoreIndex = null;
int[] versionIndex = null;
int[] discrimIndex = null;
int[] matchedFieldNumbers = new int[colCount];
int fieldNumberPosition = 0;
for (int colNum = 1; colNum <= colCount; ++colNum) {
String colName = rsmd.getColumnName(colNum);
// Find the field for this column
int fieldNumber = -1;
Integer fieldNum = (Integer) columnFieldNumberMap.get(colName);
if (fieldNum == null) {
// Try column name in lowercase
fieldNum = (Integer) columnFieldNumberMap.get(colName.toLowerCase());
if (fieldNum == null) {
// Try column name in UPPERCASE
fieldNum = (Integer) columnFieldNumberMap.get(colName.toUpperCase());
}
}
if (fieldNum != null) {
fieldNumber = fieldNum.intValue();
}
if (fieldNumber >= 0) {
int[] exprIndices = null;
if (stmtMappings[fieldNumber].getColumnPositions() != null) {
exprIndices = new int[stmtMappings[fieldNumber].getColumnPositions().length + 1];
for (int i = 0; i < stmtMappings[fieldNumber].getColumnPositions().length; i++) {
exprIndices[i] = stmtMappings[fieldNumber].getColumnPositions()[i];
}
exprIndices[exprIndices.length - 1] = colNum;
} else {
exprIndices = new int[] { colNum };
}
stmtMappings[fieldNumber].setColumnPositions(exprIndices);
remainingColumnNames.remove(colName);
matchedFieldNumbers[fieldNumberPosition++] = fieldNumber;
}
if (discrimColName != null && colName.equals(discrimColName)) {
// Identify the location of the discriminator column
discrimIndex = new int[1];
discrimIndex[0] = colNum;
}
if (versionColName != null && colName.equals(versionColName)) {
// Identify the location of the version column
versionIndex = new int[1];
versionIndex[0] = colNum;
}
if (candidateCmd.getIdentityType() == IdentityType.DATASTORE) {
// Check for existence of id column, allowing for any RDBMS using quoted identifiers
if (columnNamesAreTheSame(dba, idColNames[0], colName)) {
datastoreIndex = new int[1];
datastoreIndex[0] = colNum;
}
}
}
// Set the field numbers found to match what we really have
int[] fieldNumbers = new int[fieldNumberPosition];
for (int i = 0; i < fieldNumberPosition; i++) {
fieldNumbers[i] = matchedFieldNumbers[i];
}
StatementClassMapping mappingDefinition = new StatementClassMapping();
for (int i = 0; i < fieldNumbers.length; i++) {
mappingDefinition.addMappingForMember(fieldNumbers[i], stmtMappings[fieldNumbers[i]]);
}
if (datastoreIndex != null) {
StatementMappingIndex datastoreMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
datastoreMappingIdx.setColumnPositions(datastoreIndex);
mappingDefinition.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), datastoreMappingIdx);
}
if (discrimIndex != null) {
StatementMappingIndex discrimMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true));
discrimMappingIdx.setColumnPositions(discrimIndex);
mappingDefinition.addMappingForMember(SurrogateColumnType.DISCRIMINATOR.getFieldNumber(), discrimMappingIdx);
}
if (versionIndex != null) {
StatementMappingIndex versionMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.VERSION, true));
versionMappingIdx.setColumnPositions(versionIndex);
mappingDefinition.addMappingForMember(SurrogateColumnType.VERSION.getFieldNumber(), versionMappingIdx);
}
return new PersistentClassROF(ec, rs, ignoreCache, mappingDefinition, candidateCmd, getCandidateClass());
}
use of org.datanucleus.exceptions.NucleusUserException in project datanucleus-rdbms by datanucleus.
the class SQLQuery method compileInternal.
/**
* Verify the elements of the query and provide a hint to the query to prepare and optimize an execution plan.
*/
public void compileInternal(Map parameterValues) {
if (isCompiled) {
return;
}
// Default to using the users SQL direct with no substitution of params etc
compiledSQL = inputSQL;
if (candidateClass != null && getType() == QueryType.SELECT) {
// Perform any sanity checking of input for SELECT queries
RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
AbstractClassMetaData cmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
if (cmd == null) {
throw new ClassNotPersistableException(candidateClass.getName());
}
if (cmd.getPersistableSuperclass() != null) {
// throw new PersistentSuperclassNotAllowedException(candidateClass.getName());
}
if (getResultClass() == null) {
// Check the presence of the required columns (id, version, discriminator) in the candidate class
// Skip "SELECT "
String selections = stripComments(compiledSQL.trim()).substring(7);
int fromStart = selections.indexOf("FROM");
if (fromStart == -1) {
fromStart = selections.indexOf("from");
}
selections = selections.substring(0, fromStart).trim();
String[] selectedColumns = StringUtils.split(selections, ",");
if (selectedColumns == null || selectedColumns.length == 0) {
throw new NucleusUserException(Localiser.msg("059003", compiledSQL));
}
if (selectedColumns.length == 1 && selectedColumns[0].trim().equals("*")) {
// SQL Query using * so just end the checking since all possible columns will be selected
} else {
// Generate id column field information for later checking the id is present
DatastoreClass table = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
PersistableMapping idMapping = (PersistableMapping) table.getIdMapping();
String[] idColNames = new String[idMapping.getNumberOfDatastoreMappings()];
boolean[] idColMissing = new boolean[idMapping.getNumberOfDatastoreMappings()];
for (int i = 0; i < idMapping.getNumberOfDatastoreMappings(); i++) {
idColNames[i] = idMapping.getDatastoreMapping(i).getColumn().getIdentifier().toString();
idColMissing[i] = true;
}
// Generate discriminator/version information for later checking they are present
JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
String discriminatorColName = (discrimMapping != null) ? discrimMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
String versionColName = (versionMapping != null) ? versionMapping.getDatastoreMapping(0).getColumn().getIdentifier().toString() : null;
boolean discrimMissing = (discriminatorColName != null);
boolean versionMissing = (versionColName != null);
// Go through the selected fields and check the existence of id, version, discriminator cols
DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
final AbstractClassMetaData candidateCmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
for (int i = 0; i < selectedColumns.length; i++) {
String colName = selectedColumns[i].trim();
if (colName.indexOf(" AS ") > 0) {
// Allow for user specification of "XX.YY AS ZZ"
colName = colName.substring(colName.indexOf(" AS ") + 4).trim();
} else if (colName.indexOf(" as ") > 0) {
// Allow for user specification of "XX.YY as ZZ"
colName = colName.substring(colName.indexOf(" as ") + 4).trim();
}
if (candidateCmd.getIdentityType() == IdentityType.DATASTORE) {
// Check for existence of id column, allowing for any RDBMS using quoted identifiers
if (SQLQuery.columnNamesAreTheSame(dba, idColNames[0], colName)) {
idColMissing[0] = false;
}
} else if (candidateCmd.getIdentityType() == IdentityType.APPLICATION) {
for (int j = 0; j < idColNames.length; j++) {
// Check for existence of id column, allowing for any RDBMS using quoted identifiers
if (SQLQuery.columnNamesAreTheSame(dba, idColNames[j], colName)) {
idColMissing[j] = false;
}
}
}
if (discrimMissing && SQLQuery.columnNamesAreTheSame(dba, discriminatorColName, colName)) {
discrimMissing = false;
} else if (versionMissing && SQLQuery.columnNamesAreTheSame(dba, versionColName, colName)) {
versionMissing = false;
}
}
if (discrimMissing) {
throw new NucleusUserException(Localiser.msg("059014", compiledSQL, candidateClass.getName(), discriminatorColName));
}
if (versionMissing) {
throw new NucleusUserException(Localiser.msg("059015", compiledSQL, candidateClass.getName(), versionColName));
}
for (int i = 0; i < idColMissing.length; i++) {
if (idColMissing[i]) {
throw new NucleusUserException(Localiser.msg("059013", compiledSQL, candidateClass.getName(), idColNames[i]));
}
}
}
}
}
if (NucleusLogger.QUERY.isDebugEnabled()) {
NucleusLogger.QUERY.debug(Localiser.msg("059012", compiledSQL));
}
isCompiled = true;
}
use of org.datanucleus.exceptions.NucleusUserException in project datanucleus-rdbms by datanucleus.
the class BulkFetchExistsHandler method getStatementToBulkFetchField.
/**
* Convenience method to generate a bulk-fetch statement for the specified multi-valued field of the owning query.
* @param candidateCmd Metadata for the candidate
* @param parameters Parameters for the query
* @param mmd Metadata for the multi-valued field
* @param datastoreCompilation The datastore compilation of the query
* @param mapperOptions Any options for the query to SQL mapper
* @return The bulk-fetch statement for retrieving this multi-valued field.
*/
public IteratorStatement getStatementToBulkFetchField(AbstractClassMetaData candidateCmd, AbstractMemberMetaData mmd, Query query, Map parameters, RDBMSQueryCompilation datastoreCompilation, Set<String> mapperOptions) {
IteratorStatement iterStmt = null;
ExecutionContext ec = query.getExecutionContext();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
RDBMSStoreManager storeMgr = (RDBMSStoreManager) query.getStoreManager();
Store backingStore = storeMgr.getBackingStoreForField(clr, mmd, null);
if (backingStore instanceof JoinSetStore || backingStore instanceof JoinListStore || backingStore instanceof JoinArrayStore) {
// Set/List/array using join-table : Generate an iterator query of the form
if (backingStore instanceof JoinSetStore) {
iterStmt = ((JoinSetStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false);
} else if (backingStore instanceof JoinListStore) {
iterStmt = ((JoinListStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false, -1, -1);
} else if (backingStore instanceof JoinArrayStore) {
iterStmt = ((JoinArrayStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false);
} else {
throw new NucleusUserException("We do not support BulkFetch using EXISTS for backingStore = " + backingStore);
}
// SELECT ELEM_TBL.COL1, ELEM_TBL.COL2, ... FROM JOIN_TBL INNER_JOIN ELEM_TBL WHERE JOIN_TBL.ELEMENT_ID = ELEM_TBL.ID
// AND EXISTS (SELECT OWNER_TBL.ID FROM OWNER_TBL WHERE (queryWhereClause) AND JOIN_TBL.OWNER_ID = OWNER_TBL.ID)
SelectStatement sqlStmt = iterStmt.getSelectStatement();
JoinTable joinTbl = (JoinTable) sqlStmt.getPrimaryTable().getTable();
JavaTypeMapping joinOwnerMapping = joinTbl.getOwnerMapping();
// Generate the EXISTS subquery (based on the JDOQL/JPQL query)
SelectStatement existsStmt = RDBMSQueryUtils.getStatementForCandidates(storeMgr, sqlStmt, candidateCmd, datastoreCompilation.getResultDefinitionForClass(), ec, query.getCandidateClass(), query.isSubclasses(), query.getResult(), null, null, null);
Set<String> options = new HashSet<>();
if (mapperOptions != null) {
options.addAll(mapperOptions);
}
options.add(QueryToSQLMapper.OPTION_SELECT_CANDIDATE_ID_ONLY);
QueryToSQLMapper sqlMapper = new QueryToSQLMapper(existsStmt, query.getCompilation(), parameters, null, null, candidateCmd, query.isSubclasses(), query.getFetchPlan(), ec, query.getParsedImports(), options, query.getExtensions());
sqlMapper.compile();
// Add EXISTS clause on iterator statement so we can restrict to just the owners in this query
// ORDER BY in EXISTS is forbidden by some RDBMS
existsStmt.setOrdering(null, null);
BooleanExpression existsExpr = new BooleanSubqueryExpression(sqlStmt, "EXISTS", existsStmt);
sqlStmt.whereAnd(existsExpr, true);
// Join to outer statement so we restrict to collection elements for the query candidates
SQLExpression joinTblOwnerExpr = sqlStmt.getRDBMSManager().getSQLExpressionFactory().newExpression(sqlStmt, sqlStmt.getPrimaryTable(), joinOwnerMapping);
SQLExpression existsOwnerExpr = sqlStmt.getRDBMSManager().getSQLExpressionFactory().newExpression(existsStmt, existsStmt.getPrimaryTable(), existsStmt.getPrimaryTable().getTable().getIdMapping());
existsStmt.whereAnd(joinTblOwnerExpr.eq(existsOwnerExpr), true);
// Select the owner candidate so we can separate the collection elements out to their owner
int[] ownerColIndexes = sqlStmt.select(joinTblOwnerExpr, null);
StatementMappingIndex ownerMapIdx = new StatementMappingIndex(existsStmt.getPrimaryTable().getTable().getIdMapping());
ownerMapIdx.setColumnPositions(ownerColIndexes);
iterStmt.setOwnerMapIndex(ownerMapIdx);
} else if (backingStore instanceof FKSetStore || backingStore instanceof FKListStore || backingStore instanceof FKArrayStore) {
if (backingStore instanceof FKSetStore) {
iterStmt = ((FKSetStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false);
} else if (backingStore instanceof FKListStore) {
iterStmt = ((FKListStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false, -1, -1);
} else if (backingStore instanceof FKArrayStore) {
iterStmt = ((FKArrayStore) backingStore).getIteratorStatement(ec, ec.getFetchPlan(), false);
} else {
throw new NucleusUserException("We do not support BulkFetch using EXISTS for backingStore = " + backingStore);
}
// Set/List/array using foreign-key : Generate an iterator query of the form
// SELECT ELEM_TBL.COL1, ELEM_TBL.COL2, ... FROM ELEM_TBL
// WHERE EXISTS (SELECT OWNER_TBL.ID FROM OWNER_TBL WHERE (queryWhereClause) AND ELEM_TBL.OWNER_ID = OWNER_TBL.ID)
SelectStatement sqlStmt = iterStmt.getSelectStatement();
// Generate the EXISTS subquery (based on the JDOQL/JPQL query)
SelectStatement existsStmt = RDBMSQueryUtils.getStatementForCandidates(storeMgr, sqlStmt, candidateCmd, datastoreCompilation.getResultDefinitionForClass(), ec, query.getCandidateClass(), query.isSubclasses(), query.getResult(), null, null, null);
Set<String> options = new HashSet<>();
if (mapperOptions != null) {
options.addAll(mapperOptions);
}
options.add(QueryToSQLMapper.OPTION_SELECT_CANDIDATE_ID_ONLY);
QueryToSQLMapper sqlMapper = new QueryToSQLMapper(existsStmt, query.getCompilation(), parameters, null, null, candidateCmd, query.isSubclasses(), query.getFetchPlan(), ec, query.getParsedImports(), options, query.getExtensions());
sqlMapper.compile();
// Add EXISTS clause on iterator statement so we can restrict to just the owners in this query
// ORDER BY in EXISTS is forbidden by some RDBMS
existsStmt.setOrdering(null, null);
BooleanExpression existsExpr = new BooleanSubqueryExpression(sqlStmt, "EXISTS", existsStmt);
sqlStmt.whereAnd(existsExpr, true);
// Join to outer statement so we restrict to collection elements for the query candidates
SQLExpression elemTblOwnerExpr = sqlStmt.getRDBMSManager().getSQLExpressionFactory().newExpression(sqlStmt, sqlStmt.getPrimaryTable(), ((BaseContainerStore) backingStore).getOwnerMapping());
SQLExpression existsOwnerExpr = sqlStmt.getRDBMSManager().getSQLExpressionFactory().newExpression(existsStmt, existsStmt.getPrimaryTable(), existsStmt.getPrimaryTable().getTable().getIdMapping());
existsStmt.whereAnd(elemTblOwnerExpr.eq(existsOwnerExpr), true);
// Select the owner candidate so we can separate the collection elements out to their owner
int[] ownerColIndexes = sqlStmt.select(elemTblOwnerExpr, null);
StatementMappingIndex ownerMapIdx = new StatementMappingIndex(existsStmt.getPrimaryTable().getTable().getIdMapping());
ownerMapIdx.setColumnPositions(ownerColIndexes);
iterStmt.setOwnerMapIndex(ownerMapIdx);
}
return iterStmt;
}
Aggregations