Bug #1959 MySql embedded corrupts tables in a multi-threaded application
Submitted: 26 Nov 2003 7:32 Modified: 10 Dec 2003 5:37
Reporter: Cristiano Muzi Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Embedded Library ( libmysqld ) Severity:S1 (Critical)
Version:4.0.17 OS:Windows (Windows 2000/XP)
Assigned to: Alexey Botchkov CPU Architecture:Any

[26 Nov 2003 7:32] Cristiano Muzi
Description:
We've found that executing a multi-thread application using MySql Embedded, where multiple threads execute queries on the same table, after a while causes table corruption.

How to repeat:
We've written a test program using Visual C++ 6; we'll attach all the files to this post; however here is the main code; as you can see, there is a number of threads executing INSERT queries on the table, a number of threads executing DELETE queries on the same table, and the main thread executing UPDATE queries on the same table.
After the program runs for a while, the table gets corrupted and the queries fail.

---- begin -----------------------------------------------------------------
#include "stdafx.h"

// This function creates the database and the test table.
void createDatabaseAndTable(void);

// This function connects to the database.
MYSQL* dbConnect(void);

// This function terminates the program, priting an error message.
void die(const char* errorMsg, const char* mysqlErrorMsg = NULL);

// How many threads per type are created.
#define threadCount 50

// Handles of threads.
HANDLE inserterThreadList[threadCount], removerThreadList[threadCount];

// Main procedure of the inserter threads.
DWORD WINAPI inserterThreadProc(LPVOID lpParameter);

// Main procedure of the remover threads.
DWORD WINAPI removerThreadProc(LPVOID lpParameter);

int main(int argc, char* argv[])
{
	printf("Starting test program\n");

	// Initializes the server.
	if (mysql_server_init(0, NULL, NULL))
		die("mysql_server_init error");
	printf("mysql_server_init OK\n");

	// Creates the dabase and the table.
	createDatabaseAndTable();
	printf("Database and table created.\n");

	// Creates the remover threads.
	for (int i = 0; i < threadCount; i++)
		removerThreadList[i] = CreateThread(NULL, 0, *removerThreadProc, NULL, 0, NULL);
	printf("Remover threads created.\n");

	// Creates the inserter threads.
	for (i = 0; i < threadCount; i++)
		inserterThreadList[i] = CreateThread(NULL, 0, *inserterThreadProc, NULL, 0, NULL);
	printf("Inserter threads created.\n");

	// Repeatedly changes the value of the "passed" flag into the table.
	MYSQL *mySql = dbConnect();
	for (;;) {
		if (mysql_query(mySql, "UPDATE testTable SET passed = 1 WHERE passed = 0"))
			die("UPDATE query error", mysql_error(mySql));
	}

	mysql_close(mySql);
	mysql_thread_end();

	// Closes the server.
	mysql_server_end();
	
	return 0;
}

DWORD WINAPI inserterThreadProc(LPVOID lpParameter)
{
	// Open a connection.
	MYSQL *mySql = dbConnect();
	// Repeatedly insert rows.
	for (;;) {
		if (mysql_query(mySql, 
				"INSERT INTO testTable (name, surname, passed) VALUES (\"Mark\", \"Twain\", 0)"))
			die("inserterThread error", mysql_error(mySql));
	}

	mysql_close(mySql);
	mysql_thread_end();
}

DWORD WINAPI removerThreadProc(LPVOID lpParameter)
{
	// Open a connection.
	MYSQL *mySql = dbConnect();
	// Repeatedly insert rows.
	for (;;) {
		if (mysql_query(mySql, 
				"DELETE FROM testTable WHERE passed = 1"))
			die("inserterThread error", mysql_error(mySql));
	}

	mysql_close(mySql);
	mysql_thread_end();
}

MYSQL* dbConnect(void)
{
	MYSQL *mySql = mysql_init(NULL);
	if (!mySql)
		die("mysql_init error");

	if (!mysql_real_connect(mySql, NULL, NULL, NULL, "testDB", 0, NULL, 0))
		die("mysql_real_connect error", mysql_error(mySql));

	return mySql;
}

void createDatabaseAndTable(void)
{
	MYSQL *mySql = mysql_init(NULL);
	if (!mySql)
		die("mysql_init error");

	if (!mysql_real_connect(mySql, NULL, NULL, NULL, NULL, 0, NULL, 0))
		die("mysql_real_connect error");

	if (mysql_query(mySql, "CREATE DATABASE IF NOT EXISTS testDB"))
		die("mysql_query error creating the database.", mysql_error(mySql));

	mysql_close(mySql);
	mySql = dbConnect();

/*

 // Defining the table with an index will arise a "Duplicate entry "Mark" for key 2" error.

	if (mysql_query(mySql,
			"CREATE TABLE IF NOT EXISTS testTable (\
			id INTEGER NOT NULL AUTO_INCREMENT, \
			name TEXT NOT NULL, \
			surname TEXT NOT NULL, \
			passed TINYINT, \
			PRIMARY KEY(id), \
			INDEX nameIndex (name(255)))" ))
		die("mysql_query error creating table.", mysql_error(mySql));
*/

// Defininf the table without indices will arise other types of error.
	if (mysql_query(mySql,
			"CREATE TABLE IF NOT EXISTS testTable (\
			id INTEGER NOT NULL AUTO_INCREMENT, \
			name TEXT NOT NULL, \
			surname TEXT NOT NULL, \
			passed TINYINT, \
			PRIMARY KEY(id))" ))
		die("mysql_query error creating table.", mysql_error(mySql));

	mysql_close(mySql);
}

void die(const char* errorMsg, const char* mysqlErrorMsg)
{
	if (mysqlErrorMsg)
		printf("### DIE\n%s\n%s\n", errorMsg, mysqlErrorMsg);
	else
		printf("### DIE\n%s\n", errorMsg);
	exit(EXIT_FAILURE);
}
---- end --------------------------------------------------------------------
[26 Nov 2003 7:34] Cristiano Muzi
The Visual C++ 6 project causing the table corruption

Attachment: mySqlTest.zip (application/x-zip-compressed, text), 9.96 KiB.

[26 Nov 2003 9:01] Dean Ellis
Is this not the same issue that you reported with bug #1890?
[26 Nov 2003 9:23] Cristiano Muzi
Yes, it's the same bug; I've reposted it since it was told to me that a test case written in C with Visual C++ would have been more appreciated than one written in delphi, since it would have excluded the problem to be delphi.

I hope I didn't make a mistake reposting the same bug...
[26 Nov 2003 9:34] MySQL Verification Team
Please try to follow the bug with one only report, anyway thank you
for to send your test case with VC++, this will save my time because
I thought to build my own test case running 3 threads.
[26 Nov 2003 10:42] Sergei Golubchik
assigned for Miguel for further investigation
[5 Dec 2003 8:57] Alexey Botchkov
bk commit - 4.0 tree (hf:1.1644)