package jef.database.meta;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import jef.common.SimpleSet;
import jef.database.DbUtils;
import jef.database.Field;
import jef.database.dialect.ColumnType;
import jef.database.dialect.DatabaseDialect;
import jef.database.meta.Index.IndexItem;
import jef.database.meta.def.IndexDef;
import jef.database.support.RDBMS;
import jef.tools.StringUtils;
public class DdlGeneratorImpl implements DdlGenerator {
private DatabaseDialect profile;
private final String BRUKETS_LEFT;
private final String BRUKETS_RIGHT;
private boolean commandForEach;
public DdlGeneratorImpl(DatabaseDialect profile) {
this.profile = profile;
commandForEach = profile.has(Feature.ALTER_FOR_EACH_COLUMN);
if (profile.has(Feature.BRUKETS_FOR_ALTER_TABLE)) {
BRUKETS_LEFT = " (";
BRUKETS_RIGHT = ")";
} else {
BRUKETS_LEFT = " ";
BRUKETS_RIGHT = "";
}
}
/*
* 生成建表语句 (non-Javadoc)
*
* @see
* jef.database.SqlProcessor#toTableCreateClause(jef.database.DataObject,
* java.lang.String)
*/
public TableCreateStatement toTableCreateClause(ITableMetadata meta, String tablename) {
TableCreateStatement result = new TableCreateStatement();
result.addTableMeta(tablename, meta, profile);
return result;
}
/**
* 转换成索引创建语句
*/
public List<String> toIndexClause(ITableMetadata meta, String tablename) {
List<String> sqls = new ArrayList<String>();
int n=tablename.indexOf('.');
String tableschema=null;
if(n>-1){
tableschema=tablename.substring(0,n);
tablename=tablename.substring(n+1);
}
for (IndexDef index : meta.getIndexDefinition()) {
List<Field> fields=new ArrayList<Field>();
List<IndexItem> columns=new ArrayList<IndexItem>();
for (String fieldname : index.getColumns()) {
boolean asc=true;
if(fieldname.toLowerCase().endsWith(" desc")){
asc=false;
fieldname=fieldname.substring(0,fieldname.length()-5).trim();
}
Field field = meta.getField(fieldname);
if (field == null){
field=new FBIField(fieldname);
columns.add(new IndexItem(fieldname,asc,0));
}else{
String columnName=meta.getColumnName(field, profile,true);
columns.add(new IndexItem(columnName,asc,0));
}
fields.add(field);
}
String indexName=index.getName();
if(StringUtils.isEmpty(indexName)){
StringBuilder iNameBuilder = new StringBuilder();
iNameBuilder.append("IDX_").append(StringUtils.truncate(StringUtils.removeChars(tablename, '_'), 14));
int maxField = ((28 - iNameBuilder.length()) / index.getColumns().length) - 1;
if (maxField < 1)
maxField = 1;
for(Field field: fields){
iNameBuilder.append('_');
if(field instanceof FBIField){
iNameBuilder.append(
StringUtils.truncate(StringUtils.randomString(), maxField)
);
}else{
iNameBuilder.append(
StringUtils.truncate(meta.getColumnDef(field).getColumnName(profile,false), maxField)
);
}
}
indexName=iNameBuilder.toString();
}
if (indexName.length() > 30)
indexName = indexName.substring(0, 30);
Index indexobj=new Index(indexName);
indexobj.setTableSchema(tableschema);
indexobj.setTableName(tablename);
indexobj.setUnique(index.isUnique());
indexobj.setUserDefinition(index.getDefinition());
if(index.isClustered()){
indexobj.setType(DatabaseMetaData.tableIndexClustered);
}
for(IndexItem c:columns){
indexobj.addColumn(c.column, c.asc);
}
sqls.add(indexobj.toCreateSql(profile));
}
return sqls;
}
public List<String> toViewCreateClause() {
return null;
}
// ALTER [ COLUMN ] column TYPE type [ USING expression ]
// ALTER [ COLUMN ] column SET DEFAULT expression
// ALTER [ COLUMN ] column DROP DEFAULT
// ALTER [ COLUMN ] column { SET | DROP } NOT NULL
public List<String> toTableModifyClause(ITableMetadata meta, String tableName, Map<String, ColumnType> insert, List<ColumnModification> changed, List<String> delete) {
List<String> sqls = new ArrayList<String>();
if (!insert.isEmpty()) {
if (profile.has(Feature.ONE_COLUMN_IN_SINGLE_DDL)) {// 某些数据库一次ALTER
// TABLE语句只能修改一列
for (Entry<String, ColumnType> entry : insert.entrySet()) {
Set<Entry<String, ColumnType>> ss = new SimpleSet<Entry<String, ColumnType>>();
ss.add(entry);
sqls.add(toAddColumnSql(tableName, ss));
}
} else {;
sqls.add(toAddColumnSql(tableName, insert.entrySet()));
}
}
if (!changed.isEmpty()) {
boolean complexSyntax = profile.has(Feature.COLUMN_ALTERATION_SYNTAX);
if (profile.has(Feature.ONE_COLUMN_IN_SINGLE_DDL) || complexSyntax) {// 某些数据库一次ALTER
// TABLE语句只能修改一列
for (ColumnModification entry : changed) {
if (complexSyntax) {// complex operate here
for (ColumnChange change : entry.getChanges()) {// 要针对每种Change单独实现SQL语句,目前已知Derby和postgresql是这样的,而且两者的语法有少量差别,这里尽量用兼容写法
sqls.add(toChangeColumnSql(tableName, DbUtils.escapeColumn(profile, entry.getFrom().getColumnName()), change, profile));
}
} else {
// 简单语法时
sqls.add(toChangeColumnSql(tableName, Arrays.asList(entry)));
}
}
} else {
sqls.add(toChangeColumnSql(tableName, changed));
}
}
if (!delete.isEmpty()) {
if (profile.has(Feature.ONE_COLUMN_IN_SINGLE_DDL)) {// 某些数据库一次ALTER
// TABLE语句只能修改一列
for (String entry : delete) {
sqls.add(toDropColumnSql(tableName, Arrays.asList(entry)));
}
} else {
sqls.add(toDropColumnSql(tableName, delete));
}
}
return sqls;
}
/*
* DERBY has much complexity than Oracle in modifying table columns.
*
* key words must column-Name SET DATA TYPE VARCHAR(integer) | column-Name
* SET DATA TYPE VARCHAR FOR BIT DATA(integer) | column-name SET INCREMENT
* BY integer-constant | column-name RESTART WITH integer-constant |
* column-name [ NOT ] NULL | column-name [ WITH | SET ] DEFAULT
* default-value | column-name DROP DEFAULT
*/
private String toChangeColumnSql(String tableName, String columnName, ColumnChange change, DatabaseDialect profile) {
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(128);
sb.append("ALTER TABLE ");
sb.append(tableName).append(' ').append(profile.getProperty(DbProperty.MODIFY_COLUMN)).append(' ');
sb.append(columnName).append(' ');
String setDataType;
String setNull;
String setNotNull;
if (RDBMS.postgresql == profile.getName()) {// PG
setDataType = "TYPE";
setNull = "DROP NOT NULL";
setNotNull = "SET NOT NULL";
} else if (RDBMS.derby == profile.getName()) {// DERBY
setDataType = "SET DATA TYPE";
setNull = "NULL";
setNotNull = "NOT NULL";
} else {// HSQLDB
setDataType = "";
setNull = "SET NULL";
setNotNull = "SET NOT NULL";
}
switch (change.getType()) {
case CHG_DATATYPE:
sb.append(setDataType).append(' ').append(change.getTo());
return sb.toString();
case CHG_DEFAULT:
sb.append("SET DEFAULT ").append(change.getTo());
return sb.toString();
case CHG_DROP_DEFAULT:
sb.append("DROP DEFAULT");
return sb.toString();
case CHG_TO_NOT_NULL:
sb.append(setNotNull);
return sb.toString();
case CHG_TO_NULL:
sb.append(setNull);
return sb.toString();
default:
throw new IllegalStateException("Unknown change type" + change.getType());
}
}
private String toChangeColumnSql(String tableName, List<ColumnModification> entrySet) {
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(128);
sb.append("ALTER TABLE ");
sb.append(tableName).append(' ').append(profile.getProperty(DbProperty.MODIFY_COLUMN)).append(BRUKETS_LEFT);
int n = 0;
for (ColumnModification entry : entrySet) {
if (n > 0) {
sb.append(",\n");
if (commandForEach) {
sb.append(profile.getProperty(DbProperty.MODIFY_COLUMN)).append(' ');
}
}
sb.append(DbUtils.escapeColumn(profile, entry.getFrom().getColumnName())).append(' ');
sb.append(profile.getCreationComment(entry.getNewColumn(), true));
n++;
}
sb.append(BRUKETS_RIGHT);
return sb.toString();
}
private String toDropColumnSql(String tableName, List<String> entrySet) {
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(128);
sb.append("ALTER TABLE ");
sb.append(tableName).append(' ').append(profile.getProperty(DbProperty.DROP_COLUMN)).append(BRUKETS_LEFT);
int n = 0;
for (String entry : entrySet) {
if (n > 0) {
sb.append(",\n");
if (commandForEach) {
sb.append(profile.getProperty(DbProperty.DROP_COLUMN)).append(' ');
}
}
sb.append(entry);
n++;
}
sb.append(BRUKETS_RIGHT);
return sb.toString();
}
private String toAddColumnSql(String tableName, Set<Entry<String, ColumnType>> entrySet) {
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(128);
sb.append("ALTER TABLE ");
sb.append(tableName).append(' ').append(profile.getProperty(DbProperty.ADD_COLUMN)).append(BRUKETS_LEFT);
int n = 0;
for (Entry<String, ColumnType> entry : entrySet) {
if (n > 0) {
sb.append(", ");
if (commandForEach) {
sb.append(profile.getProperty(DbProperty.ADD_COLUMN)).append(' ');
}
}
sb.append(entry.getKey()).append(' ');
sb.append(profile.getCreationComment(entry.getValue(), true));
n++;
}
sb.append(BRUKETS_RIGHT);
return sb.toString();
}
private static final String DROP_CONSTRAINT_SQL = "alter table %1$s drop constraint %2$s";
@Override
public String getDropConstraintSql(String tablename, String constraintName) {
String template = this.profile.getProperty(DbProperty.DROP_FK_PATTERN);
if (StringUtils.isEmpty(template)) {
template = DROP_CONSTRAINT_SQL;
}
return String.format(template, tablename, constraintName);
}
}