globegit-postgresql-plruby 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/Changes +121 -0
  2. data/README.markdown +155 -0
  3. data/Rakefile +48 -0
  4. data/docs/plruby.rb +1931 -0
  5. data/ex_trans.sql +33 -0
  6. data/extconf.rb +267 -0
  7. data/plruby.html +1454 -0
  8. data/plruby.rd +1571 -0
  9. data/postgresql-plruby.gemspec +56 -0
  10. data/src/conversions.h +5 -0
  11. data/src/conversions/basic/conversions.h +25 -0
  12. data/src/conversions/basic/extconf.rb +8 -0
  13. data/src/conversions/basic/plruby_basic.c +357 -0
  14. data/src/conversions/bitstring/bitstring.sql +75 -0
  15. data/src/conversions/bitstring/conversions.h +15 -0
  16. data/src/conversions/bitstring/extconf.rb +8 -0
  17. data/src/conversions/bitstring/plruby_bitstring.c +579 -0
  18. data/src/conversions/convcommon.h +129 -0
  19. data/src/conversions/datetime/conversions.h +13 -0
  20. data/src/conversions/datetime/extconf.rb +8 -0
  21. data/src/conversions/datetime/plruby_datetime.c +269 -0
  22. data/src/conversions/geometry/conversions.h +37 -0
  23. data/src/conversions/geometry/extconf.rb +8 -0
  24. data/src/conversions/geometry/geometry.sql +196 -0
  25. data/src/conversions/geometry/plruby_geometry.c +2494 -0
  26. data/src/conversions/network/conversions.h +21 -0
  27. data/src/conversions/network/extconf.rb +8 -0
  28. data/src/conversions/network/network.sql +63 -0
  29. data/src/conversions/network/plruby_network.c +537 -0
  30. data/src/package.h +20 -0
  31. data/src/plpl.c +1708 -0
  32. data/src/plplan.c +893 -0
  33. data/src/plruby.c +1676 -0
  34. data/src/plruby.h +324 -0
  35. data/src/pltrans.c +388 -0
  36. data/test/conv_bitstring/b.rb +45 -0
  37. data/test/conv_bitstring/runtest +26 -0
  38. data/test/conv_bitstring/test.expected.73 +148 -0
  39. data/test/conv_bitstring/test.expected.74 +148 -0
  40. data/test/conv_bitstring/test.expected.80 +148 -0
  41. data/test/conv_bitstring/test.expected.81 +148 -0
  42. data/test/conv_bitstring/test.expected.82 +148 -0
  43. data/test/conv_bitstring/test.expected.83 +148 -0
  44. data/test/conv_bitstring/test.expected.84 +148 -0
  45. data/test/conv_bitstring/test.out +148 -0
  46. data/test/conv_bitstring/test_mklang.sql +8 -0
  47. data/test/conv_bitstring/test_queries.sql +63 -0
  48. data/test/conv_bitstring/test_queries.sql.in +63 -0
  49. data/test/conv_geometry/b.rb +45 -0
  50. data/test/conv_geometry/runtest +26 -0
  51. data/test/conv_geometry/test.expected.73 +265 -0
  52. data/test/conv_geometry/test.expected.74 +265 -0
  53. data/test/conv_geometry/test.expected.80 +265 -0
  54. data/test/conv_geometry/test.expected.81 +265 -0
  55. data/test/conv_geometry/test.expected.82 +265 -0
  56. data/test/conv_geometry/test.expected.83 +265 -0
  57. data/test/conv_geometry/test.expected.84 +265 -0
  58. data/test/conv_geometry/test.out +265 -0
  59. data/test/conv_geometry/test_mklang.sql +8 -0
  60. data/test/conv_geometry/test_queries.sql +194 -0
  61. data/test/conv_geometry/test_queries.sql.in +194 -0
  62. data/test/conv_network/b.rb +45 -0
  63. data/test/conv_network/runtest +26 -0
  64. data/test/conv_network/test.expected.73 +213 -0
  65. data/test/conv_network/test.expected.74 +237 -0
  66. data/test/conv_network/test.expected.80 +237 -0
  67. data/test/conv_network/test.expected.81 +237 -0
  68. data/test/conv_network/test.expected.82 +237 -0
  69. data/test/conv_network/test.expected.83 +237 -0
  70. data/test/conv_network/test.expected.84 +237 -0
  71. data/test/conv_network/test.out +237 -0
  72. data/test/conv_network/test_mklang.sql +8 -0
  73. data/test/conv_network/test_queries.sql +60 -0
  74. data/test/conv_network/test_queries.sql.in +60 -0
  75. data/test/plp/b.rb +34 -0
  76. data/test/plp/runtest +29 -0
  77. data/test/plp/test.expected.73 +472 -0
  78. data/test/plp/test.expected.74 +472 -0
  79. data/test/plp/test.expected.75 +472 -0
  80. data/test/plp/test.expected.80 +472 -0
  81. data/test/plp/test.expected.81 +472 -0
  82. data/test/plp/test.expected.82 +472 -0
  83. data/test/plp/test.expected.83 +472 -0
  84. data/test/plp/test.expected.84 +472 -0
  85. data/test/plp/test.out +472 -0
  86. data/test/plp/test_mklang.sql +8 -0
  87. data/test/plp/test_queries.sql +273 -0
  88. data/test/plp/test_setup.sql +931 -0
  89. data/test/plp/test_setup.sql.in +931 -0
  90. data/test/plt/b.rb +34 -0
  91. data/test/plt/runtest +29 -0
  92. data/test/plt/test.expected.73 +178 -0
  93. data/test/plt/test.expected.74 +178 -0
  94. data/test/plt/test.expected.75 +178 -0
  95. data/test/plt/test.expected.80 +178 -0
  96. data/test/plt/test.expected.81 +178 -0
  97. data/test/plt/test.expected.82 +178 -0
  98. data/test/plt/test.expected.83 +164 -0
  99. data/test/plt/test.expected.84 +168 -0
  100. data/test/plt/test.out +168 -0
  101. data/test/plt/test_mklang.sql +8 -0
  102. data/test/plt/test_queries.sql +72 -0
  103. data/test/plt/test_setup.sql +252 -0
  104. data/test/plt/test_setup.sql.in +252 -0
  105. data/test/range/b.rb +45 -0
  106. data/test/range/runtest +26 -0
  107. data/test/range/test.expected.73 +396 -0
  108. data/test/range/test.expected.73.in +396 -0
  109. data/test/range/test.expected.74 +396 -0
  110. data/test/range/test.expected.74.in +396 -0
  111. data/test/range/test.expected.75 +396 -0
  112. data/test/range/test.expected.75.in +396 -0
  113. data/test/range/test.expected.80 +396 -0
  114. data/test/range/test.expected.81 +397 -0
  115. data/test/range/test.expected.82 +397 -0
  116. data/test/range/test.expected.83 +397 -0
  117. data/test/range/test.expected.84 +399 -0
  118. data/test/range/test.out +399 -0
  119. data/test/range/test_mklang.sql +8 -0
  120. data/test/range/test_queries.sql +249 -0
  121. data/test/range/test_queries.sql.in +249 -0
  122. metadata +207 -0
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'rbconfig'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'globegit-postgresql-plruby'
6
+ spec.version = '0.5.4'
7
+ spec.authors = ['Akinori MUSHA', 'Guy Decoux']
8
+ spec.license = 'Ruby'
9
+ spec.email = 'akinori@musha.org'
10
+ spec.homepage = 'https://github.com/knu/postgresql-plruby'
11
+ spec.summary = 'Enable Ruby for use as a procedural language within PostgreSQL'
12
+ spec.test_files = Dir['test/test*']
13
+ spec.extensions = ['extconf.rb']
14
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') || f.include?('tmp') }
15
+
16
+ spec.rubyforge_project = 'plruby'
17
+
18
+ spec.extra_rdoc_files = [
19
+ 'README.markdown',
20
+ 'Changes'
21
+ ] + Dir['ext/*.c']
22
+
23
+ spec.description = <<-EOF
24
+ PL/Ruby is a loadable procedural language for the PostgreSQL database
25
+ system that enables the Ruby language to create functions and trigger
26
+ procedures.
27
+ EOF
28
+
29
+ plruby_bin = 'plruby.' + Config::CONFIG['DLEXT']
30
+ plruby_dir = File.join('postgresql-plruby-' + spec.version.to_s, 'src')
31
+ path_to_binary = File.join(Gem.dir, 'gems', plruby_dir, plruby_bin)
32
+
33
+ possible_paths = Gem.path.map{ |path|
34
+ File.join(path, 'gems', plruby_dir, plruby_bin)
35
+ }
36
+
37
+ spec.post_install_message = <<-EOF
38
+
39
+ Now run the following commands from within a postgresql shell in order
40
+ to create the plruby language on in database server:
41
+
42
+ create function plruby_call_handler() returns language_handler
43
+ as '#{path_to_binary}'
44
+ language 'C';
45
+
46
+ create trusted language 'plruby'
47
+ handler plruby_call_handler
48
+ lancompiler 'PL/Ruby';
49
+
50
+ NOTE: Your actual path to #{plruby_bin} may be different. Possible
51
+ paths to the plruby binary are:
52
+
53
+ #{possible_paths.join("\n ")}
54
+
55
+ EOF
56
+ end
@@ -0,0 +1,5 @@
1
+ #include "conversions/basic/conversions.h"
2
+ #include "conversions/bitstring/conversions.h"
3
+ #include "conversions/datetime/conversions.h"
4
+ #include "conversions/geometry/conversions.h"
5
+ #include "conversions/network/conversions.h"
@@ -0,0 +1,25 @@
1
+ {
2
+ extern void plruby_require(char *);
3
+
4
+ plruby_require("plruby/plruby_basic");
5
+ rb_hash_aset(plruby_classes, INT2NUM(OIDOID), rb_cFixnum);
6
+ rb_hash_aset(plruby_classes, INT2NUM(INT2OID), rb_cFixnum);
7
+ rb_hash_aset(plruby_classes, INT2NUM(INT4OID), rb_cFixnum);
8
+ rb_hash_aset(plruby_classes, INT2NUM(INT8OID), rb_cFixnum);
9
+
10
+ rb_hash_aset(plruby_classes, INT2NUM(FLOAT4OID), rb_cFloat);
11
+ rb_hash_aset(plruby_classes, INT2NUM(FLOAT8OID), rb_cFloat);
12
+ rb_hash_aset(plruby_classes, INT2NUM(CASHOID), rb_cFloat);
13
+ rb_hash_aset(plruby_classes, INT2NUM(NUMERICOID), rb_cFloat);
14
+
15
+ rb_hash_aset(plruby_classes, INT2NUM(TIMESTAMPOID), rb_cTime);
16
+ rb_hash_aset(plruby_classes, INT2NUM(TIMESTAMPTZOID), rb_cTime);
17
+ rb_hash_aset(plruby_classes, INT2NUM(ABSTIMEOID), rb_cTime);
18
+ rb_hash_aset(plruby_classes, INT2NUM(DATEOID), rb_cTime);
19
+ rb_hash_aset(plruby_classes, INT2NUM(RELTIMEOID), rb_cTime);
20
+ rb_hash_aset(plruby_classes, INT2NUM(INTERVALOID), rb_cTime);
21
+ rb_hash_aset(plruby_classes, INT2NUM(TIMETZOID), rb_cTime);
22
+ rb_hash_aset(plruby_classes, INT2NUM(TIMEOID), rb_cTime);
23
+
24
+ rb_hash_aset(plruby_classes, ULONG2NUM(BYTEAOID), rb_cString);
25
+ }
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ if CONFIG["LIBRUBYARG"] == "$(LIBRUBYARG_SHARED)" &&
4
+ !enable_config("plruby-shared")
5
+ $LIBRUBYARG = ""
6
+ end
7
+ have_library('ruby18', 'ruby_init')
8
+ create_makefile('plruby/plruby_basic')
@@ -0,0 +1,357 @@
1
+ #include "convcommon.h"
2
+
3
+ #include <utils/cash.h>
4
+ #include <utils/date.h>
5
+ #include <utils/nabstime.h>
6
+ #include <utils/pg_locale.h>
7
+ #include <utils/timestamp.h>
8
+ #include <math.h>
9
+
10
+ static double cash_divisor;
11
+ static Timestamp epoch;
12
+ static ID id_at, id_to_f, id_to_i, id_usec;
13
+
14
+ static VALUE
15
+ pl_fixnum_s_datum(VALUE obj, VALUE a)
16
+ {
17
+ Oid typoid;
18
+ Datum value;
19
+
20
+ value = plruby_datum_get(a, &typoid);
21
+ switch (typoid) {
22
+ case OIDOID:
23
+ return UINT2NUM(DatumGetObjectId(value));
24
+
25
+ case INT2OID:
26
+ return INT2NUM(DatumGetInt16(value));
27
+
28
+ case INT4OID:
29
+ return INT2NUM(DatumGetInt32(value));
30
+
31
+ case INT8OID:
32
+ return LL2NUM(DatumGetInt64(value));
33
+
34
+ default:
35
+ rb_raise(rb_eArgError, "unknown OID type %d", typoid);
36
+ }
37
+ }
38
+
39
+ static VALUE
40
+ pl_fixnum_to_datum(VALUE obj, VALUE a)
41
+ {
42
+ Datum d;
43
+
44
+ switch (plruby_datum_oid(a, NULL)) {
45
+ case OIDOID:
46
+ d = ObjectIdGetDatum(NUM2UINT(obj));
47
+ break;
48
+
49
+ case INT2OID:
50
+ d = Int16GetDatum(NUM2INT(obj));
51
+ break;
52
+
53
+ case INT4OID:
54
+ d = Int32GetDatum(NUM2INT(obj));
55
+ break;
56
+
57
+ case INT8OID:
58
+ d = Int64GetDatum(NUM2LL(obj));
59
+ break;
60
+
61
+ default:
62
+ return Qnil;
63
+ }
64
+ return plruby_datum_set(a, d);
65
+ }
66
+
67
+ static VALUE
68
+ pl_float_s_datum(VALUE obj, VALUE a)
69
+ {
70
+ Oid typoid;
71
+ Datum value;
72
+ double result;
73
+
74
+ value = plruby_datum_get(a, &typoid);
75
+ switch (typoid) {
76
+ case FLOAT4OID:
77
+ result = DatumGetFloat4(value);
78
+ break;
79
+
80
+ case FLOAT8OID:
81
+ result = DatumGetFloat8(value);
82
+ break;
83
+
84
+ case CASHOID:
85
+ result = (double) *(Cash *) DatumGetPointer(value) / cash_divisor;
86
+ break;
87
+
88
+ case NUMERICOID:
89
+ result = DatumGetFloat8(plruby_dfc1(numeric_float8, value));
90
+ break;
91
+
92
+ default:
93
+ rb_raise(rb_eArgError, "unknown OID type %d", typoid);
94
+ }
95
+ return rb_float_new(result);
96
+ }
97
+
98
+ extern double round();
99
+
100
+ static VALUE
101
+ pl_float_to_datum(VALUE obj, VALUE a)
102
+ {
103
+ double value;
104
+ Datum d;
105
+
106
+ value = RFLOAT_VALUE(obj);
107
+ switch (plruby_datum_oid(a, NULL)) {
108
+ case FLOAT4OID:
109
+ d = Float4GetDatum((float4)value);
110
+ break;
111
+
112
+ case FLOAT8OID:
113
+ d = Float8GetDatum((float8)value);
114
+ break;
115
+
116
+ case CASHOID:
117
+ {
118
+ Cash *cash = (Cash *) palloc(sizeof(Cash));
119
+ *cash = (Cash) round(value * cash_divisor);
120
+ d = PointerGetDatum(cash);
121
+ break;
122
+ }
123
+
124
+ case NUMERICOID:
125
+ d = plruby_dfc1(float8_numeric, Float8GetDatum((float8)value));
126
+ break;
127
+
128
+ default:
129
+ return Qnil;
130
+ }
131
+ return plruby_datum_set(a, d);
132
+ }
133
+
134
+ static VALUE
135
+ pl_str_s_datum(VALUE klass, VALUE a)
136
+ {
137
+ bytea *data;
138
+ Oid typoid;
139
+ Datum value;
140
+
141
+ value = plruby_datum_get(a, &typoid);
142
+ if (typoid != BYTEAOID) {
143
+ return Qnil;
144
+ }
145
+ data = DatumGetByteaP(value);
146
+ return rb_str_new(VARDATA(data), VARSIZE(data) - VARHDRSZ);
147
+ }
148
+
149
+ static VALUE
150
+ pl_str_to_datum(VALUE obj, VALUE a)
151
+ {
152
+ bytea *data;
153
+ size_t len;
154
+
155
+ /* Converts BYTEA only. */
156
+ if (plruby_datum_oid(a, NULL) != BYTEAOID)
157
+ return Qnil;
158
+
159
+ len = RSTRING_LEN(obj);
160
+ data = palloc(VARHDRSZ + len);
161
+ memcpy(VARDATA(data), RSTRING_PTR(obj), len);
162
+ #ifdef SET_VARSIZE
163
+ SET_VARSIZE(data, VARHDRSZ + len);
164
+ #else
165
+ VARATT_SIZEP(data) = VARHDRSZ + len;
166
+ #endif
167
+ return plruby_datum_set(a, PointerGetDatum(data));
168
+ }
169
+
170
+ static VALUE
171
+ pl_time_s_datum(VALUE klass, VALUE a)
172
+ {
173
+ Timestamp ts;
174
+ Oid typoid;
175
+ Datum value;
176
+
177
+ /*
178
+ * INTERVAL and RELTIME are converted to Float (number of seconds).
179
+ * For INTERVALs containing nonzero month/year component, duration of one
180
+ * month is assumed to be 30*24*60*60 seconds. A special type has to be
181
+ * created for this, because of the months/year components and also because
182
+ * long or short enough numbers do not convert back right (exponential
183
+ * notation of INTERVALs is not accepted by Postgres).
184
+ *
185
+ * TIMESTAMP, TIMESTAMP WITH TIME ZONE, ABSTIME, DATE are converted to klass
186
+ * (Time), naturally.
187
+ *
188
+ * TIME and TIME WITH TIME ZONE are also converted to klass (Time), as in
189
+ * the (totally broken anyway) 0.4.3 implementation. The result is that
190
+ * specific time since Unix epoch. That makes little sense (the reverse
191
+ * conversion of the result breaks anyway), but some at least. A special
192
+ * type has to be created for this.
193
+ */
194
+ value = plruby_datum_get(a, &typoid);
195
+ switch (typoid) {
196
+ /* Time interval types. */
197
+
198
+ case RELTIMEOID:
199
+ value = plruby_dfc1(reltime_interval, value);
200
+ /* ... */
201
+ case INTERVALOID:
202
+ {
203
+ Interval *iv = DatumGetIntervalP(value);
204
+
205
+ return rb_float_new((double) iv->month * 30*24*60*60 +
206
+ iv->time
207
+ #ifdef HAVE_INT64_TIMESTAMP
208
+ / 1E6
209
+ #endif
210
+ );
211
+ }
212
+
213
+ /*
214
+ * Time of day types.
215
+ *
216
+ * No separate conversion code is written, abusing the coincidence of C
217
+ * types used for TimeADT and Timestamp (int64 or double, depending on
218
+ * HAVE_INT64_TIMESTAMP). The proper implementation would use a special
219
+ * type anyway, see above.
220
+ */
221
+
222
+ case TIMETZOID:
223
+ {
224
+ TimeTzADT *timetz = DatumGetTimeTzADTP(value);
225
+
226
+ /* Shift according to the timezone. */
227
+ ts = timetz->time + (Timestamp) timetz->zone
228
+ #ifdef HAVE_INT64_TIMESTAMP
229
+ * 1000000
230
+ #endif
231
+ ;
232
+ }
233
+ goto convert;
234
+
235
+ case TIMEOID:
236
+ ts = (Timestamp) DatumGetTimeADT(value);
237
+ goto convert;
238
+
239
+ /* The rest of types end up as a Timestamp in `value'. */
240
+
241
+ case ABSTIMEOID:
242
+ value = plruby_dfc1(abstime_timestamptz, value);
243
+ break;
244
+
245
+ case DATEOID:
246
+ value = plruby_dfc1(date_timestamptz, value);
247
+ break;
248
+
249
+ case TIMESTAMPOID:
250
+ case TIMESTAMPTZOID:
251
+ break;
252
+
253
+ default:
254
+ rb_raise(rb_eTypeError, "%s: incompatible type OID %u",
255
+ rb_class2name(klass), typoid);
256
+ }
257
+
258
+ ts = DatumGetTimestamp(value) - epoch;
259
+
260
+ convert:
261
+ return rb_funcall(klass, id_at,
262
+ #ifndef HAVE_INT64_TIMESTAMP
263
+ 1, rb_float_new(ts)
264
+ #else
265
+ 2, LONG2NUM(ts / 1000000), ULONG2NUM(ts % 1000000)
266
+ #endif
267
+ );
268
+ }
269
+
270
+ static VALUE
271
+ pl_time_to_datum(VALUE obj, VALUE a)
272
+ {
273
+ PGFunction conv;
274
+ Datum d;
275
+ int typoid;
276
+
277
+ typoid = plruby_datum_oid(a, NULL);
278
+ switch (typoid) {
279
+ case ABSTIMEOID:
280
+ case DATEOID:
281
+ case TIMEOID:
282
+ case TIMESTAMPOID:
283
+ case TIMESTAMPTZOID:
284
+ case TIMETZOID:
285
+ break;
286
+
287
+ default:
288
+ return Qnil;
289
+ }
290
+
291
+ /* Convert Time to TimestampTz first. */
292
+ #ifndef HAVE_INT64_TIMESTAMP
293
+ d = TimestampTzGetDatum(epoch + NUM2DBL(rb_funcall(obj, id_to_f, 0)));
294
+ #else
295
+ d = TimestampTzGetDatum(epoch + (TimestampTz)
296
+ NUM2LONG(rb_funcall(obj, id_to_i, 0)) * 1000000 +
297
+ NUM2ULONG(rb_funcall(obj, id_usec, 0)));
298
+ #endif
299
+
300
+ conv = NULL;
301
+ switch (typoid) {
302
+ case ABSTIMEOID:
303
+ conv = timestamptz_abstime;
304
+ break;
305
+
306
+ case DATEOID:
307
+ conv = timestamptz_date;
308
+ break;
309
+
310
+ case TIMEOID:
311
+ conv = timestamptz_time;
312
+ break;
313
+
314
+ case TIMESTAMPOID:
315
+ conv = timestamptz_timestamp;
316
+ break;
317
+
318
+ case TIMESTAMPTZOID:
319
+ break;
320
+
321
+ case TIMETZOID:
322
+ conv = timestamptz_timetz;
323
+ break;
324
+ }
325
+
326
+ if (conv == NULL) {
327
+ return Qnil;
328
+ }
329
+ d = plruby_dfc1(conv, d);
330
+ return plruby_datum_set(a, d);
331
+ }
332
+
333
+ void Init_plruby_basic()
334
+ {
335
+ int fpoint;
336
+ struct lconv *lconvert = PGLC_localeconv();
337
+
338
+ fpoint = lconvert->frac_digits;
339
+ if (fpoint < 0 || fpoint > 10) {
340
+ fpoint = 2;
341
+ }
342
+ cash_divisor = pow(10.0, fpoint);
343
+ epoch = SetEpochTimestamp();
344
+ id_at = rb_intern("at");
345
+ id_to_f = rb_intern("to_f");
346
+ id_to_i = rb_intern("to_i");
347
+ id_usec = rb_intern("usec");
348
+
349
+ rb_define_singleton_method(rb_cFixnum, "from_datum", pl_fixnum_s_datum, 1);
350
+ rb_define_method(rb_cFixnum, "to_datum", pl_fixnum_to_datum, 1);
351
+ rb_define_singleton_method(rb_cFloat, "from_datum", pl_float_s_datum, 1);
352
+ rb_define_method(rb_cFloat, "to_datum", pl_float_to_datum, 1);
353
+ rb_define_singleton_method(rb_cString, "from_datum", pl_str_s_datum, 1);
354
+ rb_define_method(rb_cString, "to_datum", pl_str_to_datum, 1);
355
+ rb_define_singleton_method(rb_cTime, "from_datum", pl_time_s_datum, 1);
356
+ rb_define_method(rb_cTime, "to_datum", pl_time_to_datum, 1);
357
+ }