/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.query.metadata;
import static org.teiid.language.SQLConstants.NonReserved.*;
import static org.teiid.language.SQLConstants.Reserved.*;
import static org.teiid.language.SQLConstants.Tokens.*;
import static org.teiid.query.metadata.DDLConstants.*;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.teiid.adminapi.Admin.SchemaObjectType;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.StringUtil;
import org.teiid.language.SQLConstants;
import org.teiid.language.SQLConstants.NonReserved;
import org.teiid.language.SQLConstants.Tokens;
import org.teiid.metadata.*;
import org.teiid.metadata.BaseColumn.NullType;
import org.teiid.metadata.Database.ResourceType;
import org.teiid.metadata.FunctionMethod.Determinism;
import org.teiid.metadata.Grant.Permission;
import org.teiid.metadata.Grant.Permission.Privilege;
import org.teiid.metadata.ProcedureParameter.Type;
import org.teiid.query.sql.symbol.Constant;
import org.teiid.query.sql.symbol.GroupSymbol;
import org.teiid.query.sql.visitor.SQLStringVisitor;
public class DDLStringVisitor {
private static final String TAB = "\t"; //$NON-NLS-1$
private static final String NEWLINE = "\n";//$NON-NLS-1$
public static final String GENERATED = "TEIID_GENERATED"; //$NON-NLS-1$
private static final HashSet<String> LENGTH_DATATYPES = new HashSet<String>(
Arrays.asList(
DataTypeManager.DefaultDataTypes.CHAR,
DataTypeManager.DefaultDataTypes.CLOB,
DataTypeManager.DefaultDataTypes.BLOB,
DataTypeManager.DefaultDataTypes.OBJECT,
DataTypeManager.DefaultDataTypes.XML,
DataTypeManager.DefaultDataTypes.STRING,
DataTypeManager.DefaultDataTypes.VARBINARY,
DataTypeManager.DefaultDataTypes.BIG_INTEGER));
private static final HashSet<String> PRECISION_DATATYPES = new HashSet<String>(
Arrays.asList(DataTypeManager.DefaultDataTypes.BIG_DECIMAL));
protected StringBuilder buffer = new StringBuilder();
private boolean includeTables = true;
private boolean includeProcedures = true;
private boolean includeFunctions = true;
private Pattern filter;
private Map<String, String> prefixMap;
protected boolean usePrefixes = true;
protected boolean createNS = true;
private final static Map<String, String> BUILTIN_PREFIXES = new HashMap<String, String>();
static {
for (Map.Entry<String, String> entry : MetadataFactory.BUILTIN_NAMESPACES.entrySet()) {
BUILTIN_PREFIXES.put(entry.getValue(), entry.getKey());
}
}
public static String getDDLString(Schema schema, EnumSet<SchemaObjectType> types, String regexPattern) {
DDLStringVisitor visitor = new DDLStringVisitor(types, regexPattern);
visitor.visit(schema);
return visitor.toString();
}
public static String getDDLString(Database database) {
DDLStringVisitor visitor = new DDLStringVisitor(null, null);
visitor.visit(database);
return visitor.toString();
}
public DDLStringVisitor(EnumSet<SchemaObjectType> types, String regexPattern) {
if (types != null) {
this.includeTables = types.contains(SchemaObjectType.TABLES);
this.includeProcedures = types.contains(SchemaObjectType.PROCEDURES);
this.includeFunctions = types.contains(SchemaObjectType.FUNCTIONS);
}
if (regexPattern != null) {
this.filter = Pattern.compile(regexPattern);
}
}
public void visit(Database database) {
append(NEWLINE);
append("/*").append(NEWLINE);
append("###########################################").append(NEWLINE);
append("# START DATABASE ").append(database.getName()).append(NEWLINE);
append("###########################################").append(NEWLINE);
append("*/").append(NEWLINE);
append(CREATE).append(SPACE).append(DATABASE).append(SPACE)
.append(SQLStringVisitor.escapeSinglePart(database.getName())).append(SPACE).append(VERSION)
.append(SPACE).append(new Constant(database.getVersion()));
appendOptions(database);
append(SEMICOLON);
append(NEWLINE);
append(USE).append(SPACE).append(DATABASE).append(SPACE);
append(SQLStringVisitor.escapeSinglePart(database.getName())).append(SPACE);
append(VERSION).append(SPACE).append(new Constant(database.getVersion()));
append(SEMICOLON);
append(NEWLINE);
boolean outputDt = false;
for (Datatype dt : database.getMetadataStore().getDatatypes().values()) {
if (dt.getType() == Datatype.Type.Domain) {
outputDt = true;
break;
}
}
if (outputDt) {
append(NEWLINE);
append("--############ Domains ############");
append(NEWLINE);
for (Datatype dt : database.getMetadataStore().getDatatypes().values()) {
if (dt.isBuiltin()) {
continue;
}
visit(dt);
append(NEWLINE);
append(NEWLINE);
}
}
if (!database.getDataWrappers().isEmpty()){
append(NEWLINE);
append("--############ Translators ############");
append(NEWLINE);
for (DataWrapper dw : database.getDataWrappers()) {
visit(dw);
append(NEWLINE);
append(NEWLINE);
}
}
if (!database.getServers().isEmpty()){
append(NEWLINE);
append("--############ Servers ############");
append(NEWLINE);
for (Server server : database.getServers()) {
visit(server);
append(NEWLINE);
append(NEWLINE);
}
}
if (!database.getSchemas().isEmpty()) {
append(NEWLINE);
append("--############ Schemas ############");
append(NEWLINE);
for (Schema schema : database.getSchemas()) {
append(CREATE);
if (!schema.isPhysical()) {
append(SPACE).append(VIRTUAL);
}
append(SPACE).append(SCHEMA).append(SPACE).append(SQLStringVisitor.escapeSinglePart(schema.getName()));
if (!schema.getServers().isEmpty()) {
append(SPACE).append(SERVER);
boolean first = true;
for (Server s:schema.getServers()) {
if (first) {
first = false;
} else {
append(COMMA);
}
append(SPACE).append(SQLStringVisitor.escapeSinglePart(s.getName()));
}
}
appendOptions(schema);
append(SEMICOLON);
append(NEWLINE);
append(NEWLINE);
createdSchmea(schema);
}
}
if (!database.getRoles().isEmpty()){
append(NEWLINE);
append("--############ Roles ############");
append(NEWLINE);
for (Role role:database.getRoles()) {
visit(role);
append(NEWLINE);
append(NEWLINE);
}
}
for (Schema schema : database.getSchemas()) {
append(NEWLINE);
append("--############ Schema:").append(schema.getName()).append(" ############");
append(NEWLINE);
append(SET).append(SPACE).append(SCHEMA).append(SPACE);
append(SQLStringVisitor.escapeSinglePart(schema.getName())).append(SEMICOLON);
append(NEWLINE);
append(NEWLINE);
visit(schema);
}
if (!database.getRoles().isEmpty()){
append(NEWLINE);
append("--############ Grants ############");
append(NEWLINE);
for (Grant grant:database.getGrants()) {
visit(grant);
append(NEWLINE);
}
}
append(NEWLINE);
append("/*").append(NEWLINE);
append("###########################################").append(NEWLINE);
append("# END DATABASE ").append(database.getName()).append(NEWLINE);
append("###########################################").append(NEWLINE);
append("*/").append(NEWLINE);
append(NEWLINE);
}
protected void createdSchmea(Schema schema) {
}
private void visit(Datatype dt) {
append(CREATE).append(SPACE).append(DOMAIN).append(SPACE);
append(SQLStringVisitor.escapeSinglePart(dt.getName())).append(SPACE).append(AS).append(SPACE);
String runtimeTypeName = dt.getBasetypeName();
append(runtimeTypeName);
Datatype base = SystemMetadata.getInstance().getRuntimeTypeMap().get(runtimeTypeName);
if (LENGTH_DATATYPES.contains(runtimeTypeName)) {
if (dt.getLength() != base.getLength()) {
append(LPAREN).append(dt.getLength()).append(RPAREN);
}
} else if (PRECISION_DATATYPES.contains(runtimeTypeName)
&& (dt.getPrecision() != base.getPrecision() || dt.getScale() != base.getScale())) {
append(LPAREN).append(dt.getPrecision());
if (dt.getScale() != 0) {
append(COMMA).append(dt.getScale());
}
append(RPAREN);
}
if (dt.getNullType() == NullType.No_Nulls) {
append(SPACE).append(NOT_NULL);
}
append(SEMICOLON);
}
private void visit(Grant grant) {
for (Permission permission : grant.getPermissions()) {
if (permission.getResourceType() == ResourceType.DATABASE) {
for (Privilege p : permission.getPrivileges()) {
appendGrant(grant, permission, EnumSet.of(p), false);
}
for (Privilege p : permission.getRevokePrivileges()) {
appendGrant(grant, permission, EnumSet.of(p), true);
}
continue;
}
if (!permission.getPrivileges().isEmpty() || permission.getMask() != null || permission.getCondition() != null) {
appendGrant(grant, permission, permission.getPrivileges(), false);
}
if (!permission.getRevokePrivileges().isEmpty()) {
appendGrant(grant, permission, permission.getRevokePrivileges(), true);
}
}
}
private void appendGrant(Grant grant, Permission permission, EnumSet<Privilege> privileges, boolean revoke) {
append(revoke?REVOKE:GRANT);
boolean first = true;
for (Privilege allowance:privileges) {
if (first) {
first = false;
append(SPACE);
} else {
append(COMMA);
}
append(allowance);
}
if (permission.getResourceType() != ResourceType.DATABASE) {
append(SPACE).append(ON).append(SPACE).append(permission.getResourceType());
}
if (permission.getResourceName() != null) {
append(SPACE).append(SQLStringVisitor.escapeSinglePart(permission.getResourceName()));
}
if (!revoke && permission.getMask() != null) {
append(SPACE).append(MASK);
if (permission.getMaskOrder() != null && permission.getMaskOrder() != -1) {
append(SPACE).append(ORDER).append(SPACE).append(permission.getMaskOrder());
}
append(SPACE).append(new Constant(permission.getMask()));
}
if (!revoke && permission.getCondition() != null) {
append(SPACE).append(CONDITION);
if (!revoke) {
if (permission.isConditionAConstraint() != null && permission.isConditionAConstraint()) {
append(SPACE).append(CONSTRAINT);
}
append(SPACE).append(new Constant(permission.getCondition()));
}
}
append(SPACE).append(revoke?FROM:TO).append(SPACE).append(grant.getRole());
append(SEMICOLON).append(NEWLINE);
}
private void visit(Role role) {
append(CREATE).append(SPACE).append(ROLE.toUpperCase()).append(SPACE)
.append(SQLStringVisitor.escapeSinglePart(role.getName()));
if (role.getJassRoles() != null && !role.getJassRoles().isEmpty()) {
append(SPACE).append(WITH).append(SPACE).append(JAAS).append(SPACE).append(ROLE);
for (String str:role.getJassRoles()) {
append(SPACE).append(SQLStringVisitor.escapeSinglePart(str));
}
}
if (role.isAnyAuthenticated()) {
append(SPACE).append(WITH).append(SPACE).append(ANY).append(SPACE).append(AUTHENTICATED);
}
append(SEMICOLON);
}
private void visit(DataWrapper dw) {
append(CREATE).append(SPACE).append(FOREIGN).append(SPACE).append(DATA).append(SPACE).append(WRAPPER)
.append(SPACE);
append(SQLStringVisitor.escapeSinglePart(dw.getName()));
if (dw.getType() != null) {
append(SPACE).append(TYPE).append(SPACE).append(SQLStringVisitor.escapeSinglePart(dw.getType()));
}
appendOptions(dw);
append(SEMICOLON);
}
private void visit(Server server) {
append(CREATE).append(SPACE).append(SERVER).append(SPACE)
.append(SQLStringVisitor.escapeSinglePart(server.getName()));
if (!server.isVirtual()) {
append(SPACE).append(TYPE).append(SPACE).append(new Constant(server.getType()));
}
if (server.getVersion() != null) {
append(SPACE).append(VERSION).append(SPACE).append(new Constant(server.getVersion()));
}
append(SPACE).append(FOREIGN).append(SPACE).append(DATA).append(SPACE).append(WRAPPER).append(SPACE);
append(SQLStringVisitor.escapeSinglePart(server.getDataWrapper()));
appendOptions(server);
append(SEMICOLON);
}
protected void visit(Schema schema) {
boolean first = true;
for (AbstractMetadataRecord record : schema.getResolvingOrder()) {
String generated = record.getProperty(GENERATED, false);
if (generated != null && Boolean.valueOf(generated)) {
continue;
}
if (record instanceof Table) {
if (!this.includeTables) {
continue;
}
} else if (record instanceof Procedure) {
if (!this.includeProcedures) {
continue;
}
} else if (record instanceof FunctionMethod) {
if (!this.includeFunctions) {
continue;
}
}
if (first) {
first = false;
}
else {
append(NEWLINE);
append(NEWLINE);
}
if (record instanceof Table) {
visit((Table)record);
} else if (record instanceof Procedure) {
visit((Procedure)record);
} else if (record instanceof FunctionMethod) {
visit((FunctionMethod)record);
}
}
}
private void visit(Table table) {
if (this.filter != null && !filter.matcher(table.getName()).matches()) {
return;
}
append(CREATE).append(SPACE);
if (table.isPhysical()) {
append(FOREIGN_TABLE);
}
else {
if (table.getTableType() == Table.Type.TemporaryTable) {
append(GLOBAL).append(SPACE).append(TEMPORARY).append(SPACE).append(TABLE);
} else {
append(VIEW);
}
}
append(SPACE);
String name = addTableBody(table);
if (table.getTableType() != Table.Type.TemporaryTable) {
if (table.isVirtual()) {
append(NEWLINE).append(SQLConstants.Reserved.AS).append(NEWLINE).append(table.getSelectTransformation());
}
append(SQLConstants.Tokens.SEMICOLON);
if (table.isInsertPlanEnabled()) {
buildTrigger(name, null, INSERT, table.getInsertPlan(), false);
}
if (table.isUpdatePlanEnabled()) {
buildTrigger(name, null, UPDATE, table.getUpdatePlan(), false);
}
if (table.isDeletePlanEnabled()) {
buildTrigger(name, null, DELETE, table.getDeletePlan(), false);
}
for (Trigger tr : table.getTriggers().values()) {
String generated = tr.getProperty(GENERATED, false);
if (generated == null || !Boolean.valueOf(generated)) {
buildTrigger(name, tr.getName(), tr.getEvent().name(), tr.getPlan(), tr.isAfter());
}
}
} else {
append(SQLConstants.Tokens.SEMICOLON);
}
}
public String addTableBody(Table table) {
String name = SQLStringVisitor.escapeSinglePart(table.getName());
append(name);
if (table.getColumns() != null) {
append(SPACE);
append(LPAREN);
boolean first = true;
for (Column c:table.getColumns()) {
if (first) {
first = false;
}
else {
append(COMMA);
}
visit(table, c);
}
buildContraints(table);
append(NEWLINE);
append(RPAREN);
}
// options
String options = buildTableOptions(table);
if (!options.isEmpty()) {
append(SPACE).append(OPTIONS).append(SPACE).append(LPAREN).append(options).append(RPAREN);
}
return name;
}
protected DDLStringVisitor append(Object o) {
buffer.append(o);
return this;
}
private void buildTrigger(String name, String trigger_name, String type, String plan, boolean isAfter) {
append(NEWLINE);
append(NEWLINE);
append(SQLConstants.Reserved.CREATE).append(SPACE).append(TRIGGER).append(SPACE);
if (trigger_name != null) {
append(SQLStringVisitor.escapeSinglePart(trigger_name)).append(SPACE);
}
append(SQLConstants.Reserved.ON).append(SPACE).append(name).append(SPACE);
if (isAfter) {
append(AFTER);
} else {
append(INSTEAD_OF);
}
append(SPACE).append(type).append(SPACE).append(SQLConstants.Reserved.AS).append(NEWLINE);
append(plan);
append(SQLConstants.Tokens.SEMICOLON);
}
private String buildTableOptions(Table table) {
StringBuilder options = new StringBuilder();
addCommonOptions(options, table);
if (table.isMaterialized()) {
addOption(options, MATERIALIZED, table.isMaterialized());
if (table.getMaterializedTable() != null) {
addOption(options, MATERIALIZED_TABLE, table.getMaterializedTable().getName());
}
}
if (table.supportsUpdate()) {
addOption(options, UPDATABLE, table.supportsUpdate());
}
if (table.getCardinality() != -1) {
if (table.getCardinality() != table.getCardinalityAsFloat()) {
addOption(options, CARDINALITY, (long)table.getCardinalityAsFloat());
} else {
addOption(options, CARDINALITY, table.getCardinality());
}
}
if (!table.getProperties().isEmpty()) {
for (String key:table.getProperties().keySet()) {
addOption(options, key, table.getProperty(key, false));
}
}
return options.toString();
}
private void addCommonOptions(StringBuilder sb, AbstractMetadataRecord record) {
if (record.isUUIDSet() && record.getUUID() != null && !record.getUUID().startsWith("tid:")) { //$NON-NLS-1$
addOption(sb, UUID, record.getUUID());
}
if (record.getAnnotation() != null) {
addOption(sb, ANNOTATION, record.getAnnotation());
}
if (record.getNameInSource() != null) {
addOption(sb, NAMEINSOURCE, record.getNameInSource());
}
}
private void buildContraints(Table table) {
addConstraints(table.getAccessPatterns(), "AP", ACCESSPATTERN); //$NON-NLS-1$
KeyRecord pk = table.getPrimaryKey();
if (pk != null) {
addConstraint("PK", PRIMARY_KEY, pk, true); //$NON-NLS-1$
}
addConstraints(table.getUniqueKeys(), UNIQUE, UNIQUE);
addConstraints(table.getIndexes(), INDEX, INDEX);
addConstraints(table.getFunctionBasedIndexes(), INDEX, INDEX);
for (int i = 0; i < table.getForeignKeys().size(); i++) {
ForeignKey key = table.getForeignKeys().get(i);
addConstraint("FK" + i, FOREIGN_KEY, key, false); //$NON-NLS-1$
append(SPACE).append(REFERENCES);
if (key.getReferenceKey() != null) {
if (key.getReferenceKey().getParent().getParent().equals(key.getParent().getParent())) {
append(SPACE).append(new GroupSymbol(key.getReferenceKey().getParent().getName()));
} else {
append(SPACE).append(new GroupSymbol(key.getReferenceKey().getParent().getFullName()));
}
} else if (key.getReferenceTableName() != null) {
append(SPACE).append(new GroupSymbol(key.getReferenceTableName()));
}
append(SPACE);
addNames(key.getReferenceColumns());
appendOptions(key);
}
}
private void addConstraints(List<KeyRecord> constraints, String defaultName, String type) {
for (int i = 0; i < constraints.size(); i++) {
KeyRecord constraint = constraints.get(i);
addConstraint(defaultName + i, type, constraint, true);
}
}
private void addConstraint(String defaultName, String type,
KeyRecord constraint, boolean addOptions) {
append(COMMA).append(NEWLINE).append(TAB);
boolean nameMatches = defaultName.equals(constraint.getName());
if (!nameMatches) {
append(CONSTRAINT).append(SPACE).append(SQLStringVisitor.escapeSinglePart(constraint.getName())).append(SPACE);
}
append(type);
addColumns(constraint.getColumns(), false);
if (addOptions) {
appendOptions(constraint);
}
}
private void addColumns(List<Column> columns, boolean includeType) {
append(LPAREN);
boolean first = true;
for (Column c:columns) {
if (first) {
first = false;
}
else {
append(COMMA).append(SPACE);
}
if (includeType) {
appendColumn(c, true, includeType);
appendColumnOptions(c);
} else if (c.getParent() instanceof KeyRecord) {
//function based column
append(c.getNameInSource());
} else {
append(SQLStringVisitor.escapeSinglePart(c.getName()));
}
}
append(RPAREN);
}
private void addNames(List<String> columns) {
if (columns != null) {
append(LPAREN);
boolean first = true;
for (String c:columns) {
if (first) {
first = false;
}
else {
append(COMMA).append(SPACE);
}
append(SQLStringVisitor.escapeSinglePart(c));
}
append(RPAREN);
}
}
private void visit(Table table, Column column) {
append(NEWLINE).append(TAB);
if (table.getTableType() == Table.Type.TemporaryTable && column.isAutoIncremented() && column.getNullType() == NullType.No_Nulls && column.getJavaType() == DataTypeManager.DefaultDataClasses.INTEGER) {
append(SQLStringVisitor.escapeSinglePart(column.getName()));
append(SPACE);
append(SERIAL);
} else {
appendColumn(column, true, true);
if (column.isAutoIncremented()) {
append(SPACE).append(AUTO_INCREMENT);
}
}
appendDefault(column);
// options
appendColumnOptions(column);
}
private void appendDefault(BaseColumn column) {
if (column.getDefaultValue() != null) {
append(SPACE).append(DEFAULT).append(SPACE);
if (BaseColumn.EXPRESSION_DEFAULT.equalsIgnoreCase(column.getProperty(BaseColumn.DEFAULT_HANDLING, false))) {
append(column.getDefaultValue());
} else {
append(new Constant(column.getDefaultValue()));
}
}
}
private void appendColumn(BaseColumn column, boolean includeName, boolean includeType) {
if (includeName) {
append(SQLStringVisitor.escapeSinglePart(column.getName()));
}
if (includeType) {
Datatype datatype = column.getDatatype();
String runtimeTypeName = column.getRuntimeType();
boolean domain = false;
if (datatype != null) {
runtimeTypeName = datatype.getRuntimeTypeName();
domain = datatype.getType() == Datatype.Type.Domain;
}
if (includeName) {
append(SPACE);
}
if (domain) {
append(datatype.getName());
} else {
append(runtimeTypeName);
if (LENGTH_DATATYPES.contains(runtimeTypeName)) {
if (column.getLength() != 0 && (datatype == null || column.getLength() != datatype.getLength())) {
append(LPAREN).append(column.getLength()).append(RPAREN);
}
} else if (PRECISION_DATATYPES.contains(runtimeTypeName)
&& !column.isDefaultPrecisionScale()) {
append(LPAREN).append(column.getPrecision());
if (column.getScale() != 0) {
append(COMMA).append(column.getScale());
}
append(RPAREN);
}
}
if (datatype != null) {
for (int dims = column.getArrayDimensions(); dims > 0; dims--) {
append(Tokens.LSBRACE).append(Tokens.RSBRACE);
}
}
if (column.getNullType() == NullType.No_Nulls && (!domain || datatype.getNullType() != NullType.No_Nulls)) {
append(SPACE).append(NOT_NULL);
}
}
}
private void appendColumnOptions(BaseColumn column) {
StringBuilder options = new StringBuilder();
addCommonOptions(options, column);
if (!column.getDatatype().isBuiltin() && column.getDatatype().getType() != Datatype.Type.Domain) {
//an enterprise type
addOption(options, UDT, column.getDatatype().getName() + "("+column.getLength()+ ", " +column.getPrecision()+", " + column.getScale()+ ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
if (column.getDatatype().getRadix() != 0 && column.getRadix() != column.getDatatype().getRadix()) {
addOption(options, RADIX, column.getRadix());
}
buildColumnOptions(column, options);
if (options.length() != 0) {
append(SPACE).append(OPTIONS).append(SPACE).append(LPAREN).append(options).append(RPAREN);
}
}
private void buildColumnOptions(BaseColumn baseColumn,
StringBuilder options) {
if (baseColumn instanceof Column) {
Column column = (Column)baseColumn;
if (!column.isSelectable()) {
addOption(options, SELECTABLE, column.isSelectable());
}
// if table is already updatable, then columns are implicitly updatable.
if (!column.isUpdatable() && column.getParent() instanceof Table && ((Table)column.getParent()).supportsUpdate()) {
addOption(options, UPDATABLE, column.isUpdatable());
}
if (column.isCurrency()) {
addOption(options, CURRENCY, column.isCurrency());
}
// only record if not default
if (!column.isCaseSensitive() && column.getDatatype().isCaseSensitive()) {
addOption(options, CASE_SENSITIVE, column.isCaseSensitive());
}
if (!column.isSigned() && column.getDatatype().isSigned()) {
addOption(options, SIGNED, column.isSigned());
}
if (column.isFixedLength()) {
addOption(options, FIXED_LENGTH, column.isFixedLength());
}
// length and octet length should be same. so this should be never be true.
//TODO - this is not quite valid since we are dealing with length representing chars in UTF-16, then there should be twice the bytes
if (column.getCharOctetLength() != 0 && column.getLength() != column.getCharOctetLength()) {
addOption(options, CHAR_OCTET_LENGTH, column.getCharOctetLength());
}
// by default the search type is default data type search, so avoid it.
if (column.getSearchType() != null && (!column.getSearchType().equals(column.getDatatype().getSearchType()) || column.isSearchTypeSet())) {
addOption(options, SEARCHABLE, column.getSearchType().name());
}
if (column.getMinimumValue() != null) {
addOption(options, MIN_VALUE, column.getMinimumValue());
}
if (column.getMaximumValue() != null) {
addOption(options, MAX_VALUE, column.getMaximumValue());
}
if (column.getNullValues() != -1) {
addOption(options, NULL_VALUE_COUNT, column.getNullValues());
}
if (column.getDistinctValues() != -1) {
addOption(options, DISTINCT_VALUES, column.getDistinctValues());
}
}
if (baseColumn.getNativeType() != null) {
addOption(options, NATIVE_TYPE, baseColumn.getNativeType());
}
buildOptions(baseColumn, options);
}
private void appendOptions(AbstractMetadataRecord record) {
StringBuilder options = new StringBuilder();
addCommonOptions(options, record);
buildOptions(record, options);
if (options.length() != 0) {
append(SPACE).append(OPTIONS).append(SPACE).append(LPAREN).append(options).append(RPAREN);
}
}
private void buildOptions(AbstractMetadataRecord record, StringBuilder options) {
if (!record.getProperties().isEmpty()) {
for (Map.Entry<String, String> entry:record.getProperties().entrySet()) {
addOption(options, entry.getKey(), entry.getValue());
}
}
}
private void addOption(StringBuilder sb, String key, Object value) {
if (sb.length() != 0) {
sb.append(COMMA).append(SPACE);
}
if (value != null) {
value = new Constant(value);
} else {
value = Constant.NULL_CONSTANT;
}
if (key != null && key.length() > 2 && key.charAt(0) == '{') {
String origKey = key;
int index = key.indexOf('}');
if (index > 1) {
String uri = key.substring(1, index);
key = key.substring(index + 1, key.length());
String prefix = BUILTIN_PREFIXES.get(uri);
if ((prefix == null && usePrefixes) || createNS) {
if (prefixMap == null) {
prefixMap = new LinkedHashMap<String, String>();
} else {
prefix = this.prefixMap.get(uri);
}
if (prefix == null) {
prefix = "n"+this.prefixMap.size(); //$NON-NLS-1$
}
this.prefixMap.put(uri, prefix);
}
if (prefix != null) {
key = prefix + ":" + key; //$NON-NLS-1$
} else {
key = origKey;
}
}
}
sb.append(SQLStringVisitor.escapeSinglePart(key)).append(SPACE).append(value);
}
private void visit(Procedure procedure) {
if (this.filter != null && !filter.matcher(procedure.getName()).matches()) {
return;
}
append(CREATE).append(SPACE);
if (procedure.isVirtual()) {
append(VIRTUAL);
}
else {
append(FOREIGN);
}
append(SPACE).append(procedure.isFunction()?FUNCTION:PROCEDURE).append(SPACE).append(SQLStringVisitor.escapeSinglePart(procedure.getName()));
append(LPAREN);
boolean first = true;
for (ProcedureParameter pp:procedure.getParameters()) {
if (first) {
first = false;
}
else {
append(COMMA).append(SPACE);
}
visit(pp);
}
append(RPAREN);
if (procedure.getResultSet() != null) {
append(SPACE).append(RETURNS);
appendOptions(procedure.getResultSet());
append(SPACE).append(TABLE).append(SPACE);
addColumns(procedure.getResultSet().getColumns(), true);
}
/* The parser treats the RETURN clause as optional for a procedure if using the RESULT param
for (ProcedureParameter pp: procedure.getParameters()) {
if (pp.getType().equals(Type.ReturnValue)) {
append(SPACE).append(RETURNS).append(SPACE);
appendColumn(buffer, pp, false, true);
break;
}
}*/
//options
String options = buildProcedureOptions(procedure);
if (!options.isEmpty()) {
append(NEWLINE).append(OPTIONS).append(SPACE).append(LPAREN).append(options).append(RPAREN);
}
//block
if (procedure.isVirtual()) {
append(NEWLINE).append(SQLConstants.Reserved.AS).append(NEWLINE);
String plan = procedure.getQueryPlan();
append(plan);
}
append(SEMICOLON);
}
private String buildProcedureOptions(Procedure procedure) {
StringBuilder options = new StringBuilder();
addCommonOptions(options, procedure);
if (procedure.getUpdateCount() != Procedure.AUTO_UPDATECOUNT) {
addOption(options, UPDATECOUNT, procedure.getUpdateCount());
}
if (!procedure.getProperties().isEmpty()) {
for (String key:procedure.getProperties().keySet()) {
addOption(options, key, procedure.getProperty(key, false));
}
}
return options.toString();
}
private void visit(ProcedureParameter param) {
Type type = param.getType();
String typeStr = null;
switch (type) {
case InOut:
typeStr = INOUT;
break;
case ReturnValue:
case Out:
typeStr = OUT;
break;
case In:
if (param.isVarArg()) {
typeStr = NonReserved.VARIADIC;
} else {
typeStr = IN;
}
break;
}
append(typeStr).append(SPACE);
appendColumn(param, true, true);
if (type == Type.ReturnValue) {
append(SPACE).append(NonReserved.RESULT);
}
appendDefault(param);
appendColumnOptions(param);
}
private void visit(FunctionMethod function) {
if (this.filter != null && !filter.matcher(function.getName()).matches()) {
return;
}
append(CREATE).append(SPACE);
if (function.getPushdown().equals(FunctionMethod.PushDown.MUST_PUSHDOWN)) {
append(FOREIGN);
}
else {
append(VIRTUAL);
}
append(SPACE).append(FUNCTION).append(SPACE).append(SQLStringVisitor.escapeSinglePart(function.getName()));
append(LPAREN);
boolean first = true;
for (FunctionParameter fp:function.getInputParameters()) {
if (first) {
first = false;
}
else {
append(COMMA).append(SPACE);
}
visit(fp);
}
append(RPAREN);
append(SPACE).append(RETURNS);
appendOptions(function.getOutputParameter());
append(SPACE);
append(function.getOutputParameter().getType());
//options
String options = buildFunctionOptions(function);
if (!options.isEmpty()) {
append(NEWLINE).append(OPTIONS).append(SPACE).append(LPAREN).append(options).append(RPAREN);
}
/*if (function.getDefinition() != null) {
append(NEWLINE).append(SQLConstants.Reserved.AS).append(NEWLINE);
append(function.getDefinition());
}*/
append(SQLConstants.Tokens.SEMICOLON);
}
private String buildFunctionOptions(FunctionMethod function) {
StringBuilder options = new StringBuilder();
addCommonOptions(options, function);
if (function.getCategory() != null) {
addOption(options, CATEGORY, function.getCategory());
}
if (!function.getDeterminism().equals(Determinism.DETERMINISTIC)) {
addOption(options, DETERMINISM, function.getDeterminism().name());
}
if (function.getInvocationClass() != null) {
addOption(options, JAVA_CLASS, function.getInvocationClass());
}
if (function.getInvocationMethod() != null) {
addOption(options, JAVA_METHOD, function.getInvocationMethod());
}
if (!function.getProperties().isEmpty()) {
for (String key:function.getProperties().keySet()) {
addOption(options, key, function.getProperty(key, false));
}
}
return options.toString();
}
private void visit(FunctionParameter param) {
if (param.isVarArg()) {
append(NonReserved.VARIADIC).append(SPACE);
}
appendColumn(param, true, true);
}
@Override
public String toString() {
if (this.prefixMap != null) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : this.prefixMap.entrySet()) {
sb.append("SET NAMESPACE '").append(StringUtil.replaceAll(entry.getKey(), "'", "''")).append('\'') //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
.append(" AS " ).append(SQLStringVisitor.escapeSinglePart(entry.getValue())).append(";\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
return sb.append("\n").toString() + buffer.toString(); //$NON-NLS-1$
}
return buffer.toString();
}
public static String getDomainDDLString(Database database) {
DDLStringVisitor visitor = new DDLStringVisitor(null, null);
for (Datatype dt : database.getMetadataStore().getDatatypes().values()) {
if (dt.getType() != Datatype.Type.Domain) {
continue;
}
visitor.visit(dt);
visitor.append(SPACE);
}
return visitor.toString();
}
}