Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/kostja/mysql/mysql-5.0-10760-new sql/handler.cc: Auto merged sql/mysqld.cc: Auto merged
This commit is contained in:
commit
cd4bce0244
@ -62,17 +62,45 @@ enum thr_lock_type { TL_IGNORE=-1,
|
||||
/* Abort new lock request with an error */
|
||||
TL_WRITE_ONLY};
|
||||
|
||||
enum enum_thr_lock_result { THR_LOCK_SUCCESS= 0, THR_LOCK_ABORTED= 1,
|
||||
THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 };
|
||||
|
||||
|
||||
extern ulong max_write_lock_count;
|
||||
extern ulong table_lock_wait_timeout;
|
||||
extern my_bool thr_lock_inited;
|
||||
extern enum thr_lock_type thr_upgraded_concurrent_insert_lock;
|
||||
|
||||
typedef struct st_thr_lock_data {
|
||||
/*
|
||||
A description of the thread which owns the lock. The address
|
||||
of an instance of this structure is used to uniquely identify the thread.
|
||||
*/
|
||||
|
||||
typedef struct st_thr_lock_info
|
||||
{
|
||||
pthread_t thread;
|
||||
ulong thread_id;
|
||||
ulong n_cursors;
|
||||
} THR_LOCK_INFO;
|
||||
|
||||
/*
|
||||
Lock owner identifier. Globally identifies the lock owner within the
|
||||
thread and among all the threads. The address of an instance of this
|
||||
structure is used as id.
|
||||
*/
|
||||
|
||||
typedef struct st_thr_lock_owner
|
||||
{
|
||||
THR_LOCK_INFO *info;
|
||||
} THR_LOCK_OWNER;
|
||||
|
||||
|
||||
typedef struct st_thr_lock_data {
|
||||
THR_LOCK_OWNER *owner;
|
||||
struct st_thr_lock_data *next,**prev;
|
||||
struct st_thr_lock *lock;
|
||||
pthread_cond_t *cond;
|
||||
enum thr_lock_type type;
|
||||
ulong thread_id;
|
||||
void *status_param; /* Param to status functions */
|
||||
void *debug_print_param;
|
||||
} THR_LOCK_DATA;
|
||||
@ -102,13 +130,18 @@ extern LIST *thr_lock_thread_list;
|
||||
extern pthread_mutex_t THR_LOCK_lock;
|
||||
|
||||
my_bool init_thr_lock(void); /* Must be called once/thread */
|
||||
#define thr_lock_owner_init(owner, info_arg) (owner)->info= (info_arg)
|
||||
void thr_lock_info_init(THR_LOCK_INFO *info);
|
||||
void thr_lock_init(THR_LOCK *lock);
|
||||
void thr_lock_delete(THR_LOCK *lock);
|
||||
void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data,
|
||||
void *status_param);
|
||||
int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type);
|
||||
enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data,
|
||||
THR_LOCK_OWNER *owner,
|
||||
enum thr_lock_type lock_type);
|
||||
void thr_unlock(THR_LOCK_DATA *data);
|
||||
int thr_multi_lock(THR_LOCK_DATA **data,uint count);
|
||||
enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data,
|
||||
uint count, THR_LOCK_OWNER *owner);
|
||||
void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
|
||||
void thr_abort_locks(THR_LOCK *lock);
|
||||
void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread);
|
||||
|
184
mysys/thr_lock.c
184
mysys/thr_lock.c
@ -84,6 +84,7 @@ multiple read locks.
|
||||
|
||||
my_bool thr_lock_inited=0;
|
||||
ulong locks_immediate = 0L, locks_waited = 0L;
|
||||
ulong table_lock_wait_timeout;
|
||||
enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
|
||||
|
||||
/* The following constants are only for debug output */
|
||||
@ -109,25 +110,32 @@ my_bool init_thr_lock()
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline my_bool
|
||||
thr_lock_owner_equal(THR_LOCK_OWNER *rhs, THR_LOCK_OWNER *lhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXTRA_DEBUG
|
||||
#define MAX_FOUND_ERRORS 10 /* Report 10 first errors */
|
||||
static uint found_errors=0;
|
||||
|
||||
static int check_lock(struct st_lock_list *list, const char* lock_type,
|
||||
const char *where, my_bool same_thread, bool no_cond)
|
||||
const char *where, my_bool same_owner, bool no_cond)
|
||||
{
|
||||
THR_LOCK_DATA *data,**prev;
|
||||
uint count=0;
|
||||
pthread_t first_thread;
|
||||
LINT_INIT(first_thread);
|
||||
THR_LOCK_OWNER *first_owner;
|
||||
LINT_INIT(first_owner);
|
||||
|
||||
prev= &list->data;
|
||||
if (list->data)
|
||||
{
|
||||
enum thr_lock_type last_lock_type=list->data->type;
|
||||
|
||||
if (same_thread && list->data)
|
||||
first_thread=list->data->thread;
|
||||
if (same_owner && list->data)
|
||||
first_owner= list->data->owner;
|
||||
for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
|
||||
{
|
||||
if (data->type != last_lock_type)
|
||||
@ -139,7 +147,8 @@ static int check_lock(struct st_lock_list *list, const char* lock_type,
|
||||
count, lock_type, where);
|
||||
return 1;
|
||||
}
|
||||
if (same_thread && ! pthread_equal(data->thread,first_thread) &&
|
||||
if (same_owner &&
|
||||
!thr_lock_owner_equal(data->owner, first_owner) &&
|
||||
last_lock_type != TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
fprintf(stderr,
|
||||
@ -255,8 +264,8 @@ static void check_locks(THR_LOCK *lock, const char *where,
|
||||
}
|
||||
if (lock->read.data)
|
||||
{
|
||||
if (!pthread_equal(lock->write.data->thread,
|
||||
lock->read.data->thread) &&
|
||||
if (!thr_lock_owner_equal(lock->write.data->owner,
|
||||
lock->read.data->owner) &&
|
||||
((lock->write.data->type > TL_WRITE_DELAYED &&
|
||||
lock->write.data->type != TL_WRITE_ONLY) ||
|
||||
((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT ||
|
||||
@ -330,24 +339,32 @@ void thr_lock_delete(THR_LOCK *lock)
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
void thr_lock_info_init(THR_LOCK_INFO *info)
|
||||
{
|
||||
info->thread= pthread_self();
|
||||
info->thread_id= my_thread_id(); /* for debugging */
|
||||
info->n_cursors= 0;
|
||||
}
|
||||
|
||||
/* Initialize a lock instance */
|
||||
|
||||
void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
|
||||
{
|
||||
data->lock=lock;
|
||||
data->type=TL_UNLOCK;
|
||||
data->thread=pthread_self();
|
||||
data->thread_id=my_thread_id(); /* for debugging */
|
||||
data->owner= 0; /* no owner yet */
|
||||
data->status_param=param;
|
||||
data->cond=0;
|
||||
}
|
||||
|
||||
|
||||
static inline my_bool have_old_read_lock(THR_LOCK_DATA *data,pthread_t thread)
|
||||
static inline my_bool
|
||||
have_old_read_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner)
|
||||
{
|
||||
for ( ; data ; data=data->next)
|
||||
{
|
||||
if ((pthread_equal(data->thread,thread)))
|
||||
if (thr_lock_owner_equal(data->owner, owner))
|
||||
return 1; /* Already locked by thread */
|
||||
}
|
||||
return 0;
|
||||
@ -365,12 +382,16 @@ static inline my_bool have_specific_lock(THR_LOCK_DATA *data,
|
||||
}
|
||||
|
||||
|
||||
static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
||||
my_bool in_wait_list)
|
||||
static enum enum_thr_lock_result
|
||||
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
||||
my_bool in_wait_list)
|
||||
{
|
||||
pthread_cond_t *cond=get_cond();
|
||||
struct st_my_thread_var *thread_var=my_thread_var;
|
||||
int result;
|
||||
struct st_my_thread_var *thread_var= my_thread_var;
|
||||
pthread_cond_t *cond= &thread_var->suspend;
|
||||
struct timeval now;
|
||||
struct timespec wait_timeout;
|
||||
enum enum_thr_lock_result result= THR_LOCK_ABORTED;
|
||||
my_bool can_deadlock= test(data->owner->info->n_cursors);
|
||||
|
||||
if (!in_wait_list)
|
||||
{
|
||||
@ -382,31 +403,56 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
||||
/* Set up control struct to allow others to abort locks */
|
||||
thread_var->current_mutex= &data->lock->mutex;
|
||||
thread_var->current_cond= cond;
|
||||
data->cond= cond;
|
||||
|
||||
data->cond=cond;
|
||||
if (can_deadlock)
|
||||
{
|
||||
gettimeofday(&now, 0);
|
||||
wait_timeout.tv_sec= now.tv_sec + table_lock_wait_timeout;
|
||||
wait_timeout.tv_nsec= now.tv_usec * 1000;
|
||||
}
|
||||
while (!thread_var->abort || in_wait_list)
|
||||
{
|
||||
pthread_cond_wait(cond,&data->lock->mutex);
|
||||
if (data->cond != cond)
|
||||
int rc= can_deadlock ? pthread_cond_timedwait(cond, &data->lock->mutex,
|
||||
&wait_timeout) :
|
||||
pthread_cond_wait(cond, &data->lock->mutex);
|
||||
/*
|
||||
We must break the wait if one of the following occurs:
|
||||
- the connection has been aborted (!thread_var->abort), but
|
||||
this is not a delayed insert thread (in_wait_list). For a delayed
|
||||
insert thread the proper action at shutdown is, apparently, to
|
||||
acquire the lock and complete the insert.
|
||||
- the lock has been granted (data->cond is set to NULL by the granter),
|
||||
or the waiting has been aborted (additionally data->type is set to
|
||||
TL_UNLOCK).
|
||||
- the wait has timed out (rc == ETIMEDOUT)
|
||||
Order of checks below is important to not report about timeout
|
||||
if the predicate is true.
|
||||
*/
|
||||
if (data->cond == 0)
|
||||
break;
|
||||
if (rc == ETIMEDOUT)
|
||||
{
|
||||
result= THR_LOCK_WAIT_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->cond || data->type == TL_UNLOCK)
|
||||
{
|
||||
if (data->cond) /* aborted */
|
||||
if (data->cond) /* aborted or timed out */
|
||||
{
|
||||
if (((*data->prev)=data->next)) /* remove from wait-list */
|
||||
data->next->prev= data->prev;
|
||||
else
|
||||
wait->last=data->prev;
|
||||
data->type= TL_UNLOCK; /* No lock */
|
||||
}
|
||||
data->type=TL_UNLOCK; /* No lock */
|
||||
result=1; /* Didn't get lock */
|
||||
check_locks(data->lock,"failed wait_for_lock",0);
|
||||
}
|
||||
else
|
||||
{
|
||||
result=0;
|
||||
result= THR_LOCK_SUCCESS;
|
||||
statistic_increment(locks_waited, &THR_LOCK_lock);
|
||||
if (data->lock->get_status)
|
||||
(*data->lock->get_status)(data->status_param, 0);
|
||||
@ -423,20 +469,24 @@ static my_bool wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
||||
}
|
||||
|
||||
|
||||
int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
enum enum_thr_lock_result
|
||||
thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
||||
enum thr_lock_type lock_type)
|
||||
{
|
||||
THR_LOCK *lock=data->lock;
|
||||
int result=0;
|
||||
enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
|
||||
struct st_lock_list *wait_queue;
|
||||
THR_LOCK_DATA *lock_owner;
|
||||
DBUG_ENTER("thr_lock");
|
||||
|
||||
data->next=0;
|
||||
data->cond=0; /* safety */
|
||||
data->type=lock_type;
|
||||
data->thread=pthread_self(); /* Must be reset ! */
|
||||
data->thread_id=my_thread_id(); /* Must be reset ! */
|
||||
data->owner= owner; /* Must be reset ! */
|
||||
VOID(pthread_mutex_lock(&lock->mutex));
|
||||
DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d",
|
||||
data,data->thread_id,lock,(int) lock_type));
|
||||
data, data->owner->info->thread_id,
|
||||
lock, (int) lock_type));
|
||||
check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
|
||||
"enter read_lock" : "enter write_lock",0);
|
||||
if ((int) lock_type <= (int) TL_READ_NO_INSERT)
|
||||
@ -454,8 +504,8 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
*/
|
||||
|
||||
DBUG_PRINT("lock",("write locked by thread: %ld",
|
||||
lock->write.data->thread_id));
|
||||
if (pthread_equal(data->thread,lock->write.data->thread) ||
|
||||
lock->write.data->owner->info->thread_id));
|
||||
if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
|
||||
(lock->write.data->type <= TL_WRITE_DELAYED &&
|
||||
(((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) ||
|
||||
(lock->write.data->type != TL_WRITE_CONCURRENT_INSERT &&
|
||||
@ -476,14 +526,14 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
{
|
||||
/* We are not allowed to get a READ lock in this case */
|
||||
data->type=TL_UNLOCK;
|
||||
result=1; /* Can't wait for this one */
|
||||
result= THR_LOCK_ABORTED; /* Can't wait for this one */
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
else if (!lock->write_wait.data ||
|
||||
lock->write_wait.data->type <= TL_WRITE_LOW_PRIORITY ||
|
||||
lock_type == TL_READ_HIGH_PRIORITY ||
|
||||
have_old_read_lock(lock->read.data,data->thread))
|
||||
have_old_read_lock(lock->read.data, data->owner))
|
||||
{ /* No important write-locks */
|
||||
(*lock->read.last)=data; /* Add to running FIFO */
|
||||
data->prev=lock->read.last;
|
||||
@ -496,8 +546,12 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
statistic_increment(locks_immediate,&THR_LOCK_lock);
|
||||
goto end;
|
||||
}
|
||||
/* Can't get lock yet; Wait for it */
|
||||
DBUG_RETURN(wait_for_lock(&lock->read_wait,data,0));
|
||||
/*
|
||||
We're here if there is an active write lock or no write
|
||||
lock but a high priority write waiting in the write_wait queue.
|
||||
In the latter case we should yield the lock to the writer.
|
||||
*/
|
||||
wait_queue= &lock->read_wait;
|
||||
}
|
||||
else /* Request for WRITE lock */
|
||||
{
|
||||
@ -506,7 +560,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
if (lock->write.data && lock->write.data->type == TL_WRITE_ONLY)
|
||||
{
|
||||
data->type=TL_UNLOCK;
|
||||
result=1; /* Can't wait for this one */
|
||||
result= THR_LOCK_ABORTED; /* Can't wait for this one */
|
||||
goto end;
|
||||
}
|
||||
/*
|
||||
@ -540,7 +594,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
{
|
||||
/* We are not allowed to get a lock in this case */
|
||||
data->type=TL_UNLOCK;
|
||||
result=1; /* Can't wait for this one */
|
||||
result= THR_LOCK_ABORTED; /* Can't wait for this one */
|
||||
goto end;
|
||||
}
|
||||
|
||||
@ -549,7 +603,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
TL_WRITE_ALLOW_WRITE, TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED in
|
||||
the same thread, but this will never happen within MySQL.
|
||||
*/
|
||||
if (pthread_equal(data->thread,lock->write.data->thread) ||
|
||||
if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
|
||||
(lock_type == TL_WRITE_ALLOW_WRITE &&
|
||||
!lock->write_wait.data &&
|
||||
lock->write.data->type == TL_WRITE_ALLOW_WRITE))
|
||||
@ -572,7 +626,7 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
goto end;
|
||||
}
|
||||
DBUG_PRINT("lock",("write locked by thread: %ld",
|
||||
lock->write.data->thread_id));
|
||||
lock->write.data->owner->info->thread_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -608,10 +662,24 @@ int thr_lock(THR_LOCK_DATA *data,enum thr_lock_type lock_type)
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld",
|
||||
lock->read.data->thread_id,data->type));
|
||||
lock->read.data->owner->info->thread_id, data->type));
|
||||
}
|
||||
DBUG_RETURN(wait_for_lock(&lock->write_wait,data,0));
|
||||
wait_queue= &lock->write_wait;
|
||||
}
|
||||
/*
|
||||
Try to detect a trivial deadlock when using cursors: attempt to
|
||||
lock a table that is already locked by an open cursor within the
|
||||
same connection. lock_owner can be zero if we succumbed to a high
|
||||
priority writer in the write_wait queue.
|
||||
*/
|
||||
lock_owner= lock->read.data ? lock->read.data : lock->write.data;
|
||||
if (lock_owner && lock_owner->owner->info == owner->info)
|
||||
{
|
||||
result= THR_LOCK_DEADLOCK;
|
||||
goto end;
|
||||
}
|
||||
/* Can't get lock yet; Wait for it */
|
||||
DBUG_RETURN(wait_for_lock(wait_queue, data, 0));
|
||||
end:
|
||||
pthread_mutex_unlock(&lock->mutex);
|
||||
DBUG_RETURN(result);
|
||||
@ -656,7 +724,7 @@ static inline void free_all_read_locks(THR_LOCK *lock,
|
||||
lock->read_no_write_count++;
|
||||
}
|
||||
DBUG_PRINT("lock",("giving read lock to thread: %ld",
|
||||
data->thread_id));
|
||||
data->owner->info->thread_id));
|
||||
data->cond=0; /* Mark thread free */
|
||||
VOID(pthread_cond_signal(cond));
|
||||
} while ((data=data->next));
|
||||
@ -674,7 +742,7 @@ void thr_unlock(THR_LOCK_DATA *data)
|
||||
enum thr_lock_type lock_type=data->type;
|
||||
DBUG_ENTER("thr_unlock");
|
||||
DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx",
|
||||
data,data->thread_id,lock));
|
||||
data, data->owner->info->thread_id, lock));
|
||||
pthread_mutex_lock(&lock->mutex);
|
||||
check_locks(lock,"start of release lock",0);
|
||||
|
||||
@ -734,7 +802,7 @@ void thr_unlock(THR_LOCK_DATA *data)
|
||||
(*lock->check_status)(data->status_param))
|
||||
data->type=TL_WRITE; /* Upgrade lock */
|
||||
DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld",
|
||||
data->type,data->thread_id));
|
||||
data->type, data->owner->info->thread_id));
|
||||
{
|
||||
pthread_cond_t *cond=data->cond;
|
||||
data->cond=0; /* Mark thread free */
|
||||
@ -842,7 +910,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count)
|
||||
}
|
||||
|
||||
|
||||
int thr_multi_lock(THR_LOCK_DATA **data,uint count)
|
||||
enum enum_thr_lock_result
|
||||
thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
|
||||
{
|
||||
THR_LOCK_DATA **pos,**end;
|
||||
DBUG_ENTER("thr_multi_lock");
|
||||
@ -852,10 +921,11 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count)
|
||||
/* lock everything */
|
||||
for (pos=data,end=data+count; pos < end ; pos++)
|
||||
{
|
||||
if (thr_lock(*pos,(*pos)->type))
|
||||
enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type);
|
||||
if (result != THR_LOCK_SUCCESS)
|
||||
{ /* Aborted */
|
||||
thr_multi_unlock(data,(uint) (pos-data));
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
#ifdef MAIN
|
||||
printf("Thread: %s Got lock: 0x%lx type: %d\n",my_thread_name(),
|
||||
@ -909,7 +979,7 @@ int thr_multi_lock(THR_LOCK_DATA **data,uint count)
|
||||
} while (pos != data);
|
||||
}
|
||||
#endif
|
||||
DBUG_RETURN(0);
|
||||
DBUG_RETURN(THR_LOCK_SUCCESS);
|
||||
}
|
||||
|
||||
/* free all locks */
|
||||
@ -932,7 +1002,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: %ld lock: 0x%lx",
|
||||
*pos,(*pos)->thread_id,(*pos)->lock));
|
||||
*pos, (*pos)->owner->info->thread_id, (*pos)->lock));
|
||||
}
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
@ -952,6 +1022,7 @@ void thr_abort_locks(THR_LOCK *lock)
|
||||
for (data=lock->read_wait.data; data ; data=data->next)
|
||||
{
|
||||
data->type=TL_UNLOCK; /* Mark killed */
|
||||
/* It's safe to signal the cond first: we're still holding the mutex. */
|
||||
pthread_cond_signal(data->cond);
|
||||
data->cond=0; /* Removed from list */
|
||||
}
|
||||
@ -985,10 +1056,11 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
|
||||
pthread_mutex_lock(&lock->mutex);
|
||||
for (data= lock->read_wait.data; data ; data= data->next)
|
||||
{
|
||||
if (pthread_equal(thread, data->thread))
|
||||
if (pthread_equal(thread, data->owner->info->thread))
|
||||
{
|
||||
DBUG_PRINT("info",("Aborting read-wait lock"));
|
||||
data->type= TL_UNLOCK; /* Mark killed */
|
||||
/* It's safe to signal the cond first: we're still holding the mutex. */
|
||||
pthread_cond_signal(data->cond);
|
||||
data->cond= 0; /* Removed from list */
|
||||
|
||||
@ -1000,7 +1072,7 @@ void thr_abort_locks_for_thread(THR_LOCK *lock, pthread_t thread)
|
||||
}
|
||||
for (data= lock->write_wait.data; data ; data= data->next)
|
||||
{
|
||||
if (pthread_equal(thread, data->thread))
|
||||
if (pthread_equal(thread, data->owner->info->thread))
|
||||
{
|
||||
DBUG_PRINT("info",("Aborting write-wait lock"));
|
||||
data->type= TL_UNLOCK;
|
||||
@ -1117,7 +1189,8 @@ static void thr_print_lock(const char* name,struct st_lock_list *list)
|
||||
prev= &list->data;
|
||||
for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
|
||||
{
|
||||
printf("0x%lx (%lu:%d); ",(ulong) data,data->thread_id,(int) data->type);
|
||||
printf("0x%lx (%lu:%d); ", (ulong) data, data->owner->info->thread_id,
|
||||
(int) data->type);
|
||||
if (data->prev != prev)
|
||||
printf("\nWarning: prev didn't point at previous lock\n");
|
||||
prev= &data->next;
|
||||
@ -1250,11 +1323,16 @@ static void *test_thread(void *arg)
|
||||
{
|
||||
int i,j,param=*((int*) arg);
|
||||
THR_LOCK_DATA data[MAX_LOCK_COUNT];
|
||||
THR_LOCK_OWNER owner;
|
||||
THR_LOCK_INFO lock_info;
|
||||
THR_LOCK_DATA *multi_locks[MAX_LOCK_COUNT];
|
||||
my_thread_init();
|
||||
|
||||
printf("Thread %s (%d) started\n",my_thread_name(),param); fflush(stdout);
|
||||
|
||||
|
||||
thr_lock_info_init(&lock_info);
|
||||
thr_lock_owner_init(&owner, &lock_info);
|
||||
for (i=0; i < lock_counts[param] ; i++)
|
||||
thr_lock_data_init(locks+tests[param][i].lock_nr,data+i,NULL);
|
||||
for (j=1 ; j < 10 ; j++) /* try locking 10 times */
|
||||
@ -1264,7 +1342,7 @@ static void *test_thread(void *arg)
|
||||
multi_locks[i]= &data[i];
|
||||
data[i].type= tests[param][i].lock_type;
|
||||
}
|
||||
thr_multi_lock(multi_locks,lock_counts[param]);
|
||||
thr_multi_lock(multi_locks, lock_counts[param], &owner);
|
||||
pthread_mutex_lock(&LOCK_thread_count);
|
||||
{
|
||||
int tmp=rand() & 7; /* Do something from 0-2 sec */
|
||||
|
@ -149,7 +149,8 @@ static handlerton archive_hton = {
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0 /* rollback_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
|
||||
@ -208,6 +209,15 @@ bool archive_db_end()
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ha_archive::ha_archive(TABLE *table_arg)
|
||||
:handler(&archive_hton, table_arg), delayed_insert(0), bulk_insert(0)
|
||||
{
|
||||
/* Set our original buffer from pre-allocated memory */
|
||||
buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
|
||||
|
||||
/* The size of the offset value we will use for position() */
|
||||
ref_length = sizeof(z_off_t);
|
||||
}
|
||||
|
||||
/*
|
||||
This method reads the header of a datafile and returns whether or not it was successful.
|
||||
|
@ -58,14 +58,7 @@ class ha_archive: public handler
|
||||
bool bulk_insert; /* If we are performing a bulk insert */
|
||||
|
||||
public:
|
||||
ha_archive(TABLE *table): handler(table), delayed_insert(0), bulk_insert(0)
|
||||
{
|
||||
/* Set our original buffer from pre-allocated memory */
|
||||
buffer.set((char *)byte_buffer, IO_SIZE, system_charset_info);
|
||||
|
||||
/* The size of the offset value we will use for position() */
|
||||
ref_length = sizeof(z_off_t);
|
||||
}
|
||||
ha_archive(TABLE *table_arg);
|
||||
~ha_archive()
|
||||
{
|
||||
}
|
||||
|
@ -72,6 +72,24 @@
|
||||
#ifdef HAVE_EXAMPLE_DB
|
||||
#include "ha_example.h"
|
||||
|
||||
|
||||
static handlerton example_hton= {
|
||||
"CSV",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
/* Variables for example share methods */
|
||||
static HASH example_open_tables; // Hash used to track open tables
|
||||
pthread_mutex_t example_mutex; // This is the mutex we use to init the hash
|
||||
@ -179,6 +197,10 @@ static int free_share(EXAMPLE_SHARE *share)
|
||||
}
|
||||
|
||||
|
||||
ha_example::ha_example(TABLE *table_arg)
|
||||
:handler(&example_hton, table_arg)
|
||||
{}
|
||||
|
||||
/*
|
||||
If frm_error() is called then we will use this to to find out what file extentions
|
||||
exist for the storage engine. This is also used by the default rename_table and
|
||||
|
@ -45,9 +45,7 @@ class ha_example: public handler
|
||||
EXAMPLE_SHARE *share; /* Shared lock info */
|
||||
|
||||
public:
|
||||
ha_example(TABLE *table): handler(table)
|
||||
{
|
||||
}
|
||||
ha_example(TABLE *table_arg);
|
||||
~ha_example()
|
||||
{
|
||||
}
|
||||
|
@ -54,6 +54,23 @@ pthread_mutex_t tina_mutex;
|
||||
static HASH tina_open_tables;
|
||||
static int tina_init= 0;
|
||||
|
||||
static handlerton tina_hton= {
|
||||
"CSV",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
** TINA tables
|
||||
*****************************************************************************/
|
||||
@ -228,6 +245,20 @@ byte * find_eoln(byte *data, off_t begin, off_t end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ha_tina::ha_tina(TABLE *table_arg)
|
||||
:handler(&tina_hton, table_arg),
|
||||
/*
|
||||
These definitions are found in hanler.h
|
||||
These are not probably completely right.
|
||||
*/
|
||||
current_position(0), next_position(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH)
|
||||
{
|
||||
/* Set our original buffers from pre-allocated memory */
|
||||
buffer.set(byte_buffer, IO_SIZE, system_charset_info);
|
||||
chain= chain_buffer;
|
||||
}
|
||||
|
||||
/*
|
||||
Encode a buffer into the quoted format.
|
||||
*/
|
||||
|
@ -49,18 +49,8 @@ class ha_tina: public handler
|
||||
byte chain_alloced;
|
||||
uint32 chain_size;
|
||||
|
||||
public:
|
||||
ha_tina(TABLE *table): handler(table),
|
||||
/*
|
||||
These definitions are found in hanler.h
|
||||
Theses are not probably completely right.
|
||||
*/
|
||||
current_position(0), next_position(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH)
|
||||
{
|
||||
/* Set our original buffers from pre-allocated memory */
|
||||
buffer.set(byte_buffer, IO_SIZE, system_charset_info);
|
||||
chain = chain_buffer;
|
||||
}
|
||||
public:
|
||||
ha_tina(TABLE *table_arg);
|
||||
~ha_tina()
|
||||
{
|
||||
if (chain_alloced)
|
||||
|
@ -120,7 +120,8 @@ static handlerton berkeley_hton = {
|
||||
NULL, /* prepare */
|
||||
NULL, /* recover */
|
||||
NULL, /* commit_by_xid */
|
||||
NULL /* rollback_by_xid */
|
||||
NULL, /* rollback_by_xid */
|
||||
HTON_CLOSE_CURSORS_AT_COMMIT
|
||||
};
|
||||
|
||||
typedef struct st_berkeley_trx_data {
|
||||
@ -372,6 +373,17 @@ void berkeley_cleanup_log_files(void)
|
||||
/*****************************************************************************
|
||||
** Berkeley DB tables
|
||||
*****************************************************************************/
|
||||
|
||||
ha_berkeley::ha_berkeley(TABLE *table_arg)
|
||||
:handler(&berkeley_hton, table_arg), alloc_ptr(0), rec_buff(0), file(0),
|
||||
int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ |
|
||||
HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT |
|
||||
HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED |
|
||||
HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX),
|
||||
changed_rows(0), last_dup_key((uint) -1), version(0), using_ignore(0)
|
||||
{}
|
||||
|
||||
|
||||
static const char *ha_berkeley_exts[] = {
|
||||
ha_berkeley_ext,
|
||||
NullS
|
||||
|
@ -85,12 +85,7 @@ class ha_berkeley: public handler
|
||||
DBT *get_pos(DBT *to, byte *pos);
|
||||
|
||||
public:
|
||||
ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), file(0),
|
||||
int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ |
|
||||
HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT |
|
||||
HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED |
|
||||
HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX),
|
||||
changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) {}
|
||||
ha_berkeley(TABLE *table_arg);
|
||||
~ha_berkeley() {}
|
||||
const char *table_type() const { return "BerkeleyDB"; }
|
||||
ulong index_flags(uint idx, uint part, bool all_parts) const;
|
||||
|
@ -24,6 +24,34 @@
|
||||
#include "ha_blackhole.h"
|
||||
|
||||
|
||||
/* Blackhole storage engine handlerton */
|
||||
|
||||
static handlerton myisam_hton= {
|
||||
"BLACKHOLE",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
** BLACKHOLE tables
|
||||
*****************************************************************************/
|
||||
|
||||
ha_blackhole::ha_blackhole(TABLE *table_arg)
|
||||
:handler(&blackhole_hton, table_arg)
|
||||
{}
|
||||
|
||||
|
||||
static const char *ha_blackhole_exts[] = {
|
||||
NullS
|
||||
};
|
||||
|
@ -28,9 +28,7 @@ class ha_blackhole: public handler
|
||||
THR_LOCK thr_lock;
|
||||
|
||||
public:
|
||||
ha_blackhole(TABLE *table): handler(table)
|
||||
{
|
||||
}
|
||||
ha_blackhole(TABLE *table_arg);
|
||||
~ha_blackhole()
|
||||
{
|
||||
}
|
||||
|
@ -679,6 +679,37 @@ error:
|
||||
}
|
||||
|
||||
|
||||
/* Federated storage engine handlerton */
|
||||
|
||||
static handlerton federated_hton= {
|
||||
"FEDERATED",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
** FEDERATED tables
|
||||
*****************************************************************************/
|
||||
|
||||
ha_federated::ha_federated(TABLE *table_arg)
|
||||
:handler(&federated_hton, table_arg),
|
||||
mysql(0), stored_result(0), scan_flag(0),
|
||||
ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0)
|
||||
{}
|
||||
|
||||
|
||||
/*
|
||||
Convert MySQL result set row to handler internal format
|
||||
|
||||
|
@ -162,11 +162,7 @@ private:
|
||||
bool records_in_range);
|
||||
|
||||
public:
|
||||
ha_federated(TABLE *table): handler(table),
|
||||
mysql(0), stored_result(0), scan_flag(0),
|
||||
ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0)
|
||||
{
|
||||
}
|
||||
ha_federated(TABLE *table_arg);
|
||||
~ha_federated()
|
||||
{
|
||||
}
|
||||
|
@ -23,9 +23,33 @@
|
||||
#include <myisampack.h>
|
||||
#include "ha_heap.h"
|
||||
|
||||
static handlerton heap_hton= {
|
||||
"MEMORY",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
** HEAP tables
|
||||
*****************************************************************************/
|
||||
|
||||
ha_heap::ha_heap(TABLE *table_arg)
|
||||
:handler(&heap_hton, table_arg), file(0), records_changed(0),
|
||||
key_stats_ok(0)
|
||||
{}
|
||||
|
||||
|
||||
static const char *ha_heap_exts[] = {
|
||||
NullS
|
||||
};
|
||||
|
@ -31,8 +31,7 @@ class ha_heap: public handler
|
||||
uint records_changed;
|
||||
bool key_stats_ok;
|
||||
public:
|
||||
ha_heap(TABLE *table): handler(table), file(0), records_changed(0),
|
||||
key_stats_ok(0) {}
|
||||
ha_heap(TABLE *table);
|
||||
~ha_heap() {}
|
||||
const char *table_type() const
|
||||
{
|
||||
|
@ -215,7 +215,14 @@ static handlerton innobase_hton = {
|
||||
innobase_xa_prepare, /* prepare */
|
||||
innobase_xa_recover, /* recover */
|
||||
innobase_commit_by_xid, /* commit_by_xid */
|
||||
innobase_rollback_by_xid /* rollback_by_xid */
|
||||
innobase_rollback_by_xid, /* rollback_by_xid */
|
||||
/*
|
||||
For now when one opens a cursor, MySQL does not create an own
|
||||
InnoDB consistent read view for it, and uses the view of the
|
||||
currently active transaction. Therefore, cursors can not
|
||||
survive COMMIT or ROLLBACK statements, which free this view.
|
||||
*/
|
||||
HTON_CLOSE_CURSORS_AT_COMMIT
|
||||
};
|
||||
|
||||
/*********************************************************************
|
||||
@ -765,6 +772,24 @@ check_trx_exists(
|
||||
return(trx);
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
Construct ha_innobase handler. */
|
||||
|
||||
ha_innobase::ha_innobase(TABLE *table_arg)
|
||||
:handler(&innobase_hton, table_arg),
|
||||
int_table_flags(HA_REC_NOT_IN_SEQ |
|
||||
HA_NULL_IN_KEY |
|
||||
HA_CAN_INDEX_BLOBS |
|
||||
HA_CAN_SQL_HANDLER |
|
||||
HA_NOT_EXACT_COUNT |
|
||||
HA_PRIMARY_KEY_IN_READ_INDEX |
|
||||
HA_TABLE_SCAN_ON_INDEX),
|
||||
last_dup_key((uint) -1),
|
||||
start_of_scan(0),
|
||||
num_write_row(0)
|
||||
{}
|
||||
|
||||
/*************************************************************************
|
||||
Updates the user_thd field in a handle and also allocates a new InnoDB
|
||||
transaction handle if needed, and updates the transaction fields in the
|
||||
|
@ -81,19 +81,7 @@ class ha_innobase: public handler
|
||||
|
||||
/* Init values for the class: */
|
||||
public:
|
||||
ha_innobase(TABLE *table): handler(table),
|
||||
int_table_flags(HA_REC_NOT_IN_SEQ |
|
||||
HA_NULL_IN_KEY |
|
||||
HA_CAN_INDEX_BLOBS |
|
||||
HA_CAN_SQL_HANDLER |
|
||||
HA_NOT_EXACT_COUNT |
|
||||
HA_PRIMARY_KEY_IN_READ_INDEX |
|
||||
HA_TABLE_SCAN_ON_INDEX),
|
||||
last_dup_key((uint) -1),
|
||||
start_of_scan(0),
|
||||
num_write_row(0)
|
||||
{
|
||||
}
|
||||
ha_innobase(TABLE *table_arg);
|
||||
~ha_innobase() {}
|
||||
/*
|
||||
Get the row type from the storage engine. If this method returns
|
||||
|
@ -44,6 +44,29 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"",
|
||||
** MyISAM tables
|
||||
*****************************************************************************/
|
||||
|
||||
/* MyISAM handlerton */
|
||||
|
||||
static handlerton myisam_hton= {
|
||||
"MyISAM",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
/*
|
||||
MyISAM doesn't support transactions and doesn't have
|
||||
transaction-dependent context: cursors can survive a commit.
|
||||
*/
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
// collect errors printed by mi_check routines
|
||||
|
||||
static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
|
||||
@ -123,6 +146,17 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
|
||||
|
||||
}
|
||||
|
||||
|
||||
ha_myisam::ha_myisam(TABLE *table_arg)
|
||||
:handler(&myisam_hton, table_arg), file(0),
|
||||
int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
|
||||
HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
|
||||
HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME |
|
||||
HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD),
|
||||
can_enable_indexes(1)
|
||||
{}
|
||||
|
||||
|
||||
static const char *ha_myisam_exts[] = {
|
||||
".MYI",
|
||||
".MYD",
|
||||
|
@ -43,13 +43,7 @@ class ha_myisam: public handler
|
||||
int repair(THD *thd, MI_CHECK ¶m, bool optimize);
|
||||
|
||||
public:
|
||||
ha_myisam(TABLE *table): handler(table), file(0),
|
||||
int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER |
|
||||
HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY |
|
||||
HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME |
|
||||
HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD),
|
||||
can_enable_indexes(1)
|
||||
{}
|
||||
ha_myisam(TABLE *table_arg);
|
||||
~ha_myisam() {}
|
||||
const char *table_type() const { return "MyISAM"; }
|
||||
const char *index_type(uint key_number);
|
||||
|
@ -32,6 +32,30 @@
|
||||
** MyISAM MERGE tables
|
||||
*****************************************************************************/
|
||||
|
||||
/* MyISAM MERGE handlerton */
|
||||
|
||||
static handlerton myisammrg_hton= {
|
||||
"MRG_MyISAM",
|
||||
0, /* slot */
|
||||
0, /* savepoint size. */
|
||||
0, /* close_connection */
|
||||
0, /* savepoint */
|
||||
0, /* rollback to savepoint */
|
||||
0, /* release savepoint */
|
||||
0, /* commit */
|
||||
0, /* rollback */
|
||||
0, /* prepare */
|
||||
0, /* recover */
|
||||
0, /* commit_by_xid */
|
||||
0, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
|
||||
ha_myisammrg::ha_myisammrg(TABLE *table_arg)
|
||||
:handler(&myisammrg_hton, table_arg), file(0)
|
||||
{}
|
||||
|
||||
static const char *ha_myisammrg_exts[] = {
|
||||
".MRG",
|
||||
NullS
|
||||
|
@ -28,7 +28,7 @@ class ha_myisammrg: public handler
|
||||
MYRG_INFO *file;
|
||||
|
||||
public:
|
||||
ha_myisammrg(TABLE *table): handler(table), file(0) {}
|
||||
ha_myisammrg(TABLE *table_arg);
|
||||
~ha_myisammrg() {}
|
||||
const char *table_type() const { return "MRG_MyISAM"; }
|
||||
const char **bas_ext() const;
|
||||
|
@ -62,7 +62,8 @@ static handlerton ndbcluster_hton = {
|
||||
NULL, /* prepare */
|
||||
NULL, /* recover */
|
||||
NULL, /* commit_by_xid */
|
||||
NULL /* rollback_by_xid */
|
||||
NULL, /* rollback_by_xid */
|
||||
HTON_NO_FLAGS
|
||||
};
|
||||
|
||||
#define NDB_HIDDEN_PRIMARY_KEY_LENGTH 8
|
||||
@ -4174,7 +4175,7 @@ ulonglong ha_ndbcluster::get_auto_increment()
|
||||
*/
|
||||
|
||||
ha_ndbcluster::ha_ndbcluster(TABLE *table_arg):
|
||||
handler(table_arg),
|
||||
handler(&ndbcluster_hton, table_arg),
|
||||
m_active_trans(NULL),
|
||||
m_active_cursor(NULL),
|
||||
m_table(NULL),
|
||||
|
@ -208,15 +208,8 @@ handler *get_new_handler(TABLE *table, enum db_type db_type)
|
||||
case DB_TYPE_HASH:
|
||||
return new ha_hash(table);
|
||||
#endif
|
||||
#ifdef HAVE_ISAM
|
||||
case DB_TYPE_MRG_ISAM:
|
||||
return new ha_isammrg(table);
|
||||
case DB_TYPE_ISAM:
|
||||
return new ha_isam(table);
|
||||
#else
|
||||
case DB_TYPE_MRG_ISAM:
|
||||
return new ha_myisammrg(table);
|
||||
#endif
|
||||
#ifdef HAVE_BERKELEY_DB
|
||||
case DB_TYPE_BERKELEY_DB:
|
||||
return new ha_berkeley(table);
|
||||
@ -634,6 +627,11 @@ int ha_commit_trans(THD *thd, bool all)
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_EXECUTE_IF("crash_commit_before", abort(););
|
||||
|
||||
/* Close all cursors that can not survive COMMIT */
|
||||
if (is_real_trans) /* not a statement commit */
|
||||
thd->stmt_map.close_transient_cursors();
|
||||
|
||||
if (!trans->no_2pc && trans->nht > 1)
|
||||
{
|
||||
for (; *ht && !error; ht++)
|
||||
@ -735,6 +733,10 @@ int ha_rollback_trans(THD *thd, bool all)
|
||||
#ifdef USING_TRANSACTIONS
|
||||
if (trans->nht)
|
||||
{
|
||||
/* Close all cursors that can not survive ROLLBACK */
|
||||
if (is_real_trans) /* not a statement commit */
|
||||
thd->stmt_map.close_transient_cursors();
|
||||
|
||||
for (handlerton **ht=trans->ht; *ht; ht++)
|
||||
{
|
||||
int err;
|
||||
|
@ -349,8 +349,13 @@ typedef struct
|
||||
int (*recover)(XID *xid_list, uint len);
|
||||
int (*commit_by_xid)(XID *xid);
|
||||
int (*rollback_by_xid)(XID *xid);
|
||||
uint32 flags; /* global handler flags */
|
||||
} handlerton;
|
||||
|
||||
/* Possible flags of a handlerton */
|
||||
#define HTON_NO_FLAGS 0
|
||||
#define HTON_CLOSE_CURSORS_AT_COMMIT 1
|
||||
|
||||
typedef struct st_thd_trans
|
||||
{
|
||||
/* number of entries in the ht[] */
|
||||
@ -445,6 +450,7 @@ class handler :public Sql_alloc
|
||||
virtual int rnd_end() { return 0; }
|
||||
|
||||
public:
|
||||
const handlerton *ht; /* storage engine of this handler */
|
||||
byte *ref; /* Pointer to current row */
|
||||
byte *dupp_ref; /* Pointer to dupp row */
|
||||
ulonglong data_file_length; /* Length off data file */
|
||||
@ -486,7 +492,8 @@ public:
|
||||
bool implicit_emptied; /* Can be !=0 only if HEAP */
|
||||
const COND *pushed_cond;
|
||||
|
||||
handler(TABLE *table_arg) :table(table_arg),
|
||||
handler(const handlerton *ht_arg, TABLE *table_arg) :table(table_arg),
|
||||
ht(ht_arg),
|
||||
ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0),
|
||||
delete_length(0), auto_increment_value(0),
|
||||
records(0), deleted(0), mean_rec_length(0),
|
||||
|
19
sql/lock.cc
19
sql/lock.cc
@ -103,6 +103,10 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
|
||||
{
|
||||
MYSQL_LOCK *sql_lock;
|
||||
TABLE *write_lock_used;
|
||||
int rc;
|
||||
/* Map the return value of thr_lock to an error from errmsg.txt */
|
||||
const static int thr_lock_errno_to_mysql[]=
|
||||
{ 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
|
||||
DBUG_ENTER("mysql_lock_tables");
|
||||
|
||||
for (;;)
|
||||
@ -135,15 +139,24 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, uint flags)
|
||||
{
|
||||
my_free((gptr) sql_lock,MYF(0));
|
||||
sql_lock=0;
|
||||
thd->proc_info=0;
|
||||
break;
|
||||
}
|
||||
thd->proc_info="Table lock";
|
||||
thd->locked=1;
|
||||
if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count))
|
||||
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks,
|
||||
sql_lock->lock_count,
|
||||
thd->lock_id)];
|
||||
if (rc > 1) /* a timeout or a deadlock */
|
||||
{
|
||||
my_error(rc, MYF(0));
|
||||
my_free((gptr) sql_lock,MYF(0));
|
||||
sql_lock= 0;
|
||||
break;
|
||||
}
|
||||
else if (rc == 1) /* aborted */
|
||||
{
|
||||
thd->some_tables_deleted=1; // Try again
|
||||
sql_lock->lock_count=0; // Locks are alread freed
|
||||
sql_lock->lock_count= 0; // Locks are already freed
|
||||
}
|
||||
else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
|
||||
{
|
||||
|
@ -4342,7 +4342,8 @@ enum options_mysqld
|
||||
OPT_ENABLE_LARGE_PAGES,
|
||||
OPT_TIMED_MUTEXES,
|
||||
OPT_OLD_STYLE_USER_LIMITS,
|
||||
OPT_LOG_SLOW_ADMIN_STATEMENTS
|
||||
OPT_LOG_SLOW_ADMIN_STATEMENTS,
|
||||
OPT_TABLE_LOCK_WAIT_TIMEOUT
|
||||
};
|
||||
|
||||
|
||||
@ -5595,6 +5596,11 @@ The minimum value for this variable is 4096.",
|
||||
"The number of open tables for all threads.", (gptr*) &table_cache_size,
|
||||
(gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 512*1024L,
|
||||
0, 1, 0},
|
||||
{"table_lock_wait_timeout", OPT_TABLE_LOCK_WAIT_TIMEOUT, "Timeout in "
|
||||
"seconds to wait for a table level lock before returning an error. Used"
|
||||
" only if the connection has active cursors.",
|
||||
(gptr*) &table_lock_wait_timeout, (gptr*) &table_lock_wait_timeout,
|
||||
0, GET_ULONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0},
|
||||
{"thread_cache_size", OPT_THREAD_CACHE_SIZE,
|
||||
"How many threads we should keep in a cache for reuse.",
|
||||
(gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG,
|
||||
@ -7084,4 +7090,6 @@ template class I_List_iterator<THD>;
|
||||
template class I_List<i_string>;
|
||||
template class I_List<i_string_pair>;
|
||||
template class I_List<NAMED_LIST>;
|
||||
template class I_List<Statement>;
|
||||
template class I_List_iterator<Statement>;
|
||||
#endif
|
||||
|
@ -375,6 +375,8 @@ sys_var_thd_ulong sys_sync_replication_timeout(
|
||||
sys_var_bool_ptr sys_sync_frm("sync_frm", &opt_sync_frm);
|
||||
sys_var_long_ptr sys_table_cache_size("table_cache",
|
||||
&table_cache_size);
|
||||
sys_var_long_ptr sys_table_lock_wait_timeout("table_lock_wait_timeout",
|
||||
&table_lock_wait_timeout);
|
||||
sys_var_long_ptr sys_thread_cache_size("thread_cache_size",
|
||||
&thread_cache_size);
|
||||
sys_var_thd_enum sys_tx_isolation("tx_isolation",
|
||||
@ -682,6 +684,7 @@ sys_var *sys_variables[]=
|
||||
#endif
|
||||
&sys_sync_frm,
|
||||
&sys_table_cache_size,
|
||||
&sys_table_lock_wait_timeout,
|
||||
&sys_table_type,
|
||||
&sys_thread_cache_size,
|
||||
&sys_time_format,
|
||||
@ -972,6 +975,7 @@ struct show_var_st init_vars[]= {
|
||||
{"system_time_zone", system_time_zone, SHOW_CHAR},
|
||||
#endif
|
||||
{"table_cache", (char*) &table_cache_size, SHOW_LONG},
|
||||
{"table_lock_wait_timeout", (char*) &table_lock_wait_timeout, SHOW_LONG },
|
||||
{sys_table_type.name, (char*) &sys_table_type, SHOW_SYS},
|
||||
{sys_thread_cache_size.name,(char*) &sys_thread_cache_size, SHOW_SYS},
|
||||
#ifdef HAVE_THR_SETCONCURRENCY
|
||||
|
@ -173,6 +173,7 @@ Open_tables_state::Open_tables_state()
|
||||
THD::THD()
|
||||
:Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
|
||||
Open_tables_state(),
|
||||
lock_id(&main_lock_id),
|
||||
user_time(0), global_read_lock(0), is_fatal_error(0),
|
||||
rand_used(0), time_zone_used(0),
|
||||
last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
|
||||
@ -265,6 +266,8 @@ THD::THD()
|
||||
tablespace_op=FALSE;
|
||||
ulong tmp=sql_rnd_with_mutex();
|
||||
randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
|
||||
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
|
||||
thr_lock_owner_init(&main_lock_id, &lock_info);
|
||||
}
|
||||
|
||||
|
||||
@ -406,6 +409,8 @@ THD::~THD()
|
||||
net_end(&net);
|
||||
}
|
||||
#endif
|
||||
stmt_map.destroy(); /* close all prepared statements */
|
||||
DBUG_ASSERT(lock_info.n_cursors == 0);
|
||||
if (!cleanup_done)
|
||||
cleanup();
|
||||
|
||||
@ -518,6 +523,7 @@ bool THD::store_globals()
|
||||
if this is the slave SQL thread.
|
||||
*/
|
||||
variables.pseudo_thread_id= thread_id;
|
||||
thr_lock_info_init(&lock_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1563,6 +1569,12 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
|
||||
}
|
||||
|
||||
|
||||
void Statement::close_cursor()
|
||||
{
|
||||
DBUG_ASSERT("Statement::close_cursor()" == "not implemented");
|
||||
}
|
||||
|
||||
|
||||
void THD::end_statement()
|
||||
{
|
||||
/* Cleanup SQL processing state to resuse this statement in next query. */
|
||||
@ -1683,6 +1695,14 @@ int Statement_map::insert(Statement *statement)
|
||||
}
|
||||
|
||||
|
||||
void Statement_map::close_transient_cursors()
|
||||
{
|
||||
Statement *stmt;
|
||||
while ((stmt= transient_cursor_list.head()))
|
||||
stmt->close_cursor(); /* deletes itself from the list */
|
||||
}
|
||||
|
||||
|
||||
bool select_dumpvar::send_data(List<Item> &items)
|
||||
{
|
||||
List_iterator_fast<Item_func_set_user_var> li(vars);
|
||||
|
@ -756,7 +756,7 @@ class Cursor;
|
||||
be used explicitly.
|
||||
*/
|
||||
|
||||
class Statement: public Query_arena
|
||||
class Statement: public ilink, public Query_arena
|
||||
{
|
||||
Statement(const Statement &rhs); /* not implemented: */
|
||||
Statement &operator=(const Statement &rhs); /* non-copyable */
|
||||
@ -833,6 +833,8 @@ public:
|
||||
void restore_backup_statement(Statement *stmt, Statement *backup);
|
||||
/* return class type */
|
||||
virtual Type type() const;
|
||||
/* Close the cursor open for this statement, if there is one */
|
||||
virtual void close_cursor();
|
||||
};
|
||||
|
||||
|
||||
@ -884,15 +886,25 @@ public:
|
||||
}
|
||||
hash_delete(&st_hash, (byte *) statement);
|
||||
}
|
||||
void add_transient_cursor(Statement *stmt)
|
||||
{ transient_cursor_list.append(stmt); }
|
||||
void erase_transient_cursor(Statement *stmt) { stmt->unlink(); }
|
||||
/*
|
||||
Close all cursors of this connection that use tables of a storage
|
||||
engine that has transaction-specific state and therefore can not
|
||||
survive COMMIT or ROLLBACK. Currently all but MyISAM cursors are closed.
|
||||
*/
|
||||
void close_transient_cursors();
|
||||
/* Erase all statements (calls Statement destructor) */
|
||||
void reset()
|
||||
{
|
||||
my_hash_reset(&names_hash);
|
||||
my_hash_reset(&st_hash);
|
||||
transient_cursor_list.empty();
|
||||
last_found_statement= 0;
|
||||
}
|
||||
|
||||
~Statement_map()
|
||||
void destroy()
|
||||
{
|
||||
hash_free(&names_hash);
|
||||
hash_free(&st_hash);
|
||||
@ -900,6 +912,7 @@ public:
|
||||
private:
|
||||
HASH st_hash;
|
||||
HASH names_hash;
|
||||
I_List<Statement> transient_cursor_list;
|
||||
Statement *last_found_statement;
|
||||
};
|
||||
|
||||
@ -1017,8 +1030,7 @@ public:
|
||||
a thread/connection descriptor
|
||||
*/
|
||||
|
||||
class THD :public ilink,
|
||||
public Statement,
|
||||
class THD :public Statement,
|
||||
public Open_tables_state
|
||||
{
|
||||
public:
|
||||
@ -1044,6 +1056,10 @@ public:
|
||||
struct rand_struct rand; // used for authentication
|
||||
struct system_variables variables; // Changeable local variables
|
||||
struct system_status_var status_var; // Per thread statistic vars
|
||||
THR_LOCK_INFO lock_info; // Locking info of this thread
|
||||
THR_LOCK_OWNER main_lock_id; // To use for conventional queries
|
||||
THR_LOCK_OWNER *lock_id; // If not main_lock_id, points to
|
||||
// the lock_id of a cursor.
|
||||
pthread_mutex_t LOCK_delete; // Locked before thd is deleted
|
||||
/* all prepared statements and cursors of this connection */
|
||||
Statement_map stmt_map;
|
||||
|
@ -2020,6 +2020,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
DBUG_VOID_RETURN;
|
||||
/* If lex->result is set, mysql_execute_command will use it */
|
||||
stmt->lex->result= &cursor->result;
|
||||
thd->lock_id= &cursor->lock_id;
|
||||
}
|
||||
}
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
@ -2069,6 +2070,9 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
Cursor::open is buried deep in JOIN::exec of the top level join.
|
||||
*/
|
||||
cursor->init_from_thd(thd);
|
||||
|
||||
if (cursor->close_at_commit)
|
||||
thd->stmt_map.add_transient_cursor(stmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2078,6 +2082,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
|
||||
}
|
||||
|
||||
thd->set_statement(&stmt_backup);
|
||||
thd->lock_id= &thd->main_lock_id;
|
||||
thd->current_arena= thd;
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
@ -2252,6 +2257,8 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
|
||||
the previous calls.
|
||||
*/
|
||||
free_root(cursor->mem_root, MYF(0));
|
||||
if (cursor->close_at_commit)
|
||||
thd->stmt_map.erase_transient_cursor(stmt);
|
||||
}
|
||||
|
||||
thd->restore_backup_statement(stmt, &stmt_backup);
|
||||
@ -2291,14 +2298,6 @@ void mysql_stmt_reset(THD *thd, char *packet)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
stmt->close_cursor(); /* will reset statement params */
|
||||
cursor= stmt->cursor;
|
||||
if (cursor && cursor->is_open())
|
||||
{
|
||||
thd->change_list= cursor->change_list;
|
||||
cursor->close(FALSE);
|
||||
cleanup_stmt_and_thd_after_use(stmt, thd);
|
||||
free_root(cursor->mem_root, MYF(0));
|
||||
}
|
||||
|
||||
stmt->state= Query_arena::PREPARED;
|
||||
|
||||
@ -2478,6 +2477,8 @@ void Prepared_statement::close_cursor()
|
||||
cursor->close(FALSE);
|
||||
cleanup_stmt_and_thd_after_use(this, thd);
|
||||
free_root(cursor->mem_root, MYF(0));
|
||||
if (cursor->close_at_commit)
|
||||
thd->stmt_map.erase_transient_cursor(this);
|
||||
}
|
||||
/*
|
||||
Clear parameters from data which could be set by
|
||||
|
@ -1702,10 +1702,12 @@ JOIN::destroy()
|
||||
|
||||
Cursor::Cursor(THD *thd)
|
||||
:Query_arena(&main_mem_root, INITIALIZED),
|
||||
join(0), unit(0)
|
||||
join(0), unit(0),
|
||||
close_at_commit(FALSE)
|
||||
{
|
||||
/* We will overwrite it at open anyway. */
|
||||
init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
|
||||
thr_lock_owner_init(&lock_id, &thd->lock_info);
|
||||
}
|
||||
|
||||
|
||||
@ -1739,6 +1741,21 @@ Cursor::init_from_thd(THD *thd)
|
||||
free_list= thd->free_list;
|
||||
change_list= thd->change_list;
|
||||
reset_thd(thd);
|
||||
/* Now we have an active cursor and can cause a deadlock */
|
||||
thd->lock_info.n_cursors++;
|
||||
|
||||
close_at_commit= FALSE; /* reset in case we're reusing the cursor */
|
||||
for (TABLE *table= open_tables; table; table= table->next)
|
||||
{
|
||||
const handlerton *ht= table->file->ht;
|
||||
if (ht)
|
||||
close_at_commit|= (ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
|
||||
else
|
||||
{
|
||||
close_at_commit= TRUE; /* handler status is unknown */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
XXX: thd->locked_tables is not changed.
|
||||
What problems can we have with it if cursor is open?
|
||||
@ -1907,6 +1924,7 @@ Cursor::close(bool is_active)
|
||||
thd->derived_tables= tmp_derived_tables;
|
||||
thd->lock= tmp_lock;
|
||||
}
|
||||
thd->lock_info.n_cursors--; /* Decrease the number of active cursors */
|
||||
join= 0;
|
||||
unit= 0;
|
||||
free_items();
|
||||
|
@ -392,6 +392,8 @@ class Cursor: public Sql_alloc, public Query_arena
|
||||
public:
|
||||
Item_change_list change_list;
|
||||
select_send result;
|
||||
THR_LOCK_OWNER lock_id;
|
||||
my_bool close_at_commit;
|
||||
|
||||
/* Temporary implementation as now we replace THD state by value */
|
||||
/* Save THD state into cursor */
|
||||
|
@ -55,6 +55,7 @@ static char current_db[]= "client_test_db";
|
||||
static unsigned int test_count= 0;
|
||||
static unsigned int opt_count= 0;
|
||||
static unsigned int iter_count= 0;
|
||||
static my_bool have_innodb= FALSE;
|
||||
|
||||
static const char *opt_basedir= "./";
|
||||
|
||||
@ -220,6 +221,28 @@ static void print_st_error(MYSQL_STMT *stmt, const char *msg)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the connection has InnoDB tables */
|
||||
|
||||
static my_bool check_have_innodb(MYSQL *conn)
|
||||
{
|
||||
MYSQL_RES *res;
|
||||
MYSQL_ROW row;
|
||||
int rc;
|
||||
my_bool result;
|
||||
|
||||
rc= mysql_query(conn, "show variables like 'have_innodb'");
|
||||
myquery(rc);
|
||||
res= mysql_use_result(conn);
|
||||
DIE_UNLESS(res);
|
||||
|
||||
row= mysql_fetch_row(res);
|
||||
DIE_UNLESS(row);
|
||||
|
||||
result= strcmp(row[1], "YES") == 0;
|
||||
mysql_free_result(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This is to be what mysql_query() is for mysql_real_query(), for
|
||||
@ -290,6 +313,7 @@ static void client_connect(ulong flag)
|
||||
strxmov(query, "USE ", current_db, NullS);
|
||||
rc= mysql_query(mysql, query);
|
||||
myquery(rc);
|
||||
have_innodb= check_have_innodb(mysql);
|
||||
|
||||
if (!opt_silent)
|
||||
fprintf(stdout, " OK");
|
||||
@ -13749,6 +13773,110 @@ static void test_bug11037()
|
||||
myquery(rc);
|
||||
}
|
||||
|
||||
/* Bug#10760: cursors, crash in a fetch after rollback. */
|
||||
|
||||
static void test_bug10760()
|
||||
{
|
||||
MYSQL_STMT *stmt;
|
||||
MYSQL_BIND bind[1];
|
||||
int rc;
|
||||
const char *stmt_text;
|
||||
char id_buf[20];
|
||||
ulong id_len;
|
||||
int i= 0;
|
||||
ulong type;
|
||||
|
||||
myheader("test_bug10760");
|
||||
|
||||
mysql_query(mysql, "drop table if exists t1, t2");
|
||||
|
||||
/* create tables */
|
||||
rc= mysql_query(mysql, "create table t1 (id integer not null primary key)"
|
||||
" engine=MyISAM");
|
||||
myquery(rc);
|
||||
for (; i < 42; ++i)
|
||||
{
|
||||
char buf[100];
|
||||
sprintf(buf, "insert into t1 (id) values (%d)", i+1);
|
||||
rc= mysql_query(mysql, buf);
|
||||
myquery(rc);
|
||||
}
|
||||
mysql_autocommit(mysql, FALSE);
|
||||
/* create statement */
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
type= (ulong) CURSOR_TYPE_READ_ONLY;
|
||||
mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void*) &type);
|
||||
|
||||
/*
|
||||
1: check that a deadlock within the same connection
|
||||
is resolved and an error is returned. The deadlock is modelled
|
||||
as follows:
|
||||
con1: open cursor for select * from t1;
|
||||
con1: insert into t1 (id) values (1)
|
||||
*/
|
||||
stmt_text= "select id from t1 order by 1";
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
check_execute(stmt, rc);
|
||||
rc= mysql_query(mysql, "update t1 set id=id+100");
|
||||
DIE_UNLESS(rc);
|
||||
if (!opt_silent)
|
||||
printf("Got error (as expected): %s\n", mysql_error(mysql));
|
||||
/*
|
||||
2: check that MyISAM tables used in cursors survive
|
||||
COMMIT/ROLLBACK.
|
||||
*/
|
||||
rc= mysql_rollback(mysql); /* should not close the cursor */
|
||||
myquery(rc);
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
check_execute(stmt, rc);
|
||||
|
||||
/*
|
||||
3: check that cursors to InnoDB tables are closed (for now) by
|
||||
COMMIT/ROLLBACK.
|
||||
*/
|
||||
if (! have_innodb)
|
||||
{
|
||||
if (!opt_silent)
|
||||
printf("Testing that cursors are closed at COMMIT/ROLLBACK requires "
|
||||
"InnoDB.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt_text= "select id from t1 order by 1";
|
||||
rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text));
|
||||
check_execute(stmt, rc);
|
||||
|
||||
rc= mysql_query(mysql, "alter table t1 engine=InnoDB");
|
||||
myquery(rc);
|
||||
|
||||
bzero(bind, sizeof(bind));
|
||||
bind[0].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind[0].buffer= (void*) id_buf;
|
||||
bind[0].buffer_length= sizeof(id_buf);
|
||||
bind[0].length= &id_len;
|
||||
check_execute(stmt, rc);
|
||||
mysql_stmt_bind_result(stmt, bind);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
DIE_UNLESS(rc == 0);
|
||||
if (!opt_silent)
|
||||
printf("Fetched row %s\n", id_buf);
|
||||
rc= mysql_rollback(mysql); /* should close the cursor */
|
||||
myquery(rc);
|
||||
rc= mysql_stmt_fetch(stmt);
|
||||
DIE_UNLESS(rc);
|
||||
if (!opt_silent)
|
||||
printf("Got error (as expected): %s\n", mysql_error(mysql));
|
||||
}
|
||||
|
||||
mysql_stmt_close(stmt);
|
||||
rc= mysql_query(mysql, "drop table t1");
|
||||
myquery(rc);
|
||||
mysql_autocommit(mysql, TRUE); /* restore default */
|
||||
}
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
@ -13994,6 +14122,7 @@ static struct my_tests_st my_tests[]= {
|
||||
{ "test_bug9735", test_bug9735 },
|
||||
{ "test_bug11183", test_bug11183 },
|
||||
{ "test_bug11037", test_bug11037 },
|
||||
{ "test_bug10760", test_bug10760 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user