pg 0.11.0 → 0.12.0pre258

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+ require 'stringio'
5
+
6
+ # An example of how to stream data to your local host from the database as CSV.
7
+
8
+ $stderr.puts "Opening database connection ..."
9
+ conn = PGconn.connect( :dbname => 'test' )
10
+
11
+ ### You can test the error case from the database side easily by
12
+ ### changing one of the numbers at the end of one of the above rows to
13
+ ### something non-numeric like "-".
14
+
15
+ $stderr.puts "Running COPY command ..."
16
+ buf = ''
17
+ conn.transaction do
18
+ conn.exec( "COPY logs TO STDOUT WITH csv" )
19
+ $stdout.puts( buf ) while buf = conn.get_copy_data
20
+ end
21
+
22
+ conn.finish
23
+
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ SAMPLE_WRITE_DATA = 'some sample data'
6
+ SAMPLE_EXPORT_NAME = 'lowrite.txt'
7
+
8
+ conn = PGconn.connect( :dbname => 'test', :host => 'localhost', :port => 5432 )
9
+ puts "dbname: " + conn.db + "\thost: " + conn.host + "\tuser: " + conn.user
10
+
11
+ # Start a transaction, as all large object functions require one.
12
+ puts "Beginning transaction"
13
+ conn.exec( 'BEGIN' )
14
+
15
+ # Test importing from a file
16
+ puts "Import test:"
17
+ puts " importing %s" % [ __FILE__ ]
18
+ oid = conn.lo_import( __FILE__ )
19
+ puts " imported as large object %d" % [ oid ]
20
+
21
+ # Read back 50 bytes of the imported data
22
+ puts "Read test:"
23
+ fd = conn.lo_open( oid, PGconn::INV_READ|PGconn::INV_WRITE )
24
+ conn.lo_lseek( fd, 0, PGconn::SEEK_SET )
25
+ buf = conn.lo_read( fd, 50 )
26
+ puts " read: %p" % [ buf ]
27
+ puts " read was ok!" if buf =~ /require 'pg'/
28
+
29
+ # Append some test data onto the end of the object
30
+ puts "Write test:"
31
+ conn.lo_lseek( fd, 0, PGconn::SEEK_END )
32
+ buf = SAMPLE_WRITE_DATA.dup
33
+ totalbytes = 0
34
+ until buf.empty?
35
+ bytes = conn.lo_write( fd, buf )
36
+ buf.slice!( 0, bytes )
37
+ totalbytes += bytes
38
+ end
39
+ puts " appended %d bytes" % [ totalbytes ]
40
+
41
+ # Now export it
42
+ puts "Export test:"
43
+ File.unlink( SAMPLE_EXPORT_NAME ) if File.exist?( SAMPLE_EXPORT_NAME )
44
+ conn.lo_export( oid, SAMPLE_EXPORT_NAME )
45
+ puts " success!" if File.exist?( SAMPLE_EXPORT_NAME )
46
+ puts " exported as %s (%d bytes)" % [ SAMPLE_EXPORT_NAME, File.size(SAMPLE_EXPORT_NAME) ]
47
+
48
+ conn.exec( 'COMMIT' )
49
+ puts "End of transaction."
50
+
51
+
52
+ puts 'Testing read and delete from a new transaction:'
53
+ puts ' starting a new transaction'
54
+ conn.exec( 'BEGIN' )
55
+
56
+ fd = conn.lo_open( oid, PGconn::INV_READ )
57
+ puts ' reopened okay.'
58
+ conn.lo_lseek( fd, 50, PGconn::SEEK_END )
59
+ buf = conn.lo_read( fd, 50 )
60
+ puts ' read okay.' if buf == SAMPLE_WRITE_DATA
61
+
62
+ puts 'Closing and unlinking:'
63
+ conn.lo_close( fd )
64
+ puts ' closed.'
65
+ conn.lo_unlink( oid )
66
+ puts ' unlinked.'
67
+ conn.exec( 'COMMIT' )
68
+ puts 'Done.'
69
+
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Test script, demonstrating a non-poll notification for a table event.
4
+ #
5
+ # To use, create a table called 'test', and attach a NOTIFY trigger to it
6
+ # like so:
7
+ #
8
+ # CREATE OR REPLACE FUNCTION notify_test()
9
+ # RETURNS TRIGGER
10
+ # LANGUAGE plpgsql
11
+ # AS $$
12
+ # BEGIN
13
+ # NOTIFY woo;
14
+ # RETURN NULL;
15
+ # END
16
+ # $$
17
+ #
18
+ # CREATE TRIGGER notify_trigger
19
+ # AFTER UPDATE OR INSERT OR DELETE
20
+ # ON test
21
+ # FOR EACH STATEMENT
22
+ # EXECUTE PROCEDURE notify_test()
23
+ #
24
+
25
+ BEGIN {
26
+ require 'pathname'
27
+ basedir = Pathname.new( __FILE__ ).expand_path.dirname.parent
28
+ libdir = basedir + 'lib'
29
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
30
+ }
31
+
32
+ require 'pg'
33
+
34
+ conn = PGconn.connect( :dbname => 'test' )
35
+ conn.exec( 'LISTEN woo' ) # register interest in the 'woo' event
36
+
37
+ puts "Waiting up to 30 seconds for for an event!"
38
+ conn.wait_for_notify( 30 ) do |notify, pid|
39
+ puts "I got one from pid %d: %s" % [ pid, notify ]
40
+ end
41
+
42
+ puts "Awww, I didn't see any events."
43
+
@@ -0,0 +1,1181 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # an interactive front-end to postgreSQL
4
+ #
5
+ # Original source code is written by C.
6
+ # Copyright (c) 1996, Regents of the University of California
7
+ #
8
+ # ruby version is written by ematsu
9
+ # Copyright (c) 1997 Eiji-usagi-MATSUmoto <ematsu@pfu.co.jp>
10
+ #
11
+ # Changes:
12
+ #
13
+ # Fri 12 Dec 19:56:34 JST 1997
14
+ # * replace puts -> print
15
+ #
16
+ # $Id: psql.rb,v 7bc74288b271 2011/10/07 14:42:43 ged $
17
+ #
18
+
19
+ require "pg"
20
+ require "parsearg"
21
+ require "psqlHelp"
22
+
23
+ PROMPT = "=> "
24
+ MAX_QUERY_BUFFER = 20000
25
+ DEFAULT_SHELL = "/bin/sh"
26
+ DEFAULT_EDITOR = "vi"
27
+ DEFAULT_FIELD_SEP= "|"
28
+
29
+ PsqlSettings = Struct.new("PsqlSettings", :db, :queryFout, :opt, :prompt,
30
+ :gfname, :notty,:pipe, :echoQuery,:quiet,
31
+ :singleStep, :singleLineMode, :useReadline)
32
+
33
+ PrintOpt = Struct.new("PrintOpt", :header, :align, :standard, :html3,
34
+ :expanded, :pager, :fieldSep, :tableOpt,
35
+ :caption, :fieldName)
36
+
37
+ $readline_ok = TRUE
38
+
39
+ def usage()
40
+ printf("Usage: psql.rb [options] [dbname]\n")
41
+ printf("\t -a authsvc set authentication service\n")
42
+ printf("\t -A turn off alignment when printing out attributes\n")
43
+ printf("\t -c query run single query (slash commands too)\n")
44
+ printf("\t -d dbName specify database name\n")
45
+ printf("\t -e echo the query sent to the backend\n")
46
+ printf("\t -f filename use file as a source of queries\n")
47
+ printf("\t -F sep set the field separator (default is \" \")\n")
48
+ printf("\t -h host set database server host\n")
49
+ printf("\t -H turn on html3.0 table output\n")
50
+ printf("\t -l list available databases\n")
51
+ printf("\t -n don't use readline library\n")
52
+ printf("\t -o filename send output to filename or (|pipe)\n")
53
+ printf("\t -p port set port number\n")
54
+ printf("\t -q run quietly (no messages, no prompts)\n")
55
+ printf("\t -s single step mode (prompts for each query)\n")
56
+ printf("\t -S single line mode (i.e. query terminated by newline)\n")
57
+ printf("\t -t turn off printing of headings and row count\n")
58
+ printf("\t -T html set html3.0 table command options (cf. -H)\n")
59
+ printf("\t -x turn on expanded output (field names on left)\n")
60
+ exit(1)
61
+ end
62
+ $USAGE = 'usage'
63
+
64
+ def slashUsage(ps)
65
+ printf(" \\? -- help\n")
66
+ printf(" \\a -- toggle field-alignment (currenty %s)\n", on(ps.opt.align))
67
+ printf(" \\C [<captn>] -- set html3 caption (currently '%s')\n", ps.opt.caption );
68
+ printf(" \\connect <dbname> -- connect to new database (currently '%s')\n", ps.db.db)
69
+ printf(" \\copy {<table> to <file> | <file> from <table>}\n")
70
+ printf(" \\d [<table>] -- list tables in database or columns in <table>, * for all\n")
71
+ printf(" \\da -- list aggregates\n")
72
+ printf(" \\di -- list only indices\n")
73
+ printf(" \\ds -- list only sequences\n")
74
+ printf(" \\dS -- list system tables and indexes\n")
75
+ printf(" \\dt -- list only tables\n")
76
+ printf(" \\dT -- list types\n")
77
+ printf(" \\e [<fname>] -- edit the current query buffer or <fname>\n")
78
+ printf(" \\E [<fname>] -- edit the current query buffer or <fname>, and execute\n")
79
+ printf(" \\f [<sep>] -- change field separater (currently '%s')\n", ps.opt.fieldSep)
80
+ printf(" \\g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]\n")
81
+ printf(" \\h [<cmd>] -- help on syntax of sql commands, * for all commands\n")
82
+ printf(" \\H -- toggle html3 output (currently %s)\n", on(ps.opt.html3))
83
+ printf(" \\i <fname> -- read and execute queries from filename\n")
84
+ printf(" \\l -- list all databases\n")
85
+ printf(" \\m -- toggle monitor-like table display (currently %s)\n", on(ps.opt.standard))
86
+ printf(" \\o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe\n")
87
+ printf(" \\p -- print the current query buffer\n")
88
+ printf(" \\q -- quit\n")
89
+ printf(" \\r -- reset(clear) the query buffer\n")
90
+ printf(" \\s [<fname>] -- print history or save it in <fname>\n")
91
+ printf(" \\t -- toggle table headings and row count (currently %s)\n", on(ps.opt.header))
92
+ printf(" \\T [<html>] -- set html3.0 <table ...> options (currently '%s')\n", ps.opt.tableOpt)
93
+ printf(" \\x -- toggle expanded output (currently %s)\n", on(ps.opt.expanded))
94
+ printf(" \\! [<cmd>] -- shell escape or command\n")
95
+ end
96
+
97
+ def on(f)
98
+ if f
99
+ return "on"
100
+ else
101
+ return "off"
102
+ end
103
+ end
104
+
105
+ def toggle(settings, sw, msg)
106
+ sw = !sw
107
+ if !settings.quiet
108
+ printf(STDERR, "turned %s %s\n", on(sw), msg)
109
+ end
110
+ return sw
111
+ end
112
+
113
+ def gets(prompt, source)
114
+ if source == STDIN
115
+ if ($readline_ok)
116
+ line = Readline.readline(prompt,source)
117
+ else
118
+ STDOUT.print(prompt)
119
+ STDOUT.flush()
120
+ line = source.gets
121
+ end
122
+ end
123
+
124
+ if line == nil
125
+ return nil
126
+ else
127
+ if line.length > MAX_QUERY_BUFFER
128
+ printf(STDERR, "line read exceeds maximum length. Truncating at %d\n",
129
+ MAX_QUERY_BUFFER)
130
+ return line[0..MAX_QUERY_BUFFER-1]
131
+ else
132
+ return line
133
+ end
134
+ end
135
+ end
136
+
137
+ def PSQLexec(ps, query)
138
+ res = ps.db.exec(query)
139
+
140
+ if res == nil
141
+ printf(STDERR, "%s\n", ps.db.error())
142
+
143
+ else
144
+ if (res.status() == PGresult::COMMAND_OK ||
145
+ res.status() == PGresult::TUPLES_OK)
146
+ return res
147
+ end
148
+
149
+ if !ps.quiet
150
+ printf(STDERR, "%s\n", ps.db.error())
151
+ end
152
+
153
+ res.clear()
154
+ end
155
+
156
+ end
157
+
158
+ def listAllDbs(ps)
159
+ query = "select * from pg_database;"
160
+
161
+ if (results = PSQLexec(ps, query)) == nil
162
+ return 1
163
+
164
+ else
165
+ results.print(ps.queryFout, ps.opt)
166
+ results.clear()
167
+ return 0
168
+ end
169
+ end
170
+
171
+ def tableList(ps, deep_tablelist, info_type, system_tables)
172
+ listbuf = "SELECT usename, relname, relkind, relhasrules"
173
+ listbuf += " FROM pg_class, pg_user "
174
+ listbuf += "WHERE usesysid = relowner "
175
+ case info_type
176
+ when 't'
177
+ listbuf += "and ( relkind = 'r') "
178
+ when 'i'
179
+ listbuf += "and ( relkind = 'i') "
180
+ haveIndexes = true
181
+ when 'S'
182
+ listbuf += "and ( relkind = 'S') "
183
+ else
184
+ listbuf += "and ( relkind = 'r' OR relkind = 'i' OR relkind='S') "
185
+ haveIndexes = true
186
+ end
187
+ if (!system_tables)
188
+ listbuf += "and relname !~ '^pg_' "
189
+ else
190
+ listbuf += "and relname ~ '^pg_' "
191
+ end
192
+ if (haveIndexes)
193
+ listbuf += "and (relkind != 'i' OR relname !~'^xinx')"
194
+ end
195
+ listbuf += " ORDER BY relname "
196
+
197
+ res = PSQLexec(ps, listbuf)
198
+ if res == nil
199
+ return
200
+ end
201
+
202
+ # first, print out the attribute names
203
+ nColumns = res.num_tuples
204
+ if nColumns > 0
205
+ if deep_tablelist
206
+ table = res.result
207
+ res.clear
208
+ for i in 0..nColumns-1
209
+ tableDesc(ps, table[i][1])
210
+ end
211
+ else
212
+ # Display the information
213
+
214
+ printf("\nDatabase = %s\n", ps.db.db)
215
+ printf(" +------------------+----------------------------------+----------+\n")
216
+ printf(" | Owner | Relation | Type |\n")
217
+ printf(" +------------------+----------------------------------+----------+\n")
218
+
219
+ # next, print out the instances
220
+ for i in 0..res.num_tuples-1
221
+ printf(" | %-16.16s", res.getvalue(i, 0))
222
+ printf(" | %-32.32s | ", res.getvalue(i, 1))
223
+ rk = res.getvalue(i, 2)
224
+ rr = res.getvalue(i, 3)
225
+ if (rk.eql?("r"))
226
+ printf("%-8.8s |", if (rr[0] == 't') then "view?" else "table" end)
227
+ else
228
+ printf("%-8.8s |", "index")
229
+ end
230
+ printf("\n")
231
+ end
232
+ printf(" +------------------+----------------------------------+----------+\n")
233
+ res.clear()
234
+ end
235
+ else
236
+ printf(STDERR, "Couldn't find any tables!\n")
237
+ end
238
+ end
239
+
240
+ def tableDesc(ps, table)
241
+ descbuf = "SELECT a.attnum, a.attname, t.typname, a.attlen"
242
+ descbuf += " FROM pg_class c, pg_attribute a, pg_type t "
243
+ descbuf += " WHERE c.relname = '"
244
+ descbuf += table
245
+ descbuf += "'"
246
+ descbuf += " and a.attnum > 0 "
247
+ descbuf += " and a.attrelid = c.oid "
248
+ descbuf += " and a.atttypid = t.oid "
249
+ descbuf += " ORDER BY attnum "
250
+
251
+ res = PSQLexec(ps, descbuf)
252
+ if res == nil
253
+ return
254
+ end
255
+
256
+ # first, print out the attribute names
257
+ nColumns = res.num_tuples()
258
+ if nColumns > 0
259
+ #
260
+ # Display the information
261
+ #
262
+
263
+ printf("\nTable = %s\n", table)
264
+ printf("+----------------------------------+----------------------------------+-------+\n")
265
+ printf("| Field | Type | Length|\n")
266
+ printf("+----------------------------------+----------------------------------+-------+\n")
267
+
268
+ # next, print out the instances
269
+ for i in 0..res.num_tuples-1
270
+
271
+ printf("| %-32.32s | ", res.getvalue(i, 1))
272
+ rtype = res.getvalue(i, 2);
273
+ rsize = res.getvalue(i, 3).to_i
274
+
275
+ if (rtype.eql?("text"))
276
+ printf("%-32.32s |", rtype)
277
+ printf("%6s |", "var")
278
+ elsif (rtype.eql?("bpchar"))
279
+ printf("%-32.32s |", "(bp)char")
280
+ printf("%6i |", if (rsize > 0) then rsize - 4 else 0 end)
281
+ elsif (rtype.eql?("varchar"))
282
+ printf("%-32.32s |", rtype)
283
+ printf("%6d |", if (rsize > 0) then rsize - 4 else 0 end)
284
+ else
285
+ # array types start with an underscore
286
+ if (rtype[0, 1] != '_')
287
+ printf("%-32.32s |", rtype)
288
+ else
289
+ newname = rtype + "[]"
290
+ printf("%-32.32s |", newname)
291
+ end
292
+ if (rsize > 0)
293
+ printf("%6d |", rsize)
294
+ else
295
+ printf("%6s |", "var")
296
+ end
297
+ end
298
+ printf("\n")
299
+ end
300
+ printf("+----------------------------------+----------------------------------+-------+\n")
301
+
302
+ res.clear()
303
+
304
+ else
305
+ printf(STDERR, "Couldn't find table %s!\n", table)
306
+ end
307
+ end
308
+
309
+ def unescape(source)
310
+ dest = source.gsub(/(\\n|\\r|\\t|\\f|\\\\)/) {
311
+ |c|
312
+ case c
313
+ when "\\n"
314
+ "\n"
315
+ when "\\r"
316
+ "\r"
317
+ when "\\t"
318
+ "\t"
319
+ when "\\f"
320
+ "\f"
321
+ when "\\\\"
322
+ "\\"
323
+ end
324
+ }
325
+ return dest
326
+ end
327
+
328
+ def do_shell(command)
329
+ if !command
330
+ command = ENV["SHELL"]
331
+ if shellName == nil
332
+ command = DEFAULT_SHELL
333
+ end
334
+ end
335
+ system(command);
336
+ end
337
+
338
+ def do_help(topic)
339
+ if !topic
340
+ printf("type \\h <cmd> where <cmd> is one of the following:\n")
341
+
342
+ left_center_right = 'L' # Start with left column
343
+ for i in 0..QL_HELP.length-1
344
+ case left_center_right
345
+ when 'L'
346
+ printf(" %-25s", QL_HELP[i][0])
347
+ left_center_right = 'C'
348
+
349
+ when 'C'
350
+ printf("%-25s", QL_HELP[i][0])
351
+ left_center_right = 'R'
352
+
353
+ when 'R'
354
+ printf("%-25s\n", QL_HELP[i][0])
355
+ left_center_right = 'L'
356
+
357
+ end
358
+ end
359
+ if (left_center_right != 'L')
360
+ STDOUT.print("\n")
361
+ end
362
+ printf("type \\h * for a complete description of all commands\n")
363
+ else
364
+ help_found = FALSE
365
+ for i in 0..QL_HELP.length-1
366
+ if QL_HELP[i][0] == topic || topic == "*"
367
+ help_found = TRUE
368
+ printf("Command: %s\n", QL_HELP[i][0])
369
+ printf("Description: %s\n", QL_HELP[i][1])
370
+ printf("Syntax:\n")
371
+ printf("%s\n", QL_HELP[i][2])
372
+ printf("\n")
373
+ end
374
+ end
375
+ if !help_found
376
+ printf("command not found, ")
377
+ printf("try \\h with no arguments to see available help\n")
378
+ end
379
+ end
380
+ end
381
+
382
+ def do_edit(filename_arg, query)
383
+ if filename_arg
384
+ fname = filename_arg
385
+ error = FALSE
386
+ else
387
+ fname = sprintf("/tmp/psql.rb.%d", $$)
388
+ p fname
389
+ if test(?e, fname)
390
+ File.unlink(fname)
391
+ end
392
+
393
+ if query
394
+ begin
395
+ fd = File.new(fname, "w")
396
+ if query[query.length-1, 1] != "\n"
397
+ query += "\n"
398
+ end
399
+ if fd.print(query) != query.length
400
+ fd.close
401
+ File.unlink(fname)
402
+ error = TRUE
403
+ else
404
+ error = FALSE
405
+ end
406
+ fd.close
407
+ rescue
408
+ error = TRUE
409
+ end
410
+ else
411
+ error = FALSE
412
+ end
413
+ end
414
+
415
+ if error
416
+ status = 1
417
+ else
418
+ editFile(fname)
419
+ begin
420
+ fd = File.new(fname, "r")
421
+ query = fd.read
422
+ fd.close
423
+ if query == nil
424
+ status = 1
425
+ else
426
+ query.sub!(/[ \t\f\r\n]*$/, "")
427
+ if query.length != 0
428
+ status = 3
429
+ else
430
+ query = nil
431
+ status = 1
432
+ end
433
+ end
434
+ rescue
435
+ status = 1
436
+ ensure
437
+ if !filename_arg
438
+ if test(?e, fname)
439
+ File.unlink(fname)
440
+ end
441
+ end
442
+ end
443
+ end
444
+ return status, query
445
+ end
446
+
447
+ def editFile(fname)
448
+ editorName = ENV["EDITOR"]
449
+ if editorName == nil
450
+ editorName = DEFAULT_EDITOR
451
+ end
452
+ system(editorName + " " + fname)
453
+ end
454
+
455
+ def do_connect(settings, new_dbname)
456
+ dbname = settings.db.db
457
+
458
+ if !new_dbname
459
+ printf(STDERR, "\\connect must be followed by a database name\n");
460
+ else
461
+ olddb = settings.db
462
+
463
+ begin
464
+ printf("closing connection to database: %s\n", dbname);
465
+ settings.db = PGconn.connect(olddb.host, olddb.port, "", "", new_dbname)
466
+ printf("connecting to new database: %s\n", new_dbname)
467
+ olddb.finish()
468
+ rescue
469
+ printf(STDERR, "%s\n", $!)
470
+ printf("reconnecting to %s\n", dbname)
471
+ settings.db = PGconn.connect(olddb.host, olddb.port,"", "", dbname)
472
+ ensure
473
+ settings.prompt = settings.db.db + PROMPT
474
+ end
475
+ end
476
+ end
477
+
478
+ def do_copy(settings, table, from_p, file)
479
+ if (table == nil || from_p == nil || file == nil)
480
+ printf("Syntax error, reffer \\copy help with \\? \n")
481
+ return
482
+ end
483
+
484
+ if from_p.upcase! == "FROM"
485
+ from = TRUE
486
+ else
487
+ from = FALSE
488
+ end
489
+
490
+ query = "COPY "
491
+ query += table
492
+
493
+ if from
494
+ query += " FROM stdin"
495
+ copystream = File.new(file, "r")
496
+ else
497
+ query += " TO stdout"
498
+ copystream = File.new(file, "w")
499
+ end
500
+
501
+ begin
502
+ success = SendQuery(settings, query, from, !from, copystream);
503
+ copystream.close
504
+ if !settings.quiet
505
+ if success
506
+ printf("Successfully copied.\n");
507
+ else
508
+ printf("Copy failed.\n");
509
+ end
510
+ end
511
+ rescue
512
+ printf(STDERR, "Unable to open file %s which to copy.",
513
+ if from then "from" else "to" end)
514
+ end
515
+ end
516
+
517
+ def handleCopyOut(settings, copystream)
518
+ copydone = FALSE
519
+
520
+ while !copydone
521
+ copybuf = settings.db.getline
522
+
523
+ if !copybuf
524
+ copydone = TRUE
525
+ else
526
+ if copybuf == "\\."
527
+ copydone = TRUE
528
+ else
529
+ copystream.print(copybuf + "\n")
530
+ end
531
+ end
532
+ end
533
+ copystream.flush
534
+ settings.db.endcopy
535
+ end
536
+
537
+ def handleCopyIn(settings, mustprompt, copystream)
538
+ copydone = FALSE
539
+
540
+ if mustprompt
541
+ STDOUT.print("Enter info followed by a newline\n")
542
+ STDOUT.print("End with a backslash and a ")
543
+ STDOUT.print("period on a line by itself.\n")
544
+ end
545
+
546
+ while !copydone
547
+ if mustprompt
548
+ STDOUT.print(">> ")
549
+ STDOUT.flush
550
+ end
551
+
552
+ copybuf = copystream.gets
553
+ if copybuf == nil
554
+ settings.db.putline("\\.\n")
555
+ copydone = TRUE
556
+ break
557
+ end
558
+ settings.db.putline(copybuf)
559
+ if copybuf == "\\.\n"
560
+ copydone = TRUE
561
+ end
562
+ end
563
+ settings.db.endcopy
564
+ end
565
+
566
+ def setFout(ps, fname)
567
+ if (ps.queryFout && ps.queryFout != STDOUT)
568
+ ps.queryFout.close
569
+ end
570
+
571
+ if !fname
572
+ ps.queryFout = STDOUT
573
+ else
574
+ begin
575
+ if fname[0, 1] == "|"
576
+ dumy, ps.queryFout = pipe(fname)
577
+ ps.pipe = TRUE
578
+ else
579
+ ps.queryFout = File.new(fname, "w+")
580
+ ps.pipe = FALSE
581
+ end
582
+ rescue
583
+ ps.queryFout = STDOUT
584
+ ps.pipe = FALSE
585
+ end
586
+ end
587
+ end
588
+
589
+ def HandleSlashCmds(settings, line, query)
590
+ status = 1
591
+ cmd = unescape(line[1, line.length])
592
+ args = cmd.split
593
+
594
+ case args[0]
595
+ when 'a' # toggles to align fields on output
596
+ settings.opt.align =
597
+ toggle(settings, settings.opt.align, "field alignment")
598
+
599
+ when 'C' # define new caption
600
+ if !args[1]
601
+ settings.opt.caption = ""
602
+ else
603
+ settings.opt.caption = args[1]
604
+ end
605
+
606
+ when 'c', 'connect' # connect new database
607
+ do_connect(settings, args[1])
608
+
609
+ when 'copy' # copy from file
610
+ do_copy(settings, args[1], args[2], args[3])
611
+
612
+ when 'd' # \d describe tables or columns in a table
613
+ if !args[1]
614
+ tableList(settings, FALSE, 'b', FALSE)
615
+ elsif args[1] == "*"
616
+ tableList(settings, FALSE, 'b', FALSE)
617
+ tableList(settings, TRUE, 'b', FALSE)
618
+ else
619
+ tableDesc(settings, args[1])
620
+ end
621
+
622
+ when 'da'
623
+ descbuf = "SELECT a.aggname AS aggname, t.typname AS type, "
624
+ descbuf += "obj_description (a.oid) as description "
625
+ descbuf += "FROM pg_aggregate a, pg_type t "
626
+ descbuf += "WHERE a.aggbasetype = t.oid "
627
+ if (args[1])
628
+ descbuf += "AND a.aggname ~ '^"
629
+ descbuf += args[1]
630
+ descbuf += "' "
631
+ end
632
+ descbuf += "UNION SELECT a.aggname AS aggname, "
633
+ descbuf += "'all types' as type, obj_description (a.oid) "
634
+ descbuf += "as description FROM pg_aggregate a "
635
+ descbuf += "WHERE a.aggbasetype = 0"
636
+ if (args[1])
637
+ descbuf += "AND a.aggname ~ '^"
638
+ descbuf += args[1]
639
+ descbuf += "' "
640
+ end
641
+ descbuf += "ORDER BY aggname, type;"
642
+ res = SendQuery(settings, descbuf, FALSE, FALSE, 0)
643
+
644
+ when 'di'
645
+ tableList(settings, FALSE, 'i', FALSE)
646
+
647
+ when 'ds'
648
+ tableList(settings, FALSE, 'S', FALSE)
649
+
650
+ when 'dS'
651
+ tableList(settings, FALSE, 'b', TRUE)
652
+
653
+ when 'dt'
654
+ tableList(settings, FALSE, 't', FALSE)
655
+
656
+ when 'e' # edit
657
+ status, query = do_edit(args[1], query)
658
+
659
+ when 'E'
660
+ if args[1]
661
+ begin
662
+ lastfile = args[1]
663
+ File.file?(lastfile) && (mt = File.mtime(lastfile))
664
+ editFile(lastfile)
665
+ File.file?(lastfile) && (mt2 = File.mtime(lastfile))
666
+ fd = File.new(lastfile, "r")
667
+ if mt != mt2
668
+ MainLoop(settings, fd)
669
+ fd.close()
670
+ else
671
+ if !settings.quiet
672
+ printf(STDERR, "warning: %s not modified. query not executed\n", lastfile)
673
+ end
674
+ fd.close()
675
+ end
676
+ rescue
677
+ #
678
+ end
679
+ else
680
+ printf(STDERR, "\\r must be followed by a file name initially\n");
681
+ end
682
+ when 'f'
683
+ if args[1]
684
+ settings.opt.fieldSep = args[1]
685
+ if !settings.quiet
686
+ printf(STDERR, "field separater changed to '%s'\n", settings.opt.fieldSep)
687
+ end
688
+ end
689
+
690
+ when 'g' # \g means send query
691
+ if !args[1]
692
+ settings.gfname = nil
693
+ else
694
+ settings.gfname = args[1]
695
+ end
696
+ status = 0
697
+
698
+ when 'h' # help
699
+ if args[2]
700
+ args[1] += " " + args[2]
701
+ end
702
+ do_help(args[1])
703
+
704
+ when 'i' # \i is include file
705
+ if args[1]
706
+ begin
707
+ fd = File.open(args[1], "r")
708
+ MainLoop(settings, fd)
709
+ fd.close()
710
+ rescue Errno::ENOENT
711
+ printf(STDERR, "file named %s could not be opened\n", args[1])
712
+ end
713
+ else
714
+ printf(STDERR, "\\i must be followed by a file name\n")
715
+ end
716
+ when 'l' # \l is list database
717
+ listAllDbs(settings)
718
+
719
+ when 'H'
720
+ settings.opt.html3 =
721
+ toggle(settings, settings.opt.html3, "HTML3.0 tabular output")
722
+
723
+ if settings.opt.html3
724
+ settings.opt.standard = FALSE
725
+ end
726
+
727
+ when 'o'
728
+ setFout(settings, args[1])
729
+
730
+ when 'p'
731
+ if query
732
+ File.print(query)
733
+ File.print("\n")
734
+ end
735
+
736
+ when 'q' # \q is quit
737
+ status = 2
738
+
739
+ when 'r' # reset(clear) the buffer
740
+ query = nil
741
+ if !settings.quiet
742
+ printf(STDERR, "buffer reset(cleared)\n")
743
+ end
744
+
745
+ when 's' # \s is save history to a file
746
+ begin
747
+ if (args[1])
748
+ fd = File.open(args[1], "w")
749
+ else
750
+ fd = STDOUT
751
+ end
752
+ Readline::HISTORY.each do |his|
753
+ fd.write (his + "\n")
754
+ end
755
+ if !fd.tty?
756
+ begin
757
+ fd.close
758
+ end
759
+ end
760
+ rescue
761
+ printf(STDERR, "cannot write history \n");
762
+ end
763
+
764
+ when 'm' # monitor like type-setting
765
+ settings.opt.standard =
766
+ toggle(settings, settings.opt.standard, "standard SQL separaters and padding")
767
+ if settings.opt.standard
768
+ settings.opt.html3 = FALSE
769
+ settings.opt.expanded = FALSE
770
+ settings.opt.align = TRUE
771
+ settings.opt.header = TRUE
772
+ if settings.opt.fieldSep
773
+ settings.opt.fieldSep = ""
774
+ end
775
+ settings.opt.fieldSep = "|"
776
+ if !settings.quiet
777
+ printf(STDERR, "field separater changed to '%s'\n", settings.opt.fieldSep)
778
+ end
779
+ else
780
+ if settings.opt.fieldSep
781
+ settings.opt.fieldSep = ""
782
+ end
783
+ settings.opt.fieldSep = DEFAULT_FIELD_SEP
784
+ if !settings.quiet
785
+ printf(STDERR, "field separater changed to '%s'\n", settings.opt.fieldSep)
786
+ end
787
+ end
788
+
789
+ when 't' # toggle headers
790
+ settings.opt.header =
791
+ toggle(settings, settings.opt.header, "output headings and row count")
792
+
793
+ when 'T' # define html <table ...> option
794
+ if !args[1]
795
+ settings.opt.tableOpt = nil
796
+ else
797
+ settings.opt.tableOpt = args[1]
798
+ end
799
+
800
+ when 'x'
801
+ settings.opt.expanded =
802
+ toggle(settings, settings.opt.expanded, "expanded table representation")
803
+
804
+ when '!'
805
+ do_shell(args[1])
806
+
807
+ when '?' # \? is help
808
+ slashUsage(settings)
809
+ end
810
+
811
+ return status, query
812
+ end
813
+
814
+ def SendQuery(settings, query, copy_in, copy_out, copystream)
815
+ if settings.singleStep
816
+ printf("\n**************************************")
817
+ printf("*****************************************\n")
818
+ end
819
+
820
+ if (settings.echoQuery || settings.singleStep)
821
+ printf(STDERR, "QUERY: %s\n", query);
822
+ end
823
+
824
+ if settings.singleStep
825
+ printf("\n**************************************");
826
+ printf("*****************************************\n")
827
+ STDOUT.flush
828
+ printf("\npress return to continue ..\n");
829
+ gets("", STDIN);
830
+ end
831
+
832
+ begin
833
+ results = settings.db.exec(query)
834
+ case results.status
835
+ when PGresult::TUPLES_OK
836
+ success = TRUE
837
+ if settings.gfname
838
+ setFout(settings, settings.gfname)
839
+ settings.gfname = nil
840
+ results.print(settings.queryFout, settings.opt)
841
+ settings.queryFout.flush
842
+ if settings.queryFout != STDOUT
843
+ settings.queryFout.close
844
+ settings.queryFout = STDOUT
845
+ end
846
+ else
847
+ results.print(settings.queryFout, settings.opt)
848
+ settings.queryFout.flush
849
+ end
850
+ results.clear
851
+
852
+ when PGresult::EMPTY_QUERY
853
+ success = TRUE
854
+
855
+ when PGresult::COMMAND_OK
856
+ success = TRUE
857
+ if !settings.quiet
858
+ printf("%s\n", results.cmdstatus)
859
+ end
860
+
861
+ when PGresult::COPY_OUT
862
+ success = TRUE
863
+ if copy_out
864
+ handleCopyOut(settings, copystream)
865
+ else
866
+ if !settings.quiet
867
+ printf("Copy command returns...\n")
868
+ end
869
+
870
+ handleCopyOut(settings, STDOUT)
871
+ end
872
+
873
+ when PGresult::COPY_IN
874
+ success = TRUE
875
+ if copy_in
876
+ handleCopyIn(settings, FALSE, copystream)
877
+ else
878
+ handleCopyIn(settings, !settings.quiet, STDIN)
879
+ end
880
+ end
881
+
882
+ if (settings.db.status == PGconn::CONNECTION_BAD)
883
+ printf(STDERR, "We have lost the connection to the backend, so ")
884
+ printf(STDERR, "further processing is impossible. ")
885
+ printf(STDERR, "Terminating.\n")
886
+ exit(2)
887
+ end
888
+
889
+ # check for asynchronous returns
890
+ # notify = settings.db.notifies()
891
+ # if notify
892
+ # printf(STDERR,"ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
893
+ # notify.relname, notify.be_pid)
894
+ # end
895
+
896
+ rescue
897
+ printf(STDERR, "%s", $!)
898
+ success = FALSE
899
+ end
900
+
901
+ return success
902
+ end
903
+
904
+ def MainLoop(settings, source)
905
+
906
+ success = TRUE
907
+ interactive = TRUE
908
+ insideQuote = FALSE
909
+ querySent = FALSE
910
+ done = FALSE
911
+
912
+ query = nil
913
+ queryWaiting = nil
914
+ slashCmdStatus = -1
915
+ interactive = (source == STDIN && !settings.notty)
916
+
917
+ if settings.quiet
918
+ settings.prompt = nil
919
+ else
920
+ settings.prompt = settings.db.db + PROMPT
921
+ end
922
+
923
+ while !done
924
+ if slashCmdStatus == 3
925
+ line = query
926
+ query = nil
927
+ else
928
+ if interactive && !settings.quiet
929
+ if insideQuote
930
+ settings.prompt[settings.prompt.length-3,1] = "\'"
931
+ elsif (queryWaiting != nil && !querySent)
932
+ settings.prompt[settings.prompt.length-3,1] = "-"
933
+ else
934
+ settings.prompt[settings.prompt.length-3,1] = "="
935
+ end
936
+ end
937
+ line = gets(settings.prompt, source)
938
+ end
939
+
940
+ if line == nil
941
+ printf("EOF\n")
942
+ done = TRUE
943
+ else
944
+
945
+ ### debbegging information ###
946
+ if !interactive && !settings.singleStep && !settings.quiet
947
+ printf(STDERR, "%s\n", line)
948
+ end
949
+
950
+ ### ommit comment ###
951
+ begin_comment = line.index("--")
952
+ if begin_comment
953
+ line = line[0, begin_comment]
954
+ end
955
+
956
+ ### erase unnecessary characters ###
957
+ line.gsub!(/[ \t\f\n\r]+\z/, "")
958
+ if line.length == 0
959
+ next
960
+ end
961
+ ### begin slash command handling ###
962
+ if line[0, 1] == "\\"
963
+ query = line
964
+ slashCmdStatus, query = HandleSlashCmds(settings, line, nil)
965
+ if slashCmdStatus == 0 && query != nil
966
+ success = SendQuery(settings, query, FALSE, FALSE, 0) && success
967
+ querySent = TRUE
968
+ elsif slashCmdStatus == 1
969
+ query = nil
970
+ elsif slashCmdStatus == 2
971
+ break
972
+ end
973
+ line = nil
974
+ next
975
+ end
976
+
977
+ ### begin query command handling ###
978
+ slashCmdStatus = -1
979
+ if settings.singleLineMode
980
+ success = SendQuery(settings, line, FALSE, FALSE, 0) && success
981
+ querySent = TRUE
982
+ else
983
+
984
+ if queryWaiting
985
+ queryWaiting += " " + line
986
+ else
987
+ queryWaiting = line
988
+ end
989
+
990
+ for i in 0..line.length-1
991
+ if line[i, 1] == "\'"
992
+ insideQuote = !insideQuote
993
+ end
994
+ end
995
+
996
+ if !insideQuote
997
+ if line[line.length-1, 1] == ";"
998
+ query = queryWaiting
999
+ queryWaiting = nil
1000
+
1001
+ success = SendQuery(settings, query, FALSE, FALSE, 0) && success
1002
+ querySent = TRUE
1003
+ else
1004
+ querySent = FALSE
1005
+ end
1006
+ else
1007
+ querySent = FALSE
1008
+ end
1009
+ end
1010
+ end
1011
+ end # while
1012
+ return success
1013
+ end
1014
+
1015
+ def main
1016
+ dbname = nil
1017
+ host = "localhost"
1018
+ port = 5432
1019
+ qfilename = nil
1020
+
1021
+ singleQuery = nil
1022
+ settings = PsqlSettings.new(nil, nil, nil, nil, nil, FALSE, FALSE,
1023
+ FALSE, FALSE, FALSE, FALSE, FALSE)
1024
+ settings.opt = PrintOpt.new(FALSE, FALSE, FALSE, FALSE, FALSE,
1025
+ FALSE, nil, nil, nil, nil)
1026
+
1027
+ listDatabases = FALSE
1028
+ successResult = TRUE
1029
+ singleSlashCmd = FALSE
1030
+
1031
+ settings.opt.align = TRUE
1032
+ settings.opt.header = TRUE
1033
+ settings.queryFout = STDOUT
1034
+ settings.opt.fieldSep = DEFAULT_FIELD_SEP.dup
1035
+ settings.opt.pager = TRUE
1036
+ settings.quiet = FALSE
1037
+ settings.notty = FALSE
1038
+ settings.useReadline = TRUE
1039
+
1040
+ parsed = parseArgs(0, nil, "AelHnsqStx", "a:", "c:", "d:", "f:", "F:",
1041
+ "h:", "o:", "p:", "T:")
1042
+
1043
+ if $OPT_A
1044
+ settings.opt.align = FALSE
1045
+ end
1046
+
1047
+ if $OPT_a
1048
+ #fe_setauthsvc(optarg, errbuf);
1049
+ printf("not implemented, sorry.\n")
1050
+ exit(1)
1051
+ end
1052
+
1053
+ if $OPT_c
1054
+ singleQuery = $OPT_c
1055
+ if singleQuery[0, 1] == "\\"
1056
+ singleSlashCmd = TRUE
1057
+ end
1058
+ end
1059
+
1060
+ if $OPT_d
1061
+ dbname = $OPT_d
1062
+ end
1063
+
1064
+ if $OPT_e
1065
+ settings.echoQuery = TRUE
1066
+ end
1067
+
1068
+ if $OPT_f
1069
+ qfilename = $OPT_f
1070
+ end
1071
+
1072
+ if $OPT_F
1073
+ settings.opt.fieldSep = $OPT_F
1074
+ end
1075
+
1076
+ if $OPT_l
1077
+ listDatabases = TRUE
1078
+ end
1079
+
1080
+ if $OPT_h
1081
+ host = $OPT_h
1082
+ end
1083
+
1084
+ if $OPT_H
1085
+ settings.opt.html3 = TRUE
1086
+ end
1087
+
1088
+ if $OPT_n
1089
+ settings.useReadline = FALSE
1090
+ end
1091
+
1092
+ if $OPT_o
1093
+ setFout(settings, $OPT_o)
1094
+ end
1095
+
1096
+ if $OPT_p
1097
+ port = $OPT_p.to_i
1098
+ end
1099
+
1100
+ if $OPT_q
1101
+ settings.quiet = TRUE
1102
+ end
1103
+
1104
+ if $OPT_s
1105
+ settings.singleStep = TRUE
1106
+ end
1107
+
1108
+ if $OPT_S
1109
+ settings.singleLineMode = TRUE
1110
+ end
1111
+
1112
+ if $OPT_t
1113
+ settings.opt.header = FALSE
1114
+ end
1115
+
1116
+ if $OPT_T
1117
+ settings.opt.tableOpt = $OPT_T
1118
+ end
1119
+
1120
+ if $OPT_x
1121
+ settings.opt.expanded = TRUE
1122
+ end
1123
+
1124
+ if ARGV.length == 1
1125
+ dbname = ARGV[0]
1126
+ end
1127
+
1128
+ if listDatabases
1129
+ dbname = "template1"
1130
+ end
1131
+
1132
+ settings.db = PGconn.connect(host, port, "", "", dbname);
1133
+ dbname = settings.db.db
1134
+
1135
+ if settings.db.status() == PGconn::CONNECTION_BAD
1136
+ printf(STDERR, "Connection to database '%s' failed.\n", dbname)
1137
+ printf(STDERR, "%s", settings.db.error)
1138
+ exit(1)
1139
+ end
1140
+ if listDatabases
1141
+ exit(listAllDbs(settings))
1142
+ end
1143
+ if (!settings.quiet && !singleQuery && !qfilename)
1144
+ printf("Welcome to the POSTGRESQL interactive sql monitor:\n")
1145
+ printf(" Please read the file COPYRIGHT for copyright terms of POSTGRESQL\n\n")
1146
+ printf(" type \\? for help on slash commands\n")
1147
+ printf(" type \\q to quit\n")
1148
+ printf(" type \\g or terminate with semicolon to execute query\n")
1149
+ printf(" You are currently connected to the database: %s\n\n", dbname)
1150
+ end
1151
+ if (qfilename || singleSlashCmd)
1152
+ if singleSlashCmd
1153
+ line = singleQuery
1154
+ else
1155
+ line = sprintf("\\i %s", qfilename)
1156
+ end
1157
+ HandleSlashCmds(settings, line, "")
1158
+ else
1159
+ if settings.useReadline
1160
+ begin
1161
+ require "readline"
1162
+ $readline_ok = TRUE
1163
+ rescue
1164
+ $readline_ok = FALSE
1165
+ end
1166
+ else
1167
+ $readline_ok = FALSE
1168
+ end
1169
+ if singleQuery
1170
+ success = SendQuery(settings, singleQuery, false, false, 0)
1171
+ successResult = success
1172
+ else
1173
+ successResult = MainLoop(settings, STDIN)
1174
+ end
1175
+ end
1176
+ settings.db.finish()
1177
+ return !successResult
1178
+ end
1179
+
1180
+ main
1181
+