All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.blazebit.persistence.impl.dialect.DB2DbmsDialect Maven / Gradle / Ivy

The newest version!
package com.blazebit.persistence.impl.dialect;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.blazebit.persistence.spi.DbmsLimitHandler;
import com.blazebit.persistence.spi.DbmsModificationState;
import com.blazebit.persistence.spi.DbmsStatementType;
import com.blazebit.persistence.spi.OrderByElement;

public class DB2DbmsDialect extends DefaultDbmsDialect {

    public DB2DbmsDialect() {
        super(getSqlTypes());
    }

    private static Map, String> getSqlTypes() {
        Map, String> types = new HashMap, String>();

        // We have to specify a length and we just choose 2048 because it will most probably be a good fit
        types.put(String.class, "varchar(2048)");

        return types;
    }

    @Override
	public boolean supportsTupleDistinctCounts() {
		return false;
	}
    
	@Override
	public String getWithClause(boolean recursive) {
		return "with";
	}
	
	@Override
	public boolean supportsComplexGroupBy() {
		return false;
	}

    @Override
    public boolean supportsComplexJoinOn() {
        return false;
    }

    @Override
    public boolean supportsJoinsInRecursiveCte() {
        // See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/com.ibm.db2z10.doc.codes/src/tpc/n345.dita
        return false;
    }

	@Override
	public boolean supportsReturningColumns() {
		return true;
	}

    @Override
    public boolean supportsModificationQueryInWithClause() {
        return true;
    }
    
    @Override
    public boolean usesExecuteUpdateWhenWithClauseInModificationQuery() {
        return false;
    }

    @Override
    public Map appendExtendedSql(StringBuilder sqlSb, DbmsStatementType statementType, boolean isSubquery, boolean isEmbedded, StringBuilder withClause, String limit, String offset, String[] returningColumns, Map includedModificationStates) {
        // since changes in DB2 will be visible to other queries, we need to preserve the old state if required
        boolean requiresOld = includedModificationStates != null && includedModificationStates.containsKey(DbmsModificationState.OLD);
        
        if (requiresOld) {
            Map dbmsModificationStateQueries = new LinkedHashMap();
            StringBuilder sb = new StringBuilder(sqlSb.length() + 30);
            if (statementType == DbmsStatementType.INSERT) {
                StringBuilder newValuesSb = new StringBuilder();
                String newValuesTableName = includedModificationStates.get(DbmsModificationState.OLD) + "_new";
                newValuesSb.append("select * from final table (");
                newValuesSb.append(sqlSb);
                newValuesSb.append(")");
                dbmsModificationStateQueries.put(newValuesTableName, newValuesSb.toString());
                
                String needle = "into";
                int startIndex = indexOfIgnoreCase(sqlSb, needle) + needle.length() + 1;
                int endIndex = sqlSb.indexOf(" ", startIndex);
                endIndex = indexOfOrEnd(sqlSb, '(', startIndex, endIndex);
                String table = sqlSb.substring(startIndex, endIndex);

                sb.append("select * from ");
                sb.append(table);
                sb.append("\nexcept\n");
                sb.append("select * from ");
                sb.append(newValuesTableName);
            } else {
                sb.append("select * from old table (");
                sb.append(sqlSb);
                sb.append(")");
            }
            
            sqlSb.setLength(0);
            
            if (isSubquery) {
                sqlSb.append('(');
            }
            
            sqlSb.append("select ");
            for (int i = 0; i < returningColumns.length; i++) {
                if (i != 0) {
                    sqlSb.append(',');
                }
                sqlSb.append(returningColumns[i]);
            }
            
            sqlSb.append(" from ");
            sqlSb.append(includedModificationStates.get(DbmsModificationState.OLD));
            
            dbmsModificationStateQueries.put(includedModificationStates.get(DbmsModificationState.OLD), sb.toString());
            
            if (isSubquery) {
                sqlSb.append(')');
            }
            
            return dbmsModificationStateQueries;
        }
        
        boolean needsReturningWrapper = isEmbedded && (returningColumns != null || statementType != DbmsStatementType.SELECT);
        if (needsReturningWrapper || withClause != null && (statementType != DbmsStatementType.SELECT)) {
            if (isSubquery) {
                sqlSb.insert(0, '(');
            }
            
            // Insert might need limit
            if (limit != null) {
                appendLimit(sqlSb, isSubquery, limit, offset);
            }
            
            String[] columns;
            if (returningColumns == null) {
                // we will simulate the update count
                columns = new String[]{ "count(*)" };
            } else {
                columns = returningColumns;
            }
            
            if (needsReturningWrapper) {
                applyQueryReturning(sqlSb, statementType, withClause, columns);
            } else {
                applyQueryReturning(sqlSb, statementType, withClause, columns);
            }
            
            if (isSubquery) {
                sqlSb.append(')');
            }
            
            return null;
        }

        if (isSubquery) {
            sqlSb.insert(0, '(');
        }
        
        // This is a select
        if (withClause != null) {
            sqlSb.insert(indexOfIgnoreCase(sqlSb, "select"), withClause);
        }
        if (limit != null) {
            appendLimit(sqlSb, isSubquery, limit, offset);
        }
        
        if (isSubquery) {
            sqlSb.append(')');
        }
        
        return null;
    }

