All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prerna.util.sql.AbstractSqlQueryUtil Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright 2015 Defense Health Agency (DHA)
 *
 * If your use of this software does not include any GPLv2 components:
 * 	Licensed 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.
 * ----------------------------------------------------------------------------
 * If your use of this software includes any GPLv2 components:
 * 	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; either version 2
 * 	of the License, or (at your option) any later version.
 *
 * 	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.
 *******************************************************************************/
package prerna.util.sql;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.google.gson.Gson;

import prerna.algorithm.api.ITableDataFrame;
import prerna.algorithm.api.SemossDataType;
import prerna.engine.api.IDatabaseEngine;
import prerna.engine.api.IRDBMSEngine;
import prerna.engine.api.IRawSelectWrapper;
import prerna.engine.impl.CaseInsensitiveProperties;
import prerna.engine.impl.owl.WriteOWLEngine;
import prerna.query.interpreters.IQueryInterpreter;
import prerna.query.interpreters.sql.SqlInterpreter;
import prerna.query.querystruct.filters.IQueryFilter;
import prerna.query.querystruct.selectors.QueryFunctionSelector;
import prerna.rdf.engine.wrappers.WrapperManager;
import prerna.sablecc2.om.Join;
import prerna.util.ConnectionUtils;
import prerna.util.Constants;
import prerna.util.Utility;

public abstract class AbstractSqlQueryUtil {
	
	// special key when not required
	public static final String NO_KEY_REQUIRED = "NO_KEY_REQUIRED";
	
	// inputs for connection string builder
	public static final String CONNECTION_URL = Constants.CONNECTION_URL;
	@Deprecated
	public static final String DRIVER_NAME = "dbDriver";

	public static final String HOSTNAME = "hostname";
	public static final String PORT = "port";
	public static final String DATABASE = "database";
	public static final String CATALOG = "catalog";
	public static final String SCHEMA = "schema";
	public static final String USERNAME = Constants.USERNAME;
	public static final String PASSWORD = Constants.PASSWORD;
	public static final String ADDITIONAL = "additional";
	public static final String TABLE = "table";
	
	// relatively specific inputs
	// athena
	public static final String SERVICE = "service";
	public static final String REGION = "region";
	public static final String ACCESS_KEY = "accessKey";
	public static final String SECRET_KEY = "secretKey";
	public static final String OUTPUT = "output";
	// bigquery
	public static final String PROJECT_ID = "projectId";
	public static final String OAUTH_TYPE = "oauthType";
	public static final String OAUTH_SERVICE_ACCT_EMAIL = "oauthServiceAcctEmail";
	public static final String OAUTH_PRIVATE_KEY_PATH = "oauthPvtKeyPath";
	public static final String OAUTH_ACCESS_TOKEN = "oauthAccessToken";
	public static final String OAUTH_REFRESH_TOKEN = "oauthRefreshToken";
	public static final String OAUTH_CLIENT_ID = "oauthClientId";
	public static final String OAUTH_CLIENT_SECRET = "oauthClientSecret";
	public static final String DEFAULT_DATASET = "defaultDataSet";
	// snowflake
	public static final String WAREHOUSE = "warehouse";
	public static final String ROLE = "role";
	// elasticsearch
	public static final String HTTP_TYPE = "httpType";
	// databricks
	public static final String HTTP_PATH = "httpPath";
	public static final String UID = "UID";
	public static final String PWD = "PWD";
	// semoss
	public static final String PROTOCOL = "protocol";
	public static final String ENDPOINT = "endpoint";
	public static final String SUB_URL = "sub_url";
	public static final String PROJECT = "project";
	public static final String INSIGHT = "insight";

	// rdbms metadata for defining the types 
	public static final String DATA_TYPE = "DATA_TYPE";
	public static final String CHARACTER_MAXIMUM_LENGTH = "CHARACTER_MAXIMUM_LENGTH";
	public static final String NUMERIC_PRECISION = "NUMERIC_PRECISION";
	public static final String NUMERIC_SCALE = "NUMERIC_SCALE";

	// h2 force file for creating embedded file
	public static final String FORCE_FILE = "forceFile";
	
	private static final Logger classLogger = LogManager.getLogger(AbstractSqlQueryUtil.class);

	protected RdbmsTypeEnum dbType = null;
	// there are 2 different ways of providing the inputs
	// properties - primarily for grabbing from SMSS files
	// map - primarily for getting input details from FE / JSON
	protected Properties properites;
	protected Map conDetails;

	protected String connectionUrl;
	protected String username;
	protected String password;
	
	// these should be replaced and use the properties / conDetails
	protected String hostname;
	protected String port;
	protected String database;
	protected String schema;
	protected String additionalProps;
	
	// reserved words
	protected List reservedWords = null;
	// type conversions
	protected Map typeConversionMap = new HashMap<>();

	AbstractSqlQueryUtil() {
		initTypeConverstionMap();
	}
	
	AbstractSqlQueryUtil(String connectionURL, String username, String password) {
		this.connectionUrl = connectionURL;
		this.username = username;
		this.password = password;
		initTypeConverstionMap();
	}

	/**
	 * Set the connection details from a map
	 * @param configMap
	 * @return
	 * @throws SQLException 
	 */
	public abstract String setConnectionDetailsfromMap(Map configMap) throws RuntimeException;
	
	/**
	 * Set the connection details from a properties file (SMSS file)
	 * @param prop
	 * @return
	 */
	public abstract String setConnectionDetailsFromSMSS(CaseInsensitiveProperties prop) throws RuntimeException;
	
	/**
	 * Build the connection string after the connection details have been set
	 * @return
	 */
	public abstract String buildConnectionString();
	
	/**
	 * Method to get a connection to an existing RDBMS engine
	 * @param driverEnum
	 * @param connectionUrl
	 * @param connectionDetails
	 * @return
	 * @throws SQLException 
	 */
	public static Connection makeConnection(AbstractSqlQueryUtil util, String connectionUrl, Map connectionDetails) throws SQLException {
		return AbstractSqlQueryUtil.makeConnection(util.getDbType(), 
				connectionUrl, 
				(String) connectionDetails.get(util.getConnectionUserKey()), 
				(String) connectionDetails.get(util.getConnectionPasswordKey()));
	}
	
	/**
	 * Method to get a connection to an existing RDBMS engine
	 * @param driverEnum
	 * @param connectionUrl
	 * @param connectionDetails
	 * @return
	 * @throws SQLException 
	 */
	public static Connection makeConnection(AbstractSqlQueryUtil util, String connectionUrl, CaseInsensitiveProperties prop) throws SQLException {
		return AbstractSqlQueryUtil.makeConnection(util.getDbType(), 
				connectionUrl, 
				(String) prop.get(util.getConnectionUserKey()), 
				(String) prop.get(util.getConnectionPasswordKey()));
	}
	
