pg 0.15.1-x64-mingw32 → 0.16.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +395 -1
- data/History.rdoc +30 -3
- data/Manifest.txt +4 -0
- data/Rakefile +30 -10
- data/ext/errorcodes.def +931 -0
- data/ext/errorcodes.rb +45 -0
- data/ext/errorcodes.txt +463 -0
- data/ext/extconf.rb +11 -5
- data/ext/gvl_wrappers.c +6 -0
- data/ext/gvl_wrappers.h +47 -21
- data/ext/pg.c +29 -9
- data/ext/pg.h +30 -0
- data/ext/pg_connection.c +104 -44
- data/ext/pg_errors.c +89 -0
- data/ext/pg_result.c +48 -67
- data/lib/2.0/pg_ext.so +0 -0
- data/lib/pg.rb +1 -1
- data/spec/lib/helpers.rb +11 -2
- data/spec/pg/connection_spec.rb +113 -8
- data/spec/pg/result_spec.rb +69 -2
- data/spec/pg_spec.rb +13 -0
- metadata +16 -56
- metadata.gz.sig +0 -0
data/ext/extconf.rb
CHANGED
@@ -27,17 +27,22 @@ if ENV['CROSS_COMPILING']
|
|
27
27
|
have_library( 'ssl', 'SSL_new' ) && append_library( $libs, 'ssl' )
|
28
28
|
end
|
29
29
|
|
30
|
-
dir_config 'pg'
|
31
|
-
|
32
30
|
if pgconfig = ( with_config('pg-config') || with_config('pg_config') || find_executable('pg_config') )
|
33
31
|
$stderr.puts "Using config values from %s" % [ pgconfig ]
|
34
|
-
|
35
|
-
|
32
|
+
incdir = `"#{pgconfig}" --includedir`.chomp
|
36
33
|
libdir = `"#{pgconfig}" --libdir`.chomp
|
37
|
-
|
34
|
+
dir_config 'pg', incdir, libdir
|
35
|
+
|
36
|
+
# Try to use runtime path linker option, even if RbConfig doesn't know about it.
|
37
|
+
# The rpath option is usually set implicit by dir_config(), but so far not
|
38
|
+
# on MacOS-X.
|
39
|
+
if RbConfig::CONFIG["RPATHFLAG"].to_s.empty? && try_link('int main() {return 0;}', " -Wl,-rpath,#{libdir}")
|
40
|
+
$LDFLAGS << " -Wl,-rpath,#{libdir}"
|
41
|
+
end
|
38
42
|
else
|
39
43
|
$stderr.puts "No pg_config... trying anyway. If building fails, please try again with",
|
40
44
|
" --with-pg-config=/path/to/pg_config"
|
45
|
+
dir_config 'pg'
|
41
46
|
end
|
42
47
|
|
43
48
|
find_header( 'libpq-fe.h' ) or abort "Can't find the 'libpq-fe.h header"
|
@@ -78,6 +83,7 @@ have_func 'rb_w32_wrap_io_handle'
|
|
78
83
|
|
79
84
|
have_const 'PGRES_COPY_BOTH', 'libpq-fe.h'
|
80
85
|
have_const 'PGRES_SINGLE_TUPLE', 'libpq-fe.h'
|
86
|
+
have_const 'PG_DIAG_TABLE_NAME', 'libpq-fe.h'
|
81
87
|
|
82
88
|
$defs.push( "-DHAVE_ST_NOTIFY_EXTRA" ) if
|
83
89
|
have_struct_member 'struct pgNotify', 'extra', 'libpq-fe.h'
|
data/ext/gvl_wrappers.c
CHANGED
@@ -11,3 +11,9 @@ FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB );
|
|
11
11
|
FOR_EACH_CALLBACK_FUNCTION( DEFINE_GVL_WRAPPER_STRUCT );
|
12
12
|
FOR_EACH_CALLBACK_FUNCTION( DEFINE_GVLCB_SKELETON );
|
13
13
|
FOR_EACH_CALLBACK_FUNCTION( DEFINE_GVLCB_STUB );
|
14
|
+
|
15
|
+
void ubf_cancel_running_command(void *c)
|
16
|
+
{
|
17
|
+
PGconn *conn = (PGconn*) c;
|
18
|
+
PQrequestCancel(conn);
|
19
|
+
}
|
data/ext/gvl_wrappers.h
CHANGED
@@ -24,6 +24,8 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
24
24
|
rb_unblock_function_t *ubf, void *data2);
|
25
25
|
#endif
|
26
26
|
|
27
|
+
void ubf_cancel_running_command(void *c);
|
28
|
+
|
27
29
|
#define DEFINE_PARAM_LIST1(type, name) \
|
28
30
|
name,
|
29
31
|
|
@@ -36,7 +38,7 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
36
38
|
#define DEFINE_PARAM_DECL(type, name) \
|
37
39
|
type name;
|
38
40
|
|
39
|
-
#define DEFINE_GVL_WRAPPER_STRUCT(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
41
|
+
#define DEFINE_GVL_WRAPPER_STRUCT(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
40
42
|
struct gvl_wrapper_##name##_params { \
|
41
43
|
struct { \
|
42
44
|
FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_DECL) \
|
@@ -45,7 +47,7 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
45
47
|
when_non_void( rettype retval; ) \
|
46
48
|
};
|
47
49
|
|
48
|
-
#define DEFINE_GVL_SKELETON(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
50
|
+
#define DEFINE_GVL_SKELETON(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
49
51
|
static void * gvl_##name##_skeleton( void *data ){ \
|
50
52
|
struct gvl_wrapper_##name##_params *p = (struct gvl_wrapper_##name##_params*)data; \
|
51
53
|
when_non_void( p->retval = ) \
|
@@ -54,25 +56,25 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
54
56
|
}
|
55
57
|
|
56
58
|
#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
|
57
|
-
#define DEFINE_GVL_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
59
|
+
#define DEFINE_GVL_STUB(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
58
60
|
rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
|
59
61
|
struct gvl_wrapper_##name##_params params = { \
|
60
62
|
{FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
|
61
63
|
}; \
|
62
|
-
rb_thread_call_without_gvl(gvl_##name##_skeleton, ¶ms,
|
64
|
+
rb_thread_call_without_gvl(gvl_##name##_skeleton, ¶ms, cancel_params); \
|
63
65
|
when_non_void( return params.retval; ) \
|
64
66
|
}
|
65
67
|
#else
|
66
|
-
#define DEFINE_GVL_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
68
|
+
#define DEFINE_GVL_STUB(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
67
69
|
rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
|
68
70
|
return name( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
|
69
71
|
}
|
70
72
|
#endif
|
71
73
|
|
72
|
-
#define DEFINE_GVL_STUB_DECL(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
74
|
+
#define DEFINE_GVL_STUB_DECL(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
73
75
|
rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname);
|
74
76
|
|
75
|
-
#define DEFINE_GVLCB_SKELETON(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
77
|
+
#define DEFINE_GVLCB_SKELETON(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
76
78
|
static void * gvl_##name##_skeleton( void *data ){ \
|
77
79
|
struct gvl_wrapper_##name##_params *p = (struct gvl_wrapper_##name##_params*)data; \
|
78
80
|
when_non_void( p->retval = ) \
|
@@ -81,7 +83,7 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
81
83
|
}
|
82
84
|
|
83
85
|
#if defined(HAVE_RB_THREAD_CALL_WITH_GVL)
|
84
|
-
#define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
86
|
+
#define DEFINE_GVLCB_STUB(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
85
87
|
rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
|
86
88
|
struct gvl_wrapper_##name##_params params = { \
|
87
89
|
{FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname}, when_non_void((rettype)0) \
|
@@ -90,7 +92,7 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
90
92
|
when_non_void( return params.retval; ) \
|
91
93
|
}
|
92
94
|
#else
|
93
|
-
#define DEFINE_GVLCB_STUB(name, when_non_void, rettype, lastparamtype, lastparamname) \
|
95
|
+
#define DEFINE_GVLCB_STUB(name, cancel_params, when_non_void, rettype, lastparamtype, lastparamname) \
|
94
96
|
rettype gvl_##name(FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST3) lastparamtype lastparamname){ \
|
95
97
|
return name( FOR_EACH_PARAM_OF_##name(DEFINE_PARAM_LIST1) lastparamname ); \
|
96
98
|
}
|
@@ -99,11 +101,26 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
99
101
|
#define GVL_TYPE_VOID(string)
|
100
102
|
#define GVL_TYPE_NONVOID(string) string
|
101
103
|
|
104
|
+
#define GVL_CANCELABLE ubf_cancel_running_command, conn
|
105
|
+
#define GVL_NONCANCELABLE RUBY_UBF_IO, 0
|
106
|
+
|
102
107
|
|
103
108
|
/*
|
104
109
|
* Definitions of blocking functions and their parameters
|
105
110
|
*/
|
106
111
|
|
112
|
+
#define FOR_EACH_PARAM_OF_PQconnectdb(param)
|
113
|
+
|
114
|
+
#define FOR_EACH_PARAM_OF_PQconnectStart(param)
|
115
|
+
|
116
|
+
#define FOR_EACH_PARAM_OF_PQconnectPoll(param)
|
117
|
+
|
118
|
+
#define FOR_EACH_PARAM_OF_PQreset(param)
|
119
|
+
|
120
|
+
#define FOR_EACH_PARAM_OF_PQresetStart(param)
|
121
|
+
|
122
|
+
#define FOR_EACH_PARAM_OF_PQresetPoll(param)
|
123
|
+
|
107
124
|
#define FOR_EACH_PARAM_OF_PQexec(param) \
|
108
125
|
param(PGconn *, conn)
|
109
126
|
|
@@ -149,18 +166,27 @@ extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
|
|
149
166
|
param(PGconn *, conn) \
|
150
167
|
param(char **, buffer)
|
151
168
|
|
169
|
+
#define FOR_EACH_PARAM_OF_PQnotifies(param)
|
170
|
+
|
152
171
|
/* function( name, void_or_nonvoid, returntype, lastparamtype, lastparamname ) */
|
153
172
|
#define FOR_EACH_BLOCKING_FUNCTION(function) \
|
154
|
-
function(
|
155
|
-
function(
|
156
|
-
function(
|
157
|
-
function(
|
158
|
-
function(
|
159
|
-
function(
|
160
|
-
function(
|
161
|
-
function(
|
162
|
-
function(
|
163
|
-
function(
|
173
|
+
function(PQconnectdb, GVL_NONCANCELABLE, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
|
174
|
+
function(PQconnectStart, GVL_NONCANCELABLE, GVL_TYPE_NONVOID, PGconn *, const char *, conninfo) \
|
175
|
+
function(PQconnectPoll, GVL_CANCELABLE, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
|
176
|
+
function(PQreset, GVL_CANCELABLE, GVL_TYPE_VOID, void, PGconn *, conn) \
|
177
|
+
function(PQresetStart, GVL_CANCELABLE, GVL_TYPE_NONVOID, int, PGconn *, conn) \
|
178
|
+
function(PQresetPoll, GVL_CANCELABLE, GVL_TYPE_NONVOID, PostgresPollingStatusType, PGconn *, conn) \
|
179
|
+
function(PQexec, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, const char *, command) \
|
180
|
+
function(PQexecParams, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
|
181
|
+
function(PQexecPrepared, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, int, resultFormat) \
|
182
|
+
function(PQprepare, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, const Oid *, paramTypes) \
|
183
|
+
function(PQdescribePrepared, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, const char *, stmtName) \
|
184
|
+
function(PQdescribePortal, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, const char *, portalName) \
|
185
|
+
function(PQgetResult, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGresult *, PGconn *, conn) \
|
186
|
+
function(PQputCopyData, GVL_CANCELABLE, GVL_TYPE_NONVOID, int, int, nbytes) \
|
187
|
+
function(PQputCopyEnd, GVL_CANCELABLE, GVL_TYPE_NONVOID, int, const char *, errormsg) \
|
188
|
+
function(PQgetCopyData, GVL_CANCELABLE, GVL_TYPE_NONVOID, int, int, async) \
|
189
|
+
function(PQnotifies, GVL_CANCELABLE, GVL_TYPE_NONVOID, PGnotify *, PGconn *, conn);
|
164
190
|
|
165
191
|
FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB_DECL );
|
166
192
|
|
@@ -177,8 +203,8 @@ FOR_EACH_BLOCKING_FUNCTION( DEFINE_GVL_STUB_DECL );
|
|
177
203
|
|
178
204
|
/* function( name, void_or_nonvoid, returntype, lastparamtype, lastparamname ) */
|
179
205
|
#define FOR_EACH_CALLBACK_FUNCTION(function) \
|
180
|
-
function(notice_processor_proxy
|
181
|
-
function(notice_receiver_proxy
|
206
|
+
function(notice_processor_proxy,, GVL_TYPE_VOID, void, const char *, message) \
|
207
|
+
function(notice_receiver_proxy,, GVL_TYPE_VOID, void, const PGresult *, result) \
|
182
208
|
|
183
209
|
FOR_EACH_CALLBACK_FUNCTION( DEFINE_GVL_STUB_DECL );
|
184
210
|
|
data/ext/pg.c
CHANGED
@@ -48,7 +48,6 @@
|
|
48
48
|
#include "pg.h"
|
49
49
|
|
50
50
|
VALUE rb_mPG;
|
51
|
-
VALUE rb_ePGerror;
|
52
51
|
VALUE rb_mPGconstants;
|
53
52
|
|
54
53
|
|
@@ -300,7 +299,6 @@ void
|
|
300
299
|
Init_pg_ext()
|
301
300
|
{
|
302
301
|
rb_mPG = rb_define_module( "PG" );
|
303
|
-
rb_ePGerror = rb_define_class_under( rb_mPG, "Error", rb_eStandardError );
|
304
302
|
rb_mPGconstants = rb_define_module_under( rb_mPG, "Constants" );
|
305
303
|
|
306
304
|
/*************************
|
@@ -313,13 +311,6 @@ Init_pg_ext()
|
|
313
311
|
SINGLETON_ALIAS( rb_mPG, "is_threadsafe?", "isthreadsafe" );
|
314
312
|
SINGLETON_ALIAS( rb_mPG, "threadsafe?", "isthreadsafe" );
|
315
313
|
|
316
|
-
/*************************
|
317
|
-
* PG::Error
|
318
|
-
*************************/
|
319
|
-
rb_define_alias( rb_ePGerror, "error", "message" );
|
320
|
-
rb_define_attr( rb_ePGerror, "connection", 1, 0 );
|
321
|
-
rb_define_attr( rb_ePGerror, "result", 1, 0 );
|
322
|
-
|
323
314
|
/****** PG::Connection CLASS CONSTANTS: Connection Status ******/
|
324
315
|
|
325
316
|
/* Connection succeeded */
|
@@ -506,6 +497,34 @@ Init_pg_ext()
|
|
506
497
|
* function reporting the error. */
|
507
498
|
rb_define_const(rb_mPGconstants, "PG_DIAG_SOURCE_FUNCTION", INT2FIX(PG_DIAG_SOURCE_FUNCTION));
|
508
499
|
|
500
|
+
#ifdef HAVE_CONST_PG_DIAG_TABLE_NAME
|
501
|
+
/* #result_error_field argument constant: If the error was associated with a
|
502
|
+
* specific database object, the name of the schema containing that object, if any. */
|
503
|
+
rb_define_const(rb_mPGconstants, "PG_DIAG_SCHEMA_NAME", INT2FIX(PG_DIAG_SCHEMA_NAME));
|
504
|
+
|
505
|
+
/* #result_error_field argument constant: If the error was associated with a
|
506
|
+
*specific table, the name of the table. (When this field is present, the schema name
|
507
|
+
* field provides the name of the table's schema.) */
|
508
|
+
rb_define_const(rb_mPGconstants, "PG_DIAG_TABLE_NAME", INT2FIX(PG_DIAG_TABLE_NAME));
|
509
|
+
|
510
|
+
/* #result_error_field argument constant: If the error was associated with a
|
511
|
+
* specific table column, the name of the column. (When this field is present, the
|
512
|
+
* schema and table name fields identify the table.) */
|
513
|
+
rb_define_const(rb_mPGconstants, "PG_DIAG_COLUMN_NAME", INT2FIX(PG_DIAG_COLUMN_NAME));
|
514
|
+
|
515
|
+
/* #result_error_field argument constant: If the error was associated with a
|
516
|
+
* specific datatype, the name of the datatype. (When this field is present, the
|
517
|
+
* schema name field provides the name of the datatype's schema.) */
|
518
|
+
rb_define_const(rb_mPGconstants, "PG_DIAG_DATATYPE_NAME", INT2FIX(PG_DIAG_DATATYPE_NAME));
|
519
|
+
|
520
|
+
/* #result_error_field argument constant: If the error was associated with a
|
521
|
+
* specific constraint, the name of the constraint. The table or domain that the
|
522
|
+
* constraint belongs to is reported using the fields listed above. (For this
|
523
|
+
* purpose, indexes are treated as constraints, even if they weren't created with
|
524
|
+
* constraint syntax.) */
|
525
|
+
rb_define_const(rb_mPGconstants, "PG_DIAG_CONSTRAINT_NAME", INT2FIX(PG_DIAG_CONSTRAINT_NAME));
|
526
|
+
#endif
|
527
|
+
|
509
528
|
/* Invalid OID constant */
|
510
529
|
rb_define_const(rb_mPGconstants, "INVALID_OID", INT2FIX(InvalidOid));
|
511
530
|
rb_define_const(rb_mPGconstants, "InvalidOid", INT2FIX(InvalidOid));
|
@@ -521,5 +540,6 @@ Init_pg_ext()
|
|
521
540
|
/* Initialize the main extension classes */
|
522
541
|
init_pg_connection();
|
523
542
|
init_pg_result();
|
543
|
+
init_pg_errors();
|
524
544
|
}
|
525
545
|
|
data/ext/pg.h
CHANGED
@@ -66,6 +66,30 @@
|
|
66
66
|
# include "ruby/io.h"
|
67
67
|
#endif
|
68
68
|
|
69
|
+
#ifndef timeradd
|
70
|
+
#define timeradd(a, b, result) \
|
71
|
+
do { \
|
72
|
+
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
73
|
+
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
74
|
+
if ((result)->tv_usec >= 1000000L) { \
|
75
|
+
++(result)->tv_sec; \
|
76
|
+
(result)->tv_usec -= 1000000L; \
|
77
|
+
} \
|
78
|
+
} while (0)
|
79
|
+
#endif
|
80
|
+
|
81
|
+
#ifndef timersub
|
82
|
+
#define timersub(a, b, result) \
|
83
|
+
do { \
|
84
|
+
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
85
|
+
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
86
|
+
if ((result)->tv_usec < 0) { \
|
87
|
+
--(result)->tv_sec; \
|
88
|
+
(result)->tv_usec += 1000000L; \
|
89
|
+
} \
|
90
|
+
} while (0)
|
91
|
+
#endif
|
92
|
+
|
69
93
|
/* PostgreSQL headers */
|
70
94
|
#include "libpq-fe.h"
|
71
95
|
#include "libpq/libpq-fs.h" /* large-object interface */
|
@@ -85,9 +109,13 @@ typedef long suseconds_t;
|
|
85
109
|
|
86
110
|
extern VALUE rb_mPG;
|
87
111
|
extern VALUE rb_ePGerror;
|
112
|
+
extern VALUE rb_eServerError;
|
113
|
+
extern VALUE rb_eUnableToSend;
|
114
|
+
extern VALUE rb_eConnectionBad;
|
88
115
|
extern VALUE rb_mPGconstants;
|
89
116
|
extern VALUE rb_cPGconn;
|
90
117
|
extern VALUE rb_cPGresult;
|
118
|
+
extern VALUE rb_hErrors;
|
91
119
|
|
92
120
|
|
93
121
|
/***************************************************************************
|
@@ -105,6 +133,8 @@ void Init_pg_ext _(( void ));
|
|
105
133
|
|
106
134
|
void init_pg_connection _(( void ));
|
107
135
|
void init_pg_result _(( void ));
|
136
|
+
void init_pg_errors _(( void ));
|
137
|
+
VALUE lookup_error_class _(( const char *sqlstate ));
|
108
138
|
|
109
139
|
PGconn *pg_get_pgconn _(( VALUE ));
|
110
140
|
|
data/ext/pg_connection.c
CHANGED
@@ -40,7 +40,7 @@ pg_get_pgconn( VALUE self )
|
|
40
40
|
PGconn *conn = pgconn_check( self );
|
41
41
|
|
42
42
|
if ( !conn )
|
43
|
-
rb_raise(
|
43
|
+
rb_raise( rb_eConnectionBad, "connection is closed" );
|
44
44
|
|
45
45
|
return conn;
|
46
46
|
}
|
@@ -58,7 +58,7 @@ pgconn_close_socket_io( VALUE self )
|
|
58
58
|
#if defined(_WIN32) && defined(HAVE_RB_W32_WRAP_IO_HANDLE)
|
59
59
|
int ruby_sd = NUM2INT(rb_funcall( socket_io, rb_intern("fileno"), 0 ));
|
60
60
|
if( rb_w32_unwrap_io_handle(ruby_sd) ){
|
61
|
-
rb_raise(
|
61
|
+
rb_raise(rb_eConnectionBad, "Could not unwrap win32 socket handle");
|
62
62
|
}
|
63
63
|
#endif
|
64
64
|
rb_funcall( socket_io, rb_intern("close"), 0 );
|
@@ -113,7 +113,11 @@ pgconn_gc_free( PGconn *conn )
|
|
113
113
|
static VALUE
|
114
114
|
pgconn_s_allocate( VALUE klass )
|
115
115
|
{
|
116
|
-
|
116
|
+
VALUE self = Data_Wrap_Struct( klass, NULL, pgconn_gc_free, NULL );
|
117
|
+
rb_iv_set( self, "@socket_io", Qnil );
|
118
|
+
rb_iv_set( self, "@notice_receiver", Qnil);
|
119
|
+
rb_iv_set( self, "@notice_processor", Qnil);
|
120
|
+
return self;
|
117
121
|
}
|
118
122
|
|
119
123
|
|
@@ -182,16 +186,16 @@ pgconn_init(int argc, VALUE *argv, VALUE self)
|
|
182
186
|
VALUE error;
|
183
187
|
|
184
188
|
conninfo = rb_funcall2( rb_cPGconn, rb_intern("parse_connect_args"), argc, argv );
|
185
|
-
conn =
|
189
|
+
conn = gvl_PQconnectdb(StringValuePtr(conninfo));
|
186
190
|
|
187
191
|
if(conn == NULL)
|
188
|
-
rb_raise(rb_ePGerror, "
|
192
|
+
rb_raise(rb_ePGerror, "PQconnectdb() unable to allocate structure");
|
189
193
|
|
190
194
|
Check_Type(self, T_DATA);
|
191
195
|
DATA_PTR(self) = conn;
|
192
196
|
|
193
197
|
if (PQstatus(conn) == CONNECTION_BAD) {
|
194
|
-
error = rb_exc_new2(
|
198
|
+
error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(conn));
|
195
199
|
rb_iv_set(error, "@connection", self);
|
196
200
|
rb_exc_raise(error);
|
197
201
|
}
|
@@ -236,7 +240,7 @@ pgconn_s_connect_start( int argc, VALUE *argv, VALUE klass )
|
|
236
240
|
*/
|
237
241
|
rb_conn = pgconn_s_allocate( klass );
|
238
242
|
conninfo = rb_funcall2( klass, rb_intern("parse_connect_args"), argc, argv );
|
239
|
-
conn =
|
243
|
+
conn = gvl_PQconnectStart( StringValuePtr(conninfo) );
|
240
244
|
|
241
245
|
if( conn == NULL )
|
242
246
|
rb_raise(rb_ePGerror, "PQconnectStart() unable to allocate structure");
|
@@ -245,7 +249,7 @@ pgconn_s_connect_start( int argc, VALUE *argv, VALUE klass )
|
|
245
249
|
DATA_PTR(rb_conn) = conn;
|
246
250
|
|
247
251
|
if ( PQstatus(conn) == CONNECTION_BAD ) {
|
248
|
-
error = rb_exc_new2(
|
252
|
+
error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(conn));
|
249
253
|
rb_iv_set(error, "@connection", rb_conn);
|
250
254
|
rb_exc_raise(error);
|
251
255
|
}
|
@@ -415,7 +419,7 @@ static VALUE
|
|
415
419
|
pgconn_connect_poll(VALUE self)
|
416
420
|
{
|
417
421
|
PostgresPollingStatusType status;
|
418
|
-
status =
|
422
|
+
status = gvl_PQconnectPoll(pg_get_pgconn(self));
|
419
423
|
return INT2FIX((int)status);
|
420
424
|
}
|
421
425
|
|
@@ -460,7 +464,7 @@ static VALUE
|
|
460
464
|
pgconn_reset( VALUE self )
|
461
465
|
{
|
462
466
|
pgconn_close_socket_io( self );
|
463
|
-
|
467
|
+
gvl_PQreset( pg_get_pgconn(self) );
|
464
468
|
return self;
|
465
469
|
}
|
466
470
|
|
@@ -478,8 +482,8 @@ static VALUE
|
|
478
482
|
pgconn_reset_start(VALUE self)
|
479
483
|
{
|
480
484
|
pgconn_close_socket_io( self );
|
481
|
-
if(
|
482
|
-
rb_raise(
|
485
|
+
if(gvl_PQresetStart(pg_get_pgconn(self)) == 0)
|
486
|
+
rb_raise(rb_eUnableToSend, "reset has failed");
|
483
487
|
return Qnil;
|
484
488
|
}
|
485
489
|
|
@@ -495,7 +499,7 @@ static VALUE
|
|
495
499
|
pgconn_reset_poll(VALUE self)
|
496
500
|
{
|
497
501
|
PostgresPollingStatusType status;
|
498
|
-
status =
|
502
|
+
status = gvl_PQresetPoll(pg_get_pgconn(self));
|
499
503
|
return INT2FIX((int)status);
|
500
504
|
}
|
501
505
|
|
@@ -718,7 +722,7 @@ pgconn_socket(VALUE self)
|
|
718
722
|
{
|
719
723
|
int sd;
|
720
724
|
if( (sd = PQsocket(pg_get_pgconn(self))) < 0)
|
721
|
-
rb_raise(
|
725
|
+
rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
|
722
726
|
return INT2NUM(sd);
|
723
727
|
}
|
724
728
|
|
@@ -749,7 +753,7 @@ pgconn_socket_io(VALUE self)
|
|
749
753
|
|
750
754
|
if ( !RTEST(socket_io) ) {
|
751
755
|
if( (sd = PQsocket(pg_get_pgconn(self))) < 0)
|
752
|
-
rb_raise(
|
756
|
+
rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
|
753
757
|
|
754
758
|
#ifdef _WIN32
|
755
759
|
ruby_sd = rb_w32_wrap_io_handle((HANDLE)(intptr_t)sd, O_RDWR|O_BINARY|O_NOINHERIT);
|
@@ -1448,6 +1452,10 @@ pgconn_escape_literal(VALUE self, VALUE string)
|
|
1448
1452
|
PQfreemem(escaped);
|
1449
1453
|
OBJ_INFECT(result, string);
|
1450
1454
|
|
1455
|
+
#ifdef M17N_SUPPORTED
|
1456
|
+
rb_enc_associate(result, pg_conn_enc_get( pg_get_pgconn(self) ));
|
1457
|
+
#endif
|
1458
|
+
|
1451
1459
|
return result;
|
1452
1460
|
}
|
1453
1461
|
#endif
|
@@ -1458,6 +1466,9 @@ pgconn_escape_literal(VALUE self, VALUE string)
|
|
1458
1466
|
* conn.escape_identifier( str ) -> String
|
1459
1467
|
*
|
1460
1468
|
* Escape an arbitrary String +str+ as an identifier.
|
1469
|
+
*
|
1470
|
+
* This method does the same as #quote_ident, but uses libpq to
|
1471
|
+
* process the string.
|
1461
1472
|
*/
|
1462
1473
|
static VALUE
|
1463
1474
|
pgconn_escape_identifier(VALUE self, VALUE string)
|
@@ -1481,6 +1492,10 @@ pgconn_escape_identifier(VALUE self, VALUE string)
|
|
1481
1492
|
PQfreemem(escaped);
|
1482
1493
|
OBJ_INFECT(result, string);
|
1483
1494
|
|
1495
|
+
#ifdef M17N_SUPPORTED
|
1496
|
+
rb_enc_associate(result, pg_conn_enc_get( pg_get_pgconn(self) ));
|
1497
|
+
#endif
|
1498
|
+
|
1484
1499
|
return result;
|
1485
1500
|
}
|
1486
1501
|
#endif
|
@@ -1596,7 +1611,7 @@ pgconn_send_query(int argc, VALUE *argv, VALUE self)
|
|
1596
1611
|
/* If called with no parameters, use PQsendQuery */
|
1597
1612
|
if(NIL_P(params)) {
|
1598
1613
|
if(PQsendQuery(conn,StringValuePtr(command)) == 0) {
|
1599
|
-
error = rb_exc_new2(
|
1614
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1600
1615
|
rb_iv_set(error, "@connection", self);
|
1601
1616
|
rb_exc_raise(error);
|
1602
1617
|
}
|
@@ -1679,7 +1694,7 @@ pgconn_send_query(int argc, VALUE *argv, VALUE self)
|
|
1679
1694
|
xfree(paramFormats);
|
1680
1695
|
|
1681
1696
|
if(result == 0) {
|
1682
|
-
error = rb_exc_new2(
|
1697
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1683
1698
|
rb_iv_set(error, "@connection", self);
|
1684
1699
|
rb_exc_raise(error);
|
1685
1700
|
}
|
@@ -1741,7 +1756,7 @@ pgconn_send_prepare(int argc, VALUE *argv, VALUE self)
|
|
1741
1756
|
xfree(paramTypes);
|
1742
1757
|
|
1743
1758
|
if(result == 0) {
|
1744
|
-
error = rb_exc_new2(
|
1759
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1745
1760
|
rb_iv_set(error, "@connection", self);
|
1746
1761
|
rb_exc_raise(error);
|
1747
1762
|
}
|
@@ -1864,7 +1879,7 @@ pgconn_send_query_prepared(int argc, VALUE *argv, VALUE self)
|
|
1864
1879
|
xfree(paramFormats);
|
1865
1880
|
|
1866
1881
|
if(result == 0) {
|
1867
|
-
error = rb_exc_new2(
|
1882
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1868
1883
|
rb_iv_set(error, "@connection", self);
|
1869
1884
|
rb_exc_raise(error);
|
1870
1885
|
}
|
@@ -1885,7 +1900,7 @@ pgconn_send_describe_prepared(VALUE self, VALUE stmt_name)
|
|
1885
1900
|
PGconn *conn = pg_get_pgconn(self);
|
1886
1901
|
/* returns 0 on failure */
|
1887
1902
|
if(PQsendDescribePrepared(conn,StringValuePtr(stmt_name)) == 0) {
|
1888
|
-
error = rb_exc_new2(
|
1903
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1889
1904
|
rb_iv_set(error, "@connection", self);
|
1890
1905
|
rb_exc_raise(error);
|
1891
1906
|
}
|
@@ -1907,7 +1922,7 @@ pgconn_send_describe_portal(VALUE self, VALUE portal)
|
|
1907
1922
|
PGconn *conn = pg_get_pgconn(self);
|
1908
1923
|
/* returns 0 on failure */
|
1909
1924
|
if(PQsendDescribePortal(conn,StringValuePtr(portal)) == 0) {
|
1910
|
-
error = rb_exc_new2(
|
1925
|
+
error = rb_exc_new2(rb_eUnableToSend, PQerrorMessage(conn));
|
1911
1926
|
rb_iv_set(error, "@connection", self);
|
1912
1927
|
rb_exc_raise(error);
|
1913
1928
|
}
|
@@ -1965,7 +1980,7 @@ pgconn_consume_input(self)
|
|
1965
1980
|
PGconn *conn = pg_get_pgconn(self);
|
1966
1981
|
/* returns 0 on error */
|
1967
1982
|
if(PQconsumeInput(conn) == 0) {
|
1968
|
-
error = rb_exc_new2(
|
1983
|
+
error = rb_exc_new2(rb_eConnectionBad, PQerrorMessage(conn));
|
1969
1984
|
rb_iv_set(error, "@connection", self);
|
1970
1985
|
rb_exc_raise(error);
|
1971
1986
|
}
|
@@ -2122,7 +2137,7 @@ pgconn_notifies(VALUE self)
|
|
2122
2137
|
sym_be_pid = ID2SYM(rb_intern("be_pid"));
|
2123
2138
|
sym_extra = ID2SYM(rb_intern("extra"));
|
2124
2139
|
|
2125
|
-
notification =
|
2140
|
+
notification = gvl_PQnotifies(conn);
|
2126
2141
|
if (notification == NULL) {
|
2127
2142
|
return Qnil;
|
2128
2143
|
}
|
@@ -2199,32 +2214,46 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2199
2214
|
{
|
2200
2215
|
int sd = PQsocket( conn );
|
2201
2216
|
void *retval;
|
2217
|
+
struct timeval aborttime={0,0}, currtime, waittime;
|
2202
2218
|
DWORD timeout_milisec = INFINITE;
|
2203
2219
|
DWORD wait_ret;
|
2204
2220
|
WSAEVENT hEvent;
|
2205
2221
|
|
2206
2222
|
if ( sd < 0 )
|
2207
|
-
|
2223
|
+
rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
|
2208
2224
|
|
2209
2225
|
hEvent = WSACreateEvent();
|
2210
2226
|
|
2211
|
-
if ( ptimeout ) {
|
2212
|
-
timeout_milisec = (DWORD)( ptimeout->tv_sec * 1e3 + ptimeout->tv_usec / 1e3 );
|
2213
|
-
}
|
2214
|
-
|
2215
2227
|
/* Check for connection errors (PQisBusy is true on connection errors) */
|
2216
2228
|
if( PQconsumeInput(conn) == 0 ) {
|
2217
2229
|
WSACloseEvent( hEvent );
|
2218
|
-
rb_raise(
|
2230
|
+
rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
|
2231
|
+
}
|
2232
|
+
|
2233
|
+
if ( ptimeout ) {
|
2234
|
+
gettimeofday(&currtime, NULL);
|
2235
|
+
timeradd(&currtime, ptimeout, &aborttime);
|
2219
2236
|
}
|
2220
2237
|
|
2221
2238
|
while ( !(retval=is_readable(conn)) ) {
|
2222
2239
|
if ( WSAEventSelect(sd, hEvent, FD_READ|FD_CLOSE) == SOCKET_ERROR ) {
|
2223
2240
|
WSACloseEvent( hEvent );
|
2224
|
-
rb_raise(
|
2241
|
+
rb_raise( rb_eConnectionBad, "WSAEventSelect socket error: %d", WSAGetLastError() );
|
2225
2242
|
}
|
2226
2243
|
|
2227
|
-
|
2244
|
+
if ( ptimeout ) {
|
2245
|
+
gettimeofday(&currtime, NULL);
|
2246
|
+
timersub(&aborttime, &currtime, &waittime);
|
2247
|
+
timeout_milisec = (DWORD)( waittime.tv_sec * 1e3 + waittime.tv_usec / 1e3 );
|
2248
|
+
}
|
2249
|
+
|
2250
|
+
/* Is the given timeout valid? */
|
2251
|
+
if( !ptimeout || (waittime.tv_sec >= 0 && waittime.tv_usec >= 0) ){
|
2252
|
+
/* Wait for the socket to become readable before checking again */
|
2253
|
+
wait_ret = rb_w32_wait_events( &hEvent, 1, timeout_milisec );
|
2254
|
+
} else {
|
2255
|
+
wait_ret = WAIT_TIMEOUT;
|
2256
|
+
}
|
2228
2257
|
|
2229
2258
|
if ( wait_ret == WAIT_TIMEOUT ) {
|
2230
2259
|
WSACloseEvent( hEvent );
|
@@ -2237,16 +2266,16 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2237
2266
|
rb_thread_check_ints();
|
2238
2267
|
} else if ( wait_ret == WAIT_FAILED ) {
|
2239
2268
|
WSACloseEvent( hEvent );
|
2240
|
-
rb_raise(
|
2269
|
+
rb_raise( rb_eConnectionBad, "Wait on socket error (WaitForMultipleObjects): %lu", GetLastError() );
|
2241
2270
|
} else {
|
2242
2271
|
WSACloseEvent( hEvent );
|
2243
|
-
rb_raise(
|
2272
|
+
rb_raise( rb_eConnectionBad, "Wait on socket abandoned (WaitForMultipleObjects)" );
|
2244
2273
|
}
|
2245
2274
|
|
2246
2275
|
/* Check for connection errors (PQisBusy is true on connection errors) */
|
2247
2276
|
if ( PQconsumeInput(conn) == 0 ) {
|
2248
2277
|
WSACloseEvent( hEvent );
|
2249
|
-
rb_raise(
|
2278
|
+
rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
|
2250
2279
|
}
|
2251
2280
|
}
|
2252
2281
|
|
@@ -2265,18 +2294,24 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2265
2294
|
int ret;
|
2266
2295
|
void *retval;
|
2267
2296
|
rb_fdset_t sd_rset;
|
2297
|
+
struct timeval aborttime={0,0}, currtime, waittime;
|
2268
2298
|
#ifdef _WIN32
|
2269
2299
|
rb_fdset_t crt_sd_rset;
|
2270
2300
|
#endif
|
2271
2301
|
|
2272
2302
|
if ( sd < 0 )
|
2273
|
-
|
2303
|
+
rb_raise(rb_eConnectionBad, "PQsocket() can't get socket descriptor");
|
2274
2304
|
|
2275
2305
|
/* Check for connection errors (PQisBusy is true on connection errors) */
|
2276
2306
|
if ( PQconsumeInput(conn) == 0 )
|
2277
|
-
rb_raise(
|
2307
|
+
rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
|
2308
|
+
|
2309
|
+
rb_fd_init( &sd_rset );
|
2278
2310
|
|
2279
|
-
|
2311
|
+
if ( ptimeout ) {
|
2312
|
+
gettimeofday(&currtime, NULL);
|
2313
|
+
timeradd(&currtime, ptimeout, &aborttime);
|
2314
|
+
}
|
2280
2315
|
|
2281
2316
|
while ( !(retval=is_readable(conn)) ) {
|
2282
2317
|
rb_fd_zero( &sd_rset );
|
@@ -2290,8 +2325,19 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2290
2325
|
create_crt_fd(&sd_rset, &crt_sd_rset);
|
2291
2326
|
#endif
|
2292
2327
|
|
2293
|
-
|
2294
|
-
|
2328
|
+
if ( ptimeout ) {
|
2329
|
+
gettimeofday(&currtime, NULL);
|
2330
|
+
timersub(&aborttime, &currtime, &waittime);
|
2331
|
+
}
|
2332
|
+
|
2333
|
+
/* Is the given timeout valid? */
|
2334
|
+
if( !ptimeout || (waittime.tv_sec >= 0 && waittime.tv_usec >= 0) ){
|
2335
|
+
/* Wait for the socket to become readable before checking again */
|
2336
|
+
ret = rb_thread_fd_select( sd+1, &sd_rset, NULL, NULL, ptimeout ? &waittime : NULL );
|
2337
|
+
} else {
|
2338
|
+
ret = 0;
|
2339
|
+
}
|
2340
|
+
|
2295
2341
|
|
2296
2342
|
#ifdef _WIN32
|
2297
2343
|
cleanup_crt_fd(&sd_rset, &crt_sd_rset);
|
@@ -2311,7 +2357,7 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2311
2357
|
/* Check for connection errors (PQisBusy is true on connection errors) */
|
2312
2358
|
if ( PQconsumeInput(conn) == 0 ){
|
2313
2359
|
rb_fd_term( &sd_rset );
|
2314
|
-
rb_raise(
|
2360
|
+
rb_raise( rb_eConnectionBad, "PQconsumeInput() %s", PQerrorMessage(conn) );
|
2315
2361
|
}
|
2316
2362
|
}
|
2317
2363
|
|
@@ -2325,7 +2371,7 @@ wait_socket_readable( PGconn *conn, struct timeval *ptimeout, void *(*is_readabl
|
|
2325
2371
|
static void *
|
2326
2372
|
notify_readable(PGconn *conn)
|
2327
2373
|
{
|
2328
|
-
return (void*)
|
2374
|
+
return (void*)gvl_PQnotifies(conn);
|
2329
2375
|
}
|
2330
2376
|
|
2331
2377
|
/*
|
@@ -2761,7 +2807,7 @@ pgconn_set_client_encoding(VALUE self, VALUE str)
|
|
2761
2807
|
|
2762
2808
|
/*
|
2763
2809
|
* call-seq:
|
2764
|
-
* conn.transaction { |conn| ... } ->
|
2810
|
+
* conn.transaction { |conn| ... } -> result of the block
|
2765
2811
|
*
|
2766
2812
|
* Executes a +BEGIN+ at the start of the block,
|
2767
2813
|
* and a +COMMIT+ at the end of the block, or
|
@@ -2773,13 +2819,14 @@ pgconn_transaction(VALUE self)
|
|
2773
2819
|
PGconn *conn = pg_get_pgconn(self);
|
2774
2820
|
PGresult *result;
|
2775
2821
|
VALUE rb_pgresult;
|
2822
|
+
VALUE block_result = Qnil;
|
2776
2823
|
int status;
|
2777
2824
|
|
2778
2825
|
if (rb_block_given_p()) {
|
2779
2826
|
result = gvl_PQexec(conn, "BEGIN");
|
2780
2827
|
rb_pgresult = pg_new_result(result, self);
|
2781
2828
|
pg_result_check(rb_pgresult);
|
2782
|
-
rb_protect(rb_yield, self, &status);
|
2829
|
+
block_result = rb_protect(rb_yield, self, &status);
|
2783
2830
|
if(status == 0) {
|
2784
2831
|
result = gvl_PQexec(conn, "COMMIT");
|
2785
2832
|
rb_pgresult = pg_new_result(result, self);
|
@@ -2798,7 +2845,7 @@ pgconn_transaction(VALUE self)
|
|
2798
2845
|
/* no block supplied? */
|
2799
2846
|
rb_raise(rb_eArgError, "Must supply block for PG::Connection#transaction");
|
2800
2847
|
}
|
2801
|
-
return
|
2848
|
+
return block_result;
|
2802
2849
|
}
|
2803
2850
|
|
2804
2851
|
|
@@ -2832,6 +2879,9 @@ pgconn_s_quote_ident(VALUE self, VALUE in_str)
|
|
2832
2879
|
* double-quotes. */
|
2833
2880
|
char buffer[NAMEDATALEN*2+2];
|
2834
2881
|
unsigned int i=0,j=0;
|
2882
|
+
#ifdef M17N_SUPPORTED
|
2883
|
+
rb_encoding* enc;
|
2884
|
+
#endif
|
2835
2885
|
|
2836
2886
|
UNUSED( self );
|
2837
2887
|
|
@@ -2849,6 +2899,16 @@ pgconn_s_quote_ident(VALUE self, VALUE in_str)
|
|
2849
2899
|
buffer[j++] = '"';
|
2850
2900
|
ret = rb_str_new(buffer,j);
|
2851
2901
|
OBJ_INFECT(ret, in_str);
|
2902
|
+
|
2903
|
+
#ifdef M17N_SUPPORTED
|
2904
|
+
if ( rb_obj_class(self) == rb_cPGconn ) {
|
2905
|
+
enc = pg_conn_enc_get( pg_get_pgconn(self) );
|
2906
|
+
} else {
|
2907
|
+
enc = rb_enc_get(in_str);
|
2908
|
+
}
|
2909
|
+
rb_enc_associate(ret, enc);
|
2910
|
+
#endif
|
2911
|
+
|
2852
2912
|
return ret;
|
2853
2913
|
}
|
2854
2914
|
|