do_postgres 0.9.11 → 0.9.12
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.
- data/LICENSE +1 -1
- data/Manifest.txt +15 -5
- data/Rakefile +7 -121
- data/ext/do_postgres_ext/do_postgres_ext.c +245 -108
- data/ext/do_postgres_ext/extconf.rb +3 -1
- data/lib/do_postgres.rb +5 -2
- data/lib/do_postgres/version.rb +1 -1
- data/spec/command_spec.rb +9 -0
- data/spec/connection_spec.rb +19 -0
- data/spec/encoding_spec.rb +8 -0
- data/spec/lib/rspec_immediate_feedback_formatter.rb +3 -0
- data/spec/reader_spec.rb +8 -0
- data/spec/result_spec.rb +86 -0
- data/spec/spec_helper.rb +90 -57
- data/spec/typecast/array_spec.rb +8 -0
- data/spec/typecast/bigdecimal_spec.rb +9 -0
- data/spec/typecast/boolean_spec.rb +9 -0
- data/spec/typecast/byte_array_spec.rb +8 -0
- data/spec/typecast/class_spec.rb +8 -0
- data/spec/typecast/date_spec.rb +9 -0
- data/spec/typecast/datetime_spec.rb +9 -0
- data/spec/typecast/float_spec.rb +9 -0
- data/spec/typecast/integer_spec.rb +8 -0
- data/spec/typecast/nil_spec.rb +10 -0
- data/spec/typecast/range_spec.rb +8 -0
- data/spec/typecast/string_spec.rb +8 -0
- data/spec/typecast/time_spec.rb +8 -0
- data/tasks/gem.rake +61 -0
- data/tasks/install.rake +15 -0
- data/tasks/native.rake +35 -0
- data/tasks/release.rake +75 -0
- data/tasks/retrieve.rake +79 -0
- data/tasks/spec.rake +18 -0
- metadata +72 -44
- data/.gitignore +0 -0
- data/autobuild.rb +0 -90
- data/buildfile +0 -27
- data/ext-java/src/main/java/DoPostgresExtService.java +0 -23
- data/ext-java/src/main/java/do_postgres/PostgresDriverDefinition.java +0 -12
- data/script/timezone_spec_runner.rb +0 -28
- data/script/timezones.txt +0 -562
- data/spec/integration/do_postgres_spec.rb +0 -312
- data/spec/integration/logging_spec.rb +0 -53
- data/spec/integration/quoting_spec.rb +0 -25
- data/spec/integration/timezone_spec.rb +0 -66
- data/spec/spec.opts +0 -2
- data/spec/unit/transaction_spec.rb +0 -28
data/LICENSE
CHANGED
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
CHANGED
@@ -15,10 +15,20 @@ lib/do_postgres/transaction.rb
|
|
15
15
|
lib/do_postgres/version.rb
|
16
16
|
script/timezone_spec_runner.rb
|
17
17
|
script/timezones.txt
|
18
|
-
spec/
|
19
|
-
spec/
|
20
|
-
spec/
|
21
|
-
spec/
|
18
|
+
spec/command_spec.rb
|
19
|
+
spec/connection_spec.rb
|
20
|
+
spec/reader_spec.rb
|
21
|
+
spec/result_spec.rb
|
22
22
|
spec/spec.opts
|
23
23
|
spec/spec_helper.rb
|
24
|
-
spec/
|
24
|
+
spec/typecast/bigdecimal_spec.rb
|
25
|
+
spec/typecast/boolean_spec.rb
|
26
|
+
spec/typecast/byte_array_spec.rb
|
27
|
+
spec/typecast/class_spec.rb
|
28
|
+
spec/typecast/date_spec.rb
|
29
|
+
spec/typecast/datetime_spec.rb
|
30
|
+
spec/typecast/float_spec.rb
|
31
|
+
spec/typecast/integer_spec.rb
|
32
|
+
spec/typecast/nil_spec.rb
|
33
|
+
spec/typecast/string_spec.rb
|
34
|
+
spec/typecast/time_spec.rb
|
data/Rakefile
CHANGED
@@ -1,130 +1,16 @@
|
|
1
|
-
require 'pathname'
|
2
1
|
require 'rubygems'
|
3
|
-
require '
|
4
|
-
require '
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
5
4
|
|
5
|
+
require 'pathname'
|
6
|
+
require 'lib/do_postgres/version'
|
6
7
|
|
7
8
|
ROOT = Pathname(__FILE__).dirname.expand_path
|
8
9
|
JRUBY = RUBY_PLATFORM =~ /java/
|
9
10
|
WINDOWS = Gem.win_platform?
|
10
11
|
SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
|
12
|
+
BINARY_VERSION = '5.0.77'
|
11
13
|
|
12
|
-
|
13
|
-
EMAIL = "d.bussink@gmail.com"
|
14
|
-
GEM_NAME = "do_postgres"
|
15
|
-
GEM_VERSION = DataObjects::Postgres::VERSION
|
16
|
-
GEM_DEPENDENCIES = if JRUBY
|
17
|
-
[["data_objects", GEM_VERSION], ["do_jdbc", GEM_VERSION], ["jdbc-postgres", ">=8.2"]]
|
18
|
-
else
|
19
|
-
[["data_objects", GEM_VERSION]]
|
20
|
-
end
|
21
|
-
GEM_CLEAN = ['**/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,lib,def,exp,DS_Store}',
|
22
|
-
'ext/Makefile', 'ext-java/target']
|
23
|
-
GEM_EXTRAS = if JRUBY
|
24
|
-
{
|
25
|
-
:has_rdoc => false,
|
26
|
-
:platform => 'java'
|
27
|
-
}
|
28
|
-
else
|
29
|
-
{
|
30
|
-
:has_rdoc => false,
|
31
|
-
:extensions => 'ext/do_postgres_ext/extconf.rb'
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
PROJECT_NAME = "dorb"
|
36
|
-
PROJECT_URL = "http://rubyforge.org/projects/dorb"
|
37
|
-
PROJECT_DESCRIPTION = PROJECT_SUMMARY = "A DataObject.rb driver for PostgreSQL"
|
38
|
-
|
39
|
-
|
40
|
-
# RCov is run by default, except on the JRuby platform, or if NO_RCOV env is true
|
41
|
-
RUN_RCOV = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
|
42
|
-
|
43
|
-
if (tasks_dir = ROOT.parent + 'tasks').directory?
|
44
|
-
require tasks_dir + 'hoe'
|
45
|
-
require tasks_dir + 'ext_helper_java'
|
46
|
-
|
47
|
-
setup_java_extension "#{GEM_NAME}_ext", HOE.spec
|
48
|
-
end
|
49
|
-
|
50
|
-
if JRUBY
|
51
|
-
HOE.spec.files += ['lib/do_postgres_ext.jar']
|
52
|
-
HOE.spec.require_paths = Dir['lib']
|
53
|
-
end
|
54
|
-
|
55
|
-
# compile the extension
|
56
|
-
if JRUBY
|
57
|
-
Rake::Task['compile:jruby'].invoke
|
58
|
-
else
|
59
|
-
begin
|
60
|
-
gem('rake-compiler')
|
61
|
-
require 'rake/extensiontask'
|
62
|
-
Rake::ExtensionTask.new('do_postgres_ext', HOE.spec)
|
63
|
-
rescue LoadError
|
64
|
-
warn "To cross-compile, install rake-compiler (gem install rake-compiler)"
|
65
|
-
if tasks_dir.directory?
|
66
|
-
require tasks_dir + 'ext_helper'
|
67
|
-
setup_c_extension('do_postgres_ext', HOE.spec)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
def sudo_gem(cmd)
|
74
|
-
sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
|
75
|
-
end
|
76
|
-
|
77
|
-
# Installation
|
78
|
-
|
79
|
-
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
80
|
-
task :install => [ :package ] do
|
81
|
-
sudo_gem "install pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
|
82
|
-
end
|
83
|
-
|
84
|
-
desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
|
85
|
-
task :uninstall => [ :clobber ] do
|
86
|
-
sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x"
|
87
|
-
end
|
88
|
-
|
89
|
-
desc 'Run specifications'
|
90
|
-
Spec::Rake::SpecTask.new(:spec) do |t|
|
91
|
-
t.spec_opts << '--format' << 'specdoc' << '--colour'
|
92
|
-
t.spec_opts << '--loadby' << 'random'
|
93
|
-
t.spec_files = Pathname.glob(ENV['FILES'] || 'spec/**/*_spec.rb').map { |f| f.to_s }
|
94
|
-
|
95
|
-
begin
|
96
|
-
t.rcov = RUN_RCOV
|
97
|
-
t.rcov_opts << '--exclude' << 'spec'
|
98
|
-
t.rcov_opts << '--text-summary'
|
99
|
-
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
100
|
-
rescue Exception
|
101
|
-
# rcov not installed
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
namespace :ci do
|
106
|
-
|
107
|
-
task :prepare do
|
108
|
-
rm_rf ROOT + "ci"
|
109
|
-
mkdir_p ROOT + "ci"
|
110
|
-
mkdir_p ROOT + "ci/doc"
|
111
|
-
mkdir_p ROOT + "ci/cyclomatic"
|
112
|
-
mkdir_p ROOT + "ci/token"
|
113
|
-
end
|
114
|
-
|
115
|
-
task :publish do
|
116
|
-
out = ENV['CC_BUILD_ARTIFACTS'] || "out"
|
117
|
-
mkdir_p out unless File.directory? out
|
118
|
-
|
119
|
-
mv "ci/rspec_report.html", "#{out}/rspec_report.html"
|
120
|
-
mv "ci/coverage", "#{out}/coverage"
|
121
|
-
end
|
122
|
-
|
123
|
-
task :spec => :prepare do
|
124
|
-
Rake::Task[:spec].invoke
|
125
|
-
mv ROOT + "coverage", ROOT + "ci/coverage"
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
14
|
+
Dir['tasks/*.rake'].each { |f| import f }
|
129
15
|
|
130
|
-
|
16
|
+
CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext/do_postgres_ext/Makefile ext-java/target ])
|
@@ -9,6 +9,22 @@
|
|
9
9
|
#undef PACKAGE_STRING
|
10
10
|
#undef PACKAGE_TARNAME
|
11
11
|
#undef PACKAGE_VERSION
|
12
|
+
|
13
|
+
#ifdef _WIN32
|
14
|
+
/* On Windows this stuff is also defined by Postgres, but we don't
|
15
|
+
want to use Postgres' version actually */
|
16
|
+
#undef fsync
|
17
|
+
#undef vsnprintf
|
18
|
+
#undef snprintf
|
19
|
+
#undef sprintf
|
20
|
+
#undef printf
|
21
|
+
#define cCommand_execute cCommand_execute_sync
|
22
|
+
#define do_int64 signed __int64
|
23
|
+
#else
|
24
|
+
#define cCommand_execute cCommand_execute_async
|
25
|
+
#define do_int64 signed long long int
|
26
|
+
#endif
|
27
|
+
|
12
28
|
#include <ruby.h>
|
13
29
|
#include <string.h>
|
14
30
|
#include <math.h>
|
@@ -39,11 +55,6 @@
|
|
39
55
|
#define RARRAY_LEN(a) RARRAY(a)->len
|
40
56
|
#endif
|
41
57
|
|
42
|
-
#ifdef _WIN32
|
43
|
-
#define do_int64 signed __int64
|
44
|
-
#else
|
45
|
-
#define do_int64 signed long long int
|
46
|
-
#endif
|
47
58
|
|
48
59
|
// To store rb_intern values
|
49
60
|
static ID ID_NEW_DATE;
|
@@ -53,6 +64,7 @@ static ID ID_LEVEL;
|
|
53
64
|
static ID ID_TO_S;
|
54
65
|
static ID ID_RATIONAL;
|
55
66
|
|
67
|
+
static VALUE mExtlib;
|
56
68
|
static VALUE mDO;
|
57
69
|
static VALUE cDO_Quoting;
|
58
70
|
static VALUE cDO_Connection;
|
@@ -63,6 +75,7 @@ static VALUE cDO_Reader;
|
|
63
75
|
static VALUE rb_cDate;
|
64
76
|
static VALUE rb_cDateTime;
|
65
77
|
static VALUE rb_cBigDecimal;
|
78
|
+
static VALUE rb_cByteArray;
|
66
79
|
|
67
80
|
static VALUE mPostgres;
|
68
81
|
static VALUE cConnection;
|
@@ -70,6 +83,7 @@ static VALUE cCommand;
|
|
70
83
|
static VALUE cResult;
|
71
84
|
static VALUE cReader;
|
72
85
|
|
86
|
+
static VALUE eArgumentError;
|
73
87
|
static VALUE ePostgresError;
|
74
88
|
|
75
89
|
static void data_objects_debug(VALUE string, struct timeval* start) {
|
@@ -88,9 +102,6 @@ static void data_objects_debug(VALUE string, struct timeval* start) {
|
|
88
102
|
gettimeofday(&stop, NULL);
|
89
103
|
|
90
104
|
duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
|
91
|
-
if(stop.tv_usec < start->tv_usec) {
|
92
|
-
duration += 1000000;
|
93
|
-
}
|
94
105
|
|
95
106
|
snprintf(total_time, 32, "%.6f", duration / 1000000.0);
|
96
107
|
message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
|
@@ -201,9 +212,14 @@ static VALUE parse_date_time(const char *date) {
|
|
201
212
|
} else if ((max_tokens - 1) == tokens_read) {
|
202
213
|
// We read the Date and Time, but no Minute Offset
|
203
214
|
minute_offset = 0;
|
204
|
-
} else if (tokens_read == 3) {
|
205
|
-
|
206
|
-
|
215
|
+
} else if (tokens_read == 3 || tokens_read >= (max_tokens - 3)) {
|
216
|
+
if (tokens_read == 3) {
|
217
|
+
hour = 0;
|
218
|
+
min = 0;
|
219
|
+
hour_offset = 0;
|
220
|
+
minute_offset = 0;
|
221
|
+
sec = 0;
|
222
|
+
}
|
207
223
|
// We read the Date and Time, default to the current locale's offset
|
208
224
|
|
209
225
|
// Get localtime
|
@@ -258,18 +274,23 @@ static VALUE parse_date_time(const char *date) {
|
|
258
274
|
return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
|
259
275
|
}
|
260
276
|
|
261
|
-
static VALUE parse_time(char *date) {
|
277
|
+
static VALUE parse_time(const char *date) {
|
262
278
|
|
263
|
-
int year, month, day, hour, min, sec, usec;
|
279
|
+
int year, month, day, hour, min, sec, usec, tokens;
|
264
280
|
char subsec[7];
|
265
281
|
|
266
282
|
if (0 != strchr(date, '.')) {
|
267
283
|
// right padding usec with 0. e.g. '012' will become 12000 microsecond, since Time#local use microsecond
|
268
284
|
sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d.%s", &year, &month, &day, &hour, &min, &sec, subsec);
|
269
|
-
|
270
|
-
|
285
|
+
usec = atoi(subsec);
|
286
|
+
usec *= pow(10, (6 - strlen(subsec)));
|
271
287
|
} else {
|
272
|
-
sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
|
288
|
+
tokens = sscanf(date, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
|
289
|
+
if (tokens == 3) {
|
290
|
+
hour = 0;
|
291
|
+
min = 0;
|
292
|
+
sec = 0;
|
293
|
+
}
|
273
294
|
usec = 0;
|
274
295
|
}
|
275
296
|
|
@@ -279,61 +300,63 @@ static VALUE parse_time(char *date) {
|
|
279
300
|
/* ===== Typecasting Functions ===== */
|
280
301
|
|
281
302
|
static VALUE infer_ruby_type(Oid type) {
|
282
|
-
char *ruby_type = "String";
|
283
303
|
switch(type) {
|
284
304
|
case BITOID:
|
285
305
|
case VARBITOID:
|
286
306
|
case INT2OID:
|
287
307
|
case INT4OID:
|
288
|
-
case INT8OID:
|
289
|
-
|
290
|
-
break;
|
291
|
-
}
|
308
|
+
case INT8OID:
|
309
|
+
return rb_cInteger;
|
292
310
|
case FLOAT4OID:
|
293
|
-
case FLOAT8OID:
|
294
|
-
|
295
|
-
break;
|
296
|
-
}
|
311
|
+
case FLOAT8OID:
|
312
|
+
return rb_cFloat;
|
297
313
|
case NUMERICOID:
|
298
|
-
case CASHOID:
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
case BOOLOID: {
|
303
|
-
ruby_type = "TrueClass";
|
304
|
-
break;
|
305
|
-
}
|
314
|
+
case CASHOID:
|
315
|
+
return rb_cBigDecimal;
|
316
|
+
case BOOLOID:
|
317
|
+
return rb_cTrueClass;
|
306
318
|
case TIMESTAMPTZOID:
|
307
|
-
case TIMESTAMPOID:
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
case
|
312
|
-
|
313
|
-
|
314
|
-
|
319
|
+
case TIMESTAMPOID:
|
320
|
+
return rb_cDateTime;
|
321
|
+
case DATEOID:
|
322
|
+
return rb_cDate;
|
323
|
+
case BYTEAOID:
|
324
|
+
return rb_cByteArray;
|
325
|
+
default:
|
326
|
+
return rb_cString;
|
315
327
|
}
|
316
|
-
return rb_str_new2(ruby_type);
|
317
328
|
}
|
318
329
|
|
319
|
-
static VALUE typecast(char *value, long length, const
|
330
|
+
static VALUE typecast(const char *value, long length, const VALUE type) {
|
320
331
|
|
321
|
-
if (
|
322
|
-
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING(value, length));
|
323
|
-
} else if ( strcmp(type, "Integer") == 0 || strcmp(type, "Fixnum") == 0 || strcmp(type, "Bignum") == 0 ) {
|
332
|
+
if (type == rb_cInteger) {
|
324
333
|
return rb_cstr2inum(value, 10);
|
325
|
-
} else if (
|
334
|
+
} else if (type == rb_cString) {
|
335
|
+
return TAINTED_STRING(value, length);
|
336
|
+
} else if (type == rb_cFloat) {
|
326
337
|
return rb_float_new(rb_cstr_to_dbl(value, Qfalse));
|
327
|
-
} else if (
|
338
|
+
} else if (type == rb_cBigDecimal) {
|
328
339
|
return rb_funcall(rb_cBigDecimal, ID_NEW, 1, TAINTED_STRING(value, length));
|
329
|
-
} else if (
|
330
|
-
return *value == 't' ? Qtrue : Qfalse;
|
331
|
-
} else if ( strcmp(type, "Date") == 0 ) {
|
340
|
+
} else if (type == rb_cDate) {
|
332
341
|
return parse_date(value);
|
333
|
-
} else if (
|
342
|
+
} else if (type == rb_cDateTime) {
|
334
343
|
return parse_date_time(value);
|
335
|
-
} else if (
|
344
|
+
} else if (type == rb_cTime) {
|
336
345
|
return parse_time(value);
|
346
|
+
} else if (type == rb_cTrueClass) {
|
347
|
+
return *value == 't' ? Qtrue : Qfalse;
|
348
|
+
} else if (type == rb_cByteArray) {
|
349
|
+
size_t new_length = 0;
|
350
|
+
char* unescaped = (char *)PQunescapeBytea((unsigned char*)value, &new_length);
|
351
|
+
VALUE byte_array = rb_funcall(rb_cByteArray, ID_NEW, 1, TAINTED_STRING(unescaped, new_length));
|
352
|
+
PQfreemem(unescaped);
|
353
|
+
return byte_array;
|
354
|
+
} else if (type == rb_cClass) {
|
355
|
+
return rb_funcall(rb_cObject, rb_intern("full_const_get"), 1, TAINTED_STRING(value, length));
|
356
|
+
} else if (type == rb_cObject) {
|
357
|
+
return rb_marshal_load(rb_str_new2(value));
|
358
|
+
} else if (type == rb_cNilClass) {
|
359
|
+
return Qnil;
|
337
360
|
} else {
|
338
361
|
return TAINTED_STRING(value, length);
|
339
362
|
}
|
@@ -342,31 +365,72 @@ static VALUE typecast(char *value, long length, const char *type) {
|
|
342
365
|
|
343
366
|
/* ====== Public API ======= */
|
344
367
|
static VALUE cConnection_dispose(VALUE self) {
|
345
|
-
|
368
|
+
VALUE connection_container = rb_iv_get(self, "@connection");
|
369
|
+
|
370
|
+
PGconn *db;
|
371
|
+
|
372
|
+
if (Qnil == connection_container)
|
373
|
+
return Qfalse;
|
374
|
+
|
375
|
+
db = DATA_PTR(connection_container);
|
376
|
+
|
377
|
+
if (NULL == db)
|
378
|
+
return Qfalse;
|
379
|
+
|
346
380
|
PQfinish(db);
|
381
|
+
rb_iv_set(self, "@connection", Qnil);
|
382
|
+
|
347
383
|
return Qtrue;
|
348
384
|
}
|
349
385
|
|
350
|
-
static VALUE cCommand_set_types(VALUE
|
351
|
-
|
386
|
+
static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
|
387
|
+
VALUE type_strings = rb_ary_new();
|
388
|
+
VALUE array = rb_ary_new();
|
389
|
+
|
390
|
+
int i, j;
|
391
|
+
|
392
|
+
for ( i = 0; i < argc; i++) {
|
393
|
+
rb_ary_push(array, argv[i]);
|
394
|
+
}
|
395
|
+
|
396
|
+
for (i = 0; i < RARRAY_LEN(array); i++) {
|
397
|
+
VALUE entry = rb_ary_entry(array, i);
|
398
|
+
if(TYPE(entry) == T_CLASS) {
|
399
|
+
rb_ary_push(type_strings, entry);
|
400
|
+
} else if (TYPE(entry) == T_ARRAY) {
|
401
|
+
for (j = 0; j < RARRAY_LEN(entry); j++) {
|
402
|
+
VALUE sub_entry = rb_ary_entry(entry, j);
|
403
|
+
if(TYPE(sub_entry) == T_CLASS) {
|
404
|
+
rb_ary_push(type_strings, sub_entry);
|
405
|
+
} else {
|
406
|
+
rb_raise(eArgumentError, "Invalid type given");
|
407
|
+
}
|
408
|
+
}
|
409
|
+
} else {
|
410
|
+
rb_raise(eArgumentError, "Invalid type given");
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
rb_iv_set(self, "@field_types", type_strings);
|
415
|
+
|
352
416
|
return array;
|
353
417
|
}
|
354
418
|
|
355
419
|
static VALUE build_query_from_args(VALUE klass, int count, VALUE *args[]) {
|
356
420
|
VALUE query = rb_iv_get(klass, "@text");
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
}
|
363
|
-
query = rb_funcall(klass, ID_ESCAPE, 1, array);
|
421
|
+
|
422
|
+
int i;
|
423
|
+
VALUE array = rb_ary_new();
|
424
|
+
for ( i = 0; i < count; i++) {
|
425
|
+
rb_ary_push(array, (VALUE)args[i]);
|
364
426
|
}
|
427
|
+
query = rb_funcall(klass, ID_ESCAPE, 1, array);
|
428
|
+
|
365
429
|
return query;
|
366
430
|
}
|
367
431
|
|
368
|
-
static VALUE
|
369
|
-
PGconn *db = DATA_PTR(rb_iv_get(
|
432
|
+
static VALUE cConnection_quote_string(VALUE self, VALUE string) {
|
433
|
+
PGconn *db = DATA_PTR(rb_iv_get(self, "@connection"));
|
370
434
|
|
371
435
|
const char *source = RSTRING_PTR(string);
|
372
436
|
int source_len = RSTRING_LEN(string);
|
@@ -390,6 +454,63 @@ static VALUE cCommand_quote_string(VALUE self, VALUE string) {
|
|
390
454
|
return result;
|
391
455
|
}
|
392
456
|
|
457
|
+
static VALUE cConnection_quote_byte_array(VALUE self, VALUE string) {
|
458
|
+
PGconn *db = DATA_PTR(rb_iv_get(self, "@connection"));
|
459
|
+
|
460
|
+
const unsigned char *source = (unsigned char*) RSTRING_PTR(string);
|
461
|
+
size_t source_len = RSTRING_LEN(string);
|
462
|
+
|
463
|
+
unsigned char *escaped;
|
464
|
+
unsigned char *escaped_quotes;
|
465
|
+
size_t quoted_length = 0;
|
466
|
+
VALUE result;
|
467
|
+
|
468
|
+
// Allocate space for the escaped version of 'string'
|
469
|
+
// http://www.postgresql.org/docs/8.3/static/libpq-exec.html#LIBPQ-EXEC-ESCAPE-STRING
|
470
|
+
escaped = PQescapeByteaConn(db, source, source_len, "ed_length);
|
471
|
+
escaped_quotes = (unsigned char *)calloc(quoted_length + 1, sizeof(unsigned char));
|
472
|
+
memcpy(escaped_quotes + 1, escaped, quoted_length);
|
473
|
+
|
474
|
+
// Wrap the escaped string in single-quotes, this is DO's convention (replace trailing \0)
|
475
|
+
escaped_quotes[quoted_length] = escaped_quotes[0] = '\'';
|
476
|
+
|
477
|
+
result = TAINTED_STRING((char*)escaped_quotes, quoted_length + 1);
|
478
|
+
PQfreemem(escaped);
|
479
|
+
free(escaped_quotes);
|
480
|
+
return result;
|
481
|
+
}
|
482
|
+
|
483
|
+
#ifdef _WIN32
|
484
|
+
static PGresult* cCommand_execute_sync(PGconn *db, VALUE query) {
|
485
|
+
PGresult *response;
|
486
|
+
struct timeval start;
|
487
|
+
char* str = StringValuePtr(query);
|
488
|
+
|
489
|
+
while ((response = PQgetResult(db)) != NULL) {
|
490
|
+
PQclear(response);
|
491
|
+
}
|
492
|
+
|
493
|
+
gettimeofday(&start, NULL);
|
494
|
+
|
495
|
+
response = PQexec(db, str);
|
496
|
+
|
497
|
+
if (response == NULL) {
|
498
|
+
if(PQstatus(db) != CONNECTION_OK) {
|
499
|
+
PQreset(db);
|
500
|
+
if (PQstatus(db) == CONNECTION_OK) {
|
501
|
+
response = PQexec(db, str);
|
502
|
+
}
|
503
|
+
}
|
504
|
+
|
505
|
+
if(response == NULL) {
|
506
|
+
rb_raise(ePostgresError, PQerrorMessage(db));
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
data_objects_debug(query, &start);
|
511
|
+
return response;
|
512
|
+
}
|
513
|
+
#else
|
393
514
|
static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
394
515
|
int socket_fd;
|
395
516
|
int retval;
|
@@ -444,6 +565,7 @@ static PGresult* cCommand_execute_async(PGconn *db, VALUE query) {
|
|
444
565
|
data_objects_debug(query, &start);
|
445
566
|
return PQgetResult(db);
|
446
567
|
}
|
568
|
+
#endif
|
447
569
|
|
448
570
|
static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
449
571
|
PGresult *result = NULL;
|
@@ -455,6 +577,7 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
455
577
|
char *search_path_query = NULL;
|
456
578
|
char *backslash_off = "SET backslash_quote = off";
|
457
579
|
char *standard_strings_on = "SET standard_conforming_strings = on";
|
580
|
+
char *warning_messages = "SET client_min_messages = warning";
|
458
581
|
|
459
582
|
PGconn *db;
|
460
583
|
|
@@ -512,7 +635,7 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
512
635
|
search_path_query = (char *)calloc(256, sizeof(char));
|
513
636
|
snprintf(search_path_query, 256, "set search_path to %s;", search_path);
|
514
637
|
r_query = rb_str_new2(search_path_query);
|
515
|
-
result =
|
638
|
+
result = cCommand_execute(db, r_query);
|
516
639
|
|
517
640
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
518
641
|
free(search_path_query);
|
@@ -523,14 +646,21 @@ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
|
|
523
646
|
}
|
524
647
|
|
525
648
|
r_options = rb_str_new2(backslash_off);
|
526
|
-
result =
|
649
|
+
result = cCommand_execute(db, r_options);
|
527
650
|
|
528
651
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
529
652
|
rb_warn(PQresultErrorMessage(result));
|
530
653
|
}
|
531
654
|
|
532
655
|
r_options = rb_str_new2(standard_strings_on);
|
533
|
-
result =
|
656
|
+
result = cCommand_execute(db, r_options);
|
657
|
+
|
658
|
+
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
659
|
+
rb_warn(PQresultErrorMessage(result));
|
660
|
+
}
|
661
|
+
|
662
|
+
r_options = rb_str_new2(warning_messages);
|
663
|
+
result = cCommand_execute(db, r_options);
|
534
664
|
|
535
665
|
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
|
536
666
|
rb_warn(PQresultErrorMessage(result));
|
@@ -569,26 +699,32 @@ static VALUE cConnection_character_set(VALUE self) {
|
|
569
699
|
}
|
570
700
|
|
571
701
|
static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
|
572
|
-
|
702
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
703
|
+
VALUE postgres_connection = rb_iv_get(connection, "@connection");
|
704
|
+
if (Qnil == postgres_connection) {
|
705
|
+
rb_raise(ePostgresError, "This connection has already been closed.");
|
706
|
+
}
|
707
|
+
|
708
|
+
PGconn *db = DATA_PTR(postgres_connection);
|
573
709
|
PGresult *response;
|
574
710
|
int status;
|
575
711
|
|
576
|
-
|
577
|
-
|
712
|
+
VALUE affected_rows = Qnil;
|
713
|
+
VALUE insert_id = Qnil;
|
578
714
|
|
579
715
|
VALUE query = build_query_from_args(self, argc, argv);
|
580
716
|
|
581
|
-
response =
|
717
|
+
response = cCommand_execute(db, query);
|
582
718
|
|
583
719
|
status = PQresultStatus(response);
|
584
720
|
|
585
721
|
if ( status == PGRES_TUPLES_OK ) {
|
586
|
-
insert_id = atoi(PQgetvalue(response, 0, 0));
|
587
|
-
affected_rows =
|
722
|
+
insert_id = INT2NUM(atoi(PQgetvalue(response, 0, 0)));
|
723
|
+
affected_rows = INT2NUM(atoi(PQcmdTuples(response)));
|
588
724
|
}
|
589
725
|
else if ( status == PGRES_COMMAND_OK ) {
|
590
|
-
insert_id =
|
591
|
-
affected_rows = atoi(PQcmdTuples(response));
|
726
|
+
insert_id = Qnil;
|
727
|
+
affected_rows = INT2NUM(atoi(PQcmdTuples(response)));
|
592
728
|
}
|
593
729
|
else {
|
594
730
|
char *message = PQresultErrorMessage(response);
|
@@ -599,7 +735,7 @@ static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
|
|
599
735
|
|
600
736
|
PQclear(response);
|
601
737
|
|
602
|
-
return rb_funcall(cResult, ID_NEW, 3, self,
|
738
|
+
return rb_funcall(cResult, ID_NEW, 3, self, affected_rows, insert_id);
|
603
739
|
}
|
604
740
|
|
605
741
|
static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
|
@@ -610,12 +746,18 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
|
|
610
746
|
int field_count;
|
611
747
|
int infer_types = 0;
|
612
748
|
|
613
|
-
|
749
|
+
VALUE connection = rb_iv_get(self, "@connection");
|
750
|
+
VALUE postgres_connection = rb_iv_get(connection, "@connection");
|
751
|
+
if (Qnil == postgres_connection) {
|
752
|
+
rb_raise(ePostgresError, "This connection has already been closed.");
|
753
|
+
}
|
754
|
+
|
755
|
+
PGconn *db = DATA_PTR(postgres_connection);
|
614
756
|
PGresult *response;
|
615
757
|
|
616
758
|
query = build_query_from_args(self, argc, argv);
|
617
759
|
|
618
|
-
response =
|
760
|
+
response = cCommand_execute(db, query);
|
619
761
|
|
620
762
|
if ( PQresultStatus(response) != PGRES_TUPLES_OK ) {
|
621
763
|
char *message = PQresultErrorMessage(response);
|
@@ -633,9 +775,14 @@ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
|
|
633
775
|
field_names = rb_ary_new();
|
634
776
|
field_types = rb_iv_get(self, "@field_types");
|
635
777
|
|
636
|
-
if ( field_types == Qnil || RARRAY_LEN(field_types)
|
778
|
+
if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
|
637
779
|
field_types = rb_ary_new();
|
638
780
|
infer_types = 1;
|
781
|
+
} else if (RARRAY_LEN(field_types) != field_count) {
|
782
|
+
// Whoops... wrong number of types passed to set_types. Close the reader and raise
|
783
|
+
// and error
|
784
|
+
rb_funcall(reader, rb_intern("close"), 0);
|
785
|
+
rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
|
639
786
|
}
|
640
787
|
|
641
788
|
for ( i = 0; i < field_count; i++ ) {
|
@@ -678,10 +825,8 @@ static VALUE cReader_next(VALUE self) {
|
|
678
825
|
int i;
|
679
826
|
int position;
|
680
827
|
|
681
|
-
const char *type;
|
682
|
-
|
683
828
|
VALUE array = rb_ary_new();
|
684
|
-
VALUE field_types,
|
829
|
+
VALUE field_types, field_type;
|
685
830
|
VALUE value;
|
686
831
|
|
687
832
|
row_count = NUM2INT(rb_iv_get(self, "@row_count"));
|
@@ -690,22 +835,16 @@ static VALUE cReader_next(VALUE self) {
|
|
690
835
|
position = NUM2INT(rb_iv_get(self, "@position"));
|
691
836
|
|
692
837
|
if ( position > (row_count-1) ) {
|
693
|
-
|
838
|
+
rb_iv_set(self, "@values", Qnil);
|
839
|
+
return Qfalse;
|
694
840
|
}
|
695
841
|
|
696
842
|
for ( i = 0; i < field_count; i++ ) {
|
697
|
-
|
698
|
-
|
699
|
-
if ( TYPE(ruby_type) == T_STRING ) {
|
700
|
-
type = StringValuePtr(ruby_type);
|
701
|
-
}
|
702
|
-
else {
|
703
|
-
type = rb_class2name(ruby_type);
|
704
|
-
}
|
843
|
+
field_type = rb_ary_entry(field_types, i);
|
705
844
|
|
706
845
|
// Always return nil if the value returned from Postgres is null
|
707
846
|
if (!PQgetisnull(reader, position, i)) {
|
708
|
-
value = typecast(PQgetvalue(reader, position, i), PQgetlength(reader, position, i),
|
847
|
+
value = typecast(PQgetvalue(reader, position, i), PQgetlength(reader, position, i), field_type);
|
709
848
|
} else {
|
710
849
|
value = Qnil;
|
711
850
|
}
|
@@ -721,14 +860,12 @@ static VALUE cReader_next(VALUE self) {
|
|
721
860
|
|
722
861
|
static VALUE cReader_values(VALUE self) {
|
723
862
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
if ( position == Qnil || NUM2INT(position) > row_count ) {
|
863
|
+
VALUE values = rb_iv_get(self, "@values");
|
864
|
+
if(values == Qnil) {
|
728
865
|
rb_raise(ePostgresError, "Reader not initialized");
|
729
|
-
|
730
|
-
else {
|
731
|
-
return
|
866
|
+
return Qnil;
|
867
|
+
} else {
|
868
|
+
return values;
|
732
869
|
}
|
733
870
|
}
|
734
871
|
|
@@ -739,9 +876,6 @@ static VALUE cReader_fields(VALUE self) {
|
|
739
876
|
static VALUE cReader_field_count(VALUE self) {
|
740
877
|
return rb_iv_get(self, "@field_count");
|
741
878
|
}
|
742
|
-
static VALUE cReader_row_count(VALUE self) {
|
743
|
-
return rb_iv_get(self, "@row_count");
|
744
|
-
}
|
745
879
|
|
746
880
|
void Init_do_postgres_ext() {
|
747
881
|
rb_require("date");
|
@@ -750,7 +884,6 @@ void Init_do_postgres_ext() {
|
|
750
884
|
// Get references classes needed for Date/Time parsing
|
751
885
|
rb_cDate = CONST_GET(rb_mKernel, "Date");
|
752
886
|
rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
|
753
|
-
rb_cTime = CONST_GET(rb_mKernel, "Time");
|
754
887
|
rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
|
755
888
|
|
756
889
|
rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
|
@@ -766,6 +899,10 @@ void Init_do_postgres_ext() {
|
|
766
899
|
ID_TO_S = rb_intern("to_s");
|
767
900
|
ID_RATIONAL = rb_intern("Rational");
|
768
901
|
|
902
|
+
// Get references to the Extlib module
|
903
|
+
mExtlib = CONST_GET(rb_mKernel, "Extlib");
|
904
|
+
rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
|
905
|
+
|
769
906
|
// Get references to the DataObjects module and its classes
|
770
907
|
mDO = CONST_GET(rb_mKernel, "DataObjects");
|
771
908
|
cDO_Quoting = CONST_GET(mDO, "Quoting");
|
@@ -774,6 +911,7 @@ void Init_do_postgres_ext() {
|
|
774
911
|
cDO_Result = CONST_GET(mDO, "Result");
|
775
912
|
cDO_Reader = CONST_GET(mDO, "Reader");
|
776
913
|
|
914
|
+
eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
|
777
915
|
mPostgres = rb_define_module_under(mDO, "Postgres");
|
778
916
|
ePostgresError = rb_define_class("PostgresError", rb_eStandardError);
|
779
917
|
|
@@ -781,13 +919,13 @@ void Init_do_postgres_ext() {
|
|
781
919
|
rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
|
782
920
|
rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
|
783
921
|
rb_define_method(cConnection, "character_set", cConnection_character_set , 0);
|
922
|
+
rb_define_method(cConnection, "quote_string", cConnection_quote_string, 1);
|
923
|
+
rb_define_method(cConnection, "quote_byte_array", cConnection_quote_byte_array, 1);
|
784
924
|
|
785
925
|
cCommand = POSTGRES_CLASS("Command", cDO_Command);
|
786
|
-
|
787
|
-
rb_define_method(cCommand, "set_types", cCommand_set_types, 1);
|
926
|
+
rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
|
788
927
|
rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
|
789
928
|
rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
|
790
|
-
rb_define_method(cCommand, "quote_string", cCommand_quote_string, 1);
|
791
929
|
|
792
930
|
cResult = POSTGRES_CLASS("Result", cDO_Result);
|
793
931
|
|
@@ -796,7 +934,6 @@ void Init_do_postgres_ext() {
|
|
796
934
|
rb_define_method(cReader, "next!", cReader_next, 0);
|
797
935
|
rb_define_method(cReader, "values", cReader_values, 0);
|
798
936
|
rb_define_method(cReader, "fields", cReader_fields, 0);
|
799
|
-
rb_define_method(cReader, "row_count", cReader_row_count, 0);
|
800
937
|
rb_define_method(cReader, "field_count", cReader_field_count, 0);
|
801
938
|
|
802
939
|
}
|