	/**
	 * Method to get a connection to an existing RDBMS engine
	 * If the username or password are null, we will assume the information is already provided within the connectionUrl
	 * @param connectionUrl
	 * @param userName
	 * @param password
	 * @param driver
	 * @return
	 * @throws SQLException 
	 */
	public static Connection makeConnection(RdbmsTypeEnum type, String connectionUrl, String userName, String password) throws SQLException {
		try {
			Class.forName(type.getDriver());
		} catch (ClassNotFoundException e) {
			classLogger.error(Constants.STACKTRACE, e);
			throw new SQLException("Unable to find class: " + type.getDriver());
		}

		// create the iterator
		Connection conn;
		try {
			if (userName == null && password == null) {
				conn = DriverManager.getConnection(connectionUrl);
			} else {
				conn = DriverManager.getConnection(connectionUrl, userName, password);
			}
		} catch (SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
			throw new SQLException("Could not get connection.");
		}

		return conn;
	}
	
	/**
	 * Use this when we need to make any modifications to the connection object for
	 * proper usage Example ::: Adding user defined functions for RDBMS types that
	 * allow it
	 * 
	 * @param con
	 */
	public abstract void enhanceConnection(Connection con);

	/**
	 * Initialize the type conversion map to account for sql discrepancies in type
	 * names
	 */
	public abstract void initTypeConverstionMap();

	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * All connection details the setters and getters
	 */

	public RdbmsTypeEnum getDbType() {
		return dbType;
	}

	void setDbType(RdbmsTypeEnum dbType) {
		this.dbType = dbType;
	}

	public String getDriver() {
		return this.dbType.getDriver();
	}

	public String getHostname() {
		return hostname;
	}
	
	public void setHostname(String hostname) {
		this.hostname = hostname;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}
	
	public String getDatabase() {
		return database;
	}
	
	public void setDatabase(String database) {
		this.database = database;
	}
	
	public String getSchema() {
		return schema;
	}

	public void setSchema(String schema) {
		this.schema = schema;
	}
	
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}
	
	public String getPassword() {
		return password;
	}
	
	public void setPassword(String password) {
		this.password = password;
	}

	public String getAdditionalProps() {
		return additionalProps;
	}
	
	public void setAdditionalProps(String additionalProps) {
		this.additionalProps = additionalProps;
	}
	
	public String getConnectionUrl() {
		return connectionUrl;
	}
	
	public void setConnectionUrl(String connectionUrl) {
		this.connectionUrl = connectionUrl;
	}

	public String getConnectionUserKey() {
		return AbstractSqlQueryUtil.USERNAME;
	}
	
	public String getConnectionPasswordKey() {
		return AbstractSqlQueryUtil.PASSWORD;
	}

	public IQueryInterpreter getInterpreter(IDatabaseEngine engine) {
		return new SqlInterpreter(engine);
	}

	public IQueryInterpreter getInterpreter(ITableDataFrame frame) {
		return new SqlInterpreter(frame);
	}
	
	public Map getTypeConversionMap() {
		return Collections.unmodifiableMap(typeConversionMap);
	}
	
	public String getDatabaseMetadataCatalogFilter() {
		return null;
	}
	
	public String getDatabaseMetadataSchemaFilter() {
		return null;
	}

	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////

	/**
	 * Set the list of reserved words
	 * 
	 * @param reservedWords
	 */
	public void setReservedWords(List reservedWords) {
		this.reservedWords = reservedWords;
	}

	/**
	 * Check if the selector is in fact a reserved word
	 * 
	 * @param selector
	 * @return
	 */
	public boolean isSelectorKeyword(String selector) {
		return this.reservedWords != null && this.reservedWords.contains(selector.toUpperCase());
	}

	/**
	 * Get the escaped keyword Default is to wrap the selector in quotes
	 * 
	 * @param selector
	 * @return
	 */
	public String getEscapeKeyword(String selector) {
		return "\"" + selector + "\"";
	}

	/**
	 * Get any modification required to an alias
	 * @param alias
	 * @return
	 */
	public String escapeReferencedAlias(String alias) {
		return alias;
	}
	
	/**
	 * Determine if the subquery column name needs to be aliased to be recognized
	 * @param columnReturnedFromSubquery
	 * @return
	 */
	public String escapeSubqueryColumnName(String columnReturnedFromSubquery) {
		return columnReturnedFromSubquery;
	}

	/**
	 * Escape sql statement literals
	 * 
	 * @param s
	 * @return
	 */
	public static String escapeForSQLStatement(String s) {
		if (s == null) {
			return s;
		}
		return s.replace("'", "''");
	}

	/**
	 * Escape regex searching
	 * 
	 * @param s
	 * @return
	 */
	public static String escapeRegexCharacters(String s) {
		s = s.trim();
		s = s.replace("(", "\\(");
		s = s.replace(")", "\\)");
		return s;
	}
	
	/**
	 * 
	 * @param connection
	 * @param blobInput
	 * @return
	 */
	public static Blob stringToBlob(Connection connection, String blobInput) {
		Blob blob = null;

		try {
			blob = connection.createBlob();
			blob.setBytes(1, blobInput.getBytes());
		} catch (SQLException se) {
			classLogger.error("Failed to convert string to blob...");
			classLogger.error(Constants.STACKTRACE, se);
		}

		return blob;
	}
	
	/**
	 * 
	 * @param blob
	 * @return
	 */
	public static String flushBlobToString(Blob blob) throws SQLException, IOException {
		if(blob == null) {
			return null;
		}
		StringBuffer strOut = new StringBuffer();
		String aux;

		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		try {
			is = blob.getBinaryStream();
			isr = new InputStreamReader(is);
			br = new BufferedReader(isr);
			boolean firstLine = true;
			while ((aux=br.readLine())!=null) {
				if (!firstLine) {
					// append a newline before each line except the first
					strOut.append("\n");
				}
				strOut.append(aux);
				firstLine = false;
			}
		} finally {
			if(is != null) {
				try {
					is.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
			if(isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
			if(br != null) {
				try {
					br.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}
		return strOut.toString();
	}

	/**
	 * Flush clob to string
	 * 
	 * @param inputClob
	 * @return
	 */
	public static String flushClobToString(java.sql.Clob inputClob) {
		Reader inputstream = null;
		if (inputClob != null) {
			try {
				inputstream = inputClob.getCharacterStream();
				return IOUtils.toString(inputstream);
			} catch (SQLException sqe) {
				classLogger.error(Constants.STACKTRACE, sqe);
			} catch (IOException e) {
				classLogger.error(Constants.STACKTRACE, e);
			}
		}
		return null;
	}
	
	/**
	 * 
	 * @param sourceClob
	 * @param targetClob
	 * @throws SQLException
	 * @throws IOException
	 */
	public static void transferClob(Clob sourceClob, Clob targetClob) throws SQLException, IOException {
		InputStream source = sourceClob.getAsciiStream();
		OutputStream target = targetClob.setAsciiStream(1);
		
		byte[] buf = new byte[8192];
		int length;
		while ((length = source.read(buf)) > 0) {
			target.write(buf, 0, length);
		}
	}
	
	/**
	 * Clean the table name so it is valid for SQL
	 * @param tableName
	 * @return
	 */
	public static String cleanTableName(String tableName) {
		tableName = Utility.makeAlphaNumeric(tableName);
		if(tableName.isEmpty()) {
			throw new IllegalArgumentException("After removing unallowed special characters, the table name is empty");
		}
		if(Character.isDigit(tableName.charAt(0))) {
			tableName = "_" + tableName;
		}
		return tableName;
	}

	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * Methods to clean the sql type
	 */

	/**
	 * Clean the types to account for sql naming differences
	 * 
	 * @param type
	 * @return
	 */
	public String cleanType(String type) {
		if (type == null) {
			type = "VARCHAR(800)";
		}
		type = type.toUpperCase();
		if (typeConversionMap.containsKey(type)) {
			type = typeConversionMap.get(type);
		} else {
			if (typeConversionMap.containsValue(type)) {
				return type;
			}
			type = "VARCHAR(800)";
		}
		return type;
	}

	/**
	 * Clean the types to account for sql naming differences
	 * 
	 * @param types
	 * @return
	 */
	public String[] cleanTypes(String[] types) {
		String[] cleanTypes = new String[types.length];
		for (int i = 0; i < types.length; i++) {
			cleanTypes[i] = cleanType(types[i]);
		}

		return cleanTypes;
	}

	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * This section is so I can properly convert the intended function names
	 */

	/**
	 * Get the sql function string
	 * 
	 * @param inputFunction
	 * @return
	 */

	// there are all the specific functions
	// the {@link #getSqlFunctionSyntax(String) getSqlFunctionSyntax}
	// only needs to be implemented in the AnsiSqlQueryUtil
	// where it loops through everything and the specifics can be
	// implemented in the query util implementations

	public abstract String getSqlFunctionSyntax(String inputFunction);

	public abstract String getMinFunctionSyntax();

	public abstract String getMaxFunctionSyntax();

	public abstract String getAvgFunctionSyntax();

	public abstract String getMedianFunctionSyntax();

	public abstract String getSumFunctionSyntax();

	public abstract String getStdevFunctionSyntax();

	public abstract String getCountFunctionSyntax();

	public abstract String getConcatFunctionSyntax();

	public abstract String getGroupConcatFunctionSyntax();
	
	public abstract String getSubstringFunctionSyntax();
	
	public abstract String getDateFormatFunctionSyntax();
	
	public abstract String getCastFunctionSyntax();

	public abstract String getLowerFunctionSyntax();

	public abstract String getCoalesceFunctionSyntax();

	public abstract String getRegexLikeFunctionSyntax();

	public abstract String getMonthNameFunctionSyntax();
	
	public abstract String getDayNameFunctionSyntax();
	
	public abstract String getQuarterFunctionSyntax();
	
	public abstract String getWeekFunctionSyntax();
	
	public abstract String getYearFunctionSyntax();
	
	// TODO: NEED TO BUILD OUT MORE FUNCTIONS THIS WAY TO ACCOUNT 
	// FUNCTION SYNTAX REQUIREMENTS BASED ON THE SQL TYPE
	public abstract String processGroupByFunction(String selector, String separator, boolean distinct);
	
	// TODO: this might potentially be replaced from the above
	// once we implement all the various functions
	public abstract void appendDefaultFunctionOptions(QueryFunctionSelector fun);

	// date functions - require more complex inputs
	public abstract String getCurrentDate();
	
	public abstract String getCurrentTimestamp();
	
	public abstract String getDateAddFunctionSyntax(String timeUnit, int value, String  dateTimeField);
	
	public abstract String getDateDiffFunctionSyntax(String timeUnit, String dateTimeField1, String dateTimeField2);
	
	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * This section is intended for modifications to select queries to pull data
	 */

	
	/**
	 * Retrieve the first row of a query
	 * 
	 * @param query
	 * @param limit
	 * @param offset
	 * @return
	 */
	public abstract StringBuilder getFirstRow(StringBuilder query);
	
	/**
	 * Add the limit and offset to a query
	 * 
	 * @param query
	 * @param limit
	 * @param offset
	 * @return
	 */
	public abstract StringBuilder addLimitOffsetToQuery(StringBuilder query, long limit, long offset);

	/**
	 * Add the limit and offset to a query
	 * 
	 * @param query
	 * @param limit
	 * @param offset
	 * @return
	 */
	public abstract StringBuffer addLimitOffsetToQuery(StringBuffer query, long limit, long offset);
	
	/**
	 * Remove duplicates that exist from an existing table by creating a new temp
	 * intermediary table
	 * 
	 * @param tableName
	 * @param fullColumnNameList
	 * @return
	 */
	public abstract String removeDuplicatesFromTable(String tableName, String fullColumnNameList);

	/**
	 * Create an insert prepared statement
	 * 
	 * @param tableName
	 * @param columns
	 */
	public abstract String createInsertPreparedStatementString(String tableName, String[] columns);

	/**
	 * Create an update prepared statement
	 * 
	 * @param tableName
	 * @param columnsToUpdate
	 * @param whereColumns
	 * @return
	 */
	public abstract String createUpdatePreparedStatementString(String tableName, String[] columnsToUpdate,
			String[] whereColumns);

	/**
	 * Append a regex filter search on a column
	 * @param qs
	 * @param columnQs
	 * @param searchTerm
	 */
	public abstract IQueryFilter getSearchRegexFilter(String columnQs, String searchTerm);
	
	/**
	 * Create the syntax to merge 2 tables together
	 * @param returnTableName	The return table name
	 * @param leftTableName		The left table
	 * @param leftTableTypes	The {header -> type} of the left table
	 * @param rightTableName	The right table name
	 * @param rightTableTypes	The {header -> type} of the right table
	 * @param joins				The joins between the right and left table
	 * @param leftTableAlias	The {header -> alias} of the left table
	 * @param rightTableAlias	The {header -> alias} of the right table
	 * @param rightJoinFlag		Flag if we are doing a right join to switch the ordering of the tables
	 * @return
	 */
	public abstract String createNewTableFromJoiningTables(String returnTableName, String leftTableName,
			Map leftTableTypes, String rightTableName,
			Map rightTableTypes, List joins, Map leftTableAlias,
			Map rightTableAlias, boolean rightJoinFlag);
	
	/**
	 * Similar to {@link #createNewTableFromJoiningTables()} but only returns the select portion without 
	 * the "CREATE TABLE AS " syntax
	 * @param leftTableName
	 * @param leftTableTypes
	 * @param rightTableName
	 * @param rightTableTypes
	 * @param joins
	 * @param leftTableAlias
	 * @param rightTableAlias
	 * @param rightJoinFlag
	 * @return
	 */
	public abstract String selectFromJoiningTables(String leftTableName,
			Map leftTableTypes, String rightTableName,
			Map rightTableTypes, List joins, Map leftTableAlias,
			Map rightTableAlias, boolean rightJoinFlag);

	/////////////////////////////////////////////////////////////////////////////////////

	/**
	 * Does the RDBMS type support array data types
	 * 
	 * @return
	 */
	public abstract boolean allowArrayDatatype();

	/**
	 * Does the RDBMS type support boolean types
	 * @return
	 */
	public abstract boolean allowBooleanDataType();
	
	/**
	 * Get the date time data type used by the RDBMS
	 * @return
	 */
	public abstract String getDateWithTimeDataType();
	
	/**
	 * Does the RDBMS type support blob data type
	 * @return
	 */
	public abstract boolean allowBlobDataType();
	
	/**
	 * Does the RDBMS type support blob java object storage
	 * i.e. - connection.createBlob();
	 * @return
	 */
	public abstract boolean allowBlobJavaObject();
	
	/**
	 * 
	 * @param conn
	 * @param statement
	 * @param object
	 * @param index
	 * @throws SQLException
	 * @throws UnsupportedEncodingException 
	 */
	public abstract void handleInsertionOfBlob(Connection conn, PreparedStatement statement, String object, int index) throws SQLException, UnsupportedEncodingException;
	
	/**
	 * 
	 * @param result
	 * @param key
	 * @return
	 * @throws IOException 
	 * @throws SQLException 
	 */
	public abstract String handleBlobRetrieval(ResultSet result, String key) throws SQLException, IOException;
	
	/**
	 * 
	 * @param result
	 * @param index
	 * @return
	 * @throws IOException 
	 * @throws SQLException 
	 */
	public abstract String handleBlobRetrieval(ResultSet result, int index) throws SQLException, IOException;
	
	/**
	 * 
	 * @param conn
	 * @param statement
	 * @param object
	 * @param index
	 * @param gson
	 * @throws SQLException
	 * @throws UnsupportedEncodingException
	 */
	public abstract void handleInsertionOfClob(Connection conn, PreparedStatement statement, Object object, int index, Gson gson) throws SQLException, UnsupportedEncodingException;
	
	/**
	 * Get the RDBMS type name for blob type (BLOB is ANSI)
	 * @return
	 */
	public abstract String getBlobDataTypeName();
	
	/**
	 * Get the RDBMS type equivalent for clob type (CLOB is ANSI)
	 * @return
	 */
	public abstract String getClobDataTypeName();
	
	/**
	 * Get the RDBMS type equivalent for varchar() type
	 * @return
	 */
	public abstract String getVarcharDataTypeName();
//	
//	/**
//	 * Get the RDBMS type equivalent for varchar(MAX) type
//	 * @return
//	 */
//	
//	public abstract String getVarcharMaxDataTypeName();
//
//	
	/**
	 * Get the RDBMS type equivalent for boolean type
	 * @return
	 */
	public abstract String getBooleanDataTypeName();

	/**
	 * Get the RDBMS type equivalent for int type
	 * @return
	 */
	public abstract String getIntegerDataTypeName();
	
	/**
	 * Get the RDBMS type equivalent for double type
	 * @return
	 */
	public abstract String getDoubleDataTypeName();
	
	/**
	 * Get the RDBMS type equivalent for image data type
	 * @return
	 */
	public abstract String getImageDataTypeName();
	
	/**
	 * Does the RDBMS type support clob java object storage
	 * i.e. - connection.createClob();
	 * @return
	 */
	public abstract boolean allowClobJavaObject();

	/**
	 * Does the engine allow you to add a column to an existing table
	 * 
	 * @return
	 */
	public abstract boolean allowAddColumn();

	/**
	 * Does the engine allow you to add multiple columns in a single statement
	 * 
	 * @return
	 */
	public abstract boolean allowMultiAddColumn();

	/**
	 * Does the engine allow you to rename a column in an existing table
	 * 
	 * @return
	 */
	public abstract boolean allowRedefineColumn();

	/**
	 * Does the engine allow you to drop a column in an existing table
	 * 
	 * @return
	 */
	public abstract boolean allowDropColumn();

	/**
	 * Does the engine allow you to drop multiple columns in a single statement
	 * 
	 * @return
	 */
	public abstract boolean allowMultiDropColumn();
	
	/**
	 * Does the engine allow "CREATE TABLE IF NOT EXISTS " syntax
	 * 
	 * @return
	 */
	public abstract boolean allowsIfExistsTableSyntax();

	/**
	 * Does the engine allow "CREATE INDEX IF NOT EXISTS " syntax
	 * 
	 * @return
	 */
	public abstract boolean allowIfExistsIndexSyntax();

	/**
	 * Does the engine allow "ALTER TABLE xxx ADD COLUMN IF NOT EXISTS" and "ALTER
	 * TABLE xxx DROP COLUMN IF EXISTS" syntax
	 * 
	 * @return
	 */
	public abstract boolean allowIfExistsModifyColumnSyntax();
	
	/**
	 * Does the engine allow " ADD CONSTRAINT IF NOT EXISTS " syntax
	 * 
	 * @return
	 */
	public abstract boolean allowIfExistsAddConstraint();

	/**
	 * Is the savepoint auto released? 
	 * If true, then dont need to release savepoint / method might not be implemented and throw error
	 * @return
	 */
	public abstract boolean savePointAutoRelease();
	
	/////////////////////////////////////////////////////////////////////////

	/*
	 * Create table scripts
	 */

	/**
	 * Create a new table with passed in columns + types + default values
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @return
	 */
	public abstract String createTable(String tableName, String[] colNames, String[] types);

	/**
	 * Create a new table with passed in columns + types + default values
	 * 
	 * @param tableName
	 * @param colToTypeMap
	 * @return
	 */
	public abstract String createTable(String tableName, Map colToTypeMap);
	
	/**
	 * Create a new table with passed in columns + types + default values
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @param defaultValues
	 * @return
	 */
	public abstract String createTableWithDefaults(String tableName, String[] colNames, String[] types, Object[] defaultValues);

	/**
	 * Create a new table with custom constraints
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @param customConstraints
	 * @return
	 */
	public abstract String createTableWithCustomConstraints(String tableName, String[] colNames, String[] types, Object[] customConstraints);

	/**
	 * Create a new table if it does not exist with passed in columns + types +
	 * default values
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @return
	 */
	public abstract String createTableIfNotExists(String tableName, String[] colNames, String[] types);

	/**
	 * Create a new table if it does not exist with passed in columns + types +
	 * default values
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @param defaultValues
	 * @return
	 */
	public abstract String createTableIfNotExistsWithDefaults(String tableName, String[] colNames, String[] types, Object[] defaultValues);

	/**
	 * Create a new table if it does not exist with custom constraints
	 * 
	 * @param tableName
	 * @param colNames
	 * @param types
	 * @param customConstraints
	 * @return
	 */
	public abstract String createTableIfNotExistsWithCustomConstraints(String tableName, String[] colNames, String[] types, Object[] customConstraints);
	
	/*
	 * Drop table scripts
	 */

	/**
	 * Drop a table
	 * 
	 * @param tableName
	 * @return
	 */
	public abstract String dropTable(String tableName);

	/**
	 * Drop a table if it exists
	 * 
	 * @param tableName
	 * @return
	 */
	public abstract String dropTableIfExists(String tableName);

	/*
	 * Alter table scripts
	 */

	/**
	 * Rename a table
	 * 
	 * @param tableName
	 * @param newName
	 * @return
	 */
	public abstract String alterTableName(String tableName, String newTableName);

	/**
	 * Add a new column to an existing table
	 * 
	 * @param tableName
	 * @param newColumn
	 * @param newColType
	 * @return
	 */
	public abstract String alterTableAddColumn(String tableName, String newColumn, String newColType);

	/**
	 * Add a new column to an existing table with default value
	 * 
	 * @param tableName
	 * @param newColumn
	 * @param newColType
	 * @param defaultValue
	 * @return
	 */
	public abstract String alterTableAddColumnWithDefault(String tableName, String newColumn, String newColType, Object defualtValue);

	/**
	 * Add a new column to an existing table if the column does not exist
	 * 
	 * @param tableName
	 * @param newColumn
	 * @param newColType
	 * @return
	 */
	public abstract String alterTableAddColumnIfNotExists(String tableName, String newColumn, String newColType);

	/**
	 * Add a new column to an existing table if the column does not exist with
	 * default value
	 * 
	 * @param tableName
	 * @param newColumn
	 * @param newColType
	 * @param defaultValue
	 * @return
	 */
	public abstract String alterTableAddColumnIfNotExistsWithDefault(String tableName, String newColumn,
			String newColType, Object defualtValue);

	/**
	 * Add new columns to an existing table
	 * 
	 * @param tableName
	 * @param newColumns
	 * @param newColTypes
	 * @return
	 */
	public abstract String alterTableAddColumns(String tableName, String[] newColumns, String[] newColTypes);

	/**
	 * Add new columns to an existing table
	 * 
	 * @param tableName
	 * @param newColToTypeMap
	 * @return
	 */
	public abstract String alterTableAddColumns(String tableName, Map newColToTypeMap);
	
	/**
	 * Add new columns to an existing table with default value
	 * 
	 * @param tableName
	 * @param newColumns
	 * @param newColTypes
	 * @param defaultValue
	 * @return
	 */
	public abstract String alterTableAddColumnsWithDefaults(String tableName, String[] newColumns, String[] newColTypes, Object[] defaultValues);

	/**
	 * Drop a column from an existing table
	 * 
	 * @param tableName
	 * @param columnName
	 * @return
	 */
	public abstract String alterTableDropColumn(String tableName, String columnName);

	/**
	 * Drop a column from an existing table if it exists
	 * 
	 * @param tableName
	 * @param columnName
	 * @return
	 */
	public abstract String alterTableDropColumnIfExists(String tableName, String columnName);

	/**
	 * Drop columns from an existing table
	 * 
	 * @param tableName
	 * @param columnNames
	 * @return
	 */
	public abstract String alterTableDropColumns(String tableName, Collection columnNames);

	/**
	 * Modify a column definition
	 * 
	 * @param tableName
	 * @param columnName
	 * @param dataType
	 * @return
	 */
	public abstract String modColumnType(String tableName, String columnName, String dataType);

	/**
	 * Modify a column definition with default value
	 * 
	 * @param tableName
	 * @param columnName
	 * @param dataType
	 * @param defaultValue
	 * @return
	 */
	public abstract String modColumnTypeWithDefault(String tableName, String columnName, String dataType, Object defualtValue);

	/**
	 * Modify a column definition if it exists
	 * 
	 * @param tableName
	 * @param columnName
	 * @param dataType
	 * @return
	 */
	public abstract String modColumnTypeIfExists(String tableName, String columnName, String dataType);

	/**
	 * Modify a column definition with a default value if it exists
	 * 
	 * @param tableName
	 * @param columnName
	 * @param dataType
	 * @param defaultValue
	 * @return
	 */
	public abstract String modColumnTypeIfExistsWithDefault(String tableName, String columnName, String dataType, Object defualtValue);

	/**
	 * Modify a column to not allow nulls
	 * @param tableName
	 * @param columnName
	 * @param dataType
	 * @return
	 */
	public abstract String modColumnNotNull(String tableName, String columnName, String dataType);
	
	/**
	 * Modify a column name in a table
	 * @param tableName
	 * @param curColName
	 * @param newColName
	 * @return
	 */
	public abstract String modColumnName(String tableName, String curColName, String newColName);
	
	/*
	 * Index
	 */

	/**
	 * Create an index on a table for a given column
	 * 
	 * @param indexName
	 * @param tableName
	 * @param column
	 * @return
	 */
	public abstract String createIndex(String indexName, String tableName, String columnName);

	/**
	 * Create an index on a table with a set of columns
	 * 
	 * @param indexName
	 * @param tableName
	 * @param columns
	 * @return
	 */
	public abstract String createIndex(String indexName, String tableName, Collection columns);

	/**
	 * Create an index on a table for a given column
	 * 
	 * @param indexName
	 * @param tableName
	 * @param column
	 * @return
	 */
	public abstract String createIndexIfNotExists(String indexName, String tableName, String columnName);

	/**
	 * Create an index on a table with a set of columns
	 * 
	 * @param indexName
	 * @param tableName
	 * @param columns
	 * @return
	 */
	public abstract String createIndexIfNotExists(String indexName, String tableName, Collection columns);

	/**
	 * Drop an existing index
	 * 
	 * @param indexName
	 * @param tableName
	 * @return
	 */
	public abstract String dropIndex(String indexName, String tableName);

	/**
	 * Drop an index if it exists
	 * 
	 * @param indexName
	 * @param tableName
	 * @return
	 */
	public abstract String dropIndexIfExists(String indexName, String tableName);

	/**
	 * Insert a row into a table
	 * 
	 * @param tableName
	 * @param columnNames
	 * @param types
	 * @param values
	 * @return
	 */
	public abstract String insertIntoTable(String tableName, String[] columnNames, String[] types, Object[] values);

	/**
	 * Drop all rows from a table
	 * 
	 * @param tableName
	 * @return
	 */
	public abstract String deleteAllRowsFromTable(String tableName);

	/**
	 * Quick syntax to copy a table into another table
	 * 
	 * @param newTableName
	 * @param oldTableName
	 * @return
	 */
	public abstract String copyTable(String newTableName, String oldTableName);
	
	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * Query database scripts
	 */

	/**
	 * Query to execute if has next, the table exists 
	 * The database and schema input is optional and only required by certain engines
	 * 
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String tableExistsQuery(String tableName, String database, String schema);

	/**
	 * Query to execute if has next, the table constraint exists
	 * The database and schema input is optional and only required by certain engines
	 * 
	 * @param constraintName
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String tableConstraintExistsQuery(String constraintName, String tableName, String database, String schema);
	
	/**
	 * Query to execute if has next, the referential constraint exists
	 * The database and schema input is optional and only required by certain engines
	 * 
	 * @param constraintName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String referentialConstraintExistsQuery(String constraintName, String database, String schema);
	
	/**
	 * Query to get the list of column names for a table 
	 * The schema input is optional and only required by certain engines
	 * Returns the column name and column type
	 *
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String getAllColumnDetails(String tableName, String database, String schema);

	/**
	 * Query to execute to get the column details 
	 * Can also imply if the query returns that the column exists
	 * 
	 * @param tableName
	 * @param columnName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String columnDetailsQuery(String tableName, String columnName, String database, String schema);

	/**
	 * Query to get a list of all the indexes in the schema Since indexes are not
	 * unique across tables, this must return (index based) 1) INDEX_NAME 2)
	 * TABLE_NAME The schema input is optional and only required by certain engines
	 * 
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String getIndexList(String database, String schema);

	/**
	 * Query to get the index details Must return data in the following order (index
	 * based) 1) TABLE_NAME 2) COLUMN_NAME The schema input is optional and only
	 * required by certain engines
	 * 
	 * @param indexName
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String getIndexDetails(String indexName, String tableName, String database, String schema);

	/**
	 * Query to get all the indexes on a given table Must return the data in the
	 * following order (index based) 1) INDEX NAME 2) COLUMN_NAME The schema input
	 * is optional and only required by certain engines
	 * 
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public abstract String allIndexForTableQuery(String tableName, String database, String schema);

	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * Utility methods
	 */

	/**
	 * Test on the connection if a table exists Assumption that the conn and sql
	 * util are of same type
	 * 
	 * @param conn
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean tableExists(Connection conn, String tableName, String database, String schema) {
		String query = this.tableExistsQuery(tableName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);

			return rs.next();
		} catch (SQLException e) {
			return false;
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}
	}

	/**
	 * Test on the connection if a table exists Assumption that the conn and sql
	 * util are of same type
	 * 
	 * @param engine
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean tableExists(IDatabaseEngine engine, String tableName, String database, String schema) {
		String query = this.tableExistsQuery(tableName, database, schema);
		IRawSelectWrapper wrapper = null;
		try {
			wrapper = WrapperManager.getInstance().getRawWrapper(engine, query);
			if (wrapper.hasNext()) {
				return true;
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if (wrapper != null) {
				try {
					wrapper.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}

		return false;
	}

	/**
	 * Helper method to see if an index exists based on Query Utility class
	 * 
	 * @param engine
	 * @param indexName
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean indexExists(IDatabaseEngine engine, String indexName, String tableName, String database, String schema) {
		String indexCheckQ = this.getIndexDetails(indexName, tableName, database, schema);
		IRawSelectWrapper wrapper = null;
		try {
			wrapper = WrapperManager.getInstance().getRawWrapper(engine, indexCheckQ);
			if (wrapper.hasNext()) {
				return true;
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if (wrapper != null) {
				try {
					wrapper.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}

		return false;
	}
	
	/**
	 * Test on the connection if a constraint exists
	 * 
	 * @param conn
	 * @param constraintName
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean tableConstraintExists(Connection conn, String constraintName, String tableName, String database, String schema) {
		String query = this.tableConstraintExistsQuery(constraintName, tableName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);

			return rs.next();
		} catch (SQLException e) {
			return false;
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}
	}
	
	/**
	 * Test on the engine if a constraint exists
	 * 
	 * @param engine
	 * @param constraintName
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean tableConstraintExists(IDatabaseEngine engine, String constraintName, String tableName, String database, String schema) {
		String query = this.tableConstraintExistsQuery(constraintName, tableName, database, schema);
		IRawSelectWrapper wrapper = null;
		try {
			wrapper = WrapperManager.getInstance().getRawWrapper(engine, query);
			if (wrapper.hasNext()) {
				return true;
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if (wrapper != null) {
				try {
					wrapper.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}

		return false;
	}

	/**
	 * Test on the connection if a constraint exists
	 * 
	 * @param conn
	 * @param constraintName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean referentialConstraintExists(Connection conn, String constraintName, String database, String schema) {
		String query = this.referentialConstraintExistsQuery(constraintName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);

			return rs.next();
		} catch (SQLException e) {
			return false;
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}
	}
	
	/**
	 * Test on the engine if a constraint exists
	 * 
	 * @param engine
	 * @param constraintName
	 * @param database
	 * @param schema
	 * @return
	 */
	public boolean referentialConstraintExists(IDatabaseEngine engine, String constraintName, String database, String schema) {
		String query = this.referentialConstraintExistsQuery(constraintName, database, schema);
		IRawSelectWrapper wrapper = null;
		try {
			wrapper = WrapperManager.getInstance().getRawWrapper(engine, query);
			if (wrapper.hasNext()) {
				return true;
			}
		} catch (Exception e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			if (wrapper != null) {
				try {
					wrapper.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}

		return false;
	}
	
	/**
	 * Get all the table columns Will return them all upper cased
	 * 
	 * @param conn
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public List getTableColumns(Connection conn, String tableName, String database, String schema) {
		List tableColumns = new ArrayList<>();
		String query = this.getAllColumnDetails(tableName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);
			while (rs.next()) {
				tableColumns.add(rs.getString(1).toUpperCase());
			}
		} catch (SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}

		return tableColumns;
	}
	
	/**
	 * Get all the table columns and their types
	 * @param conn
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public LinkedHashMap> getAllTableColumnTypes(Connection conn, String tableName, String database, String schema) {
		LinkedHashMap> tableColumns = new LinkedHashMap<>();
		String query = this.getAllColumnDetails(tableName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);
			
			ResultSetMetaData metadata = rs.getMetaData();
			int numCols = metadata.getColumnCount();
			while (rs.next()) {
				Map columnDetails = new HashMap<>();
				columnDetails.put(DATA_TYPE, rs.getString(2));
				if(numCols >= 3) {
					columnDetails.put(CHARACTER_MAXIMUM_LENGTH, rs.getString(3));
				}
				if(numCols >= 4) {
					columnDetails.put(NUMERIC_PRECISION, rs.getInt(4));
				}
				if(numCols >= 5) {
					columnDetails.put(NUMERIC_SCALE, rs.getInt(5));
				}
				tableColumns.put(rs.getString(1), columnDetails);
			}
		} catch (SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}

		return tableColumns;
	}
	
	/**
	 * Get all the table columns and their types
	 * @param conn
	 * @param tableName
	 * @param database
	 * @param schema
	 * @return
	 */
	public LinkedHashMap getAllTableColumnTypesSimple(Connection conn, String tableName, String database, String schema) {
		// the final map
		LinkedHashMap columnDetails = new LinkedHashMap<>();
		
		// call the base method
		// and just combine for the varchar max / decimal percision
		LinkedHashMap> allColumnDetails = this.getAllTableColumnTypes(conn, tableName, database, schema);
		for(String col : allColumnDetails.keySet()) {
			Map details = allColumnDetails.get(col);
			String type = details.get(AbstractSqlQueryUtil.DATA_TYPE)+"";
			Object maxCharLength = details.get(AbstractSqlQueryUtil.CHARACTER_MAXIMUM_LENGTH);
			Object numericPrecision = details.get(AbstractSqlQueryUtil.NUMERIC_PRECISION);
			Object numericScale = details.get(AbstractSqlQueryUtil.NUMERIC_SCALE);

			String finalDataType = type;
			if(Utility.isStringType(type) && maxCharLength != null) {
				finalDataType += "(" + maxCharLength + ")";
			} else if(Utility.isDoubleType(type) && numericPrecision != null && numericScale != null) {
				finalDataType += "(" + numericPrecision + "," + numericScale + ")";
			}
			columnDetails.put(col, finalDataType);
		}
		
		return columnDetails;
	}
	
	/**
	 * Get the details for a specific column
	 * @param conn
	 * @param tableName
	 * @param columnName
	 * @param database
	 * @param schema
	 * @return
	 */
	public String[] getColumnDetails(Connection conn, String tableName, String columnName, String database, String schema) {
		String query = this.columnDetailsQuery(tableName, columnName, database, schema);
		Statement stmt = null;
		ResultSet rs = null;
		try {
			stmt = conn.createStatement();
			rs = stmt.executeQuery(query);
			while (rs.next()) {
				return new String[] { rs.getString(1).toUpperCase(), rs.getString(2) };
			}
		} catch (SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			ConnectionUtils.closeAllConnections(null, stmt, rs);
		}

		return null;
	}
	
	public static DatabaseUpdateMetadata performDatabaseAdditions(IRDBMSEngine rdbmsDb, Map> updates, Logger logger) throws InterruptedException {
		DatabaseUpdateMetadata meta = new DatabaseUpdateMetadata();
		
		Set tableExists = new HashSet<>();

		AbstractSqlQueryUtil queryUtil = rdbmsDb.getQueryUtil();
		Map typeConversionMap = queryUtil.getTypeConversionMap();
		
		try {
			Connection conn = rdbmsDb.getConnection();
			String database = rdbmsDb.getDatabase();
			String schema = rdbmsDb.getSchema();
			
			// first run a validation on the input
			for(String tableName : updates.keySet()) {
				logger.info("Validating table " + tableName);
				if(queryUtil.tableExists(rdbmsDb, tableName, database, schema)) {
					logger.info("Validating columns for " + tableName);
					// we are altering - make sure everything is valid
					
					List currentColumns = queryUtil.getTableColumns(conn, tableName, database, schema);
					Set currentColumnsLower = currentColumns.stream().map(s -> s.toLowerCase()).collect(Collectors.toSet());
					
					Set newColumns = updates.get(tableName).keySet();
					Set newColumnsLower = newColumns.stream().map(s -> s.toLowerCase()).collect(Collectors.toSet());
					
					Set overlap = newColumnsLower.stream().filter(s -> currentColumnsLower.contains(s)).collect(Collectors.toSet());
					if(!overlap.isEmpty()) {
						throw new IllegalArgumentException("The following column names already exist: " + overlap);
					}

					tableExists.add(tableName);
				}
			}
		} catch(SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
			throw new IllegalArgumentException("Error validating the input. Detailed message = " + e.getMessage());
		}
		
		// create an owler to track the meta modifications
		WriteOWLEngine owlEngine = rdbmsDb.getOWLEngineFactory().getWriteOWL();
		meta.setOwlEngine(owlEngine);
		
		StringBuilder errorMessages = new StringBuilder();
		// now do the operations
		for(String tableName : updates.keySet()) {

			Map finalColumnUpdates = new HashMap<>(updates.size());

			Map columnUpdates = updates.get(tableName);
			for(String column : columnUpdates.keySet()) {

				String columnType = columnUpdates.get(column).toUpperCase();
				if(typeConversionMap.containsKey(columnType)) {
					columnType = typeConversionMap.get(columnType);
				}

				finalColumnUpdates.put(column, columnType);
			}

			String query = null;
			if(tableExists.contains(tableName)) {
				logger.info("Altering table " + tableName);
				query = queryUtil.alterTableAddColumns(tableName, finalColumnUpdates);
			} else {
				logger.info("Creating table " + tableName);
				query = queryUtil.createTable(tableName, finalColumnUpdates);
			}
			try {
				rdbmsDb.insertData(query);
				
				// add to the owl
				logger.info("Updating metadata for table " + tableName);
				owlEngine.addConcept(tableName, null, null);
				for(String column : finalColumnUpdates.keySet()) {
					String columnType = finalColumnUpdates.get(column);
					owlEngine.addProp(tableName, column, columnType);
				}
				
				// store the metadata
				meta.addSuccessfulUpdate(tableName);
			} catch(Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
				errorMessages.append("Error executing query = '" + query +"' with detailed error = " + e.getMessage() + ". ");
				meta.addFailedUpdates(tableName);
			}
		}
		
		meta.setCombinedErrors(errorMessages.toString());
		
		return meta;
	}

	public static DatabaseUpdateMetadata performDatabaseDeletions(IRDBMSEngine rdbmsDb, Map> updates, Logger logger) throws InterruptedException {
		DatabaseUpdateMetadata meta = new DatabaseUpdateMetadata();
		Set tableDeletes = new HashSet<>();
		AbstractSqlQueryUtil queryUtil = rdbmsDb.getQueryUtil();
		
		// validate that the tables and columns provided exist, and tag tables for removal if all or no columns given
		try {
			Connection conn = rdbmsDb.getConnection();
			String database = rdbmsDb.getDatabase();
			String schema = rdbmsDb.getSchema();
			
			for(String tableName : updates.keySet()) {
				logger.info("Validating table " + tableName);
				if(queryUtil.tableExists(rdbmsDb, tableName, database, schema)) {
					logger.info("Validating columns for " + tableName);
					
					List currentColumns = queryUtil.getTableColumns(conn, tableName, database, schema);
					Set currentColumnsLower = currentColumns.stream().map(s -> s.toLowerCase()).collect(Collectors.toSet());
					
					Set givenColumnsLower = updates.get(tableName).stream().map(s -> s.toLowerCase()).collect(Collectors.toSet());
					
					if(givenColumnsLower.isEmpty()) {
						tableDeletes.add(tableName);
					} else {
						Set gap = givenColumnsLower.stream().filter(s -> !currentColumnsLower.contains(s)).collect(Collectors.toSet());
						if(!gap.isEmpty()) {
							throw new IllegalArgumentException("The following column names do not exist in table " + tableName + ": " + gap);
						}
						if(givenColumnsLower.size() == currentColumnsLower.size()) {
							tableDeletes.add(tableName);
						}
					}
				} else {
					throw new IllegalArgumentException("The following table does not exist:" + tableName);
				}
			}
		} catch(SQLException e) {
			classLogger.error(Constants.STACKTRACE, e);
			throw new IllegalArgumentException("Error validating the input. Detailed message = " + e.getMessage());
		}
		
		// create an owler to track the meta modifications
		WriteOWLEngine owlEngine = rdbmsDb.getOWLEngineFactory().getWriteOWL();
		meta.setOwlEngine(owlEngine);
		
		StringBuilder errorMessages = new StringBuilder();
		// now do the operations
		for(String tableName : updates.keySet()) {
			boolean deleteTable = tableDeletes.contains(tableName);
			String query = null;
			try {
				if(deleteTable) {
					logger.info("Dropping table " + tableName);
					query = queryUtil.dropTable(tableName);
					rdbmsDb.insertData(query);
				} else {
					logger.info("Removing columns from table " + tableName);
					// prefer using multi-drop if supported
					if(queryUtil.allowMultiDropColumn()) {
						query = queryUtil.alterTableDropColumns(tableName, updates.get(tableName));
						rdbmsDb.insertData(query);
					} else {
						for(String columnName : updates.get(tableName)) {
							query = queryUtil.alterTableDropColumn(tableName, columnName);
							rdbmsDb.insertData(query);
						}
					}
				}
				
				// update the owl
				logger.info("Updating metadata for table " + tableName);
				if(deleteTable) {
					owlEngine.removeConcept(tableName);
				} else {
					for(String column : updates.get(tableName)) {
						owlEngine.removeProp(tableName, column);
					}
				}
				// store the metadata
				meta.addSuccessfulUpdate(tableName);
			} catch(Exception e) {
				classLogger.error(Constants.STACKTRACE, e);
				errorMessages.append("Error executing query = '" + query +"' with detailed error = " + e.getMessage() + ". ");
				meta.addFailedUpdates(tableName);
			}
		}
		
		meta.setCombinedErrors(errorMessages.toString());
		return meta;
	}
	
	/**
	 * 
	 * @param value
	 * @return
	 */
	public static int getConnectionTypeValueFromString(String value) {
		if(value == null) {
			return -1;
		}
		
		if(value.equalsIgnoreCase(Constants.TRANSACTION_NONE)) {
			return Connection.TRANSACTION_NONE;
		} else if(value.equalsIgnoreCase(Constants.TRANSACTION_READ_UNCOMMITTED)) {
			return Connection.TRANSACTION_READ_UNCOMMITTED;
		} else if(value.equalsIgnoreCase(Constants.TRANSACTION_READ_COMMITTED)) {
			return Connection.TRANSACTION_READ_COMMITTED;
		} else if(value.equalsIgnoreCase(Constants.TRANSACTION_REPEATABLE_READ)) {
			return Connection.TRANSACTION_REPEATABLE_READ;
		} else if(value.equalsIgnoreCase(Constants.TRANSACTION_SERIALIZABLE)) {
			return Connection.TRANSACTION_SERIALIZABLE;
		}
		
		return -1;
	}

	/////////////////////////////////////////////////////////////////////////////////////

	/*
	 * These are older methods
	 * Need to come back and see where to 
	 * utilize these/clean up
	 */

	public String getDialectSelectRowCountFrom(String tableName, String whereClause) {
		String query = "SELECT COUNT(*) as ROW_COUNT FROM " + tableName;
		if (whereClause.length() > 0) {
			query += " WHERE " + whereClause;
		}
		return query;
	}

	public String getDialectMergeStatement(String tableKey, String insertIntoClause, List columnList,
			HashMap whereValues, String fkVal, String whereClause) {
		ArrayList subqueries = new ArrayList<>();
		String query = "INSERT INTO " + tableKey + " (" + insertIntoClause + ") SELECT DISTINCT ";
		for (String column : columnList) {
			String tempColumnName = column + "TEMP";
			String subquery = "(SELECT DISTINCT " + column + " FROM " + tableKey + " WHERE " + whereClause;
			String tempquery = subquery + " union select null where not exists" + subquery + ")) AS " + tempColumnName;
			subqueries.add(tempquery);
			query += tempColumnName + "." + column + " AS " + column + ",";
		}
		for (String whereKey : whereValues.keySet()) {
			query += whereValues.get(whereKey) + " AS " + whereKey + ", ";
		}
		query += fkVal + " FROM " + tableKey;
		for (int i = 0; i < subqueries.size(); i++) {
			query += ", " + subqueries.get(i);
		}
		return query;
	}

	public String hashColumn(String tableName, String[] columns){
		throw new UnsupportedOperationException();
	}


	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////

//	public static void main(String[] args) throws Exception {
//		TestUtilityMethods.loadAll("C:\\workspace2\\Semoss_Dev\\RDF_Map.prop");
//
//		RDBMSNativeEngine security = (RDBMSNativeEngine) Utility.getEngine("security");
//		AbstractSqlQueryUtil util = security.getQueryUtil();
//		IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(security,
//				"SELECT * FROM PRAGMA_TABLE_INFO('USER') WHERE NAME='email'");
//		while (wrapper.hasNext()) {
//			logger.debug(wrapper.next());
//		}
//	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy