Bug #68495 Segfault in prepared statements returning strings using embedded libs.
Submitted: 26 Feb 2013 12:13 Modified: 21 Jun 2014 16:15
Reporter: Ravi Desai Email Updates:
Status: No Feedback Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S1 (Critical)
Version:5.5.2 and 5.6.10 OS:MacOS (10.8.2)
Assigned to: CPU Architecture:Any
Tags: C API, embedded libs, prepared statements

[26 Feb 2013 12:13] Ravi Desai
Description:
This program fails with a segmentation fault on Mac OS/X Mountain Lion version 10.8.2 when compiled against the embedded mysqld library.  The same program succeeds on the same platform when compiled against the mysql threaded client libraries.

The segfault occurs in the mysql_stmt_execute function.

I used the prebuilt installer for MYSQL pulled from the official website entitled: mysql-5.5.29-osx10.6-x86_64.dmg.  I installed the sakila database (specifically the country table) and populated it from the default scripts provided on the MYSQL website

I am using gcc version 4.2.1 (as provided by XCode 4.6 command line tools installer).
I compile to a build directory (here just called 'build/'), you'll need to create it to use my g++ lines (below) verbatim.

The working version of the program (bug) is compiled in the following way and run against the installed MYSQL database instance: The Makefile actually uses the 'mysql_config --include' and 'mysql_config --libs_r' scripts for many of the switches below:

g++ -Wall -g -O0 -c bug.cpp -I/usr/local/mysql/include  -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug.o
g++ -Wall build/bug.o -I/usr/local/mysql/include  -Os -g -fno-common -fno-strict-aliasing -arch x86_64  -o build/bug -L/usr/local/mysql/lib -lmysqlclient_r   -lpthread -Lbuild/

The version of the program (bug) that produces a segfault is compiled in the following way and run against an embedded MYSQL database instance (created in my home directory using the /usr/local/mysql/scripts/mysql_install_db script).  As before, the Makefile actually uses the 'mysql_config --include' and 'mysql_config --libmysqld-libs' scripts to obtain many of the swithes below:

g++ -Wall -g -O0 -c bug.cpp -I/usr/local/mysql/include  -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug.o
g++ -Wall build/bug.o -I/usr/local/mysql/include  -Os -g -fno-common -fno-strict-aliasing -arch x86_64  -o build/bug -L/usr/local/mysql/lib -lmysqld    -lpthread -Lbuild/

How to repeat:
///////////////////
/// bug.cpp
///////////////////
#include <iostream>
#include <vector>
#include <fstream>
#include <sys/stat.h>

#include <typeinfo>

#include "my_global.h"
#include "mysql.h"

using namespace std;

static void print_stmt_error (MYSQL_STMT *stmt, const char *message) {
	fprintf (stderr, "%s\n", message);
	if (stmt != NULL) {
		fprintf(stderr, "Error %u (%s): %s\n",
			mysql_stmt_errno(stmt),
			mysql_stmt_sqlstate(stmt),
			mysql_stmt_error(stmt)); 
	} 
}

