/*
* 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.
*/
package org.apache.ambari.server.orm;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import org.apache.ambari.server.configuration.Configuration.DatabaseType;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.sessions.DatabaseSession;
/**
* Interface for schema manipulation
* Note: IF NOT EXISTS is default for all supported DDL statements
*/
public interface DBAccessor {
/**
* @return database connection
*/
Connection getConnection();
/**
* @return new database connection
*/
Connection getNewConnection();
/**
* Wraps object name with dbms-specific quotes
* @param name object name without quotes
* @return quoted name
*/
String quoteObjectName(String name);
/**
* Create new table
* @param tableName
* @param columnInfo
* @param primaryKeyColumns
* @throws SQLException
*/
void createTable(String tableName, List<DBColumnInfo> columnInfo,
String... primaryKeyColumns) throws SQLException;
/**
* Create new index
* @param indexName
* @param tableName
* @param columnNames
* @throws SQLException
*/
void createIndex(String indexName, String tableName,
String... columnNames) throws SQLException;
/**
* Create new index
* @param indexName The name of the index to be created
* @param tableName The database table the index to be created on
* @param columnNames The columns included into the index
* @param isUnique Specifies whether unique index is to be created.
* @throws SQLException Exception in case the index creation fails.
*/
void createIndex(String indexName, String tableName, boolean isUnique,
String... columnNames) throws SQLException;
/**
* Add foreign key for a relation
* @param tableName
* @param constraintName
* @param keyColumn
* @param referenceColumn
* @throws SQLException
*/
void addFKConstraint(String tableName,
String constraintName,
String keyColumn,
String referenceTableName,
String referenceColumn,
boolean ignoreFailure) throws SQLException;
/**
*
* @param tableName
* @param constraintName
* @param keyColumn
* @param referenceTableName
* @param referenceColumn
* @param shouldCascadeOnDelete
* @param ignoreFailure
* @throws SQLException
*/
void addFKConstraint(String tableName,
String constraintName,
String keyColumn,
String referenceTableName,
String referenceColumn,
boolean shouldCascadeOnDelete,
boolean ignoreFailure) throws SQLException;
/**
* Add foreign key for a relation
* @param tableName
* @param constraintName
* @param keyColumns
* @param referenceTableName
* @param referenceColumns
* @param shouldCascadeOnDelete
* @param ignoreFailure
* @throws SQLException
*/
void addFKConstraint(String tableName,
String constraintName,
String[] keyColumns,
String referenceTableName,
String[] referenceColumns,
boolean shouldCascadeOnDelete,
boolean ignoreFailure) throws SQLException;
/**
* Add foreign key for a relation
* @param tableName
* @param constraintName
* @param keyColumns
* @param referenceTableName
* @param referenceColumns
* @param ignoreFailure
* @throws SQLException
*/
void addFKConstraint(String tableName,
String constraintName,
String[] keyColumns,
String referenceTableName,
String[] referenceColumns,
boolean ignoreFailure) throws SQLException;
/**
* Adds a column to an existing table. This method will honor the
* {@link DBColumnInfo#isNullable} and {@link DBColumnInfo#getDefaultValue()}
* methods and create a new column that has a {@code DEFAULT} constraint.
* <p/>
* Oracle is, of course, the exception here since their syntax doesn't conform
* to widely accepted SQL standards. Because they switch the order of the
* {@code NULL} constraint and the {@code DEFAULT} constraint, we need to
* create the table in a non-atomic fashion. This is because the EclipseLink
* {@link FieldDeclaration} class hard codes the order of the constraints. In
* the case of Oracle, we alter the nullability of the table after its
* creation.
* <p/>
* No work is performed if the column already exists.
*
* @param tableName
* @param columnInfo
* @throws SQLException
*/
void addColumn(String tableName,
DBColumnInfo columnInfo) throws SQLException;
/**
* Add unique table constraint
* @param constraintName name of the constraint
* @param tableName name of the table
* @param columnNames list of columns
* @throws SQLException
*/
void addUniqueConstraint(String tableName, String constraintName, String... columnNames)
throws SQLException;
/**
*
* @param tableName name of the table
* @param constraintName name of the constraint
* @param columnName name of the column
* @param ignoreErrors true to ignore database errors
* @throws SQLException
*/
void addPKConstraint(String tableName, String constraintName,boolean ignoreErrors, String... columnName) throws SQLException;
/**
*
* @param tableName name of the table
* @param constraintName name of the constraint
* @param columnName name of the column
* @throws SQLException
*/
void addPKConstraint(String tableName, String constraintName, String... columnName) throws SQLException;
/**
* Rename existing column
* @param tableName
* @param oldColumnName
* @param columnInfo
* @throws SQLException
*/
void renameColumn(String tableName, String oldColumnName,
DBColumnInfo columnInfo) throws SQLException;
/**
* Alter column from existing table, only supports varchar extension <br/>
* Use following sequence for more complex stuff: <br/>
* <li/>{@link #addColumn(String, org.apache.ambari.server.orm.DBAccessor.DBColumnInfo)}
* <li/>{@link #updateTable(String, String, Object, String)}
* <li/>{@link #dropColumn(String, String)}
* <li/>{@link #renameColumn(String, String, org.apache.ambari.server.orm.DBAccessor.DBColumnInfo)}
* @param tableName
* @param columnInfo
* @throws SQLException
*/
void alterColumn(String tableName,
DBColumnInfo columnInfo) throws SQLException;
/**
* Insert row into table
*
* @param tableName
* @param columnNames
* @param values
* @param ignoreFailure
* @return
* @throws SQLException
*/
boolean insertRow(String tableName, String[] columnNames, String[] values, boolean ignoreFailure) throws SQLException;
/**
* Conditionally insert row into table if it does not already exist
*
* @param tableName
* @param columnNames
* @param values
* @param ignoreFailure
* @return
* @throws SQLException
*/
boolean insertRowIfMissing(String tableName, String[] columnNames, String[] values, boolean ignoreFailure) throws SQLException;
/**
* Simple update operation on table
* @param tableName
* @param columnName
* @param value
* @param whereClause
* @return
* @throws SQLException
*/
int updateTable(String tableName, String columnName, Object value,
String whereClause) throws SQLException;
/**
* Simple update operation on table
*
* @param tableName
* @param columnNameSrc
* @param columnNameTgt
* @return
* @throws SQLException
*/
void updateTable(String tableName, DBColumnInfo columnNameSrc,
DBColumnInfo columnNameTgt) throws SQLException;
/**
* Helper method to run third party scripts like Quartz DDL
* @param filePath
* @throws SQLException
*/
void executeScript(String filePath) throws SQLException, IOException;
/**
*
* @param query update query
* @return same like {@code java.sql.Statement}
* @throws SQLException
*/
int executeUpdate(String query) throws SQLException;
/**
*
* @param query update query
* @param ignoreErrors true to ignore errors
* @return same like {@code java.sql.Statement}
* @throws SQLException
*/
int executeUpdate(String query, boolean ignoreErrors) throws SQLException;
/**
* Conditional ad-hoc query on DB
* @param query
* @param tableName
* @param hasColumnName
* @throws SQLException
*/
void executeQuery(String query, String tableName, String hasColumnName) throws SQLException;
/**
* Execute ad-hoc query on DB.
* @param query
* @throws SQLException
*/
void executeQuery(String query) throws SQLException;
/**
* Execute query on DB
* @param query
* @param ignoreFailure
* @throws SQLException
*/
void executeQuery(String query, boolean ignoreFailure) throws SQLException;
/**
* Drop table from schema
* @param tableName
* @throws SQLException
*/
void dropTable(String tableName) throws SQLException;
/**
* Delete all table data
* @param tableName
* @throws SQLException
*/
void truncateTable(String tableName) throws SQLException;
/**
* Drop a column from table
* @param tableName
* @param columnName
* @throws SQLException
*/
void dropColumn(String tableName, String columnName) throws SQLException;
/**
* Drop sequence
* @param sequenceName
* @throws SQLException
*/
void dropSequence(String sequenceName) throws SQLException;
/**
* Drops a FK constraint from a table. In the case of
* {@link DatabaseType#MYSQL}, this will also ensure that any associated
* indexes are also dropped.
*
* @param tableName
* name of the table
* @param constraintName
* name of the constraint
* @throws SQLException
*/
void dropFKConstraint(String tableName, String constraintName) throws SQLException;
/**
* Drop a PK constraint from table
* @param tableName
* @param constraintName name of the constraint
* @param ignoreFailure
* @param cascade cascade delete
* @throws SQLException
*/
void dropPKConstraint(String tableName, String constraintName, boolean ignoreFailure, boolean cascade) throws SQLException;
/**
* Drop a PK constraint from table
* @param tableName name of the table
* @param constraintName name of the constraint
* @param cascade cascade delete
* @throws SQLException
*/
void dropPKConstraint(String tableName, String constraintName, boolean cascade) throws SQLException;
/**
* Drop a PK constraint from table
* @param tableName name of the table
* @param constraintName name of the constraint
* @param columnName name of the column from the pk constraint
* @param cascade cascade delete
* @throws SQLException
*/
void dropPKConstraint(String tableName, String constraintName, String columnName, boolean cascade) throws SQLException;
/**
* Drop a FK constraint from table
* @param tableName name of the table
* @param constraintName name of the constraint
* @throws SQLException
*/
void dropFKConstraint(String tableName, String constraintName, boolean ignoreFailure) throws SQLException;
/**
* Drop a unique constraint from table
* @param tableName name of the table
* @param constraintName name of the constraint
* @param ignoreFailure
* @throws SQLException
*/
void dropUniqueConstraint(String tableName, String constraintName, boolean ignoreFailure) throws SQLException;
/**
* Drop a unique constraint from table
* @param tableName name of the table
* @param constraintName name of the constraint
* @throws SQLException
*/
void dropUniqueConstraint(String tableName, String constraintName) throws SQLException;
/**
* Verify if table exists by looking at metadata.
* @param tableName name of the table
* @return
* @throws SQLException
*/
boolean tableExists(String tableName) throws SQLException;
/**
* Verify if table has any data
* @param tableName
* @return
* @throws SQLException
*/
boolean tableHasData(String tableName) throws SQLException;
/**
* Verify if table already has a column defined.
* @param tableName
* @param columnName
* @return
* @throws SQLException
*/
boolean tableHasColumn(String tableName, String columnName) throws SQLException;
/**
* Verify if table already has a column defined.
* @param tableName name of the table
* @param columnName name of the column to check
* @return false if one from passed column names not exists
* @throws SQLException
*/
boolean tableHasColumn(String tableName, String... columnName) throws SQLException;
/**
* Verify if table has a FK constraint.
* @param tableName
* @param fkName
* @return true if FK with such name exists
* @throws SQLException
*/
boolean tableHasForeignKey(String tableName, String fkName) throws SQLException;
/**
* Verify if table already has a FK constraint.
* @param tableName
* @param refTableName
* @param columnName
* @param refColumnName
* @return true if described relation exists
* @throws SQLException
*/
boolean tableHasForeignKey(String tableName, String refTableName,
String columnName, String refColumnName) throws SQLException;
/**
* Verify if table already has a FK constraint.
* @param tableName
* @param referenceTableName
* @param keyColumns
* @param referenceColumns
* @return true if described relation exists
* @throws SQLException
*/
boolean tableHasForeignKey(String tableName, String referenceTableName, String[] keyColumns,
String[] referenceColumns) throws SQLException;
/**
* Get a new DB session
* @return
*/
DatabaseSession getNewDatabaseSession();
/**
* Table has primary key
* @param tableName name of the table
* @param columnName name of the constraint, could be {@code null}
* @return true if constraint exists
* @throws SQLException
*/
boolean tableHasPrimaryKey(String tableName, String columnName) throws SQLException;
/**
* Gets list of index names from database metadata
* @param tableName
* the name of the table (not {@code null}).
* @param unique
* list only unique indexes (not {@code null}).
* @return the string list of index names
* @throws SQLException
*/
List<String> getIndexesList(String tableName, boolean unique) throws SQLException;
/**
* Check if index is already in scheme
* @param tableName
* the name of the table (not {@code null}).
* @param unique
* list only unique indexes (not {@code null}).
* @param indexName
* name of the index to check
* @return true if index present in the schema
*/
boolean tableHasIndex(String tableName, boolean unique, String indexName) throws SQLException;
/**
* Gets the column's SQL type
*
* @param tableName
* the name of the table (not {@code null}).
* @param columnName
* the name of the column to retrieve type for (not {@code null}).
* @return the integer representation of the column type
* @throws SQLException
*/
int getColumnType(String tableName, String columnName)
throws SQLException;
/**
* Get type class of the column
* @param tableName name of the table
* @param columnName name of the column
* @return type class of the column
* @throws SQLException
* @throws ClassNotFoundException
*/
Class getColumnClass(String tableName, String columnName) throws SQLException, ClassNotFoundException;
/**
* Check if column could be nullable
* @param tableName name of the table
* @param columnName name of the column
* @return true if column could be nullable
* @throws SQLException
*/
boolean isColumnNullable(String tableName, String columnName) throws SQLException;
/**
* Sets the specified column to either allow or prohibit {@code NULL}.
*
* @param tableName
* the name of the table (not {@code null}).
* @param columnInfo
* the column object to get name and type of column (not {@code null}).
* @param nullable
* {@code true} to indicate that the column allows {@code NULL}
* values, {@code false} otherwise.
* @throws SQLException
*/
void setColumnNullable(String tableName, DBAccessor.DBColumnInfo columnInfo, boolean nullable)
throws SQLException;
void setColumnNullable(String tableName, String columnName, boolean nullable)
throws SQLException;
/**
* Alter column wrapper, which handle DB specific type conversion
* @param tableName name of the table
* @param columnName name of the column
* @param fromType previous type
* @param toType new desired type
* @throws SQLException
*/
void changeColumnType(String tableName, String columnName, Class fromType, Class toType) throws SQLException;
/**
* Queries the database to determine the name of the primary key constraint on
* the specified table. Currently, this is only implemented for
* {@link DatabaseType#POSTGRES}, {@link DatabaseType#ORACLE} and
* {@link DatabaseType#SQL_SERVER}. {@link DatabaseType#MYSQL} does not need
* this since PKs can be dropped without referencing their name.
*
* @param tableName
* the name of the table to lookup the PK constraint.
* @return the name of the PK, or {@code null} if none.
* @throws SQLException
*/
String getPrimaryKeyConstraintName(String tableName) throws SQLException;
/**
* Attempts to drop the discovered PRIMARY KEY constraint on the specified
* table, defaulting to the specified default if not found.
*
* @param tableName
* the table to drop the PK from (not {@code null}).
* @param defaultConstraintName
* the default name of the PK constraint if none is found.
* @throws SQLException
*/
void dropPKConstraint(String tableName, String defaultConstraintName) throws SQLException;
/**
* Adds a default constraint to an existing column.
*
* @param tableName
* the table where the column is defined (not {@code null}).
* @param column
* the column information which contains the default value (not
* {@code null}).
* @throws SQLException
*/
void addDefaultConstraint(String tableName, DBColumnInfo column) throws SQLException;
/**
* Move column data from {@code sourceTableName} to {@code targetTableName} using {@code sourceIDFieldName} and
* {@code targetIDFieldName} keys to match right rows
*
* @param sourceTableName
* the source table name
* @param sourceColumn
* the source column name
* @param sourceIDFieldName
* the source id key filed name matched with {@code targetIDFieldName}
* @param targetTableName
* the target table name
* @param targetColumn
* the target column name
* @param targetIDFieldName
* the target id key name matched with {@code sourceIDFieldName}
* @param isColumnNullable
* should be target column nullable or not
*
* @throws SQLException
*/
void moveColumnToAnotherTable(String sourceTableName, DBColumnInfo sourceColumn, String sourceIDFieldName,
String targetTableName, DBColumnInfo targetColumn, String targetIDFieldName, boolean isColumnNullable) throws SQLException;
enum DbType {
ORACLE,
MYSQL,
POSTGRES,
DERBY,
H2,
UNKNOWN
}
/**
* Get type of database platform
* @return @DbType
*/
DbType getDbType();
/**
* Get database schema name
* @return @dbSchema
*/
String getDbSchema();
/**
* Capture column type
*/
class DBColumnInfo {
private String name;
private Class type;
private Integer length;
private Object defaultValue;
private boolean isNullable;
private FieldTypeDefinition dbType = null;
public DBColumnInfo(String name, Class type) {
this(name, type, null, null, true);
}
public DBColumnInfo(String name, Class type, Integer length) {
this(name, type, length, null, true);
}
public DBColumnInfo(String name, Class type, Integer length,
Object defaultValue, boolean nullable) {
this.name = name;
this.type = type;
this.length = length;
this.defaultValue = defaultValue;
isNullable = nullable;
}
public DBColumnInfo(String name, FieldTypeDefinition dbType, Integer length, Object defaultValue, boolean isNullable) {
this.name = name;
this.length = length;
this.isNullable = isNullable;
this.defaultValue = defaultValue;
this.dbType = dbType;
}
public DBColumnInfo(String name, FieldTypeDefinition dbType, Integer length) {
this(name, dbType, length, null, true);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
public Integer getLength() {
return length;
}
public void setLength(Integer length) {
this.length = length;
}
public Object getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(Object defaultValue) {
this.defaultValue = defaultValue;
}
public boolean isNullable() {
return isNullable;
}
public void setNullable(boolean nullable) {
isNullable = nullable;
}
public FieldTypeDefinition getDbType() {
return dbType;
}
public void setDbType(FieldTypeDefinition dbType) {
this.dbType = dbType;
}
}
}