ruby-pg 0.7.9.2008.01.08

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+