blob: 4cf31224585878aae3b451b6885583d28dfbf121 [file] [log] [blame]
package org.apache.ddlutils.platform.sybase;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.apache.ddlutils.DdlUtilsException;
import org.apache.ddlutils.Platform;
import org.apache.ddlutils.model.Column;
import org.apache.ddlutils.model.ForeignKey;
import org.apache.ddlutils.model.Index;
import org.apache.ddlutils.model.Reference;
import org.apache.ddlutils.model.Table;
import org.apache.ddlutils.model.TypeMap;
import org.apache.ddlutils.platform.DatabaseMetaDataWrapper;
import org.apache.ddlutils.platform.JdbcModelReader;
/**
* Reads a database model from a Sybase database.
*
* @version $Revision: $
*/
public class SybaseModelReader extends JdbcModelReader
{
/** The regular expression pattern for the ISO dates. */
private Pattern _isoDatePattern;
/** The regular expression pattern for the ISO times. */
private Pattern _isoTimePattern;
/**
* Creates a new model reader for Sybase databases.
*
* @param platform The platform that this model reader belongs to
*/
public SybaseModelReader(Platform platform)
{
super(platform);
setDefaultCatalogPattern(null);
setDefaultSchemaPattern(null);
setDefaultTablePattern("%");
try
{
_isoDatePattern = Pattern.compile("'(\\d{4}\\-\\d{2}\\-\\d{2})'");
_isoTimePattern = Pattern.compile("'(\\d{2}:\\d{2}:\\d{2})'");
}
catch (PatternSyntaxException ex)
{
throw new DdlUtilsException(ex);
}
}
/**
* {@inheritDoc}
*/
protected Table readTable(DatabaseMetaDataWrapper metaData, Map values) throws SQLException
{
Table table = super.readTable(metaData, values);
if (table != null)
{
// Sybase does not return the auto-increment status via the database metadata
determineAutoIncrementFromResultSetMetaData(table, table.getColumns());
}
return table;
}
/**
* {@inheritDoc}
*/
protected Column readColumn(DatabaseMetaDataWrapper metaData, Map values) throws SQLException
{
Column column = super.readColumn(metaData, values);
if ((column.getTypeCode() == Types.DECIMAL) && (column.getSizeAsInt() == 19) && (column.getScale() == 0))
{
// Back-mapping to BIGINT
column.setTypeCode(Types.BIGINT);
}
else if (column.getDefaultValue() != null)
{
if (column.getTypeCode() == Types.TIMESTAMP)
{
// Sybase maintains the default values for DATE/TIME jdbc types, so we have to
// migrate the default value to TIMESTAMP
Matcher matcher = _isoDatePattern.matcher(column.getDefaultValue());
Timestamp timestamp = null;
if (matcher.matches())
{
timestamp = new Timestamp(Date.valueOf(matcher.group(1)).getTime());
}
else
{
matcher = _isoTimePattern.matcher(column.getDefaultValue());
if (matcher.matches())
{
timestamp = new Timestamp(Time.valueOf(matcher.group(1)).getTime());
}
}
if (timestamp != null)
{
column.setDefaultValue(timestamp.toString());
}
}
else if (TypeMap.isTextType(column.getTypeCode()))
{
column.setDefaultValue(unescape(column.getDefaultValue(), "'", "''"));
}
}
return column;
}
/**
* {@inheritDoc}
*/
protected void readIndex(DatabaseMetaDataWrapper metaData, Map values, Map knownIndices) throws SQLException
{
if (getPlatform().isDelimitedIdentifierModeOn())
{
String indexName = (String)values.get("INDEX_NAME");
// Sometimes, Sybase keeps the delimiter quotes around the index names
// when returning them in the metadata, so we strip them
if (indexName != null)
{
String delimiter = getPlatformInfo().getDelimiterToken();
if ((indexName != null) && indexName.startsWith(delimiter) && indexName.endsWith(delimiter))
{
indexName = indexName.substring(delimiter.length(), indexName.length() - delimiter.length());
values.put("INDEX_NAME", indexName);
}
}
}
super.readIndex(metaData, values, knownIndices);
}
/**
* {@inheritDoc}
*/
protected Collection readForeignKeys(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException
{
// Sybase (or jConnect) does not return the foreign key names, thus we have to
// read the foreign keys manually from the system tables
final String colQuery =
"SELECT refobjs.name, localtables.id, remotetables.name, remotetables.id," +
" refs.fokey1, refs.refkey1, refs.fokey2, refs.refkey2, refs.fokey3, refs.refkey3, refs.fokey4, refs.refkey4," +
" refs.fokey5, refs.refkey5, refs.fokey6, refs.refkey6, refs.fokey7, refs.refkey7, refs.fokey8, refs.refkey8," +
" refs.fokey9, refs.refkey9, refs.fokey10, refs.refkey10, refs.fokey11, refs.refkey11, refs.fokey12, refs.refkey12," +
" refs.fokey13, refs.refkey13, refs.fokey14, refs.refkey14, refs.fokey15, refs.refkey15, refs.fokey16, refs.refkey16," +
" FROM sysreferences refs, sysobjects refobjs, sysobjects localtables, sysobjects remotetables" +
" WHERE refobjs.type = 'RI' AND refs.constrid = refobjs.id AND" +
" localtables.type = 'U' AND refs.tableid = localtables.id AND localtables.name = ?" +
" AND remotetables.type = 'U' AND refs.reftabid = remotetables.id";
final String refObjQuery =
"SELECT name FROM syscolumns WHERE id = ? AND colid = ?";
PreparedStatement colStmt = null;
PreparedStatement refObjStmt = null;
ArrayList result = new ArrayList();
try
{
colStmt = getConnection().prepareStatement(colQuery);
refObjStmt = getConnection().prepareStatement(refObjQuery);
ResultSet fkRs = colStmt.executeQuery();
while (fkRs.next())
{
ForeignKey fk = new ForeignKey(fkRs.getString(1));
int localTableId = fkRs.getInt(2);
int remoteTableId = fkRs.getInt(4);
fk.setForeignTableName(fkRs.getString(3));
for (int idx = 0; idx < 16; idx++)
{
short fkColIdx = fkRs.getShort(5 + idx + idx);
short pkColIdx = fkRs.getShort(6 + idx + idx);
Reference ref = new Reference();
if (fkColIdx == 0)
{
break;
}
refObjStmt.setInt(1, localTableId);
refObjStmt.setShort(2, fkColIdx);
ResultSet colRs = refObjStmt.executeQuery();
if (colRs.next())
{
ref.setLocalColumnName(colRs.getString(1));
}
colRs.close();
refObjStmt.setInt(1, remoteTableId);
refObjStmt.setShort(2, pkColIdx);
colRs = refObjStmt.executeQuery();
if (colRs.next())
{
ref.setForeignColumnName(colRs.getString(1));
}
colRs.close();
fk.addReference(ref);
}
result.add(fk);
}
}
finally
{
closeStatement(colStmt);
closeStatement(refObjStmt);
}
return result;
}
/**
* {@inheritDoc}
*/
protected boolean isInternalPrimaryKeyIndex(DatabaseMetaDataWrapper metaData, Table table, Index index) throws SQLException
{
// We can simply check the sysindexes table where a specific flag is set for pk indexes
final String query = "SELECT name = sysindexes.name FROM sysindexes, sysobjects WHERE sysobjects.name = ? " +
"AND sysindexes.name = ? AND sysobjects.id = sysindexes.id AND (sysindexes.status & 2048) > 0";
PreparedStatement stmt = null;
try
{
stmt = getConnection().prepareStatement(query);
stmt.setString(1, table.getName());
stmt.setString(2, index.getName());
ResultSet rs = stmt.executeQuery();
return rs.next();
}
finally
{
closeStatement(stmt);
}
}
}