~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/pbxt/src/ha_pbxt.cc

lp:drizzle + pbxt 1.1 + test results

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2005 PrimeBase Technologies GmbH
 
2
 *
 
3
 * Derived from ha_example.h
 
4
 * Copyright (C) 2003 MySQL AB
 
5
 *
 
6
 * PrimeBase XT
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     02111-1307      USA
 
21
 *
 
22
 * 2005-11-10   Paul McCullagh
 
23
 *
 
24
 */
 
25
 
 
26
#ifdef USE_PRAGMA_IMPLEMENTATION
 
27
#pragma implementation                          // gcc: Class implementation
 
28
#endif
 
29
 
 
30
#include "xt_config.h"
 
31
 
 
32
#if defined(XT_WIN)
 
33
#include <windows.h>
 
34
#endif
 
35
 
 
36
#include <stdlib.h>
 
37
#include <time.h>
 
38
#include <ctype.h>
 
39
 
 
40
#ifdef DRIZZLED
 
41
#include <fcntl.h>
 
42
#include <drizzled/common.h>
 
43
#include <drizzled/plugin.h>
 
44
#include <drizzled/field.h>
 
45
#include <drizzled/session.h>
 
46
#include <drizzled/data_home.h>
 
47
#include <drizzled/error.h>
 
48
#include <drizzled/table.h>
 
49
#include <drizzled/field/timestamp.h>
 
50
#include <drizzled/session.h>
 
51
 
 
52
#define my_strdup(a,b) strdup(a)
 
53
 
 
54
using namespace drizzled;
 
55
using namespace drizzled::plugin;
 
56
 
 
57
#define DEFAULT_FILE_EXTENSION ".dfe"
 
58
 
 
59
#else
 
60
#include "mysql_priv.h"
 
61
#include <mysql/plugin.h>
 
62
#endif
 
63
 
 
64
#include "ha_pbxt.h"
 
65
#include "ha_xtsys.h"
 
66
 
 
67
#include "strutil_xt.h"
 
68
#include "database_xt.h"
 
69
#include "cache_xt.h"
 
70
#include "trace_xt.h"
 
71
#include "heap_xt.h"
 
72
#include "myxt_xt.h"
 
73
#include "datadic_xt.h"
 
74
#ifdef PBMS_ENABLED
 
75
#include "pbms_enabled.h"
 
76
#endif
 
77
#include "tabcache_xt.h"
 
78
#include "systab_xt.h"
 
79
#include "xaction_xt.h"
 
80
#include "backup_xt.h"
 
81
 
 
82
#ifdef DEBUG
 
83
//#define XT_USE_SYS_PAR_DEBUG_SIZES
 
84
#define PBXT_HANDLER_TRACE
 
85
//#define PBXT_TRACE_RETURN
 
86
//#define XT_PRINT_INDEX_OPT
 
87
//#define XT_SHOW_DUMPS_TRACE
 
88
//#define XT_UNIT_TEST
 
89
//#define LOAD_TABLE_ON_OPEN
 
90
//#define CHECK_TABLE_LOADS
 
91
 
 
92
/* Enable to trace the statements executed by the engine: */
 
93
//#define TRACE_STATEMENTS
 
94
 
 
95
/* Enable to print the trace to the stdout, instead of
 
96
 * to the trace log.
 
97
 */
 
98
//#define PRINT_STATEMENTS
 
99
#endif
 
100
 
 
101
#ifndef DRIZZLED
 
102
static handler  *pbxt_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root);
 
103
static int              pbxt_init(void *p);
 
104
static int              pbxt_end(void *p);
 
105
static int              pbxt_panic(handlerton *hton, enum ha_panic_function flag);
 
106
static void             pbxt_drop_database(handlerton *hton, char *path);
 
107
static int              pbxt_close_connection(handlerton *hton, THD* thd);
 
108
static int              pbxt_commit(handlerton *hton, THD *thd, bool all);
 
109
static int              pbxt_rollback(handlerton *hton, THD *thd, bool all);
 
110
static int              pbxt_prepare(handlerton *hton, THD *thd, bool all);
 
111
static int              pbxt_recover(handlerton *hton, XID *xid_list, uint len);
 
112
static int              pbxt_commit_by_xid(handlerton *hton, XID *xid);
 
113
static int              pbxt_rollback_by_xid(handlerton *hton, XID *xid);
 
114
static int              pbxt_start_consistent_snapshot(handlerton *hton, THD *thd);
 
115
#endif
 
116
static void             ha_aquire_exclusive_use(XTThreadPtr self, XTSharePtr share, ha_pbxt *mine);
 
117
static void             ha_release_exclusive_use(XTThreadPtr self, XTSharePtr share);
 
118
static void             ha_close_open_tables(XTThreadPtr self, XTSharePtr share, ha_pbxt *mine);
 
119
 
 
120
#ifdef TRACE_STATEMENTS
 
121
 
 
122
#ifdef PRINT_STATEMENTS
 
123
#define STAT_TRACE(y, x)                printf("%s: %s\n", y ? y->t_name : "-unknown-", x)
 
124
#else
 
125
#define STAT_TRACE(y, x)                xt_ttraceq(y, x)
 
126
#endif
 
127
 
 
128
#else
 
129
 
 
130
#define STAT_TRACE(y, x)
 
131
 
 
132
#endif
 
133
 
 
134
#ifdef PBXT_HANDLER_TRACE
 
135
#define PBXT_ALLOW_PRINTING
 
136
 
 
137
#define XT_TRACE_CALL()                         ha_trace_function(__FUNC__, NULL)
 
138
#define XT_TRACE_METHOD()                       ha_trace_function(__FUNC__, pb_share->sh_table_path->ps_path)
 
139
 
 
140
#ifdef PBXT_TRACE_RETURN
 
141
#define XT_RETURN(x)                            do { printf("%d\n", (int) (x)); return (x); } while (0)
 
142
#define XT_RETURN_VOID                          do { printf("out\n"); return; } while (0)
 
143
#else
 
144
#define XT_RETURN(x)                            return (x)
 
145
#define XT_RETURN_VOID                          return
 
146
#endif
 
147
 
 
148
#else
 
149
 
 
150
#define XT_TRACE_CALL()
 
151
#define XT_TRACE_METHOD()
 
152
#define XT_RETURN(x)                            return (x)
 
153
#define XT_RETURN_VOID                          return
 
154
 
 
155
#endif
 
156
 
 
157
#ifdef PBXT_ALLOW_PRINTING
 
158
#define XT_PRINT0(y, x)                         do { XTThreadPtr s = (y); printf("%s " x, s ? s->t_name : "-unknown-"); } while (0)
 
159
#define XT_PRINT1(y, x, a)                      do { XTThreadPtr s = (y); printf("%s " x, s ? s->t_name : "-unknown-", a); } while (0)
 
160
#define XT_PRINT2(y, x, a, b)           do { XTThreadPtr s = (y); printf("%s " x, s ? s->t_name : "-unknown-", a, b); } while (0)
 
161
#define XT_PRINT3(y, x, a, b, c)        do { XTThreadPtr s = (y); printf("%s " x, s ? s->t_name : "-unknown-", a, b, c); } while (0)
 
162
#else
 
163
#define XT_PRINT0(y, x)
 
164
#define XT_PRINT1(y, x, a)
 
165
#define XT_PRINT2(y, x, a, b)
 
166
#define XT_PRINT3(y, x, a, b, c)
 
167
#endif
 
168
 
 
169
 
 
170
#define TS(x)                                   (x)->s
 
171
 
 
172
handlerton                              *pbxt_hton;
 
173
bool                                    pbxt_inited = false;            // Variable for checking the init state of hash
 
174
xtBool                                  pbxt_ignore_case = true;
 
175
const char                              *pbxt_extensions[]= { ".xtr", ".xtd", ".xtl", ".xti", ".xt", "", NULL };
 
176
#ifdef XT_CRASH_DEBUG
 
177
xtBool                                  pbxt_crash_debug = TRUE;
 
178
#else
 
179
xtBool                                  pbxt_crash_debug = FALSE;
 
180
#endif
 
181
 
 
182
 
 
183
/* Variables for pbxt share methods */
 
184
static xt_mutex_type    pbxt_database_mutex;            // Prevent a database from being opened while it is being dropped
 
185
static XTHashTabPtr             pbxt_share_tables;                      // Hash used to track open tables
 
186
static char                             *pbxt_index_cache_size;
 
187
static char                             *pbxt_record_cache_size;
 
188
static char                             *pbxt_log_cache_size;
 
189
static char                             *pbxt_log_file_threshold;
 
190
static char                             *pbxt_transaction_buffer_size;
 
191
static char                             *pbxt_log_buffer_size;
 
192
static char                             *pbxt_checkpoint_frequency;
 
193
static char                             *pbxt_data_log_threshold;
 
194
static char                             *pbxt_data_file_grow_size;
 
195
static char                             *pbxt_row_file_grow_size;
 
196
static char                             *pbxt_record_write_threshold;
 
197
static my_bool                  pbxt_support_xa;
 
198
 
 
199
#ifndef DRIZZLED
 
200
// drizzle complains it's not used
 
201
static XTXactEnumXARec  pbxt_xa_enum;
 
202
#endif
 
203
 
 
204
#ifdef DEBUG
 
205
#define XT_SHARE_LOCK_WAIT              5000
 
206
#else
 
207
#define XT_SHARE_LOCK_WAIT              500
 
208
#endif
 
209
 
 
210
/* 
 
211
 * Lock timeout in 1/1000ths of a second
 
212
 */
 
213
#define XT_SHARE_LOCK_TIMEOUT   30000
 
214
 
 
215
/*
 
216
 * -----------------------------------------------------------------------
 
217
 * SYSTEM VARIABLES
 
218
 *
 
219
 */
 
220
 
 
221
//#define XT_FOR_TEAMDRIVE
 
222
 
 
223
typedef struct HAVarParams {
 
224
        const char              *vp_var;                                                /* Variable name. */
 
225
        const char              *vp_def;                                                /* Default value. */
 
226
        const char              *vp_min;                                                /* Minimum allowed value. */
 
227
        const char              *vp_max4;                                               /* Maximum allowed value on 32-bit processors. */
 
228
        const char              *vp_max8;                                               /* Maximum allowed value on 64-bit processors. */
 
229
} HAVarParamsRec, *HAVarParamsPtr;
 
230
 
 
231
#ifdef XT_USE_SYS_PAR_DEBUG_SIZES
 
232
static HAVarParamsRec vp_index_cache_size = { "pbxt_index_cache_size", "32MB", "8MB", "2GB", "2000GB" };
 
233
static HAVarParamsRec vp_record_cache_size = { "pbxt_record_cache_size", "32MB", "8MB", "2GB", "2000GB" };
 
234
static HAVarParamsRec vp_log_cache_size = { "pbxt_log_cache_size", "16MB", "4MB", "2GB", "2000GB" };
 
235
static HAVarParamsRec vp_checkpoint_frequency = { "pbxt_checkpoint_frequency", "1GB", "2MB", "2000GB", "2000GB" };
 
236
static HAVarParamsRec vp_log_file_threshold = { "pbxt_log_file_threshold", "32MB", "1MB", "2GB", "256TB" };
 
237
static HAVarParamsRec vp_transaction_buffer_size = { "pbxt_transaction_buffer_size", "1MB", "128K", "1GB", "24GB" };
 
238
static HAVarParamsRec vp_log_buffer_size = { "pbxt_log_buffer_size", "256K", "128K", "1GB", "24GB" };
 
239
static HAVarParamsRec vp_data_log_threshold = { "pbxt_data_log_threshold", "400K", "400K", "2GB", "256TB" };
 
240
static HAVarParamsRec vp_data_file_grow_size = { "pbxt_data_file_grow_size", "2MB", "128K", "1GB", "2GB" };
 
241
static HAVarParamsRec vp_row_file_grow_size = { "pbxt_row_file_grow_size", "256K", "32K", "1GB", "2GB" };
 
242
static HAVarParamsRec vp_record_write_threshold = { "pbxt_record_write_threshold", "4MB", "0", "2GB", "8GB" };
 
243
#define XT_DL_DEFAULT_XLOG_COUNT                3
 
244
#define XT_DL_DEFAULT_GARBAGE_LEVEL             10
 
245
#else
 
246
static HAVarParamsRec vp_index_cache_size = { "pbxt_index_cache_size", "32MB", "8MB", "2GB", "2000GB" };
 
247
static HAVarParamsRec vp_record_cache_size = { "pbxt_record_cache_size", "32MB", "8MB", "2GB", "2000GB" };
 
248
static HAVarParamsRec vp_log_cache_size = { "pbxt_log_cache_size", "16MB", "4MB", "2GB", "2000GB" };
 
249
static HAVarParamsRec vp_checkpoint_frequency = { "pbxt_checkpoint_frequency", "1GB", "2MB", "2000GB", "2000GB" };
 
250
static HAVarParamsRec vp_log_file_threshold = { "pbxt_log_file_threshold", "32MB", "1MB", "2GB", "256TB" };
 
251
static HAVarParamsRec vp_transaction_buffer_size = { "pbxt_transaction_buffer_size", "1MB", "128K", "1GB", "24GB" };
 
252
static HAVarParamsRec vp_log_buffer_size = { "pbxt_log_buffer_size", "256K", "128K", "1GB", "24GB" };
 
253
static HAVarParamsRec vp_data_log_threshold = { "pbxt_data_log_threshold", "64MB", "1MB", "2GB", "256TB" };
 
254
static HAVarParamsRec vp_data_file_grow_size = { "pbxt_data_file_grow_size", "2MB", "128K", "1GB", "2GB" };
 
255
static HAVarParamsRec vp_row_file_grow_size = { "pbxt_row_file_grow_size", "256K", "32K", "1GB", "2GB" };
 
256
static HAVarParamsRec vp_record_write_threshold = { "pbxt_record_write_threshold", "4MB", "0", "2GB", "8GB" };
 
257
#define XT_DL_DEFAULT_XLOG_COUNT                3
 
258
#define XT_DL_DEFAULT_GARBAGE_LEVEL             50
 
259
#endif
 
260
 
 
261
#define XT_AUTO_INCREMENT_DEF                   0
 
262
#define XT_DL_DEFAULT_INDEX_DIRTY_LEVEL 80
 
263
 
 
264
#ifdef XT_MAC
 
265
#ifdef DEBUG
 
266
/* For debugging on the Mac, we check the re-use logs: */
 
267
#define XT_OFFLINE_LOG_FUNCTION_DEF             XT_RECYCLE_LOGS
 
268
#else
 
269
#define XT_OFFLINE_LOG_FUNCTION_DEF             XT_DELETE_LOGS
 
270
#endif
 
271
#else
 
272
#define XT_OFFLINE_LOG_FUNCTION_DEF             XT_RECYCLE_LOGS
 
273
#endif
 
274
 
 
275
/* TeamDrive, uses special auto-increment, and
 
276
 * we keep the logs for the moment:
 
277
 */
 
278
#ifdef XT_FOR_TEAMDRIVE
 
279
#undef XT_OFFLINE_LOG_FUNCTION_DEF
 
280
#define XT_OFFLINE_LOG_FUNCTION_DEF             XT_KEEP_LOGS
 
281
//#undef XT_AUTO_INCREMENT_DEF
 
282
//#define XT_AUTO_INCREMENT_DEF                 1
 
283
#endif
 
284
 
 
285
#ifdef PBXT_HANDLER_TRACE
 
286
static void ha_trace_function(const char *function, char *table)
 
287
{
 
288
        char            func_buf[50], *ptr;
 
289
        XTThreadPtr     thread = xt_get_self(); 
 
290
 
 
291
        if ((ptr = const_cast<char *>(strchr(function, '(')))) {
 
292
                ptr--;
 
293
                while (ptr > function) {
 
294
                        if (!(isalnum(*ptr) || *ptr == '_'))
 
295
                                break;
 
296
                        ptr--;
 
297
                }
 
298
                ptr++;
 
299
                xt_strcpy(50, func_buf, ptr);
 
300
                if ((ptr = strchr(func_buf, '(')))
 
301
                        *ptr = 0;
 
302
        }
 
303
        else
 
304
                xt_strcpy(50, func_buf, function);
 
305
        if (table)
 
306
                printf("%s %s (%s)\n", thread ? thread->t_name : "-unknown-", func_buf, table);
 
307
        else
 
308
                printf("%s %s\n", thread ? thread->t_name : "-unknown-", func_buf);
 
309
}
 
310
#endif
 
311
 
 
312
/*
 
313
 * -----------------------------------------------------------------------
 
314
 * SHARED TABLE DATA
 
315
 *
 
316
 */
 
317
 
 
318
static xtBool ha_hash_comp(void *key, void *data)
 
319
{
 
320
        XTSharePtr      share = (XTSharePtr) data;
 
321
 
 
322
        return strcmp((char *) key, share->sh_table_path->ps_path) == 0;
 
323
}
 
324
 
 
325
static xtHashValue ha_hash(xtBool is_key, void *key_data)
 
326
{
 
327
        XTSharePtr      share = (XTSharePtr) key_data;
 
328
 
 
329
        if (is_key)
 
330
                return xt_ht_hash((char *) key_data);
 
331
        return xt_ht_hash(share->sh_table_path->ps_path);
 
332
}
 
333
 
 
334
static xtBool ha_hash_comp_ci(void *key, void *data)
 
335
{
 
336
        XTSharePtr      share = (XTSharePtr) data;
 
337
 
 
338
        return strcasecmp((char *) key, share->sh_table_path->ps_path) == 0;
 
339
}
 
340
 
 
341
static xtHashValue ha_hash_ci(xtBool is_key, void *key_data)
 
342
{
 
343
        XTSharePtr      share = (XTSharePtr) key_data;
 
344
 
 
345
        if (is_key)
 
346
                return xt_ht_casehash((char *) key_data);
 
347
        return xt_ht_casehash(share->sh_table_path->ps_path);
 
348
}
 
349
 
 
350
static void ha_open_share(XTThreadPtr self, XTShareRec *share)
 
351
{
 
352
        xt_lock_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
353
        pushr_(xt_unlock_mutex, share->sh_ex_mutex);
 
354
 
 
355
        if (!share->sh_table) {
 
356
                share->sh_table = xt_use_table(self, share->sh_table_path, FALSE, FALSE);
 
357
                share->sh_dic_key_count = share->sh_table->tab_dic.dic_key_count;
 
358
                share->sh_dic_keys = share->sh_table->tab_dic.dic_keys;
 
359
                share->sh_recalc_selectivity = FALSE;
 
360
        }
 
361
 
 
362
        freer_(); // xt_ht_unlock(pbxt_share_tables)
 
363
}
 
364
 
 
365
static void ha_close_share(XTThreadPtr self, XTShareRec *share)
 
366
{
 
367
        XTTableHPtr tab;
 
368
 
 
369
        if ((tab = share->sh_table)) {
 
370
                /* Save this, in case the share is re-opened. */
 
371
                share->sh_min_auto_inc = tab->tab_auto_inc;
 
372
 
 
373
                xt_heap_release(self, tab);
 
374
                share->sh_table = NULL;
 
375
        }
 
376
 
 
377
        /* This are only references: */
 
378
        share->sh_dic_key_count = 0;
 
379
        share->sh_dic_keys = NULL;
 
380
}
 
381
 
 
382
static void ha_cleanup_share(XTThreadPtr self, XTSharePtr share)
 
383
{
 
384
        ha_close_share(self, share);
 
385
 
 
386
        if (share->sh_table_path) {
 
387
                xt_free(self, share->sh_table_path);
 
388
                share->sh_table_path = NULL;
 
389
        }
 
390
 
 
391
        if (share->sh_ex_cond) {
 
392
                thr_lock_delete(&share->sh_lock);
 
393
                xt_delete_cond(self, (xt_cond_type *) share->sh_ex_cond);
 
394
                share->sh_ex_cond = NULL;
 
395
        }
 
396
 
 
397
        if (share->sh_ex_mutex) {
 
398
                xt_delete_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
399
                share->sh_ex_mutex = NULL;
 
400
        }
 
401
 
 
402
        xt_free(self, share);
 
403
}
 
404
 
 
405
static void ha_hash_free(XTThreadPtr self, void *data)
 
406
{
 
407
        XTSharePtr      share = (XTSharePtr) data;
 
408
 
 
409
        ha_cleanup_share(self, share);
 
410
}
 
411
 
 
412
/*
 
413
 * This structure contains information that is common to all handles.
 
414
 * (i.e. it is table specific).
 
415
 */
 
416
static XTSharePtr ha_get_share(XTThreadPtr self, const char *table_path, bool open_table)
 
417
{
 
418
        XTShareRec      *share;
 
419
 
 
420
        enter_();
 
421
        xt_ht_lock(self, pbxt_share_tables);
 
422
        pushr_(xt_ht_unlock, pbxt_share_tables);
 
423
 
 
424
        // Check if the table exists...
 
425
        if (!(share = (XTSharePtr) xt_ht_get(self, pbxt_share_tables, (void *) table_path))) {
 
426
                share = (XTSharePtr) xt_calloc(self, sizeof(XTShareRec));               
 
427
                pushr_(ha_cleanup_share, share);
 
428
 
 
429
                share->sh_ex_mutex = (xt_mutex_type *) xt_new_mutex(self);
 
430
                share->sh_ex_cond = (xt_cond_type *) xt_new_cond(self);
 
431
 
 
432
                thr_lock_init(&share->sh_lock);
 
433
 
 
434
                share->sh_use_count = 0;
 
435
                share->sh_table_path = (XTPathStrPtr) xt_dup_string(self, table_path);
 
436
 
 
437
                if (open_table)
 
438
                        ha_open_share(self, share);
 
439
 
 
440
                popr_(); // Discard ha_cleanup_share(share);
 
441
 
 
442
                xt_ht_put(self, pbxt_share_tables, share);
 
443
        }
 
444
 
 
445
        share->sh_use_count++;
 
446
        freer_(); // xt_ht_unlock(pbxt_share_tables)
 
447
 
 
448
        return_(share);
 
449
}
 
450
 
 
451
/*
 
452
 * Free shared information.
 
453
 */
 
454
static void ha_unget_share(XTThreadPtr self, XTSharePtr share)
 
455
{
 
456
        xt_ht_lock(self, pbxt_share_tables);
 
457
        pushr_(xt_ht_unlock, pbxt_share_tables);
 
458
 
 
459
        if (!--share->sh_use_count)
 
460
                xt_ht_del(self, pbxt_share_tables, share->sh_table_path);
 
461
 
 
462
        freer_(); // xt_ht_unlock(pbxt_share_tables)
 
463
}
 
464
 
 
465
static xtBool ha_unget_share_removed(XTThreadPtr self, XTSharePtr share)
 
466
{
 
467
        xtBool removed = FALSE;
 
468
 
 
469
        xt_ht_lock(self, pbxt_share_tables);
 
470
        pushr_(xt_ht_unlock, pbxt_share_tables);
 
471
 
 
472
        if (!--share->sh_use_count) {
 
473
                removed = TRUE;
 
474
                xt_ht_del(self, pbxt_share_tables, share->sh_table_path);
 
475
        }
 
476
 
 
477
        freer_(); // xt_ht_unlock(pbxt_share_tables)
 
478
        return removed;
 
479
}
 
480
 
 
481
static inline void thd_init_xact(THD *thd, XTThreadPtr self, bool set_table_trans)
 