int main(int argc, char** argv) {
	bool embedded = false;
	if (argc > 1 && (strcmp(argv[1], "embedded") == 0)) {
		embedded = true;
	}

	if (embedded) {
		cout << "Hello you are connected in embedded mode" << endl;

		static const char *server_args[] = { "this_program", "--basedir=/usr/local/mysql", "--datadir=/Users/ravidesai/mysql/data", 
					     	"--plugin-dir=/Users/ravidesai/mysql/plugins", "--log-error=/Users/ravidesai/mysql/tmp/test.err",
					     	"--pid-file=/Users/ravidesai/mysql/tmp/test.pid",
					     	"--key_buffer_size=32M", "--log-bin=/Users/ravidesai/mysql/log/logbin"
					     	"--log-bin-trust-function-creators=1"
					     	"--log-bin-trust-routine-creators=1"
					    	};
		static const char *server_groups[] = { "embedded", "server", "this_program_SERVER", (char *) NULL };
	
		if (mysql_library_init(sizeof(server_args) / sizeof(char *), (char**) server_args, (char **)server_groups) != 0) {
			cout << "Error in Database::Initialize" << endl;
			return 1;
		}
	} else {
		cout << "Hello you are connected in standard client (non-embedded) mode" << endl;
		if (mysql_library_init(0, NULL, NULL) != 0) {
			cout << "Error in Database::Initialize" << endl;
		}
	}

	if (mysql_thread_init() != 0) {
		cout << "Error in Database::ThreadInitialize" << endl;
	}

	MYSQL *connect;
	connect = mysql_init(NULL);
	if (! connect) {
		cout << "MySQL initialization failure" << endl;
		return 1;
	}

	if (embedded) {
		mysql_options(connect, MYSQL_READ_DEFAULT_GROUP, "embedded");
		mysql_options(connect, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL);
	}

	connect = mysql_real_connect(connect, "localhost", "root", "", "sakila", 0, NULL, 0);
	if (connect)
	{
		cout << "Connection succeeded" << endl;
	}	

	const char *stmt_str = "SELECT country, country_id, last_update  FROM COUNTRY";
	MYSQL_STMT *stmt;

	stmt = mysql_stmt_init(connect);

	if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0) {
		print_stmt_error(stmt, "Count not prepare SELECT statement");
		return 1;
	}

	int numberParams = mysql_stmt_param_count(stmt);
	cout << "Number parameters: " << numberParams << endl;
	MYSQL_RES *metaData;
	if ((metaData = mysql_stmt_result_metadata(stmt)) == NULL) {
		print_stmt_error(stmt, "Error fetching stmt metadata");
	}

	int numberFields = 0;
	MYSQL_FIELD *field;
	while ((field = mysql_fetch_field(metaData)) != NULL) {
		numberFields++;
		cout << "[" << field->name << ", " << field->type <<  "] ";
	}
	cout << endl;

	const int STRING_SIZE = 50;

	unsigned short countryId = 0;
	char countryName[STRING_SIZE];
	unsigned long countryNameLen;
	MYSQL_TIME lastUpdate;
	memset((void *) &lastUpdate, 0, sizeof(MYSQL_TYPE_TIME));
	my_bool is_null[numberFields];
	my_bool error[numberFields];
	size_t countryIdLength = sizeof(unsigned short);
	size_t lastUpdateLength = sizeof(MYSQL_TYPE_TIME);

	MYSQL_BIND bind[numberFields];
	memset ((void *) &bind, 0, sizeof(bind));

	bind[0].buffer_type = MYSQL_TYPE_STRING;
	bind[0].buffer = (void *) countryName;
	bind[0].is_null = &is_null[1];
	bind[0].buffer_length = sizeof(countryName);
	bind[0].length = &countryNameLen;
	bind[0].error = &error[1];

	bind[1].buffer_type = MYSQL_TYPE_SHORT;
	bind[1].buffer = (void *) &countryId;
	bind[1].buffer_length = sizeof(unsigned short);
	bind[1].length = &countryIdLength;
	bind[1].is_unsigned = 1;
	bind[1].is_null = &is_null[0];
	bind[1].error = &error[0];

	bind[2].buffer_type = MYSQL_TYPE_DATETIME;
	bind[2].buffer = (void *) &lastUpdate;
	bind[2].buffer_length = sizeof(MYSQL_TYPE_DATETIME);
	bind[2].length = &lastUpdateLength;
	bind[2].is_null = &is_null[2];
	bind[2].error = &error[2];
	
	cout << "binding results" << endl;
	if (mysql_stmt_bind_result(stmt, bind) != 0) {
		print_stmt_error(stmt, "Count not bind SELECT results");
		return 1;
	}

	cout << "executing statement" << endl;
	if (mysql_stmt_execute(stmt) != 0) {
		print_stmt_error(stmt, "Could not execute SELECT statement");
		return 1;
	}

	cout << "storing results" << endl;
	if (mysql_stmt_store_result(stmt) != 0) {
		print_stmt_error(stmt, "Could not execute STORE RESULT");
		return 1;
	}

	cout << "fetching statement" << endl;
	while (mysql_stmt_fetch(stmt) == 0) {
		printf("[%d] ", countryId);
		printf("[%.*s] ", (int) countryNameLen, countryName);
		printf("[%04d-%02d-%02d %02d:%02d:%02d]", 
			lastUpdate.year, lastUpdate.month, lastUpdate.day,
                     	lastUpdate.hour, lastUpdate.minute, lastUpdate.second);
		printf("\n");
	}

	cout << "closing statement" << endl;
	mysql_stmt_close(stmt);
	cout << "closing database" << endl;
	mysql_close(connect);

	return 0;
}

Suggested fix:

Additional info.  The bug now appears only to be related to prepared statement when compiled against the embedded datbase libraries and returning a "string" type.  When bug.cpp is modified to only return the country_id and/or last_update fields of the country table, the program completes normally even against the embedded libraries.  The error only seems to occur when the country field is part of the result set.  

I'm sorry, I have no fix suggestion.  I could not successfully build the MYSQL binaries for OS/X (ended up installing from disk image), so I could not debug your code.
[26 Feb 2013 12:15] Ravi Desai
bug.cpp

Attachment: bug.cpp (application/octet-stream, text), 5.07 KiB.

[26 Feb 2013 21:01] Ravi Desai
Recently installed 5.6.10 of MYSQL and verified the failure still occurs.
[21 May 2014 16:15] MySQL Verification Team
Please try latest release.
[22 Jun 2014 1:00] Bugs System
No feedback was provided for this bug for over a month, so it is
being suspended automatically. If you are able to provide the
information that was originally requested, please do so and change
the status of the bug back to "Open".