use of org.datanucleus.store.rdbms.adapter.DatastoreAdapter in project datanucleus-rdbms by datanucleus.
the class ColumnImpl method getSQLDefinition.
/* (non-Javadoc)
* @see org.datanucleus.store.rdbms.table.Column#getSQLDefinition()
*/
public String getSQLDefinition() {
StringBuilder def = new StringBuilder(identifier.toString());
if (!StringUtils.isWhitespace(columnMetaData.getColumnDdl())) {
// User-defined DDL, so assume they set the type etc to something sensible
// Note that the JPA spec doesn't explicitly specify if this has to include the type or not
def.append(" ").append(columnMetaData.getColumnDdl());
return def.toString();
}
DatastoreAdapter adapter = getStoreManager().getDatastoreAdapter();
// Add any type specification.
if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity() && !adapter.supportsOption(DatastoreAdapter.AUTO_INCREMENT_COLUMN_TYPE_SPECIFICATION)) {
// Don't add type
} else if (typeName != null) {
// Allow for manual override of "type" for things like MySQL ENUM where we want to define the type with its options
def.append(" " + typeName);
} else {
StringBuilder typeSpec = new StringBuilder(typeInfo.getTypeName());
// i.e. if it contains parentheses, and the type name itself doesn't. createParams is mighty ill-defined by the JDBC spec, but attempt to interpret it.
if (typeInfo.getCreateParams() != null && typeInfo.getCreateParams().indexOf('(') >= 0 && typeInfo.getTypeName().indexOf('(') < 0) {
StringTokenizer toks = new StringTokenizer(typeInfo.getCreateParams());
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok.startsWith("[") && tok.endsWith("]")) {
// skip
continue;
}
typeSpec.append(" " + tok);
}
}
// Add any precision. We use the "allowsPrecisionSpec" flag for this
StringBuilder precSpec = new StringBuilder();
int sqlPrecision = getSQLPrecision();
if (sqlPrecision > 0 && typeInfo.isAllowsPrecisionSpec()) {
precSpec.append(sqlPrecision);
if (columnMetaData.getScale() != null) {
precSpec.append("," + columnMetaData.getScale());
}
} else if (sqlPrecision > 0 && !typeInfo.isAllowsPrecisionSpec()) {
NucleusLogger.DATASTORE_SCHEMA.warn(Localiser.msg("020183", this.toString()));
}
int lParenIdx = typeSpec.toString().indexOf('(');
int rParenIdx = typeSpec.toString().indexOf(')', lParenIdx);
if (lParenIdx > 0 && rParenIdx > 0) {
// present ready for you to insert the values instead of appending them.
if (precSpec.length() > 0) {
typeSpec.replace(lParenIdx + 1, rParenIdx, precSpec.toString());
} else if (rParenIdx == lParenIdx + 1) {
throw new ColumnDefinitionException(Localiser.msg("020184", this.toString()));
}
} else if (precSpec.length() > 0) {
typeSpec.append('(');
typeSpec.append(precSpec.toString());
typeSpec.append(')');
}
def.append(" " + typeSpec.toString());
}
// Add DEFAULT (if specifiable before NULL)
if (adapter.supportsOption(DatastoreAdapter.DEFAULT_BEFORE_NULL_IN_COLUMN_OPTIONS) && adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_IN_COLUMN_OPTIONS) && columnMetaData.getDefaultValue() != null) {
def.append(" ").append(getDefaultDefinition());
}
if (isIdentity() && isPrimaryKey() && adapter.supportsOption(DatastoreAdapter.AUTO_INCREMENT_PK_IN_CREATE_TABLE_COLUMN_DEF)) {
def.append(" PRIMARY KEY");
}
// Nullability
if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity() && !adapter.supportsOption(DatastoreAdapter.AUTO_INCREMENT_KEYS_NULL_SPECIFICATION)) {
// Do nothing since the adapter doesn't allow NULL specifications with autoincrement/identity
} else {
if (!isNullable()) {
if (columnMetaData.getDefaultValue() == null || adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_WITH_NOT_NULL_IN_COLUMN_OPTIONS)) {
def.append(" NOT NULL");
}
} else if (typeInfo.getNullable() == DatabaseMetaData.columnNullable) {
if (adapter.supportsOption(DatastoreAdapter.NULLS_KEYWORD_IN_COLUMN_OPTIONS)) {
def.append(" NULL");
}
}
}
// Add DEFAULT (if specifiable after NULL)
if (!adapter.supportsOption(DatastoreAdapter.DEFAULT_BEFORE_NULL_IN_COLUMN_OPTIONS) && adapter.supportsOption(DatastoreAdapter.DEFAULT_KEYWORD_IN_COLUMN_OPTIONS) && columnMetaData.getDefaultValue() != null) {
def.append(" ").append(getDefaultDefinition());
}
// CHECK Constraints
if (adapter.supportsOption(DatastoreAdapter.CHECK_IN_CREATE_STATEMENTS) && checkConstraints != null) {
def.append(" " + checkConstraints.toString());
}
// Auto Increment
if (adapter.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS) && isIdentity()) {
def.append(" " + adapter.getAutoIncrementKeyword());
}
// Uniqueness
if (isUnique() && !adapter.supportsOption(DatastoreAdapter.UNIQUE_IN_END_CREATE_STATEMENTS)) {
def.append(" UNIQUE");
}
return def.toString();
}
use of org.datanucleus.store.rdbms.adapter.DatastoreAdapter in project datanucleus-rdbms by datanucleus.
the class DatastoreUUIDHexGenerator method reserveBlock.
/**
* Reserve a block of ids.
* @param size Block size
* @return The reserved block
*/
protected synchronized ValueGenerationBlock<String> reserveBlock(long size) {
if (size < 1) {
return null;
}
List<String> oids = new ArrayList<>();
try {
ManagedConnection mconn = connectionProvider.retrieveConnection();
PreparedStatement ps = null;
ResultSet rs = null;
RDBMSStoreManager rdbmsMgr = (RDBMSStoreManager) storeMgr;
SQLController sqlControl = rdbmsMgr.getSQLController();
try {
// Find the next ID from the database
DatastoreAdapter dba = rdbmsMgr.getDatastoreAdapter();
String stmt = dba.getSelectNewUUIDStmt();
ps = sqlControl.getStatementForQuery(mconn, stmt);
for (int i = 1; i < size; i++) {
rs = sqlControl.executeStatementQuery(null, mconn, stmt, ps);
if (rs.next()) {
oids.add(rs.getString(1));
}
}
} catch (SQLException e) {
throw new ValueGenerationException(Localiser.msg("040008", e.getMessage()));
} finally {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
sqlControl.closeStatement(mconn, ps);
}
} catch (SQLException e) {
// non-recoverable error
}
}
} finally {
connectionProvider.releaseConnection();
}
return new ValueGenerationBlock(oids);
}
use of org.datanucleus.store.rdbms.adapter.DatastoreAdapter in project datanucleus-rdbms by datanucleus.
the class TableGenerator method initialiseSequenceTable.
/**
* Method to initialise the sequence table used for storing the sequence values.
*/
protected synchronized void initialiseSequenceTable() {
// Set catalog/schema name (using properties, and if not specified using the values for the table)
String catalogName = properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_CATALOG);
if (catalogName == null) {
catalogName = properties.getProperty(ValueGenerator.PROPERTY_CATALOG_NAME);
}
String schemaName = properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_SCHEMA);
if (schemaName == null) {
schemaName = properties.getProperty(ValueGenerator.PROPERTY_SCHEMA_NAME);
}
String tableName = (properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_TABLE) == null ? DEFAULT_TABLE_NAME : properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_TABLE));
RDBMSStoreManager storeMgr = (RDBMSStoreManager) this.storeMgr;
DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
DatastoreIdentifier identifier = storeMgr.getIdentifierFactory().newTableIdentifier(tableName);
if (dba.supportsOption(DatastoreAdapter.CATALOGS_IN_TABLE_DEFINITIONS) && catalogName != null) {
identifier.setCatalogName(catalogName);
}
if (dba.supportsOption(DatastoreAdapter.SCHEMAS_IN_TABLE_DEFINITIONS) && schemaName != null) {
identifier.setSchemaName(schemaName);
}
DatastoreClass table = storeMgr.getDatastoreClass(identifier);
if (table != null) {
sequenceTable = (SequenceTable) table;
} else {
String sequenceNameColumnName = DEFAULT_SEQUENCE_COLUMN_NAME;
if (properties.containsKey(ValueGenerator.PROPERTY_SEQUENCETABLE_NAME_COLUMN)) {
sequenceNameColumnName = properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_NAME_COLUMN);
}
String nextValColumnName = DEFAULT_NEXTVALUE_COLUMN_NAME;
if (properties.containsKey(ValueGenerator.PROPERTY_SEQUENCETABLE_NEXTVAL_COLUMN)) {
nextValColumnName = properties.getProperty(ValueGenerator.PROPERTY_SEQUENCETABLE_NEXTVAL_COLUMN);
}
sequenceTable = new SequenceTable(identifier, storeMgr, sequenceNameColumnName, nextValColumnName);
sequenceTable.initialize(storeMgr.getNucleusContext().getClassLoaderResolver(null));
}
}
use of org.datanucleus.store.rdbms.adapter.DatastoreAdapter in project datanucleus-rdbms by datanucleus.
the class SQLStatementHelper method selectMemberOfSourceInStatement.
/**
* Method to select the specified member (field/property) of the source table in the passed SQL
* statement. This populates the mappingDefinition with the column details for this member.
* @param stmt The SQL statement
* @param mappingDefinition Mapping definition for the results (will be populated by any
* selected mappings if provided as input)
* @param fetchPlan FetchPlan
* @param sourceSqlTbl Table that has the member (or a super-table/secondary-table of this table)
* @param mmd Meta-data for the field/property in the source that we are selecting
* @param clr ClassLoader resolver
* @param maxFetchPlanLimit Max fetch depth from this point to select (0 implies no other objects)
* @param inputJoinType Optional join type to use for subobjects (otherwise decide join type internally)
*/
public static void selectMemberOfSourceInStatement(SelectStatement stmt, StatementClassMapping mappingDefinition, FetchPlan fetchPlan, SQLTable sourceSqlTbl, AbstractMemberMetaData mmd, ClassLoaderResolver clr, int maxFetchPlanLimit, JoinType inputJoinType) {
boolean selectSubobjects = false;
if (maxFetchPlanLimit > 0) {
selectSubobjects = true;
}
// Set table-group name for any related object we join to (naming based on member name)
String tableGroupName = sourceSqlTbl.getGroupName() + "." + mmd.getName();
JavaTypeMapping m = sourceSqlTbl.getTable().getMemberMapping(mmd);
if (m != null && m.includeInFetchStatement()) {
RelationType relationType = mmd.getRelationType(clr);
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
if (!dba.validToSelectMappingInStatement(stmt, m)) {
// Not valid to select this mapping for this statement so return
return;
}
MetaDataManager mmgr = storeMgr.getMetaDataManager();
StatementMappingIndex stmtMapping = new StatementMappingIndex(m);
if (m.getNumberOfDatastoreMappings() > 0) {
// Select of fields with columns in source table(s)
// Adds inner/outer join to any required superclass/secondary tables
SQLTable sqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, sourceSqlTbl, m);
boolean selectFK = true;
if (selectSubobjects && (relationType == RelationType.ONE_TO_ONE_UNI || (relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() == null)) && !mmd.isSerialized() && !mmd.isEmbedded()) {
// Related object with FK at this side
selectFK = selectFetchPlanFieldsOfFKRelatedObject(stmt, mappingDefinition, fetchPlan, sourceSqlTbl, mmd, clr, maxFetchPlanLimit, m, tableGroupName, stmtMapping, sqlTbl, inputJoinType);
} else if (selectSubobjects && (!mmd.isEmbedded() && !mmd.isSerialized()) && relationType == RelationType.MANY_TO_ONE_BI) {
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
if (mmd.getJoinMetaData() != null || relatedMmds[0].getJoinMetaData() != null) {
// N-1 bidirectional join table relation
// TODO Add left outer join from {sourceTable}.ID to {joinTable}.ELEM_FK
Table joinTable = storeMgr.getTable(relatedMmds[0]);
DatastoreElementContainer collTable = (DatastoreElementContainer) joinTable;
JavaTypeMapping selectMapping = collTable.getOwnerMapping();
SQLTable joinSqlTbl = null;
if (stmt.getPrimaryTable().getTable() != joinTable) {
// Join to the join table
JavaTypeMapping referenceMapping = collTable.getElementMapping();
joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), collTable, null, referenceMapping, null, tableGroupName, true);
} else {
// Main table of the statement is the join table so no need to join
joinSqlTbl = stmt.getPrimaryTable();
}
// Select the owner mapping of the join table
int[] colNumbers = stmt.select(joinSqlTbl, selectMapping, null);
stmtMapping.setColumnPositions(colNumbers);
// TODO Join to 1 side from join table?
} else {
// N-1 bidirectional FK relation
// Related object with FK at this side, so join/select related object as required
selectFK = selectFetchPlanFieldsOfFKRelatedObject(stmt, mappingDefinition, fetchPlan, sourceSqlTbl, mmd, clr, maxFetchPlanLimit, m, tableGroupName, stmtMapping, sqlTbl, inputJoinType);
}
}
if (selectFK) {
int[] colNumbers = stmt.select(sqlTbl, m, null);
stmtMapping.setColumnPositions(colNumbers);
}
} else {
// Select of related objects with FK in other table
if (relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() != null) {
// 1-1 bidirectional relation with FK in related table
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
AbstractMemberMetaData relatedMmd = relatedMmds[0];
String[] clsNames = null;
if (mmd.getType().isInterface()) {
if (mmd.getFieldTypes() != null && mmd.getFieldTypes().length == 1) {
// Use field-type since only one class specified
Class fldTypeCls = clr.classForName(mmd.getFieldTypes()[0]);
if (fldTypeCls.isInterface()) {
// User has specified an interface, so find its implementations
clsNames = mmgr.getClassesImplementingInterface(mmd.getFieldTypes()[0], clr);
} else {
// Use user-provided field-type
clsNames = new String[] { mmd.getFieldTypes()[0] };
}
}
if (clsNames == null) {
clsNames = mmgr.getClassesImplementingInterface(mmd.getTypeName(), clr);
}
} else {
String typeName = mmd.isSingleCollection() ? mmd.getCollection().getElementType() : mmd.getTypeName();
clsNames = new String[] { typeName };
}
DatastoreClass relatedTbl = storeMgr.getDatastoreClass(clsNames[0], clr);
JavaTypeMapping relatedMapping = relatedTbl.getMemberMapping(relatedMmd);
JavaTypeMapping relatedDiscrimMapping = relatedTbl.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true);
Object[] discrimValues = null;
JavaTypeMapping relatedTypeMapping = null;
AbstractClassMetaData relatedCmd = relatedMmd.getAbstractClassMetaData();
if (relatedDiscrimMapping != null && (relatedCmd.getSuperAbstractClassMetaData() != null || !relatedCmd.getFullClassName().equals(mmd.getTypeName()))) {
// Related table has a discriminator and the field can store other types
List discValueList = null;
for (String clsName : clsNames) {
List values = getDiscriminatorValuesForMember(clsName, relatedDiscrimMapping, storeMgr, clr);
if (discValueList == null) {
discValueList = values;
} else {
discValueList.addAll(values);
}
}
if (discValueList != null) {
discrimValues = discValueList.toArray(new Object[discValueList.size()]);
}
} else if (relatedTbl != relatedMapping.getTable()) {
// The relation is to a base class table, and the type stored is a sub-class
relatedTypeMapping = relatedTbl.getIdMapping();
}
SQLTable relatedSqlTbl = null;
if (relatedTypeMapping == null) {
// Join the 1-1 relation
JoinType joinType = getJoinTypeForOneToOneRelationJoin(sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, inputJoinType);
if (joinType == JoinType.LEFT_OUTER_JOIN || joinType == JoinType.RIGHT_OUTER_JOIN) {
inputJoinType = joinType;
}
relatedSqlTbl = addJoinForOneToOneRelation(stmt, sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, relatedMapping, relatedTbl, null, discrimValues, tableGroupName, joinType);
// Select the id mapping in the related table
int[] colNumbers = stmt.select(relatedSqlTbl, relatedTbl.getIdMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
} else {
DatastoreClass relationTbl = (DatastoreClass) relatedMapping.getTable();
if (relatedTbl != relatedMapping.getTable()) {
if (relatedMapping.isNullable()) {
// Nullable - left outer join from {sourceTable}.ID to {relatedBaseTable}.FK
// and inner join from {relatedBaseTable}.ID to {relatedTable}.ID
// (joins the relation and restricts to the right type)
relatedSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), relatedMapping.getTable(), null, relatedMapping, null, tableGroupName, true);
relatedSqlTbl = stmt.join(JoinType.INNER_JOIN, relatedSqlTbl, relatedMapping.getTable().getIdMapping(), relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName, true);
} else {
// Not nullable - inner join from {sourceTable}.ID to {relatedBaseTable}.FK
// and inner join from {relatedBaseTable}.ID to {relatedTable}.ID
// (joins the relation and restricts to the right type)
relatedSqlTbl = stmt.join(JoinType.INNER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), relatedMapping.getTable(), null, relatedMapping, null, tableGroupName, true);
relatedSqlTbl = stmt.join(JoinType.INNER_JOIN, relatedSqlTbl, relatedMapping.getTable().getIdMapping(), relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName, true);
}
} else {
// Join the 1-1 relation
JoinType joinType = getJoinTypeForOneToOneRelationJoin(sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, inputJoinType);
if (joinType == JoinType.LEFT_OUTER_JOIN || joinType == JoinType.RIGHT_OUTER_JOIN) {
inputJoinType = joinType;
}
relatedSqlTbl = addJoinForOneToOneRelation(stmt, sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, relatedMapping, relationTbl, null, null, tableGroupName, joinType);
}
// Select the id mapping in the subclass of the related table
// Note this adds an inner join from relatedTable to its subclass
relatedSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, relatedSqlTbl, relatedTbl.getIdMapping());
int[] colNumbers = stmt.select(relatedSqlTbl, relatedTbl.getIdMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
}
if (selectSubobjects && !mmd.isSerialized() && !mmd.isEmbedded()) {
// Select the fetch-plan fields of the related object
StatementClassMapping subMappingDefinition = new StatementClassMapping(null, mmd.getName());
selectFetchPlanOfSourceClassInStatement(stmt, subMappingDefinition, fetchPlan, relatedSqlTbl, relatedMmd.getAbstractClassMetaData(), maxFetchPlanLimit - 1, inputJoinType);
if (mappingDefinition != null) {
mappingDefinition.addMappingDefinitionForMember(mmd.getAbsoluteFieldNumber(), subMappingDefinition);
}
}
} else if (relationType == RelationType.MANY_TO_ONE_BI) {
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
if (mmd.getJoinMetaData() != null || relatedMmds[0].getJoinMetaData() != null) {
// N-1 bidirectional join table relation
// Add left outer join from {sourceTable}.ID to {joinTable}.ELEM_FK
Table joinTable = storeMgr.getTable(relatedMmds[0]);
DatastoreElementContainer collTable = (DatastoreElementContainer) joinTable;
JavaTypeMapping selectMapping = collTable.getOwnerMapping();
SQLTable joinSqlTbl = null;
if (stmt.getPrimaryTable().getTable() != joinTable) {
// Join to the join table
JavaTypeMapping referenceMapping = collTable.getElementMapping();
if (referenceMapping instanceof ReferenceMapping) {
// Join table has a reference mapping pointing to our table, so get the submapping for the implementation
ReferenceMapping refMap = (ReferenceMapping) referenceMapping;
Class implType = clr.classForName(mmd.getClassName(true));
referenceMapping = refMap.getJavaTypeMappingForType(implType);
}
joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), collTable, null, referenceMapping, null, tableGroupName + "_JOIN", true);
} else {
// Main table of the statement is the join table so no need to join
joinSqlTbl = stmt.getPrimaryTable();
}
// Select the owner mapping of the join table
int[] colNumbers = stmt.select(joinSqlTbl, selectMapping, null);
stmtMapping.setColumnPositions(colNumbers);
}
// TODO Select fetch plan fields of this related object
} else if (relationType == RelationType.MANY_TO_ONE_UNI) {
// Add left outer join from {sourceTable}.ID to {joinTable}.OWNER_FK
PersistableJoinTable joinTable = (PersistableJoinTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), joinTable, null, joinTable.getOwnerMapping(), null, tableGroupName + "_JOIN", true);
int[] colNumbers = stmt.select(joinSqlTbl, joinTable.getRelatedMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
// TODO Select fetch plan fields of this related object
}
}
if (mappingDefinition != null) {
mappingDefinition.addMappingForMember(mmd.getAbsoluteFieldNumber(), stmtMapping);
}
}
}
use of org.datanucleus.store.rdbms.adapter.DatastoreAdapter in project datanucleus-rdbms by datanucleus.
the class SelectStatement method getSQLText.
public synchronized SQLText getSQLText() {
if (sql != null) {
return sql;
}
DatastoreAdapter dba = getDatastoreAdapter();
boolean lock = false;
Boolean val = (Boolean) getValueForExtension(EXTENSION_LOCK_FOR_UPDATE);
if (val != null) {
lock = val.booleanValue();
}
boolean addAliasToAllSelects = false;
if (rangeOffset > 0 || rangeCount > -1) {
if (dba.getRangeByRowNumberColumn2().length() > 0) {
// Doing "SELECT * FROM (...)" so to be safe we need alias on all selects
addAliasToAllSelects = true;
}
}
// SELECT ..., ..., ...
sql = new SQLText("SELECT ");
if (distinct) {
sql.append("DISTINCT ");
}
addOrderingColumnsToSelect();
if (selectedItems.isEmpty()) {
// Nothing selected so select all
sql.append("*");
} else {
int autoAliasNum = 0;
Iterator<SelectedItem> selectItemIter = selectedItems.iterator();
while (selectItemIter.hasNext()) {
SelectedItem selectedItem = selectItemIter.next();
SQLText selectedST = selectedItem.getSQLText();
sql.append(selectedST);
if (selectedItem.getAlias() != null) {
sql.append(" AS " + rdbmsMgr.getIdentifierFactory().getIdentifierInAdapterCase(selectedItem.getAlias()));
} else {
if (addAliasToAllSelects) {
// This query needs an alias on all selects, so add "DN_{X}"
sql.append(" AS ").append(rdbmsMgr.getIdentifierFactory().getIdentifierInAdapterCase("DN_" + autoAliasNum));
autoAliasNum++;
}
}
if (selectItemIter.hasNext()) {
sql.append(',');
}
}
if ((rangeOffset > -1 || rangeCount > -1) && dba.getRangeByRowNumberColumn().length() > 0) {
// Add a ROW NUMBER column if supported as the means of handling ranges by the RDBMS
sql.append(',').append(dba.getRangeByRowNumberColumn()).append(" rn");
}
}
// FROM ...
sql.append(" FROM ");
sql.append(primaryTable.toString());
if (lock && dba.supportsOption(DatastoreAdapter.LOCK_OPTION_PLACED_AFTER_FROM)) {
sql.append(" WITH ").append(dba.getSelectWithLockOption());
}
if (joins != null) {
sql.append(getSqlForJoins(lock));
}
// WHERE ...
if (where != null) {
sql.append(" WHERE ").append(where.toSQLText());
}
// GROUP BY ...
if (groupingExpressions != null) {
List<SQLText> groupBy = new ArrayList();
Iterator<SQLExpression> groupIter = groupingExpressions.iterator();
while (groupIter.hasNext()) {
SQLExpression expr = groupIter.next();
boolean exists = false;
String exprSQL = expr.toSQLText().toSQL();
for (SQLText st : groupBy) {
String sql = st.toSQL();
if (sql.equals(exprSQL)) {
exists = true;
break;
}
}
if (!exists) {
groupBy.add(expr.toSQLText());
}
}
if (dba.supportsOption(DatastoreAdapter.GROUP_BY_REQUIRES_ALL_SELECT_PRIMARIES)) {
// Check that all select items are represented in the grouping for those RDBMS that need that
for (SelectedItem selItem : selectedItems) {
if (selItem.isPrimary()) {
boolean exists = false;
String selItemSQL = selItem.getSQLText().toSQL();
for (SQLText st : groupBy) {
String sql = st.toSQL();
if (sql.equals(selItemSQL)) {
exists = true;
break;
}
}
if (!exists) {
groupBy.add(selItem.getSQLText());
}
}
}
}
if (groupBy.size() > 0 && aggregated) {
sql.append(" GROUP BY ");
for (int i = 0; i < groupBy.size(); i++) {
if (i > 0) {
sql.append(',');
}
sql.append(groupBy.get(i));
}
}
}
// HAVING ...
if (having != null) {
sql.append(" HAVING ").append(having.toSQLText());
}
if (unions != null && allowUnions) {
// Add on any UNIONed statements
if (!dba.supportsOption(DatastoreAdapter.UNION_SYNTAX)) {
throw new NucleusException(Localiser.msg("052504", "UNION")).setFatal();
}
Iterator<SelectStatement> unionIter = unions.iterator();
while (unionIter.hasNext()) {
if (dba.supportsOption(DatastoreAdapter.USE_UNION_ALL)) {
sql.append(" UNION ALL ");
} else {
sql.append(" UNION ");
}
SelectStatement stmt = unionIter.next();
SQLText unionSql = stmt.getSQLText();
sql.append(unionSql);
}
}
// ORDER BY ...
SQLText orderStmt = generateOrderingStatement();
if (orderStmt != null) {
sql.append(" ORDER BY ").append(orderStmt);
}
// RANGE
if (rangeOffset > -1 || rangeCount > -1) {
// Add a LIMIT clause to end of statement if supported by the adapter
String limitClause = dba.getRangeByLimitEndOfStatementClause(rangeOffset, rangeCount, orderStmt != null);
if (limitClause.length() > 0) {
sql.append(" ").append(limitClause);
}
}
if (lock && dba.supportsOption(DatastoreAdapter.LOCK_WITH_SELECT_FOR_UPDATE)) {
// Add any required locking based on the RDBMS capability
if (distinct && !dba.supportsOption(DatastoreAdapter.DISTINCT_WITH_SELECT_FOR_UPDATE)) {
NucleusLogger.QUERY.warn(Localiser.msg("052502"));
} else if (groupingExpressions != null && !dba.supportsOption(DatastoreAdapter.GROUPING_WITH_SELECT_FOR_UPDATE)) {
NucleusLogger.QUERY.warn(Localiser.msg("052506"));
} else if (having != null && !dba.supportsOption(DatastoreAdapter.HAVING_WITH_SELECT_FOR_UPDATE)) {
NucleusLogger.QUERY.warn(Localiser.msg("052507"));
} else if (orderingExpressions != null && !dba.supportsOption(DatastoreAdapter.ORDERING_WITH_SELECT_FOR_UPDATE)) {
NucleusLogger.QUERY.warn(Localiser.msg("052508"));
} else if (joins != null && !joins.isEmpty() && !dba.supportsOption(DatastoreAdapter.MULTITABLES_WITH_SELECT_FOR_UPDATE)) {
NucleusLogger.QUERY.warn(Localiser.msg("052509"));
} else {
sql.append(" " + dba.getSelectForUpdateText());
if (dba.supportsOption(DatastoreAdapter.SELECT_FOR_UPDATE_NOWAIT)) {
Boolean nowait = (Boolean) getValueForExtension(EXTENSION_LOCK_FOR_UPDATE_NOWAIT);
if (nowait != null) {
sql.append(" NOWAIT");
}
}
}
}
if (lock && !dba.supportsOption(DatastoreAdapter.LOCK_WITH_SELECT_FOR_UPDATE) && !dba.supportsOption(DatastoreAdapter.LOCK_OPTION_PLACED_AFTER_FROM) && !dba.supportsOption(DatastoreAdapter.LOCK_OPTION_PLACED_WITHIN_JOIN)) {
NucleusLogger.QUERY.warn("Requested locking of query statement, but this RDBMS doesn't support a convenient mechanism");
}
if (rangeOffset > 0 || rangeCount > -1) {
if (dba.getRangeByRowNumberColumn2().length() > 0) {
// Oracle-specific using ROWNUM. Creates a query of the form
// SELECT * FROM (
// SELECT subq.*, ROWNUM rn FROM (
// SELECT x1, x2, ... FROM ... WHERE ... ORDER BY ...
// ) subq
// ) WHERE rn > {offset} AND rn <= {count}
SQLText userSql = sql;
// SELECT all columns of userSql, plus ROWNUM, with the FROM being the users query
SQLText innerSql = new SQLText("SELECT subq.*");
innerSql.append(',').append(dba.getRangeByRowNumberColumn2()).append(" rn");
innerSql.append(" FROM (").append(userSql).append(") subq ");
// Put that query as the FROM of the outer query, and apply the ROWNUM restrictions
SQLText outerSql = new SQLText("SELECT * FROM (").append(innerSql).append(") ");
outerSql.append("WHERE ");
if (rangeOffset > 0) {
outerSql.append("rn > " + rangeOffset);
if (rangeCount > -1) {
outerSql.append(" AND rn <= " + (rangeCount + rangeOffset));
}
} else {
outerSql.append(" rn <= " + rangeCount);
}
sql = outerSql;
} else if (dba.getRangeByRowNumberColumn().length() > 0) {
// DB2-specific ROW_NUMBER weirdness. Creates a query of the form
// SELECT subq.x1, subq.x2, ... FROM (
// SELECT x1, x2, ..., {keyword} rn FROM ... WHERE ... ORDER BY ...) subq
// WHERE subq.rn >= {offset} AND subq.rn < {count}
// This apparently works for DB2 (unverified, but claimed by IBM employee)
SQLText userSql = sql;
sql = new SQLText("SELECT ");
Iterator<SelectedItem> selectedItemIter = selectedItems.iterator();
while (selectedItemIter.hasNext()) {
SelectedItem selectedItemExpr = selectedItemIter.next();
sql.append("subq.");
String selectedCol = selectedItemExpr.getSQLText().toSQL();
if (selectedItemExpr.getAlias() != null) {
selectedCol = rdbmsMgr.getIdentifierFactory().getIdentifierInAdapterCase(selectedItemExpr.getAlias());
} else {
// strip out qualifier when encountered from column name since we are adding a new qualifier above.
// NOTE THAT THIS WILL FAIL IF THE ORIGINAL QUERY HAD "A0.COL1, B0.COL1" IN THE SELECT
int dotIndex = selectedCol.indexOf(".");
if (dotIndex > 0) {
// Remove qualifier name and the dot
selectedCol = selectedCol.substring(dotIndex + 1);
}
}
sql.append(selectedCol);
if (selectedItemIter.hasNext()) {
sql.append(',');
}
}
sql.append(" FROM (").append(userSql).append(") subq WHERE ");
if (rangeOffset > 0) {
sql.append("subq.rn").append(">").append("" + rangeOffset);
}
if (rangeCount > 0) {
if (rangeOffset > 0) {
sql.append(" AND ");
}
sql.append("subq.rn").append("<=").append("" + (rangeCount + rangeOffset));
}
}
}
return sql;
}
Aggregations