Bug 133090 Unable to set parameter value based on name
I have a data set definition that contains a query with 1 parameter. The parameter definition within the rptdesign file is as follows: <list-property name="parameters"> <structure> <property name="name">param1</property> <property name="dataType">string</property> <property name="position">-1</property> <expression name="defaultValue">'130'</expression> <property name="isInput">true</property> </structure> </list-property> The ODA driver I am testing with does not support setString() with position argument (it only supports setString() by name), so setting position=1 above is not an option. public void setString( int parameterId, String value ) throws OdaException { throw new UnsupportedOperationException( "AcSapMDQueryStatement.setString( int parameterId, String value )" ); } So I specified position=-1 above and hope the parameter-value-setting will be done by name. But this still fails. Tracing the code, I found that in following code in birt.data.engine.odaconsumer.ParameterHint.setPosition( int ), position value being -1 will cause an exception thrown: public void setPosition( int position ) { final String methodName = "setPosition"; if( position < 1 ) { String localizedMessage = DataResourceHandle.getInstance().getMessage( ResourceConstants.PARAMETER_POSITION_CANNOT_BE_LESS_THAN_ONE ); sm_logger.logp( Level.SEVERE, sm_className, methodName, "Invalid parameter position {0} ", new Integer( position ) ); throw new IllegalArgumentException( localizedMessage ); } m_position = position; } Here is the call stack: ParameterHint.setPosition(int) line: 100 DataSourceQuery.addParameterDefns() line: 371 DataSourceQuery.prepare() line: 309 PreparedOdaDSQuery$OdaDSQueryExecutor.prepareOdiQuery() line: 278 PreparedOdaDSQuery$OdaDSQueryExecutor(QueryExecutor).prepareExecution(IQueryResults, Scriptable) line: 238 PreparedQuery.doPrepare(IQueryResults, Scriptable, QueryExecutor, PreparedDataSourceQuery) line: 323 PreparedOdaDSQuery(PreparedDataSourceQuery).execute(IQueryResults, Scriptable) line: 174 PreparedOdaDSQuery(PreparedDataSourceQuery).execute(Scriptable) line: 157 DteDataEngine.execute(IBaseQueryDefinition) line: 205 As discussed with Gary Xue, there are two issues here: - Currently the default position value within ParameterHint is left as-is during initialization so it becomes 0: public class ParameterHint { private String m_name; private int m_position; .... } It should be initialized to -1. - Another issue is in the logic of org.eclipse.birt.data.engine.executor.DataSourceQuery.addParameterDefns() function. The choice of whether to set parameter value by position or by name is determined by the parameterHint's position value (see red text below): private void addParameterDefns() throws DataException { if ( this.paramDefnAndValBindings == null ) return; // nothing to add // iterate thru the collection to add parameter hints Iterator list = this.paramDefnAndValBindings.iterator( ); while ( list.hasNext( ) ) { ParamDefnAndValBinding paramDefnAndValBinding = (ParamDefnAndValBinding) list.next( ); IParameterDefinition parameterDefn = paramDefnAndValBinding.getParamDefn(); ParameterHint parameterHint = new ParameterHint( parameterDefn.getName(), parameterDefn.isInputMode(), parameterDefn.isOutputMode() ); parameterHint.setPosition( parameterDefn.getPosition( ) ); // following data types is not supported by odaconsumer currently Class dataTypeClass = DataType.getClass( parameterDefn.getType( ) ); if ( dataTypeClass == DataType.AnyType.class || dataTypeClass == Boolean.class || dataTypeClass == Blob.class ) { dataTypeClass = null; } parameterHint.setDataType( dataTypeClass ); parameterHint.setIsInputOptional( parameterDefn.isInputOptional( ) ); parameterHint.setDefaultInputValue( paramDefnAndValBinding.getValue() ); parameterHint.setIsNullable( parameterDefn.isNullable() ); odaStatement.addParameterHint( parameterHint ); //If the parameter is input parameter then add it to input value list. if ( parameterHint.isInputMode( ) && parameterHint.getDefaultInputValue( ) != null ) { Object inputValue = convertToValue( parameterHint.getDefaultInputValue( ), parameterDefn.getType( ) ); if ( parameterHint.getPosition( ) != -1 ) this.setInputParamValue( parameterHint.getPosition( ), inputValue ); else this.setInputParamValue( parameterHint.getName( ), inputValue ); } } this.setInputParameterBinding(); } The line above that assigns the position value: parameterHint.setPosition( parameterDefn.getPosition( ) ); This line should be changed to if ( parameterDefn.getPosition() != -1 ) parameterHint.setPosition( parameterDefn.getPosition( ) ); This will make sure the code does not trigger the sanity check within parameterHint.setPosition().
data/org.eclipse.birt.data/src/org/eclipse/birt/data/engine/executor/DataSourceQuery.java
*  
 *************************************************************************
 */ 
package org.eclipse.birt.data.engine.executor;
import java.sql.Blob;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.birt.core.data.DataType;
import org.eclipse.birt.core.data.DataTypeUtil;
import org.eclipse.birt.data.engine.api.IParameterDefinition;
import org.eclipse.birt.data.engine.core.DataException;
import org.eclipse.birt.data.engine.executor.dscache.DataSetResultCache;
import org.eclipse.birt.data.engine.executor.transform.CachedResultSet;
import org.eclipse.birt.data.engine.i18n.ResourceConstants;
import org.eclipse.birt.data.engine.impl.ParamDefnAndValBinding;
import org.eclipse.birt.data.engine.odaconsumer.ColumnHint;
import org.eclipse.birt.data.engine.odaconsumer.ParameterHint;
import org.eclipse.birt.data.engine.odaconsumer.PreparedStatement;
import org.eclipse.birt.data.engine.odaconsumer.ResultSet;
import org.eclipse.birt.data.engine.odi.IDataSource;
import org.eclipse.birt.data.engine.odi.IDataSourceQuery;
import org.eclipse.birt.data.engine.odi.IParameterMetaData;
import org.eclipse.birt.data.engine.odi.IPreparedDSQuery;
import org.eclipse.birt.data.engine.odi.IResultClass;
import org.eclipse.birt.data.engine.odi.IResultIterator;
/**
 *	Structure to hold info of a custom field. 
 */
final class CustomField