kamk-pg 0.8.0 → 0.8.0.3
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/BSD +23 -0
- data/COPYING.txt +340 -0
- data/ChangeLog +261 -0
- data/Contributors +28 -0
- data/GPL +340 -0
- data/LICENSE +58 -0
- data/README +125 -0
- data/Rakefile +103 -0
- data/doc/postgres.html +278 -0
- data/doc/postgres.jp.html +256 -0
- data/ext/compat.c +541 -0
- data/ext/compat.h +180 -0
- data/ext/mingw/Rakefile +24 -0
- data/ext/mingw/build.rake +40 -0
- data/ext/mkrf_config.rb +138 -0
- data/ext/pg-m17n3.patch +535 -0
- data/ext/pg.h +42 -0
- data/ext/vc/pg.sln +26 -0
- data/sample/losample.rb +47 -0
- data/sample/psql.rb +1181 -0
- data/sample/psqlHelp.rb +158 -0
- data/sample/test1.rb +63 -0
- data/sample/test2.rb +44 -0
- data/sample/test4.rb +71 -0
- data/spec/data/expected_trace.out +26 -0
- data/spec/data/random_binary_data +0 -0
- data/spec/pgconn_spec.rb +130 -0
- data/spec/pgresult_spec.rb +112 -0
- metadata +33 -1
data/ext/compat.h
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
|
|
2
|
+
#ifndef __compat_h
|
|
3
|
+
#define __compat_h
|
|
4
|
+
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
|
|
7
|
+
#include "libpq-fe.h"
|
|
8
|
+
#include "libpq/libpq-fs.h" /* large-object interface */
|
|
9
|
+
|
|
10
|
+
#include "ruby.h"
|
|
11
|
+
|
|
12
|
+
/* pg_config.h does not exist in older versions of
|
|
13
|
+
* PostgreSQL, so I can't effectively use PG_VERSION_NUM
|
|
14
|
+
* Instead, I create some #defines to help organization.
|
|
15
|
+
*/
|
|
16
|
+
#ifndef HAVE_PQCONNECTIONUSEDPASSWORD
|
|
17
|
+
#define PG_BEFORE_080300
|
|
18
|
+
#endif
|
|
19
|
+
|
|
20
|
+
#ifndef HAVE_PQISTHREADSAFE
|
|
21
|
+
#define PG_BEFORE_080200
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
#ifndef HAVE_LO_CREATE
|
|
25
|
+
#define PG_BEFORE_080100
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#ifndef HAVE_PQPREPARE
|
|
29
|
+
#define PG_BEFORE_080000
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
#ifndef HAVE_PQEXECPARAMS
|
|
33
|
+
#define PG_BEFORE_070400
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
#ifndef HAVE_PQESCAPESTRINGCONN
|
|
37
|
+
#define PG_BEFORE_070300
|
|
38
|
+
#error PostgreSQL client version too old, requires 7.3 or later.
|
|
39
|
+
#endif
|
|
40
|
+
|
|
41
|
+
/* This is necessary because NAMEDATALEN is defined in
|
|
42
|
+
* pg_config_manual.h in 8.3, and that include file doesn't
|
|
43
|
+
* exist before 7.4
|
|
44
|
+
*/
|
|
45
|
+
#ifndef PG_BEFORE_070400
|
|
46
|
+
#include "pg_config_manual.h"
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
#ifndef PG_DIAG_INTERNAL_POSITION
|
|
50
|
+
#define PG_DIAG_INTERNAL_POSITION 'p'
|
|
51
|
+
#endif /* PG_DIAG_INTERNAL_POSITION */
|
|
52
|
+
|
|
53
|
+
#ifndef PG_DIAG_INTERNAL_QUERY
|
|
54
|
+
#define PG_DIAG_INTERNAL_QUERY 'q'
|
|
55
|
+
#endif /* PG_DIAG_INTERNAL_QUERY */
|
|
56
|
+
|
|
57
|
+
#ifdef PG_BEFORE_080300
|
|
58
|
+
|
|
59
|
+
#ifndef HAVE_PG_ENCODING_TO_CHAR
|
|
60
|
+
#define pg_encoding_to_char(x) "SQL_ASCII"
|
|
61
|
+
#else
|
|
62
|
+
/* Some versions ofPostgreSQL prior to 8.3 define pg_encoding_to_char
|
|
63
|
+
* but do not declare it in a header file, so this declaration will
|
|
64
|
+
* eliminate an unecessary warning
|
|
65
|
+
*/
|
|
66
|
+
extern char* pg_encoding_to_char(int);
|
|
67
|
+
#endif /* HAVE_PG_ENCODING_TO_CHAR */
|
|
68
|
+
|
|
69
|
+
int PQconnectionNeedsPassword(PGconn *conn);
|
|
70
|
+
int PQconnectionUsedPassword(PGconn *conn);
|
|
71
|
+
int lo_truncate(PGconn *conn, int fd, size_t len);
|
|
72
|
+
|
|
73
|
+
#endif /* PG_BEFORE_080300 */
|
|
74
|
+
|
|
75
|
+
#ifdef PG_BEFORE_080200
|
|
76
|
+
int PQisthreadsafe(void);
|
|
77
|
+
int PQnparams(const PGresult *res);
|
|
78
|
+
Oid PQparamtype(const PGresult *res, int param_number);
|
|
79
|
+
PGresult * PQdescribePrepared(PGconn *conn, const char *stmtName);
|
|
80
|
+
PGresult * PQdescribePortal(PGconn *conn, const char *portalName);
|
|
81
|
+
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
|
|
82
|
+
int PQsendDescribePortal(PGconn *conn, const char *portalName);
|
|
83
|
+
char *PQencryptPassword(const char *passwd, const char *user);
|
|
84
|
+
#endif /* PG_BEFORE_080200 */
|
|
85
|
+
|
|
86
|
+
#ifdef PG_BEFORE_080100
|
|
87
|
+
Oid lo_create(PGconn *conn, Oid lobjId);
|
|
88
|
+
#endif /* PG_BEFORE_080100 */
|
|
89
|
+
|
|
90
|
+
#ifdef PG_BEFORE_080000
|
|
91
|
+
PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query,
|
|
92
|
+
int nParams, const Oid *paramTypes);
|
|
93
|
+
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query,
|
|
94
|
+
int nParams, const Oid *paramTypes);
|
|
95
|
+
int PQserverVersion(const PGconn* conn);
|
|
96
|
+
#endif /* PG_BEFORE_080000 */
|
|
97
|
+
|
|
98
|
+
#ifdef PG_BEFORE_070400
|
|
99
|
+
|
|
100
|
+
#define PG_DIAG_SEVERITY 'S'
|
|
101
|
+
#define PG_DIAG_SQLSTATE 'C'
|
|
102
|
+
#define PG_DIAG_MESSAGE_PRIMARY 'M'
|
|
103
|
+
#define PG_DIAG_MESSAGE_DETAIL 'D'
|
|
104
|
+
#define PG_DIAG_MESSAGE_HINT 'H'
|
|
105
|
+
#define PG_DIAG_STATEMENT_POSITION 'P'
|
|
106
|
+
#define PG_DIAG_CONTEXT 'W'
|
|
107
|
+
#define PG_DIAG_SOURCE_FILE 'F'
|
|
108
|
+
#define PG_DIAG_SOURCE_LINE 'L'
|
|
109
|
+
#define PG_DIAG_SOURCE_FUNCTION 'R'
|
|
110
|
+
|
|
111
|
+
#define PQfreemem(ptr) free(ptr)
|
|
112
|
+
#define PGNOTIFY_EXTRA(notify) ""
|
|
113
|
+
|
|
114
|
+
/* CONNECTION_SSL_STARTUP was added to an enum type
|
|
115
|
+
* after 7.3. For 7.3 in order to compile, we just need
|
|
116
|
+
* it to evaluate to something that is not present in that
|
|
117
|
+
* enum.
|
|
118
|
+
*/
|
|
119
|
+
#define CONNECTION_SSL_STARTUP 1000000
|
|
120
|
+
|
|
121
|
+
typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
|
|
122
|
+
|
|
123
|
+
typedef enum
|
|
124
|
+
{
|
|
125
|
+
PQERRORS_TERSE, /* single-line error messages */
|
|
126
|
+
PQERRORS_DEFAULT, /* recommended style */
|
|
127
|
+
PQERRORS_VERBOSE /* all the facts, ma'am */
|
|
128
|
+
} PGVerbosity;
|
|
129
|
+
|
|
130
|
+
typedef enum
|
|
131
|
+
{
|
|
132
|
+
PQTRANS_IDLE, /* connection idle */
|
|
133
|
+
PQTRANS_ACTIVE, /* command in progress */
|
|
134
|
+
PQTRANS_INTRANS, /* idle, within transaction block */
|
|
135
|
+
PQTRANS_INERROR, /* idle, within failed transaction */
|
|
136
|
+
PQTRANS_UNKNOWN /* cannot determine status */
|
|
137
|
+
} PGTransactionStatusType;
|
|
138
|
+
|
|
139
|
+
PGresult *PQexecParams(PGconn *conn, const char *command, int nParams,
|
|
140
|
+
const Oid *paramTypes, const char * const * paramValues, const int *paramLengths,
|
|
141
|
+
const int *paramFormats, int resultFormat);
|
|
142
|
+
PGTransactionStatusType PQtransactionStatus(const PGconn *conn);
|
|
143
|
+
char *PQparameterStatus(const PGconn *conn, const char *paramName);
|
|
144
|
+
int PQprotocolVersion(const PGconn *conn);
|
|
145
|
+
PGresult *PQexecPrepared(PGconn *conn, const char *stmtName, int nParams,
|
|
146
|
+
const char * const *ParamValues, const int *paramLengths, const int *paramFormats,
|
|
147
|
+
int resultFormat);
|
|
148
|
+
int PQsendQueryParams(PGconn *conn, const char *command, int nParams,
|
|
149
|
+
const Oid *paramTypes, const char * const * paramValues, const int *paramLengths,
|
|
150
|
+
const int *paramFormats, int resultFormat);
|
|
151
|
+
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams,
|
|
152
|
+
const char * const *ParamValues, const int *paramLengths, const int *paramFormats,
|
|
153
|
+
int resultFormat);
|
|
154
|
+
int PQputCopyData(PGconn *conn, const char *buffer, int nbytes);
|
|
155
|
+
int PQputCopyEnd(PGconn *conn, const char *errormsg);
|
|
156
|
+
int PQgetCopyData(PGconn *conn, char **buffer, int async);
|
|
157
|
+
PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
|
|
158
|
+
Oid PQftable(const PGresult *res, int column_number);
|
|
159
|
+
int PQftablecol(const PGresult *res, int column_number);
|
|
160
|
+
int PQfformat(const PGresult *res, int column_number);
|
|
161
|
+
char *PQresultErrorField(const PGresult *res, int fieldcode);
|
|
162
|
+
PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg);
|
|
163
|
+
|
|
164
|
+
#else
|
|
165
|
+
#define PGNOTIFY_EXTRA(notify) ((notify)->extra)
|
|
166
|
+
#endif /* PG_BEFORE_070400 */
|
|
167
|
+
|
|
168
|
+
#ifdef PG_BEFORE_070300
|
|
169
|
+
#error unsupported postgresql version, requires 7.3 or later.
|
|
170
|
+
int PQsetClientEncoding(PGconn *conn, const char *encoding)
|
|
171
|
+
size_t PQescapeString(char *to, const char *from, size_t length);
|
|
172
|
+
unsigned char * PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen);
|
|
173
|
+
unsigned char * PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen);
|
|
174
|
+
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from,
|
|
175
|
+
size_t length, int *error);
|
|
176
|
+
unsigned char *PQescapeByteaConn(PGconn *conn, const unsigned char *from,
|
|
177
|
+
size_t from_length, size_t *to_length);
|
|
178
|
+
#endif /* PG_BEFORE_070300 */
|
|
179
|
+
|
|
180
|
+
#endif /* __compat_h */
|
data/ext/mingw/Rakefile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# We can't use Ruby's standard build procedures
|
|
2
|
+
# on Windows because the Ruby executable is
|
|
3
|
+
# built with VC++ while here we want to build
|
|
4
|
+
# with MingW. So just roll our own...
|
|
5
|
+
|
|
6
|
+
require 'fileutils'
|
|
7
|
+
require 'rbconfig'
|
|
8
|
+
|
|
9
|
+
EXTENSION_NAME = "pg.#{Config::CONFIG["DLEXT"]}"
|
|
10
|
+
|
|
11
|
+
# This is called when the Windows GEM is installed!
|
|
12
|
+
task :install do
|
|
13
|
+
# Gems will pass these two environment variables:
|
|
14
|
+
# RUBYARCHDIR=#{dest_path}
|
|
15
|
+
# RUBYLIBDIR=#{dest_path}
|
|
16
|
+
|
|
17
|
+
dest_path = ENV['RUBYLIBDIR']
|
|
18
|
+
mkdir_p(dest_path)
|
|
19
|
+
|
|
20
|
+
# Copy the extension
|
|
21
|
+
cp(EXTENSION_NAME, dest_path)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
task :default => :install
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# We can't use Ruby's standard build procedures
|
|
2
|
+
# on Windows because the Ruby executable is
|
|
3
|
+
# built with VC++ while here we want to build
|
|
4
|
+
# with MingW. So just roll our own...
|
|
5
|
+
|
|
6
|
+
require 'rake/clean'
|
|
7
|
+
require 'rbconfig'
|
|
8
|
+
|
|
9
|
+
RUBY_INCLUDE_DIR = Config::CONFIG["archdir"]
|
|
10
|
+
RUBY_BIN_DIR = Config::CONFIG["bindir"]
|
|
11
|
+
RUBY_LIB_DIR = Config::CONFIG["libdir"]
|
|
12
|
+
RUBY_SHARED_LIB = Config::CONFIG["LIBRUBY"]
|
|
13
|
+
RUBY_SHARED_DLL = RUBY_SHARED_LIB.gsub(/lib$/, 'dll')
|
|
14
|
+
|
|
15
|
+
EXTENSION_NAME = "pg.#{Config::CONFIG["DLEXT"]}"
|
|
16
|
+
|
|
17
|
+
CLEAN.include('*.o')
|
|
18
|
+
CLOBBER.include(EXTENSION_NAME)
|
|
19
|
+
|
|
20
|
+
task :default => "pg"
|
|
21
|
+
|
|
22
|
+
DEFINES = "-DHAVE_LIBPQ_FE_H -DHAVE_LIBPQ_LIBPQ_FS_H -DHAVE_PQCONNECTIONUSEDPASSWORD -DHAVE_PQISTHREADSAFE -DHAVE_LO_CREATE -DHAVE_PQPREPARE -DHAVE_PQEXECPARAMS -DHAVE_PQESCAPESTRING -DHAVE_PQESCAPESTRINGCONN -DHAVE_PG_ENCODING_TO_CHAR -DHAVE_PQSETCLIENTENCODING"
|
|
23
|
+
LIBS = "-lpq -lm"
|
|
24
|
+
SRC = FileList['../*.c']
|
|
25
|
+
OBJ = SRC.collect do |file_name|
|
|
26
|
+
File.basename(file_name).ext('o')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
SRC.each do |srcfile|
|
|
30
|
+
objfile = File.basename(srcfile).ext('o')
|
|
31
|
+
file objfile => srcfile do
|
|
32
|
+
command = "gcc -c -O2 -Wall #{DEFINES} -o #{objfile} -I/usr/local/include #{srcfile} -I#{RUBY_INCLUDE_DIR}"
|
|
33
|
+
sh "sh -c '#{command}'"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
file "pg" => OBJ do
|
|
38
|
+
command = "gcc -shared -o #{EXTENSION_NAME} #{OBJ} -L/usr/local/lib #{LIBS} #{RUBY_BIN_DIR}/#{RUBY_SHARED_DLL}"
|
|
39
|
+
sh "sh -c '#{command}'"
|
|
40
|
+
end
|
data/ext/mkrf_config.rb
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'mkrf'
|
|
3
|
+
|
|
4
|
+
pg_config_command =
|
|
5
|
+
if RUBY_PLATFORM.match(/win32/)
|
|
6
|
+
"pg_config --bindir > nul"
|
|
7
|
+
else
|
|
8
|
+
"pg_config --bindir > /dev/null"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
unless system(pg_config_command)
|
|
12
|
+
$stderr.write("ERROR: can't find pg_config.\n")
|
|
13
|
+
$stderr.write("HINT: Make sure pg_config is in your PATH\n")
|
|
14
|
+
exit 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
$functions = %w[
|
|
18
|
+
lo_create
|
|
19
|
+
PQconnectionUsedPassword
|
|
20
|
+
PQisthreadsafe
|
|
21
|
+
PQprepare
|
|
22
|
+
PQexecParams
|
|
23
|
+
PQescapeString
|
|
24
|
+
PQescapeStringConn
|
|
25
|
+
lo_create
|
|
26
|
+
pg_encoding_to_char
|
|
27
|
+
PQsetClientEncoding
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
# OS X compatibility
|
|
31
|
+
if(PLATFORM =~ /darwin/) then
|
|
32
|
+
# test if postgresql is probably universal
|
|
33
|
+
bindir = escape_path(IO.popen("pg_config --bindir").readline.chomp)
|
|
34
|
+
Open3.popen3('file',"#{bindir}/pg_config") do |the_in, the_out, the_err|
|
|
35
|
+
filetype = the_out.readline.chomp
|
|
36
|
+
end
|
|
37
|
+
# if it's not universal, ARCHFLAGS should be set
|
|
38
|
+
if((filetype !~ /universal binary/) && ENV['ARCHFLAGS'].nil?) then
|
|
39
|
+
arch_tmp = (IO.popen("uname -p").readline.chomp rescue nil)
|
|
40
|
+
if(arch_tmp == 'powerpc')
|
|
41
|
+
arch = 'ppc'
|
|
42
|
+
else
|
|
43
|
+
arch = 'i386'
|
|
44
|
+
end
|
|
45
|
+
$stderr.write %{
|
|
46
|
+
=========== WARNING ===========
|
|
47
|
+
|
|
48
|
+
You are building this extension on OS X without setting the
|
|
49
|
+
ARCHFLAGS environment variable, and PostgreSQL does not appear
|
|
50
|
+
to have been built as a universal binary. If you are seeing this
|
|
51
|
+
message, that means that the build will probably fail.
|
|
52
|
+
|
|
53
|
+
Try setting the environment variable ARCHFLAGS
|
|
54
|
+
to '-arch #{arch}' before building.
|
|
55
|
+
|
|
56
|
+
For example:
|
|
57
|
+
(in bash) $ export ARCHFLAGS='-arch #{arch}'
|
|
58
|
+
(in tcsh) % setenv ARCHFLAGS '-arch #{arch}'
|
|
59
|
+
|
|
60
|
+
Then try building again.
|
|
61
|
+
|
|
62
|
+
===================================
|
|
63
|
+
}
|
|
64
|
+
# We don't exit here. Who knows? It might build.
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
if RUBY_VERSION < '1.8'
|
|
69
|
+
puts 'This library is for ruby-1.8 or higher.'
|
|
70
|
+
exit 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def escape_path(path)
|
|
74
|
+
if(PLATFORM =~ /mswin|mingw/) then
|
|
75
|
+
'"' + path + '"'
|
|
76
|
+
else
|
|
77
|
+
path.gsub(%r{([^a-zA-Z0-9/._-])}, "\\\\\\1")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def pg_config(type)
|
|
82
|
+
IO.popen("pg_config --#{type}dir").readline.chomp
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def config_value(type)
|
|
86
|
+
escape_path(ENV["POSTGRES_#{type.upcase}"] || pg_config(type))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
Mkrf::Generator.new('pg', '*.c',
|
|
90
|
+
{
|
|
91
|
+
:includes => [config_value('include'), Config::CONFIG['includedir'],
|
|
92
|
+
Config::CONFIG["archdir"], Config::CONFIG['sitelibdir'], "."],
|
|
93
|
+
:library_paths => [config_value('lib')],
|
|
94
|
+
# must set loaded_libs to work around a mkrf bug on some platforms
|
|
95
|
+
:loaded_libs => []
|
|
96
|
+
}
|
|
97
|
+
) do |g|
|
|
98
|
+
|
|
99
|
+
$stdout.write("checking for libpq-fe.h... ")
|
|
100
|
+
if g.include_header('libpq-fe.h') &&
|
|
101
|
+
g.include_header('libpq/libpq-fs.h')
|
|
102
|
+
then
|
|
103
|
+
puts 'yes'
|
|
104
|
+
else
|
|
105
|
+
puts 'no'
|
|
106
|
+
puts 'Could not find PostgreSQL headers: ' +
|
|
107
|
+
'Rakefile not created'
|
|
108
|
+
exit 1
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
$stdout.write("checking for libpq... ")
|
|
112
|
+
# we have to check a few possible names to account
|
|
113
|
+
# for building on windows
|
|
114
|
+
if g.include_library('pq') ||
|
|
115
|
+
g.include_library('libpq') ||
|
|
116
|
+
g.include_library('ms/libpq')
|
|
117
|
+
then
|
|
118
|
+
puts 'yes'
|
|
119
|
+
else
|
|
120
|
+
puts 'no'
|
|
121
|
+
puts 'Could not find PostgreSQL client library: ' +
|
|
122
|
+
'Rakefile not created'
|
|
123
|
+
exit 1
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
$functions.each do |func|
|
|
127
|
+
$stdout.write("checking for #{func}()... ")
|
|
128
|
+
if(g.has_function?(func)) then
|
|
129
|
+
g.add_define("HAVE_#{func.upcase}")
|
|
130
|
+
puts 'yes'
|
|
131
|
+
else
|
|
132
|
+
puts 'no'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
puts "creating Rakefile"
|
|
137
|
+
end
|
|
138
|
+
|
data/ext/pg-m17n3.patch
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
Index: ext/pg.c
|
|
2
|
+
===================================================================
|
|
3
|
+
--- ext/pg.c (revision 199)
|
|
4
|
+
+++ ext/pg.c (working copy)
|
|
5
|
+
@@ -13,6 +13,9 @@
|
|
6
|
+
************************************************/
|
|
7
|
+
|
|
8
|
+
#include "pg.h"
|
|
9
|
+
+#if defined(HAVE_RUBY_ENCODING_H) && HAVE_RUBY_ENCODING_H
|
|
10
|
+
+# define M17N_SUPPORTED
|
|
11
|
+
+#endif
|
|
12
|
+
|
|
13
|
+
#define rb_define_singleton_alias(klass,new,old) rb_define_alias(rb_singleton_class(klass),new,old)
|
|
14
|
+
|
|
15
|
+
@@ -52,9 +55,18 @@
|
|
16
|
+
static VALUE pgresult_clear(VALUE self);
|
|
17
|
+
static VALUE pgresult_aref(VALUE self, VALUE index);
|
|
18
|
+
|
|
19
|
+
+#ifdef M17N_SUPPORTED
|
|
20
|
+
+# define ASSOCIATE_INDEX(obj, index_holder) rb_enc_associate_index((obj), enc_get_index((index_holder)))
|
|
21
|
+
+static rb_encoding * pgconn_get_client_encoding_as_rb_encoding(PGconn* conn);
|
|
22
|
+
+static int enc_get_index(VALUE val);
|
|
23
|
+
+#else
|
|
24
|
+
+# define ASSOCIATE_INDEX(obj, index_holder) /* nothing */
|
|
25
|
+
+#endif
|
|
26
|
+
+
|
|
27
|
+
static PQnoticeReceiver default_notice_receiver = NULL;
|
|
28
|
+
static PQnoticeProcessor default_notice_processor = NULL;
|
|
29
|
+
|
|
30
|
+
+
|
|
31
|
+
/*
|
|
32
|
+
* Used to quote the values passed in a Hash to PGconn.init
|
|
33
|
+
* when building the connection string.
|
|
34
|
+
@@ -115,11 +127,23 @@
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
+#ifdef M17N_SUPPORTED
|
|
39
|
+
static VALUE
|
|
40
|
+
+new_pgresult(PGresult *result, PGconn *conn)
|
|
41
|
+
+{
|
|
42
|
+
+ VALUE val = Data_Wrap_Struct(rb_cPGresult, NULL, free_pgresult, result);
|
|
43
|
+
+ rb_encoding *enc = pgconn_get_client_encoding_as_rb_encoding(conn);
|
|
44
|
+
+ rb_enc_set_index(val, rb_enc_to_index(enc));
|
|
45
|
+
+ return val;
|
|
46
|
+
+}
|
|
47
|
+
+#else
|
|
48
|
+
+static VALUE
|
|
49
|
+
new_pgresult(PGresult *result)
|
|
50
|
+
{
|
|
51
|
+
return Data_Wrap_Struct(rb_cPGresult, NULL, free_pgresult, result);
|
|
52
|
+
}
|
|
53
|
+
+# define new_pgresult(result, conn) new_pgresult((result))
|
|
54
|
+
+#endif
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* Raises appropriate exception if PGresult is
|
|
58
|
+
@@ -923,7 +947,7 @@
|
|
59
|
+
/* If called with no parameters, use PQexec */
|
|
60
|
+
if(NIL_P(params)) {
|
|
61
|
+
result = PQexec(conn, StringValuePtr(command));
|
|
62
|
+
- rb_pgresult = new_pgresult(result);
|
|
63
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
64
|
+
pgresult_check(self, rb_pgresult);
|
|
65
|
+
if (rb_block_given_p()) {
|
|
66
|
+
return rb_ensure(yield_pgresult, rb_pgresult,
|
|
67
|
+
@@ -1007,7 +1031,7 @@
|
|
68
|
+
xfree(paramLengths);
|
|
69
|
+
xfree(paramFormats);
|
|
70
|
+
|
|
71
|
+
- rb_pgresult = new_pgresult(result);
|
|
72
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
73
|
+
pgresult_check(self, rb_pgresult);
|
|
74
|
+
if (rb_block_given_p()) {
|
|
75
|
+
return rb_ensure(yield_pgresult, rb_pgresult,
|
|
76
|
+
@@ -1070,7 +1094,7 @@
|
|
77
|
+
|
|
78
|
+
xfree(paramTypes);
|
|
79
|
+
|
|
80
|
+
- rb_pgresult = new_pgresult(result);
|
|
81
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
82
|
+
pgresult_check(self, rb_pgresult);
|
|
83
|
+
return rb_pgresult;
|
|
84
|
+
}
|
|
85
|
+
@@ -1189,7 +1213,7 @@
|
|
86
|
+
xfree(paramLengths);
|
|
87
|
+
xfree(paramFormats);
|
|
88
|
+
|
|
89
|
+
- rb_pgresult = new_pgresult(result);
|
|
90
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
91
|
+
pgresult_check(self, rb_pgresult);
|
|
92
|
+
if (rb_block_given_p()) {
|
|
93
|
+
return rb_ensure(yield_pgresult, rb_pgresult,
|
|
94
|
+
@@ -1220,7 +1244,7 @@
|
|
95
|
+
stmt = StringValuePtr(stmt_name);
|
|
96
|
+
}
|
|
97
|
+
result = PQdescribePrepared(conn, stmt);
|
|
98
|
+
- rb_pgresult = new_pgresult(result);
|
|
99
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
100
|
+
pgresult_check(self, rb_pgresult);
|
|
101
|
+
return rb_pgresult;
|
|
102
|
+
}
|
|
103
|
+
@@ -1248,7 +1272,7 @@
|
|
104
|
+
stmt = StringValuePtr(stmt_name);
|
|
105
|
+
}
|
|
106
|
+
result = PQdescribePortal(conn, stmt);
|
|
107
|
+
- rb_pgresult = new_pgresult(result);
|
|
108
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
109
|
+
pgresult_check(self, rb_pgresult);
|
|
110
|
+
return rb_pgresult;
|
|
111
|
+
}
|
|
112
|
+
@@ -1276,7 +1300,7 @@
|
|
113
|
+
VALUE rb_pgresult;
|
|
114
|
+
PGconn *conn = get_pgconn(self);
|
|
115
|
+
result = PQmakeEmptyPGresult(conn, NUM2INT(status));
|
|
116
|
+
- rb_pgresult = new_pgresult(result);
|
|
117
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
118
|
+
pgresult_check(self, rb_pgresult);
|
|
119
|
+
return rb_pgresult;
|
|
120
|
+
}
|
|
121
|
+
@@ -1796,7 +1820,7 @@
|
|
122
|
+
result = PQgetResult(conn);
|
|
123
|
+
if(result == NULL)
|
|
124
|
+
return Qnil;
|
|
125
|
+
- rb_pgresult = new_pgresult(result);
|
|
126
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
127
|
+
if (rb_block_given_p()) {
|
|
128
|
+
return rb_ensure(yield_pgresult, rb_pgresult,
|
|
129
|
+
pgresult_clear, rb_pgresult);
|
|
130
|
+
@@ -2317,18 +2341,18 @@
|
|
131
|
+
|
|
132
|
+
if (rb_block_given_p()) {
|
|
133
|
+
result = PQexec(conn, "BEGIN");
|
|
134
|
+
- rb_pgresult = new_pgresult(result);
|
|
135
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
136
|
+
pgresult_check(self, rb_pgresult);
|
|
137
|
+
rb_protect(rb_yield, self, &status);
|
|
138
|
+
if(status == 0) {
|
|
139
|
+
result = PQexec(conn, "COMMIT");
|
|
140
|
+
- rb_pgresult = new_pgresult(result);
|
|
141
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
142
|
+
pgresult_check(self, rb_pgresult);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
/* exception occurred, ROLLBACK and re-raise */
|
|
146
|
+
result = PQexec(conn, "ROLLBACK");
|
|
147
|
+
- rb_pgresult = new_pgresult(result);
|
|
148
|
+
+ rb_pgresult = new_pgresult(result, conn);
|
|
149
|
+
pgresult_check(self, rb_pgresult);
|
|
150
|
+
rb_jump_tag(status);
|
|
151
|
+
}
|
|
152
|
+
@@ -2830,7 +2854,9 @@
|
|
153
|
+
static VALUE
|
|
154
|
+
pgresult_res_status(VALUE self, VALUE status)
|
|
155
|
+
{
|
|
156
|
+
- return rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
|
|
157
|
+
+ VALUE ret = rb_tainted_str_new2(PQresStatus(NUM2INT(status)));
|
|
158
|
+
+ ASSOCIATE_INDEX(ret, self);
|
|
159
|
+
+ return ret;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/*
|
|
163
|
+
@@ -2842,7 +2868,9 @@
|
|
164
|
+
static VALUE
|
|
165
|
+
pgresult_result_error_message(VALUE self)
|
|
166
|
+
{
|
|
167
|
+
- return rb_tainted_str_new2(PQresultErrorMessage(get_pgresult(self)));
|
|
168
|
+
+ VALUE ret = rb_tainted_str_new2(PQresultErrorMessage(get_pgresult(self)));
|
|
169
|
+
+ ASSOCIATE_INDEX(ret, self);
|
|
170
|
+
+ return ret;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
@@ -2870,7 +2898,9 @@
|
|
175
|
+
{
|
|
176
|
+
PGresult *result = get_pgresult(self);
|
|
177
|
+
int fieldcode = NUM2INT(field);
|
|
178
|
+
- return rb_tainted_str_new2(PQresultErrorField(result,fieldcode));
|
|
179
|
+
+ VALUE ret = rb_tainted_str_new2(PQresultErrorField(result,fieldcode));
|
|
180
|
+
+ ASSOCIATE_INDEX(ret, self);
|
|
181
|
+
+ return ret;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
@@ -2920,6 +2950,7 @@
|
|
186
|
+
static VALUE
|
|
187
|
+
pgresult_fname(VALUE self, VALUE index)
|
|
188
|
+
{
|
|
189
|
+
+ VALUE fname;
|
|
190
|
+
PGresult *result;
|
|
191
|
+
int i = NUM2INT(index);
|
|
192
|
+
|
|
193
|
+
@@ -2927,7 +2958,9 @@
|
|
194
|
+
if (i < 0 || i >= PQnfields(result)) {
|
|
195
|
+
rb_raise(rb_eArgError,"invalid field number %d", i);
|
|
196
|
+
}
|
|
197
|
+
- return rb_tainted_str_new2(PQfname(result, i));
|
|
198
|
+
+ fname = rb_tainted_str_new2(PQfname(result, i));
|
|
199
|
+
+ ASSOCIATE_INDEX(fname, self);
|
|
200
|
+
+ return fname;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/*
|
|
204
|
+
@@ -3094,6 +3127,7 @@
|
|
205
|
+
static VALUE
|
|
206
|
+
pgresult_getvalue(VALUE self, VALUE tup_num, VALUE field_num)
|
|
207
|
+
{
|
|
208
|
+
+ VALUE ret;
|
|
209
|
+
PGresult *result;
|
|
210
|
+
int i = NUM2INT(tup_num);
|
|
211
|
+
int j = NUM2INT(field_num);
|
|
212
|
+
@@ -3107,8 +3141,10 @@
|
|
213
|
+
}
|
|
214
|
+
if(PQgetisnull(result, i, j))
|
|
215
|
+
return Qnil;
|
|
216
|
+
- return rb_tainted_str_new(PQgetvalue(result, i, j),
|
|
217
|
+
+ ret = rb_tainted_str_new(PQgetvalue(result, i, j),
|
|
218
|
+
PQgetlength(result, i, j));
|
|
219
|
+
+ ASSOCIATE_INDEX(ret, self);
|
|
220
|
+
+ return ret;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
@@ -3200,7 +3236,9 @@
|
|
225
|
+
static VALUE
|
|
226
|
+
pgresult_cmd_status(VALUE self)
|
|
227
|
+
{
|
|
228
|
+
- return rb_tainted_str_new2(PQcmdStatus(get_pgresult(self)));
|
|
229
|
+
+ VALUE ret = rb_tainted_str_new2(PQcmdStatus(get_pgresult(self)));
|
|
230
|
+
+ ASSOCIATE_INDEX(ret, self);
|
|
231
|
+
+ return ret;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
@@ -3264,12 +3302,14 @@
|
|
236
|
+
tuple = rb_hash_new();
|
|
237
|
+
for(field_num = 0; field_num < PQnfields(result); field_num++) {
|
|
238
|
+
fname = rb_tainted_str_new2(PQfname(result,field_num));
|
|
239
|
+
+ ASSOCIATE_INDEX(fname, self);
|
|
240
|
+
if(PQgetisnull(result, tuple_num, field_num)) {
|
|
241
|
+
rb_hash_aset(tuple, fname, Qnil);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
val = rb_tainted_str_new(PQgetvalue(result, tuple_num, field_num),
|
|
245
|
+
PQgetlength(result, tuple_num, field_num));
|
|
246
|
+
+ ASSOCIATE_INDEX(val, self);
|
|
247
|
+
rb_hash_aset(tuple, fname, val);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
@@ -3311,11 +3351,276 @@
|
|
251
|
+
n = PQnfields(result);
|
|
252
|
+
ary = rb_ary_new2(n);
|
|
253
|
+
for (i=0;i<n;i++) {
|
|
254
|
+
- rb_ary_push(ary, rb_tainted_str_new2(PQfname(result, i)));
|
|
255
|
+
+ VALUE val = rb_tainted_str_new2(PQfname(result, i));
|
|
256
|
+
+ ASSOCIATE_INDEX(val, self);
|
|
257
|
+
+ rb_ary_push(ary, val);
|
|
258
|
+
}
|
|
259
|
+
return ary;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
+#ifdef M17N_SUPPORTED
|
|
263
|
+
+/**
|
|
264
|
+
+ * The mapping from canonical encoding names in PostgreSQL to ones in Ruby.
|
|
265
|
+
+ */
|
|
266
|
+
+static const char * const (enc_pg2ruby_mapping[][2]) = {
|
|
267
|
+
+ {"BIG5", "Big5" },
|
|
268
|
+
+ {"EUC_CN", "GB2312" },
|
|
269
|
+
+ {"EUC_JP", "EUC-JP" },
|
|
270
|
+
+ {"EUC_JIS_2004", "EUC-JP" },
|
|
271
|
+
+ {"EUC_KR", "EUC-KR" },
|
|
272
|
+
+ {"EUC_TW", "EUC-TW" },
|
|
273
|
+
+ {"GB18030", "GB18030" },
|
|
274
|
+
+ {"GBK", "GBK" },
|
|
275
|
+
+ {"ISO_8859_5", "ISO-8859-5" },
|
|
276
|
+
+ {"ISO_8859_6", "ISO-8859-6" },
|
|
277
|
+
+ {"ISO_8859_7", "ISO-8859-7" },
|
|
278
|
+
+ {"ISO_8859_8", "ISO-8859-8" },
|
|
279
|
+
+ /* {"JOHAB", "JOHAB" }, dummy */
|
|
280
|
+
+ {"KOI8", "KOI8-U" },
|
|
281
|
+
+ {"LATIN1", "ISO-8859-1" },
|
|
282
|
+
+ {"LATIN2", "ISO-8859-2" },
|
|
283
|
+
+ {"LATIN3", "ISO-8859-3" },
|
|
284
|
+
+ {"LATIN4", "ISO-8859-4" },
|
|
285
|
+
+ {"LATIN5", "ISO-8859-5" },
|
|
286
|
+
+ {"LATIN6", "ISO-8859-6" },
|
|
287
|
+
+ {"LATIN7", "ISO-8859-7" },
|
|
288
|
+
+ {"LATIN8", "ISO-8859-8" },
|
|
289
|
+
+ {"LATIN9", "ISO-8859-9" },
|
|
290
|
+
+ {"LATIN10", "ISO-8859-10" },
|
|
291
|
+
+ {"MULE_INTERNAL", "Emacs-Mule" },
|
|
292
|
+
+ {"SJIS", "Windows-31J" },
|
|
293
|
+
+ {"SHIFT_JIS_2004","Windows-31J" },
|
|
294
|
+
+ /*{"SQL_ASCII", NULL }, special case*/
|
|
295
|
+
+ {"UHC", "CP949" },
|
|
296
|
+
+ {"UTF8", "UTF-8" },
|
|
297
|
+
+ {"WIN866", "IBM866" },
|
|
298
|
+
+ {"WIN874", "Windows-874" },
|
|
299
|
+
+ {"WIN1250", "Windows-1250"},
|
|
300
|
+
+ {"WIN1251", "Windows-1251"},
|
|
301
|
+
+ {"WIN1252", "Windows-1252"},
|
|
302
|
+
+ {"WIN1253", "Windows-1253"},
|
|
303
|
+
+ {"WIN1254", "Windows-1254"},
|
|
304
|
+
+ {"WIN1255", "Windows-1255"},
|
|
305
|
+
+ {"WIN1256", "Windows-1256"},
|
|
306
|
+
+ {"WIN1257", "Windows-1257"},
|
|
307
|
+
+ {"WIN1258", "Windows-1258"}
|
|
308
|
+
+};
|
|
309
|
+
+
|
|
310
|
+
+
|
|
311
|
+
+/*
|
|
312
|
+
+ * A cache of mapping from PostgreSQL's encoding indices to Ruby's rb_encoding*s.
|
|
313
|
+
+ */
|
|
314
|
+
+static struct st_table *enc_pg2ruby;
|
|
315
|
+
+static ID s_id_index;
|
|
316
|
+
+
|
|
317
|
+
+static int enc_get_index(VALUE val)
|
|
318
|
+
+{
|
|
319
|
+
+ int i = ENCODING_GET_INLINED(val);
|
|
320
|
+
+ if (i == ENCODING_INLINE_MAX) {
|
|
321
|
+
+ VALUE iv = rb_ivar_get(val, s_id_index);
|
|
322
|
+
+ i = NUM2INT(iv);
|
|
323
|
+
+ }
|
|
324
|
+
+ return i;
|
|
325
|
+
+}
|
|
326
|
+
+
|
|
327
|
+
+extern int rb_enc_alias(const char *alias, const char *orig); /* declaration missing in Ruby 1.9.1 */
|
|
328
|
+
+static rb_encoding *
|
|
329
|
+
+find_or_create_johab(void)
|
|
330
|
+
+{
|
|
331
|
+
+ static const char * const aliases[] = { "JOHAB", "Windows-1361", "CP1361" };
|
|
332
|
+
+ int enc_index;
|
|
333
|
+
+ int i;
|
|
334
|
+
+ for (i = 0; i < sizeof(aliases)/sizeof(aliases[0]); ++i) {
|
|
335
|
+
+ enc_index = rb_enc_find_index(aliases[i]);
|
|
336
|
+
+ if (enc_index > 0) return rb_enc_from_index(enc_index);
|
|
337
|
+
+ }
|
|
338
|
+
+
|
|
339
|
+
+ enc_index = rb_define_dummy_encoding(aliases[0]);
|
|
340
|
+
+ for (i = 1; i < sizeof(aliases)/sizeof(aliases[0]); ++i) {
|
|
341
|
+
+ rb_enc_alias(aliases[i], aliases[0]);
|
|
342
|
+
+ }
|
|
343
|
+
+ return rb_enc_from_index(enc_index);
|
|
344
|
+
+}
|
|
345
|
+
+
|
|
346
|
+
+/*
|
|
347
|
+
+ * Returns the client_encoding of the given connection as a rb_encoding*
|
|
348
|
+
+ *
|
|
349
|
+
+ * * returns NULL if the client encoding is 'SQL_ASCII'.
|
|
350
|
+
+ * * returns ASCII-8BIT if the client encoding is unknown.
|
|
351
|
+
+ */
|
|
352
|
+
+static rb_encoding *
|
|
353
|
+
+pgconn_get_client_encoding_as_rb_encoding(PGconn* conn)
|
|
354
|
+
+{
|
|
355
|
+
+ rb_encoding *enc;
|
|
356
|
+
+ int enc_id = PQclientEncoding(conn);
|
|
357
|
+
+
|
|
358
|
+
+ if (st_lookup(enc_pg2ruby, (st_data_t)enc_id, (st_data_t*)&enc)) {
|
|
359
|
+
+ return enc;
|
|
360
|
+
+ }
|
|
361
|
+
+ else {
|
|
362
|
+
+ int i;
|
|
363
|
+
+ const char *name = pg_encoding_to_char(enc_id);
|
|
364
|
+
+ if (strcmp("SQL_ASCII", name) == 0) {
|
|
365
|
+
+ enc = NULL;
|
|
366
|
+
+ goto cache;
|
|
367
|
+
+ }
|
|
368
|
+
+ for (i = 0; i < sizeof(enc_pg2ruby_mapping)/sizeof(enc_pg2ruby_mapping[0]); ++i) {
|
|
369
|
+
+ if (strcmp(name, enc_pg2ruby_mapping[i][0]) == 0) {
|
|
370
|
+
+ enc = rb_enc_find(enc_pg2ruby_mapping[i][1]);
|
|
371
|
+
+ goto cache;
|
|
372
|
+
+ }
|
|
373
|
+
+ }
|
|
374
|
+
+
|
|
375
|
+
+ /* Ruby 1.9.1 does not supoort JOHAB */
|
|
376
|
+
+ if (strcmp(name, "JOHAB") == 0) {
|
|
377
|
+
+ enc = find_or_create_johab();
|
|
378
|
+
+ goto cache;
|
|
379
|
+
+ }
|
|
380
|
+
+
|
|
381
|
+
+ enc = rb_ascii8bit_encoding();
|
|
382
|
+
+ }
|
|
383
|
+
+cache:
|
|
384
|
+
+ st_insert(enc_pg2ruby, (st_data_t)enc_id, (st_data_t)enc);
|
|
385
|
+
+ return enc;
|
|
386
|
+
+}
|
|
387
|
+
+
|
|
388
|
+
+/*
|
|
389
|
+
+ * call-seq:
|
|
390
|
+
+ * conn.internal_encoding() -> Encoding
|
|
391
|
+
+ *
|
|
392
|
+
+ * defined in Ruby 1.9 or later.
|
|
393
|
+
+ *
|
|
394
|
+
+ * Returns:
|
|
395
|
+
+ * * an Encoding - client_encoding of the connection as a Ruby's Encoding object.
|
|
396
|
+
+ * * nil - the client_encoding is 'SQL_ASCII'
|
|
397
|
+
+ */
|
|
398
|
+
+static VALUE
|
|
399
|
+
+pgconn_internal_encoding(VALUE self)
|
|
400
|
+
+{
|
|
401
|
+
+ return rb_enc_from_encoding(pgconn_get_client_encoding_as_rb_encoding(get_pgconn(self)));
|
|
402
|
+
+}
|
|
403
|
+
+
|
|
404
|
+
+static VALUE pgconn_external_encoding(VALUE self);
|
|
405
|
+
+
|
|
406
|
+
+/*
|
|
407
|
+
+ * call-seq:
|
|
408
|
+
+ * conn.internal_encoding = value
|
|
409
|
+
+ *
|
|
410
|
+
+ * A wrapper of +PGconn#set_client_encoding+.
|
|
411
|
+
+ * defined in Ruby 1.9 or later.
|
|
412
|
+
+ *
|
|
413
|
+
+ * +value+ can be one of:
|
|
414
|
+
+ * * an Encoding
|
|
415
|
+
+ * * a String - a name of Encoding
|
|
416
|
+
+ * * +nil+ - sets 'SQL_ASCII' to the client_encoding.
|
|
417
|
+
+ */
|
|
418
|
+
+static VALUE
|
|
419
|
+
+pgconn_internal_encoding_set(VALUE self, VALUE enc)
|
|
420
|
+
+{
|
|
421
|
+
+ if (NIL_P(enc)) {
|
|
422
|
+
+ pgconn_set_client_encoding(self, rb_usascii_str_new_cstr("SQL_ASCII"));
|
|
423
|
+
+ return enc;
|
|
424
|
+
+ }
|
|
425
|
+
+ else if (TYPE(enc) == T_STRING && strcasecmp("JOHAB", RSTRING_PTR(enc)) == 0) {
|
|
426
|
+
+ pgconn_set_client_encoding(self, rb_usascii_str_new_cstr("JOHAB"));
|
|
427
|
+
+ return enc;
|
|
428
|
+
+ }
|
|
429
|
+
+ else {
|
|
430
|
+
+ int i;
|
|
431
|
+
+ const char *name;
|
|
432
|
+
+ name = rb_enc_name(rb_to_encoding(enc));
|
|
433
|
+
+
|
|
434
|
+
+ /* sequential search becuase rarely called */
|
|
435
|
+
+ for (i = 0; i < sizeof(enc_pg2ruby_mapping)/sizeof(enc_pg2ruby_mapping[0]); ++i) {
|
|
436
|
+
+ if (strcmp(name, enc_pg2ruby_mapping[i][1]) == 0) {
|
|
437
|
+
+ if (PQsetClientEncoding(get_pgconn(self), enc_pg2ruby_mapping[i][0]) == -1) {
|
|
438
|
+
+ VALUE server_encoding = pgconn_external_encoding(self);
|
|
439
|
+
+ rb_raise(rb_eEncCompatError, "imcompatible character encodings: %s and %s",
|
|
440
|
+
+ rb_enc_name(rb_to_encoding(server_encoding)),
|
|
441
|
+
+ enc_pg2ruby_mapping[i][0]);
|
|
442
|
+
+ }
|
|
443
|
+
+ return enc;
|
|
444
|
+
+ }
|
|
445
|
+
+ }
|
|
446
|
+
+
|
|
447
|
+
+ /* Ruby 1.9.1 does not support JOHAB */
|
|
448
|
+
+ if (strcasecmp(name, "JOHAB") == 0) {
|
|
449
|
+
+ pgconn_set_client_encoding(self, rb_usascii_str_new_cstr("JOHAB"));
|
|
450
|
+
+ return enc;
|
|
451
|
+
+ }
|
|
452
|
+
+ }
|
|
453
|
+
+
|
|
454
|
+
+ enc = rb_inspect(enc);
|
|
455
|
+
+ rb_raise(rb_ePGError, "unknown encoding: %s", StringValuePtr(enc));
|
|
456
|
+
+}
|
|
457
|
+
+
|
|
458
|
+
+
|
|
459
|
+
+
|
|
460
|
+
+static VALUE enc_server_encoding_getvalue(VALUE pgresult)
|
|
461
|
+
+{
|
|
462
|
+
+ return pgresult_getvalue(pgresult, INT2FIX(0), INT2FIX(0));
|
|
463
|
+
+}
|
|
464
|
+
+
|
|
465
|
+
+/*
|
|
466
|
+
+ * call-seq:
|
|
467
|
+
+ * conn.external_encoding() -> Encoding
|
|
468
|
+
+ *
|
|
469
|
+
+ * defined in Ruby 1.9 or later.
|
|
470
|
+
+ * * Returns the server_encoding of the connected database as a Ruby's Encoding object.
|
|
471
|
+
+ * * Maps 'SQL_ASCII' to ASCII-8BIT.
|
|
472
|
+
+ */
|
|
473
|
+
+static VALUE
|
|
474
|
+
+pgconn_external_encoding(VALUE self)
|
|
475
|
+
+{
|
|
476
|
+
+ VALUE enc;
|
|
477
|
+
+ enc = rb_iv_get(self, "@external_encoding");
|
|
478
|
+
+ if (RTEST(enc)) {
|
|
479
|
+
+ return enc;
|
|
480
|
+
+ }
|
|
481
|
+
+ else {
|
|
482
|
+
+ int i;
|
|
483
|
+
+ VALUE query = rb_usascii_str_new_cstr("SHOW server_encoding");
|
|
484
|
+
+ VALUE pgresult = pgconn_exec(1, &query, self);
|
|
485
|
+
+ VALUE enc_name = rb_ensure(enc_server_encoding_getvalue, pgresult, pgresult_clear, pgresult);
|
|
486
|
+
+
|
|
487
|
+
+ if (strcmp("SQL_ASCII", StringValuePtr(enc_name)) == 0) {
|
|
488
|
+
+ enc = rb_enc_from_encoding(rb_ascii8bit_encoding());
|
|
489
|
+
+ goto cache;
|
|
490
|
+
+ }
|
|
491
|
+
+ for (i = 0; i < sizeof(enc_pg2ruby_mapping)/sizeof(enc_pg2ruby_mapping[0]); ++i) {
|
|
492
|
+
+ if (strcmp(StringValuePtr(enc_name), enc_pg2ruby_mapping[i][0]) == 0) {
|
|
493
|
+
+ enc = rb_enc_from_encoding(rb_enc_find(enc_pg2ruby_mapping[i][1]));
|
|
494
|
+
+ goto cache;
|
|
495
|
+
+ }
|
|
496
|
+
+ }
|
|
497
|
+
+
|
|
498
|
+
+ /* Ruby 1.9.1 does not supoort JOHAB */
|
|
499
|
+
+ if (strcmp(StringValuePtr(enc_name), "JOHAB") == 0) {
|
|
500
|
+
+ enc = rb_enc_from_encoding(find_or_create_johab());
|
|
501
|
+
+ goto cache;
|
|
502
|
+
+ }
|
|
503
|
+
+
|
|
504
|
+
+ /* fallback */
|
|
505
|
+
+ enc = rb_enc_from_encoding(rb_enc_find(StringValuePtr(enc_name)));
|
|
506
|
+
+ }
|
|
507
|
+
+
|
|
508
|
+
+cache:
|
|
509
|
+
+ rb_iv_set(self, "@external_encoding", enc);
|
|
510
|
+
+ return enc;
|
|
511
|
+
+}
|
|
512
|
+
+
|
|
513
|
+
+static void
|
|
514
|
+
+init_m17n(void)
|
|
515
|
+
+{
|
|
516
|
+
+ enc_pg2ruby = st_init_numtable();
|
|
517
|
+
+ s_id_index = rb_intern("@encoding");
|
|
518
|
+
+ rb_define_method(rb_cPGconn, "internal_encoding", pgconn_internal_encoding, 0);
|
|
519
|
+
+ rb_define_method(rb_cPGconn, "internal_encoding=", pgconn_internal_encoding_set, 1);
|
|
520
|
+
+ rb_define_method(rb_cPGconn, "external_encoding", pgconn_external_encoding, 0);
|
|
521
|
+
+}
|
|
522
|
+
+
|
|
523
|
+
+
|
|
524
|
+
+#endif
|
|
525
|
+
/**************************************************************************/
|
|
526
|
+
|
|
527
|
+
void
|
|
528
|
+
@@ -3566,4 +3871,7 @@
|
|
529
|
+
rb_define_method(rb_cPGresult, "each", pgresult_each, 0);
|
|
530
|
+
rb_define_method(rb_cPGresult, "fields", pgresult_fields, 0);
|
|
531
|
+
|
|
532
|
+
+#ifdef M17N_SUPPORTED
|
|
533
|
+
+ init_m17n();
|
|
534
|
+
+#endif
|
|
535
|
+
}
|