pg 0.12.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data/BSDL +22 -0
  4. data/ChangeLog +1504 -11
  5. data/Contributors.rdoc +7 -0
  6. data/History.rdoc +181 -3
  7. data/LICENSE +12 -14
  8. data/Manifest.txt +29 -15
  9. data/{BSD → POSTGRES} +0 -0
  10. data/{README.OS_X.rdoc → README-OS_X.rdoc} +0 -0
  11. data/{README.windows.rdoc → README-Windows.rdoc} +0 -0
  12. data/README.ja.rdoc +10 -3
  13. data/README.rdoc +54 -28
  14. data/Rakefile +53 -26
  15. data/Rakefile.cross +235 -196
  16. data/ext/errorcodes.def +931 -0
  17. data/ext/errorcodes.rb +45 -0
  18. data/ext/errorcodes.txt +463 -0
  19. data/ext/extconf.rb +37 -7
  20. data/ext/gvl_wrappers.c +19 -0
  21. data/ext/gvl_wrappers.h +211 -0
  22. data/ext/pg.c +317 -4277
  23. data/ext/pg.h +124 -21
  24. data/ext/pg_connection.c +3642 -0
  25. data/ext/pg_errors.c +89 -0
  26. data/ext/pg_result.c +920 -0
  27. data/lib/pg/connection.rb +86 -0
  28. data/lib/pg/constants.rb +11 -0
  29. data/lib/pg/exceptions.rb +11 -0
  30. data/lib/pg/result.rb +16 -0
  31. data/lib/pg.rb +26 -43
  32. data/sample/array_insert.rb +20 -0
  33. data/sample/async_api.rb +21 -24
  34. data/sample/async_copyto.rb +2 -2
  35. data/sample/async_mixed.rb +56 -0
  36. data/sample/check_conn.rb +21 -0
  37. data/sample/copyfrom.rb +1 -1
  38. data/sample/copyto.rb +1 -1
  39. data/sample/cursor.rb +2 -2
  40. data/sample/disk_usage_report.rb +186 -0
  41. data/sample/issue-119.rb +94 -0
  42. data/sample/losample.rb +6 -6
  43. data/sample/minimal-testcase.rb +17 -0
  44. data/sample/notify_wait.rb +51 -22
  45. data/sample/pg_statistics.rb +294 -0
  46. data/sample/replication_monitor.rb +231 -0
  47. data/sample/test_binary_values.rb +4 -6
  48. data/sample/wal_shipper.rb +434 -0
  49. data/sample/warehouse_partitions.rb +320 -0
  50. data/spec/lib/helpers.rb +70 -23
  51. data/spec/pg/connection_spec.rb +1128 -0
  52. data/spec/{pgresult_spec.rb → pg/result_spec.rb} +142 -47
  53. data/spec/pg_spec.rb +44 -0
  54. data.tar.gz.sig +0 -0
  55. metadata +145 -100
  56. metadata.gz.sig +0 -0
  57. data/GPL +0 -340
  58. data/ext/compat.c +0 -541
  59. data/ext/compat.h +0 -184
  60. data/misc/openssl-pg-segfault.rb +0 -31
  61. data/sample/psql.rb +0 -1181
  62. data/sample/psqlHelp.rb +0 -158
  63. data/sample/test1.rb +0 -60
  64. data/sample/test2.rb +0 -44
  65. data/sample/test4.rb +0 -71
  66. data/spec/m17n_spec.rb +0 -151
  67. data/spec/pgconn_spec.rb +0 -643
