/* * (C) Copyright IBM Corp. 2008 * * LICENSE: Eclipse Public License v1.0 * http://www.eclipse.org/legal/epl-v10.html */ package com.ibm.db2j; import java.util.Arrays; import com.ibm.gaiandb.DataSourcesManager; import com.ibm.gaiandb.GaianDBConfig; import com.ibm.gaiandb.Logger; import com.ibm.gaiandb.Util; import com.ibm.gaiandb.diags.GDBMessages; /** * @author DavidVyvyan */ public class GaianQuery extends GaianTable { // Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice. public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2008"; private static final Logger logger = new Logger( "GaianQuery", 20 ); private String queryArguments = null; private String[] targetCIDs = null; // target RDBMS connection IDs private String splitColumnRanges = null; private static final String SOURCELISTARG_KEY = "SOURCELIST"; private static final String SPLITCOLUMN_KEY = "SPLITCOLUMN"; public GaianQuery( String sqlQuery ) throws Exception { this( sqlQuery, null, null, null, null ); //, null, new Integer(-1)); //, new Integer(-1) ); } // public GaianQuery( String sqlQuery, Integer maxSteps ) throws Exception { // this( sqlQuery, null, null, null, null, null, new Integer(-1), maxSteps); // } public GaianQuery( String sqlQuery, String tableArguments ) throws Exception { this( sqlQuery, tableArguments, null, null, null ); //, null, new Integer(-1)); //, new Integer(-1) ); } // public GaianQuery( String sqlQuery, String tableArguments, Integer maxSteps ) throws Exception { // this( sqlQuery, tableArguments, null, null, null, null, new Integer(-1), maxSteps ); // } public GaianQuery( String sqlQuery, String tableArguments, String queryArguments ) throws Exception { this( sqlQuery, tableArguments, queryArguments, null, null ); //, null, new Integer(-1)); //, new Integer(-1) ); } public GaianQuery( String sqlQuery, String tableArguments, String queryArguments, String tableDef ) throws Exception { this( sqlQuery, tableArguments, queryArguments, tableDef, null ); //, null, new Integer(-1)); //, new Integer(-1) ); } // public GaianQuery( String sqlQuery, String tableArguments, String queryArguments, Integer maxSteps ) throws Exception { // this( sqlQuery, tableArguments, queryArguments, null, null, null, new Integer(-1), maxSteps ); // } public GaianQuery( String sqlQuery, String tableArguments, String queryArguments, String tableDef, String forwardingNode) throws Exception { //, String queryID, Integer steps) throws Exception { //, Integer maxSteps) throws Exception { super( sqlQuery, tableArguments, tableDef, forwardingNode ); //, queryID, steps); //, maxSteps ); this.queryArguments = null == queryArguments || 1 > queryArguments.trim().length() ? null : queryArguments.trim(); getQueryDetails().put( QRY_IS_GAIAN_QUERY, QRY_IS_GAIAN_QUERY ); ltSignature = this.logicalTableName + ( this.tableArguments + this.queryArguments + this.tableDefinition + this.forwardingNode ).replaceAll("\\s", " "); // logger.logInfo("tableDefinition: " + this.tableDefinition); if ( null == this.tableDefinition ) { // note do not use just 'tableDef', must use GaianTable's tabeDefinition // We have to validate that the data source exists in one of our source list sources, // so that we can propagate the table def - if not the Exception MUST be thrown here - so that // the resulting InvocationTargetException can carry our exception back. logger.logInfo( "Setting up sub query meta data using subquery and first data source (or local gaiandb if none was specified)" ); targetCIDs = getQueriedDBs(); logger.logInfo( "Gaian Query Source List = " + Arrays.asList(targetCIDs) ); // NOTE: THIS CALL MAY THROW AN EXCEPTION: If the source/table is not defined or if another Exception occurs // This will be seen in the InvocationTargetException that is propagated back to the client. try { logicalTableRSMD = DataSourcesManager.deriveMetaDataFromSubQuery( sqlQuery, targetCIDs[0], targetCIDs[0]+withProvenance+""+isExplain ); } catch ( Exception e ) { final String msg = "Unable to resolve result structure for subquery \"" + sqlQuery + "\" on RDBMS db: "+targetCIDs[0]+" (aborting query)"; logger.logWarning(GDBMessages.ENGINE_SUBQUERY_ERROR, msg + ": " + e); throw new Exception(IEX_PREFIX + " " + msg + ": " + e.getMessage(), e); } } } public String getQueryArguments() { if ( null==queryArguments ) return ""; return queryArguments; } // The function below is used to pick out a different range of rows on different nodes, e.g. syntax wd be: // select * from new com.ibm.db2j.GaianQuery( // 'SELECT item, price FROM stock', '', 'SPLITCOLUMN ID host1 0 10 host2 20 30 host3 40 50') Q public String getSplitColumnRangeSQL() { if ( null==splitColumnRanges ) return null; // String[] elmts = splitColumnRanges.split(" "); // return elmts[0] + " between " + elmts[1] + " and " + elmts[2]; String[] elmts = splitColumnRanges.split(" "); for ( int i=1; i<elmts.length; i++ ) if ( elmts[i].equals(GaianDBConfig.getGaianNodeID()) ) return elmts[0] + " between " + elmts[i+1] + " and " + elmts[i+2]; return null; } private String[] getQueriedDBs() { String sourceList = null; if ( null != queryArguments ) { String[] options = Util.splitByCommas( queryArguments ); for (int i=0; i<options.length; i++) { String option = options[i]; int idx = option.indexOf('='); if ( -1 < idx ) { String key = option.substring(0,idx).trim(), val = option.substring(idx+1).trim(); // Validate key - // only use so far is for SOURCELIST, which is used to specify more than just the local Derby database // to run the query against if ( key.equals( SOURCELISTARG_KEY ) ) sourceList = val; else if ( key.equals( SPLITCOLUMN_KEY ) ) splitColumnRanges = val; else logger.logInfo("Ignoring unknown GaianQuery arg: " + key); } else if ( option.equalsIgnoreCase("unzip") ) unzipLobs = true; } } return GaianDBConfig.getSourceListSources( sourceList ); } protected void setupMetaDataAndDataSourcesArray(boolean isMetaDataLookupOnly) throws Exception { if ( null == logicalTableRSMD ) { // We *MUST* have a table definition defined (otherwise an exception would have aborted execution in the constructor) logger.logInfo( "Setting up sub query meta data using tableDefinition: " + tableDefinition ); logicalTableRSMD = DataSourcesManager.deriveMetaDataFromTableDef( tableDefinition, null, withProvenance+""+isExplain ); } // if ( isPropagatedQuery ) logicalTableRSMD.includeNullColumns(); // else logicalTableRSMD.excludeNullColumns(); if ( !isMetaDataLookupOnly ) { logger.logInfo( "Setting up sub query vti array" ); // if ( null == dataSources ) { // we didnt need the data source list until now because we had the table def to give us meta-data - but now we do targetCIDs = getQueriedDBs(); logger.logInfo( "Gaian Query Source List = " + Arrays.asList(targetCIDs) ); // } // if ( null != queryArguments && splitColumnRanges != null ) { // // int start = queryArguments.indexOf( splitColumnRanges ); // start = queryArguments.indexOf(' ', start); // int end = queryArguments.indexOf(' ', start+1); // end = queryArguments.indexOf(' ', end+1); // end = queryArguments.indexOf(' ', end+1); // // // Delete the first range from the query arguments, which will be forwarded on with the query. // // Note that we still have a splitColumnRanges argument starting with the range we just deleted, so we can use it. // queryArguments = new StringBuffer(queryArguments).delete(start, end).toString(); // } allLogicalTableVTIs = DataSourcesManager.constructDynamicDataSources( DataSourcesManager.SUBQUERY_PREFIX, logicalTableRSMD, targetCIDs ); logger.logInfo( "Built subquery vtis: " + Arrays.asList(allLogicalTableVTIs) ); } } }