ruby-pg 0.7.9.2008.01.08

Sign up to get free protection for your applications and to get access to all the features.
Files changed (12) hide show
  1. data/COPYING +340 -0
  2. data/COPYING.txt +340 -0
  3. data/Contributors +28 -0
  4. data/GPL +340 -0
  5. data/LICENSE +58 -0
  6. data/README +125 -0
  7. data/ext/compat.c +494 -0
  8. data/ext/compat.h +148 -0
  9. data/ext/extconf.rb +52 -0
  10. data/ext/pg.c +3043 -0
  11. data/ext/pg.h +13 -0
  12. metadata +59 -0
data/ext/compat.c ADDED
@@ -0,0 +1,494 @@
1
+ /************************************************
2
+
3
+ compat.c -
4
+
5
+ Author: matz
6
+ created at: Tue May 13 20:07:35 JST 1997
7
+
8
+ Author: ematsu
9
+ modified at: Wed Jan 20 16:41:51 1999
10
+
11
+ $Author: jdavis $
12
+ $Date: 2007-12-04 14:25:44 -0800 (Tue, 04 Dec 2007) $
13
+ ************************************************/
14
+
15
+ #include "compat.h"
16
+
17
+ #ifndef HAVE_PQDESCRIBEPREPARED
18
+ PGresult *
19
+ PQdescribePrepared(PGconn *conn, const char *stmtName)
20
+ {
21
+ rb_raise(rb_eStandardError, "PQdescribePrepared not supported by this client version.");
22
+ }
23
+ #endif /* HAVE_PQDESCRIBEPREPARED */
24
+
25
+ #ifndef HAVE_PQDESCRIBEPORTAL
26
+ PGresult *
27
+ PQdescribePortal(PGconn *conn, const char *portalName)
28
+ {
29
+ rb_raise(rb_eStandardError, "PQdescribePortal not supported by this client version.");
30
+ }
31
+ #endif /* HAVE_PQDESCRIBEPORTAL */
32
+
33
+ #ifndef HAVE_PQESCAPESTRINGCONN
34
+ size_t
35
+ PQescapeStringConn(PGconn *conn, char *to, const char *from,
36
+ size_t length, int *error)
37
+ {
38
+ return PQescapeString(to,from,length);
39
+ }
40
+
41
+ unsigned char *
42
+ PQescapeByteaConn(PGconn *conn, const unsigned char *from,
43
+ size_t from_length, size_t *to_length)
44
+ {
45
+ return PQescapeBytea(from, from_length, to_length);
46
+ }
47
+ #endif /* HAVE_PQESCAPESTRINGCONN */
48
+
49
+ #ifndef HAVE_PQPREPARE
50
+ PGresult *
51
+ PQprepare(PGconn *conn, const char *stmtName, const char *query,
52
+ int nParams, const Oid *paramTypes)
53
+ {
54
+ rb_raise(rb_eStandardError, "PQprepare not supported by this client version.");
55
+ }
56
+ #endif /* HAVE_PQPREPARE */
57
+
58
+ #ifndef HAVE_PQCONNECTIONNEEDSPASSWORD
59
+ int
60
+ PQconnectionNeedsPassword(PGconn *conn)
61
+ {
62
+ rb_raise(rb_eStandardError,
63
+ "PQconnectionNeedsPassword not supported by this client version.");
64
+ }
65
+ #endif /* HAVE_PQCONNECTIONUSEDPASSWORD */
66
+
67
+ #ifndef HAVE_PQCONNECTIONUSEDPASSWORD
68
+ int
69
+ PQconnectionUsedPassword(PGconn *conn)
70
+ {
71
+ rb_raise(rb_eStandardError,
72
+ "PQconnectionUsedPassword not supported by this client version.");
73
+ }
74
+ #endif /* HAVE_PQCONNECTIONUSEDPASSWORD */
75
+
76
+ #ifndef HAVE_PQISTHREADSAFE
77
+ int
78
+ PQisthreadsafe()
79
+ {
80
+ return Qfalse;
81
+ }
82
+ #endif /* HAVE_PQISTHREADSAFE */
83
+
84
+ #ifndef HAVE_LO_TRUNCATE
85
+ int
86
+ lo_truncate(PGconn *conn, int fd, size_t len)
87
+ {
88
+ rb_raise(rb_eStandardError, "lo_truncate not supported by this client version.");
89
+ }
90
+ #endif /* HAVE_LO_TRUNCATE */
91
+
92
+ #ifndef HAVE_LO_CREATE
93
+ Oid
94
+ lo_create(PGconn *conn, Oid lobjId)
95
+ {
96
+ rb_raise(rb_eStandardError, "lo_create not supported by this client version.");
97
+ }
98
+ #endif /* HAVE_LO_CREATE */
99
+
100
+ #ifndef HAVE_PQNPARAMS
101
+ int
102
+ PQnparams(const PGresult *res)
103
+ {
104
+ rb_raise(rb_eStandardError, "PQnparams not supported by this client version.");
105
+ }
106
+ #endif /* HAVE_PQNPARAMS */
107
+
108
+ #ifndef HAVE_PQPARAMTYPE
109
+ Oid
110
+ PQparamtype(const PGresult *res, int param_number)
111
+ {
112
+ rb_raise(rb_eStandardError, "PQparamtype not supported by this client version.");
113
+ }
114
+ #endif /* HAVE_PQPARAMTYPE */
115
+
116
+ #ifndef HAVE_PQSERVERVERSION
117
+ int
118
+ PQserverVersion(const PGconn* conn)
119
+ {
120
+ rb_raise(rb_eStandardError, "PQserverVersion not supported by this client version.");
121
+ }
122
+ #endif /* HAVE_PQSERVERVERSION */
123
+
124
+ #ifndef HAVE_PQSENDDESCRIBEPREPARED
125
+ int
126
+ PQsendDescribePrepared(PGconn *conn, const char *stmtName)
127
+ {
128
+ rb_raise(rb_eStandardError, "PQsendDescribePrepared not supported by this client version.");
129
+ }
130
+ #endif /* HAVE_PQSENDDESCRIBEPREPARED */
131
+
132
+ #ifndef HAVE_PQSENDDESCRIBEPORTAL
133
+ int
134
+ PQsendDescribePortal(PGconn *conn, const char *portalName)
135
+ {
136
+ rb_raise(rb_eStandardError, "PQsendDescribePortal not supported by this client version.");
137
+ }
138
+ #endif /* HAVE_PQSENDDESCRIBEPORTAL */
139
+
140
+ #ifndef HAVE_PQSENDPREPARE
141
+ int
142
+ PQsendPrepare(PGconn *conn, const char *stmtName, const char *query,
143
+ int nParams, const Oid *paramTypes)
144
+ {
145
+ rb_raise(rb_eStandardError, "PQsendPrepare not supported by this client version.");
146
+ }
147
+ #endif /* HAVE_PQSENDPREPARE */
148
+
149
+ #ifndef HAVE_PQENCRYPTPASSWORD
150
+ char *
151
+ PQencryptPassword(const char *passwd, const char *user)
152
+ {
153
+ rb_raise(rb_eStandardError, "PQencryptPassword not supported by this client version.");
154
+ }
155
+ #endif /* HAVE_PQENCRYPTPASSWORD */
156
+
157
+ #ifndef HAVE_PQEXECPARAMS
158
+ PGresult *
159
+ PQexecParams(PGconn *conn, const char *command, int nParams,
160
+ const Oid *paramTypes, const char * const * paramValues, const int *paramLengths,
161
+ const int *paramFormats, int resultFormat)
162
+ {
163
+ rb_raise(rb_eStandardError, "PQexecParams not supported by this client version.");
164
+ }
165
+
166
+ #define BIND_PARAM_PATTERN "\\$(\\d+)"
167
+ #include <ruby.h>
168
+ #include <re.h>
169
+ PGresult *
170
+ PQexecParams_compat(PGconn *conn, VALUE command, VALUE values)
171
+ {
172
+ VALUE bind_param_re = rb_reg_new(BIND_PARAM_PATTERN, 7, 0);
173
+ VALUE result = rb_str_buf_new(RSTRING(command)->len);
174
+ char* ptr = RSTRING(command)->ptr;
175
+ int scan = 0;
176
+ while ((scan = rb_reg_search(bind_param_re, command, scan, 0)) > 0) {
177
+ VALUE match = rb_backref_get();
178
+ int pos = BindParamNumber(match);
179
+ if (pos < RARRAY(values)->len) {
180
+ rb_str_buf_cat(result, ptr, scan - (ptr - RSTRING(command)->ptr));
181
+ ptr = RSTRING(command)->ptr + scan;
182
+ rb_str_buf_append(result, RARRAY(values)->ptr[pos]);
183
+ }
184
+ scan += RSTRING(rb_reg_nth_match(0, match))->len;
185
+ ptr += RSTRING(rb_reg_nth_match(0, match))->len;
186
+ }
187
+ rb_str_buf_cat(result, ptr, RSTRING(command)->len - (ptr - RSTRING(command)->ptr));
188
+
189
+ return PQexec(conn, StringValuePtr(result));
190
+ }
191
+ #endif /* HAVE_PQEXECPARAMS */
192
+
193
+
194
+ /**************************************************************************
195
+
196
+ IF ANY CODE IS COPIED FROM POSTGRESQL, PLACE IT AFTER THIS COMMENT.
197
+ THE BSD LICENSE REQUIRES THAT YOU MAINTAIN THIS COPYRIGHT NOTICE.
198
+
199
+ ***************************************************************************
200
+
201
+ Portions of code after this point were copied from the PostgreSQL source
202
+ distribution, available at http://www.postgresql.org
203
+
204
+ ***************************************************************************
205
+
206
+ Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
207
+
208
+ Portions Copyright (c) 1994, The Regents of the University of California
209
+
210
+ Permission to use, copy, modify, and distribute this software and its
211
+ documentation for any purpose, without fee, and without a written agreement
212
+ is hereby granted, provided that the above copyright notice and this
213
+ paragraph and the following two paragraphs appear in all copies.
214
+
215
+ IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
216
+ DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
217
+ LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
218
+ DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
219
+ POSSIBILITY OF SUCH DAMAGE.
220
+
221
+ THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
222
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
223
+ AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
224
+ ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
225
+ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
226
+
227
+ **************************************************************************/
228
+
229
+ #ifndef HAVE_PQSETCLIENTENCODING
230
+
231
+ int
232
+ PQsetClientEncoding(PGconn *conn, const char *encoding)
233
+ {
234
+ char qbuf[128];
235
+ static const char query[] = "set client_encoding to '%s'";
236
+ PGresult *res;
237
+ int status;
238
+
239
+ if (!conn || conn->status != CONNECTION_OK)
240
+ return -1;
241
+
242
+ if (!encoding)
243
+ return -1;
244
+
245
+ /* check query buffer overflow */
246
+ if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
247
+ return -1;
248
+
249
+ /* ok, now send a query */
250
+ sprintf(qbuf, query, encoding);
251
+ res = PQexec(conn, qbuf);
252
+
253
+ if (res == NULL)
254
+ return -1;
255
+ if (res->resultStatus != PGRES_COMMAND_OK)
256
+ status = -1;
257
+ else
258
+ {
259
+ /*
260
+ * In protocol 2 we have to assume the setting will stick, and adjust
261
+ * our state immediately. In protocol 3 and up we can rely on the
262
+ * backend to report the parameter value, and we'll change state at
263
+ * that time.
264
+ */
265
+ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
266
+ pqSaveParameterStatus(conn, "client_encoding", encoding);
267
+ status = 0; /* everything is ok */
268
+ }
269
+ PQclear(res);
270
+ return status;
271
+ }
272
+ #endif /* HAVE_PQSETCLIENTENCODING */
273
+
274
+ #ifndef HAVE_PQESCAPESTRING
275
+ /*
276
+ * Escaping arbitrary strings to get valid SQL literal strings.
277
+ *
278
+ * Replaces "\\" with "\\\\" and "'" with "''".
279
+ *
280
+ * length is the length of the source string. (Note: if a terminating NUL
281
+ * is encountered sooner, PQescapeString stops short of "length"; the behavior
282
+ * is thus rather like strncpy.)
283
+ *
284
+ * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
285
+ * A terminating NUL character is added to the output string, whether the
286
+ * input is NUL-terminated or not.
287
+ *
288
+ * Returns the actual length of the output (not counting the terminating NUL).
289
+ */
290
+ size_t
291
+ PQescapeString(char *to, const char *from, size_t length)
292
+ {
293
+ const char *source = from;
294
+ char *target = to;
295
+ size_t remaining = length;
296
+
297
+ while (remaining > 0 && *source != '\0')
298
+ {
299
+ switch (*source)
300
+ {
301
+ case '\\':
302
+ *target++ = '\\';
303
+ *target++ = '\\';
304
+ break;
305
+
306
+ case '\'':
307
+ *target++ = '\'';
308
+ *target++ = '\'';
309
+ break;
310
+
311
+ default:
312
+ *target++ = *source;
313
+ break;
314
+ }
315
+ source++;
316
+ remaining--;
317
+ }
318
+
319
+ /* Write the terminating NUL character. */
320
+ *target = '\0';
321
+
322
+ return target - to;
323
+ }
324
+
325
+ /*
326
+ * PQescapeBytea - converts from binary string to the
327
+ * minimal encoding necessary to include the string in an SQL
328
+ * INSERT statement with a bytea type column as the target.
329
+ *
330
+ * The following transformations are applied
331
+ * '\0' == ASCII 0 == \\000
332
+ * '\'' == ASCII 39 == \'
333
+ * '\\' == ASCII 92 == \\\\
334
+ * anything < 0x20, or > 0x7e ---> \\ooo
335
+ * (where ooo is an octal expression)
336
+ */
337
+ unsigned char *
338
+ PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
339
+ {
340
+ const unsigned char *vp;
341
+ unsigned char *rp;
342
+ unsigned char *result;
343
+ size_t i;
344
+ size_t len;
345
+
346
+ /*
347
+ * empty string has 1 char ('\0')
348
+ */
349
+ len = 1;
350
+
351
+ vp = bintext;
352
+ for (i = binlen; i > 0; i--, vp++)
353
+ {
354
+ if (*vp < 0x20 || *vp > 0x7e)
355
+ len += 5; /* '5' is for '\\ooo' */
356
+ else if (*vp == '\'')
357
+ len += 2;
358
+ else if (*vp == '\\')
359
+ len += 4;
360
+ else
361
+ len++;
362
+ }
363
+
364
+ rp = result = (unsigned char *) malloc(len);
365
+ if (rp == NULL)
366
+ return NULL;
367
+
368
+ vp = bintext;
369
+ *bytealen = len;
370
+
371
+ for (i = binlen; i > 0; i--, vp++)
372
+ {
373
+ if (*vp < 0x20 || *vp > 0x7e)
374
+ {
375
+ (void) sprintf(rp, "\\\\%03o", *vp);
376
+ rp += 5;
377
+ }
378
+ else if (*vp == '\'')
379
+ {
380
+ rp[0] = '\\';
381
+ rp[1] = '\'';
382
+ rp += 2;
383
+ }
384
+ else if (*vp == '\\')
385
+ {
386
+ rp[0] = '\\';
387
+ rp[1] = '\\';
388
+ rp[2] = '\\';
389
+ rp[3] = '\\';
390
+ rp += 4;
391
+ }
392
+ else
393
+ *rp++ = *vp;
394
+ }
395
+ *rp = '\0';
396
+
397
+ return result;
398
+ }
399
+
400
+ #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
401
+ #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
402
+ #define OCTVAL(CH) ((CH) - '0')
403
+
404
+ /*
405
+ * PQunescapeBytea - converts the null terminated string representation
406
+ * of a bytea, strtext, into binary, filling a buffer. It returns a
407
+ * pointer to the buffer (or NULL on error), and the size of the
408
+ * buffer in retbuflen. The pointer may subsequently be used as an
409
+ * argument to the function free(3). It is the reverse of PQescapeBytea.
410
+ *
411
+ * The following transformations are made:
412
+ * \\ == ASCII 92 == \
413
+ * \ooo == a byte whose value = ooo (ooo is an octal number)
414
+ * \x == x (x is any character not matched by the above transformations)
415
+ */
416
+ unsigned char *
417
+ PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
418
+ {
419
+ size_t strtextlen,
420
+ buflen;
421
+ unsigned char *buffer,
422
+ *tmpbuf;
423
+ size_t i,
424
+ j;
425
+
426
+ if (strtext == NULL)
427
+ return NULL;
428
+
429
+ strtextlen = strlen(strtext);
430
+
431
+ /*
432
+ * Length of input is max length of output, but add one to avoid
433
+ * unportable malloc(0) if input is zero-length.
434
+ */
435
+ buffer = (unsigned char *) malloc(strtextlen + 1);
436
+ if (buffer == NULL)
437
+ return NULL;
438
+
439
+ for (i = j = 0; i < strtextlen;)
440
+ {
441
+ switch (strtext[i])
442
+ {
443
+ case '\\':
444
+ i++;
445
+ if (strtext[i] == '\\')
446
+ buffer[j++] = strtext[i++];
447
+ else
448
+ {
449
+ if ((ISFIRSTOCTDIGIT(strtext[i])) &&
450
+ (ISOCTDIGIT(strtext[i + 1])) &&
451
+ (ISOCTDIGIT(strtext[i + 2])))
452
+ {
453
+ int byte;
454
+
455
+ byte = OCTVAL(strtext[i++]);
456
+ byte = (byte << 3) + OCTVAL(strtext[i++]);
457
+ byte = (byte << 3) + OCTVAL(strtext[i++]);
458
+ buffer[j++] = byte;
459
+ }
460
+ }
461
+
462
+ /*
463
+ * Note: if we see '\' followed by something that isn't a
464
+ * recognized escape sequence, we loop around having done
465
+ * nothing except advance i. Therefore the something will
466
+ * be emitted as ordinary data on the next cycle. Corner
467
+ * case: '\' at end of string will just be discarded.
468
+ */
469
+ break;
470
+
471
+ default:
472
+ buffer[j++] = strtext[i++];
473
+ break;
474
+ }
475
+ }
476
+ buflen = j; /* buflen is the length of the dequoted
477
+ * data */
478
+
479
+ /* Shrink the buffer to be no larger than necessary */
480
+ /* +1 avoids unportable behavior when buflen==0 */
481
+ tmpbuf = realloc(buffer, buflen + 1);
482
+
483
+ /* It would only be a very brain-dead realloc that could fail, but... */
484
+ if (!tmpbuf)
485
+ {
486
+ free(buffer);
487
+ return NULL;
488
+ }
489
+
490
+ *retbuflen = buflen;
491
+ return tmpbuf;
492
+ }
493
+ #endif
494
+