~posulliv/drizzle/optimizer-style-cleanup

« back to all changes in this revision

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

  • Committer: Padraig O'Sullivan
  • Date: 2010-04-17 01:38:47 UTC
  • mfrom: (1237.9.238 bad-staging)
  • Revision ID: osullivan.padraig@gmail.com-20100417013847-ibjioqsfbmf5yg4g
Merge trunk.

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