/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.core.db.setup;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.rhq.core.db.DatabaseType;
import org.rhq.core.db.DatabaseTypeFactory;
import org.rhq.core.db.SQLServerDatabaseType;
class Table {
private DBSetup m_parent;
private String m_strName;
private boolean m_indexOrganized = false; // Index Organized Table
private boolean m_parallel = false; // Parallel processing
private boolean m_logging = true;
private boolean m_cache = false;
private String m_tableSpace;
private String m_storage;
private String m_engine;
private List<Column> m_listColumns;
private Collection<Index> m_collIndexes;
private Collection<Constraint> m_collConstraints;
private DataSet m_dataset;
private boolean m_obsolete = false; // will be true if the table should no longer exist in the schema
protected Table(Node node, DatabaseType dbtype, DBSetup dbsetup) throws SAXException {
m_parent = dbsetup;
if (Table.isTable(node)) {
NamedNodeMap map = node.getAttributes();
for (int iTab = 0; iTab < map.getLength(); iTab++) {
Node nodeMap = map.item(iTab);
if (nodeMap.getNodeName().equalsIgnoreCase("name")) {
this.m_strName = nodeMap.getNodeValue();
} else if (nodeMap.getNodeName().equalsIgnoreCase("index-organized")) {
this.m_indexOrganized = nodeMap.getNodeValue().equalsIgnoreCase("true");
} else if (nodeMap.getNodeName().equalsIgnoreCase("parallel")) {
this.m_parallel = nodeMap.getNodeValue().equalsIgnoreCase("true");
} else if (nodeMap.getNodeName().equalsIgnoreCase("logging")) {
this.m_logging = nodeMap.getNodeValue().equalsIgnoreCase("true");
} else if (nodeMap.getNodeName().equalsIgnoreCase("cache")) {
this.m_cache = nodeMap.getNodeValue().equalsIgnoreCase("true");
} else if (nodeMap.getNodeName().equalsIgnoreCase("tablespace")) {
this.m_tableSpace = nodeMap.getNodeValue();
} else if (nodeMap.getNodeName().equalsIgnoreCase("storage-options")) {
this.m_storage = nodeMap.getNodeValue();
} else if (nodeMap.getNodeName().equalsIgnoreCase("engine")) {
this.m_engine = nodeMap.getNodeValue();
} else if (nodeMap.getNodeName().equalsIgnoreCase("obsolete")) {
this.m_obsolete = (nodeMap.getNodeValue() != null)
&& nodeMap.getNodeValue().equalsIgnoreCase("true");
} else {
System.out.println("Unknown attribute \'" + nodeMap.getNodeName() + "\' in tag \'table\'");
}
}
this.m_listColumns = Column.getColumns(node, this, dbtype);
this.m_collIndexes = Index.getIndexes(this, node, dbtype);
this.m_collConstraints = Constraint.getConstraints(this, node, dbtype);
if (dbsetup.getDatabaseType() instanceof SQLServerDatabaseType) {
// needs special pre and post handling for data set insertions
this.m_dataset = new SQLServerXmlDataSet(this, node);
} else {
this.m_dataset = new XmlDataSet(this, node);
}
} else {
throw new SAXException("node is not a table.");
}
}
protected Table(ResultSet set, DatabaseMetaData meta, DBSetup dbsetup) throws SQLException {
this.m_parent = dbsetup;
this.m_strName = set.getString(3);
this.m_listColumns = Column.getColumns(meta, this, dbsetup.getJdbcUser());
if (dbsetup.getDatabaseType() instanceof SQLServerDatabaseType) {
// needs special pre and post handling for data set insertions
this.m_dataset = new SQLServerSqlDataSet(this);
} else {
this.m_dataset = new SqlDataSet(this);
}
}
protected void create(Collection typemaps) throws SQLException {
List collCmds = new java.util.Vector();
this.getCreateCommands(collCmds, typemaps, m_parent.getDatabaseType());
for (Object collCmd : collCmds) {
String strCmd = (String) collCmd;
m_parent.doSQL(strCmd);
}
}
private void doCmd(List collCmds) throws SQLException {
for (Object collCmd : collCmds) {
String strCmd = (String) collCmd;
m_parent.doSQL(strCmd);
}
}
private void doCmdsWithoutAbortingOnErrors(List collCmds) throws SQLException {
SQLException sqlException = null;
for (Object collCmd : collCmds) {
String strCmd = (String) collCmd;
try {
m_parent.doSQL(strCmd);
} catch (SQLException e) {
if (sqlException == null) {
sqlException = e;
} else {
sqlException.setNextException(e);
sqlException = e;
}
}
}
if (sqlException != null) {
throw sqlException;
}
}
protected void clear() throws SQLException {
List collCmds = new ArrayList<String>();
this.getClearCommands(collCmds);
doCmd(collCmds);
}
protected void drop() throws SQLException {
List collCmds = new ArrayList<String>();
this.getDropCommands(collCmds);
// *** NOTE *** we must execute all the DROPs no matter what, because although a
// table may not exist, sequences associated with it could still exist!
doCmdsWithoutAbortingOnErrors(collCmds);
}
protected List getColumns() {
return this.m_listColumns;
}
protected String getTableSpace() {
return this.m_tableSpace;
}
protected String getStorage() {
return this.m_storage;
}
protected String getEngine() {
return this.m_engine;
}
protected boolean isObsolete() {
return this.m_obsolete;
}
protected void getCreateCommands(List cmds, Collection typemaps, DatabaseType dbtype) {
String strCmd = "CREATE TABLE " + this.getName() + " (";
if (this.getName().length() > 30) {
throw new RuntimeException("Column names must be at most 30 characters for oracle compatibility "
+ this.getName());
}
Iterator iter = this.getColumns().iterator();
boolean bFirst = true;
List<String> preCreateCommands = new ArrayList<String>();
List<String> postCreateCommands = new ArrayList<String>();
Column col;
while (iter.hasNext()) {
if (bFirst) {
bFirst = false;
} else {
strCmd += ", ";
}
col = (Column) iter.next();
col.getPreCreateCommands(preCreateCommands);
strCmd += col.getCreateCommand(cmds, typemaps, dbtype);
col.getPostCreateCommands(postCreateCommands);
}
// Add constraint decls
iter = this.getConstraints().iterator();
Constraint constraint;
while (iter.hasNext()) {
constraint = (Constraint) iter.next();
strCmd += constraint.getCreateString();
constraint.getPostCreateCommands(postCreateCommands);
}
strCmd += ')';
if (this.m_indexOrganized) {
strCmd += this.getIndexOrganizedSyntax(dbtype);
}
// deal with the special tablespace attr
if ((this.m_tableSpace != null) && !this.m_tableSpace.equals("DEFAULT")
&& !this.getTableSpaceSyntax().equals("")) {
strCmd += this.getTableSpaceSyntax() + this.getTableSpace();
}
// add a storage clause if any apply
strCmd += this.getStorageClause();
if (this.m_parallel) {
strCmd += this.getParallelSyntax();
}
if (!this.m_logging) // Oracle does NOLOGGING
{
strCmd += this.getLoggingSyntax();
}
if (this.m_cache) {
strCmd += this.getCacheSyntax();
}
cmds.addAll(preCreateCommands);
cmds.add(strCmd);
cmds.addAll(postCreateCommands);
}
/**
* Get the storage clause for this table. Returns an empty string if it is not applicable
*
* @return the storage clause for this table
*/
public String getStorageClause() {
if ((this.m_storage != null) && !this.getStorageSyntax().equals("")) {
return this.getStorageSyntax() + "(" + m_storage + ")";
} else {
return "";
}
}
/**
* Get the engine clause for this table. Returns an empty string if not applicable
*/
public String getEngineClause() {
// if(this.m_engine != null && !this.getEngineSyntax().equals("")) {
// return this.getEngineSyntax() + "(" + m_engine + ")";
// }
// FIXME hardcoded to all tables w/ same engine clause if engine is supported
if (!this.getEngineSyntax().equals("")) {
return this.getEngineSyntax();
} else {
return "";
}
}
protected String getIndexOrganizedSyntax(DatabaseType dbtype) {
return (DatabaseTypeFactory.isOracle(dbtype)) ? " ORGANIZATION INDEX" : "";
}
protected String getParallelSyntax() {
return "";
}
/**
* This is actually the syntax for NO logging. If logging is set to false,
* this returns a string to correspond to syntax that turns off logging.
*/
protected String getLoggingSyntax() {
return "";
}
protected String getCacheSyntax() {
return "";
}
protected String getTableSpaceSyntax() {
return "";
}
protected String getStorageSyntax() {
return "";
}
protected String getEngineSyntax() {
return "";
}
protected DataSet getDataSet() {
return this.m_dataset;
}
protected void getClearCommands(List cmds) {
String strCmd = "DELETE FROM " + this.getName();
cmds.add(strCmd);
}
protected void getDropCommands(List cmds) {
String strCmd = "DROP TABLE " + this.getName();
cmds.add(strCmd);
for (Object o : this.getColumns()) {
((Column) o).getDropCommands(cmds);
}
}
protected Collection<Constraint> getConstraints() {
return this.m_collConstraints;
}
protected Collection<Index> getIndexes() {
return this.m_collIndexes;
}
protected String getQueryCommand() {
String strCmd = "SELECT ";
Iterator iter = this.getColumns().iterator();
while (iter.hasNext()) {
strCmd += ((Column) iter.next()).getName();
if (iter.hasNext()) {
strCmd += ',';
}
strCmd += ' ';
}
strCmd = strCmd + "FROM " + this.getName();
return strCmd;
}
protected String getName() {
return this.m_strName;
}
protected static List<Table> getTables(Node node, DatabaseType dbtype, DBSetup parent) {
List<Table> colResult = new ArrayList<Table>();
NodeList listTabs = node.getChildNodes();
for (int iTab = 0; iTab < listTabs.getLength(); iTab++) {
Node nodeTab = listTabs.item(iTab);
if (Table.isTable(nodeTab)) {
//noinspection EmptyCatchBlock
try {
if (DatabaseTypeFactory.isOracle(dbtype)) {
colResult.add(new OracleTable(nodeTab, dbtype, parent));
} else {
colResult.add(new Table(nodeTab, dbtype, parent));
}
} catch (SAXException e) {
}
}
}
return colResult;
}
protected static Collection<Table> getTables(DatabaseType dbtype, DBSetup parent, String username)
throws SQLException {
if (DatabaseTypeFactory.isOracle(dbtype)) {
return OracleTable.getTables(parent, username);
}
Collection<Table> coll = new ArrayList<Table>();
DatabaseMetaData meta = parent.getConnection().getMetaData();
String[] types = { "TABLE" };
// there doesn't seem to be a general case for this but we
// know this works for Oracle, so we'll start there
ResultSet setTabs = meta.getTables(null, null, "%", types);
// Find Tables
while (setTabs.next()) {
coll.add(new Table(setTabs, meta, parent));
}
return coll;
}
protected static boolean isTable(Node nodeTable) {
return nodeTable.getNodeName().equalsIgnoreCase("table");
}
// Can be overridden to do cleanup on an uninstall
protected static void uninstallCleanup(DBSetup parent) throws SQLException {
}
protected DBSetup getDBSetup() {
return m_parent;
}
}