pg-ct 0.10.0

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/Rakefile.local ADDED
@@ -0,0 +1,298 @@
1
+ #!rake
2
+
3
+ require 'uri'
4
+ require 'tempfile'
5
+ require 'rbconfig'
6
+
7
+ require 'rake/clean'
8
+ require 'rake/extensiontask'
9
+ require 'rake/extensioncompiler'
10
+
11
+ MISCDIR = BASEDIR + 'misc'
12
+
13
+ EXT_MAKEFILE = EXTDIR + 'Makefile'
14
+ EXT_SOURCES = FileList[ EXTDIR + '*.c' ]
15
+ EXT_SO = EXTDIR + "pg_ext.#{CONFIG['DLEXT']}"
16
+
17
+ NUM_CPUS = if File.exist?('/proc/cpuinfo')
18
+ File.read('/proc/cpuinfo').scan('processor').length
19
+ elsif RUBY_PLATFORM.include?( 'darwin' )
20
+ `system_profiler SPHardwareDataType | grep 'Cores' | awk '{print $5}'`.chomp
21
+ else
22
+ 1
23
+ end
24
+
25
+ # Cross-compilation constants
26
+ OPENSSL_VERSION = ENV['OPENSSL_VERSION'] || '1.0.0a'
27
+ POSTGRESQL_VERSION = ENV['POSTGRESQL_VERSION'] || '9.0.1'
28
+
29
+ COMPILE_HOME = Pathname( "~/.rake-compiler" ).expand_path
30
+ STATIC_SOURCESDIR = COMPILE_HOME + 'sources'
31
+ STATIC_BUILDDIR = COMPILE_HOME + 'builds'
32
+
33
+ # Static OpenSSL build vars
34
+ STATIC_OPENSSL_BUILDDIR = STATIC_BUILDDIR + "openssl-#{OPENSSL_VERSION}"
35
+
36
+ OPENSSL_SOURCE_URI =
37
+ URI( "http://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz" )
38
+ OPENSSL_TARBALL = STATIC_SOURCESDIR + File.basename( OPENSSL_SOURCE_URI.path )
39
+ OPENSSL_MAKEFILE = STATIC_OPENSSL_BUILDDIR + 'Makefile'
40
+
41
+ LIBSSLEAY32 = STATIC_OPENSSL_BUILDDIR + 'libssleay32.a'
42
+ LIBEAY32 = STATIC_OPENSSL_BUILDDIR + 'libeay32.a'
43
+
44
+ OPENSSL_PATCHES = Rake::FileList[ MISCDIR + "openssl-#{OPENSSL_VERSION}.*.patch" ]
45
+
46
+ # Static PostgreSQL build vars
47
+ STATIC_POSTGRESQL_BUILDDIR = STATIC_BUILDDIR + "postgresql-#{POSTGRESQL_VERSION}"
48
+ POSTGRESQL_SOURCE_URI = begin
49
+ uristring = "http://ftp9.us.postgresql.org/pub/mirrors/postgresql/source/" +
50
+ "v%s/postgresql-%s.tar.gz" % [ POSTGRESQL_VERSION, POSTGRESQL_VERSION ]
51
+ URI( uristring )
52
+ end
53
+ POSTGRESQL_TARBALL = STATIC_SOURCESDIR + File.basename( POSTGRESQL_SOURCE_URI.path )
54
+
55
+ STATIC_POSTGRESQL_SRCDIR = STATIC_POSTGRESQL_BUILDDIR + 'src'
56
+ STATIC_POSTGRESQL_LIBDIR = STATIC_POSTGRESQL_SRCDIR + 'interfaces/libpq'
57
+ STATIC_POSTGRESQL_INCDIR = STATIC_POSTGRESQL_SRCDIR + 'include'
58
+
59
+ POSTGRESQL_GLOBAL_MAKEFILE = STATIC_POSTGRESQL_SRCDIR + 'Makefile.global'
60
+ POSTGRESQL_SHLIB_MAKEFILE = STATIC_POSTGRESQL_SRCDIR + 'Makefile.shlib'
61
+ POSTGRESQL_SHLIB_MF_ORIG = STATIC_POSTGRESQL_SRCDIR + 'Makefile.shlib.orig'
62
+ POSTGRESQL_LIB = STATIC_POSTGRESQL_LIBDIR + 'libpq.a'
63
+
64
+ CROSS_PREFIX = if RUBY_PLATFORM.include?( 'darwin' )
65
+ 'i386-mingw32'
66
+ else
67
+ 'i586-mingw32msvc'
68
+ end
69
+
70
+ # Make sure the spec data is packaged up with the gem
71
+ SPEC_DATA = Rake::FileList[ SPECDIR + 'data/*' ]
72
+ GEMSPEC.test_files += SPEC_DATA.to_a
73
+
74
+ # Clean up any testing database directories
75
+ TESTING_TMPDIRS = Rake::FileList[ "#{BASEDIR}/tmp_test_*" ]
76
+ CLOBBER.include( STATIC_SOURCESDIR.to_s, *TESTING_TMPDIRS )
77
+
78
+ # clean intermediate files and folders
79
+ CLEAN.include( STATIC_BUILDDIR.to_s )
80
+
81
+
82
+ #####################################################################
83
+ ### T A S K S
84
+ #####################################################################
85
+
86
+ # Make both the default task and the spec task depend on building the extension
87
+ task :local => :compile
88
+ task :spec => :compile
89
+ namespace :spec do
90
+ task :doc => [ :compile ]
91
+ task :quiet => [ :compile ]
92
+ task :html => [ :compile ]
93
+ task :text => [ :compile ]
94
+ end
95
+
96
+ ENV['RUBY_CC_VERSION'] = '1.8.6:1.9.2'
97
+
98
+ Rake::ExtensionTask.new do |ext|
99
+ ext.name = 'pg_ext'
100
+ ext.gem_spec = GEMSPEC
101
+ ext.ext_dir = EXTDIR.to_s
102
+ ext.lib_dir = LIBDIR.to_s
103
+ ext.source_pattern = "*.{c,h}"
104
+
105
+ # If there's an explicit 'compile' argument, use everything after it as options.
106
+ if offset = ARGV.index( 'compile' )
107
+ trace "config options = %p" % [ ARGV[(offset + 1)..-1] ]
108
+ ext.config_options = ARGV[ (offset + 1)..-1 ]
109
+ # Otherwise, just grab everything from the first option onward
110
+ elsif offset = ARGV.index( ARGV.find {|arg| arg =~ /^--/ } )
111
+ trace "config options = %p" % [ ARGV[offset..-1] ]
112
+ ext.config_options = ARGV[ offset..-1 ]
113
+ else
114
+ trace "No config options (ARGV = %p)" % [ ARGV ]
115
+ end
116
+
117
+ ext.cross_compile = true
118
+ ext.cross_platform = %w[i386-mswin32 i386-mingw32]
119
+
120
+ # configure options only for cross compile
121
+ ext.cross_config_options += [
122
+ "--with-pg-include=#{STATIC_POSTGRESQL_LIBDIR}",
123
+ "--with-opt-include=#{STATIC_POSTGRESQL_INCDIR}",
124
+ "--with-pg-lib=#{STATIC_POSTGRESQL_LIBDIR}",
125
+ "--with-opt-lib=#{STATIC_OPENSSL_BUILDDIR}",
126
+ "--enable-static-build",
127
+ ]
128
+
129
+ end
130
+
131
+ task :cross do
132
+ ENV['CROSS_COMPILING'] = 'yes'
133
+ end
134
+
135
+
136
+ desc "Stop any Postmaster instances that remain after testing."
137
+ task :cleanup_testing_dbs do
138
+ require 'spec/lib/helpers'
139
+ PgTestingHelpers.stop_existing_postmasters()
140
+ Rake::Task[:clean].invoke
141
+ end
142
+
143
+
144
+ #####################################################################
145
+ ### C R O S S - C O M P I L A T I O N - T A S K S
146
+ #####################################################################
147
+
148
+
149
+ directory STATIC_SOURCESDIR.to_s
150
+
151
+ #
152
+ # Static OpenSSL build tasks
153
+ #
154
+ directory STATIC_OPENSSL_BUILDDIR.to_s
155
+
156
+ # openssl source file should be stored there
157
+ file OPENSSL_TARBALL => STATIC_SOURCESDIR do |t|
158
+ download( OPENSSL_SOURCE_URI, t.name )
159
+ end
160
+
161
+ # Extract the openssl builds
162
+ file STATIC_OPENSSL_BUILDDIR => OPENSSL_TARBALL do |t|
163
+ trace "extracting %s to %s" % [ OPENSSL_TARBALL, STATIC_OPENSSL_BUILDDIR.parent ]
164
+ STATIC_OPENSSL_BUILDDIR.mkpath
165
+ run 'tar', '-xzf', OPENSSL_TARBALL.to_s, '-C', STATIC_OPENSSL_BUILDDIR.parent.to_s
166
+ OPENSSL_MAKEFILE.unlink if OPENSSL_MAKEFILE.exist?
167
+
168
+ OPENSSL_PATCHES.each do |patchfile|
169
+ trace " applying patch #{patchfile}..."
170
+ run 'patch', '-Np1', '-d', STATIC_OPENSSL_BUILDDIR.to_s,
171
+ '-i', File.expand_path( patchfile, BASEDIR )
172
+ end
173
+ end
174
+
175
+ CMD_PRELUDE = [
176
+ 'env',
177
+ "CC=#{CROSS_PREFIX}-gcc",
178
+ "CFLAGS=-DDSO_WIN32",
179
+ "AR=#{CROSS_PREFIX}-ar",
180
+ "RANLIB=#{CROSS_PREFIX}-ranlib"
181
+ ]
182
+
183
+
184
+ # generate the makefile in a clean build location
185
+ file OPENSSL_MAKEFILE => STATIC_OPENSSL_BUILDDIR do |t|
186
+ Dir.chdir( STATIC_OPENSSL_BUILDDIR ) do
187
+ cmd = CMD_PRELUDE.dup
188
+ cmd << "./Configure" << 'mingw'
189
+
190
+ run( *cmd )
191
+ end
192
+ end
193
+
194
+ desc "compile static openssl libraries"
195
+ task :openssl_libs => [ LIBSSLEAY32, LIBEAY32 ]
196
+
197
+ task :compile_static_openssl => OPENSSL_MAKEFILE do |t|
198
+ Dir.chdir( STATIC_OPENSSL_BUILDDIR ) do
199
+ cmd = CMD_PRELUDE.dup
200
+ cmd << 'make' << "-j#{NUM_CPUS}" << 'build_libs'
201
+
202
+ run( *cmd )
203
+ end
204
+ end
205
+
206
+ desc "compile static #{LIBEAY32}"
207
+ file LIBEAY32 => :compile_static_openssl do |t|
208
+ FileUtils.cp( STATIC_OPENSSL_BUILDDIR + 'libcrypto.a', LIBEAY32.to_s )
209
+ end
210
+
211
+ desc "compile static #{LIBSSLEAY32}"
212
+ file LIBSSLEAY32 => :compile_static_openssl do |t|
213
+ FileUtils.cp( STATIC_OPENSSL_BUILDDIR + 'libssl.a', LIBSSLEAY32.to_s )
214
+ end
215
+
216
+
217
+
218
+ #
219
+ # Static PostgreSQL build tasks
220
+ #
221
+ directory STATIC_POSTGRESQL_BUILDDIR.to_s
222
+
223
+
224
+ # postgresql source file should be stored there
225
+ file POSTGRESQL_TARBALL => STATIC_SOURCESDIR do |t|
226
+ download( POSTGRESQL_SOURCE_URI, t.name )
227
+ end
228
+
229
+ # Extract the postgresql sources
230
+ file STATIC_POSTGRESQL_BUILDDIR => POSTGRESQL_TARBALL do |t|
231
+ trace "extracting %s to %s" % [ POSTGRESQL_TARBALL, STATIC_POSTGRESQL_BUILDDIR.parent ]
232
+ STATIC_POSTGRESQL_BUILDDIR.mkpath
233
+ run 'tar', '-xzf', POSTGRESQL_TARBALL.to_s, '-C', STATIC_POSTGRESQL_BUILDDIR.parent.to_s
234
+ mv POSTGRESQL_SHLIB_MAKEFILE, POSTGRESQL_SHLIB_MF_ORIG
235
+ end
236
+
237
+ # generate the makefile in a clean build location
238
+ file POSTGRESQL_GLOBAL_MAKEFILE => [ STATIC_POSTGRESQL_BUILDDIR, :openssl_libs ] do |t|
239
+ options = [
240
+ '--target=i386-mingw32',
241
+ "--host=#{Rake::ExtensionCompiler.mingw_host}",
242
+ '--with-openssl',
243
+ '--without-zlib',
244
+ '--disable-shared',
245
+ ]
246
+
247
+ Dir.chdir( STATIC_POSTGRESQL_BUILDDIR ) do
248
+ configure_path = STATIC_POSTGRESQL_BUILDDIR + 'configure'
249
+ cmd = [ configure_path.to_s, *options ]
250
+ cmd << "CFLAGS=-L#{STATIC_OPENSSL_BUILDDIR}"
251
+ cmd << "LDFLAGS=-L#{STATIC_OPENSSL_BUILDDIR}"
252
+ cmd << "LDFLAGS_SL=-L#{STATIC_OPENSSL_BUILDDIR}"
253
+ cmd << "LIBS=-lwsock32 -lgdi32"
254
+ cmd << "CPPFLAGS=-I#{STATIC_OPENSSL_BUILDDIR}/include"
255
+
256
+ run( *cmd )
257
+ end
258
+ end
259
+
260
+
261
+ # patch the Makefile.shlib -- depend on the build dir so it's only
262
+ # rewritten if the tarball is re-extracted.
263
+ file POSTGRESQL_SHLIB_MAKEFILE => POSTGRESQL_SHLIB_MF_ORIG do |t|
264
+ tf = Tempfile.new( POSTGRESQL_SHLIB_MAKEFILE.basename )
265
+ POSTGRESQL_SHLIB_MF_ORIG.open( File::RDONLY ) do |ifh|
266
+ ifh.each_line do |line|
267
+ tf.print( line.sub(/^(\s*haslibarule\s*=\s*yes)/, "# \\1 ") )
268
+ end
269
+ end
270
+ tf.close
271
+
272
+ FileUtils.mv( tf.path, t.name, :verbose => $trace )
273
+ end
274
+
275
+
276
+ # make libpq.a
277
+ file POSTGRESQL_LIB => [ POSTGRESQL_GLOBAL_MAKEFILE, POSTGRESQL_SHLIB_MAKEFILE ] do |t|
278
+ Dir.chdir( POSTGRESQL_LIB.dirname ) do
279
+ sh 'make', "-j#{NUM_CPUS}", POSTGRESQL_LIB.basename.to_s, 'PORTNAME=win32'
280
+ end
281
+ end
282
+
283
+
284
+ #desc 'compile static libpg.a'
285
+ task :static_libpq => POSTGRESQL_LIB
286
+
287
+ desc 'cross compile pg for win32'
288
+ task :cross => [ :mingw32, :static_libpq ]
289
+
290
+ task :mingw32 do
291
+ # Use Rake::ExtensionCompiler helpers to find the proper host
292
+ unless Rake::ExtensionCompiler.mingw_host then
293
+ warn "You need to install mingw32 cross compile functionality to be able to continue."
294
+ warn "Please refer to your distribution/package manager documentation about installation."
295
+ fail
296
+ end
297
+ end
298
+
data/ext/compat.c ADDED
@@ -0,0 +1,541 @@
1
+ /************************************************
2
+
3
+ compat.c -
4
+
5
+ Author: matz
6
+ created at: Tue May 13 20:07:35 JST 1997
7
+
8
+ Author: ematsu
9
+ modified at: Wed Jan 20 16:41:51 1999
10
+
11
+ $Author$
12
+ $Date$
13
+ ************************************************/
14
+
15
+ #include <ctype.h>
16
+ #include "compat.h"
17
+
18
+ #ifdef PG_BEFORE_080300
19
+ int
20
+ PQconnectionNeedsPassword(PGconn *conn)
21
+ {
22
+ rb_raise(rb_eStandardError,
23
+ "PQconnectionNeedsPassword not supported by this client version.");
24
+ }
25
+
26
+ int
27
+ PQconnectionUsedPassword(PGconn *conn)
28
+ {
29
+ rb_raise(rb_eStandardError,
30
+ "PQconnectionUsedPassword not supported by this client version.");
31
+ }
32
+
33
+ int
34
+ lo_truncate(PGconn *conn, int fd, size_t len)
35
+ {
36
+ rb_raise(rb_eStandardError, "lo_truncate not supported by this client version.");
37
+ }
38
+ #endif /* PG_BEFORE_080300 */
39
+
40
+ #ifdef PG_BEFORE_080200
41
+ int
42
+ PQisthreadsafe()
43
+ {
44
+ return Qfalse;
45
+ }
46
+
47
+ int
48
+ PQnparams(const PGresult *res)
49
+ {
50
+ rb_raise(rb_eStandardError, "PQnparams not supported by this client version.");
51
+ }
52
+
53
+ Oid
54
+ PQparamtype(const PGresult *res, int param_number)
55
+ {
56
+ rb_raise(rb_eStandardError, "PQparamtype not supported by this client version.");
57
+ }
58
+
59
+ PGresult *
60
+ PQdescribePrepared(PGconn *conn, const char *stmtName)
61
+ {
62
+ rb_raise(rb_eStandardError, "PQdescribePrepared not supported by this client version.");
63
+ }
64
+
65
+ PGresult *
66
+ PQdescribePortal(PGconn *conn, const char *portalName)
67
+ {
68
+ rb_raise(rb_eStandardError, "PQdescribePortal not supported by this client version.");
69
+ }
70
+
71
+ int
72
+ PQsendDescribePrepared(PGconn *conn, const char *stmtName)
73
+ {
74
+ rb_raise(rb_eStandardError, "PQsendDescribePrepared not supported by this client version.");
75
+ }
76
+
77
+ int
78
+ PQsendDescribePortal(PGconn *conn, const char *portalName)
79
+ {
80
+ rb_raise(rb_eStandardError, "PQsendDescribePortal not supported by this client version.");
81
+ }
82
+
83
+ char *
84
+ PQencryptPassword(const char *passwd, const char *user)
85
+ {
86
+ rb_raise(rb_eStandardError, "PQencryptPassword not supported by this client version.");
87
+ }
88
+ #endif /* PG_BEFORE_080200 */
89
+
90
+ #ifdef PG_BEFORE_080100
91
+ Oid
92
+ lo_create(PGconn *conn, Oid lobjId)
93
+ {
94
+ rb_raise(rb_eStandardError, "lo_create not supported by this client version.");
95
+ }
96
+ #endif /* PG_BEFORE_080100 */
97
+
98
+ #ifdef PG_BEFORE_080000
99
+ PGresult *
100
+ PQprepare(PGconn *conn, const char *stmtName, const char *query,
101
+ int nParams, const Oid *paramTypes)
102
+ {
103
+ rb_raise(rb_eStandardError, "PQprepare not supported by this client version.");
104
+ }
105
+
106
+ int
107
+ PQsendPrepare(PGconn *conn, const char *stmtName, const char *query,
108
+ int nParams, const Oid *paramTypes)
109
+ {
110
+ rb_raise(rb_eStandardError, "PQsendPrepare not supported by this client version.");
111
+ }
112
+
113
+ int
114
+ PQserverVersion(const PGconn* conn)
115
+ {
116
+ rb_raise(rb_eStandardError, "PQserverVersion not supported by this client version.");
117
+ }
118
+ #endif /* PG_BEFORE_080000 */
119
+
120
+ #ifdef PG_BEFORE_070400
121
+ PGresult *
122
+ PQexecParams(PGconn *conn, const char *command, int nParams,
123
+ const Oid *paramTypes, const char * const * paramValues, const int *paramLengths,
124
+ const int *paramFormats, int resultFormat)
125
+ {
126
+ rb_raise(rb_eStandardError, "PQexecParams not supported by this client version.");
127
+ }
128
+
129
+ PGTransactionStatusType
130
+ PQtransactionStatus(const PGconn *conn)
131
+ {
132
+ rb_raise(rb_eStandardError, "PQtransactionStatus not supported by this client version.");
133
+ }
134
+
135
+ char *
136
+ PQparameterStatus(const PGconn *conn, const char *paramName)
137
+ {
138
+ rb_raise(rb_eStandardError, "PQparameterStatus not supported by this client version.");
139
+ }
140
+
141
+ int
142
+ PQprotocolVersion(const PGconn *conn)
143
+ {
144
+ rb_raise(rb_eStandardError, "PQprotocolVersion not supported by this client version.");
145
+ }
146
+
147
+ PGresult
148
+ *PQexecPrepared(PGconn *conn, const char *stmtName, int nParams,
149
+ const char * const *ParamValues, const int *paramLengths, const int *paramFormats,
150
+ int resultFormat)
151
+ {
152
+ rb_raise(rb_eStandardError, "PQexecPrepared not supported by this client version.");
153
+ }
154
+
155
+ int
156
+ PQsendQueryParams(PGconn *conn, const char *command, int nParams,
157
+ const Oid *paramTypes, const char * const * paramValues, const int *paramLengths,
158
+ const int *paramFormats, int resultFormat)
159
+ {
160
+ rb_raise(rb_eStandardError, "PQsendQueryParams not supported by this client version.");
161
+ }
162
+
163
+ int
164
+ PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams,
165
+ const char * const *ParamValues, const int *paramLengths, const int *paramFormats,
166
+ int resultFormat)
167
+ {
168
+ rb_raise(rb_eStandardError, "PQsendQueryPrepared not supported by this client version.");
169
+ }
170
+
171
+ int
172
+ PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
173
+ {
174
+ rb_raise(rb_eStandardError, "PQputCopyData not supported by this client version.");
175
+ }
176
+
177
+ int
178
+ PQputCopyEnd(PGconn *conn, const char *errormsg)
179
+ {
180
+ rb_raise(rb_eStandardError, "PQputCopyEnd not supported by this client version.");
181
+ }
182
+
183
+ int
184
+ PQgetCopyData(PGconn *conn, char **buffer, int async)
185
+ {
186
+ rb_raise(rb_eStandardError, "PQgetCopyData not supported by this client version.");
187
+ }
188
+
189
+ PGVerbosity
190
+ PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
191
+ {
192
+ rb_raise(rb_eStandardError, "PQsetErrorVerbosity not supported by this client version.");
193
+ }
194
+
195
+ Oid
196
+ PQftable(const PGresult *res, int column_number)
197
+ {
198
+ rb_raise(rb_eStandardError, "PQftable not supported by this client version.");
199
+ }
200
+
201
+ int
202
+ PQftablecol(const PGresult *res, int column_number)
203
+ {
204
+ rb_raise(rb_eStandardError, "PQftablecol not supported by this client version.");
205
+ }
206
+
207
+ int
208
+ PQfformat(const PGresult *res, int column_number)
209
+ {
210
+ rb_raise(rb_eStandardError, "PQfformat not supported by this client version.");
211
+ }
212
+
213
+ PQnoticeReceiver
214
+ PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
215
+ {
216
+ rb_raise(rb_eStandardError, "PQsetNoticeReceiver not supported by this client version.");
217
+ }
218
+
219
+ char *
220
+ PQresultErrorField(const PGresult *res, int fieldcode)
221
+ {
222
+ rb_raise(rb_eStandardError, "PQresultErrorField not supported by this client version.");
223
+ }
224
+ #endif /* PG_BEFORE_070400 */
225
+
226
+ #ifdef PG_BEFORE_070300
227
+ size_t
228
+ PQescapeStringConn(PGconn *conn, char *to, const char *from,
229
+ size_t length, int *error)
230
+ {
231
+ return PQescapeString(to,from,length);
232
+ }
233
+
234
+ unsigned char *
235
+ PQescapeByteaConn(PGconn *conn, const unsigned char *from,
236
+ size_t from_length, size_t *to_length)
237
+ {
238
+ return PQescapeBytea(from, from_length, to_length);
239
+ }
240
+ #endif /* PG_BEFORE_070300 */
241
+
242
+
243
+ /**************************************************************************
244
+
245
+ IF ANY CODE IS COPIED FROM POSTGRESQL, PLACE IT AFTER THIS COMMENT.
246
+
247
+ ***************************************************************************
248
+
249
+ Portions of code after this point were copied from the PostgreSQL source
250
+ distribution, available at http://www.postgresql.org
251
+
252
+ ***************************************************************************
253
+
254
+ Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
255
+
256
+ Portions Copyright (c) 1994, The Regents of the University of California
257
+
258
+ Permission to use, copy, modify, and distribute this software and its
259
+ documentation for any purpose, without fee, and without a written agreement
260
+ is hereby granted, provided that the above copyright notice and this
261
+ paragraph and the following two paragraphs appear in all copies.
262
+
263
+ IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
264
+ DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
265
+ LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
266
+ DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
267
+ POSSIBILITY OF SUCH DAMAGE.
268
+
269
+ THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
270
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
271
+ AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
272
+ ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
273
+ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
274
+
275
+ **************************************************************************/
276
+
277
+ #ifndef HAVE_PQSETCLIENTENCODING
278
+ int
279
+ PQsetClientEncoding(PGconn *conn, const char *encoding)
280
+ {
281
+ char qbuf[128];
282
+ static const char query[] = "set client_encoding to '%s'";
283
+ PGresult *res;
284
+ int status;
285
+
286
+ if (!conn || PQstatus(conn) != CONNECTION_OK)
287
+ return -1;
288
+
289
+ if (!encoding)
290
+ return -1;
291
+
292
+ /* check query buffer overflow */
293
+ if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
294
+ return -1;
295
+
296
+ /* ok, now send a query */
297
+ sprintf(qbuf, query, encoding);
298
+ res = PQexec(conn, qbuf);
299
+
300
+ if (res == NULL)
301
+ return -1;
302
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
303
+ status = -1;
304
+ else
305
+ {
306
+ /*
307
+ * In protocol 2 we have to assume the setting will stick, and adjust
308
+ * our state immediately. In protocol 3 and up we can rely on the
309
+ * backend to report the parameter value, and we'll change state at
310
+ * that time.
311
+ */
312
+ if (PQprotocolVersion(conn) < 3)
313
+ pqSaveParameterStatus(conn, "client_encoding", encoding);
314
+ status = 0; /* everything is ok */
315
+ }
316
+ PQclear(res);
317
+ return status;
318
+ }
319
+ #endif /* HAVE_PQSETCLIENTENCODING */
320
+
321
+ #ifndef HAVE_PQESCAPESTRING
322
+ /*
323
+ * Escaping arbitrary strings to get valid SQL literal strings.
324
+ *
325
+ * Replaces "\\" with "\\\\" and "'" with "''".
326
+ *
327
+ * length is the length of the source string. (Note: if a terminating NUL
328
+ * is encountered sooner, PQescapeString stops short of "length"; the behavior
329
+ * is thus rather like strncpy.)
330
+ *
331
+ * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
332
+ * A terminating NUL character is added to the output string, whether the
333
+ * input is NUL-terminated or not.
334
+ *
335
+ * Returns the actual length of the output (not counting the terminating NUL).
336
+ */
337
+ size_t
338
+ PQescapeString(char *to, const char *from, size_t length)
339
+ {
340
+ const char *source = from;
341
+ char *target = to;
342
+ size_t remaining = length;
343
+
344
+ while (remaining > 0 && *source != '\0')
345
+ {
346
+ switch (*source)
347
+ {
348
+ case '\\':
349
+ *target++ = '\\';
350
+ *target++ = '\\';
351
+ break;
352
+
353
+ case '\'':
354
+ *target++ = '\'';
355
+ *target++ = '\'';
356
+ break;
357
+
358
+ default:
359
+ *target++ = *source;
360
+ break;
361
+ }
362
+ source++;
363
+ remaining--;
364
+ }
365
+
366
+ /* Write the terminating NUL character. */
367
+ *target = '\0';
368
+
369
+ return target - to;
370
+ }
371
+
372
+ /*
373
+ * PQescapeBytea - converts from binary string to the
374
+ * minimal encoding necessary to include the string in an SQL
375
+ * INSERT statement with a bytea type column as the target.
376
+ *
377
+ * The following transformations are applied
378
+ * '\0' == ASCII 0 == \\000
379
+ * '\'' == ASCII 39 == \'
380
+ * '\\' == ASCII 92 == \\\\
381
+ * anything < 0x20, or > 0x7e ---> \\ooo
382
+ * (where ooo is an octal expression)
383
+ */
384
+ unsigned char *
385
+ PQescapeBytea(const unsigned char *bintext, size_t binlen, size_t *bytealen)
386
+ {
387
+ const unsigned char *vp;
388
+ unsigned char *rp;
389
+ unsigned char *result;
390
+ size_t i;
391
+ size_t len;
392
+
393
+ /*
394
+ * empty string has 1 char ('\0')
395
+ */
396
+ len = 1;
397
+
398
+ vp = bintext;
399
+ for (i = binlen; i > 0; i--, vp++)
400
+ {
401
+ if (*vp < 0x20 || *vp > 0x7e)
402
+ len += 5; /* '5' is for '\\ooo' */
403
+ else if (*vp == '\'')
404
+ len += 2;
405
+ else if (*vp == '\\')
406
+ len += 4;
407
+ else
408
+ len++;
409
+ }
410
+
411
+ rp = result = (unsigned char *) malloc(len);
412
+ if (rp == NULL)
413
+ return NULL;
414
+
415
+ vp = bintext;
416
+ *bytealen = len;
417
+
418
+ for (i = binlen; i > 0; i--, vp++)
419
+ {
420
+ if (*vp < 0x20 || *vp > 0x7e)
421
+ {
422
+ (void) sprintf(rp, "\\\\%03o", *vp);
423
+ rp += 5;
424
+ }
425
+ else if (*vp == '\'')
426
+ {
427
+ rp[0] = '\\';
428
+ rp[1] = '\'';
429
+ rp += 2;
430
+ }
431
+ else if (*vp == '\\')
432
+ {
433
+ rp[0] = '\\';
434
+ rp[1] = '\\';
435
+ rp[2] = '\\';
436
+ rp[3] = '\\';
437
+ rp += 4;
438
+ }
439
+ else
440
+ *rp++ = *vp;
441
+ }
442
+ *rp = '\0';
443
+
444
+ return result;
445
+ }
446
+
447
+ #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
448
+ #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
449
+ #define OCTVAL(CH) ((CH) - '0')
450
+
451
+ /*
452
+ * PQunescapeBytea - converts the null terminated string representation
453
+ * of a bytea, strtext, into binary, filling a buffer. It returns a
454
+ * pointer to the buffer (or NULL on error), and the size of the
455
+ * buffer in retbuflen. The pointer may subsequently be used as an
456
+ * argument to the function free(3). It is the reverse of PQescapeBytea.
457
+ *
458
+ * The following transformations are made:
459
+ * \\ == ASCII 92 == \
460
+ * \ooo == a byte whose value = ooo (ooo is an octal number)
461
+ * \x == x (x is any character not matched by the above transformations)
462
+ */
463
+ unsigned char *
464
+ PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
465
+ {
466
+ size_t strtextlen,
467
+ buflen;
468
+ unsigned char *buffer,
469
+ *tmpbuf;
470
+ size_t i,
471
+ j;
472
+
473
+ if (strtext == NULL)
474
+ return NULL;
475
+
476
+ strtextlen = strlen(strtext);
477
+
478
+ /*
479
+ * Length of input is max length of output, but add one to avoid
480
+ * unportable malloc(0) if input is zero-length.
481
+ */
482
+ buffer = (unsigned char *) malloc(strtextlen + 1);
483
+ if (buffer == NULL)
484
+ return NULL;
485
+
486
+ for (i = j = 0; i < strtextlen;)
487
+ {
488
+ switch (strtext[i])
489
+ {
490
+ case '\\':
491
+ i++;
492
+ if (strtext[i] == '\\')
493
+ buffer[j++] = strtext[i++];
494
+ else
495
+ {
496
+ if ((ISFIRSTOCTDIGIT(strtext[i])) &&
497
+ (ISOCTDIGIT(strtext[i + 1])) &&
498
+ (ISOCTDIGIT(strtext[i + 2])))
499
+ {
500
+ int byte;
501
+
502
+ byte = OCTVAL(strtext[i++]);
503
+ byte = (byte << 3) + OCTVAL(strtext[i++]);
504
+ byte = (byte << 3) + OCTVAL(strtext[i++]);
505
+ buffer[j++] = byte;
506
+ }
507
+ }
508
+
509
+ /*
510
+ * Note: if we see '\' followed by something that isn't a
511
+ * recognized escape sequence, we loop around having done
512
+ * nothing except advance i. Therefore the something will
513
+ * be emitted as ordinary data on the next cycle. Corner
514
+ * case: '\' at end of string will just be discarded.
515
+ */
516
+ break;
517
+
518
+ default:
519
+ buffer[j++] = strtext[i++];
520
+ break;
521
+ }
522
+ }
523
+ buflen = j; /* buflen is the length of the dequoted
524
+ * data */
525
+
526
+ /* Shrink the buffer to be no larger than necessary */
527
+ /* +1 avoids unportable behavior when buflen==0 */
528
+ tmpbuf = realloc(buffer, buflen + 1);
529
+
530
+ /* It would only be a very brain-dead realloc that could fail, but... */
531
+ if (!tmpbuf)
532
+ {
533
+ free(buffer);
534
+ return NULL;
535
+ }
536
+
537
+ *retbuflen = buflen;
538
+ return tmpbuf;
539
+ }
540
+ #endif
541
+