    @Override
    public DbmsLimitHandler createLimitHandler() {
        if (isCompatibilityVectorMYS()) {
            return super.createLimitHandler();
        }
        return new DB2DbmsLimitHandler();
    }

    protected boolean isCompatibilityVectorMYS() {
        // This requires DB2_COMPATIBILITY_VECTOR=MYS
        // See for reference: https://www.ibm.com/developerworks/community/blogs/SQLTips4DB2LUW/entry/limit_offset?lang=en
        return false;
    }
    
    @Override
    protected void appendSetOperands(StringBuilder sqlSb, String operator, boolean isSubquery, List operands, boolean hasOuterClause) {
        if (!hasOuterClause) {
            super.appendSetOperands(sqlSb, operator, isSubquery, operands, hasOuterClause);
        } else {
            sqlSb.append("select * from (");
            super.appendSetOperands(sqlSb, operator, isSubquery, operands, hasOuterClause);
            sqlSb.append(')');
        }
    }
    
    @Override
    protected void appendOrderByElement(StringBuilder sqlSb, OrderByElement element) {
        if ((element.isNullsFirst() && !element.isAscending()) || (!element.isNullsFirst() && element.isAscending())) {
            // The following are ok according to DB2 docs
            // ASC + NULLS LAST
            // DESC + NULLS FIRST
            super.appendOrderByElement(sqlSb, element);
        } else {
            appendEmulatedOrderByElementWithNulls(sqlSb, element);
        }
    }

	private void applyQueryReturning(StringBuilder sqlSb, DbmsStatementType statementType, StringBuilder withClause, String[] returningColumns) {
	    int initial = withClause != null ? withClause.length() : 0;
		StringBuilder sb = new StringBuilder(initial + 25 + returningColumns.length * 20);
		if (withClause != null) {
		    sb.append(withClause);
		}
		
		sb.append("select ");
		for (int i = 0; i < returningColumns.length; i++) {
			if (i != 0) {
			    sb.append(',');
			}
			sb.append(returningColumns[i]);
			sb.append(" as ret_col_");
			sb.append(i);
		}
		sb.append(" from ");
		
		if (statementType == DbmsStatementType.DELETE) {
		    sb.append("old");
		} else {
            sb.append("final");
		}
		
		sb.append(" table (");
		sqlSb.insert(0, sb);
		sqlSb.append(')');
	}

    private static int indexOfOrEnd(StringBuilder sb, char needle, int startIndex, int endIndex) {
        while (startIndex < endIndex) {
            if (sb.charAt(startIndex) == needle) {
                return startIndex;
            }
            
            startIndex++;
        }
        
        return endIndex;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy