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

prerna.reactor.imports.RdbmsImporter Maven / Gradle / Ivy

The newest version!
package prerna.reactor.imports;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import prerna.algorithm.api.ITableDataFrame;
import prerna.algorithm.api.SemossDataType;
import prerna.ds.OwlTemporalEngineMeta;
import prerna.ds.rdbms.AbstractRdbmsFrame;
import prerna.ds.util.RdbmsQueryBuilder;
import prerna.engine.api.IHeadersDataRow;
import prerna.engine.api.IRawSelectWrapper;
import prerna.query.querystruct.HardSelectQueryStruct;
import prerna.query.querystruct.SelectQueryStruct;
import prerna.query.querystruct.selectors.IQuerySelector;
import prerna.query.querystruct.selectors.QueryColumnSelector;
import prerna.sablecc2.om.Join;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.execptions.SemossPixelException;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.util.Constants;
import prerna.util.Utility;
import prerna.util.sql.AbstractSqlQueryUtil;

public class RdbmsImporter extends AbstractImporter {

	private static final Logger logger = LogManager.getLogger(RdbmsImporter.class);

	private AbstractRdbmsFrame dataframe;
	private AbstractSqlQueryUtil queryUtil;
	private SelectQueryStruct qs;
	private Iterator it;
	
	public RdbmsImporter(AbstractRdbmsFrame dataframe, SelectQueryStruct qs) {
		this.dataframe = dataframe;
		this.queryUtil = dataframe.getQueryUtil();
		this.qs = qs;
		try {
			this.it = ImportUtility.generateIterator(this.qs, this.dataframe);
		} catch (Exception e) {
			logger.error(Constants.STACKTRACE, e);
			throw new SemossPixelException(
					new NounMetadata("Error occurred executing query before loading into frame", 
							PixelDataType.CONST_STRING, PixelOperationType.ERROR));
		}
	}
	
	public RdbmsImporter(AbstractRdbmsFrame dataframe, SelectQueryStruct qs, Iterator it) {
		this.dataframe = dataframe;
		this.queryUtil = dataframe.getQueryUtil();
		this.qs = qs;
		this.it = it;
		// generate the iterator
		if(this.it == null) {
			try {
				this.it = ImportUtility.generateIterator(this.qs, this.dataframe);
			} catch (Exception e) {
				logger.error(Constants.STACKTRACE, e);
				throw new SemossPixelException(
						new NounMetadata("Error occurred executing query before loading into frame", 
								PixelDataType.CONST_STRING, PixelOperationType.ERROR));
			}
		}
	}

	@Override
	public void insertData() {
		ImportUtility.parseQueryStructToFlatTable(this.dataframe, this.qs, this.dataframe.getName(), this.it, false);
		processInsertData();
	}
	
	@Override
	public void insertData(OwlTemporalEngineMeta metaData) {
		this.dataframe.setMetaData(metaData);
		processInsertData();
	}
	
	/**
	 * Based on the metadata that was set (either through QS processing or directly passed in)
	 * Insert data from the iterator that the QS contains
	 */
	private void processInsertData() {
		// get the meta information from the new metadata
		Map rawDataTypeMap = this.dataframe.getMetaData().getHeaderToTypeMap();
		
		// TODO: this is annoying, need to get the frame on the same page as the meta
		Map dataTypeMap = new HashMap();
		for(String rawHeader : rawDataTypeMap.keySet()) {
			dataTypeMap.put(rawHeader.split("__")[1], rawDataTypeMap.get(rawHeader));
		}
		
		// use the base table name
		String tableName = this.dataframe.getName();
		try {
			this.dataframe.addRowsViaIterator(this.it, tableName, dataTypeMap);
		} catch(Exception e) {
			logger.error(Constants.STACKTRACE, e);
			// if we have an error
			// just make sure the headers are all there
			int size = dataTypeMap.size();
			String[] newHeaders = new String[size];
			String[] newTypes = new String[size];
			int counter = 0;
			for(String header : dataTypeMap.keySet()) {
				newHeaders[counter] = header;
				newTypes[counter] = this.dataframe.getQueryUtil().cleanType(dataTypeMap.get(header).toString());
				counter++;
			}
			try {
				this.dataframe.getBuilder().alterTableNewColumns(tableName, newHeaders, newTypes);
			} catch (Exception ex) {
				logger.error(Constants.STACKTRACE, ex);
			}
		}
	}

	@Override
	public ITableDataFrame mergeData(List joins) {
		// need to determine if we will do a 
		// merge vs. a join on the frame
		// we will do this based on 
		String[] origHeaders = this.dataframe.getColumnHeaders();
		List newSelectors = getSelectors();
		int numNew = newSelectors.size();
		String[] newHeaders = new String[numNew];
		for(int i = 0; i < numNew; i++) {
			newHeaders[i] = newSelectors.get(i).getAlias();
		}
		boolean performMerge = allHeadersAccounted(origHeaders, newHeaders, joins);
		if(performMerge) {
			return performMerge(joins, origHeaders, newHeaders);
		} else {
			return performJoin(joins);
		}
	}
	
	private List getSelectors() {
		if(this.qs instanceof HardSelectQueryStruct) {
			// we are querying a frame or engine
			// it is a raw select wrapper
			String[] headers = ((IRawSelectWrapper) this.it).getHeaders();
			List selectors = new Vector();
			for(int i = 0; i < headers.length; i++) {
				selectors.add(new QueryColumnSelector(headers[i]));
			}
			return selectors;
		} else {
			return qs.getSelectors();
		}
	}
	
	private ITableDataFrame performMerge(List joins, String[] origHeaders, String[] newHeaders) {
		// need to know the starting headers
		// we will lose this once we synchronize the frame with the new header info
		String leftTableName = this.dataframe.getName();
//		testGridData("select * from " + leftTableName);
		Map leftTableTypes = this.dataframe.getMetaData().getHeaderToTypeMap();
		
		// define a new temporary table with a random name
		// we will flush out the iterator into this table
		String rightTableName = Utility.getRandomString(6);
		Map rightTableTypes = ImportUtility.getTypesFromQs(this.qs, this.it);
		this.dataframe.addRowsViaIterator(this.it, rightTableName, rightTableTypes);
		
		String mergeTable = rightTableName;
		
		//flag added since createNewTableFromJoiningTables function is called for merge and join, added a default flag here as well
		boolean rightJoinFlag = false;
		
		// if the size is not the same
		// we need to do a join to ensure that what we merge
		// we get the new headers that are required
		if(origHeaders.length != newHeaders.length) {
			// now, i will make another temp table
			// where i do an inner join between the current table and the above new table
			// but we want to remove any of the duplicate headers not present in the join
			String innerJoinTable = Utility.getRandomString(6);
			Set removeHeaders = new HashSet();
			MAIN_LOOP : for(String rightTableHeader : rightTableTypes.keySet()) {
				for(Join join : joins) {
					if(rightTableHeader.equals(join.getRColumn())) {
						// this is a join column
						// do not remove it
						continue MAIN_LOOP;
					}
				}
				for(String leftTableHeader : leftTableTypes.keySet()) {
					String leftTableAlias = leftTableHeader;
					if(leftTableAlias.contains("__")) {
						leftTableAlias = leftTableHeader.split("__")[1];
					}
					if(leftTableAlias.equals(rightTableHeader)) {
						// we found a match that isn't in the join
						// remove it
						removeHeaders.add(leftTableHeader);
					}
				}
			}
			leftTableTypes.keySet().removeAll(removeHeaders);
			String joinQuery = queryUtil.createNewTableFromJoiningTables(innerJoinTable, leftTableName, leftTableTypes, 
					rightTableName, rightTableTypes, joins, new HashMap(), new HashMap(), rightJoinFlag);
			try {
				this.dataframe.getBuilder().runQuery(joinQuery);
			} catch (Exception e) {
				logger.error(Constants.STACKTRACE, e);
			}
//			testGridData("select * from " + innerJoinTable);
			mergeTable = innerJoinTable;
			
			// now drop the right table since we will only be using the innerJoinTable now
			try {
				this.dataframe.getBuilder().runQuery(queryUtil.dropTable(rightTableName));
			} catch (Exception e) {
				logger.error(Constants.STACKTRACE, e);
			}
		}
		// now we merge the 2 tables together
		// need to get this in the right order
		origHeaders = this.dataframe.getBuilder().getHeaders(leftTableName);
		String[] keyColumns = new String[leftTableTypes.keySet().size()];
		int counter = 0;
		for(String col : leftTableTypes.keySet()) {
			keyColumns[counter] = col.split("__")[1];
			counter++;
		}
		try {
			// the new headers are the keys for the merge
			this.dataframe.getBuilder().runQuery(RdbmsQueryBuilder.makeMergeIntoQuery(leftTableName, mergeTable, keyColumns, origHeaders));
		} catch (Exception e) {
			logger.error(Constants.STACKTRACE, e);
		}
//		testGridData("select * from " + leftTableName);
			
		// now drop the merge table
		try {
			this.dataframe.getBuilder().runQuery(this.dataframe.getQueryUtil().dropTable(mergeTable));
		} catch (Exception e) {
			logger.error(Constants.STACKTRACE, e);
		}
		
		return this.dataframe;
	}

	private AbstractRdbmsFrame performJoin(List joins) {
		// need to know the starting headers
		// we will lose this once we synchronize the frame with the new header info
		Map leftTableTypes = this.dataframe.getMetaData().getHeaderToTypeMap();

		// get the columns and types of the new columns we are about to add
		Map rightTableTypes = ImportUtility.getTypesFromQs(this.qs, this.it);

		// these will only be used if we have an outer join!
		String leftJoinReturnTableName = null;
		String rightJoinReturnTableName = null;

		// we will also figure out if there are columns that are repeated
		// but are not join columns
		// so we need to alias them
		Set leftTableHeaders = leftTableTypes.keySet();
		Set rightTableHeaders = rightTableTypes.keySet();
		Set rightTableJoinCols = getRightJoinColumns(joins);
		Map rightTableAlias = new HashMap();
		// note, we are not going to modify the existing headers
		// even though the query builder code allows for it
		Map leftTableAlias = new HashMap();
		for(String leftTableHeader : leftTableHeaders) {
			if(leftTableHeader.contains("__")) {
				leftTableHeader = leftTableHeader.split("__")[1];
			}
			// instead of making the method return a boolean and then having to perform
			// another ignore case match later on
			// we return the match and do a null check
			String dupRightTableHeader = setIgnoreCaseMatch(leftTableHeader, rightTableHeaders, rightTableJoinCols);
			if(dupRightTableHeader != null) {
				rightTableAlias.put(dupRightTableHeader, leftTableHeader + "_1");
			}
		}

		// we will also create a random table name as the return of this operation
		// dont worry, we will override this back to normal once we are done
		String returnTableName = Utility.getRandomString(6);
		String leftTableName = this.dataframe.getName();
		// we will make the right table from the iterator
		// this will happen in the try catch
		String rightTableName = Utility.getRandomString(6);
		
		boolean successfullyAddedData = true;
		
		//flag added to check for right join , default is false and if its right join this is updated to true.
		// this flag is to make sure the outer join has no issues
		boolean rightJoinFlag = false;
		
		try {
			// now, flush the iterator into the right table 
			this.dataframe.addRowsViaIterator(this.it, rightTableName, rightTableTypes);
	
			// improve performance
			generateIndicesOnJoinColumns(leftTableName, rightTableName, joins);
			
			// merge the two tables together
			if(joins.get(0).getJoinType().equals("outer.join")) {
				// h2 does not support full outer join
				// so we will do a left outer join
				// and then a right outer join
				// and then union them together
				joins.get(0).setJoinType("left.outer.join");
				leftJoinReturnTableName = Utility.getRandomString(6);
				String leftOuterJoin = queryUtil.createNewTableFromJoiningTables(leftJoinReturnTableName, leftTableName, leftTableTypes, rightTableName, 
						rightTableTypes, joins, leftTableAlias, rightTableAlias, rightJoinFlag);
				this.dataframe.getBuilder().runQuery(leftOuterJoin);
				
				joins.get(0).setJoinType("right.outer.join");
				rightJoinReturnTableName = Utility.getRandomString(6);
				String rightOuterJoin = queryUtil.createNewTableFromJoiningTables(rightJoinReturnTableName, leftTableName, leftTableTypes, rightTableName, 
						rightTableTypes, joins, leftTableAlias, rightTableAlias, rightJoinFlag);
				this.dataframe.getBuilder().runQuery(rightOuterJoin);

				// run a union between the 2 tables
				String unionQuery = "CREATE TABLE " + returnTableName + " AS (SELECT * FROM " + leftJoinReturnTableName + " UNION " + " SELECT * FROM " + rightJoinReturnTableName + ")";
				this.dataframe.getBuilder().runQuery(unionQuery);
			} else {
				// this is the normal case
				// we just need to make a basic join query
				rightJoinFlag = true;
				String joinQuery = queryUtil.createNewTableFromJoiningTables(returnTableName, leftTableName, leftTableTypes, rightTableName, 
						rightTableTypes, joins, leftTableAlias, rightTableAlias, rightJoinFlag);
				this.dataframe.getBuilder().runQuery(joinQuery);
			}
//		} catch(EmptyIteratorException e) {
//			// no data was returned from iterator
//			// so the right table wasn't created
//			successfullyAddedData = false;
//			// if we have a non-inner join
//			// add the columns into the frame
//			if(!joins.get(0).getJoinType().equals("inner.join")) {
//				// add columns onto the frame
//				String alterQuery = RdbmsQueryBuilder.alterMissingColumns(leftTableName, rightTableTypes, joins, rightTableAlias, this.dataframe.getQueryUtil());
//				try {
//					this.dataframe.getBuilder().runQuery(alterQuery);
//				} catch (Exception ex) {
//					// if this messes up... not sure what to do now 
//					classLogger.error(Constants.STACKTRACE, ex);
//				}
//			} else {
//				// continue the message up
//				// if we have an inner join and no data
//				// result will be null and we dont want that
//				throw new EmptyIteratorException("Query returned no data. Cannot add new data with existing grid");
//			}
		}
		catch(Exception e) {
			logger.error(Constants.STACKTRACE, e);
			throw new IllegalArgumentException(e.getMessage());
		} finally {
			// now drop the 2 join tables if we used an outer join
			if(leftJoinReturnTableName != null) {
				try {
					this.dataframe.getBuilder().runQuery(queryUtil.dropTable(leftJoinReturnTableName));
				} catch (Exception e) {
					logger.error(Constants.STACKTRACE, e);
				}
			}
			if(rightJoinReturnTableName != null) {
				try {
					this.dataframe.getBuilder().runQuery(queryUtil.dropTable(rightJoinReturnTableName));
				} catch (Exception e) {
					logger.error(Constants.STACKTRACE, e);
				}
			}
			
			// also, drop the temporary right table we created
			if(successfullyAddedData) {
				try {
					this.dataframe.getBuilder().runQuery(queryUtil.dropTable(rightTableName));
				} catch (Exception e) {
					logger.error(Constants.STACKTRACE, e);
				}
			}
		}
		
		// if we can here without any errors
		// then the sql join was sucessful
		// drop the left table and the right table
		// then rename the return table to be the left table name
		if(successfullyAddedData) {
			try {
				this.dataframe.getBuilder().runQuery(queryUtil.dropTable(leftTableName));
			} catch (Exception e) {
				logger.error(Constants.STACKTRACE, e);
			}
			try {
				this.dataframe.getBuilder().runQuery(queryUtil.alterTableName(returnTableName, leftTableName));
			} catch (Exception e) {
				logger.error(Constants.STACKTRACE, e);
			}
		}
		
		// merge the QS so it is accurate
		// but we need to consider if there were headers that have been modified
		for(String rightCol : rightTableAlias.keySet()) {
			List selectors = this.qs.getSelectors();
			int numSelectors = selectors.size();
			for(int i = 0; i < numSelectors; i++) {
				IQuerySelector selector = selectors.get(i);
				String alias =  selector.getAlias();
				if(alias.equals(rightCol)) {
					selector.setAlias(rightTableAlias.get(rightCol));
				}
			}
		}

		updateMetaWithAlias(this.dataframe, this.qs, this.it, joins, rightTableAlias);
		return this.dataframe;
	}
	
