use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.
the class DeleteRequest method updateOneToOneBidirectionalOwnerObjectForField.
/**
* Method to update any 1-1 bidir non-owner fields where the foreign-key is stored in the other object.
* @param sm StateManager of this object
* @param mmd MetaData for field that has related (owner) objects
*/
private void updateOneToOneBidirectionalOwnerObjectForField(DNStateManager sm, AbstractMemberMetaData mmd) {
if (softDeleteStmt != null) {
// If we are soft deleting the owner then no need to null the linkage BUT we need to check the soft delete status on retrieval of the owner from related
return;
}
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052217", IdentityUtils.getPersistableIdentityForId(sm.getInternalObjectId()), mmd.getFullFieldName()));
}
RDBMSStoreManager storeMgr = table.getStoreManager();
ExecutionContext ec = sm.getExecutionContext();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
// Check if we should null here, or leave to the datastore FK handler
boolean checkFK = true;
if (ec.getStringProperty(PropertyNames.PROPERTY_DELETION_POLICY).equals("JDO2")) {
// JDO2 doesn't currently (2.0 spec) take note of foreign-key
checkFK = false;
}
if (checkFK) {
for (int i = 0; i < relatedMmds.length; i++) {
ForeignKeyMetaData relFkmd = relatedMmds[i].getForeignKeyMetaData();
if (relFkmd != null && relFkmd.getDeleteAction() != null) {
// Field has a FK with a delete-action so leave to the datastore to process the delete
return;
}
}
}
// TODO Cater for more than 1 related field
String fullClassName = ((AbstractClassMetaData) relatedMmds[0].getParent()).getFullClassName();
// TODO I'm not sure that we need to loop all implementations. will we have the fk set to all tables, if many?
String[] classes;
if (((AbstractClassMetaData) relatedMmds[0].getParent()) instanceof InterfaceMetaData) {
classes = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(fullClassName, clr);
} else {
classes = new String[] { fullClassName };
}
Set<DatastoreClass> datastoreClasses = new HashSet();
for (int i = 0; i < classes.length; i++) {
// just remove duplicates
datastoreClasses.add(storeMgr.getDatastoreClass(classes[i], clr));
}
Iterator<DatastoreClass> it = datastoreClasses.iterator();
while (it.hasNext()) {
DatastoreClass refTable = it.next();
JavaTypeMapping refMapping = refTable.getMemberMapping(mmd.getMappedBy());
if (// Only clear the references that can be cleared
refMapping.isNullable()) {
// Create a statement to clear the link from the previous related object
StringBuilder clearLinkStmt = new StringBuilder("UPDATE " + refTable.toString() + " SET ");
for (int j = 0; j < refMapping.getNumberOfColumnMappings(); j++) {
if (j > 0) {
clearLinkStmt.append(",");
}
clearLinkStmt.append(refMapping.getColumnMapping(j).getColumn().getIdentifier());
clearLinkStmt.append("=NULL");
}
clearLinkStmt.append(" WHERE ");
for (int j = 0; j < refMapping.getNumberOfColumnMappings(); j++) {
if (j > 0) {
clearLinkStmt.append(" AND ");
}
clearLinkStmt.append(refMapping.getColumnMapping(j).getColumn().getIdentifier());
clearLinkStmt.append("=?");
}
try {
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
// Null out the relationship to the object being deleted.
PreparedStatement ps = null;
try {
ps = sqlControl.getStatementForUpdate(mconn, clearLinkStmt.toString(), false);
refMapping.setObject(ec, ps, MappingHelper.getMappingIndices(1, refMapping), sm.getObject());
sqlControl.executeStatementUpdate(ec, mconn, clearLinkStmt.toString(), ps, true);
} finally {
if (ps != null) {
sqlControl.closeStatement(mconn, ps);
}
}
} finally {
mconn.release();
}
} catch (Exception e) {
throw new NucleusDataStoreException("Update request failed", e);
}
}
}
}
use of org.datanucleus.ClassLoaderResolver 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) {
if (getBooleanExtensionProperty(EXTENSION_SQL_SYNTAX_CHECKS, true)) {
// Check syntax for 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.getNumberOfColumnMappings()];
boolean[] idColMissing = new boolean[idMapping.getNumberOfColumnMappings()];
for (int i = 0; i < idMapping.getNumberOfColumnMappings(); i++) {
idColNames[i] = idMapping.getColumnMapping(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.getColumnMapping(0).getColumn().getIdentifier().toString() : null;
JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
String versionColName = (versionMapping != null) ? versionMapping.getColumnMapping(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();
} else if (colName.indexOf(".") > 0) {
// Note that this assumes the alias is the candidate table
colName = colName.substring(colName.indexOf(".") + 1);
}
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.ClassLoaderResolver 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().getMappingWithColumnMapping(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.getNumberOfColumnMappings()];
for (int i = 0; i < idMapping.getNumberOfColumnMappings(); i++) {
idColNames[i] = idMapping.getColumnMapping(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.getColumnMapping(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.getColumnMapping(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();
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);
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, getFetchPlan(), mappingDefinition, candidateCmd, getCandidateClass());
}
use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.
the class DynamicSchemaFieldManager method storeObjectField.
/**
* Method to store an object field into the attached instance.
* @param fieldNumber Number of the field to store
* @param value the value in the detached instance
*/
public void storeObjectField(int fieldNumber, Object value) {
if (value == null) {
// No value so nothing to do
return;
}
ExecutionContext ec = sm.getExecutionContext();
ClassLoaderResolver clr = ec.getClassLoaderResolver();
AbstractMemberMetaData mmd = sm.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
if (mmd != null) {
DatastoreClass table = rdbmsMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
JavaTypeMapping fieldMapping = table.getMemberMapping(mmd);
if (fieldMapping != null) {
if (fieldMapping instanceof InterfaceMapping) {
// 1-1 Interface field
InterfaceMapping intfMapping = (InterfaceMapping) fieldMapping;
if (mmd.getFieldTypes() != null || mmd.hasExtension(MetaData.EXTENSION_MEMBER_IMPLEMENTATION_CLASSES)) {
// Field is defined to not accept this type so just return
return;
}
processInterfaceMappingForValue(intfMapping, value, mmd, ec);
} else if (mmd.hasCollection() || mmd.hasArray()) {
boolean hasJoin = false;
if (mmd.getJoinMetaData() != null) {
hasJoin = true;
} else {
AbstractMemberMetaData[] relMmds = mmd.getRelatedMemberMetaData(clr);
if (relMmds != null && relMmds[0].getJoinMetaData() != null) {
hasJoin = true;
}
}
if (!hasJoin) {
// Not join table so no supported schema updates
return;
}
Table joinTbl = fieldMapping.getStoreManager().getTable(mmd);
ElementContainerTable collTbl = (ElementContainerTable) joinTbl;
JavaTypeMapping elemMapping = collTbl.getElementMapping();
if (elemMapping instanceof InterfaceMapping) {
InterfaceMapping intfMapping = (InterfaceMapping) elemMapping;
if (mmd.hasCollection()) {
Collection coll = (Collection) value;
if (coll.isEmpty()) {
return;
}
// Update value mapping using first element. Maybe we should do the same for all elements?
Object elementValue = coll.iterator().next();
processInterfaceMappingForValue(intfMapping, elementValue, mmd, ec);
} else if (mmd.hasArray()) {
if (Array.getLength(value) == 0) {
return;
}
// Update value mapping using first element. Maybe we should do the same for all elements?
Object elementValue = Array.get(value, 0);
processInterfaceMappingForValue(intfMapping, elementValue, mmd, ec);
}
}
} else if (mmd.hasMap()) {
boolean hasJoin = false;
if (mmd.getJoinMetaData() != null) {
hasJoin = true;
} else {
AbstractMemberMetaData[] relMmds = mmd.getRelatedMemberMetaData(clr);
if (relMmds != null && relMmds[0].getJoinMetaData() != null) {
hasJoin = true;
}
}
if (!hasJoin) {
// Not join table so no supported schema updates
return;
}
Map map = (Map) value;
if (map.isEmpty()) {
return;
}
Table joinTbl = fieldMapping.getStoreManager().getTable(mmd);
MapTable mapTbl = (MapTable) joinTbl;
JavaTypeMapping keyMapping = mapTbl.getKeyMapping();
if (keyMapping instanceof InterfaceMapping) {
// Update key mapping using first key. Maybe we should do the same for all keys?
InterfaceMapping intfMapping = (InterfaceMapping) keyMapping;
Object keyValue = map.keySet().iterator().next();
processInterfaceMappingForValue(intfMapping, keyValue, mmd, ec);
}
JavaTypeMapping valMapping = mapTbl.getValueMapping();
if (valMapping instanceof InterfaceMapping) {
// Update value mapping using first value. Maybe we should do the same for all values?
InterfaceMapping intfMapping = (InterfaceMapping) valMapping;
Object valValue = map.values().iterator().next();
processInterfaceMappingForValue(intfMapping, valValue, mmd, ec);
}
}
}
}
}
use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.
the class ObjectExpression method is.
/**
* An "is" (instanceOf) expression, providing a BooleanExpression whether this expression is an instanceof the provided type.
* @param expr The expression representing the type
* @param not Whether the operator is "!instanceof"
* @return Whether this expression is an instance of the provided type
*/
public BooleanExpression is(SQLExpression expr, boolean not) {
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
ClassLoaderResolver clr = stmt.getClassLoaderResolver();
// Extract instanceOf type
String instanceofClassName = null;
SQLExpression classExpr = expr;
if (expr instanceof TypeConverterLiteral) {
// For some reason the user has input "TYPE(field) = :param" and param is a type-converted value
classExpr = ((TypeConverterLiteral) expr).getDelegate();
}
if (classExpr instanceof StringLiteral) {
instanceofClassName = (String) ((StringLiteral) classExpr).getValue();
} else if (classExpr instanceof CollectionLiteral) {
// "a instanceof (b1,b2,b3)"
CollectionLiteral typesLiteral = (CollectionLiteral) classExpr;
Collection values = (Collection) typesLiteral.getValue();
if (values.size() == 1) {
Object value = values.iterator().next();
String valueStr = null;
if (value instanceof Class) {
valueStr = ((Class) value).getCanonicalName();
} else if (value instanceof String) {
valueStr = (String) value;
} else {
throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
}
return is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), not);
}
List<BooleanExpression> listExp = new LinkedList<>();
for (Object value : values) {
String valueStr = null;
if (value instanceof Class) {
valueStr = ((Class) value).getCanonicalName();
} else if (value instanceof String) {
valueStr = (String) value;
} else {
throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
}
listExp.add(is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), false));
}
BooleanExpression result = null;
for (BooleanExpression sqlExpression : listExp) {
result = result == null ? sqlExpression : result.ior(sqlExpression);
}
if (result != null) {
return not ? result.not() : result;
}
} else {
throw new NucleusUserException("Do not currently support `instanceof` with class expression of type " + classExpr);
}
Class type = null;
try {
type = stmt.getQueryGenerator().resolveClass(instanceofClassName);
} catch (ClassNotResolvedException cnre) {
type = null;
}
if (type == null) {
throw new NucleusUserException(Localiser.msg("037016", instanceofClassName));
}
// Extract type of member and check obvious conditions
SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
Class memberType = clr.classForName(mapping.getType());
if (!memberType.isAssignableFrom(type) && !type.isAssignableFrom(memberType)) {
// Member type and instanceof type are totally incompatible, so just return false
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
} else if (memberType == type) {
// instanceof type is the same as the member type therefore must comply (can't store supertypes)
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
}
if (mapping instanceof EmbeddedMapping) {
// Don't support embedded instanceof expressions
AbstractClassMetaData fieldCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
if (fieldCmd.hasDiscriminatorStrategy()) {
// Embedded field with inheritance so add discriminator restriction
JavaTypeMapping discMapping = ((EmbeddedMapping) mapping).getDiscriminatorMapping();
AbstractClassMetaData typeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(type, clr);
SQLExpression discExpr = stmt.getSQLExpressionFactory().newExpression(stmt, table, discMapping);
SQLExpression discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, typeCmd.getDiscriminatorValue());
BooleanExpression typeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
while (subclassIter.hasNext()) {
String subclassName = subclassIter.next();
AbstractClassMetaData subtypeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(subclassName, clr);
Object subtypeDiscVal = subtypeCmd.getDiscriminatorValue();
discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, subtypeDiscVal);
BooleanExpression subtypeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
if (not) {
typeExpr = typeExpr.and(subtypeExpr);
} else {
typeExpr = typeExpr.ior(subtypeExpr);
}
}
return typeExpr;
}
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
} else if (mapping instanceof PersistableMapping || mapping instanceof ReferenceMapping) {
// Field has its own table, so join to it
AbstractClassMetaData memberCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
DatastoreClass memberTable = null;
if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
// Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
if (cmds != null) {
// TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
if (cmds.length > 1) {
NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
}
memberTable = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
} else {
// No subclasses with tables to join to, so throw a user error
throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
}
} else {
// Class of the field will have its own table
memberTable = storeMgr.getDatastoreClass(mapping.getType(), clr);
}
DiscriminatorMetaData dismd = memberTable.getDiscriminatorMetaData();
DiscriminatorMapping discMapping = (DiscriminatorMapping) memberTable.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
if (discMapping != null) {
SQLTable targetSqlTbl = null;
if (mapping.getTable() != memberTable) {
// FK is on source table so inner join to target table (holding the discriminator)
targetSqlTbl = stmt.getTable(memberTable, null);
if (targetSqlTbl == null) {
targetSqlTbl = stmt.join(JoinType.INNER_JOIN, getSQLTable(), mapping, memberTable, null, memberTable.getIdMapping(), null, null);
}
} else {
// FK is on target side and already joined
targetSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, getSQLTable(), discMapping);
}
// Add restrict to discriminator for the instanceOf type and subclasses
SQLTable discSqlTbl = targetSqlTbl;
BooleanExpression discExpr = null;
if (!Modifier.isAbstract(type.getModifiers())) {
discExpr = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, type.getName(), dismd, discMapping, discSqlTbl, clr);
}
Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
boolean multiplePossibles = false;
while (subclassIter.hasNext()) {
String subclassName = subclassIter.next();
Class subclass = clr.classForName(subclassName);
if (Modifier.isAbstract(subclass.getModifiers())) {
continue;
}
BooleanExpression discExprSub = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, subclassName, dismd, discMapping, discSqlTbl, clr);
if (discExpr != null) {
multiplePossibles = true;
discExpr = discExpr.ior(discExprSub);
} else {
discExpr = discExprSub;
}
}
if (multiplePossibles && discExpr != null) {
discExpr.encloseInParentheses();
}
return ((not && discExpr != null) ? discExpr.not() : discExpr);
}
// No discriminator, so the following is likely incomplete.
// Join to member table
DatastoreClass table = null;
if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
// Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
if (cmds != null) {
// TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
if (cmds.length > 1) {
NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
}
table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
} else {
// No subclasses with tables to join to, so throw a user error
throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
}
} else {
// Class of the field will have its own table
table = storeMgr.getDatastoreClass(mapping.getType(), clr);
}
if (table.managesClass(type.getName())) {
// This type is managed in this table so must be an instance TODO Is this correct, what if member is using discrim?
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
}
if (table == stmt.getPrimaryTable().getTable()) {
// This is member table, so just need to restrict to the instanceof type now
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
if (stmt instanceof SelectStatement) {
SelectStatement selectStmt = (SelectStatement) stmt;
if (selectStmt.getNumberOfUnions() == 0) {
// No UNIONs so just check the main statement and return according to whether it is allowed
Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
if (type.isAssignableFrom(mainCandidateCls) == not) {
SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
return (BooleanExpression) returnExpr;
}
SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
return (BooleanExpression) returnExpr;
}
NucleusLogger.QUERY.warn("TYPE/INSTANCEOF operator for class=" + memberCmd.getFullClassName() + " on table=" + memberTable + " for type=" + instanceofClassName + " but there is no discriminator and using UNIONs. Any subsequent handling is likely incorrect TODO");
// a). we have unions for the member, so restrict to just the applicable unions
// Note that this is only really valid is wanting "a instanceof SUB1".
// It fails when we want to do "a instanceof SUB1 || a instanceof SUB2"
// TODO What if this "OP_IS" is in the SELECT clause??? Need to update QueryToSQLMapper.compileResult
Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
if (type.isAssignableFrom(mainCandidateCls) == not) {
SQLExpression unionClauseExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
stmt.whereAnd((BooleanExpression) unionClauseExpr, false);
}
List<SelectStatement> unionStmts = selectStmt.getUnions();
for (SelectStatement unionStmt : unionStmts) {
Class unionCandidateCls = clr.classForName(unionStmt.getCandidateClassName());
if (type.isAssignableFrom(unionCandidateCls) == not) {
SQLExpression unionClauseExpr = exprFactory.newLiteral(unionStmt, m, true).eq(exprFactory.newLiteral(unionStmt, m, false));
// TODO Avoid using whereAnd
unionStmt.whereAnd((BooleanExpression) unionClauseExpr, false);
}
}
// Just return true since we applied the condition direct to the unions
SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
return (BooleanExpression) returnExpr;
}
// b). The member table doesn't manage the instanceof type, so do inner join to
// the table of the instanceof to impose the instanceof condition
DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
}
// Do inner join to this table to impose the instanceOf
DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
} else {
// TODO Implement instanceof for other types
throw new NucleusException("Dont currently support " + this + " instanceof " + type.getName());
}
}
Aggregations