482
{
 
483
        self->st_xact_mode = thd_tx_isolation(thd) <= ISO_READ_COMMITTED ? XT_XACT_COMMITTED_READ : XT_XACT_REPEATABLE_READ;
 
484
        self->st_ignore_fkeys = (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) != 0;
 
485
        self->st_auto_commit = (thd_test_options(thd,(OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) == 0;
 
486
        if (set_table_trans) {
 
487
#ifdef DRIZZLED
 
488
                self->st_table_trans = FALSE;
 
489
#else
 
490
                self->st_table_trans = thd_sql_command(thd) == SQLCOM_LOCK_TABLES;
 
491
#endif
 
492
        }
 
493
        self->st_abort_trans = FALSE;
 
494
        self->st_stat_ended = FALSE;
 
495
        self->st_stat_trans = FALSE;
 
496
        XT_PRINT0(self, "xt_xn_begin\n");
 
497
        xt_xres_wait_for_recovery(self, XT_RECOVER_SWEPT);
 
498
}
 
499
 
 
500
/*
 
501
 * -----------------------------------------------------------------------
 
502
 * PUBLIC FUNCTIONS
 
503
 *
 
504
 */
 
505
 
 
506
xtPublic void xt_ha_unlock_table(XTThreadPtr self, void *share)
 
507
{
 
508
        ha_release_exclusive_use(self, (XTSharePtr) share);
 
509
        ha_unget_share(self, (XTSharePtr) share);
 
510
}
 
511
 
 
512
xtPublic void xt_ha_close_global_database(XTThreadPtr self)
 
513
{
 
514
        if (pbxt_database) {
 
515
                xt_heap_release(self, pbxt_database);
 
516
                pbxt_database = NULL;
 
517
        }
 
518
}
 
519
 
 
520
/*
 
521
 * Open a PBXT database given the path of a table.
 
522
 * This function also returns the name of the table.
 
523
 *
 
524
 * We use the pbxt_database_mutex to lock this
 
525
 * operation to make sure it does not occur while
 
526
 * some other thread is doing a "closeall".
 
527
 */
 
528
xtPublic void xt_ha_open_database_of_table(XTThreadPtr self, XTPathStrPtr XT_UNUSED(table_path))
 
529
{
 
530
#ifdef XT_USE_GLOBAL_DB
 
531
        if (!self->st_database) {
 
532
                if (!pbxt_database) {
 
533
                        xt_open_database(self, mysql_real_data_home, TRUE);
 
534
                        /* {GLOBAL-DB}
 
535
                         * This can be done at the same time as the recovery thread,
 
536
                         * strictly speaking I need a lock.
 
537
                         */
 
538
                        if (!pbxt_database) {
 
539
                                pbxt_database = self->st_database;
 
540
                                xt_heap_reference(self, pbxt_database);
 
541
                        }
 
542
                }
 
543
                else
 
544
                        xt_use_database(self, pbxt_database, XT_FOR_USER);
 
545
        }
 
546
#else
 
547
        char db_path[PATH_MAX];
 
548
 
 
549
        xt_strcpy(PATH_MAX, db_path, (char *) table_path);
 
550
        xt_remove_last_name_of_path(db_path);
 
551
        xt_remove_dir_char(db_path);
 
552
 
 
553
        if (self->st_database && xt_tab_compare_paths(self->st_database->db_name, xt_last_name_of_path(db_path)) == 0)
 
554
                /* This thread already has this database open! */
 
555
                return;
 
556
 
 
557
        /* Auto commit before changing the database: */
 
558
        if (self->st_xact_data) {
 
559
                /* PMC - This probably indicates something strange is happening:
 
560
                 *
 
561
                 * This sequence generates this error:
 
562
                 *
 
563
                 * delimiter |
 
564
                 * 
 
565
                 * create temporary table t3 (id int)|
 
566
                 * 
 
567
                 * create function f10() returns int
 
568
                 * begin
 
569
                 *   drop temporary table if exists t3;
 
570
                 *   create temporary table t3 (id int) engine=myisam;
 
571
                 *   insert into t3 select id from t4;
 
572
                 *   return (select count(*) from t3);
 
573
                 * end|
 
574
                 * 
 
575
                 * select f10()|
 
576
                 *
 
577
                 * An error is generated because the same thread is used
 
578
                 * to open table t4 (at the start of the functions), and
 
579
                 * then to drop table t3. To drop t3 we need to
 
580
                 * switch the database, so we land up here!
 
581
                 */
 
582
                xt_throw_xterr(XT_CONTEXT, XT_ERR_CANNOT_CHANGE_DB);
 
583
                /*
 
584
                 if (!xt_xn_commit(self))
 
585
                        throw_();
 
586
                 */
 
587
        }
 
588
 
 
589
        xt_lock_mutex(self, &pbxt_database_mutex);
 
590
        pushr_(xt_unlock_mutex, &pbxt_database_mutex);
 
591
        xt_open_database(self, db_path, FALSE);
 
592
        freer_(); // xt_unlock_mutex(&pbxt_database_mutex);
 
593
#endif
 
594
}
 
595
 
 
596
xtPublic XTThreadPtr xt_ha_set_current_thread(THD *thd, XTExceptionPtr e)
 
597
{
 
598
        XTThreadPtr     self;
 
599
        static int      ha_thread_count = 0, ha_id;
 
600
 
 
601
#ifdef DRIZZLED
 
602
        if (!(self = (XTThreadPtr) *thd->getEngineData(pbxt_hton))) {
 
603
#else
 
604
        if (!(self = (XTThreadPtr) *thd_ha_data(thd, pbxt_hton))) {
 
605
#endif
 
606
//              const                   Security_context *sctx;
 
607
                char                    name[120];
 
608
                char                    ha_id_str[50];
 
609
 
 
610
                ha_id = ++ha_thread_count;
 
611
                sprintf(ha_id_str, "_%d", ha_id);
 
612
                xt_strcpy(120,name,"user"); // TODO: Fix this hack
 
613
/*
 
614
                sctx = &thd->main_security_ctx;
 
615
 
 
616
                if (sctx->user) {
 
617
                        xt_strcpy(120, name, sctx->user);
 
618
                        xt_strcat(120, name, "@");
 
619
                }
 
620
                else
 
621
                        *name = 0;
 
622
                if (sctx->host)
 
623
                        xt_strcat(120, name, sctx->host);
 
624
                else if (sctx->ip)
 
625
                        xt_strcat(120, name, sctx->ip);
 
626
                else if (thd->proc_info)
 
627
                        xt_strcat(120, name, (char *) thd->proc_info);
 
628
                else
 
629
                        xt_strcat(120, name, "system");
 
630
*/
 
631
                xt_strcat(120, name, ha_id_str);
 
632
                if (!(self = xt_create_thread(name, FALSE, TRUE, e)))
 
633
                        return NULL;
 
634
 
 
635
                self->st_xact_mode = XT_XACT_REPEATABLE_READ;
 
636
#ifdef DRIZZLED
 
637
                *thd->getEngineData(pbxt_hton) = (void *) self;
 
638
#else
 
639
                *thd_ha_data(thd, pbxt_hton) = (void *) self;
 
640
#endif
 
641
        }
 
642
        return self;
 
643
}
 
644
 
 
645
xtPublic void xt_ha_close_connection(THD* thd)
 
646
{
 
647
        XTThreadPtr             self;
 
648
 
 
649
#ifdef DRIZZLED
 
650
        if (!(self = (XTThreadPtr) *thd->getEngineData(pbxt_hton))) {
 
651
        *thd->getEngineData(pbxt_hton) = NULL;
 
652
#else
 
653
        if ((self = (XTThreadPtr) *thd_ha_data(thd, pbxt_hton))) {
 
654
                *thd_ha_data(thd, pbxt_hton) = NULL;
 
655
#endif
 
656
                xt_free_thread(self);
 
657
        }
 
658
}
 
659
 
 
660
xtPublic XTThreadPtr xt_ha_thd_to_self(THD *thd)
 
661
{
 
662
#ifdef DRIZZLED
 
663
        return (XTThreadPtr) *thd->getEngineData(pbxt_hton);
 
664
#else
 
665
        return (XTThreadPtr) *thd_ha_data(thd, pbxt_hton);
 
666
#endif
 
667
}
 
668
 
 
669
/* The first bit is 1. */
 
670
static u_int ha_get_max_bit(MX_BITMAP *map)
 
671
{
 
672
#ifdef DRIZZLED
 
673
        uint32_t        cnt = map->numOfBitsInMap();
 
674
        uint32_t        max_bit = 0;
 
675
 
 
676
        for (uint32_t i = 0; i < cnt; i++)
 
677
                if (map->isBitSet(i))
 
678
                        max_bit = i;
 
679
 
 
680
        return max_bit;
 
681
#else
 
682
        my_bitmap_map   *data_ptr = map->bitmap;
 
683
        my_bitmap_map   *end_ptr = map->last_word_ptr;
 
684
        u_int           cnt = map->n_bits;
 
685
        my_bitmap_map   b;
 
686
        
 
687
        for (; end_ptr >= data_ptr; end_ptr--) {
 
688
                if ((b = *end_ptr)) {
 
689
                        my_bitmap_map mask;
 
690
                        
 
691
                        if (end_ptr == map->getLastWordPtr() && map->getLastWordMask())
 
692
                                mask = map->getLastWordMask() >> 1;
 
693
                        else
 
694
                                mask = 0x80000000;
 
695
                        while (!(b & mask)) {
 
696
                                b = b << 1;
 
697
                                /* Should not happen, but if it does, we hang! */
 
698
                                if (!b)
 
699
                                        return map->numOfBitsInMap();
 
700
                                cnt--;
 
701
                        }
 
702
                        return cnt;
 
703
                }
 
704
                if (end_ptr == map->getLastWordPtr())
 
705
                        cnt = ((cnt-1) / 32) * 32;
 
706
                else
 
707
                        cnt -= 32;
 
708
        }
 
709
        return 0;
 
710
#endif
 
711
}
 
712
 
 
713
/*
 
714
 * -----------------------------------------------------------------------
 
715
 * SUPPORT FUNCTIONS
 
716
 *
 
717
 */
 
718
 
 
719
/*
 
720
 * In PBXT, as in MySQL: thread == connection.
 
721
 *
 
722
 * So we simply attach a PBXT thread to a MySQL thread.
 
723
 */
 
724
static XTThreadPtr ha_set_current_thread(THD *thd, int *err)
 
725
{
 
726
        XTThreadPtr             self;
 
727
        XTExceptionRec  e;
 
728
 
 
729
        if (!(self = xt_ha_set_current_thread(thd, &e))) {
 
730
                xt_log_exception(NULL, &e, XT_LOG_DEFAULT);
 
731
                *err = e.e_xt_err;
 
732
                return NULL;
 
733
        }
 
734
        return self;
 
735
}
 
736
 
 
737
xtPublic int xt_ha_pbxt_to_mysql_error(int xt_err)
 
738
{
 
739
        switch (xt_err) {
 
740
                case XT_NO_ERR:
 
741
                        return(0);
 
742
                case XT_ERR_DUPLICATE_KEY:
 
743
                                return HA_ERR_FOUND_DUPP_KEY;
 
744
                case XT_ERR_DEADLOCK:
 
745
                                return HA_ERR_LOCK_DEADLOCK;
 
746
                case XT_ERR_RECORD_CHANGED:
 
747
                        /* If we generate HA_ERR_RECORD_CHANGED instead of HA_ERR_LOCK_WAIT_TIMEOUT
 
748
                         * then sysbench does not work because it does not handle this error.
 
749
                         */
 
750
                        //return HA_ERR_LOCK_WAIT_TIMEOUT; // but HA_ERR_RECORD_CHANGED is the correct error for a optimistic lock failure.
 
751
                        return HA_ERR_RECORD_CHANGED;
 
752
                case XT_ERR_LOCK_TIMEOUT:
 
753
                        return HA_ERR_LOCK_WAIT_TIMEOUT;
 
754
                case XT_ERR_TABLE_IN_USE:
 
755
                                return HA_ERR_WRONG_COMMAND;
 
756
                case XT_ERR_TABLE_NOT_FOUND:
 
757
                        return HA_ERR_NO_SUCH_TABLE;
 
758
                case XT_ERR_TABLE_EXISTS:
 
759
                        return HA_ERR_TABLE_EXIST;
 
760
                case XT_ERR_CANNOT_CHANGE_DB:
 
761
                        return ER_TRG_IN_WRONG_SCHEMA;
 
762
                case XT_ERR_COLUMN_NOT_FOUND:
 
763
                        return HA_ERR_CANNOT_ADD_FOREIGN;
 
764
                case XT_ERR_NO_REFERENCED_ROW:
 
765
                case XT_ERR_REF_TABLE_NOT_FOUND:
 
766
                case XT_ERR_REF_TYPE_WRONG:
 
767
                        return HA_ERR_NO_REFERENCED_ROW;
 
768
                case XT_ERR_ROW_IS_REFERENCED:
 
769
                        return HA_ERR_ROW_IS_REFERENCED;
 
770
                case XT_ERR_COLUMN_IS_NOT_NULL:
 
771
                case XT_ERR_INCORRECT_NO_OF_COLS:
 
772
                case XT_ERR_FK_ON_TEMP_TABLE:
 
773
                case XT_ERR_FK_REF_TEMP_TABLE:
 
774
                        return HA_ERR_CANNOT_ADD_FOREIGN;
 
775
                case XT_ERR_DUPLICATE_FKEY:
 
776
                        return HA_ERR_FOREIGN_DUPLICATE_KEY;
 
777
                case XT_ERR_RECORD_DELETED:
 
778
                        return HA_ERR_RECORD_DELETED;
 
779
        }
 
780
        return(-1);                     // Unknown error
 
781
}
 
782
 
 
783
xtPublic int xt_ha_pbxt_thread_error_for_mysql(THD *thd, const XTThreadPtr self, int ignore_dup_key)
 
784
{
 
785
        int             xt_err = self->t_exception.e_xt_err;
 
786
        xtBool  dup_key = FALSE;
 
787
 
 
788
        XT_PRINT2(self, "xt_ha_pbxt_thread_error_for_mysql xt_err=%d auto commit=%d\n", (int) xt_err, (int) self->st_auto_commit);
 
789
        switch (xt_err) {
 
790
                case XT_NO_ERR:
 
791
                        break;
 
792
                case XT_ERR_DUPLICATE_KEY:
 
793
                case XT_ERR_DUPLICATE_FKEY:
 
794
                        /* Let MySQL call rollback as and when it wants to for duplicate
 
795
                         * key.
 
796
                         *
 
797
                         * In addition, we are not allowed to do an auto-rollback
 
798
                         * inside a sub-statement (function() or procedure())
 
799
                         * For example:
 
800
                         * 
 
801
                         * delimiter |
 
802
                         *
 
803
                         * create table t3 (c1 char(1) primary key not null)|
 
804
                         * 
 
805
                         * create function bug12379()
 
806
                         *   returns integer
 
807
                         * begin
 
808
                         *    insert into t3 values('X');
 
809
                         *    insert into t3 values('X');
 
810
                         *    return 0;
 
811
                         * end|
 
812
                         * 
 
813
                         * --error 1062
 
814
                         * select bug12379()|
 
815
                         *
 
816
                         *
 
817
                         * Not doing an auto-rollback should solve this problem in the
 
818
                         * case of duplicate key (but not in others - like deadlock)!
 
819
                         * I don't think this situation is handled correctly by MySQL.
 
820
                         */
 
821
 
 
822
                        /* If we are in auto-commit mode (and we are not ignoring
 
823
                         * duplicate keys) then rollback the transaction automatically.
 
824
                         */
 
825
                        dup_key = TRUE;
 
826
                        if (!ignore_dup_key && self->st_auto_commit)
 
827
                                goto abort_transaction;
 
828
                        break;
 
829
                case XT_ERR_DEADLOCK:
 
830
                case XT_ERR_NO_REFERENCED_ROW:
 
831
                case XT_ERR_ROW_IS_REFERENCED:
 
832
                        goto abort_transaction;
 
833
                case XT_ERR_RECORD_CHANGED:
 
834
                        /* MySQL also handles the locked error. NOTE: There is no automatic
 
835
                         * rollback!
 
836
                         */
 
837
                        break;
 
838
                default:
 
839
                        xt_log_exception(self, &self->t_exception, XT_LOG_DEFAULT);
 
840
                        abort_transaction:
 
841
                        /* PMC 2006-08-30: It should be that this is not necessary!
 
842
                         *
 
843
                         * It is only necessary to call ha_rollback() if the engine
 
844
                         * aborts the transaction.
 
845
                         *
 
846
                         * On the other hand, I shouldn't need to rollback the
 
847
                         * transaction because, if I return an error, MySQL
 
848
                         * should do it for me.
 
849
                         *
 
850
                         * Unfortunately, when auto-commit is off, MySQL does not
 
851
                         * rollback automatically (for example when a deadlock
 
852
                         * is provoked).
 
853
                         *
 
854
                         * And when we have a multi update we cannot rely on this
 
855
                         * either (see comment above).
 
856
                         */
 
857
                        if (self->st_xact_data) {
 
858
                                /*
 
859
                                 * GOTCHA:
 
860
                                 * A result of the "st_abort_trans = TRUE" below is that
 
861
                                 * the following code results in an empty set.
 
862
                                 * The reason is "ignore_dup_key" is not set so
 
863
                                 * the duplicate key leads to an error which causes
 
864
                                 * the transaction to be aborted.
 
865
                                 * The delayed inserts are all execute in one transaction.
 
866
                                 * 
 
867
                                 * CREATE TABLE t1 (
 
868
                                 * c1 INT(11) NOT NULL AUTO_INCREMENT,
 
869
                                 * c2 INT(11) DEFAULT NULL,
 
870
                                 * PRIMARY KEY (c1)
 
871
                                 * );
 
872
                                 * SET insert_id= 14;
 
873
                                 * INSERT DELAYED INTO t1 VALUES(NULL, 11), (NULL, 12);
 
874
                                 * INSERT DELAYED INTO t1 VALUES(14, 91);
 
875
                                 * INSERT DELAYED INTO t1 VALUES (NULL, 92), (NULL, 93);
 
876
                                 * FLUSH TABLE t1;
 
877
                                 * SELECT * FROM t1;
 
878
                                 */
 
879
                                if (self->st_lock_count == 0) {
 
880
                                        /* No table locks, must rollback immediately
 
881
                                         * (there will be no possibility later!
 
882
                                         */
 
883
                                        XT_PRINT1(self, "xt_xn_rollback xt_err=%d\n", xt_err);
 
884
                                        if (!xt_xn_rollback(self))
 
885
                                                xt_log_exception(self, &self->t_exception, XT_LOG_DEFAULT);
 
886
                                }
 
887
                                else {
 
888
                                        /* Locks are held on tables.
 
889
                                         * Only rollback after locks are released.
 
890
                                         */
 
891
                                        /* I do not think this is required, because
 
892
                                         * I tell mysql to rollback below, 
 
893
                                         * besides it is a hack!
 
894
                                         self->st_auto_commit = TRUE;
 
895
                                         */
 
896
                                        self->st_abort_trans = TRUE;
 
897
                                }
 
898
                                /* Only tell MySQL to rollback if we automatically rollback.
 
899
                                 * Note: calling this with (thd, FALSE), cause sp.test to fail.
 
900
                                 */
 
901
                                if (!dup_key) {
 
902
                                        if (thd)
 
903
                                                thd_mark_transaction_to_rollback(thd, TRUE);
 
904
                                }
 
905
                        }
 
906
                        break;
 
907
        }
 
908
        return xt_ha_pbxt_to_mysql_error(xt_err);
 
909
}
 
910
 
 
911
static void ha_conditional_close_database(XTThreadPtr self, XTThreadPtr other_thr, void *db)
 
912
{
 
913
        if (other_thr->st_database == (XTDatabaseHPtr) db)
 
914
                xt_unuse_database(self, other_thr);
 
915
}
 
916
 
 
917
/*
 
918
 * This is only called from drop database, so we know that
 
919
 * no thread is actually using the database. This means that it
 
920
 * must be safe to close the database.
 
921
 */
 
922
xtPublic void xt_ha_all_threads_close_database(XTThreadPtr self, XTDatabaseHPtr db)
 
923
{
 
924
        xt_lock_mutex(self, &pbxt_database_mutex);
 
925
        pushr_(xt_unlock_mutex, &pbxt_database_mutex);
 
926
        xt_do_to_all_threads(self, ha_conditional_close_database, db);
 
927
        freer_(); // xt_unlock_mutex(&pbxt_database_mutex);
 
928
}
 
929
 
 
930
static int ha_log_pbxt_thread_error_for_mysql(int ignore_dup_key)
 
931
{
 
932
        return xt_ha_pbxt_thread_error_for_mysql(current_thd, myxt_get_self(), ignore_dup_key);
 
933
}
 
934
 
 
935
/*
 
936
 * -----------------------------------------------------------------------
 
937
 * STATIC HOOKS
 
938
 *
 
939
 */
 
940
static xtWord8 ha_set_variable(char **value, HAVarParamsPtr vp)
 
941
{
 
942
        xtWord8 result;
 
943
        xtWord8 mi, ma;
 
944
        char    *mm;
 
945
 
 
946
        if (!*value)
 
947
                *value = getenv(vp->vp_var);
 
948
        if (!*value)
 
949
                *value = (char *) vp->vp_def;
 
950
        result = xt_byte_size_to_int8(*value);
 
951
        mi = (xtWord8) xt_byte_size_to_int8(vp->vp_min);
 
952
        if (result < mi) {
 
953
                result = mi;
 
954
                *value = (char *) vp->vp_min;
 
955
        }
 
956
        if (sizeof(size_t) == 8)
 
957
                mm = (char *) vp->vp_max8;
 
958
        else
 
959
                mm = (char *) vp->vp_max4;
 
960
        ma = (xtWord8) xt_byte_size_to_int8(mm);
 
961
        if (result > ma) {
 
962
                result = ma;
 
963
                *value = mm;
 
964
        }
 
965
        return result;
 
966
}
 
967
 
 
968
static void pbxt_call_init(XTThreadPtr self)
 
969
{
 
970
        xtInt8  index_cache_size;
 
971
        xtInt8  record_cache_size;
 
972
        xtInt8  log_cache_size;
 
973
        xtInt8  log_file_threshold;
 
974
        xtInt8  transaction_buffer_size;
 
975
        xtInt8  log_buffer_size;
 
976
        xtInt8  checkpoint_frequency;
 
977
        xtInt8  data_log_threshold;
 
978
        xtInt8  data_file_grow_size;
 
979
        xtInt8  row_file_grow_size;
 
980
        xtInt8  record_write_threshold;
 
981
 
 
982
        xt_logf(XT_NT_INFO, "PrimeBase XT (PBXT) Engine %s loaded...\n", xt_get_version());
 
983
        xt_logf(XT_NT_INFO, "Paul McCullagh, PrimeBase Technologies GmbH, http://www.primebase.org\n");
 
984
 
 
985
        index_cache_size = ha_set_variable(&pbxt_index_cache_size, &vp_index_cache_size);
 
986
        record_cache_size = ha_set_variable(&pbxt_record_cache_size, &vp_record_cache_size);
 
987
        log_cache_size = ha_set_variable(&pbxt_log_cache_size, &vp_log_cache_size);
 
988
        log_file_threshold = ha_set_variable(&pbxt_log_file_threshold, &vp_log_file_threshold);
 
989
        transaction_buffer_size = ha_set_variable(&pbxt_transaction_buffer_size, &vp_transaction_buffer_size);
 
990
        log_buffer_size = ha_set_variable(&pbxt_log_buffer_size, &vp_log_buffer_size);
 
991
        checkpoint_frequency = ha_set_variable(&pbxt_checkpoint_frequency, &vp_checkpoint_frequency);
 
992
        data_log_threshold = ha_set_variable(&pbxt_data_log_threshold, &vp_data_log_threshold);
 
993
        data_file_grow_size = ha_set_variable(&pbxt_data_file_grow_size, &vp_data_file_grow_size);
 
994
        row_file_grow_size = ha_set_variable(&pbxt_row_file_grow_size, &vp_row_file_grow_size);
 
995
        record_write_threshold = ha_set_variable(&pbxt_record_write_threshold, &vp_record_write_threshold);
 
996
 
 
997
        xt_db_log_file_threshold = (xtLogOffset) log_file_threshold;
 
998
        xt_db_log_buffer_size = (size_t) xt_align_offset(log_buffer_size, 512);
 
999
        xt_db_transaction_buffer_size = (size_t) xt_align_offset(transaction_buffer_size, 512);
 
1000
        xt_db_checkpoint_frequency = (size_t) checkpoint_frequency;
 
1001
        xt_db_data_log_threshold = (off_t) data_log_threshold;
 
1002
        xt_db_data_file_grow_size = (size_t) data_file_grow_size;
 
1003
        xt_db_row_file_grow_size = (size_t) row_file_grow_size;
 
1004
        xt_db_record_write_threshold = (size_t) record_write_threshold;
 
1005
 
 
1006
#ifdef DRIZZLED
 
1007
        pbxt_ignore_case = TRUE;
 
1008
#else
 
1009
        pbxt_ignore_case = lower_case_table_names != 0;
 
1010
#endif
 
1011
        if (pbxt_ignore_case)
 
1012
                pbxt_share_tables = xt_new_hashtable(self, ha_hash_comp_ci, ha_hash_ci, ha_hash_free, TRUE, FALSE);
 
1013
        else
 
1014
                pbxt_share_tables = xt_new_hashtable(self, ha_hash_comp, ha_hash, ha_hash_free, TRUE, FALSE);
 
1015
 
 
1016
        xt_fs_init(self);
 
1017
        xt_lock_installation(self, mysql_real_data_home);
 
1018
        XTSystemTableShare::startUp(self);
 
1019
        xt_init_databases(self);
 
1020
        xt_ind_init(self, (size_t) index_cache_size);
 
1021
        xt_tc_init(self, (size_t) record_cache_size);
 
1022
        xt_xlog_init(self, (size_t) log_cache_size);
 
1023
}
 
1024
 
 
1025
static void pbxt_call_exit(XTThreadPtr self)
 
1026
{
 
1027
        xt_logf(XT_NT_INFO, "PrimeBase XT Engine shutdown...\n");
 
1028
 
 
1029
#ifdef TRACE_STATEMENTS
 
1030
        xt_dump_trace();
 
1031
#endif
 
1032
#ifdef XT_USE_GLOBAL_DB
 
1033
        xt_ha_close_global_database(self);
 
1034
#endif
 
1035
#ifdef DEBUG
 
1036
        //xt_stop_database_threads(self, FALSE);
 
1037
        xt_stop_database_threads(self, TRUE);
 
1038
#else
 
1039
        xt_stop_database_threads(self, TRUE);
 
1040
#endif
 
1041
        /* This will tell the freeer to quit ASAP: */
 
1042
        xt_quit_freeer(self);
 
1043
        /* We conditional stop the freeer here, because if we are
 
1044
         * in startup, then the free will be hanging.
 
1045
         * {FREEER-HANG}
 
1046
         *
 
1047
         * This problem has been solved by MySQL!
 
1048
         */
 
1049
        xt_stop_freeer(self);
 
1050
        xt_exit_databases(self);
 
1051
        XTSystemTableShare::shutDown(self);
 
1052
        xt_xlog_exit(self);
 
1053
        xt_tc_exit(self);
 
1054
        xt_ind_exit(self);
 
1055
        xt_unlock_installation(self, mysql_real_data_home);
 
1056
        xt_fs_exit(self);
 
1057
        if (pbxt_share_tables) {
 
1058
                xt_free_hashtable(self, pbxt_share_tables);
 
1059
                pbxt_share_tables = NULL;
 
1060
        }
 
1061
}
 
1062
 
 
1063
/*
 
1064
 * Shutdown the PBXT sub-system.
 
1065
 */
 
1066
static void ha_exit(XTThreadPtr self)
 
1067
{
 
1068
        xt_xres_terminate_recovery(self);
 
1069
 
 
1070
        /* Wrap things up... */
 
1071
        xt_unuse_database(self, self);  /* Just in case the main thread has a database in use (for testing)? */
 
1072
        /* This may cause the streaming engine to cleanup connections and 
 
1073
         * tables belonging to this engine. This in turn may require some of
 
1074
         * the stuff below (like xt_create_thread() called from pbxt_close_table()! */
 
1075
#ifdef PBMS_ENABLED
 
1076
        pbms_finalize();
 
1077
#endif
 
1078
        pbxt_call_exit(self);
 
1079
        xt_exit_threading(self);
 
1080
        xt_exit_memory();
 
1081
        xt_exit_logging();
 
1082
        xt_p_mutex_destroy(&pbxt_database_mutex);               
 
1083
        pbxt_inited = false;
 
1084
}
 
1085
 
 
1086
/*
 
1087
 * Outout the PBXT status. Return FALSE on error.
 
1088
 */
 
1089
#ifdef DRIZZLED
 
1090
bool PBXTStorageEngine::show_status(Session *thd, stat_print_fn *stat_print, enum ha_stat_type)
 
1091
#else
 
1092
static bool pbxt_show_status(handlerton *XT_UNUSED(hton), THD* thd, 
 
1093
                          stat_print_fn* stat_print,
 
1094
                          enum ha_stat_type XT_UNUSED(stat_type))
 
1095
#endif
 
1096
{
 
1097
        XTThreadPtr                     self;   
 
1098
        int                                     err = 0;
 
1099
        XTStringBufferRec       strbuf = { 0, 0, 0 };
 
1100
        bool                            not_ok = FALSE;
 
1101
 
 
1102
        if (!(self = ha_set_current_thread(thd, &err)))
 
1103
                return FALSE;
 
1104
 
 
1105
#ifdef XT_SHOW_DUMPS_TRACE
 
1106
        //if (pbxt_database)
 
1107
        //      xt_dump_xlogs(pbxt_database, 0);
 
1108
        xt_trace("// %s - dump\n", xt_trace_clock_diff(NULL));
 
1109
        xt_dump_trace();
 
1110
#endif
 
1111
#ifdef XT_TRACK_CONNECTIONS
 
1112
        xt_dump_conn_tracking();
 
1113
#endif
 
1114
 
 
1115
#ifdef XT_UNIT_TEST
 
1116
        xt_unit_test_async_task(self);
 
1117
#endif
 
1118
 
 
1119
        try_(a) {
 
1120
                myxt_get_status(self, &strbuf);
 
1121
        }
 
1122
        catch_(a) {
 
1123
                not_ok = TRUE;
 
1124
        }
 
1125
        cont_(a);
 
1126
 
 
1127
        if (!not_ok) {
 
1128
                if (stat_print(thd, "PBXT", 4, "", 0, strbuf.sb_cstring, (uint) strbuf.sb_len))
 
1129
                        not_ok = TRUE;
 
1130
        }
 
1131
        xt_sb_set_size(self, &strbuf, 0);
 
1132
 
 
1133
        return not_ok;
 
1134
}
 
1135
 
 
1136
/*
 
1137
 * Initialize the PBXT sub-system.
 
1138
 *
 
1139
 * return 1 on error, else 0.
 
1140
 */
 
1141
#ifdef DRIZZLED
 
1142
static int pbxt_init(Context &registry)
 
1143
#else
 
1144
static int pbxt_init(void *p)
 
1145
#endif
 
1146
{
 
1147
        int init_err = 0;
 
1148
 
 
1149
        XT_PRINT0(NULL, "pbxt_init\n");
 
1150
 
 
1151
        if (sizeof(xtWordPS) != sizeof(void *)) {
 
1152
                printf("PBXT: This won't work, I require that sizeof(xtWordPS) == sizeof(void *)!\n");
 
1153
                XT_RETURN(1);
 
1154
        }
 
1155
 
 
1156
        /* GOTCHA: This will "detect" if are loading the plug-in
 
1157
         * with different --with-debug option to MySQL.
 
1158
         *
 
1159
         * In this case, you will get an error when loading the
 
1160
         * library that some symbol was not found.
 
1161
         */
 
1162
        void *dummy = my_malloc(100, MYF(0));
 
1163
        my_free((byte *) dummy, MYF(0));
 
1164
 
 
1165
        if (!pbxt_inited) {
 
1166
                XTThreadPtr self = NULL;
 
1167
 
 
1168
                xt_p_mutex_init_with_autoname(&pbxt_database_mutex, NULL);
 
1169
 
 
1170
#ifdef DRIZZLED
 
1171
                pbxt_hton= new PBXTStorageEngine(std::string("PBXT"));
 
1172
                registry.add(pbxt_hton);
 
1173
#else
 
1174
                pbxt_hton = (handlerton *) p;
 
1175
                pbxt_hton->state = SHOW_OPTION_YES;
 
1176
                pbxt_hton->db_type = DB_TYPE_PBXT; // Wow! I have my own!
 
1177
                pbxt_hton->close_connection = pbxt_close_connection; /* close_connection, cleanup thread related data. */
 
1178
                pbxt_hton->commit = pbxt_commit; /* commit */
 
1179
                pbxt_hton->rollback = pbxt_rollback; /* rollback */
 
1180
                if (pbxt_support_xa) {
 
1181
                        pbxt_hton->prepare = pbxt_prepare;
 
1182
                        pbxt_hton->recover = pbxt_recover;
 
1183
                        pbxt_hton->commit_by_xid = pbxt_commit_by_xid;
 
1184
                        pbxt_hton->rollback_by_xid = pbxt_rollback_by_xid;
 
1185
                }
 
1186
                else {
 
1187
                        pbxt_hton->prepare = NULL;
 
1188
                        pbxt_hton->recover = NULL;
 
1189
                        pbxt_hton->commit_by_xid = NULL;
 
1190
                        pbxt_hton->rollback_by_xid = NULL;
 
1191
                }
 
1192
                pbxt_hton->create = pbxt_create_handler; /* Create a new handler */
 
1193
                pbxt_hton->drop_database = pbxt_drop_database; /* Drop a database */
 
1194
                pbxt_hton->panic = pbxt_panic; /* Panic call */
 
1195
                pbxt_hton->show_status = pbxt_show_status;
 
1196
                pbxt_hton->flags = HTON_NO_FLAGS; /* HTON_CAN_RECREATE - Without this flags TRUNCATE uses delete_all_rows() */
 
1197
                pbxt_hton->slot = (uint)-1; /* assign invald value, so we know when it's inited later */
 
1198
                pbxt_hton->start_consistent_snapshot = pbxt_start_consistent_snapshot;
 
1199
#if defined(MYSQL_SUPPORTS_BACKUP) && defined(XT_ENABLE_ONLINE_BACKUP)
 
1200
                pbxt_hton->get_backup_engine = pbxt_backup_engine;
 
1201
#endif
 
1202
#endif
 
1203
                if (!xt_init_logging())                                 /* Initialize logging */
 
1204
                        goto error_1;
 
1205
 
 
1206
#ifdef PBMS_ENABLED
 
1207
                PBMSResultRec result;
 
1208
                if (!pbms_initialize("PBXT", false, &result)) {
 
1209
                        xt_logf(XT_NT_ERROR, "pbms_initialize() Error: %s", result.mr_message);
 
1210
                        goto error_2;
 
1211
                }
 
1212
#endif
 
1213
 
 
1214
                if (!xt_init_memory())                                  /* Initialize memory */
 
1215
                        goto error_3;
 
1216
 
 
1217
                self = xt_init_threading();                             /* Create the main self: */
 
1218
                if (!self)
 
1219
                        goto error_3;
 
1220
 
 
1221
                pbxt_inited = true;
 
1222
 
 
1223
                try_(a) {
 
1224
                        /* Initialize all systems */
 
1225
                        pbxt_call_init(self);
 
1226
 
 
1227
                        /* Conditional unit test: */
 
1228
#ifdef XT_UNIT_TEST
 
1229
                        //xt_unit_test_create_threads(self);
 
1230
                        //xt_unit_test_read_write_locks(self);
 
1231
                        //xt_unit_test_mutex_locks(self);
 
1232
#endif
 
1233
 
 
1234
                        /* {OPEN-DB-SWEEPER-WAIT}
 
1235
                         * I have to start the freeer before I open and recover the database
 
1236
                         * because it we run out of cache while waiting for the sweeper
 
1237
                         * we will hang!
 
1238
                         */
 
1239
                        xt_start_freeer(self);
 
1240
 
 
1241
                        /* This function is called with LOCK_plugin locked.
 
1242
                         * This prevents the opening of .frm files, which
 
1243
                         * is required for recovery.
 
1244
                         * Our solution is to start reovery in a thread
 
1245
                         * so that it can run after LOCK_plugin is released.
 
1246
                         */
 
1247
                        xt_xres_start_database_recovery(self);
 
1248
                }
 
1249
                catch_(a) {
 
1250
                        xt_log_exception(self, &self->t_exception, XT_LOG_DEFAULT);
 
1251
                        init_err = 1;
 
1252
                }
 
1253
                cont_(a);
 
1254
 
 
1255
                if (init_err) {
 
1256
                        /* {FREEER-HANG} The free-er will be hung in:
 
1257
                                #0      0x91fc6a2e in semaphore_wait_signal_trap
 
1258
                                #1      0x91fce505 in pthread_mutex_lock
 
1259
                                #2      0x00489633 in safe_mutex_lock at thr_mutex.c:149
 
1260
                                #3      0x002dfca9 in plugin_thdvar_init at sql_plugin.cc:2398
 
1261
                                #4      0x000d6a12 in THD::init at sql_class.cc:715
 
1262
                                #5      0x000de9d3 in THD::THD at sql_class.cc:597
 
1263
                                #6      0x000debe1 in THD::THD at sql_class.cc:631
 
1264
                                #7      0x00e207a4 in myxt_create_thread at myxt_xt.cc:2666
 
1265
                                #8      0x00e3134b in tabc_fr_run_thread at tabcache_xt.cc:982
 
1266
                                #9      0x00e422ca in xt_thread_main at thread_xt.cc:1006
 
1267
                                #10     0x91ff7c55 in _pthread_start
 
1268
                                #11     0x91ff7b12 in thread_start
 
1269
                         *
 
1270
                         * so it is not good trying to stop it here!
 
1271
                         *
 
1272
                         * With regard to this problem, see {OPEN-DB-SWEEPER-WAIT}
 
1273
                         * Due to this problem, I will probably have to hack
 
1274
                         * the mutex so that the freeer can get started...
 
1275
                         *
 
1276
                         * NOPE! problem has gone in 6.0.9. Also not a problem in
 
1277
                         * 5.1.29.
 
1278
                         */
 
1279
                        
 
1280
                        /* {OPEN-DB-SWEEPER-WAIT} 
 
1281
                         * I have to stop the freeer here because it was
 
1282
                         * started before opening the database.
 
1283
                         */
 
1284
 
 
1285
                        /* {FREEER-HANG-ON-INIT-ERROR}
 
1286
                         * pbxt_init is called with LOCK_plugin and if it fails and tries to exit
 
1287
                         * the freeer here it hangs because the freeer calls THD::~THD which tries
 
1288
                         * to aquire the same lock and hangs. OTOH MySQL calls pbxt_end() after
 
1289
                         * an unsuccessful call to pbxt_init, so we defer cleaup, except 
 
1290
                         * releasing 'self'
 
1291
                         */
 
1292
                        xt_free_thread(self);
 
1293
                        goto error_3;
 
1294
                }
 
1295
                xt_free_thread(self);
 
1296
        }
 
1297
        XT_RETURN(init_err);
 
1298
 
 
1299
        error_3:
 
1300
#ifdef PBMS_ENABLED
 
1301
        pbms_finalize();
 
1302
 
 
1303
        error_2:
 
1304
#endif
 
1305
 
 
1306
        error_1:
 
1307
        XT_RETURN(1);
 
1308
}
 
1309
 
 
1310
#ifdef DRIZZLED
 
1311
int pbxt_end(Registry &registry)
 
1312
#else
 
1313
static int pbxt_end(void *)
 
1314
#endif
 
1315
{
 
1316
        XTThreadPtr             self;
 
1317
        int                             err = 0;
 
1318
 
 
1319
        XT_TRACE_CALL();
 
1320
 
 
1321
        if (pbxt_inited) {
 
1322
                XTExceptionRec  e;
 
1323
 
 
1324
                /* This flag also means "shutting down". */
 
1325
                pbxt_inited = false; 
 
1326
                self = xt_create_thread("TempForEnd", FALSE, TRUE, &e);
 
1327
                if (self) {
 
1328
                        self->t_main = TRUE;
 
1329
                        ha_exit(self);
 
1330
                }
 
1331
        }
 
1332
 
 
1333
#ifdef DRIZZLED
 
1334
        registry.remove(pbxt_hton);
 
1335
#endif
 
1336
        XT_RETURN(err);
 
1337
}
 
1338
 
 
1339
#ifndef DRIZZLED
 
1340
static int pbxt_panic(handlerton *hton, enum ha_panic_function flag)
 
1341
{
 
1342
        return pbxt_end(hton);
 
1343
}
 
1344
#endif
 
1345
 
 
1346
/*
 
1347
 * Kill the PBXT thread associated with the MySQL thread.
 
1348
 */
 
1349
#ifdef DRIZZLED
 
1350
int PBXTStorageEngine::close_connection(Session *thd)
 
1351
{
 
1352
        PBXTStorageEngine * const hton = this;
 
1353
#else
 
1354
static int pbxt_close_connection(handlerton *hton, THD* thd)
 
1355
{
 
1356
#endif
 
1357
        XTThreadPtr             self;
 
1358
 
 
1359
        XT_TRACE_CALL();
 
1360
#ifdef DRIZZLED
 
1361
        if ((self = (XTThreadPtr) *thd->getEngineData(hton))) {
 
1362
                *thd->getEngineData(pbxt_hton) = NULL;
 
1363
#else
 
1364
        if ((self = (XTThreadPtr) *thd_ha_data(thd, hton))) {
 
1365
                *thd_ha_data(thd, hton) = NULL;
 
1366
#endif
 
1367
                /* Required because freeing the thread could cause
 
1368
                 * free of database which could call xt_close_file_ns()!
 
1369
                 */
 
1370
                xt_set_self(self);
 
1371
                xt_free_thread(self);
 
1372
        }
 
1373
        return 0;
 
1374
}
 
1375
 
 
1376
/*
 
1377
 * Currently does nothing because it was all done
 
1378
 * when the last PBXT table was removed from the 
 
1379
 * database.
 
1380
 */
 
1381
#ifdef DRIZZLED
 
1382
void PBXTStorageEngine::drop_database(char *)
 
1383
#else
 
1384
static void pbxt_drop_database(handlerton *XT_UNUSED(hton), char *XT_UNUSED(path))
 
1385
#endif
 
1386
{
 
1387
        XT_TRACE_CALL();
 
1388
}
 
1389
 
 
1390
/*
 
1391
 * NOTES ON TRANSACTIONS:
 
1392
 *
 
1393
 * 1. If self->st_lock_count == 0 and transaction can be ended immediately.
 
1394
 *    If not, we must wait until the last lock is released on the last handler
 
1395
 *    to ensure that the tables are flushed before the transaction is
 
1396
 *    committed or aborted.
 
1397
 *
 
1398
 * 2. all (below) indicates, within a BEGIN/END (i.e. auto_commit off) whether
 
1399
 *    the statement or the entire transation is being terminated.
 
1400
 *    We currently ignore statement termination.
 
1401
 * 
 
1402
 * 3. If in BEGIN/END we must call ha_rollback() if we abort the transaction
 
1403
 *    internally.
 
1404
 *
 
1405
 * NOTE ON CONSISTENT SNAPSHOTS:
 
1406
 * 
 
1407
 * PBXT itself doesn't need this functiona as its transaction mechanism provides
 
1408
 * consistent snapshots for all transactions by default. This function is needed
 
1409
 * only for multi-engine cases like this:
 
1410
 *
 
1411
 * CREATE TABLE t1 ... ENGINE=INNODB
 
1412
 * CREATE TABLE t2 ... ENGINE=PBXT
 
1413
 * START TRANSACTION WITH CONSISTENT SNAPSHOT
 
1414
 * SELECT * FROM t1 <-- at this point we need to know about the snapshot
 
1415
 */
 
1416
 
 
1417
#ifndef DRIZZLED
 
1418
static int pbxt_start_consistent_snapshot(handlerton *hton, THD *thd)
 
1419
{
 
1420
        int err          = 0;
 
1421
        XTThreadPtr self = ha_set_current_thread(thd, &err);
 
1422
 
 
1423
        if (!self->st_database && pbxt_database) {
 
1424
                xt_ha_open_database_of_table(self, (XTPathStrPtr) NULL);
 
1425
        }
 
1426
 
 
1427
        thd_init_xact(thd, self, true);
 
1428
 
 
1429
        if (xt_xn_begin(self)) {
 
1430
                trans_register_ha(thd, TRUE, hton);     
 
1431
        } else {
 
1432
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1433
        }
 
1434
 
 
1435
        /*
 
1436
         * As of MySQL 5.1.41 the return value is not checked, so the server might assume 
 
1437
         * everything is fine even it isn't. InnoDB returns 0 on success.
 
1438
         */
 
1439
        return err;
 
1440
}
 
1441
#endif
 
1442
 
 
1443
/*
 
1444
 * Commit the PBXT transaction of the given thread.
 
1445
 * thd is the MySQL thread structure.
 
1446
 * pbxt_thr is a pointer the the PBXT thread structure.
 
1447
 *
 
1448
 */
 
1449
#ifdef DRIZZLED
 
1450
int PBXTStorageEngine::commit(Session *thd, bool all)
 
1451
{
 
1452
        PBXTStorageEngine * const hton = this;
 
1453
#else
 
1454
static int pbxt_commit(handlerton *hton, THD *thd, bool all)
 
1455
{
 
1456
#endif
 
1457
        int                     err = 0;
 
1458
        XTThreadPtr     self;
 
1459
 
 
1460
#ifdef DRIZZLED
 
1461
        if ((self = (XTThreadPtr) *thd->getEngineData(hton))) {
 
1462
#else
 
1463
        if ((self = (XTThreadPtr) *thd_ha_data(thd, hton))) {
 
1464
#endif
 
1465
                XT_PRINT2(self, "%s pbxt_commit all=%d\n", all ? "END CONN XACT" : "END STAT", all);
 
1466
 
 
1467
                if (self->st_xact_data) {
 
1468
                        /* There are no table locks, commit immediately in all cases
 
1469
                         * except when this is a statement commit with an explicit
 
1470
                         * transaction (!all && !self->st_auto_commit).
 
1471
                         */
 
1472
                        if (all || self->st_auto_commit) {
 
1473
                                XT_PRINT0(self, "xt_xn_commit in pbxt_commit\n");
 
1474
 
 
1475
                                if (!xt_xn_commit(self))
 
1476
                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1477
                        }
 
1478
                }
 
1479
                if (!all)
 
1480
                        self->st_stat_trans = FALSE;
 
1481
        }
 
1482
        return err;
 
1483
}
 
1484
 
 
1485
#ifdef DRIZZLED
 
1486
int PBXTStorageEngine::rollback(Session *thd, bool all)
 
1487
{
 
1488
        PBXTStorageEngine * const hton = this;
 
1489
#else
 
1490
static int pbxt_rollback(handlerton *hton, THD *thd, bool all)
 
1491
{
 
1492
#endif
 
1493
        int                     err = 0;
 
1494
        XTThreadPtr     self;
 
1495
 
 
1496
#ifdef DRIZZLED
 
1497
        if ((self = (XTThreadPtr) *thd->getEngineData(hton))) {
 
1498
#else
 
1499
        if ((self = (XTThreadPtr) *thd_ha_data(thd, hton))) {
 
1500
#endif
 
1501
                XT_PRINT2(self, "%s pbxt_rollback all=%d\n", all ? "CONN END XACT" : "STAT END", all);
 
1502
 
 
1503
                if (self->st_xact_data) {
 
1504
                        /* There are no table locks, rollback immediately in all cases
 
1505
                         * except when this is a statement commit with an explicit
 
1506
                         * transaction (!all && !self->st_auto_commit).
 
1507
                         *
 
1508
                         * Note, the only reason for a rollback of a operation is
 
1509
                         * due to an error. In this case PBXT has already
 
1510
                         * undone the effects of the operation.
 
1511
                         *
 
1512
                         * However, this is not the same as statement rollback
 
1513
                         * which can involve a number of operations.
 
1514
                         *
 
1515
                         * TODO: Implement statement rollback.
 
1516
                         */
 
1517
                        if (all || self->st_auto_commit) {
 
1518
                                XT_PRINT0(self, "xt_xn_rollback\n");
 
1519
                                if (!xt_xn_rollback(self))
 
1520
                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1521
                        }
 
1522
                }
 
1523
                if (!all)
 
1524
                        self->st_stat_trans = FALSE;
 
1525
        }
 
1526
        return 0;
 
1527
}
 
1528
 
 
1529
#ifdef DRIZZLED
 
1530
Cursor *PBXTStorageEngine::create(TableShare& table, memory::Root *mem_root)
 
1531
{
 
1532
        PBXTStorageEngine * const hton = this;
 
1533
        if (XTSystemTableShare::isSystemTable(table.path.str))
 
1534
#else
 
1535
static handler *pbxt_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root)
 
1536
{
 
1537
        if (table && XTSystemTableShare::isSystemTable(table->path.str))
 
1538
#endif
 
1539
                return new (mem_root) ha_xtsys(hton, table);
 
1540
        else
 
1541
                return new (mem_root) ha_pbxt(hton, table);
 
1542
}
 
1543
 
 
1544
/*
 
1545
 * -----------------------------------------------------------------------
 
1546
 * 2-PHASE COMMIT
 
1547
 *
 
1548
 */
 
1549
 
 
1550
#ifndef DRIZZLED
 
1551
 
 
1552
static int pbxt_prepare(handlerton *hton, THD *thd, bool all)
 
1553
{
 
1554
        int                     err = 0;
 
1555
        XTThreadPtr     self;
 
1556
 
 
1557
        XT_TRACE_CALL();
 
1558
        if ((self = (XTThreadPtr) *thd_ha_data(thd, hton))) {
 
1559
                XT_PRINT1(self, "pbxt_commit all=%d\n", all);
 
1560
 
 
1561
                if (self->st_xact_data) {
 
1562
                        /* There are no table locks, commit immediately in all cases
 
1563
                         * except when this is a statement commit with an explicit
 
1564
                         * transaction (!all && !self->st_auto_commit).
 
1565
                         */
 
1566
                        if (all || self->st_auto_commit) {
 
1567
                                XID xid;
 
1568
 
 
1569
                                XT_PRINT0(self, "xt_xn_prepare in pbxt_prepare\n");
 
1570
                                thd_get_xid(thd, (MYSQL_XID*) &xid);
 
1571
 
 
1572
                                if (!xt_xn_prepare(xid.length(), (xtWord1 *) &xid, self))
 
1573
                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1574
                        }
 
1575
                }
 
1576
        }
 
1577
        return err;
 
1578
}
 
1579
 
 
1580
static XTThreadPtr ha_temp_open_global_database(handlerton *hton, THD **ret_thd, int *temp_thread, char *thread_name, int *err)
 
1581
{
 
1582
        THD                     *thd;
 
1583
        XTThreadPtr     self = NULL;
 
1584
 
 
1585
        *temp_thread = 0;
 
1586
        if ((thd = current_thd))
 
1587
                self = (XTThreadPtr) *thd_ha_data(thd, hton);
 
1588
        else {
 
1589
                //thd = (THD *) myxt_create_thread();
 
1590
                //*temp_thread |= 2;
 
1591
        }
 
1592
 
 
1593
        if (!self) {
 
1594
                XTExceptionRec e;
 
1595
 
 
1596
                if (!(self = xt_create_thread(thread_name, FALSE, TRUE, &e))) {
 
1597
                        *err = xt_ha_pbxt_to_mysql_error(e.e_xt_err);
 
1598
                        xt_log_exception(NULL, &e, XT_LOG_DEFAULT);
 
1599
                        return NULL;
 
1600
                }
 
1601
                *temp_thread |= 1;
 
1602
        }
 
1603
 
 
1604
        xt_xres_wait_for_recovery(self, XT_RECOVER_DONE);
 
1605
 
 
1606
        try_(a) {
 
1607
                xt_open_database(self, mysql_real_data_home, TRUE);
 
1608
        }
 
1609
        catch_(a) {
 
1610
                *err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1611
                if ((*temp_thread & 1))
 
1612
                        xt_free_thread(self);
 
1613
                if (*temp_thread & 2)
 
1614
                        myxt_destroy_thread(thd, FALSE);
 
1615
                self = NULL;
 
1616
        }
 
1617
        cont_(a);
 
1618
 
 
1619
        *ret_thd = thd;
 
1620
        return self;
 
1621
}
 
1622
 
 
1623
static void ha_temp_close_database(XTThreadPtr self, THD *thd, int temp_thread)
 
1624
{
 
1625
        xt_unuse_database(self, self);
 
1626
        if (temp_thread & 1)
 
1627
                xt_free_thread(self);
 
1628
        if (temp_thread & 2)
 
1629
                myxt_destroy_thread(thd, TRUE);
 
1630
}
 
1631
 
 
1632
/* Return all prepared transactions, found during recovery.
 
1633
 * This function returns a count. If len is returned, the
 
1634
 * function will be called again.
 
1635
 */
 
1636
static int pbxt_recover(handlerton *hton, XID *xid_list, uint len)
 
1637
{
 
1638
        xtBool                          temp_thread;
 
1639
        XTThreadPtr                     self;
 
1640
        XTDatabaseHPtr          db;
 
1641
        uint                            count = 0;
 
1642
        XTXactPreparePtr        xap;
 
1643
        int                                     err;
 
1644
        THD                                     *thd;
 
1645
 
 
1646
        if (!(self = ha_temp_open_global_database(hton, &thd, &temp_thread, "TempForRecover", &err)))
 
1647
                return 0;
 
1648
 
 
1649
        db = self->st_database;
 
1650
 
 
1651
        for (count=0; count<len; count++) {
 
1652
                xap = xt_xn_enum_xa_data(db, &pbxt_xa_enum);
 
1653
                if (!xap)
 
1654
                        break;
 
1655
                memcpy(&xid_list[count], xap->xp_xa_data, xap->xp_data_len);
 
1656
        }
 
1657
 
 
1658
        ha_temp_close_database(self, thd, temp_thread);
 
1659
        return (int) count;
 
1660
}
 
1661
 
 
1662
static int pbxt_commit_by_xid(handlerton *hton, XID *xid)
 
1663
{
 
1664
        xtBool                          temp_thread;
 
1665
        XTThreadPtr                     self;
 
1666
        XTDatabaseHPtr          db;
 
1667
        int                                     err = 0;
 
1668
        XTXactPreparePtr        xap;
 
1669
        THD                                     *thd;
 
1670
 
 
1671
        XT_TRACE_CALL();
 
1672
 
 
1673
        if (!(self = ha_temp_open_global_database(hton, &thd, &temp_thread, "TempForCommitXA", &err)))
 
1674
                return err;
 
1675
        db = self->st_database;
 
1676
 
 
1677
        if ((xap = xt_xn_find_xa_data(db, xid->length(), (xtWord1 *) xid, TRUE, self))) {
 
1678
                if ((self->st_xact_data = xt_xn_get_xact(db, xap->xp_xact_id, self))) {
 
1679
                        self->st_xact_data->xd_flags &= ~XT_XN_XAC_PREPARED;  // Prepared transactions cannot be swept!
 
1680
                        if (!xt_xn_commit(self))
 
1681
                                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1682
                }
 
1683
                xt_xn_delete_xa_data(db, xap, TRUE, self);
 
1684
        }
 
1685
 
 
1686
        ha_temp_close_database(self, thd, temp_thread);
 
1687
        return 0;
 
1688
}
 
1689
 
 
1690
static int pbxt_rollback_by_xid(handlerton *hton, XID *xid)
 
1691
{
 
1692
        int                                     temp_thread;
 
1693
        XTThreadPtr                     self;
 
1694
        XTDatabaseHPtr          db;
 
1695
        int                                     err = 0;
 
1696
        XTXactPreparePtr        xap;
 
1697
        THD                                     *thd;
 
1698
 
 
1699
        XT_TRACE_CALL();
 
1700
 
 
1701
        if (!(self = ha_temp_open_global_database(hton, &thd, &temp_thread, "TempForRollbackXA", &err)))
 
1702
                return err;
 
1703
        db = self->st_database;
 
1704
 
 
1705
        if ((xap = xt_xn_find_xa_data(db, xid->length(), (xtWord1 *) xid, TRUE, self))) {
 
1706
                if ((self->st_xact_data = xt_xn_get_xact(db, xap->xp_xact_id, self))) {
 
1707
                        self->st_xact_data->xd_flags &= ~XT_XN_XAC_PREPARED;  // Prepared transactions cannot be swept!
 
1708
                        if (!xt_xn_rollback(self))
 
1709
                                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1710
                }
 
1711
                xt_xn_delete_xa_data(db, xap, TRUE, self);
 
1712
        }
 
1713
 
 
1714
        ha_temp_close_database(self, thd, temp_thread);
 
1715
        return 0;
 
1716
}
 
1717
 
 
1718
#endif
 
1719
 
 
1720
/*
 
1721
 * -----------------------------------------------------------------------
 
1722
 * HANDLER LOCKING FUNCTIONS
 
1723
 *
 
1724
 * These functions are used get a lock on all handles of a particular table.
 
1725
 *
 
1726
 */
 
1727
 
 
1728
static void ha_add_to_handler_list(XTThreadPtr self, XTSharePtr share, ha_pbxt *handler)
 
1729
{
 
1730
        xt_lock_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
1731
        pushr_(xt_unlock_mutex, share->sh_ex_mutex);
 
1732
 
 
1733
        handler->pb_ex_next = share->sh_handlers;
 
1734
        handler->pb_ex_prev = NULL;
 
1735
        if (share->sh_handlers)
 
1736
                share->sh_handlers->pb_ex_prev = handler;
 
1737
        share->sh_handlers = handler;
 
1738
 
 
1739
        freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1740
}
 
1741
 
 
1742
static void ha_remove_from_handler_list(XTThreadPtr self, XTSharePtr share, ha_pbxt *handler)
 
1743
{
 
1744
        xt_lock_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
1745
        pushr_(xt_unlock_mutex, share->sh_ex_mutex);
 
1746
 
 
1747
        /* Move front pointer: */
 
1748
        if (share->sh_handlers == handler)
 
1749
                share->sh_handlers = handler->pb_ex_next;
 
1750
 
 
1751
        /* Remove from list: */
 
1752
        if (handler->pb_ex_prev)
 
1753
                handler->pb_ex_prev->pb_ex_next = handler->pb_ex_next;
 
1754
        if (handler->pb_ex_next)
 
1755
                handler->pb_ex_next->pb_ex_prev = handler->pb_ex_prev;
 
1756
 
 
1757
        freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1758
}
 
1759
 
 
1760
/*
 
1761
 * Aquire exclusive use of a table, by waiting for all
 
1762
 * threads to complete use of all handlers of the table.
 
1763
 * At the same time we hold up all threads
 
1764
 * that want to use handlers belonging to the table.
 
1765
 *
 
1766
 * But we do not hold up threads that close the handlers.
 
1767
 */
 
1768
static void ha_aquire_exclusive_use(XTThreadPtr self, XTSharePtr share, ha_pbxt *mine)
 
1769
{
 
1770
        ha_pbxt *handler;
 
1771
        time_t  end_time = time(NULL) + XT_SHARE_LOCK_TIMEOUT / 1000;
 
1772
 
 
1773
        XT_PRINT1(self, "ha_aquire_exclusive_use (%s) PBXT X lock\n", share->sh_table_path->ps_path);
 
1774
        /* GOTCHA: It is possible to hang here, if you hold
 
1775
         * onto the sh_ex_mutex lock, before we really
 
1776
         * have the exclusive lock (i.e. before all
 
1777
         * handlers are no longer in use.
 
1778
         * The reason is, because reopen() is not possible
 
1779
         * when some other thread holds sh_ex_mutex.
 
1780
         * So this can prevent a thread from completing its
 
1781
         * use of a handler, when prevents exclusive use
 
1782
         * here.
 
1783
         */
 
1784
        xt_lock_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
1785
        pushr_(xt_unlock_mutex, share->sh_ex_mutex);
 
1786
 
 
1787
        /* Wait until we can get an exclusive lock: */
 
1788
        while (share->sh_table_lock) {
 
1789
                xt_timed_wait_cond(self, (xt_cond_type *) share->sh_ex_cond, (xt_mutex_type *) share->sh_ex_mutex, XT_SHARE_LOCK_WAIT);
 
1790
                if (time(NULL) > end_time) {
 
1791
                        freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1792
                        xt_throw_taberr(XT_CONTEXT, XT_ERR_LOCK_TIMEOUT, share->sh_table_path);
 
1793
                }
 
1794
        }
 
1795
 
 
1796
        /* This tells readers (and other exclusive lockers) that someone has an exclusive lock. */
 
1797
        share->sh_table_lock = TRUE;
 
1798
        
 
1799
        /* Wait for all open handlers use count to go to 0 */   
 
1800
        retry:
 
1801
        handler = share->sh_handlers;
 
1802
        while (handler) {
 
1803
                if (handler == mine || !handler->pb_ex_in_use)
 
1804
                        handler = handler->pb_ex_next;
 
1805
                else {
 
1806
                        /* Wait a bit, and try again: */
 
1807
                        xt_timed_wait_cond(self, (xt_cond_type *) share->sh_ex_cond, (xt_mutex_type *) share->sh_ex_mutex, XT_SHARE_LOCK_WAIT);
 
1808
                        if (time(NULL) > end_time) {
 
1809
                                freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1810
                                xt_throw_taberr(XT_CONTEXT, XT_ERR_LOCK_TIMEOUT, share->sh_table_path);
 
1811
                        }
 
1812
                        /* Handler may have been freed, check from the begining again: */
 
1813
                        goto retry;
 
1814
                }
 
1815
        }
 
1816
 
 
1817
        freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1818
}
 
1819
 
 
1820
/*
 
1821
 * If you have exclusively locked the table, you can close all handler
 
1822
 * open tables.
 
1823
 *
 
1824
 * Call ha_close_open_tables() to get an exclusive lock.
 
1825
 */
 
1826
static void ha_close_open_tables(XTThreadPtr self, XTSharePtr share, ha_pbxt *mine)
 
1827
{
 
1828
        ha_pbxt *handler;
 
1829
 
 
1830
        xt_lock_mutex(self, (xt_mutex_type *) share->sh_ex_mutex);
 
1831
        pushr_(xt_unlock_mutex, share->sh_ex_mutex);
 
1832
 
 
1833
        /* Now that we know no handler is in use, we can close all the
 
1834
         * open tables...
 
1835
         */
 
1836
        handler = share->sh_handlers;
 
1837
        while (handler) {
 
1838
                if (handler != mine && handler->pb_open_tab) {
 
1839
                        xt_db_return_table_to_pool_ns(handler->pb_open_tab);
 
1840
                        handler->pb_open_tab = NULL;
 
1841
                }
 
1842
                handler = handler->pb_ex_next;
 
1843
        }
 
1844
 
 
1845
        freer_(); // xt_unlock_mutex(share->sh_ex_mutex)
 
1846
}
 
1847
 
 
1848
#ifdef PBXT_ALLOW_PRINTING
 
1849
static void ha_release_exclusive_use(XTThreadPtr self, XTSharePtr share)
 
1850
#else
 
1851
static void ha_release_exclusive_use(XTThreadPtr XT_UNUSED(self), XTSharePtr share)
 
1852
#endif
 
1853
{
 
1854
        XT_PRINT1(self, "ha_release_exclusive_use (%s) PBXT X UNLOCK\n", share->sh_table_path->ps_path);
 
1855
        xt_lock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1856
        share->sh_table_lock = FALSE;
 
1857
        xt_broadcast_cond_ns((xt_cond_type *) share->sh_ex_cond);
 
1858
        xt_unlock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1859
}
 
1860
 
 
1861
static xtBool ha_wait_for_shared_use(ha_pbxt *mine, XTSharePtr share)
 
1862
{
 
1863
        time_t  end_time = time(NULL) + XT_SHARE_LOCK_TIMEOUT / 1000;
 
1864
 
 
1865
        XT_PRINT1(xt_get_self(), "ha_wait_for_shared_use (%s) share lock wait...\n", share->sh_table_path->ps_path);
 
1866
        mine->pb_ex_in_use = 0;
 
1867
        xt_lock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1868
        while (share->sh_table_lock) {
 
1869
                /* Wake up the exclusive locker (may be waiting). He can try to continue: */
 
1870
                xt_broadcast_cond_ns((xt_cond_type *) share->sh_ex_cond);
 
1871
 
 
1872
                if (!xt_timed_wait_cond(NULL, (xt_cond_type *) share->sh_ex_cond, (xt_mutex_type *) share->sh_ex_mutex, XT_SHARE_LOCK_WAIT)) {
 
1873
                        xt_unlock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1874
                        return FAILED;
 
1875
                }
 
1876
 
 
1877
                if (time(NULL) > end_time) {
 
1878
                        xt_unlock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1879
                        xt_register_taberr(XT_REG_CONTEXT, XT_ERR_LOCK_TIMEOUT, share->sh_table_path);
 
1880
                        return FAILED;
 
1881
                }
 
1882
        }
 
1883
        mine->pb_ex_in_use = 1;
 
1884
        xt_unlock_mutex_ns((xt_mutex_type *) share->sh_ex_mutex);
 
1885
        return OK;
 
1886
}
 
1887
 
 
1888
xtPublic int ha_pbxt::reopen()
 
1889
{
 
1890
        THD                             *thd = current_thd;
 
1891
        int                             err = 0;
 
1892
        XTThreadPtr             self;   
 
1893
 
 
1894
        if (!(self = ha_set_current_thread(thd, &err)))
 
1895
                return xt_ha_pbxt_to_mysql_error(err);
 
1896
 
 
1897
        try_(a) {
 
1898
                xt_ha_open_database_of_table(self, pb_share->sh_table_path);
 
1899
 
 
1900
                ha_open_share(self, pb_share);
 
1901
 
 
1902
                if (!(pb_open_tab = xt_db_open_table_using_tab(pb_share->sh_table, self)))
 
1903
                        xt_throw(self);
 
1904
                pb_open_tab->ot_thread = self;
 
1905
 
 
1906
                /* {TABLE-STATS}
 
1907
                 * We no longer use the information that a table
 
1908
                 * was opened in order to know when to calculate
 
1909
                 * statistics.
 
1910
                 */
 
1911
                if (!pb_open_tab->ot_table->tab_ind_stat_calc_time) {
 
1912
#ifdef LOAD_TABLE_ON_OPEN
 
1913
                        xt_tab_load_table(self, pb_open_tab);
 
1914
#else
 
1915
                        xt_tab_load_row_pointers(self, pb_open_tab);
 
1916
#endif
 
1917
                        xt_ind_set_index_selectivity(pb_open_tab, self);
 
1918
                        /* If the number of rows is less than 150 we will recalculate the
 
1919
                         * selectity of the indices, as soon as the number of rows
 
1920
                         * exceeds 200 (see [**])
 
1921
                         */
 
1922
                        /* {FREE-ROWS-BAD} */
 
1923
                        pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
 
1924
                }
 
1925
 
 
1926
                /* I am not doing this anymore because it was only required
 
1927
                 * for DELETE FROM table;, which is now implemented
 
1928
                 * by deleting each row.
 
1929
                 * TRUNCATE TABLE does not preserve the counter value.
 
1930
                 */
 
1931
                //init_auto_increment(pb_share->sh_min_auto_inc);
 
1932
                init_auto_increment(0);
 
1933
        }
 
1934
        catch_(a) {
 
1935
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
1936
        }
 
1937
        cont_(a);
 
1938
        
 
1939
        return err;
 
1940
}
 
1941
 
 
1942
/*
 
1943
 * -----------------------------------------------------------------------
 
1944
 * INFORMATION SCHEMA FUNCTIONS
 
1945
 *
 
1946
 */
 
1947
#ifdef DRI_IS
 
1948
static int pbxt_statistics_fill_table(THD *thd, TABLE_LIST *tables, COND *cond)
 
1949
{
 
1950
        XTThreadPtr             self = NULL;    
 
1951
        int                             err = 0;
 
1952
 
 
1953
        if (!pbxt_hton) {
 
1954
                /* Can't do if PBXT is not loaded! */
 
1955
                XTExceptionRec  e;
 
1956
 
 
1957
                xt_exception_xterr(&e, XT_CONTEXT, XT_ERR_PBXT_NOT_INSTALLED);
 
1958
                xt_log_exception(NULL, &e, XT_LOG_DEFAULT);
 
1959
                /* Just return an empty set: */
 
1960
                return 0;
 
1961
        }
 
1962
 
 
1963
        if (!(self = ha_set_current_thread(thd, &err)))
 
1964
                return xt_ha_pbxt_to_mysql_error(err);
 
1965
 
 
1966
 
 
1967
        try_(a) {
 
1968
                /* If the thread has no open database, and the global
 
1969
                 * database is already open, then open
 
1970
                 * the database. Otherwise the statement will be
 
1971
                 * executed without an open database, which means
 
1972
                 * that the related statistics will be missing.
 
1973
                 *
 
1974
                 * This includes all background threads.
 
1975
                 */
 
1976
                if (!self->st_database && pbxt_database) {
 
1977
                        xt_ha_open_database_of_table(self, (XTPathStrPtr) NULL);
 
1978
                }
 
1979
 
 
1980
                err = myxt_statistics_fill_table(self, thd, tables, cond, system_charset_info);
 
1981
        }
 
1982
        catch_(a) {
 
1983
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
1984
        }
 
1985
        cont_(a);
 
1986
        return err;
 
1987
}
 
1988
#endif // DRI_IS
 
1989
 
 
1990
#ifdef DRIZZLED
 
1991
#ifdef DRI_IS
 
1992
ColumnInfo pbxt_statistics_fields_info[]=
 
1993
{
 
1994
        ColumnInfo("ID", 4, MYSQL_TYPE_LONG,  0, 0, "The ID of the statistic", SKIP_OPEN_TABLE),
 
1995
        ColumnInfo("Name", 40, MYSQL_TYPE_STRING, 0, 0, "The name of the statistic", SKIP_OPEN_TABLE),
 
1996
        ColumnInfo("Value", 8, MYSQL_TYPE_LONGLONG, 0, 0, "The accumulated value", SKIP_OPEN_TABLE),
 
1997
        ColumnInfo()
 
1998
};
 
1999
 
 
2000
class PBXTStatisticsMethods : public InfoSchemaMethods
 
2001
{
 
2002
public:
 
2003
  int fillTable(Session *session, TableList *tables, COND *cond)
 
2004
  {
 
2005
        return pbxt_statistics_fill_table(session, tables, cond);
 
2006
  }
 
2007
};
 
2008
#endif // DRI_IS
 
2009
#else
 
2010
ST_FIELD_INFO pbxt_statistics_fields_info[]=
 
2011
{
 
2012
        { "ID",         4,      MYSQL_TYPE_LONG,                0, 0, "The ID of the statistic", SKIP_OPEN_TABLE},
 
2013
        { "Name",       40, MYSQL_TYPE_STRING,          0, 0, "The name of the statistic", SKIP_OPEN_TABLE},
 
2014
        { "Value",      8,      MYSQL_TYPE_LONGLONG,    0, 0, "The accumulated value", SKIP_OPEN_TABLE},
 
2015
        { 0,            0,      MYSQL_TYPE_STRING,              0, 0, 0, SKIP_OPEN_TABLE}
 
2016
};
 
2017
#endif
 
2018
 
 
2019
#ifdef DRIZZLED
 
2020
#ifdef DRI_IS
 
2021
static InfoSchemaTable  *pbxt_statistics_table;
 
2022
static PBXTStatisticsMethods pbxt_statistics_methods;
 
2023
static int pbxt_init_statistics(Registry &registry)
 
2024
{
 
2025
        //pbxt_statistics_table = (InfoSchemaTable *)xt_calloc_ns(sizeof(InfoSchemaTable));
 
2026
        //pbxt_statistics_table->table_name= "PBXT_STATISTICS";
 
2027
        pbxt_statistics_table = new InfoSchemaTable("PBXT_STATISTICS");
 
2028
        pbxt_statistics_table->setColumnInfo(pbxt_statistics_fields_info);
 
2029
        pbxt_statistics_table->setInfoSchemaMethods(&pbxt_statistics_methods);
 
2030
        registry.add(pbxt_statistics_table);
 
2031
        return 0;
 
2032
}
 
2033
#endif // DRI_IS
 
2034
#else  // DRIZZLED
 
2035
static int pbxt_init_statistics(void *p)
 
2036
{
 
2037
        ST_SCHEMA_TABLE *pbxt_statistics_table = (ST_SCHEMA_TABLE *) p;
 
2038
        pbxt_statistics_table->fields_info = pbxt_statistics_fields_info;
 
2039
        pbxt_statistics_table->fill_table = pbxt_statistics_fill_table;
 
2040
 
 
2041
#if defined(XT_WIN) && defined(XT_COREDUMP)
 
2042
        void register_crash_filter();
 
2043
 
 
2044
        if (pbxt_crash_debug)
 
2045
                register_crash_filter();
 
2046
#endif
 
2047
        return 0;
 
2048
}
 
2049
#endif
 
2050
 
 
2051
#ifdef DRIZZLED
 
2052
#ifdef DRI_IS
 
2053
static int pbxt_exit_statistics(Registry &registry)
 
2054
        registry.remove(pbxt_statistics_table);
 
2055
        delete pbxt_statistics_table;
 
2056
        return(0);
 
2057
}
 
2058
#endif // DRI_IS
 
2059
#else  // DRIZZLED
 
2060
static int pbxt_exit_statistics(void *XT_UNUSED(p))
 
2061
{
 
2062
        return(0);
 
2063
}
 
2064
#endif  // DRIZZLED
 
2065
 
 
2066
/*
 
2067
 * -----------------------------------------------------------------------
 
2068
 * DYNAMIC HOOKS
 
2069
 *
 
2070
 */
 
2071
 
 
2072
#ifdef DRIZZLED
 
2073
ha_pbxt::ha_pbxt(handlerton *hton, TableShare& table_arg) : handler(*hton, table_arg)
 
2074
#else
 
2075
ha_pbxt::ha_pbxt(handlerton *hton, TABLE_SHARE *table_arg) : handler(hton, table_arg)
 
2076
#endif
 
2077
{
 
2078
        pb_share = NULL;
 
2079
        pb_open_tab = NULL;
 
2080
        pb_key_read = FALSE;
 
2081
        pb_ignore_dup_key = 0;
 
2082
        pb_lock_table = FALSE;
 
2083
        pb_table_locked = 0;
 
2084
        pb_ex_next = NULL;
 
2085
        pb_ex_prev = NULL;
 
2086
        pb_ex_in_use = 0;
 
2087
        pb_in_stat = FALSE;
 
2088
}
 
2089
 
 
2090
/*
 
2091
 * If frm_error() is called then we will use this to to find out what file extentions
 
2092
 * exist for the storage engine. This is also used by the default rename_table and
 
2093
 * delete_table method in handler.cc.
 
2094
 */
 
2095
#ifdef DRIZZLED
 
2096
const char **PBXTStorageEngine::bas_ext() const
 
2097
#else
 
2098
const char **ha_pbxt::bas_ext() const
 
2099
#endif
 
2100
{
 
2101
        return pbxt_extensions;
 
2102
}
 
2103
 
 
2104
/*
 
2105
 * Specify the caching type: HA_CACHE_TBL_NONTRANSACT, HA_CACHE_TBL_NOCACHE
 
2106
 * HA_CACHE_TBL_ASKTRANSACT, HA_CACHE_TBL_TRANSACT
 
2107
 */
 
2108
MX_UINT8_T ha_pbxt::table_cache_type()
 
2109
{
 
2110
        return HA_CACHE_TBL_TRANSACT; /* Use transactional query cache */
 
2111
}
 
2112
 
 
2113
#ifndef DRIZZLED
 
2114
MX_TABLE_TYPES_T ha_pbxt::table_flags() const
 
2115
{
 
2116
        return (
 
2117
                /* We need this flag because records are not packed
 
2118
                 * into a table which means #ROWID != offset
 
2119
                 */
 
2120
                HA_REC_NOT_IN_SEQ |
 
2121
                /* Since PBXT caches read records itself, I believe
 
2122
                 * this to be the case.
 
2123
                 */
 
2124
                HA_FAST_KEY_READ |
 
2125
                /*
 
2126
                 * I am assuming a "key" means a unique index.
 
2127
                 * Of course a primary key does not allow nulls.
 
2128
                 */
 
2129
                HA_NULL_IN_KEY |
 
2130
                /*
 
2131
                 * This is necessary because a MySQL blob can be
 
2132
                 * fairly small.
 
2133
                 */
 
2134
                HA_CAN_INDEX_BLOBS |
 
2135
                /*
 
2136
                 * Due to transactional influences, this will be
 
2137
                 * the case.
 
2138
                 * Although the count is good enough for practical
 
2139
                 * purposes!
 
2140
                HA_NOT_EXACT_COUNT |
 
2141
                 */
 
2142
#ifndef DRIZZLED
 
2143
                /*
 
2144
                 * This basically means we have a file with the name of
 
2145
                 * database table (which we do).
 
2146
                 */
 
2147
                HA_FILE_BASED |
 
2148
#endif
 
2149
                /*
 
2150
                 * Not sure what this does (but MyISAM and InnoDB have it)?!
 
2151
                 * Could it mean that we support the handler functions.
 
2152
                 */
 
2153
                HA_CAN_SQL_HANDLER |
 
2154
                /*
 
2155
                 * This is not true, we cannot insert delayed, but a
 
2156
                 * really cannot see what's wrong with inserting normally
 
2157
                 * when asked to insert delayed!
 
2158
                 * And the functionallity is required to pass the alter_table
 
2159
                 * test.
 
2160
                 *
 
2161
                 * Disabled because of MySQL bug #40505
 
2162
                 */
 
2163
                /*HA_CAN_INSERT_DELAYED |*/
 
2164
#if MYSQL_VERSION_ID > 50119
 
2165
                /* We can do row logging, but not statement, because
 
2166
                 * MVCC is not serializable!
 
2167
                 */
 
2168
                HA_BINLOG_ROW_CAPABLE |
 
2169
#endif
 
2170
                /*
 
2171
                 * Auto-increment is allowed on a partial key.
 
2172
                 */
 
2173
                HA_AUTO_PART_KEY);
 
2174
}
 
2175
#endif
 
2176
 
 
2177
/*
 
2178
 * The following query from the DBT1 test is VERY slow
 
2179
 * if we do not set HA_READ_ORDER.
 
2180
 * The reason is that it must scan all duplicates, then
 
2181
 * sort.
 
2182
 *
 
2183
 * SELECT o_id, o_carrier_id, o_entry_d, o_ol_cnt
 
2184
 * FROM orders FORCE INDEX (o_w_id)
 
2185
 * WHERE o_w_id = 2
 
2186
   * AND o_d_id = 1
 
2187
   * AND o_c_id = 500
 
2188
 * ORDER BY o_id DESC limit 1;
 
2189
 *
 
2190
 */
 
2191
#define FLAGS_ARE_READ_DYNAMICALLY
 
2192
 
 
2193
MX_ULONG_T ha_pbxt::index_flags(uint XT_UNUSED(inx), uint XT_UNUSED(part), bool XT_UNUSED(all_parts)) const
 
2194
{
 
2195
        /* It would be nice if the dynamic version of this function works,
 
2196
         * but it does not. MySQL loads this information when the table is openned,
 
2197
         * and then it is fixed.
 
2198
         *
 
2199
         * The problem is, I have had to remove the HA_READ_ORDER option although
 
2200
         * it applies to PBXT. PBXT returns entries in index order during an index
 
2201
         * scan in _almost_ all cases.
 
2202
         *
 
2203
         * A number of cases are demostrated here: [(11)]
 
2204
         *
 
2205
         * If involves the following conditions:
 
2206
         * - a SELECT FOR UPDATE, UPDATE or DELETE statement
 
2207
         * - an ORDER BY, or join that requires the sort order
 
2208
         * - another transaction which updates the index while it is being
 
2209
         *   scanned.
 
2210
         *
 
2211
         * In this "obscure" case, the index scan may return index
 
2212
         * entries in the wrong order.
 
2213
         */
 
2214
#ifdef FLAGS_ARE_READ_DYNAMICALLY
 
2215
        /* If were are in an update (SELECT FOR UPDATE, UPDATE or DELETE), then
 
2216
         * it may be that we return the rows from an index in the wrong
 
2217
         * order! This is due to the fact that update reads wait for transactions
 
2218
         * to commit and this means that index entries may change position during
 
2219
         * the scan!
 
2220
         */
 
2221
        if (pb_open_tab && pb_open_tab->ot_for_update)
 
2222
                return (HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE | HA_KEYREAD_ONLY);
 
2223
        /* If I understand HA_KEYREAD_ONLY then this means I do not
 
2224
         * need to fetch the record associated with an index
 
2225
         * key.
 
2226
         */
 
2227
        return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_READ_RANGE | HA_KEYREAD_ONLY);
 
2228
#else
 
2229
        return (HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE | HA_KEYREAD_ONLY);
 
2230
#endif
 
2231
}
 
2232
 
 
2233
void ha_pbxt::internal_close(THD *thd, struct XTThread *self)
 
2234
{
 
2235
        if (pb_share) {
 
2236
                xtBool                  removed;
 
2237
                XTOpenTablePtr  ot;
 
2238
 
 
2239
                try_(a) {
 
2240
                        /* This lock must be held when we remove the handler's
 
2241
                         * open table because ha_close_open_tables() can run
 
2242
                         * concurrently.
 
2243
                         */
 
2244
                        xt_lock_mutex_ns(pb_share->sh_ex_mutex);
 
2245
                        if ((ot = pb_open_tab)) {
 
2246
                                pb_open_tab->ot_thread = self;
 
2247
                                if (self->st_database != pb_open_tab->ot_table->tab_db)
 
2248
                                        xt_ha_open_database_of_table(self, pb_share->sh_table_path);
 
2249
                                pb_open_tab = NULL;
 
2250
                                pushr_(xt_db_return_table_to_pool, ot);
 
2251
                        }
 
2252
                        xt_unlock_mutex_ns(pb_share->sh_ex_mutex);
 
2253
 
 
2254
                        ha_remove_from_handler_list(self, pb_share, this);
 
2255
 
 
2256
                        /* Someone may be waiting for me to complete: */
 
2257
                        xt_broadcast_cond_ns((xt_cond_type *) pb_share->sh_ex_cond);
 
2258
 
 
2259
                        removed = ha_unget_share_removed(self, pb_share);
 
2260
 
 
2261
                        if (ot) {
 
2262
                                /* Flush the table if this was the last handler: */
 
2263
                                /* This is not necessary but has the affect that
 
2264
                                 * FLUSH TABLES; does a checkpoint!
 
2265
                                 */
 
2266
                                if (removed) {
 
2267
                                        /* GOTCHA:
 
2268
                                         * This was killing performance as the number of threads increased!
 
2269
                                         *
 
2270
                                         * When MySQL runs out of table handlers because the table
 
2271
                                         * handler cache is too small, it starts to close handlers.
 
2272
                                         * (open_cache.records > table_cache_size)
 
2273
                                         *
 
2274
                                         * Which can lead to closing all handlers for a particular table.
 
2275
                                         *
 
2276
                                         * It does this while holding lock_OPEN!
 
2277
                                         * So this code below leads to a sync operation while lock_OPEN
 
2278
                                         * is held. The result is that the whole server comes to a stop.
 
2279
                                         */
 
2280
                                        if (!thd || thd_sql_command(thd) == SQLCOM_FLUSH) // FLUSH TABLES
 
2281
                                                xt_sync_flush_table(self, ot, thd ? 0 : 4);
 
2282
                                }
 
2283
                                freer_(); // xt_db_return_table_to_pool(ot);
 
2284
                        }
 
2285
                }
 
2286
                catch_(a) {
 
2287
                        xt_log_and_clear_exception(self);
 
2288
                }
 
2289
                cont_(a);
 
2290
 
 
2291
                pb_share = NULL;
 
2292
        }
 
2293
}
 
2294
 
 
2295
/*
 
2296
 * Used for opening tables. The name will be the name of the file.
 
2297
 * A table is opened when it needs to be opened. For instance
 
2298
 * when a request comes in for a select on the table (tables are not
 
2299
 * open and closed for each request, they are cached).
 
2300
 
 
2301
 * Called from handler.cc by handler::ha_open(). The server opens all tables by
 
2302
 * calling ha_open() which then calls the handler specific open().
 
2303
 */
 
2304
int ha_pbxt::open(const char *table_path, int XT_UNUSED(mode), uint XT_UNUSED(test_if_locked))
 
2305
{
 
2306
        THD                     *thd = current_thd;
 
2307
        int                     err = 0;
 
2308
        XTThreadPtr     self;
 
2309
 
 
2310
        ref_length = XT_RECORD_OFFS_SIZE;
 
2311
 
 
2312
        if (!(self = ha_set_current_thread(thd, &err)))
 
2313
                return xt_ha_pbxt_to_mysql_error(err);
 
2314
 
 
2315
        XT_PRINT1(self, "open (%s)\n", table_path);
 
2316
 
 
2317
        pb_ex_in_use = 1;
 
2318
        try_(a) {
 
2319
                xt_ha_open_database_of_table(self, (XTPathStrPtr) table_path);
 
2320
 
 
2321
                pb_share = ha_get_share(self, table_path, false);
 
2322
                ha_add_to_handler_list(self, pb_share, this);
 
2323
                if (pb_share->sh_table_lock) {
 
2324
                        if (!ha_wait_for_shared_use(this, pb_share))
 
2325
                                xt_throw(self);
 
2326
                }
 
2327
 
 
2328
                ha_open_share(self, pb_share);
 
2329
 
 
2330
                thr_lock_data_init(&pb_share->sh_lock, &pb_lock, NULL);
 
2331
                if (!(pb_open_tab = xt_db_open_table_using_tab(pb_share->sh_table, self)))
 
2332
                        xt_throw(self);
 
2333
                pb_open_tab->ot_thread = self;
 
2334
 
 
2335
                /* {TABLE-STATS} */
 
2336
                if (!pb_open_tab->ot_table->tab_ind_stat_calc_time) {
 
2337
#ifdef LOAD_TABLE_ON_OPEN
 
2338
                        xt_tab_load_table(self, pb_open_tab);
 
2339
#else
 
2340
                        xt_tab_load_row_pointers(self, pb_open_tab);
 
2341
#endif
 
2342
                        xt_ind_set_index_selectivity(pb_open_tab, self);
 
2343
                        /* {FREE-ROWS-BAD} */
 
2344
                        pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
 
2345
                }
 
2346
 
 
2347
                init_auto_increment(0);
 
2348
        }
 
2349
        catch_(a) {
 
2350
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
2351
                internal_close(thd, self);
 
2352
        }
 
2353
        cont_(a);
 
2354
 
 
2355
        if (!err)
 
2356
                info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
 
2357
 
 
2358
        pb_ex_in_use = 0;
 
2359
        if (pb_share) {
 
2360
                /* Someone may be waiting for me to complete: */
 
2361
                if (pb_share->sh_table_lock)
 
2362
                        xt_broadcast_cond_ns((xt_cond_type *) pb_share->sh_ex_cond);
 
2363
        }
 
2364
        return err;
 
2365
}
 
2366
 
 
2367
 
 
2368
/*
 
2369
        Closes a table. We call the free_share() function to free any resources
 
2370
        that we have allocated in the "shared" structure.
 
2371
 
 
2372
        Called from sql_base.cc, sql_select.cc, and table.cc.
 
2373
        In sql_select.cc it is only used to close up temporary tables or during
 
2374
        the process where a temporary table is converted over to being a
 
2375
        myisam table.
 
2376
        For sql_base.cc look at close_data_tables().
 
2377
*/
 
2378
int ha_pbxt::close(void)
 
2379
{
 
2380
        THD                                             *thd = current_thd;
 
2381
        volatile int                    err = 0;
 
2382
        volatile XTThreadPtr    self;
 
2383
 
 
2384
        if (thd)
 
2385
                self = ha_set_current_thread(thd, (int *) &err);
 
2386
        else {
 
2387
                XTExceptionRec e;
 
2388
 
 
2389
                if (!(self = xt_create_thread("TempForClose", FALSE, TRUE, &e))) {
 
2390
                        xt_log_exception(NULL, &e, XT_LOG_DEFAULT);
 
2391
                        return 0;
 
2392
                }
 
2393
        }
 
2394
 
 
2395
        XT_PRINT1(self, "close (%s)\n", pb_share && pb_share->sh_table_path->ps_path ? pb_share->sh_table_path->ps_path : "unknown");
 
2396
 
 
2397
        if (self) {
 
2398
                try_(a) {
 
2399
                        internal_close(thd, self);
 
2400
                }
 
2401
                catch_(a) {
 
2402
                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
2403
                }
 
2404
                cont_(a);
 
2405
 
 
2406
                if (!thd)
 
2407
                        xt_free_thread(self);
 
2408
        }
 
2409
        else
 
2410
                xt_log(XT_NS_CONTEXT, XT_LOG_WARNING, "Unable to release table reference\n");
 
2411
                
 
2412
        return err;
 
2413
}
 
2414
 
 
2415
void ha_pbxt::init_auto_increment(xtWord8 min_auto_inc)
 
2416
{
 
2417
        XTTableHPtr     tab;
 
2418
        xtWord8         nr = 0;
 
2419
        int                     err;
 
2420
 
 
2421
        /* Get the value of the auto-increment value by
 
2422
         * loading the highest value from the index...
 
2423
         */
 
2424
        tab = pb_open_tab->ot_table;
 
2425
 
 
2426
        /* Cannot do this if the index version is bad! */
 
2427
        if (tab->tab_dic.dic_disable_index)
 
2428
                return;
 
2429
 
 
2430
        xt_spinlock_lock(&tab->tab_ainc_lock);
 
2431
        if (table->found_next_number_field && !tab->tab_auto_inc) {
 
2432
                Field           *tmp_fie = table->next_number_field;
 
2433
                THD                     *tmp_thd = table->in_use;
 
2434
                xtBool          xn_started = FALSE;
 
2435
                XTThreadPtr     self = pb_open_tab->ot_thread;
 
2436
 
 
2437
//#ifndef DRIZZLED
 
2438
                /*
 
2439
                 * A table may be opened by a thread with a running
 
2440
                 * transaction!
 
2441
                 * Since get_auto_increment() does not do an update,
 
2442
                 * it should be OK to use the transaction we already
 
2443
                 * have to get the next auto-increment value.
 
2444
                 */
 
2445
                if (!self->st_xact_data) {
 
2446
                        self->st_xact_mode = XT_XACT_REPEATABLE_READ;
 
2447
                        self->st_ignore_fkeys = FALSE;
 
2448
                        self->st_auto_commit = TRUE;
 
2449
                        self->st_table_trans = FALSE;
 
2450
                        self->st_abort_trans = FALSE;
 
2451
                        self->st_stat_ended = FALSE;
 
2452
                        self->st_stat_trans = FALSE;
 
2453
                        self->st_is_update = NULL;
 
2454
                        if (!xt_xn_begin(self)) {
 
2455
                                xt_spinlock_unlock(&tab->tab_ainc_lock);
 
2456
                                xt_throw(self);
 
2457
                        }
 
2458
                        xn_started = TRUE;
 
2459
                }
 
2460
//#endif
 
2461
                /* Setup the conditions for the next call! */
 
2462
                table->in_use = current_thd;
 
2463
                table->next_number_field = table->found_next_number_field;
 
2464
 
 
2465
                extra(HA_EXTRA_KEYREAD);
 
2466
                table->mark_columns_used_by_index_no_reset(TS(table)->next_number_index, table->read_set);
 
2467
                column_bitmaps_signal();
 
2468
                index_init(TS(table)->next_number_index, 0);
 
2469
                if (!TS(table)->next_number_key_offset) {
 
2470
                        // Autoincrement at key-start
 
2471
                        err = index_last(table->record[1]);
 
2472
                        if (!err && !table->next_number_field->is_null(TS(table)->rec_buff_length)) {
 
2473
                                /* {PRE-INC} */
 
2474
                                nr = (xtWord8) table->next_number_field->val_int_offset(TS(table)->rec_buff_length);
 
2475
                        }
 
2476
                }
 
2477
                else {
 
2478
                        /* Do an index scan to find the largest value! */
 
2479
                        /* The standard method will not work because it forces
 
2480
                         * us to lock that table!
 
2481
                         */
 
2482
                        xtWord8 val;
 
2483
 
 
2484
                        err = index_first(table->record[1]);
 
2485
                        while (!err) {
 
2486
                                /* {PRE-INC} */
 
2487
                                val = (xtWord8) table->next_number_field->val_int_offset(TS(table)->rec_buff_length);
 
2488
                                if (val > nr)
 
2489
                                        nr = val;
 
2490
                                err = index_next(table->record[1]);
 
2491
                        }
 
2492
                }
 
2493
 
 
2494
                index_end();
 
2495
                extra(HA_EXTRA_NO_KEYREAD);
 
2496
 
 
2497
                /* {PRE-INC}
 
2498
                 * I have changed this from post increment to pre-increment!
 
2499
                 * The reason is:
 
2500
                 * When using post increment we are not able to return
 
2501
                 * the last valid value in the range.
 
2502
                 *
 
2503
                 * Here the test example:
 
2504
                 *
 
2505
                 * drop table if exists t1;
 
2506
                 * create table t1 (i tinyint unsigned not null auto_increment primary key) engine=pbxt;
 
2507
                 * insert into t1 set i = 254;
 
2508
                 * insert into t1 set i = null;
 
2509
                 *
 
2510
                 * With post-increment, this last insert fails because on post increment
 
2511
                 * the value overflows!
 
2512
                 *
 
2513
                 * Pre-increment means we store the current max, and increment
 
2514
                 * before returning the next value.
 
2515
                 *
 
2516
                 * This will work in this situation.
 
2517
                 */
 
2518
                tab->tab_auto_inc = nr;
 
2519
                if (tab->tab_auto_inc < tab->tab_dic.dic_min_auto_inc)
 
2520
                        tab->tab_auto_inc = tab->tab_dic.dic_min_auto_inc-1;
 
2521
                if (tab->tab_auto_inc < min_auto_inc)
 
2522
                        tab->tab_auto_inc = min_auto_inc-1;
 
2523
 
 
2524
                /* Restore the changed values: */
 
2525
                table->next_number_field = tmp_fie;
 
2526
                table->in_use = tmp_thd;
 
2527
 
 
2528
                if (xn_started) {
 
2529
                        XT_PRINT0(self, "xt_xn_commit in init_auto_increment\n");
 
2530
                        xt_xn_commit(self);
 
2531
                }
 
2532
        }
 
2533
        xt_spinlock_unlock(&tab->tab_ainc_lock);
 
2534
}
 
2535
 
 
2536
void ha_pbxt::get_auto_increment(MX_ULONGLONG_T offset, MX_ULONGLONG_T increment,
 
2537
                                 MX_ULONGLONG_T XT_UNUSED(nb_desired_values),
 
2538
                                 MX_ULONGLONG_T *first_value,
 
2539
                                 MX_ULONGLONG_T *nb_reserved_values)
 
2540
{
 
2541
        register XTTableHPtr    tab;
 
2542
        MX_ULONGLONG_T                  nr, nr_less_inc;
 
2543
 
 
2544
        ASSERT_NS(pb_ex_in_use);
 
2545
 
 
2546
        tab = pb_open_tab->ot_table;
 
2547
 
 
2548
        /* {PRE-INC}
 
2549
         * Assume that nr contains the last value returned!
 
2550
         * We will increment and then return the value.
 
2551
         */
 
2552
        xt_spinlock_lock(&tab->tab_ainc_lock);
 
2553
        nr = (MX_ULONGLONG_T) tab->tab_auto_inc;
 
2554
        nr_less_inc = nr;
 
2555
        if (nr < offset)
 
2556
                nr = offset;
 
2557
        else if (increment > 1 && ((nr - offset) % increment) != 0)
 
2558
                nr += increment - ((nr - offset) % increment);
 
2559
        else
 
2560
                nr += increment;
 
2561
        if (table->next_number_field->cmp((const unsigned char *)&nr_less_inc, (const unsigned char *)&nr) < 0)
 
2562
                tab->tab_auto_inc = (xtWord8) (nr);
 
2563
        else
 
2564
                nr = ~0;        /* indicate error to the caller */
 
2565
        xt_spinlock_unlock(&tab->tab_ainc_lock);
 
2566
 
 
2567
        *first_value = nr;
 
2568
        *nb_reserved_values = 1;
 
2569
}
 
2570
 
 
2571
/* GOTCHA: We need to use signed value here because of the test
 
2572
 * (from auto_increment.test):
 
2573
 * create table t1 (a int not null auto_increment primary key);
 
2574
 * insert into t1 values (NULL);
 
2575
 * insert into t1 values (-1);
 
2576
 * insert into t1 values (NULL);
 
2577
 */
 
2578
xtPublic void ha_set_auto_increment(XTOpenTablePtr ot, Field *nr)
 
2579
{
 
2580
        register XTTableHPtr    tab;
 
2581
        MX_ULONGLONG_T                  nr_int_val;
 
2582
        
 
2583
        nr_int_val = nr->val_int();
 
2584
        tab = ot->ot_table;
 
2585
 
 
2586
        if (nr->cmp((const unsigned char *)&tab->tab_auto_inc) > 0) {
 
2587
                xt_spinlock_lock(&tab->tab_ainc_lock);
 
2588
 
 
2589
                if (nr->cmp((const unsigned char *)&tab->tab_auto_inc) > 0) {
 
2590
                        /* {PRE-INC}
 
2591
                         * We increment later, so just set the value!
 
2592
                        MX_ULONGLONG_T nr_int_val_plus_one = nr_int_val + 1;
 
2593
                        if (nr->cmp((const unsigned char *)&nr_int_val_plus_one) < 0)
 
2594
                                tab->tab_auto_inc = nr_int_val_plus_one;
 
2595
                        else
 
2596
                         */
 
2597
                        tab->tab_auto_inc = nr_int_val;
 
2598
                }
 
2599
                xt_spinlock_unlock(&tab->tab_ainc_lock);
 
2600
        }
 
2601
 
 
2602
        if (xt_db_auto_increment_mode == 1) {
 
2603
                if (nr_int_val > (MX_ULONGLONG_T) tab->tab_dic.dic_min_auto_inc) {
 
2604
                        /* Do this every 100 calls: */
 
2605
#ifdef DEBUG
 
2606
                        tab->tab_dic.dic_min_auto_inc = nr_int_val + 5;
 
2607
#else
 
2608
                        tab->tab_dic.dic_min_auto_inc = nr_int_val + 100;
 
2609
#endif
 
2610
                        ot->ot_thread = xt_get_self();
 
2611
                        if (!xt_tab_write_min_auto_inc(ot))
 
2612
                                xt_log_and_clear_exception(ot->ot_thread);
 
2613
                }
 
2614
        }
 
2615
}
 
2616
 
 
2617
/*
 
2618
static void dump_buf(unsigned char *buf, int len)
 
2619
{
 
2620
        int i;
 
2621
        
 
2622
        for (i=0; i<len; i++) printf("%2c", buf[i] <= 127 ? buf[i] : '.');
 
2623
        printf("\n");
 
2624
        for (i=0; i<len; i++) printf("%02x", buf[i]);
 
2625
        printf("\n");
 
2626
}
 
2627
*/
 
2628
 
 
2629
/*
 
2630
 * write_row() inserts a row. No extra() hint is given currently if a bulk load
 
2631
 * is happeneding. buf() is a byte array of data. You can use the field
 
2632
 * information to extract the data from the native byte array type.
 
2633
 * Example of this would be:
 
2634
 * for (Field **field=table->field ; *field ; field++)
 
2635
 * {
 
2636
 *              ...
 
2637
 * }
 
2638
 
 
2639
 * See ha_tina.cc for an example of extracting all of the data as strings.
 
2640
 * ha_berekly.cc has an example of how to store it intact by "packing" it
 
2641
 * for ha_berkeley's own native storage type.
 
2642
 
 
2643
 * See the note for update_row() on auto_increments and timestamps. This
 
2644
 * case also applied to write_row().
 
2645
 
 
2646
 * Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
 
2647
 * sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
 
2648
 */
 
2649
int ha_pbxt::write_row(byte *buf)
 
2650
{
 
2651
        int err = 0;
 
2652
 
 
2653
        ASSERT_NS(pb_ex_in_use);
 
2654
 
 
2655
        XT_PRINT1(pb_open_tab->ot_thread, "write_row (%s)\n", pb_share->sh_table_path->ps_path);
 
2656
        XT_DISABLED_TRACE(("INSERT tx=%d val=%d\n", (int) pb_open_tab->ot_thread->st_xact_data->xd_start_xn_id, (int) XT_GET_DISK_4(&buf[1])));
 
2657
        //statistic_increment(ha_write_count,&LOCK_status);
 
2658
#ifdef PBMS_ENABLED
 
2659
        PBMSResultRec result;
 
2660
        err = pbms_write_row_blobs(table, buf, &result);
 
2661
        if (err) {
 
2662
                xt_logf(XT_NT_ERROR, "pbms_write_row_blobs() Error: %s", result.mr_message);
 
2663
                return err;
 
2664
        }
 
2665
#endif
 
2666
 
 
2667
        /* {START-STAT-HACK} previously position of start statement hack. */
 
2668
        xt_xlog_check_long_writer(pb_open_tab->ot_thread);
 
2669
 
 
2670
        if (pb_open_tab->ot_thread->st_import_stat) {
 
2671
                if (pb_import_row_count >= XT_IMPORT_ROW_COUNT) {
 
2672
                        /* Commit and restart the transaction. */
 
2673
                        XTThreadPtr thread = pb_open_tab->ot_thread;
 
2674
 
 
2675
                        XT_PRINT0(thread, "xt_xn_commit in write_row\n");
 
2676
                        if (!xt_xn_commit(thread)) {
 
2677
                                err = xt_ha_pbxt_thread_error_for_mysql(pb_mysql_thd, thread, pb_ignore_dup_key);
 
2678
                                return err;
 
2679
                        }
 
2680
                        XT_PRINT0(thread, "xt_xn_begin in write_row\n");
 
2681
                        if (!xt_xn_begin(thread)) {
 
2682
                                err = xt_ha_pbxt_thread_error_for_mysql(pb_mysql_thd, thread, pb_ignore_dup_key);
 
2683
                                return err;
 
2684
                        }
 
2685
                        pb_import_row_count = 0;
 
2686
                }
 
2687
                else
 
2688
                        pb_import_row_count++;
 
2689
        }
 
2690
 
 
2691
        if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
 
2692
                table->timestamp_field->set_time();
 
2693
 
 
2694
        if (table->next_number_field && buf == table->record[0]) {
 
2695
                int update_err = update_auto_increment();
 
2696
                if (update_err) {
 
2697
                        ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
2698
                        err = update_err;
 
2699
                        goto done;
 
2700
                }
 
2701
                ha_set_auto_increment(pb_open_tab, table->next_number_field);
 
2702
        }
 
2703
 
 
2704
        if (!xt_tab_new_record(pb_open_tab, (xtWord1 *) buf)) {
 
2705
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
2706
 
 
2707
                /*
 
2708
                 * This is needed to allow the same row to be updated multiple times in case of bulk REPLACE.
 
2709
                 * This happens during execution of LOAD DATA...REPLACE MySQL first tries to INSERT the row 
 
2710
                 * and if it gets dup-key error it tries UPDATE, so the same row can be overwriten multiple 
 
2711
                 * times within the same statement
 
2712
                 */
 
2713
                if (err == HA_ERR_FOUND_DUPP_KEY && pb_open_tab->ot_thread->st_is_update) {
 
2714
                        /* Pop the update stack: */
 
2715
                        //pb_open_tab->ot_thread->st_update_id++;
 
2716
                        XTOpenTablePtr curr = pb_open_tab->ot_thread->st_is_update;
 
2717
 
 
2718
                        pb_open_tab->ot_thread->st_is_update = curr->ot_prev_update;
 
2719
                        curr->ot_prev_update = NULL;
 
2720
                }
 
2721
        }
 
2722
 
 
2723
        done:
 
2724
#ifdef PBMS_ENABLED
 
2725
        pbms_completed(table, (err == 0));
 
2726
#endif
 
2727
        return err;
 
2728
}
 
2729
 
 
2730
#ifdef UNUSED_CODE
 
2731
static int equ_bin(const byte *a, const char *b)
 
2732
{
 
2733
        while (*a && *b) {
 
2734
                if (*a != *b)
 
2735
                        return 0;
 
2736
                a++;
 
2737
                b++;
 
2738
        }
 
2739
        return 1;
 
2740
}
 
2741
static void dump_bin(const byte *a_in, int offset, int len_in)
 
2742
{
 
2743
        const byte      *a = a_in;
 
2744
        int                     len = len_in;
 
2745
        
 
2746
        a += offset;
 
2747
        while (len > 0) {
 
2748
                xt_trace("%02X", (int) *a);
 
2749
                a++;
 
2750
                len--;
 
2751
        }
 
2752
        xt_trace("==");
 
2753
        a = a_in;
 
2754
        len = len_in;
 
2755
        a += offset;
 
2756
        while (len > 0) {
 
2757
                xt_trace("%c", (*a > 8 && *a < 127) ? *a : '.');
 
2758
                a++;
 
2759
                len--;
 
2760
        }
 
2761
        xt_trace("\n");
 
2762
}
 
2763
#endif
 
2764
 
 
2765
/*
 
2766
 * Yes, update_row() does what you expect, it updates a row. old_data will have
 
2767
 * the previous row record in it, while new_data will have the newest data in
 
2768
 * it. Keep in mind that the server can do updates based on ordering if an ORDER BY
 
2769
 * clause was used. Consecutive ordering is not guarenteed.
 
2770
 *
 
2771
 * Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
 
2772
 */
 
2773
int ha_pbxt::update_row(const byte * old_data, byte * new_data)
 
2774
{
 
2775
        int                                             err = 0;
 
2776
        register XTThreadPtr    self = pb_open_tab->ot_thread;
 
2777
 
 
2778
        ASSERT_NS(pb_ex_in_use);
 
2779
 
 
2780
        XT_PRINT1(self, "update_row (%s)\n", pb_share->sh_table_path->ps_path);
 
2781
        XT_DISABLED_TRACE(("UPDATE tx=%d val=%d\n", (int) self->st_xact_data->xd_start_xn_id, (int) XT_GET_DISK_4(&new_data[1])));
 
2782
        //statistic_increment(ha_update_count,&LOCK_status);
 
2783
        /* {START-STAT-HACK} previously position of start statement hack. */
 
2784
        xt_xlog_check_long_writer(self);
 
2785
 
 
2786
        /* {UPDATE-STACK} */
 
2787
        if (self->st_is_update != pb_open_tab) {
 
2788
                /* Push the update stack: */
 
2789
                pb_open_tab->ot_prev_update = self->st_is_update;
 
2790
                self->st_is_update = pb_open_tab;
 
2791
                pb_open_tab->ot_update_id++;
 
2792
        }
 
2793
 
 
2794
        if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
 
2795
                table->timestamp_field->set_time();
 
2796
 
 
2797
#ifdef PBMS_ENABLED
 
2798
        PBMSResultRec result;
 
2799
 
 
2800
        err = pbms_delete_row_blobs(table, old_data, &result);
 
2801
        if (err) {
 
2802
                xt_logf(XT_NT_ERROR, "update_row:pbms_delete_row_blobs() Error: %s", result.mr_message);
 
2803
                return err;
 
2804
        }
 
2805
        err = pbms_write_row_blobs(table, new_data, &result);
 
2806
        if (err) { 
 
2807
                xt_logf(XT_NT_ERROR, "update_row:pbms_write_row_blobs() Error: %s", result.mr_message);
 
2808
                goto pbms_done;
 
2809
        }
 
2810
#endif
 
2811
 
 
2812
        /* GOTCHA: We need to check the auto-increment value on update
 
2813
         * because of the following test (which fails for InnoDB) -
 
2814
         * auto_increment.test:
 
2815
         * create table t1 (a int not null auto_increment primary key, val int);
 
2816
         * insert into t1 (val) values (1);
 
2817
         * update t1 set a=2 where a=1;
 
2818
         * insert into t1 (val) values (1);
 
2819
         */
 
2820
        if (table->found_next_number_field && new_data == table->record[0]) {
 
2821
                MX_LONGLONG_T   nr;
 
2822
                my_bitmap_map   *old_map;
 
2823
 
 
2824
                old_map = mx_tmp_use_all_columns(table, table->read_set);
 
2825
                nr = table->found_next_number_field->val_int();
 
2826
                ha_set_auto_increment(pb_open_tab, table->found_next_number_field);
 
2827
                mx_tmp_restore_column_map(table, old_map);
 
2828
        }
 
2829
 
 
2830
        if (!xt_tab_update_record(pb_open_tab, (xtWord1 *) old_data, (xtWord1 *) new_data))
 
2831
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
2832
 
 
2833
        pb_open_tab->ot_table->tab_locks.xt_remove_temp_lock(pb_open_tab, TRUE);
 
2834
        
 
2835
#ifdef PBMS_ENABLED
 
2836
        pbms_done:
 
2837
        pbms_completed(table, (err == 0));
 
2838
#endif
 
2839
 
 
2840
        return err;
 
2841
}
 
2842
 
 
2843
/*
 
2844
 * This will delete a row. buf will contain a copy of the row to be deleted.
 
2845
 * The server will call this right after the current row has been called (from
 
2846
 * either a previous rnd_next() or index call).
 
2847
 *
 
2848
 * Called in sql_acl.cc and sql_udf.cc to manage internal table information.
 
2849
 * Called in sql_delete.cc, sql_insert.cc, and sql_select.cc. In sql_select it is
 
2850
 * used for removing duplicates while in insert it is used for REPLACE calls.
 
2851
*/
 
2852
int ha_pbxt::delete_row(const byte * buf)
 
2853
{
 
2854
        int err = 0;
 
2855
 
 
2856
        ASSERT_NS(pb_ex_in_use);
 
2857
 
 
2858
        XT_PRINT1(pb_open_tab->ot_thread, "delete_row (%s)\n", pb_share->sh_table_path->ps_path);
 
2859
        XT_DISABLED_TRACE(("DELETE tx=%d val=%d\n", (int) pb_open_tab->ot_thread->st_xact_data->xd_start_xn_id, (int) XT_GET_DISK_4(&buf[1])));
 
2860
        //statistic_increment(ha_delete_count,&LOCK_status);
 
2861
 
 
2862
#ifdef PBMS_ENABLED
 
2863
        PBMSResultRec result;
 
2864
 
 
2865
        err = pbms_delete_row_blobs(table, buf, &result);
 
2866
        if (err) {
 
2867
                xt_logf(XT_NT_ERROR, "pbms_delete_row_blobs() Error: %s", result.mr_message);
 
2868
                return err;
 
2869
        }
 
2870
#endif
 
2871
        /* {START-STAT-HACK} previously position of start statement hack. */
 
2872
        xt_xlog_check_long_writer(pb_open_tab->ot_thread);
 
2873
 
 
2874
        if (!xt_tab_delete_record(pb_open_tab, (xtWord1 *) buf))
 
2875
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
2876
 
 
2877
        pb_open_tab->ot_table->tab_locks.xt_remove_temp_lock(pb_open_tab, TRUE);
 
2878
 
 
2879
#ifdef PBMS_ENABLED
 
2880
        pbms_completed(table, (err == 0));
 
2881
#endif
 
2882
        return err;
 
2883
}
 
2884
 
 
2885
/*
 
2886
 * -----------------------------------------------------------------------
 
2887
 * INDEX METHODS
 
2888
 */
 
2889
 
 
2890
/*
 
2891
 * This looks like a hack, but actually, it is OK.
 
2892
 * It depends on the setup done by the super-class. It involves an extra
 
2893
 * range check that we need to do if a "new" record is returned during
 
2894
 * an index scan.
 
2895
 *
 
2896
 * A new record is returned if a row is updated (by another transaction)
 
2897
 * during the index scan. If an update is detected, then the scan stops
 
2898
 * and waits for the transaction to end.
 
2899
 *
 
2900
 * If the transaction commits, then the updated row is returned instead
 
2901
 * of the row it would have returned when doing a consistant read
 
2902
 * (repeatable read).
 
2903
 *
 
2904
 * These new records can appear out of index order, and may not even
 
2905
 * belong to the index range that we are concerned with.
 
2906
 *
 
2907
 * Notice that there is not check for the start of the range. It appears
 
2908
 * that this is not necessary, MySQL seems to have no problem ignoring
 
2909
 * such values.
 
2910
 *
 
2911
 * A number of test have been given below which demonstrate the use
 
2912
 * of the function.
 
2913
 *
 
2914
 * They also demonstrate the ORDER BY problem described here: [(11)].
 
2915
 *
 
2916
 * DROP TABLE IF EXISTS test_tab, test_tab_1, test_tab_2;
 
2917
 * CREATE TABLE test_tab (ID int primary key, Value int, Name varchar(20), index(Value, Name)) ENGINE=pbxt;
 
2918
 * INSERT test_tab values(1, 1, 'A');
 
2919
 * INSERT test_tab values(2, 1, 'B');
 
2920
 * INSERT test_tab values(3, 1, 'C');
 
2921
 * INSERT test_tab values(4, 2, 'D');
 
2922
 * INSERT test_tab values(5, 2, 'E');
 
2923
 * INSERT test_tab values(6, 2, 'F');
 
2924
 * INSERT test_tab values(7, 2, 'G');
 
2925
 * 
 
2926
 * select * from test_tab where value = 1 order by value, name for update;
 
2927
 * 
 
2928
 * -- Test: 1
 
2929
 * -- C1
 
2930
 * begin;
 
2931
 * select * from test_tab where id = 5 for update;
 
2932
 * 
 
2933
 * -- C2
 
2934
 * begin;
 
2935
 * select * from test_tab where value = 2 order by value, name for update;
 
2936
 * 
 
2937
 * -- C1
 
2938
 * update test_tab set value = 3 where id = 6;
 
2939
 * commit;
 
2940
 * 
 
2941
 * -- Test: 2
 
2942
 * -- C1
 
2943
 * begin;
 
2944
 * select * from test_tab where id = 5 for update;
 
2945
 * 
 
2946
 * -- C2
 
2947
 * begin;
 
2948
 * select * from test_tab where value >= 2 order by value, name for update;
 
2949
 * 
 
2950
 * -- C1
 
2951
 * update test_tab set value = 3 where id = 6;
 
2952
 * commit;
 
2953
 * 
 
2954
 * -- Test: 3
 
2955
 * -- C1
 
2956
 * begin;
 
2957
 * select * from test_tab where id = 5 for update;
 
2958
 * 
 
2959
 * -- C2
 
2960
 * begin;
 
2961
 * select * from test_tab where value = 2 order by value, name for update;
 
2962
 * 
 
2963
 * -- C1
 
2964
 * update test_tab set value = 1 where id = 6;
 
2965
 * commit;
 
2966
 */
 
2967
 
 
2968
int ha_pbxt::xt_index_in_range(register XTOpenTablePtr XT_UNUSED(ot), register XTIndexPtr ind,
 
2969
        register XTIdxSearchKeyPtr search_key, xtWord1 *buf)
 
2970
{
 
2971
        /* If search key is given, this means we want an exact match. */
 
2972
        if (search_key) {
 
2973
                xtWord1 key_buf[XT_INDEX_MAX_KEY_SIZE];
 
2974
 
 
2975
                myxt_create_key_from_row(ind, key_buf, buf, NULL);
 
2976
                search_key->sk_on_key = myxt_compare_key(ind, search_key->sk_key_value.sv_flags, search_key->sk_key_value.sv_length,
 
2977
                        search_key->sk_key_value.sv_key, key_buf) == 0;
 
2978
                return search_key->sk_on_key;
 
2979
        }
 
2980
 
 
2981
        /* Otherwise, check the end of the range. */
 
2982
        if (end_range)
 
2983
                return compare_key(end_range) <= 0;
 
2984
        return 1;
 
2985
}
 
2986
 
 
2987
int ha_pbxt::xt_index_next_read(register XTOpenTablePtr ot, register XTIndexPtr ind, xtBool key_only,
 
2988
        register XTIdxSearchKeyPtr search_key, byte *buf)
 
2989
{
 
2990
        xt_xlog_check_long_writer(ot->ot_thread);
 
2991
 
 
2992
        if (key_only) {
 
2993
                /* We only need to read the data from the key: */
 
2994
                while (ot->ot_curr_rec_id) {
 
2995
                        if (search_key && !search_key->sk_on_key)
 
2996
                                break;
 
2997
 
 
2998
                        switch (xt_tab_visible(ot)) {
 
2999
                                case FALSE:
 
3000
                                        if (xt_idx_next(ot, ind, search_key))
 
3001
                                                break;
 
3002
                                case XT_ERR:
 
3003
                                        goto failed;
 
3004
                                case XT_NEW:
 
3005
                                        if (!xt_idx_read(ot, ind, (xtWord1 *) buf))
 
3006
                                                goto failed;
 
3007
                                        if (xt_index_in_range(ot, ind, search_key, buf)) {
 
3008
                                                return 0;
 
3009
                                        }
 
3010
                                        if (!xt_idx_next(ot, ind, search_key))
 
3011
                                                goto failed;
 
3012
                                        break;
 
3013
                                case XT_RETRY:
 
3014
                                        /* We cannot start from the beginning again, if we have
 
3015
                                         * already output rows!
 
3016
                                         * And we need the orginal search key.
 
3017
                                         *
 
3018
                                         * The case in which this occurs is:
 
3019
                                         *
 
3020
                                         * T1: UPDATE tbl_file SET GlobalID = 'DBCD5C4514210200825501089884844_6M' WHERE ID = 39
 
3021
                                         * Locks a particular row.
 
3022
                                         *
 
3023
                                         * T2: SELECT ID,Flags FROM tbl_file WHERE SpaceID = 1 AND Path = '/zi/America/' AND 
 
3024
                                         * Name = 'Cuiaba' AND Flags IN ( 0,1,4,5 ) FOR UPDATE
 
3025
                                         * scans the index and stops on the lock (of the before image) above.
 
3026
                                         *
 
3027
                                         * T1 quits, the sweeper deletes the record updated by T1?!
 
3028
                                         * BUG: Cleanup should wait until T2 is complete!
 
3029
                                         *
 
3030
                                         * T2 continues, and returns XT_RETRY.
 
3031
                                         *
 
3032
                                         * At this stage T2 has already returned some rows, so it may not retry from the
 
3033
                                         * start. Instead it tries to locate the last record it tried to lock.
 
3034
                                         * This record is gone (or not visible), so it finds the next one.
 
3035
                                         *
 
3036
                                         * POTENTIAL BUG: If cleanup does not wait until T2 is complete, then
 
3037
                                         * I may miss the update record, if it is moved before the index scan
 
3038
                                         * position.
 
3039
                                         */
 
3040
                                        if (!pb_ind_row_count && search_key) {
 
3041
                                                if (!xt_idx_search(pb_open_tab, ind, search_key))
 
3042
                                                        return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3043
                                        }
 
3044
                                        else {
 
3045
                                                if (!xt_idx_research(pb_open_tab, ind))
 
3046
                                                        goto failed;
 
3047
                                        }
 
3048
                                        break;
 
3049
                                default:
 
3050
                                        if (!xt_idx_read(ot, ind, (xtWord1 *) buf))
 
3051
                                                goto failed;
 
3052
                                        return 0;
 
3053
                        }
 
3054
                }
 
3055
        }
 
3056
        else {
 
3057
                while (ot->ot_curr_rec_id) {
 
3058
                        if (search_key && !search_key->sk_on_key)
 
3059
                                break;
 
3060
 
 
3061
                        switch (xt_tab_read_record(ot, (xtWord1 *) buf)) {
 
3062
                                case FALSE:
 
3063
                                        XT_DISABLED_TRACE(("not visi tx=%d rec=%d\n", (int) ot->ot_thread->st_xact_data->xd_start_xn_id, (int) ot->ot_curr_rec_id));
 
3064
                                        if (xt_idx_next(ot, ind, search_key))
 
3065
                                                break;
 
3066
                                case XT_ERR:
 
3067
                                        goto failed;
 
3068
                                case XT_NEW:
 
3069
                                        if (xt_index_in_range(ot, ind, search_key, buf))
 
3070
                                                return 0;
 
3071
                                        if (!xt_idx_next(ot, ind, search_key))
 
3072
                                                goto failed;
 
3073
                                        break;
 
3074
                                case XT_RETRY:
 
3075
                                        if (!pb_ind_row_count && search_key) {
 
3076
                                                if (!xt_idx_search(pb_open_tab, ind, search_key))
 
3077
                                                        return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3078
                                        }
 
3079
                                        else {
 
3080
                                                if (!xt_idx_research(pb_open_tab, ind))
 
3081
                                                        goto failed;
 
3082
                                        }
 
3083
                                        break;
 
3084
                                default:
 
3085
                                        XT_DISABLED_TRACE(("visible tx=%d rec=%d\n", (int) ot->ot_thread->st_xact_data->xd_start_xn_id, (int) ot->ot_curr_rec_id));
 
3086
                                        return 0;
 
3087
                        }
 
3088
                }
 
3089
        }
 
3090
        return HA_ERR_END_OF_FILE;
 
3091
 
 
3092
        failed:
 
3093
        return ha_log_pbxt_thread_error_for_mysql(FALSE);
 
3094
}
 
3095
 
 
3096
int ha_pbxt::xt_index_prev_read(XTOpenTablePtr ot, XTIndexPtr ind, xtBool key_only,
 
3097
        register XTIdxSearchKeyPtr search_key, byte *buf)
 
3098
{
 
3099
        if (key_only) {
 
3100
                /* We only need to read the data from the key: */
 
3101
                while (ot->ot_curr_rec_id) {
 
3102
                        if (search_key && !search_key->sk_on_key)
 
3103
                                break;
 
3104
 
 
3105
                        switch (xt_tab_visible(ot)) {
 
3106
                                case FALSE:
 
3107
                                        if (xt_idx_prev(ot, ind, search_key))
 
3108
                                                break;
 
3109
                                case XT_ERR:
 
3110
                                        goto failed;
 
3111
                                case XT_NEW:
 
3112
                                        if (!xt_idx_read(ot, ind, (xtWord1 *) buf))
 
3113
                                                goto failed;
 
3114
                                        if (xt_index_in_range(ot, ind, search_key, buf))
 
3115
                                                return 0;
 
3116
                                        if (!xt_idx_next(ot, ind, search_key))
 
3117
                                                goto failed;
 
3118
                                        break;
 
3119
                                case XT_RETRY:
 
3120
                                        if (!pb_ind_row_count && search_key) {
 
3121
                                                if (!xt_idx_search_prev(pb_open_tab, ind, search_key))
 
3122
                                                        return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3123
                                        }
 
3124
                                        else {
 
3125
                                                if (!xt_idx_research(pb_open_tab, ind))
 
3126
                                                        goto failed;
 
3127
                                        }
 
3128
                                        break;
 
3129
                                default:
 
3130
                                        if (!xt_idx_read(ot, ind, (xtWord1 *) buf))
 
3131
                                                goto failed;
 
3132
                                        return 0;
 
3133
                        }
 
3134
                }
 
3135
        }
 
3136
        else {
 
3137
                /* We need to read the entire record: */
 
3138
                while (ot->ot_curr_rec_id) {
 
3139
                        if (search_key && !search_key->sk_on_key)
 
3140
                                break;
 
3141
 
 
3142
                        switch (xt_tab_read_record(ot, (xtWord1 *) buf)) {
 
3143
                                case FALSE:
 
3144
                                        if (xt_idx_prev(ot, ind, search_key))
 
3145
                                                break;
 
3146
                                case XT_ERR:
 
3147
                                        goto failed;
 
3148
                                case XT_NEW:
 
3149
                                        if (xt_index_in_range(ot, ind, search_key, buf))
 
3150
                                                return 0;
 
3151
                                        if (!xt_idx_next(ot, ind, search_key))
 
3152
                                                goto failed;
 
3153
                                        break;
 
3154
                                case XT_RETRY:
 
3155
                                        if (!pb_ind_row_count && search_key) {
 
3156
                                                if (!xt_idx_search_prev(pb_open_tab, ind, search_key))
 
3157
                                                        return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3158
                                        }
 
3159
                                        else {
 
3160
                                                if (!xt_idx_research(pb_open_tab, ind))
 
3161
                                                        goto failed;
 
3162
                                        }
 
3163
                                        break;
 
3164
                                default:
 
3165
                                        return 0;
 
3166
                        }
 
3167
                }
 
3168
        }
 
3169
        return HA_ERR_END_OF_FILE;
 
3170
 
 
3171
        failed:
 
3172
        return ha_log_pbxt_thread_error_for_mysql(FALSE);
 
3173
}
 
3174
 
 
3175
int ha_pbxt::index_init(uint idx, bool XT_UNUSED(sorted))
 
3176
{
 
3177
        XTIndexPtr      ind;
 
3178
        XTThreadPtr     thread = pb_open_tab->ot_thread;
 
3179
 
 
3180
        /* select count(*) from smalltab_PBXT;
 
3181
         * ignores the error below, and continues to
 
3182
         * call index_first!
 
3183
         */
 
3184
        active_index = idx;
 
3185
 
 
3186
        if (pb_open_tab->ot_table->tab_dic.dic_disable_index) {
 
3187
                active_index = MAX_KEY;
 
3188
                xt_tab_set_index_error(pb_open_tab->ot_table);
 
3189
                return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3190
        }
 
3191
 
 
3192
        /* The number of columns required: */
 
3193
        if (pb_open_tab->ot_is_modify) {
 
3194
 
 
3195
                pb_open_tab->ot_cols_req = table->read_set->MX_BIT_SIZE();
 
3196
#ifdef XT_PRINT_INDEX_OPT
 
3197
                ind = (XTIndexPtr) pb_share->sh_dic_keys[idx];
 
3198
 
 
3199
                printf("index_init %s index %d cols req=%d/%d read_bits=%X write_bits=%X index_bits=%X\n", pb_open_tab->ot_table->tab_name->ps_path, (int) idx, pb_open_tab->ot_cols_req, pb_open_tab->ot_cols_req, (int) *table->read_set->bitmap, (int) *table->write_set->bitmap, (int) *ind->mi_col_map.bitmap);
 
3200
#endif
 
3201
                /* {START-STAT-HACK} previously position of start statement hack,
 
3202
                 * previous comment to code below: */
 
3203
                /* Start a statement based transaction as soon
 
3204
                 * as a read is done for a modify type statement!
 
3205
                 * Previously, this was done too late!
 
3206
                 */
 
3207
        }
 
3208
        else {
 
3209
                pb_open_tab->ot_cols_req = ha_get_max_bit(table->read_set);
 
3210
 
 
3211
                /* Check for index coverage!
 
3212
                 *
 
3213
                 * Given the following table:
 
3214
                 *
 
3215
                 * CREATE TABLE `customer` (
 
3216
                 * `c_id` int(11) NOT NULL DEFAULT '0',
 
3217
                 * `c_d_id` int(11) NOT NULL DEFAULT '0',
 
3218
                 * `c_w_id` int(11) NOT NULL DEFAULT '0',
 
3219
                 * `c_first` varchar(16) DEFAULT NULL,
 
3220
                 * `c_middle` char(2) DEFAULT NULL,
 
3221
                 * `c_last` varchar(16) DEFAULT NULL,
 
3222
                 * `c_street_1` varchar(20) DEFAULT NULL,
 
3223
                 * `c_street_2` varchar(20) DEFAULT NULL,
 
3224
                 * `c_city` varchar(20) DEFAULT NULL,
 
3225
                 * `c_state` char(2) DEFAULT NULL,
 
3226
                 * `c_zip` varchar(9) DEFAULT NULL,
 
3227
                 * `c_phone` varchar(16) DEFAULT NULL,
 
3228
                 * `c_since` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 
3229
                 * `c_credit` char(2) DEFAULT NULL,
 
3230
                 * `c_credit_lim` decimal(24,12) DEFAULT NULL,
 
3231
                 * `c_discount` double DEFAULT NULL,
 
3232
                 * `c_balance` decimal(24,12) DEFAULT NULL,
 
3233
                 * `c_ytd_payment` decimal(24,12) DEFAULT NULL,
 
3234
                 * `c_payment_cnt` double DEFAULT NULL,
 
3235
                 * `c_delivery_cnt` double DEFAULT NULL,
 
3236
                 * `c_data` text,
 
3237
                 * PRIMARY KEY (`c_w_id`,`c_d_id`,`c_id`),
 
3238
                 * KEY `c_w_id` (`c_w_id`,`c_d_id`,`c_last`,`c_first`,`c_id`)
 
3239
                 * ) ENGINE=PBXT;
 
3240
                 *
 
3241
                 * MySQL does not recognize index coverage on the followin select:
 
3242
                 *
 
3243
                 * SELECT c_id FROM customer WHERE c_w_id = 3 AND c_d_id = 8 AND 
 
3244
                 * c_last = 'EINGATIONANTI' ORDER BY c_first ASC LIMIT 1;
 
3245
                 *
 
3246
                 * TODO: Find out why this is necessary, MyISAM does not
 
3247
                 * seem to have this problem!
 
3248
                 */
 
3249
                ind = (XTIndexPtr) pb_share->sh_dic_keys[idx];
 
3250
                if (MX_BIT_IS_SUBSET(table->read_set, &ind->mi_col_map))
 
3251
                        pb_key_read = TRUE;
 
3252
#ifdef XT_PRINT_INDEX_OPT
 
3253
                printf("index_init %s index %d cols req=%d/%d read_bits=%X write_bits=%X index_bits=%X converage=%d\n", pb_open_tab->ot_table->tab_name->ps_path, (int) idx, pb_open_tab->ot_cols_req, table->read_set->MX_BIT_SIZE(), (int) *table->read_set->bitmap, (int) *table->write_set->bitmap, (int) *ind->mi_col_map.bitmap, (int) (MX_BIT_IS_SUBSET(table->read_set, &ind->mi_col_map) != 0));
 
3254
#endif
 
3255
        }
 
3256
        
 
3257
        xt_xlog_check_long_writer(thread);
 
3258
 
 
3259
        pb_open_tab->ot_thread->st_statistics.st_scan_index++;
 
3260
        return 0;
 
3261
}
 
3262
 
 
3263
int ha_pbxt::index_end()
 
3264
{
 
3265
        int err = 0;
 
3266
 
 
3267
        XT_TRACE_METHOD();
 
3268
 
 
3269
        XTThreadPtr thread = pb_open_tab->ot_thread;
 
3270
 
 
3271
        /*
 
3272
         * the assertion below is not always held, because the sometimes handler is unlocked
 
3273
         * before this function is called
 
3274
         */
 
3275
        /*ASSERT_NS(pb_ex_in_use);*/
 
3276
 
 
3277
        if (pb_open_tab->ot_ind_rhandle) {
 
3278
                xt_ind_release_handle(pb_open_tab->ot_ind_rhandle, FALSE, thread);
 
3279
                pb_open_tab->ot_ind_rhandle = NULL;
 
3280
        }
 
3281
 
 
3282
        /*
 
3283
         * make permanent the lock for the last scanned row
 
3284
         */
 
3285
        if (pb_open_tab)
 
3286
                pb_open_tab->ot_table->tab_locks.xt_make_lock_permanent(pb_open_tab, &thread->st_lock_list);
 
3287
 
 
3288
        xt_xlog_check_long_writer(thread);
 
3289
 
 
3290
        active_index = MAX_KEY;
 
3291
        XT_RETURN(err);
 
3292
}
 
3293
 
 
3294
#ifdef XT_TRACK_RETURNED_ROWS
 
3295
void ha_start_scan(XTOpenTablePtr ot, u_int index)
 
3296
{
 
3297
        xt_ttracef(ot->ot_thread, "SCAN %d:%d\n", (int) ot->ot_table->tab_id, (int) index);
 
3298
        ot->ot_rows_ret_curr = 0;
 
3299
        for (u_int i=0; i<ot->ot_rows_ret_max; i++)
 
3300
                ot->ot_rows_returned[i] = 0;
 
3301
}
 
3302
 
 
3303
void ha_return_row(XTOpenTablePtr ot, u_int index)
 
3304
{
 
3305
        xt_ttracef(ot->ot_thread, "%d:%d ROW=%d:%d\n",
 
3306
                (int) ot->ot_table->tab_id, (int) index, (int) ot->ot_curr_row_id, (int) ot->ot_curr_rec_id);
 
3307
        ot->ot_rows_ret_curr++;
 
3308
        if (ot->ot_curr_row_id >= ot->ot_rows_ret_max) {
 
3309
                if (!xt_realloc_ns((void **) &ot->ot_rows_returned, (ot->ot_curr_row_id+1) * sizeof(xtRecordID)))
 
3310
                        ASSERT_NS(FALSE);
 
3311
                memset(&ot->ot_rows_returned[ot->ot_rows_ret_max], 0, (ot->ot_curr_row_id+1 - ot->ot_rows_ret_max) * sizeof(xtRecordID));
 
3312
                ot->ot_rows_ret_max = ot->ot_curr_row_id+1;
 
3313
        }
 
3314
        if (!ot->ot_curr_row_id || !ot->ot_curr_rec_id || ot->ot_rows_returned[ot->ot_curr_row_id]) {
 
3315
                char *sql = *thd_query(current_thd);
 
3316
 
 
3317
                xt_ttracef(ot->ot_thread, "DUP %d:%d %s\n",
 
3318
                        (int) ot->ot_table->tab_id, (int) index, *thd_query(current_thd));
 
3319
                xt_dump_trace();
 
3320
                printf("ERROR: row=%d rec=%d newr=%d, already returned!\n", (int) ot->ot_curr_row_id, (int) ot->ot_rows_returned[ot->ot_curr_row_id], (int) ot->ot_curr_rec_id);
 
3321
                printf("ERROR: %s\n", sql);
 
3322
#ifdef XT_WIN
 
3323
                FatalAppExit(0, "Debug Me!");
 
3324
#endif
 
3325
        }
 
3326
        else
 
3327
                ot->ot_rows_returned[ot->ot_curr_row_id] = ot->ot_curr_rec_id;
 
3328
}
 
3329
#endif
 
3330
 
 
3331
int ha_pbxt::index_read_xt(byte * buf, uint idx, const byte *key, uint key_len, enum ha_rkey_function find_flag)
 
3332
{
 
3333
        int                                     err = 0;
 
3334
        XTIndexPtr                      ind;
 
3335
        int                                     prefix = 0;
 
3336
        XTIdxSearchKeyRec       search_key;
 
3337
 
 
3338
        if (idx == MAX_KEY) {
 
3339
                err = HA_ERR_WRONG_INDEX;
 
3340
                goto done;
 
3341
        }
 
3342
#ifdef XT_TRACK_RETURNED_ROWS
 
3343
        ha_start_scan(pb_open_tab, idx);
 
3344
#endif
 
3345
 
 
3346
        /* This call starts a search on this handler! */
 
3347
        pb_ind_row_count = 0;
 
3348
 
 
3349
        ASSERT_NS(pb_ex_in_use);
 
3350
 
 
3351
        XT_PRINT1(pb_open_tab->ot_thread, "index_read_xt (%s)\n", pb_share->sh_table_path->ps_path);
 
3352
        XT_DISABLED_TRACE(("search tx=%d val=%d update=%d\n", (int) pb_open_tab->ot_thread->st_xact_data->xd_start_xn_id, (int) XT_GET_DISK_4(key), pb_modified));
 
3353
        ind = (XTIndexPtr) pb_share->sh_dic_keys[idx];
 
3354
 
 
3355
        switch (find_flag) {
 
3356
                case HA_READ_PREFIX_LAST:
 
3357
                case HA_READ_PREFIX_LAST_OR_PREV:
 
3358
                        prefix = SEARCH_PREFIX;
 
3359
                case HA_READ_BEFORE_KEY:
 
3360
                case HA_READ_KEY_OR_PREV: // I assume you want to be positioned on the last entry in the key duplicate list!! 
 
3361
                        xt_idx_prep_key(ind, &search_key, ((find_flag == HA_READ_BEFORE_KEY) ? 0 : XT_SEARCH_AFTER_KEY) | prefix, (xtWord1 *) key, (size_t) key_len);
 
3362
                        if (!xt_idx_search_prev(pb_open_tab, ind, &search_key))
 
3363
                                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3364
                        else
 
3365
                                err = xt_index_prev_read(pb_open_tab, ind, pb_key_read,
 
3366
                                        (find_flag == HA_READ_PREFIX_LAST) ? &search_key : NULL, buf);
 
3367
                        break;
 
3368
                case HA_READ_PREFIX:
 
3369
                        prefix = SEARCH_PREFIX;
 
3370
                case HA_READ_KEY_EXACT:
 
3371
                case HA_READ_KEY_OR_NEXT:
 
3372
                case HA_READ_AFTER_KEY:
 
3373
                default:
 
3374
                        xt_idx_prep_key(ind, &search_key, ((find_flag == HA_READ_AFTER_KEY) ? XT_SEARCH_AFTER_KEY : 0) | prefix, (xtWord1 *) key, key_len);
 
3375
                        if (!xt_idx_search(pb_open_tab, ind, &search_key))
 
3376
                                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3377
                        else {
 
3378
                                err = xt_index_next_read(pb_open_tab, ind, pb_key_read,
 
3379
                                        (find_flag == HA_READ_KEY_EXACT || find_flag == HA_READ_PREFIX) ? &search_key : NULL, buf);
 
3380
                                if (err == HA_ERR_END_OF_FILE && find_flag == HA_READ_AFTER_KEY)
 
3381
                                        err = HA_ERR_KEY_NOT_FOUND;                     
 
3382
                        }
 
3383
                        break;
 
3384
        }
 
3385
 
 
3386
        pb_ind_row_count++;
 
3387
#ifdef XT_TRACK_RETURNED_ROWS
 
3388
        if (!err)
 
3389
                ha_return_row(pb_open_tab, idx);
 
3390
#endif
 
3391
        XT_DISABLED_TRACE(("search tx=%d val=%d err=%d\n", (int) pb_open_tab->ot_thread->st_xact_data->xd_start_xn_id, (int) XT_GET_DISK_4(key), err));
 
3392
        done:
 
3393
        if (err)
 
3394
                table->status = STATUS_NOT_FOUND;
 
3395
        else {
 
3396
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3397
                table->status = 0;
 
3398
        }
 
3399
        return err;
 
3400
}
 
3401
 
 
3402
/*
 
3403
 * Positions an index cursor to the index specified in the handle. Fetches the
 
3404
 * row if available. If the key value is null, begin at the first key of the
 
3405
 * index.
 
3406
 */
 
3407
int ha_pbxt::index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag)
 
3408
{
 
3409
        //statistic_increment(ha_read_key_count,&LOCK_status);
 
3410
        return index_read_xt(buf, active_index, key, key_len, find_flag);
 
3411
}
 
3412
 
 
3413
int ha_pbxt::index_read_idx(byte * buf, uint idx, const byte *key, uint key_len, enum ha_rkey_function find_flag)
 
3414
{
 
3415
        //statistic_increment(ha_read_key_count,&LOCK_status);
 
3416
        return index_read_xt(buf, idx, key, key_len, find_flag);
 
3417
}
 
3418
 
 
3419
int ha_pbxt::index_read_last(byte * buf, const byte * key, uint key_len)
 
3420
{
 
3421
        //statistic_increment(ha_read_key_count,&LOCK_status);
 
3422
        return index_read_xt(buf, active_index, key, key_len, HA_READ_PREFIX_LAST);
 
3423
}
 
3424
 
 
3425
/*
 
3426
 * Used to read forward through the index.
 
3427
 */
 
3428
int ha_pbxt::index_next(byte * buf)
 
3429
{
 
3430
        int                     err = 0;
 
3431
        XTIndexPtr      ind;
 
3432
 
 
3433
        XT_TRACE_METHOD();
 
3434
        //statistic_increment(ha_read_next_count,&LOCK_status);
 
3435
        ASSERT_NS(pb_ex_in_use);
 
3436
 
 
3437
        if (active_index == MAX_KEY) {
 
3438
                err = HA_ERR_WRONG_INDEX;
 
3439
                goto done;
 
3440
        }
 
3441
        ind = (XTIndexPtr) pb_share->sh_dic_keys[active_index];
 
3442
 
 
3443
        if (!xt_idx_next(pb_open_tab, ind, NULL))
 
3444
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3445
        else
 
3446
                err = xt_index_next_read(pb_open_tab, ind, pb_key_read, NULL, buf);
 
3447
 
 
3448
        pb_ind_row_count++;
 
3449
#ifdef XT_TRACK_RETURNED_ROWS
 
3450
        if (!err)
 
3451
                ha_return_row(pb_open_tab, active_index);
 
3452
#endif
 
3453
        done:
 
3454
        if (err)
 
3455
                table->status = STATUS_NOT_FOUND;
 
3456
        else {
 
3457
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3458
                table->status = 0;
 
3459
        }
 
3460
        XT_RETURN(err);
 
3461
}
 
3462
 
 
3463
/*
 
3464
 * I have implemented this because there is currently a
 
3465
 * bug in handler::index_next_same().
 
3466
 *
 
3467
 * drop table if exists t1;
 
3468
 * CREATE TABLE t1 (a int, b int, primary key(a,b))
 
3469
 * PARTITION BY KEY(b,a) PARTITIONS 2;
 
3470
 * insert into t1 values (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
 
3471
 * select * from t1 where a = 4;
 
3472
 * 
 
3473
 */
 
3474
int ha_pbxt::index_next_same(byte * buf, const byte *key, uint length)
 
3475
{
 
3476
        int                                     err = 0;
 
3477
        XTIndexPtr                      ind;
 
3478
        XTIdxSearchKeyRec       search_key;
 
3479
 
 
3480
        XT_TRACE_METHOD();
 
3481
        //statistic_increment(ha_read_next_count,&LOCK_status);
 
3482
        ASSERT_NS(pb_ex_in_use);
 
3483
 
 
3484
        if (active_index == MAX_KEY) {
 
3485
                err = HA_ERR_WRONG_INDEX;
 
3486
                goto done;
 
3487
        }
 
3488
        ind = (XTIndexPtr) pb_share->sh_dic_keys[active_index];
 
3489
 
 
3490
        search_key.sk_key_value.sv_flags = HA_READ_KEY_EXACT;
 
3491
        search_key.sk_key_value.sv_rec_id = 0;
 
3492
        search_key.sk_key_value.sv_row_id = 0;
 
3493
        search_key.sk_key_value.sv_key = search_key.sk_key_buf;
 
3494
        search_key.sk_key_value.sv_length = myxt_create_key_from_key(ind, search_key.sk_key_buf, (xtWord1 *) key, (u_int) length);
 
3495
        search_key.sk_on_key = TRUE;
 
3496
 
 
3497
        if (!xt_idx_next(pb_open_tab, ind, &search_key))
 
3498
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3499
        else
 
3500
                err = xt_index_next_read(pb_open_tab, ind, pb_key_read, &search_key, buf);
 
3501
 
 
3502
        pb_ind_row_count++;
 
3503
#ifdef XT_TRACK_RETURNED_ROWS
 
3504
        if (!err)
 
3505
                ha_return_row(pb_open_tab, active_index);
 
3506
#endif
 
3507
        done:
 
3508
        if (err)
 
3509
                table->status = STATUS_NOT_FOUND;
 
3510
        else {
 
3511
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3512
                table->status = 0;
 
3513
        }
 
3514
        XT_RETURN(err);
 
3515
}
 
3516
 
 
3517
/*
 
3518
 * Used to read backwards through the index.
 
3519
 */
 
3520
int ha_pbxt::index_prev(byte * buf)
 
3521
{
 
3522
        int                     err = 0;
 
3523
        XTIndexPtr      ind;
 
3524
 
 
3525
        XT_TRACE_METHOD();
 
3526
        //statistic_increment(ha_read_prev_count,&LOCK_status);
 
3527
        ASSERT_NS(pb_ex_in_use);
 
3528
 
 
3529
        if (active_index == MAX_KEY) {
 
3530
                err = HA_ERR_WRONG_INDEX;
 
3531
                goto done;
 
3532
        }
 
3533
        ind = (XTIndexPtr) pb_share->sh_dic_keys[active_index];
 
3534
 
 
3535
        if (!xt_idx_prev(pb_open_tab, ind, NULL))
 
3536
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3537
        else
 
3538
                err = xt_index_prev_read(pb_open_tab, ind, pb_key_read, NULL, buf);
 
3539
 
 
3540
        pb_ind_row_count++;
 
3541
#ifdef XT_TRACK_RETURNED_ROWS
 
3542
        if (!err)
 
3543
                ha_return_row(pb_open_tab, active_index);
 
3544
#endif
 
3545
        done:
 
3546
        if (err)
 
3547
                table->status = STATUS_NOT_FOUND;
 
3548
        else {
 
3549
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3550
                table->status = 0;
 
3551
        }
 
3552
        XT_RETURN(err);
 
3553
}
 
3554
 
 
3555
/*
 
3556
 * index_first() asks for the first key in the index.
 
3557
 */
 
3558
int ha_pbxt::index_first(byte * buf)
 
3559
{
 
3560
        int                                     err = 0;
 
3561
        XTIndexPtr                      ind;
 
3562
        XTIdxSearchKeyRec       search_key;
 
3563
 
 
3564
        XT_TRACE_METHOD();
 
3565
        //statistic_increment(ha_read_first_count,&LOCK_status);
 
3566
        ASSERT_NS(pb_ex_in_use);
 
3567
 
 
3568
        /* This is required because MySQL ignores the error returned
 
3569
         * init init_index sometimes, for example:
 
3570
         *
 
3571
     * if (!table->file->inited)
 
3572
     *    table->file->ha_index_init(tab->index, tab->sorted);
 
3573
     *  if ((error=tab->table->file->index_first(tab->table->record[0])))
 
3574
         */
 
3575
        if (active_index == MAX_KEY) {
 
3576
                err = HA_ERR_WRONG_INDEX;
 
3577
                goto done;
 
3578
        }
 
3579
 
 
3580
#ifdef XT_TRACK_RETURNED_ROWS
 
3581
        ha_start_scan(pb_open_tab, active_index);
 
3582
#endif
 
3583
        pb_ind_row_count = 0;
 
3584
 
 
3585
        ind = (XTIndexPtr) pb_share->sh_dic_keys[active_index];
 
3586
 
 
3587
        xt_idx_prep_key(ind, &search_key, XT_SEARCH_FIRST_FLAG, NULL, 0);
 
3588
        if (!xt_idx_search(pb_open_tab, ind, &search_key))
 
3589
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3590
        else
 
3591
                err = xt_index_next_read(pb_open_tab, ind, pb_key_read, NULL, buf);
 
3592
 
 
3593
        pb_ind_row_count++;
 
3594
#ifdef XT_TRACK_RETURNED_ROWS
 
3595
        if (!err)
 
3596
                ha_return_row(pb_open_tab, active_index);
 
3597
#endif
 
3598
        done:
 
3599
        if (err)
 
3600
                table->status = STATUS_NOT_FOUND;
 
3601
        else {
 
3602
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3603
                table->status = 0;
 
3604
        }
 
3605
        XT_RETURN(err);
 
3606
}
 
3607
 
 
3608
/*
 
3609
 * index_last() asks for the last key in the index.
 
3610
 */
 
3611
int ha_pbxt::index_last(byte * buf)
 
3612
{
 
3613
        int                                     err = 0;
 
3614
        XTIndexPtr                      ind;
 
3615
        XTIdxSearchKeyRec       search_key;
 
3616
 
 
3617
        XT_TRACE_METHOD();
 
3618
        //statistic_increment(ha_read_last_count,&LOCK_status);
 
3619
        ASSERT_NS(pb_ex_in_use);
 
3620
 
 
3621
        if (active_index == MAX_KEY) {
 
3622
                err = HA_ERR_WRONG_INDEX;
 
3623
                goto done;
 
3624
        }
 
3625
 
 
3626
#ifdef XT_TRACK_RETURNED_ROWS
 
3627
        ha_start_scan(pb_open_tab, active_index);
 
3628
#endif
 
3629
        pb_ind_row_count = 0;
 
3630
 
 
3631
        ind = (XTIndexPtr) pb_share->sh_dic_keys[active_index];
 
3632
 
 
3633
        xt_idx_prep_key(ind, &search_key, XT_SEARCH_AFTER_LAST_FLAG, NULL, 0);
 
3634
        if (!xt_idx_search_prev(pb_open_tab, ind, &search_key))
 
3635
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3636
        else
 
3637
                err = xt_index_prev_read(pb_open_tab, ind, pb_key_read, NULL, buf);
 
3638
 
 
3639
        pb_ind_row_count++;
 
3640
#ifdef XT_TRACK_RETURNED_ROWS
 
3641
        if (!err)
 
3642
                ha_return_row(pb_open_tab, active_index);
 
3643
#endif
 
3644
        done:
 
3645
        if (err)
 
3646
                table->status = STATUS_NOT_FOUND;
 
3647
        else {
 
3648
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3649
                table->status = 0;
 
3650
        }
 
3651
        XT_RETURN(err);
 
3652
}
 
3653
 
 
3654
/*
 
3655
 * -----------------------------------------------------------------------
 
3656
 * RAMDOM/SEQUENTIAL READ METHODS
 
3657
 */
 
3658
 
 
3659
/*
 
3660
 * rnd_init() is called when the system wants the storage engine to do a table
 
3661
 * scan.
 
3662
 * See the example in the introduction at the top of this file to see when
 
3663
 * rnd_init() is called.
 
3664
 *
 
3665
 * Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
 
3666
 * and sql_update.cc.
 
3667
 */
 
3668
int ha_pbxt::rnd_init(bool scan)
 
3669
{
 
3670
        int                     err = 0;
 
3671
        XTThreadPtr     thread = pb_open_tab->ot_thread;
 
3672
 
 
3673
        XT_PRINT1(thread, "rnd_init (%s)\n", pb_share->sh_table_path->ps_path);
 
3674
        XT_DISABLED_TRACE(("seq scan tx=%d\n", (int) thread->st_xact_data->xd_start_xn_id));
 
3675
 
 
3676
        /* Call xt_tab_seq_exit() to make sure the resources used by the previous
 
3677
         * scan are freed. In particular make sure cache page ref count is decremented.
 
3678
         * This is needed as rnd_init() can be called mulitple times w/o matching calls 
 
3679
         * to rnd_end(). Our experience is that currently this is done in queries like:
 
3680
         *
 
3681
         * SELECT t1.c1,t2.c1 FROM t1 LEFT JOIN t2 USING (c1);
 
3682
         * UPDATE t1 LEFT JOIN t2 USING (c1) SET t1.c1 = t2.c1 WHERE t1.c1 = t2.c1;
 
3683
         *
 
3684
         * when scanning inner tables. It is important to understand that in such case
 
3685
         * multiple calls to rnd_init() are not semantically equal to a new query. For
 
3686
         * example we cannot make row locks permanent as we do in rnd_end(), as 
 
3687
         * ha_pbxt::unlock_row still can be called.
 
3688
         */
 
3689
        xt_tab_seq_exit(pb_open_tab);
 
3690
 
 
3691
        /* The number of columns required: */
 
3692
        if (pb_open_tab->ot_is_modify) {
 
3693
                pb_open_tab->ot_cols_req = table->read_set->MX_BIT_SIZE();
 
3694
                /* {START-STAT-HACK} previously position of start statement hack,
 
3695
                 * previous comment to code below: */
 
3696
                /* Start a statement based transaction as soon
 
3697
                 * as a read is done for a modify type statement!
 
3698
                 * Previously, this was done too late!
 
3699
                 */
 
3700
        }
 
3701
        else {
 
3702
                pb_open_tab->ot_cols_req = ha_get_max_bit(table->read_set);
 
3703
 
 
3704
                /*
 
3705
                 * in case of queries like SELECT COUNT(*) FROM t
 
3706
                 * table->read_set is empty. Otoh, ot_cols_req == 0 can be treated
 
3707
                 * as "all columns" by some internal code (see e.g. myxt_load_row), 
 
3708
                 * which makes such queries very ineffective for the records with 
 
3709
                 * extended part. Setting column count to 1 makes sure that the 
 
3710
                 * extended part will not be acessed in most cases.
 
3711
                 */
 
3712
 
 
3713
                if (pb_open_tab->ot_cols_req == 0)
 
3714
                        pb_open_tab->ot_cols_req = 1;
 
3715
        }
 
3716
 
 
3717
        ASSERT_NS(pb_ex_in_use);
 
3718
        if (scan) {
 
3719
                if (!xt_tab_seq_init(pb_open_tab))
 
3720
                        err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3721
        }
 
3722
        else
 
3723
                xt_tab_seq_reset(pb_open_tab);
 
3724
 
 
3725
        xt_xlog_check_long_writer(thread);
 
3726
 
 
3727
        return err;
 
3728
}
 
3729
 
 
3730
int ha_pbxt::rnd_end()
 
3731
{
 
3732
        XT_TRACE_METHOD();
 
3733
 
 
3734
        /*
 
3735
         * make permanent the lock for the last scanned row
 
3736
         */
 
3737
        XTThreadPtr thread = pb_open_tab->ot_thread;
 
3738
        if (pb_open_tab)
 
3739
                pb_open_tab->ot_table->tab_locks.xt_make_lock_permanent(pb_open_tab, &thread->st_lock_list);
 
3740
 
 
3741
        xt_xlog_check_long_writer(thread);
 
3742
 
 
3743
        xt_tab_seq_exit(pb_open_tab);
 
3744
        XT_RETURN(0);
 
3745
}
 
3746
 
 
3747
/*
 
3748
 * This is called for each row of the table scan. When you run out of records
 
3749
 * you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
 
3750
 * The Field structure for the table is the key to getting data into buf
 
3751
 * in a manner that will allow the server to understand it.
 
3752
 *
 
3753
 * Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
 
3754
 * and sql_update.cc.
 
3755
 */
 
3756
int ha_pbxt::rnd_next(byte *buf)
 
3757
{
 
3758
        int             err = 0;
 
3759
        xtBool  eof;
 
3760
 
 
3761
        XT_TRACE_METHOD();
 
3762
        ASSERT_NS(pb_ex_in_use);
 
3763
        //statistic_increment(ha_read_rnd_next_count, &LOCK_status);
 
3764
        xt_xlog_check_long_writer(pb_open_tab->ot_thread);
 
3765
 
 
3766
        if (!xt_tab_seq_next(pb_open_tab, (xtWord1 *) buf, &eof))
 
3767
                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3768
        else if (eof)
 
3769
                err = HA_ERR_END_OF_FILE;
 
3770
 
 
3771
        if (err)
 
3772
                table->status = STATUS_NOT_FOUND;
 
3773
        else {
 
3774
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3775
                table->status = 0;
 
3776
        }
 
3777
        XT_RETURN(err);
 
3778
}
 
3779
 
 
3780
/*
 
3781
 * position() is called after each call to rnd_next() if the data needs
 
3782
 * to be ordered. You can do something like the following to store
 
3783
 * the position:
 
3784
 * ha_store_ptr(ref, ref_length, current_position);
 
3785
 *
 
3786
 * The server uses ref to store data. ref_length in the above case is
 
3787
 * the size needed to store current_position. ref is just a byte array
 
3788
 * that the server will maintain. If you are using offsets to mark rows, then
 
3789
 * current_position should be the offset. If it is a primary key like in
 
3790
 * BDB, then it needs to be a primary key.
 
3791
 *
 
3792
 * Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
 
3793
 */
 
3794
void ha_pbxt::position(const byte *XT_UNUSED(record))
 
3795
{
 
3796
        XT_TRACE_METHOD();
 
3797
        ASSERT_NS(pb_ex_in_use);
 
3798
        /*
 
3799
         * I changed this from using little endian to big endian.
 
3800
         *
 
3801
         * The reason is because sometime the pointer are sorted.
 
3802
         * When they are are sorted a binary compare is used.
 
3803
         * A binary compare sorts big endian values correctly!
 
3804
         *
 
3805
         * Take the followin example:
 
3806
         *
 
3807
         * create table t1 (a int, b text);
 
3808
         * insert into t1 values (1, 'aa'), (1, 'bb'), (1, 'cc');
 
3809
         * select group_concat(b) from t1 group by a;
 
3810
         *
 
3811
         * With little endian pointers the result is:
 
3812
         * aa,bb,cc
 
3813
         *
 
3814
         * With big-endian pointer the result is:
 
3815
         * aa,cc,bb
 
3816
         *
 
3817
         */
 
3818
        (void) ASSERT_NS(XT_RECORD_OFFS_SIZE == 4);
 
3819
        mi_int4store((xtWord1 *) ref, pb_open_tab->ot_curr_rec_id);
 
3820
        XT_RETURN_VOID;
 
3821
}
 
3822
 
 
3823
/*
 
3824
 * Given the #ROWID retrieve the record.
 
3825
 *
 
3826
 * Called from filesort.cc records.cc sql_insert.cc sql_select.cc sql_update.cc.
 
3827
 */
 
3828
int ha_pbxt::rnd_pos(byte * buf, byte *pos)
 
3829
{
 
3830
        int err = 0;
 
3831
 
 
3832
        XT_TRACE_METHOD();
 
3833
        ASSERT_NS(pb_ex_in_use);
 
3834
        //statistic_increment(ha_read_rnd_count, &LOCK_status);
 
3835
        XT_PRINT1(pb_open_tab->ot_thread, "rnd_pos (%s)\n", pb_share->sh_table_path->ps_path);
 
3836
 
 
3837
        pb_open_tab->ot_curr_rec_id = mi_uint4korr((xtWord1 *) pos);
 
3838
        switch (xt_tab_dirty_read_record(pb_open_tab, (xtWord1 *) buf)) {
 
3839
                case FALSE:
 
3840
                        err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3841
                        break;
 
3842
                default:
 
3843
                        break;
 
3844
        }               
 
3845
 
 
3846
        if (err)
 
3847
                table->status = STATUS_NOT_FOUND;
 
3848
        else {
 
3849
                pb_open_tab->ot_thread->st_statistics.st_row_select++;
 
3850
                table->status = 0;
 
3851
        }
 
3852
        XT_RETURN(err);
 
3853
}
 
3854
 
 
3855
/*
 
3856
 * -----------------------------------------------------------------------
 
3857
 * INFO METHODS
 
3858
 */
 
3859
 
 
3860
/*
 
3861
        ::info() is used to return information to the optimizer.
 
3862
        Currently this table handler doesn't implement most of the fields
 
3863
        really needed. SHOW also makes use of this data
 
3864
        Another note, you will probably want to have the following in your
 
3865
        code:
 
3866
        if (records < 2)
 
3867
                records = 2;
 
3868
        The reason is that the server will optimize for cases of only a single
 
3869
        record. If in a table scan you don't know the number of records
 
3870
        it will probably be better to set records to two so you can return
 
3871
        as many records as you need.
 
3872
        Along with records a few more variables you may wish to set are:
 
3873
                records
 
3874
                deleted
 
3875
                data_file_length
 
3876
                index_file_length
 
3877
                delete_length
 
3878
                check_time
 
3879
        Take a look at the public variables in handler.h for more information.
 
3880
 
 
3881
        Called in:
 
3882
                filesort.cc
 
3883
                ha_heap.cc
 
3884
                item_sum.cc
 
3885
                opt_sum.cc
 
3886
                sql_delete.cc
 
3887
                sql_delete.cc
 
3888
                sql_derived.cc
 
3889
                sql_select.cc
 
3890
                sql_select.cc
 
3891
                sql_select.cc
 
3892
                sql_select.cc
 
3893
                sql_select.cc
 
3894
                sql_show.cc
 
3895
                sql_show.cc
 
3896
                sql_show.cc
 
3897
                sql_show.cc
 
3898
                sql_table.cc
 
3899
                sql_union.cc
 
3900
                sql_update.cc
 
3901
 
 
3902
*/
 
3903
#if MYSQL_VERSION_ID < 50114
 
3904
void ha_pbxt::info(uint flag)
 
3905
#else
 
3906
int ha_pbxt::info(uint flag)
 
3907
#endif
 
3908
{
 
3909
        XTOpenTablePtr  ot;
 
3910
        int                             in_use;
 
3911
 
 
3912
        XT_TRACE_METHOD();
 
3913
        
 
3914
        if (!(in_use = pb_ex_in_use)) {
 
3915
                pb_ex_in_use = 1;
 
3916
                if (pb_share && pb_share->sh_table_lock) {
 
3917
                        /* If some thread has an exclusive lock, then
 
3918
                         * we wait for the lock to be removed:
 
3919
                         */
 
3920
#if MYSQL_VERSION_ID < 50114
 
3921
                        ha_wait_for_shared_use(this, pb_share);
 
3922
                        pb_ex_in_use = 1;
 
3923
#else
 
3924
                        if (!ha_wait_for_shared_use(this, pb_share))
 
3925
                                return ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
3926
#endif
 
3927
                }
 
3928
        }
 
3929
 
 
3930
        if ((ot = pb_open_tab)) {
 
3931
                if (flag & HA_STATUS_VARIABLE) {
 
3932
                        /* {FREE-ROWS-BAD}
 
3933
                         * Free row count is not reliable, so ignore it.
 
3934
                         * The problem is if tab_row_fnum > tab_row_eof_id - 1 then
 
3935
                         * we have a very bad result.
 
3936
                         *
 
3937
                         * If stats.records+EXTRA_RECORDS == 0 as returned by 
 
3938
                         * estimate_rows_upper_bound(), then filesort will crash here:
 
3939
                         *
 
3940
                         * make_sortkey(param,sort_keys[idx++],ref_pos);
 
3941
                         * 
 
3942
                         * #0   0x000bf69c in Field_long::sort_string at field.cc:3766
 
3943
                         * #1   0x0022e1f1 in make_sortkey at filesort.cc:769
 
3944
                         * #2   0x0022f1cf in find_all_keys at filesort.cc:619
 
3945
                         * #3   0x00230eec in filesort at filesort.cc:243
 
3946
                         * #4   0x001b9d89 in mysql_update at sql_update.cc:415
 
3947
                         * #5   0x0010db12 in mysql_execute_command at sql_parse.cc:2959
 
3948
                         * #6   0x0011480d in mysql_parse at sql_parse.cc:5787
 
3949
                         * #7   0x00115afb in dispatch_command at sql_parse.cc:1200
 
3950
                         * #8   0x00116de2 in do_command at sql_parse.cc:857
 
3951
                         * #9   0x00101ee4 in handle_one_connection at sql_connect.cc:1115
 
3952
                         *
 
3953
                         * The problem is that sort_keys is allocated to handle just 1 vector.
 
3954
                         * Sorting one vector crashes. Although I could not find a check for
 
3955
                         * the actual number of vectors. But it must assume that it has at
 
3956
                         * least EXTRA_RECORDS vectors.
 
3957
                         */
 
3958
                        stats.deleted = /* ot->ot_table->tab_row_fnum */ 0;
 
3959
                        stats.records = (ha_rows) (ot->ot_table->tab_row_eof_id - 1 /* - stats.deleted */);
 
3960
                        stats.data_file_length = xt_rec_id_to_rec_offset(ot->ot_table, ot->ot_table->tab_rec_eof_id);
 
3961
                        stats.index_file_length = xt_ind_node_to_offset(ot->ot_table, ot->ot_table->tab_ind_eof);
 
3962
                        stats.delete_length = ot->ot_table->tab_rec_fnum * ot->ot_rec_size;
 
3963
                        //check_time = info.check_time;
 
3964
                        stats.mean_rec_length = (ulong) ot->ot_rec_size;
 
3965
                }
 
3966
 
 
3967
                if (flag & HA_STATUS_CONST) {
 
3968
                        ha_rows         rec_per_key;
 
3969
                        XTIndexPtr      ind;
 
3970
                        TABLE_SHARE     *share= TS(table);
 
3971
 
 
3972
                        stats.max_data_file_length = 0x00FFFFFF;
 
3973
                        stats.max_index_file_length = 0x00FFFFFF;
 
3974
                        //stats.create_time = info.create_time;
 
3975
                        ref_length = XT_RECORD_OFFS_SIZE;
 
3976
                        //share->db_options_in_use = info.options;
 
3977
                        stats.block_size = XT_INDEX_PAGE_SIZE;
 
3978
 
 
3979
#ifdef DRIZZLED
 
3980
                        if (share->tmp_table == message::Table::STANDARD)
 
3981
#else
 
3982
                        if (share->tmp_table == NO_TMP_TABLE)
 
3983
#endif
 
3984
#ifdef DRIZZLED
 
3985
#define WHICH_MUTEX                     mutex
 
3986
#elif MYSQL_VERSION_ID >= 50404
 
3987
#define WHICH_MUTEX                     LOCK_ha_data
 
3988
#else
 
3989
                        if (share->tmp_table == NO_TMP_TABLE)
 
3990
#define WHICH_MUTEX                     mutex
 
3991
#endif
 
3992
 
 
3993
#ifdef SAFE_MUTEX
 
3994
 
 
3995
#if MYSQL_VERSION_ID < 50404
 
3996
#if MYSQL_VERSION_ID < 50123
 
3997
                                safe_mutex_lock(&share->mutex,__FILE__,__LINE__);
 
3998
#else
 
3999
                                safe_mutex_lock(&share->mutex,0,__FILE__,__LINE__);
 
4000
#endif
 
4001
#else
 
4002
                                safe_mutex_lock(&share->WHICH_MUTEX,0,__FILE__,__LINE__);
 
4003
#endif
 
4004
 
 
4005
#else // SAFE_MUTEX
 
4006
 
 
4007
#ifdef MY_PTHREAD_FASTMUTEX
 
4008
                                my_pthread_fastmutex_lock(&share->WHICH_MUTEX);
 
4009
#else
 
4010
                                pthread_mutex_lock(&share->WHICH_MUTEX);
 
4011
#endif
 
4012
 
 
4013
#endif // SAFE_MUTEX
 
4014
#ifdef DRIZZLED
 
4015
                        set_prefix(share->keys_in_use, share->keys);
 
4016
                        share->keys_for_keyread&= share->keys_in_use;
 
4017
#else
 
4018
                        share->keys_in_use.set_prefix(share->keys);
 
4019
                        //share->keys_in_use.intersect_extended(info.key_map);
 
4020
                        share->keys_for_keyread.intersect(share->keys_in_use);
 
4021
                        //share->db_record_offset = info.record_offset;
 
4022
#endif
 
4023
                        for (u_int i = 0; i < share->keys; i++) {
 
4024
                                ind = pb_share->sh_dic_keys[i];
 
4025
 
 
4026
                                rec_per_key = 0;
 
4027
                                if (ind->mi_seg_count == 1 && (ind->mi_flags & HA_NOSAME))
 
4028
                                        rec_per_key = 1;
 
4029
                                else {
 
4030
                                        rec_per_key = 1;        
 
4031
                                }
 
4032
                                for (u_int j = 0; j < table->key_info[i].key_parts; j++)
 
4033
                                        table->key_info[i].rec_per_key[j] = (ulong) rec_per_key;
 
4034
                        }
 
4035
#ifdef DRIZZLED
 
4036
                        if (share->tmp_table == message::Table::STANDARD)
 
4037
#else
 
4038
                        if (share->tmp_table == NO_TMP_TABLE)
 
4039
#endif
 
4040
#ifdef SAFE_MUTEX
 
4041
                                safe_mutex_unlock(&share->WHICH_MUTEX,__FILE__,__LINE__);
 
4042
#else
 
4043
#ifdef MY_PTHREAD_FASTMUTEX
 
4044
                                pthread_mutex_unlock(&share->WHICH_MUTEX.mutex);
 
4045
#else
 
4046
                                pthread_mutex_unlock(&share->WHICH_MUTEX);
 
4047
#endif
 
4048
#endif
 
4049
                        /*
 
4050
                         Set data_file_name and index_file_name to point at the symlink value
 
4051
                         if table is symlinked (Ie;  Real name is not same as generated name)
 
4052
                        */
 
4053
                        /*
 
4054
                        data_file_name = index_file_name = 0;
 
4055
                        fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2);
 
4056
                        if (strcmp(name_buff, info.data_file_name))
 
4057
                                data_file_name = info.data_file_name;
 
4058
                        strmov(fn_ext(name_buff), MI_NAME_IEXT);
 
4059
                        if (strcmp(name_buff, info.index_file_name))
 
4060
                                index_file_name = info.index_file_name;
 
4061
                        */
 
4062
                }
 
4063
 
 
4064
                if (flag & HA_STATUS_ERRKEY)
 
4065
                        errkey = ot->ot_err_index_no;
 
4066
 
 
4067
                /* {PRE-INC}
 
4068
                 * We assume they want the next value to be returned!
 
4069
                 *
 
4070
                 * At least, this is what works for the following code:
 
4071
                 *
 
4072
                 * create table t1 (a int auto_increment primary key)
 
4073
                 * auto_increment=100
 
4074
                 * engine=pbxt
 
4075
                 * partition by list (a)
 
4076
                 * (partition p0 values in (1, 98,99, 100, 101));
 
4077
                 * create index inx on t1 (a);
 
4078
                 * insert into t1 values (null);
 
4079
                 * select * from t1;
 
4080
                 */
 
4081
                if (flag & HA_STATUS_AUTO)
 
4082
                        stats.auto_increment_value = (ulonglong) ot->ot_table->tab_auto_inc+1;
 
4083
        }
 
4084
        else
 
4085
                errkey = (uint) -1;
 
4086
 
 
4087
        if (!in_use) {
 
4088
                pb_ex_in_use = 0;
 
4089
                if (pb_share) {
 
4090
                        /* Someone may be waiting for me to complete: */
 
4091
                        if (pb_share->sh_table_lock)
 
4092
                                xt_broadcast_cond_ns((xt_cond_type *) pb_share->sh_ex_cond);
 
4093
                }
 
4094
        }
 
4095
#if MYSQL_VERSION_ID < 50114
 
4096
        XT_RETURN_VOID;
 
4097
#else
 
4098
        XT_RETURN(0);
 
4099
#endif
 
4100
}
 
4101
 
 
4102
/*
 
4103
 * extra() is called whenever the server wishes to send a hint to
 
4104
 * the storage engine. The myisam engine implements the most hints.
 
4105
 * ha_innodb.cc has the most exhaustive list of these hints.
 
4106
 */
 
4107
int ha_pbxt::extra(enum ha_extra_function operation)
 
4108
{
 
4109
        int err = 0;
 
4110
 
 
4111
        XT_PRINT2(xt_get_self(), "ha_pbxt::extra (%s) operation=%d\n", pb_share->sh_table_path->ps_path, operation);
 
4112
 
 
4113
        switch (operation) {
 
4114
                case HA_EXTRA_RESET_STATE:
 
4115
                        pb_key_read = FALSE;
 
4116
                        pb_ignore_dup_key = 0;
 
4117
                        /* As far as I can tell, this function is called for
 
4118
                         * every table at the end of a statement.
 
4119
                         *
 
4120
                         * So, during a LOCK TABLES ... UNLOCK TABLES, I use
 
4121
                         * this to find the end of a statement.
 
4122
                         * start_stmt() indicates the start of a statement,
 
4123
                         * and is also called once for each table in the
 
4124
                         * statement.
 
4125
                         *
 
4126
                         * So the statement boundary is indicated by 
 
4127
                         * self->st_stat_count == 0
 
4128
                         *
 
4129
                         * GOTCHA: I cannot end the transaction here!
 
4130
                         * I must end it in start_stmt().
 
4131
                         * The reason is because there are situations
 
4132
                         * where this would end a transaction that
 
4133
                         * was begin by external_lock().
 
4134
                         *
 
4135
                         * An example of this is when a function
 
4136
                         * is called when doing CREATE TABLE SELECT.
 
4137
                         */
 
4138
                        if (pb_in_stat) {
 
4139
                                /* NOTE: pb_in_stat is just used to avoid getting
 
4140
                                 * self, if it is not necessary!!
 
4141
                                 */
 
4142
                                XTThreadPtr self;
 
4143
 
 
4144
                                pb_in_stat = FALSE;
 
4145
 
 
4146
                                if (!(self = ha_set_current_thread(pb_mysql_thd, &err)))
 
4147
                                        return xt_ha_pbxt_to_mysql_error(err);
 
4148
 
 
4149
                                if (self->st_stat_count > 0) {
 
4150
                                        self->st_stat_count--;
 
4151
                                        if (self->st_stat_count == 0)
 
4152
                                                self->st_stat_ended = TRUE;
 
4153
                                }
 
4154
 
 
4155
                                /* This is the end of a statement, I can turn any locks into perminant locks now: */
 
4156
                                if (pb_open_tab)
 
4157
                                        pb_open_tab->ot_table->tab_locks.xt_make_lock_permanent(pb_open_tab, &self->st_lock_list);
 
4158
                        }
 
4159
                        if (pb_open_tab)
 
4160
                                pb_open_tab->ot_for_update = 0;
 
4161
                        break;
 
4162
                case HA_EXTRA_KEYREAD:
 
4163
                        /* This means we so not need to read the entire record. */
 
4164
                        pb_key_read = TRUE;
 
4165
                        break;
 
4166
                case HA_EXTRA_NO_KEYREAD:
 
4167
                        pb_key_read = FALSE;
 
4168
                        break;
 
4169
                case HA_EXTRA_IGNORE_DUP_KEY:
 
4170
                        /* NOTE!!! Calls to extra(HA_EXTRA_IGNORE_DUP_KEY) can be nested!
 
4171
                         * In fact, the calls are from different threads, so
 
4172
                         * strictly speaking I should protect this variable!!
 
4173
                         * Here is the sequence that produces the duplicate call:
 
4174
                         *
 
4175
                         * drop table if exists t1;
 
4176
                         * CREATE TABLE t1 (x int not null, y int, primary key (x)) engine=pbxt;
 
4177
                         * insert into t1 values (1, 3), (4, 1);
 
4178
                         * replace DELAYED into t1 (x, y) VALUES (4, 2);
 
4179
                         * select * from t1 order by x;
 
4180
                         *
 
4181
                         */
 
4182
                        pb_ignore_dup_key++;
 
4183
                        break;
 
4184
                case HA_EXTRA_NO_IGNORE_DUP_KEY:
 
4185
                        pb_ignore_dup_key--;
 
4186
                        break;
 
4187
                case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
 
4188
                        /* MySQL needs all fields */
 
4189
                        pb_key_read = FALSE;
 
4190
                        break;
 
4191
                default:
 
4192
                        break;
 
4193
        }
 
4194
 
 
4195
        return err;
 
4196
}
 
4197
 
 
4198
 
 
4199
/*
 
4200
 * Deprecated and likely to be removed in the future. Storage engines normally
 
4201
 * just make a call like:
 
4202
 * ha_pbxt::extra(HA_EXTRA_RESET);
 
4203
 * to handle it.
 
4204
 */
 
4205
int ha_pbxt::reset(void)
 
4206
{
 
4207
        XT_TRACE_METHOD();
 
4208
        extra(HA_EXTRA_RESET_STATE);
 
4209
        XT_RETURN(0);
 
4210
}
 
4211
 
 
4212
void ha_pbxt::unlock_row()
 
4213
{
 
4214
        XT_TRACE_METHOD();
 
4215
        if (pb_open_tab)
 
4216
                pb_open_tab->ot_table->tab_locks.xt_remove_temp_lock(pb_open_tab, FALSE);
 
4217
}
 
4218
 
 
4219
/*
 
4220
 * Used to delete all rows in a table. Both for cases of truncate and
 
4221
 * for cases where the optimizer realizes that all rows will be
 
4222
 * removed as a result of a SQL statement.
 
4223
 *
 
4224
 * Called from item_sum.cc by Item_func_group_concat::clear(),
 
4225
 * Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
 
4226
 * Called from sql_delete.cc by mysql_delete().
 
4227
 * Called from sql_select.cc by JOIN::reinit().
 
4228
 * Called from sql_union.cc by st_select_lex_unit::exec().
 
4229
 */
 
4230
int ha_pbxt::delete_all_rows()
 
4231
{
 
4232
        THD                             *thd = current_thd;
 
4233
        int                             err = 0;
 
4234
        XTThreadPtr             self;
 
4235
        XTDDTable               *tab_def = NULL;
 
4236
        char                    path[PATH_MAX];
 
4237
 
 
4238
        XT_TRACE_METHOD();
 
4239
 
 
4240
        if (thd_sql_command(thd) != SQLCOM_TRUNCATE) {
 
4241
                /* Just like InnoDB we only handle TRUNCATE TABLE
 
4242
                 * by recreating the table.
 
4243
                 * DELETE FROM t must be handled by deleting
 
4244
                 * each row because it may be part of a transaction,
 
4245
                 * and there may be foreign key actions.
 
4246
                 */
 
4247
#ifdef DRIZZLED
 
4248
                XT_RETURN (errno = HA_ERR_WRONG_COMMAND);
 
4249
#else
 
4250
                XT_RETURN (my_errno = HA_ERR_WRONG_COMMAND);
 
4251
#endif
 
4252
        }
 
4253
 
 
4254
        if (!(self = ha_set_current_thread(thd, &err)))
 
4255
                return xt_ha_pbxt_to_mysql_error(err);
 
4256
 
 
4257
        try_(a) {
 
4258
                XTDictionaryRec dic;
 
4259
 
 
4260
                memset(&dic, 0, sizeof(dic));
 
4261
 
 
4262
                dic = pb_share->sh_table->tab_dic;
 
4263
                xt_strcpy(PATH_MAX, path, pb_share->sh_table->tab_name->ps_path);
 
4264
 
 
4265
                if ((tab_def = dic.dic_table))
 
4266
                        tab_def->reference();
 
4267
 
 
4268
                if (!(thd_test_options(thd,OPTION_NO_FOREIGN_KEY_CHECKS)))
 
4269
                        tab_def->deleteAllRows(self);
 
4270
 
 
4271
                /* We should have a table lock! */
 
4272
                //ASSERT(pb_lock_table);
 
4273
                if (!pb_table_locked) {
 
4274
                        ha_aquire_exclusive_use(self, pb_share, this);
 
4275
                        pushr_(ha_release_exclusive_use, pb_share);
 
4276
                }
 
4277
                ha_close_open_tables(self, pb_share, NULL);
 
4278
 
 
4279
                /* This is required in the case of delete_all_rows, because we must
 
4280
                 * ensure that the handlers no longer reference the old
 
4281
                 * table, so that it will not be used again. The table
 
4282
                 * must be re-openned, because the ID has changed!
 
4283
                 *
 
4284
                 * 0.9.86+ Must check if this is still necessary.
 
4285
                 *
 
4286
                 * the ha_close_share(self, pb_share) call was moved from above
 
4287
                 * (before tab_def = dic.dic_table), because of a crash.
 
4288
                 * Test case:
 
4289
                 *
 
4290
                 * set storage_engine = pbxt;
 
4291
                 * create table t1 (s1 int primary key);
 
4292
                 * insert into t1 values (1);
 
4293
                 * create table t2 (s1 int, foreign key (s1) references t1 (s1));
 
4294
                 * insert into t2 values (1); 
 
4295
                 * truncate table t1; -- this should fail because of FK constraint
 
4296
                 * alter table t1 engine = myisam; -- this caused crash
 
4297
                 *
 
4298
                 */
 
4299
                ha_close_share(self, pb_share);
 
4300
 
 
4301
                /* MySQL documentation requires us to reset auto increment value to 1
 
4302
                 * on truncate even if the table was created with a different value. 
 
4303
                 * This is also consistent with other engines.
 
4304
                 */
 
4305
                dic.dic_min_auto_inc = 1;
 
4306
 
 
4307
                xt_create_table(self, (XTPathStrPtr) path, &dic);
 
4308
                if (!pb_table_locked)
 
4309
                        freer_(); // ha_release_exclusive_use(pb_share)
 
4310
        }
 
4311
        catch_(a) {
 
4312
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4313
        }
 
4314
        cont_(a);
 
4315
 
 
4316
        if (tab_def)
 
4317
                tab_def->release(self);
 
4318
 
 
4319
        XT_RETURN(err);
 
4320
}
 
4321
 
 
4322
/*
 
4323
 * TODO: Implement!
 
4324
 * Assuming a key (a,b,c)
 
4325
 * 
 
4326
 * rec_per_key[0] = SELECT COUNT(*)/COUNT(DISTINCT a) FROM t;
 
4327
 * rec_per_key[1] = SELECT COUNT(*)/COUNT(DISTINCT a,b) FROM t;
 
4328
 * rec_per_key[2] = SELECT COUNT(*)/COUNT(DISTINCT a,b,c) FROM t;
 
4329
 *
 
4330
 * After this is implemented, the selectivity can serve as
 
4331
 * a quick estimate of records_in_range().
 
4332
 *
 
4333
 * After you have done this, you need to redo the index_merge*
 
4334
 * tests. Restore the standard result to check if we
 
4335
 * now agree with the MyISAM strategy.
 
4336
 * 
 
4337
 */
 
4338
int ha_pbxt::analyze(THD *thd, HA_CHECK_OPT *XT_UNUSED(check_opt))
 
4339
{
 
4340
        int                             err = 0;
 
4341
        XTDatabaseHPtr  db;
 
4342
        xtXactID                my_xn_id;
 
4343
        xtXactID                clean_xn_id = 0;
 
4344
        uint                    cnt = 10;
 
4345
 
 
4346
        XT_TRACE_METHOD();
 
4347
 
 
4348
        if (!pb_open_tab) {
 
4349
                if ((err = reopen()))
 
4350
                        XT_RETURN(err);
 
4351
        }
 
4352
 
 
4353
        /* Wait until the sweeper is no longer busy!
 
4354
         * If you want an accurate count(*) value, then call
 
4355
         * ANALYZE TABLE first. This function waits until the
 
4356
         * sweeper has completed.
 
4357
         */
 
4358
        db = pb_open_tab->ot_table->tab_db;
 
4359
        
 
4360
        /*
 
4361
         * Wait until everything is cleaned up before this transaction.
 
4362
         * But this will only work if the we quit out transaction!
 
4363
         *
 
4364
         * GOTCHA: When a PBXT table is partitioned, then analyze() is
 
4365
         * called for each component. The first calls xt_xn_commit().
 
4366
         * All following calls have no transaction!:
 
4367
         *
 
4368
         * CREATE TABLE t1 (a int)
 
4369
         * PARTITION BY LIST (a)
 
4370
         * (PARTITION x1 VALUES IN (10), PARTITION x2 VALUES IN (20));
 
4371
         * 
 
4372
         * analyze table t1;
 
4373
         * 
 
4374
         */
 
4375
        if (pb_open_tab->ot_thread && pb_open_tab->ot_thread->st_xact_data) {
 
4376
                my_xn_id = pb_open_tab->ot_thread->st_xact_data->xd_start_xn_id;
 
4377
                XT_PRINT0(xt_get_self(), "xt_xn_commit\n");
 
4378
                xt_xn_commit(pb_open_tab->ot_thread);
 
4379
        }
 
4380
        else
 
4381
                my_xn_id = db->db_xn_to_clean_id;
 
4382
 
 
4383
        while ((!db->db_sw_idle || xt_xn_is_before(db->db_xn_to_clean_id, my_xn_id)) && !thd_killed(thd)) {
 
4384
                xt_busy_wait();
 
4385
 
 
4386
                /*
 
4387
                 * It is possible that the sweeper gets stuck because
 
4388
                 * it has no dictionary information!
 
4389
                 * As in the example below.
 
4390
                 *
 
4391
                 * create table t4 (
 
4392
                 *   pk_col int auto_increment primary key, a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' '
 
4393
                 * ) engine=pbxt;
 
4394
                 *
 
4395
                 * insert into t4 (a1, a2, b, c, d, dummy) select * from t1;
 
4396
                 * 
 
4397
                 * create index idx12672_0 on t4 (a1);
 
4398
                 * create index idx12672_1 on t4 (a1,a2,b,c);
 
4399
                 * create index idx12672_2 on t4 (a1,a2,b);
 
4400
                 * analyze table t1;
 
4401
                 */
 
4402
                if (db->db_sw_idle) {
 
4403
                        /* This will make sure we don't wait forever: */
 
4404
                        if (clean_xn_id != db->db_xn_to_clean_id) {
 
4405
                                clean_xn_id = db->db_xn_to_clean_id;
 
4406
                                cnt = 10;
 
4407
                        }
 
4408
                        else {
 
4409
                                cnt--;
 
4410
                                if (!cnt)
 
4411
                                        break;
 
4412
                        }
 
4413
                        xt_wakeup_sweeper(db);
 
4414
                }
 
4415
        }
 
4416
 
 
4417
        XT_RETURN(err);
 
4418
}
 
4419
 
 
4420
#ifndef DRIZZLED
 
4421
int ha_pbxt::repair(THD *XT_UNUSED(thd), HA_CHECK_OPT *XT_UNUSED(check_opt))
 
4422
{
 
4423
        return(HA_ADMIN_TRY_ALTER);
 
4424
}
 
4425
 
 
4426
/*
 
4427
 * This is mapped to "ALTER TABLE tablename TYPE=PBXT", which rebuilds
 
4428
 * the table in MySQL.
 
4429
 */
 
4430
int ha_pbxt::optimize(THD *XT_UNUSED(thd), HA_CHECK_OPT *XT_UNUSED(check_opt))
 
4431
{
 
4432
        return(HA_ADMIN_TRY_ALTER);
 
4433
}
 
4434
#endif
 
4435
 
 
4436
#ifdef DEBUG
 
4437
extern int pbxt_mysql_trace_on;
 
4438
#endif
 
4439
 
 
4440
int ha_pbxt::check(THD* thd, HA_CHECK_OPT* XT_UNUSED(check_opt))
 
4441
{
 
4442
        int                             err = 0;
 
4443
        XTThreadPtr             self;
 
4444
 
 
4445
        if (!(self = ha_set_current_thread(thd, &err)))
 
4446
                return xt_ha_pbxt_to_mysql_error(err);
 
4447
        if (self->st_lock_count)
 
4448
                ASSERT(self->st_xact_data);
 
4449
 
 
4450
        if (!pb_table_locked) {
 
4451
                ha_aquire_exclusive_use(self, pb_share, this);
 
4452
                pushr_(ha_release_exclusive_use, pb_share);
 
4453
        }
 
4454
 
 
4455
#ifdef CHECK_TABLE_LOADS
 
4456
        xt_tab_load_table(self, pb_open_tab);
 
4457
#endif
 
4458
        xt_check_table(self, pb_open_tab);
 
4459
 
 
4460
        if (!pb_table_locked)
 
4461
                freer_(); // ha_release_exclusive_use(pb_share)
 
4462
 
 
4463
        //pbxt_mysql_trace_on = TRUE;
 
4464
        return 0;
 
4465
}
 
4466
 
 
4467
/*
 
4468
 * This function is called:
 
4469
 * For each table in LOCK TABLES,
 
4470
 * OR
 
4471
 * For each table in a statement.
 
4472
 *
 
4473
 * It is called with F_UNLCK:
 
4474
 * in UNLOCK TABLES
 
4475
 * OR
 
4476
 * at the end of a statement.
 
4477
 *
 
4478
 */
 
4479
xtPublic int ha_pbxt::external_lock(THD *thd, int lock_type)
 
4480
{
 
4481
        int                             err = 0;
 
4482
        XTThreadPtr             self;
 
4483
        
 
4484
        if (!(self = ha_set_current_thread(thd, &err)))
 
4485
                return xt_ha_pbxt_to_mysql_error(err);
 
4486
 
 
4487
        /* F_UNLCK is set when this function is called at end
 
4488
         * of statement or UNLOCK TABLES
 
4489
         */
 
4490
        if (lock_type == F_UNLCK) {
 
4491
                /* This is not TRUE if external_lock() FAILED!
 
4492
                 * Can we rely on external_unlock being called when
 
4493
                 * external_lock() fails? Currently yes, but it does
 
4494
                 * not make sense!
 
4495
                ASSERT_NS(pb_ex_in_use);
 
4496
                */
 
4497
 
 
4498
                XT_PRINT1(self, "EXTERNAL_LOCK (%s) lock_type=UNLOCK\n", pb_share->sh_table_path->ps_path);
 
4499
 
 
4500
                /* Make any temporary locks on this table permanent.
 
4501
                 *
 
4502
                 * This is required here because of the following example:
 
4503
                 * create table t1 (a int NOT NULL, b int, primary key (a));
 
4504
                 * create table t2 (a int NOT NULL, b int, primary key (a));
 
4505
                 * insert into t1 values (0, 10),(1, 11),(2, 12);
 
4506
                 * insert into t2 values (1, 21),(2, 22),(3, 23);
 
4507
                 * update t1 set b= (select b from t2 where t1.a = t2.a);
 
4508
                 * update t1 set b= (select b from t2 where t1.a = t2.a);
 
4509
                 * select * from t1;
 
4510
                 * drop table t1, t2;
 
4511
                 *
 
4512
                 */
 
4513
 
 
4514
                /* GOTCHA! It's weird, but, if this function returns an error
 
4515
                 * on lock, then UNLOCK is called?!
 
4516
                 * This should not be done, because if lock fails, it should be
 
4517
                 * assumed that no UNLOCK is required.
 
4518
                 * Basically, I have to assume that some code will presume this,
 
4519
                 * although the function lock_external() calls unlock, even
 
4520
                 * when lock fails.
 
4521
                 * The result is, that my lock count can go wrong. So I could
 
4522
                 * change the lock method, and increment the lock count, even
 
4523
                 * if it fails. However, the consequences are more serious,
 
4524
                 * if some code decides not to call UNLOCK after lock fails.
 
4525
                 * The result is that I would have a permanent too high lock,
 
4526
                 * count and nothing will work.
 
4527
                 * So instead, I handle the fact that I might too many unlocks
 
4528
                 * here.
 
4529
                 */
 
4530
                if (self->st_lock_count > 0)
 
4531
                        self->st_lock_count--;
 
4532
                if (!self->st_lock_count) {
 
4533
                        /* This section handles "auto-commit"... */
 
4534
 
 
4535
#ifdef XT_IMPLEMENT_NO_ACTION
 
4536
                        /* {NO-ACTION-BUG}
 
4537
                         * This is required here because it marks the end of a statement.
 
4538
                         * If we are in a non-auto-commit mode, then we cannot
 
4539
                         * wait for st_is_update to be set by the begining of a new transaction.
 
4540
                         */
 
4541
                        if (self->st_restrict_list.bl_count) {
 
4542
                                if (!xt_tab_restrict_rows(&self->st_restrict_list, self))
 
4543
                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4544
                        }
 
4545
#endif
 
4546
 
 
4547
                        if (self->st_xact_data) {
 
4548
                                if (self->st_auto_commit) {
 
4549
                                        /*
 
4550
                                         * Normally I could assume that if the transaction
 
4551
                                         * has not been aborted by now, then it should be committed.
 
4552
                                         *
 
4553
                                         * Unfortunately, this is not the case!
 
4554
                                         *
 
4555
                                         * create table t1 (id int primary key) engine = pbxt;
 
4556
                                         * create table t2 (id int) engine = pbxt;
 
4557
                                         * 
 
4558
                                         * insert into t1 values ( 1 ) ;
 
4559
                                         * insert into t1 values ( 2 ) ;
 
4560
                                         * insert into t2 values ( 1 ) ;
 
4561
                                         * insert into t2 values ( 2 ) ;
 
4562
                                         * 
 
4563
                                         * --This statement is returns an error calls ha_autocommit_or_rollback():
 
4564
                                         * update t1 set t1.id=1 where t1.id=2;
 
4565
                                         * 
 
4566
                                         * --This statement is returns no error and calls ha_autocommit_or_rollback():
 
4567
                                         * update t1,t2 set t1.id=3, t2.id=3 where t1.id=2 and t2.id = t1.id;
 
4568
                                         * 
 
4569
                                         * --But this statement returns an error and does not call ha_autocommit_or_rollback():
 
4570
                                         * update t1,t2 set t1.id=1, t2.id=1 where t1.id=3 and t2.id = t1.id;
 
4571
                                         * 
 
4572
                                         * The result is, I cannot rely on ha_autocommit_or_rollback() being called :(
 
4573
                                         * So I have to abort myself here...
 
4574
                                         */
 
4575
                                        if (pb_open_tab)
 
4576
                                                pb_open_tab->ot_table->tab_locks.xt_make_lock_permanent(pb_open_tab, &self->st_lock_list);
 
4577
 
 
4578
                                        if (self->st_abort_trans) {
 
4579
                                                XT_PRINT0(self, "xt_xn_rollback in unlock\n");
 
4580
                                                if (!xt_xn_rollback(self))
 
4581
                                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4582
                                        }
 
4583
                                        else {
 
4584
                                                XT_PRINT0(self, "xt_xn_commit in unlock\n");
 
4585
                                                if (!xt_xn_commit(self))
 
4586
                                                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4587
                                        }
 
4588
                                }
 
4589
                        }
 
4590
 
 
4591
                        /* If the previous statement was "for update", then set the visibilty
 
4592
                         * so that non- for update SELECTs will see what the for update select
 
4593
                         * (or update statement) just saw.
 
4594
                         */
 
4595
                        if (pb_open_tab) {
 
4596
                                if (pb_open_tab->ot_for_update) {
 
4597
                                        self->st_visible_time = self->st_database->db_xn_end_time;
 
4598
                                        pb_open_tab->ot_for_update = 0;
 
4599
                                }
 
4600
 
 
4601
                                if (pb_share->sh_recalc_selectivity) {
 
4602
                                        /* {FREE-ROWS-BAD} */
 
4603
                                        if ((pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) >= 200) {
 
4604
                                                /* [**] */
 
4605
                                                pb_share->sh_recalc_selectivity = FALSE;
 
4606
                                                xt_ind_set_index_selectivity(pb_open_tab, self);
 
4607
                                                /* {FREE-ROWS-BAD} */
 
4608
                                                pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
 
4609
                                        }
 
4610
                                }
 
4611
                        }
 
4612
 
 
4613
                        if (self->st_stat_modify)
 
4614
                                self->st_statistics.st_stat_write++;
 
4615
                        else
 
4616
                                self->st_statistics.st_stat_read++;
 
4617
                        self->st_stat_modify = FALSE;
 
4618
                        self->st_import_stat = XT_IMP_NO_IMPORT;
 
4619
 
 
4620
                        /* Only reset this if there is no transactions running, and
 
4621
                         * no tables are open!
 
4622
                         */
 
4623
                        if (!self->st_xact_data)
 
4624
                                self->st_non_temp_opened = FALSE;
 
4625
                }
 
4626
 
 
4627
                if (pb_table_locked) {
 
4628
                        pb_table_locked--;
 
4629
                        if (!pb_table_locked)
 
4630
                                ha_release_exclusive_use(self, pb_share);
 
4631
                }
 
4632
 
 
4633
                /* No longer in use: */
 
4634
                pb_ex_in_use = 0;
 
4635
                /* Someone may be waiting for me to complete: */
 
4636
                if (pb_share->sh_table_lock)
 
4637
                        xt_broadcast_cond_ns((xt_cond_type *) pb_share->sh_ex_cond);
 
4638
        }
 
4639
        else {
 
4640
                XT_PRINT2(self, "ha_pbxt::EXTERNAL_LOCK (%s) lock_type=%d\n", pb_share->sh_table_path->ps_path, lock_type);
 
4641
                
 
4642
                if (pb_lock_table) {
 
4643
                        pb_ex_in_use = 1;
 
4644
                        try_(a) {
 
4645
                                if (!pb_table_locked)
 
4646
                                        ha_aquire_exclusive_use(self, pb_share, this);
 
4647
                                pb_table_locked++;
 
4648
 
 
4649
                                ha_close_open_tables(self, pb_share, this);
 
4650
 
 
4651
                                if (!pb_share->sh_table) {
 
4652
                                        xt_ha_open_database_of_table(self, pb_share->sh_table_path);
 
4653
 
 
4654
                                        ha_open_share(self, pb_share);
 
4655
                                }
 
4656
                        }
 
4657
                        catch_(a) {
 
4658
                                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4659
                                pb_ex_in_use = 0;
 
4660
                                goto complete;
 
4661
                        }
 
4662
                        cont_(a);
 
4663
                }
 
4664
                else {
 
4665
                        pb_ex_in_use = 1;
 
4666
                        if (pb_share->sh_table_lock && !pb_table_locked) {
 
4667
                                /* If some thread has an exclusive lock, then
 
4668
                                 * we wait for the lock to be removed:
 
4669
                                 */
 
4670
                                if (!ha_wait_for_shared_use(this, pb_share)) {
 
4671
                                        err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
4672
                                        goto complete;
 
4673
                                }
 
4674
                        }
 
4675
 
 
4676
                        if (!pb_open_tab) {
 
4677
                                if ((err = reopen())) {
 
4678
                                        pb_ex_in_use = 0;
 
4679
                                        goto complete;
 
4680
                                }
 
4681
                        }
 
4682
 
 
4683
                        /* Set the current thread for this open table: */
 
4684
                        pb_open_tab->ot_thread = self;
 
4685
 
 
4686
                        /* If this is a set, then it is in UPDATE/DELETE TABLE ...
 
4687
                         * or SELECT ... FOR UPDATE
 
4688
                         */     
 
4689
                        pb_open_tab->ot_is_modify = FALSE;
 
4690
                        if ((pb_open_tab->ot_for_update = (lock_type == F_WRLCK))) {
 
4691
                                switch ((int) thd_sql_command(thd)) {
 
4692
                                        case SQLCOM_DELETE:
 
4693
#ifndef DRIZZLED
 
4694
                                        case SQLCOM_DELETE_MULTI:
 
4695
#endif
 
4696
                                                /* turn DELETE IGNORE into normal DELETE. The IGNORE option causes problems because 
 
4697
                                                 * when a record is deleted we add an xlog record which we cannot "rollback" later
 
4698
                                                 * when we find that an FK-constraint has failed. 
 
4699
                                                 */
 
4700
                                                thd->lex->ignore = false;
 
4701
                                        case SQLCOM_UPDATE:
 
4702
#ifndef DRIZZLED
 
4703
                                        case SQLCOM_UPDATE_MULTI:
 
4704
#endif
 
4705
                                        case SQLCOM_REPLACE:
 
4706
                                        case SQLCOM_REPLACE_SELECT:
 
4707
                                        case SQLCOM_INSERT:
 
4708
                                        case SQLCOM_INSERT_SELECT:
 
4709
                                                pb_open_tab->ot_is_modify = TRUE;
 
4710
                                                self->st_stat_modify = TRUE;
 
4711
                                                break;
 
4712
                                        case SQLCOM_ALTER_TABLE:
 
4713
                                        case SQLCOM_CREATE_INDEX:
 
4714
#ifndef DRIZZLED
 
4715
                                        case SQLCOM_REPAIR:
 
4716
                                        case SQLCOM_OPTIMIZE:
 
4717
#endif
 
4718
                                        case SQLCOM_DROP_INDEX:
 
4719
                                                self->st_stat_modify = TRUE;
 
4720
                                                self->st_import_stat = XT_IMP_COPY_TABLE;
 
4721
                                                pb_import_row_count = 0;
 
4722
                                                /* Do not read FOR UPDATE!
 
4723
                                                 * this avoids taking locks on the rows that are read
 
4724
                                                 * Which leads to the assertion failure:
 
4725
                                                 * int XTRowLocks::xt_make_lock_permanent(XTOpenTable*, XTRowLockList*)(lock_xt.cc:646) item
 
4726
                                                 * after the transaction is committed in write_row.
 
4727
                                                 */
 
4728
                                                pb_open_tab->ot_for_update = FALSE;
 
4729
                                                break;
 
4730
                                        case SQLCOM_LOAD:
 
4731
                                                self->st_stat_modify = TRUE;
 
4732
                                                self->st_import_stat = XT_IMP_LOAD_TABLE;
 
4733
                                                pb_import_row_count = 0;
 
4734
                                                pb_open_tab->ot_for_update = FALSE;
 
4735
                                                break;
 
4736
                                        case SQLCOM_CREATE_TABLE:
 
4737
                                        case SQLCOM_TRUNCATE:
 
4738
                                        case SQLCOM_DROP_TABLE:
 
4739
                                                self->st_stat_modify = TRUE;
 
4740
                                                break;
 
4741
                                }
 
4742
                        }
 
4743
 
 
4744
                        if (pb_open_tab->ot_is_modify && pb_open_tab->ot_table->tab_dic.dic_disable_index) {
 
4745
                                xt_tab_set_index_error(pb_open_tab->ot_table);
 
4746
                                err = ha_log_pbxt_thread_error_for_mysql(pb_ignore_dup_key);
 
4747
                                goto complete;
 
4748
                        }
 
4749
                }
 
4750
 
 
4751
                /* Record the associated MySQL thread: */
 
4752
                pb_mysql_thd = thd;
 
4753
 
 
4754
                if (self->st_database != pb_share->sh_table->tab_db) {                          
 
4755
                        try_(b) {
 
4756
                                /* PBXT does not permit multiple databases us one statement,
 
4757
                                 * or in a single transaction!
 
4758
                                 *
 
4759
                                 * Example query:
 
4760
                                 *
 
4761
                                 * update mysqltest_1.t1, mysqltest_2.t2 set a=10,d=10;
 
4762
                                 */
 
4763
                                if (self->st_lock_count > 0)
 
4764
                                        xt_throw_xterr(XT_CONTEXT, XT_ERR_MULTIPLE_DATABASES);
 
4765
 
 
4766
                                xt_ha_open_database_of_table(self, pb_share->sh_table_path);
 
4767
                        }
 
4768
                        catch_(b) {
 
4769
                                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4770
                                pb_ex_in_use = 0;
 
4771
                                goto complete;
 
4772
                        }
 
4773
                        cont_(b);
 
4774
                }
 
4775
 
 
4776
                /* See {IS-UPDATE-STAT} nad {UPDATE-STACK} */
 
4777
                self->st_is_update = NULL;
 
4778
 
 
4779
                /* Auto begin a transaction (if one is not already running): */
 
4780
                if (!self->st_xact_data) {
 
4781
                        /* Transaction mode numbers must be identical! */
 
4782
                        (void) ASSERT_NS(ISO_READ_UNCOMMITTED == XT_XACT_UNCOMMITTED_READ);
 
4783
                        (void) ASSERT_NS(ISO_SERIALIZABLE == XT_XACT_SERIALIZABLE);
 
4784
 
 
4785
                        thd_init_xact(thd, self, true);
 
4786
                        
 
4787
                        if (!xt_xn_begin(self)) {
 
4788
                                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
4789
                                pb_ex_in_use = 0;
 
4790
                                goto complete;
 
4791
                        }
 
4792
                        /*
 
4793
                         * {START-TRANS} GOTCHA: trans_register_ha() is not mentioned in the documentation.
 
4794
                         * It must be called to inform MySQL that we have a transaction (see start_stmt).
 
4795
                         *
 
4796
                         * Here are some tests that confirm whether things are done correctly:
 
4797
                         *
 
4798
                         * drop table if exists t1, t2;
 
4799
                         * create table t1 (c1 int);
 
4800
                         * insert t1 values (1);
 
4801
                         * select * from t1;
 
4802
                         * rename table t1 to t2;
 
4803
                         *
 
4804
                         * rename will generate an error if MySQL thinks a transaction is
 
4805
                         * still running.
 
4806
                         *
 
4807
                         * create table t1 (a text character set utf8, b text character set latin1);
 
4808
                         * insert t1 values (0x4F736E616272C3BC636B, 0x4BF66C6E);
 
4809
                         * select * from t1;
 
4810
                         * --exec $MYSQL_DUMP --tab=$MYSQLTEST_VARDIR/tmp/ test
 
4811
                         * --exec $MYSQL test < $MYSQLTEST_VARDIR/tmp/t1.sql
 
4812
                         * --exec $MYSQL_IMPORT test $MYSQLTEST_VARDIR/tmp/t1.txt
 
4813
                         * select * from t1;
 
4814
                         *
 
4815
                         * This test forces a begin transaction in start_stmt()
 
4816
                         *
 
4817
                         * drop tables if exists t1;
 
4818
                         * create table t1 (c1 int);
 
4819
                         * lock tables t1 write;
 
4820
                         * insert t1 values (1);
 
4821
                         * insert t1 values (2);
 
4822
                         * unlock tables;
 
4823
                         *
 
4824
                         * The second select will return an empty result of the
 
4825
                         * MySQL is not informed that a transaction is running (auto-commit 
 
4826
                         * in external_lock comes too late)!
 
4827
                         *
 
4828
                         */
 
4829
#ifndef DRIZZLED
 
4830
                        if (!self->st_auto_commit) {
 
4831
                                trans_register_ha(thd, TRUE, pbxt_hton);
 
4832
                                XT_PRINT0(self, "CONN START XACT - ha_pbxt::external_lock --> trans_register_ha\n");
 
4833
                        }
 
4834
#endif
 
4835
                }
 
4836
 
 
4837
                /* Any open table can cause this to be FALSE: */
 
4838
                if (!XT_IS_TEMP_TABLE(pb_open_tab->ot_table->tab_dic.dic_tab_flags))
 
4839
                        self->st_non_temp_opened = TRUE;
 
4840
 
 
4841
                /* Start a statment transaction: */
 
4842
                /* {START-STAT-HACK} The problem that ha_commit_trans() is not
 
4843
                 * called by MySQL seems to be fixed (tests confirm this).
 
4844
                 * Here is the previous comment when this code was execute 
 
4845
                 * here {START-STAT-HACK}
 
4846
                 *
 
4847
                 * GOTCHA: I have a huge problem with the transaction statement.
 
4848
                 * It is not ALWAYS committed (I mean ha_commit_trans() is
 
4849
                 * not always called - for example in SELECT).
 
4850
                 *
 
4851
                 * If I call trans_register_ha() but ha_commit_trans() is not called
 
4852
                 * then MySQL thinks a transaction is still running (while
 
4853
                 * I have committed the auto-transaction in ha_pbxt::external_lock()).
 
4854
                 *
 
4855
                 * This causes all kinds of problems, like transactions
 
4856
                 * are killed when they should not be.
 
4857
                 *
 
4858
                 * To prevent this, I only inform MySQL that a transaction
 
4859
                 * has beens started when an update is performed. I have determined that
 
4860
                 * ha_commit_trans() is only guarenteed to be called if an update is done.
 
4861
                 * --------
 
4862
                 *
 
4863
                 * So, this is the correct place to start a statement transaction.
 
4864
                 *
 
4865
                 * Note: if trans_register_ha() is not called before ha_write_row(), then 
 
4866
                 * PBXT is not registered correctly as a modification transaction.
 
4867
                 * (mark_trx_read_write call in ha_write_row).
 
4868
                 * This leads to 2-phase commit not being called as it should when
 
4869
                 * binary logging is enabled.
 
4870
                 */
 
4871
#ifndef DRIZZLED
 
4872
                if (!pb_open_tab->ot_thread->st_stat_trans) {
 
4873
                        trans_register_ha(pb_mysql_thd, FALSE, pbxt_hton);
 
4874
                        XT_PRINT0(pb_open_tab->ot_thread, "STAT START - ha_pbxt::external_lock --> trans_register_ha\n");
 
4875
                        pb_open_tab->ot_thread->st_stat_trans = TRUE;
 
4876
                }
 
4877
#endif
 
4878
                if (lock_type == F_WRLCK || self->st_xact_mode < XT_XACT_REPEATABLE_READ)
 
4879
                        self->st_visible_time = self->st_database->db_xn_end_time;
 
4880
 
 
4881
#ifdef TRACE_STATEMENTS
 
4882
                if (self->st_lock_count == 0)
 
4883
                        STAT_TRACE(self, *thd_query(thd));
 
4884
#endif
 
4885
                self->st_lock_count++;
 
4886
        }
 
4887
 
 
4888
        complete:
 
4889
        return err;
 
4890
}
 
4891
 
 
4892
/*
 
4893
 * This function is called for each table in a statement
 
4894
 * after LOCK TABLES has been used.
 
4895
 *
 
4896
 * Currently I only use this function to set the
 
4897
 * current thread of the table handle. 
 
4898
 *
 
4899
 * GOTCHA: The prototype of start_stmt() has changed
 
4900
 * from version 4.1 to 5.1!
 
4901
 */
 
4902
int ha_pbxt::start_stmt(THD *thd, thr_lock_type lock_type)
 
4903
{
 
4904
        int                             err = 0;
 
4905
        XTThreadPtr             self;
 
4906
 
 
4907
        ASSERT_NS(pb_ex_in_use);
 
4908
 
 
4909
        if (!(self = ha_set_current_thread(thd, &err)))
 
4910
                return xt_ha_pbxt_to_mysql_error(err);
 
4911
 
 
4912
        XT_PRINT2(self, "ha_pbxt::start_stmt (%s) lock_type=%d\n", pb_share->sh_table_path->ps_path, (int) lock_type);
 
4913
 
 
4914
        if (!pb_open_tab) {
 
4915
                if ((err = reopen()))
 
4916
                        goto complete;
 
4917
        }
 
4918
 
 
4919
        ASSERT_NS(pb_open_tab->ot_thread == self);
 
4920
        ASSERT_NS(thd == pb_mysql_thd);
 
4921
        ASSERT_NS(self->st_database == pb_open_tab->ot_table->tab_db);
 
4922
 
 
4923
        if (self->st_stat_ended) {
 
4924
                self->st_stat_ended = FALSE;
 
4925
                self->st_stat_trans = FALSE;
 
4926
 
 
4927
#ifdef XT_IMPLEMENT_NO_ACTION
 
4928
                if (self->st_restrict_list.bl_count) {
 
4929
                        if (!xt_tab_restrict_rows(&self->st_restrict_list, self)) {
 
4930
                                err = xt_ha_pbxt_thread_error_for_mysql(pb_mysql_thd, self, pb_ignore_dup_key);
 
4931
                        }
 
4932
                }
 
4933
#endif
 
4934
 
 
4935
                /* This section handles "auto-commit"... */
 
4936
                if (self->st_xact_data && self->st_auto_commit && self->st_table_trans) {
 
4937
                        if (self->st_abort_trans) {
 
4938
                                XT_PRINT0(self, "xt_xn_rollback in start_stmt\n");
 
4939
                                if (!xt_xn_rollback(self))
 
4940
                                        err = xt_ha_pbxt_thread_error_for_mysql(pb_mysql_thd, self, pb_ignore_dup_key);
 
4941
                        }
 
4942
                        else {
 
4943
                                XT_PRINT0(self, "xt_xn_commit in start_stmt\n");
 
4944
                                if (!xt_xn_commit(self))
 
4945
                                        err = xt_ha_pbxt_thread_error_for_mysql(pb_mysql_thd, self, pb_ignore_dup_key);
 
4946
                        }
 
4947
                }
 
4948
 
 
4949
                if (self->st_stat_modify)
 
4950
                        self->st_statistics.st_stat_write++;
 
4951
                else
 
4952
                        self->st_statistics.st_stat_read++;
 
4953
                self->st_stat_modify = FALSE;
 
4954
                self->st_import_stat = XT_IMP_NO_IMPORT;
 
4955
 
 
4956
                /* If the previous statement was "for update", then set the visibilty
 
4957
                 * so that non- for update SELECTs will see what the for update select
 
4958
                 * (or update statement) just saw.
 
4959
                 */
 
4960
                if (pb_open_tab->ot_for_update)
 
4961
                        self->st_visible_time = self->st_database->db_xn_end_time;
 
4962
        }
 
4963
 
 
4964
        pb_open_tab->ot_for_update =
 
4965
                (lock_type != TL_READ && 
 
4966
                 lock_type != TL_READ_WITH_SHARED_LOCKS &&
 
4967
#ifndef DRIZZLED
 
4968
                 lock_type != TL_READ_HIGH_PRIORITY && 
 
4969
#endif
 
4970
                 lock_type != TL_READ_NO_INSERT);
 
4971
        pb_open_tab->ot_is_modify = FALSE;
 
4972
        if (pb_open_tab->ot_for_update) {
 
4973
                switch ((int) thd_sql_command(thd)) {
 
4974
                        case SQLCOM_UPDATE:
 
4975
                        case SQLCOM_DELETE:
 
4976
#ifndef DRIZZLED
 
4977
                        case SQLCOM_UPDATE_MULTI:
 
4978
                        case SQLCOM_DELETE_MULTI:
 
4979
#endif
 
4980
                        case SQLCOM_REPLACE:
 
4981
                        case SQLCOM_REPLACE_SELECT:
 
4982
                        case SQLCOM_INSERT:
 
4983
                        case SQLCOM_INSERT_SELECT:
 
4984
                                pb_open_tab->ot_is_modify = TRUE;
 
4985
                                self->st_stat_modify = TRUE;
 
4986
                                break;
 
4987
                        case SQLCOM_CREATE_TABLE:
 
4988
                        case SQLCOM_CREATE_INDEX:
 
4989
                        case SQLCOM_ALTER_TABLE:
 
4990
                        case SQLCOM_TRUNCATE:
 
4991
                        case SQLCOM_DROP_TABLE:
 
4992
                        case SQLCOM_DROP_INDEX:
 
4993
                        case SQLCOM_LOAD:
 
4994
#ifndef DRIZZLED
 
4995
                        case SQLCOM_REPAIR:
 
4996
                        case SQLCOM_OPTIMIZE:
 
4997
                                self->st_stat_modify = TRUE;
 
4998
#endif
 
4999
                                break;
 
5000
                }
 
5001
        }
 
5002
 
 
5003
        /* {IS-UPDATE-STAT} This is required at this level!
 
5004
         * No matter how often it is called, it is still the start of a
 
5005
         * statement. We need to make sure statements that are NOT mistaken
 
5006
         * for different type of statement.
 
5007
         *
 
5008
         * Here is an example:
 
5009
         * select * from t1 where data = getcount("bar")
 
5010
         *
 
5011
         * If the procedure getcount() addresses another table.
 
5012
         * then open and close of the statements in getcount()
 
5013
         * are nested within an open close of the select t1
 
5014
         * statement.
 
5015
         */
 
5016
        /* {UPDATE-STACK}
 
5017
         * Add to this I add the following:
 
5018
         * A trigger in the middle of an update also causes nested
 
5019
         * statements. If I reset st_is_update, then then
 
5020
         * when the trigger returns the system thinks we
 
5021
         * are in a different update statement, and may
 
5022
         * update the same row again.
 
5023
         */
 
5024
        if (self->st_is_update == pb_open_tab) {
 
5025
                /* Pop the update stack: */
 
5026
                XTOpenTablePtr curr = pb_open_tab->ot_thread->st_is_update;
 
5027
 
 
5028
                pb_open_tab->ot_thread->st_is_update = curr->ot_prev_update;
 
5029
                curr->ot_prev_update = NULL;
 
5030
        }
 
5031
 
 
5032
        /* See comment {START-TRANS} */
 
5033
        if (!self->st_xact_data) {
 
5034
 
 
5035
                thd_init_xact(thd, self, false);
 
5036
 
 
5037
                if (!xt_xn_begin(self)) {
 
5038
                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
5039
                        goto complete;
 
5040
                }
 
5041
#ifndef DRIZZLED
 
5042
                if (!self->st_auto_commit) {
 
5043
                        trans_register_ha(thd, TRUE, pbxt_hton);
 
5044
                        XT_PRINT0(self, "START CONN XACT - ha_pbxt::start_stmt --> trans_register_ha\n");
 
5045
                }
 
5046
#endif
 
5047
        }
 
5048
 
 
5049
        /* Start a statment (see {START-STAT-HACK}): */
 
5050
#ifndef DRIZZLED
 
5051
        if (!pb_open_tab->ot_thread->st_stat_trans) {
 
5052
                trans_register_ha(pb_mysql_thd, FALSE, pbxt_hton);
 
5053
                XT_PRINT0(pb_open_tab->ot_thread, "START STAT - ha_pbxt::start_stmt --> trans_register_ha\n");
 
5054
                pb_open_tab->ot_thread->st_stat_trans = TRUE;
 
5055
        }
 
5056
#endif
 
5057
        if (pb_open_tab->ot_for_update || self->st_xact_mode < XT_XACT_REPEATABLE_READ)
 
5058
                self->st_visible_time = self->st_database->db_xn_end_time;
 
5059
 
 
5060
        pb_in_stat = TRUE;
 
5061
 
 
5062
        self->st_stat_count++;
 
5063
 
 
5064
        complete:
 
5065
        return err;
 
5066
}
 
5067
 
 
5068
/*
 
5069
 * The idea with handler::store_lock() is the following:
 
5070
 *
 
5071
 * The statement decided which locks we should need for the table
 
5072
 * for updates/deletes/inserts we get WRITE locks, for SELECT... we get
 
5073
 * read locks.
 
5074
 *
 
5075
 * Before adding the lock into the table lock handler (see thr_lock.c)
 
5076
 * mysqld calls store lock with the requested locks. Store lock can now
 
5077
 * modify a write lock to a read lock (or some other lock), ignore the
 
5078
 * lock (if we don't want to use MySQL table locks at all) or add locks
 
5079
 * for many tables (like we do when we are using a MERGE handler).
 
5080
 *
 
5081
 * When releasing locks, store_lock() are also called. In this case one
 
5082
 * usually doesn't have to do anything.
 
5083
 *
 
5084
 * In some exceptional cases MySQL may send a request for a TL_IGNORE;
 
5085
 * This means that we are requesting the same lock as last time and this
 
5086
 * should also be ignored. (This may happen when someone does a flush
 
5087
 * table when we have opened a part of the tables, in which case mysqld
 
5088
 * closes and reopens the tables and tries to get the same locks at last
 
5089
 * time). In the future we will probably try to remove this.
 
5090
 *
 
5091
 * Called from lock.cc by get_lock_data().
 
5092
 */
 
5093
THR_LOCK_DATA **ha_pbxt::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type)
 
5094
{
 
5095
        /*
 
5096
         * TL_READ means concurrent INSERTs are allowed. This is a problem as in this mode
 
5097
         * PBXT is not compatible with MyISAM which allows INSERTs but isolates them from
 
5098
         * current "transaction" (started by LOCK TABLES, ended by UNLOCK TABLES). PBXT 
 
5099
         * used to allow INSERTs and made them visible to the locker (on commit). 
 
5100
         * While MySQL manual doesn't state anything regarding row visibility limitations 
 
5101
         * we choose to convert local locks into normal read locks for better compatibility 
 
5102
         * with MyISAM.
 
5103
         */
 
5104
        if (lock_type == TL_READ)
 
5105
                lock_type = TL_READ_NO_INSERT;
 
5106
 
 
5107
        if (lock_type != TL_IGNORE && pb_lock.type == TL_UNLOCK) {
 
5108
                /* Set to TRUE for operations that require a table lock: */
 
5109
                switch (thd_sql_command(thd)) {
 
5110
                        case SQLCOM_TRUNCATE:
 
5111
                                /* GOTCHA:
 
5112
                                 * The problem is, if I do not do this, then
 
5113
                                 * TRUNCATE TABLE deadlocks with a normal update of the table!
 
5114
                                 * The reason is:
 
5115
                                 *
 
5116
                                 * external_lock() is called before MySQL actually locks the
 
5117
                                 * table. In external_lock(), the table is shared locked,
 
5118
                                 * by indicating that the handler is in use.
 
5119
                                 *
 
5120
                                 * Then later, in delete_all_rows(), a exclusive lock must be
 
5121
                                 * obtained. If an UPDATE or INSERT has also gained a shared
 
5122
                                 * lock in the meantime, then TRUNCATE TABLE hangs.
 
5123
                                 *
 
5124
                                 * By setting pb_lock_table we indicate that an exclusive lock
 
5125
                                 * should be gained in external_lock().
 
5126
                                 *
 
5127
                                 * This is the locking behaviour:
 
5128
                                 *
 
5129
                                 * TRUNCATE TABLE:
 
5130
                                 * XT SHARE LOCK (mysql_lock_tables calls external_lock)
 
5131
                                 * MySQL WRITE LOCK (mysql_lock_tables)
 
5132
                                 * ...
 
5133
                                 * XT EXCLUSIVE LOCK (delete_all_rows)
 
5134
                                 *
 
5135
                                 * INSERT:
 
5136
                                 * XT SHARED LOCK (mysql_lock_tables calls external_lock)
 
5137
                                 * MySQL WRITE_ALLOW_WRITE LOCK (mysql_lock_tables)
 
5138
                                 *
 
5139
                                 * If the locking for INSERT is done in the ... phase
 
5140
                                 * above, then we have a deadlock because 
 
5141
                                 * WRITE_ALLOW_WRITE conflicts with WRITE.
 
5142
                                 *
 
5143
                                 * Making TRUNCATE TABLE take a WRITE_ALLOW_WRITE LOCK, will
 
5144
                                 * not solve the problem because then 2 TRUNCATE TABLES
 
5145
                                 * can deadlock due to lock escalation.
 
5146
                                 *
 
5147
                                 * What may work is if MySQL were to lock BEFORE calling
 
5148
                                 * external_lock()!
 
5149
                                 *
 
5150
                                 * However, using this method, TRUNCATE TABLE does deadlock
 
5151
                                 * with other operations such as ALTER TABLE!
 
5152
                                 *
 
5153
                                 * This is handled with a lock timeout. Assuming 
 
5154
                                 * TRUNCATE TABLE will be mixed with DML this is the
 
5155
                                 * best solution!
 
5156
                                 */
 
5157
                                pb_lock_table = TRUE;
 
5158
                                break;
 
5159
                        default:
 
5160
                                pb_lock_table = FALSE;
 
5161
                                break;
 
5162
                }
 
5163
 
 
5164
#ifdef PBXT_HANDLER_TRACE
 
5165
                pb_lock.type = lock_type;
 
5166
#endif
 
5167
                /* GOTCHA: Before it was OK to weaken the lock after just checking
 
5168
                 * that !thd->in_lock_tables. However, when starting a procedure, MySQL
 
5169
                 * simulates a LOCK TABLES statement.
 
5170
                 *
 
5171
                 * So we need to be more specific here, and check what the actual statement
 
5172
                 * type. Before doing this I got a deadlock (undetected) on the following test.
 
5173
                 * However, now we get a failed assertion in ha_rollback_trans():
 
5174
                 * TODO: Check this with InnoDB!
 
5175
                 *
 
5176
                 * DBUG_ASSERT(0);
 
5177
                 * my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0));
 
5178
                 *
 
5179
                 * drop table if exists t3;
 
5180
                 * create table t3 (a smallint primary key) engine=pbxt;
 
5181
                 * insert into t3 (a) values (40);
 
5182
                 * insert into t3 (a) values (50);
 
5183
                 * 
 
5184
                 * delimiter |
 
5185
                 * 
 
5186
                 * drop function if exists t3_update|
 
5187
                 * 
 
5188
                 * create function t3_update() returns int
 
5189
                 * begin
 
5190
                 *   insert into t3 values (10);
 
5191
                 *   return 100;
 
5192
                 * end|
 
5193
                 * 
 
5194
                 * delimiter ;
 
5195
                 * 
 
5196
                 * CONN 1:
 
5197
                 * 
 
5198
                 * begin;
 
5199
                 * update t3 set a = 5 where a = 50;
 
5200
                 * 
 
5201
                 * CONN 2:
 
5202
                 * 
 
5203
                 * begin;
 
5204
                 * update t3 set a = 4 where a = 40;
 
5205
                 * 
 
5206
                 * CONN 1:
 
5207
                 * 
 
5208
                 * update t3 set a = 4 where a = 40; // Hangs waiting CONN 2.
 
5209
                 * 
 
5210
                 * CONN 2:
 
5211
                 * 
 
5212
                 * select t3_update(); // Hangs waiting for table lock.
 
5213
                 * 
 
5214
                 */
 
5215
                if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && lock_type <= TL_WRITE) && 
 
5216
#ifndef DRIZZLED
 
5217
                        !(thd_in_lock_tables(thd) && thd_sql_command(thd) == SQLCOM_LOCK_TABLES) &&
 
5218
#endif
 
5219
                        !thd_tablespace_op(thd) &&
 
5220
                        thd_sql_command(thd) != SQLCOM_TRUNCATE &&
 
5221
#ifndef DRIZZLED
 
5222
                        thd_sql_command(thd) != SQLCOM_OPTIMIZE &&
 
5223
#endif
 
5224
                        thd_sql_command(thd) != SQLCOM_CREATE_TABLE) {
 
5225
                        lock_type = TL_WRITE_ALLOW_WRITE;
 
5226
                }
 
5227
 
 
5228
                /* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
 
5229
                 * MySQL would use the lock TL_READ_NO_INSERT on t2, and that
 
5230
                 * would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
 
5231
                 * to t2. Convert the lock to a normal read lock to allow
 
5232
                 * concurrent inserts to t2.
 
5233
                 * 
 
5234
                 * (This one from InnoDB)
 
5235
 
 
5236
                 * Stewart: removed SQLCOM_CALL, not sure of implications.
 
5237
                 */
 
5238
                if (lock_type == TL_READ_NO_INSERT
 
5239
#ifndef DRIZZLED
 
5240
                        && (!thd_in_lock_tables(thd)
 
5241
                         || thd_sql_command(thd) == SQLCOM_CALL
 
5242
                        )
 
5243
#endif
 
5244
                        )
 
5245
                {
 
5246
                        lock_type = TL_READ;
 
5247
                }
 
5248
 
 
5249
                XT_PRINT3(xt_get_self(), "store_lock (%s) %d->%d\n", pb_share->sh_table_path->ps_path, pb_lock.type, lock_type);
 
5250
                pb_lock.type = lock_type;
 
5251
        }
 
5252
#ifdef PBXT_HANDLER_TRACE
 
5253
        else {
 
5254
                XT_PRINT3(xt_get_self(), "store_lock (%s) %d->%d (ignore/unlock)\n", pb_share->sh_table_path->ps_path, lock_type, lock_type);
 
5255
        }
 
5256
#endif
 
5257
        *to++= &pb_lock;
 
5258
        return to;
 
5259
}
 
5260
 
 
5261
/*
 
5262
 * Used to delete a table. By the time delete_table() has been called all
 
5263
 * opened references to this table will have been closed (and your globally
 
5264
 * shared references released. The variable name will just be the name of
 
5265
 * the table. You will need to remove any files you have created at this point.
 
5266
 *
 
5267
 * Called from handler.cc by delete_table and ha_create_table(). Only used
 
5268
 * during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
 
5269
 * the storage engine.
 
5270
*/
 
5271
#ifdef DRIZZLED
 
5272
int PBXTStorageEngine::doDropTable(Session &, TableIdentifier& ident)
 
5273
{
 
5274
        const std::string& path = ident.getPath();
 
5275
        const char *table_path = path.c_str();
 
5276
#else
 
5277
int ha_pbxt::delete_table(const char *table_path)
 
5278
{
 
5279
#endif
 
5280
        THD                             *thd = current_thd;
 
5281
        int                             err = 0;
 
5282
        XTThreadPtr             self = NULL;
 
5283
        XTSharePtr              share;
 
5284
 
 
5285
        STAT_TRACE(self, *thd_query(thd));
 
5286
        XT_PRINT1(self, "delete_table (%s)\n", table_path);
 
5287
 
 
5288
        if (XTSystemTableShare::isSystemTable(table_path))
 
5289
                return delete_system_table(table_path);
 
5290
 
 
5291
        if (!(self = ha_set_current_thread(thd, &err)))
 
5292
                return xt_ha_pbxt_to_mysql_error(err);
 
5293
 
 
5294
        self->st_ignore_fkeys = (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) != 0;
 
5295
 
 
5296
        try_(a) {
 
5297
                xt_ha_open_database_of_table(self, (XTPathStrPtr) table_path);
 
5298
 
 
5299
                ASSERT(xt_get_self() == self);
 
5300
                try_(b) {
 
5301
                        /* NOTE: MySQL does not drop a table by first locking it!
 
5302
                         * We also cannot use pb_share because the handler used
 
5303
                         * to delete a table is not openned correctly.
 
5304
                         */
 
5305
                        share = ha_get_share(self, table_path, false);
 
5306
                        pushr_(ha_unget_share, share);
 
5307
                        ha_aquire_exclusive_use(self, share, NULL);
 
5308
                        pushr_(ha_release_exclusive_use, share);
 
5309
                        ha_close_open_tables(self, share, NULL);
 
5310
 
 
5311
                        xt_drop_table(self, (XTPathStrPtr) table_path, thd_sql_command(thd) == SQLCOM_DROP_DB);
 
5312
 
 
5313
                        freer_(); // ha_release_exclusive_use(share)
 
5314
                        freer_(); // ha_unget_share(share)
 
5315
                }
 
5316
                catch_(b) {
 
5317
                        /* In MySQL if the table does not exist, just log the error and continue. This is
 
5318
                         * needed to delete table in the case when CREATE TABLE fails and no PBXT disk
 
5319
                         * structures were created. 
 
5320
                         * Drizzle unlike MySQL iterates over all handlers and tries to delete table. It
 
5321
                         * stops after when a handler returns TRUE, so in Drizzle we need to report error.  
 
5322
                         */
 
5323
#ifndef DRIZZLED
 
5324
                        if (self->t_exception.e_xt_err == XT_ERR_TABLE_NOT_FOUND)
 
5325
                                xt_log_and_clear_exception(self);
 
5326
                        else
 
5327
#endif
 
5328
                                throw_();
 
5329
                }
 
5330
                cont_(b);
 
5331
 
 
5332
                /*
 
5333
                 * If there are no more PBXT tables in the database, we
 
5334
                 * "drop the database", which deletes all PBXT resources
 
5335
                 * in the database.
 
5336
                 */
 
5337
                /* We now only drop the pbxt system data,
 
5338
                 * when the PBXT database is dropped.
 
5339
                 */
 
5340
#ifndef XT_USE_GLOBAL_DB
 
5341
                if (!xt_table_exists(self->st_database)) {
 
5342
                        xt_ha_all_threads_close_database(self, self->st_database);
 
5343
                        xt_drop_database(self, self->st_database);
 
5344
                        xt_unuse_database(self, self);
 
5345
                        xt_ha_close_global_database(self);
 
5346
                }
 
5347
#endif
 
5348
        }
 
5349
        catch_(a) {
 
5350
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5351
#ifdef DRIZZLED
 
5352
                if (err == HA_ERR_NO_SUCH_TABLE)
 
5353
                        err = ENOENT;
 
5354
#endif
 
5355
        }
 
5356
        cont_(a);
 
5357
        
 
5358
#ifdef PBMS_ENABLED
 
5359
        /* Call pbms_delete_table_with_blobs() last because it cannot be undone. */
 
5360
        if (!err) {
 
5361
                PBMSResultRec result;
 
5362
 
 
5363
                if (pbms_delete_table_with_blobs(table_path, &result)) {
 
5364
                        xt_logf(XT_NT_WARNING, "pbms_delete_table_with_blobs() Error: %s", result.mr_message);
 
5365
                }
 
5366
                
 
5367
                pbms_completed(NULL, true);
 
5368
        }
 
5369
#endif
 
5370
 
 
5371
        return err;
 
5372
}
 
5373
 
 
5374
#ifdef DRIZZLED
 
5375
int PBXTStorageEngine::delete_system_table(const char *table_path)
 
5376
#else
 
5377
int ha_pbxt::delete_system_table(const char *table_path)
 
5378
#endif
 
5379
{
 
5380
        THD                             *thd = current_thd;
 
5381
        XTExceptionRec  e;
 
5382
        int                             err = 0;
 
5383
        XTThreadPtr             self;
 
5384
 
 
5385
        if (!(self = xt_ha_set_current_thread(thd, &e)))
 
5386
                return xt_ha_pbxt_to_mysql_error(e.e_xt_err);
 
5387
 
 
5388
        try_(a) {
 
5389
                xt_ha_open_database_of_table(self, (XTPathStrPtr) table_path);
 
5390
 
 
5391
                if (xt_table_exists(self->st_database))
 
5392
                        xt_throw_xterr(XT_CONTEXT, XT_ERR_PBXT_TABLE_EXISTS);
 
5393
 
 
5394
                XTSystemTableShare::setSystemTableDeleted(table_path);
 
5395
 
 
5396
                if (!XTSystemTableShare::doesSystemTableExist()) {
 
5397
                        xt_ha_all_threads_close_database(self, self->st_database);
 
5398
                        xt_drop_database(self, self->st_database);
 
5399
                        xt_unuse_database(self, self);
 
5400
                        xt_ha_close_global_database(self);
 
5401
                }
 
5402
        }
 
5403
        catch_(a) {
 
5404
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5405
        }
 
5406
        cont_(a);
 
5407
 
 
5408
        return err;
 
5409
}
 
5410
 
 
5411
/*
 
5412
 * Renames a table from one name to another from alter table call.
 
5413
 * This function can be used to move a table from one database to
 
5414
 * another.
 
5415
 */
 
5416
#ifdef DRIZZLED
 
5417
int PBXTStorageEngine::doRenameTable(Session&,
 
5418
                                     TableIdentifier& from_ident,
 
5419
                                     TableIdentifier& to_ident)
 
5420
{
 
5421
        const char *from = from_ident.getPath().c_str();
 
5422
        const char *to = to_ident.getPath().c_str();
 
5423
#else
 
5424
int ha_pbxt::rename_table(const char *from, const char *to)
 
5425
{
 
5426
#endif
 
5427
        THD                             *thd = current_thd;
 
5428
        int                             err = 0;
 
5429
        XTThreadPtr             self;
 
5430
        XTSharePtr              share;
 
5431
        XTDatabaseHPtr  to_db;
 
5432
 
 
5433
        if (XTSystemTableShare::isSystemTable(from))
 
5434
                return rename_system_table(from, to);
 
5435
 
 
5436
        if (!(self = ha_set_current_thread(thd, &err)))
 
5437
                return xt_ha_pbxt_to_mysql_error(err);
 
5438
 
 
5439
        XT_PRINT2(self, "rename_table (%s -> %s)\n", from, to);
 
5440
 
 
5441
#ifdef PBMS_ENABLED
 
5442
        PBMSResultRec result;
 
5443
 
 
5444
        err = pbms_rename_table_with_blobs(from, to, &result);
 
5445
        if (err) {
 
5446
                xt_logf(XT_NT_ERROR, "pbms_rename_table_with_blobs() Error: %s", result.mr_message);
 
5447
                return err;
 
5448
        }
 
5449
#endif
 
5450
 
 
5451
        try_(a) {
 
5452
                xt_ha_open_database_of_table(self, (XTPathStrPtr) to);
 
5453
                to_db = self->st_database;
 
5454
 
 
5455
                xt_ha_open_database_of_table(self, (XTPathStrPtr) from);
 
5456
 
 
5457
                if (self->st_database != to_db)
 
5458
                        xt_throw_xterr(XT_CONTEXT, XT_ERR_CANNOT_CHANGE_DB);
 
5459
 
 
5460
                /*
 
5461
                 * NOTE: MySQL does not lock before calling rename table!
 
5462
                 *
 
5463
                 * We cannot use pb_share because rename_table() is
 
5464
                 * called without correctly initializing
 
5465
                 * the handler!
 
5466
                 */
 
5467
                share = ha_get_share(self, from, true);
 
5468
                pushr_(ha_unget_share, share);
 
5469
                ha_aquire_exclusive_use(self, share, NULL);
 
5470
                pushr_(ha_release_exclusive_use, share);
 
5471
                ha_close_open_tables(self, share, NULL);
 
5472
 
 
5473
                self->st_ignore_fkeys = (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) != 0;
 
5474
                xt_rename_table(self, (XTPathStrPtr) from, (XTPathStrPtr) to);
 
5475
 
 
5476
                freer_(); // ha_release_exclusive_use(share)
 
5477
                freer_(); // ha_unget_share(share)
 
5478
 
 
5479
                /*
 
5480
                 * If there are no more PBXT tables in the database, we
 
5481
                 * "drop the database", which deletes all PBXT resources
 
5482
                 * in the database.
 
5483
                 */
 
5484
#ifdef XT_USE_GLOBAL_DB
 
5485
                /* We now only drop the pbxt system data,
 
5486
                 * when the PBXT database is dropped.
 
5487
                 */
 
5488
                if (!xt_table_exists(self->st_database)) {
 
5489
                        xt_ha_all_threads_close_database(self, self->st_database);
 
5490
                        xt_drop_database(self, self->st_database);
 
5491
                }
 
5492
#endif
 
5493
        }
 
5494
        catch_(a) {
 
5495
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5496
        }
 
5497
        cont_(a);
 
5498
        
 
5499
#ifdef PBMS_ENABLED
 
5500
        pbms_completed(NULL, (err == 0));
 
5501
#endif
 
5502
 
 
5503
        XT_RETURN(err);
 
5504
}
 
5505
 
 
5506
#ifdef DRIZZLED
 
5507
int PBXTStorageEngine::rename_system_table(const char *XT_UNUSED(from), const char *XT_UNUSED(to))
 
5508
#else
 
5509
int ha_pbxt::rename_system_table(const char *XT_UNUSED(from), const char *XT_UNUSED(to))
 
5510
#endif
 
5511
{
 
5512
        return ER_NOT_SUPPORTED_YET;
 
5513
}
 
5514
 
 
5515
uint ha_pbxt::max_supported_key_length() const
 
5516
{
 
5517
        return XT_INDEX_MAX_KEY_SIZE;
 
5518
}
 
5519
 
 
5520
uint ha_pbxt::max_supported_key_part_length() const
 
5521
{
 
5522
        /* There is a little overhead in order to fit! */
 
5523
        return XT_INDEX_MAX_KEY_SIZE-4;
 
5524
}
 
5525
 
 
5526
/*
 
5527
 * Called in test_quick_select to determine if indexes should be used.
 
5528
 *
 
5529
 * As far as I can tell, time is measured in "disk reads". So the
 
5530
 * calculation below means the system reads about 20 rows per read.
 
5531
 *
 
5532
 * For example a sequence scan uses a read buffer which reads a
 
5533
 * number of rows at once, or a sequential scan can make use
 
5534
 * of the cache (so it need to read less).
 
5535
 */
 
5536
double ha_pbxt::scan_time()
 
5537
{
 
5538
        double result = (double) (stats.records + stats.deleted) / 38.0 + 2;
 
5539
        return result;
 
5540
}
 
5541
 
 
5542
/*
 
5543
 * The next method will never be called if you do not implement indexes.
 
5544
 */
 
5545
double ha_pbxt::read_time(uint XT_UNUSED(index), uint ranges, ha_rows rows)
 
5546
{
 
5547
        double result = rows2double(ranges+rows);
 
5548
        return result;
 
5549
}
 
5550
 
 
5551
/*
 
5552
 * Given a starting key, and an ending key estimate the number of rows that
 
5553
 * will exist between the two. end_key may be empty which in case determine
 
5554
 * if start_key matches any rows.
 
5555
 * 
 
5556
 * Called from opt_range.cc by check_quick_keys().
 
5557
 *
 
5558
 */
 
5559
ha_rows ha_pbxt::records_in_range(uint inx, key_range *min_key, key_range *max_key)
 
5560
{
 
5561
        XTIndexPtr              ind;
 
5562
        key_part_map    keypart_map;
 
5563
        u_int                   segement = 0;
 
5564
        ha_rows                 result;
 
5565
 
 
5566
        if (min_key)
 
5567
                keypart_map = min_key->keypart_map;
 
5568
        else if (max_key)
 
5569
                keypart_map = max_key->keypart_map;
 
5570
        else
 
5571
                return 1;
 
5572
        ind = (XTIndexPtr) pb_share->sh_dic_keys[inx];
 
5573
        
 
5574
        while (keypart_map & 1) {
 
5575
                segement++;
 
5576
                keypart_map = keypart_map >> 1;
 
5577
        }
 
5578
 
 
5579
        if (segement < 1 || segement > ind->mi_seg_count)
 
5580
                result = 1;
 
5581
        else
 
5582
                result = ind->mi_seg[segement-1].is_recs_in_range;
 
5583
#ifdef XT_PRINT_INDEX_OPT
 
5584
        printf("records_in_range %s index %d cols req=%d/%d read_bits=%X write_bits=%X index_bits=%X --> %d\n", pb_open_tab->ot_table->tab_name->ps_path, (int) inx, segement, ind->mi_seg_count, (int) *table->read_set->bitmap, (int) *table->write_set->bitmap, (int) *ind->mi_col_map.bitmap, (int) result);
 
5585
#endif
 
5586
        return result;
 
5587
}
 
5588
 
 
5589
/*
 
5590
 * create() is called to create a table/database. The variable name will have the name
 
5591
 * of the table. When create() is called you do not need to worry about opening
 
5592
 * the table. Also, the FRM file will have already been created so adjusting
 
5593
 * create_info will not do you any good. You can overwrite the frm file at this
 
5594
 * point if you wish to change the table definition, but there are no methods
 
5595
 * currently provided for doing that.
 
5596
 
 
5597
 * Called from handle.cc by ha_create_table().
 
5598
*/
 
5599
#ifdef DRIZZLED
 
5600
int PBXTStorageEngine::doCreateTable(Session&, 
 
5601
                                     Table& table_arg, 
 
5602
                                     TableIdentifier& ident,
 
5603
                                     drizzled::message::Table& proto)
 
5604
{
 
5605
        const std::string& path = ident.getPath();
 
5606
        const char *table_path = path.c_str();
 
5607
#else
 
5608
int ha_pbxt::create(const char *table_path, TABLE *table_arg, HA_CREATE_INFO *create_info)
 
5609
{
 
5610
#endif
 
5611
        THD                             *thd = current_thd;
 
5612
        int                             err = 0;
 
5613
        XTThreadPtr             self;
 
5614
        XTDDTable               *tab_def = NULL;
 
5615
        XTDictionaryRec dic, source_dic;
 
5616
 
 
5617
        if ((strcmp(table_path, "./pbxt/location") == 0) || 
 
5618
                (strcmp(table_path, "./pbxt/tables") == 0) ||
 
5619
                (strcmp(table_path, "./pbxt/statistics") == 0))
 
5620
                return 0;
 
5621
 
 
5622
        if ((strcmp(table_path, "./pbxt/location") == 0) || (strcmp(table_path, "./pbxt/statistics") == 0))
 
5623
                return 0;
 
5624
 
 
5625
        memset(&dic, 0, sizeof(dic));
 
5626
        memset(&source_dic, 0, sizeof(source_dic));
 
5627
 
 
5628
        if (!(self = ha_set_current_thread(thd, &err)))
 
5629
                return xt_ha_pbxt_to_mysql_error(err);
 
5630
#ifdef DRIZZLED
 
5631
        XT_PRINT2(self, "create (%s) %s\n", table_path, (proto.type() == message::Table::TEMPORARY) ? "temporary" : "");
 
5632
        switch(ident.getType()) {
 
5633
                case message::Table::STANDARD:
 
5634
                        dic.dic_table_type = XT_TABLE_TYPE_STANDARD;
 
5635
                        break;
 
5636
 
 
5637
                case message::Table::TEMPORARY:
 
5638
                        dic.dic_table_type = XT_TABLE_TYPE_TEMPORARY;
 
5639
                        break;
 
5640
 
 
5641
                case message::Table::INTERNAL:
 
5642
                        dic.dic_table_type = XT_TABLE_TYPE_INTERNAL;
 
5643
                        break;
 
5644
 
 
5645
                case message::Table::FUNCTION:
 
5646
                        dic.dic_table_type = XT_TABLE_TYPE_FUNCTION;
 
5647
                        break;
 
5648
        }
 
5649
#else
 
5650
        XT_PRINT2(self, "create (%s) %s\n", table_path, (create_info->options & HA_LEX_CREATE_TMP_TABLE) ? "temporary" : "");
 
5651
#endif
 
5652
 
 
5653
        STAT_TRACE(self, *thd_query(thd));
 
5654
 
 
5655
        try_(a) {
 
5656
                xt_ha_open_database_of_table(self, (XTPathStrPtr) table_path);
 
5657
 
 
5658
#ifdef DRIZZLED
 
5659
                for (uint i=0; i<TS(&table_arg)->keys; i++) {
 
5660
                        if (table_arg.key_info[i].key_length > XT_INDEX_MAX_KEY_SIZE)
 
5661
                                xt_throw_sulxterr(XT_CONTEXT, XT_ERR_KEY_TOO_LARGE, table_arg.key_info[i].name, (u_long) XT_INDEX_MAX_KEY_SIZE);
 
5662
                }
 
5663
#else
 
5664
                for (uint i=0; i<TS(table_arg)->keys; i++) {
 
5665
                        if (table_arg->key_info[i].key_length > XT_INDEX_MAX_KEY_SIZE)
 
5666
                                xt_throw_sulxterr(XT_CONTEXT, XT_ERR_KEY_TOO_LARGE, table_arg->key_info[i].name, (u_long) XT_INDEX_MAX_KEY_SIZE);
 
5667
                }
 
5668
#endif
 
5669
 
 
5670
                /* ($) auto_increment_value will be zero if 
 
5671
                 * AUTO_INCREMENT is not used. Otherwise
 
5672
                 * Query was ALTER TABLE ... AUTO_INCREMENT = x; or 
 
5673
                 * CREATE TABLE ... AUTO_INCREMENT = x;
 
5674
                 */
 
5675
#ifdef XT_USE_DEFAULT_MEMORY_TABS
 
5676
                if (create_info->storage_media == HA_SM_DEFAULT)
 
5677
                        source_dic.dic_tab_flags |= XT_TF_MEMORY_TABLE;
 
5678
#endif
 
5679
 
 
5680
#ifdef DRIZZLED
 
5681
                StorageEngine::writeDefinitionFromPath(ident, proto);
 
5682
 
 
5683
                tab_def = xt_ri_create_table(self, true, (XTPathStrPtr) table_path, const_cast<char *>(thd->getQueryString().c_str()), myxt_create_table_from_table(self, &table_arg), &source_dic);
 
5684
                tab_def->checkForeignKeys(self, proto.type() == message::Table::TEMPORARY);
 
5685
#else
 
5686
                // tab_def = xt_ri_create_table(self, true, (XTPathStrPtr) table_path, *thd_query(thd), myxt_create_table_from_table(self, table_arg));
 
5687
                tab_def = xt_ri_create_table(self, true, (XTPathStrPtr) table_path, *thd_query(thd), myxt_create_table_from_table(self, table_arg), &source_dic);
 
5688
                tab_def->checkForeignKeys(self, create_info->options & HA_LEX_CREATE_TMP_TABLE);
 
5689
                dic.dic_table_type = XT_TABLE_TYPE_STANDARD;
 
5690
#endif
 
5691
 
 
5692
                dic.dic_table = tab_def;
 
5693
#ifdef DRIZZLED
 
5694
                dic.dic_my_table = &table_arg;
 
5695
                dic.dic_tab_flags = source_dic.dic_tab_flags;
 
5696
                //if (create_info.storage_media == HA_SM_MEMORY)
 
5697
                //      dic.dic_tab_flags |= XT_TF_MEMORY_TABLE;
 
5698
                if (proto.type() == message::Table::TEMPORARY)
 
5699
                        dic.dic_tab_flags |= XT_TF_REAL_TEMP_TABLE;
 
5700
                if (myxt_temp_table_name(table_path))
 
5701
                        dic.dic_tab_flags |= XT_TF_DDL_TEMP_TABLE;
 
5702
 
 
5703
                dic.dic_min_auto_inc = (xtWord8) proto.options().auto_increment_value(); /* ($) */
 
5704
                dic.dic_def_ave_row_size =  proto.options().avg_row_length();
 
5705
#else
 
5706
                dic.dic_my_table = table_arg;
 
5707
                dic.dic_tab_flags = source_dic.dic_tab_flags;
 
5708
 
 
5709
                if (create_info->storage_media == HA_SM_MEMORY)
 
5710
                        dic.dic_tab_flags |= XT_TF_MEMORY_TABLE;
 
5711
                if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
 
5712
                        dic.dic_tab_flags |= XT_TF_REAL_TEMP_TABLE;
 
5713
                if (myxt_temp_table_name(table_path))
 
5714
                        dic.dic_tab_flags |= XT_TF_DDL_TEMP_TABLE;
 
5715
 
 
5716
                dic.dic_min_auto_inc = (xtWord8) create_info->auto_increment_value; /* ($) */
 
5717
                dic.dic_def_ave_row_size = (xtWord8) table_arg->s->avg_row_length;
 
5718
#endif
 
5719
                myxt_setup_dictionary(self, &dic);
 
5720
 
 
5721
                /*
 
5722
                 * We used to ignore the value of foreign_key_checks flag and allowed creation
 
5723
                 * of tables with "hanging" references. Now we validate FKs if foreign_key_checks != 0
 
5724
                 */
 
5725
                self->st_ignore_fkeys = (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) != 0;
 
5726
 
 
5727
                /*
 
5728
                 * Previously I set delete_if_exists=TRUE because
 
5729
                 * CREATE TABLE was being used to TRUNCATE.
 
5730
                 * This was due to the flag HTON_CAN_RECREATE.
 
5731
                 * Now I could set delete_if_exists=FALSE, but
 
5732
                 * leaving it TRUE should not cause any problems.
 
5733
                 */
 
5734
                xt_create_table(self, (XTPathStrPtr) table_path, &dic);
 
5735
        }
 
5736
        catch_(a) {
 
5737
                if (tab_def)
 
5738
                        tab_def->finalize(self);
 
5739
                dic.dic_table = NULL;
 
5740
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5741
        }
 
5742
        cont_(a);
 
5743
 
 
5744
        /* Free the dictionary, but not 'table_arg'! */
 
5745
        dic.dic_my_table = NULL;
 
5746
        myxt_free_dictionary(self, &dic);
 
5747
 
 
5748
        XT_RETURN(err);
 
5749
}
 
5750
 
 
5751
void ha_pbxt::update_create_info(HA_CREATE_INFO *create_info)
 
5752
{
 
5753
        XTOpenTablePtr  ot;
 
5754
 
 
5755
        if ((ot = pb_open_tab)) {
 
5756
                if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
 
5757
                        /* Fill in the minimum auto-increment value! */
 
5758
                        create_info->auto_increment_value = ot->ot_table->tab_dic.dic_min_auto_inc;
 
5759
                }
 
5760
        }
 
5761
}
 
5762
 
 
5763
#ifdef DRIZZLED
 
5764
int PBXTStorageEngine::doStartTransaction(Session *thd, start_transaction_option_t XT_UNUSED(options))
 
5765
{
 
5766
        int err = 0;
 
5767
        XTThreadPtr self = ha_set_current_thread(thd, &err);    
 
5768
 
 
5769
        XT_PRINT0(self, "PBXTStorageEngine::doStartTransaction\n");
 
5770
 
 
5771
        /* Transaction mode numbers must be identical! */
 
5772
        (void) ASSERT_NS(ISO_READ_UNCOMMITTED == XT_XACT_UNCOMMITTED_READ);
 
5773
        (void) ASSERT_NS(ISO_SERIALIZABLE == XT_XACT_SERIALIZABLE);
 
5774
 
 
5775
        self->st_xact_mode = thd_tx_isolation(thd) <= ISO_READ_COMMITTED ? XT_XACT_COMMITTED_READ : XT_XACT_REPEATABLE_READ;
 
5776
        self->st_ignore_fkeys = (thd_test_options(thd,OPTION_NO_FOREIGN_KEY_CHECKS)) != 0;
 
5777
        self->st_auto_commit = (thd_test_options(thd, (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) == 0;
 
5778
        self->st_table_trans = FALSE;
 
5779
        self->st_abort_trans = FALSE;
 
5780
        self->st_stat_ended = FALSE;
 
5781
        self->st_stat_trans = FALSE;
 
5782
        xt_xres_wait_for_recovery(self, XT_RECOVER_SWEPT);
 
5783
 
 
5784
        if (!self->st_database)
 
5785
                xt_ha_open_database_of_table(self, NULL);
 
5786
 
 
5787
        if (!xt_xn_begin(self)) {
 
5788
                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, /*pb_ignore_dup_key*/false);
 
5789
                        //pb_ex_in_use = 0;
 
5790
        }
 
5791
 
 
5792
        return err;
 
5793
}
 
5794
 
 
5795
int PBXTStorageEngine::doSetSavepoint(drizzled::Session* thd, drizzled::NamedSavepoint&)
 
5796
 
5797
        return xt_ha_pbxt_thread_error_for_mysql(thd, xt_ha_thd_to_self(thd), false); 
 
5798
}
 
5799
        
 
5800
int PBXTStorageEngine::doRollbackToSavepoint(drizzled::Session* thd, drizzled::NamedSavepoint&) 
 
5801
{
 
5802
        return xt_ha_pbxt_thread_error_for_mysql(thd, xt_ha_thd_to_self(thd), false);
 
5803
}
 
5804
 
 
5805
int PBXTStorageEngine::doReleaseSavepoint(drizzled::Session* thd, drizzled::NamedSavepoint&) 
 
5806
{
 
5807
        return xt_ha_pbxt_thread_error_for_mysql(thd, xt_ha_thd_to_self(thd), false);
 
5808
}
 
5809
 
 
5810
int PBXTStorageEngine::doCommit(drizzled::Session* thd, bool)
 
5811
{
 
5812
        int err = 0;
 
5813
        XTThreadPtr self = (XTThreadPtr) *thd->getEngineData(pbxt_hton);
 
5814
 
 
5815
        bool real_commit = !session_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
 
5816
        
 
5817
        XT_PRINT1(self, "PBXTStorageEngine::doCommit(real_commit = %s)\n", real_commit ? "true" : "false");
 
5818
 
 
5819
        if (real_commit && self) {
 
5820
                if (!xt_xn_commit(self))
 
5821
                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5822
        }
 
5823
 
 
5824
        return err;
 
5825
}
 
5826
 
 
5827
int PBXTStorageEngine::doRollback(drizzled::Session* thd, bool)
 
5828
{
 
5829
        int err = 0;
 
5830
        XTThreadPtr self = (XTThreadPtr) *thd->getEngineData(pbxt_hton);
 
5831
 
 
5832
        bool real_commit = !session_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN);
 
5833
 
 
5834
        XT_PRINT1(self, "PBXTStorageEngine::doRollback(real_commit = %s)\n", real_commit ? "true" : "false");
 
5835
 
 
5836
        if (real_commit && self) {
 
5837
                if (!xt_xn_rollback(self))
 
5838
                        err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE);
 
5839
        }
 
5840
 
 
5841
        return err;
 
5842
}
 
5843
 
 
5844
void PBXTStorageEngine::doGetTableIdentifiers(drizzled::CachedDirectory &directory,
 
5845
                                           drizzled::SchemaIdentifier &schema_identifier,
 
5846
                                           drizzled::TableIdentifiers &set_of_identifiers)
 
5847
{
 
5848
  CachedDirectory::Entries entries= directory.getEntries();
 
5849
 
 
5850
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin();
 
5851
       entry_iter != entries.end(); ++entry_iter)
 
5852
  {
 
5853
    CachedDirectory::Entry *entry= *entry_iter;
 
5854
    const std::string *filename= &entry->filename;
 
5855
 
 
5856
    assert(filename->size());
 
5857
 
 
5858
    const char *ext= strchr(filename->c_str(), '.');
 
5859
 
 
5860
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, DEFAULT_FILE_EXTENSION) ||
 
5861
        (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
 
5862
    { }
 
5863
    else
 
5864
    {
 
5865
      char uname[NAME_LEN + 1];
 
5866
      uint32_t file_name_len;
 
5867
 
 
5868
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
 
5869
      // TODO: Remove need for memory copy here
 
5870
      uname[file_name_len - sizeof(DEFAULT_FILE_EXTENSION) + 1]= '\0'; // Subtract ending, place NULL 
 
5871
 
 
5872
      set_of_identifiers.push_back(TableIdentifier(schema_identifier, uname));
 
5873
    }
 
5874
  }
 
5875
}
 
5876
 
 
5877
void PBXTStorageEngine::doGetTableNames(
 
5878
        CachedDirectory &directory, 
 
5879
        SchemaIdentifier&, 
 
5880
        std::set<std::string>& set_of_names)
 
5881
{
 
5882
  CachedDirectory::Entries entries= directory.getEntries();
 
5883
 
 
5884
  for (CachedDirectory::Entries::iterator entry_iter= entries.begin();
 
5885
       entry_iter != entries.end(); ++entry_iter)
 
5886
  {
 
5887
    CachedDirectory::Entry *entry= *entry_iter;
 
5888
    const std::string *filename= &entry->filename;
 
5889
 
 
5890
    assert(filename->size());
 
5891
 
 
5892
    const char *ext= strchr(filename->c_str(), '.');
 
5893
 
 
5894
    if (ext == NULL || my_strcasecmp(system_charset_info, ext, DEFAULT_FILE_EXTENSION) ||
 
5895
        (filename->compare(0, strlen(TMP_FILE_PREFIX), TMP_FILE_PREFIX) == 0))
 
5896
    { }
 
5897
    else
 
5898
    {
 
5899
      char uname[NAME_LEN + 1];
 
5900
      uint32_t file_name_len;
 
5901
 
 
5902
      file_name_len= filename_to_tablename(filename->c_str(), uname, sizeof(uname));
 
5903
      // TODO: Remove need for memory copy here
 
5904
      uname[file_name_len - sizeof(DEFAULT_FILE_EXTENSION) + 1]= '\0'; // Subtract ending, place NULL 
 
5905
      set_of_names.insert(uname);
 
5906
    }
 
5907
  }
 
5908
}
 
5909
 
 
5910
bool PBXTStorageEngine::doDoesTableExist(Session&, TableIdentifier &identifier)
 
5911
{
 
5912
  std::string proto_path(identifier.getPath());
 
5913
  proto_path.append(DEFAULT_FILE_EXTENSION);
 
5914
 
 
5915
  if (access(proto_path.c_str(), F_OK))
 
5916
  {
 
5917
    return false;
 
5918
  }
 
5919
 
 
5920
  return true;
 
5921
}
 
5922
 
 
5923
#endif // DRIZZLED
 
5924
 
 
5925
char *ha_pbxt::get_foreign_key_create_info()
 
5926
{
 
5927
        THD                                     *thd = current_thd;
 
5928
        int                                     err = 0;
 
5929
        XTThreadPtr                     self;
 
5930
        XTStringBufferRec       tab_def = { 0, 0, 0 };
 
5931
 
 
5932
        if (!(self = ha_set_current_thread(thd, &err))) {
 
5933
                xt_ha_pbxt_to_mysql_error(err);
 
5934
                return NULL;
 
5935
        }
 
5936
 
 
5937
        if (!pb_open_tab) {
 
5938
                if ((err = reopen()))
 
5939
                        return NULL;
 
5940
        }
 
5941
 
 
5942
        if (!pb_open_tab->ot_table->tab_dic.dic_table)
 
5943
                return NULL;
 
5944
 
 
5945
        try_(a) {
 
5946
                pb_open_tab->ot_table->tab_dic.dic_table->loadForeignKeyString(self, &tab_def);
 
5947
        }
 
5948
        catch_(a) {
 
5949
                xt_sb_set_size(self, &tab_def, 0);
 
5950
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
5951
        }
 
5952
        cont_(a);
 
5953
 
 
5954
        return tab_def.sb_cstring;
 
5955
}
 
5956
 
 
5957
void ha_pbxt::free_foreign_key_create_info(char* str)
 
5958
{
 
5959
        xt_free(NULL, str);
 
5960
}
 
5961
 
 
5962
bool ha_pbxt::get_error_message(int XT_UNUSED(error), String *buf)
 
5963
{
 
5964
        THD                             *thd = current_thd;
 
5965
        int                             err = 0;
 
5966
        XTThreadPtr             self;
 
5967
 
 
5968
        if (!(self = ha_set_current_thread(thd, &err)))
 
5969
                return FALSE;
 
5970
 
 
5971
        if (!self->t_exception.e_xt_err)
 
5972
                return FALSE;
 
5973
 
 
5974
        buf->copy(self->t_exception.e_err_msg, (uint32_t) strlen(self->t_exception.e_err_msg), system_charset_info);
 
5975
        return TRUE;
 
5976
}
 
5977
 
 
5978
/* 
 
5979
 * get info about FKs of the currently open table
 
5980
 * used in 
 
5981
 * 1. REPLACE; is > 0 if table is referred by a FOREIGN KEY 
 
5982
 * 2. INFORMATION_SCHEMA tables: TABLE_CONSTRAINTS, REFERENTIAL_CONSTRAINTS
 
5983
 * Return value: as of 5.1.24 it's ignored
 
5984
 */
 
5985
#ifdef DRI_IS
 
5986
int ha_pbxt::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
 
5987
{
 
5988
        int err = 0;
 
5989
        XTThreadPtr     self;
 
5990
        const char *action;
 
5991
 
 
5992
        if (!(self = ha_set_current_thread(thd, &err))) {
 
5993
                return xt_ha_pbxt_to_mysql_error(err);
 
5994
        }
 
5995
 
 
5996
        try_(a) {
 
5997
                XTDDTable *table_dic = pb_open_tab->ot_table->tab_dic.dic_table;
 
5998
 
 
5999
                if (table_dic == NULL)
 
6000
                        xt_throw_errno(XT_CONTEXT, XT_ERR_NO_DICTIONARY);
 
6001
 
 
6002
                for (int i = 0, sz = table_dic->dt_fkeys.size(); i < sz; i++) {
 
6003
                        FOREIGN_KEY_INFO *fk_info= new  // assumed that C++ exceptions are disabled
 
6004
                                (thd_alloc(thd, sizeof(FOREIGN_KEY_INFO))) FOREIGN_KEY_INFO;
 
6005
 
 
6006
                        if (fk_info == NULL)
 
6007
                                xt_throw_errno(XT_CONTEXT, XT_ENOMEM);
 
6008
 
 
6009
                        XTDDForeignKey *fk = table_dic->dt_fkeys.itemAt(i);
 
6010
 
 
6011
                        const char *path = fk->fk_ref_tab_name->ps_path;
 
6012
                        const char *ref_tbl_name = path + strlen(path);
 
6013
 
 
6014
                        while (ref_tbl_name != path && !XT_IS_DIR_CHAR(*ref_tbl_name)) 
 
6015
                                ref_tbl_name--;
 
6016
 
 
6017
                        const char * ref_db_name = ref_tbl_name - 1;
 
6018
 
 
6019
                        while (ref_db_name != path && !XT_IS_DIR_CHAR(*ref_db_name)) 
 
6020
                                ref_db_name--;
 
6021
 
 
6022
                        ref_tbl_name++;
 
6023
                        ref_db_name++;
 
6024
 
 
6025
                        fk_info->forein_id = thd_make_lex_string(thd, 0,
 
6026
                                fk->co_name, (uint) strlen(fk->co_name), 1);
 
6027
 
 
6028
                        fk_info->referenced_db = thd_make_lex_string(thd, 0,
 
6029
                                ref_db_name, (uint) (ref_tbl_name - ref_db_name - 1), 1);
 
6030
 
 
6031
                        fk_info->referenced_table = thd_make_lex_string(thd, 0,
 
6032
                                ref_tbl_name, (uint) strlen(ref_tbl_name), 1);
 
6033
                        
 
6034
                        fk_info->referenced_key_name = NULL;                    
 
6035
 
 
6036
                        XTIndex *ix = fk->getReferenceIndexPtr();
 
6037
                        if (ix == NULL) /* can be NULL if another thread changes referenced table at the moment */
 
6038
                                continue;
 
6039
                        
 
6040
                        XTDDTable *ref_table = fk->fk_ref_table;
 
6041
 
 
6042
                        // might be a self-reference
 
6043
                        if ((ref_table == NULL) 
 
6044
                                && (xt_tab_compare_names(path, table_dic->dt_table->tab_name->ps_path) == 0)) {
 
6045
                                ref_table = table_dic;
 
6046
                        }
 
6047
 
 
6048
                        if (ref_table != NULL) {
 
6049
                                const XTList<XTDDIndex>& ix_list = ref_table->dt_indexes;
 
6050
                                for (int j = 0, sz2 = ix_list.size(); j < sz2; j++) {
 
6051
                                        XTDDIndex *ddix = ix_list.itemAt(j);
 
6052
                                        if (ddix->in_index ==  ix->mi_index_no) {
 
6053
                                                const char *ix_name = 
 
6054
                                                        ddix->co_name ? ddix->co_name : ddix->co_ind_name;
 
6055
                                                fk_info->referenced_key_name = thd_make_lex_string(thd, 0,
 
6056
                                                        ix_name, (uint) strlen(ix_name), 1);
 
6057
                                                break;
 
6058
                                        }
 
6059
                                }
 
6060
                        }
 
6061
 
 
6062
                        action = XTDDForeignKey::actionTypeToString(fk->fk_on_delete);
 
6063
                        fk_info->delete_method = thd_make_lex_string(thd, 0,
 
6064
                                action, (uint) strlen(action), 1);
 
6065
                        action = XTDDForeignKey::actionTypeToString(fk->fk_on_update);
 
6066
                        fk_info->update_method = thd_make_lex_string(thd, 0,
 
6067
                                action, (uint) strlen(action), 1);
 
6068
 
 
6069
                        const XTList<XTDDColumnRef>& cols = fk->co_cols;
 
6070
                        for (int j = 0, sz2 = cols.size(); j < sz2; j++) {
 
6071
                                XTDDColumnRef *col_ref= cols.itemAt(j);
 
6072
                                fk_info->foreign_fields.push_back(thd_make_lex_string(thd, 0,
 
6073
                                        col_ref->cr_col_name, (uint) strlen(col_ref->cr_col_name), 1));
 
6074
                        }
 
6075
 
 
6076
                        const XTList<XTDDColumnRef>& ref_cols = fk->fk_ref_cols;
 
6077
                        for (int j = 0, sz2 = ref_cols.size(); j < sz2; j++) {
 
6078
                                XTDDColumnRef *col_ref= ref_cols.itemAt(j);
 
6079
                                fk_info->referenced_fields.push_back(thd_make_lex_string(thd, 0,
 
6080
                                        col_ref->cr_col_name, (uint) strlen(col_ref->cr_col_name), 1));
 
6081
                        }
 
6082
 
 
6083
                        f_key_list->push_back(fk_info);
 
6084
                }
 
6085
        }
 
6086
        catch_(a) {
 
6087
                err = xt_ha_pbxt_thread_error_for_mysql(thd, self, pb_ignore_dup_key);
 
6088
        }
 
6089
        cont_(a);
 
6090
 
 
6091
        return err; 
 
6092
}
 
6093
 
 
6094
uint ha_pbxt::referenced_by_foreign_key()
 
6095
{
 
6096
        XTDDTable *table_dic = pb_open_tab->ot_table->tab_dic.dic_table;
 
6097
 
 
6098
        if (!table_dic)
 
6099
                return 0;
 
6100
        /* Check the list of referencing tables: */
 
6101
        return table_dic->dt_trefs ? 1 : 0;
 
6102
}
 
6103
#endif // DRI_IS
 
6104
 
 
6105
struct st_mysql_sys_var
 
6106
{
 
6107
        MYSQL_PLUGIN_VAR_HEADER;
 
6108
};
 
6109
 
 
6110
#if MYSQL_VERSION_ID < 60000
 
6111
#if MYSQL_VERSION_ID >= 50124
 
6112
#define USE_CONST_SAVE
 
6113
#endif
 
6114
#else
 
6115
#if MYSQL_VERSION_ID >= 60005
 
6116
#define USE_CONST_SAVE
 
6117
#endif
 
6118
#endif
 
6119
 
 
6120
#ifdef DRIZZLED
 
6121
#define st_mysql_sys_var drizzled::drizzle_sys_var
 
6122
#endif
 
6123
 
 
6124
#ifdef USE_CONST_SAVE
 
6125
static void pbxt_record_cache_size_func(THD *XT_UNUSED(thd), struct st_mysql_sys_var *var, void *tgt, const void *save)
 
6126
#else
 
6127
static void pbxt_record_cache_size_func(THD *XT_UNUSED(thd), struct st_mysql_sys_var *var, void *tgt, void *save)
 
6128
#endif
 
6129
{
 
6130
        xtInt8  record_cache_size;
 
6131
 
 
6132
        char *old= *(char **) tgt;
 
6133
        *(char **)tgt= *(char **) save;
 
6134
        if (var->flags & PLUGIN_VAR_MEMALLOC)
 
6135
        {
 
6136
                *(char **)tgt= my_strdup(*(char **) save, MYF(0));
 
6137
                my_free(old, MYF(0));
 
6138
        }
 
6139
        record_cache_size = ha_set_variable(&pbxt_record_cache_size, &vp_record_cache_size);
 
6140
        xt_tc_set_cache_size((size_t) record_cache_size);
 
6141
#ifdef DEBUG
 
6142
        char buffer[200];
 
6143
 
 
6144
        sprintf(buffer, "pbxt_record_cache_size=%llu\n", (u_llong) record_cache_size);
 
6145
        xt_logf(XT_NT_INFO, buffer);
 
6146
#endif
 
6147
}
 
6148
 
 
6149
#ifndef DRIZZLED
 
6150
struct st_mysql_storage_engine pbxt_storage_engine = {
 
6151
        MYSQL_HANDLERTON_INTERFACE_VERSION
 
6152
};
 
6153
static st_mysql_information_schema pbxt_statitics = {
 
6154
        MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
 
6155
};
 
6156
#endif
 
6157
 
 
6158
#if MYSQL_VERSION_ID >= 50118
 
6159
static MYSQL_SYSVAR_STR(index_cache_size, pbxt_index_cache_size,
 
6160
  PLUGIN_VAR_READONLY,
 
6161
  "The amount of memory allocated to the index cache, used only to cache index data.",
 
6162
  NULL, NULL, NULL);
 
6163
 
 
6164
static MYSQL_SYSVAR_STR(record_cache_size, pbxt_record_cache_size,
 
6165
  PLUGIN_VAR_READONLY, // PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_MEMALLOC,
 
6166
  "The amount of memory allocated to the record cache used to cache table data.",
 
6167
  NULL, pbxt_record_cache_size_func, NULL);
 
6168
 
 
6169
static MYSQL_SYSVAR_STR(log_cache_size, pbxt_log_cache_size,
 
6170
  PLUGIN_VAR_READONLY,
 
6171
  "The amount of memory allocated to the transaction log cache used to cache transaction log data.",
 
6172
  NULL, NULL, NULL);
 
6173
 
 
6174
static MYSQL_SYSVAR_STR(log_file_threshold, pbxt_log_file_threshold,
 
6175
  PLUGIN_VAR_READONLY,
 
6176
  "The size of a transaction log before rollover, and a new log is created.",
 
6177
  NULL, NULL, NULL);
 
6178
 
 
6179
static MYSQL_SYSVAR_STR(transaction_buffer_size, pbxt_transaction_buffer_size,
 
6180
  PLUGIN_VAR_READONLY,
 
6181
  "The size of the global transaction log buffer (the engine allocates 2 buffers of this size).",
 
6182
  NULL, NULL, NULL);
 
6183
 
 
6184
static MYSQL_SYSVAR_STR(log_buffer_size, pbxt_log_buffer_size,
 
6185
  PLUGIN_VAR_READONLY,
 
6186
  "The size of the buffer used to cache data from transaction and data logs during sequential scans, or when writing a data log.",
 
6187
  NULL, NULL, NULL);
 
6188
 
 
6189
static MYSQL_SYSVAR_STR(checkpoint_frequency, pbxt_checkpoint_frequency,
 
6190
  PLUGIN_VAR_READONLY,
 
6191
  "The size of the transaction data buffer which is allocate by each thread.",
 
6192
  NULL, NULL, NULL);
 
6193
 
 
6194
static MYSQL_SYSVAR_STR(data_log_threshold, pbxt_data_log_threshold,
 
6195
  PLUGIN_VAR_READONLY,
 
6196
  "The maximum size of a data log file.",
 
6197
  NULL, NULL, NULL);
 
6198
 
 
6199
static MYSQL_SYSVAR_STR(data_file_grow_size, pbxt_data_file_grow_size,
 
6200
  PLUGIN_VAR_READONLY,
 
6201
  "The amount by which the handle data files (.xtd) grow.",
 
6202
  NULL, NULL, NULL);
 
6203
 
 
6204
static MYSQL_SYSVAR_STR(row_file_grow_size, pbxt_row_file_grow_size,
 
6205
  PLUGIN_VAR_READONLY,
 
6206
  "The amount by which the row pointer files (.xtr) grow.",
 
6207
  NULL, NULL, NULL);
 
6208
 
 
6209
static MYSQL_SYSVAR_STR(record_write_threshold, pbxt_record_write_threshold,
 
6210
  PLUGIN_VAR_READONLY,
 
6211
  "The amount data written to the record files (.xtd and .xtr) before the changes are applied to the database.",
 
6212
  NULL, NULL, NULL);
 
6213
 
 
6214
static MYSQL_SYSVAR_INT(garbage_threshold, xt_db_garbage_threshold,
 
6215
        PLUGIN_VAR_OPCMDARG,
 
6216
        "The percentage of garbage in a repository file before it is compacted.",
 
6217
        NULL, NULL, XT_DL_DEFAULT_GARBAGE_LEVEL, 0, 100, 1);
 
6218
 
 
6219
static MYSQL_SYSVAR_INT(log_file_count, xt_db_log_file_count,
 
6220
        PLUGIN_VAR_OPCMDARG,
 
6221
        "The minimum number of transaction logs used.",
 
6222
        NULL, NULL, XT_DL_DEFAULT_XLOG_COUNT, 1, 20000, 1);
 
6223
 
 
6224
static MYSQL_SYSVAR_INT(auto_increment_mode, xt_db_auto_increment_mode,
 
6225
        PLUGIN_VAR_OPCMDARG,
 
6226
        "The auto-increment mode, 0 = MySQL standard (default), 1 = previous ID's never reused.",
 
6227
        NULL, NULL, XT_AUTO_INCREMENT_DEF, 0, 1, 1);
 
6228
 
 
6229
/* {RN145} */
 
6230
static MYSQL_SYSVAR_INT(offline_log_function, xt_db_offline_log_function,
 
6231
        PLUGIN_VAR_OPCMDARG,
 
6232
        "Determines what happens to transaction logs when the are moved offline, 0 = recycle logs (default), 1 = delete logs (default on Mac OS X), 2 = keep logs.",
 
6233
        NULL, NULL, XT_OFFLINE_LOG_FUNCTION_DEF, 0, 2, 1);
 
6234
 
 
6235
/* {RN150} */
 
6236
static MYSQL_SYSVAR_INT(sweeper_priority, xt_db_sweeper_priority,
 
6237
        PLUGIN_VAR_OPCMDARG,
 
6238
        "Determines the priority of the background sweeper process, 0 = low (default), 1 = normal (same as user threads), 2 = high.",
 
6239
        NULL, NULL, XT_PRIORITY_LOW, XT_PRIORITY_LOW, XT_PRIORITY_HIGH, 1);
 
6240
 
 
6241
#ifndef DEBUG
 
6242
static MYSQL_SYSVAR_BOOL(support_xa, pbxt_support_xa,
 
6243
        PLUGIN_VAR_OPCMDARG,
 
6244
        "Enable PBXT support for the XA two-phase commit, default is enabled",
 
6245
        NULL, NULL, TRUE);
 
6246
#else
 
6247
static MYSQL_SYSVAR_BOOL(support_xa, pbxt_support_xa,
 
6248
        PLUGIN_VAR_OPCMDARG,
 
6249
        "Enable PBXT support for the XA two-phase commit, default is disabled (due to assertion failure in MySQL)",
 
6250
        /* The problem is, in MySQL an assertion fails in debug mode: 
 
6251
         * Assertion failed: (total_ha_2pc == (ulong) opt_bin_log+1), function ha_recover, file handler.cc, line 1557.
 
6252
     */
 
6253
        NULL, NULL, FALSE);
 
6254
#endif
 
6255
 
 
6256
static MYSQL_SYSVAR_INT(index_dirty_threshold, xt_db_index_dirty_threshold,
 
6257
        PLUGIN_VAR_OPCMDARG,
 
6258
        "The percentage of the index cache that must be dirty before the index cache is flushed.",
 
6259
        NULL, NULL, XT_DL_DEFAULT_INDEX_DIRTY_LEVEL, 0, 100, 1);
 
6260
        
 
6261
static MYSQL_SYSVAR_INT(flush_log_at_trx_commit, xt_db_flush_log_at_trx_commit,
 
6262
        PLUGIN_VAR_OPCMDARG,
 
6263
        "Determines whether the transaction log is written and/or flushed when a transaction is committed (no matter what the setting the log is written and flushed once per second), 0 = no write & no flush, 1 = write & flush (default), 2 = write & no flush.",
 
6264
        NULL, NULL, 1, 0, 2, 1);
 
6265
 
 
6266
static struct st_mysql_sys_var* pbxt_system_variables[] = {
 
6267
  MYSQL_SYSVAR(index_cache_size),
 
6268
  MYSQL_SYSVAR(record_cache_size),
 
6269
  MYSQL_SYSVAR(log_cache_size),
 
6270
  MYSQL_SYSVAR(log_file_threshold),
 
6271
  MYSQL_SYSVAR(transaction_buffer_size),
 
6272
  MYSQL_SYSVAR(log_buffer_size),
 
6273
  MYSQL_SYSVAR(checkpoint_frequency),
 
6274
  MYSQL_SYSVAR(data_log_threshold),
 
6275
  MYSQL_SYSVAR(data_file_grow_size),
 
6276
  MYSQL_SYSVAR(row_file_grow_size),
 
6277
  MYSQL_SYSVAR(record_write_threshold),
 
6278
  MYSQL_SYSVAR(garbage_threshold),
 
6279
  MYSQL_SYSVAR(log_file_count),
 
6280
  MYSQL_SYSVAR(auto_increment_mode),
 
6281
  MYSQL_SYSVAR(offline_log_function),
 
6282
  MYSQL_SYSVAR(sweeper_priority),
 
6283
  MYSQL_SYSVAR(support_xa),
 
6284
  MYSQL_SYSVAR(index_dirty_threshold),
 
6285
  MYSQL_SYSVAR(flush_log_at_trx_commit),
 
6286
  NULL
 
6287
};
 
6288
#endif
 
6289
 
 
6290
#ifdef DRIZZLED
 
6291
DRIZZLE_DECLARE_PLUGIN
 
6292
{
 
6293
        DRIZZLE_VERSION_ID,
 
6294
        "PBXT",
 
6295
        "1.0",
 
6296
        "Paul McCullagh, PrimeBase Technologies GmbH",
 
6297
        "High performance, multi-versioning transactional engine",
 
6298
        PLUGIN_LICENSE_GPL,
 
6299
        pbxt_init, /* Plugin Init */
 
6300
        pbxt_system_variables,          /* system variables                */
 
6301
        NULL                                            /* config options                  */
 
6302
}
 
6303
DRIZZLE_DECLARE_PLUGIN_END;
 
6304
#else // MySQL case
 
6305
mysql_declare_plugin(pbxt)
 
6306
{
 
6307
        MYSQL_STORAGE_ENGINE_PLUGIN,
 
6308
        &pbxt_storage_engine,
 
6309
        "PBXT",
 
6310
        "Paul McCullagh, PrimeBase Technologies GmbH",
 
6311
        "High performance, multi-versioning transactional engine",
 
6312
        PLUGIN_LICENSE_GPL,
 
6313
        pbxt_init, /* Plugin Init */
 
6314
        pbxt_end, /* Plugin Deinit */
 
6315
        0x0001 /* 0.1 */,
 
6316
        NULL,                       /* status variables                */
 
6317
#if MYSQL_VERSION_ID >= 50118
 
6318
        pbxt_system_variables,          /* system variables                */
 
6319
#else
 
6320
        NULL,
 
6321
#endif
 
6322
        NULL                                            /* config options                  */
 
6323
}, {
 
6324
        MYSQL_INFORMATION_SCHEMA_PLUGIN,
 
6325
        &pbxt_statitics,
 
6326
        "PBXT_STATISTICS",
 
6327
        "Paul McCullagh, PrimeBase Technologies GmbH",
 
6328
        "PBXT internal system statitics",
 
6329
        PLUGIN_LICENSE_GPL,
 
6330
        pbxt_init_statistics,                                           /* plugin init */
 
6331
        pbxt_exit_statistics,                                           /* plugin deinit */
 
6332
        0x0005,
 
6333
        NULL,                                                                           /* status variables */
 
6334
        NULL,                                                                           /* system variables */
 
6335
        NULL                                                                            /* config options */
 
6336
}
 
6337
mysql_declare_plugin_end;
 
6338
#endif
 
6339
 
 
6340
#if defined(XT_WIN) && defined(XT_COREDUMP)
 
6341
 
 
6342
/*
 
6343
 * WINDOWS CORE DUMP SUPPORT
 
6344
 *
 
6345
 * MySQL supports core dumping on Windows with --core-file command line option. 
 
6346
 * However it creates dumps with the MiniDumpNormal option which saves only stack traces.
 
6347
 *
 
6348
 * We instead (or in addition) create dumps with MiniDumpWithoutOptionalData option
 
6349
 * which saves all available information. To enable core dumping enable XT_COREDUMP
 
6350
 * at compile time.
 
6351
 * In addition, pbxt_crash_debug must be set to TRUE which is the case if XT_CRASH_DEBUG
 
6352
 * is defined.
 
6353
 * This switch is also controlled by creating a file called "no-debug" or "crash-debug"
 
6354
 * in the pbxt database directory.
 
6355
 */
 
6356
 
 
6357
typedef enum _MINIDUMP_TYPE {
 
6358
    MiniDumpNormal                         = 0x0000,
 
6359
    MiniDumpWithDataSegs                   = 0x0001,
 
6360
    MiniDumpWithFullMemory                 = 0x0002,
 
6361
    MiniDumpWithHandleData                 = 0x0004,
 
6362
    MiniDumpFilterMemory                   = 0x0008,
 
6363
    MiniDumpScanMemory                     = 0x0010,
 
6364
    MiniDumpWithUnloadedModules            = 0x0020,
 
6365
    MiniDumpWithIndirectlyReferencedMemory = 0x0040,
 
6366
    MiniDumpFilterModulePaths              = 0x0080,
 
6367
    MiniDumpWithProcessThreadData          = 0x0100,
 
6368
    MiniDumpWithPrivateReadWriteMemory     = 0x0200,
 
6369
} MINIDUMP_TYPE;
 
6370
 
 
6371
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
 
6372
    DWORD ThreadId;
 
6373
    PEXCEPTION_POINTERS ExceptionPointers;
 
6374
    BOOL ClientPointers;
 
6375
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
 
6376
 
 
6377
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
 
6378
        HANDLE hProcess, 
 
6379
        DWORD dwPid, 
 
6380
        HANDLE hFile, 
 
6381
        MINIDUMP_TYPE DumpType,
 
6382
        void *ExceptionParam,
 
6383
        void *UserStreamParam,
 
6384
        void *CallbackParam
 
6385
        );
 
6386
 
 
6387
char base_path[_MAX_PATH] = {0};
 
6388
char dump_path[_MAX_PATH] = {0};
 
6389
 
 
6390
void core_dump(struct _EXCEPTION_POINTERS *pExceptionInfo)
 
6391
{
 
6392
        SECURITY_ATTRIBUTES     sa = { sizeof(SECURITY_ATTRIBUTES), 0, 0 };
 
6393
        int i;
 
6394
        HMODULE hDll = NULL;
 
6395
        HANDLE hFile;
 
6396
        MINIDUMPWRITEDUMP pDump;
 
6397
        char *end_ptr = base_path;
 
6398
 
 
6399
        MINIDUMP_EXCEPTION_INFORMATION ExInfo, *ExInfoPtr = NULL;
 
6400
 
 
6401
        if (pExceptionInfo) {
 
6402
                ExInfo.ThreadId = GetCurrentThreadId();
 
6403
                ExInfo.ExceptionPointers = pExceptionInfo;
 
6404
                ExInfo.ClientPointers = NULL;
 
6405
                ExInfoPtr = &ExInfo;
 
6406
        }
 
6407
 
 
6408
        end_ptr = base_path + strlen(base_path);
 
6409
 
 
6410
        strcat(base_path, "DBGHELP.DLL" );
 
6411
        hDll = LoadLibrary(base_path);
 
6412
        *end_ptr = 0;
 
6413
        if (hDll==NULL) {
 
6414
                int err;
 
6415
                err = HRESULT_CODE(GetLastError());
 
6416
                hDll = LoadLibrary( "DBGHELP.DLL" );
 
6417
                if (hDll==NULL) {
 
6418
                        err = HRESULT_CODE(GetLastError());
 
6419
                        return;
 
6420
                }
 
6421
        }
 
6422
 
 
6423
        pDump = (MINIDUMPWRITEDUMP)GetProcAddress( hDll, "MiniDumpWriteDump" );
 
6424
        if (!pDump) {
 
6425
                int err;
 
6426
                err = HRESULT_CODE(GetLastError());
 
6427
                return;
 
6428
        }
 
6429
 
 
6430
        for (i = 1; i < INT_MAX; i++) {
 
6431
                sprintf(dump_path, "%sPBXTCore%08d.dmp", base_path, i);
 
6432
                hFile = CreateFile( dump_path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW,
 
6433
                                                        FILE_ATTRIBUTE_NORMAL, NULL );
 
6434
 
 
6435
                if ( hFile != INVALID_HANDLE_VALUE )
 
6436
                        break;
 
6437
 
 
6438
                if (HRESULT_CODE(GetLastError()) == ERROR_FILE_EXISTS )
 
6439
                        continue;
 
6440
 
 
6441
                return;
 
6442
        }
 
6443
 
 
6444
        // write the dump
 
6445
        BOOL bOK = pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, 
 
6446
                MiniDumpWithPrivateReadWriteMemory, ExInfoPtr, NULL, NULL );
 
6447
 
 
6448
        CloseHandle(hFile);
 
6449
}
 
6450
 
 
6451
LONG crash_filter( struct _EXCEPTION_POINTERS *pExceptionInfo )
 
6452
{
 
6453
        core_dump(pExceptionInfo);
 
6454
        return EXCEPTION_EXECUTE_HANDLER;
 
6455
}
 
6456
 
 
6457
void register_crash_filter()
 
6458
{
 
6459
        SetUnhandledExceptionFilter( (LPTOP_LEVEL_EXCEPTION_FILTER) crash_filter );
 
6460
}
 
6461
 
 
6462
#endif // XT_WIN && XT_COREDUMP