blob: fb1cb09414c6c6f6edefad77f052ae60dd4f889c [file] [log] [blame]
package org.apache.ddlutils.platform.postgresql;
/*
* 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.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
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.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 PostgreSql database.
*
* @version $Revision: $
*/
public class PostgreSqlModelReader extends JdbcModelReader
{
/**
* Creates a new model reader for PostgreSql databases.
*
* @param platform The platform that this model reader belongs to
*/
public PostgreSqlModelReader(Platform platform)
{
super(platform);
setDefaultCatalogPattern(null);
setDefaultSchemaPattern(null);
setDefaultTablePattern(null);
}
/**
* {@inheritDoc}
*/
protected Table readTable(DatabaseMetaDataWrapper metaData, Map values) throws SQLException
{
Table table = super.readTable(metaData, values);
if (table != null)
{
// PostgreSQL also returns unique indexes for pk and non-pk auto-increment columns
// which are of the form "[table]_[column]_key"
HashMap uniquesByName = new HashMap();
for (int indexIdx = 0; indexIdx < table.getIndexCount(); indexIdx++)
{
Index index = table.getIndex(indexIdx);
if (index.isUnique() && (index.getName() != null))
{
uniquesByName.put(index.getName(), index);
}
}
for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++)
{
Column column = table.getColumn(columnIdx);
if (column.isAutoIncrement())
{
String indexName = table.getName() + "_" + column.getName() + "_key";
if (uniquesByName.containsKey(indexName))
{
table.removeIndex((Index)uniquesByName.get(indexName));
uniquesByName.remove(indexName);
}
}
}
}
return table;
}
/**
* {@inheritDoc}
*/
protected Column readColumn(DatabaseMetaDataWrapper metaData, Map values) throws SQLException
{
Column column = super.readColumn(metaData, values);
if (column.getSize() != null)
{
if (column.getSizeAsInt() <= 0)
{
column.setSize(null);
// PostgreSQL reports BYTEA and TEXT as BINARY(-1) and VARCHAR(-1) respectively
// Since we cannot currently use the Blob/Clob interface with BYTEA, we instead
// map them to LONGVARBINARY/LONGVARCHAR
if (column.getTypeCode() == Types.BINARY)
{
column.setTypeCode(Types.LONGVARBINARY);
}
else if (column.getTypeCode() == Types.VARCHAR)
{
column.setTypeCode(Types.LONGVARCHAR);
}
}
// fix issue DDLUTILS-165 as postgresql-8.2-504-jdbc3.jar seems to return Integer.MAX_VALUE
// on columns defined as TEXT.
else if (column.getSizeAsInt() == Integer.MAX_VALUE)
{
column.setSize(null);
if (column.getTypeCode() == Types.VARCHAR)
{
column.setTypeCode(Types.LONGVARCHAR);
}
else if (column.getTypeCode() == Types.BINARY)
{
column.setTypeCode(Types.LONGVARBINARY);
}
}
}
String defaultValue = column.getDefaultValue();
if ((defaultValue != null) && (defaultValue.length() > 0))
{
// If the default value looks like "nextval('ROUNDTRIP_VALUE_seq'::text)"
// then it is an auto-increment column
if (defaultValue.startsWith("nextval("))
{
column.setAutoIncrement(true);
defaultValue = null;
}
else
{
// PostgreSQL returns default values in the forms "-9000000000000000000::bigint" or
// "'some value'::character varying" or "'2000-01-01'::date"
switch (column.getTypeCode())
{
case Types.INTEGER:
case Types.BIGINT:
case Types.DECIMAL:
case Types.NUMERIC:
defaultValue = extractUndelimitedDefaultValue(defaultValue);
break;
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
defaultValue = extractDelimitedDefaultValue(defaultValue);
break;
}
if (TypeMap.isTextType(column.getTypeCode()))
{
// We assume escaping via double quote (see also the backslash_quote setting:
// http://www.postgresql.org/docs/7.4/interactive/runtime-config.html#RUNTIME-CONFIG-COMPATIBLE)
defaultValue = unescape(defaultValue, "'", "''");
}
}
column.setDefaultValue(defaultValue);
}
return column;
}
/**
* Extractes the default value from a default value spec of the form
* "'some value'::character varying" or "'2000-01-01'::date".
*
* @param defaultValue The default value spec
* @return The default value
*/
private String extractDelimitedDefaultValue(String defaultValue)
{
if (defaultValue.startsWith("'"))
{
int valueEnd = defaultValue.indexOf("'::");
if (valueEnd > 0)
{
return defaultValue.substring("'".length(), valueEnd);
}
}
return defaultValue;
}
/**
* Extractes the default value from a default value spec of the form
* "-9000000000000000000::bigint".
*
* @param defaultValue The default value spec
* @return The default value
*/
private String extractUndelimitedDefaultValue(String defaultValue)
{
int valueEnd = defaultValue.indexOf("::");
if (valueEnd > 0)
{
return defaultValue.substring(0, valueEnd);
}
else
{
return defaultValue;
}
}
/**
* {@inheritDoc}
*/
protected boolean isInternalForeignKeyIndex(DatabaseMetaDataWrapper metaData, Table table, ForeignKey fk, Index index)
{
// PostgreSQL does not return an index for a foreign key
return false;
}
/**
* {@inheritDoc}
*/
protected boolean isInternalPrimaryKeyIndex(DatabaseMetaDataWrapper metaData, Table table, Index index)
{
// PostgreSql uses the form "[tablename]_pkey"
return (table.getName() + "_pkey").equals(index.getName());
}
}