data/ext/pg_result.c ADDED
@@ -0,0 +1,920 @@
1
+ /*
2
+ * pg_result.c - PG::Result class extension
3
+ * $Id: pg_result.c,v de6bee6a208c 2013/07/18 13:47:31 kanis $
4
+ *
5
+ */
6
+
7
+ #include "pg.h"
8
+
9
+
10
+ VALUE rb_cPGresult;
11
+
12
+ static void pgresult_gc_free( PGresult * );
13
+ static PGresult* pgresult_get( VALUE );
14
+
15
+
16
+ /*
17
+ * Global functions
18
+ */
19
+
20
+ /*
21
+ * Result constructor
22
+ */
23
+ VALUE
24
+ pg_new_result(PGresult *result, VALUE rb_pgconn)
25
+ {
26
+ PGconn *conn = pg_get_pgconn( rb_pgconn );
27
+ VALUE val = Data_Wrap_Struct(rb_cPGresult, NULL, pgresult_gc_free, result);
28
+ #ifdef M17N_SUPPORTED
29
+ rb_encoding *enc = pg_conn_enc_get( conn );
30
+ ENCODING_SET( val, rb_enc_to_index(enc) );
31
+ #endif
32
+
33
+ rb_iv_set( val, "@connection", rb_pgconn );
34
+
35
+ return val;
36
+ }
37
+
38
+ /*
39
+ * call-seq:
40
+ * res.check -> nil
41
+ *
42
+ * Raises appropriate exception if PG::Result is in a bad state.
43
+ */
44
+ VALUE
45
+ pg_result_check( VALUE self )
46
+ {
47
+ VALUE error, exception, klass;
48
+ VALUE rb_pgconn = rb_iv_get( self, "@connection" );
49
+ PGconn *conn = pg_get_pgconn(rb_pgconn);
50
+ PGresult *result;
51
+ #ifdef M17N_SUPPORTED
52
+ rb_encoding *enc = pg_conn_enc_get( conn );
53
+ #endif
54
+ char * sqlstate;
55
+
56
+ Data_Get_Struct(self, PGresult, result);
57
+
58
+ if(result == NULL)
59
+ {
60
+ error = rb_str_new2( PQerrorMessage(conn) );
61
+ }
62
+ else
63
+ {
64
+ switch (PQresultStatus(result))
65
+ {
66
+ case PGRES_TUPLES_OK:
67
+ case PGRES_COPY_OUT:
68
+ case PGRES_COPY_IN:
69
+ #ifdef HAVE_CONST_PGRES_COPY_BOTH
70
+ case PGRES_COPY_BOTH:
71
+ #endif
72
+ #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
73
+ case PGRES_SINGLE_TUPLE:
74
+ #endif
75
+ case PGRES_EMPTY_QUERY:
76
+ case PGRES_COMMAND_OK:
77
+ return Qnil;
78
+ case PGRES_BAD_RESPONSE:
79
+ case PGRES_FATAL_ERROR:
80
+ case PGRES_NONFATAL_ERROR:
81
+ error = rb_str_new2( PQresultErrorMessage(result) );
82
+ break;
83
+ default:
84
+ error = rb_str_new2( "internal error : unknown result status." );
85
+ }
86
+ }
87
+
88
+ #ifdef M17N_SUPPORTED
89
+ rb_enc_set_index( error, rb_enc_to_index(enc) );
90
+ #endif
91
+
92
+ sqlstate = PQresultErrorField( result, PG_DIAG_SQLSTATE );
93
+ klass = lookup_error_class( sqlstate );
94
+ exception = rb_exc_new3( klass, error );
95
+ rb_iv_set( exception, "@connection", rb_pgconn );
96
+ rb_iv_set( exception, "@result", result ? self : Qnil );
97
+ rb_exc_raise( exception );
98
+
99
+ /* Not reached */
100
+ return Qnil;
101
+ }
102
+
103
+
104
+ /*
105
+ * :TODO: This shouldn't be a global function, but it needs to be as long as pg_new_result
106
+ * doesn't handle blocks, check results, etc. Once connection and result are disentangled
107
+ * a bit more, I can make this a static pgresult_clear() again.
108
+ */
109
+
110
+ /*
111
+ * call-seq:
112
+ * res.clear() -> nil
113
+ *
114
+ * Clears the PG::Result object as the result of the query.
115
+ */
116
+ VALUE
117
+ pg_result_clear(VALUE self)
118
+ {
119
+ PQclear(pgresult_get(self));
120
+ DATA_PTR(self) = NULL;
121
+ return Qnil;
122
+ }
123
+
124
+
125
+
126
+ /*
127
+ * DATA pointer functions
128
+ */
129
+
130
+ /*
131
+ * GC Free function
132
+ */
133
+ static void
134
+ pgresult_gc_free( PGresult *result )
135
+ {
136
+ if(result != NULL)
137
+ PQclear(result);
138
+ }
139
+
140
+ /*
141
+ * Fetch the data pointer for the result object
142
+ */
143
+ static PGresult*
144
+ pgresult_get(VALUE self)
145
+ {
146
+ PGresult *result;
147
+ Data_Get_Struct(self, PGresult, result);
148
+ if (result == NULL) rb_raise(rb_ePGerror, "result has been cleared");
149
+ return result;
150
+ }
151
+
152
+
153
+ /********************************************************************
154
+ *
155
+ * Document-class: PG::Result
156
+ *
157
+ * The class to represent the query result tuples (rows).
158
+ * An instance of this class is created as the result of every query.
159
+ * You may need to invoke the #clear method of the instance when finished with
160
+ * the result for better memory performance.
161
+ *
162
+ * Example:
163
+ * require 'pg'
164
+ * conn = PGconn.open(:dbname => 'test')
165
+ * res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
166
+ * res.getvalue(0,0) # '1'
167
+ * res[0]['b'] # '2'
168
+ * res[0]['c'] # nil
169
+ *
170
+ */
171
+
172
+ /**************************************************************************
173
+ * PG::Result INSTANCE METHODS
174
+ **************************************************************************/
175
+
176
+ /*
177
+ * call-seq:
178
+ * res.result_status() -> Fixnum
179
+ *
180
+ * Returns the status of the query. The status value is one of:
181
+ * * +PGRES_EMPTY_QUERY+
182
+ * * +PGRES_COMMAND_OK+
183
+ * * +PGRES_TUPLES_OK+
184
+ * * +PGRES_COPY_OUT+
185
+ * * +PGRES_COPY_IN+
186
+ * * +PGRES_BAD_RESPONSE+
187
+ * * +PGRES_NONFATAL_ERROR+
188
+ * * +PGRES_FATAL_ERROR+
189
+ * * +PGRES_COPY_BOTH+
190
+ */
191
+ static VALUE
192
+ pgresult_result_status(VALUE self)
193
+ {
194
+ return INT2FIX(PQresultStatus(pgresult_get(self)));
195
+ }
196
+
197
+ /*
198
+ * call-seq:
199
+ * res.res_status( status ) -> String
200
+ *
201
+ * Returns the string representation of status +status+.
202
+ *
203
+ */
204
+ static VALUE
205
+ pgresult_res_status(VALUE self, VALUE status)
206
+ {
207
+ VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
208
+ ASSOCIATE_INDEX(ret, self);
209
+ return ret;
210
+ }
211
+
212
+ /*
213
+ * call-seq:
214
+ * res.error_message() -> String
215
+ *
216
+ * Returns the error message of the command as a string.
217
+ */
218
+ static VALUE
219
+ pgresult_error_message(VALUE self)
220
+ {
221
+ VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(pgresult_get(self)));
222
+ ASSOCIATE_INDEX(ret, self);
223
+ return ret;
224
+ }
225
+
226
+ /*
227
+ * call-seq:
228
+ * res.error_field(fieldcode) -> String
229
+ *
230
+ * Returns the individual field of an error.
231
+ *
232
+ * +fieldcode+ is one of:
233
+ * * +PG_DIAG_SEVERITY+
234
+ * * +PG_DIAG_SQLSTATE+
235
+ * * +PG_DIAG_MESSAGE_PRIMARY+
236
+ * * +PG_DIAG_MESSAGE_DETAIL+
237
+ * * +PG_DIAG_MESSAGE_HINT+
238
+ * * +PG_DIAG_STATEMENT_POSITION+
239
+ * * +PG_DIAG_INTERNAL_POSITION+
240
+ * * +PG_DIAG_INTERNAL_QUERY+
241
+ * * +PG_DIAG_CONTEXT+
242
+ * * +PG_DIAG_SOURCE_FILE+
243
+ * * +PG_DIAG_SOURCE_LINE+
244
+ * * +PG_DIAG_SOURCE_FUNCTION+
245
+ *
246
+ * An example:
247
+ *
248
+ * begin
249
+ * conn.exec( "SELECT * FROM nonexistant_table" )
250
+ * rescue PG::Error => err
251
+ * p [
252
+ * err.result.error_field( PG::Result::PG_DIAG_SEVERITY ),
253
+ * err.result.error_field( PG::Result::PG_DIAG_SQLSTATE ),
254
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_PRIMARY ),
255
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_DETAIL ),
256
+ * err.result.error_field( PG::Result::PG_DIAG_MESSAGE_HINT ),
257
+ * err.result.error_field( PG::Result::PG_DIAG_STATEMENT_POSITION ),
258
+ * err.result.error_field( PG::Result::PG_DIAG_INTERNAL_POSITION ),
259
+ * err.result.error_field( PG::Result::PG_DIAG_INTERNAL_QUERY ),
260
+ * err.result.error_field( PG::Result::PG_DIAG_CONTEXT ),
261
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_FILE ),
262
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_LINE ),
263
+ * err.result.error_field( PG::Result::PG_DIAG_SOURCE_FUNCTION ),
264
+ * ]
265
+ * end
266
+ *
267
+ * Outputs:
268
+ *
269
+ * ["ERROR", "42P01", "relation \"nonexistant_table\" does not exist", nil, nil,
270
+ * "15", nil, nil, nil, "path/to/parse_relation.c", "857", "parserOpenTable"]
271
+ */
272
+ static VALUE
273
+ pgresult_error_field(VALUE self, VALUE field)
274
+ {
275
+ PGresult *result = pgresult_get( self );
276
+ int fieldcode = NUM2INT( field );
277
+ char * fieldstr = PQresultErrorField( result, fieldcode );
278
+ VALUE ret = Qnil;
279
+
280
+ if ( fieldstr ) {
281
+ ret = rb_tainted_str_new2( fieldstr );
282
+ ASSOCIATE_INDEX( ret, self );
283
+ }
284
+
285
+ return ret;
286
+ }
287
+
288
+ /*
289
+ * call-seq:
290
+ * res.ntuples() -> Fixnum
291
+ *
292
+ * Returns the number of tuples in the query result.
293
+ */
294
+ static VALUE
295
+ pgresult_ntuples(VALUE self)
296
+ {
297
+ return INT2FIX(PQntuples(pgresult_get(self)));
298
+ }
299
+
300
+ /*
301
+ * call-seq:
302
+ * res.nfields() -> Fixnum
303
+ *
304
+ * Returns the number of columns in the query result.
305
+ */
306
+ static VALUE
307
+ pgresult_nfields(VALUE self)
308
+ {
309
+ return INT2NUM(PQnfields(pgresult_get(self)));
310
+ }
311
+
312
+ /*
313
+ * call-seq:
314
+ * res.fname( index ) -> String
315
+ *
316
+ * Returns the name of the column corresponding to _index_.
317
+ */
318
+ static VALUE
319
+ pgresult_fname(VALUE self, VALUE index)
320
+ {
321
+ VALUE fname;
322
+ PGresult *result;
323
+ int i = NUM2INT(index);
324
+
325
+ result = pgresult_get(self);
326
+ if (i < 0 || i >= PQnfields(result)) {
327
+ rb_raise(rb_eArgError,"invalid field number %d", i);
328
+ }
329
+ fname = rb_tainted_str_new2(PQfname(result, i));
330
+ ASSOCIATE_INDEX(fname, self);
331
+ return fname;
332
+ }
333
+
334
+ /*
335
+ * call-seq:
336
+ * res.fnumber( name ) -> Fixnum
337
+ *
338
+ * Returns the index of the field specified by the string +name+.
339
+ * The given +name+ is treated like an identifier in an SQL command, that is,
340
+ * it is downcased unless double-quoted. For example, given a query result
341
+ * generated from the SQL command:
342
+ *
343
+ * result = conn.exec( %{SELECT 1 AS FOO, 2 AS "BAR"} )
344
+ *
345
+ * we would have the results:
346
+ *
347
+ * result.fname( 0 ) # => "foo"
348
+ * result.fname( 1 ) # => "BAR"
349
+ * result.fnumber( "FOO" ) # => 0
350
+ * result.fnumber( "foo" ) # => 0
351
+ * result.fnumber( "BAR" ) # => ArgumentError
352
+ * result.fnumber( %{"BAR"} ) # => 1
353
+ *
354
+ * Raises an ArgumentError if the specified +name+ isn't one of the field names;
355
+ * raises a TypeError if +name+ is not a String.
356
+ */
357
+ static VALUE
358
+ pgresult_fnumber(VALUE self, VALUE name)
359
+ {
360
+ int n;
361
+
362
+ Check_Type(name, T_STRING);
363
+
364
+ n = PQfnumber(pgresult_get(self), StringValuePtr(name));
365
+ if (n == -1) {
366
+ rb_raise(rb_eArgError,"Unknown field: %s", StringValuePtr(name));
367
+ }
368
+ return INT2FIX(n);
369
+ }
370
+
371
+ /*
372
+ * call-seq:
373
+ * res.ftable( column_number ) -> Fixnum
374
+ *
375
+ * Returns the Oid of the table from which the column _column_number_
376
+ * was fetched.
377
+ *
378
+ * Raises ArgumentError if _column_number_ is out of range or if
379
+ * the Oid is undefined for that column.
380
+ */
381
+ static VALUE
382
+ pgresult_ftable(VALUE self, VALUE column_number)
383
+ {
384
+ Oid n ;
385
+ int col_number = NUM2INT(column_number);
386
+ PGresult *pgresult = pgresult_get(self);
387
+
388
+ if( col_number < 0 || col_number >= PQnfields(pgresult))
389
+ rb_raise(rb_eArgError,"Invalid column index: %d", col_number);
390
+
391
+ n = PQftable(pgresult, col_number);
392
+ return INT2FIX(n);
393
+ }
394
+
395
+ /*
396
+ * call-seq:
397
+ * res.ftablecol( column_number ) -> Fixnum
398
+ *
399
+ * Returns the column number (within its table) of the table from
400
+ * which the column _column_number_ is made up.
401
+ *
402
+ * Raises ArgumentError if _column_number_ is out of range or if
403
+ * the column number from its table is undefined for that column.
404
+ */
405
+ static VALUE
406
+ pgresult_ftablecol(VALUE self, VALUE column_number)
407
+ {
408
+ int col_number = NUM2INT(column_number);
409
+ PGresult *pgresult = pgresult_get(self);
410
+
411
+ int n;
412
+
413
+ if( col_number < 0 || col_number >= PQnfields(pgresult))
414
+ rb_raise(rb_eArgError,"Invalid column index: %d", col_number);
415
+
416
+ n = PQftablecol(pgresult, col_number);
417
+ return INT2FIX(n);
418
+ }
419
+
420
+ /*
421
+ * call-seq:
422
+ * res.fformat( column_number ) -> Fixnum
423
+ *
424
+ * Returns the format (0 for text, 1 for binary) of column
425
+ * _column_number_.
426
+ *
427
+ * Raises ArgumentError if _column_number_ is out of range.
428
+ */
429
+ static VALUE
430
+ pgresult_fformat(VALUE self, VALUE column_number)
431
+ {
432
+ PGresult *result = pgresult_get(self);
433
+ int fnumber = NUM2INT(column_number);
434
+ if (fnumber < 0 || fnumber >= PQnfields(result)) {
435
+ rb_raise(rb_eArgError, "Column number is out of range: %d",
436
+ fnumber);
437
+ }
438
+ return INT2FIX(PQfformat(result, fnumber));
439
+ }
440
+
441
+ /*
442
+ * call-seq:
443
+ * res.ftype( column_number )
444
+ *
445
+ * Returns the data type associated with _column_number_.
446
+ *
447
+ * The integer returned is the internal +OID+ number (in PostgreSQL)
448
+ * of the type. To get a human-readable value for the type, use the
449
+ * returned OID and the field's #fmod value with the format_type() SQL
450
+ * function:
451
+ *
452
+ * # Get the type of the second column of the result 'res'
453
+ * typename = conn.
454
+ * exec( "SELECT format_type($1,$2)", [res.ftype(1), res.fmod(1)] ).
455
+ * getvalue( 0, 0 )
456
+ *
457
+ * Raises an ArgumentError if _column_number_ is out of range.
458
+ */
459
+ static VALUE
460
+ pgresult_ftype(VALUE self, VALUE index)
461
+ {
462
+ PGresult* result = pgresult_get(self);
463
+ int i = NUM2INT(index);
464
+ if (i < 0 || i >= PQnfields(result)) {
465
+ rb_raise(rb_eArgError, "invalid field number %d", i);
466
+ }
467
+ return INT2NUM(PQftype(result, i));
468
+ }
469
+
470
+ /*
471
+ * call-seq:
472
+ * res.fmod( column_number )
473
+ *
474
+ * Returns the type modifier associated with column _column_number_. See
475
+ * the #ftype method for an example of how to use this.
476
+ *
477
+ * Raises an ArgumentError if _column_number_ is out of range.
478
+ */
479
+ static VALUE
480
+ pgresult_fmod(VALUE self, VALUE column_number)
481
+ {
482
+ PGresult *result = pgresult_get(self);
483
+ int fnumber = NUM2INT(column_number);
484
+ int modifier;
485
+ if (fnumber < 0 || fnumber >= PQnfields(result)) {
486
+ rb_raise(rb_eArgError, "Column number is out of range: %d",
487
+ fnumber);
488
+ }
489
+ modifier = PQfmod(result,fnumber);
490
+
491
+ return INT2NUM(modifier);
492
+ }
493
+
494
+ /*
495
+ * call-seq:
496
+ * res.fsize( index )
497
+ *
498
+ * Returns the size of the field type in bytes. Returns <tt>-1</tt> if the field is variable sized.
499
+ *
500
+ * res = conn.exec("SELECT myInt, myVarChar50 FROM foo")
501
+ * res.size(0) => 4
502
+ * res.size(1) => -1
503
+ */
504
+ static VALUE
505
+ pgresult_fsize(VALUE self, VALUE index)
506
+ {
507
+ PGresult *result;
508
+ int i = NUM2INT(index);
509
+
510
+ result = pgresult_get(self);
511
+ if (i < 0 || i >= PQnfields(result)) {
512
+ rb_raise(rb_eArgError,"invalid field number %d", i);
513
+ }
514
+ return INT2NUM(PQfsize(result, i));
515
+ }
516
+
517
+
518
+ static VALUE
519
+ pgresult_value(VALUE self, PGresult *result, int tuple_num, int field_num)
520
+ {
521
+ VALUE val;
522
+ if ( PQgetisnull(result, tuple_num, field_num) ) {
523
+ return Qnil;
524
+ }
525
+ else {
526
+ val = rb_tainted_str_new( PQgetvalue(result, tuple_num, field_num ),
527
+ PQgetlength(result, tuple_num, field_num) );
528
+
529
+ #ifdef M17N_SUPPORTED
530
+ /* associate client encoding for text format only */
531
+ if ( 0 == PQfformat(result, field_num) ) {
532
+ ASSOCIATE_INDEX( val, self );
533
+ } else {
534
+ rb_enc_associate( val, rb_ascii8bit_encoding() );
535
+ }
536
+ #endif
537
+
538
+ return val;
539
+ }
540
+ }
541
+
542
+ /*
543
+ * call-seq:
544
+ * res.getvalue( tup_num, field_num )
545
+ *
546
+ * Returns the value in tuple number _tup_num_, field _field_num_,
547
+ * or +nil+ if the field is +NULL+.
548
+ */
549
+ static VALUE
550
+ pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
551
+ {
552
+ PGresult *result;
553
+ int i = NUM2INT(tup_num);
554
+ int j = NUM2INT(field_num);
555
+
556
+ result = pgresult_get(self);
557
+ if(i < 0 || i >= PQntuples(result)) {
558
+ rb_raise(rb_eArgError,"invalid tuple number %d", i);
559
+ }
560
+ if(j < 0 || j >= PQnfields(result)) {
561
+ rb_raise(rb_eArgError,"invalid field number %d", j);
562
+ }
563
+ return pgresult_value(self, result, i, j);
564
+ }
565
+
566
+ /*
567
+ * call-seq:
568
+ * res.getisnull(tuple_position, field_position) -> boolean
569
+ *
570
+ * Returns +true+ if the specified value is +nil+; +false+ otherwise.
571
+ */
572
+ static VALUE
573
+ pgresult_getisnull(VALUE self, VALUE tup_num, VALUE field_num)
574
+ {
575
+ PGresult *result;
576
+ int i = NUM2INT(tup_num);
577
+ int j = NUM2INT(field_num);
578
+
579
+ result = pgresult_get(self);
580
+ if (i < 0 || i >= PQntuples(result)) {
581
+ rb_raise(rb_eArgError,"invalid tuple number %d", i);
582
+ }
583
+ if (j < 0 || j >= PQnfields(result)) {
584
+ rb_raise(rb_eArgError,"invalid field number %d", j);
585
+ }
586
+ return PQgetisnull(result, i, j) ? Qtrue : Qfalse;
587
+ }
588
+
589
+ /*
590
+ * call-seq:
591
+ * res.getlength( tup_num, field_num ) -> Fixnum
592
+ *
593
+ * Returns the (String) length of the field in bytes.
594
+ *
595
+ * Equivalent to <tt>res.value(<i>tup_num</i>,<i>field_num</i>).length</tt>.
596
+ */
597
+ static VALUE
598
+ pgresult_getlength(VALUE self, VALUE tup_num, VALUE field_num)
599
+ {
600
+ PGresult *result;
601
+ int i = NUM2INT(tup_num);
602
+ int j = NUM2INT(field_num);
603
+
604
+ result = pgresult_get(self);
605
+ if (i < 0 || i >= PQntuples(result)) {
606
+ rb_raise(rb_eArgError,"invalid tuple number %d", i);
607
+ }
608
+ if (j < 0 || j >= PQnfields(result)) {
609
+ rb_raise(rb_eArgError,"invalid field number %d", j);
610
+ }
611
+ return INT2FIX(PQgetlength(result, i, j));
612
+ }
613
+
614
+ /*
615
+ * call-seq:
616
+ * res.nparams() -> Fixnum
617
+ *
618
+ * Returns the number of parameters of a prepared statement.
619
+ * Only useful for the result returned by conn.describePrepared
620
+ */
621
+ static VALUE
622
+ pgresult_nparams(VALUE self)
623
+ {
624
+ PGresult *result;
625
+
626
+ result = pgresult_get(self);
627
+ return INT2FIX(PQnparams(result));
628
+ }
629
+
630
+ /*
631
+ * call-seq:
632
+ * res.paramtype( param_number ) -> Oid
633
+ *
634
+ * Returns the Oid of the data type of parameter _param_number_.
635
+ * Only useful for the result returned by conn.describePrepared
636
+ */
637
+ static VALUE
638
+ pgresult_paramtype(VALUE self, VALUE param_number)
639
+ {
640
+ PGresult *result;
641
+
642
+ result = pgresult_get(self);
643
+ return INT2FIX(PQparamtype(result,NUM2INT(param_number)));
644
+ }
645
+
646
+ /*
647
+ * call-seq:
648
+ * res.cmd_status() -> String
649
+ *
650
+ * Returns the status string of the last query command.
651
+ */
652
+ static VALUE
653
+ pgresult_cmd_status(VALUE self)
654
+ {
655
+ VALUE ret = rb_tainted_str_new2(PQcmdStatus(pgresult_get(self)));
656
+ ASSOCIATE_INDEX(ret, self);
657
+ return ret;
658
+ }
659
+
660
+ /*
661
+ * call-seq:
662
+ * res.cmd_tuples() -> Fixnum
663
+ *
664
+ * Returns the number of tuples (rows) affected by the SQL command.
665
+ *
666
+ * If the SQL command that generated the PG::Result was not one of:
667
+ * * +INSERT+
668
+ * * +UPDATE+
669
+ * * +DELETE+
670
+ * * +MOVE+
671
+ * * +FETCH+
672
+ * or if no tuples were affected, <tt>0</tt> is returned.
673
+ */
674
+ static VALUE
675
+ pgresult_cmd_tuples(VALUE self)
676
+ {
677
+ long n;
678
+ n = strtol(PQcmdTuples(pgresult_get(self)),NULL, 10);
679
+ return INT2NUM(n);
680
+ }
681
+
682
+ /*
683
+ * call-seq:
684
+ * res.oid_value() -> Fixnum
685
+ *
686
+ * Returns the +oid+ of the inserted row if applicable,
687
+ * otherwise +nil+.
688
+ */
689
+ static VALUE
690
+ pgresult_oid_value(VALUE self)
691
+ {
692
+ Oid n = PQoidValue(pgresult_get(self));
693
+ if (n == InvalidOid)
694
+ return Qnil;
695
+ else
696
+ return INT2FIX(n);
697
+ }
698
+
699
+ /* Utility methods not in libpq */
700
+
701
+ /*
702
+ * call-seq:
703
+ * res[ n ] -> Hash
704
+ *
705
+ * Returns tuple _n_ as a hash.
706
+ */
707
+ static VALUE
708
+ pgresult_aref(VALUE self, VALUE index)
709
+ {
710
+ PGresult *result = pgresult_get(self);
711
+ int tuple_num = NUM2INT(index);
712
+ int field_num;
713
+ VALUE fname;
714
+ VALUE tuple;
715
+
716
+ if ( tuple_num < 0 || tuple_num >= PQntuples(result) )
717
+ rb_raise( rb_eIndexError, "Index %d is out of range", tuple_num );
718
+
719
+ tuple = rb_hash_new();
720
+ for ( field_num = 0; field_num < PQnfields(result); field_num++ ) {
721
+ fname = rb_tainted_str_new2( PQfname(result,field_num) );
722
+ ASSOCIATE_INDEX(fname, self);
723
+ rb_hash_aset( tuple, fname, pgresult_value(self, result, tuple_num, field_num) );
724
+ }
725
+ return tuple;
726
+ }
727
+
728
+ /*
729
+ * call-seq:
730
+ * res.each_row { |row| ... }
731
+ *
732
+ * Yields each row of the result. The row is a list of column values.
733
+ */
734
+ static VALUE
735
+ pgresult_each_row(VALUE self)
736
+ {
737
+ PGresult* result = (PGresult*) pgresult_get(self);
738
+ int row;
739
+ int field;
740
+ int num_rows = PQntuples(result);
741
+ int num_fields = PQnfields(result);
742
+
743
+ for ( row = 0; row < num_rows; row++ ) {
744
+ VALUE new_row = rb_ary_new2(num_fields);
745
+
746
+ /* populate the row */
747
+ for ( field = 0; field < num_fields; field++ ) {
748
+ rb_ary_store( new_row, field, pgresult_value(self, result, row, field) );
749
+ }
750
+ rb_yield( new_row );
751
+ }
752
+
753
+ return Qnil;
754
+ }
755
+
756
+
757
+ /*
758
+ * Make a Ruby array out of the encoded values from the specified
759
+ * column in the given result.
760
+ */
761
+ static VALUE
762
+ make_column_result_array( VALUE self, int col )
763
+ {
764
+ PGresult *result = pgresult_get( self );
765
+ int rows = PQntuples( result );
766
+ int i;
767
+ VALUE val = Qnil;
768
+ VALUE results = rb_ary_new2( rows );
769
+
770
+ if ( col >= PQnfields(result) )
771
+ rb_raise( rb_eIndexError, "no column %d in result", col );
772
+
773
+ for ( i=0; i < rows; i++ ) {
774
+ val = rb_tainted_str_new( PQgetvalue(result, i, col),
775
+ PQgetlength(result, i, col) );
776
+
777
+ #ifdef M17N_SUPPORTED
778
+ /* associate client encoding for text format only */
779
+ if ( 0 == PQfformat(result, col) ) {
780
+ ASSOCIATE_INDEX( val, self );
781
+ } else {
782
+ rb_enc_associate( val, rb_ascii8bit_encoding() );
783
+ }
784
+ #endif
785
+
786
+ rb_ary_store( results, i, val );
787
+ }
788
+
789
+ return results;
790
+ }
791
+
792
+
793
+ /*
794
+ * call-seq:
795
+ * res.column_values( n ) -> array
796
+ *
797
+ * Returns an Array of the values from the nth column of each
798
+ * tuple in the result.
799
+ *
800
+ */
801
+ static VALUE
802
+ pgresult_column_values(VALUE self, VALUE index)
803
+ {
804
+ int col = NUM2INT( index );
805
+ return make_column_result_array( self, col );
806
+ }
807
+
808
+
809
+ /*
810
+ * call-seq:
811
+ * res.field_values( field ) -> array
812
+ *
813
+ * Returns an Array of the values from the given _field_ of each tuple in the result.
814
+ *
815
+ */
816
+ static VALUE
817
+ pgresult_field_values( VALUE self, VALUE field )
818
+ {
819
+ PGresult *result = pgresult_get( self );
820
+ const char *fieldname = StringValuePtr( field );
821
+ int fnum = PQfnumber( result, fieldname );
822
+
823
+ if ( fnum < 0 )
824
+ rb_raise( rb_eIndexError, "no such field '%s' in result", fieldname );
825
+
826
+ return make_column_result_array( self, fnum );
827
+ }
828
+
829
+
830
+ /*
831
+ * call-seq:
832
+ * res.each{ |tuple| ... }
833
+ *
834
+ * Invokes block for each tuple in the result set.
835
+ */
836
+ static VALUE
837
+ pgresult_each(VALUE self)
838
+ {
839
+ PGresult *result = pgresult_get(self);
840
+ int tuple_num;
841
+
842
+ for(tuple_num = 0; tuple_num < PQntuples(result); tuple_num++) {
843
+ rb_yield(pgresult_aref(self, INT2NUM(tuple_num)));
844
+ }
845
+ return self;
846
+ }
847
+
848
+ /*
849
+ * call-seq:
850
+ * res.fields() -> Array
851
+ *
852
+ * Returns an array of Strings representing the names of the fields in the result.
853
+ */
854
+ static VALUE
855
+ pgresult_fields(VALUE self)
856
+ {
857
+ PGresult *result = pgresult_get( self );
858
+ int n = PQnfields( result );
859
+ VALUE fields = rb_ary_new2( n );
860
+ int i;
861
+
862
+ for ( i = 0; i < n; i++ ) {
863
+ VALUE val = rb_tainted_str_new2(PQfname(result, i));
864
+ ASSOCIATE_INDEX(val, self);
865
+ rb_ary_store( fields, i, val );
866
+ }
867
+
868
+ return fields;
869
+ }
870
+
871
+
872
+ void
873
+ init_pg_result()
874
+ {
875
+ rb_cPGresult = rb_define_class_under( rb_mPG, "Result", rb_cObject );
876
+ rb_include_module(rb_cPGresult, rb_mEnumerable);
877
+ rb_include_module(rb_cPGresult, rb_mPGconstants);
878
+
879
+ /****** PG::Result INSTANCE METHODS: libpq ******/
880
+ rb_define_method(rb_cPGresult, "result_status", pgresult_result_status, 0);
881
+ rb_define_method(rb_cPGresult, "res_status", pgresult_res_status, 1);
882
+ rb_define_method(rb_cPGresult, "error_message", pgresult_error_message, 0);
883
+ rb_define_alias( rb_cPGresult, "result_error_message", "error_message");
884
+ rb_define_method(rb_cPGresult, "error_field", pgresult_error_field, 1);
885
+ rb_define_alias( rb_cPGresult, "result_error_field", "error_field" );
886
+ rb_define_method(rb_cPGresult, "clear", pg_result_clear, 0);
887
+ rb_define_method(rb_cPGresult, "check", pg_result_check, 0);
888
+ rb_define_alias (rb_cPGresult, "check_result", "check");
889
+ rb_define_method(rb_cPGresult, "ntuples", pgresult_ntuples, 0);
890
+ rb_define_alias(rb_cPGresult, "num_tuples", "ntuples");
891
+ rb_define_method(rb_cPGresult, "nfields", pgresult_nfields, 0);
892
+ rb_define_alias(rb_cPGresult, "num_fields", "nfields");
893
+ rb_define_method(rb_cPGresult, "fname", pgresult_fname, 1);
894
+ rb_define_method(rb_cPGresult, "fnumber", pgresult_fnumber, 1);
895
+ rb_define_method(rb_cPGresult, "ftable", pgresult_ftable, 1);
896
+ rb_define_method(rb_cPGresult, "ftablecol", pgresult_ftablecol, 1);
897
+ rb_define_method(rb_cPGresult, "fformat", pgresult_fformat, 1);
898
+ rb_define_method(rb_cPGresult, "ftype", pgresult_ftype, 1);
899
+ rb_define_method(rb_cPGresult, "fmod", pgresult_fmod, 1);
900
+ rb_define_method(rb_cPGresult, "fsize", pgresult_fsize, 1);
901
+ rb_define_method(rb_cPGresult, "getvalue", pgresult_getvalue, 2);
902
+ rb_define_method(rb_cPGresult, "getisnull", pgresult_getisnull, 2);
903
+ rb_define_method(rb_cPGresult, "getlength", pgresult_getlength, 2);
904
+ rb_define_method(rb_cPGresult, "nparams", pgresult_nparams, 0);
905
+ rb_define_method(rb_cPGresult, "paramtype", pgresult_paramtype, 1);
906
+ rb_define_method(rb_cPGresult, "cmd_status", pgresult_cmd_status, 0);
907
+ rb_define_method(rb_cPGresult, "cmd_tuples", pgresult_cmd_tuples, 0);
908
+ rb_define_alias(rb_cPGresult, "cmdtuples", "cmd_tuples");
909
+ rb_define_method(rb_cPGresult, "oid_value", pgresult_oid_value, 0);
910
+
911
+ /****** PG::Result INSTANCE METHODS: other ******/
912
+ rb_define_method(rb_cPGresult, "[]", pgresult_aref, 1);
913
+ rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
914
+ rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
915
+ rb_define_method(rb_cPGresult, "each_row", pgresult_each_row, 0);
916
+ rb_define_method(rb_cPGresult, "column_values", pgresult_column_values, 1);
917
+ rb_define_method(rb_cPGresult, "field_values", pgresult_field_values, 1);
918
+ }
919
+
920
+