	/**
	 * Improve performance by adding indices on the join columns between 2 tables
	 * @param leftTable
	 * @param rightTable
	 * @param joins
	 */
	private void generateIndicesOnJoinColumns(String leftTable, String rightTable, List joins) {
		for(Join j : joins) {
			String leftCol = j.getLColumn();
			if(leftCol.contains("__")) {
				leftCol = leftCol.split("__")[1];
			}
			String rightCol = j.getRColumn();
			if(rightCol.contains("__")) {
				rightCol = rightCol.split("__")[1];
			}
			generateIndices(leftTable, leftCol);
			generateIndices(rightTable, rightCol);
		}
	}
	
	private void generateIndices(String tableName, String columnName) {
		try {
			this.dataframe.getBuilder().addColumnIndex(tableName, columnName);
		} catch (Exception e) {
			logger.error(Constants.STACKTRACE, e);
		}
	}
	
//	private void testGridData(String query) {
//		// print out this new table for testing
//		System.out.println(query);
//		System.out.println(query);
//		System.out.println(query);
//		System.out.println(query);
//		ResultSet rs = this.dataframe.execQuery(query);
//		
//		try {
//			ResultSetMetaData rsmd = rs.getMetaData();
//			int numCols = rsmd.getColumnCount();
//			String[] columns = new String[numCols];
//			for(int i = 0; i < numCols; i++) {
//				columns[i] = rsmd.getColumnName(i+1);
//			}
//			System.out.println(Arrays.toString(columns));
//			while(rs.next()) {
//				Object[] data = new Object[numCols];
//				for(int i = 0; i < numCols; i++) {
//					data[i] = rs.getObject(i+1);
//				}
//				System.out.println(Arrays.toString(data));
//			}
//		} catch (SQLException e) {
//			logger.error(Constants.STACKTRACE, e);
//		} finally {
//			try {
//				rs.close();
//			} catch (SQLException e) {
//				logger.error(Constants.STACKTRACE, e);
//			}
//		}
//	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy