ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/PApp-SQL/SQL.xs
Revision: 1.22
Committed: Sun Jun 21 03:30:00 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
Changes since 1.21: +97 -37 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 /* import some stuff from DBIXS.h and DBI.xs */
6 #define DBIXS_VERSION 93
7 #define DBI_MAGIC '~'
8
9 #define DBISTATE_PERLNAME "DBI::_dbistate"
10 #define DBISTATE_ADDRSV (perl_get_sv(DBISTATE_PERLNAME, 0x05))
11 #define DBIS_PUBLISHED_LVALUE (*(INT2PTR(dbistate_t**, &SvIVX(DBISTATE_ADDRSV))))
12
13 struct dbistate_st {
14 #define DBISTATE_VERSION 94 /* Must change whenever dbistate_t does */
15 /* this must be the first member in structure */
16 void (*check_version) _((const char *name,
17 int dbis_cv, int dbis_cs, int need_dbixs_cv,
18 int drc_s, int dbc_s, int stc_s, int fdc_s));
19
20 /* version and size are used to check for DBI/DBD version mis-match */
21 U16 version; /* version of this structure */
22 U16 size;
23 U16 xs_version; /* version of the overall DBIXS / DBD interface */
24 U16 spare_pad;
25 };
26 typedef struct dbistate_st dbistate_t;
27
28 #define DBIcf_ACTIVE 0x000004 /* needs finish/disconnect before clear */
29
30 typedef U32 imp_sth;
31
32 /* not strictly part of the API... */
33 static imp_sth *
34 sth_get_imp (SV *sth)
35 {
36 MAGIC *mg = mg_find (SvRV (sth), PERL_MAGIC_tied);
37 sth = mg->mg_obj;
38 mg = mg_find (SvRV (sth), DBI_MAGIC);
39 return (imp_sth *)SvPVX (mg->mg_obj);
40 }
41
42 #define DBI_STH_ACTIVE(imp) (*(imp) & DBIcf_ACTIVE)
43
44 /* end of import section */
45
46 #if (PERL_VERSION < 5) || ((PERL_VERSION == 5) && (PERL_SUBVERSION <= 6))
47 # define get_sv perl_get_sv
48 # define call_method perl_call_method
49 # define call_sv perl_call_sv
50 #endif
51
52 #if (PERL_VERSION > 5) || ((PERL_VERSION == 5) && (PERL_SUBVERSION >= 6))
53 # define CAN_UTF8 1
54 #endif
55
56 #define MAX_CACHED_STATEMENT_SIZE 2048
57
58 static SV *
59 sql_upgrade_utf8 (SV *sv)
60 {
61 #if CAN_UTF8
62 if (SvPOK (sv))
63 sv_utf8_upgrade (sv);
64 #endif
65 return sv;
66 }
67
68 static SV *
69 mortalcopy_and_maybe_force_utf8(int utf8, SV *sv)
70 {
71 sv = sv_mortalcopy (sv);
72 #if CAN_UTF8
73 if (utf8 && SvPOK (sv))
74 SvUTF8_on (sv);
75 #endif
76 return sv;
77 }
78
79 #define maybe_upgrade_utf8(utf8,sv) ((utf8) ? sql_upgrade_utf8 (sv) : (sv))
80
81 #define is_dbh(sv) ((sv) && sv_isobject (sv) && sv_derived_from ((sv), "DBI::db"))
82
83 typedef struct lru_node {
84 struct lru_node *next;
85 struct lru_node *prev;
86 U32 hash; /* bit 31 is used to mark active nodes */
87 SV *dbh;
88 SV *sql;
89
90 SV *sth;
91 imp_sth *sth_imp;
92 #if 0 /* method cache */
93 GV *execute;
94 GV *bind_columns;
95 GV *fetch;
96 GV *finish;
97 #endif
98 } lru_node;
99
100 static lru_node lru_list;
101 static int lru_size;
102 static int lru_maxsize;
103
104 #define lru_init() lru_list.next = &lru_list; lru_list.prev = &lru_list /* other fields are zero */
105
106 /* this is primitive, yet effective */
107 /* the returned value must never be zero (or bad things will happen) */
108 #define lru_hash \
109 do { \
110 hash = (((U32)(long)dbh)>>4); \
111 hash += *statement; \
112 hash += len; \
113 } while (0)
114
115 /* fetch and "use" */
116 /* could be done using a single call (we could call prepare!) */
117 static SV *
118 lru_fetch (SV *dbh, SV *sql)
119 {
120 lru_node *n;
121
122 U32 hash;
123 STRLEN len;
124 char *statement = SvPV (sql, len);
125
126 dbh = SvRV (dbh);
127
128 lru_hash;
129
130 n = &lru_list;
131 do {
132 n = n->next;
133
134 if (!n->hash)
135 return 0;
136 } while (n->hash != hash
137 || DBI_STH_ACTIVE (n->sth_imp)
138 || !sv_eq (n->sql, sql)
139 || n->dbh != dbh);
140
141 /* found, so return to the start of the list */
142 n->prev->next = n->next;
143 n->next->prev = n->prev;
144
145 n->next = lru_list.next;
146 n->prev = &lru_list;
147 lru_list.next->prev = n;
148 lru_list.next = n;
149
150 return sv_2mortal (SvREFCNT_inc (n->sth));
151 }
152
153 static void
154 lru_trim (void)
155 {
156 while (lru_size > lru_maxsize)
157 {
158 /* nuke at the end */
159 lru_node *n = lru_list.prev;
160
161 n = lru_list.prev;
162
163 lru_list.prev = n->prev;
164 n->prev->next = &lru_list;
165
166 SvREFCNT_dec (n->dbh);
167 SvREFCNT_dec (n->sql);
168 SvREFCNT_dec (n->sth);
169 Safefree (n);
170
171 lru_size--;
172 }
173 }
174
175 /* store a not-yet existing entry(!) */
176 static void
177 lru_store (SV *dbh, SV *sql, SV *sth)
178 {
179 lru_node *n;
180 U32 hash;
181 STRLEN len;
182 char *statement;
183
184 if (!lru_maxsize)
185 return;
186
187 statement = SvPV (sql, len);
188 dbh = SvRV (dbh);
189
190 lru_hash;
191
192 lru_size++;
193 lru_trim ();
194
195 New (0, n, 1, lru_node);
196
197 n->hash = hash;
198 n->dbh = dbh; SvREFCNT_inc (dbh); /* note: this is the dbi hash itself, not the reference */
199 n->sql = newSVsv (sql);
200 n->sth = sth; SvREFCNT_inc (sth);
201 n->sth_imp = sth_get_imp (sth);
202
203 n->next = lru_list.next;
204 n->prev = &lru_list;
205 lru_list.next->prev = n;
206 lru_list.next = n;
207 }
208
209 static void
210 lru_cachesize (int size)
211 {
212 if (size >= 0)
213 {
214 lru_maxsize = size;
215 lru_trim ();
216 }
217 }
218
219 static GV *sql_exec;
220 static GV *DBH;
221 static SV *sv_prepare, *sv_execute, *sv_bind_columns,
222 *sv_fetchrow_arrayref, *sv_fetchall_arrayref,
223 *sv_finish;
224
225 #define newconstpv(str) newSVpvn ((str), sizeof (str))
226
227 MODULE = PApp::SQL PACKAGE = PApp::SQL
228
229 PROTOTYPES: DISABLE
230
231 BOOT:
232 {
233 struct dbistate_st *dbis = DBIS_PUBLISHED_LVALUE;
234
235 /* this is atcually wrong, we should call the check member, apparently */
236 assert (dbis->version == DBISTATE_VERSION);
237 assert (dbis->xs_version == DBIXS_VERSION);
238
239 sql_exec = gv_fetchpv ("PApp::SQL::sql_exec", TRUE, SVt_PV);
240 DBH = gv_fetchpv ("PApp::SQL::DBH" , TRUE, SVt_PV);
241
242 if (!sv_prepare)
243 {
244 sv_prepare = newconstpv ("prepare");
245 sv_execute = newconstpv ("execute");
246 sv_bind_columns = newconstpv ("bind_columns");
247 sv_fetchrow_arrayref = newconstpv ("fetchrow_arrayref");
248 sv_fetchall_arrayref = newconstpv ("fetchall_arrayref");
249 sv_finish = newconstpv ("finish");
250 }
251
252 /* apache might BOOT: twice :( */
253 if (lru_size)
254 lru_cachesize (0);
255
256 lru_init ();
257 lru_cachesize (50);
258 }
259
260 int
261 cachesize(size = -1)
262 int size
263 CODE:
264 RETVAL = lru_maxsize;
265 lru_cachesize (size);
266 OUTPUT:
267 RETVAL
268
269 void
270 sql_exec(...)
271 ALIAS:
272 sql_uexec = 1
273 sql_fetch = 2
274 sql_ufetch = 3
275 sql_fetchall = 4
276 sql_ufetchall = 5
277 sql_exists = 6
278 sql_uexists = 7
279 PPCODE:
280 {
281 if (items == 0)
282 croak ("Usage: sql_exec [database-handle,] [bind-var-refs,... ] \"sql-statement\", [arguments, ...]");
283 else
284 {
285 int arg = 0;
286 int bind_first, bind_last;
287 int count;
288 SV *dbh = ST(0);
289 SV *sth;
290 SV *sql;
291 SV *execute;
292 STRLEN dc, dd; /* dummy */
293
294 /* save our arguments against destruction through function calls */
295 SP += items;
296
297 /* first check wether we should use an explicit db handle */
298 if (!is_dbh (dbh))
299 {
300 /* the next line doesn't work - check why later maybe */
301 /* dbh = get_sv ("DBH", FALSE);
302 if (!is_dbh (dbh))
303 {*/
304 dbh = GvSV (DBH);
305 if (!is_dbh (dbh))
306 croak ("sql_exec: no $DBH argument and no fallback in $PApp::SQL::DBH");
307 /*croak ("sql_exec: no $DBH found in current package or in PApp::SQL::");
308 }*/
309 }
310 else
311 arg++; /* we consumed one argument */
312
313 /* be more Coro-friendly by keeping a copy, so different threads */
314 /* can replace their global handles */
315 dbh = sv_2mortal (newSVsv (dbh));
316
317 /* count the remaining references (for bind_columns) */
318 bind_first = arg;
319 while (items > arg && SvROK (ST(arg)))
320 arg++;
321
322 bind_last = arg;
323
324 /* consume the sql-statement itself */
325 if (items <= arg)
326 croak ("sql_exec: required argument \"sql-statement\" missing");
327
328 if (!SvPOK (ST(arg)))
329 croak ("sql_exec: sql-statement must be a string");
330
331 sql = ST(arg); arg++;
332
333 if ((ix & ~1) == 6)
334 {
335 SV *neu = sv_2mortal (newSVpv ("select count(*) > 0 from ", 0));
336 sv_catsv (neu, sql);
337 sv_catpv (neu, " limit 1");
338 sql = neu;
339 ix -= 4; /* sql_fetch */
340 }
341
342 /* check cache for existing statement handle */
343 sth = lru_fetch (dbh, sql);
344 if (!sth)
345 {
346 PUSHMARK (SP);
347 EXTEND (SP, 2);
348 PUSHs (dbh);
349 PUSHs (sql);
350 PUTBACK;
351 count = call_sv (sv_prepare, G_METHOD | G_SCALAR);
352 SPAGAIN;
353
354 if (count != 1)
355 croak ("sql_exec: unable to prepare() statement '%s': %s",
356 SvPV (sql, dc),
357 SvPV (get_sv ("DBI::errstr", TRUE), dd));
358
359 sth = POPs;
360
361 if (SvLEN (sql) < MAX_CACHED_STATEMENT_SIZE)
362 lru_store (dbh, sql, sth);
363 }
364
365 PUSHMARK (SP);
366 EXTEND (SP, items - arg + 1);
367 PUSHs (sth);
368 while (items > arg)
369 {
370 SV *sv = ST(arg);
371 /* we sv_mortalcopy magical values since DBI seems to have a memory
372 * leak when magical values are passed into execute().
373 */
374 PUSHs (maybe_upgrade_utf8 (ix & 1, SvMAGICAL(sv) ? sv_mortalcopy(sv) : sv));
375 arg++;
376 }
377
378 PUTBACK;
379 /* { static GV *execute;
380 if (!execute) execute = gv_fetchmethod_autoload(SvSTASH(SvRV(sth)), "execute", 0);
381 count = call_sv(GvCV(execute), G_SCALAR);
382 }*/
383 count = call_sv (sv_execute, G_METHOD | G_SCALAR);
384 SPAGAIN;
385
386 if (count != 1)
387 croak ("sql_exec: execute() didn't return any value ('%s'): %s",
388 SvPV (sql, dc),
389 SvPV (get_sv ("DBI::errstr", TRUE), dd));
390
391 execute = POPs;
392
393 if (!SvTRUE (execute))
394 croak ("sql_exec: unable to execute statement '%s' (%s)",
395 SvPV (sql, dc),
396 SvPV (get_sv ("DBI::errstr", TRUE), dd));
397
398 sv_setsv (GvSV(sql_exec), execute);
399
400 if (bind_first != bind_last)
401 {
402 PUSHMARK (SP);
403 EXTEND (SP, bind_last - bind_first + 2);
404 PUSHs (sth);
405 do {
406 #if CAN_UTF8
407 if (ix & 1)
408 SvUTF8_on (SvRV(ST(bind_first)));
409 #endif
410 PUSHs (ST(bind_first));
411 bind_first++;
412 } while (bind_first != bind_last);
413
414 PUTBACK;
415 count = call_sv (sv_bind_columns, G_METHOD | G_SCALAR);
416 SPAGAIN;
417
418 if (count != 1)
419 croak ("sql_exec: bind_columns() didn't return any value ('%s'): %s",
420 SvPV (sql, dc),
421 SvPV (get_sv ("DBI::errstr", TRUE), dd));
422
423 if (!SvOK (TOPs))
424 croak ("sql_exec: bind_columns() didn't return a true ('%s'): %s",
425 SvPV (sql, dc),
426 SvPV (get_sv ("DBI::errstr", TRUE), dd));
427
428 POPs;
429 }
430
431 /* restore our arguments again */
432 SP -= items;
433
434 if ((ix & ~1) == 2)
435 { /* sql_fetch */
436 SV *row;
437
438 PUSHMARK (SP);
439 XPUSHs (sth);
440 PUTBACK;
441 count = call_sv (sv_fetchrow_arrayref, G_METHOD | G_SCALAR);
442 SPAGAIN;
443
444 if (count != 1)
445 abort ();
446
447 row = POPs;
448
449 if (SvROK (row))
450 {
451 AV *av;
452
453 switch (GIMME_V)
454 {
455 case G_VOID:
456 /* no thing */
457 break;
458 case G_SCALAR:
459 /* the first element */
460 XPUSHs (mortalcopy_and_maybe_force_utf8 (ix & 1, *av_fetch ((AV *)SvRV (row), 0, 1)));
461 break;
462 case G_ARRAY:
463 av = (AV *)SvRV (row);
464 count = AvFILL (av) + 1;
465 EXTEND (SP, count);
466 for (arg = 0; arg < count; arg++)
467 PUSHs (mortalcopy_and_maybe_force_utf8 (ix & 1, AvARRAY (av)[arg]));
468
469 break;
470 default:
471 abort ();
472 }
473 }
474 }
475 else if ((ix & ~1) == 4)
476 { /* sql_fetchall */
477 SV *rows;
478
479 PUSHMARK (SP);
480 XPUSHs (sth);
481 PUTBACK;
482 count = call_sv (sv_fetchall_arrayref, G_METHOD | G_SCALAR);
483 SPAGAIN;
484
485 if (count != 1)
486 abort ();
487
488 rows = POPs;
489
490 if (SvROK (rows))
491 {
492 AV *av = (AV *)SvRV (rows);
493 count = AvFILL (av) + 1;
494
495 if (count)
496 {
497 int columns = AvFILL ((AV *) SvRV (AvARRAY (av)[0])) + 1; /* columns? */
498
499 EXTEND (SP, count);
500 if (columns == 1)
501 for (arg = 0; arg < count; arg++)
502 PUSHs (mortalcopy_and_maybe_force_utf8 (ix & 1, AvARRAY ((AV *)SvRV (AvARRAY (av)[arg]))[0]));
503 else
504 for (arg = 0; arg < count; arg++)
505 PUSHs (mortalcopy_and_maybe_force_utf8 (ix & 1, AvARRAY (av)[arg]));
506 }
507 }
508 }
509 else
510 XPUSHs (sth);
511
512 if (ix > 1 || GIMME_V == G_VOID)
513 {
514 PUSHMARK (SP);
515 XPUSHs (sth);
516 PUTBACK;
517 (void) call_sv (sv_finish, G_METHOD | G_DISCARD);
518 SPAGAIN;
519 }
520 }
521 }
522
523
524