Bug #4199 RLIKE operator does not pick up changes in parameters
Submitted: 18 Jun 2004 10:46 Modified: 22 Jun 2004 15:04
Reporter: Anthon Ouwendijk Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S2 (Serious)
Version:4.1.2 OS:HP/UX (HP-UX 11)
Assigned to: Oleksandr Byelkin CPU Architecture:Any

[18 Jun 2004 10:46] Anthon Ouwendijk
Description:
This happens when the C API for prepared statements is used.
A sample C source has been included.

After changing the value of a parameter and executing the query again, a wrong result set is returned. This result set is identical to the one returned on the first execute.

Query:  select name from mysql.help_topic where name rlike ? order by name

Prepare, bind parameter- and result-buffers
copy '^C.*' to parameter buffer
Execute
Fetch result set --> 'CACHE INDEX', 'CASE', 'CAST' . . . .
copy '^R.*' to parameter buffer
Execute
Fetch result set --> 'CACHE INDEX', 'CASE', 'CAST' . . . .

This is wrong! The result set 'RADIANS', 'RAND' . . . .
should have been returned.

Please note that a similar query with 'like' instead of 'rlike' and patterns like 'C%' instead of 'C.*' works fine.

How to repeat:
#include <stdlib.h>
#include <mysql.h>

typedef enum {
	BUG_UNKNOWN	= -1,	/* Error occurred */
	BUG_SOLVED	= 0,
	BUG_PRESENT	= 1,
} BugStatus;

char * str_bug_stat( BugStatus bugstat )
{
	if ( bugstat == BUG_PRESENT  ) {
		return "Bug still present";
	} else if ( bugstat == BUG_SOLVED ) {
		return "Bug solved";
	}
	return "Status UNKNOWN (or error occurred)";
}

