=== modified file 'ChangeLog' --- ChangeLog 2009-12-08 17:52:49 +0000 +++ ChangeLog 2009-12-18 00:23:51 +0000 @@ -2,6 +2,7 @@ Functionality added or changed: * Options in the Windows GUI are groupped on named tabs. + * Added parameters arrays support. 5.1.6 (09-Nov-2009) === modified file 'driver/execute.c' --- driver/execute.c 2009-10-07 20:43:23 +0000 +++ driver/execute.c 2009-12-18 00:14:07 +0000 @@ -34,13 +34,16 @@ frees query if query != stmt->query */ -SQLRETURN do_query(STMT FAR *stmt,char *query) +SQLRETURN do_query(STMT FAR *stmt,char *query, SQLULEN query_length) { int error= SQL_ERROR; if ( !query ) return error; /* Probably error from insert_param */ + if (query_length == 0) + query_length= strlen(query); + if (stmt->stmt_options.max_rows && stmt->stmt_options.max_rows != (SQLULEN)~0L) { @@ -58,6 +61,7 @@ if ( query != stmt->query ) my_free(query,MYF(0)); query= tmp_buffer; + query_length=length + strlen(tmp_buffer+length); } } } @@ -70,7 +74,7 @@ goto exit; } - if ( mysql_query(&stmt->dbc->mysql,query) ) + if ( mysql_real_query(&stmt->dbc->mysql,query,query_length) ) { set_stmt_error(stmt,"HY000",mysql_error(&stmt->dbc->mysql), mysql_errno(&stmt->dbc->mysql)); @@ -92,7 +96,7 @@ { error= SQL_SUCCESS; /* no result set */ stmt->state= ST_EXECUTED; - stmt->affected_rows= mysql_affected_rows(&stmt->dbc->mysql); + stmt->affected_rows+= mysql_affected_rows(&stmt->dbc->mysql); goto exit; } set_error(stmt,MYERR_S1000,mysql_error(&stmt->dbc->mysql), @@ -166,18 +170,24 @@ /* @type : myodbc3 internal @purpose : insert sql params at parameter positions + @param[in] stmt Statement + @param[in] row Parameters row + @param[in,out] finalquery if NULL, final query is not copied + @param[in,out] length Length of the query. Pointed value is used as initial offset */ -SQLRETURN insert_params(STMT FAR *stmt, char **finalquery) +SQLRETURN insert_params(STMT FAR *stmt, SQLULEN row, char **finalquery, + SQLULEN *finalquery_length) { char *query= stmt->query,*to; uint i,length; NET *net; SQLRETURN rc= SQL_SUCCESS; - pthread_mutex_lock(&stmt->dbc->lock); + int mutex_was_locked= pthread_mutex_trylock(&stmt->dbc->lock); + net= &stmt->dbc->mysql.net; - to= (char*) net->buff; + to= (char*) net->buff + (finalquery_length!= NULL ? *finalquery_length : 0); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC, "C"); /* force use of '.' as decimal point */ for ( i= 0; i < stmt->param_count; i++ ) @@ -199,30 +209,36 @@ if ( !(to= add_to_buffer(net,to,query,length)) ) goto memerror; query= pos+1; /* Skip '?' */ - if (!SQL_SUCCEEDED(rc= insert_param(stmt,&to,stmt->apd,aprec,iprec,0))) + if (!SQL_SUCCEEDED(rc= insert_param(stmt,&to,stmt->apd,aprec,iprec,row))) goto error; } length= (uint) (stmt->query_end - query); if ( !(to= add_to_buffer(net,to,query,length+1)) ) goto memerror; - if ( !(to= (char*) my_memdup((char*) net->buff, - (uint) (to - (char*) net->buff),MYF(0))) ) + if (finalquery_length!= NULL) + *finalquery_length= to - net->buff - 1; + if (finalquery!=NULL) + { + if ( !(to= (char*) my_memdup((char*) net->buff, + (uint) (to - (char*) net->buff),MYF(0))) ) goto memerror; - - /* TODO We don't *yet* support PARAMSET */ - if ( stmt->apd->rows_processed_ptr ) - *stmt->apd->rows_processed_ptr= 1; - - pthread_mutex_unlock(&stmt->dbc->lock); + } + + if (!mutex_was_locked) + pthread_mutex_unlock(&stmt->dbc->lock); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); - *finalquery= to; + + if (finalquery!=NULL) + *finalquery= to; return rc; memerror: /* Too much data */ rc= set_error(stmt,MYERR_S1001,NULL,4001); error: - pthread_mutex_unlock(&stmt->dbc->lock); + /* ! was _already_ locked, when we tried to lock */ + if (!mutex_was_locked) + pthread_mutex_unlock(&stmt->dbc->lock); if (!stmt->dbc->ds->dont_use_set_locale) setlocale(LC_NUMERIC,default_locale); return rc; @@ -664,6 +680,31 @@ } +BOOL map_error_to_param_status( SQLUSMALLINT *param_status_ptr, SQLRETURN rc) +{ + if (param_status_ptr) + { + switch (rc) + { + case SQL_SUCCESS: + *param_status_ptr= SQL_PARAM_SUCCESS; + break; + case SQL_SUCCESS_WITH_INFO: + *param_status_ptr= SQL_PARAM_SUCCESS_WITH_INFO; + break; + + default: + /* SQL_PARAM_ERROR is set at the end of processing for last erroneous paramset + so we have diagnostics for it */ + *param_status_ptr= SQL_PARAM_DIAG_UNAVAILABLE; + return TRUE; + } + } + + return FALSE; +} + + /* @type : myodbc3 internal @purpose : executes a prepared statement, using the current values @@ -674,9 +715,12 @@ SQLRETURN my_SQLExecute( STMT FAR *pStmt ) { char *query, *cursor_pos; - int dae_rec; + int dae_rec, is_select_stmt; STMT FAR * pStmtCursor = pStmt; - SQLRETURN rc; + SQLRETURN rc; + SQLULEN row, length= 0; + + SQLUSMALLINT *param_operation_ptr= NULL, *param_status_ptr= NULL, *lastError= NULL; if ( !pStmt ) return SQL_ERROR; @@ -718,28 +762,155 @@ } my_SQLFreeStmt((SQLHSTMT)pStmt,MYSQL_RESET_BUFFERS); query= pStmt->query; - - if ( pStmt->apd->rows_processed_ptr ) - *pStmt->apd->rows_processed_ptr= 0; - - if ( pStmt->param_count ) - { - /* - * If any parameters are required at execution time, cannot perform the - * statement. It will be done through SQLPutData() and SQLParamData(). - */ - if ((dae_rec= desc_find_dae_rec(pStmt->apd)) > -1) - { - pStmt->current_param= dae_rec; - pStmt->dae_type= DAE_NORMAL; - return SQL_NEED_DATA; - } - - if (!SQL_SUCCEEDED(rc= insert_params(pStmt, &query))) - return rc; - } - - rc= do_query(pStmt, query); + is_select_stmt= is_select_statement((SQLCHAR *)query); + + if ( pStmt->ipd->rows_processed_ptr ) + *pStmt->ipd->rows_processed_ptr= 0; + + /* Locking if we have params array for "SELECT" statemnt */ + /* if param_count is zero, the rest probably are artifacts(not reset + attributes) from a previously executed statement. besides this lock + is not needed when there are no params*/ + if (pStmt->param_count && pStmt->apd->array_size > 1 && is_select_stmt) + pthread_mutex_lock(&pStmt->dbc->lock); + + for (row= 0; row < pStmt->apd->array_size; ++row) + { + if ( pStmt->param_count ) + { + /* "The SQL_DESC_ROWS_PROCESSED_PTR field of the APD points to a buffer + that contains the number of sets of parameters that have been processed, + including error sets." + "If SQL_NEED_DATA is returned, the value pointed to by the SQL_DESC_ROWS_PROCESSED_PTR + field of the APD is set to the set of parameters that is being processed". + And actually driver may continue to process paramsets after error. + We need to decide do we want that. + (http://msdn.microsoft.com/en-us/library/ms710963%28VS.85%29.aspx + see "Using Arrays of Parameters") + */ + if ( pStmt->ipd->rows_processed_ptr ) + *pStmt->ipd->rows_processed_ptr+= 1; + + param_operation_ptr= ptr_offset_adjust(pStmt->apd->array_status_ptr, + NULL, + 0/*SQL_BIND_BY_COLUMN*/, + sizeof(SQLUSMALLINT), row); + param_status_ptr= ptr_offset_adjust(pStmt->ipd->array_status_ptr, + NULL, + 0/*SQL_BIND_BY_COLUMN*/, + sizeof(SQLUSMALLINT), row); + + if ( param_operation_ptr + && *param_operation_ptr == SQL_PARAM_IGNORE) + { + /* http://msdn.microsoft.com/en-us/library/ms712631%28VS.85%29.aspx + - comments for SQL_ATTR_PARAM_STATUS_PTR */ + if (param_status_ptr) + *param_status_ptr= SQL_PARAM_UNUSED; + + /* If this is last paramset - we will miss unlock */ + if (pStmt->apd->array_size > 1 && is_select_stmt + && row == pStmt->apd->array_size - 1) + pthread_mutex_unlock(&pStmt->dbc->lock); + + continue; + } + + /* + * If any parameters are required at execution time, cannot perform the + * statement. It will be done through SQLPutData() and SQLParamData(). + */ + if ((dae_rec= desc_find_dae_rec(pStmt->apd)) > -1) + { + if (pStmt->apd->array_size > 1) + { + rc= set_stmt_error(pStmt, "HYC00", "Parameter arrays" + "with data at execution are not supported", 0); + lastError= param_status_ptr; + + /* unlocking since we do break*/ + if (is_select_stmt) + pthread_mutex_unlock(&pStmt->dbc->lock); + + /* For other errors we continue processing of paramsets + So this creates some inconsistency. But I guess that's better + that user see diagnostics for this type of error */ + break; + } + pStmt->current_param= dae_rec; + pStmt->dae_type= DAE_NORMAL; + return SQL_NEED_DATA; + } + + /* Making copy of the built query if that is not last paramset for select + query. */ + if (is_select_stmt && row < pStmt->apd->array_size - 1) + rc= insert_params(pStmt, row, NULL, &length); + else + rc= insert_params(pStmt, row, &query, &length); + + /* Setting status for this paramset*/ + if (map_error_to_param_status( param_status_ptr, rc)) + lastError= param_status_ptr; + + if (!SQL_SUCCEEDED(rc)) + { + if (pStmt->apd->array_size > 1 && is_select_stmt + && row == pStmt->apd->array_size - 1) + pthread_mutex_unlock(&pStmt->dbc->lock); + + continue/*return rc*/; + } + + /* For "SELECT" statement constructing single statement using + "UNION ALL" */ + if (pStmt->apd->array_size > 1 && is_select_stmt) + { + if (row < pStmt->apd->array_size - 1) + { + const char * stmtsBinder= " UNION ALL "; + const ulong binderLength= strlen(stmtsBinder); + + add_to_buffer(&pStmt->dbc->mysql.net, (char*)pStmt->dbc->mysql.net.buff + length, + stmtsBinder, binderLength); + length+= binderLength; + } + else + /* last select statement has been constructed - so releasing lock*/ + pthread_mutex_unlock(&pStmt->dbc->lock); + } + } + + if (!is_select_stmt || row == pStmt->apd->array_size-1) + { + rc= do_query(pStmt, query, length); + if (map_error_to_param_status(param_status_ptr, rc)) + lastError= param_status_ptr; + length= 0; + } + } + + /* Changing status for last detected error to SQL_PARAM_ERROR as we have + diagnostics for it */ + if (lastError != NULL) + *lastError= SQL_PARAM_ERROR; + + /* Setting not processed paramsets status to SQL_PARAM_UNUSED + this is needed if we stop paramsets processing on error. + */ + if (param_status_ptr != NULL) + { + while (++row < pStmt->apd->array_size) + { + param_status_ptr= ptr_offset_adjust(pStmt->ipd->array_status_ptr, + NULL, + 0/*SQL_BIND_BY_COLUMN*/, + sizeof(SQLUSMALLINT), row); + + *param_status_ptr= SQL_PARAM_UNUSED; + } + } + if (pStmt->dummy_state == ST_DUMMY_PREPARED) pStmt->dummy_state= ST_DUMMY_EXECUTED; return rc; @@ -811,9 +982,9 @@ switch(stmt->dae_type) { case DAE_NORMAL: - if (!SQL_SUCCEEDED(rc= insert_params(stmt, &query))) + if (!SQL_SUCCEEDED(rc= insert_params(stmt, 0, &query, 0))) break; - rc= do_query(stmt, query); + rc= do_query(stmt, query, 0); break; case DAE_SETPOS_INSERT: stmt->dae_type= DAE_SETPOS_DONE; === modified file 'driver/info.c' --- driver/info.c 2009-05-02 20:53:50 +0000 +++ driver/info.c 2009-12-14 15:56:08 +0000 @@ -555,7 +555,7 @@ MYINFO_SET_ULONG(SQL_PARC_NO_BATCH); case SQL_PARAM_ARRAY_SELECTS: - MYINFO_SET_ULONG(SQL_PAS_NO_SELECT); + MYINFO_SET_ULONG(SQL_PAS_NO_BATCH); case SQL_PROCEDURE_TERM: if (is_minimum_version(dbc->mysql.server_version, "5.0", 3)) === modified file 'driver/myutil.h' --- driver/myutil.h 2009-05-02 20:53:50 +0000 +++ driver/myutil.h 2009-12-16 17:15:11 +0000 @@ -118,8 +118,9 @@ SQLRETURN SQL_API my_SQLFreeStmtExtended(SQLHSTMT hstmt, SQLUSMALLINT fOption, uint clearAllResults); SQLRETURN SQL_API my_SQLAllocStmt(SQLHDBC hdbc,SQLHSTMT FAR *phstmt); -SQLRETURN do_query(STMT FAR *stmt,char *query); -SQLRETURN insert_params(STMT FAR *stmt, char **finalquery); +SQLRETURN do_query(STMT FAR *stmt,char *query, SQLULEN query_length); +SQLRETURN insert_params(STMT FAR *stmt, SQLULEN row, char **finalquery, + SQLULEN *length); SQLRETURN odbc_stmt(DBC FAR *dbc, const char *query); void mysql_link_fields(STMT *stmt,MYSQL_FIELD *fields,uint field_count); void fix_result_types(STMT *stmt); @@ -134,6 +135,7 @@ DESCREC *aprec, DESCREC *iprec, SQLULEN row); char *add_to_buffer(NET *net,char *to,const char *from,ulong length); int is_set_names_statement(SQLCHAR *query); +int is_select_statement(SQLCHAR *query); void reset_getdata_position(STMT *stmt); === modified file 'driver/options.c' --- driver/options.c 2009-05-02 20:53:50 +0000 +++ driver/options.c 2009-11-20 16:56:08 +0000 @@ -642,13 +642,10 @@ ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAMSET_SIZE: - return set_error(hstmt,MYERR_01S02, - "Param arrays not supported",0); -#if ALLOW_SETTING_DESC_ARRAY_SIZE return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_SIZE, ValuePtr, SQL_IS_ULEN); -#endif + case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: === modified file 'driver/utility.c' --- driver/utility.c 2009-11-17 19:14:58 +0000 +++ driver/utility.c 2009-12-14 17:47:58 +0000 @@ -2675,6 +2675,18 @@ /** +Detect if a statement is a SELECT statement. +*/ +int is_select_statement(SQLCHAR *query) +{ + /* Skip leading spaces */ + while (query && isspace(*query)) + query++; + return myodbc_casecmp((char *)query, "SELECT", 6) == 0; +} + + +/** Adjust a pointer based on bind offset and bind type. @param[in] ptr The base pointer === modified file 'test/my_param.c' --- test/my_param.c 2008-08-22 22:12:25 +0000 +++ test/my_param.c 2009-12-18 00:24:40 +0000 @@ -368,6 +368,374 @@ } +/* +Bug 48310 - parameters array support request. +Binding by row test +*/ +DECLARE_TEST(paramarray_by_row) +{ +#define ROWS_TO_INSERT 3 +#define STR_FIELD_LENGTH 255 + typedef struct DataBinding + { + SQLCHAR bData[5]; + SQLINTEGER intField; + SQLCHAR strField[STR_FIELD_LENGTH]; + SQLINTEGER indBin; + SQLINTEGER indInt; + SQLINTEGER indStr; + } DATA_BINDING; + + const SQLCHAR *str[]= {"nothing for 1st", "longest string for row 2", "shortest" }; + + SQLCHAR buff[50]; + DATA_BINDING dataBinding[ROWS_TO_INSERT]; + SQLUSMALLINT paramStatusArray[ROWS_TO_INSERT]; + SQLULEN paramsProcessed, i, nLen; + SQLLEN rowsCount; + + //ok_stmt(hstmt, SQLExecDirect(hstmt, "set @@session.sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES'", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ + "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, (SQLPOINTER)sizeof(DATA_BINDING), 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); + + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, + 0, 0, dataBinding[0].bData, 0, &dataBinding[0].indBin)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, + 0, 0, &dataBinding[0].intField, 0, &dataBinding[0].indInt)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, + 0, 0, dataBinding[0].strField, 0, &dataBinding[0].indStr )); + + memcpy(dataBinding[0].bData, "\x01\x80\x00\x80\x00", 5); + dataBinding[0].intField= 1; + + memcpy(dataBinding[1].bData, "\x02\x80\x00\x80", 4); + dataBinding[1].intField= 0; + + memcpy(dataBinding[2].bData, "\x03\x80\x00", 3); + dataBinding[2].intField= 223322; + + for (i= 0; i < ROWS_TO_INSERT; ++i) + { + strcpy(dataBinding[i].strField, str[i]); + dataBinding[i].indBin= 5 - i; + dataBinding[i].indInt= 0; + dataBinding[i].indStr= SQL_NTS; + } + + ok_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ + "VALUES (?,?,?)", SQL_NTS)); + + is_num(paramsProcessed, ROWS_TO_INSERT); + + ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); + is_num(rowsCount, ROWS_TO_INSERT); + + for (i= 0; i < paramsProcessed; ++i) + if ( paramStatusArray[i] != SQL_PARAM_SUCCESS + && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) + { + printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); + return FAIL; + } + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ + FROM t_bug48310\ + ORDER BY id", SQL_NTS)); + + /* Just to make sure RowCount isn't broken */ + ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); + is_num(rowsCount, ROWS_TO_INSERT); + + for (i= 0; i < paramsProcessed; ++i) + { + ok_stmt(hstmt, SQLFetch(hstmt)); + + ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); + is(memcmp((const void*) buff, (const void*)dataBinding[i].bData, 5 - i)==0); + is_num(my_fetch_int(hstmt, 2), dataBinding[i].intField); + is_str(my_fetch_str(hstmt, buff, 3), dataBinding[i].strField, strlen(str[i])); + } + + expect_stmt(hstmt,SQLFetch(hstmt), SQL_NO_DATA_FOUND); + + /* One more check that RowCount isn't broken. check may get broken if input data + changes */ + ok_sql(hstmt, "update t_bug48310 set strField='changed' where intField > 1"); + ok_stmt(hstmt, SQLRowCount(hstmt, &rowsCount)); + is_num(rowsCount, 1); + + /* Clean-up */ + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); + + return OK; + +#undef ROWS_TO_INSERT +#undef STR_FIELD_LENGTH +} + + +/* +Bug 48310 - parameters array support request. +Binding by column test +*/ +DECLARE_TEST(paramarray_by_column) +{ +#define ROWS_TO_INSERT 3 +#define STR_FIELD_LENGTH 5 + SQLCHAR buff[50]; + + SQLCHAR bData[ROWS_TO_INSERT][STR_FIELD_LENGTH]={{0x01, 0x80, 0x00, 0x80, 0x03}, + {0x02, 0x80, 0x00, 0x02}, + {0x03, 0x80, 0x01}}; + SQLLEN bInd[ROWS_TO_INSERT]= {5,4,3}; + + const SQLCHAR strField[ROWS_TO_INSERT][STR_FIELD_LENGTH]= {{'\0'}, {'x','\0'}, {'x','x','x','\0'} }; + SQLLEN strInd[ROWS_TO_INSERT]= {SQL_NTS, SQL_NTS, SQL_NTS}; + + SQLINTEGER intField[ROWS_TO_INSERT] = {123321, 1, 0}; + SQLLEN intInd[ROWS_TO_INSERT]= {5,4,3}; + + SQLUSMALLINT paramStatusArray[ROWS_TO_INSERT]; + SQLULEN paramsProcessed, i, nLen; + + //ok_stmt(hstmt, SQLExecDirect(hstmt, "set @@session.sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES'", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ + "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); + + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, + 0, 0, bData, 5, bInd)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, + 0, 0, intField, 0, intInd)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, + 0, 0, (SQLPOINTER)strField, 5, strInd )); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ + "VALUES (?,?,?)", SQL_NTS)); + + is_num(paramsProcessed, ROWS_TO_INSERT); + + for (i= 0; i < paramsProcessed; ++i) + if ( paramStatusArray[i] != SQL_PARAM_SUCCESS + && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) + { + printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); + return FAIL; + } + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ + FROM t_bug48310\ + ORDER BY id", SQL_NTS)); + + for (i= 0; i < paramsProcessed; ++i) + { + ok_stmt(hstmt, SQLFetch(hstmt)); + + ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); + if (memcmp((const void*) buff, bData[i], 5 - i)!=0) + { + printMessage("Bin data inserted wrongly. Read: 0x%02X%02X%02X%02X%02X Had to be: 0x%02X%02X%02X%02X%02X" + , buff[0], buff[1], buff[2], buff[3], buff[4] + , bData[i][0], bData[i][1], bData[i][2], bData[i][3], bData[i][4]); + return FAIL; + } + is_num(my_fetch_int(hstmt, 2), intField[i]); + is_str(my_fetch_str(hstmt, buff, 3), strField[i], strlen(strField[i])); + } + + /* Clean-up */ + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + //ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); + + return OK; + +#undef ROWS_TO_INSERT +#undef STR_FIELD_LENGTH +} + + +/* +Bug 48310 - parameters array support request. +Ignore paramset test +*/ +DECLARE_TEST(paramarray_ignore_paramset) +{ +#define ROWS_TO_INSERT 4 +#define STR_FIELD_LENGTH 5 + SQLCHAR buff[50]; + + SQLCHAR bData[ROWS_TO_INSERT][STR_FIELD_LENGTH]={{0x01, 0x80, 0x00, 0x80, 0x03}, + {0x02, 0x80, 0x00, 0x02}, + {0x03, 0x80, 0x01}}; + SQLLEN bInd[ROWS_TO_INSERT]= {5,4,3}; + + const SQLCHAR strField[ROWS_TO_INSERT][STR_FIELD_LENGTH]= {{'\0'}, {'x','\0'}, {'x','x','x','\0'} }; + SQLLEN strInd[ROWS_TO_INSERT]= {SQL_NTS, SQL_NTS, SQL_NTS}; + + SQLINTEGER intField[ROWS_TO_INSERT] = {123321, 1, 0}; + SQLLEN intInd[ROWS_TO_INSERT]= {5,4,3}; + + SQLUSMALLINT paramOperationArr[ROWS_TO_INSERT]={0,SQL_PARAM_IGNORE,0,SQL_PARAM_IGNORE}; + SQLUSMALLINT paramStatusArr[ROWS_TO_INSERT]; + SQLULEN paramsProcessed, i, nLen, rowsInserted= 0; + + //ok_stmt(hstmt, SQLExecDirect(hstmt, "set @@session.sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_BACKSLASH_ESCAPES'", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS t_bug48310", SQL_NTS)); + ok_stmt(hstmt, SQLExecDirect(hstmt, "CREATE TABLE t_bug48310 (id int primary key auto_increment,"\ + "bData binary(5) NULL, intField int not null, strField varchar(255) not null)", SQL_NTS)); + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS_TO_INSERT, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArr, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_OPERATION_PTR, paramOperationArr, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); + + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, + 0, 0, bData, 5, bInd)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, + 0, 0, intField, 0, intInd)); + ok_stmt(hstmt, SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, + 0, 0, (SQLPOINTER)strField, 5, strInd )); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "INSERT INTO t_bug48310 (bData, intField, strField) " \ + "VALUES (?,?,?)", SQL_NTS)); + + is_num(paramsProcessed, ROWS_TO_INSERT); + + for (i= 0; i < paramsProcessed; ++i) + { + if (paramOperationArr[i] == SQL_PARAM_IGNORE) + { + is_num(paramStatusArr[i], SQL_PARAM_UNUSED); + } + else if ( paramStatusArr[i] != SQL_PARAM_SUCCESS + && paramStatusArr[i] != SQL_PARAM_SUCCESS_WITH_INFO ) + { + printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArr[i]); + return FAIL; + } + } + + /* Resetting statements attributes */ + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)1, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, NULL, 0)); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "SELECT bData, intField, strField\ + FROM t_bug48310\ + ORDER BY id", SQL_NTS)); + + i= 0; + while(i < paramsProcessed) + { + if (paramStatusArr[i] == SQL_PARAM_UNUSED) + { + ++i; + continue; + } + + ok_stmt(hstmt, SQLFetch(hstmt)); + + ok_stmt(hstmt, SQLGetData(hstmt, 1, SQL_BINARY, (SQLPOINTER)buff, 50, &nLen)); + + if (memcmp((const void*) buff, bData[i], 5 - i)!=0) + { + printMessage("Bin data inserted wrongly. Read: 0x%02X%02X%02X%02X%02X Had to be: 0x%02X%02X%02X%02X%02X" + , buff[0], buff[1], buff[2], buff[3], buff[4] + , bData[i][0], bData[i][1], bData[i][2], bData[i][3], bData[i][4]); + return FAIL; + } + is_num(my_fetch_int(hstmt, 2), intField[i]); + is_str(my_fetch_str(hstmt, buff, 3), strField[i], strlen(strField[i])); + + ++rowsInserted; + ++i; + } + + /* Making sure that there is nothing else to fetch ... */ + expect_stmt(hstmt,SQLFetch(hstmt), SQL_NO_DATA_FOUND); + + /* ... and that inserted was less than SQL_ATTR_PARAMSET_SIZE rows */ + is( rowsInserted < ROWS_TO_INSERT); + + /* Clean-up */ + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + //ok_stmt(hstmt, SQLExecDirect(hstmt, "DROP TABLE IF EXISTS bug48310", SQL_NTS)); + + return OK; + +#undef ROWS_TO_INSERT +#undef STR_FIELD_LENGTH +} + + +/* + Bug 48310 - parameters array support request. + Select statement. +*/ +DECLARE_TEST(paramarray_select) +{ +#define STMTS_TO_EXEC 3 + + SQLINTEGER intField[STMTS_TO_EXEC] = {3, 1, 2}; + SQLLEN intInd[STMTS_TO_EXEC]= {5,4,3}; + + SQLUSMALLINT paramStatusArray[STMTS_TO_EXEC]; + SQLULEN paramsProcessed, i; + + + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)STMTS_TO_EXEC, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAM_STATUS_PTR, paramStatusArray, 0)); + ok_stmt(hstmt, SQLSetStmtAttr(hstmt, SQL_ATTR_PARAMS_PROCESSED_PTR, ¶msProcessed, 0)); + + ok_stmt(hstmt, SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, + 0, 0, intField, 0, intInd)); + + ok_stmt(hstmt, SQLExecDirect(hstmt, "SELect ?,'So what'", SQL_NTS)); + + is_num(paramsProcessed, STMTS_TO_EXEC); + + for (i= 0; i < paramsProcessed; ++i) + { + if ( paramStatusArray[i] != SQL_PARAM_SUCCESS + && paramStatusArray[i] != SQL_PARAM_SUCCESS_WITH_INFO ) + { + printMessage("Parameter #%u status isn't successful(0x%X)", i+1, paramStatusArray[i]); + return FAIL; + } + + ok_stmt(hstmt, SQLFetch(hstmt)); + + is_num(my_fetch_int(hstmt, 1), intField[i]); + } + + /* Clean-up */ + ok_stmt(hstmt, SQLFreeStmt(hstmt, SQL_CLOSE)); + + return OK; + +#undef STMTS_TO_EXEC +} + + BEGIN_TESTS ADD_TEST(my_init_table) ADD_TEST(my_param_insert) @@ -375,6 +743,10 @@ ADD_TEST(my_param_delete) ADD_TEST(tmysql_fix) ADD_TEST(t_param_offset) + ADD_TEST(paramarray_by_row) + ADD_TEST(paramarray_by_column) + ADD_TEST(paramarray_ignore_paramset) + ADD_TEST(paramarray_select) END_TESTS