pg 0.11.0 → 0.12.0pre258
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/.gemtest +0 -0
- data/Contributors.rdoc +37 -0
- data/History.rdoc +60 -0
- data/Manifest.txt +39 -0
- data/README.OS_X.rdoc +68 -0
- data/README.ja.rdoc +7 -0
- data/{README → README.rdoc} +33 -18
- data/{README.windows → README.windows.rdoc} +20 -26
- data/Rakefile +98 -324
- data/ext/extconf.rb +7 -2
- data/ext/pg.c +173 -35
- data/ext/vc/pg.sln +26 -0
- data/ext/vc/pg_18/pg.vcproj +216 -0
- data/ext/vc/pg_19/pg_19.vcproj +209 -0
- data/lib/pg.rb +5 -7
- data/misc/openssl-pg-segfault.rb +31 -0
- data/sample/async_api.rb +109 -0
- data/sample/copyfrom.rb +81 -0
- data/sample/copyto.rb +23 -0
- data/sample/losample.rb +69 -0
- data/sample/notify_wait.rb +43 -0
- data/sample/psql.rb +1181 -0
- data/sample/psqlHelp.rb +158 -0
- data/sample/test1.rb +60 -0
- data/sample/test2.rb +44 -0
- data/sample/test4.rb +71 -0
- data/sample/test_binary_values.rb +35 -0
- data/spec/m17n_spec.rb +1 -1
- data/spec/pgconn_spec.rb +50 -5
- data/spec/pgresult_spec.rb +1 -1
- metadata +142 -66
- data.tar.gz.sig +0 -2
- data/ChangeLog +0 -693
- data/Contributors +0 -32
- data/README.OS_X +0 -19
- data/README.ja +0 -183
- data/Rakefile.local +0 -312
- data/rake/191_compat.rb +0 -26
- data/rake/dependencies.rb +0 -76
- data/rake/documentation.rb +0 -123
- data/rake/helpers.rb +0 -502
- data/rake/hg.rb +0 -318
- data/rake/manual.rb +0 -787
- data/rake/packaging.rb +0 -129
- data/rake/publishing.rb +0 -341
- data/rake/style.rb +0 -62
- data/rake/svn.rb +0 -668
- data/rake/testing.rb +0 -152
- data/rake/verifytask.rb +0 -64
- metadata.gz.sig +0 -0
data/sample/copyto.rb
ADDED
@@ -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
|
+
|
data/sample/losample.rb
ADDED
@@ -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
|
+
|
data/sample/psql.rb
ADDED
@@ -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
|
+
|