/*******************************************************************************
*
* Copyright 2010 Alexandru Craciun, and individual contributors as indicated
* by the @authors tag.
*
* This 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 3 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
******************************************************************************/
package org.netxilia.spi.impl.storage.db.ddl;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.netxilia.spi.impl.storage.db.ddl.schema.DbColumn;
import org.netxilia.spi.impl.storage.db.ddl.schema.DbDataType;
import org.netxilia.spi.impl.storage.db.ddl.schema.DbSchema;
import org.netxilia.spi.impl.storage.db.ddl.schema.DbTable;
/**
* Reader for DB schema.
*
* @author catac
*/
public class DDLReader {
private final static Logger log = Logger.getLogger(DDLReader.class);
private final DataSource dataSource;
private String dbDialect;
public DDLReader(DataSource dataSource) {
this(dataSource, null);
}
public DDLReader(DataSource dataSource, String forceDbDialect) {
this.dataSource = dataSource;
this.dbDialect = forceDbDialect;
}
public void setForceDbDialect(String forceDbDialect) {
this.dbDialect = forceDbDialect;
}
/** Retrieve the forced of the actual DB's dialect */
public String getDbDialect() throws SQLException {
String d = dbDialect;
if (d == null) {
d = dbDialect = loadDbDialect();
}
return d;
}
/** Load DB dialect from the DB itself */
protected String loadDbDialect() throws SQLException {
Connection conn = dataSource.getConnection();
try {
DatabaseMetaData dmd = conn.getMetaData();
return dmd.getDatabaseProductName();
} finally {
conn.close();
}
}
/** Load existing DB Schema. We take all tables in the default schema. */
public DbSchema loadDbSchema() throws SQLException {
DbSchema dbSchema = new DbSchema();
dbSchema.setName("default");
loadTables(dbSchema);
return dbSchema;
}
/** Load existing DB tables f */
protected void loadTables(DbSchema dbSchema) throws SQLException {
Connection conn = dataSource.getConnection();
try {
DatabaseMetaData dmd = conn.getMetaData();
if (log.isDebugEnabled()) {
log.debug("reading metadata");
}
ResultSet rs = dmd.getTables(null, null, "%", new String[] { "TABLE" });
while (rs.next()) {
DbTable table = new DbTable();
table.setName(rs.getString("TABLE_NAME"));
loadColumns(table);
loadPrimaryKeys(table);
dbSchema.addTable(table);
if (log.isDebugEnabled()) {
log.debug("loaded:" + table.getName());
}
}
} finally {
conn.close();
}
}
/** Load existing DB columns for the given table. */
protected void loadColumns(DbTable dbTable) throws SQLException {
String tableName = dbTable.getName();
Connection conn = dataSource.getConnection();
try {
DatabaseMetaData dmd = conn.getMetaData();
ResultSet rs = dmd.getColumns(null, null, tableName, "%");
while (rs.next()) {
DbColumn col = new DbColumn();
col.setName(rs.getString("COLUMN_NAME"));
col.setDataType(DbDataType.from(rs.getString("TYPE_NAME")));
col.setSize((Integer) rs.getObject("COLUMN_SIZE"));
String defaultValue = (String) rs.getObject("COLUMN_DEF");
if ("NULL".equals(defaultValue)) {
defaultValue = null;
}
if ((defaultValue != null) && col.getDataType().isQuotedValue()) {
col.setDefaultValue(defaultValue.substring(1, defaultValue.length() - 1));
} else {
col.setDefaultValue(defaultValue);
}
col.setNullable("YES".equals(rs.getString("IS_NULLABLE")));
dbTable.addColumn(col);
}
} finally {
conn.close();
}
}
/** Set the primaryKey flags in the DbColumns of the given DbTable, based on what's in DB. */
protected void loadPrimaryKeys(DbTable dbTable) throws SQLException {
String tableName = dbTable.getName();
Connection conn = dataSource.getConnection();
try {
DatabaseMetaData dmd = conn.getMetaData();
ResultSet rs = dmd.getPrimaryKeys(null, null, tableName);
// FIXME we should reorder the columns based on the primary keys's KEY_SEQ index
while (rs.next()) {
DbColumn col = dbTable.getColumn(rs.getString("COLUMN_NAME"));
col.setPrimaryKey(true);
}
} finally {
conn.close();
}
}
}