/* * 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.HashMap; import java.util.Iterator; import java.util.List; import org.rhq.core.db.builders.CreateSequenceExprBuilder; 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.TypeMap; class Column { protected static final int DEFAULT_NONE = 0; protected static final int DEFAULT_AUTO_INCREMENT = 1; protected static final int DEFAULT_CURRENT_TIME = 2; protected static final int DEFAULT_SEQUENCE_ONLY = 3; private static final int DATABASE_TYPE = 0; // Any database protected String m_strName; protected String m_strType; protected String m_sReferences; protected int m_iSize; protected boolean m_bPrimaryKey; protected boolean m_bRequired; protected int m_iDefault; protected String m_sDefault; protected int m_iInitialSequence = 1; protected int m_iIncrementSequence = 1; private String m_strSequenceCacheSize = null; // falls back to factory defaults private String ondelete; protected String m_strTableName; protected Column(Node node, Table table) throws SAXException { if (Column.isColumn(node)) { NamedNodeMap map = node.getAttributes(); for (int iAttr = 0; iAttr < map.getLength(); iAttr++) { Node nodeMap = map.item(iAttr); String strName = nodeMap.getNodeName(); String strValue = nodeMap.getNodeValue(); if (strName.equalsIgnoreCase("name") || strName.equalsIgnoreCase("ref")) { // Get the Column Name this.m_strName = strValue; } else if (strName.equalsIgnoreCase("type")) { // Get the Column Type this.m_strType = strValue; } else if (strName.equalsIgnoreCase("size")) { // Get the Column Size this.m_iSize = Integer.parseInt(strValue); } else if (strName.equalsIgnoreCase("primarykey")) { // Is this a Primary Key Column this.m_bPrimaryKey = strValue.equalsIgnoreCase("true"); } else if (strName.equalsIgnoreCase("required")) { // Is the Column NotNull this.m_bRequired = strValue.equalsIgnoreCase("true"); } else if (strName.equalsIgnoreCase("default")) { // Get the default behavior for the column if (strValue.equalsIgnoreCase("autoincrement")) { this.m_iDefault = Column.DEFAULT_AUTO_INCREMENT; } else if (strValue.equalsIgnoreCase("sequence-only")) { this.m_iDefault = Column.DEFAULT_SEQUENCE_ONLY; } else if (strValue.equalsIgnoreCase("current_time") || strValue.equalsIgnoreCase("currenttime")) { this.m_iDefault = Column.DEFAULT_CURRENT_TIME; } else { // Assume that this is a default value for the column this.m_sDefault = strValue; } } else if (strName.equalsIgnoreCase("initial")) { // Get the initial autoincrement value this.m_iInitialSequence = Integer.parseInt(strValue); } else if (strName.equalsIgnoreCase("sequencecachesize")) { Integer.parseInt(strValue); this.m_strSequenceCacheSize = strValue; } else if (strName.equalsIgnoreCase("references")) { this.m_sReferences = strValue; } else if (strName.equalsIgnoreCase("ondelete")) { this.ondelete = strValue; } else if (strName.equalsIgnoreCase("increment")) { // Get the increment value for the autoincrement default this.m_iIncrementSequence = Integer.parseInt(strValue); } else { System.out.println("Unknown attribute '" + nodeMap.getNodeName() + "' in tag 'table'"); } this.m_strTableName = table.getName(); } } else { throw new SAXException("node is not a Column."); } } protected Column(ResultSet set) throws SQLException { this.m_strName = set.getString(4); this.m_strType = set.getString(6); this.m_iSize = set.getInt(7); if (set.getInt(11) == DatabaseMetaData.columnNoNulls) { this.m_bRequired = true; } //System.out.println(set.getString(13)); } protected int getDefault() { return this.m_iDefault; } protected int getInitialSequence() { return this.m_iInitialSequence; } protected int getIncrementSequence() { return this.m_iIncrementSequence; } protected String getSequenceCacheSize() { return this.m_strSequenceCacheSize; } protected String getMappedType(Collection<TypeMap> typemaps, DatabaseType dbtype) { Iterator<TypeMap> iter = typemaps.iterator(); String strResult = null; String strType = this.getType(); while (iter.hasNext()) { TypeMap map = iter.next(); strResult = map.getMappedType(strType, dbtype); if (strResult != null) { break; } } if (strResult == null) { strResult = strType; } return strResult; } protected String getName() { return this.m_strName; } protected String getType() { return this.m_strType; } protected int getSize() { return this.m_iSize; } protected boolean isPrimaryKey() { return this.m_bPrimaryKey; } protected boolean isRequired() { return this.m_bRequired; } protected String getReferences() { return this.m_sReferences; } protected String getOnDelete() { return this.ondelete; } protected String getsDefault() { return this.m_sDefault; } protected void validate(String type) { if (type.equals("VARCHAR2") && (this.getSize() == 0)) { throw new RuntimeException("VARCHAR columns must declare size attributes for oracle compatiblity " + this.m_strTableName + ":" + this.getName()); } if (this.getName().length() > 30) { throw new RuntimeException("Column names must be at most 30 characters for oracle compatibility " + this.m_strTableName + ":" + this.getName()); } if ((this.m_iDefault == Column.DEFAULT_SEQUENCE_ONLY) && ((this.m_strTableName.length() + this.getName().length()) > 25)) { throw new RuntimeException( "Columns with sequences must be at most 25 characters including their table names for oracle compatibility " + this.m_strTableName + ":" + this.getName()); } if (this.getOnDelete() != null && this.getReferences() == null) { throw new RuntimeException("Specifying 'ondelete' requires a 'references' attribute also: " + this.m_strTableName + ":" + this.getName()); } } protected String getCreateCommand(List cmds, Collection typemaps, DatabaseType dbtype) { String type = this.getMappedType(typemaps, dbtype); String strCmd = this.getName() + ' ' + type; validate(type); if (this.getSize() > 0) { strCmd = strCmd + this.getSizeCommand(cmds); } if (this.hasDefault()) { String strDefault = this.getDefaultCommand(cmds); if (strDefault.length() > 0) { strCmd = strCmd + ' ' + strDefault; } } if (this.m_sDefault != null) { strCmd += " DEFAULT '" + this.getsDefault() + "'"; } if (this.isRequired()) { strCmd += " NOT NULL"; } if (this.isPrimaryKey()) { strCmd += " PRIMARY KEY"; } if (this.getReferences() != null) { strCmd += " REFERENCES " + this.getReferences(); if (this.getOnDelete() != null) { strCmd += " ON DELETE " + this.getOnDelete(); } } return strCmd; } protected static List<Column> getColumns(Node nodeTable, Table table, DatabaseType dbtype) { /////////////////////////////////////////////////////////////// // Get the Columns Names and Related Info NodeList listCols = nodeTable.getChildNodes(); List<Column> colResult = new ArrayList<Column>(); for (int iCol = 0; iCol < listCols.getLength(); iCol++) { Node node = listCols.item(iCol); if (Column.isColumn(node)) { try { Column col; if (DatabaseTypeFactory.isOracle(dbtype)) { col = new OracleColumn(node, table); } else if (DatabaseTypeFactory.isPostgres(dbtype)) { col = new PostgresColumn(node, table); } else if (DatabaseTypeFactory.isH2(dbtype)) { col = new H2Column(node, table); } else if (DatabaseTypeFactory.isSQLServer(dbtype)) { col = new SQLServerColumn(node, table); } else { col = new Column(node, table); } colResult.add(col); } catch (SAXException e) { } } } return colResult; } protected static List<Column> getColumns(DatabaseMetaData meta, Table table, String username) throws SQLException { ResultSet setCols = meta.getColumns(null, null, table.getName(), null); List<Column> colResult = new ArrayList<Column>(); while (setCols.next()) { Column col = new Column(setCols); colResult.add(col); } return colResult; } protected static int getClassType() { return Column.DATABASE_TYPE; } protected String getDefaultCommand(List cmds) { String strCmd = "DEFAULT "; switch (this.getDefault()) { case Column.DEFAULT_AUTO_INCREMENT: { strCmd += "AUTOINCREMENT"; if (this.getInitialSequence() > 0) { strCmd = strCmd + " INITIAL " + this.getInitialSequence(); } if (this.getIncrementSequence() > 0) { strCmd = strCmd + " INCREMENT " + this.getIncrementSequence(); } break; } case Column.DEFAULT_CURRENT_TIME: { strCmd += "CURRENT_TIME"; break; } } return strCmd; } protected String getSizeCommand(List cmds) { return "(" + this.getSize() + ')'; } protected void getPreCreateCommands(List cmds) { // Do nothing. Subclasses may need to add commands to the collection. } protected void getPostCreateCommands(List cmds) { // Do nothing. Subclasses may need to add commands to the collection. } protected void getDropCommands(List cmds) { // Do nothing. Subclasses may need to add commands to the collection. } protected boolean hasDefault() { return (this.m_iDefault != Column.DEFAULT_NONE); } protected static boolean isColumn(Node node) { return node.getNodeName().equalsIgnoreCase("column"); } protected String buildSequenceSqlExpr(CreateSequenceExprBuilder builder, String name) { HashMap<String, Object> terms = new HashMap<String, Object>(); terms.put(CreateSequenceExprBuilder.KEY_SEQ_NAME, name); terms.put(CreateSequenceExprBuilder.KEY_SEQ_START, getInitialSequence()); terms.put(CreateSequenceExprBuilder.KEY_SEQ_INCREMENT, getIncrementSequence()); // fall back to factory defaults if left unspecified... terms.put(CreateSequenceExprBuilder.KEY_SEQ_CACHE_SIZE, CreateSequenceExprBuilder.getSafeSequenceCacheSize(builder, getSequenceCacheSize())); return builder.build(terms); } }