pg-jdguyot 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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
+