BugStatus do_the_bug_test( MYSQL * mysql, char ** desc )
{
	MYSQL_STMT *	stmt;

	/* Bind data */
	MYSQL_BIND	ParamBind[1];
	char		ParamStrData[8];
	unsigned long	ParamLength;

	MYSQL_BIND	ResultBind[2];
	unsigned long	ResultLength[2];
	my_bool		ResultIsNull[2];
	int		ResultIntData;
	char		ResultStrData[128];

	char		query[256];
	int		rows = 0;

	*desc =
	"MySQL-#.... Baan-040617	RLIKE operator does not pick up changes in parameters \n"
	"			ok:	select str from T where str like ?\n"
	"				bind 'A%' exec fetch bind 'B%' exec fetch \n"
	"			err:	select str from T where str RLIKE ?\n"
	"				bind '^A.*' exec fetch bind '^B.*' exec fetch \n";

	if ( !(stmt = mysql_stmt_init(mysql) ) ) {
		return BUG_UNKNOWN;
	}
	sprintf( query,
			"select help_topic_id, name "
			"from mysql.help_topic "
			"where name rlike ? "
			"order by name "
			"limit 0, 5 "
		);

	if (mysql_stmt_prepare(stmt, query, strlen(query))) {
		mysql_stmt_close(stmt);
		return BUG_UNKNOWN;
	}

	/* Bind the param buffers */
	ParamBind[0].buffer_type= MYSQL_TYPE_STRING;
	ParamBind[0].buffer= ParamStrData;
	ParamBind[0].is_null= 0;
	ParamBind[0].length = &ParamLength;

	if (mysql_stmt_bind_param(stmt, ParamBind)) {
		mysql_stmt_close(stmt);
		return BUG_UNKNOWN;
	}

	/* Bind the result buffers */
	ResultBind[0].buffer_type= MYSQL_TYPE_LONG;
	ResultBind[0].buffer= (char *)&ResultIntData;
	ResultBind[0].is_null= &ResultIsNull[0];
	ResultBind[0].length= &ResultLength[0];

	ResultBind[1].buffer_type= MYSQL_TYPE_STRING;
	ResultBind[1].buffer= (char *)&ResultStrData;
	ResultBind[1].is_null= &ResultIsNull[1];
	ResultBind[1].length= &ResultLength[1];

	if (mysql_stmt_bind_result(stmt, ResultBind)) {
		mysql_stmt_close(stmt);
		return BUG_UNKNOWN;
	}

	/* Set actual Param value */
	strcpy(ParamStrData, "^C.*" );
	ParamLength = strlen( ParamStrData );

	/* Execute */
	if (mysql_stmt_execute(stmt)) {
		mysql_stmt_close(stmt);
		return BUG_UNKNOWN;
	}

	/* Fetch all rows */
	while (!mysql_stmt_fetch(stmt))
	{
		rows++;

		if (ResultIsNull[0] || ResultIsNull[1])
			return BUG_UNKNOWN;
			
		printf ( "id = %d, name = %s", ResultIntData, ResultStrData );

		if ( ResultStrData[0] != ParamStrData[1] ) {
			/* The data is equal after revering the byte order */
			printf ( "\tUnexpected Error: fetched data does not match pattern %s\n", ParamStrData );
			return BUG_UNKNOWN;
		}
		printf( "\n" );
	}

	printf ( ">> Change actual parameter value and re-execute\n" );

	/* Change actual Param value */
	strcpy(ParamStrData, "^R.*" );
	ParamLength = strlen( ParamStrData );
	/* Re-Execute with different bind value ! */
	if (mysql_stmt_execute(stmt)) {
		mysql_stmt_close(stmt);
		return BUG_UNKNOWN;
	}
	/* Fetch all rows */
	while (!mysql_stmt_fetch(stmt))
	{
		rows++;

		if (ResultIsNull[0] || ResultIsNull[0])
			return BUG_UNKNOWN;
			
		printf ( "id = %d, name = %s", ResultIntData, ResultStrData );

		if ( ResultStrData[0] != ParamStrData[1] ) {
			/* The data is equal after revering the byte order */
			printf ( "\tError: fetched data does not match pattern %s\n", ParamStrData );
			return BUG_PRESENT;
		}
		printf( "\n" );
	}

	/* Close the statement */
	mysql_stmt_close(stmt);

	if ( rows ) {
		return BUG_SOLVED;
	} else {
		return BUG_UNKNOWN;
	}
}

main()
{
	MYSQL *		mysql = mysql_init( NULL );
	BugStatus	bugstat;
	char *		desc;

	/* Bind data */

	if ( ! mysql_real_connect( mysql,
					/* host */ NULL,
					/* user */ User,
					/* pwd */ Passwd,
					/* dbase */ Dbase, 

					/* uint port */ 0,
					/* char * socket */ NULL,
					/* client_flag */ 0 ) )
	{
		fprintf(stderr, " mysql_real_connec() failed\n");
		exit(0);
	}

	bugstat = do_the_bug_test( mysql, &desc );
	fprintf(stdout, "%s\n%s\n\n", desc, str_bug_stat( bugstat ) );

	return 0;
}

Suggested fix:
For rlike the regular expression should be re-compiled for each execute.
[18 Jun 2004 16:52] Oleksandr Byelkin
Thank you for bugreport, but we need your schema and data to repeat this bug. 
Can you provide them here or upload it to 
ftp://ftp.mysql.com/pub/mysql/upload/ and say file name here.
[18 Jun 2004 17:01] Oleksandr Byelkin
Sorry, I do not noticed that it is standard base
[19 Jun 2004 12:27] Oleksandr Byelkin
ChangeSet 
  1.1943 04/06/19 13:26:39 bell@sanja.is.com.ua +4 -0 
  cleanup for Item_func_regex (Bug #4199)
[22 Jun 2004 15:04] Oleksandr Byelkin
Thank you for bugreport! Bug is fixed and patch is pushed to our source 
repository.