pg 0.8.0 → 0.9.0.pre156

Sign up to get free protection for your applications and to get access to all the features.
data/ext/pg.h CHANGED
@@ -2,10 +2,17 @@
2
2
  #include <stdlib.h>
3
3
  #include <sys/types.h>
4
4
 
5
+ #ifdef RUBY_EXTCONF_H
6
+ #include RUBY_EXTCONF_H
7
+ #endif
8
+
9
+ #ifdef HAVE_UNISTD_H
10
+ # include <unistd.h>
11
+ #endif /* HAVE_UNISTD_H */
12
+
5
13
  #include "ruby.h"
6
14
  #include "libpq-fe.h"
7
15
  #include "libpq/libpq-fs.h" /* large-object interface */
8
-
9
16
  #include "compat.h"
10
17
 
11
18
  #if RUBY_VM != 1
@@ -38,5 +45,5 @@
38
45
  #if defined(_WIN32)
39
46
  __declspec(dllexport)
40
47
  #endif
41
- void Init_pg(void);
48
+ void Init_pg_ext(void);
42
49
 
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Load the correct version if it's a Windows binary gem
4
+ if RUBY_PLATFORM =~/(mswin|mingw)/i
5
+ major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
6
+ raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
7
+ require "#{major_minor}/pg_ext"
8
+ else
9
+ require 'pg_ext'
10
+ end
11
+
@@ -0,0 +1,26 @@
1
+ # 1.9.1 fixes
2
+
3
+
4
+ # Make Pathname compatible with 1.8.7 Pathname.
5
+ unless Pathname.instance_methods.include?( :=~ )
6
+ class Pathname
7
+ def self::glob( *args ) # :yield: p
8
+ args = args.collect {|p| p.to_s }
9
+ if block_given?
10
+ Dir.glob(*args) {|f| yield self.new(f) }
11
+ else
12
+ Dir.glob(*args).map {|f| self.new(f) }
13
+ end
14
+ end
15
+
16
+ def =~( other )
17
+ self.to_s =~ other
18
+ end
19
+
20
+ def to_str
21
+ self.to_s
22
+ end
23
+ end
24
+ end
25
+
26
+
@@ -0,0 +1,76 @@
1
+ #
2
+ # Dependency-checking and Installation Rake Tasks
3
+
4
+ #
5
+
6
+ require 'rubygems/dependency_installer'
7
+ require 'rubygems/source_index'
8
+ require 'rubygems/requirement'
9
+ require 'rubygems/doc_manager'
10
+
11
+ ### Install the specified +gems+ if they aren't already installed.
12
+ def install_gems( gems )
13
+
14
+ defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
15
+ :generate_rdoc => true,
16
+ :generate_ri => true,
17
+ :install_dir => Gem.dir,
18
+ :format_executable => false,
19
+ :test => false,
20
+ :version => Gem::Requirement.default,
21
+ })
22
+
23
+ # Check for root
24
+ if Process.euid != 0
25
+ $stderr.puts "This probably won't work, as you aren't root, but I'll try anyway"
26
+ end
27
+
28
+ gemindex = Gem::SourceIndex.from_installed_gems
29
+
30
+ gems.each do |gemname, reqstring|
31
+ requirement = Gem::Requirement.new( reqstring )
32
+ trace "requirement is: %p" % [ requirement ]
33
+
34
+ trace "Searching for an installed #{gemname}..."
35
+ specs = gemindex.find_name( gemname )
36
+ trace "...found %d specs: %s" %
37
+ [ specs.length, specs.collect {|s| "%s %s" % [s.name, s.version] }.join(', ') ]
38
+
39
+ if spec = specs.find {|spec| requirement.satisfied_by?(spec.version) }
40
+ log "Version %s of %s is already installed (needs %s); skipping..." %
41
+ [ spec.version, spec.name, requirement ]
42
+ next
43
+ end
44
+
45
+ rgv = Gem::Version.new( Gem::RubyGemsVersion )
46
+ installer = nil
47
+
48
+ log "Trying to install #{gemname.inspect} #{requirement}..."
49
+ if rgv >= Gem::Version.new( '1.1.1' )
50
+ installer = Gem::DependencyInstaller.new
51
+ installer.install( gemname, requirement )
52
+ else
53
+ installer = Gem::DependencyInstaller.new( gemname )
54
+ installer.install
55
+ end
56
+
57
+ installer.installed_gems.each do |spec|
58
+ log "Installed: %s" % [ spec.full_name ]
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+
65
+ ### Task: install runtime dependencies
66
+ desc "Install runtime dependencies as gems"
67
+ task :install_dependencies do
68
+ install_gems( DEPENDENCIES )
69
+ end
70
+
71
+ ### Task: install gems for development tasks
72
+ desc "Install development dependencies as gems"
73
+ task :install_dev_dependencies do
74
+ install_gems( DEVELOPMENT_DEPENDENCIES )
75
+ end
76
+
@@ -0,0 +1,435 @@
1
+ # encoding: utf-8
2
+ #####################################################################
3
+ ### G L O B A L H E L P E R F U N C T I O N S
4
+ #####################################################################
5
+
6
+
7
+ require 'pathname'
8
+ require 'uri'
9
+ require 'open3'
10
+
11
+ begin
12
+ require 'readline'
13
+ include Readline
14
+ rescue LoadError
15
+ # Fall back to a plain prompt
16
+ def readline( text )
17
+ $stderr.print( text.chomp )
18
+ return $stdin.gets
19
+ end
20
+ end
21
+
22
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
23
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
24
+ ANSI_ATTRIBUTES = {
25
+ 'clear' => 0,
26
+ 'reset' => 0,
27
+ 'bold' => 1,
28
+ 'dark' => 2,
29
+ 'underline' => 4,
30
+ 'underscore' => 4,
31
+ 'blink' => 5,
32
+ 'reverse' => 7,
33
+ 'concealed' => 8,
34
+
35
+ 'black' => 30, 'on_black' => 40,
36
+ 'red' => 31, 'on_red' => 41,
37
+ 'green' => 32, 'on_green' => 42,
38
+ 'yellow' => 33, 'on_yellow' => 43,
39
+ 'blue' => 34, 'on_blue' => 44,
40
+ 'magenta' => 35, 'on_magenta' => 45,
41
+ 'cyan' => 36, 'on_cyan' => 46,
42
+ 'white' => 37, 'on_white' => 47
43
+ }
44
+
45
+
46
+ MULTILINE_PROMPT = <<-'EOF'
47
+ Enter one or more values for '%s'.
48
+ A blank line finishes input.
49
+ EOF
50
+
51
+
52
+ CLEAR_TO_EOL = "\e[K"
53
+ CLEAR_CURRENT_LINE = "\e[2K"
54
+
55
+
56
+ ### Output a logging message
57
+ def log( *msg )
58
+ output = colorize( msg.flatten.join(' '), 'cyan' )
59
+ $stderr.puts( output )
60
+ end
61
+
62
+
63
+ ### Output a logging message if tracing is on
64
+ def trace( *msg )
65
+ return unless $trace
66
+ output = colorize( msg.flatten.join(' '), 'yellow' )
67
+ $stderr.puts( output )
68
+ end
69
+
70
+
71
+ ### Return the specified args as a string, quoting any that have a space.
72
+ def quotelist( *args )
73
+ return args.flatten.collect {|part| part =~ /\s/ ? part.inspect : part}
74
+ end
75
+
76
+
77
+ ### Run the specified command +cmd+ with system(), failing if the execution
78
+ ### fails.
79
+ def run( *cmd )
80
+ cmd.flatten!
81
+
82
+ if cmd.length > 1
83
+ trace( quotelist(*cmd) )
84
+ else
85
+ trace( cmd )
86
+ end
87
+
88
+ if $dryrun
89
+ $stderr.puts "(dry run mode)"
90
+ else
91
+ system( *cmd )
92
+ unless $?.success?
93
+ fail "Command failed: [%s]" % [cmd.join(' ')]
94
+ end
95
+ end
96
+ end
97
+
98
+
99
+ ### Run the given +cmd+ with the specified +args+ without interpolation by the shell and
100
+ ### return anything written to its STDOUT.
101
+ def read_command_output( cmd, *args )
102
+ trace "Reading output from: %s" % [ cmd, quotelist(cmd, *args) ]
103
+ output = IO.read( '|-' ) or exec cmd, *args
104
+ return output
105
+ end
106
+
107
+
108
+ ### Run a subordinate Rake process with the same options and the specified +targets+.
109
+ def rake( *targets )
110
+ opts = ARGV.select {|arg| arg[0,1] == '-' }
111
+ args = opts + targets.map {|t| t.to_s }
112
+ run 'rake', '-N', *args
113
+ end
114
+
115
+
116
+ ### Open a pipe to a process running the given +cmd+ and call the given block with it.
117
+ def pipeto( *cmd )
118
+ $DEBUG = true
119
+
120
+ cmd.flatten!
121
+ log( "Opening a pipe to: ", cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
122
+ if $dryrun
123
+ $stderr.puts "(dry run mode)"
124
+ else
125
+ open( '|-', 'w+' ) do |io|
126
+
127
+ # Parent
128
+ if io
129
+ yield( io )
130
+
131
+ # Child
132
+ else
133
+ exec( *cmd )
134
+ fail "Command failed: [%s]" % [cmd.join(' ')]
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+
141
+ ### Download the file at +sourceuri+ via HTTP and write it to +targetfile+.
142
+ def download( sourceuri, targetfile=nil )
143
+ oldsync = $stdout.sync
144
+ $stdout.sync = true
145
+ require 'open-uri'
146
+
147
+ targetpath = Pathname.new( targetfile )
148
+
149
+ log "Downloading %s to %s" % [sourceuri, targetfile]
150
+ trace " connecting..."
151
+ ifh = open( sourceuri ) do |ifh|
152
+ trace " connected..."
153
+ targetpath.open( File::WRONLY|File::TRUNC|File::CREAT, 0644 ) do |ofh|
154
+ log "Downloading..."
155
+ buf = ''
156
+
157
+ while ifh.read( 16384, buf )
158
+ until buf.empty?
159
+ bytes = ofh.write( buf )
160
+ buf.slice!( 0, bytes )
161
+ end
162
+ end
163
+
164
+ log "Done."
165
+ end
166
+
167
+ end
168
+
169
+ return targetpath
170
+ ensure
171
+ $stdout.sync = oldsync
172
+ end
173
+
174
+
175
+ ### Return the fully-qualified path to the specified +program+ in the PATH.
176
+ def which( program )
177
+ return nil unless ENV['PATH']
178
+ ENV['PATH'].split(/:/).
179
+ collect {|dir| Pathname.new(dir) + program }.
180
+ find {|path| path.exist? && path.executable? }
181
+ end
182
+
183
+
184
+ ### Create a string that contains the ANSI codes specified and return it
185
+ def ansi_code( *attributes )
186
+ attributes.flatten!
187
+ attributes.collect! {|at| at.to_s }
188
+ # $stderr.puts "Returning ansicode for TERM = %p: %p" %
189
+ # [ ENV['TERM'], attributes ]
190
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
191
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
192
+
193
+ # $stderr.puts " attr is: %p" % [attributes]
194
+ if attributes.empty?
195
+ return ''
196
+ else
197
+ return "\e[%sm" % attributes
198
+ end
199
+ end
200
+
201
+
202
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
203
+ ### line-endings, color reset, etc.
204
+ def colorize( *args )
205
+ string = ''
206
+
207
+ if block_given?
208
+ string = yield
209
+ else
210
+ string = args.shift
211
+ end
212
+
213
+ ending = string[/(\s)$/] || ''
214
+ string = string.rstrip
215
+
216
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
217
+ end
218
+
219
+
220
+ ### Output the specified <tt>msg</tt> as an ANSI-colored error message
221
+ ### (white on red).
222
+ def error_message( msg, details='' )
223
+ $stderr.puts colorize( 'bold', 'white', 'on_red' ) { msg } + details
224
+ end
225
+ alias :error :error_message
226
+
227
+
228
+ ### Highlight and embed a prompt control character in the given +string+ and return it.
229
+ def make_prompt_string( string )
230
+ return CLEAR_CURRENT_LINE + colorize( 'bold', 'green' ) { string + ' ' }
231
+ end
232
+
233
+
234
+ ### Output the specified <tt>prompt_string</tt> as a prompt (in green) and
235
+ ### return the user's input with leading and trailing spaces removed. If a
236
+ ### test is provided, the prompt will repeat until the test returns true.
237
+ ### An optional failure message can also be passed in.
238
+ def prompt( prompt_string, failure_msg="Try again." ) # :yields: response
239
+ prompt_string.chomp!
240
+ prompt_string << ":" unless /\W$/.match( prompt_string )
241
+ response = nil
242
+
243
+ begin
244
+ prompt = make_prompt_string( prompt_string )
245
+ response = readline( prompt ) || ''
246
+ response.strip!
247
+ if block_given? && ! yield( response )
248
+ error_message( failure_msg + "\n\n" )
249
+ response = nil
250
+ end
251
+ end while response.nil?
252
+
253
+ return response
254
+ end
255
+
256
+
257
+ ### Prompt the user with the given <tt>prompt_string</tt> via #prompt,
258
+ ### substituting the given <tt>default</tt> if the user doesn't input
259
+ ### anything. If a test is provided, the prompt will repeat until the test
260
+ ### returns true. An optional failure message can also be passed in.
261
+ def prompt_with_default( prompt_string, default, failure_msg="Try again." )
262
+ response = nil
263
+
264
+ begin
265
+ default ||= '~'
266
+ response = prompt( "%s [%s]" % [ prompt_string, default ] )
267
+ response = default.to_s if !response.nil? && response.empty?
268
+
269
+ trace "Validating response %p" % [ response ]
270
+
271
+ # the block is a validator. We need to make sure that the user didn't
272
+ # enter '~', because if they did, it's nil and we should move on. If
273
+ # they didn't, then call the block.
274
+ if block_given? && response != '~' && ! yield( response )
275
+ error_message( failure_msg + "\n\n" )
276
+ response = nil
277
+ end
278
+ end while response.nil?
279
+
280
+ return nil if response == '~'
281
+ return response
282
+ end
283
+
284
+
285
+ ### Prompt for an array of values
286
+ def prompt_for_multiple_values( label, default=nil )
287
+ $stderr.puts( MULTILINE_PROMPT % [label] )
288
+ if default
289
+ $stderr.puts "Enter a single blank line to keep the default:\n %p" % [ default ]
290
+ end
291
+
292
+ results = []
293
+ result = nil
294
+
295
+ begin
296
+ result = readline( make_prompt_string("> ") )
297
+ if result.nil? || result.empty?
298
+ results << default if default && results.empty?
299
+ else
300
+ results << result
301
+ end
302
+ end until result.nil? || result.empty?
303
+
304
+ return results.flatten
305
+ end
306
+
307
+
308
+ ### Turn echo and masking of input on/off.
309
+ def noecho( masked=false )
310
+ require 'termios'
311
+
312
+ rval = nil
313
+ term = Termios.getattr( $stdin )
314
+
315
+ begin
316
+ newt = term.dup
317
+ newt.c_lflag &= ~Termios::ECHO
318
+ newt.c_lflag &= ~Termios::ICANON if masked
319
+
320
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, newt )
321
+
322
+ rval = yield
323
+ ensure
324
+ Termios.tcsetattr( $stdin, Termios::TCSANOW, term )
325
+ end
326
+
327
+ return rval
328
+ end
329
+
330
+
331
+ ### Prompt the user for her password, turning off echo if the 'termios' module is
332
+ ### available.
333
+ def prompt_for_password( prompt="Password: " )
334
+ return noecho( true ) do
335
+ $stderr.print( prompt )
336
+ ($stdin.gets || '').chomp
337
+ end
338
+ end
339
+
340
+
341
+ ### Display a description of a potentially-dangerous task, and prompt
342
+ ### for confirmation. If the user answers with anything that begins
343
+ ### with 'y', yield to the block. If +abort_on_decline+ is +true+,
344
+ ### any non-'y' answer will fail with an error message.
345
+ def ask_for_confirmation( description, abort_on_decline=true )
346
+ puts description
347
+
348
+ answer = prompt_with_default( "Continue?", 'n' ) do |input|
349
+ input =~ /^[yn]/i
350
+ end
351
+
352
+ if answer =~ /^y/i
353
+ return yield
354
+ elsif abort_on_decline
355
+ error "Aborted."
356
+ fail
357
+ end
358
+
359
+ return false
360
+ end
361
+ alias :prompt_for_confirmation :ask_for_confirmation
362
+
363
+
364
+ ### Search line-by-line in the specified +file+ for the given +regexp+, returning the
365
+ ### first match, or nil if no match was found. If the +regexp+ has any capture groups,
366
+ ### those will be returned in an Array, else the whole matching line is returned.
367
+ def find_pattern_in_file( regexp, file )
368
+ rval = nil
369
+
370
+ File.open( file, 'r' ).each do |line|
371
+ if (( match = regexp.match(line) ))
372
+ rval = match.captures.empty? ? match[0] : match.captures
373
+ break
374
+ end
375
+ end
376
+
377
+ return rval
378
+ end
379
+
380
+
381
+ ### Search line-by-line in the output of the specified +cmd+ for the given +regexp+,
382
+ ### returning the first match, or nil if no match was found. If the +regexp+ has any
383
+ ### capture groups, those will be returned in an Array, else the whole matching line
384
+ ### is returned.
385
+ def find_pattern_in_pipe( regexp, *cmd )
386
+ output = []
387
+
388
+ log( cmd.collect {|part| part =~ /\s/ ? part.inspect : part} )
389
+ Open3.popen3( *cmd ) do |stdin, stdout, stderr|
390
+ stdin.close
391
+
392
+ output << stdout.gets until stdout.eof?
393
+ output << stderr.gets until stderr.eof?
394
+ end
395
+
396
+ result = output.find { |line| regexp.match(line) }
397
+ return $1 || result
398
+ end
399
+
400
+
401
+ ### Invoke the user's editor on the given +filename+ and return the exit code
402
+ ### from doing so.
403
+ def edit( filename )
404
+ editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
405
+ system editor, filename
406
+ unless $?.success? || editor =~ /vim/i
407
+ fail "Editor exited uncleanly."
408
+ end
409
+ end
410
+
411
+
412
+ ### Extract all the non Rake-target arguments from ARGV and return them.
413
+ def get_target_args
414
+ args = ARGV.reject {|arg| arg =~ /^-/ || Rake::Task.task_defined?(arg) }
415
+ return args
416
+ end
417
+
418
+
419
+ ### Log a subdirectory change, execute a block, and exit the subdirectory
420
+ def in_subdirectory( subdir )
421
+ block = Proc.new
422
+
423
+ log "Entering #{subdir}"
424
+ Dir.chdir( subdir, &block )
425
+ log "Leaving #{subdir}"
426
+ end
427
+
428
+
429
+ ### Make an easily-comparable version vector out of +ver+ and return it.
430
+ def vvec( ver )
431
+ return ver.split('.').collect {|char| char.to_i }.pack('N*')
432
+ end
433
+
434
+
435
+