summaryrefslogtreecommitdiff
path: root/DBIXS.h
blob: f1a3963d933ce0d9b1d1fea69206dd32c5cbe898 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
/* vim: ts=8:sw=4:expandtab
 *
 * $Id: DBIXS.h 15268 2012-04-18 11:34:59Z timbo $
 *
 * Copyright (c) 1994-2010  Tim Bunce  Ireland
 *
 * See COPYRIGHT section in DBI.pm for usage and distribution rights.
 */

/* DBI Interface Definitions for DBD Modules */

#ifndef DBIXS_VERSION                           /* prevent multiple inclusion */

#ifndef DBIS
#define DBIS    dbis    /* default name for dbistate_t variable */
#endif

/* Here for backwards compat. PERL_POLLUTE was removed in perl 5.13.3 */
#define PERL_POLLUTE

/* first pull in the standard Perl header files for extensions  */
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#ifdef debug            /* causes problems with DBIS->debug     */
#undef debug
#endif

#ifdef std             /* causes problems with STLport <tscheresky@micron.com> */
#undef std
#endif

/* define DBIXS_REVISION */
#include "dbixs_rev.h"

/* Perl backwards compatibility definitions */
#include "dbipport.h"

/* DBI SQL_* type definitions */
#include "dbi_sql.h"


#define DBIXS_VERSION 93 /* superceeded by DBIXS_REVISION */

#ifdef NEED_DBIXS_VERSION
#if NEED_DBIXS_VERSION > DBIXS_VERSION
error You_need_to_upgrade_your_DBI_module_before_building_this_driver
#endif
#else
#define NEED_DBIXS_VERSION DBIXS_VERSION
#endif


#define DBI_LOCK
#define DBI_UNLOCK

#ifndef DBI_NO_THREADS
#ifdef USE_ITHREADS
#define DBI_USE_THREADS
#endif /* USE_ITHREADS */
#endif /* DBI_NO_THREADS */


/* forward struct declarations                                          */

typedef struct dbistate_st dbistate_t;
/* implementor needs to define actual struct { dbih_??c_t com; ... }*/
typedef struct imp_drh_st imp_drh_t;    /* driver                       */
typedef struct imp_dbh_st imp_dbh_t;    /* database                     */
typedef struct imp_sth_st imp_sth_t;    /* statement                    */
typedef struct imp_fdh_st imp_fdh_t;    /* field descriptor             */
typedef struct imp_xxh_st imp_xxh_t;    /* any (defined below)          */
#define DBI_imp_data_ imp_xxh_t         /* friendly for take_imp_data   */



/* --- DBI Handle Common Data Structure (all handles have one) ---      */

/* Handle types. Code currently assumes child = parent + 1.             */
#define DBIt_DR         1
#define DBIt_DB         2
#define DBIt_ST         3
#define DBIt_FD         4

/* component structures */

typedef struct dbih_com_std_st {
    U32  flags;
    int  call_depth;    /* used by DBI to track nested calls (int)      */
    U16  type;          /* DBIt_DR, DBIt_DB, DBIt_ST                    */
    HV   *my_h;         /* copy of outer handle HV (not refcounted)     */
    SV   *parent_h;     /* parent inner handle (ref to hv) (r.c.inc)    */
    imp_xxh_t *parent_com;      /* parent com struct shortcut           */
    PerlInterpreter * thr_user;  /* thread that owns the handle         */

    HV   *imp_stash;    /* who is the implementor for this handle       */
    SV   *imp_data;     /* optional implementors data (for perl imp's)  */

    I32  kids;          /* count of db's for dr's, st's for db's etc    */
    I32  active_kids;   /* kids which are currently DBIc_ACTIVE         */
    U32  pid;           /* pid of process that created handle */
    dbistate_t *dbistate;
} dbih_com_std_t;

typedef struct dbih_com_attr_st {
    /* These are copies of the Hash values (ref.cnt.inc'd)      */
    /* Many of the hash values are themselves references        */
    SV *TraceLevel;
    SV *State;          /* Standard SQLSTATE, 5 char string     */
    SV *Err;            /* Native engine error code             */
    SV *Errstr;         /* Native engine error message          */
    UV ErrCount;
    U32  LongReadLen;   /* auto read length for long/blob types */
    SV *FetchHashKeyName;       /* for fetchrow_hashref         */
    /* (NEW FIELDS?... DON'T FORGET TO UPDATE dbih_clearcom()!) */
} dbih_com_attr_t;


struct dbih_com_st {    /* complete core structure (typedef'd above)    */
    dbih_com_std_t      std;
    dbih_com_attr_t     attr;
};

/* This 'implementors' type the DBI defines by default as a way to      */
/* refer to the imp_??h data of a handle without considering its type.  */
struct imp_xxh_st { struct dbih_com_st com; };

/* Define handle-type specific structures for implementors to include   */
/* at the start of their private structures.                            */

typedef struct {                /* -- DRIVER --                         */
    dbih_com_std_t      std;
    dbih_com_attr_t     attr;
    HV          *_old_cached_kids; /* not used, here for binary compat */
} dbih_drc_t;

typedef struct {                /* -- DATABASE --                       */
    dbih_com_std_t      std;    /* \__ standard structure               */
    dbih_com_attr_t     attr;   /* /   plus... (nothing else right now) */
    HV          *_old_cached_kids; /* not used, here for binary compat */
} dbih_dbc_t;

typedef struct {                /* -- STATEMENT --                      */
    dbih_com_std_t      std;    /* \__ standard structure               */
    dbih_com_attr_t     attr;   /* /   plus ...                         */

    int         num_params;     /* number of placeholders               */
    int         num_fields;     /* NUM_OF_FIELDS, must be set           */
    AV          *fields_svav;   /* special row buffer (inc bind_cols)   */
    IV          row_count;      /* incremented by get_fbav()            */

    AV          *fields_fdav;   /* not used yet, may change */

    I32  spare1;
    void *spare2;
} dbih_stc_t;


/* XXX THIS STRUCTURE SHOULD NOT BE USED */
typedef struct {                /* -- FIELD DESCRIPTOR --               */
    dbih_com_std_t      std;    /* standard structure (not fully setup) */

    /* core attributes (from DescribeCol in ODBC)               */
    char *col_name;             /* see dbih_make_fdsv           */
    I16   col_name_len;
    I16   col_sql_type;
    I16   col_precision;
    I16   col_scale;
    I16   col_nullable;

    /* additional attributes (from ColAttributes in ODBC)       */
    I32   col_length;
    I32   col_disp_size;

    I32  spare1;
    void *spare2;
} dbih_fdc_t;


#define _imp2com(p,f)           ((p)->com.f) /* private */

#define DBIc_FLAGS(imp)         _imp2com(imp, std.flags)
#define DBIc_TYPE(imp)          _imp2com(imp, std.type)
#define DBIc_CALL_DEPTH(imp)    _imp2com(imp, std.call_depth)
#define DBIc_MY_H(imp)          _imp2com(imp, std.my_h)
#define DBIc_PARENT_H(imp)      _imp2com(imp, std.parent_h)
#define DBIc_PARENT_COM(imp)    _imp2com(imp, std.parent_com)
#define DBIc_THR_COND(imp)      _imp2com(imp, std.thr_cond)
#define DBIc_THR_USER(imp)      _imp2com(imp, std.thr_user)
#define DBIc_THR_USER_NONE      (0xFFFF)
#define DBIc_IMP_STASH(imp)     _imp2com(imp, std.imp_stash)
#define DBIc_IMP_DATA(imp)      _imp2com(imp, std.imp_data)
#define DBIc_DBISTATE(imp)      _imp2com(imp, std.dbistate)
#define DBIc_LOGPIO(imp)        DBIc_DBISTATE(imp)->logfp
#define DBIc_KIDS(imp)          _imp2com(imp, std.kids)
#define DBIc_ACTIVE_KIDS(imp)   _imp2com(imp, std.active_kids)
#define DBIc_LAST_METHOD(imp)   _imp2com(imp, std.last_method)

/*  d = DBD flags,  l = DBD level (needs to be shifted down)
 *  D - DBI flags,  r = reserved,  L = DBI trace level
 *  Trace level bit allocation: 0xddlDDDrL   */
#define DBIc_TRACE_LEVEL_MASK   0x0000000F
#define DBIc_TRACE_FLAGS_MASK   0xFF0FFF00  /* includes DBD flag bits for DBIc_TRACE */
#define DBIc_TRACE_SETTINGS(imp) (DBIc_DBISTATE(imp)->debug)
#define DBIc_TRACE_LEVEL(imp)   (DBIc_TRACE_SETTINGS(imp) & DBIc_TRACE_LEVEL_MASK)
#define DBIc_TRACE_FLAGS(imp)   (DBIc_TRACE_SETTINGS(imp) & DBIc_TRACE_FLAGS_MASK)
/* DBI defined trace flags */
#define DBIf_TRACE_SQL          0x00000100
#define DBIf_TRACE_CON          0x00000200
#define DBIf_TRACE_ENC          0x00000400
#define DBIf_TRACE_DBD          0x00000800
#define DBIf_TRACE_TXN          0x00001000

#define DBDc_TRACE_LEVEL_MASK   0x00F00000
#define DBDc_TRACE_LEVEL_SHIFT  20
#define DBDc_TRACE_LEVEL(imp)         ( (DBIc_TRACE_SETTINGS(imp) & DBDc_TRACE_LEVEL_MASK) >> DBDc_TRACE_LEVEL_SHIFT )
#define DBDc_TRACE_LEVEL_set(imp, l)  ( DBIc_TRACE_SETTINGS(imp) |= (((l) << DBDc_TRACE_LEVEL_SHIFT) & DBDc_TRACE_LEVEL_MASK ))

/* DBIc_TRACE_MATCHES(this, crnt): true if this 'matches' (is within) crnt
   DBIc_TRACE_MATCHES(foo, DBIc_TRACE_SETTINGS(imp))
*/
#define DBIc_TRACE_MATCHES(this, crnt)  \
        (  ((crnt & DBIc_TRACE_LEVEL_MASK) >= (this & DBIc_TRACE_LEVEL_MASK)) \
        || ((crnt & DBIc_TRACE_FLAGS_MASK)  & (this & DBIc_TRACE_FLAGS_MASK)) )

/* DBIc_TRACE(imp, flags, flag_level, fallback_level)
   True if flags match the handle trace flags & handle trace level >= flag_level,
   OR if handle trace_level > fallback_level (typically > flag_level).
   This is the main trace testing macro to be used by drivers.
   (Drivers should define their own DBDf_TRACE_* macros for the top 8 bits: 0xFF000000)
   DBIc_TRACE(imp,              0, 0, 4) = if trace level >= 4
   DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 4) = if tracing DBDf_FOO & level>=2 or level>=4
   DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 0) = as above but never trace just due to level
   e.g.
    if (DBIc_TRACE(imp_xxh, DBIf_TRACE_SQL|DBIf_TRACE_xxx, 2, 0)) {
        PerlIO_printf(DBIc_LOGPIO(imp_sth), "\tThe %s wibbled the %s\n", ...);
    }
*/
#define DBIc_TRACE(imp, flags, flaglevel, level)        \
        (  (flags && (DBIc_TRACE_FLAGS(imp) & flags) && (DBIc_TRACE_LEVEL(imp) >= flaglevel)) \
        || (level && DBIc_TRACE_LEVEL(imp) >= level) )

#define DBIc_DEBUG(imp)         (_imp2com(imp, attr.TraceLevel)) /* deprecated */
#define DBIc_DEBUGIV(imp)       SvIV(DBIc_DEBUG(imp))            /* deprecated */
#define DBIc_STATE(imp)         SvRV(_imp2com(imp, attr.State))
#define DBIc_ERR(imp)           SvRV(_imp2com(imp, attr.Err))
#define DBIc_ERRSTR(imp)        SvRV(_imp2com(imp, attr.Errstr))
#define DBIc_ErrCount(imp)      _imp2com(imp, attr.ErrCount)
#define DBIc_LongReadLen(imp)   _imp2com(imp, attr.LongReadLen)
#define DBIc_LongReadLen_init   80      /* may change */
#define DBIc_FetchHashKeyName(imp) (_imp2com(imp, attr.FetchHashKeyName))

/* handle sub-type specific fields                                              */
/*      dbh & drh       */
#define DBIc_CACHED_KIDS(imp)   Nullhv /* no longer used, here for src compat */
/*      sth     */
#define DBIc_NUM_FIELDS(imp)    _imp2com(imp, num_fields)
#define DBIc_NUM_PARAMS(imp)    _imp2com(imp, num_params)
#define DBIc_NUM_PARAMS_AT_EXECUTE      -9 /* see Driver.xst */
#define DBIc_ROW_COUNT(imp)     _imp2com(imp, row_count)
#define DBIc_FIELDS_AV(imp)     _imp2com(imp, fields_svav)
#define DBIc_FDESC_AV(imp)      _imp2com(imp, fields_fdav)
#define DBIc_FDESC(imp, i)      ((imp_fdh_t*)(void*)SvPVX(AvARRAY(DBIc_FDESC_AV(imp))[i]))

/* XXX --- DO NOT CHANGE THESE VALUES AS THEY ARE COMPILED INTO DRIVERS --- XXX */
#define DBIcf_COMSET      0x000001      /* needs to be clear'd before free'd    */
#define DBIcf_IMPSET      0x000002      /* has implementor data to be clear'd   */
#define DBIcf_ACTIVE      0x000004      /* needs finish/disconnect before clear */
#define DBIcf_IADESTROY   0x000008      /* do DBIc_ACTIVE_off before DESTROY    */
#define DBIcf_WARN        0x000010      /* warn about poor practice etc         */
#define DBIcf_COMPAT      0x000020      /* compat/emulation mode (eg oraperl)   */
#define DBIcf_ChopBlanks  0x000040      /* rtrim spaces from fetch char columns */
#define DBIcf_RaiseError  0x000080      /* throw exception (croak) on error     */
#define DBIcf_PrintError  0x000100      /* warn() on error                      */
#define DBIcf_AutoCommit  0x000200      /* dbh only. used by drivers            */
#define DBIcf_LongTruncOk 0x000400      /* truncation to LongReadLen is okay    */
#define DBIcf_MultiThread 0x000800      /* allow multiple threads to enter      */
#define DBIcf_HandleSetErr 0x001000     /* has coderef HandleSetErr attribute   */
#define DBIcf_ShowErrorStatement  0x002000   /* include Statement in error      */
#define DBIcf_BegunWork   0x004000      /* between begin_work & commit/rollback */
#define DBIcf_HandleError 0x008000      /* has coderef in HandleError attribute */
#define DBIcf_Profile     0x010000      /* profile activity on this handle      */
#define DBIcf_TaintIn     0x020000      /* check inputs for taintedness */
#define DBIcf_TaintOut    0x040000      /* taint outgoing data */
#define DBIcf_Executed    0x080000      /* do/execute called since commit/rollb */
#define DBIcf_PrintWarn   0x100000      /* warn() on warning (err="0")          */
#define DBIcf_Callbacks   0x200000      /* has Callbacks attribute hash         */
#define DBIcf_AIADESTROY  0x400000      /* auto DBIcf_IADESTROY if pid changes  */
/* NOTE: new flags may require clone() to be updated */

#define DBIcf_INHERITMASK               /* what NOT to pass on to children */   \
  (U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY \
  | DBIcf_AutoCommit | DBIcf_BegunWork | DBIcf_Executed | DBIcf_Callbacks )

/* general purpose bit setting and testing macros                       */
#define DBIbf_is( bitset,flag)          ((bitset) &   (flag))
#define DBIbf_has(bitset,flag)          DBIbf_is(bitset, flag) /* alias for _is */
#define DBIbf_on( bitset,flag)          ((bitset) |=  (flag))
#define DBIbf_off(bitset,flag)          ((bitset) &= ~(flag))
#define DBIbf_set(bitset,flag,on)       ((on) ? DBIbf_on(bitset, flag) : DBIbf_off(bitset,flag))

/* as above, but specifically for DBIc_FLAGS imp flags (except ACTIVE)  */
#define DBIc_is(imp, flag)      DBIbf_is( DBIc_FLAGS(imp), flag)
#define DBIc_has(imp,flag)      DBIc_is(imp, flag) /* alias for DBIc_is */
#define DBIc_on(imp, flag)      DBIbf_on( DBIc_FLAGS(imp), flag)
#define DBIc_off(imp,flag)      DBIbf_off(DBIc_FLAGS(imp), flag)
#define DBIc_set(imp,flag,on)   DBIbf_set(DBIc_FLAGS(imp), flag, on)

#define DBIc_COMSET(imp)        DBIc_is(imp, DBIcf_COMSET)
#define DBIc_COMSET_on(imp)     DBIc_on(imp, DBIcf_COMSET)
#define DBIc_COMSET_off(imp)    DBIc_off(imp,DBIcf_COMSET)

#define DBIc_IMPSET(imp)        DBIc_is(imp, DBIcf_IMPSET)
#define DBIc_IMPSET_on(imp)     DBIc_on(imp, DBIcf_IMPSET)
#define DBIc_IMPSET_off(imp)    DBIc_off(imp,DBIcf_IMPSET)

#define DBIc_ACTIVE(imp)        (DBIc_FLAGS(imp) &   DBIcf_ACTIVE)
#define DBIc_ACTIVE_on(imp)     /* adjust parent's active kid count */  \
    do {                                                                \
        imp_xxh_t *ph_com = DBIc_PARENT_COM(imp);                       \
        if (!DBIc_ACTIVE(imp) && ph_com && !PL_dirty                    \
                && ++DBIc_ACTIVE_KIDS(ph_com) > DBIc_KIDS(ph_com))      \
            croak("panic: DBI active kids (%ld) > kids (%ld)",          \
                (long)DBIc_ACTIVE_KIDS(ph_com),                         \
                (long)DBIc_KIDS(ph_com));                               \
        DBIc_FLAGS(imp) |=  DBIcf_ACTIVE;                               \
    } while(0)
#define DBIc_ACTIVE_off(imp)    /* adjust parent's active kid count */  \
    do {                                                                \
        imp_xxh_t *ph_com = DBIc_PARENT_COM(imp);                       \
        if (DBIc_ACTIVE(imp) && ph_com && !PL_dirty                     \
                && (--DBIc_ACTIVE_KIDS(ph_com) > DBIc_KIDS(ph_com)      \
                   || DBIc_ACTIVE_KIDS(ph_com) < 0) )                   \
            croak("panic: DBI active kids (%ld) < 0 or > kids (%ld)",   \
                (long)DBIc_ACTIVE_KIDS(ph_com),                         \
                (long)DBIc_KIDS(ph_com));                               \
        DBIc_FLAGS(imp) &= ~DBIcf_ACTIVE;                               \
    } while(0)

#define DBIc_IADESTROY(imp)     (DBIc_FLAGS(imp) &   DBIcf_IADESTROY)
#define DBIc_IADESTROY_on(imp)  (DBIc_FLAGS(imp) |=  DBIcf_IADESTROY)
#define DBIc_IADESTROY_off(imp) (DBIc_FLAGS(imp) &= ~DBIcf_IADESTROY)

#define DBIc_AIADESTROY(imp)     (DBIc_FLAGS(imp) &   DBIcf_AIADESTROY)
#define DBIc_AIADESTROY_on(imp)  (DBIc_FLAGS(imp) |=  DBIcf_AIADESTROY)
#define DBIc_AIADESTROY_off(imp) (DBIc_FLAGS(imp) &= ~DBIcf_AIADESTROY)

#define DBIc_WARN(imp)          (DBIc_FLAGS(imp) &   DBIcf_WARN)
#define DBIc_WARN_on(imp)       (DBIc_FLAGS(imp) |=  DBIcf_WARN)
#define DBIc_WARN_off(imp)      (DBIc_FLAGS(imp) &= ~DBIcf_WARN)

#define DBIc_COMPAT(imp)        (DBIc_FLAGS(imp) &   DBIcf_COMPAT)
#define DBIc_COMPAT_on(imp)     (DBIc_FLAGS(imp) |=  DBIcf_COMPAT)
#define DBIc_COMPAT_off(imp)    (DBIc_FLAGS(imp) &= ~DBIcf_COMPAT)


#ifdef IN_DBI_XS                /* get Handle Common Data Structure     */
#define DBIh_COM(h)             (dbih_getcom2(aTHX_ h, 0))
#else
#define DBIh_COM(h)             (DBIS->getcom(h))
#define neatsvpv(sv,len)        (DBIS->neat_svpv(sv,len))
#endif

/* --- For sql_type_cast_svpv() --- */

#define DBIstcf_DISCARD_STRING  0x0001
#define DBIstcf_STRICT          0x0002

/* --- Implementors Private Data Support --- */

#define D_impdata(name,type,h)  type *name = (type*)(DBIh_COM(h))
#define D_imp_drh(h) D_impdata(imp_drh, imp_drh_t, h)
#define D_imp_dbh(h) D_impdata(imp_dbh, imp_dbh_t, h)
#define D_imp_sth(h) D_impdata(imp_sth, imp_sth_t, h)
#define D_imp_xxh(h) D_impdata(imp_xxh, imp_xxh_t, h)

#define D_imp_from_child(name,type,child)       \
                                type *name = (type*)(DBIc_PARENT_COM(child))
#define D_imp_drh_from_dbh D_imp_from_child(imp_drh, imp_drh_t, imp_dbh)
#define D_imp_dbh_from_sth D_imp_from_child(imp_dbh, imp_dbh_t, imp_sth)

#define DBI_IMP_SIZE(n,s) sv_setiv(get_sv((n), GV_ADDMULTI), (s)) /* XXX */



/* --- Event Support (VERY LIABLE TO CHANGE) --- */

#define DBIh_EVENTx(h,t,a1,a2)  /* deprecated XXX */ &PL_sv_no
#define DBIh_EVENT0(h,t)        DBIh_EVENTx((h), (t), &PL_sv_undef, &PL_sv_undef)
#define DBIh_EVENT1(h,t, a1)    DBIh_EVENTx((h), (t), (a1),         &PL_sv_undef)
#define DBIh_EVENT2(h,t, a1,a2) DBIh_EVENTx((h), (t), (a1),         (a2))

#define ERROR_event     "ERROR"
#define WARN_event      "WARN"
#define MSG_event       "MESSAGE"
#define DBEVENT_event   "DBEVENT"
#define UNKNOWN_event   "UNKNOWN"

#define DBIh_SET_ERR_SV(h,i, err, errstr, state, method) \
        (DBIc_DBISTATE(i)->set_err_sv(h,i, err, errstr, state, method))
#define DBIh_SET_ERR_CHAR(h,i, err_c, err_i, errstr, state, method) \
        (DBIc_DBISTATE(i)->set_err_char(h,i, err_c, err_i, errstr, state, method))


/* --- Handy Macros --- */

#define DBIh_CLEAR_ERROR(imp_xxh) (void)( \
        (void)SvOK_off(DBIc_ERR(imp_xxh)),      \
        (void)SvOK_off(DBIc_ERRSTR(imp_xxh)),   \
        (void)SvOK_off(DBIc_STATE(imp_xxh))     \
    )


/* --- DBI State Structure --- */

struct dbistate_st {

/* DBISTATE_VERSION is checked at runtime via DBISTATE_INIT and check_version.
 * It should be incremented on incompatible changes to dbistate_t structure.
 * Additional function pointers being assigned from spare padding, where the
 * size of the structure doesn't change, doesn't require an increment.
 * Incrementing forces all XS drivers to need to be recompiled.
 * (See also DBIXS_REVISION as a driver source compatibility tool.)
 */
#define DBISTATE_VERSION  94   /* ++ on incompatible dbistate_t changes */

    /* this must be the first member in structure                       */
    void (*check_version) _((const char *name,
                int dbis_cv, int dbis_cs, int need_dbixs_cv,
                int drc_s, int dbc_s, int stc_s, int fdc_s));

    /* version and size are used to check for DBI/DBD version mis-match */
    U16 version;        /* version of this structure                    */
    U16 size;
    U16 xs_version;     /* version of the overall DBIXS / DBD interface */
    U16 spare_pad;

    I32 debug;
    PerlIO *logfp;

    /* pointers to DBI functions which the DBD's will want to use       */
    char      * (*neat_svpv)    _((SV *sv, STRLEN maxlen));
    imp_xxh_t * (*getcom)       _((SV *h));     /* see DBIh_COM macro   */
    void        (*clearcom)     _((imp_xxh_t *imp_xxh));
    SV        * (*event)        _((SV *h, const char *name, SV*, SV*));
    int         (*set_attr_k)   _((SV *h, SV *keysv, int dbikey, SV *valuesv));
    SV        * (*get_attr_k)   _((SV *h, SV *keysv, int dbikey));
    AV        * (*get_fbav)     _((imp_sth_t *imp_sth));
    SV        * (*make_fdsv)    _((SV *sth, const char *imp_class, STRLEN imp_size, const char *col_name));
    int         (*bind_as_num)  _((int sql_type, int p, int s, int *t, void *v)); /* XXX deprecated */
    I32         (*hash)         _((const char *string, long i));
    SV        * (*preparse)     _((SV *sth, char *statement, IV ps_return, IV ps_accept, void *foo));

    SV *neatsvpvlen;            /* only show dbgpvlen chars when debugging pv's */

    PerlInterpreter * thr_owner;        /* thread that owns this dbistate       */

    int         (*logmsg)       _((imp_xxh_t *imp_xxh, const char *fmt, ...));
    int         (*set_err_sv)   _((SV *h, imp_xxh_t *imp_xxh, SV   *err, SV   *errstr, SV   *state, SV   *method));
    int         (*set_err_char) _((SV *h, imp_xxh_t *imp_xxh, const char *err, IV err_i, const char *errstr, const char *state, const char *method));
    int         (*bind_col)     _((SV *sth, SV *col, SV *ref, SV *attribs));

    IO *logfp_ref;              /* keep ptr to filehandle for refcounting */

    int         (*sql_type_cast_svpv) _((pTHX_ SV *sv, int sql_type, U32 flags, void *v));

    /* WARNING: Only add new structure members here, and reduce pad2 to keep */
    /* the memory footprint exactly the same */
    void *pad2[3];
};

/* macros for backwards compatibility */
#define set_attr(h, k, v)       set_attr_k(h, k, 0, v)
#define get_attr(h, k)          get_attr_k(h, k, 0)

#define DBILOGFP        (DBIS->logfp)
#ifdef IN_DBI_XS
#define DBILOGMSG       (dbih_logmsg)
#else
#define DBILOGMSG       (DBIS->logmsg)
#endif

/* --- perl object (ActiveState) / multiplicity hooks and hoops --- */
/* note that USE_ITHREADS implies MULTIPLICITY                      */

typedef dbistate_t** (*_dbi_state_lval_t)(pTHX);

# define _DBISTATE_DECLARE_COMMON \
    static _dbi_state_lval_t dbi_state_lval_p = 0;                          \
    static dbistate_t** dbi_get_state(pTHX) {                               \
        if (!dbi_state_lval_p) {                                            \
            CV *cv = get_cv("DBI::_dbi_state_lval", 0);                     \
            if (!cv)                                                        \
                croak("Unable to get DBI state function. DBI not loaded."); \
            dbi_state_lval_p = (_dbi_state_lval_t)CvXSUB(cv);               \
        }                                                                   \
        return dbi_state_lval_p(aTHX);                                      \
    }                                                                       \
    typedef int dummy_dbistate /* keep semicolon from feeling lonely */

#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || defined(PERL_CAPI)

# define DBISTATE_DECLARE _DBISTATE_DECLARE_COMMON
# define _DBISTATE_INIT_DBIS
# undef  DBIS
# define DBIS (*dbi_get_state(aTHX))
# define dbis DBIS /* temp for old drivers using 'dbis' instead of 'DBIS' */

#else   /* plain and simple non perl object / multiplicity case */

# define DBISTATE_DECLARE \
    static dbistate_t *DBIS; \
    _DBISTATE_DECLARE_COMMON

# define _DBISTATE_INIT_DBIS      DBIS = *dbi_get_state(aTHX);
#endif

# define DBISTATE_INIT {        /* typically use in BOOT: of XS file    */    \
    _DBISTATE_INIT_DBIS \
    if (DBIS == NULL)   \
        croak("Unable to get DBI state. DBI not loaded."); \
    DBIS->check_version(__FILE__, DBISTATE_VERSION, sizeof(*DBIS), NEED_DBIXS_VERSION, \
                sizeof(dbih_drc_t), sizeof(dbih_dbc_t), sizeof(dbih_stc_t), sizeof(dbih_fdc_t) \
    ); \
}


/* --- Assorted Utility Macros  --- */

#define DBD_ATTRIB_OK(attribs)  /* is this a usable attrib value */     \
        (attribs && SvROK(attribs) && SvTYPE(SvRV(attribs))==SVt_PVHV)

/* If attribs value supplied then croak if it's not a hash ref.         */
/* Also map undef to Null. Should always be called to pre-process the   */
/* attribs value. One day we may add some extra magic in here.          */
#define DBD_ATTRIBS_CHECK(func, h, attribs)     \
    if ((attribs) && SvOK(attribs)) {           \
        if (!SvROK(attribs) || SvTYPE(SvRV(attribs))!=SVt_PVHV)         \
            croak("%s->%s(...): attribute parameter '%s' is not a hash ref",    \
                    SvPV_nolen(h), func, SvPV_nolen(attribs));          \
    } else (attribs) = Nullsv

#define DBD_ATTRIB_GET_SVP(attribs, key,klen)                   \
        (DBD_ATTRIB_OK(attribs)                                 \
            ? hv_fetch((HV*)SvRV(attribs), key,klen, 0)         \
            : (SV **)Nullsv)

#define DBD_ATTRIB_GET_IV(attribs, key,klen, svp, var)                  \
        if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)        \
            var = SvIV(*svp)

#define DBD_ATTRIB_GET_UV(attribs, key,klen, svp, var)                  \
        if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)        \
            var = SvUV(*svp)

#define DBD_ATTRIB_GET_BOOL(attribs, key,klen, svp, var)                \
        if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)        \
            var = SvTRUE(*svp)

#define DBD_ATTRIB_TRUE(attribs, key,klen, svp)                         \
        (  ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)        \
            ? SvTRUE(*svp) : 0 )

#define DBD_ATTRIB_GET_PV(attribs, key,klen, svp, dflt)                 \
        (((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)          \
            ? SvPV_nolen(*svp) : (dflt))

#define DBD_ATTRIB_DELETE(attribs, key, klen)                   \
        hv_delete((HV*)SvRV(attribs), key, klen, G_DISCARD)

#endif /* DBIXS_VERSION */
/* end of DBIXS.h */