Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
0e6652e6
Commit
0e6652e6
authored
Nov 04, 1999
by
Bruce Momjian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
psql cleanup
parent
2323b636
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
5236 additions
and
8021 deletions
+5236
-8021
src/bin/psql/command.c
src/bin/psql/command.c
+1049
-904
src/bin/psql/command.h
src/bin/psql/command.h
+30
-33
src/bin/psql/common.c
src/bin/psql/common.c
+354
-321
src/bin/psql/common.h
src/bin/psql/common.h
+8
-8
src/bin/psql/copy.c
src/bin/psql/copy.c
+273
-253
src/bin/psql/copy.h
src/bin/psql/copy.h
+3
-3
src/bin/psql/describe.c
src/bin/psql/describe.c
+681
-639
src/bin/psql/describe.h
src/bin/psql/describe.h
+10
-10
src/bin/psql/help.c
src/bin/psql/help.c
+217
-205
src/bin/psql/help.h
src/bin/psql/help.h
+4
-5
src/bin/psql/input.c
src/bin/psql/input.c
+86
-70
src/bin/psql/input.h
src/bin/psql/input.h
+5
-5
src/bin/psql/large_obj.c
src/bin/psql/large_obj.c
+228
-199
src/bin/psql/large_obj.h
src/bin/psql/large_obj.h
+5
-5
src/bin/psql/mainloop.c
src/bin/psql/mainloop.c
+326
-289
src/bin/psql/mainloop.h
src/bin/psql/mainloop.h
+2
-2
src/bin/psql/print.c
src/bin/psql/print.c
+844
-760
src/bin/psql/print.h
src/bin/psql/print.h
+38
-32
src/bin/psql/prompt.c
src/bin/psql/prompt.c
+201
-184
src/bin/psql/prompt.h
src/bin/psql/prompt.h
+11
-10
src/bin/psql/psql.c
src/bin/psql/psql.c
+0
-3297
src/bin/psql/psqlHelp.h
src/bin/psql/psqlHelp.h
+3
-2
src/bin/psql/settings.h
src/bin/psql/settings.h
+21
-18
src/bin/psql/sql_help.h
src/bin/psql/sql_help.h
+182
-182
src/bin/psql/startup.c
src/bin/psql/startup.c
+411
-376
src/bin/psql/stringutils.c
src/bin/psql/stringutils.c
+133
-116
src/bin/psql/stringutils.h
src/bin/psql/stringutils.h
+6
-7
src/bin/psql/variables.c
src/bin/psql/variables.c
+93
-75
src/bin/psql/variables.h
src/bin/psql/variables.h
+12
-11
No files found.
src/bin/psql/command.c
View file @
0e6652e6
...
@@ -7,9 +7,9 @@
...
@@ -7,9 +7,9 @@
#include <stdlib.h>
#include <stdlib.h>
#include <ctype.h>
#include <ctype.h>
#ifndef WIN32
#ifndef WIN32
#include <sys/types.h>
/* for umask() */
#include <sys/types.h>
/* for umask() */
#include <sys/stat.h>
/* for umask(), stat() */
#include <sys/stat.h>
/* for umask(), stat() */
#include <unistd.h>
/* for geteuid(), getpid(), stat() */
#include <unistd.h>
/* for geteuid(), getpid(), stat() */
#endif
#endif
#include <assert.h>
#include <assert.h>
...
@@ -35,21 +35,20 @@
...
@@ -35,21 +35,20 @@
/* functions for use in this file only */
/* functions for use in this file only */
static
backslashResult
static
backslashResult
exec_command
(
const
char
*
cmd
,
exec_command
(
const
char
*
cmd
,
char
*
const
*
options
,
char
*
const
*
options
,
const
char
*
options_string
,
const
char
*
options_string
,
PQExpBuffer
query_buf
,
PQExpBuffer
query_buf
,
PsqlSettings
*
pset
);
PsqlSettings
*
pset
);
static
bool
static
bool
do_edit
(
const
char
*
filename_arg
,
PQExpBuffer
query_buf
);
do_edit
(
const
char
*
filename_arg
,
PQExpBuffer
query_buf
);
static
char
*
static
char
*
unescape
(
const
char
*
source
,
PsqlSettings
*
pset
);
unescape
(
const
char
*
source
,
PsqlSettings
*
pset
);
static
bool
static
bool
do_shell
(
const
char
*
command
);
do_shell
(
const
char
*
command
);
...
@@ -72,538 +71,616 @@ do_shell(const char *command);
...
@@ -72,538 +71,616 @@ do_shell(const char *command);
backslashResult
backslashResult
HandleSlashCmds
(
PsqlSettings
*
pset
,
HandleSlashCmds
(
PsqlSettings
*
pset
,
const
char
*
line
,
const
char
*
line
,
PQExpBuffer
query_buf
,
PQExpBuffer
query_buf
,
const
char
**
end_of_cmd
)
const
char
**
end_of_cmd
)
{
{
backslashResult
status
=
CMD_SKIP_LINE
;
backslashResult
status
=
CMD_SKIP_LINE
;
char
*
my_line
;
char
*
my_line
;
char
*
options
[
17
]
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
char
*
options
[
17
]
=
{
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
};
char
*
token
;
char
*
token
;
const
char
*
options_string
=
NULL
;
const
char
*
options_string
=
NULL
;
const
char
*
cmd
;
const
char
*
cmd
;
size_t
blank_loc
;
size_t
blank_loc
;
int
i
;
int
i
;
const
char
*
continue_parse
=
NULL
;
/* tell the mainloop where the backslash command ended */
const
char
*
continue_parse
=
NULL
;
/* tell the mainloop where the
* backslash command ended */
my_line
=
xstrdup
(
line
);
my_line
=
xstrdup
(
line
);
/* Find the first whitespace (or backslash)
line[blank_loc] will now be the whitespace character
/*
or the \0 at the end */
* Find the first whitespace (or backslash) line[blank_loc] will now
blank_loc
=
strcspn
(
my_line
,
"
\t
"
);
* be the whitespace character or the \0 at the end
*/
/* do we have an option string? */
blank_loc
=
strcspn
(
my_line
,
"
\t
"
);
if
(
my_line
[
blank_loc
]
!=
'\0'
)
{
options_string
=
&
my_line
[
blank_loc
+
1
];
/* do we have an option string? */
if
(
my_line
[
blank_loc
]
!=
'\0'
)
my_line
[
blank_loc
]
=
'\0'
;
{
}
options_string
=
&
my_line
[
blank_loc
+
1
];
if
(
options_string
)
{
char
quote
;
unsigned
int
pos
;
options_string
=
&
options_string
[
strspn
(
options_string
,
"
\t
"
)];
/* skip leading whitespace */
i
=
0
;
token
=
strtokx
(
options_string
,
"
\t
"
,
"
\"
'`"
,
'\\'
,
&
quote
,
&
pos
);
for
(
i
=
0
;
token
&&
i
<
16
;
i
++
)
{
switch
(
quote
)
{
case
'"'
:
options
[
i
]
=
unescape
(
token
,
pset
);
break
;
case
'\''
:
options
[
i
]
=
xstrdup
(
token
);
break
;
case
'`'
:
{
bool
error
=
false
;
FILE
*
fd
=
NULL
;
char
*
file
=
unescape
(
token
,
pset
);
PQExpBufferData
output
;
char
buf
[
512
];
size_t
result
;
fd
=
popen
(
file
,
"r"
);
if
(
!
fd
)
{
perror
(
file
);
error
=
true
;
}
if
(
!
error
)
{
my_line
[
blank_loc
]
=
'\0'
;
initPQExpBuffer
(
&
output
);
}
do
{
if
(
options_string
)
result
=
fread
(
buf
,
1
,
512
,
fd
);
{
if
(
ferror
(
fd
))
{
char
quote
;
perror
(
file
);
unsigned
int
pos
;
error
=
true
;
break
;
options_string
=
&
options_string
[
strspn
(
options_string
,
"
\t
"
)];
/* skip leading
* whitespace */
i
=
0
;
token
=
strtokx
(
options_string
,
"
\t
"
,
"
\"
'`"
,
'\\'
,
&
quote
,
&
pos
);
for
(
i
=
0
;
token
&&
i
<
16
;
i
++
)
{
switch
(
quote
)
{
case
'"'
:
options
[
i
]
=
unescape
(
token
,
pset
);
break
;
case
'\''
:
options
[
i
]
=
xstrdup
(
token
);
break
;
case
'`'
:
{
bool
error
=
false
;
FILE
*
fd
=
NULL
;
char
*
file
=
unescape
(
token
,
pset
);
PQExpBufferData
output
;
char
buf
[
512
];
size_t
result
;
fd
=
popen
(
file
,
"r"
);
if
(
!
fd
)
{
perror
(
file
);
error
=
true
;
}
if
(
!
error
)
{
initPQExpBuffer
(
&
output
);
do
{
result
=
fread
(
buf
,
1
,
512
,
fd
);
if
(
ferror
(
fd
))
{
perror
(
file
);
error
=
true
;
break
;
}
appendBinaryPQExpBuffer
(
&
output
,
buf
,
result
);
}
while
(
!
feof
(
fd
));
appendPQExpBufferChar
(
&
output
,
'\0'
);
if
(
pclose
(
fd
)
==
-
1
)
{
perror
(
file
);
error
=
true
;
}
}
if
(
!
error
)
{
if
(
output
.
data
[
strlen
(
output
.
data
)
-
1
]
==
'\n'
)
output
.
data
[
strlen
(
output
.
data
)
-
1
]
=
'\0'
;
}
free
(
file
);
if
(
!
error
)
options
[
i
]
=
output
.
data
;
else
{
options
[
i
]
=
xstrdup
(
""
);
termPQExpBuffer
(
&
output
);
}
break
;
}
case
0
:
default:
if
(
token
[
0
]
==
'\\'
)
continue_parse
=
options_string
+
pos
;
else
if
(
token
[
0
]
==
'$'
)
options
[
i
]
=
xstrdup
(
interpolate_var
(
token
+
1
,
pset
));
else
options
[
i
]
=
xstrdup
(
token
);
break
;
}
}
appendBinaryPQExpBuffer
(
&
output
,
buf
,
result
);
}
while
(
!
feof
(
fd
));
appendPQExpBufferChar
(
&
output
,
'\0'
);
if
(
pclose
(
fd
)
==
-
1
)
{
perror
(
file
);
error
=
true
;
}
}
if
(
!
error
)
{
if
(
continue_parse
)
if
(
output
.
data
[
strlen
(
output
.
data
)
-
1
]
==
'\n'
)
break
;
output
.
data
[
strlen
(
output
.
data
)
-
1
]
=
'\0'
;
}
free
(
file
);
token
=
strtokx
(
NULL
,
"
\t
"
,
"
\"
'`"
,
'\\'
,
&
quote
,
&
pos
);
if
(
!
error
)
options
[
i
]
=
output
.
data
;
else
{
options
[
i
]
=
xstrdup
(
""
);
termPQExpBuffer
(
&
output
);
}
}
break
;
}
case
0
:
default:
if
(
token
[
0
]
==
'\\'
)
continue_parse
=
options_string
+
pos
;
else
if
(
token
[
0
]
==
'$'
)
options
[
i
]
=
xstrdup
(
interpolate_var
(
token
+
1
,
pset
));
else
options
[
i
]
=
xstrdup
(
token
);
break
;
}
if
(
continue_parse
)
break
;
token
=
strtokx
(
NULL
,
"
\t
"
,
"
\"
'`"
,
'\\'
,
&
quote
,
&
pos
);
}
}
}
cmd
=
my_line
;
cmd
=
my_line
;
status
=
exec_command
(
cmd
,
options
,
options_string
,
query_buf
,
pset
);
status
=
exec_command
(
cmd
,
options
,
options_string
,
query_buf
,
pset
);
if
(
status
==
CMD_UNKNOWN
)
{
if
(
status
==
CMD_UNKNOWN
)
/* If the command was not recognized, try inserting a space after
{
the first letter and call again. The one letter commands
allow arguments to start immediately after the command,
but that is no longer encouraged. */
const
char
*
new_options
[
17
];
char
new_cmd
[
2
];
int
i
;
for
(
i
=
1
;
i
<
17
;
i
++
)
/*
new_options
[
i
]
=
options
[
i
-
1
];
* If the command was not recognized, try inserting a space after
new_options
[
0
]
=
cmd
+
1
;
* the first letter and call again. The one letter commands allow
* arguments to start immediately after the command, but that is
* no longer encouraged.
*/
const
char
*
new_options
[
17
];
char
new_cmd
[
2
];
int
i
;
new_cmd
[
0
]
=
cmd
[
0
];
for
(
i
=
1
;
i
<
17
;
i
++
)
new_cmd
[
1
]
=
'\0'
;
new_options
[
i
]
=
options
[
i
-
1
];
new_options
[
0
]
=
cmd
+
1
;
new_cmd
[
0
]
=
cmd
[
0
];
new_cmd
[
1
]
=
'\0'
;
status
=
exec_command
(
new_cmd
,
(
char
*
const
*
)
new_options
,
my_line
+
2
,
query_buf
,
pset
);
}
status
=
exec_command
(
new_cmd
,
(
char
*
const
*
)
new_options
,
my_line
+
2
,
query_buf
,
pset
);
if
(
status
==
CMD_UNKNOWN
)
}
{
fprintf
(
stderr
,
"Unrecognized command:
\\
%s. Try
\\
? for help.
\n
"
,
cmd
);
status
=
CMD_ERROR
;
}
if
(
status
==
CMD_UNKNOWN
)
{
if
(
continue_parse
&&
*
(
continue_parse
+
1
)
==
'\\'
)
fprintf
(
stderr
,
"Unrecognized command:
\\
%s. Try
\\
? for help.
\n
"
,
cmd
);
continue_parse
+=
2
;
status
=
CMD_ERROR
;
}
if
(
continue_parse
&&
*
(
continue_parse
+
1
)
==
'\\'
)
continue_parse
+=
2
;
if
(
end_of_cmd
)
{
if
(
end_of_cmd
)
if
(
continue_parse
)
{
*
end_of_cmd
=
line
+
(
continue_parse
-
my_line
);
if
(
continue_parse
)
else
*
end_of_cmd
=
line
+
(
continue_parse
-
my_line
);
*
end_of_cmd
=
NULL
;
else
}
*
end_of_cmd
=
NULL
;
}
/* clean up */
/* clean up */
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
free
(
options
[
i
]);
free
(
options
[
i
]);
free
(
my_line
);
free
(
my_line
);
return
status
;
return
status
;
}
}
static
backslashResult
static
backslashResult
exec_command
(
const
char
*
cmd
,
exec_command
(
const
char
*
cmd
,
char
*
const
*
options
,
char
*
const
*
options
,
const
char
*
options_string
,
const
char
*
options_string
,
PQExpBuffer
query_buf
,
PQExpBuffer
query_buf
,
PsqlSettings
*
pset
)
PsqlSettings
*
pset
)
{
{
bool
success
=
true
;
/* indicate here if the command ran ok or failed */
bool
success
=
true
;
/* indicate here if the command ran ok or
bool
quiet
=
GetVariableBool
(
pset
->
vars
,
"quiet"
);
* failed */
bool
quiet
=
GetVariableBool
(
pset
->
vars
,
"quiet"
);
backslashResult
status
=
CMD_SKIP_LINE
;
backslashResult
status
=
CMD_SKIP_LINE
;
/* \a -- toggle field alignment
/*
This is deprecated and makes no sense, but we keep it around. */
* \a -- toggle field alignment This is deprecated and makes no sense,
if
(
strcmp
(
cmd
,
"a"
)
==
0
)
{
* but we keep it around.
if
(
pset
->
popt
.
topt
.
format
!=
PRINT_ALIGNED
)
*/
success
=
do_pset
(
"format"
,
"aligned"
,
&
pset
->
popt
,
quiet
);
if
(
strcmp
(
cmd
,
"a"
)
==
0
)
else
{
success
=
do_pset
(
"format"
,
"unaligned"
,
&
pset
->
popt
,
quiet
);
if
(
pset
->
popt
.
topt
.
format
!=
PRINT_ALIGNED
)
}
success
=
do_pset
(
"format"
,
"aligned"
,
&
pset
->
popt
,
quiet
);
else
success
=
do_pset
(
"format"
,
"unaligned"
,
&
pset
->
popt
,
quiet
);
/* \C -- override table title
(formerly change HTML caption) This is deprecated. */
else
if
(
strcmp
(
cmd
,
"C"
)
==
0
)
success
=
do_pset
(
"title"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
/* \c or \connect -- connect to new database or as different user
*
* \c foo bar : connect to db "foo" as user "bar"
* \c foo [-] : connect to db "foo" as current user
* \c - bar : connect to current db as user "bar"
* \c : connect to default db as default user
*/
else
if
(
strcmp
(
cmd
,
"c"
)
==
0
||
strcmp
(
cmd
,
"connect"
)
==
0
)
{
if
(
options
[
1
])
/* gave username */
success
=
do_connect
(
options
[
0
],
options
[
1
],
pset
);
else
{
if
(
options
[
0
])
/* gave database name */
success
=
do_connect
(
options
[
0
],
""
,
pset
);
/* empty string is same username as before,
NULL would mean libpq default */
else
/* connect to default db as default user */
success
=
do_connect
(
NULL
,
NULL
,
pset
);
}
}
}
/*
/* \copy */
* \C -- override table title (formerly change HTML caption) This is
else
if
(
strcmp
(
cmd
,
"copy"
)
==
0
)
* deprecated.
success
=
do_copy
(
options_string
,
pset
);
*/
else
if
(
strcmp
(
cmd
,
"C"
)
==
0
)
/* \copyright */
success
=
do_pset
(
"title"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
else
if
(
strcmp
(
cmd
,
"copyright"
)
==
0
)
print_copyright
();
/* \d* commands */
/*
else
if
(
cmd
[
0
]
==
'd'
)
{
* \c or \connect -- connect to new database or as different user
switch
(
cmd
[
1
])
{
*
case
'\0'
:
* \c foo bar : connect to db "foo" as user "bar" \c foo [-] :
if
(
options
[
0
])
* connect to db "foo" as current user \c - bar : connect to
success
=
describeTableDetails
(
options
[
0
],
pset
);
* current db as user "bar" \c : connect to default db as
else
* default user
success
=
listTables
(
"tvs"
,
NULL
,
pset
);
/* standard listing of interesting things */
*/
break
;
else
if
(
strcmp
(
cmd
,
"c"
)
==
0
||
strcmp
(
cmd
,
"connect"
)
==
0
)
case
'a'
:
{
success
=
describeAggregates
(
options
[
0
],
pset
);
if
(
options
[
1
])
break
;
/* gave username */
case
'd'
:
success
=
do_connect
(
options
[
0
],
options
[
1
],
pset
);
success
=
objectDescription
(
options
[
0
],
pset
);
else
break
;
{
case
'f'
:
if
(
options
[
0
])
success
=
describeFunctions
(
options
[
0
],
pset
);
/* gave database name */
break
;
success
=
do_connect
(
options
[
0
],
""
,
pset
);
/* empty string is same
case
'l'
:
* username as before,
success
=
do_lo_list
(
pset
);
* NULL would mean libpq
break
;
* default */
case
'o'
:
else
success
=
describeOperators
(
options
[
0
],
pset
);
/* connect to default db as default user */
break
;
success
=
do_connect
(
NULL
,
NULL
,
pset
);
case
'p'
:
}
success
=
permissionsList
(
options
[
0
],
pset
);
break
;
case
'T'
:
success
=
describeTypes
(
options
[
0
],
pset
);
break
;
case
't'
:
case
'v'
:
case
'i'
:
case
's'
:
case
'S'
:
if
(
cmd
[
1
]
==
'S'
&&
cmd
[
2
]
==
'\0'
)
success
=
listTables
(
"Stvs"
,
NULL
,
pset
);
else
success
=
listTables
(
&
cmd
[
1
],
options
[
0
],
pset
);
break
;
default:
status
=
CMD_UNKNOWN
;
}
}
}
/* \e or \edit -- edit the current query buffer (or a file and make it the
/* \copy */
query buffer */
else
if
(
strcmp
(
cmd
,
"copy"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"e"
)
==
0
||
strcmp
(
cmd
,
"edit"
)
==
0
)
success
=
do_copy
(
options_string
,
pset
);
status
=
do_edit
(
options
[
0
],
query_buf
)
?
CMD_NEWEDIT
:
CMD_ERROR
;
/* \copyright */
else
if
(
strcmp
(
cmd
,
"copyright"
)
==
0
)
print_copyright
();
/* \echo */
/* \d* commands */
else
if
(
strcmp
(
cmd
,
"echo"
)
==
0
)
{
else
if
(
cmd
[
0
]
==
'd'
)
int
i
;
{
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
switch
(
cmd
[
1
])
fputs
(
options
[
i
],
stdout
);
{
fputs
(
"
\n
"
,
stdout
);
case
'\0'
:
}
if
(
options
[
0
])
success
=
describeTableDetails
(
options
[
0
],
pset
);
else
success
=
listTables
(
"tvs"
,
NULL
,
pset
);
/* standard listing of
* interesting things */
break
;
case
'a'
:
success
=
describeAggregates
(
options
[
0
],
pset
);
break
;
case
'd'
:
success
=
objectDescription
(
options
[
0
],
pset
);
break
;
case
'f'
:
success
=
describeFunctions
(
options
[
0
],
pset
);
break
;
case
'l'
:
success
=
do_lo_list
(
pset
);
break
;
case
'o'
:
success
=
describeOperators
(
options
[
0
],
pset
);
break
;
case
'p'
:
success
=
permissionsList
(
options
[
0
],
pset
);
break
;
case
'T'
:
success
=
describeTypes
(
options
[
0
],
pset
);
break
;
case
't'
:
case
'v'
:
case
'i'
:
case
's'
:
case
'S'
:
if
(
cmd
[
1
]
==
'S'
&&
cmd
[
2
]
==
'\0'
)
success
=
listTables
(
"Stvs"
,
NULL
,
pset
);
else
success
=
listTables
(
&
cmd
[
1
],
options
[
0
],
pset
);
break
;
default:
status
=
CMD_UNKNOWN
;
}
}
/* \f -- change field separator
(This is deprecated in favour of \pset.) */
else
if
(
strcmp
(
cmd
,
"f"
)
==
0
)
success
=
do_pset
(
"fieldsep"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
/*
* \e or \edit -- edit the current query buffer (or a file and make it
* the query buffer
*/
else
if
(
strcmp
(
cmd
,
"e"
)
==
0
||
strcmp
(
cmd
,
"edit"
)
==
0
)
status
=
do_edit
(
options
[
0
],
query_buf
)
?
CMD_NEWEDIT
:
CMD_ERROR
;
/* \g means send query */
else
if
(
strcmp
(
cmd
,
"g"
)
==
0
)
{
if
(
!
options
[
0
])
pset
->
gfname
=
NULL
;
else
pset
->
gfname
=
xstrdup
(
options
[
0
]);
status
=
CMD_SEND
;
}
/* help */
/* \echo */
else
if
(
strcmp
(
cmd
,
"h"
)
==
0
||
strcmp
(
cmd
,
"help"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"echo"
)
==
0
)
helpSQL
(
options_string
);
{
int
i
;
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
fputs
(
options
[
i
],
stdout
);
fputs
(
"
\n
"
,
stdout
);
}
/* HTML mode */
/*
else
if
(
strcmp
(
cmd
,
"H"
)
==
0
||
strcmp
(
cmd
,
"html"
)
==
0
)
* \f -- change field separator (This is deprecated in favour of
success
=
do_pset
(
"format"
,
"html"
,
&
pset
->
popt
,
quiet
);
* \pset.)
*/
else
if
(
strcmp
(
cmd
,
"f"
)
==
0
)
success
=
do_pset
(
"fieldsep"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
/* \i is include file */
/* \g means send query */
else
if
(
strcmp
(
cmd
,
"i"
)
==
0
||
strcmp
(
cmd
,
"include"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"g"
)
==
0
)
{
{
if
(
!
options
[
0
])
{
if
(
!
options
[
0
])
fputs
(
"Usage:
\\
i <filename>
\n
"
,
stderr
);
pset
->
gfname
=
NULL
;
success
=
false
;
else
pset
->
gfname
=
xstrdup
(
options
[
0
]);
status
=
CMD_SEND
;
}
}
else
success
=
process_file
(
options
[
0
],
pset
);
/* help */
}
else
if
(
strcmp
(
cmd
,
"h"
)
==
0
||
strcmp
(
cmd
,
"help"
)
==
0
)
helpSQL
(
options_string
);
/* \l is list databases */
else
if
(
strcmp
(
cmd
,
"l"
)
==
0
||
strcmp
(
cmd
,
"list"
)
==
0
)
success
=
listAllDbs
(
pset
);
/* HTML mode */
else
if
(
strcmp
(
cmd
,
"H"
)
==
0
||
strcmp
(
cmd
,
"html"
)
==
0
)
success
=
do_pset
(
"format"
,
"html"
,
&
pset
->
popt
,
quiet
);
/* large object things */
else
if
(
strncmp
(
cmd
,
"lo_"
,
3
)
==
0
)
{
if
(
strcmp
(
cmd
+
3
,
"export"
)
==
0
)
{
/* \i is include file */
if
(
!
options
[
1
])
{
else
if
(
strcmp
(
cmd
,
"i"
)
==
0
||
strcmp
(
cmd
,
"include"
)
==
0
)
fputs
(
"Usage:
\\
lo_export <loid> <filename>
\n
"
,
stderr
);
{
success
=
false
;
if
(
!
options
[
0
])
}
{
else
fputs
(
"Usage:
\\
i <filename>
\n
"
,
stderr
);
success
=
do_lo_export
(
pset
,
options
[
0
],
options
[
1
]);
success
=
false
;
}
else
success
=
process_file
(
options
[
0
],
pset
);
}
}
else
if
(
strcmp
(
cmd
+
3
,
"import"
)
==
0
)
{
/* \l is list databases */
if
(
!
options
[
0
])
{
else
if
(
strcmp
(
cmd
,
"l"
)
==
0
||
strcmp
(
cmd
,
"list"
)
==
0
)
fputs
(
"Usage:
\\
lo_import <filename> [<description>]
\n
"
,
stderr
);
success
=
listAllDbs
(
pset
);
success
=
false
;
}
else
/* large object things */
success
=
do_lo_import
(
pset
,
options
[
0
],
options
[
1
]);
else
if
(
strncmp
(
cmd
,
"lo_"
,
3
)
==
0
)
{
if
(
strcmp
(
cmd
+
3
,
"export"
)
==
0
)
{
if
(
!
options
[
1
])
{
fputs
(
"Usage:
\\
lo_export <loid> <filename>
\n
"
,
stderr
);
success
=
false
;
}
else
success
=
do_lo_export
(
pset
,
options
[
0
],
options
[
1
]);
}
else
if
(
strcmp
(
cmd
+
3
,
"import"
)
==
0
)
{
if
(
!
options
[
0
])
{
fputs
(
"Usage:
\\
lo_import <filename> [<description>]
\n
"
,
stderr
);
success
=
false
;
}
else
success
=
do_lo_import
(
pset
,
options
[
0
],
options
[
1
]);
}
else
if
(
strcmp
(
cmd
+
3
,
"list"
)
==
0
)
success
=
do_lo_list
(
pset
);
else
if
(
strcmp
(
cmd
+
3
,
"unlink"
)
==
0
)
{
if
(
!
options
[
0
])
{
fputs
(
"Usage:
\\
lo_unlink <loid>
\n
"
,
stderr
);
success
=
false
;
}
else
success
=
do_lo_unlink
(
pset
,
options
[
0
]);
}
else
status
=
CMD_UNKNOWN
;
}
}
else
if
(
strcmp
(
cmd
+
3
,
"list"
)
==
0
)
/* \o -- set query output */
success
=
do_lo_list
(
pset
);
else
if
(
strcmp
(
cmd
,
"o"
)
==
0
||
strcmp
(
cmd
,
"out"
)
==
0
)
success
=
setQFout
(
options
[
0
],
pset
);
else
if
(
strcmp
(
cmd
+
3
,
"unlink"
)
==
0
)
{
if
(
!
options
[
0
])
{
/* \p prints the current query buffer */
fputs
(
"Usage:
\\
lo_unlink <loid>
\n
"
,
stderr
);
else
if
(
strcmp
(
cmd
,
"p"
)
==
0
||
strcmp
(
cmd
,
"print"
)
==
0
)
success
=
false
;
{
}
if
(
query_buf
&&
query_buf
->
len
>
0
)
else
puts
(
query_buf
->
data
);
success
=
do_lo_unlink
(
pset
,
options
[
0
]);
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
puts
(
"Query buffer is empty."
);
}
}
else
/* \pset -- set printing parameters */
status
=
CMD_UNKNOWN
;
else
if
(
strcmp
(
cmd
,
"pset"
)
==
0
)
}
{
if
(
!
options
[
0
])
/* \o -- set query output */
{
else
if
(
strcmp
(
cmd
,
"o"
)
==
0
||
strcmp
(
cmd
,
"out"
)
==
0
)
fputs
(
"Usage:
\\
pset <parameter> [<value>]
\n
"
,
stderr
);
success
=
setQFout
(
options
[
0
],
pset
);
success
=
false
;
}
else
/* \p prints the current query buffer */
success
=
do_pset
(
options
[
0
],
options
[
1
],
&
pset
->
popt
,
quiet
);
else
if
(
strcmp
(
cmd
,
"p"
)
==
0
||
strcmp
(
cmd
,
"print"
)
==
0
)
{
if
(
query_buf
&&
query_buf
->
len
>
0
)
puts
(
query_buf
->
data
);
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
puts
(
"Query buffer is empty."
);
}
/* \pset -- set printing parameters */
else
if
(
strcmp
(
cmd
,
"pset"
)
==
0
)
{
if
(
!
options
[
0
])
{
fputs
(
"Usage:
\\
pset <parameter> [<value>]
\n
"
,
stderr
);
success
=
false
;
}
}
else
success
=
do_pset
(
options
[
0
],
options
[
1
],
&
pset
->
popt
,
quiet
);
/* \q or \quit */
}
else
if
(
strcmp
(
cmd
,
"q"
)
==
0
||
strcmp
(
cmd
,
"quit"
)
==
0
)
status
=
CMD_TERMINATE
;
/* \q or \quit */
else
if
(
strcmp
(
cmd
,
"q"
)
==
0
||
strcmp
(
cmd
,
"quit"
)
==
0
)
/* \qecho */
status
=
CMD_TERMINATE
;
else
if
(
strcmp
(
cmd
,
"qecho"
)
==
0
)
{
/* \qecho */
int
i
;
else
if
(
strcmp
(
cmd
,
"qecho"
)
==
0
)
{
int
i
;
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
for
(
i
=
0
;
i
<
16
&&
options
[
i
];
i
++
)
fputs
(
options
[
i
],
pset
->
queryFout
);
fputs
(
options
[
i
],
pset
->
queryFout
);
fputs
(
"
\n
"
,
pset
->
queryFout
);
fputs
(
"
\n
"
,
pset
->
queryFout
);
}
/* reset(clear) the buffer */
else
if
(
strcmp
(
cmd
,
"r"
)
==
0
||
strcmp
(
cmd
,
"reset"
)
==
0
)
{
resetPQExpBuffer
(
query_buf
);
if
(
!
quiet
)
puts
(
"Query buffer reset (cleared)."
);
}
/* \s save history in a file or show it on the screen */
else
if
(
strcmp
(
cmd
,
"s"
)
==
0
)
{
const
char
*
fname
;
if
(
!
options
[
0
])
fname
=
"/dev/tty"
;
else
fname
=
options
[
0
];
success
=
saveHistory
(
fname
);
if
(
success
&&
!
quiet
&&
options
[
0
])
printf
(
"Wrote history to %s.
\n
"
,
fname
);
}
/* \set -- generalized set option command */
else
if
(
strcmp
(
cmd
,
"set"
)
==
0
)
{
if
(
!
options
[
0
])
{
/* list all variables */
/* (This is in utter violation of the GetVariable abstraction, but
I have not dreamt up a better way.) */
struct
_variable
*
ptr
;
for
(
ptr
=
pset
->
vars
;
ptr
->
next
;
ptr
=
ptr
->
next
)
fprintf
(
stdout
,
"%s = '%s'
\n
"
,
ptr
->
next
->
name
,
ptr
->
next
->
value
);
success
=
true
;
}
}
else
{
if
(
!
SetVariable
(
pset
->
vars
,
options
[
0
],
options
[
1
]))
{
/* reset(clear) the buffer */
fprintf
(
stderr
,
"Set variable failed.
\n
"
);
else
if
(
strcmp
(
cmd
,
"r"
)
==
0
||
strcmp
(
cmd
,
"reset"
)
==
0
)
success
=
false
;
{
}
resetPQExpBuffer
(
query_buf
);
if
(
!
quiet
)
puts
(
"Query buffer reset (cleared)."
);
}
}
}
/* \t -- turn off headers and row count */
else
if
(
strcmp
(
cmd
,
"t"
)
==
0
)
success
=
do_pset
(
"tuples_only"
,
NULL
,
&
pset
->
popt
,
quiet
);
/* \s save history in a file or show it on the screen */
else
if
(
strcmp
(
cmd
,
"s"
)
==
0
)
{
const
char
*
fname
;
if
(
!
options
[
0
])
fname
=
"/dev/tty"
;
else
fname
=
options
[
0
];
/* \T -- define html <table ...> attributes */
success
=
saveHistory
(
fname
);
else
if
(
strcmp
(
cmd
,
"T"
)
==
0
)
success
=
do_pset
(
"tableattr"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
if
(
success
&&
!
quiet
&&
options
[
0
])
printf
(
"Wrote history to %s.
\n
"
,
fname
);
}
/* \w -- write query buffer to file */
else
if
(
strcmp
(
cmd
,
"w"
)
==
0
||
strcmp
(
cmd
,
"write"
)
==
0
)
{
FILE
*
fd
=
NULL
;
bool
pipe
=
false
;
if
(
!
options
[
0
])
{
/* \set -- generalized set option command */
fprintf
(
stderr
,
"Usage
\\
%s <filename>
\n
"
,
cmd
);
else
if
(
strcmp
(
cmd
,
"set"
)
==
0
)
success
=
false
;
{
if
(
!
options
[
0
])
{
/* list all variables */
/*
* (This is in utter violation of the GetVariable abstraction,
* but I have not dreamt up a better way.)
*/
struct
_variable
*
ptr
;
for
(
ptr
=
pset
->
vars
;
ptr
->
next
;
ptr
=
ptr
->
next
)
fprintf
(
stdout
,
"%s = '%s'
\n
"
,
ptr
->
next
->
name
,
ptr
->
next
->
value
);
success
=
true
;
}
else
{
if
(
!
SetVariable
(
pset
->
vars
,
options
[
0
],
options
[
1
]))
{
fprintf
(
stderr
,
"Set variable failed.
\n
"
);
success
=
false
;
}
}
}
}
else
{
if
(
options
[
0
][
0
]
==
'|'
)
{
/* \t -- turn off headers and row count */
pipe
=
true
;
else
if
(
strcmp
(
cmd
,
"t"
)
==
0
)
success
=
do_pset
(
"tuples_only"
,
NULL
,
&
pset
->
popt
,
quiet
);
/* \T -- define html <table ...> attributes */
else
if
(
strcmp
(
cmd
,
"T"
)
==
0
)
success
=
do_pset
(
"tableattr"
,
options
[
0
],
&
pset
->
popt
,
quiet
);
/* \w -- write query buffer to file */
else
if
(
strcmp
(
cmd
,
"w"
)
==
0
||
strcmp
(
cmd
,
"write"
)
==
0
)
{
FILE
*
fd
=
NULL
;
bool
pipe
=
false
;
if
(
!
options
[
0
])
{
fprintf
(
stderr
,
"Usage
\\
%s <filename>
\n
"
,
cmd
);
success
=
false
;
}
else
{
if
(
options
[
0
][
0
]
==
'|'
)
{
pipe
=
true
;
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
fd
=
popen
(
&
options
[
0
][
1
],
"w"
);
fd
=
popen
(
&
options
[
0
][
1
],
"w"
);
#else
#else
fd
=
popen
(
&
options
[
0
][
1
],
"wb"
);
fd
=
popen
(
&
options
[
0
][
1
],
"wb"
);
#endif
#endif
}
}
else
{
else
{
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
fd
=
fopen
(
options
[
0
],
"w"
);
fd
=
fopen
(
options
[
0
],
"w"
);
#else
#else
fd
=
fopen
(
options
[
0
],
"wb"
);
fd
=
fopen
(
options
[
0
],
"wb"
);
#endif
#endif
}
}
if
(
!
fd
)
{
if
(
!
fd
)
perror
(
options
[
0
]);
{
success
=
false
;
perror
(
options
[
0
]);
}
success
=
false
;
}
}
}
if
(
fd
)
{
if
(
fd
)
int
result
;
{
int
result
;
if
(
query_buf
&&
query_buf
->
len
>
0
)
if
(
query_buf
&&
query_buf
->
len
>
0
)
fprintf
(
fd
,
"%s
\n
"
,
query_buf
->
data
);
fprintf
(
fd
,
"%s
\n
"
,
query_buf
->
data
);
if
(
pipe
)
if
(
pipe
)
result
=
pclose
(
fd
);
result
=
pclose
(
fd
);
else
else
result
=
fclose
(
fd
);
result
=
fclose
(
fd
);
if
(
result
==
EOF
)
{
if
(
result
==
EOF
)
perror
(
"close"
);
{
success
=
false
;
perror
(
"close"
);
}
success
=
false
;
}
}
}
}
}
/* \x -- toggle expanded table representation */
/* \x -- toggle expanded table representation */
else
if
(
strcmp
(
cmd
,
"x"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"x"
)
==
0
)
success
=
do_pset
(
"expanded"
,
NULL
,
&
pset
->
popt
,
quiet
);
success
=
do_pset
(
"expanded"
,
NULL
,
&
pset
->
popt
,
quiet
);
/* list table rights (grant/revoke) */
/* list table rights (grant/revoke) */
else
if
(
strcmp
(
cmd
,
"z"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"z"
)
==
0
)
success
=
permissionsList
(
options
[
0
],
pset
);
success
=
permissionsList
(
options
[
0
],
pset
);
else
if
(
strcmp
(
cmd
,
"!"
)
==
0
)
success
=
do_shell
(
options_string
);
else
if
(
strcmp
(
cmd
,
"?"
)
==
0
)
else
if
(
strcmp
(
cmd
,
"!"
)
==
0
)
slashUsage
(
pset
);
success
=
do_shell
(
options_string
);
else
if
(
strcmp
(
cmd
,
"?"
)
==
0
)
slashUsage
(
pset
);
#ifdef NOT_USED
#ifdef NOT_USED
/* These commands don't do anything. I just use them to test the parser. */
else
if
(
strcmp
(
cmd
,
"void"
)
==
0
||
strcmp
(
cmd
,
"#"
)
==
0
)
/*
{
* These commands don't do anything. I just use them to test the
int
i
;
* parser.
fprintf
(
stderr
,
"+ optline = |%s|
\n
"
,
options_string
);
*/
for
(
i
=
0
;
options
[
i
];
i
++
)
else
if
(
strcmp
(
cmd
,
"void"
)
==
0
||
strcmp
(
cmd
,
"#"
)
==
0
)
fprintf
(
stderr
,
"+ opt%d = |%s|
\n
"
,
i
,
options
[
i
]);
{
}
int
i
;
fprintf
(
stderr
,
"+ optline = |%s|
\n
"
,
options_string
);
for
(
i
=
0
;
options
[
i
];
i
++
)
fprintf
(
stderr
,
"+ opt%d = |%s|
\n
"
,
i
,
options
[
i
]);
}
#endif
#endif
else
{
else
status
=
CMD_UNKNOWN
;
status
=
CMD_UNKNOWN
;
}
if
(
!
success
)
status
=
CMD_ERROR
;
if
(
!
success
)
return
status
;
status
=
CMD_ERROR
;
return
status
;
}
}
...
@@ -617,104 +694,119 @@ exec_command(const char * cmd,
...
@@ -617,104 +694,119 @@ exec_command(const char * cmd,
* The return value is malloc()'ed.
* The return value is malloc()'ed.
*/
*/
static
char
*
static
char
*
unescape
(
const
char
*
source
,
PsqlSettings
*
pset
)
unescape
(
const
char
*
source
,
PsqlSettings
*
pset
)
{
{
unsigned
char
*
p
;
unsigned
char
*
p
;
bool
esc
=
false
;
/* Last character we saw was the
bool
esc
=
false
;
/* Last character we saw was the escape
escape character */
* character */
char
*
destination
,
*
tmp
;
char
*
destination
,
size_t
length
;
*
tmp
;
size_t
length
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
source
);
assert
(
source
);
#endif
#endif
length
=
strlen
(
source
)
+
1
;
length
=
strlen
(
source
)
+
1
;
tmp
=
destination
=
(
char
*
)
malloc
(
length
);
if
(
!
tmp
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
for
(
p
=
(
char
*
)
source
;
*
p
;
p
+=
PQmblen
(
p
))
{
if
(
esc
)
{
char
c
;
switch
(
*
p
)
{
case
'n'
:
c
=
'\n'
;
break
;
case
'r'
:
c
=
'\r'
;
break
;
case
't'
:
c
=
'\t'
;
break
;
case
'f'
:
c
=
'\f'
;
break
;
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
{
long
int
l
;
char
*
end
;
l
=
strtol
(
p
,
&
end
,
0
);
c
=
l
;
p
=
end
-
1
;
break
;
}
default:
c
=
*
p
;
}
*
tmp
++
=
c
;
esc
=
false
;
}
else
if
(
*
p
==
'\\'
)
{
tmp
=
destination
=
(
char
*
)
malloc
(
length
);
esc
=
true
;
if
(
!
tmp
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
}
else
if
(
*
p
==
'$'
)
for
(
p
=
(
char
*
)
source
;
*
p
;
p
+=
PQmblen
(
p
)
)
{
{
if
(
*
(
p
+
1
)
==
'{'
)
{
if
(
esc
)
unsigned
int
len
;
{
char
*
copy
;
char
c
;
const
char
*
value
;
void
*
new
;
switch
(
*
p
)
len
=
strcspn
(
p
+
2
,
"}"
);
{
copy
=
xstrdup
(
p
+
2
);
case
'n'
:
copy
[
len
]
=
'\0'
;
c
=
'\n'
;
value
=
interpolate_var
(
copy
,
pset
);
break
;
case
'r'
:
length
+=
strlen
(
value
)
-
(
len
+
3
);
c
=
'\r'
;
new
=
realloc
(
destination
,
length
);
break
;
if
(
!
new
)
{
case
't'
:
perror
(
"realloc"
);
c
=
'\t'
;
exit
(
EXIT_FAILURE
);
break
;
case
'f'
:
c
=
'\f'
;
break
;
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
{
long
int
l
;
char
*
end
;
l
=
strtol
(
p
,
&
end
,
0
);
c
=
l
;
p
=
end
-
1
;
break
;
}
default:
c
=
*
p
;
}
*
tmp
++
=
c
;
esc
=
false
;
}
else
if
(
*
p
==
'\\'
)
esc
=
true
;
else
if
(
*
p
==
'$'
)
{
if
(
*
(
p
+
1
)
==
'{'
)
{
unsigned
int
len
;
char
*
copy
;
const
char
*
value
;
void
*
new
;
len
=
strcspn
(
p
+
2
,
"}"
);
copy
=
xstrdup
(
p
+
2
);
copy
[
len
]
=
'\0'
;
value
=
interpolate_var
(
copy
,
pset
);
length
+=
strlen
(
value
)
-
(
len
+
3
);
new
=
realloc
(
destination
,
length
);
if
(
!
new
)
{
perror
(
"realloc"
);
exit
(
EXIT_FAILURE
);
}
tmp
=
new
+
(
tmp
-
destination
);
destination
=
new
;
strcpy
(
tmp
,
value
);
tmp
+=
strlen
(
value
);
p
+=
len
+
2
;
free
(
copy
);
}
else
*
tmp
++
=
'$'
;
}
}
tmp
=
new
+
(
tmp
-
destination
);
destination
=
new
;
strcpy
(
tmp
,
value
);
tmp
+=
strlen
(
value
);
p
+=
len
+
2
;
free
(
copy
);
}
else
{
*
tmp
++
=
'$'
;
}
}
else
{
else
*
tmp
++
=
*
p
;
{
esc
=
false
;
*
tmp
++
=
*
p
;
esc
=
false
;
}
}
}
}
*
tmp
=
'\0'
;
*
tmp
=
'\0'
;
return
destination
;
return
destination
;
}
}
...
@@ -731,109 +823,120 @@ unescape(const char * source, PsqlSettings * pset)
...
@@ -731,109 +823,120 @@ unescape(const char * source, PsqlSettings * pset)
* but the old one was set back. Otherwise it terminates the program.
* but the old one was set back. Otherwise it terminates the program.
*/
*/
bool
bool
do_connect
(
const
char
*
new_dbname
,
const
char
*
new_user
,
PsqlSettings
*
pset
)
do_connect
(
const
char
*
new_dbname
,
const
char
*
new_user
,
PsqlSettings
*
pset
)
{
{
PGconn
*
oldconn
=
pset
->
db
;
PGconn
*
oldconn
=
pset
->
db
;
const
char
*
dbparam
=
NULL
;
const
char
*
dbparam
=
NULL
;
const
char
*
userparam
=
NULL
;
const
char
*
userparam
=
NULL
;
char
*
pwparam
=
NULL
;
char
*
pwparam
=
NULL
;
char
*
prompted_password
=
NULL
;
char
*
prompted_password
=
NULL
;
char
*
prompted_user
=
NULL
;
char
*
prompted_user
=
NULL
;
bool
need_pass
;
bool
need_pass
;
bool
success
=
false
;
bool
success
=
false
;
/* If dbname is "-" then use old name, else new one (even if NULL) */
/* If dbname is "-" then use old name, else new one (even if NULL) */
if
(
new_dbname
&&
PQdb
(
oldconn
)
&&
(
strcmp
(
new_dbname
,
"-"
)
==
0
||
strcmp
(
new_dbname
,
PQdb
(
oldconn
))
==
0
))
if
(
new_dbname
&&
PQdb
(
oldconn
)
&&
(
strcmp
(
new_dbname
,
"-"
)
==
0
||
strcmp
(
new_dbname
,
PQdb
(
oldconn
))
==
0
))
dbparam
=
PQdb
(
oldconn
);
dbparam
=
PQdb
(
oldconn
);
else
else
dbparam
=
new_dbname
;
dbparam
=
new_dbname
;
/* If user is "" or "-" then use the old one */
/* If user is "" or "-" then use the old one */
if
(
new_user
&&
PQuser
(
oldconn
)
&&
(
strcmp
(
new_user
,
""
)
==
0
||
strcmp
(
new_user
,
"-"
)
==
0
||
strcmp
(
new_user
,
PQuser
(
oldconn
))
==
0
))
{
if
(
new_user
&&
PQuser
(
oldconn
)
&&
(
strcmp
(
new_user
,
""
)
==
0
||
strcmp
(
new_user
,
"-"
)
==
0
||
strcmp
(
new_user
,
PQuser
(
oldconn
))
==
0
))
userparam
=
PQuser
(
oldconn
);
userparam
=
PQuser
(
oldconn
);
}
/* If username is "?" then prompt */
/* If username is "?" then prompt */
else
if
(
new_user
&&
strcmp
(
new_user
,
"?"
)
==
0
)
else
if
(
new_user
&&
strcmp
(
new_user
,
"?"
)
==
0
)
userparam
=
prompted_user
=
simple_prompt
(
"Username: "
,
100
,
true
);
/* save for free() */
userparam
=
prompted_user
=
simple_prompt
(
"Username: "
,
100
,
true
);
/* save for free() */
else
else
userparam
=
new_user
;
userparam
=
new_user
;
/* need to prompt for password? */
/* need to prompt for password? */
if
(
pset
->
getPassword
)
if
(
pset
->
getPassword
)
pwparam
=
prompted_password
=
simple_prompt
(
"Password: "
,
100
,
false
);
/* need to save for
pwparam
=
prompted_password
=
simple_prompt
(
"Password: "
,
100
,
false
);
/* need to save for free() */
* free() */
/* Use old password if no new one given (if you didn't have an old one, fine) */
/*
if
(
!
pwparam
)
* Use old password if no new one given (if you didn't have an old
pwparam
=
PQpass
(
oldconn
);
* one, fine)
*/
if
(
!
pwparam
)
pwparam
=
PQpass
(
oldconn
);
#ifdef MULTIBYTE
#ifdef MULTIBYTE
/*
* PGCLIENTENCODING may be set by the previous connection. if a
/*
* user does not explicitly set PGCLIENTENCODING, we should
* PGCLIENTENCODING may be set by the previous connection. if a user
* discard PGCLIENTENCODING so that libpq could get the backen
d
* does not explicitly set PGCLIENTENCODING, we should discar
d
* encoding as the default PGCLIENTENCODING value. -- 1998/12/12
* PGCLIENTENCODING so that libpq could get the backend encoding as
*
Tatsuo Ishii
* the default PGCLIENTENCODING value. -- 1998/12/12
Tatsuo Ishii
*/
*/
if
(
!
pset
->
has_client_encoding
)
if
(
!
pset
->
has_client_encoding
)
putenv
(
"PGCLIENTENCODING="
);
putenv
(
"PGCLIENTENCODING="
);
#endif
#endif
do
{
do
need_pass
=
false
;
{
pset
->
db
=
PQsetdbLogin
(
PQhost
(
oldconn
),
PQport
(
oldconn
),
need_pass
=
false
;
NULL
,
NULL
,
dbparam
,
userparam
,
pwparam
);
pset
->
db
=
PQsetdbLogin
(
PQhost
(
oldconn
),
PQport
(
oldconn
),
NULL
,
NULL
,
dbparam
,
userparam
,
pwparam
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
&&
strcmp
(
PQerrorMessage
(
pset
->
db
),
"fe_sendauth: no password supplied
\n
"
)
==
0
)
{
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
&&
need_pass
=
true
;
strcmp
(
PQerrorMessage
(
pset
->
db
),
"fe_sendauth: no password supplied
\n
"
)
==
0
)
free
(
prompted_password
);
{
prompted_password
=
NULL
;
need_pass
=
true
;
pwparam
=
prompted_password
=
simple_prompt
(
"Password: "
,
100
,
false
);
free
(
prompted_password
);
}
prompted_password
=
NULL
;
}
while
(
need_pass
);
pwparam
=
prompted_password
=
simple_prompt
(
"Password: "
,
100
,
false
);
}
free
(
prompted_password
);
}
while
(
need_pass
);
free
(
prompted_user
);
free
(
prompted_password
);
/* If connection failed, try at least keep the old one.
free
(
prompted_user
);
That's probably more convenient than just kicking you out of the
program. */
/*
if
(
!
pset
->
db
||
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
* If connection failed, try at least keep the old one. That's
{
* probably more convenient than just kicking you out of the program.
fprintf
(
stderr
,
"Could not establish database connection.
\n
%s"
,
PQerrorMessage
(
pset
->
db
));
*/
PQfinish
(
pset
->
db
);
if
(
!
pset
->
db
||
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
if
(
!
oldconn
||
!
pset
->
cur_cmd_interactive
)
{
/* we don't want unpredictable things to happen
{
in scripting mode */
fprintf
(
stderr
,
"Could not establish database connection.
\n
%s"
,
PQerrorMessage
(
pset
->
db
));
fputs
(
"Terminating.
\n
"
,
stderr
);
PQfinish
(
pset
->
db
);
if
(
oldconn
)
if
(
!
oldconn
||
!
pset
->
cur_cmd_interactive
)
PQfinish
(
oldconn
);
{
/* we don't want unpredictable things to
pset
->
db
=
NULL
;
* happen in scripting mode */
}
fputs
(
"Terminating.
\n
"
,
stderr
);
else
{
if
(
oldconn
)
fputs
(
"Keeping old connection.
\n
"
,
stderr
);
PQfinish
(
oldconn
);
pset
->
db
=
oldconn
;
pset
->
db
=
NULL
;
}
}
}
else
else
{
{
if
(
!
GetVariable
(
pset
->
vars
,
"quiet"
))
{
fputs
(
"Keeping old connection.
\n
"
,
stderr
);
if
(
userparam
!=
new_user
)
/* no new user */
pset
->
db
=
oldconn
;
printf
(
"You are now connected to database %s.
\n
"
,
dbparam
);
}
else
if
(
dbparam
!=
new_dbname
)
/* no new db */
printf
(
"You are now connected as new user %s.
\n
"
,
new_user
);
else
/* both new */
printf
(
"You are now connected to database %s as user %s.
\n
"
,
PQdb
(
pset
->
db
),
PQuser
(
pset
->
db
));
}
}
else
{
if
(
!
GetVariable
(
pset
->
vars
,
"quiet"
))
{
if
(
userparam
!=
new_user
)
/* no new user */
printf
(
"You are now connected to database %s.
\n
"
,
dbparam
);
else
if
(
dbparam
!=
new_dbname
)
/* no new db */
printf
(
"You are now connected as new user %s.
\n
"
,
new_user
);
else
/* both new */
printf
(
"You are now connected to database %s as user %s.
\n
"
,
PQdb
(
pset
->
db
),
PQuser
(
pset
->
db
));
}
if
(
oldconn
)
if
(
oldconn
)
PQfinish
(
oldconn
);
PQfinish
(
oldconn
);
success
=
true
;
success
=
true
;
}
}
return
success
;
return
success
;
}
}
...
@@ -848,35 +951,36 @@ do_connect(const char *new_dbname, const char *new_user, PsqlSettings * pset)
...
@@ -848,35 +951,36 @@ do_connect(const char *new_dbname, const char *new_user, PsqlSettings * pset)
static
bool
static
bool
editFile
(
const
char
*
fname
)
editFile
(
const
char
*
fname
)
{
{
char
*
editorName
;
char
*
editorName
;
char
*
sys
;
char
*
sys
;
int
result
;
int
result
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
fname
);
assert
(
fname
);
#else
#else
if
(
!
fname
)
return
false
;
if
(
!
fname
)
return
false
;
#endif
#endif
/* Find an editor to use */
/* Find an editor to use */
editorName
=
getenv
(
"PSQL_EDITOR"
);
editorName
=
getenv
(
"PSQL_EDITOR"
);
if
(
!
editorName
)
if
(
!
editorName
)
editorName
=
getenv
(
"EDITOR"
);
editorName
=
getenv
(
"EDITOR"
);
if
(
!
editorName
)
if
(
!
editorName
)
editorName
=
getenv
(
"VISUAL"
);
editorName
=
getenv
(
"VISUAL"
);
if
(
!
editorName
)
if
(
!
editorName
)
editorName
=
DEFAULT_EDITOR
;
editorName
=
DEFAULT_EDITOR
;
sys
=
malloc
(
strlen
(
editorName
)
+
strlen
(
fname
)
+
32
+
1
);
sys
=
malloc
(
strlen
(
editorName
)
+
strlen
(
fname
)
+
32
+
1
);
if
(
!
sys
)
if
(
!
sys
)
return
false
;
return
false
;
sprintf
(
sys
,
"exec %s %s"
,
editorName
,
fname
);
sprintf
(
sys
,
"exec %s %s"
,
editorName
,
fname
);
result
=
system
(
sys
);
result
=
system
(
sys
);
if
(
result
==
-
1
||
result
==
127
)
if
(
result
==
-
1
||
result
==
127
)
perror
(
sys
);
perror
(
sys
);
free
(
sys
);
free
(
sys
);
return
result
==
0
;
return
result
==
0
;
}
}
...
@@ -884,117 +988,135 @@ editFile(const char *fname)
...
@@ -884,117 +988,135 @@ editFile(const char *fname)
static
bool
static
bool
do_edit
(
const
char
*
filename_arg
,
PQExpBuffer
query_buf
)
do_edit
(
const
char
*
filename_arg
,
PQExpBuffer
query_buf
)
{
{
char
fnametmp
[
64
];
char
fnametmp
[
64
];
FILE
*
stream
;
FILE
*
stream
;
const
char
*
fname
;
const
char
*
fname
;
bool
error
=
false
;
bool
error
=
false
;
#ifndef WIN32
#ifndef WIN32
struct
stat
before
,
after
;
struct
stat
before
,
after
;
#endif
#endif
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
query_buf
);
assert
(
query_buf
);
#else
#else
if
(
!
query_buf
)
return
false
;
if
(
!
query_buf
)
return
false
;
#endif
#endif
if
(
filename_arg
)
if
(
filename_arg
)
fname
=
filename_arg
;
fname
=
filename_arg
;
else
{
else
/* make a temp file to edit */
{
/* make a temp file to edit */
#ifndef WIN32
#ifndef WIN32
mode_t
oldumask
;
mode_t
oldumask
;
sprintf
(
fnametmp
,
"/tmp/psql.edit.%ld.%ld"
,
(
long
)
geteuid
(),
(
long
)
getpid
());
sprintf
(
fnametmp
,
"/tmp/psql.edit.%ld.%ld"
,
(
long
)
geteuid
(),
(
long
)
getpid
());
#else
#else
GetTempFileName
(
"."
,
"psql"
,
0
,
fnametmp
);
GetTempFileName
(
"."
,
"psql"
,
0
,
fnametmp
);
#endif
#endif
fname
=
(
const
char
*
)
fnametmp
;
fname
=
(
const
char
*
)
fnametmp
;
#ifndef WIN32
#ifndef WIN32
oldumask
=
umask
(
0177
);
oldumask
=
umask
(
0177
);
#endif
#endif
stream
=
fopen
(
fname
,
"w"
);
stream
=
fopen
(
fname
,
"w"
);
#ifndef WIN32
#ifndef WIN32
umask
(
oldumask
);
umask
(
oldumask
);
#endif
#endif
if
(
!
stream
)
{
if
(
!
stream
)
perror
(
fname
);
{
error
=
true
;
perror
(
fname
);
error
=
true
;
}
else
{
unsigned
int
ql
=
query_buf
->
len
;
if
(
ql
==
0
||
query_buf
->
data
[
ql
-
1
]
!=
'\n'
)
{
appendPQExpBufferChar
(
query_buf
,
'\n'
);
ql
++
;
}
if
(
fwrite
(
query_buf
->
data
,
1
,
ql
,
stream
)
!=
ql
)
{
perror
(
fname
);
fclose
(
stream
);
remove
(
fname
);
error
=
true
;
}
else
fclose
(
stream
);
}
}
}
else
{
unsigned
int
ql
=
query_buf
->
len
;
#ifndef WIN32
if
(
ql
==
0
||
query_buf
->
data
[
ql
-
1
]
!=
'\n'
)
{
if
(
!
error
&&
stat
(
fname
,
&
before
)
!=
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
{
ql
++
;
}
if
(
fwrite
(
query_buf
->
data
,
1
,
ql
,
stream
)
!=
ql
)
{
perror
(
fname
);
perror
(
fname
);
fclose
(
stream
);
remove
(
fname
);
error
=
true
;
error
=
true
;
}
else
fclose
(
stream
);
}
}
}
#ifndef WIN32
if
(
!
error
&&
stat
(
fname
,
&
before
)
!=
0
)
{
perror
(
fname
);
error
=
true
;
}
#endif
#endif
/* call editor */
/* call editor */
if
(
!
error
)
if
(
!
error
)
error
=
!
editFile
(
fname
);
error
=
!
editFile
(
fname
);
#ifndef WIN32
#ifndef WIN32
if
(
!
error
&&
stat
(
fname
,
&
after
)
!=
0
)
{
if
(
!
error
&&
stat
(
fname
,
&
after
)
!=
0
)
perror
(
fname
);
{
error
=
true
;
perror
(
fname
);
}
error
=
true
;
}
if
(
!
error
&&
before
.
st_mtime
!=
after
.
st_mtime
)
{
if
(
!
error
&&
before
.
st_mtime
!=
after
.
st_mtime
)
{
#else
#else
if
(
!
error
)
{
if
(
!
error
)
{
#endif
#endif
stream
=
fopen
(
fname
,
"r"
);
stream
=
fopen
(
fname
,
"r"
);
if
(
!
stream
)
{
if
(
!
stream
)
perror
(
fname
);
{
error
=
true
;
perror
(
fname
);
}
error
=
true
;
else
{
}
/* read file back in */
else
char
line
[
1024
];
{
size_t
result
;
/* read file back in */
char
line
[
1024
];
resetPQExpBuffer
(
query_buf
);
size_t
result
;
do
{
result
=
fread
(
line
,
1
,
1024
,
stream
);
resetPQExpBuffer
(
query_buf
);
if
(
ferror
(
stream
))
{
do
perror
(
fname
);
{
error
=
true
;
result
=
fread
(
line
,
1
,
1024
,
stream
);
break
;
if
(
ferror
(
stream
))
{
perror
(
fname
);
error
=
true
;
break
;
}
appendBinaryPQExpBuffer
(
query_buf
,
line
,
result
);
}
while
(
!
feof
(
stream
));
appendPQExpBufferChar
(
query_buf
,
'\0'
);
fclose
(
stream
);
}
}
appendBinaryPQExpBuffer
(
query_buf
,
line
,
result
);
}
while
(
!
feof
(
stream
));
appendPQExpBufferChar
(
query_buf
,
'\0'
);
fclose
(
stream
);
/* remove temp file */
if
(
!
filename_arg
)
remove
(
fname
);
}
}
/* remove temp file */
return
!
error
;
if
(
!
filename_arg
)
remove
(
fname
);
}
return
!
error
;
}
}
...
@@ -1008,26 +1130,27 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
...
@@ -1008,26 +1130,27 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
bool
bool
process_file
(
const
char
*
filename
,
PsqlSettings
*
pset
)
process_file
(
const
char
*
filename
,
PsqlSettings
*
pset
)
{
{
FILE
*
fd
;
FILE
*
fd
;
int
result
;
int
result
;
if
(
!
filename
)
if
(
!
filename
)
return
false
;
return
false
;
#ifdef __CYGWIN32__
#ifdef __CYGWIN32__
fd
=
fopen
(
filename
,
"rb"
);
fd
=
fopen
(
filename
,
"rb"
);
#else
#else
fd
=
fopen
(
filename
,
"r"
);
fd
=
fopen
(
filename
,
"r"
);
#endif
#endif
if
(
!
fd
)
{
if
(
!
fd
)
perror
(
filename
);
{
return
false
;
perror
(
filename
);
}
return
false
;
}
result
=
MainLoop
(
pset
,
fd
);
result
=
MainLoop
(
pset
,
fd
);
fclose
(
fd
);
fclose
(
fd
);
return
(
result
==
EXIT_SUCCESS
);
return
(
result
==
EXIT_SUCCESS
);
}
}
...
@@ -1039,158 +1162,178 @@ process_file(const char *filename, PsqlSettings *pset)
...
@@ -1039,158 +1162,178 @@ process_file(const char *filename, PsqlSettings *pset)
static
const
char
*
static
const
char
*
_align2string
(
enum
printFormat
in
)
_align2string
(
enum
printFormat
in
)
{
{
switch
(
in
)
{
switch
(
in
)
case
PRINT_NOTHING
:
{
return
"nothing"
;
case
PRINT_NOTHING
:
break
;
return
"nothing"
;
case
PRINT_UNALIGNED
:
break
;
return
"unaligned"
;
case
PRINT_UNALIGNED
:
break
;
return
"unaligned"
;
case
PRINT_ALIGNED
:
break
;
return
"aligned"
;
case
PRINT_ALIGNED
:
break
;
return
"aligned"
;
case
PRINT_HTML
:
break
;
return
"html"
;
case
PRINT_HTML
:
break
;
return
"html"
;
case
PRINT_LATEX
:
break
;
return
"latex"
;
case
PRINT_LATEX
:
break
;
return
"latex"
;
}
break
;
return
"unknown"
;
}
return
"unknown"
;
}
}
bool
bool
do_pset
(
const
char
*
param
,
const
char
*
value
,
printQueryOpt
*
popt
,
bool
quiet
)
do_pset
(
const
char
*
param
,
const
char
*
value
,
printQueryOpt
*
popt
,
bool
quiet
)
{
{
size_t
vallen
=
0
;
size_t
vallen
=
0
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
param
);
assert
(
param
);
#else
#else
if
(
!
param
)
return
false
;
if
(
!
param
)
return
false
;
#endif
#endif
if
(
value
)
if
(
value
)
vallen
=
strlen
(
value
);
vallen
=
strlen
(
value
);
/* set format */
/* set format */
if
(
strcmp
(
param
,
"format"
)
==
0
)
{
if
(
strcmp
(
param
,
"format"
)
==
0
)
if
(
!
value
)
{
;
if
(
!
value
)
else
if
(
strncasecmp
(
"unaligned"
,
value
,
vallen
)
==
0
)
;
popt
->
topt
.
format
=
PRINT_UNALIGNED
;
else
if
(
strncasecmp
(
"unaligned"
,
value
,
vallen
)
==
0
)
else
if
(
strncasecmp
(
"aligned"
,
value
,
vallen
)
==
0
)
popt
->
topt
.
format
=
PRINT_UNALIGNED
;
popt
->
topt
.
format
=
PRINT_ALIGNED
;
else
if
(
strncasecmp
(
"aligned"
,
value
,
vallen
)
==
0
)
else
if
(
strncasecmp
(
"html"
,
value
,
vallen
)
==
0
)
popt
->
topt
.
format
=
PRINT_ALIGNED
;
popt
->
topt
.
format
=
PRINT_HTML
;
else
if
(
strncasecmp
(
"html"
,
value
,
vallen
)
==
0
)
else
if
(
strncasecmp
(
"latex"
,
value
,
vallen
)
==
0
)
popt
->
topt
.
format
=
PRINT_HTML
;
popt
->
topt
.
format
=
PRINT_LATEX
;
else
if
(
strncasecmp
(
"latex"
,
value
,
vallen
)
==
0
)
else
{
popt
->
topt
.
format
=
PRINT_LATEX
;
fprintf
(
stderr
,
"Allowed formats are unaligned, aligned, html, latex.
\n
"
);
else
return
false
;
{
fprintf
(
stderr
,
"Allowed formats are unaligned, aligned, html, latex.
\n
"
);
return
false
;
}
if
(
!
quiet
)
printf
(
"Output format is %s.
\n
"
,
_align2string
(
popt
->
topt
.
format
));
}
}
if
(
!
quiet
)
/* set border style/width */
printf
(
"Output format is %s.
\n
"
,
_align2string
(
popt
->
topt
.
format
));
else
if
(
strcmp
(
param
,
"border"
)
==
0
)
}
{
if
(
value
)
popt
->
topt
.
border
=
atoi
(
value
);
/* set border style/width */
if
(
!
quiet
)
else
if
(
strcmp
(
param
,
"border"
)
==
0
)
{
printf
(
"Border style is %d.
\n
"
,
popt
->
topt
.
border
);
if
(
value
)
popt
->
topt
.
border
=
atoi
(
value
);
if
(
!
quiet
)
printf
(
"Border style is %d.
\n
"
,
popt
->
topt
.
border
);
}
/* set expanded/vertical mode */
else
if
(
strcmp
(
param
,
"x"
)
==
0
||
strcmp
(
param
,
"expanded"
)
==
0
||
strcmp
(
param
,
"vertical"
)
==
0
)
{
popt
->
topt
.
expanded
=
!
popt
->
topt
.
expanded
;
if
(
!
quiet
)
printf
(
"Expanded display is %s.
\n
"
,
popt
->
topt
.
expanded
?
"on"
:
"off"
);
}
/* null display */
else
if
(
strcmp
(
param
,
"null"
)
==
0
)
{
if
(
value
)
{
free
(
popt
->
nullPrint
);
popt
->
nullPrint
=
xstrdup
(
value
);
}
if
(
!
quiet
)
printf
(
"Null display is
\"
%s
\"
.
\n
"
,
popt
->
nullPrint
?
popt
->
nullPrint
:
""
);
}
/* field separator for unaligned text */
else
if
(
strcmp
(
param
,
"fieldsep"
)
==
0
)
{
if
(
value
)
{
free
(
popt
->
topt
.
fieldSep
);
popt
->
topt
.
fieldSep
=
xstrdup
(
value
);
}
}
if
(
!
quiet
)
printf
(
"Field separator is
\"
%s
\"
.
\n
"
,
popt
->
topt
.
fieldSep
);
/* set expanded/vertical mode */
}
else
if
(
strcmp
(
param
,
"x"
)
==
0
||
strcmp
(
param
,
"expanded"
)
==
0
||
strcmp
(
param
,
"vertical"
)
==
0
)
{
/* toggle between full and barebones format */
popt
->
topt
.
expanded
=
!
popt
->
topt
.
expanded
;
else
if
(
strcmp
(
param
,
"t"
)
==
0
||
strcmp
(
param
,
"tuples_only"
)
==
0
)
{
if
(
!
quiet
)
popt
->
topt
.
tuples_only
=
!
popt
->
topt
.
tuples_only
;
printf
(
"Expanded display is %s.
\n
"
,
popt
->
topt
.
expanded
?
"on"
:
"off"
);
if
(
!
quiet
)
{
if
(
popt
->
topt
.
tuples_only
)
puts
(
"Showing only tuples."
);
else
puts
(
"Tuples only is off."
);
}
}
}
/* set title override */
/* null display */
else
if
(
strcmp
(
param
,
"title"
)
==
0
)
{
else
if
(
strcmp
(
param
,
"null"
)
==
0
)
free
(
popt
->
title
);
{
if
(
!
value
)
if
(
value
)
popt
->
title
=
NULL
;
{
else
free
(
popt
->
nullPrint
);
popt
->
title
=
xstrdup
(
value
);
popt
->
nullPrint
=
xstrdup
(
value
);
}
if
(
!
quiet
)
{
if
(
!
quiet
)
if
(
popt
->
title
)
printf
(
"Null display is
\"
%s
\"
.
\n
"
,
popt
->
nullPrint
?
popt
->
nullPrint
:
""
);
printf
(
"Title is
\"
%s
\"
.
\n
"
,
popt
->
title
);
else
printf
(
"Title is unset.
\n
"
);
}
}
}
/* set HTML table tag options */
/* field separator for unaligned text */
else
if
(
strcmp
(
param
,
"T"
)
==
0
||
strcmp
(
param
,
"tableattr"
)
==
0
)
{
else
if
(
strcmp
(
param
,
"fieldsep"
)
==
0
)
free
(
popt
->
topt
.
tableAttr
);
{
if
(
!
value
)
if
(
value
)
popt
->
topt
.
tableAttr
=
NULL
;
{
else
free
(
popt
->
topt
.
fieldSep
);
popt
->
topt
.
tableAttr
=
xstrdup
(
value
);
popt
->
topt
.
fieldSep
=
xstrdup
(
value
);
}
if
(
!
quiet
)
printf
(
"Field separator is
\"
%s
\"
.
\n
"
,
popt
->
topt
.
fieldSep
);
}
if
(
!
quiet
)
{
/* toggle between full and barebones format */
if
(
popt
->
topt
.
tableAttr
)
else
if
(
strcmp
(
param
,
"t"
)
==
0
||
strcmp
(
param
,
"tuples_only"
)
==
0
)
printf
(
"Table attribute is
\"
%s
\"
.
\n
"
,
popt
->
topt
.
tableAttr
);
{
else
popt
->
topt
.
tuples_only
=
!
popt
->
topt
.
tuples_only
;
printf
(
"Table attributes unset.
\n
"
);
if
(
!
quiet
)
{
if
(
popt
->
topt
.
tuples_only
)
puts
(
"Showing only tuples."
);
else
puts
(
"Tuples only is off."
);
}
}
/* set title override */
else
if
(
strcmp
(
param
,
"title"
)
==
0
)
{
free
(
popt
->
title
);
if
(
!
value
)
popt
->
title
=
NULL
;
else
popt
->
title
=
xstrdup
(
value
);
if
(
!
quiet
)
{
if
(
popt
->
title
)
printf
(
"Title is
\"
%s
\"
.
\n
"
,
popt
->
title
);
else
printf
(
"Title is unset.
\n
"
);
}
}
}
}
/* set HTML table tag options */
/* toggle use of pager */
else
if
(
strcmp
(
param
,
"T"
)
==
0
||
strcmp
(
param
,
"tableattr"
)
==
0
)
else
if
(
strcmp
(
param
,
"pager"
)
==
0
)
{
{
popt
->
topt
.
pager
=
!
popt
->
topt
.
pager
;
free
(
popt
->
topt
.
tableAttr
);
if
(
!
quiet
)
{
if
(
!
value
)
if
(
popt
->
topt
.
pager
)
popt
->
topt
.
tableAttr
=
NULL
;
puts
(
"Using pager is on."
);
else
else
popt
->
topt
.
tableAttr
=
xstrdup
(
value
);
puts
(
"Using pager is off."
);
if
(
!
quiet
)
{
if
(
popt
->
topt
.
tableAttr
)
printf
(
"Table attribute is
\"
%s
\"
.
\n
"
,
popt
->
topt
.
tableAttr
);
else
printf
(
"Table attributes unset.
\n
"
);
}
}
}
}
/* toggle use of pager */
else
{
else
if
(
strcmp
(
param
,
"pager"
)
==
0
)
fprintf
(
stderr
,
"Unknown option: %s
\n
"
,
param
);
{
return
false
;
popt
->
topt
.
pager
=
!
popt
->
topt
.
pager
;
}
if
(
!
quiet
)
{
if
(
popt
->
topt
.
pager
)
puts
(
"Using pager is on."
);
else
puts
(
"Using pager is off."
);
}
}
return
true
;
else
{
fprintf
(
stderr
,
"Unknown option: %s
\n
"
,
param
);
return
false
;
}
return
true
;
}
}
...
@@ -1200,29 +1343,31 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
...
@@ -1200,29 +1343,31 @@ do_pset(const char * param, const char * value, printQueryOpt * popt, bool quiet
static
bool
static
bool
do_shell
(
const
char
*
command
)
do_shell
(
const
char
*
command
)
{
{
int
result
;
int
result
;
if
(
!
command
)
{
if
(
!
command
)
char
*
sys
;
{
char
*
shellName
;
char
*
sys
;
char
*
shellName
;
shellName
=
getenv
(
"SHELL"
);
if
(
shellName
==
NULL
)
shellName
=
DEFAULT_SHELL
;
sys
=
malloc
(
strlen
(
shellName
)
+
16
);
if
(
!
sys
)
return
false
;
sprintf
(
sys
,
"exec %s"
,
shellName
);
result
=
system
(
sys
);
free
(
sys
);
}
else
result
=
system
(
command
);
shellName
=
getenv
(
"SHELL"
);
if
(
result
==
127
||
result
==
-
1
)
if
(
shellName
==
NULL
)
{
shellName
=
DEFAULT_SHELL
;
perror
(
"system"
);
return
false
;
sys
=
malloc
(
strlen
(
shellName
)
+
16
);
}
if
(
!
sys
)
return
true
;
return
false
;
sprintf
(
sys
,
"exec %s"
,
shellName
);
result
=
system
(
sys
);
free
(
sys
);
}
else
result
=
system
(
command
);
if
(
result
==
127
||
result
==-
1
)
{
perror
(
"system"
);
return
false
;
}
return
true
;
}
}
src/bin/psql/command.h
View file @
0e6652e6
...
@@ -11,39 +11,36 @@
...
@@ -11,39 +11,36 @@
typedef
enum
_backslashResult
{
typedef
enum
_backslashResult
CMD_UNKNOWN
=
0
,
/* not done parsing yet (internal only) */
{
CMD_SEND
,
/* query complete; send off */
CMD_UNKNOWN
=
0
,
/* not done parsing yet (internal only) */
CMD_SKIP_LINE
,
/* keep building query */
CMD_SEND
,
/* query complete; send off */
CMD_TERMINATE
,
/* quit program */
CMD_SKIP_LINE
,
/* keep building query */
CMD_NEWEDIT
,
/* query buffer was changed (e.g., via \e) */
CMD_TERMINATE
,
/* quit program */
CMD_ERROR
/* the execution of the backslash command resulted
CMD_NEWEDIT
,
/* query buffer was changed (e.g., via \e) */
in an error */
CMD_ERROR
/* the execution of the backslash command
}
backslashResult
;
* resulted in an error */
}
backslashResult
;
backslashResult
HandleSlashCmds
(
PsqlSettings
*
pset
,
backslashResult
HandleSlashCmds
(
PsqlSettings
*
pset
,
const
char
*
line
,
const
char
*
line
,
PQExpBuffer
query_buf
,
PQExpBuffer
query_buf
,
const
char
**
end_of_cmd
);
const
char
**
end_of_cmd
);
bool
bool
do_connect
(
const
char
*
new_dbname
,
do_connect
(
const
char
*
new_dbname
,
const
char
*
new_user
,
const
char
*
new_user
,
PsqlSettings
*
pset
);
PsqlSettings
*
pset
);
bool
process_file
(
const
char
*
filename
,
bool
PsqlSettings
*
pset
);
process_file
(
const
char
*
filename
,
PsqlSettings
*
pset
);
bool
do_pset
(
const
char
*
param
,
const
char
*
value
,
bool
printQueryOpt
*
popt
,
do_pset
(
const
char
*
param
,
bool
quiet
);
const
char
*
value
,
printQueryOpt
*
popt
,
bool
quiet
);
#endif
#endif
src/bin/psql/common.c
View file @
0e6652e6
...
@@ -14,7 +14,7 @@
...
@@ -14,7 +14,7 @@
#include <signal.h>
#include <signal.h>
#include <assert.h>
#include <assert.h>
#ifndef WIN32
#ifndef WIN32
#include <unistd.h>
/* for write() */
#include <unistd.h>
/* for write() */
#endif
#endif
#include <libpq-fe.h>
#include <libpq-fe.h>
...
@@ -39,19 +39,23 @@
...
@@ -39,19 +39,23 @@
* "Safe" wrapper around strdup()
* "Safe" wrapper around strdup()
* (Using this also avoids writing #ifdef HAVE_STRDUP in every file :)
* (Using this also avoids writing #ifdef HAVE_STRDUP in every file :)
*/
*/
char
*
xstrdup
(
const
char
*
string
)
char
*
xstrdup
(
const
char
*
string
)
{
{
char
*
tmp
;
char
*
tmp
;
if
(
!
string
)
{
fprintf
(
stderr
,
"xstrdup: Cannot duplicate null pointer.
\n
"
);
if
(
!
string
)
exit
(
EXIT_FAILURE
);
{
}
fprintf
(
stderr
,
"xstrdup: Cannot duplicate null pointer.
\n
"
);
tmp
=
strdup
(
string
);
exit
(
EXIT_FAILURE
);
if
(
!
tmp
)
{
}
perror
(
"strdup"
);
tmp
=
strdup
(
string
);
exit
(
EXIT_FAILURE
);
if
(
!
tmp
)
}
{
return
tmp
;
perror
(
"strdup"
);
exit
(
EXIT_FAILURE
);
}
return
tmp
;
}
}
...
@@ -67,66 +71,67 @@ char * xstrdup(const char * string)
...
@@ -67,66 +71,67 @@ char * xstrdup(const char * string)
bool
bool
setQFout
(
const
char
*
fname
,
PsqlSettings
*
pset
)
setQFout
(
const
char
*
fname
,
PsqlSettings
*
pset
)
{
{
bool
status
=
true
;
bool
status
=
true
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
pset
);
assert
(
pset
);
#else
#else
if
(
!
pset
)
return
false
;
if
(
!
pset
)
return
false
;
#endif
#endif
/* Close old file/pipe */
/* Close old file/pipe */
if
(
pset
->
queryFout
&&
pset
->
queryFout
!=
stdout
&&
pset
->
queryFout
!=
stderr
)
if
(
pset
->
queryFout
&&
pset
->
queryFout
!=
stdout
&&
pset
->
queryFout
!=
stderr
)
{
{
if
(
pset
->
queryFoutPipe
)
if
(
pset
->
queryFoutPipe
)
pclose
(
pset
->
queryFout
);
pclose
(
pset
->
queryFout
);
else
else
fclose
(
pset
->
queryFout
);
fclose
(
pset
->
queryFout
);
}
}
/* If no filename, set stdout */
/* If no filename, set stdout */
if
(
!
fname
||
fname
[
0
]
==
'\0'
)
if
(
!
fname
||
fname
[
0
]
==
'\0'
)
{
{
pset
->
queryFout
=
stdout
;
pset
->
queryFout
=
stdout
;
pset
->
queryFoutPipe
=
false
;
pset
->
queryFoutPipe
=
false
;
}
}
else
if
(
*
fname
==
'|'
)
else
if
(
*
fname
==
'|'
)
{
{
const
char
*
pipename
=
fname
+
1
;
const
char
*
pipename
=
fname
+
1
;
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
pset
->
queryFout
=
popen
(
pipename
,
"w"
);
pset
->
queryFout
=
popen
(
pipename
,
"w"
);
#else
#else
pset
->
queryFout
=
popen
(
pipename
,
"wb"
);
pset
->
queryFout
=
popen
(
pipename
,
"wb"
);
#endif
#endif
pset
->
queryFoutPipe
=
true
;
pset
->
queryFoutPipe
=
true
;
}
}
else
else
{
{
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
pset
->
queryFout
=
fopen
(
fname
,
"w"
);
pset
->
queryFout
=
fopen
(
fname
,
"w"
);
#else
#else
pset
->
queryFout
=
fopen
(
fname
,
"wb"
);
pset
->
queryFout
=
fopen
(
fname
,
"wb"
);
#endif
#endif
pset
->
queryFoutPipe
=
false
;
pset
->
queryFoutPipe
=
false
;
}
}
if
(
!
pset
->
queryFout
)
if
(
!
pset
->
queryFout
)
{
{
perror
(
fname
);
perror
(
fname
);
pset
->
queryFout
=
stdout
;
pset
->
queryFout
=
stdout
;
pset
->
queryFoutPipe
=
false
;
pset
->
queryFoutPipe
=
false
;
status
=
false
;
status
=
false
;
}
}
/* Direct signals */
/* Direct signals */
if
(
pset
->
queryFoutPipe
)
if
(
pset
->
queryFoutPipe
)
pqsignal
(
SIGPIPE
,
SIG_IGN
);
pqsignal
(
SIGPIPE
,
SIG_IGN
);
else
else
pqsignal
(
SIGPIPE
,
SIG_DFL
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
return
status
;
return
status
;
}
}
...
@@ -137,60 +142,67 @@ setQFout(const char *fname, PsqlSettings *pset)
...
@@ -137,60 +142,67 @@ setQFout(const char *fname, PsqlSettings *pset)
* Generalized function especially intended for reading in usernames and
* Generalized function especially intended for reading in usernames and
* password interactively. Reads from stdin.
* password interactively. Reads from stdin.
*
*
* prompt:
The prompt to print
* prompt:
The prompt to print
* maxlen:
How many characters to accept
* maxlen:
How many characters to accept
* echo:
Set to false if you want to hide what is entered (for passwords)
* echo:
Set to false if you want to hide what is entered (for passwords)
*
*
* Returns a malloc()'ed string with the input (w/o trailing newline).
* Returns a malloc()'ed string with the input (w/o trailing newline).
*/
*/
char
*
char
*
simple_prompt
(
const
char
*
prompt
,
int
maxlen
,
bool
echo
)
simple_prompt
(
const
char
*
prompt
,
int
maxlen
,
bool
echo
)
{
{
int
length
;
int
length
;
char
*
destination
;
char
*
destination
;
#ifdef HAVE_TERMIOS_H
#ifdef HAVE_TERMIOS_H
struct
termios
t_orig
,
t
;
struct
termios
t_orig
,
t
;
#endif
#endif
destination
=
(
char
*
)
malloc
(
maxlen
+
2
);
destination
=
(
char
*
)
malloc
(
maxlen
+
2
);
if
(
!
destination
)
if
(
!
destination
)
return
NULL
;
return
NULL
;
if
(
prompt
)
fputs
(
prompt
,
stdout
);
if
(
prompt
)
fputs
(
prompt
,
stdout
);
#ifdef HAVE_TERMIOS_H
#ifdef HAVE_TERMIOS_H
if
(
!
echo
)
if
(
!
echo
)
{
{
tcgetattr
(
0
,
&
t
);
tcgetattr
(
0
,
&
t
);
t_orig
=
t
;
t_orig
=
t
;
t
.
c_lflag
&=
~
ECHO
;
t
.
c_lflag
&=
~
ECHO
;
tcsetattr
(
0
,
TCSADRAIN
,
&
t
);
tcsetattr
(
0
,
TCSADRAIN
,
&
t
);
}
}
#endif
#endif
fgets
(
destination
,
maxlen
,
stdin
);
fgets
(
destination
,
maxlen
,
stdin
);
#ifdef HAVE_TERMIOS_H
#ifdef HAVE_TERMIOS_H
if
(
!
echo
)
{
if
(
!
echo
)
tcsetattr
(
0
,
TCSADRAIN
,
&
t_orig
);
{
puts
(
""
);
tcsetattr
(
0
,
TCSADRAIN
,
&
t_orig
);
}
puts
(
""
);
}
#endif
#endif
length
=
strlen
(
destination
);
length
=
strlen
(
destination
);
if
(
length
>
0
&&
destination
[
length
-
1
]
!=
'\n'
)
{
if
(
length
>
0
&&
destination
[
length
-
1
]
!=
'\n'
)
/* eat rest of the line */
{
char
buf
[
512
];
/* eat rest of the line */
do
{
char
buf
[
512
];
fgets
(
buf
,
512
,
stdin
);
}
while
(
buf
[
strlen
(
buf
)
-
1
]
!=
'\n'
);
do
}
{
fgets
(
buf
,
512
,
stdin
);
}
while
(
buf
[
strlen
(
buf
)
-
1
]
!=
'\n'
);
}
if
(
length
>
0
&&
destination
[
length
-
1
]
==
'\n'
)
if
(
length
>
0
&&
destination
[
length
-
1
]
==
'\n'
)
/* remove trailing newline */
/* remove trailing newline */
destination
[
length
-
1
]
=
'\0'
;
destination
[
length
-
1
]
=
'\0'
;
return
destination
;
return
destination
;
}
}
...
@@ -205,68 +217,79 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
...
@@ -205,68 +217,79 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
* immediate consumption.
* immediate consumption.
*/
*/
const
char
*
const
char
*
interpolate_var
(
const
char
*
name
,
PsqlSettings
*
pset
)
interpolate_var
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
const
char
*
var
;
const
char
*
var
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
name
);
assert
(
name
);
assert
(
pset
);
assert
(
pset
);
#else
#else
if
(
!
name
||
!
pset
)
return
NULL
;
if
(
!
name
||
!
pset
)
return
NULL
;
#endif
#endif
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
==
strlen
(
name
))
{
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
==
strlen
(
name
))
var
=
GetVariable
(
pset
->
vars
,
name
);
{
if
(
var
)
var
=
GetVariable
(
pset
->
vars
,
name
);
return
var
;
if
(
var
)
else
return
var
;
return
""
;
else
}
return
""
;
}
/* otherwise return magic variable */
/* (by convention these should be capitalized (but not all caps), to not be
shadowed by regular vars or to shadow env vars) */
if
(
strcmp
(
name
,
"Version"
)
==
0
)
return
PG_VERSION_STR
;
if
(
strcmp
(
name
,
"Database"
)
==
0
)
{
if
(
PQdb
(
pset
->
db
))
return
PQdb
(
pset
->
db
);
else
return
""
;
}
if
(
strcmp
(
name
,
"User"
)
==
0
)
{
/* otherwise return magic variable */
if
(
PQuser
(
pset
->
db
))
return
PQuser
(
pset
->
db
);
else
return
""
;
}
if
(
strcmp
(
name
,
"Host"
)
==
0
)
{
/*
if
(
PQhost
(
pset
->
db
))
* (by convention these should be capitalized (but not all caps), to
return
PQhost
(
pset
->
db
);
* not be shadowed by regular vars or to shadow env vars)
else
*/
return
""
;
if
(
strcmp
(
name
,
"Version"
)
==
0
)
}
return
PG_VERSION_STR
;
if
(
strcmp
(
name
,
"Port"
)
==
0
)
{
if
(
strcmp
(
name
,
"Database"
)
==
0
)
if
(
PQport
(
pset
->
db
))
{
return
PQport
(
pset
->
db
);
if
(
PQdb
(
pset
->
db
))
else
return
PQdb
(
pset
->
db
);
return
""
;
else
}
return
""
;
}
if
(
strcmp
(
name
,
"User"
)
==
0
)
{
if
(
PQuser
(
pset
->
db
))
return
PQuser
(
pset
->
db
);
else
return
""
;
}
/* env vars (if env vars are all caps there should be no prob, otherwise
if
(
strcmp
(
name
,
"Host"
)
==
0
)
you're on your own */
{
if
(
PQhost
(
pset
->
db
))
return
PQhost
(
pset
->
db
);
else
return
""
;
}
if
(
strcmp
(
name
,
"Port"
)
==
0
)
{
if
(
PQport
(
pset
->
db
))
return
PQport
(
pset
->
db
);
else
return
""
;
}
/*
* env vars (if env vars are all caps there should be no prob,
* otherwise you're on your own
*/
if
((
var
=
getenv
(
name
)))
if
((
var
=
getenv
(
name
)))
return
var
;
return
var
;
return
""
;
return
""
;
}
}
/*
/*
...
@@ -284,7 +307,7 @@ interpolate_var(const char * name, PsqlSettings * pset)
...
@@ -284,7 +307,7 @@ interpolate_var(const char * name, PsqlSettings * pset)
* at least avoid trusting printf by using the more primitive fputs().
* at least avoid trusting printf by using the more primitive fputs().
*/
*/
PGconn
*
cancelConn
;
PGconn
*
cancelConn
;
#ifdef WIN32
#ifdef WIN32
#define safe_write_stderr(String) fputs(s, stderr)
#define safe_write_stderr(String) fputs(s, stderr)
...
@@ -296,79 +319,84 @@ PGconn * cancelConn;
...
@@ -296,79 +319,84 @@ PGconn * cancelConn;
static
void
static
void
handle_sigint
(
SIGNAL_ARGS
)
handle_sigint
(
SIGNAL_ARGS
)
{
{
/* accept signal if no connection */
/* accept signal if no connection */
if
(
cancelConn
==
NULL
)
if
(
cancelConn
==
NULL
)
exit
(
1
);
exit
(
1
);
/* Try to send cancel request */
/* Try to send cancel request */
if
(
PQrequestCancel
(
cancelConn
))
if
(
PQrequestCancel
(
cancelConn
))
safe_write_stderr
(
"
\n
CANCEL request sent
\n
"
);
safe_write_stderr
(
"
\n
CANCEL request sent
\n
"
);
else
{
else
safe_write_stderr
(
"
\n
Could not send cancel request: "
);
{
safe_write_stderr
(
PQerrorMessage
(
cancelConn
));
safe_write_stderr
(
"
\n
Could not send cancel request: "
);
}
safe_write_stderr
(
PQerrorMessage
(
cancelConn
));
}
}
}
/*
/*
* PSQLexec
* PSQLexec
*
*
* This is the way to send "backdoor" queries (those not directly entered
* This is the way to send "backdoor" queries (those not directly entered
* by the user). It is subject to -E (echo_secret) but not -e (echo).
* by the user). It is subject to -E (echo_secret) but not -e (echo).
*/
*/
PGresult
*
PGresult
*
PSQLexec
(
PsqlSettings
*
pset
,
const
char
*
query
)
PSQLexec
(
PsqlSettings
*
pset
,
const
char
*
query
)
{
{
PGresult
*
res
;
PGresult
*
res
;
const
char
*
var
;
const
char
*
var
;
if
(
!
pset
->
db
)
{
if
(
!
pset
->
db
)
fputs
(
"You are not currently connected to a database.
\n
"
,
stderr
);
{
return
NULL
;
fputs
(
"You are not currently connected to a database.
\n
"
,
stderr
);
}
return
NULL
;
}
var
=
GetVariable
(
pset
->
vars
,
"echo_secret"
);
if
(
var
)
{
var
=
GetVariable
(
pset
->
vars
,
"echo_secret"
);
printf
(
"********* QUERY *********
\n
%s
\n
*************************
\n\n
"
,
query
);
if
(
var
)
fflush
(
stdout
);
{
}
printf
(
"********* QUERY *********
\n
%s
\n
*************************
\n\n
"
,
query
);
fflush
(
stdout
);
if
(
var
&&
strcmp
(
var
,
"noexec"
)
==
0
)
}
return
NULL
;
if
(
var
&&
strcmp
(
var
,
"noexec"
)
==
0
)
cancelConn
=
pset
->
db
;
return
NULL
;
pqsignal
(
SIGINT
,
handle_sigint
);
/* control-C => cancel */
cancelConn
=
pset
->
db
;
res
=
PQexec
(
pset
->
db
,
query
);
pqsignal
(
SIGINT
,
handle_sigint
);
/* control-C => cancel */
pqsignal
(
SIGINT
,
SIG_DFL
);
/* no control-C is back to normal */
res
=
PQexec
(
pset
->
db
,
query
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
pqsignal
(
SIGINT
,
SIG_DFL
);
/* no control-C is back to normal */
{
fputs
(
"The connection to the server was lost. Attempting reset: "
,
stderr
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
PQreset
(
pset
->
db
);
{
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
{
fputs
(
"The connection to the server was lost. Attempting reset: "
,
stderr
);
fputs
(
"Failed.
\n
"
,
stderr
);
PQreset
(
pset
->
db
);
PQfinish
(
pset
->
db
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
PQclear
(
res
);
{
pset
->
db
=
NULL
;
fputs
(
"Failed.
\n
"
,
stderr
);
return
NULL
;
PQfinish
(
pset
->
db
);
PQclear
(
res
);
pset
->
db
=
NULL
;
return
NULL
;
}
else
fputs
(
"Succeeded.
\n
"
,
stderr
);
}
}
if
(
res
&&
(
PQresultStatus
(
res
)
==
PGRES_COMMAND_OK
||
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
||
PQresultStatus
(
res
)
==
PGRES_COPY_IN
||
PQresultStatus
(
res
)
==
PGRES_COPY_OUT
)
)
return
res
;
else
else
fputs
(
"Succeeded.
\n
"
,
stderr
);
{
}
fprintf
(
stderr
,
"%s"
,
PQerrorMessage
(
pset
->
db
));
PQclear
(
res
);
if
(
res
&&
(
PQresultStatus
(
res
)
==
PGRES_COMMAND_OK
||
return
NULL
;
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
||
}
PQresultStatus
(
res
)
==
PGRES_COPY_IN
||
PQresultStatus
(
res
)
==
PGRES_COPY_OUT
)
)
return
res
;
else
{
fprintf
(
stderr
,
"%s"
,
PQerrorMessage
(
pset
->
db
));
PQclear
(
res
);
return
NULL
;
}
}
}
...
@@ -388,131 +416,136 @@ PSQLexec(PsqlSettings *pset, const char *query)
...
@@ -388,131 +416,136 @@ PSQLexec(PsqlSettings *pset, const char *query)
bool
bool
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
)
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
)
{
{
bool
success
=
false
;
bool
success
=
false
;
PGresult
*
results
;
PGresult
*
results
;
PGnotify
*
notify
;
PGnotify
*
notify
;
if
(
!
pset
->
db
)
{
fputs
(
"You are not currently connected to a database.
\n
"
,
stderr
);
return
false
;
}
if
(
GetVariableBool
(
pset
->
vars
,
"singlestep"
))
{
char
buf
[
3
];
fprintf
(
stdout
,
"***(Single step mode: Verify query)*********************************************
\n
"
"QUERY: %s
\n
"
"***(press return to proceed or enter x and return to cancel)********************
\n
"
,
query
);
fflush
(
stdout
);
fgets
(
buf
,
3
,
stdin
);
if
(
buf
[
0
]
==
'x'
)
return
false
;
fflush
(
stdin
);
}
cancelConn
=
pset
->
db
;
pqsignal
(
SIGINT
,
handle_sigint
);
results
=
PQexec
(
pset
->
db
,
query
);
pqsignal
(
SIGINT
,
SIG_DFL
);
if
(
results
==
NULL
)
{
fputs
(
PQerrorMessage
(
pset
->
db
),
pset
->
queryFout
);
success
=
false
;
}
else
{
switch
(
PQresultStatus
(
results
))
{
case
PGRES_TUPLES_OK
:
if
(
pset
->
gfname
)
{
PsqlSettings
settings_copy
=
*
pset
;
settings_copy
.
queryFout
=
stdout
;
if
(
!
setQFout
(
pset
->
gfname
,
&
settings_copy
))
{
success
=
false
;
break
;
}
printQuery
(
results
,
&
settings_copy
.
popt
,
settings_copy
.
queryFout
);
if
(
!
pset
->
db
)
{
/* close file/pipe */
fputs
(
"You are not currently connected to a database.
\n
"
,
stderr
);
setQFout
(
NULL
,
&
settings_copy
);
return
false
;
free
(
pset
->
gfname
);
pset
->
gfname
=
NULL
;
success
=
true
;
break
;
}
else
{
success
=
true
;
printQuery
(
results
,
&
pset
->
popt
,
pset
->
queryFout
);
fflush
(
pset
->
queryFout
);
}
break
;
case
PGRES_EMPTY_QUERY
:
success
=
true
;
break
;
case
PGRES_COMMAND_OK
:
success
=
true
;
fprintf
(
pset
->
queryFout
,
"%s
\n
"
,
PQcmdStatus
(
results
));
break
;
case
PGRES_COPY_OUT
:
if
(
pset
->
cur_cmd_interactive
&&
!
GetVariable
(
pset
->
vars
,
"quiet"
))
puts
(
"Copy command returns:"
);
success
=
handleCopyOut
(
pset
->
db
,
pset
->
queryFout
);
break
;
case
PGRES_COPY_IN
:
if
(
pset
->
cur_cmd_interactive
&&
!
GetVariable
(
pset
->
vars
,
"quiet"
))
puts
(
"Enter data to be copied followed by a newline.
\n
"
"End with a backslash and a period on a line by itself."
);
success
=
handleCopyIn
(
pset
->
db
,
pset
->
cur_cmd_source
,
pset
->
cur_cmd_interactive
?
get_prompt
(
pset
,
PROMPT_COPY
)
:
NULL
);
break
;
case
PGRES_NONFATAL_ERROR
:
case
PGRES_FATAL_ERROR
:
case
PGRES_BAD_RESPONSE
:
success
=
false
;
fputs
(
PQerrorMessage
(
pset
->
db
),
pset
->
queryFout
);
break
;
}
}
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
if
(
GetVariableBool
(
pset
->
vars
,
"singlestep"
)
)
{
{
fputs
(
"The connection to the server was lost. Attempting reset: "
,
stderr
)
;
char
buf
[
3
]
;
PQreset
(
pset
->
db
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
{
fprintf
(
stdout
,
"***(Single step mode: Verify query)*********************************************
\n
"
fputs
(
"Failed.
\n
"
,
stderr
);
"QUERY: %s
\n
"
PQfinish
(
pset
->
db
);
"***(press return to proceed or enter x and return to cancel)********************
\n
"
,
PQclear
(
results
);
query
);
pset
->
db
=
NULL
;
fflush
(
stdout
)
;
return
false
;
fgets
(
buf
,
3
,
stdin
)
;
}
if
(
buf
[
0
]
==
'x'
)
else
return
false
;
f
puts
(
"Succeeded.
\n
"
,
stderr
);
f
flush
(
stdin
);
}
}
/* check for asynchronous notification returns */
cancelConn
=
pset
->
db
;
while
((
notify
=
PQnotifies
(
pset
->
db
))
!=
NULL
)
pqsignal
(
SIGINT
,
handle_sigint
);
results
=
PQexec
(
pset
->
db
,
query
);
pqsignal
(
SIGINT
,
SIG_DFL
);
if
(
results
==
NULL
)
{
{
fprintf
(
pset
->
queryFout
,
"Asynchronous NOTIFY '%s' from backend with pid '%d' received.
\n
"
,
fputs
(
PQerrorMessage
(
pset
->
db
),
pset
->
queryFout
);
notify
->
relname
,
notify
->
be_pid
);
success
=
false
;
free
(
notify
);
}
}
else
{
switch
(
PQresultStatus
(
results
))
{
case
PGRES_TUPLES_OK
:
if
(
pset
->
gfname
)
{
PsqlSettings
settings_copy
=
*
pset
;
settings_copy
.
queryFout
=
stdout
;
if
(
!
setQFout
(
pset
->
gfname
,
&
settings_copy
))
{
success
=
false
;
break
;
}
printQuery
(
results
,
&
settings_copy
.
popt
,
settings_copy
.
queryFout
);
/* close file/pipe */
setQFout
(
NULL
,
&
settings_copy
);
free
(
pset
->
gfname
);
pset
->
gfname
=
NULL
;
success
=
true
;
break
;
}
else
{
success
=
true
;
printQuery
(
results
,
&
pset
->
popt
,
pset
->
queryFout
);
fflush
(
pset
->
queryFout
);
}
break
;
case
PGRES_EMPTY_QUERY
:
success
=
true
;
break
;
case
PGRES_COMMAND_OK
:
success
=
true
;
fprintf
(
pset
->
queryFout
,
"%s
\n
"
,
PQcmdStatus
(
results
));
break
;
case
PGRES_COPY_OUT
:
if
(
pset
->
cur_cmd_interactive
&&
!
GetVariable
(
pset
->
vars
,
"quiet"
))
puts
(
"Copy command returns:"
);
success
=
handleCopyOut
(
pset
->
db
,
pset
->
queryFout
);
break
;
case
PGRES_COPY_IN
:
if
(
pset
->
cur_cmd_interactive
&&
!
GetVariable
(
pset
->
vars
,
"quiet"
))
puts
(
"Enter data to be copied followed by a newline.
\n
"
"End with a backslash and a period on a line by itself."
);
success
=
handleCopyIn
(
pset
->
db
,
pset
->
cur_cmd_source
,
pset
->
cur_cmd_interactive
?
get_prompt
(
pset
,
PROMPT_COPY
)
:
NULL
);
break
;
case
PGRES_NONFATAL_ERROR
:
case
PGRES_FATAL_ERROR
:
case
PGRES_BAD_RESPONSE
:
success
=
false
;
fputs
(
PQerrorMessage
(
pset
->
db
),
pset
->
queryFout
);
break
;
}
if
(
results
)
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
PQclear
(
results
);
{
}
fputs
(
"The connection to the server was lost. Attempting reset: "
,
stderr
);
PQreset
(
pset
->
db
);
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
{
fputs
(
"Failed.
\n
"
,
stderr
);
PQfinish
(
pset
->
db
);
PQclear
(
results
);
pset
->
db
=
NULL
;
return
false
;
}
else
fputs
(
"Succeeded.
\n
"
,
stderr
);
}
/* check for asynchronous notification returns */
while
((
notify
=
PQnotifies
(
pset
->
db
))
!=
NULL
)
{
fprintf
(
pset
->
queryFout
,
"Asynchronous NOTIFY '%s' from backend with pid '%d' received.
\n
"
,
notify
->
relname
,
notify
->
be_pid
);
free
(
notify
);
}
if
(
results
)
PQclear
(
results
);
}
return
success
;
return
success
;
}
}
src/bin/psql/common.h
View file @
0e6652e6
...
@@ -5,21 +5,21 @@
...
@@ -5,21 +5,21 @@
#include "settings.h"
#include "settings.h"
char
*
char
*
xstrdup
(
const
char
*
string
);
xstrdup
(
const
char
*
string
);
bool
bool
setQFout
(
const
char
*
fname
,
PsqlSettings
*
pset
);
setQFout
(
const
char
*
fname
,
PsqlSettings
*
pset
);
char
*
char
*
simple_prompt
(
const
char
*
prompt
,
int
maxlen
,
bool
echo
);
simple_prompt
(
const
char
*
prompt
,
int
maxlen
,
bool
echo
);
const
char
*
const
char
*
interpolate_var
(
const
char
*
name
,
PsqlSettings
*
pset
);
interpolate_var
(
const
char
*
name
,
PsqlSettings
*
pset
);
PGresult
*
PGresult
*
PSQLexec
(
PsqlSettings
*
pset
,
const
char
*
query
);
PSQLexec
(
PsqlSettings
*
pset
,
const
char
*
query
);
bool
bool
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
);
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
);
#endif
/* COMMON_H */
#endif
/* COMMON_H */
src/bin/psql/copy.c
View file @
0e6652e6
...
@@ -8,9 +8,9 @@
...
@@ -8,9 +8,9 @@
#include <errno.h>
#include <errno.h>
#include <assert.h>
#include <assert.h>
#ifndef WIN32
#ifndef WIN32
#include <unistd.h>
/* for isatty */
#include <unistd.h>
/* for isatty */
#else
#else
#include <io.h>
/* I think */
#include <io.h>
/* I think */
#endif
#endif
#include <libpq-fe.h>
#include <libpq-fe.h>
...
@@ -33,137 +33,151 @@
...
@@ -33,137 +33,151 @@
* returns a malloc'ed structure with the options, or NULL on parsing error
* returns a malloc'ed structure with the options, or NULL on parsing error
*/
*/
struct
copy_options
{
struct
copy_options
char
*
table
;
{
char
*
file
;
char
*
table
;
bool
from
;
char
*
file
;
bool
binary
;
bool
from
;
bool
oids
;
bool
binary
;
char
*
delim
;
bool
oids
;
char
*
delim
;
};
};
static
void
static
void
free_copy_options
(
struct
copy_options
*
ptr
)
free_copy_options
(
struct
copy_options
*
ptr
)
{
{
if
(
!
ptr
)
if
(
!
ptr
)
return
;
return
;
free
(
ptr
->
table
);
free
(
ptr
->
table
);
free
(
ptr
->
file
);
free
(
ptr
->
file
);
free
(
ptr
->
delim
);
free
(
ptr
->
delim
);
free
(
ptr
);
free
(
ptr
);
}
}
static
struct
copy_options
*
static
struct
copy_options
*
parse_slash_copy
(
const
char
*
args
)
parse_slash_copy
(
const
char
*
args
)
{
{
struct
copy_options
*
result
;
struct
copy_options
*
result
;
char
*
line
;
char
*
line
;
char
*
token
;
char
*
token
;
bool
error
=
false
;
bool
error
=
false
;
char
quote
;
char
quote
;
line
=
xstrdup
(
args
);
line
=
xstrdup
(
args
);
if
(
!
(
result
=
calloc
(
1
,
sizeof
(
struct
copy_options
))))
{
if
(
!
(
result
=
calloc
(
1
,
sizeof
(
struct
copy_options
))))
perror
(
"calloc"
);
{
exit
(
EXIT_FAILURE
);
perror
(
"calloc"
);
}
exit
(
EXIT_FAILURE
);
}
token
=
strtokx
(
line
,
"
\t
"
,
"
\"
"
,
'\\'
,
&
quote
,
NULL
);
if
(
!
token
)
token
=
strtokx
(
line
,
"
\t
"
,
"
\"
"
,
'\\'
,
&
quote
,
NULL
);
error
=
true
;
if
(
!
token
)
else
{
if
(
!
quote
&&
strcasecmp
(
token
,
"binary"
)
==
0
)
{
result
->
binary
=
true
;
token
=
strtokx
(
NULL
,
"
\t
"
,
"
\"
"
,
'\\'
,
&
quote
,
NULL
);
if
(
!
token
)
error
=
true
;
error
=
true
;
else
{
if
(
!
quote
&&
strcasecmp
(
token
,
"binary"
)
==
0
)
{
result
->
binary
=
true
;
token
=
strtokx
(
NULL
,
"
\t
"
,
"
\"
"
,
'\\'
,
&
quote
,
NULL
);
if
(
!
token
)
error
=
true
;
}
if
(
token
)
result
->
table
=
xstrdup
(
token
);
}
}
if
(
token
)
result
->
table
=
xstrdup
(
token
);
}
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
error
||
result
->
table
);
assert
(
error
||
result
->
table
);
#endif
#endif
if
(
!
error
)
{
if
(
!
error
)
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
{
if
(
!
token
)
error
=
true
;
else
{
if
(
strcasecmp
(
token
,
"with"
)
==
0
)
{
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
if
(
!
token
||
strcasecmp
(
token
,
"oids"
)
!=
0
)
if
(
!
token
)
error
=
true
;
else
result
->
oids
=
true
;
if
(
!
error
)
{
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
if
(
!
token
)
error
=
true
;
error
=
true
;
else
{
if
(
strcasecmp
(
token
,
"with"
)
==
0
)
{
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
if
(
!
token
||
strcasecmp
(
token
,
"oids"
)
!=
0
)
error
=
true
;
else
result
->
oids
=
true
;
if
(
!
error
)
{
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
if
(
!
token
)
error
=
true
;
}
}
if
(
!
error
&&
strcasecmp
(
token
,
"from"
)
==
0
)
result
->
from
=
true
;
else
if
(
!
error
&&
strcasecmp
(
token
,
"to"
)
==
0
)
result
->
from
=
false
;
else
error
=
true
;
}
}
}
if
(
!
error
&&
strcasecmp
(
token
,
"from"
)
==
0
)
result
->
from
=
true
;
else
if
(
!
error
&&
strcasecmp
(
token
,
"to"
)
==
0
)
result
->
from
=
false
;
else
error
=
true
;
}
}
}
if
(
!
error
)
{
if
(
!
error
)
token
=
strtokx
(
NULL
,
"
\t
"
,
"'"
,
'\\'
,
NULL
,
NULL
);
{
if
(
!
token
)
token
=
strtokx
(
NULL
,
"
\t
"
,
"'"
,
'\\'
,
NULL
,
NULL
);
error
=
true
;
if
(
!
token
)
else
error
=
true
;
result
->
file
=
xstrdup
(
token
);
else
}
result
->
file
=
xstrdup
(
token
);
}
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
error
||
result
->
file
);
assert
(
error
||
result
->
file
);
#endif
#endif
if
(
!
error
)
{
if
(
!
error
)
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
{
if
(
token
)
{
if
(
strcasecmp
(
token
,
"using"
)
!=
0
)
error
=
true
;
else
{
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
if
(
!
token
||
strcasecmp
(
token
,
"delimiters"
)
!=
0
)
if
(
token
)
error
=
true
;
{
else
{
if
(
strcasecmp
(
token
,
"using"
)
!=
0
)
token
=
strtokx
(
NULL
,
"
\t
"
,
"'"
,
'\\'
,
NULL
,
NULL
);
error
=
true
;
if
(
token
)
else
result
->
delim
=
xstrdup
(
token
);
{
else
token
=
strtokx
(
NULL
,
"
\t
"
,
NULL
,
'\\'
,
NULL
,
NULL
);
error
=
true
;
if
(
!
token
||
strcasecmp
(
token
,
"delimiters"
)
!=
0
)
error
=
true
;
else
{
token
=
strtokx
(
NULL
,
"
\t
"
,
"'"
,
'\\'
,
NULL
,
NULL
);
if
(
token
)
result
->
delim
=
xstrdup
(
token
);
else
error
=
true
;
}
}
}
}
}
}
}
}
free
(
line
);
free
(
line
);
if
(
error
)
{
if
(
error
)
fputs
(
"Parse error at "
,
stderr
);
{
if
(
!
token
)
fputs
(
"Parse error at "
,
stderr
);
fputs
(
"end of line."
,
stderr
);
if
(
!
token
)
fputs
(
"end of line."
,
stderr
);
else
fprintf
(
stderr
,
"'%s'."
,
token
);
fputs
(
"
\n
"
,
stderr
);
free
(
result
);
return
NULL
;
}
else
else
fprintf
(
stderr
,
"'%s'."
,
token
);
return
result
;
fputs
(
"
\n
"
,
stderr
);
}
free
(
result
);
return
NULL
;
}
else
return
result
;
}
...
@@ -171,103 +185,109 @@ parse_slash_copy(const char *args)
...
@@ -171,103 +185,109 @@ parse_slash_copy(const char *args)
* Execute a \copy command (frontend copy). We have to open a file, then
* Execute a \copy command (frontend copy). We have to open a file, then
* submit a COPY query to the backend and either feed it data from the
* submit a COPY query to the backend and either feed it data from the
* file or route its response into the file.
* file or route its response into the file.
*/
*/
bool
bool
do_copy
(
const
char
*
args
,
PsqlSettings
*
pset
)
do_copy
(
const
char
*
args
,
PsqlSettings
*
pset
)
{
{
char
query
[
128
+
NAMEDATALEN
];
char
query
[
128
+
NAMEDATALEN
];
FILE
*
copystream
;
FILE
*
copystream
;
struct
copy_options
*
options
;
struct
copy_options
*
options
;
PGresult
*
result
;
PGresult
*
result
;
bool
success
;
bool
success
;
/* parse options */
/* parse options */
options
=
parse_slash_copy
(
args
);
options
=
parse_slash_copy
(
args
);
if
(
!
options
)
if
(
!
options
)
return
false
;
return
false
;
strcpy
(
query
,
"COPY "
);
strcpy
(
query
,
"COPY "
);
if
(
options
->
binary
)
if
(
options
->
binary
)
fputs
(
"Warning:
\\
copy binary is not implemented. Resorting to text output.
\n
"
,
stderr
);
fputs
(
"Warning:
\\
copy binary is not implemented. Resorting to text output.
\n
"
,
stderr
);
/* strcat(query, "BINARY "); */
/* strcat(query, "BINARY "); */
strcat
(
query
,
"
\"
"
);
strcat
(
query
,
"
\"
"
);
strncat
(
query
,
options
->
table
,
NAMEDATALEN
);
strncat
(
query
,
options
->
table
,
NAMEDATALEN
);
strcat
(
query
,
"
\"
"
);
strcat
(
query
,
"
\"
"
);
if
(
options
->
oids
)
if
(
options
->
oids
)
strcat
(
query
,
"WITH OIDS "
);
strcat
(
query
,
"WITH OIDS "
);
if
(
options
->
from
)
if
(
options
->
from
)
strcat
(
query
,
"FROM stdin"
);
strcat
(
query
,
"FROM stdin"
);
else
else
strcat
(
query
,
"TO stdout"
);
strcat
(
query
,
"TO stdout"
);
if
(
options
->
delim
)
{
if
(
options
->
delim
)
/* backend copy only uses the first character here,
{
but that might be the escape backslash
(makes me wonder though why it's called delimiterS) */
/*
strncat
(
query
,
" USING DELIMITERS '"
,
2
);
* backend copy only uses the first character here, but that might
strcat
(
query
,
options
->
delim
);
* be the escape backslash (makes me wonder though why it's called
strcat
(
query
,
"'"
);
* delimiterS)
}
*/
strncat
(
query
,
" USING DELIMITERS '"
,
2
);
strcat
(
query
,
options
->
delim
);
strcat
(
query
,
"'"
);
}
if
(
options
->
from
)
if
(
options
->
from
)
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
copystream
=
fopen
(
options
->
file
,
"r"
);
copystream
=
fopen
(
options
->
file
,
"r"
);
#else
#else
copystream
=
fopen
(
options
->
file
,
"rb"
);
copystream
=
fopen
(
options
->
file
,
"rb"
);
#endif
#endif
else
else
#ifndef __CYGWIN32__
#ifndef __CYGWIN32__
copystream
=
fopen
(
options
->
file
,
"w"
);
copystream
=
fopen
(
options
->
file
,
"w"
);
#else
#else
copystream
=
fopen
(
options
->
file
,
"wb"
);
copystream
=
fopen
(
options
->
file
,
"wb"
);
#endif
#endif
if
(
!
copystream
)
{
if
(
!
copystream
)
fprintf
(
stderr
,
{
"Unable to open file %s which to copy: %s
\n
"
,
fprintf
(
stderr
,
options
->
from
?
"from"
:
"to"
,
strerror
(
errno
));
"Unable to open file %s which to copy: %s
\n
"
,
free_copy_options
(
options
);
options
->
from
?
"from"
:
"to"
,
strerror
(
errno
));
return
false
;
free_copy_options
(
options
);
}
return
false
;
}
result
=
PSQLexec
(
pset
,
query
);
result
=
PSQLexec
(
pset
,
query
);
switch
(
PQresultStatus
(
result
))
{
switch
(
PQresultStatus
(
result
))
case
PGRES_COPY_OUT
:
{
success
=
handleCopyOut
(
pset
->
db
,
copystream
);
case
PGRES_COPY_OUT
:
break
;
success
=
handleCopyOut
(
pset
->
db
,
copystream
);
case
PGRES_COPY_IN
:
break
;
success
=
handleCopyIn
(
pset
->
db
,
copystream
,
NULL
);
case
PGRES_COPY_IN
:
break
;
success
=
handleCopyIn
(
pset
->
db
,
copystream
,
NULL
);
case
PGRES_NONFATAL_ERROR
:
break
;
case
PGRES_FATAL_ERROR
:
case
PGRES_NONFATAL_ERROR
:
case
PGRES_BAD_RESPONSE
:
case
PGRES_FATAL_ERROR
:
success
=
false
;
case
PGRES_BAD_RESPONSE
:
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
success
=
false
;
break
;
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
default:
break
;
success
=
false
;
default:
fprintf
(
stderr
,
"Unexpected response (%d)
\n
"
,
PQresultStatus
(
result
));
success
=
false
;
}
fprintf
(
stderr
,
"Unexpected response (%d)
\n
"
,
PQresultStatus
(
result
));
}
PQclear
(
result
);
PQclear
(
result
);
if
(
!
GetVariable
(
pset
->
vars
,
"quiet"
))
{
if
(
success
)
puts
(
"Successfully copied."
);
else
puts
(
"Copy failed."
);
}
fclose
(
copystream
);
if
(
!
GetVariable
(
pset
->
vars
,
"quiet"
))
free_copy_options
(
options
);
{
return
success
;
if
(
success
)
puts
(
"Successfully copied."
);
else
puts
(
"Copy failed."
);
}
fclose
(
copystream
);
free_copy_options
(
options
);
return
success
;
}
}
...
@@ -287,38 +307,38 @@ do_copy(const char * args, PsqlSettings *pset)
...
@@ -287,38 +307,38 @@ do_copy(const char * args, PsqlSettings *pset)
bool
bool
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
)
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
)
{
{
bool
copydone
=
false
;
/* haven't started yet */
bool
copydone
=
false
;
/* haven't started yet */
char
copybuf
[
COPYBUFSIZ
];
char
copybuf
[
COPYBUFSIZ
];
int
ret
;
int
ret
;
while
(
!
copydone
)
{
ret
=
PQgetline
(
conn
,
copybuf
,
COPYBUFSIZ
);
if
(
copybuf
[
0
]
==
'\\'
&&
while
(
!
copydone
)
copybuf
[
1
]
==
'.'
&&
copybuf
[
2
]
==
'\0'
)
{
{
copydone
=
true
;
/* we're at the end */
ret
=
PQgetline
(
conn
,
copybuf
,
COPYBUFSIZ
);
}
else
if
(
copybuf
[
0
]
==
'\\'
&&
{
copybuf
[
1
]
==
'.'
&&
fputs
(
copybuf
,
copystream
);
copybuf
[
2
]
==
'\0'
)
switch
(
ret
)
{
{
copydone
=
true
;
/* we're at the end */
case
EOF
:
}
copydone
=
true
;
else
/* FALLTHROUGH */
{
case
0
:
fputs
(
copybuf
,
copystream
);
fputc
(
'\n'
,
copystream
);
switch
(
ret
)
break
;
{
case
1
:
case
EOF
:
break
;
copydone
=
true
;
}
/* FALLTHROUGH */
case
0
:
fputc
(
'\n'
,
copystream
);
break
;
case
1
:
break
;
}
}
}
}
}
fflush
(
copystream
);
fflush
(
copystream
);
return
!
PQendcopy
(
conn
);
return
!
PQendcopy
(
conn
);
}
}
...
@@ -333,58 +353,58 @@ handleCopyOut(PGconn *conn, FILE *copystream)
...
@@ -333,58 +353,58 @@ handleCopyOut(PGconn *conn, FILE *copystream)
* (and which gave you PGRES_COPY_IN back);
* (and which gave you PGRES_COPY_IN back);
* copystream is the file stream you want the input to come from
* copystream is the file stream you want the input to come from
* prompt is something to display to request user input (only makes sense
* prompt is something to display to request user input (only makes sense
*
if stdin is an interactive tty)
*
if stdin is an interactive tty)
*/
*/
bool
bool
handleCopyIn
(
PGconn
*
conn
,
FILE
*
copystream
,
const
char
*
prompt
)
handleCopyIn
(
PGconn
*
conn
,
FILE
*
copystream
,
const
char
*
prompt
)
{
{
bool
copydone
=
false
;
bool
copydone
=
false
;
bool
firstload
;
bool
firstload
;
bool
linedone
;
bool
linedone
;
char
copybuf
[
COPYBUFSIZ
];
char
copybuf
[
COPYBUFSIZ
];
char
*
s
;
char
*
s
;
int
buflen
;
int
buflen
;
int
c
=
0
;
int
c
=
0
;
while
(
!
copydone
)
while
(
!
copydone
)
{
/* for each input line ... */
{
/* for each input line ... */
if
(
prompt
&&
isatty
(
fileno
(
stdin
)))
if
(
prompt
&&
isatty
(
fileno
(
stdin
)))
{
fputs
(
prompt
,
stdout
);
fflush
(
stdout
);
}
firstload
=
true
;
linedone
=
false
;
while
(
!
linedone
)
{
/* for each buffer ... */
s
=
copybuf
;
for
(
buflen
=
COPYBUFSIZ
;
buflen
>
1
;
buflen
--
)
{
c
=
getc
(
copystream
);
if
(
c
==
'\n'
||
c
==
EOF
)
{
{
linedone
=
true
;
fputs
(
prompt
,
stdout
);
break
;
fflush
(
stdout
);
}
firstload
=
true
;
linedone
=
false
;
while
(
!
linedone
)
{
/* for each buffer ... */
s
=
copybuf
;
for
(
buflen
=
COPYBUFSIZ
;
buflen
>
1
;
buflen
--
)
{
c
=
getc
(
copystream
);
if
(
c
==
'\n'
||
c
==
EOF
)
{
linedone
=
true
;
break
;
}
*
s
++
=
c
;
}
*
s
=
'\0'
;
if
(
c
==
EOF
)
{
PQputline
(
conn
,
"
\\
."
);
copydone
=
true
;
break
;
}
PQputline
(
conn
,
copybuf
);
if
(
firstload
)
{
if
(
!
strcmp
(
copybuf
,
"
\\
."
))
copydone
=
true
;
firstload
=
false
;
}
}
}
*
s
++
=
c
;
PQputline
(
conn
,
"
\n
"
);
}
*
s
=
'\0'
;
if
(
c
==
EOF
)
{
PQputline
(
conn
,
"
\\
."
);
copydone
=
true
;
break
;
}
PQputline
(
conn
,
copybuf
);
if
(
firstload
)
{
if
(
!
strcmp
(
copybuf
,
"
\\
."
))
copydone
=
true
;
firstload
=
false
;
}
}
}
PQputline
(
conn
,
"
\n
"
);
return
!
PQendcopy
(
conn
);
}
return
!
PQendcopy
(
conn
);
}
}
src/bin/psql/copy.h
View file @
0e6652e6
...
@@ -8,15 +8,15 @@
...
@@ -8,15 +8,15 @@
/* handler for \copy */
/* handler for \copy */
bool
bool
do_copy
(
const
char
*
args
,
PsqlSettings
*
pset
);
do_copy
(
const
char
*
args
,
PsqlSettings
*
pset
);
/* lower level processors for copy in/out streams */
/* lower level processors for copy in/out streams */
bool
bool
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
);
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
);
bool
bool
handleCopyIn
(
PGconn
*
conn
,
FILE
*
copystream
,
const
char
*
prompt
);
handleCopyIn
(
PGconn
*
conn
,
FILE
*
copystream
,
const
char
*
prompt
);
#endif
#endif
src/bin/psql/describe.c
View file @
0e6652e6
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
#include <string.h>
#include <string.h>
#include <postgres.h>
/* for VARHDRSZ, int4 type */
#include <postgres.h>
/* for VARHDRSZ, int4 type */
#include <postgres_ext.h>
#include <postgres_ext.h>
#include <libpq-fe.h>
#include <libpq-fe.h>
...
@@ -20,7 +20,7 @@
...
@@ -20,7 +20,7 @@
*
*
* If you add something here, consider this:
* If you add something here, consider this:
* - If (and only if) the variable "description" is set, the description/
* - If (and only if) the variable "description" is set, the description/
*
comment for the object should be displayed.
*
comment for the object should be displayed.
* - Try to format the query to look nice in -E output.
* - Try to format the query to look nice in -E output.
*----------------
*----------------
*/
*/
...
@@ -34,61 +34,66 @@
...
@@ -34,61 +34,66 @@
* takes an optional regexp to match specific aggregates by name
* takes an optional regexp to match specific aggregates by name
*/
*/
bool
bool
describeAggregates
(
const
char
*
name
,
PsqlSettings
*
pset
)
describeAggregates
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
384
+
2
*
REGEXP_CUTOFF
];
/* observe/adjust this if you change the query */
char
descbuf
[
384
+
2
*
REGEXP_CUTOFF
];
/* observe/adjust this
PGresult
*
res
;
* if you change the
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
* query */
printQueryOpt
myopt
=
pset
->
popt
;
PGresult
*
res
;
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
descbuf
[
0
]
=
'\0'
;
printQueryOpt
myopt
=
pset
->
popt
;
/* There are two kinds of aggregates: ones that work on particular types
descbuf
[
0
]
=
'\0'
;
ones that work on all */
strcat
(
descbuf
,
/*
"SELECT a.aggname AS
\"
Name
\"
, t.typname AS
\"
Type
\"
"
);
* There are two kinds of aggregates: ones that work on particular
if
(
description
)
* types ones that work on all
strcat
(
descbuf
,
*/
",
\n
obj_description(a.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
strcat
(
descbuf
,
"SELECT a.aggname AS
\"
Name
\"
, t.typname AS
\"
Type
\"
"
);
"
\n
FROM pg_aggregate a, pg_type t
\n
"
if
(
description
)
"WHERE a.aggbasetype = t.oid
\n
"
);
strcat
(
descbuf
,
if
(
name
)
{
",
\n
obj_description(a.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
strcat
(
descbuf
,
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
"
\n
FROM pg_aggregate a, pg_type t
\n
"
strcat
(
descbuf
,
"'
\n
"
);
"WHERE a.aggbasetype = t.oid
\n
"
);
}
if
(
name
)
{
strcat
(
descbuf
,
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
"UNION
\n
"
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
"SELECT a.aggname AS
\"
Name
\"
, '(all types)' as
\"
Type
\"
"
);
strcat
(
descbuf
,
"'
\n
"
);
if
(
description
)
}
strcat
(
descbuf
,
",
\n
obj_description(a.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
strcat
(
descbuf
,
"UNION
\n
"
"
\n
FROM pg_aggregate a
\n
"
"SELECT a.aggname AS
\"
Name
\"
, '(all types)' as
\"
Type
\"
"
);
"WHERE a.aggbasetype = 0
\n
"
);
if
(
description
)
if
(
name
)
strcat
(
descbuf
,
{
",
\n
obj_description(a.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
strcat
(
descbuf
,
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
"
\n
FROM pg_aggregate a
\n
"
strcat
(
descbuf
,
"'
\n
"
);
"WHERE a.aggbasetype = 0
\n
"
);
}
if
(
name
)
{
strcat
(
descbuf
,
"ORDER BY
\"
Name
\"
,
\"
Type
\"
"
);
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
res
=
PSQLexec
(
pset
,
descbuf
);
strcat
(
descbuf
,
"'
\n
"
);
if
(
!
res
)
}
return
false
;
strcat
(
descbuf
,
"ORDER BY
\"
Name
\"
,
\"
Type
\"
"
);
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
res
=
PSQLexec
(
pset
,
descbuf
);
myopt
.
title
=
"List of aggregates"
;
if
(
!
res
)
return
false
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
myopt
.
topt
.
tuples_only
=
false
;
PQclear
(
res
);
myopt
.
nullPrint
=
NULL
;
return
true
;
myopt
.
title
=
"List of aggregates"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
return
true
;
}
}
...
@@ -96,45 +101,44 @@ describeAggregates(const char * name, PsqlSettings * pset)
...
@@ -96,45 +101,44 @@ describeAggregates(const char * name, PsqlSettings * pset)
* takes an optional regexp to narrow down the function name
* takes an optional regexp to narrow down the function name
*/
*/
bool
bool
describeFunctions
(
const
char
*
name
,
PsqlSettings
*
pset
)
describeFunctions
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
384
+
REGEXP_CUTOFF
];
char
descbuf
[
384
+
REGEXP_CUTOFF
];
PGresult
*
res
;
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
/*
/*
* we skip in/out funcs by excluding functions that take
* we skip in/out funcs by excluding functions that take some
* some arguments, but have no types defined for those
* arguments, but have no types defined for those arguments
* arguments
*/
*/
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT t.typname as
\"
Result
\"
, p.proname as
\"
Function
\"
,
\n
"
strcat
(
descbuf
,
"SELECT t.typname as
\"
Result
\"
, p.proname as
\"
Function
\"
,
\n
"
" oid8types(p.proargtypes) as
\"
Arguments
\"
"
);
" oid8types(p.proargtypes) as
\"
Arguments
\"
"
);
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
strcat
(
descbuf
,
"
\n
, obj_description(p.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
"
\n
, obj_description(p.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
"
\n
FROM pg_proc p, pg_type t
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_proc p, pg_type t
\n
"
"WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')
\n
"
);
"WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')
\n
"
);
if
(
name
)
if
(
name
)
{
{
strcat
(
descbuf
,
" AND p.proname ~* '^"
);
strcat
(
descbuf
,
" AND p.proname ~* '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"'
\n
"
);
}
}
strcat
(
descbuf
,
"ORDER BY
\"
Function
\"
,
\"
Result
\"
,
\"
Arguments
\"
"
);
strcat
(
descbuf
,
"ORDER BY
\"
Function
\"
,
\"
Result
\"
,
\"
Arguments
\"
"
);
res
=
PSQLexec
(
pset
,
descbuf
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
if
(
!
res
)
return
false
;
return
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"List of functions"
;
myopt
.
title
=
"List of functions"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
PQclear
(
res
);
return
true
;
return
true
;
}
}
...
@@ -145,38 +149,39 @@ describeFunctions(const char * name, PsqlSettings * pset)
...
@@ -145,38 +149,39 @@ describeFunctions(const char * name, PsqlSettings * pset)
* for \dT
* for \dT
*/
*/
bool
bool
describeTypes
(
const
char
*
name
,
PsqlSettings
*
pset
)
describeTypes
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
256
+
REGEXP_CUTOFF
];
char
descbuf
[
256
+
REGEXP_CUTOFF
];
PGresult
*
res
;
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT typname AS
\"
Type
\"
"
);
strcat
(
descbuf
,
"SELECT typname AS
\"
Type
\"
"
);
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
strcat
(
descbuf
,
", obj_description(p.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
", obj_description(p.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
"
\n
FROM pg_type
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_type
\n
"
"WHERE typrelid = 0 AND typname !~ '^_.*'
\n
"
);
"WHERE typrelid = 0 AND typname !~ '^_.*'
\n
"
);
if
(
name
)
{
if
(
name
)
strcat
(
descbuf
,
" AND typname ~* '^"
);
{
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
" AND typname ~* '^"
);
strcat
(
descbuf
,
"' "
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
}
strcat
(
descbuf
,
"' "
);
strcat
(
descbuf
,
"ORDER BY typname;"
);
}
strcat
(
descbuf
,
"ORDER BY typname;"
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
res
=
PSQLexec
(
pset
,
descbuf
);
return
false
;
if
(
!
res
)
return
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
title
=
"List of types"
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"List of types"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
return
true
;
PQclear
(
res
);
return
true
;
}
}
...
@@ -187,87 +192,88 @@ describeTypes(const char * name, PsqlSettings * pset)
...
@@ -187,87 +192,88 @@ describeTypes(const char * name, PsqlSettings * pset)
* exact match string.
* exact match string.
*/
*/
bool
bool
describeOperators
(
const
char
*
name
,
PsqlSettings
*
pset
)
describeOperators
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
1536
+
3
*
32
];
/* 32 is max length for operator name */
char
descbuf
[
1536
+
3
*
32
];
/* 32 is max length for operator
PGresult
*
res
;
* name */
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT o.oprname AS
\"
Op
\"
,
\n
"
" t1.typname AS
\"
Left arg
\"
,
\n
"
strcat
(
descbuf
,
"SELECT o.oprname AS
\"
Op
\"
,
\n
"
" t2.typname AS
\"
Right arg
\"
,
\n
"
" t1.typname AS
\"
Left arg
\"
,
\n
"
" t0.typname AS
\"
Result
\"
"
);
" t2.typname AS
\"
Right arg
\"
,
\n
"
if
(
description
)
" t0.typname AS
\"
Result
\"
"
);
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
if
(
description
)
strcat
(
descbuf
,
"
\n
FROM pg_proc p, pg_type t0,
\n
"
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
" pg_type t1, pg_type t2,
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_proc p, pg_type t0,
\n
"
" pg_operator o
\n
"
" pg_type t1, pg_type t2,
\n
"
"WHERE p.prorettype = t0.oid AND
\n
"
" pg_operator o
\n
"
" RegprocToOid(o.oprcode) = p.oid AND
\n
"
"WHERE p.prorettype = t0.oid AND
\n
"
" p.pronargs = 2 AND
\n
"
" RegprocToOid(o.oprcode) = p.oid AND
\n
"
" o.oprleft = t1.oid AND
\n
"
" p.pronargs = 2 AND
\n
"
" o.oprright = t2.oid
\n
"
);
" o.oprleft = t1.oid AND
\n
"
if
(
name
)
" o.oprright = t2.oid
\n
"
);
{
if
(
name
)
strcat
(
descbuf
,
" AND o.oprname = '"
);
{
strncat
(
descbuf
,
name
,
32
);
strcat
(
descbuf
,
" AND o.oprname = '"
);
strcat
(
descbuf
,
"'
\n
"
);
strncat
(
descbuf
,
name
,
32
);
}
strcat
(
descbuf
,
"'
\n
"
);
}
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
"SELECT o.oprname as
\"
Op
\"
,
\n
"
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
" ''::name AS
\"
Left arg
\"
,
\n
"
"SELECT o.oprname as
\"
Op
\"
,
\n
"
" t1.typname AS
\"
Right arg
\"
,
\n
"
" ''::name AS
\"
Left arg
\"
,
\n
"
" t0.typname AS
\"
Result
\"
"
);
" t1.typname AS
\"
Right arg
\"
,
\n
"
if
(
description
)
" t0.typname AS
\"
Result
\"
"
);
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
if
(
description
)
strcat
(
descbuf
,
"
\n
FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1
\n
"
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
"WHERE RegprocToOid(o.oprcode) = p.oid AND
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1
\n
"
" o.oprresult = t0.oid AND
\n
"
"WHERE RegprocToOid(o.oprcode) = p.oid AND
\n
"
" o.oprkind = 'l' AND
\n
"
" o.oprresult = t0.oid AND
\n
"
" o.oprright = t1.oid
\n
"
);
" o.oprkind = 'l' AND
\n
"
if
(
name
)
" o.oprright = t1.oid
\n
"
);
{
if
(
name
)
strcat
(
descbuf
,
"AND o.oprname = '"
);
{
strncat
(
descbuf
,
name
,
32
);
strcat
(
descbuf
,
"AND o.oprname = '"
);
strcat
(
descbuf
,
"'
\n
"
);
strncat
(
descbuf
,
name
,
32
);
}
strcat
(
descbuf
,
"'
\n
"
);
}
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
"SELECT o.oprname as
\"
Op
\"
,
\n
"
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
" t1.typname AS
\"
Left arg
\"
,
\n
"
"SELECT o.oprname as
\"
Op
\"
,
\n
"
" ''::name AS
\"
Right arg
\"
,
\n
"
" t1.typname AS
\"
Left arg
\"
,
\n
"
" t0.typname AS
\"
Result
\"
"
);
" ''::name AS
\"
Right arg
\"
,
\n
"
if
(
description
)
" t0.typname AS
\"
Result
\"
"
);
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
if
(
description
)
strcat
(
descbuf
,
"
\n
FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1
\n
"
strcat
(
descbuf
,
",
\n
obj_description(p.oid) as
\"
Description
\"
"
);
"WHERE RegprocToOid(o.oprcode) = p.oid AND
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1
\n
"
" o.oprresult = t0.oid AND
\n
"
"WHERE RegprocToOid(o.oprcode) = p.oid AND
\n
"
" o.oprkind = 'r' AND
\n
"
" o.oprresult = t0.oid AND
\n
"
" o.oprleft = t1.oid
\n
"
);
" o.oprkind = 'r' AND
\n
"
if
(
name
)
" o.oprleft = t1.oid
\n
"
);
{
if
(
name
)
strcat
(
descbuf
,
"AND o.oprname = '"
);
{
strncat
(
descbuf
,
name
,
32
);
strcat
(
descbuf
,
"AND o.oprname = '"
);
strcat
(
descbuf
,
"'
\n
"
);
strncat
(
descbuf
,
name
,
32
);
}
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Op
\"
,
\"
Left arg
\"
,
\"
Right arg
\"
,
\"
Result
\"
"
);
}
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Op
\"
,
\"
Left arg
\"
,
\"
Right arg
\"
,
\"
Result
\"
"
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
res
=
PSQLexec
(
pset
,
descbuf
);
return
false
;
if
(
!
res
)
return
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
title
=
"List of operators"
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"List of operators"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
return
true
;
PQclear
(
res
);
return
true
;
}
}
...
@@ -279,81 +285,82 @@ describeOperators(const char * name, PsqlSettings * pset)
...
@@ -279,81 +285,82 @@ describeOperators(const char * name, PsqlSettings * pset)
bool
bool
listAllDbs
(
PsqlSettings
*
pset
)
listAllDbs
(
PsqlSettings
*
pset
)
{
{
PGresult
*
res
;
PGresult
*
res
;
char
descbuf
[
256
];
char
descbuf
[
256
];
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT pg_database.datname as
\"
Database
\"
,
\n
"
strcat
(
descbuf
,
"SELECT pg_database.datname as
\"
Database
\"
,
\n
"
" pg_user.usename as
\"
Owner
\"
"
" pg_user.usename as
\"
Owner
\"
"
#ifdef MULTIBYTE
#ifdef MULTIBYTE
",
\n
pg_database.encoding as
\"
Encoding
\"
"
",
\n
pg_database.encoding as
\"
Encoding
\"
"
#endif
#endif
);
);
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
strcat
(
descbuf
,
",
\n
obj_description(pg_database.oid) as
\"
Description
\"\n
"
);
strcat
(
descbuf
,
",
\n
obj_description(pg_database.oid) as
\"
Description
\"\n
"
);
strcat
(
descbuf
,
"FROM pg_database, pg_user
\n
"
strcat
(
descbuf
,
"FROM pg_database, pg_user
\n
"
"WHERE pg_database.datdba = pg_user.usesysid
\n
"
"WHERE pg_database.datdba = pg_user.usesysid
\n
"
"ORDER BY
\"
Database
\"
"
);
"ORDER BY
\"
Database
\"
"
);
res
=
PSQLexec
(
pset
,
descbuf
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
if
(
!
res
)
return
false
;
return
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"List of databases"
;
myopt
.
title
=
"List of databases"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
PQclear
(
res
);
return
true
;
return
true
;
}
}
/* List Tables Grant/Revoke Permissions
/* List Tables Grant/Revoke Permissions
* \z (now also \dp -- perhaps more mnemonic)
* \z (now also \dp -- perhaps more mnemonic)
*
*
*/
*/
bool
bool
permissionsList
(
const
char
*
name
,
PsqlSettings
*
pset
)
permissionsList
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
256
+
REGEXP_CUTOFF
];
char
descbuf
[
256
+
REGEXP_CUTOFF
];
PGresult
*
res
;
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
/* Currently, we ignore indexes since they have no meaningful rights */
/* Currently, we ignore indexes since they have no meaningful rights */
strcat
(
descbuf
,
"SELECT relname as
\"
Relation
\"
,
\n
"
strcat
(
descbuf
,
"SELECT relname as
\"
Relation
\"
,
\n
"
" relacl as
\"
Access permissions
\"\n
"
" relacl as
\"
Access permissions
\"\n
"
"FROM pg_class
\n
"
"FROM pg_class
\n
"
"WHERE ( relkind = 'r' OR relkind = 'S') AND
\n
"
"WHERE ( relkind = 'r' OR relkind = 'S') AND
\n
"
" relname !~ '^pg_'
\n
"
);
" relname !~ '^pg_'
\n
"
);
if
(
name
)
{
if
(
name
)
strcat
(
descbuf
,
" AND rename ~ '^"
);
{
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
" AND rename ~ '^"
);
strcat
(
descbuf
,
"'
\n
"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
}
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"ORDER BY relname"
);
}
strcat
(
descbuf
,
"ORDER BY relname"
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
return
false
;
if
(
PQntuples
(
res
)
==
0
)
{
fputs
(
"Couldn't find any tables.
\n
"
,
pset
->
queryFout
);
}
else
{
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
sprintf
(
descbuf
,
"Access permissions for database
\"
%s
\"
"
,
PQdb
(
pset
->
db
));
myopt
.
title
=
descbuf
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
res
=
PSQLexec
(
pset
,
descbuf
);
}
if
(
!
res
)
return
false
;
if
(
PQntuples
(
res
)
==
0
)
fputs
(
"Couldn't find any tables.
\n
"
,
pset
->
queryFout
);
else
{
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
sprintf
(
descbuf
,
"Access permissions for database
\"
%s
\"
"
,
PQdb
(
pset
->
db
));
myopt
.
title
=
descbuf
;
PQclear
(
res
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
return
true
;
}
PQclear
(
res
);
return
true
;
}
}
...
@@ -368,106 +375,113 @@ permissionsList(const char * name, PsqlSettings *pset)
...
@@ -368,106 +375,113 @@ permissionsList(const char * name, PsqlSettings *pset)
* lists of things, there are other \d? commands.
* lists of things, there are other \d? commands.
*/
*/
bool
bool
objectDescription
(
const
char
*
object
,
PsqlSettings
*
pset
)
objectDescription
(
const
char
*
object
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
2048
+
7
*
REGEXP_CUTOFF
];
char
descbuf
[
2048
+
7
*
REGEXP_CUTOFF
];
PGresult
*
res
;
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
/* Aggregate descriptions */
/* Aggregate descriptions */
strcat
(
descbuf
,
"SELECT DISTINCT a.aggname as
\"
Name
\"
, 'aggregate'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strcat
(
descbuf
,
"SELECT DISTINCT a.aggname as
\"
Name
\"
, 'aggregate'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
"FROM pg_aggregate a, pg_description d
\n
"
"FROM pg_aggregate a, pg_description d
\n
"
"WHERE a.oid = d.objoid
\n
"
);
"WHERE a.oid = d.objoid
\n
"
);
if
(
object
)
{
if
(
object
)
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
{
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
" AND a.aggname ~* '^"
);
strcat
(
descbuf
,
"'
\n
"
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
}
strcat
(
descbuf
,
"'
\n
"
);
}
/* Function descriptions (except in/outs for datatypes) */
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
/* Function descriptions (except in/outs for datatypes) */
strcat
(
descbuf
,
"SELECT DISTINCT p.proname as
\"
Name
\"
, 'function'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
"FROM pg_proc p, pg_description d
\n
"
strcat
(
descbuf
,
"SELECT DISTINCT p.proname as
\"
Name
\"
, 'function'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
"WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')
\n
"
);
"FROM pg_proc p, pg_description d
\n
"
if
(
object
)
{
"WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')
\n
"
);
strcat
(
descbuf
,
" AND p.proname ~* '^"
);
if
(
object
)
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
{
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
" AND p.proname ~* '^"
);
}
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
/* Operator descriptions */
}
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strcat
(
descbuf
,
"SELECT DISTINCT o.oprname as
\"
Name
\"
, 'operator'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
/* Operator descriptions */
"FROM pg_operator o, pg_description d
\n
"
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
// must get comment via associated function
strcat
(
descbuf
,
"SELECT DISTINCT o.oprname as
\"
Name
\"
, 'operator'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
"WHERE RegprocToOid(o.oprcode) = d.objoid
\n
"
);
"FROM pg_operator o, pg_description d
\n
"
if
(
object
)
{
// must get comment via associated function
strcat
(
descbuf
,
" AND o.oprname = '"
);
"WHERE RegprocToOid(o.oprcode) = d.objoid
\n
"
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
if
(
object
)
strcat
(
descbuf
,
"'
\n
"
);
{
}
strcat
(
descbuf
,
" AND o.oprname = '"
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
/* Type description */
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
}
strcat
(
descbuf
,
"SELECT DISTINCT t.typname as
\"
Name
\"
, 'type'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
"FROM pg_type t, pg_description d
\n
"
/* Type description */
"WHERE t.oid = d.objoid
\n
"
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
if
(
object
)
{
strcat
(
descbuf
,
"SELECT DISTINCT t.typname as
\"
Name
\"
, 'type'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strcat
(
descbuf
,
" AND t.typname ~* '^"
);
"FROM pg_type t, pg_description d
\n
"
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
"WHERE t.oid = d.objoid
\n
"
);
strcat
(
descbuf
,
"'
\n
"
);
if
(
object
)
}
{
strcat
(
descbuf
,
" AND t.typname ~* '^"
);
/* Relation (tables, views, indices, sequences) descriptions */
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"SELECT DISTINCT c.relname as
\"
Name
\"
, 'relation'::text||'('||c.relkind||')' as
\"
What
\"
, d.description as
\"
Description
\"\n
"
}
"FROM pg_class c, pg_description d
\n
"
"WHERE c.oid = d.objoid
\n
"
);
/* Relation (tables, views, indices, sequences) descriptions */
if
(
object
)
{
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strcat
(
descbuf
,
" AND c.relname ~* '^"
);
strcat
(
descbuf
,
"SELECT DISTINCT c.relname as
\"
Name
\"
, 'relation'::text||'('||c.relkind||')' as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
"FROM pg_class c, pg_description d
\n
"
strcat
(
descbuf
,
"'
\n
"
);
"WHERE c.oid = d.objoid
\n
"
);
}
if
(
object
)
{
/* Rule description (ignore rules for views) */
strcat
(
descbuf
,
" AND c.relname ~* '^"
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"SELECT DISTINCT r.rulename as
\"
Name
\"
, 'rule'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strcat
(
descbuf
,
"'
\n
"
);
"FROM pg_rewrite r, pg_description d
\n
"
}
"WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'
\n
"
);
if
(
object
)
{
/* Rule description (ignore rules for views) */
strcat
(
descbuf
,
" AND r.rulename ~* '^"
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"SELECT DISTINCT r.rulename as
\"
Name
\"
, 'rule'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strcat
(
descbuf
,
"'
\n
"
);
"FROM pg_rewrite r, pg_description d
\n
"
}
"WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'
\n
"
);
if
(
object
)
/* Trigger description */
{
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strcat
(
descbuf
,
" AND r.rulename ~* '^"
);
strcat
(
descbuf
,
"SELECT DISTINCT t.tgname as
\"
Name
\"
, 'trigger'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
"FROM pg_trigger t, pg_description d
\n
"
strcat
(
descbuf
,
"'
\n
"
);
"WHERE t.oid = d.objoid
\n
"
);
}
if
(
object
)
{
strcat
(
descbuf
,
" AND t.tgname ~* '^"
);
/* Trigger description */
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"
\n
UNION ALL
\n\n
"
);
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
"SELECT DISTINCT t.tgname as
\"
Name
\"
, 'trigger'::text as
\"
What
\"
, d.description as
\"
Description
\"\n
"
}
"FROM pg_trigger t, pg_description d
\n
"
"WHERE t.oid = d.objoid
\n
"
);
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Name
\"
"
);
if
(
object
)
{
strcat
(
descbuf
,
" AND t.tgname ~* '^"
);
res
=
PSQLexec
(
pset
,
descbuf
);
strncat
(
descbuf
,
object
,
REGEXP_CUTOFF
);
if
(
!
res
)
strcat
(
descbuf
,
"'
\n
"
);
return
false
;
}
myopt
.
topt
.
tuples_only
=
false
;
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Name
\"
"
);
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"Object descriptions"
;
res
=
PSQLexec
(
pset
,
descbuf
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
if
(
!
res
)
return
false
;
PQclear
(
res
);
return
true
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"Object descriptions"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
return
true
;
}
}
...
@@ -480,199 +494,218 @@ objectDescription(const char * object, PsqlSettings *pset)
...
@@ -480,199 +494,218 @@ objectDescription(const char * object, PsqlSettings *pset)
* and pass it to the underlying printTable() function.
* and pass it to the underlying printTable() function.
*
*
*/
*/
static
void
*
xmalloc
(
size_t
size
)
static
void
*
xmalloc
(
size_t
size
)
{
{
void
*
tmp
;
void
*
tmp
;
tmp
=
malloc
(
size
);
if
(
!
tmp
)
{
tmp
=
malloc
(
size
);
perror
(
"malloc"
);
if
(
!
tmp
)
exit
(
EXIT_FAILURE
);
{
}
perror
(
"malloc"
);
return
tmp
;
exit
(
EXIT_FAILURE
);
}
return
tmp
;
}
}
bool
bool
describeTableDetails
(
const
char
*
name
,
PsqlSettings
*
pset
)
describeTableDetails
(
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
char
descbuf
[
512
+
NAMEDATALEN
];
char
descbuf
[
512
+
NAMEDATALEN
];
PGresult
*
res
=
NULL
,
*
res2
=
NULL
,
*
res3
=
NULL
;
PGresult
*
res
=
NULL
,
printTableOpt
myopt
=
pset
->
popt
.
topt
;
*
res2
=
NULL
,
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
*
res3
=
NULL
;
int
i
;
printTableOpt
myopt
=
pset
->
popt
.
topt
;
char
*
view_def
=
NULL
;
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
char
*
headers
[
5
];
int
i
;
char
**
cells
=
NULL
;
char
*
view_def
=
NULL
;
char
*
title
=
NULL
;
char
*
headers
[
5
];
char
**
footers
=
NULL
;
char
**
cells
=
NULL
;
char
**
ptr
;
char
*
title
=
NULL
;
unsigned
int
cols
;
char
**
footers
=
NULL
;
char
**
ptr
;
cols
=
3
+
(
description
?
1
:
0
);
unsigned
int
cols
;
headers
[
0
]
=
"Attribute"
;
cols
=
3
+
(
description
?
1
:
0
);
headers
[
1
]
=
"Type"
;
headers
[
2
]
=
"Info"
;
headers
[
0
]
=
"Attribute"
;
if
(
description
)
{
headers
[
1
]
=
"Type"
;
headers
[
3
]
=
"Description"
;
headers
[
2
]
=
"Info"
;
headers
[
4
]
=
NULL
;
if
(
description
)
}
{
else
headers
[
3
]
=
"Description"
;
headers
[
3
]
=
NULL
;
headers
[
4
]
=
NULL
;
/* Get general table info */
strcpy
(
descbuf
,
"SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum"
);
if
(
description
)
strcat
(
descbuf
,
", obj_description(a.oid)"
);
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_attribute a, pg_type t
\n
"
"WHERE c.relname = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"'
\n
AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
\n
"
"ORDER BY a.attnum"
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
return
false
;
/* Did we get anything? */
if
(
PQntuples
(
res
)
==
0
)
{
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
fprintf
(
stdout
,
"Did not find any class named
\"
%s
\"
.
\n
"
,
name
);
PQclear
(
res
);
return
false
;
}
/* Check if table is a view */
strcpy
(
descbuf
,
"SELECT definition FROM pg_views WHERE viewname = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"'"
);
res2
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res2
)
return
false
;
if
(
PQntuples
(
res2
)
>
0
)
view_def
=
PQgetvalue
(
res2
,
0
,
0
);
/* Generate table cells to be printed */
cells
=
calloc
(
PQntuples
(
res
)
*
cols
+
1
,
sizeof
(
*
cells
));
if
(
!
cells
)
{
perror
(
"calloc"
);
exit
(
EXIT_FAILURE
);
}
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
int4
attypmod
=
atoi
(
PQgetvalue
(
res
,
i
,
3
));
char
*
attype
=
PQgetvalue
(
res
,
i
,
1
);
/* Name */
cells
[
i
*
cols
+
0
]
=
PQgetvalue
(
res
,
i
,
0
);
/* don't free this afterwards */
/* Type */
cells
[
i
*
cols
+
1
]
=
xmalloc
(
NAMEDATALEN
+
16
);
if
(
strcmp
(
attype
,
"bpchar"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"char(%d)"
,
attypmod
!=
-
1
?
attypmod
-
VARHDRSZ
:
0
);
else
if
(
strcmp
(
attype
,
"varchar"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"varchar(%d)"
,
attypmod
!=
-
1
?
attypmod
-
VARHDRSZ
:
0
);
else
if
(
strcmp
(
attype
,
"numeric"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"numeric(%d,%d)"
,
((
attypmod
-
VARHDRSZ
)
>>
16
)
&
0xffff
,
(
attypmod
-
VARHDRSZ
)
&
0xffff
);
else
if
(
attype
[
0
]
==
'_'
)
sprintf
(
cells
[
i
*
cols
+
1
],
"%s[]"
,
attype
+
1
);
else
strcpy
(
cells
[
i
*
cols
+
1
],
attype
);
/* Info */
cells
[
i
*
cols
+
2
]
=
xmalloc
(
128
+
128
);
/* I'm cutting off the default string at 128 */
cells
[
i
*
cols
+
2
][
0
]
=
'\0'
;
if
(
strcmp
(
PQgetvalue
(
res
,
i
,
4
),
"t"
)
==
0
)
strcat
(
cells
[
i
*
cols
+
2
],
"not null"
);
if
(
strcmp
(
PQgetvalue
(
res
,
i
,
5
),
"t"
)
==
0
)
{
/* handle "default" here */
strcpy
(
descbuf
,
"SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c
\n
"
"WHERE c.relname = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"' AND c.oid = d.adrelid AND d.adnum = "
);
strcat
(
descbuf
,
PQgetvalue
(
res
,
i
,
6
));
res3
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
return
false
;
if
(
cells
[
i
*
cols
+
2
][
0
])
strcat
(
cells
[
i
*
cols
+
2
],
" "
);
strcat
(
cells
[
i
*
cols
+
2
],
"default "
);
strcat
(
cells
[
i
*
cols
+
2
],
PQgetvalue
(
res3
,
0
,
0
));
}
}
else
headers
[
3
]
=
NULL
;
/* Description */
/* Get general table info */
strcpy
(
descbuf
,
"SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum"
);
if
(
description
)
if
(
description
)
cells
[
i
*
cols
+
3
]
=
PQgetvalue
(
res
,
i
,
7
);
strcat
(
descbuf
,
", obj_description(a.oid)"
);
}
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_attribute a, pg_type t
\n
"
"WHERE c.relname = '"
);
/* Make title */
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
title
=
xmalloc
(
10
+
strlen
(
name
));
strcat
(
descbuf
,
"'
\n
AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
\n
"
if
(
view_def
)
"ORDER BY a.attnum"
);
sprintf
(
title
,
"View
\"
%s
\"
"
,
name
);
else
res
=
PSQLexec
(
pset
,
descbuf
);
sprintf
(
title
,
"Table
\"
%s
\"
"
,
name
);
if
(
!
res
)
return
false
;
/* Make footers */
if
(
view_def
)
{
/* Did we get anything? */
footers
=
xmalloc
(
2
*
sizeof
(
*
footers
));
if
(
PQntuples
(
res
)
==
0
)
footers
[
0
]
=
xmalloc
(
20
+
strlen
(
view_def
));
{
sprintf
(
footers
[
0
],
"View definition: %s"
,
view_def
);
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
footers
[
1
]
=
NULL
;
fprintf
(
stdout
,
"Did not find any class named
\"
%s
\"
.
\n
"
,
name
)
;
}
PQclear
(
res
);
else
{
return
false
;
/* display indices */
}
strcpy
(
descbuf
,
"SELECT c2.relname
\n
"
"FROM pg_class c, pg_class c2, pg_index i
\n
"
/* Check if table is a view */
"WHERE c.rel
name = '"
);
strcpy
(
descbuf
,
"SELECT definition FROM pg_views WHERE view
name = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
\n
"
strcat
(
descbuf
,
"'"
);
"ORDER BY c2.relname"
);
res2
=
PSQLexec
(
pset
,
descbuf
);
res3
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res2
)
if
(
!
res3
)
return
false
;
return
false
;
if
(
PQntuples
(
res2
)
>
0
)
if
(
PQntuples
(
res3
)
>
0
)
{
view_def
=
PQgetvalue
(
res2
,
0
,
0
);
footers
=
xmalloc
((
PQntuples
(
res3
)
+
1
)
*
sizeof
(
*
footers
));
for
(
i
=
0
;
i
<
PQntuples
(
res3
);
i
++
)
{
footers
[
i
]
=
xmalloc
(
10
+
NAMEDATALEN
);
/* Generate table cells to be printed */
if
(
PQntuples
(
res3
)
==
1
)
cells
=
calloc
(
PQntuples
(
res
)
*
cols
+
1
,
sizeof
(
*
cells
));
sprintf
(
footers
[
i
],
"Index: %s"
,
PQgetvalue
(
res3
,
i
,
0
));
if
(
!
cells
)
else
if
(
i
==
0
)
{
sprintf
(
footers
[
i
],
"Indices: %s"
,
PQgetvalue
(
res3
,
i
,
0
));
perror
(
"calloc"
);
exit
(
EXIT_FAILURE
);
}
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
int4
attypmod
=
atoi
(
PQgetvalue
(
res
,
i
,
3
));
char
*
attype
=
PQgetvalue
(
res
,
i
,
1
);
/* Name */
cells
[
i
*
cols
+
0
]
=
PQgetvalue
(
res
,
i
,
0
);
/* don't free this
* afterwards */
/* Type */
cells
[
i
*
cols
+
1
]
=
xmalloc
(
NAMEDATALEN
+
16
);
if
(
strcmp
(
attype
,
"bpchar"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"char(%d)"
,
attypmod
!=
-
1
?
attypmod
-
VARHDRSZ
:
0
);
else
if
(
strcmp
(
attype
,
"varchar"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"varchar(%d)"
,
attypmod
!=
-
1
?
attypmod
-
VARHDRSZ
:
0
);
else
if
(
strcmp
(
attype
,
"numeric"
)
==
0
)
sprintf
(
cells
[
i
*
cols
+
1
],
"numeric(%d,%d)"
,
((
attypmod
-
VARHDRSZ
)
>>
16
)
&
0xffff
,
(
attypmod
-
VARHDRSZ
)
&
0xffff
);
else
if
(
attype
[
0
]
==
'_'
)
sprintf
(
cells
[
i
*
cols
+
1
],
"%s[]"
,
attype
+
1
);
else
else
sprintf
(
footers
[
i
],
" %s"
,
PQgetvalue
(
res3
,
i
,
0
));
strcpy
(
cells
[
i
*
cols
+
1
],
attype
);
}
/* Info */
cells
[
i
*
cols
+
2
]
=
xmalloc
(
128
+
128
);
/* I'm cutting off the
* default string at 128 */
cells
[
i
*
cols
+
2
][
0
]
=
'\0'
;
if
(
strcmp
(
PQgetvalue
(
res
,
i
,
4
),
"t"
)
==
0
)
strcat
(
cells
[
i
*
cols
+
2
],
"not null"
);
if
(
strcmp
(
PQgetvalue
(
res
,
i
,
5
),
"t"
)
==
0
)
{
/* handle "default" here */
strcpy
(
descbuf
,
"SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c
\n
"
"WHERE c.relname = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"' AND c.oid = d.adrelid AND d.adnum = "
);
strcat
(
descbuf
,
PQgetvalue
(
res
,
i
,
6
));
res3
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
return
false
;
if
(
cells
[
i
*
cols
+
2
][
0
])
strcat
(
cells
[
i
*
cols
+
2
],
" "
);
strcat
(
cells
[
i
*
cols
+
2
],
"default "
);
strcat
(
cells
[
i
*
cols
+
2
],
PQgetvalue
(
res3
,
0
,
0
));
}
/* Description */
if
(
description
)
cells
[
i
*
cols
+
3
]
=
PQgetvalue
(
res
,
i
,
7
);
}
footers
[
i
]
=
NULL
;
/* Make title */
title
=
xmalloc
(
10
+
strlen
(
name
));
if
(
view_def
)
sprintf
(
title
,
"View
\"
%s
\"
"
,
name
);
else
sprintf
(
title
,
"Table
\"
%s
\"
"
,
name
);
/* Make footers */
if
(
view_def
)
{
footers
=
xmalloc
(
2
*
sizeof
(
*
footers
));
footers
[
0
]
=
xmalloc
(
20
+
strlen
(
view_def
));
sprintf
(
footers
[
0
],
"View definition: %s"
,
view_def
);
footers
[
1
]
=
NULL
;
}
else
{
/* display indices */
strcpy
(
descbuf
,
"SELECT c2.relname
\n
"
"FROM pg_class c, pg_class c2, pg_index i
\n
"
"WHERE c.relname = '"
);
strncat
(
descbuf
,
name
,
NAMEDATALEN
);
strcat
(
descbuf
,
"' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
\n
"
"ORDER BY c2.relname"
);
res3
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res3
)
return
false
;
if
(
PQntuples
(
res3
)
>
0
)
{
footers
=
xmalloc
((
PQntuples
(
res3
)
+
1
)
*
sizeof
(
*
footers
));
for
(
i
=
0
;
i
<
PQntuples
(
res3
);
i
++
)
{
footers
[
i
]
=
xmalloc
(
10
+
NAMEDATALEN
);
if
(
PQntuples
(
res3
)
==
1
)
sprintf
(
footers
[
i
],
"Index: %s"
,
PQgetvalue
(
res3
,
i
,
0
));
else
if
(
i
==
0
)
sprintf
(
footers
[
i
],
"Indices: %s"
,
PQgetvalue
(
res3
,
i
,
0
));
else
sprintf
(
footers
[
i
],
" %s"
,
PQgetvalue
(
res3
,
i
,
0
));
}
footers
[
i
]
=
NULL
;
}
}
}
}
myopt
.
tuples_only
=
false
;
myopt
.
tuples_only
=
false
;
printTable
(
title
,
headers
,
cells
,
footers
,
"llll"
,
&
myopt
,
pset
->
queryFout
);
printTable
(
title
,
headers
,
cells
,
footers
,
"llll"
,
&
myopt
,
pset
->
queryFout
);
/* clean up */
/* clean up */
free
(
title
);
free
(
title
);
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
free
(
cells
[
i
*
cols
+
1
]);
{
free
(
cells
[
i
*
cols
+
2
]);
free
(
cells
[
i
*
cols
+
1
]);
}
free
(
cells
[
i
*
cols
+
2
]);
free
(
cells
);
}
free
(
cells
);
for
(
ptr
=
footers
;
footers
&&
*
ptr
;
ptr
++
)
for
(
ptr
=
footers
;
footers
&&
*
ptr
;
ptr
++
)
free
(
*
ptr
);
free
(
*
ptr
);
free
(
footers
);
free
(
footers
);
PQclear
(
res
);
PQclear
(
res
);
PQclear
(
res2
);
PQclear
(
res2
);
PQclear
(
res3
);
PQclear
(
res3
);
return
true
;
return
true
;
}
}
...
@@ -691,125 +724,134 @@ describeTableDetails(const char * name, PsqlSettings * pset)
...
@@ -691,125 +724,134 @@ describeTableDetails(const char * name, PsqlSettings * pset)
* (any order of the above is fine)
* (any order of the above is fine)
*/
*/
bool
bool
listTables
(
const
char
*
infotype
,
const
char
*
name
,
PsqlSettings
*
pset
)
listTables
(
const
char
*
infotype
,
const
char
*
name
,
PsqlSettings
*
pset
)
{
{
bool
showTables
=
strchr
(
infotype
,
't'
)
!=
NULL
;
bool
showTables
=
strchr
(
infotype
,
't'
)
!=
NULL
;
bool
showIndices
=
strchr
(
infotype
,
'i'
)
!=
NULL
;
bool
showIndices
=
strchr
(
infotype
,
'i'
)
!=
NULL
;
bool
showViews
=
strchr
(
infotype
,
'v'
)
!=
NULL
;
bool
showViews
=
strchr
(
infotype
,
'v'
)
!=
NULL
;
bool
showSeq
=
strchr
(
infotype
,
's'
)
!=
NULL
;
bool
showSeq
=
strchr
(
infotype
,
's'
)
!=
NULL
;
bool
showSystem
=
strchr
(
infotype
,
'S'
)
!=
NULL
;
bool
showSystem
=
strchr
(
infotype
,
'S'
)
!=
NULL
;
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
bool
description
=
GetVariableBool
(
pset
->
vars
,
"description"
);
char
descbuf
[
1536
+
4
*
REGEXP_CUTOFF
];
char
descbuf
[
1536
+
4
*
REGEXP_CUTOFF
];
PGresult
*
res
;
PGresult
*
res
;
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
/* tables */
/* tables */
if
(
showTables
)
{
if
(
showTables
)
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'table'::text as
\"
Type
\"
"
);
{
if
(
description
)
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'table'::text as
\"
Type
\"
"
);
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
if
(
description
)
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
" AND not exists (select 1 from pg_views where viewname = c.relname)
\n
"
);
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'
\n
"
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
" AND not exists (select 1 from pg_views where viewname = c.relname)
\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
if
(
name
)
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
{
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
}
}
}
}
/* views */
if
(
showViews
)
{
if
(
descbuf
[
0
])
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'view'::text as
\"
Type
\"
"
);
/* views */
if
(
description
)
if
(
showViews
)
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
{
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
if
(
descbuf
[
0
])
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'
\n
"
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
" AND exists (select 1 from pg_views where viewname = c.relname)
\n
"
);
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'view'::text as
\"
Type
\"
"
);
if
(
name
)
{
if
(
description
)
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
strcat
(
descbuf
,
"'
\n
"
);
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'
\n
"
" AND exists (select 1 from pg_views where viewname = c.relname)
\n
"
);
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
}
}
}
}
/* indices, sequences */
if
(
showIndices
||
showSeq
)
{
if
(
descbuf
[
0
])
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
,
\n
"
/* indices, sequences */
" (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as
\"
Type
\"
"
);
if
(
showIndices
||
showSeq
)
if
(
description
)
{
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
if
(
descbuf
[
0
])
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
"WHERE c.relowner = u.usesysid AND relkind in ("
);
if
(
showIndices
&&
showSeq
)
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
,
\n
"
strcat
(
descbuf
,
"'i', 'S'"
);
" (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as
\"
Type
\"
"
);
else
if
(
showIndices
)
if
(
description
)
strcat
(
descbuf
,
"'i'"
);
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
else
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
strcat
(
descbuf
,
"'S'"
);
"WHERE c.relowner = u.usesysid AND relkind in ("
);
strcat
(
descbuf
,
")
\n
"
);
if
(
showIndices
&&
showSeq
)
strcat
(
descbuf
,
"'i', 'S'"
);
/* ignore large-obj indices */
else
if
(
showIndices
)
if
(
showIndices
)
strcat
(
descbuf
,
"'i'"
);
strcat
(
descbuf
,
" AND (c.relkind != 'i' OR c.relname !~ '^xinx')
\n
"
);
else
strcat
(
descbuf
,
"'S'"
);
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
strcat
(
descbuf
,
")
\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
/* ignore large-obj indices */
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
if
(
showIndices
)
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
" AND (c.relkind != 'i' OR c.relname !~ '^xinx')
\n
"
);
strcat
(
descbuf
,
showSystem
?
" AND c.relname ~ '^pg_'
\n
"
:
" AND c.relname !~ '^pg_'
\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
}
}
}
}
/* real system catalogue tables */
if
(
showSystem
&&
showTables
)
{
if
(
descbuf
[
0
])
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'system'::text as
\"
Type
\"
"
);
/* real system catalogue tables */
if
(
description
)
if
(
showSystem
&&
showTables
)
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
{
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
if
(
descbuf
[
0
])
"WHERE c.relowner = u.usesysid AND c.relkind = 's'
\n
"
);
strcat
(
descbuf
,
"
\n
UNION
\n\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strcat
(
descbuf
,
"SELECT u.usename as
\"
Owner
\"
, c.relname as
\"
Name
\"
, 'system'::text as
\"
Type
\"
"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
if
(
description
)
strcat
(
descbuf
,
"'
\n
"
);
strcat
(
descbuf
,
", obj_description(c.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
"
\n
FROM pg_class c, pg_user u
\n
"
"WHERE c.relowner = u.usesysid AND c.relkind = 's'
\n
"
);
if
(
name
)
{
strcat
(
descbuf
,
" AND c.relname ~ '^"
);
strncat
(
descbuf
,
name
,
REGEXP_CUTOFF
);
strcat
(
descbuf
,
"'
\n
"
);
}
}
}
}
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Name
\"
"
);
strcat
(
descbuf
,
"
\n
ORDER BY
\"
Name
\"
"
);
res
=
PSQLexec
(
pset
,
descbuf
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
if
(
!
res
)
return
false
;
return
false
;
if
(
PQntuples
(
res
)
==
0
)
if
(
PQntuples
(
res
)
==
0
)
fprintf
(
pset
->
queryFout
,
"No matching classes found.
\n
"
);
fprintf
(
pset
->
queryFout
,
"No matching classes found.
\n
"
);
else
{
else
myopt
.
topt
.
tuples_only
=
false
;
{
myopt
.
nullPrint
=
NULL
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
title
=
"List of classes"
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"List of classes"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
}
}
PQclear
(
res
);
PQclear
(
res
);
return
true
;
return
true
;
}
}
...
...
src/bin/psql/describe.h
View file @
0e6652e6
...
@@ -5,38 +5,38 @@
...
@@ -5,38 +5,38 @@
/* \da */
/* \da */
bool
bool
describeAggregates
(
const
char
*
name
,
PsqlSettings
*
pset
);
describeAggregates
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \df */
/* \df */
bool
bool
describeFunctions
(
const
char
*
name
,
PsqlSettings
*
pset
);
describeFunctions
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \dT */
/* \dT */
bool
bool
describeTypes
(
const
char
*
name
,
PsqlSettings
*
pset
);
describeTypes
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \do */
/* \do */
bool
bool
describeOperators
(
const
char
*
name
,
PsqlSettings
*
pset
);
describeOperators
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \dp (formerly \z) */
/* \dp (formerly \z) */
bool
bool
permissionsList
(
const
char
*
name
,
PsqlSettings
*
pset
);
permissionsList
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \dd */
/* \dd */
bool
bool
objectDescription
(
const
char
*
object
,
PsqlSettings
*
pset
);
objectDescription
(
const
char
*
object
,
PsqlSettings
*
pset
);
/* \d foo */
/* \d foo */
bool
bool
describeTableDetails
(
const
char
*
name
,
PsqlSettings
*
pset
);
describeTableDetails
(
const
char
*
name
,
PsqlSettings
*
pset
);
/* \l */
/* \l */
bool
bool
listAllDbs
(
PsqlSettings
*
pset
);
listAllDbs
(
PsqlSettings
*
pset
);
/* \dt, \di, \dS, etc. */
/* \dt, \di, \dS, etc. */
bool
bool
listTables
(
const
char
*
infotype
,
const
char
*
name
,
PsqlSettings
*
pset
);
listTables
(
const
char
*
infotype
,
const
char
*
name
,
PsqlSettings
*
pset
);
#endif
/* DESCRIBE_H */
#endif
/* DESCRIBE_H */
src/bin/psql/help.c
View file @
0e6652e6
...
@@ -7,12 +7,12 @@
...
@@ -7,12 +7,12 @@
#include <signal.h>
#include <signal.h>
#ifndef WIN32
#ifndef WIN32
#include <sys/ioctl.h>
/* for ioctl() */
#include <sys/ioctl.h>
/* for ioctl() */
#ifdef HAVE_PWD_H
#ifdef HAVE_PWD_H
#include <pwd.h>
/* for getpwuid() */
#include <pwd.h>
/* for getpwuid() */
#endif
#endif
#include <sys/types.h>
/* (ditto) */
#include <sys/types.h>
/* (ditto) */
#include <unistd.h>
/* for getuid() */
#include <unistd.h>
/* for getuid() */
#else
#else
#define strcasecmp(x,y) stricmp(x,y)
#define strcasecmp(x,y) stricmp(x,y)
#define popen(x,y) _popen(x,y)
#define popen(x,y) _popen(x,y)
...
@@ -34,85 +34,94 @@
...
@@ -34,85 +34,94 @@
*/
*/
#define ON(var) (var ? "on" : "off")
#define ON(var) (var ? "on" : "off")
void
usage
(
void
)
void
usage
(
void
)
{
{
const
char
*
env
;
const
char
*
env
;
const
char
*
user
;
const
char
*
user
;
#ifndef WIN32
#ifndef WIN32
struct
passwd
*
pw
=
NULL
;
struct
passwd
*
pw
=
NULL
;
#endif
#endif
/* Find default user, in case we need it. */
/* Find default user, in case we need it. */
user
=
getenv
(
"USER"
);
user
=
getenv
(
"USER"
);
if
(
!
user
)
{
if
(
!
user
)
{
#ifndef WIN32
#ifndef WIN32
pw
=
getpwuid
(
getuid
());
pw
=
getpwuid
(
getuid
());
if
(
pw
)
user
=
pw
->
pw_name
;
if
(
pw
)
else
{
user
=
pw
->
pw_name
;
perror
(
"getpwuid()"
);
else
exit
(
EXIT_FAILURE
);
{
}
perror
(
"getpwuid()"
);
exit
(
EXIT_FAILURE
);
}
#else
#else
user
=
"?"
;
user
=
"?"
;
#endif
#endif
}
}
/* If string begins " here, then it ought to end there to fit on an 80 column terminal> > > > > > > " */
/* If string begins " here, then it ought to end there to fit on an 80 column terminal> > > > > > > " */
fprintf
(
stderr
,
"Usage: psql [options] [dbname [username]]
\n
"
);
fprintf
(
stderr
,
"Usage: psql [options] [dbname [username]]
\n
"
);
fprintf
(
stderr
,
" -A Unaligned table output mode (-P format=unaligned)
\n
"
);
fprintf
(
stderr
,
" -A Unaligned table output mode (-P format=unaligned)
\n
"
);
fprintf
(
stderr
,
" -c query Run single query (slash commands, too) and exit
\n
"
);
fprintf
(
stderr
,
" -c query Run single query (slash commands, too) and exit
\n
"
);
/* Display default database */
/* Display default database */
env
=
getenv
(
"PGDATABASE"
);
env
=
getenv
(
"PGDATABASE"
);
if
(
!
env
)
env
=
user
;
if
(
!
env
)
fprintf
(
stderr
,
" -d dbname Specify database name to connect to (default: %s)
\n
"
,
env
);
env
=
user
;
fprintf
(
stderr
,
" -d dbname Specify database name to connect to (default: %s)
\n
"
,
env
);
fprintf
(
stderr
,
" -e Echo all input in non-interactive mode
\n
"
);
fprintf
(
stderr
,
" -E Display queries that internal commands generate
\n
"
);
fprintf
(
stderr
,
" -e Echo all input in non-interactive mode
\n
"
);
fprintf
(
stderr
,
" -f filename Execute queries from file, then exit
\n
"
);
fprintf
(
stderr
,
" -E Display queries that internal commands generate
\n
"
);
fprintf
(
stderr
,
" -F sep Set field separator (default: '"
DEFAULT_FIELD_SEP
"') (-P fieldsep=)
\n
"
);
fprintf
(
stderr
,
" -f filename Execute queries from file, then exit
\n
"
);
fprintf
(
stderr
,
" -F sep Set field separator (default: '"
DEFAULT_FIELD_SEP
"') (-P fieldsep=)
\n
"
);
/* Display default host */
env
=
getenv
(
"PGHOST"
);
/* Display default host */
fprintf
(
stderr
,
" -h host Specify database server host (default: "
);
env
=
getenv
(
"PGHOST"
);
if
(
env
)
fprintf
(
stderr
,
" -h host Specify database server host (default: "
);
fprintf
(
stderr
,
env
);
if
(
env
)
else
fprintf
(
stderr
,
env
);
fprintf
(
stderr
,
"domain socket"
);
else
fprintf
(
stderr
,
")
\n
"
);
fprintf
(
stderr
,
"domain socket"
);
fprintf
(
stderr
,
")
\n
"
);
fprintf
(
stderr
,
" -H HTML table output mode (-P format=html)
\n
"
);
fprintf
(
stderr
,
" -l List available databases, then exit
\n
"
);
fprintf
(
stderr
,
" -H HTML table output mode (-P format=html)
\n
"
);
fprintf
(
stderr
,
" -n Do not use readline and history
\n
"
);
fprintf
(
stderr
,
" -l List available databases, then exit
\n
"
);
fprintf
(
stderr
,
" -o filename Send query output to filename (or |pipe)
\n
"
);
fprintf
(
stderr
,
" -n Do not use readline and history
\n
"
);
fprintf
(
stderr
,
" -o filename Send query output to filename (or |pipe)
\n
"
);
/* Display default port */
env
=
getenv
(
"PGPORT"
);
/* Display default port */
fprintf
(
stderr
,
" -p port Specify database server port (default: %s)
\n
"
,
env
=
getenv
(
"PGPORT"
);
env
?
env
:
"hardwired"
);
fprintf
(
stderr
,
" -p port Specify database server port (default: %s)
\n
"
,
env
?
env
:
"hardwired"
);
fprintf
(
stderr
,
" -P var[=arg] Set printing option 'var' to 'arg'. (see
\\
pset command)
\n
"
);
fprintf
(
stderr
,
" -q Run quietly (no messages, no prompts)
\n
"
);
fprintf
(
stderr
,
" -P var[=arg] Set printing option 'var' to 'arg'. (see
\\
pset command)
\n
"
);
fprintf
(
stderr
,
" -s Single step mode (confirm each query)
\n
"
);
fprintf
(
stderr
,
" -q Run quietly (no messages, no prompts)
\n
"
);
fprintf
(
stderr
,
" -S Single line mode (newline sends query)
\n
"
);
fprintf
(
stderr
,
" -s Single step mode (confirm each query)
\n
"
);
fprintf
(
stderr
,
" -t Don't print headings and row count (-P tuples_only)
\n
"
);
fprintf
(
stderr
,
" -S Single line mode (newline sends query)
\n
"
);
fprintf
(
stderr
,
" -T text Set HTML table tag options (e.g., width, border)
\n
"
);
fprintf
(
stderr
,
" -t Don't print headings and row count (-P tuples_only)
\n
"
);
fprintf
(
stderr
,
" -u Prompt for username and password (same as
\"
-U ? -W
\"
)
\n
"
);
fprintf
(
stderr
,
" -T text Set HTML table tag options (e.g., width, border)
\n
"
);
fprintf
(
stderr
,
" -u Prompt for username and password (same as
\"
-U ? -W
\"
)
\n
"
);
/* Display default user */
env
=
getenv
(
"PGUSER"
);
/* Display default user */
if
(
!
env
)
env
=
user
;
env
=
getenv
(
"PGUSER"
);
fprintf
(
stderr
,
" -U [username] Specifiy username,
\"
?
\"
=prompt (default user: %s)
\n
"
,
env
);
if
(
!
env
)
env
=
user
;
fprintf
(
stderr
,
" -x Turn on expanded table output (-P expanded)
\n
"
);
fprintf
(
stderr
,
" -U [username] Specifiy username,
\"
?
\"
=prompt (default user: %s)
\n
"
,
env
);
fprintf
(
stderr
,
" -v name=val Set psql variable 'name' to 'value'
\n
"
);
fprintf
(
stderr
,
" -V Show version information and exit
\n
"
);
fprintf
(
stderr
,
" -x Turn on expanded table output (-P expanded)
\n
"
);
fprintf
(
stderr
,
" -W Prompt for password (should happen automatically)
\n
"
);
fprintf
(
stderr
,
" -v name=val Set psql variable 'name' to 'value'
\n
"
);
fprintf
(
stderr
,
" -V Show version information and exit
\n
"
);
fprintf
(
stderr
,
"Consult the documentation for the complete details.
\n
"
);
fprintf
(
stderr
,
" -W Prompt for password (should happen automatically)
\n
"
);
fprintf
(
stderr
,
"Consult the documentation for the complete details.
\n
"
);
#ifndef WIN32
#ifndef WIN32
if
(
pw
)
free
(
pw
);
if
(
pw
)
free
(
pw
);
#endif
#endif
}
}
...
@@ -125,82 +134,85 @@ void usage(void)
...
@@ -125,82 +134,85 @@ void usage(void)
*/
*/
#ifndef TIOCGWINSZ
#ifndef TIOCGWINSZ
struct
winsize
{
struct
winsize
int
ws_row
;
{
int
ws_col
;
int
ws_row
;
int
ws_col
;
};
};
#endif
#endif
void
void
slashUsage
(
PsqlSettings
*
pset
)
slashUsage
(
PsqlSettings
*
pset
)
{
{
bool
usePipe
=
false
;
bool
usePipe
=
false
;
const
char
*
pagerenv
;
const
char
*
pagerenv
;
FILE
*
fout
;
FILE
*
fout
;
struct
winsize
screen_size
;
struct
winsize
screen_size
;
#ifdef TIOCGWINSZ
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
screen_size
.
ws_row
==
0
))
{
{
#endif
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
#ifdef TIOCGWINSZ
}
}
#endif
#endif
if
(
pset
->
notty
==
0
&&
if
(
pset
->
notty
==
0
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
(
pagerenv
[
0
]
!=
'\0'
)
&&
(
pagerenv
[
0
]
!=
'\0'
)
&&
screen_size
.
ws_row
<=
36
&&
screen_size
.
ws_row
<=
36
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
{
usePipe
=
true
;
usePipe
=
true
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
}
else
else
fout
=
stdout
;
fout
=
stdout
;
/* if you add/remove a line here, change the row test above */
/* if you add/remove a line here, change the row test above */
fprintf
(
fout
,
"
\\
? -- help
\n
"
);
fprintf
(
fout
,
"
\\
? -- help
\n
"
);
fprintf
(
fout
,
"
\\
c[onnect] [<dbname>|- [<user>|?]] -- connect to new database (currently '%s')
\n
"
,
PQdb
(
pset
->
db
));
fprintf
(
fout
,
"
\\
c[onnect] [<dbname>|- [<user>|?]] -- connect to new database (currently '%s')
\n
"
,
PQdb
(
pset
->
db
));
fprintf
(
fout
,
"
\\
copy [binary] <table> [with oids] {from|to} <fname> [with delimiters '<char>']
\n
"
);
fprintf
(
fout
,
"
\\
copy [binary] <table> [with oids] {from|to} <fname> [with delimiters '<char>']
\n
"
);
fprintf
(
fout
,
"
\\
copyright -- show PostgreSQL copyright
\n
"
);
fprintf
(
fout
,
"
\\
copyright -- show PostgreSQL copyright
\n
"
);
fprintf
(
fout
,
"
\\
d -- list tables, views, and sequences
\n
"
);
fprintf
(
fout
,
"
\\
d -- list tables, views, and sequences
\n
"
);
fprintf
(
fout
,
"
\\
distvS -- list only indices/sequences/tables/views/system tables
\n
"
);
fprintf
(
fout
,
"
\\
distvS -- list only indices/sequences/tables/views/system tables
\n
"
);
fprintf
(
fout
,
"
\\
da -- list aggregates
\n
"
);
fprintf
(
fout
,
"
\\
da -- list aggregates
\n
"
);
fprintf
(
fout
,
"
\\
dd [<object>]- list comment for table, type, function, or operator
\n
"
);
fprintf
(
fout
,
"
\\
dd [<object>]- list comment for table, type, function, or operator
\n
"
);
fprintf
(
fout
,
"
\\
df -- list functions
\n
"
);
fprintf
(
fout
,
"
\\
df -- list functions
\n
"
);
fprintf
(
fout
,
"
\\
do -- list operators
\n
"
);
fprintf
(
fout
,
"
\\
do -- list operators
\n
"
);
fprintf
(
fout
,
"
\\
dT -- list data types
\n
"
);
fprintf
(
fout
,
"
\\
dT -- list data types
\n
"
);
fprintf
(
fout
,
"
\\
e [<fname>] -- edit the current query buffer or <fname> with external editor
\n
"
);
fprintf
(
fout
,
"
\\
e [<fname>] -- edit the current query buffer or <fname> with external editor
\n
"
);
fprintf
(
fout
,
"
\\
echo <text> -- write text to stdout
\n
"
);
fprintf
(
fout
,
"
\\
echo <text> -- write text to stdout
\n
"
);
fprintf
(
fout
,
"
\\
g [<fname>] -- send query to backend (and results in <fname> or |pipe)
\n
"
);
fprintf
(
fout
,
"
\\
g [<fname>] -- send query to backend (and results in <fname> or |pipe)
\n
"
);
fprintf
(
fout
,
"
\\
h [<cmd>] -- help on syntax of sql commands, * for all commands
\n
"
);
fprintf
(
fout
,
"
\\
h [<cmd>] -- help on syntax of sql commands, * for all commands
\n
"
);
fprintf
(
fout
,
"
\\
i <fname> -- read and execute queries from filename
\n
"
);
fprintf
(
fout
,
"
\\
i <fname> -- read and execute queries from filename
\n
"
);
fprintf
(
fout
,
"
\\
l -- list all databases
\n
"
);
fprintf
(
fout
,
"
\\
l -- list all databases
\n
"
);
fprintf
(
fout
,
"
\\
lo_export,
\\
lo_import,
\\
lo_list,
\\
lo_unlink -- large object operations
\n
"
);
fprintf
(
fout
,
"
\\
lo_export,
\\
lo_import,
\\
lo_list,
\\
lo_unlink -- large object operations
\n
"
);
fprintf
(
fout
,
"
\\
o [<fname>] -- send all query results to <fname>, or |pipe
\n
"
);
fprintf
(
fout
,
"
\\
o [<fname>] -- send all query results to <fname>, or |pipe
\n
"
);
fprintf
(
fout
,
"
\\
p -- print the content of the current query buffer
\n
"
);
fprintf
(
fout
,
"
\\
p -- print the content of the current query buffer
\n
"
);
fprintf
(
fout
,
"
\\
pset -- set table output options
\n
"
);
fprintf
(
fout
,
"
\\
pset -- set table output options
\n
"
);
fprintf
(
fout
,
"
\\
q -- quit
\n
"
);
fprintf
(
fout
,
"
\\
q -- quit
\n
"
);
fprintf
(
fout
,
"
\\
qecho <text>-- write text to query output stream (see
\\
o)
\n
"
);
fprintf
(
fout
,
"
\\
qecho <text>-- write text to query output stream (see
\\
o)
\n
"
);
fprintf
(
fout
,
"
\\
r -- reset (clear) the query buffer
\n
"
);
fprintf
(
fout
,
"
\\
r -- reset (clear) the query buffer
\n
"
);
fprintf
(
fout
,
"
\\
s [<fname>] -- print history or save it in <fname>
\n
"
);
fprintf
(
fout
,
"
\\
s [<fname>] -- print history or save it in <fname>
\n
"
);
fprintf
(
fout
,
"
\\
set <var> [<value>] -- set/unset internal variable
\n
"
);
fprintf
(
fout
,
"
\\
set <var> [<value>] -- set/unset internal variable
\n
"
);
fprintf
(
fout
,
"
\\
t -- don't show table headers or footers (currently %s)
\n
"
,
ON
(
pset
->
popt
.
topt
.
tuples_only
));
fprintf
(
fout
,
"
\\
t -- don't show table headers or footers (currently %s)
\n
"
,
ON
(
pset
->
popt
.
topt
.
tuples_only
));
fprintf
(
fout
,
"
\\
x -- toggle expanded output (currently %s)
\n
"
,
ON
(
pset
->
popt
.
topt
.
expanded
));
fprintf
(
fout
,
"
\\
x -- toggle expanded output (currently %s)
\n
"
,
ON
(
pset
->
popt
.
topt
.
expanded
));
fprintf
(
fout
,
"
\\
w <fname> -- write current query buffer to a file
\n
"
);
fprintf
(
fout
,
"
\\
w <fname> -- write current query buffer to a file
\n
"
);
fprintf
(
fout
,
"
\\
z -- list table access permissions
\n
"
);
fprintf
(
fout
,
"
\\
z -- list table access permissions
\n
"
);
fprintf
(
fout
,
"
\\
! [<cmd>] -- shell escape or command
\n
"
);
fprintf
(
fout
,
"
\\
! [<cmd>] -- shell escape or command
\n
"
);
if
(
usePipe
)
{
if
(
usePipe
)
pclose
(
fout
);
{
pqsignal
(
SIGPIPE
,
SIG_DFL
);
pclose
(
fout
);
}
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
}
}
...
@@ -212,59 +224,59 @@ slashUsage(PsqlSettings *pset)
...
@@ -212,59 +224,59 @@ slashUsage(PsqlSettings *pset)
void
void
helpSQL
(
const
char
*
topic
)
helpSQL
(
const
char
*
topic
)
{
{
if
(
!
topic
||
strlen
(
topic
)
==
0
)
if
(
!
topic
||
strlen
(
topic
)
==
0
)
{
char
left_center_right
;
/* Which column we're displaying */
int
i
;
/* Index into QL_HELP[] */
puts
(
"Syntax:
\\
h <cmd> or
\\
help <cmd>, where <cmd> is one of the following:"
);
left_center_right
=
'L'
;
/* Start with left column */
i
=
0
;
while
(
QL_HELP
[
i
].
cmd
!=
NULL
)
{
{
switch
(
left_center_right
)
char
left_center_right
;
/* Which column we're displaying */
{
int
i
;
/* Index into QL_HELP[] */
case
'L'
:
printf
(
" %-25s"
,
QL_HELP
[
i
].
cmd
);
puts
(
"Syntax:
\\
h <cmd> or
\\
help <cmd>, where <cmd> is one of the following:"
);
left_center_right
=
'C'
;
break
;
left_center_right
=
'L'
;
/* Start with left column */
case
'C'
:
i
=
0
;
printf
(
"%-25s"
,
QL_HELP
[
i
].
cmd
);
while
(
QL_HELP
[
i
].
cmd
!=
NULL
)
left_center_right
=
'R'
;
{
break
;
switch
(
left_center_right
)
case
'R'
:
{
printf
(
"%-25s
\n
"
,
QL_HELP
[
i
].
cmd
);
case
'L'
:
left_center_right
=
'L'
;
printf
(
" %-25s"
,
QL_HELP
[
i
].
cmd
);
break
;
left_center_right
=
'C'
;
}
break
;
i
++
;
case
'C'
:
printf
(
"%-25s"
,
QL_HELP
[
i
].
cmd
);
left_center_right
=
'R'
;
break
;
case
'R'
:
printf
(
"%-25s
\n
"
,
QL_HELP
[
i
].
cmd
);
left_center_right
=
'L'
;
break
;
}
i
++
;
}
if
(
left_center_right
!=
'L'
)
puts
(
"
\n
"
);
puts
(
"Or type
\\
h * for a complete description of all commands."
);
}
}
if
(
left_center_right
!=
'L'
)
puts
(
"
\n
"
);
puts
(
"Or type
\\
h * for a complete description of all commands."
);
}
else
else
{
int
i
;
bool
help_found
=
false
;
for
(
i
=
0
;
QL_HELP
[
i
].
cmd
;
i
++
)
{
{
if
(
strcasecmp
(
QL_HELP
[
i
].
cmd
,
topic
)
==
0
||
int
i
;
strcmp
(
topic
,
"*"
)
==
0
)
bool
help_found
=
false
;
{
help_found
=
true
;
for
(
i
=
0
;
QL_HELP
[
i
].
cmd
;
i
++
)
printf
(
"Command: %s
\n
Description: %s
\n
Syntax:
\n
%s
\n\n
"
,
{
QL_HELP
[
i
].
cmd
,
QL_HELP
[
i
].
help
,
QL_HELP
[
i
].
syntax
);
if
(
strcasecmp
(
QL_HELP
[
i
].
cmd
,
topic
)
==
0
||
}
strcmp
(
topic
,
"*"
)
==
0
)
{
help_found
=
true
;
printf
(
"Command: %s
\n
Description: %s
\n
Syntax:
\n
%s
\n\n
"
,
QL_HELP
[
i
].
cmd
,
QL_HELP
[
i
].
help
,
QL_HELP
[
i
].
syntax
);
}
}
if
(
!
help_found
)
printf
(
"No help available for '%s'.
\n
Try
\\
h with no arguments to see available help.
\n
"
,
topic
);
}
}
if
(
!
help_found
)
printf
(
"No help available for '%s'.
\n
Try
\\
h with no arguments to see available help.
\n
"
,
topic
);
}
}
}
...
@@ -273,34 +285,34 @@ helpSQL(const char *topic)
...
@@ -273,34 +285,34 @@ helpSQL(const char *topic)
void
void
print_copyright
(
void
)
print_copyright
(
void
)
{
{
puts
(
puts
(
"
"
PostgreSQL Data Base Management System
PostgreSQL Data Base Management System
Copyright (c) 1996-
9 PostgreSQL Global Development Group
Copyright(c) 1996 -
9 PostgreSQL Global Development Group
This software is based on Postgres95, formerly known as Postgres,
which
This software is based on Postgres95, formerly known as Postgres,
which
contains the following notice:
contains the following notice:
Copyright (c) 1994-
7 Regents of the University of California
Copyright(c) 1994 -
7 Regents of the University of California
Permission to use, copy, modify, and distribute this software and
its
Permission to use, copy, modify, and distribute this software and
its
documentation for any purpose, without fee, and without
a written agreement
documentation for any purpose, without fee, and without
a written agreement
is hereby granted,
provided that the above copyright notice and this paragraph
is hereby granted,
provided that the above copyright notice and this paragraph
and the following two paragraphs appear in all copies.
and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
PARTY FOR
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH
DAMAGE.
DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING,
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
\
"
AS IS
\"
BASIS,
PARTICULAR PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN \
"
AS
IS
\
" BASIS,
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE
MAINTENANCE,
AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE
MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
(end of terms)
"
(end of terms)
"
);
);
}
}
src/bin/psql/help.h
View file @
0e6652e6
...
@@ -3,14 +3,13 @@
...
@@ -3,14 +3,13 @@
#include "settings.h"
#include "settings.h"
void
usage
(
void
);
void
usage
(
void
);
void
slashUsage
(
PsqlSettings
*
pset
);
void
slashUsage
(
PsqlSettings
*
pset
);
void
helpSQL
(
const
char
*
topic
);
void
helpSQL
(
const
char
*
topic
);
void
print_copyright
(
void
);
void
print_copyright
(
void
);
#endif
#endif
src/bin/psql/input.c
View file @
0e6652e6
...
@@ -10,9 +10,11 @@
...
@@ -10,9 +10,11 @@
/* (of course there is no runtime command for doing that :) */
/* (of course there is no runtime command for doing that :) */
#ifdef USE_READLINE
#ifdef USE_READLINE
static
bool
useReadline
;
static
bool
useReadline
;
#endif
#endif
#ifdef USE_HISTORY
#ifdef USE_HISTORY
static
bool
useHistory
;
static
bool
useHistory
;
#endif
#endif
...
@@ -25,29 +27,31 @@ static bool useHistory;
...
@@ -25,29 +27,31 @@ static bool useHistory;
char
*
char
*
gets_interactive
(
const
char
*
prompt
)
gets_interactive
(
const
char
*
prompt
)
{
{
char
*
s
;
char
*
s
;
#ifdef USE_READLINE
#ifdef USE_READLINE
if
(
useReadline
)
{
if
(
useReadline
)
s
=
readline
(
prompt
);
{
fputc
(
'\r'
,
stdout
);
s
=
readline
(
prompt
);
fflush
(
stdout
);
fputc
(
'\r'
,
stdout
);
}
fflush
(
stdout
);
else
{
}
else
{
#endif
#endif
fputs
(
prompt
,
stdout
);
fputs
(
prompt
,
stdout
);
fflush
(
stdout
);
fflush
(
stdout
);
s
=
gets_fromFile
(
stdin
);
s
=
gets_fromFile
(
stdin
);
#ifdef USE_READLINE
#ifdef USE_READLINE
}
}
#endif
#endif
#ifdef USE_HISTORY
#ifdef USE_HISTORY
if
(
useHistory
&&
s
&&
s
[
0
]
!=
'\0'
)
if
(
useHistory
&&
s
&&
s
[
0
]
!=
'\0'
)
add_history
(
s
);
add_history
(
s
);
#endif
#endif
return
s
;
return
s
;
}
}
...
@@ -60,25 +64,27 @@ gets_interactive(const char *prompt)
...
@@ -60,25 +64,27 @@ gets_interactive(const char *prompt)
char
*
char
*
gets_fromFile
(
FILE
*
source
)
gets_fromFile
(
FILE
*
source
)
{
{
PQExpBufferData
buffer
;
PQExpBufferData
buffer
;
char
line
[
1024
];
char
line
[
1024
];
initPQExpBuffer
(
&
buffer
);
initPQExpBuffer
(
&
buffer
);
while
(
fgets
(
line
,
1024
,
source
)
!=
NULL
)
{
while
(
fgets
(
line
,
1024
,
source
)
!=
NULL
)
appendPQExpBufferStr
(
&
buffer
,
line
);
{
if
(
buffer
.
data
[
buffer
.
len
-
1
]
==
'\n'
)
{
appendPQExpBufferStr
(
&
buffer
,
line
);
buffer
.
data
[
buffer
.
len
-
1
]
=
'\0'
;
if
(
buffer
.
data
[
buffer
.
len
-
1
]
==
'\n'
)
return
buffer
.
data
;
{
buffer
.
data
[
buffer
.
len
-
1
]
=
'\0'
;
return
buffer
.
data
;
}
}
}
}
if
(
buffer
.
len
>
0
)
if
(
buffer
.
len
>
0
)
return
buffer
.
data
;
/* EOF after reading some bufferload(s) */
return
buffer
.
data
;
/* EOF after reading some bufferload(s) */
/* EOF, so return null */
/* EOF, so return null */
termPQExpBuffer
(
&
buffer
);
termPQExpBuffer
(
&
buffer
);
return
NULL
;
return
NULL
;
}
}
...
@@ -93,28 +99,33 @@ void
...
@@ -93,28 +99,33 @@ void
initializeInput
(
int
flags
)
initializeInput
(
int
flags
)
{
{
#ifdef USE_READLINE
#ifdef USE_READLINE
if
(
flags
==
1
)
{
if
(
flags
==
1
)
useReadline
=
true
;
{
rl_readline_name
=
"psql"
;
useReadline
=
true
;
}
rl_readline_name
=
"psql"
;
}
#endif
#endif
#ifdef USE_HISTORY
#ifdef USE_HISTORY
if
(
flags
==
1
)
{
if
(
flags
==
1
)
const
char
*
home
;
{
const
char
*
home
;
useHistory
=
true
;
using_history
();
useHistory
=
true
;
home
=
getenv
(
"HOME"
);
using_history
();
if
(
home
)
{
home
=
getenv
(
"HOME"
);
char
*
psql_history
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
if
(
home
)
if
(
psql_history
)
{
{
sprintf
(
psql_history
,
"%s/.psql_history"
,
home
);
char
*
psql_history
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
read_history
(
psql_history
);
free
(
psql_history
);
if
(
psql_history
)
}
{
sprintf
(
psql_history
,
"%s/.psql_history"
,
home
);
read_history
(
psql_history
);
free
(
psql_history
);
}
}
}
}
}
#endif
#endif
}
}
...
@@ -124,17 +135,19 @@ bool
...
@@ -124,17 +135,19 @@ bool
saveHistory
(
const
char
*
fname
)
saveHistory
(
const
char
*
fname
)
{
{
#ifdef USE_HISTORY
#ifdef USE_HISTORY
if
(
useHistory
)
{
if
(
useHistory
)
if
(
write_history
(
fname
)
!=
0
)
{
{
perror
(
fname
);
if
(
write_history
(
fname
)
!=
0
)
return
false
;
{
perror
(
fname
);
return
false
;
}
return
true
;
}
}
return
true
;
else
}
return
false
;
else
return
false
;
#else
#else
return
false
;
return
false
;
#endif
#endif
}
}
...
@@ -144,19 +157,22 @@ void
...
@@ -144,19 +157,22 @@ void
finishInput
(
void
)
finishInput
(
void
)
{
{
#ifdef USE_HISTORY
#ifdef USE_HISTORY
if
(
useHistory
)
{
if
(
useHistory
)
char
*
home
;
{
char
*
psql_history
;
char
*
home
;
char
*
psql_history
;
home
=
getenv
(
"HOME"
);
if
(
home
)
{
home
=
getenv
(
"HOME"
);
psql_history
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
if
(
home
)
if
(
psql_history
)
{
{
sprintf
(
psql_history
,
"%s/.psql_history"
,
home
);
psql_history
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
write_history
(
psql_history
);
if
(
psql_history
)
free
(
psql_history
);
{
}
sprintf
(
psql_history
,
"%s/.psql_history"
,
home
);
write_history
(
psql_history
);
free
(
psql_history
);
}
}
}
}
}
#endif
#endif
}
}
src/bin/psql/input.h
View file @
0e6652e6
...
@@ -38,19 +38,19 @@
...
@@ -38,19 +38,19 @@
char
*
char
*
gets_interactive
(
const
char
*
prompt
);
gets_interactive
(
const
char
*
prompt
);
char
*
char
*
gets_fromFile
(
FILE
*
source
);
gets_fromFile
(
FILE
*
source
);
void
void
initializeInput
(
int
flags
);
initializeInput
(
int
flags
);
bool
bool
saveHistory
(
const
char
*
fname
);
saveHistory
(
const
char
*
fname
);
void
void
finishInput
(
void
);
finishInput
(
void
);
#endif
#endif
src/bin/psql/large_obj.c
View file @
0e6652e6
...
@@ -14,7 +14,7 @@
...
@@ -14,7 +14,7 @@
#include "print.h"
#include "print.h"
/*
/*
* Since all large object ops must be in a transaction, we must do some magic
* Since all large object ops must be in a transaction, we must do some magic
* here. You can set the variable lo_transaction to one of commit|rollback|
* here. You can set the variable lo_transaction to one of commit|rollback|
* nothing to get your favourite behaviour regarding any transaction in
* nothing to get your favourite behaviour regarding any transaction in
...
@@ -24,47 +24,50 @@
...
@@ -24,47 +24,50 @@
static
char
notice
[
80
];
static
char
notice
[
80
];
static
void
static
void
_my_notice_handler
(
void
*
arg
,
const
char
*
message
)
{
_my_notice_handler
(
void
*
arg
,
const
char
*
message
)
(
void
)
arg
;
{
strncpy
(
notice
,
message
,
79
);
(
void
)
arg
;
notice
[
79
]
=
'\0'
;
strncpy
(
notice
,
message
,
79
);
notice
[
79
]
=
'\0'
;
}
}
static
bool
static
bool
handle_transaction
(
PsqlSettings
*
pset
)
handle_transaction
(
PsqlSettings
*
pset
)
{
{
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
PGresult
*
res
;
PGresult
*
res
;
bool
commit
;
bool
commit
;
PQnoticeProcessor
old_notice_hook
;
PQnoticeProcessor
old_notice_hook
;
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
return
true
;
return
true
;
commit
=
(
var
&&
strcmp
(
var
,
"commit"
)
==
0
);
notice
[
0
]
=
'\0'
;
old_notice_hook
=
PQsetNoticeProcessor
(
pset
->
db
,
_my_notice_handler
,
NULL
);
res
=
PSQLexec
(
pset
,
commit
?
"COMMIT"
:
"ROLLBACK"
);
if
(
!
res
)
return
false
;
commit
=
(
var
&&
strcmp
(
var
,
"commit"
)
==
0
);
if
(
notice
[
0
])
{
notice
[
0
]
=
'\0'
;
if
((
!
commit
&&
strcmp
(
notice
,
"NOTICE: UserAbortTransactionBlock and not in in-progress state
\n
"
)
!=
0
)
||
old_notice_hook
=
PQsetNoticeProcessor
(
pset
->
db
,
_my_notice_handler
,
NULL
);
(
commit
&&
strcmp
(
notice
,
"NOTICE: EndTransactionBlock and not inprogress/abort state
\n
"
)
!=
0
))
fputs
(
notice
,
stderr
);
res
=
PSQLexec
(
pset
,
commit
?
"COMMIT"
:
"ROLLBACK"
);
}
if
(
!
res
)
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
return
false
;
{
if
(
commit
)
if
(
notice
[
0
])
{
puts
(
"Warning: Your transaction in progress has been committed."
);
if
(
(
!
commit
&&
strcmp
(
notice
,
"NOTICE: UserAbortTransactionBlock and not in in-progress state
\n
"
)
!=
0
)
||
else
(
commit
&&
strcmp
(
notice
,
"NOTICE: EndTransactionBlock and not inprogress/abort state
\n
"
)
!=
0
)
)
puts
(
"Warning: Your transaction in progress has been rolled back."
);
fputs
(
notice
,
stderr
);
}
}
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
{
PQsetNoticeProcessor
(
pset
->
db
,
old_notice_hook
,
NULL
);
if
(
commit
)
return
true
;
puts
(
"Warning: Your transaction in progress has been committed."
);
else
puts
(
"Warning: Your transaction in progress has been rolled back."
);
}
PQsetNoticeProcessor
(
pset
->
db
,
old_notice_hook
,
NULL
);
return
true
;
}
}
...
@@ -75,54 +78,61 @@ handle_transaction(PsqlSettings * pset)
...
@@ -75,54 +78,61 @@ handle_transaction(PsqlSettings * pset)
* Write a large object to a file
* Write a large object to a file
*/
*/
bool
bool
do_lo_export
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
,
const
char
*
filename_arg
)
do_lo_export
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
,
const
char
*
filename_arg
)
{
{
PGresult
*
res
;
PGresult
*
res
;
int
status
;
int
status
;
bool
own_transaction
=
true
;
bool
own_transaction
=
true
;
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
own_transaction
=
false
;
own_transaction
=
false
;
if
(
!
pset
->
db
)
{
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
return
false
;
}
if
(
!
pset
->
db
)
{
if
(
own_transaction
)
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
{
return
false
;
if
(
!
handle_transaction
(
pset
))
}
return
false
;
if
(
own_transaction
)
{
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
if
(
!
handle_transaction
(
pset
))
return
false
;
return
false
;
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
PQclear
(
res
);
return
false
;
}
PQclear
(
res
);
status
=
lo_export
(
pset
->
db
,
atol
(
loid_arg
),
(
char
*
)
filename_arg
);
}
if
(
status
!=
1
)
{
/* of course this status is documented
status
=
lo_export
(
pset
->
db
,
atol
(
loid_arg
),
(
char
*
)
filename_arg
);
* nowhere :( */
if
(
status
!=
1
)
{
/* of course this status is documented nowhere :( */
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
if
(
own_transaction
)
if
(
own_transaction
)
{
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
PQclear
(
res
);
}
return
false
;
}
}
return
false
;
}
if
(
own_transaction
)
{
if
(
own_transaction
)
{
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
{
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
PQclear
(
res
);
return
false
;
return
false
;
}
PQclear
(
res
);
}
}
PQclear
(
res
);
fprintf
(
pset
->
queryFout
,
"lo_export
\n
"
);
}
fprintf
(
pset
->
queryFout
,
"lo_export
\n
"
);
return
true
;
return
true
;
}
}
...
@@ -133,76 +143,85 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a
...
@@ -133,76 +143,85 @@ do_lo_export(PsqlSettings * pset, const char * loid_arg, const char * filename_a
* Copy large object from file to database
* Copy large object from file to database
*/
*/
bool
bool
do_lo_import
(
PsqlSettings
*
pset
,
const
char
*
filename_arg
,
const
char
*
comment_arg
)
do_lo_import
(
PsqlSettings
*
pset
,
const
char
*
filename_arg
,
const
char
*
comment_arg
)
{
{
PGresult
*
res
;
PGresult
*
res
;
Oid
loid
;
Oid
loid
;
char
buf
[
1024
];
char
buf
[
1024
];
unsigned
int
i
;
unsigned
int
i
;
bool
own_transaction
=
true
;
bool
own_transaction
=
true
;
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
own_transaction
=
false
;
own_transaction
=
false
;
if
(
!
pset
->
db
)
{
if
(
!
pset
->
db
)
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
{
return
false
;
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
}
return
false
;
}
if
(
own_transaction
)
{
if
(
own_transaction
)
if
(
!
handle_transaction
(
pset
))
{
return
false
;
if
(
!
handle_transaction
(
pset
))
return
false
;
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
return
false
;
return
false
;
PQclear
(
res
);
}
loid
=
lo_import
(
pset
->
db
,
(
char
*
)
filename_arg
);
if
(
loid
==
InvalidOid
)
{
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
if
(
own_transaction
)
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
}
return
false
;
}
/* insert description if given */
if
(
comment_arg
)
{
sprintf
(
buf
,
"INSERT INTO pg_description VALUES (%d, '"
,
loid
);
for
(
i
=
0
;
i
<
strlen
(
comment_arg
);
i
++
)
if
(
comment_arg
[
i
]
==
'\''
)
strcat
(
buf
,
"
\\
'"
);
else
strncat
(
buf
,
&
comment_arg
[
i
],
1
);
strcat
(
buf
,
"')"
);
if
(
!
(
res
=
PSQLexec
(
pset
,
buf
)))
{
if
(
own_transaction
)
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
PQclear
(
res
);
}
return
false
;
}
}
}
if
(
own_transaction
)
{
loid
=
lo_import
(
pset
->
db
,
(
char
*
)
filename_arg
);
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
{
if
(
loid
==
InvalidOid
)
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
{
PQclear
(
res
);
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
return
false
;
if
(
own_transaction
)
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
}
return
false
;
}
}
PQclear
(
res
);
/* insert description if given */
}
if
(
comment_arg
)
{
sprintf
(
buf
,
"INSERT INTO pg_description VALUES (%d, '"
,
loid
);
for
(
i
=
0
;
i
<
strlen
(
comment_arg
);
i
++
)
if
(
comment_arg
[
i
]
==
'\''
)
strcat
(
buf
,
"
\\
'"
);
else
strncat
(
buf
,
&
comment_arg
[
i
],
1
);
strcat
(
buf
,
"')"
);
if
(
!
(
res
=
PSQLexec
(
pset
,
buf
)))
{
if
(
own_transaction
)
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
}
return
false
;
}
}
if
(
own_transaction
)
{
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
return
false
;
}
fprintf
(
pset
->
queryFout
,
"lo_import %d
\n
"
,
loid
);
PQclear
(
res
);
}
return
true
;
fprintf
(
pset
->
queryFout
,
"lo_import %d
\n
"
,
loid
);
return
true
;
}
}
...
@@ -212,67 +231,76 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
...
@@ -212,67 +231,76 @@ do_lo_import(PsqlSettings * pset, const char * filename_arg, const char * commen
*
*
* removes a large object out of the database
* removes a large object out of the database
*/
*/
bool
do_lo_unlink
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
)
bool
do_lo_unlink
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
)
{
{
PGresult
*
res
;
PGresult
*
res
;
int
status
;
int
status
;
Oid
loid
=
(
Oid
)
atol
(
loid_arg
);
Oid
loid
=
(
Oid
)
atol
(
loid_arg
);
char
buf
[
256
];
char
buf
[
256
];
bool
own_transaction
=
true
;
bool
own_transaction
=
true
;
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
const
char
*
var
=
GetVariable
(
pset
->
vars
,
"lo_transaction"
);
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
if
(
var
&&
strcmp
(
var
,
"nothing"
)
==
0
)
own_transaction
=
false
;
own_transaction
=
false
;
if
(
!
pset
->
db
)
{
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
return
false
;
}
if
(
!
pset
->
db
)
{
if
(
own_transaction
)
fputs
(
"You are not connected to a database.
\n
"
,
stderr
);
{
return
false
;
if
(
!
handle_transaction
(
pset
))
}
return
false
;
if
(
own_transaction
)
{
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
if
(
!
handle_transaction
(
pset
))
return
false
;
return
false
;
if
(
!
(
res
=
PSQLexec
(
pset
,
"BEGIN"
)))
PQclear
(
res
);
return
false
;
}
PQclear
(
res
);
status
=
lo_unlink
(
pset
->
db
,
loid
);
}
if
(
status
==
-
1
)
{
status
=
lo_unlink
(
pset
->
db
,
loid
);
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
if
(
status
==
-
1
)
{
if
(
own_transaction
)
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
{
if
(
own_transaction
)
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
PQclear
(
res
);
}
return
false
;
}
}
return
false
;
}
/* remove the comment as well */
sprintf
(
buf
,
"DELETE FROM pg_description WHERE objoid = %d"
,
loid
);
/* remove the comment as well */
if
(
!
(
res
=
PSQLexec
(
pset
,
buf
)))
sprintf
(
buf
,
"DELETE FROM pg_description WHERE objoid = %d"
,
loid
);
{
if
(
!
(
res
=
PSQLexec
(
pset
,
buf
)))
{
if
(
own_transaction
)
if
(
own_transaction
)
{
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
PQclear
(
res
);
}
return
false
;
}
}
return
false
;
}
if
(
own_transaction
)
{
if
(
own_transaction
)
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
{
{
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
if
(
!
(
res
=
PSQLexec
(
pset
,
"COMMIT"
)))
PQclear
(
res
);
{
return
false
;
res
=
PQexec
(
pset
->
db
,
"ROLLBACK"
);
PQclear
(
res
);
return
false
;
}
PQclear
(
res
);
}
}
PQclear
(
res
);
}
fprintf
(
pset
->
queryFout
,
"lo_unlink %d
\n
"
,
loid
);
fprintf
(
pset
->
queryFout
,
"lo_unlink %d
\n
"
,
loid
);
return
true
;
return
true
;
}
}
...
@@ -282,30 +310,31 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
...
@@ -282,30 +310,31 @@ bool do_lo_unlink(PsqlSettings * pset, const char * loid_arg)
*
*
* Show all large objects in database, with comments if desired
* Show all large objects in database, with comments if desired
*/
*/
bool
do_lo_list
(
PsqlSettings
*
pset
)
bool
do_lo_list
(
PsqlSettings
*
pset
)
{
{
PGresult
*
res
;
PGresult
*
res
;
char
descbuf
[
512
];
char
descbuf
[
512
];
printQueryOpt
myopt
=
pset
->
popt
;
printQueryOpt
myopt
=
pset
->
popt
;
descbuf
[
0
]
=
'\0'
;
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT usename as
\"
Owner
\"
, substring(relname from 5) as
\"
ID
\"
"
);
strcat
(
descbuf
,
"SELECT usename as
\"
Owner
\"
, substring(relname from 5) as
\"
ID
\"
"
);
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
if
(
GetVariableBool
(
pset
->
vars
,
"description"
))
strcat
(
descbuf
,
",
\n
obj_description(pg_class.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
",
\n
obj_description(pg_class.oid) as
\"
Description
\"
"
);
strcat
(
descbuf
,
"
\n
FROM pg_class, pg_user
\n
"
strcat
(
descbuf
,
"
\n
FROM pg_class, pg_user
\n
"
"WHERE usesysid = relowner AND relkind = 'l'
\n
"
"WHERE usesysid = relowner AND relkind = 'l'
\n
"
"ORDER BY
\"
ID
\"
"
);
"ORDER BY
\"
ID
\"
"
);
res
=
PSQLexec
(
pset
,
descbuf
);
res
=
PSQLexec
(
pset
,
descbuf
);
if
(
!
res
)
if
(
!
res
)
return
false
;
return
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
topt
.
tuples_only
=
false
;
myopt
.
nullPrint
=
NULL
;
myopt
.
nullPrint
=
NULL
;
myopt
.
title
=
"Large objects"
;
myopt
.
title
=
"Large objects"
;
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
printQuery
(
res
,
&
myopt
,
pset
->
queryFout
);
PQclear
(
res
);
PQclear
(
res
);
return
true
;
return
true
;
}
}
src/bin/psql/large_obj.h
View file @
0e6652e6
...
@@ -3,9 +3,9 @@
...
@@ -3,9 +3,9 @@
#include "settings.h"
#include "settings.h"
bool
do_lo_export
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
,
const
char
*
filename_arg
);
bool
do_lo_export
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
,
const
char
*
filename_arg
);
bool
do_lo_import
(
PsqlSettings
*
pset
,
const
char
*
filename_arg
,
const
char
*
comment_arg
);
bool
do_lo_import
(
PsqlSettings
*
pset
,
const
char
*
filename_arg
,
const
char
*
comment_arg
);
bool
do_lo_unlink
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
);
bool
do_lo_unlink
(
PsqlSettings
*
pset
,
const
char
*
loid_arg
);
bool
do_lo_list
(
PsqlSettings
*
pset
);
bool
do_lo_list
(
PsqlSettings
*
pset
);
#endif
/* LARGE_OBJ_H */
#endif
/* LARGE_OBJ_H */
src/bin/psql/mainloop.c
View file @
0e6652e6
...
@@ -26,343 +26,380 @@
...
@@ -26,343 +26,380 @@
int
int
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
)
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
)
{
{
PQExpBuffer
query_buf
;
/* buffer for query being accumulated */
PQExpBuffer
query_buf
;
/* buffer for query being accumulated */
char
*
line
;
/* current line of input */
char
*
line
;
/* current line of input */
char
*
xcomment
;
/* start of extended comment */
char
*
xcomment
;
/* start of extended comment */
int
len
;
/* length of the line */
int
len
;
/* length of the line */
int
successResult
=
EXIT_SUCCESS
;
int
successResult
=
EXIT_SUCCESS
;
backslashResult
slashCmdStatus
;
backslashResult
slashCmdStatus
;
bool
eof
=
false
;
/* end of our command input? */
bool
eof
=
false
;
/* end of our command input? */
bool
success
;
bool
success
;
char
in_quote
;
/* == 0 for no in_quote */
char
in_quote
;
/* == 0 for no in_quote */
bool
was_bslash
;
/* backslash */
bool
was_bslash
;
/* backslash */
int
paren_level
;
int
paren_level
;
unsigned
int
query_start
;
unsigned
int
query_start
;
int
i
,
prevlen
,
thislen
;
int
i
,
prevlen
,
thislen
;
/* Save the prior command source */
/* Save the prior command source */
FILE
*
prev_cmd_source
;
FILE
*
prev_cmd_source
;
bool
prev_cmd_interactive
;
bool
prev_cmd_interactive
;
bool
die_on_error
;
bool
die_on_error
;
const
char
*
interpol_char
;
const
char
*
interpol_char
;
/* Save old settings */
/* Save old settings */
prev_cmd_source
=
pset
->
cur_cmd_source
;
prev_cmd_source
=
pset
->
cur_cmd_source
;
prev_cmd_interactive
=
pset
->
cur_cmd_interactive
;
prev_cmd_interactive
=
pset
->
cur_cmd_interactive
;
/* Establish new source */
/* Establish new source */
pset
->
cur_cmd_source
=
source
;
pset
->
cur_cmd_source
=
source
;
pset
->
cur_cmd_interactive
=
((
source
==
stdin
)
&&
!
pset
->
notty
);
pset
->
cur_cmd_interactive
=
((
source
==
stdin
)
&&
!
pset
->
notty
);
query_buf
=
createPQExpBuffer
();
query_buf
=
createPQExpBuffer
();
if
(
!
query_buf
)
{
if
(
!
query_buf
)
perror
(
"createPQExpBuffer"
);
{
exit
(
EXIT_FAILURE
);
perror
(
"createPQExpBuffer"
);
}
exit
(
EXIT_FAILURE
);
}
xcomment
=
NULL
;
xcomment
=
NULL
;
in_quote
=
0
;
in_quote
=
0
;
paren_level
=
0
;
paren_level
=
0
;
slashCmdStatus
=
CMD_UNKNOWN
;
/* set default */
slashCmdStatus
=
CMD_UNKNOWN
;
/* set default */
/* main loop to get queries and execute them */
/* main loop to get queries and execute them */
while
(
!
eof
)
while
(
!
eof
)
{
if
(
slashCmdStatus
==
CMD_NEWEDIT
)
{
{
/*
if
(
slashCmdStatus
==
CMD_NEWEDIT
)
* just returned from editing the line? then just copy to the
{
* input buffer
*/
/*
line
=
strdup
(
query_buf
->
data
);
* just returned from editing the line? then just copy to the
resetPQExpBuffer
(
query_buf
);
* input buffer
/* reset parsing state since we are rescanning whole query */
*/
xcomment
=
NULL
;
line
=
strdup
(
query_buf
->
data
);
in_quote
=
0
;
resetPQExpBuffer
(
query_buf
);
paren_level
=
0
;
/* reset parsing state since we are rescanning whole query */
}
xcomment
=
NULL
;
else
in_quote
=
0
;
{
paren_level
=
0
;
/*
}
* otherwise, set interactive prompt if necessary
* and get another line
*/
if
(
pset
->
cur_cmd_interactive
)
{
int
prompt_status
;
if
(
in_quote
&&
in_quote
==
'\''
)
prompt_status
=
PROMPT_SINGLEQUOTE
;
else
if
(
in_quote
&&
in_quote
==
'"'
)
prompt_status
=
PROMPT_DOUBLEQUOTE
;
else
if
(
xcomment
!=
NULL
)
prompt_status
=
PROMPT_COMMENT
;
else
if
(
query_buf
->
len
>
0
)
prompt_status
=
PROMPT_CONTINUE
;
else
else
prompt_status
=
PROMPT_READY
;
{
line
=
gets_interactive
(
get_prompt
(
pset
,
prompt_status
));
/*
}
* otherwise, set interactive prompt if necessary and get
else
* another line
line
=
gets_fromFile
(
source
);
*/
}
if
(
pset
->
cur_cmd_interactive
)
{
int
prompt_status
;
if
(
in_quote
&&
in_quote
==
'\''
)
prompt_status
=
PROMPT_SINGLEQUOTE
;
else
if
(
in_quote
&&
in_quote
==
'"'
)
prompt_status
=
PROMPT_DOUBLEQUOTE
;
else
if
(
xcomment
!=
NULL
)
prompt_status
=
PROMPT_COMMENT
;
else
if
(
query_buf
->
len
>
0
)
prompt_status
=
PROMPT_CONTINUE
;
else
prompt_status
=
PROMPT_READY
;
line
=
gets_interactive
(
get_prompt
(
pset
,
prompt_status
));
}
else
line
=
gets_fromFile
(
source
);
}
/* Setting these will not have effect until next line */
/* Setting these will not have effect until next line */
die_on_error
=
GetVariableBool
(
pset
->
vars
,
"die_on_error"
);
die_on_error
=
GetVariableBool
(
pset
->
vars
,
"die_on_error"
);
interpol_char
=
GetVariable
(
pset
->
vars
,
"sql_interpol"
);;
interpol_char
=
GetVariable
(
pset
->
vars
,
"sql_interpol"
);;
/*
/*
* query_buf holds query already accumulated. line is the malloc'd
* query_buf holds query already accumulated. line is the
* new line of input (note it must be freed before looping around!)
* malloc'd new line of input (note it must be freed before
* query_start is the next command start location within the line.
* looping around!) query_start is the next command start location
*/
* within the line.
*/
/* No more input. Time to quit, or \i done */
/* No more input. Time to quit, or \i done */
if
(
line
==
NULL
||
(
!
pset
->
cur_cmd_interactive
&&
*
line
==
'\0'
))
if
(
line
==
NULL
||
(
!
pset
->
cur_cmd_interactive
&&
*
line
==
'\0'
))
{
{
if
(
GetVariableBool
(
pset
->
vars
,
"echo"
)
&&
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
if
(
GetVariableBool
(
pset
->
vars
,
"echo"
)
&&
!
GetVariableBool
(
pset
->
vars
,
"quiet"
))
puts
(
"EOF"
);
puts
(
"EOF"
);
eof
=
true
;
eof
=
true
;
continue
;
continue
;
}
}
/* not currently inside an extended comment? */
/* not currently inside an extended comment? */
if
(
xcomment
)
if
(
xcomment
)
xcomment
=
line
;
xcomment
=
line
;
/* strip trailing backslashes, they don't have a clear meaning */
/* strip trailing backslashes, they don't have a clear meaning */
while
(
1
)
{
while
(
1
)
char
*
cp
=
strrchr
(
line
,
'\\'
);
{
if
(
cp
&&
(
*
(
cp
+
1
)
==
'\0'
))
char
*
cp
=
strrchr
(
line
,
'\\'
);
*
cp
=
'\0'
;
else
break
;
}
if
(
cp
&&
(
*
(
cp
+
1
)
==
'\0'
))
/* echo back if input is from file and flag is set */
*
cp
=
'\0'
;
if
(
!
pset
->
cur_cmd_interactive
&&
GetVariableBool
(
pset
->
vars
,
"echo"
))
else
fprintf
(
stderr
,
"%s
\n
"
,
line
);
break
;
/* interpolate variables into SQL */
len
=
strlen
(
line
);
thislen
=
PQmblen
(
line
);
for
(
i
=
0
;
line
[
i
];
i
+=
(
thislen
=
PQmblen
(
&
line
[
i
]))
)
{
if
(
interpol_char
&&
interpol_char
[
0
]
!=
'\0'
&&
interpol_char
[
0
]
==
line
[
i
])
{
size_t
in_length
,
out_length
;
const
char
*
value
;
char
*
new
;
bool
closer
;
/* did we have a closing delimiter or just an end of line? */
in_length
=
strcspn
(
&
line
[
i
+
thislen
],
interpol_char
);
closer
=
line
[
i
+
thislen
+
in_length
]
==
line
[
i
];
line
[
i
+
thislen
+
in_length
]
=
'\0'
;
value
=
interpolate_var
(
&
line
[
i
+
thislen
],
pset
);
out_length
=
strlen
(
value
);
new
=
malloc
(
len
+
out_length
-
(
in_length
+
(
closer
?
2
:
1
))
+
1
);
if
(
!
new
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
}
new
[
0
]
=
'\0'
;
strncat
(
new
,
line
,
i
);
strcat
(
new
,
value
);
if
(
closer
)
strcat
(
new
,
line
+
i
+
2
+
in_length
);
free
(
line
);
/* echo back if input is from file and flag is set */
line
=
new
;
if
(
!
pset
->
cur_cmd_interactive
&&
GetVariableBool
(
pset
->
vars
,
"echo"
))
i
+=
out_length
;
fprintf
(
stderr
,
"%s
\n
"
,
line
);
}
}
/* interpolate variables into SQL */
len
=
strlen
(
line
);
thislen
=
PQmblen
(
line
);
for
(
i
=
0
;
line
[
i
];
i
+=
(
thislen
=
PQmblen
(
&
line
[
i
])))
{
if
(
interpol_char
&&
interpol_char
[
0
]
!=
'\0'
&&
interpol_char
[
0
]
==
line
[
i
])
{
size_t
in_length
,
out_length
;
const
char
*
value
;
char
*
new
;
bool
closer
;
/* did we have a closing delimiter
* or just an end of line? */
in_length
=
strcspn
(
&
line
[
i
+
thislen
],
interpol_char
);
closer
=
line
[
i
+
thislen
+
in_length
]
==
line
[
i
];
line
[
i
+
thislen
+
in_length
]
=
'\0'
;
value
=
interpolate_var
(
&
line
[
i
+
thislen
],
pset
);
out_length
=
strlen
(
value
);
new
=
malloc
(
len
+
out_length
-
(
in_length
+
(
closer
?
2
:
1
))
+
1
);
if
(
!
new
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
new
[
0
]
=
'\0'
;
strncat
(
new
,
line
,
i
);
strcat
(
new
,
value
);
if
(
closer
)
strcat
(
new
,
line
+
i
+
2
+
in_length
);
free
(
line
);
line
=
new
;
i
+=
out_length
;
}
}
/* nothing left on line? then ignore */
/* nothing left on line? then ignore */
if
(
line
[
0
]
==
'\0'
)
{
if
(
line
[
0
]
==
'\0'
)
free
(
line
);
{
continue
;
free
(
line
);
}
continue
;
}
slashCmdStatus
=
CMD_UNKNOWN
;
slashCmdStatus
=
CMD_UNKNOWN
;
len
=
strlen
(
line
);
len
=
strlen
(
line
);
query_start
=
0
;
query_start
=
0
;
/*
/*
* Parse line, looking for command separators.
* Parse line, looking for command separators.
*
*
* The current character is at line[i], the prior character at
* The current character is at line[i], the prior character at line[i
* line[i
- prevlen], the next character at line[i + thislen].
*
- prevlen], the next character at line[i + thislen].
*/
*/
prevlen
=
0
;
prevlen
=
0
;
thislen
=
(
len
>
0
)
?
PQmblen
(
line
)
:
0
;
thislen
=
(
len
>
0
)
?
PQmblen
(
line
)
:
0
;
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
success
=
true
;
success
=
true
;
for
(
i
=
0
;
i
<
len
;
ADVANCE_1
)
{
for
(
i
=
0
;
i
<
len
;
ADVANCE_1
)
if
(
!
success
&&
die_on_error
)
{
break
;
if
(
!
success
&&
die_on_error
)
break
;
/* was the previous character a backslash? */
if
(
i
>
0
&&
line
[
i
-
prevlen
]
==
'\\'
)
/* was the previous character a backslash? */
was_bslash
=
true
;
if
(
i
>
0
&&
line
[
i
-
prevlen
]
==
'\\'
)
else
was_bslash
=
true
;
was_bslash
=
false
;
else
was_bslash
=
false
;
/* in quote? */
if
(
in_quote
)
{
/* in quote? */
/* end of quote */
if
(
in_quote
)
if
(
line
[
i
]
==
in_quote
&&
!
was_bslash
)
{
in_quote
=
'\0'
;
/* end of quote */
}
if
(
line
[
i
]
==
in_quote
&&
!
was_bslash
)
in_quote
=
'\0'
;
/* start of quote */
}
else
if
(
line
[
i
]
==
'\''
||
line
[
i
]
==
'"'
)
in_quote
=
line
[
i
];
/* start of quote */
else
if
(
line
[
i
]
==
'\''
||
line
[
i
]
==
'"'
)
/* in extended comment? */
in_quote
=
line
[
i
];
else
if
(
xcomment
!=
NULL
)
{
if
(
line
[
i
]
==
'*'
&&
line
[
i
+
thislen
]
==
'/'
)
{
/* in extended comment? */
xcomment
=
NULL
;
else
if
(
xcomment
!=
NULL
)
ADVANCE_1
;
{
}
if
(
line
[
i
]
==
'*'
&&
line
[
i
+
thislen
]
==
'/'
)
}
{
xcomment
=
NULL
;
/* start of extended comment? */
ADVANCE_1
;
else
if
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'*'
)
{
}
xcomment
=
&
line
[
i
];
}
ADVANCE_1
;
}
/* start of extended comment? */
else
if
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'*'
)
/* single-line comment? truncate line */
{
else
if
((
line
[
i
]
==
'-'
&&
line
[
i
+
thislen
]
==
'-'
)
||
xcomment
=
&
line
[
i
];
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'/'
))
ADVANCE_1
;
{
}
line
[
i
]
=
'\0'
;
/* remove comment */
break
;
/* single-line comment? truncate line */
}
else
if
((
line
[
i
]
==
'-'
&&
line
[
i
+
thislen
]
==
'-'
)
||
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'/'
))
/* count nested parentheses */
{
else
if
(
line
[
i
]
==
'('
)
line
[
i
]
=
'\0'
;
/* remove comment */
paren_level
++
;
break
;
}
else
if
(
line
[
i
]
==
')'
&&
paren_level
>
0
)
paren_level
--
;
/* count nested parentheses */
else
if
(
line
[
i
]
==
'('
)
/* semicolon? then send query */
paren_level
++
;
else
if
(
line
[
i
]
==
';'
&&
!
was_bslash
&&
paren_level
==
0
)
{
line
[
i
]
=
'\0'
;
else
if
(
line
[
i
]
==
')'
&&
paren_level
>
0
)
/* is there anything else on the line? */
paren_level
--
;
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
/* insert a cosmetic newline, if this is not the first line in the buffer */
/* semicolon? then send query */
if
(
query_buf
->
len
>
0
)
else
if
(
line
[
i
]
==
';'
&&
!
was_bslash
&&
paren_level
==
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
{
/* append the line to the query buffer */
line
[
i
]
=
'\0'
;
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
/* is there anything else on the line? */
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
/* append the line to the query buffer */
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
}
/* execute query */
success
=
SendQuery
(
pset
,
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
query_start
=
i
+
thislen
;
}
/* backslash command */
else
if
(
was_bslash
)
{
const
char
*
end_of_cmd
=
NULL
;
line
[
i
-
prevlen
]
=
'\0'
;
/* overwrites backslash */
/* is there anything else on the line? */
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
*/
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
/* append the line to the query buffer */
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
}
/* handle backslash command */
slashCmdStatus
=
HandleSlashCmds
(
pset
,
&
line
[
i
],
query_buf
,
&
end_of_cmd
);
success
=
slashCmdStatus
!=
CMD_ERROR
;
if
(
slashCmdStatus
==
CMD_SEND
)
{
success
=
SendQuery
(
pset
,
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
query_start
=
i
+
thislen
;
}
/* is there anything left after the backslash command? */
if
(
end_of_cmd
)
{
i
+=
end_of_cmd
-
&
line
[
i
];
query_start
=
i
;
}
else
break
;
}
}
}
/* execute query */
success
=
SendQuery
(
pset
,
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
query_start
=
i
+
thislen
;
}
/* backslash command */
else
if
(
was_bslash
)
{
const
char
*
end_of_cmd
=
NULL
;
line
[
i
-
prevlen
]
=
'\0'
;
/* overwrites backslash */
/* is there anything else on the line? */
if
(
!
success
&&
die_on_error
&&
!
pset
->
cur_cmd_interactive
)
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
{
/* insert a cosmetic newline, if this is not the first line in the buffer */
successResult
=
EXIT_USER
;
if
(
query_buf
->
len
>
0
)
break
;
appendPQExpBufferChar
(
query_buf
,
'\n'
);
/* append the line to the query buffer */
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
}
}
/* handle backslash command */
slashCmdStatus
=
HandleSlashCmds
(
pset
,
&
line
[
i
],
query_buf
,
&
end_of_cmd
);
if
(
slashCmdStatus
==
CMD_TERMINATE
)
{
success
=
slashCmdStatus
!=
CMD_ERROR
;
successResult
=
EXIT_SUCCESS
;
break
;
if
(
slashCmdStatus
==
CMD_SEND
)
{
success
=
SendQuery
(
pset
,
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
query_start
=
i
+
thislen
;
}
}
/* is there anything left after the backslash command? */
if
(
end_of_cmd
)
{
i
+=
end_of_cmd
-
&
line
[
i
];
query_start
=
i
;
}
else
break
;
}
}
if
(
!
success
&&
die_on_error
&&
!
pset
->
cur_cmd_interactive
)
{
successResult
=
EXIT_USER
;
break
;
}
/* Put the rest of the line in the query buffer. */
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
}
if
(
slashCmdStatus
==
CMD_TERMINATE
)
{
free
(
line
);
successResult
=
EXIT_SUCCESS
;
break
;
}
/* Put the rest of the line in the query buffer. */
if
(
line
[
query_start
+
strspn
(
line
+
query_start
,
"
\t
"
)]
!=
'\0'
)
{
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
appendPQExpBufferStr
(
query_buf
,
line
+
query_start
);
}
free
(
line
);
/* In single line mode, send off the query if any */
if
(
query_buf
->
data
[
0
]
!=
'\0'
&&
GetVariableBool
(
pset
->
vars
,
"singleline"
))
{
success
=
SendQuery
(
pset
,
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
}
/* In single line mode, send off the query if any */
/* Have we lost the db connection? */
if
(
query_buf
->
data
[
0
]
!=
'\0'
&&
GetVariableBool
(
pset
->
vars
,
"singleline"
))
{
if
(
pset
->
db
==
NULL
&&
!
pset
->
cur_cmd_interactive
)
success
=
SendQuery
(
pset
,
query_buf
->
data
);
{
resetPQExpBuffer
(
query_buf
);
successResult
=
EXIT_BADCONN
;
}
break
;
}
}
/* while */
/* Have we lost the db connection? */
if
(
pset
->
db
==
NULL
&&
!
pset
->
cur_cmd_interactive
)
{
successResult
=
EXIT_BADCONN
;
break
;
}
}
/* while */
destroyPQExpBuffer
(
query_buf
);
destroyPQExpBuffer
(
query_buf
);
pset
->
cur_cmd_source
=
prev_cmd_source
;
pset
->
cur_cmd_source
=
prev_cmd_source
;
pset
->
cur_cmd_interactive
=
prev_cmd_interactive
;
pset
->
cur_cmd_interactive
=
prev_cmd_interactive
;
return
successResult
;
return
successResult
;
}
/* MainLoop() */
}
/* MainLoop() */
src/bin/psql/mainloop.h
View file @
0e6652e6
...
@@ -5,6 +5,6 @@
...
@@ -5,6 +5,6 @@
#include "settings.h"
#include "settings.h"
int
int
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
);
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
);
#endif
MAINLOOP_H
#endif
/* MAINLOOP_H */
src/bin/psql/print.c
View file @
0e6652e6
...
@@ -7,8 +7,8 @@
...
@@ -7,8 +7,8 @@
#include <math.h>
#include <math.h>
#include <signal.h>
#include <signal.h>
#ifndef WIN32
#ifndef WIN32
#include <unistd.h>
/* for isatty() */
#include <unistd.h>
/* for isatty() */
#include <sys/ioctl.h>
/* for ioctl() */
#include <sys/ioctl.h>
/* for ioctl() */
#else
#else
#define popen(x,y) _popen(x,y)
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#define pclose(x) _pclose(x)
...
@@ -16,386 +16,422 @@
...
@@ -16,386 +16,422 @@
#include <pqsignal.h>
#include <pqsignal.h>
#include <libpq-fe.h>
#include <libpq-fe.h>
#include <postgres_ext.h>
/* for Oid type */
#include <postgres_ext.h>
/* for Oid type */
#define DEFAULT_PAGER "/bin/more"
#define DEFAULT_PAGER "/bin/more"
/*************************/
/*************************/
/* Unaligned text
*/
/* Unaligned text
*/
/*************************/
/*************************/
static
void
static
void
print_unaligned_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_unaligned_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_fieldsep
,
bool
opt_barebones
,
const
char
*
opt_fieldsep
,
bool
opt_barebones
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
char
**
ptr
;
char
**
ptr
;
if
(
!
opt_fieldsep
)
if
(
!
opt_fieldsep
)
opt_fieldsep
=
""
;
opt_fieldsep
=
""
;
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
if
(
!
opt_barebones
&&
title
)
fprintf
(
fout
,
"%s
\n
"
,
title
);
fprintf
(
fout
,
"%s
\n
"
,
title
);
/* print headers and count columns */
/* print headers and count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
{
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
{
if
(
!
opt_barebones
)
{
col_count
++
;
if
(
col_count
>
1
)
if
(
!
opt_barebones
)
fputs
(
opt_fieldsep
,
fout
);
{
fputs
(
*
ptr
,
fout
);
if
(
col_count
>
1
)
}
fputs
(
opt_fieldsep
,
fout
);
}
fputs
(
*
ptr
,
fout
);
if
(
!
opt_barebones
)
}
fputs
(
"
\n
"
,
fout
);
}
if
(
!
opt_barebones
)
/* print cells */
fputs
(
"
\n
"
,
fout
);
i
=
0
;
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
{
/* print cells */
fputs
(
*
ptr
,
fout
);
i
=
0
;
if
((
i
+
1
)
%
col_count
)
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
fputs
(
opt_fieldsep
,
fout
);
{
else
fputs
(
*
ptr
,
fout
);
fputs
(
"
\n
"
,
fout
);
if
((
i
+
1
)
%
col_count
)
i
++
;
fputs
(
opt_fieldsep
,
fout
);
}
else
fputs
(
"
\n
"
,
fout
);
i
++
;
}
/* print footers */
/* print footers */
if
(
!
opt_barebones
&&
footers
)
if
(
!
opt_barebones
&&
footers
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
}
}
static
void
static
void
print_unaligned_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_unaligned_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_fieldsep
,
bool
opt_barebones
,
const
char
*
opt_fieldsep
,
bool
opt_barebones
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
unsigned
int
record
=
1
;
unsigned
int
record
=
1
;
char
**
ptr
;
char
**
ptr
;
if
(
!
opt_fieldsep
)
if
(
!
opt_fieldsep
)
opt_fieldsep
=
""
;
opt_fieldsep
=
""
;
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
if
(
!
opt_barebones
&&
title
)
fprintf
(
fout
,
"%s
\n
"
,
title
);
fprintf
(
fout
,
"%s
\n
"
,
title
);
/* count columns */
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
{
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
col_count
++
;
}
/* print records */
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
{
if
(
i
%
col_count
==
0
)
{
if
(
i
%
col_count
==
0
)
if
(
!
opt_barebones
)
{
fprintf
(
fout
,
"-- RECORD %d
\n
"
,
record
++
);
if
(
!
opt_barebones
)
else
fprintf
(
fout
,
"-- RECORD %d
\n
"
,
record
++
);
fputc
(
'\n'
,
fout
);
else
fputc
(
'\n'
,
fout
);
}
fprintf
(
fout
,
"%s%s%s
\n
"
,
headers
[
i
%
col_count
],
opt_fieldsep
,
*
ptr
);
}
}
fprintf
(
fout
,
"%s%s%s
\n
"
,
headers
[
i
%
col_count
],
opt_fieldsep
,
*
ptr
);
}
/* print footers */
/* print footers */
if
(
!
opt_barebones
&&
footers
)
{
if
(
!
opt_barebones
&&
footers
)
fputs
(
"--- END ---
\n
"
,
fout
);
{
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
fputs
(
"--- END ---
\n
"
,
fout
);
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
}
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
}
}
}
/********************/
/********************/
/* Aligned text
*/
/* Aligned text
*/
/********************/
/********************/
/* draw "line" */
/* draw "line" */
static
void
static
void
_print_horizontal_line
(
const
unsigned
int
col_count
,
const
unsigned
int
*
widths
,
unsigned
short
border
,
FILE
*
fout
)
_print_horizontal_line
(
const
unsigned
int
col_count
,
const
unsigned
int
*
widths
,
unsigned
short
border
,
FILE
*
fout
)
{
{
unsigned
int
i
,
j
;
unsigned
int
i
,
if
(
border
==
1
)
j
;
fputc
(
'-'
,
fout
);
else
if
(
border
==
2
)
if
(
border
==
1
)
fputs
(
"+-"
,
fout
);
fputc
(
'-'
,
fout
);
else
if
(
border
==
2
)
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
fputs
(
"+-"
,
fout
);
for
(
j
=
0
;
j
<
widths
[
i
];
j
++
)
fputc
(
'-'
,
fout
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
if
(
i
<
col_count
-
1
)
{
for
(
j
=
0
;
j
<
widths
[
i
];
j
++
)
if
(
border
==
0
)
fputc
(
'-'
,
fout
);
fputc
(
' '
,
fout
);
else
if
(
i
<
col_count
-
1
)
fputs
(
"-+-"
,
fout
);
{
}
if
(
border
==
0
)
}
fputc
(
' '
,
fout
);
else
if
(
border
==
2
)
fputs
(
"-+-"
,
fout
);
fputs
(
"-+"
,
fout
);
}
else
if
(
border
==
1
)
}
fputc
(
'-'
,
fout
);
if
(
border
==
2
)
fputc
(
'\n'
,
fout
);
fputs
(
"-+"
,
fout
);
else
if
(
border
==
1
)
fputc
(
'-'
,
fout
);
fputc
(
'\n'
,
fout
);
}
}
static
void
static
void
print_aligned_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_aligned_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
,
tmp
;
unsigned
int
i
,
unsigned
int
*
widths
,
total_w
;
tmp
;
char
**
ptr
;
unsigned
int
*
widths
,
total_w
;
/* count columns */
char
**
ptr
;
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
widths
=
calloc
(
col_count
,
sizeof
(
*
widths
));
col_count
++
;
if
(
!
widths
)
{
perror
(
"calloc"
);
widths
=
calloc
(
col_count
,
sizeof
(
*
widths
));
exit
(
EXIT_FAILURE
);
if
(
!
widths
)
}
{
perror
(
"calloc"
);
/* calc column widths */
exit
(
EXIT_FAILURE
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
}
if
((
tmp
=
strlen
(
headers
[
i
]))
>
widths
[
i
])
widths
[
i
]
=
tmp
;
/* don't wanna call strlen twice */
/* calc column widths */
for
(
i
=
0
;
i
<
col_count
;
i
++
)
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
ptr
++
,
i
++
)
if
((
tmp
=
strlen
(
headers
[
i
]))
>
widths
[
i
])
if
((
tmp
=
strlen
(
*
ptr
))
>
widths
[
i
%
col_count
])
widths
[
i
]
=
tmp
;
/* don't wanna call strlen twice */
widths
[
i
%
col_count
]
=
tmp
;
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
ptr
++
,
i
++
)
if
(
opt_border
==
0
)
if
((
tmp
=
strlen
(
*
ptr
))
>
widths
[
i
%
col_count
])
total_w
=
col_count
-
1
;
widths
[
i
%
col_count
]
=
tmp
;
else
if
(
opt_border
==
1
)
total_w
=
col_count
*
3
-
2
;
if
(
opt_border
==
0
)
else
total_w
=
col_count
-
1
;
total_w
=
col_count
*
3
+
1
;
else
if
(
opt_border
==
1
)
total_w
=
col_count
*
3
-
2
;
for
(
i
=
0
;
i
<
col_count
;
i
++
)
total_w
+=
widths
[
i
];
/* print title */
if
(
title
&&
!
opt_barebones
)
{
if
(
strlen
(
title
)
>=
total_w
)
fprintf
(
fout
,
"%s
\n
"
,
title
);
else
else
fprintf
(
fout
,
"%-*s%s
\n
"
,
(
total_w
-
strlen
(
title
))
/
2
,
""
,
title
);
total_w
=
col_count
*
3
+
1
;
}
for
(
i
=
0
;
i
<
col_count
;
i
++
)
/* print headers */
total_w
+=
widths
[
i
];
if
(
!
opt_barebones
)
{
if
(
opt_border
==
2
)
/* print title */
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
if
(
title
&&
!
opt_barebones
)
{
if
(
opt_border
==
2
)
if
(
strlen
(
title
)
>=
total_w
)
fputs
(
"| "
,
fout
);
fprintf
(
fout
,
"%s
\n
"
,
title
);
else
if
(
opt_border
==
1
)
fputc
(
' '
,
fout
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
/* centered */
fprintf
(
fout
,
"%-*s%s%-*s"
,
(
int
)
floor
((
widths
[
i
]
-
strlen
(
headers
[
i
]))
/
2
.
0
),
""
,
headers
[
i
],
(
int
)
ceil
((
widths
[
i
]
-
strlen
(
headers
[
i
]))
/
2
.
0
)
,
""
);
if
(
i
<
col_count
-
1
)
{
if
(
opt_border
==
0
)
fputc
(
' '
,
fout
);
else
else
fputs
(
" | "
,
fout
);
fprintf
(
fout
,
"%-*s%s
\n
"
,
(
total_w
-
strlen
(
title
))
/
2
,
""
,
title
);
}
}
}
if
(
opt_border
==
2
)
/* print headers */
fputs
(
" |"
,
fout
);
if
(
!
opt_barebones
)
else
if
(
opt_border
==
1
)
{
fputc
(
' '
,
fout
);;
if
(
opt_border
==
2
)
fputc
(
'\n'
,
fout
);
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
if
(
opt_border
==
2
)
}
fputs
(
"| "
,
fout
);
else
if
(
opt_border
==
1
)
fputc
(
' '
,
fout
);
for
(
i
=
0
;
i
<
col_count
;
i
++
)
{
/* centered */
fprintf
(
fout
,
"%-*s%s%-*s"
,
(
int
)
floor
((
widths
[
i
]
-
strlen
(
headers
[
i
]))
/
2
.
0
),
""
,
headers
[
i
],
(
int
)
ceil
((
widths
[
i
]
-
strlen
(
headers
[
i
]))
/
2
.
0
),
""
);
if
(
i
<
col_count
-
1
)
{
if
(
opt_border
==
0
)
fputc
(
' '
,
fout
);
else
fputs
(
" | "
,
fout
);
}
}
/* print cells */
if
(
opt_border
==
2
)
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
fputs
(
" |"
,
fout
);
/* beginning of line */
else
if
(
opt_border
==
1
)
if
(
i
%
col_count
==
0
)
{
fputc
(
' '
,
fout
);;
if
(
opt_border
==
2
)
fputc
(
'\n'
,
fout
);
fputs
(
"| "
,
fout
);
else
if
(
opt_border
==
1
)
fputc
(
' '
,
fout
);
}
/* content */
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
if
(
opt_align
[(
i
)
%
col_count
]
==
'r'
)
fprintf
(
fout
,
"%*s"
,
widths
[
i
%
col_count
],
cells
[
i
]);
else
{
if
((
i
+
1
)
%
col_count
==
0
&&
opt_border
!=
2
)
fputs
(
cells
[
i
],
fout
);
else
fprintf
(
fout
,
"%-*s"
,
widths
[
i
%
col_count
],
cells
[
i
]);
}
}
/* divider */
/* print cells */
if
((
i
+
1
)
%
col_count
)
{
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
if
(
opt_border
==
0
)
{
fputc
(
' '
,
fout
);
/* beginning of line */
else
if
(
i
%
col_count
==
0
)
fputs
(
" | "
,
fout
);
{
}
if
(
opt_border
==
2
)
/* end of line */
fputs
(
"| "
,
fout
);
else
{
else
if
(
opt_border
==
1
)
if
(
opt_border
==
2
)
fputc
(
' '
,
fout
);
fputs
(
" |"
,
fout
);
}
fputc
(
'\n'
,
fout
);
/* content */
if
(
opt_align
[(
i
)
%
col_count
]
==
'r'
)
fprintf
(
fout
,
"%*s"
,
widths
[
i
%
col_count
],
cells
[
i
]);
else
{
if
((
i
+
1
)
%
col_count
==
0
&&
opt_border
!=
2
)
fputs
(
cells
[
i
],
fout
);
else
fprintf
(
fout
,
"%-*s"
,
widths
[
i
%
col_count
],
cells
[
i
]);
}
/* divider */
if
((
i
+
1
)
%
col_count
)
{
if
(
opt_border
==
0
)
fputc
(
' '
,
fout
);
else
fputs
(
" | "
,
fout
);
}
/* end of line */
else
{
if
(
opt_border
==
2
)
fputs
(
" |"
,
fout
);
fputc
(
'\n'
,
fout
);
}
}
}
}
if
(
opt_border
==
2
)
if
(
opt_border
==
2
)
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
_print_horizontal_line
(
col_count
,
widths
,
opt_border
,
fout
);
/* print footers */
/* print footers */
if
(
footers
&&
!
opt_barebones
)
if
(
footers
&&
!
opt_barebones
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
fputc
(
'\n'
,
fout
);
fputc
(
'\n'
,
fout
);
/* clean up */
/* clean up */
free
(
widths
);
free
(
widths
);
}
}
static
void
static
void
print_aligned_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_aligned_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
record
=
1
;
unsigned
int
record
=
1
;
char
**
ptr
;
char
**
ptr
;
unsigned
int
i
,
tmp
,
hwidth
=
0
,
dwidth
=
0
;
unsigned
int
i
,
char
*
divider
;
tmp
,
hwidth
=
0
,
/* count columns and find longest header */
dwidth
=
0
;
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
{
char
*
divider
;
col_count
++
;
if
((
tmp
=
strlen
(
*
ptr
))
>
hwidth
)
/* count columns and find longest header */
hwidth
=
tmp
;
/* don't wanna call strlen twice */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
}
{
col_count
++
;
/* find longest data cell */
if
((
tmp
=
strlen
(
*
ptr
))
>
hwidth
)
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
hwidth
=
tmp
;
/* don't wanna call strlen twice */
if
((
tmp
=
strlen
(
*
ptr
))
>
dwidth
)
}
dwidth
=
tmp
;
/* print title */
if
(
!
opt_barebones
&&
title
)
fprintf
(
fout
,
"%s
\n
"
,
title
);
/* make horizontal border */
divider
=
malloc
(
hwidth
+
dwidth
+
10
);
if
(
!
divider
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
divider
[
0
]
=
'\0'
;
if
(
opt_border
==
2
)
strcat
(
divider
,
"+-"
);
for
(
i
=
0
;
i
<
hwidth
;
i
++
)
strcat
(
divider
,
opt_border
>
0
?
"-"
:
" "
);
if
(
opt_border
>
0
)
strcat
(
divider
,
"-+-"
);
else
strcat
(
divider
,
" "
);
for
(
i
=
0
;
i
<
dwidth
;
i
++
)
strcat
(
divider
,
opt_border
>
0
?
"-"
:
" "
);
if
(
opt_border
==
2
)
strcat
(
divider
,
"-+"
);
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
if
(
i
%
col_count
==
0
)
{
if
(
!
opt_barebones
)
{
char
*
div_copy
=
strdup
(
divider
);
char
*
record_str
=
malloc
(
32
);
size_t
record_str_len
;
if
(
!
div_copy
||
!
record_str
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
if
(
opt_border
==
0
)
/* find longest data cell */
sprintf
(
record_str
,
"* Record %d"
,
record
++
);
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
else
if
((
tmp
=
strlen
(
*
ptr
))
>
dwidth
)
sprintf
(
record_str
,
"[ RECORD %d ]"
,
record
++
);
dwidth
=
tmp
;
record_str_len
=
strlen
(
record_str
);
if
(
record_str_len
+
opt_border
>
strlen
(
div_copy
))
{
/* print title */
void
*
new
;
if
(
!
opt_barebones
&&
title
)
new
=
realloc
(
div_copy
,
record_str_len
+
opt_border
);
fprintf
(
fout
,
"%s
\n
"
,
title
);
if
(
!
new
)
{
perror
(
"realloc"
);
/* make horizontal border */
exit
(
EXIT_FAILURE
);
divider
=
malloc
(
hwidth
+
dwidth
+
10
);
}
if
(
!
divider
)
div_copy
=
new
;
{
}
perror
(
"malloc"
);
strncpy
(
div_copy
+
opt_border
,
record_str
,
record_str_len
);
exit
(
EXIT_FAILURE
);
fprintf
(
fout
,
"%s
\n
"
,
div_copy
);
free
(
record_str
);
free
(
div_copy
);
}
else
if
(
i
!=
0
&&
opt_border
<
2
)
fprintf
(
fout
,
"%s
\n
"
,
divider
);
}
}
divider
[
0
]
=
'\0'
;
if
(
opt_border
==
2
)
if
(
opt_border
==
2
)
fputs
(
"| "
,
fout
);
strcat
(
divider
,
"+-"
);
fprintf
(
fout
,
"%-*s"
,
hwidth
,
headers
[
i
%
col_count
]);
for
(
i
=
0
;
i
<
hwidth
;
i
++
)
strcat
(
divider
,
opt_border
>
0
?
"-"
:
" "
);
if
(
opt_border
>
0
)
if
(
opt_border
>
0
)
fputs
(
" | "
,
fout
);
strcat
(
divider
,
"-+-"
);
else
else
fputs
(
" "
,
fout
);
strcat
(
divider
,
" "
);
for
(
i
=
0
;
i
<
dwidth
;
i
++
)
strcat
(
divider
,
opt_border
>
0
?
"-"
:
" "
);
if
(
opt_border
==
2
)
strcat
(
divider
,
"-+"
);
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
if
(
i
%
col_count
==
0
)
{
if
(
!
opt_barebones
)
{
char
*
div_copy
=
strdup
(
divider
);
char
*
record_str
=
malloc
(
32
);
size_t
record_str_len
;
if
(
!
div_copy
||
!
record_str
)
{
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
if
(
opt_border
==
0
)
sprintf
(
record_str
,
"* Record %d"
,
record
++
);
else
sprintf
(
record_str
,
"[ RECORD %d ]"
,
record
++
);
record_str_len
=
strlen
(
record_str
);
if
(
record_str_len
+
opt_border
>
strlen
(
div_copy
))
{
void
*
new
;
new
=
realloc
(
div_copy
,
record_str_len
+
opt_border
);
if
(
!
new
)
{
perror
(
"realloc"
);
exit
(
EXIT_FAILURE
);
}
div_copy
=
new
;
}
strncpy
(
div_copy
+
opt_border
,
record_str
,
record_str_len
);
fprintf
(
fout
,
"%s
\n
"
,
div_copy
);
free
(
record_str
);
free
(
div_copy
);
}
else
if
(
i
!=
0
&&
opt_border
<
2
)
fprintf
(
fout
,
"%s
\n
"
,
divider
);
}
if
(
opt_border
==
2
)
fputs
(
"| "
,
fout
);
fprintf
(
fout
,
"%-*s"
,
hwidth
,
headers
[
i
%
col_count
]);
if
(
opt_border
>
0
)
fputs
(
" | "
,
fout
);
else
fputs
(
" "
,
fout
);
if
(
opt_border
<
2
)
if
(
opt_border
<
2
)
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
else
else
fprintf
(
fout
,
"%-*s |
\n
"
,
dwidth
,
*
ptr
);
fprintf
(
fout
,
"%-*s |
\n
"
,
dwidth
,
*
ptr
);
}
}
if
(
opt_border
==
2
)
if
(
opt_border
==
2
)
fprintf
(
fout
,
"%s
\n
"
,
divider
);
fprintf
(
fout
,
"%s
\n
"
,
divider
);
/* print footers */
/* print footers */
if
(
!
opt_barebones
&&
footers
&&
*
footers
)
{
if
(
!
opt_barebones
&&
footers
&&
*
footers
)
if
(
opt_border
<
2
)
{
fputc
(
'\n'
,
fout
);
if
(
opt_border
<
2
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
fputc
(
'\n'
,
fout
);
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
}
fprintf
(
fout
,
"%s
\n
"
,
*
ptr
);
}
fputc
(
'\n'
,
fout
);
fputc
(
'\n'
,
fout
);
free
(
divider
);
free
(
divider
);
}
}
...
@@ -408,351 +444,382 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
...
@@ -408,351 +444,382 @@ print_aligned_vertical(const char * title, char ** headers, char ** cells, char
static
void
static
void
html_escaped_print
(
const
char
*
in
,
FILE
*
fout
)
html_escaped_print
(
const
char
*
in
,
FILE
*
fout
)
{
{
const
char
*
p
;
const
char
*
p
;
for
(
p
=
in
;
*
p
;
p
++
)
switch
(
*
p
)
{
for
(
p
=
in
;
*
p
;
p
++
)
case
'&'
:
switch
(
*
p
)
fputs
(
"&"
,
fout
);
{
break
;
case
'&'
:
case
'<'
:
fputs
(
"&"
,
fout
);
fputs
(
"<"
,
fout
);
break
;
break
;
case
'<'
:
case
'>'
:
fputs
(
"<"
,
fout
);
fputs
(
">"
,
fout
);
break
;
break
;
case
'>'
:
case
'\n'
:
fputs
(
">"
,
fout
);
fputs
(
"<br>"
,
fout
);
break
;
break
;
case
'\n'
:
default:
fputs
(
"<br>"
,
fout
);
fputc
(
*
p
,
fout
);
break
;
}
default:
fputc
(
*
p
,
fout
);
}
}
}
static
void
static
void
print_html_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_html_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
char
*
opt_table_attr
,
char
*
opt_table_attr
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
char
**
ptr
;
char
**
ptr
;
fprintf
(
fout
,
"<table border=%d"
,
opt_border
);
fprintf
(
fout
,
"<table border=%d"
,
opt_border
);
if
(
opt_table_attr
)
if
(
opt_table_attr
)
fprintf
(
fout
,
" %s"
,
opt_table_attr
);
fprintf
(
fout
,
" %s"
,
opt_table_attr
);
fputs
(
">
\n
"
,
fout
);
fputs
(
">
\n
"
,
fout
);
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
{
if
(
!
opt_barebones
&&
title
)
fputs
(
" <caption>"
,
fout
);
{
html_escaped_print
(
title
,
fout
);
fputs
(
" <caption>"
,
fout
);
fputs
(
"</caption>
\n
"
,
fout
);
html_escaped_print
(
title
,
fout
);
}
fputs
(
"</caption>
\n
"
,
fout
);
}
/* print headers and count columns */
if
(
!
opt_barebones
)
fputs
(
" <tr>
\n
"
,
fout
);
for
(
i
=
0
,
ptr
=
headers
;
*
ptr
;
i
++
,
ptr
++
)
{
col_count
++
;
if
(
!
opt_barebones
)
{
fputs
(
" <th align=center>"
,
fout
);
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</th>
\n
"
,
fout
);
}
}
if
(
!
opt_barebones
)
fputs
(
" </tr>
\n
"
,
fout
);
/* print cells */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
if
(
i
%
col_count
==
0
)
fputs
(
" <tr valign=top>
\n
"
,
fout
);
fprintf
(
fout
,
" <td align=%s>"
,
opt_align
[(
i
)
%
col_count
]
==
'r'
?
"right"
:
"left"
);
if
(
(
*
ptr
)[
strspn
(
*
ptr
,
"
\t
"
)]
==
'\0'
)
/* is string only whitespace? */
fputs
(
" "
,
fout
);
else
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</td>
\n
"
,
fout
);
if
(
(
i
+
1
)
%
col_count
==
0
)
/* print headers and count columns */
fputs
(
" </tr>
\n
"
,
fout
);
if
(
!
opt_barebones
)
}
fputs
(
" <tr>
\n
"
,
fout
);
for
(
i
=
0
,
ptr
=
headers
;
*
ptr
;
i
++
,
ptr
++
)
{
col_count
++
;
if
(
!
opt_barebones
)
{
fputs
(
" <th align=center>"
,
fout
);
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</th>
\n
"
,
fout
);
}
}
if
(
!
opt_barebones
)
fputs
(
" </tr>
\n
"
,
fout
);
/* print cells */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
if
(
i
%
col_count
==
0
)
fputs
(
" <tr valign=top>
\n
"
,
fout
);
fprintf
(
fout
,
" <td align=%s>"
,
opt_align
[(
i
)
%
col_count
]
==
'r'
?
"right"
:
"left"
);
if
((
*
ptr
)[
strspn
(
*
ptr
,
"
\t
"
)]
==
'\0'
)
/* is string only
* whitespace? */
fputs
(
" "
,
fout
);
else
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</td>
\n
"
,
fout
);
fputs
(
"</table>
\n
"
,
fout
);
if
((
i
+
1
)
%
col_count
==
0
)
fputs
(
" </tr>
\n
"
,
fout
);
}
/* print footers */
fputs
(
"</table>
\n
"
,
fout
);
if
(
footers
&&
!
opt_barebones
)
/* print footers */
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
html_escaped_print
(
*
ptr
,
fout
);
if
(
footers
&&
!
opt_barebones
)
fputs
(
"<br>
\n
"
,
fout
);
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
}
{
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"<br>
\n
"
,
fout
);
}
fputc
(
'\n'
,
fout
);
fputc
(
'\n'
,
fout
);
}
}
static
void
static
void
print_html_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_html_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
char
*
opt_table_attr
,
char
*
opt_table_attr
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
unsigned
int
record
=
1
;
unsigned
int
record
=
1
;
char
**
ptr
;
char
**
ptr
;
fprintf
(
fout
,
"<table border=%d"
,
opt_border
);
fprintf
(
fout
,
"<table border=%d"
,
opt_border
);
if
(
opt_table_attr
)
if
(
opt_table_attr
)
fprintf
(
fout
,
" %s"
,
opt_table_attr
);
fprintf
(
fout
,
" %s"
,
opt_table_attr
);
fputs
(
">
\n
"
,
fout
);
fputs
(
">
\n
"
,
fout
);
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
{
if
(
!
opt_barebones
&&
title
)
fputs
(
" <caption>"
,
fout
);
{
html_escaped_print
(
title
,
fout
);
fputs
(
" <caption>"
,
fout
);
fputs
(
"</caption>
\n
"
,
fout
);
html_escaped_print
(
title
,
fout
);
}
fputs
(
"</caption>
\n
"
,
fout
);
}
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
if
(
i
%
col_count
==
0
)
{
if
(
!
opt_barebones
)
fprintf
(
fout
,
"
\n
<tr><td colspan=2 align=center>Record %d</td></tr>
\n
"
,
record
++
);
else
fputs
(
"
\n
<tr><td colspan=2> </td></tr>
\n
"
,
fout
);
}
fputs
(
" <tr valign=top>
\n
"
" <th>"
,
fout
);
html_escaped_print
(
headers
[
i
%
col_count
],
fout
);
fputs
(
"</th>
\n
"
,
fout
);
fprintf
(
fout
,
" <td align=%s>"
,
opt_align
[
i
%
col_count
]
==
'r'
?
"right"
:
"left"
);
if
(
(
*
ptr
)[
strspn
(
*
ptr
,
"
\t
"
)]
==
'\0'
)
/* is string only whitespace? */
fputs
(
" "
,
fout
);
else
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</td>
\n
</tr>
\n
"
,
fout
);
}
fputs
(
"</table>
\n
"
,
fout
);
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
/* print footers */
/* print records */
if
(
footers
&&
!
opt_barebones
)
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
{
html_escaped_print
(
*
ptr
,
fout
);
if
(
i
%
col_count
==
0
)
fputs
(
"<br>
\n
"
,
fout
);
{
if
(
!
opt_barebones
)
fprintf
(
fout
,
"
\n
<tr><td colspan=2 align=center>Record %d</td></tr>
\n
"
,
record
++
);
else
fputs
(
"
\n
<tr><td colspan=2> </td></tr>
\n
"
,
fout
);
}
fputs
(
" <tr valign=top>
\n
"
" <th>"
,
fout
);
html_escaped_print
(
headers
[
i
%
col_count
],
fout
);
fputs
(
"</th>
\n
"
,
fout
);
fprintf
(
fout
,
" <td align=%s>"
,
opt_align
[
i
%
col_count
]
==
'r'
?
"right"
:
"left"
);
if
((
*
ptr
)[
strspn
(
*
ptr
,
"
\t
"
)]
==
'\0'
)
/* is string only
* whitespace? */
fputs
(
" "
,
fout
);
else
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"</td>
\n
</tr>
\n
"
,
fout
);
}
}
fputc
(
'\n'
,
fout
);
fputs
(
"</table>
\n
"
,
fout
);
/* print footers */
if
(
footers
&&
!
opt_barebones
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
html_escaped_print
(
*
ptr
,
fout
);
fputs
(
"<br>
\n
"
,
fout
);
}
fputc
(
'\n'
,
fout
);
}
}
/*************************/
/*************************/
/* LaTeX
*/
/* LaTeX
*/
/*************************/
/*************************/
static
void
static
void
latex_escaped_print
(
const
char
*
in
,
FILE
*
fout
)
latex_escaped_print
(
const
char
*
in
,
FILE
*
fout
)
{
{
const
char
*
p
;
const
char
*
p
;
for
(
p
=
in
;
*
p
;
p
++
)
switch
(
*
p
)
{
for
(
p
=
in
;
*
p
;
p
++
)
case
'&'
:
switch
(
*
p
)
fputs
(
"
\\
&"
,
fout
);
{
break
;
case
'&'
:
case
'%'
:
fputs
(
"
\\
&"
,
fout
);
fputs
(
"
\\
%"
,
fout
);
break
;
break
;
case
'%'
:
case
'$'
:
fputs
(
"
\\
%"
,
fout
);
fputs
(
"
\\
$"
,
fout
);
break
;
break
;
case
'$'
:
case
'{'
:
fputs
(
"
\\
$"
,
fout
);
fputs
(
"
\\
{"
,
fout
);
break
;
break
;
case
'{'
:
case
'}'
:
fputs
(
"
\\
{"
,
fout
);
fputs
(
"
\\
}"
,
fout
);
break
;
break
;
case
'}'
:
case
'\\'
:
fputs
(
"
\\
}"
,
fout
);
fputs
(
"
\\
backslash"
,
fout
);
break
;
break
;
case
'\\'
:
case
'\n'
:
fputs
(
"
\\
backslash"
,
fout
);
fputs
(
"
\\\\
"
,
fout
);
break
;
break
;
case
'\n'
:
default:
fputs
(
"
\\\\
"
,
fout
);
fputc
(
*
p
,
fout
);
break
;
}
default:
fputc
(
*
p
,
fout
);
}
}
}
static
void
static
void
print_latex_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_latex_text
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
const
char
*
cp
;
const
char
*
cp
;
char
**
ptr
;
char
**
ptr
;
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
{
if
(
!
opt_barebones
&&
title
)
fputs
(
"
\b
egin{center}
\n
"
,
fout
);
{
latex_escaped_print
(
title
,
fout
);
fputs
(
"
\b
egin{center}
\n
"
,
fout
);
fputs
(
"
\n
\end{center}
\n\n
"
,
fout
);
latex_escaped_print
(
title
,
fout
);
}
fputs
(
"
\n
\end{center}
\n\n
"
,
fout
);
/* begin environment and set alignments and borders */
fputs
(
"
\\
begin{tabular}{"
,
fout
);
if
(
opt_border
==
0
)
fputs
(
opt_align
,
fout
);
else
if
(
opt_border
==
1
)
{
for
(
cp
=
opt_align
;
*
cp
;
cp
++
)
{
if
(
cp
!=
opt_align
)
fputc
(
'|'
,
fout
);
fputc
(
*
cp
,
fout
);
}
}
else
if
(
opt_border
==
2
)
{
for
(
cp
=
opt_align
;
*
cp
;
cp
++
)
{
fputc
(
'|'
,
fout
);
fputc
(
*
cp
,
fout
);
}
fputc
(
'|'
,
fout
);
}
fputs
(
"}
\n
"
,
fout
);
if
(
!
opt_barebones
&&
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
/* print headers and count columns */
for
(
i
=
0
,
ptr
=
headers
;
*
ptr
;
i
++
,
ptr
++
)
{
col_count
++
;
if
(
!
opt_barebones
)
{
if
(
i
!=
0
)
fputs
(
" & "
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
}
}
}
if
(
!
opt_barebones
)
{
/* begin environment and set alignments and borders */
fputs
(
"
\\\\\n
"
,
fout
);
fputs
(
"
\\
begin{tabular}{"
,
fout
);
fputs
(
"
\\
hline
\n
"
,
fout
);
if
(
opt_border
==
0
)
}
fputs
(
opt_align
,
fout
);
else
if
(
opt_border
==
1
)
{
for
(
cp
=
opt_align
;
*
cp
;
cp
++
)
{
if
(
cp
!=
opt_align
)
fputc
(
'|'
,
fout
);
fputc
(
*
cp
,
fout
);
}
}
else
if
(
opt_border
==
2
)
{
for
(
cp
=
opt_align
;
*
cp
;
cp
++
)
{
fputc
(
'|'
,
fout
);
fputc
(
*
cp
,
fout
);
}
fputc
(
'|'
,
fout
);
}
fputs
(
"}
\n
"
,
fout
);
/* print cells */
if
(
!
opt_barebones
&&
opt_border
==
2
)
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
fputs
(
"
\\
hline
\n
"
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
if
(
(
i
+
1
)
%
col_count
==
0
)
/* print headers and count columns */
fputs
(
"
\\\\\n
"
,
fout
);
for
(
i
=
0
,
ptr
=
headers
;
*
ptr
;
i
++
,
ptr
++
)
else
{
fputs
(
" & "
,
fout
);
col_count
++
;
}
if
(
!
opt_barebones
)
{
if
(
i
!=
0
)
fputs
(
" & "
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
}
}
if
(
!
opt_barebones
)
{
fputs
(
"
\\\\\n
"
,
fout
);
fputs
(
"
\\
hline
\n
"
,
fout
);
}
/* print cells */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
latex_escaped_print
(
*
ptr
,
fout
);
if
(
opt_border
==
2
)
if
((
i
+
1
)
%
col_count
==
0
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
else
fputs
(
" & "
,
fout
);
}
fputs
(
"
\\
end{tabular}
\n\n
"
,
fout
);
if
(
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fputs
(
"
\\
end{tabular}
\n\n
"
,
fout
);
/* print footers */
if
(
footers
&&
!
opt_barebones
)
/* print footers */
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
latex_escaped_print
(
*
ptr
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
}
fputc
(
'\n'
,
fout
);
if
(
footers
&&
!
opt_barebones
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
latex_escaped_print
(
*
ptr
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
}
fputc
(
'\n'
,
fout
);
}
}
static
void
static
void
print_latex_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
print_latex_vertical
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
const
char
*
opt_align
,
bool
opt_barebones
,
unsigned
short
int
opt_border
,
FILE
*
fout
)
FILE
*
fout
)
{
{
unsigned
int
col_count
=
0
;
unsigned
int
col_count
=
0
;
unsigned
int
i
;
unsigned
int
i
;
char
**
ptr
;
char
**
ptr
;
unsigned
int
record
=
1
;
unsigned
int
record
=
1
;
(
void
)
opt_align
;
/* currently unused parameter */
(
void
)
opt_align
;
/* currently unused parameter */
/* print title */
/* print title */
if
(
!
opt_barebones
&&
title
)
{
if
(
!
opt_barebones
&&
title
)
fputs
(
"
\b
egin{center}
\n
"
,
fout
);
{
latex_escaped_print
(
title
,
fout
);
fputs
(
"
\b
egin{center}
\n
"
,
fout
);
fputs
(
"
\n
\end{center}
\n\n
"
,
fout
);
latex_escaped_print
(
title
,
fout
);
}
fputs
(
"
\n
\end{center}
\n\n
"
,
fout
);
/* begin environment and set alignments and borders */
fputs
(
"
\\
begin{tabular}{"
,
fout
);
if
(
opt_border
==
0
)
fputs
(
"cl"
,
fout
);
else
if
(
opt_border
==
1
)
fputs
(
"c|l"
,
fout
);
else
if
(
opt_border
==
2
)
fputs
(
"|c|l|"
,
fout
);
fputs
(
"}
\n
"
,
fout
);
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
/* new record */
if
(
i
%
col_count
==
0
)
{
if
(
!
opt_barebones
)
{
if
(
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fprintf
(
fout
,
"
\\
multicolumn{2}{c}{Record %d}
\\\\\n
"
,
record
++
);
}
if
(
opt_border
>=
1
)
fputs
(
"
\\
hline
\n
"
,
fout
);
}
}
latex_escaped_print
(
headers
[
i
%
col_count
],
fout
);
/* begin environment and set alignments and borders */
fputs
(
" & "
,
fout
);
fputs
(
"
\\
begin{tabular}{"
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
if
(
opt_border
==
0
)
fputs
(
"
\\\\\n
"
,
fout
);
fputs
(
"cl"
,
fout
);
}
else
if
(
opt_border
==
1
)
fputs
(
"c|l"
,
fout
);
else
if
(
opt_border
==
2
)
fputs
(
"|c|l|"
,
fout
);
fputs
(
"}
\n
"
,
fout
);
if
(
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fputs
(
"
\\
end{tabular}
\n\n
"
,
fout
);
/* count columns */
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
/* print footers */
/* print records */
for
(
i
=
0
,
ptr
=
cells
;
*
ptr
;
i
++
,
ptr
++
)
{
/* new record */
if
(
i
%
col_count
==
0
)
{
if
(
!
opt_barebones
)
{
if
(
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fprintf
(
fout
,
"
\\
multicolumn{2}{c}{Record %d}
\\\\\n
"
,
record
++
);
}
if
(
opt_border
>=
1
)
fputs
(
"
\\
hline
\n
"
,
fout
);
}
if
(
footers
&&
!
opt_barebones
)
latex_escaped_print
(
headers
[
i
%
col_count
],
fout
);
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
fputs
(
" & "
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
latex_escaped_print
(
*
ptr
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
}
}
fputc
(
'\n'
,
fout
);
if
(
opt_border
==
2
)
fputs
(
"
\\
hline
\n
"
,
fout
);
fputs
(
"
\\
end{tabular}
\n\n
"
,
fout
);
/* print footers */
if
(
footers
&&
!
opt_barebones
)
for
(
ptr
=
footers
;
*
ptr
;
ptr
++
)
{
latex_escaped_print
(
*
ptr
,
fout
);
fputs
(
"
\\\\\n
"
,
fout
);
}
fputc
(
'\n'
,
fout
);
}
}
...
@@ -761,214 +828,231 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
...
@@ -761,214 +828,231 @@ print_latex_vertical(const char * title, char ** headers, char ** cells, char **
/********************************/
/********************************/
/* Public functions
*/
/* Public functions
*/
/********************************/
/********************************/
void
void
printTable
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
printTable
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
align
,
const
char
*
align
,
const
printTableOpt
*
opt
,
FILE
*
fout
)
const
printTableOpt
*
opt
,
FILE
*
fout
)
{
{
char
*
default_footer
[]
=
{
NULL
};
char
*
default_footer
[]
=
{
NULL
};
unsigned
short
int
border
=
opt
->
border
;
unsigned
short
int
border
=
opt
->
border
;
FILE
*
pager
=
NULL
,
FILE
*
pager
=
NULL
,
*
output
;
*
output
;
if
(
opt
->
format
==
PRINT_NOTHING
)
if
(
opt
->
format
==
PRINT_NOTHING
)
return
;
return
;
if
(
!
footers
)
if
(
!
footers
)
footers
=
default_footer
;
footers
=
default_footer
;
if
(
opt
->
format
!=
PRINT_HTML
&&
border
>
2
)
if
(
opt
->
format
!=
PRINT_HTML
&&
border
>
2
)
border
=
2
;
border
=
2
;
/* check whether we need / can / are supposed to use pager */
/* check whether we need / can / are supposed to use pager */
if
(
fout
==
stdout
&&
opt
->
pager
if
(
fout
==
stdout
&&
opt
->
pager
#ifndef WIN32
#ifndef WIN32
&&
&&
isatty
(
fileno
(
stdin
))
&&
isatty
(
fileno
(
stdin
))
&&
isatty
(
fileno
(
stdout
))
isatty
(
fileno
(
stdout
))
#endif
#endif
)
)
{
{
const
char
*
pagerprog
;
const
char
*
pagerprog
;
#ifdef TIOCGWINSZ
unsigned
int
col_count
=
0
,
row_count
=
0
,
lines
;
char
**
ptr
;
int
result
;
struct
winsize
screen_size
;
/* rough estimate of columns and rows */
if
(
headers
)
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
if
(
cells
)
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
row_count
++
;
row_count
/=
col_count
;
if
(
opt
->
expanded
)
#ifdef TIOCGWINSZ
lines
=
(
col_count
+
1
)
*
row_count
;
unsigned
int
col_count
=
0
,
else
row_count
=
0
,
lines
=
row_count
+
1
;
lines
;
if
(
!
opt
->
tuples_only
)
char
**
ptr
;
lines
+=
5
;
int
result
;
struct
winsize
screen_size
;
/* rough estimate of columns and rows */
if
(
headers
)
for
(
ptr
=
headers
;
*
ptr
;
ptr
++
)
col_count
++
;
if
(
cells
)
for
(
ptr
=
cells
;
*
ptr
;
ptr
++
)
row_count
++
;
row_count
/=
col_count
;
if
(
opt
->
expanded
)
lines
=
(
col_count
+
1
)
*
row_count
;
else
lines
=
row_count
+
1
;
if
(
!
opt
->
tuples_only
)
lines
+=
5
;
result
=
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
);
result
=
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
);
if
(
result
==-
1
||
lines
>
screen_size
.
ws_row
)
{
if
(
result
==
-
1
||
lines
>
screen_size
.
ws_row
)
{
#endif
#endif
pagerprog
=
getenv
(
"PAGER"
);
pagerprog
=
getenv
(
"PAGER"
);
if
(
!
pagerprog
)
pagerprog
=
DEFAULT_PAGER
;
if
(
!
pagerprog
)
pager
=
popen
(
pagerprog
,
"w"
);
pagerprog
=
DEFAULT_PAGER
;
pager
=
popen
(
pagerprog
,
"w"
);
#ifdef TIOCGWINSZ
#ifdef TIOCGWINSZ
}
}
#endif
#endif
}
}
if
(
pager
)
{
output
=
pager
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
output
=
fout
;
/* print the stuff */
switch
(
opt
->
format
)
{
if
(
pager
)
case
PRINT_UNALIGNED
:
{
if
(
opt
->
expanded
)
output
=
pager
;
print_unaligned_vertical
(
title
,
headers
,
cells
,
footers
,
opt
->
fieldSep
,
opt
->
tuples_only
,
output
);
pqsignal
(
SIGPIPE
,
SIG_IGN
);
else
}
print_unaligned_text
(
title
,
headers
,
cells
,
footers
,
opt
->
fieldSep
,
opt
->
tuples_only
,
output
);
break
;
case
PRINT_ALIGNED
:
if
(
opt
->
expanded
)
print_aligned_vertical
(
title
,
headers
,
cells
,
footers
,
opt
->
tuples_only
,
border
,
output
);
else
print_aligned_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
break
;
case
PRINT_HTML
:
if
(
opt
->
expanded
)
print_html_vertical
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
opt
->
tableAttr
,
output
);
else
print_html_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
opt
->
tableAttr
,
output
);
break
;
case
PRINT_LATEX
:
if
(
opt
->
expanded
)
print_latex_vertical
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
else
else
print_latex_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
output
=
fout
;
break
;
default:
fprintf
(
stderr
,
"+ Oops, you shouldn't see this!
\n
"
);
/* print the stuff */
}
switch
(
opt
->
format
)
if
(
pager
)
{
{
pclose
(
pager
);
case
PRINT_UNALIGNED
:
pqsignal
(
SIGPIPE
,
SIG_DFL
);
if
(
opt
->
expanded
)
}
print_unaligned_vertical
(
title
,
headers
,
cells
,
footers
,
opt
->
fieldSep
,
opt
->
tuples_only
,
output
);
else
print_unaligned_text
(
title
,
headers
,
cells
,
footers
,
opt
->
fieldSep
,
opt
->
tuples_only
,
output
);
break
;
case
PRINT_ALIGNED
:
if
(
opt
->
expanded
)
print_aligned_vertical
(
title
,
headers
,
cells
,
footers
,
opt
->
tuples_only
,
border
,
output
);
else
print_aligned_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
break
;
case
PRINT_HTML
:
if
(
opt
->
expanded
)
print_html_vertical
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
opt
->
tableAttr
,
output
);
else
print_html_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
opt
->
tableAttr
,
output
);
break
;
case
PRINT_LATEX
:
if
(
opt
->
expanded
)
print_latex_vertical
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
else
print_latex_text
(
title
,
headers
,
cells
,
footers
,
align
,
opt
->
tuples_only
,
border
,
output
);
break
;
default:
fprintf
(
stderr
,
"+ Oops, you shouldn't see this!
\n
"
);
}
if
(
pager
)
{
pclose
(
pager
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
}
}
void
void
printQuery
(
PGresult
*
result
,
const
printQueryOpt
*
opt
,
FILE
*
fout
)
printQuery
(
PGresult
*
result
,
const
printQueryOpt
*
opt
,
FILE
*
fout
)
{
{
int
nfields
;
int
nfields
;
char
**
headers
;
char
**
headers
;
char
**
cells
;
char
**
cells
;
char
**
footers
;
char
**
footers
;
char
*
align
;
char
*
align
;
int
i
;
int
i
;
/* extract headers */
nfields
=
PQnfields
(
result
);
headers
=
calloc
(
nfields
+
1
,
sizeof
(
*
headers
));
if
(
!
headers
)
{
perror
(
"calloc"
);
exit
(
EXIT_FAILURE
);
}
/* extract headers */
for
(
i
=
0
;
i
<
nfields
;
i
++
)
headers
[
i
]
=
PQfname
(
result
,
i
);
nfields
=
PQnfields
(
result
);
/* set cells */
headers
=
calloc
(
nfields
+
1
,
sizeof
(
*
headers
));
cells
=
calloc
(
nfields
*
PQntuples
(
result
)
+
1
,
sizeof
(
*
cells
));
if
(
!
headers
)
{
if
(
!
cells
)
perror
(
"calloc"
);
{
exit
(
EXIT_FAILURE
);
perror
(
"calloc"
);
}
exit
(
EXIT_FAILURE
);
}
for
(
i
=
0
;
i
<
nfields
;
i
++
)
for
(
i
=
0
;
i
<
nfields
*
PQntuples
(
result
);
i
++
)
headers
[
i
]
=
PQfname
(
result
,
i
);
{
if
(
PQgetisnull
(
result
,
i
/
nfields
,
i
%
nfields
))
cells
[
i
]
=
opt
->
nullPrint
?
opt
->
nullPrint
:
""
;
else
cells
[
i
]
=
PQgetvalue
(
result
,
i
/
nfields
,
i
%
nfields
);
}
/* set cell
s */
/* set footer
s */
cells
=
calloc
(
nfields
*
PQntuples
(
result
)
+
1
,
sizeof
(
*
cells
));
if
(
opt
->
footers
)
if
(
!
cells
)
{
footers
=
opt
->
footers
;
perror
(
"calloc"
);
else
if
(
!
opt
->
topt
.
expanded
)
exit
(
EXIT_FAILURE
);
{
}
footers
=
calloc
(
2
,
sizeof
(
*
footers
));
if
(
!
footers
)
{
perror
(
"calloc"
);
exit
(
EXIT_FAILURE
);
}
for
(
i
=
0
;
i
<
nfields
*
PQntuples
(
result
);
i
++
)
{
footers
[
0
]
=
malloc
(
100
);
if
(
PQgetisnull
(
result
,
i
/
nfields
,
i
%
nfields
))
if
(
PQntuples
(
result
)
==
1
)
cells
[
i
]
=
opt
->
nullPrint
?
opt
->
nullPrint
:
""
;
strcpy
(
footers
[
0
],
"(1 row)"
);
else
sprintf
(
footers
[
0
],
"(%d rows)"
,
PQntuples
(
result
));
}
else
else
cells
[
i
]
=
PQgetvalue
(
result
,
i
/
nfields
,
i
%
nfields
);
footers
=
NULL
;
}
/* set footers
*/
/* set alignment
*/
if
(
opt
->
footers
)
align
=
calloc
(
nfields
+
1
,
sizeof
(
*
align
));
footers
=
opt
->
footers
;
if
(
!
align
)
else
if
(
!
opt
->
topt
.
expanded
)
{
{
footers
=
calloc
(
2
,
sizeof
(
*
footers
));
perror
(
"calloc"
);
if
(
!
footers
)
{
exit
(
EXIT_FAILURE
);
perror
(
"calloc"
);
exit
(
EXIT_FAILURE
);
}
}
footers
[
0
]
=
malloc
(
100
);
for
(
i
=
0
;
i
<
nfields
;
i
++
)
if
(
PQntuples
(
result
)
==
1
)
{
strcpy
(
footers
[
0
],
"(1 row)"
);
Oid
ftype
=
PQftype
(
result
,
i
);
else
sprintf
(
footers
[
0
],
"(%d rows)"
,
PQntuples
(
result
));
if
(
ftype
==
20
||
/* int8 */
}
ftype
==
21
||
/* int2 */
else
ftype
==
23
||
/* int4 */
footers
=
NULL
;
(
ftype
>=
26
&&
ftype
<=
30
)
||
/* ?id */
ftype
==
700
||
/* float4 */
/* set alignment */
ftype
==
701
||
/* float8 */
ftype
==
790
||
/* money */
align
=
calloc
(
nfields
+
1
,
sizeof
(
*
align
));
ftype
==
1700
/* numeric */
if
(
!
align
)
{
)
perror
(
"calloc"
);
align
[
i
]
=
'r'
;
exit
(
EXIT_FAILURE
);
else
}
align
[
i
]
=
'l'
;
}
for
(
i
=
0
;
i
<
nfields
;
i
++
)
{
Oid
ftype
=
PQftype
(
result
,
i
);
if
(
ftype
==
20
||
/* int8 */
ftype
==
21
||
/* int2 */
ftype
==
23
||
/* int4 */
(
ftype
>=
26
&&
ftype
<=
30
)
||
/* ?id */
ftype
==
700
||
/* float4 */
ftype
==
701
||
/* float8 */
ftype
==
790
||
/* money */
ftype
==
1700
/* numeric */
)
align
[
i
]
=
'r'
;
else
align
[
i
]
=
'l'
;
}
/* call table printer */
/* call table printer */
printTable
(
opt
->
title
,
headers
,
cells
,
footers
?
footers
:
opt
->
footers
,
align
,
printTable
(
opt
->
title
,
headers
,
cells
,
footers
?
footers
:
opt
->
footers
,
align
,
&
opt
->
topt
,
fout
);
&
opt
->
topt
,
fout
);
free
(
headers
);
free
(
headers
);
free
(
cells
);
free
(
cells
);
if
(
footers
)
{
if
(
footers
)
free
(
footers
[
0
]);
{
free
(
footers
);
free
(
footers
[
0
]);
}
free
(
footers
);
}
}
}
...
...
src/bin/psql/print.h
View file @
0e6652e6
...
@@ -7,52 +7,58 @@
...
@@ -7,52 +7,58 @@
#include <stdio.h>
#include <stdio.h>
#include <libpq-fe.h>
#include <libpq-fe.h>
enum
printFormat
{
enum
printFormat
PRINT_NOTHING
=
0
,
/* to make sure someone initializes this */
{
PRINT_UNALIGNED
,
PRINT_NOTHING
=
0
,
/* to make sure someone initializes this */
PRINT_ALIGNED
,
PRINT_UNALIGNED
,
PRINT_HTML
,
PRINT_ALIGNED
,
PRINT_LATEX
PRINT_HTML
,
/* add your favourite output format here ... */
PRINT_LATEX
/* add your favourite output format here ... */
};
};
typedef
struct
_printTableOpt
{
typedef
struct
_printTableOpt
enum
printFormat
format
;
/* one of the above */
{
bool
expanded
;
/* expanded/vertical output (if supported by output format) */
enum
printFormat
format
;
/* one of the above */
bool
pager
;
/* use pager for output (if to stdout and stdout is a tty) */
bool
expanded
;
/* expanded/vertical output (if supported
bool
tuples_only
;
/* don't output headers, row counts, etc. */
* by output format) */
unsigned
short
int
border
;
/* Print a border around the table. 0=none, 1=dividing lines, 2=full */
bool
pager
;
/* use pager for output (if to stdout and
char
*
fieldSep
;
/* field separator for unaligned text mode */
* stdout is a tty) */
char
*
tableAttr
;
/* attributes for HTML <table ...> */
bool
tuples_only
;
/* don't output headers, row counts, etc. */
}
printTableOpt
;
unsigned
short
int
border
;
/* Print a border around the table.
* 0=none, 1=dividing lines, 2=full */
char
*
fieldSep
;
/* field separator for unaligned text mode */
char
*
tableAttr
;
/* attributes for HTML <table ...> */
}
printTableOpt
;
/*
/*
* Use this to print just any table in the supported formats.
* Use this to print just any table in the supported formats.
* - title is just any string (NULL is fine)
* - title is just any string (NULL is fine)
* - headers is the column headings (NULL ptr terminated). It must be given and
* - headers is the column headings (NULL ptr terminated). It must be given and
*
complete since the column count is generated from this.
*
complete since the column count is generated from this.
* - cells are the data cells to be printed. Now you know why the correct
* - cells are the data cells to be printed. Now you know why the correct
*
column count is important
*
column count is important
* - footers are lines to be printed below the table
* - footers are lines to be printed below the table
* - align is an 'l' or an 'r' for every column, if the output format needs it.
* - align is an 'l' or an 'r' for every column, if the output format needs it.
*
(You must specify this long enough. Otherwise anything could happen.)
*
(You must specify this long enough. Otherwise anything could happen.)
*/
*/
void
void
printTable
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
printTable
(
const
char
*
title
,
char
**
headers
,
char
**
cells
,
char
**
footers
,
const
char
*
align
,
const
char
*
align
,
const
printTableOpt
*
opt
,
FILE
*
fout
);
const
printTableOpt
*
opt
,
FILE
*
fout
);
typedef
struct
_printQueryOpt
{
typedef
struct
_printQueryOpt
printTableOpt
topt
;
/* the options above */
{
char
*
nullPrint
;
/* how to print null entities */
printTableOpt
topt
;
/* the options above */
bool
quote
;
/* quote all values as much as possible */
char
*
nullPrint
;
/* how to print null entities */
char
*
title
;
/* override title */
bool
quote
;
/* quote all values as much as possible */
char
**
footers
;
/* override footer (default is "(xx rows)") */
char
*
title
;
/* override title */
}
printQueryOpt
;
char
**
footers
;
/* override footer (default is "(xx
* rows)") */
}
printQueryOpt
;
/*
/*
* Use this to print query results
* Use this to print query results
...
@@ -60,7 +66,7 @@ typedef struct _printQueryOpt {
...
@@ -60,7 +66,7 @@ typedef struct _printQueryOpt {
* It calls the printTable above with all the things set straight.
* It calls the printTable above with all the things set straight.
*/
*/
void
void
printQuery
(
PGresult
*
result
,
const
printQueryOpt
*
opt
,
FILE
*
fout
);
printQuery
(
PGresult
*
result
,
const
printQueryOpt
*
opt
,
FILE
*
fout
);
#endif
/* PRINT_H */
#endif
/* PRINT_H */
src/bin/psql/prompt.c
View file @
0e6652e6
...
@@ -34,20 +34,20 @@
...
@@ -34,20 +34,20 @@
* %~ - like %/ but "~" when database name equals user name
* %~ - like %/ but "~" when database name equals user name
* %# - "#" if the username is postgres, ">" otherwise
* %# - "#" if the username is postgres, ">" otherwise
* %R - in prompt1 normally =, or ^ if single line mode,
* %R - in prompt1 normally =, or ^ if single line mode,
*
or a ! if session is not connected to a database;
*
or a ! if session is not connected to a database;
*
in prompt2 -, *, ', or ";
*
in prompt2 -, *, ', or ";
*
in prompt3 nothing
*
in prompt3 nothing
* %? - the error code of the last query (not yet implemented)
* %? - the error code of the last query (not yet implemented)
* %% - a percent sign
* %% - a percent sign
*
*
* %[0-9]
- the character with the given decimal code
* %[0-9]
- the character with the given decimal code
* %0[0-7]
- the character with the given octal code
* %0[0-7]
- the character with the given octal code
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
*
*
* %`command`
- The result of executing command in /bin/sh with trailing
* %`command`
- The result of executing command in /bin/sh with trailing
*
newline stripped.
*
newline stripped.
* %$name$
- The value of the psql/environment/magic varible 'name'
* %$name$
- The value of the psql/environment/magic varible 'name'
*
(same rules as for, e.g., \echo $foo)
*
(same rules as for, e.g., \echo $foo)
* (those will not be rescanned for more escape sequences!)
* (those will not be rescanned for more escape sequences!)
*
*
*
*
...
@@ -60,197 +60,214 @@ const char *
...
@@ -60,197 +60,214 @@ const char *
get_prompt
(
PsqlSettings
*
pset
,
promptStatus_t
status
)
get_prompt
(
PsqlSettings
*
pset
,
promptStatus_t
status
)
{
{
#define MAX_PROMPT_SIZE 256
#define MAX_PROMPT_SIZE 256
static
char
destination
[
MAX_PROMPT_SIZE
+
1
];
static
char
destination
[
MAX_PROMPT_SIZE
+
1
];
char
buf
[
MAX_PROMPT_SIZE
+
1
];
char
buf
[
MAX_PROMPT_SIZE
+
1
];
bool
esc
=
false
;
bool
esc
=
false
;
const
char
*
p
;
const
char
*
p
;
const
char
*
prompt_string
;
const
char
*
prompt_string
;
if
(
GetVariable
(
pset
->
vars
,
"quiet"
))
if
(
GetVariable
(
pset
->
vars
,
"quiet"
))
return
""
;
return
""
;
if
(
status
==
PROMPT_READY
)
if
(
status
==
PROMPT_READY
)
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt1"
);
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt1"
);
else
if
(
status
==
PROMPT_CONTINUE
||
status
==
PROMPT_SINGLEQUOTE
||
status
==
PROMPT_DOUBLEQUOTE
||
status
==
PROMPT_COMMENT
)
else
if
(
status
==
PROMPT_CONTINUE
||
status
==
PROMPT_SINGLEQUOTE
||
status
==
PROMPT_DOUBLEQUOTE
||
status
==
PROMPT_COMMENT
)
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt2"
);
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt2"
);
else
if
(
status
==
PROMPT_COPY
)
else
if
(
status
==
PROMPT_COPY
)
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt3"
);
prompt_string
=
GetVariable
(
pset
->
vars
,
"prompt3"
);
else
else
prompt_string
=
"? "
;
prompt_string
=
"? "
;
destination
[
0
]
=
'\0'
;
destination
[
0
]
=
'\0'
;
for
(
p
=
prompt_string
;
for
(
p
=
prompt_string
;
p
&&
*
p
&&
strlen
(
destination
)
<
MAX_PROMPT_SIZE
;
p
&&
*
p
&&
strlen
(
destination
)
<
MAX_PROMPT_SIZE
;
p
++
)
p
++
)
{
MemSet
(
buf
,
0
,
MAX_PROMPT_SIZE
+
1
);
if
(
esc
)
{
{
switch
(
*
p
)
MemSet
(
buf
,
0
,
MAX_PROMPT_SIZE
+
1
);
{
if
(
esc
)
case
'%'
:
{
strcpy
(
buf
,
"%"
);
switch
(
*
p
)
break
;
{
case
'%'
:
strcpy
(
buf
,
"%"
);
break
;
/* Current database */
/* Current database */
case
'/'
:
case
'/'
:
if
(
pset
->
db
)
if
(
pset
->
db
)
strncpy
(
buf
,
PQdb
(
pset
->
db
),
MAX_PROMPT_SIZE
);
strncpy
(
buf
,
PQdb
(
pset
->
db
),
MAX_PROMPT_SIZE
);
break
;
break
;
case
'~'
:
{
case
'~'
:
const
char
*
var
;
{
if
(
pset
->
db
)
{
const
char
*
var
;
if
(
strcmp
(
PQdb
(
pset
->
db
),
PQuser
(
pset
->
db
))
==
0
||
(
(
var
=
getenv
(
"PGDATABASE"
))
&&
strcmp
(
var
,
PQdb
(
pset
->
db
))
==
0
)
)
strcpy
(
buf
,
"~"
);
else
strncpy
(
buf
,
PQdb
(
pset
->
db
),
MAX_PROMPT_SIZE
);
}
break
;
}
/* DB server hostname (long/short) */
case
'M'
:
case
'm'
:
if
(
pset
->
db
)
{
if
(
PQhost
(
pset
->
db
))
{
strncpy
(
buf
,
PQhost
(
pset
->
db
),
MAX_PROMPT_SIZE
);
if
(
*
p
==
'm'
)
buf
[
strcspn
(
buf
,
"."
)]
=
'\0'
;
}
else
buf
[
0
]
=
'.'
;
}
break
;
/* DB server port number */
case
'>'
:
if
(
pset
->
db
)
{
if
(
PQhost
(
pset
->
db
))
strncpy
(
buf
,
PQport
(
pset
->
db
),
MAX_PROMPT_SIZE
);
else
buf
[
0
]
=
'.'
;
}
break
;
/* DB server user name */
case
'n'
:
if
(
pset
->
db
)
strncpy
(
buf
,
PQuser
(
pset
->
db
),
MAX_PROMPT_SIZE
);
break
;
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
if
(
pset
->
db
)
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
{
{
if
(
strcmp
(
PQdb
(
pset
->
db
),
PQuser
(
pset
->
db
))
==
0
||
long
int
l
;
((
var
=
getenv
(
"PGDATABASE"
))
&&
strcmp
(
var
,
PQdb
(
pset
->
db
))
==
0
))
char
*
end
;
strcpy
(
buf
,
"~"
);
l
=
strtol
(
p
,
&
end
,
0
);
else
sprintf
(
buf
,
"%c"
,
(
unsigned
char
)
l
);
strncpy
(
buf
,
PQdb
(
pset
->
db
),
MAX_PROMPT_SIZE
);
p
=
end
-
1
;
}
break
;
break
;
}
}
/* DB server hostname (long/short) */
case
'M'
:
case
'm'
:
if
(
pset
->
db
)
{
if
(
PQhost
(
pset
->
db
))
{
strncpy
(
buf
,
PQhost
(
pset
->
db
),
MAX_PROMPT_SIZE
);
if
(
*
p
==
'm'
)
buf
[
strcspn
(
buf
,
"."
)]
=
'\0'
;
}
else
buf
[
0
]
=
'.'
;
}
break
;
/* DB server port number */
case
'>'
:
if
(
pset
->
db
)
{
if
(
PQhost
(
pset
->
db
))
strncpy
(
buf
,
PQport
(
pset
->
db
),
MAX_PROMPT_SIZE
);
else
buf
[
0
]
=
'.'
;
}
break
;
/* DB server user name */
case
'n'
:
if
(
pset
->
db
)
strncpy
(
buf
,
PQuser
(
pset
->
db
),
MAX_PROMPT_SIZE
);
break
;
case
'R'
:
case
'0'
:
switch
(
status
)
{
case
'1'
:
case
PROMPT_READY
:
case
'2'
:
if
(
!
pset
->
db
)
case
'3'
:
buf
[
0
]
=
'!'
;
case
'4'
:
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"singleline"
))
case
'5'
:
buf
[
0
]
=
'='
;
case
'6'
:
else
case
'7'
:
buf
[
0
]
=
'^'
;
case
'8'
:
break
;
case
'9'
:
case
PROMPT_CONTINUE
:
{
buf
[
0
]
=
'-'
;
long
int
l
;
break
;
char
*
end
;
case
PROMPT_SINGLEQUOTE
:
buf
[
0
]
=
'\''
;
break
;
case
PROMPT_DOUBLEQUOTE
:
buf
[
0
]
=
'"'
;
break
;
case
PROMPT_COMMENT
:
buf
[
0
]
=
'*'
;
break
;
default:
buf
[
0
]
=
'\0'
;
break
;
}
case
'?'
:
l
=
strtol
(
p
,
&
end
,
0
);
/* not here yet */
sprintf
(
buf
,
"%c"
,
(
unsigned
char
)
l
);
break
;
p
=
end
-
1
;
break
;
}
case
'#'
:
case
'R'
:
{
switch
(
status
)
if
(
pset
->
db
&&
strcmp
(
PQuser
(
pset
->
db
),
"postgres"
)
==
0
)
{
buf
[
0
]
=
'#'
;
case
PROMPT_READY
:
else
if
(
!
pset
->
db
)
buf
[
0
]
=
'>'
;
buf
[
0
]
=
'!'
;
else
if
(
!
GetVariableBool
(
pset
->
vars
,
"singleline"
))
break
;
buf
[
0
]
=
'='
;
}
else
buf
[
0
]
=
'^'
;
break
;
case
PROMPT_CONTINUE
:
buf
[
0
]
=
'-'
;
break
;
case
PROMPT_SINGLEQUOTE
:
buf
[
0
]
=
'\''
;
break
;
case
PROMPT_DOUBLEQUOTE
:
buf
[
0
]
=
'"'
;
break
;
case
PROMPT_COMMENT
:
buf
[
0
]
=
'*'
;
break
;
default:
buf
[
0
]
=
'\0'
;
break
;
}
/* execute command */
case
'?'
:
case
'`'
:
/* not here yet */
{
break
;
FILE
*
fd
=
NULL
;
char
*
file
=
strdup
(
p
+
1
);
int
cmdend
;
cmdend
=
strcspn
(
file
,
"`"
);
file
[
cmdend
]
=
'\0'
;
if
(
file
)
fd
=
popen
(
file
,
"r"
);
if
(
fd
)
{
fgets
(
buf
,
MAX_PROMPT_SIZE
-
1
,
fd
);
pclose
(
fd
);
}
if
(
buf
[
strlen
(
buf
)
-
1
]
==
'\n'
)
buf
[
strlen
(
buf
)
-
1
]
=
'\0'
;
free
(
file
);
p
+=
cmdend
+
1
;
break
;
}
/* interpolate variable */
case
'#'
:
case
'$'
:
{
{
if
(
pset
->
db
&&
strcmp
(
PQuser
(
pset
->
db
),
"postgres"
)
==
0
)
char
*
name
;
buf
[
0
]
=
'#'
;
const
char
*
val
;
else
int
nameend
;
buf
[
0
]
=
'>'
;
name
=
strdup
(
p
+
1
);
nameend
=
strcspn
(
name
,
"$"
);
name
[
nameend
]
=
'\0'
;
val
=
interpolate_var
(
name
,
pset
);
if
(
val
)
strncpy
(
buf
,
val
,
MAX_PROMPT_SIZE
);
free
(
name
);
p
+=
nameend
+
1
;
break
;
}
default:
break
;
buf
[
0
]
=
*
p
;
}
buf
[
1
]
=
'\0'
;
}
/* execute command */
esc
=
false
;
case
'`'
:
}
{
else
if
(
*
p
==
'%'
)
FILE
*
fd
=
NULL
;
esc
=
true
;
char
*
file
=
strdup
(
p
+
1
);
else
int
cmdend
;
{
buf
[
0
]
=
*
p
;
buf
[
1
]
=
'\0'
;
esc
=
false
;
}
if
(
!
esc
)
{
cmdend
=
strcspn
(
file
,
"`"
);
strncat
(
destination
,
buf
,
MAX_PROMPT_SIZE
-
strlen
(
destination
));
file
[
cmdend
]
=
'\0'
;
if
(
file
)
fd
=
popen
(
file
,
"r"
);
if
(
fd
)
{
fgets
(
buf
,
MAX_PROMPT_SIZE
-
1
,
fd
);
pclose
(
fd
);
}
if
(
buf
[
strlen
(
buf
)
-
1
]
==
'\n'
)
buf
[
strlen
(
buf
)
-
1
]
=
'\0'
;
free
(
file
);
p
+=
cmdend
+
1
;
break
;
}
/* interpolate variable */
case
'$'
:
{
char
*
name
;
const
char
*
val
;
int
nameend
;
name
=
strdup
(
p
+
1
);
nameend
=
strcspn
(
name
,
"$"
);
name
[
nameend
]
=
'\0'
;
val
=
interpolate_var
(
name
,
pset
);
if
(
val
)
strncpy
(
buf
,
val
,
MAX_PROMPT_SIZE
);
free
(
name
);
p
+=
nameend
+
1
;
break
;
}
default:
buf
[
0
]
=
*
p
;
buf
[
1
]
=
'\0'
;
}
esc
=
false
;
}
else
if
(
*
p
==
'%'
)
esc
=
true
;
else
{
buf
[
0
]
=
*
p
;
buf
[
1
]
=
'\0'
;
esc
=
false
;
}
if
(
!
esc
)
strncat
(
destination
,
buf
,
MAX_PROMPT_SIZE
-
strlen
(
destination
));
}
}
}
destination
[
MAX_PROMPT_SIZE
]
=
'\0'
;
destination
[
MAX_PROMPT_SIZE
]
=
'\0'
;
return
destination
;
return
destination
;
}
}
src/bin/psql/prompt.h
View file @
0e6652e6
...
@@ -3,17 +3,18 @@
...
@@ -3,17 +3,18 @@
#include "settings.h"
#include "settings.h"
typedef
enum
_promptStatus
{
typedef
enum
_promptStatus
PROMPT_READY
,
{
PROMPT_CONTINUE
,
PROMPT_READY
,
PROMPT_COMMENT
,
PROMPT_CONTINUE
,
PROMPT_SINGLEQUOTE
,
PROMPT_COMMENT
,
PROMPT_DOUBLEQUOTE
,
PROMPT_SINGLEQUOTE
,
PROMPT_COPY
PROMPT_DOUBLEQUOTE
,
}
promptStatus_t
;
PROMPT_COPY
}
promptStatus_t
;
const
char
*
const
char
*
get_prompt
(
PsqlSettings
*
pset
,
promptStatus_t
status
);
get_prompt
(
PsqlSettings
*
pset
,
promptStatus_t
status
);
#endif
/* PROMPT_H */
#endif
/* PROMPT_H */
src/bin/psql/psql.c
deleted
100644 → 0
View file @
2323b636
/*-------------------------------------------------------------------------
*
* psql.c
* an interactive front-end to postgreSQL
*
* Copyright (c) 1996, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.195 1999/10/26 04:40:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include "postgres.h"
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "pqsignal.h"
#include "stringutils.h"
#include "psqlHelp.h"
#ifndef HAVE_STRDUP
#include "strdup.h"
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#ifdef HAVE_LIBREADLINE
#ifdef HAVE_READLINE_H
#include <readline.h>
#define USE_READLINE 1
#if defined(HAVE_HISTORY_H)
#include <history.h>
#define USE_HISTORY 1
#endif
#else
#if defined(HAVE_READLINE_READLINE_H)
#include <readline/readline.h>
#define USE_READLINE 1
#if defined(HAVE_READLINE_HISTORY_H)
#include <readline/history.h>
#define USE_HISTORY 1
#endif
#endif
#endif
#if defined(HAVE_HISTORY) && !defined(USE_HISTORY)
#define USE_HISTORY 1
#endif
#endif
#ifdef WIN32
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#define open(x,y,z) _open(x,y,z)
#define strcasecmp(x,y) stricmp(x,y)
#define pqsignal(x,y)
#define R_OK 0
/* getopt is not in the standard includes on Win32 */
extern
char
*
optarg
;
extern
int
optind
,
opterr
,
optopt
;
int
getopt
(
int
,
char
*
const
[],
const
char
*
);
char
*
__progname
=
"psql"
;
#endif
#ifdef MULTIBYTE
/* flag to indicate if PGCLIENTENCODING has been set by a user */
static
char
*
has_client_encoding
=
0
;
#endif
/* This prompt string is assumed to have at least 3 characters by code in MainLoop().
* A character two characters from the end is replaced each time by a mode character.
*/
#define PROMPT "=> "
#define PROMPT_READY '='
#define PROMPT_CONTINUE '-'
#define PROMPT_COMMENT '*'
#define PROMPT_SINGLEQUOTE '\''
#define PROMPT_DOUBLEQUOTE '"'
/* Backslash command handling:
* 0 - send currently constructed query to backend (i.e. we got a \g)
* 1 - skip processing of this line, continue building up query
* 2 - terminate processing (i.e. we got a \q)
* 3 - new query supplied by edit
*/
#define CMD_UNKNOWN -1
#define CMD_SEND 0
#define CMD_SKIP_LINE 1
#define CMD_TERMINATE 2
#define CMD_NEWEDIT 3
#define COPYBUFSIZ 8192
#define DEFAULT_FIELD_SEP "|"
#define DEFAULT_EDITOR "vi"
#define DEFAULT_SHELL "/bin/sh"
typedef
struct
_psqlSettings
{
PGconn
*
db
;
/* connection to backend */
FILE
*
queryFout
;
/* where to send the query results */
PQprintOpt
opt
;
/* options to be passed to PQprint */
char
*
prompt
;
/* prompt to display */
char
*
gfname
;
/* one-shot file output argument for \g */
bool
notty
;
/* input or output is not a tty */
bool
pipe
;
/* queryFout is from a popen() */
bool
echoQuery
;
/* echo the query before sending it */
bool
echoAllQueries
;
/* echo all queries before sending it */
bool
quiet
;
/* run quietly, no messages, no promt */
bool
singleStep
;
/* prompt before for each query */
bool
singleLineMode
;
/* query terminated by newline */
bool
useReadline
;
/* use libreadline routines */
bool
getPassword
;
/* prompt the user for a username and
* password */
}
PsqlSettings
;
/*
* cur_cmd_source and cur_cmd_interactive are the top of a stack of
* source files (one stack level per recursive invocation of MainLoop).
* It's kinda grotty to make these global variables, but the alternative
* of passing them around through many function parameter lists seems
* worse.
*/
static
FILE
*
cur_cmd_source
=
NULL
;
/* current source of command input */
static
bool
cur_cmd_interactive
=
false
;
/* is it an interactive
* source? */
#ifdef TIOCGWINSZ
struct
winsize
screen_size
;
#else
struct
winsize
{
int
ws_row
;
int
ws_col
;
}
screen_size
;
#endif
/* declarations for functions in this file */
static
void
usage
(
char
*
progname
);
static
void
slashUsage
();
static
bool
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
);
static
bool
handleCopyIn
(
PGconn
*
conn
,
const
bool
mustprompt
,
FILE
*
copystream
);
static
int
tableList
(
PsqlSettings
*
pset
,
bool
deep_tablelist
,
char
info_type
,
bool
system_tables
);
static
int
tableDesc
(
PsqlSettings
*
pset
,
char
*
table
,
FILE
*
fout
);
static
int
objectDescription
(
PsqlSettings
*
pset
,
char
*
object
);
static
int
rightsList
(
PsqlSettings
*
pset
);
static
void
emitNtimes
(
FILE
*
fout
,
const
char
*
str
,
int
N
);
static
void
prompt_for_password
(
char
*
username
,
char
*
password
);
static
char
*
gets_noreadline
(
char
*
prompt
,
FILE
*
source
);
static
char
*
gets_readline
(
char
*
prompt
,
FILE
*
source
);
static
char
*
gets_fromFile
(
char
*
prompt
,
FILE
*
source
);
static
int
listAllDbs
(
PsqlSettings
*
pset
);
static
bool
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
,
FILE
*
copy_in_stream
,
FILE
*
copy_out_stream
);
static
int
HandleSlashCmds
(
PsqlSettings
*
pset
,
char
*
line
,
PQExpBuffer
query_buf
);
static
int
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
);
static
FILE
*
setFout
(
PsqlSettings
*
pset
,
char
*
fname
);
static
char
*
selectVersion
(
PsqlSettings
*
pset
);
/*
* usage print out usage for command line arguments
*/
static
void
usage
(
char
*
progname
)
{
fprintf
(
stderr
,
"Usage: %s [options] [dbname]
\n
"
,
progname
);
fprintf
(
stderr
,
"
\t
-a authsvc set authentication service
\n
"
);
fprintf
(
stderr
,
"
\t
-A turn off alignment when printing out attributes
\n
"
);
fprintf
(
stderr
,
"
\t
-c query run single query (slash commands too)
\n
"
);
fprintf
(
stderr
,
"
\t
-d dbName specify database name
\n
"
);
fprintf
(
stderr
,
"
\t
-e echo the query sent to the backend
\n
"
);
fprintf
(
stderr
,
"
\t
-E echo all queries sent to the backend
\n
"
);
fprintf
(
stderr
,
"
\t
-f filename use file as a source of queries
\n
"
);
fprintf
(
stderr
,
"
\t
-F sep set the field separator (default is '|')
\n
"
);
fprintf
(
stderr
,
"
\t
-h host set database server host
\n
"
);
fprintf
(
stderr
,
"
\t
-H turn on html3.0 table output
\n
"
);
fprintf
(
stderr
,
"
\t
-l list available databases
\n
"
);
fprintf
(
stderr
,
"
\t
-n don't use readline library
\n
"
);
fprintf
(
stderr
,
"
\t
-o filename send output to filename or (|pipe)
\n
"
);
fprintf
(
stderr
,
"
\t
-p port set port number
\n
"
);
fprintf
(
stderr
,
"
\t
-q run quietly (no messages, no prompts)
\n
"
);
fprintf
(
stderr
,
"
\t
-s single step mode (prompts for each query)
\n
"
);
fprintf
(
stderr
,
"
\t
-S single line mode (i.e. query terminated by newline)
\n
"
);
fprintf
(
stderr
,
"
\t
-t turn off printing of headings and row count
\n
"
);
fprintf
(
stderr
,
"
\t
-T html set html3.0 table command options (cf. -H)
\n
"
);
fprintf
(
stderr
,
"
\t
-u ask for a username and password for authentication
\n
"
);
fprintf
(
stderr
,
"
\t
-x turn on expanded output (field names on left)
\n
"
);
exit
(
1
);
}
/*
* slashUsage print out usage for the backslash commands
*/
static
char
*
on
(
bool
f
)
{
return
f
?
"on"
:
"off"
;
}
static
void
slashUsage
(
PsqlSettings
*
pset
)
{
int
usePipe
=
0
;
char
*
pagerenv
;
FILE
*
fout
;
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
{
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
}
#endif
if
(
pset
->
notty
==
0
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
(
pagerenv
[
0
]
!=
'\0'
)
&&
screen_size
.
ws_row
<=
35
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
usePipe
=
1
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
fout
=
stdout
;
/* if you add/remove a line here, change the row test above */
fprintf
(
fout
,
"
\\
? -- help
\n
"
);
fprintf
(
fout
,
"
\\
a -- toggle field-alignment (currently %s)
\n
"
,
on
(
pset
->
opt
.
align
));
fprintf
(
fout
,
"
\\
C [<captn>] -- set html3 caption (currently '%s')
\n
"
,
pset
->
opt
.
caption
?
pset
->
opt
.
caption
:
""
);
fprintf
(
fout
,
"
\\
connect <dbname|-> <user> -- connect to new database (currently '%s')
\n
"
,
PQdb
(
pset
->
db
));
fprintf
(
fout
,
"
\\
copy table {from | to} <fname>
\n
"
);
fprintf
(
fout
,
"
\\
d [<table>] -- list tables and indices, columns in <table>, or * for all
\n
"
);
fprintf
(
fout
,
"
\\
da -- list aggregates
\n
"
);
fprintf
(
fout
,
"
\\
dd [<object>]- list comment an object.
\n
"
);
fprintf
(
fout
,
"
\\
df -- list functions
\n
"
);
fprintf
(
fout
,
"
\\
di -- list only indices
\n
"
);
fprintf
(
fout
,
"
\\
do -- list operators
\n
"
);
fprintf
(
fout
,
"
\\
ds -- list only sequences
\n
"
);
fprintf
(
fout
,
"
\\
dS -- list system tables and indexes
\n
"
);
fprintf
(
fout
,
"
\\
dt -- list only tables
\n
"
);
fprintf
(
fout
,
"
\\
dT -- list types
\n
"
);
fprintf
(
fout
,
"
\\
e [<fname>] -- edit the current query buffer or <fname>
\n
"
);
fprintf
(
fout
,
"
\\
E [<fname>] -- edit the current query buffer or <fname>, and execute
\n
"
);
fprintf
(
fout
,
"
\\
f [<sep>] -- change field separater (currently '%s')
\n
"
,
pset
->
opt
.
fieldSep
);
fprintf
(
fout
,
"
\\
g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]
\n
"
);
fprintf
(
fout
,
"
\\
h [<cmd>] -- help on syntax of sql commands, * for all commands
\n
"
);
fprintf
(
fout
,
"
\\
H -- toggle html3 output (currently %s)
\n
"
,
on
(
pset
->
opt
.
html3
));
fprintf
(
fout
,
"
\\
i <fname> -- read and execute queries from filename
\n
"
);
fprintf
(
fout
,
"
\\
l -- list all databases
\n
"
);
fprintf
(
fout
,
"
\\
m -- toggle monitor-like table display (currently %s)
\n
"
,
on
(
pset
->
opt
.
standard
));
fprintf
(
fout
,
"
\\
o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe
\n
"
);
fprintf
(
fout
,
"
\\
p -- print the current query buffer
\n
"
);
fprintf
(
fout
,
"
\\
q -- quit
\n
"
);
fprintf
(
fout
,
"
\\
r -- reset(clear) the query buffer
\n
"
);
fprintf
(
fout
,
"
\\
s [<fname>] -- print history or save it in <fname>
\n
"
);
fprintf
(
fout
,
"
\\
t -- toggle table headings and row count (currently %s)
\n
"
,
on
(
pset
->
opt
.
header
));
fprintf
(
fout
,
"
\\
T [<html>] -- set html3.0 <table ...> options (currently '%s')
\n
"
,
pset
->
opt
.
tableOpt
?
pset
->
opt
.
tableOpt
:
""
);
fprintf
(
fout
,
"
\\
x -- toggle expanded output (currently %s)
\n
"
,
on
(
pset
->
opt
.
expanded
));
fprintf
(
fout
,
"
\\
w <fname> -- write current buffer to a file
\n
"
);
fprintf
(
fout
,
"
\\
z -- list current grant/revoke permissions
\n
"
);
fprintf
(
fout
,
"
\\
! [<cmd>] -- shell escape or command
\n
"
);
if
(
usePipe
)
{
pclose
(
fout
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
}
static
PGresult
*
PSQLexec
(
PsqlSettings
*
pset
,
char
*
query
)
{
PGresult
*
res
;
if
(
pset
->
echoAllQueries
)
{
fprintf
(
stderr
,
"QUERY: %s
\n
"
,
query
);
fprintf
(
stderr
,
"
\n
"
);
fflush
(
stderr
);
}
res
=
PQexec
(
pset
->
db
,
query
);
if
(
!
res
)
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
else
{
if
(
PQresultStatus
(
res
)
==
PGRES_COMMAND_OK
||
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
)
return
res
;
if
(
!
pset
->
quiet
)
fputs
(
PQerrorMessage
(
pset
->
db
),
stderr
);
PQclear
(
res
);
}
return
NULL
;
}
/*
* Code to support command cancellation.
* If interactive, we enable a SIGINT signal catcher that sends
* a cancel request to the backend.
* Note that sending the cancel directly from the signal handler
* is safe only because PQrequestCancel is carefully written to
* make it so. We have to be very careful what else we do in the
* signal handler.
* Writing on stderr is potentially dangerous, if the signal interrupted
* some stdio operation on stderr. On Unix we can avoid trouble by using
* write() instead; on Windows that's probably not workable, but we can
* at least avoid trusting printf by using the more primitive fputs.
*/
static
PGconn
*
cancelConn
=
NULL
;
/* connection to try cancel on */
static
void
safe_write_stderr
(
const
char
*
s
)
{
#ifdef WIN32
fputs
(
s
,
stderr
);
#else
write
(
fileno
(
stderr
),
s
,
strlen
(
s
));
#endif
}
static
void
handle_sigint
(
SIGNAL_ARGS
)
{
if
(
cancelConn
==
NULL
)
exit
(
1
);
/* accept signal if no connection */
/* Try to send cancel request */
if
(
PQrequestCancel
(
cancelConn
))
safe_write_stderr
(
"
\n
CANCEL request sent
\n
"
);
else
{
safe_write_stderr
(
"
\n
Cannot send cancel request:
\n
"
);
safe_write_stderr
(
PQerrorMessage
(
cancelConn
));
}
}
/*
* listAllDbs
*
* list all the databases in the system returns 0 if all went well
*
*
*/
static
int
listAllDbs
(
PsqlSettings
*
pset
)
{
PGresult
*
results
;
char
*
query
=
"select * from pg_database;"
;
if
(
!
(
results
=
PSQLexec
(
pset
,
query
)))
return
1
;
else
{
PQprint
(
pset
->
queryFout
,
results
,
&
pset
->
opt
);
PQclear
(
results
);
return
0
;
}
}
/*
* List The Database Tables returns 0 if all went well
*
*/
static
int
tableList
(
PsqlSettings
*
pset
,
bool
deep_tablelist
,
char
info_type
,
bool
system_tables
)
{
char
listbuf
[
512
];
int
nColumns
;
int
i
;
char
*
rk
;
char
*
rr
;
PGresult
*
res
;
int
usePipe
=
0
;
bool
haveIndexes
=
false
;
char
*
pagerenv
;
FILE
*
fout
;
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
{
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
}
#endif
listbuf
[
0
]
=
'\0'
;
strcat
(
listbuf
,
"SELECT usename, relname, relkind, relhasrules "
);
strcat
(
listbuf
,
"FROM pg_class, pg_user "
);
strcat
(
listbuf
,
"WHERE usesysid = relowner "
);
switch
(
info_type
)
{
case
't'
:
strcat
(
listbuf
,
"and ( relkind = 'r') "
);
break
;
case
'i'
:
strcat
(
listbuf
,
"and ( relkind = 'i') "
);
haveIndexes
=
true
;
break
;
case
'S'
:
strcat
(
listbuf
,
"and ( relkind = 'S') "
);
break
;
case
'b'
:
default:
strcat
(
listbuf
,
"and ( relkind = 'r' OR relkind = 'i' OR relkind = 'S') "
);
haveIndexes
=
true
;
break
;
}
if
(
!
system_tables
)
strcat
(
listbuf
,
"and relname !~ '^pg_' "
);
else
strcat
(
listbuf
,
"and relname ~ '^pg_' "
);
/*
* Large-object relations are automatically ignored because they have
* relkind 'l'. However, we want to ignore their indexes as well.
* The clean way to do that would be to do a join to find out which
* table each index is for. The ugly but fast way is to know that
* large object indexes have names starting with 'xinx'.
*/
if
(
haveIndexes
)
strcat
(
listbuf
,
"and (relkind != 'i' OR relname !~ '^xinx') "
);
strcat
(
listbuf
,
" ORDER BY relname "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
listbuf
)))
return
-
1
;
/* first, print out the attribute names */
nColumns
=
PQntuples
(
res
);
if
(
nColumns
>
0
)
{
if
(
pset
->
notty
==
0
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
pagerenv
[
0
]
!=
'\0'
&&
(
deep_tablelist
||
screen_size
.
ws_row
<=
nColumns
+
7
)
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
usePipe
=
1
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
fout
=
stdout
;
if
(
deep_tablelist
)
{
/* describe everything here */
char
**
table
;
table
=
(
char
**
)
malloc
(
nColumns
*
sizeof
(
char
*
));
if
(
table
==
NULL
)
perror
(
"malloc"
);
/* load table table */
/*
* Put double quotes around the table name to allow for
* mixed-case and whitespaces in the table name. - BGA
* 1998-11-14
*/
for
(
i
=
0
;
i
<
nColumns
;
i
++
)
{
table
[
i
]
=
(
char
*
)
malloc
(
PQgetlength
(
res
,
i
,
1
)
*
sizeof
(
char
)
+
3
);
if
(
table
[
i
]
==
NULL
)
perror
(
"malloc"
);
strcpy
(
table
[
i
],
"
\"
"
);
strcat
(
table
[
i
],
PQgetvalue
(
res
,
i
,
1
));
strcat
(
table
[
i
],
"
\"
"
);
}
PQclear
(
res
);
for
(
i
=
0
;
i
<
nColumns
;
i
++
)
tableDesc
(
pset
,
table
[
i
],
fout
);
free
(
table
);
}
else
{
/* Display the information */
fprintf
(
fout
,
"Database = %s
\n
"
,
PQdb
(
pset
->
db
));
fprintf
(
fout
,
" +------------------+----------------------------------+----------+
\n
"
);
fprintf
(
fout
,
" | Owner | Relation | Type |
\n
"
);
fprintf
(
fout
,
" +------------------+----------------------------------+----------+
\n
"
);
/* next, print out the instances */
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
fprintf
(
fout
,
" | %-16.16s"
,
PQgetvalue
(
res
,
i
,
0
));
fprintf
(
fout
,
" | %-32.32s | "
,
PQgetvalue
(
res
,
i
,
1
));
rk
=
PQgetvalue
(
res
,
i
,
2
);
rr
=
PQgetvalue
(
res
,
i
,
3
);
if
(
strcmp
(
rk
,
"r"
)
==
0
)
fprintf
(
fout
,
"%-8.8s |"
,
(
rr
[
0
]
==
't'
)
?
"view?"
:
"table"
);
else
if
(
strcmp
(
rk
,
"i"
)
==
0
)
fprintf
(
fout
,
"%-8.8s |"
,
"index"
);
else
fprintf
(
fout
,
"%-8.8s |"
,
"sequence"
);
fprintf
(
fout
,
"
\n
"
);
}
fprintf
(
fout
,
" +------------------+----------------------------------+----------+
\n
"
);
fprintf
(
fout
,
"
\n
"
);
PQclear
(
res
);
}
if
(
usePipe
)
{
pclose
(
fout
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
return
0
;
}
else
{
PQclear
(
res
);
switch
(
info_type
)
{
case
't'
:
fprintf
(
stderr
,
"Couldn't find any tables!
\n
"
);
break
;
case
'i'
:
fprintf
(
stderr
,
"Couldn't find any indices!
\n
"
);
break
;
case
'S'
:
fprintf
(
stderr
,
"Couldn't find any sequences!
\n
"
);
break
;
case
'b'
:
default:
fprintf
(
stderr
,
"Couldn't find any tables, sequences or indices!
\n
"
);
break
;
}
return
-
1
;
}
}
/*
* List Tables Grant/Revoke Permissions returns 0 if all went well
*
*/
static
int
rightsList
(
PsqlSettings
*
pset
)
{
char
listbuf
[
512
];
int
nColumns
;
int
i
;
int
maxCol1Len
;
int
maxCol2Len
;
int
usePipe
=
0
;
char
*
pagerenv
;
FILE
*
fout
;
PGresult
*
res
;
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
{
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
}
#endif
listbuf
[
0
]
=
'\0'
;
strcat
(
listbuf
,
"SELECT relname, relacl "
);
strcat
(
listbuf
,
"FROM pg_class "
);
/* Currently, we ignore indexes since they have no meaningful rights */
strcat
(
listbuf
,
"WHERE ( relkind = 'r' OR relkind = 'S') "
);
strcat
(
listbuf
,
" and relname !~ '^pg_'"
);
strcat
(
listbuf
,
" ORDER BY relname "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
listbuf
)))
return
-
1
;
/* first, print out the attribute names */
nColumns
=
PQntuples
(
res
);
if
(
nColumns
>
0
)
{
if
(
pset
->
notty
==
0
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
pagerenv
[
0
]
!=
'\0'
&&
screen_size
.
ws_row
<=
nColumns
+
7
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
usePipe
=
1
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
fout
=
stdout
;
/* choose column widths */
maxCol1Len
=
strlen
(
"Relation"
);
maxCol2Len
=
strlen
(
"Grant/Revoke Permissions"
);
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
int
l
=
strlen
(
PQgetvalue
(
res
,
i
,
0
));
if
(
l
>
maxCol1Len
)
maxCol1Len
=
l
;
l
=
strlen
(
PQgetvalue
(
res
,
i
,
1
));
if
(
l
>
maxCol2Len
)
maxCol2Len
=
l
;
}
/* Display the information */
fprintf
(
fout
,
"Database = %s
\n
"
,
PQdb
(
pset
->
db
));
fprintf
(
fout
,
" +"
);
emitNtimes
(
fout
,
"-"
,
maxCol1Len
+
2
);
fprintf
(
fout
,
"+"
);
emitNtimes
(
fout
,
"-"
,
maxCol2Len
+
2
);
fprintf
(
fout
,
"+
\n
"
);
fprintf
(
fout
,
" | %-*s | %-*s |
\n
"
,
maxCol1Len
,
"Relation"
,
maxCol2Len
,
"Grant/Revoke Permissions"
);
fprintf
(
fout
,
" +"
);
emitNtimes
(
fout
,
"-"
,
maxCol1Len
+
2
);
fprintf
(
fout
,
"+"
);
emitNtimes
(
fout
,
"-"
,
maxCol2Len
+
2
);
fprintf
(
fout
,
"+
\n
"
);
/* next, print out the instances */
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
fprintf
(
fout
,
" | %-*s | %-*s |
\n
"
,
maxCol1Len
,
PQgetvalue
(
res
,
i
,
0
),
maxCol2Len
,
PQgetvalue
(
res
,
i
,
1
));
}
fprintf
(
fout
,
" +"
);
emitNtimes
(
fout
,
"-"
,
maxCol1Len
+
2
);
fprintf
(
fout
,
"+"
);
emitNtimes
(
fout
,
"-"
,
maxCol2Len
+
2
);
fprintf
(
fout
,
"+
\n
"
);
PQclear
(
res
);
if
(
usePipe
)
{
pclose
(
fout
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
return
0
;
}
else
{
PQclear
(
res
);
fprintf
(
stderr
,
"Couldn't find any tables!
\n
"
);
return
-
1
;
}
}
static
void
emitNtimes
(
FILE
*
fout
,
const
char
*
str
,
int
N
)
{
int
i
;
for
(
i
=
0
;
i
<
N
;
i
++
)
fputs
(
str
,
fout
);
}
/*
* Describe a table
*
* Describe the columns in a database table. returns 0 if all went well
*
*
*/
static
int
tableDesc
(
PsqlSettings
*
pset
,
char
*
table
,
FILE
*
fout
)
{
char
descbuf
[
512
];
int
nColumns
,
nIndices
;
char
*
rtype
;
char
*
rnotnull
;
char
*
rhasdef
;
int
i
;
int
attlen
,
atttypmod
;
PGresult
*
res
,
*
res2
;
int
usePipe
=
0
;
char
*
pagerenv
;
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
{
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
}
#endif
/* Build the query */
/*
* if the table name is surrounded by double-quotes, then don't
* convert case
*/
if
(
*
table
==
'"'
)
{
table
++
;
if
(
*
(
table
+
strlen
(
table
)
-
1
)
==
'"'
)
*
(
table
+
strlen
(
table
)
-
1
)
=
'\0'
;
}
else
{
#ifdef MULTIBYTE
for
(
i
=
0
;
table
[
i
];
i
+=
PQmblen
(
table
+
i
))
#else
for
(
i
=
0
;
table
[
i
];
i
++
)
#endif
if
(
isascii
((
unsigned
char
)
table
[
i
])
&&
isupper
(
table
[
i
]))
table
[
i
]
=
tolower
(
table
[
i
]);
}
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT a.attnum, a.attname, t.typname, a.attlen, "
);
strcat
(
descbuf
,
"a.atttypmod, a.attnotnull, a.atthasdef "
);
strcat
(
descbuf
,
"FROM pg_class c, pg_attribute a, pg_type t "
);
strcat
(
descbuf
,
"WHERE c.relname = '"
);
strcat
(
descbuf
,
table
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and a.attnum > 0 "
);
strcat
(
descbuf
,
" and a.attrelid = c.oid "
);
strcat
(
descbuf
,
" and a.atttypid = t.oid "
);
strcat
(
descbuf
,
" ORDER BY attnum "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
/* first, print out the attribute names */
nColumns
=
PQntuples
(
res
);
if
(
nColumns
>
0
)
{
if
(
fout
==
NULL
)
{
if
(
pset
->
notty
==
0
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
pagerenv
[
0
]
!=
'\0'
&&
screen_size
.
ws_row
<=
nColumns
+
7
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
usePipe
=
1
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
fout
=
stdout
;
}
/*
* Extract the veiw name and veiw definition from pg_views. -Ryan
* 2/14/99
*/
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT viewname, definition "
);
strcat
(
descbuf
,
"FROM pg_views "
);
strcat
(
descbuf
,
"WHERE viewname like '"
);
strcat
(
descbuf
,
table
);
strcat
(
descbuf
,
"' "
);
if
(
!
(
res2
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
/*
* Display the information
*/
if
(
PQntuples
(
res2
))
{
/*
* display the query. o * -Ryan 2/14/99
*/
fprintf
(
fout
,
"View = %s
\n
"
,
table
);
fprintf
(
fout
,
"Query = %s
\n
"
,
PQgetvalue
(
res2
,
0
,
1
));
}
else
fprintf
(
fout
,
"Table = %s
\n
"
,
table
);
PQclear
(
res2
);
fprintf
(
fout
,
"+----------------------------------+----------------------------------+-------+
\n
"
);
fprintf
(
fout
,
"| Field | Type | Length|
\n
"
);
fprintf
(
fout
,
"+----------------------------------+----------------------------------+-------+
\n
"
);
/* next, print out the instances */
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
{
char
type_str
[
33
];
fprintf
(
fout
,
"| %-32.32s | "
,
PQgetvalue
(
res
,
i
,
1
));
rtype
=
PQgetvalue
(
res
,
i
,
2
);
attlen
=
atoi
(
PQgetvalue
(
res
,
i
,
3
));
atttypmod
=
atoi
(
PQgetvalue
(
res
,
i
,
4
));
rnotnull
=
PQgetvalue
(
res
,
i
,
5
);
rhasdef
=
PQgetvalue
(
res
,
i
,
6
);
strcpy
(
type_str
,
rtype
);
if
(
strcmp
(
rtype
,
"bpchar"
)
==
0
)
strcpy
(
type_str
,
"char()"
);
else
if
(
strcmp
(
rtype
,
"varchar"
)
==
0
)
strcpy
(
type_str
,
"varchar()"
);
else
if
(
rtype
[
0
]
==
'_'
)
{
strcpy
(
type_str
,
rtype
+
1
);
strncat
(
type_str
,
"[]"
,
32
-
strlen
(
type_str
));
type_str
[
32
]
=
'\0'
;
}
if
(
rnotnull
[
0
]
==
't'
)
{
strncat
(
type_str
,
" not null"
,
32
-
strlen
(
type_str
));
type_str
[
32
]
=
'\0'
;
}
if
(
rhasdef
[
0
]
==
't'
)
{
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT d.adsrc "
);
strcat
(
descbuf
,
"FROM pg_attrdef d, pg_class c "
);
strcat
(
descbuf
,
"WHERE c.relname = '"
);
strcat
(
descbuf
,
table
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and c.oid = d.adrelid "
);
strcat
(
descbuf
,
" and d.adnum = "
);
strcat
(
descbuf
,
PQgetvalue
(
res
,
i
,
0
));
if
(
!
(
res2
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
strcat
(
type_str
,
" default "
);
strncat
(
type_str
,
PQgetvalue
(
res2
,
0
,
0
),
32
-
strlen
(
type_str
));
type_str
[
32
]
=
'\0'
;
}
fprintf
(
fout
,
"%-32.32s |"
,
type_str
);
if
(
strcmp
(
rtype
,
"text"
)
==
0
)
fprintf
(
fout
,
"%6s |"
,
"var"
);
else
if
(
strcmp
(
rtype
,
"bpchar"
)
==
0
||
strcmp
(
rtype
,
"varchar"
)
==
0
)
fprintf
(
fout
,
"%6i |"
,
atttypmod
!=
-
1
?
atttypmod
-
VARHDRSZ
:
0
);
else
if
(
strcmp
(
rtype
,
"numeric"
)
==
0
)
fprintf
(
fout
,
"%3i.%-2i |"
,
((
atttypmod
-
VARHDRSZ
)
>>
16
)
&
0xffff
,
(
atttypmod
-
VARHDRSZ
)
&
0xffff
);
else
{
if
(
attlen
>
0
)
fprintf
(
fout
,
"%6i |"
,
attlen
);
else
fprintf
(
fout
,
"%6s |"
,
"var"
);
}
fprintf
(
fout
,
"
\n
"
);
}
fprintf
(
fout
,
"+----------------------------------+----------------------------------+-------+
\n
"
);
PQclear
(
res
);
/* display defined indexes for this table */
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT c2.relname "
);
strcat
(
descbuf
,
"FROM pg_class c, pg_class c2, pg_index i "
);
strcat
(
descbuf
,
"WHERE c.relname = '"
);
strcat
(
descbuf
,
table
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and c.oid = i.indrelid "
);
strcat
(
descbuf
,
" and i.indexrelid = c2.oid "
);
strcat
(
descbuf
,
" ORDER BY c2.relname "
);
if
((
res
=
PSQLexec
(
pset
,
descbuf
)))
{
nIndices
=
PQntuples
(
res
);
if
(
nIndices
>
0
)
{
/*
* Display the information
*/
if
(
nIndices
==
1
)
fprintf
(
fout
,
"Index: "
);
else
fprintf
(
fout
,
"Indices: "
);
/* next, print out the instances */
for
(
i
=
0
;
i
<
PQntuples
(
res
);
i
++
)
if
(
i
==
0
)
fprintf
(
fout
,
"%s
\n
"
,
PQgetvalue
(
res
,
i
,
0
));
else
fprintf
(
fout
,
" %s
\n
"
,
PQgetvalue
(
res
,
i
,
0
));
fprintf
(
fout
,
"
\n
"
);
}
PQclear
(
res
);
}
if
(
usePipe
)
{
pclose
(
fout
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
return
0
;
}
else
{
PQclear
(
res
);
fprintf
(
stderr
,
"Couldn't find table %s!
\n
"
,
table
);
return
-
1
;
}
}
/*
* Get object comments
*
* Describe the columns in a database table. returns 0 if all went well
*
*
*/
static
int
objectDescription
(
PsqlSettings
*
pset
,
char
*
object
)
{
char
descbuf
[
512
];
PGresult
*
res
;
int
i
;
bool
success
;
/* Build the query */
while
(
isspace
(
*
object
))
object
++
;
/*
* if the object name is surrounded by double-quotes, then don't
* convert case
*/
if
(
*
object
==
'"'
)
{
object
++
;
if
(
*
(
object
+
strlen
(
object
)
-
1
)
==
'"'
)
*
(
object
+
strlen
(
object
)
-
1
)
=
'\0'
;
}
else
{
#ifdef MULTIBYTE
for
(
i
=
0
;
object
[
i
];
i
+=
PQmblen
(
object
+
i
))
#else
for
(
i
=
0
;
object
[
i
];
i
++
)
#endif
if
(
isupper
(
object
[
i
]))
object
[
i
]
=
tolower
(
object
[
i
]);
}
descbuf
[
0
]
=
'\0'
;
if
(
strchr
(
object
,
'.'
)
!=
NULL
)
{
char
table
[
NAMEDATALEN
],
column
[
NAMEDATALEN
];
StrNCpy
(
table
,
object
,
((
strchr
(
object
,
'.'
)
-
object
+
1
)
<
NAMEDATALEN
)
?
(
strchr
(
object
,
'.'
)
-
object
+
1
)
:
NAMEDATALEN
);
StrNCpy
(
column
,
strchr
(
object
,
'.'
)
+
1
,
NAMEDATALEN
);
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_class, pg_attribute, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_class.relname = '"
);
strcat
(
descbuf
,
table
);
strcat
(
descbuf
,
"' and "
);
strcat
(
descbuf
,
"pg_class.oid = pg_attribute.attrelid and "
);
strcat
(
descbuf
,
"pg_attribute.attname = '"
);
strcat
(
descbuf
,
column
);
strcat
(
descbuf
,
"' and "
);
strcat
(
descbuf
,
" pg_attribute.oid = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
}
else
{
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_class, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_class.relname ~ '^"
);
strcat
(
descbuf
,
object
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and pg_class.oid = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
else
if
(
PQntuples
(
res
)
<=
0
)
{
PQclear
(
res
);
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_type, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_type.typname ~ '^"
);
strcat
(
descbuf
,
object
);
strcat
(
descbuf
,
"' and "
);
strcat
(
descbuf
,
" pg_type.oid = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
else
if
(
PQntuples
(
res
)
<=
0
)
{
PQclear
(
res
);
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_proc, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_proc.proname ~ '^"
);
strcat
(
descbuf
,
object
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and pg_proc.oid = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
else
if
(
PQntuples
(
res
)
<=
0
)
{
PQclear
(
res
);
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_operator, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_operator.oprname ~ '^"
);
strcat
(
descbuf
,
object
);
strcat
(
descbuf
,
"'"
);
/* operator descriptions are attached to the proc */
strcat
(
descbuf
,
" and RegprocToOid(pg_operator.oprcode) = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
else
if
(
PQntuples
(
res
)
<=
0
)
{
PQclear
(
res
);
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT DISTINCT description "
);
strcat
(
descbuf
,
"FROM pg_aggregate, pg_description "
);
strcat
(
descbuf
,
"WHERE pg_aggregate.aggname ~ '^"
);
strcat
(
descbuf
,
object
);
strcat
(
descbuf
,
"'"
);
strcat
(
descbuf
,
" and pg_aggregate.oid = pg_description.objoid "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
else
if
(
PQntuples
(
res
)
<=
0
)
{
PQclear
(
res
);
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT 'no description' as description "
);
if
(
!
(
res
=
PSQLexec
(
pset
,
descbuf
)))
return
-
1
;
}
}
}
}
}
}
PQclear
(
res
);
success
=
SendQuery
(
pset
,
descbuf
,
NULL
,
NULL
);
return
0
;
}
/*
* Basic routines to read a line of input
*
* All three routines will return a malloc'd string of indefinite size.
*/
typedef
char
*
(
*
READ_ROUTINE
)
(
char
*
prompt
,
FILE
*
source
);
/*
* gets_noreadline
* gets a line of interactive input (without using readline)
*
* the source is ignored
*/
static
char
*
gets_noreadline
(
char
*
prompt
,
FILE
*
source
)
{
fputs
(
prompt
,
stdout
);
fflush
(
stdout
);
return
gets_fromFile
(
prompt
,
stdin
);
}
/*
* gets_readline
* gets a line of interactive input using readline library
*
* the source is ignored
*/
static
char
*
gets_readline
(
char
*
prompt
,
FILE
*
source
)
{
char
*
s
;
#ifdef USE_READLINE
s
=
readline
(
prompt
);
#else
s
=
gets_noreadline
(
prompt
,
source
);
#endif
fputc
(
'\r'
,
stdout
);
fflush
(
stdout
);
return
s
;
}
/*
* gets_fromFile
* gets a line of noninteractive input from a file
*
* the prompt is ignored
*/
static
char
*
gets_fromFile
(
char
*
prompt
,
FILE
*
source
)
{
PQExpBufferData
buffer
;
char
line
[
COPYBUFSIZ
];
initPQExpBuffer
(
&
buffer
);
while
(
fgets
(
line
,
COPYBUFSIZ
,
source
)
!=
NULL
)
{
appendPQExpBufferStr
(
&
buffer
,
line
);
if
(
buffer
.
data
[
buffer
.
len
-
1
]
==
'\n'
)
return
buffer
.
data
;
}
if
(
buffer
.
len
>
0
)
return
buffer
.
data
;
/* EOF after reading some bufferload(s) */
/* EOF, so return null */
termPQExpBuffer
(
&
buffer
);
return
NULL
;
}
/*
* SendQuery: send the query string to the backend.
*
* Return true if the query executed successfully, false otherwise.
*
* If not NULL, copy_in_stream and copy_out_stream are files to redirect
* copy in/out data to.
*/
static
bool
SendQuery
(
PsqlSettings
*
pset
,
const
char
*
query
,
FILE
*
copy_in_stream
,
FILE
*
copy_out_stream
)
{
bool
success
=
false
;
PGresult
*
results
;
PGnotify
*
notify
;
if
(
pset
->
singleStep
)
fprintf
(
stdout
,
"
\n
**************************************"
"*****************************************
\n
"
);
if
(
pset
->
echoQuery
||
pset
->
singleStep
)
{
fprintf
(
stderr
,
"QUERY: %s
\n
"
,
query
);
fflush
(
stderr
);
}
if
(
pset
->
singleStep
)
{
fprintf
(
stdout
,
"
\n
**************************************"
"*****************************************
\n
"
);
fflush
(
stdout
);
printf
(
"
\n
press return to continue ..
\n
"
);
gets_fromFile
(
""
,
stdin
);
}
results
=
PQexec
(
pset
->
db
,
query
);
if
(
results
==
NULL
)
{
fprintf
(
stderr
,
"%s"
,
PQerrorMessage
(
pset
->
db
));
success
=
false
;
}
else
{
switch
(
PQresultStatus
(
results
))
{
case
PGRES_TUPLES_OK
:
if
(
pset
->
gfname
)
{
PsqlSettings
settings_copy
=
*
pset
;
FILE
*
fp
;
settings_copy
.
queryFout
=
stdout
;
fp
=
setFout
(
&
settings_copy
,
pset
->
gfname
);
if
(
!
fp
||
fp
==
stdout
)
{
success
=
false
;
break
;
}
PQprint
(
fp
,
results
,
&
pset
->
opt
);
if
(
settings_copy
.
pipe
)
pclose
(
fp
);
else
fclose
(
fp
);
free
(
pset
->
gfname
);
pset
->
gfname
=
NULL
;
success
=
true
;
break
;
}
else
{
success
=
true
;
PQprint
(
pset
->
queryFout
,
results
,
&
(
pset
->
opt
));
fflush
(
pset
->
queryFout
);
}
break
;
case
PGRES_EMPTY_QUERY
:
success
=
true
;
break
;
case
PGRES_COMMAND_OK
:
success
=
true
;
if
(
!
pset
->
quiet
)
printf
(
"%s
\n
"
,
PQcmdStatus
(
results
));
break
;
case
PGRES_COPY_OUT
:
if
(
copy_out_stream
)
success
=
handleCopyOut
(
pset
->
db
,
copy_out_stream
);
else
{
if
(
pset
->
queryFout
==
stdout
&&
!
pset
->
quiet
)
printf
(
"Copy command returns...
\n
"
);
success
=
handleCopyOut
(
pset
->
db
,
pset
->
queryFout
);
}
break
;
case
PGRES_COPY_IN
:
if
(
copy_in_stream
)
success
=
handleCopyIn
(
pset
->
db
,
false
,
copy_in_stream
);
else
success
=
handleCopyIn
(
pset
->
db
,
cur_cmd_interactive
&&
!
pset
->
quiet
,
cur_cmd_source
);
break
;
case
PGRES_NONFATAL_ERROR
:
case
PGRES_FATAL_ERROR
:
case
PGRES_BAD_RESPONSE
:
success
=
false
;
fprintf
(
stderr
,
"%s"
,
PQerrorMessage
(
pset
->
db
));
break
;
}
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
{
fprintf
(
stderr
,
"We have lost the connection to the backend, so "
"further processing is impossible. "
"Terminating.
\n
"
);
exit
(
2
);
/* we are out'ta here */
}
/* check for asynchronous returns */
while
((
notify
=
PQnotifies
(
pset
->
db
))
!=
NULL
)
{
fprintf
(
stderr
,
"ASYNC NOTIFY of '%s' from backend pid '%d' received
\n
"
,
notify
->
relname
,
notify
->
be_pid
);
free
(
notify
);
}
if
(
results
)
PQclear
(
results
);
}
return
success
;
}
static
void
editFile
(
char
*
fname
)
{
char
*
editorName
;
char
*
sys
;
editorName
=
getenv
(
"EDITOR"
);
if
(
!
editorName
)
editorName
=
DEFAULT_EDITOR
;
sys
=
malloc
(
strlen
(
editorName
)
+
strlen
(
fname
)
+
32
+
1
);
if
(
!
sys
)
{
perror
(
"malloc"
);
exit
(
1
);
}
sprintf
(
sys
,
"exec '%s' '%s'"
,
editorName
,
fname
);
system
(
sys
);
free
(
sys
);
}
static
bool
toggle
(
PsqlSettings
*
pset
,
bool
*
sw
,
char
*
msg
)
{
*
sw
=
!*
sw
;
if
(
!
pset
->
quiet
)
printf
(
"turned %s %s
\n
"
,
on
(
*
sw
),
msg
);
return
*
sw
;
}
static
void
unescape
(
char
*
dest
,
const
char
*
source
)
{
/*-----------------------------------------------------------------------------
Return as the string <dest> the value of string <source> with escape
sequences turned into the bytes they represent.
-----------------------------------------------------------------------------*/
char
*
p
;
bool
esc
;
/* Last character we saw was the escape
* character (/) */
esc
=
false
;
/* Haven't seen escape character yet */
for
(
p
=
(
char
*
)
source
;
*
p
;
p
++
)
{
char
c
;
/* Our output character */
if
(
esc
)
{
switch
(
*
p
)
{
case
'n'
:
c
=
'\n'
;
break
;
case
'r'
:
c
=
'\r'
;
break
;
case
't'
:
c
=
'\t'
;
break
;
case
'f'
:
c
=
'\f'
;
break
;
case
'\\'
:
c
=
'\\'
;
break
;
default:
c
=
*
p
;
}
esc
=
false
;
}
else
if
(
*
p
==
'\\'
)
{
esc
=
true
;
c
=
' '
;
/* meaningless, but compiler doesn't know
* that */
}
else
{
c
=
*
p
;
esc
=
false
;
}
if
(
!
esc
)
*
dest
++
=
c
;
}
*
dest
=
'\0'
;
/* Terminating null character */
}
static
void
parse_slash_copy
(
const
char
*
args
,
char
*
table
,
const
int
table_len
,
char
*
file
,
const
int
file_len
,
bool
*
from_p
,
bool
*
error_p
)
{
char
work_args
[
200
];
/*
* A copy of the \copy command arguments, except that we modify it as
* we parse to suit our parsing needs.
*/
char
*
table_tok
,
*
fromto_tok
;
strncpy
(
work_args
,
args
,
sizeof
(
work_args
));
work_args
[
sizeof
(
work_args
)
-
1
]
=
'\0'
;
*
error_p
=
false
;
/* initial assumption */
table_tok
=
strtok
(
work_args
,
" "
);
if
(
table_tok
==
NULL
)
{
fprintf
(
stderr
,
"
\\
copy needs arguments.
\n
"
);
*
error_p
=
true
;
}
else
{
strncpy
(
table
,
table_tok
,
table_len
);
file
[
table_len
-
1
]
=
'\0'
;
fromto_tok
=
strtok
(
NULL
,
" "
);
if
(
fromto_tok
==
NULL
)
{
fprintf
(
stderr
,
"'FROM' or 'TO' must follow table name.
\n
"
);
*
error_p
=
true
;
}
else
{
if
(
strcasecmp
(
fromto_tok
,
"from"
)
==
0
)
*
from_p
=
true
;
else
if
(
strcasecmp
(
fromto_tok
,
"to"
)
==
0
)
*
from_p
=
false
;
else
{
fprintf
(
stderr
,
"Unrecognized token found where "
"'FROM' or 'TO' expected: '%s'.
\n
"
,
fromto_tok
);
*
error_p
=
true
;
}
if
(
!*
error_p
)
{
char
*
file_tok
;
file_tok
=
strtok
(
NULL
,
" "
);
if
(
file_tok
==
NULL
)
{
fprintf
(
stderr
,
"A file pathname must follow '%s'.
\n
"
,
fromto_tok
);
*
error_p
=
true
;
}
else
{
strncpy
(
file
,
file_tok
,
file_len
);
file
[
file_len
-
1
]
=
'\0'
;
if
(
strtok
(
NULL
,
" "
)
!=
NULL
)
{
fprintf
(
stderr
,
"You have extra tokens after the filename.
\n
"
);
*
error_p
=
true
;
}
}
}
}
}
}
static
void
do_copy
(
const
char
*
args
,
PsqlSettings
*
pset
)
{
/*---------------------------------------------------------------------------
Execute a \copy command (frontend copy). We have to open a file, then
submit a COPY query to the backend and either feed it data from the
file or route its response into the file.
We do a text copy with default (tab) column delimiters. Some day, we
should do all the things a backend copy can do.
----------------------------------------------------------------------------*/
char
query
[
200
];
/* The COPY command we send to the back end */
bool
from
;
/* The direction of the copy is from a file to a table. */
char
file
[
MAXPGPATH
];
/* The pathname of the file from/to which we copy */
char
table
[
NAMEDATALEN
];
/* The name of the table from/to which we copy */
bool
syntax_error
;
/* The \c command has invalid syntax */
FILE
*
copystream
;
parse_slash_copy
(
args
,
table
,
sizeof
(
table
),
file
,
sizeof
(
file
),
&
from
,
&
syntax_error
);
if
(
!
syntax_error
)
{
strcpy
(
query
,
"COPY "
);
strcat
(
query
,
table
);
if
(
from
)
strcat
(
query
,
" FROM stdin"
);
else
strcat
(
query
,
" TO stdout"
);
if
(
from
)
#ifndef __CYGWIN32__
copystream
=
fopen
(
file
,
"r"
);
#else
copystream
=
fopen
(
file
,
"rb"
);
#endif
else
#ifndef __CYGWIN32__
copystream
=
fopen
(
file
,
"w"
);
#else
copystream
=
fopen
(
file
,
"wb"
);
#endif
if
(
copystream
==
NULL
)
fprintf
(
stderr
,
"Unable to open file %s which to copy, errno = %s (%d)."
,
from
?
"from"
:
"to"
,
strerror
(
errno
),
errno
);
else
{
bool
success
;
/* The query succeeded at the backend */
success
=
SendQuery
(
pset
,
query
,
from
?
copystream
:
(
FILE
*
)
NULL
,
!
from
?
copystream
:
(
FILE
*
)
NULL
);
fclose
(
copystream
);
if
(
!
pset
->
quiet
)
{
if
(
success
)
printf
(
"Successfully copied.
\n
"
);
else
printf
(
"Copy failed.
\n
"
);
}
}
}
}
static
void
do_connect
(
const
char
*
new_dbname
,
const
char
*
new_user
,
PsqlSettings
*
pset
)
{
if
(
!
new_dbname
)
fprintf
(
stderr
,
"
\\
connect must be followed by a database name
\n
"
);
else
if
(
new_user
!=
NULL
&&
pset
->
getPassword
)
fprintf
(
stderr
,
"You can't specify a username when using passwords.
\n
"
);
else
{
PGconn
*
olddb
=
pset
->
db
;
const
char
*
dbparam
;
const
char
*
userparam
;
const
char
*
pwparam
;
if
(
strcmp
(
new_dbname
,
"-"
)
!=
0
)
dbparam
=
new_dbname
;
else
dbparam
=
PQdb
(
olddb
);
if
(
new_user
!=
NULL
&&
strcmp
(
new_user
,
"-"
)
!=
0
)
userparam
=
new_user
;
else
userparam
=
PQuser
(
olddb
);
/* FIXME: if changing user, ought to prompt for a new password? */
pwparam
=
PQpass
(
olddb
);
#ifdef MULTIBYTE
/*
* PGCLIENTENCODING may be set by the previous connection. if a
* user does not explicitly set PGCLIENTENCODING, we should
* discard PGCLIENTENCODING so that libpq could get the backend
* encoding as the default PGCLIENTENCODING value. -- 1998/12/12
* Tatsuo Ishii
*/
if
(
!
has_client_encoding
)
{
static
const
char
ev
[]
=
"PGCLIENTENCODING="
;
putenv
(
ev
);
}
#endif
pset
->
db
=
PQsetdbLogin
(
PQhost
(
olddb
),
PQport
(
olddb
),
NULL
,
NULL
,
dbparam
,
userparam
,
pwparam
);
if
(
!
pset
->
quiet
)
{
if
(
!
new_user
)
printf
(
"connecting to new database: %s
\n
"
,
dbparam
);
else
if
(
dbparam
!=
new_dbname
)
printf
(
"connecting as new user: %s
\n
"
,
new_user
);
else
printf
(
"connecting to new database: %s as user: %s
\n
"
,
dbparam
,
new_user
);
}
if
(
PQstatus
(
pset
->
db
)
==
CONNECTION_BAD
)
{
fprintf
(
stderr
,
"%s
\n
"
,
PQerrorMessage
(
pset
->
db
));
fprintf
(
stderr
,
"Could not connect to new database. exiting
\n
"
);
exit
(
2
);
}
else
{
cancelConn
=
pset
->
db
;
/* redirect sigint's loving
* attentions */
PQfinish
(
olddb
);
free
(
pset
->
prompt
);
pset
->
prompt
=
malloc
(
strlen
(
PQdb
(
pset
->
db
))
+
10
);
sprintf
(
pset
->
prompt
,
"%s%s"
,
PQdb
(
pset
->
db
),
PROMPT
);
}
}
}
static
void
do_edit
(
const
char
*
filename_arg
,
PQExpBuffer
query_buf
,
int
*
status_p
)
{
int
fd
;
char
fnametmp
[
64
];
char
*
fname
;
int
cc
;
int
ql
=
query_buf
->
len
;
bool
error
;
char
line
[
COPYBUFSIZ
+
1
];
if
(
filename_arg
)
{
fname
=
(
char
*
)
filename_arg
;
error
=
false
;
}
else
{
#ifndef WIN32
sprintf
(
fnametmp
,
"/tmp/psql.%ld.%ld"
,
(
long
)
geteuid
(),
(
long
)
getpid
());
#else
GetTempFileName
(
"."
,
"psql"
,
0
,
fnametmp
);
#endif
fname
=
fnametmp
;
unlink
(
fname
);
if
((
fd
=
open
(
fname
,
O_EXCL
|
O_CREAT
|
O_WRONLY
,
0600
))
<
0
)
{
perror
(
fname
);
error
=
true
;
}
else
{
if
(
ql
==
0
||
query_buf
->
data
[
ql
-
1
]
!=
'\n'
)
{
appendPQExpBufferChar
(
query_buf
,
'\n'
);
ql
++
;
}
if
(
write
(
fd
,
query_buf
->
data
,
ql
)
!=
ql
)
{
perror
(
fname
);
close
(
fd
);
unlink
(
fname
);
error
=
true
;
}
else
{
close
(
fd
);
error
=
false
;
}
}
}
if
(
error
)
*
status_p
=
CMD_SKIP_LINE
;
else
{
editFile
(
fname
);
if
((
fd
=
open
(
fname
,
O_RDONLY
,
0
))
<
0
)
{
perror
(
fname
);
*
status_p
=
CMD_SKIP_LINE
;
}
else
{
resetPQExpBuffer
(
query_buf
);
while
((
cc
=
(
int
)
read
(
fd
,
line
,
COPYBUFSIZ
))
>
0
)
{
line
[
cc
]
=
'\0'
;
appendPQExpBufferStr
(
query_buf
,
line
);
}
close
(
fd
);
rightTrim
(
query_buf
->
data
);
query_buf
->
len
=
strlen
(
query_buf
->
data
);
*
status_p
=
CMD_NEWEDIT
;
}
if
(
!
filename_arg
)
unlink
(
fname
);
}
}
static
void
do_help
(
PsqlSettings
*
pset
,
const
char
*
topic
)
{
if
(
!
topic
)
{
char
left_center_right
;
/* Which column we're displaying */
int
i
;
/* Index into QL_HELP[] */
printf
(
"type
\\
h <cmd> where <cmd> is one of the following:
\n
"
);
left_center_right
=
'L'
;
/* Start with left column */
i
=
0
;
while
(
QL_HELP
[
i
].
cmd
!=
NULL
)
{
switch
(
left_center_right
)
{
case
'L'
:
printf
(
" %-25s"
,
QL_HELP
[
i
].
cmd
);
left_center_right
=
'C'
;
break
;
case
'C'
:
printf
(
"%-25s"
,
QL_HELP
[
i
].
cmd
);
left_center_right
=
'R'
;
break
;
case
'R'
:
printf
(
"%-25s
\n
"
,
QL_HELP
[
i
].
cmd
);
left_center_right
=
'L'
;
break
;
}
i
++
;
}
if
(
left_center_right
!=
'L'
)
puts
(
"
\n
"
);
printf
(
"type
\\
h * for a complete description of all commands
\n
"
);
}
else
{
int
i
;
/* Index into QL_HELP[] */
bool
help_found
;
/* We found the help he asked for */
int
usePipe
=
0
;
char
*
pagerenv
;
FILE
*
fout
;
if
(
strcmp
(
topic
,
"*"
)
==
0
&&
(
pset
->
notty
==
0
)
&&
(
pagerenv
=
getenv
(
"PAGER"
))
&&
(
pagerenv
[
0
]
!=
'\0'
)
&&
(
fout
=
popen
(
pagerenv
,
"w"
)))
{
usePipe
=
1
;
pqsignal
(
SIGPIPE
,
SIG_IGN
);
}
else
fout
=
stdout
;
help_found
=
false
;
/* Haven't found it yet */
for
(
i
=
0
;
QL_HELP
[
i
].
cmd
;
i
++
)
{
if
(
strcasecmp
(
QL_HELP
[
i
].
cmd
,
topic
)
==
0
||
strcmp
(
topic
,
"*"
)
==
0
)
{
help_found
=
true
;
fprintf
(
fout
,
"Command: %s
\n
"
,
QL_HELP
[
i
].
cmd
);
fprintf
(
fout
,
"Description: %s
\n
"
,
QL_HELP
[
i
].
help
);
fprintf
(
fout
,
"Syntax:
\n
"
);
fprintf
(
fout
,
"%s
\n
"
,
QL_HELP
[
i
].
syntax
);
fprintf
(
fout
,
"
\n
"
);
}
}
if
(
usePipe
)
{
pclose
(
fout
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
if
(
!
help_found
)
fprintf
(
stderr
,
"command not found, "
"try
\\
h with no arguments to see available help
\n
"
);
}
}
static
void
do_shell
(
const
char
*
command
)
{
if
(
!
command
)
{
char
*
sys
;
char
*
shellName
;
shellName
=
getenv
(
"SHELL"
);
if
(
shellName
==
NULL
)
shellName
=
DEFAULT_SHELL
;
sys
=
malloc
(
strlen
(
shellName
)
+
16
);
if
(
!
sys
)
{
perror
(
"malloc"
);
exit
(
1
);
}
sprintf
(
sys
,
"exec %s"
,
shellName
);
system
(
sys
);
free
(
sys
);
}
else
system
(
command
);
}
/*----------
* HandleSlashCmds:
*
* Handles all the different commands that start with \
*
* 'line' is the current input line (ie, the backslash command)
* 'query_buf' contains the query-so-far, which may be modified by
* execution of the backslash command (for example, \r clears it)
*
* query_buf can be NULL if there is no query-so-far.
*
* Returns a status code:
* 0 - send currently constructed query to backend (i.e. we got a \g)
* 1 - skip processing of this line, continue building up query
* 2 - terminate processing (i.e. we got a \q)
* 3 - new query supplied by edit
*----------
*/
static
int
HandleSlashCmds
(
PsqlSettings
*
pset
,
char
*
line
,
PQExpBuffer
query_buf
)
{
int
status
=
CMD_SKIP_LINE
;
bool
success
;
char
*
cmd
;
/*
* String: value of the slash command, less the slash and with escape
* sequences decoded.
*/
char
*
optarg
;
/*
* Pointer inside the <cmd> string to the argument of the slash
* command, assuming it is a one-character slash command. If it's not
* a one-character command, this is meaningless.
*/
char
*
optarg2
;
/*
* Pointer inside the <cmd> string to the argument of the slash
* command assuming it's not a one-character command. If it's a
* one-character command, this is meaningless.
*/
int
blank_loc
;
/* Offset within <cmd> of first blank */
cmd
=
malloc
(
strlen
(
line
));
/* unescaping better not make string grow. */
unescape
(
cmd
,
line
+
1
);
/* sets cmd string */
if
(
strlen
(
cmd
)
>=
1
&&
cmd
[
strlen
(
cmd
)
-
1
]
==
';'
)
/* strip trailing ; */
cmd
[
strlen
(
cmd
)
-
1
]
=
'\0'
;
/*
* Originally, there were just single character commands. Now, we
* define some longer, friendly commands, but we have to keep the old
* single character commands too. \c used to be what \connect is now.
* Complicating matters is the fact that with the single-character
* commands, you can start the argument right after the single
* character, so "\copy" would mean "connect to database named 'opy'".
*/
if
(
strlen
(
cmd
)
>
1
)
optarg
=
cmd
+
1
+
strspn
(
cmd
+
1
,
"
\t
"
);
else
optarg
=
NULL
;
blank_loc
=
strcspn
(
cmd
,
"
\t
"
);
if
(
blank_loc
==
0
||
!
cmd
[
blank_loc
])
optarg2
=
NULL
;
else
optarg2
=
cmd
+
blank_loc
+
strspn
(
cmd
+
blank_loc
,
"
\t
"
);
switch
(
cmd
[
0
])
{
case
'a'
:
/* toggles to align fields on output */
toggle
(
pset
,
&
pset
->
opt
.
align
,
"field alignment"
);
break
;
case
'C'
:
/* define new caption */
if
(
pset
->
opt
.
caption
)
{
free
(
pset
->
opt
.
caption
);
pset
->
opt
.
caption
=
NULL
;
}
if
(
optarg
&&
!
(
pset
->
opt
.
caption
=
strdup
(
optarg
)))
{
perror
(
"malloc"
);
exit
(
CMD_TERMINATE
);
}
break
;
case
'c'
:
{
if
(
strncmp
(
cmd
,
"copy "
,
strlen
(
"copy "
))
==
0
||
strncmp
(
cmd
,
"copy "
,
strlen
(
"copy "
))
==
0
)
do_copy
(
optarg2
,
pset
);
else
if
(
strcmp
(
cmd
,
"copy"
)
==
0
)
{
fprintf
(
stderr
,
"See
\\
? for help
\n
"
);
break
;
}
else
if
(
strncmp
(
cmd
,
"connect "
,
strlen
(
"connect "
))
==
0
||
strcmp
(
cmd
,
"connect"
)
==
0
/* issue error message */
)
{
char
*
optarg3
=
NULL
;
int
blank_loc2
;
if
(
optarg2
)
{
blank_loc2
=
strcspn
(
optarg2
,
"
\t
"
);
if
(
blank_loc2
==
0
||
*
(
optarg2
+
blank_loc2
)
==
'\0'
)
optarg3
=
NULL
;
else
{
optarg3
=
optarg2
+
blank_loc2
+
strspn
(
optarg2
+
blank_loc2
,
"
\t
"
);
*
(
optarg2
+
blank_loc2
)
=
'\0'
;
}
}
do_connect
(
optarg2
,
optarg3
,
pset
);
}
else
{
char
*
optarg3
=
NULL
;
int
blank_loc2
;
if
(
optarg
)
{
blank_loc2
=
strcspn
(
optarg
,
"
\t
"
);
if
(
blank_loc2
==
0
||
*
(
optarg
+
blank_loc2
)
==
'\0'
)
optarg3
=
NULL
;
else
{
optarg3
=
optarg
+
blank_loc2
+
strspn
(
optarg
+
blank_loc2
,
"
\t
"
);
*
(
optarg
+
blank_loc2
)
=
'\0'
;
}
}
do_connect
(
optarg
,
optarg3
,
pset
);
}
}
break
;
case
'd'
:
/* \d describe database information */
/*
* if the optarg2 name is surrounded by double-quotes, then
* don't convert case
*/
if
(
optarg2
)
{
if
(
*
optarg2
==
'"'
)
{
optarg2
++
;
if
(
*
(
optarg2
+
strlen
(
optarg2
)
-
1
)
==
'"'
)
*
(
optarg2
+
strlen
(
optarg2
)
-
1
)
=
'\0'
;
}
else
{
int
i
;
#ifdef MULTIBYTE
for
(
i
=
0
;
optarg2
[
i
];
i
+=
PQmblen
(
optarg2
+
i
))
#else
for
(
i
=
0
;
optarg2
[
i
];
i
++
)
#endif
if
(
isupper
(
optarg2
[
i
]))
optarg2
[
i
]
=
tolower
(
optarg2
[
i
]);
}
}
#ifdef TIOCGWINSZ
if
(
pset
->
notty
==
0
&&
(
ioctl
(
fileno
(
stdout
),
TIOCGWINSZ
,
&
screen_size
)
==
-
1
||
screen_size
.
ws_col
==
0
||
screen_size
.
ws_row
==
0
))
{
#endif
screen_size
.
ws_row
=
24
;
screen_size
.
ws_col
=
80
;
#ifdef TIOCGWINSZ
}
#endif
if
(
strncmp
(
cmd
,
"da"
,
2
)
==
0
)
{
char
descbuf
[
4096
];
/* aggregates */
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT a.aggname AS aggname, "
);
strcat
(
descbuf
,
" t.typname AS type, "
);
strcat
(
descbuf
,
" obj_description(a.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_aggregate a, pg_type t "
);
strcat
(
descbuf
,
"WHERE a.aggbasetype = t.oid "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND a.aggname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"UNION "
);
strcat
(
descbuf
,
"SELECT a.aggname AS aggname, "
);
strcat
(
descbuf
,
" 'all types' as type, "
);
strcat
(
descbuf
,
" obj_description(a.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_aggregate a "
);
strcat
(
descbuf
,
"WHERE a.aggbasetype = 0 "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND a.aggname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"ORDER BY aggname, type;"
);
success
=
SendQuery
(
pset
,
descbuf
,
NULL
,
NULL
);
}
else
if
(
strncmp
(
cmd
,
"dd"
,
2
)
==
0
)
/* descriptions */
objectDescription
(
pset
,
optarg
+
1
);
else
if
(
strncmp
(
cmd
,
"df"
,
2
)
==
0
)
{
char
descbuf
[
4096
];
/* functions/procedures */
/*
* we skip in/out funcs by excluding functions that take
* some arguments, but have no types defined for those
* arguments
*/
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT t.typname as result, "
);
strcat
(
descbuf
,
" p.proname as function, "
);
if
(
screen_size
.
ws_col
<=
80
)
strcat
(
descbuf
,
" substr(oid8types(p.proargtypes),1,14) as arguments, "
);
else
strcat
(
descbuf
,
" oid8types(p.proargtypes) as arguments, "
);
if
(
screen_size
.
ws_col
<=
80
)
strcat
(
descbuf
,
" substr(obj_description(p.oid),1,34) as description "
);
else
strcat
(
descbuf
,
" obj_description(p.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_proc p, pg_type t "
);
strcat
(
descbuf
,
"WHERE p.prorettype = t.oid and "
);
strcat
(
descbuf
,
"(pronargs = 0 or oid8types(p.proargtypes) != '') "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND p.proname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"ORDER BY result, function, arguments;"
);
success
=
SendQuery
(
pset
,
descbuf
,
NULL
,
NULL
);
}
else
if
(
strncmp
(
cmd
,
"di"
,
2
)
==
0
)
/* only indices */
tableList
(
pset
,
false
,
'i'
,
false
);
else
if
(
strncmp
(
cmd
,
"do"
,
2
)
==
0
)
{
char
descbuf
[
4096
];
/* operators */
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT o.oprname AS op, "
);
strcat
(
descbuf
,
" t1.typname AS left_arg, "
);
strcat
(
descbuf
,
" t2.typname AS right_arg, "
);
strcat
(
descbuf
,
" t0.typname AS result, "
);
if
(
screen_size
.
ws_col
<=
80
)
strcat
(
descbuf
,
" substr(obj_description(p.oid),1,41) as description "
);
else
strcat
(
descbuf
,
" obj_description(p.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_proc p, pg_type t0, "
);
strcat
(
descbuf
,
" pg_type t1, pg_type t2, "
);
strcat
(
descbuf
,
" pg_operator o "
);
strcat
(
descbuf
,
"WHERE p.prorettype = t0.oid AND "
);
strcat
(
descbuf
,
" RegprocToOid(o.oprcode) = p.oid AND "
);
strcat
(
descbuf
,
" p.pronargs = 2 AND "
);
strcat
(
descbuf
,
" o.oprleft = t1.oid AND "
);
strcat
(
descbuf
,
" o.oprright = t2.oid "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND o.oprname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"UNION "
);
strcat
(
descbuf
,
"SELECT o.oprname as op, "
);
strcat
(
descbuf
,
" ''::name AS left_arg, "
);
strcat
(
descbuf
,
" t1.typname AS right_arg, "
);
strcat
(
descbuf
,
" t0.typname AS result, "
);
if
(
screen_size
.
ws_col
<=
80
)
strcat
(
descbuf
,
" substr(obj_description(p.oid),1,41) as description "
);
else
strcat
(
descbuf
,
" obj_description(p.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 "
);
strcat
(
descbuf
,
"WHERE RegprocToOid(o.oprcode) = p.oid AND "
);
strcat
(
descbuf
,
" o.oprresult = t0.oid AND "
);
strcat
(
descbuf
,
" o.oprkind = 'l' AND "
);
strcat
(
descbuf
,
" o.oprright = t1.oid "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND o.oprname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"UNION "
);
strcat
(
descbuf
,
"SELECT o.oprname as op, "
);
strcat
(
descbuf
,
" t1.typname AS left_arg, "
);
strcat
(
descbuf
,
" ''::name AS right_arg, "
);
strcat
(
descbuf
,
" t0.typname AS result, "
);
if
(
screen_size
.
ws_col
<=
80
)
strcat
(
descbuf
,
" substr(obj_description(p.oid),1,41) as description "
);
else
strcat
(
descbuf
,
" obj_description(p.oid) as description "
);
strcat
(
descbuf
,
"FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 "
);
strcat
(
descbuf
,
"WHERE RegprocToOid(o.oprcode) = p.oid AND "
);
strcat
(
descbuf
,
" o.oprresult = t0.oid AND "
);
strcat
(
descbuf
,
" o.oprkind = 'r' AND "
);
strcat
(
descbuf
,
" o.oprleft = t1.oid "
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND o.oprname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
strcat
(
descbuf
,
"ORDER BY op, left_arg, right_arg, result;"
);
success
=
SendQuery
(
pset
,
descbuf
,
NULL
,
NULL
);
}
else
if
(
strncmp
(
cmd
,
"ds"
,
2
)
==
0
)
/* only sequences */
tableList
(
pset
,
false
,
'S'
,
false
);
else
if
(
strncmp
(
cmd
,
"dS"
,
2
)
==
0
)
/* system tables */
tableList
(
pset
,
false
,
'b'
,
true
);
else
if
(
strncmp
(
cmd
,
"dt"
,
2
)
==
0
)
/* only tables */
tableList
(
pset
,
false
,
't'
,
false
);
else
if
(
strncmp
(
cmd
,
"dT"
,
2
)
==
0
)
{
char
descbuf
[
4096
];
/* types */
descbuf
[
0
]
=
'\0'
;
strcat
(
descbuf
,
"SELECT typname AS type, "
);
strcat
(
descbuf
,
" obj_description(oid) as description "
);
strcat
(
descbuf
,
"FROM pg_type "
);
strcat
(
descbuf
,
"WHERE typrelid = 0 AND "
);
strcat
(
descbuf
,
" typname !~ '^_.*' "
);
strcat
(
descbuf
,
"ORDER BY type;"
);
if
(
optarg2
)
{
strcat
(
descbuf
,
"AND typname ~ '^"
);
strcat
(
descbuf
,
optarg2
);
strcat
(
descbuf
,
"' "
);
}
success
=
SendQuery
(
pset
,
descbuf
,
NULL
,
NULL
);
}
else
if
(
!
optarg
)
/* show tables, sequences and indices */
tableList
(
pset
,
false
,
'b'
,
false
);
else
if
(
strcmp
(
optarg
,
"*"
)
==
0
)
{
/* show everything */
if
(
tableList
(
pset
,
false
,
'b'
,
false
)
==
0
)
tableList
(
pset
,
true
,
'b'
,
false
);
}
else
if
(
strncmp
(
cmd
,
"d "
,
2
)
==
0
)
/* describe the specified table */
tableDesc
(
pset
,
optarg
,
NULL
);
else
slashUsage
(
pset
);
break
;
case
'e'
:
/* edit */
if
(
query_buf
)
do_edit
(
optarg
,
query_buf
,
&
status
);
break
;
case
'E'
:
{
FILE
*
fd
;
static
char
*
lastfile
;
struct
stat
st
,
st2
;
if
(
optarg
)
{
if
(
lastfile
)
free
(
lastfile
);
lastfile
=
malloc
(
strlen
(
optarg
+
1
));
if
(
!
lastfile
)
{
perror
(
"malloc"
);
exit
(
CMD_TERMINATE
);
}
strcpy
(
lastfile
,
optarg
);
}
else
if
(
!
lastfile
)
{
fprintf
(
stderr
,
"
\\
r must be followed by a file name initially
\n
"
);
break
;
}
stat
(
lastfile
,
&
st
);
editFile
(
lastfile
);
#ifndef __CYGWIN32__
if
((
stat
(
lastfile
,
&
st2
)
==
-
1
)
||
((
fd
=
fopen
(
lastfile
,
"r"
))
==
NULL
))
#else
if
((
stat
(
lastfile
,
&
st2
)
==
-
1
)
||
((
fd
=
fopen
(
lastfile
,
"rb"
))
==
NULL
))
#endif
{
perror
(
lastfile
);
break
;
}
if
(
st2
.
st_mtime
==
st
.
st_mtime
)
{
if
(
!
pset
->
quiet
)
fprintf
(
stderr
,
"warning: %s not modified. query not executed
\n
"
,
lastfile
);
fclose
(
fd
);
break
;
}
MainLoop
(
pset
,
fd
);
fclose
(
fd
);
break
;
}
case
'f'
:
{
char
*
fs
=
DEFAULT_FIELD_SEP
;
if
(
optarg
)
fs
=
optarg
;
/* handle \f \{space} */
if
(
optarg
&&
!*
optarg
&&
strlen
(
cmd
)
>
1
)
{
int
i
;
/* line and cmd match until the first blank space */
for
(
i
=
2
;
isspace
(
line
[
i
]);
i
++
)
;
fs
=
cmd
+
i
-
1
;
}
if
(
pset
->
opt
.
fieldSep
)
free
(
pset
->
opt
.
fieldSep
);
if
(
!
(
pset
->
opt
.
fieldSep
=
strdup
(
fs
)))
{
perror
(
"malloc"
);
exit
(
CMD_TERMINATE
);
}
if
(
!
pset
->
quiet
)
printf
(
"field separator changed to '%s'
\n
"
,
pset
->
opt
.
fieldSep
);
break
;
}
case
'g'
:
/* \g means send query */
if
(
!
optarg
)
pset
->
gfname
=
NULL
;
else
if
(
!
(
pset
->
gfname
=
strdup
(
optarg
)))
{
perror
(
"malloc"
);
exit
(
CMD_TERMINATE
);
}
status
=
CMD_SEND
;
break
;
case
'h'
:
/* help */
{
do_help
(
pset
,
optarg
);
break
;
}
case
'i'
:
/* \i is include file */
{
FILE
*
fd
;
if
(
!
optarg
)
{
fprintf
(
stderr
,
"
\\
i must be followed by a file name
\n
"
);
break
;
}
#ifndef __CYGWIN32__
if
((
fd
=
fopen
(
optarg
,
"r"
))
==
NULL
)
#else
if
((
fd
=
fopen
(
optarg
,
"rb"
))
==
NULL
)
#endif
{
fprintf
(
stderr
,
"file named %s could not be opened
\n
"
,
optarg
);
break
;
}
MainLoop
(
pset
,
fd
);
fclose
(
fd
);
break
;
}
case
'H'
:
if
(
toggle
(
pset
,
&
pset
->
opt
.
html3
,
"HTML3.0 tabular output"
))
pset
->
opt
.
standard
=
0
;
break
;
case
'l'
:
/* \l is list database */
listAllDbs
(
pset
);
break
;
case
'm'
:
/* monitor like type-setting */
if
(
toggle
(
pset
,
&
pset
->
opt
.
standard
,
"standard SQL separaters and padding"
))
{
pset
->
opt
.
html3
=
pset
->
opt
.
expanded
=
0
;
pset
->
opt
.
align
=
pset
->
opt
.
header
=
1
;
if
(
pset
->
opt
.
fieldSep
)
free
(
pset
->
opt
.
fieldSep
);
pset
->
opt
.
fieldSep
=
strdup
(
"|"
);
if
(
!
pset
->
quiet
)
printf
(
"field separator changed to '%s'
\n
"
,
pset
->
opt
.
fieldSep
);
}
else
{
if
(
pset
->
opt
.
fieldSep
)
free
(
pset
->
opt
.
fieldSep
);
pset
->
opt
.
fieldSep
=
strdup
(
DEFAULT_FIELD_SEP
);
if
(
!
pset
->
quiet
)
printf
(
"field separator changed to '%s'
\n
"
,
pset
->
opt
.
fieldSep
);
}
break
;
case
'o'
:
setFout
(
pset
,
optarg
);
break
;
case
'p'
:
if
(
query_buf
&&
query_buf
->
len
>
0
)
{
fputs
(
query_buf
->
data
,
stdout
);
fputc
(
'\n'
,
stdout
);
}
break
;
case
'q'
:
/* \q is quit */
status
=
CMD_TERMINATE
;
break
;
case
'r'
:
/* reset(clear) the buffer */
if
(
query_buf
)
{
resetPQExpBuffer
(
query_buf
);
if
(
!
pset
->
quiet
)
printf
(
"buffer reset(cleared)
\n
"
);
}
break
;
case
's'
:
/* \s is save history to a file */
if
(
!
optarg
)
optarg
=
"/dev/tty"
;
#ifdef USE_HISTORY
if
(
write_history
(
optarg
)
!=
0
)
fprintf
(
stderr
,
"cannot write history to %s
\n
"
,
optarg
);
#endif
break
;
case
't'
:
/* toggle headers */
toggle
(
pset
,
&
pset
->
opt
.
header
,
"output headings and row count"
);
break
;
case
'T'
:
/* define html <table ...> option */
if
(
pset
->
opt
.
tableOpt
)
free
(
pset
->
opt
.
tableOpt
);
if
(
!
optarg
)
pset
->
opt
.
tableOpt
=
NULL
;
else
if
(
!
(
pset
->
opt
.
tableOpt
=
strdup
(
optarg
)))
{
perror
(
"malloc"
);
exit
(
CMD_TERMINATE
);
}
break
;
case
'w'
:
{
FILE
*
fd
;
if
(
!
optarg
)
{
fprintf
(
stderr
,
"
\\
w must be followed by a file name
\n
"
);
break
;
}
#ifndef __CYGWIN32__
if
((
fd
=
fopen
(
optarg
,
"w"
))
==
NULL
)
#else
if
((
fd
=
fopen
(
optarg
,
"w"
))
==
NULL
)
#endif
{
fprintf
(
stderr
,
"file named %s could not be opened
\n
"
,
optarg
);
break
;
}
if
(
query_buf
)
fputs
(
query_buf
->
data
,
fd
);
fputc
(
'\n'
,
fd
);
fclose
(
fd
);
break
;
}
case
'x'
:
toggle
(
pset
,
&
pset
->
opt
.
expanded
,
"expanded table representation"
);
break
;
case
'z'
:
/* list table rights (grant/revoke) */
rightsList
(
pset
);
break
;
case
'!'
:
do_shell
(
optarg
);
break
;
default:
case
'?'
:
/* \? is help */
slashUsage
(
pset
);
break
;
}
free
(
cmd
);
return
status
;
}
/* MainLoop()
* Main processing loop for reading lines of input
* and sending them to the backend.
*
* This loop is re-entrant. May be called by \i command
* which reads input from a file.
* db_ptr must be initialized and set.
*/
static
int
MainLoop
(
PsqlSettings
*
pset
,
FILE
*
source
)
{
PQExpBuffer
query_buf
;
/* buffer for query being accumulated */
char
*
line
;
/* current line of input */
char
*
xcomment
;
/* start of extended comment */
int
len
;
/* length of the line */
int
successResult
=
1
;
int
slashCmdStatus
=
CMD_SEND
;
/*--------------------------------------------------------------
* slashCmdStatus can be:
* CMD_UNKNOWN - send currently constructed query to backend
* (i.e. we got a \g)
* CMD_SEND - send currently constructed query to backend
* (i.e. we got a \g)
* CMD_SKIP_LINE - skip processing of this line, continue building
* up query
* CMD_TERMINATE - terminate processing of this query entirely
* CMD_NEWEDIT - new query supplied by edit
*---------------------------------------------------------------
*/
bool
querySent
=
false
;
READ_ROUTINE
GetNextLine
;
bool
eof
=
false
;
/* end of our command input? */
bool
success
;
char
in_quote
;
/* == 0 for no in_quote */
bool
was_bslash
;
/* backslash */
int
paren_level
;
char
*
query_start
;
/* Stack the prior command source */
FILE
*
prev_cmd_source
=
cur_cmd_source
;
bool
prev_cmd_interactive
=
cur_cmd_interactive
;
/* Establish new source */
cur_cmd_source
=
source
;
cur_cmd_interactive
=
((
source
==
stdin
)
&&
!
pset
->
notty
);
if
(
cur_cmd_interactive
)
{
if
(
pset
->
prompt
)
free
(
pset
->
prompt
);
pset
->
prompt
=
malloc
(
strlen
(
PQdb
(
pset
->
db
))
+
strlen
(
PROMPT
)
+
1
);
if
(
pset
->
quiet
)
pset
->
prompt
[
0
]
=
'\0'
;
else
sprintf
(
pset
->
prompt
,
"%s%s"
,
PQdb
(
pset
->
db
),
PROMPT
);
if
(
pset
->
useReadline
)
{
#ifdef USE_HISTORY
using_history
();
#endif
GetNextLine
=
gets_readline
;
}
else
GetNextLine
=
gets_noreadline
;
}
else
GetNextLine
=
gets_fromFile
;
query_buf
=
createPQExpBuffer
();
xcomment
=
NULL
;
in_quote
=
false
;
paren_level
=
0
;
slashCmdStatus
=
CMD_UNKNOWN
;
/* set default */
/* main loop to get queries and execute them */
while
(
!
eof
)
{
if
(
slashCmdStatus
==
CMD_NEWEDIT
)
{
/*
* just returned from editing the line? then just copy to the
* input buffer
*/
line
=
strdup
(
query_buf
->
data
);
resetPQExpBuffer
(
query_buf
);
/* reset parsing state since we are rescanning whole query */
xcomment
=
NULL
;
in_quote
=
false
;
paren_level
=
0
;
}
else
{
/*
* otherwise, set interactive prompt if necessary
* and get another line
*/
if
(
cur_cmd_interactive
&&
!
pset
->
quiet
)
{
if
(
in_quote
&&
in_quote
==
PROMPT_SINGLEQUOTE
)
pset
->
prompt
[
strlen
(
pset
->
prompt
)
-
3
]
=
PROMPT_SINGLEQUOTE
;
else
if
(
in_quote
&&
in_quote
==
PROMPT_DOUBLEQUOTE
)
pset
->
prompt
[
strlen
(
pset
->
prompt
)
-
3
]
=
PROMPT_DOUBLEQUOTE
;
else
if
(
xcomment
!=
NULL
)
pset
->
prompt
[
strlen
(
pset
->
prompt
)
-
3
]
=
PROMPT_COMMENT
;
else
if
(
query_buf
->
len
>
0
&&
!
querySent
)
pset
->
prompt
[
strlen
(
pset
->
prompt
)
-
3
]
=
PROMPT_CONTINUE
;
else
pset
->
prompt
[
strlen
(
pset
->
prompt
)
-
3
]
=
PROMPT_READY
;
}
line
=
GetNextLine
(
pset
->
prompt
,
source
);
#ifdef USE_HISTORY
if
(
cur_cmd_interactive
&&
pset
->
useReadline
&&
line
!=
NULL
)
add_history
(
line
);
/* save non-empty lines in history */
#endif
}
/*
* query_buf holds query already accumulated. line is the malloc'd
* new line of input (note it must be freed before looping around!)
* query_start is the next command start location within the line.
*/
if
(
line
==
NULL
||
(
!
cur_cmd_interactive
&&
*
line
==
'\0'
))
{
/* No more input. Time to quit, or \i
* done */
if
(
!
pset
->
quiet
)
printf
(
"EOF
\n
"
);
/* Goes on prompt line */
eof
=
true
;
continue
;
}
/* not currently inside an extended comment? */
if
(
xcomment
==
NULL
)
{
query_start
=
line
;
}
else
{
/* otherwise, continue the extended comment... */
query_start
=
line
;
xcomment
=
line
;
}
/* remove whitespaces on the right, incl. \n's */
line
=
rightTrim
(
line
);
/* echo back if input is from file */
if
(
!
cur_cmd_interactive
&&
!
pset
->
singleStep
&&
!
pset
->
quiet
)
fprintf
(
stderr
,
"%s
\n
"
,
line
);
slashCmdStatus
=
CMD_UNKNOWN
;
/* nothing on line after trimming? then ignore */
if
(
line
[
0
]
==
'\0'
)
{
free
(
line
);
continue
;
}
len
=
strlen
(
line
);
if
(
pset
->
singleLineMode
)
{
success
=
SendQuery
(
pset
,
line
,
NULL
,
NULL
);
successResult
&=
success
;
querySent
=
true
;
}
else
{
int
i
;
/*
* Parse line, looking for command separators.
*
* The current character is at line[i], the prior character at
* line[i - prevlen], the next character at line[i + thislen].
*/
#ifdef MULTIBYTE
int
prevlen
=
0
;
int
thislen
=
(
len
>
0
)
?
PQmblen
(
line
)
:
0
;
#define ADVANCE_I (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
#else
#define prevlen 1
#define thislen 1
#define ADVANCE_I (i++)
#endif
was_bslash
=
false
;
for
(
i
=
0
;
i
<
len
;
ADVANCE_I
)
{
if
(
line
[
i
]
==
'\\'
&&
!
in_quote
)
{
/* backslash command. Copy whatever is before \ to
* query_buf.
*/
char
hold_char
=
line
[
i
];
line
[
i
]
=
'\0'
;
if
(
query_start
[
0
]
!=
'\0'
)
{
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
appendPQExpBufferStr
(
query_buf
,
query_start
);
}
line
[
i
]
=
hold_char
;
query_start
=
line
+
i
;
break
;
/* go handle backslash command */
}
if
(
querySent
&&
isascii
((
unsigned
char
)
(
line
[
i
]))
&&
!
isspace
(
line
[
i
]))
{
resetPQExpBuffer
(
query_buf
);
querySent
=
false
;
}
if
(
was_bslash
)
was_bslash
=
false
;
else
if
(
i
>
0
&&
line
[
i
-
prevlen
]
==
'\\'
)
was_bslash
=
true
;
/* inside a quote? */
if
(
in_quote
&&
(
line
[
i
]
!=
in_quote
||
was_bslash
))
/* do nothing */
;
/* inside an extended comment? */
else
if
(
xcomment
!=
NULL
)
{
if
(
line
[
i
]
==
'*'
&&
line
[
i
+
thislen
]
==
'/'
)
{
xcomment
=
NULL
;
ADVANCE_I
;
}
}
/* start of extended comment? */
else
if
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'*'
)
{
xcomment
=
line
+
i
;
ADVANCE_I
;
}
/* single-line comment? truncate line */
else
if
((
line
[
i
]
==
'-'
&&
line
[
i
+
thislen
]
==
'-'
)
||
(
line
[
i
]
==
'/'
&&
line
[
i
+
thislen
]
==
'/'
))
{
/* print comment at top of query */
if
(
pset
->
singleStep
)
fprintf
(
stdout
,
"%s
\n
"
,
line
+
i
);
line
[
i
]
=
'\0'
;
/* remove comment */
break
;
}
else
if
(
in_quote
&&
line
[
i
]
==
in_quote
)
in_quote
=
false
;
else
if
(
!
in_quote
&&
(
line
[
i
]
==
'\''
||
line
[
i
]
==
'"'
))
in_quote
=
line
[
i
];
/* semi-colon? then send query now */
else
if
(
!
paren_level
&&
line
[
i
]
==
';'
)
{
char
hold_char
=
line
[
i
+
thislen
];
line
[
i
+
thislen
]
=
'\0'
;
if
(
query_start
[
0
]
!=
'\0'
)
{
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
appendPQExpBufferStr
(
query_buf
,
query_start
);
}
success
=
SendQuery
(
pset
,
query_buf
->
data
,
NULL
,
NULL
);
successResult
&=
success
;
line
[
i
+
thislen
]
=
hold_char
;
query_start
=
line
+
i
+
thislen
;
/* sometimes, people do ';\g', don't execute twice */
if
(
*
query_start
==
'\\'
&&
*
(
query_start
+
1
)
==
'g'
)
query_start
+=
2
;
querySent
=
true
;
}
else
if
(
line
[
i
]
==
'('
)
{
paren_level
++
;
}
else
if
(
paren_level
&&
line
[
i
]
==
')'
)
paren_level
--
;
}
}
/* nothing on line after trimming? then ignore */
if
(
line
[
0
]
==
'\0'
)
{
free
(
line
);
continue
;
}
if
(
!
in_quote
&&
query_start
[
0
]
==
'\\'
)
{
/* loop to handle \p\g and other backslash combinations */
while
(
query_start
[
0
]
!=
'\0'
)
{
char
hold_char
;
#ifndef WIN32
/* I believe \w \dos\system\x would cause a problem */
/* do we have '\p\g' or '\p \g' ? */
if
(
strlen
(
query_start
)
>
2
&&
query_start
[
2
+
strspn
(
query_start
+
2
,
"
\t
"
)]
==
'\\'
)
{
hold_char
=
query_start
[
2
+
strspn
(
query_start
+
2
,
"
\t
"
)];
query_start
[
2
+
strspn
(
query_start
+
2
,
"
\t
"
)]
=
'\0'
;
}
else
/* spread over #endif */
#endif
hold_char
=
'\0'
;
slashCmdStatus
=
HandleSlashCmds
(
pset
,
query_start
,
query_buf
);
if
(
slashCmdStatus
==
CMD_SKIP_LINE
&&
!
hold_char
)
{
if
(
query_buf
->
len
==
0
)
paren_level
=
0
;
break
;
}
if
(
slashCmdStatus
==
CMD_TERMINATE
)
break
;
query_start
+=
strlen
(
query_start
);
if
(
hold_char
)
query_start
[
0
]
=
hold_char
;
}
free
(
line
);
if
(
slashCmdStatus
==
CMD_TERMINATE
)
break
;
/* They did \q, leave the loop */
}
else
{
if
(
query_start
[
0
]
!=
'\0'
)
{
querySent
=
false
;
if
(
query_buf
->
len
>
0
)
appendPQExpBufferChar
(
query_buf
,
'\n'
);
appendPQExpBufferStr
(
query_buf
,
query_start
);
}
free
(
line
);
}
/* had a backslash-g? force the query to be sent */
if
(
slashCmdStatus
==
CMD_SEND
)
{
success
=
SendQuery
(
pset
,
query_buf
->
data
,
NULL
,
NULL
);
successResult
&=
success
;
xcomment
=
NULL
;
in_quote
=
false
;
paren_level
=
0
;
querySent
=
true
;
}
}
/* while */
destroyPQExpBuffer
(
query_buf
);
cur_cmd_source
=
prev_cmd_source
;
cur_cmd_interactive
=
prev_cmd_interactive
;
return
successResult
;
}
/* MainLoop() */
int
main
(
int
argc
,
char
**
argv
)
{
extern
char
*
optarg
;
extern
int
optind
;
char
*
dbname
=
NULL
;
char
*
host
=
NULL
;
char
*
port
=
NULL
;
char
*
qfilename
=
NULL
;
PsqlSettings
settings
;
char
*
singleQuery
=
NULL
;
bool
listDatabases
=
0
;
int
successResult
=
1
;
bool
singleSlashCmd
=
0
;
int
c
;
char
*
home
=
NULL
;
/* Used to store $HOME */
char
*
version
=
NULL
;
/* PostgreSQL version */
/*
* initialize cur_cmd_source in case we do not use MainLoop ... some
* systems fail if we try to use a static initializer for this :-(
*/
cur_cmd_source
=
stdin
;
cur_cmd_interactive
=
false
;
MemSet
(
&
settings
,
0
,
sizeof
settings
);
settings
.
opt
.
align
=
1
;
settings
.
opt
.
header
=
1
;
settings
.
queryFout
=
stdout
;
settings
.
opt
.
fieldSep
=
strdup
(
DEFAULT_FIELD_SEP
);
settings
.
opt
.
pager
=
1
;
if
(
!
isatty
(
0
)
||
!
isatty
(
1
))
{
/* Noninteractive defaults */
settings
.
notty
=
1
;
}
else
{
/* Interactive defaults */
pqsignal
(
SIGINT
,
handle_sigint
);
/* control-C => cancel */
#ifdef USE_READLINE
settings
.
useReadline
=
1
;
{
/* Set the application name, used for parsing .inputrc */
char
*
progname
=
strrchr
(
argv
[
0
],
SEP_CHAR
);
rl_readline_name
=
(
progname
?
progname
+
1
:
argv
[
0
]);
}
#endif
}
#ifdef PSQL_ALWAYS_GET_PASSWORDS
settings
.
getPassword
=
1
;
#else
settings
.
getPassword
=
0
;
#endif
#ifdef MULTIBYTE
has_client_encoding
=
getenv
(
"PGCLIENTENCODING"
);
#endif
while
((
c
=
getopt
(
argc
,
argv
,
"Aa:c:d:eEf:F:lh:Hnso:p:qStT:ux"
))
!=
EOF
)
{
switch
(
c
)
{
case
'A'
:
settings
.
opt
.
align
=
0
;
break
;
case
'a'
:
#ifdef NOT_USED
/* this no longer does anything */
fe_setauthsvc
(
optarg
,
errbuf
);
#endif
break
;
case
'c'
:
singleQuery
=
strdup
(
optarg
);
if
(
singleQuery
[
0
]
==
'\\'
)
singleSlashCmd
=
1
;
break
;
case
'd'
:
dbname
=
optarg
;
break
;
case
'e'
:
settings
.
echoQuery
=
1
;
break
;
case
'E'
:
settings
.
echoAllQueries
=
1
;
settings
.
echoQuery
=
1
;
break
;
case
'f'
:
qfilename
=
optarg
;
break
;
case
'F'
:
settings
.
opt
.
fieldSep
=
strdup
(
optarg
);
break
;
case
'l'
:
listDatabases
=
1
;
break
;
case
'h'
:
host
=
optarg
;
break
;
case
'H'
:
settings
.
opt
.
html3
=
1
;
break
;
case
'n'
:
settings
.
useReadline
=
0
;
break
;
case
'o'
:
setFout
(
&
settings
,
optarg
);
break
;
case
'p'
:
port
=
optarg
;
break
;
case
'q'
:
settings
.
quiet
=
1
;
break
;
case
's'
:
settings
.
singleStep
=
1
;
break
;
case
'S'
:
settings
.
singleLineMode
=
1
;
break
;
case
't'
:
settings
.
opt
.
header
=
0
;
break
;
case
'T'
:
settings
.
opt
.
tableOpt
=
strdup
(
optarg
);
break
;
case
'u'
:
settings
.
getPassword
=
1
;
break
;
case
'x'
:
settings
.
opt
.
expanded
=
1
;
break
;
default:
usage
(
argv
[
0
]);
break
;
}
}
/* if we still have an argument, use it as the database name */
if
(
argc
-
optind
==
1
)
dbname
=
argv
[
optind
];
if
(
listDatabases
)
dbname
=
"template1"
;
if
(
settings
.
getPassword
)
{
char
username
[
100
];
char
password
[
100
];
prompt_for_password
(
username
,
password
);
settings
.
db
=
PQsetdbLogin
(
host
,
port
,
NULL
,
NULL
,
dbname
,
username
,
password
);
}
else
settings
.
db
=
PQsetdb
(
host
,
port
,
NULL
,
NULL
,
dbname
);
dbname
=
PQdb
(
settings
.
db
);
if
(
PQstatus
(
settings
.
db
)
==
CONNECTION_BAD
)
{
fprintf
(
stderr
,
"Connection to database '%s' failed.
\n
"
,
dbname
);
fprintf
(
stderr
,
"%s
\n
"
,
PQerrorMessage
(
settings
.
db
));
PQfinish
(
settings
.
db
);
exit
(
1
);
}
cancelConn
=
settings
.
db
;
/* enable SIGINT to send cancel */
if
(
listDatabases
)
exit
(
listAllDbs
(
&
settings
));
if
(
!
settings
.
quiet
&&
!
settings
.
notty
&&
!
singleQuery
&&
!
qfilename
)
{
printf
(
"Welcome to the POSTGRESQL interactive sql monitor:
\n
"
);
printf
(
" Please read the file COPYRIGHT for copyright terms "
"of POSTGRESQL
\n
"
);
if
((
version
=
selectVersion
(
&
settings
))
!=
NULL
)
printf
(
"[%s]
\n
"
,
version
);
printf
(
"
\n
"
);
printf
(
" type
\\
? for help on slash commands
\n
"
);
printf
(
" type
\\
q to quit
\n
"
);
printf
(
" type
\\
g or terminate with semicolon to execute query
\n
"
);
printf
(
" You are currently connected to the database: %s
\n\n
"
,
dbname
);
}
/*
* 20.06.97 ACRM See if we've got a /etc/psqlrc or .psqlrc file
*/
if
(
!
access
(
"/etc/psqlrc"
,
R_OK
))
HandleSlashCmds
(
&
settings
,
"
\\
i /etc/psqlrc"
,
NULL
);
if
((
home
=
getenv
(
"HOME"
))
!=
NULL
)
{
char
*
psqlrc
=
NULL
,
*
line
=
NULL
;
if
((
psqlrc
=
(
char
*
)
malloc
(
strlen
(
home
)
+
10
))
!=
NULL
)
{
sprintf
(
psqlrc
,
"%s/.psqlrc"
,
home
);
if
(
!
access
(
psqlrc
,
R_OK
))
{
if
((
line
=
(
char
*
)
malloc
(
strlen
(
psqlrc
)
+
5
))
!=
NULL
)
{
sprintf
(
line
,
"
\\
i %s"
,
psqlrc
);
HandleSlashCmds
(
&
settings
,
line
,
NULL
);
free
(
line
);
}
}
free
(
psqlrc
);
}
}
/* End of check for psqlrc files */
if
(
qfilename
||
singleSlashCmd
)
{
/*
* read in a file full of queries instead of reading in queries
* interactively
*/
char
*
line
;
if
(
singleSlashCmd
)
{
/* Not really a query, but "Do what I mean, not what I say." */
line
=
singleQuery
;
}
else
{
line
=
malloc
(
strlen
(
qfilename
)
+
5
);
sprintf
(
line
,
"
\\
i %s"
,
qfilename
);
}
HandleSlashCmds
(
&
settings
,
line
,
NULL
);
free
(
line
);
}
else
{
if
(
singleQuery
)
successResult
=
SendQuery
(
&
settings
,
singleQuery
,
NULL
,
NULL
);
else
successResult
=
MainLoop
(
&
settings
,
stdin
);
}
PQfinish
(
settings
.
db
);
free
(
settings
.
opt
.
fieldSep
);
if
(
settings
.
prompt
)
free
(
settings
.
prompt
);
return
!
successResult
;
}
static
bool
handleCopyOut
(
PGconn
*
conn
,
FILE
*
copystream
)
{
bool
copydone
;
char
copybuf
[
COPYBUFSIZ
];
int
ret
;
copydone
=
false
;
/* Can't be done; haven't started. */
while
(
!
copydone
)
{
ret
=
PQgetline
(
conn
,
copybuf
,
COPYBUFSIZ
);
if
(
copybuf
[
0
]
==
'\\'
&&
copybuf
[
1
]
==
'.'
&&
copybuf
[
2
]
==
'\0'
)
{
copydone
=
true
;
/* don't print this... */
}
else
{
fputs
(
copybuf
,
copystream
);
switch
(
ret
)
{
case
EOF
:
copydone
=
true
;
/* FALLTHROUGH */
case
0
:
fputc
(
'\n'
,
copystream
);
break
;
case
1
:
break
;
}
}
}
fflush
(
copystream
);
return
!
PQendcopy
(
conn
);
}
static
bool
handleCopyIn
(
PGconn
*
conn
,
const
bool
mustprompt
,
FILE
*
copystream
)
{
bool
copydone
=
false
;
bool
firstload
;
bool
linedone
;
char
copybuf
[
COPYBUFSIZ
];
char
*
s
;
int
buflen
;
int
c
=
0
;
if
(
mustprompt
)
{
fputs
(
"Enter info followed by a newline
\n
"
,
stdout
);
fputs
(
"End with a backslash and a "
"period on a line by itself.
\n
"
,
stdout
);
}
while
(
!
copydone
)
{
/* for each input line ... */
if
(
mustprompt
)
{
fputs
(
">> "
,
stdout
);
fflush
(
stdout
);
}
firstload
=
true
;
linedone
=
false
;
while
(
!
linedone
)
{
/* for each buffer ... */
s
=
copybuf
;
for
(
buflen
=
COPYBUFSIZ
;
buflen
>
1
;
buflen
--
)
{
c
=
getc
(
copystream
);
if
(
c
==
'\n'
||
c
==
EOF
)
{
linedone
=
true
;
break
;
}
*
s
++
=
c
;
}
*
s
=
'\0'
;
if
(
c
==
EOF
)
{
PQputline
(
conn
,
"
\\
."
);
copydone
=
true
;
break
;
}
PQputline
(
conn
,
copybuf
);
if
(
firstload
)
{
if
(
!
strcmp
(
copybuf
,
"
\\
."
))
copydone
=
true
;
firstload
=
false
;
}
}
PQputline
(
conn
,
"
\n
"
);
}
return
!
PQendcopy
(
conn
);
}
/*
* try to open fname and return a FILE *, if it fails, use stdout, instead
*/
static
FILE
*
setFout
(
PsqlSettings
*
pset
,
char
*
fname
)
{
if
(
pset
->
queryFout
&&
pset
->
queryFout
!=
stdout
)
{
if
(
pset
->
pipe
)
pclose
(
pset
->
queryFout
);
else
fclose
(
pset
->
queryFout
);
}
if
(
!
fname
)
{
pset
->
queryFout
=
stdout
;
pqsignal
(
SIGPIPE
,
SIG_DFL
);
}
else
{
if
(
*
fname
==
'|'
)
{
pqsignal
(
SIGPIPE
,
SIG_IGN
);
#ifndef __CYGWIN32__
pset
->
queryFout
=
popen
(
fname
+
1
,
"w"
);
#else
pset
->
queryFout
=
popen
(
fname
+
1
,
"wb"
);
#endif
pset
->
pipe
=
1
;
}
else
{
pset
->
queryFout
=
fopen
(
fname
,
"w"
);
pqsignal
(
SIGPIPE
,
SIG_DFL
);
pset
->
pipe
=
0
;
}
if
(
!
pset
->
queryFout
)
{
perror
(
fname
);
pset
->
queryFout
=
stdout
;
}
}
return
pset
->
queryFout
;
}
static
void
prompt_for_password
(
char
*
username
,
char
*
password
)
{
char
buf
[
512
];
int
length
;
#ifdef HAVE_TERMIOS_H
struct
termios
t_orig
,
t
;
#endif
printf
(
"Username: "
);
fgets
(
username
,
100
,
stdin
);
length
=
strlen
(
username
);
/* skip rest of the line */
if
(
length
>
0
&&
username
[
length
-
1
]
!=
'\n'
)
{
do
{
fgets
(
buf
,
512
,
stdin
);
}
while
(
buf
[
strlen
(
buf
)
-
1
]
!=
'\n'
);
}
if
(
length
>
0
&&
username
[
length
-
1
]
==
'\n'
)
username
[
length
-
1
]
=
'\0'
;
printf
(
"Password: "
);
#ifdef HAVE_TERMIOS_H
tcgetattr
(
0
,
&
t
);
t_orig
=
t
;
t
.
c_lflag
&=
~
ECHO
;
tcsetattr
(
0
,
TCSADRAIN
,
&
t
);
#endif
fgets
(
password
,
100
,
stdin
);
#ifdef HAVE_TERMIOS_H
tcsetattr
(
0
,
TCSADRAIN
,
&
t_orig
);
#endif
length
=
strlen
(
password
);
/* skip rest of the line */
if
(
length
>
0
&&
password
[
length
-
1
]
!=
'\n'
)
{
do
{
fgets
(
buf
,
512
,
stdin
);
}
while
(
buf
[
strlen
(
buf
)
-
1
]
!=
'\n'
);
}
if
(
length
>
0
&&
password
[
length
-
1
]
==
'\n'
)
password
[
length
-
1
]
=
'\0'
;
printf
(
"
\n\n
"
);
}
static
char
*
selectVersion
(
PsqlSettings
*
pset
)
{
#define PGVERSIONBUFSZ 128
static
char
version
[
PGVERSIONBUFSZ
+
1
];
PGresult
*
res
;
char
*
query
=
"select version();"
;
if
(
!
(
res
=
PQexec
(
pset
->
db
,
query
)))
return
(
NULL
);
if
(
PQresultStatus
(
res
)
==
PGRES_COMMAND_OK
||
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
)
{
strncpy
(
version
,
PQgetvalue
(
res
,
0
,
0
),
PGVERSIONBUFSZ
);
version
[
PGVERSIONBUFSZ
]
=
'\0'
;
PQclear
(
res
);
return
(
version
);
}
else
{
PQclear
(
res
);
return
(
NULL
);
}
}
src/bin/psql/psqlHelp.h
View file @
0e6652e6
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
*
*
* Copyright (c) 1994, Regents of the University of California
* Copyright (c) 1994, Regents of the University of California
*
*
* $Id: psqlHelp.h,v 1.8
0 1999/10/29 23:52:22
momjian Exp $
* $Id: psqlHelp.h,v 1.8
1 1999/11/04 23:14:29
momjian Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -384,5 +384,6 @@ TIMEZONE|XACTISOLEVEL|CLIENT_ENCODING|SERVER_ENCODING"},
...
@@ -384,5 +384,6 @@ TIMEZONE|XACTISOLEVEL|CLIENT_ENCODING|SERVER_ENCODING"},
\t
VACUUM [VERBOSE] [ANALYZE] [table]
\n
\
\t
VACUUM [VERBOSE] [ANALYZE] [table]
\n
\
\t
or
\n
\
\t
or
\n
\
\t
VACUUM [VERBOSE] ANALYZE [table [(column_name1, ...column_nameN)]];"
},
\t
VACUUM [VERBOSE] ANALYZE [table [(column_name1, ...column_nameN)]];"
},
{
NULL
,
NULL
,
NULL
}
/* important to keep a NULL terminator here!*/
{
NULL
,
NULL
,
NULL
}
/* important to keep a NULL terminator
* here! */
};
};
src/bin/psql/settings.h
View file @
0e6652e6
...
@@ -20,24 +20,27 @@
...
@@ -20,24 +20,27 @@
typedef
struct
_psqlSettings
typedef
struct
_psqlSettings
{
{
PGconn
*
db
;
/* connection to backend */
PGconn
*
db
;
/* connection to backend */
FILE
*
queryFout
;
/* where to send the query results */
FILE
*
queryFout
;
/* where to send the query results */
bool
queryFoutPipe
;
/* queryFout is from a popen() */
bool
queryFoutPipe
;
/* queryFout is from a popen() */
printQueryOpt
popt
;
printQueryOpt
popt
;
VariableSpace
vars
;
/* "shell variable" repository */
VariableSpace
vars
;
/* "shell variable" repository */
char
*
gfname
;
/* one-shot file output argument for \g */
char
*
gfname
;
/* one-shot file output argument for \g */
bool
notty
;
/* stdin or stdout is not a tty (as determined on startup) */
bool
notty
;
/* stdin or stdout is not a tty (as
bool
useReadline
;
/* use libreadline routines */
* determined on startup) */
bool
useHistory
;
bool
useReadline
;
/* use libreadline routines */
bool
getPassword
;
/* prompt the user for a username and
bool
useHistory
;
password */
bool
getPassword
;
/* prompt the user for a username and
FILE
*
cur_cmd_source
;
/* describe the status of the current main loop */
* password */
bool
cur_cmd_interactive
;
FILE
*
cur_cmd_source
;
/* describe the status of the current main
* loop */
bool
has_client_encoding
;
/* was PGCLIENTENCODING set on startup? */
bool
cur_cmd_interactive
;
bool
has_client_encoding
;
/* was PGCLIENTENCODING set on
* startup? */
}
PsqlSettings
;
}
PsqlSettings
;
...
...
src/bin/psql/sql_help.h
View file @
0e6652e6
...
@@ -7,251 +7,251 @@
...
@@ -7,251 +7,251 @@
struct
_helpStruct
struct
_helpStruct
{
{
char
*
cmd
;
/* the command name */
char
*
cmd
;
/* the command name */
char
*
help
;
/* the help associated with it */
char
*
help
;
/* the help associated with it */
char
*
syntax
;
/* the syntax associated with it */
char
*
syntax
;
/* the syntax associated with it */
};
};
static
struct
_helpStruct
QL_HELP
[]
=
{
static
struct
_helpStruct
QL_HELP
[]
=
{
{
"TRUNCATE"
,
{
"TRUNCATE"
,
"Empty a table"
,
"Empty a table"
,
"TRUNCATE [ TABLE ] name"
},
"TRUNCATE [ TABLE ] name"
},
{
"ABORT"
,
{
"ABORT"
,
"Aborts the current transaction"
,
"Aborts the current transaction"
,
"ABORT [ WORK | TRANSACTION ]"
},
"ABORT [ WORK | TRANSACTION ]"
},
{
"ALTER TABLE"
,
{
"ALTER TABLE"
,
"Modifies table properties"
,
"Modifies table properties"
,
"ALTER TABLE table
\n
[ * ] ADD [ COLUMN ] ER
\"
>coBLE> type
\n
ALTER TABLE table
\n
[ * ] RENAME [ COLUMN ] ER
\"
>coBLE> TO newcolumn
\n
ALTER TABLE table
\n
RENAME TO newtable"
},
"ALTER TABLE table
\n
[ * ] ADD [ COLUMN ] ER
\"
>coBLE> type
\n
ALTER TABLE table
\n
[ * ] RENAME [ COLUMN ] ER
\"
>coBLE> TO newcolumn
\n
ALTER TABLE table
\n
RENAME TO newtable"
},
{
"ALTER USER"
,
{
"ALTER USER"
,
"Modifies user account information"
,
"Modifies user account information"
,
"ALTER USER username [ WITH PASSWORD password ]
\n
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
\n
[ IN GROUP groupname [, ...] ]
\n
[ VALID UNTIL 'abstime' ]"
},
"ALTER USER username [ WITH PASSWORD password ]
\n
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
\n
[ IN GROUP groupname [, ...] ]
\n
[ VALID UNTIL 'abstime' ]"
},
{
"BEGIN"
,
{
"BEGIN"
,
"Begins a transaction in chained mode"
,
"Begins a transaction in chained mode"
,
"BEGIN [ WORK | TRANSACTION ]"
},
"BEGIN [ WORK | TRANSACTION ]"
},
{
"CLOSE"
,
{
"CLOSE"
,
"Close a cursor"
,
"Close a cursor"
,
"CLOSE cursor"
},
"CLOSE cursor"
},
{
"CLUSTER"
,
{
"CLUSTER"
,
"Gives storage clustering advice to the server"
,
"Gives storage clustering advice to the server"
,
"CLUSTER indexname ON table"
},
"CLUSTER indexname ON table"
},
{
"COMMIT"
,
{
"COMMIT"
,
"Commits the current transaction"
,
"Commits the current transaction"
,
"COMMIT [ WORK | TRANSACTION ]"
},
"COMMIT [ WORK | TRANSACTION ]"
},
{
"COPY"
,
{
"COPY"
,
"Copies data between files and tables"
,
"Copies data between files and tables"
,
"COPY [ BINARY ] table [ WITH OIDS ]
\n
FROM { 'filename' | stdin }
\n
[ [USING] DELIMITERS 'delimiter' ]
\n
COPY [ BINARY ] table [ WITH OIDS ]
\n
TO { 'filename' | stdout }
\n
[ [USING] DELIMITERS 'delimiter' ]"
},
"COPY [ BINARY ] table [ WITH OIDS ]
\n
FROM { 'filename' | stdin }
\n
[ [USING] DELIMITERS 'delimiter' ]
\n
COPY [ BINARY ] table [ WITH OIDS ]
\n
TO { 'filename' | stdout }
\n
[ [USING] DELIMITERS 'delimiter' ]"
},
{
"CREATE AGGREGATE"
,
{
"CREATE AGGREGATE"
,
"Defines a new aggregate function"
,
"Defines a new aggregate function"
,
"CREATE AGGREGATE name [ AS ] ( BASETYPE = data_type
\n
[ , SFUNC1 = sfunc1, STYPE1 = sfunc1_return_type ]
\n
[ , SFUNC2 = sfunc2, STYPE2 = sfunc2_return_type ]
\n
[ , FINALFUNC = ffunc ]
\n
[ , INITCOND1 = initial_condition1 ]
\n
[ , INITCOND2 = initial_condition2 ] )"
},
"CREATE AGGREGATE name [ AS ] ( BASETYPE = data_type
\n
[ , SFUNC1 = sfunc1, STYPE1 = sfunc1_return_type ]
\n
[ , SFUNC2 = sfunc2, STYPE2 = sfunc2_return_type ]
\n
[ , FINALFUNC = ffunc ]
\n
[ , INITCOND1 = initial_condition1 ]
\n
[ , INITCOND2 = initial_condition2 ] )"
},
{
"CREATE DATABASE"
,
{
"CREATE DATABASE"
,
"Creates a new database"
,
"Creates a new database"
,
"CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]"
},
"CREATE DATABASE name [ WITH LOCATION = 'dbpath' ]"
},
{
"CREATE FUNCTION"
,
{
"CREATE FUNCTION"
,
"Defines a new function"
,
"Defines a new function"
,
"CREATE FUNCTION name ( [ ftype [, ...] ] )
\n
RETURNS rtype
\n
[ WITH ( attribute [, ...] ) ]
\n
AS definition
\n
LANGUAGE 'langname'
\n\n\n
CREATE FUNCTION name ( [ ftype [, ...] ] )
\n
RETURNS rtype
\n
[ WITH ( attribute [, ...] ) ]
\n
AS obj_file , link_symbol
\n
LANGUAGE 'C'"
},
"CREATE FUNCTION name ( [ ftype [, ...] ] )
\n
RETURNS rtype
\n
[ WITH ( attribute [, ...] ) ]
\n
AS definition
\n
LANGUAGE 'langname'
\n\n\n
CREATE FUNCTION name ( [ ftype [, ...] ] )
\n
RETURNS rtype
\n
[ WITH ( attribute [, ...] ) ]
\n
AS obj_file , link_symbol
\n
LANGUAGE 'C'"
},
{
"CREATE INDEX"
,
{
"CREATE INDEX"
,
"Constructs a secondary index"
,
"Constructs a secondary index"
,
"CREATE [ UNIQUE ] INDEX index_name ON table
\n
[ USING acc_name ] ( column [ ops_name] [, ...] )
\n
CREATE [ UNIQUE ] INDEX index_name ON table
\n
[ USING acc_name ] ( func_name( r
\"
>colle> [, ... ]) ops_name )"
},
"CREATE [ UNIQUE ] INDEX index_name ON table
\n
[ USING acc_name ] ( column [ ops_name] [, ...] )
\n
CREATE [ UNIQUE ] INDEX index_name ON table
\n
[ USING acc_name ] ( func_name( r
\"
>colle> [, ... ]) ops_name )"
},
{
"CREATE LANGUAGE"
,
{
"CREATE LANGUAGE"
,
"Defines a new language for functions"
,
"Defines a new language for functions"
,
"CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'
\n
HANDLER call_handler
\n
LANCOMPILER 'comment'"
},
"CREATE [ TRUSTED ] PROCEDURAL LANGUAGE 'langname'
\n
HANDLER call_handler
\n
LANCOMPILER 'comment'"
},
{
"CREATE OPERATOR"
,
{
"CREATE OPERATOR"
,
"Defines a new user operator"
,
"Defines a new user operator"
,
"CREATE OPERATOR name ( PROCEDURE = func_name
\n
[, LEFTARG = type1 ] [, RIGHTARG = type2 ]
\n
[, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
\n
[, RESTRICT = res_proc ] [, JOIN = join_proc ]
\n
[, HASHES ] [, SORT1 = left_sort_op ] [, SORT2 = right_sort_op ] )"
},
"CREATE OPERATOR name ( PROCEDURE = func_name
\n
[, LEFTARG = type1 ] [, RIGHTARG = type2 ]
\n
[, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
\n
[, RESTRICT = res_proc ] [, JOIN = join_proc ]
\n
[, HASHES ] [, SORT1 = left_sort_op ] [, SORT2 = right_sort_op ] )"
},
{
"CREATE RULE"
,
{
"CREATE RULE"
,
"Defines a new rule"
,
"Defines a new rule"
,
"CREATE RULE name AS ON event
\n
TO object [ WHERE condition ]
\n
DO [ INSTEAD ] [ action | NOTHING ]"
},
"CREATE RULE name AS ON event
\n
TO object [ WHERE condition ]
\n
DO [ INSTEAD ] [ action | NOTHING ]"
},
{
"CREATE SEQUENCE"
,
{
"CREATE SEQUENCE"
,
"Creates a new sequence number generator"
,
"Creates a new sequence number generator"
,
"CREATE SEQUENCE seqname [ INCREMENT increment ]
\n
[ MINVALUE minvalue ] [ MAXVALUE maxvalue ]
\n
[ START start ] [ CACHE cache ] [ CYCLE ]"
},
"CREATE SEQUENCE seqname [ INCREMENT increment ]
\n
[ MINVALUE minvalue ] [ MAXVALUE maxvalue ]
\n
[ START start ] [ CACHE cache ] [ CYCLE ]"
},
{
"CREATE TABLE"
,
{
"CREATE TABLE"
,
"Creates a new table"
,
"Creates a new table"
,
"CREATE [ TEMPORARY | TEMP ] TABLE table (
\n
column type
\n
[ NULL | NOT NULL ] [ UNIQUE ] [ DEFAULT value ]
\n
[column_constraint_clause | PRIMARY KEY } [ ... ] ]
\n
[, ... ]
\n
[, PRIMARY KEY ( column [, ...] ) ]
\n
[, CHECK ( condition ) ]
\n
[, table_constraint_clause ]
\n
) [ INHERITS ( inherited_table [, ...] ) ]"
},
"CREATE [ TEMPORARY | TEMP ] TABLE table (
\n
column type
\n
[ NULL | NOT NULL ] [ UNIQUE ] [ DEFAULT value ]
\n
[column_constraint_clause | PRIMARY KEY } [ ... ] ]
\n
[, ... ]
\n
[, PRIMARY KEY ( column [, ...] ) ]
\n
[, CHECK ( condition ) ]
\n
[, table_constraint_clause ]
\n
) [ INHERITS ( inherited_table [, ...] ) ]"
},
{
"CREATE TABLE AS"
,
{
"CREATE TABLE AS"
,
"Creates a new table"
,
"Creates a new table"
,
"CREATE TABLE table [ (column [, ...] ) ]
\n
AS select_clause"
},
"CREATE TABLE table [ (column [, ...] ) ]
\n
AS select_clause"
},
{
"CREATE TRIGGER"
,
{
"CREATE TRIGGER"
,
"Creates a new trigger"
,
"Creates a new trigger"
,
"CREATE TRIGGER name { BEFORE | AFTER } { event [OR ...] }
\n
ON table FOR EACH { ROW | STATEMENT }
\n
EXECUTE PROCEDURE ER
\"
>funcBLE> ( arguments )"
},
"CREATE TRIGGER name { BEFORE | AFTER } { event [OR ...] }
\n
ON table FOR EACH { ROW | STATEMENT }
\n
EXECUTE PROCEDURE ER
\"
>funcBLE> ( arguments )"
},
{
"CREATE TYPE"
,
{
"CREATE TYPE"
,
"Defines a new base data type"
,
"Defines a new base data type"
,
"CREATE TYPE typename ( INPUT = input_function, OUTPUT = output_function
\n
, INTERNALLENGTH = { internallength | VARIABLE } [ , EXTERNALLENGTH = { externallength | VARIABLE } ]
\n
[ , DEFAULT =
\"
default
\"
]
\n
[ , ELEMENT = element ] [ , DELIMITER = delimiter ]
\n
[ , SEND = send_function ] [ , RECEIVE = receive_function ]
\n
[ , PASSEDBYVALUE ] )"
},
"CREATE TYPE typename ( INPUT = input_function, OUTPUT = output_function
\n
, INTERNALLENGTH = { internallength | VARIABLE } [ , EXTERNALLENGTH = { externallength | VARIABLE } ]
\n
[ , DEFAULT =
\"
default
\"
]
\n
[ , ELEMENT = element ] [ , DELIMITER = delimiter ]
\n
[ , SEND = send_function ] [ , RECEIVE = receive_function ]
\n
[ , PASSEDBYVALUE ] )"
},
{
"CREATE USER"
,
{
"CREATE USER"
,
"Creates account information for a new user"
,
"Creates account information for a new user"
,
"CREATE USER username
\n
[ WITH PASSWORD password ]
\n
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
\n
[ IN GROUP groupname [, ...] ]
\n
[ VALID UNTIL 'abstime' ]"
},
"CREATE USER username
\n
[ WITH PASSWORD password ]
\n
[ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
\n
[ IN GROUP groupname [, ...] ]
\n
[ VALID UNTIL 'abstime' ]"
},
{
"CREATE VIEW"
,
{
"CREATE VIEW"
,
"Constructs a virtual table"
,
"Constructs a virtual table"
,
"CREATE VIEW view AS SELECT query"
},
"CREATE VIEW view AS SELECT query"
},
{
"DECLARE"
,
{
"DECLARE"
,
"Defines a cursor for table access"
,
"Defines a cursor for table access"
,
"DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]
\n
CURSOR FOR query
\n
[ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]"
},
"DECLARE cursor [ BINARY ] [ INSENSITIVE ] [ SCROLL ]
\n
CURSOR FOR query
\n
[ FOR { READ ONLY | UPDATE [ OF column [, ...] ] ]"
},
{
"DELETE"
,
{
"DELETE"
,
"Removes rows from a table"
,
"Removes rows from a table"
,
"DELETE FROM table [ WHERE condition ]"
},
"DELETE FROM table [ WHERE condition ]"
},
{
"DROP AGGREGATE"
,
{
"DROP AGGREGATE"
,
"Removes the definition of an aggregate function"
,
"Removes the definition of an aggregate function"
,
"DROP AGGREGATE name type"
},
"DROP AGGREGATE name type"
},
{
"FETCH"
,
{
"FETCH"
,
"Gets rows using a cursor"
,
"Gets rows using a cursor"
,
"FETCH [ selector ] [ count ] { IN | FROM } cursor
\n
FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"
},
"FETCH [ selector ] [ count ] { IN | FROM } cursor
\n
FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"
},
{
"DROP DATABASE"
,
{
"DROP DATABASE"
,
"Destroys an existing database"
,
"Destroys an existing database"
,
"DROP DATABASE name"
},
"DROP DATABASE name"
},
{
"DROP FUNCTION"
,
{
"DROP FUNCTION"
,
"Removes a user-defined C function"
,
"Removes a user-defined C function"
,
"DROP FUNCTION name ( [ type [, ...] ] )"
},
"DROP FUNCTION name ( [ type [, ...] ] )"
},
{
"DROP INDEX"
,
{
"DROP INDEX"
,
"Removes an index from a database"
,
"Removes an index from a database"
,
"DROP INDEX index_name"
},
"DROP INDEX index_name"
},
{
"DROP LANGUAGE"
,
{
"DROP LANGUAGE"
,
"Removes a user-defined procedural language"
,
"Removes a user-defined procedural language"
,
"DROP PROCEDURAL LANGUAGE 'name'"
},
"DROP PROCEDURAL LANGUAGE 'name'"
},
{
"DROP OPERATOR"
,
{
"DROP OPERATOR"
,
"Removes an operator from the database"
,
"Removes an operator from the database"
,
"DROP OPERATOR id ( type | NONE [,...] )"
},
"DROP OPERATOR id ( type | NONE [,...] )"
},
{
"DROP RULE"
,
{
"DROP RULE"
,
"Removes an existing rule from the database"
,
"Removes an existing rule from the database"
,
"DROP RULE name"
},
"DROP RULE name"
},
{
"DROP SEQUENCE"
,
{
"DROP SEQUENCE"
,
"Removes an existing sequence"
,
"Removes an existing sequence"
,
"DROP SEQUENCE name [, ...]"
},
"DROP SEQUENCE name [, ...]"
},
{
"DROP TABLE"
,
{
"DROP TABLE"
,
"Removes existing tables from a database"
,
"Removes existing tables from a database"
,
"DROP TABLE name [, ...]"
},
"DROP TABLE name [, ...]"
},
{
"DROP TRIGGER"
,
{
"DROP TRIGGER"
,
"Removes the definition of a trigger"
,
"Removes the definition of a trigger"
,
"DROP TRIGGER name ON table"
},
"DROP TRIGGER name ON table"
},
{
"DROP TYPE"
,
{
"DROP TYPE"
,
"Removes a user-defined type from the system catalogs"
,
"Removes a user-defined type from the system catalogs"
,
"DROP TYPE typename"
},
"DROP TYPE typename"
},
{
"DROP USER"
,
{
"DROP USER"
,
"Removes an user account information"
,
"Removes an user account information"
,
"DROP USER name"
},
"DROP USER name"
},
{
"DROP VIEW"
,
{
"DROP VIEW"
,
"Removes an existing view from a database"
,
"Removes an existing view from a database"
,
"DROP VIEW name"
},
"DROP VIEW name"
},
{
"EXPLAIN"
,
{
"EXPLAIN"
,
"Shows statement execution details"
,
"Shows statement execution details"
,
"EXPLAIN [ VERBOSE ] query"
},
"EXPLAIN [ VERBOSE ] query"
},
{
"GRANT"
,
{
"GRANT"
,
"Grants access privilege to a user, a group or all users"
,
"Grants access privilege to a user, a group or all users"
,
"GRANT privilege [, ...] ON object [, ...]
\n
TO { PUBLIC | GROUP group | username }"
},
"GRANT privilege [, ...] ON object [, ...]
\n
TO { PUBLIC | GROUP group | username }"
},
{
"INSERT"
,
{
"INSERT"
,
"Inserts new rows into a table"
,
"Inserts new rows into a table"
,
"INSERT INTO table [ ( column [, ...] ) ]
\n
{ VALUES ( expression [, ...] ) | SELECT query }"
},
"INSERT INTO table [ ( column [, ...] ) ]
\n
{ VALUES ( expression [, ...] ) | SELECT query }"
},
{
"LISTEN"
,
{
"LISTEN"
,
"Listen for a response on a notify condition"
,
"Listen for a response on a notify condition"
,
"LISTEN name"
},
"LISTEN name"
},
{
"LOAD"
,
{
"LOAD"
,
"Dynamically loads an object file"
,
"Dynamically loads an object file"
,
"LOAD 'filename'"
},
"LOAD 'filename'"
},
{
"LOCK"
,
{
"LOCK"
,
"Explicitly lock a table inside a transaction"
,
"Explicitly lock a table inside a transaction"
,
"LOCK [ TABLE ] name
\n
LOCK [ TABLE ] name IN [ ROW | ACCESS ] { SHARE | EXCLUSIVE } MODE
\n
LOCK [ TABLE ] name IN SHARE ROW EXCLUSIVE MODE"
},
"LOCK [ TABLE ] name
\n
LOCK [ TABLE ] name IN [ ROW | ACCESS ] { SHARE | EXCLUSIVE } MODE
\n
LOCK [ TABLE ] name IN SHARE ROW EXCLUSIVE MODE"
},
{
"MOVE"
,
{
"MOVE"
,
"Moves cursor position"
,
"Moves cursor position"
,
"MOVE [ selector ] [ count ]
\n
{ IN | FROM } cursor
\n
FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"
},
"MOVE [ selector ] [ count ]
\n
{ IN | FROM } cursor
\n
FETCH [ RELATIVE ] [ { [ # | ALL | NEXT | PRIOR ] } ] FROM ] cursor"
},
{
"NOTIFY"
,
{
"NOTIFY"
,
"Signals all frontends and backends listening on a notify condition"
,
"Signals all frontends and backends listening on a notify condition"
,
"NOTIFY name"
},
"NOTIFY name"
},
{
"RESET"
,
{
"RESET"
,
"Restores run-time parameters for session to default values"
,
"Restores run-time parameters for session to default values"
,
"RESET variable"
},
"RESET variable"
},
{
"REVOKE"
,
{
"REVOKE"
,
"Revokes access privilege from a user, a group or all users."
,
"Revokes access privilege from a user, a group or all users."
,
"REVOKE privilege [, ...]
\n
ON object [, ...]
\n
FROM { PUBLIC | GROUP ER
\"
>gBLE> | username }"
},
"REVOKE privilege [, ...]
\n
ON object [, ...]
\n
FROM { PUBLIC | GROUP ER
\"
>gBLE> | username }"
},
{
"ROLLBACK"
,
{
"ROLLBACK"
,
"Aborts the current transaction"
,
"Aborts the current transaction"
,
"ROLLBACK [ WORK | TRANSACTION ]"
},
"ROLLBACK [ WORK | TRANSACTION ]"
},
{
"SELECT"
,
{
"SELECT"
,
"Retrieve rows from a table or view."
,
"Retrieve rows from a table or view."
,
"SELECT [ ALL | DISTINCT [ ON column ] ]
\n
expression [ AS name ] [, ...]
\n
[ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ]
\n
[ FROM table [ alias ] [, ...] ]
\n
[ WHERE condition ]
\n
[ GROUP BY column [, ...] ]
\n
[ HAVING condition [, ...] ]
\n
[ { UNION [ ALL ] | INTERSECT | EXCEPT } select ]
\n
[ ORDER BY column [ ASC | DESC ] [, ...] ]
\n
[ FOR UPDATE [ OF class_name... ] ]
\n
[ LIMIT { count | ALL } [ { OFFSET | , } count ] ]"
},
"SELECT [ ALL | DISTINCT [ ON column ] ]
\n
expression [ AS name ] [, ...]
\n
[ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ]
\n
[ FROM table [ alias ] [, ...] ]
\n
[ WHERE condition ]
\n
[ GROUP BY column [, ...] ]
\n
[ HAVING condition [, ...] ]
\n
[ { UNION [ ALL ] | INTERSECT | EXCEPT } select ]
\n
[ ORDER BY column [ ASC | DESC ] [, ...] ]
\n
[ FOR UPDATE [ OF class_name... ] ]
\n
[ LIMIT { count | ALL } [ { OFFSET | , } count ] ]"
},
{
"SELECT INTO"
,
{
"SELECT INTO"
,
"Create a new table from an existing table or view"
,
"Create a new table from an existing table or view"
,
"SELECT [ ALL | DISTINCT ] expression [ AS name ] [, ...]
\n
INTO [TEMP] [ TABLE ] new_table ]
\n
[ FROM table [alias] [, ...] ]
\n
[ WHERE condition ]
\n
[ GROUP BY column [, ...] ]
\n
[ HAVING condition [, ...] ]
\n
[ { UNION [ALL] | INTERSECT | EXCEPT } select]
\n
[ ORDER BY column [ ASC | DESC ] [, ...] ]
\n
[ FOR UPDATE [OF class_name...]]
\n
[ LIMIT count [OFFSET|, count]]"
},
"SELECT [ ALL | DISTINCT ] expression [ AS name ] [, ...]
\n
INTO [TEMP] [ TABLE ] new_table ]
\n
[ FROM table [alias] [, ...] ]
\n
[ WHERE condition ]
\n
[ GROUP BY column [, ...] ]
\n
[ HAVING condition [, ...] ]
\n
[ { UNION [ALL] | INTERSECT | EXCEPT } select]
\n
[ ORDER BY column [ ASC | DESC ] [, ...] ]
\n
[ FOR UPDATE [OF class_name...]]
\n
[ LIMIT count [OFFSET|, count]]"
},
{
"SET"
,
{
"SET"
,
"Set run-time parameters for session"
,
"Set run-time parameters for session"
,
"SET variable { TO | = } { 'value' | DEFAULT }
\n
SET TIME ZONE { 'timezone' | LOCAL | DEFAULT }
\n
SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }"
},
"SET variable { TO | = } { 'value' | DEFAULT }
\n
SET TIME ZONE { 'timezone' | LOCAL | DEFAULT }
\n
SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }"
},
{
"SHOW"
,
{
"SHOW"
,
"Shows run-time parameters for session"
,
"Shows run-time parameters for session"
,
"SHOW keyword"
},
"SHOW keyword"
},
{
"UNLISTEN"
,
{
"UNLISTEN"
,
"Stop listening for notification"
,
"Stop listening for notification"
,
"UNLISTEN { notifyname | * }"
},
"UNLISTEN { notifyname | * }"
},
{
"UPDATE"
,
{
"UPDATE"
,
"Replaces values of columns in a table"
,
"Replaces values of columns in a table"
,
"UPDATE table SET R
\"
>colle> = expression [, ...]
\n
[ FROM fromlist ]
\n
[ WHERE condition ]"
},
"UPDATE table SET R
\"
>colle> = expression [, ...]
\n
[ FROM fromlist ]
\n
[ WHERE condition ]"
},
{
"VACUUM"
,
{
"VACUUM"
,
"Clean and analyze a Postgres database"
,
"Clean and analyze a Postgres database"
,
"VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]
\n
VACUUM [ VERBOSE ] ANALYZE [ ER
\"
>tBLE> [ (column [, ...] ) ] ]"
},
"VACUUM [ VERBOSE ] [ ANALYZE ] [ table ]
\n
VACUUM [ VERBOSE ] ANALYZE [ ER
\"
>tBLE> [ (column [, ...] ) ] ]"
},
{
"END"
,
{
"END"
,
"Commits the current transaction"
,
"Commits the current transaction"
,
"END [ WORK | TRANSACTION ]"
},
"END [ WORK | TRANSACTION ]"
},
{
"COMMENT"
,
{
"COMMENT"
,
"Add comment to an object"
,
"Add comment to an object"
,
"COMMENT ON
\n
[
\n
[ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
\n
object_name |
\n
COLUMN table_name.column_name|
\n
AGGREGATE agg_name agg_type|
\n
FUNCTION func_name (arg1, arg2, ...)|
\n
OPERATOR op (leftoperand_type rightoperand_type) |
\n
TRIGGER trigger_name ON table_name
\n
] IS 'text'"
},
"COMMENT ON
\n
[
\n
[ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
\n
object_name |
\n
COLUMN table_name.column_name|
\n
AGGREGATE agg_name agg_type|
\n
FUNCTION func_name (arg1, arg2, ...)|
\n
OPERATOR op (leftoperand_type rightoperand_type) |
\n
TRIGGER trigger_name ON table_name
\n
] IS 'text'"
},
{
NULL
,
NULL
,
NULL
}
/* End of list marker */
{
NULL
,
NULL
,
NULL
}
/* End of list marker */
};
};
#endif
/* SQL_HELP_H */
#endif
/* SQL_HELP_H */
src/bin/psql/startup.c
View file @
0e6652e6
...
@@ -37,35 +37,38 @@
...
@@ -37,35 +37,38 @@
static
void
static
void
process_psqlrc
(
PsqlSettings
*
pset
);
process_psqlrc
(
PsqlSettings
*
pset
);
static
void
static
void
showVersion
(
PsqlSettings
*
pset
,
bool
verbose
);
showVersion
(
PsqlSettings
*
pset
,
bool
verbose
);
/* Structures to pass information between the option parsing routine
/* Structures to pass information between the option parsing routine
* and the main function
* and the main function
*/
*/
enum
_actions
{
ACT_NOTHING
=
0
,
enum
_actions
ACT_SINGLE_SLASH
,
{
ACT_LIST_DB
,
ACT_NOTHING
=
0
,
ACT_SHOW_VER
,
ACT_SINGLE_SLASH
,
ACT_SINGLE_QUERY
,
ACT_LIST_DB
,
ACT_FILE
ACT_SHOW_VER
,
ACT_SINGLE_QUERY
,
ACT_FILE
};
};
struct
adhoc_opts
{
struct
adhoc_opts
char
*
dbname
;
{
char
*
host
;
char
*
dbname
;
char
*
port
;
char
*
host
;
char
*
username
;
char
*
port
;
enum
_actions
action
;
char
*
username
;
char
*
action_string
;
enum
_actions
action
;
bool
no_readline
;
char
*
action_string
;
bool
no_readline
;
};
};
static
void
static
void
parse_options
(
int
argc
,
char
*
argv
[],
PsqlSettings
*
pset
,
struct
adhoc_opts
*
options
);
parse_options
(
int
argc
,
char
*
argv
[],
PsqlSettings
*
pset
,
struct
adhoc_opts
*
options
);
...
@@ -77,133 +80,140 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
...
@@ -77,133 +80,140 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
int
int
main
(
int
argc
,
char
**
argv
)
main
(
int
argc
,
char
**
argv
)
{
{
PsqlSettings
settings
;
PsqlSettings
settings
;
struct
adhoc_opts
options
;
struct
adhoc_opts
options
;
int
successResult
;
int
successResult
;
char
*
username
=
NULL
;
char
*
username
=
NULL
;
char
*
password
=
NULL
;
char
*
password
=
NULL
;
bool
need_pass
;
bool
need_pass
;
MemSet
(
&
settings
,
0
,
sizeof
settings
);
MemSet
(
&
settings
,
0
,
sizeof
settings
);
settings
.
cur_cmd_source
=
stdin
;
settings
.
cur_cmd_source
=
stdin
;
settings
.
cur_cmd_interactive
=
false
;
settings
.
cur_cmd_interactive
=
false
;
settings
.
vars
=
CreateVariableSpace
();
settings
.
vars
=
CreateVariableSpace
();
settings
.
popt
.
topt
.
format
=
PRINT_ALIGNED
;
settings
.
popt
.
topt
.
format
=
PRINT_ALIGNED
;
settings
.
queryFout
=
stdout
;
settings
.
queryFout
=
stdout
;
settings
.
popt
.
topt
.
fieldSep
=
strdup
(
DEFAULT_FIELD_SEP
);
settings
.
popt
.
topt
.
fieldSep
=
strdup
(
DEFAULT_FIELD_SEP
);
settings
.
popt
.
topt
.
border
=
1
;
settings
.
popt
.
topt
.
border
=
1
;
SetVariable
(
settings
.
vars
,
"prompt1"
,
DEFAULT_PROMPT1
);
SetVariable
(
settings
.
vars
,
"prompt1"
,
DEFAULT_PROMPT1
);
SetVariable
(
settings
.
vars
,
"prompt2"
,
DEFAULT_PROMPT2
);
SetVariable
(
settings
.
vars
,
"prompt2"
,
DEFAULT_PROMPT2
);
SetVariable
(
settings
.
vars
,
"prompt3"
,
DEFAULT_PROMPT3
);
SetVariable
(
settings
.
vars
,
"prompt3"
,
DEFAULT_PROMPT3
);
settings
.
notty
=
(
!
isatty
(
fileno
(
stdin
))
||
!
isatty
(
fileno
(
stdout
)));
settings
.
notty
=
(
!
isatty
(
fileno
(
stdin
))
||
!
isatty
(
fileno
(
stdout
)));
/* This is obsolete and will be removed very soon. */
/* This is obsolete and will be removed very soon. */
#ifdef PSQL_ALWAYS_GET_PASSWORDS
#ifdef PSQL_ALWAYS_GET_PASSWORDS
settings
.
getPassword
=
true
;
settings
.
getPassword
=
true
;
#else
#else
settings
.
getPassword
=
false
;
settings
.
getPassword
=
false
;
#endif
#endif
#ifdef MULTIBYTE
#ifdef MULTIBYTE
settings
.
has_client_encoding
=
(
getenv
(
"PGCLIENTENCODING"
)
!=
NULL
);
settings
.
has_client_encoding
=
(
getenv
(
"PGCLIENTENCODING"
)
!=
NULL
);
#endif
#endif
parse_options
(
argc
,
argv
,
&
settings
,
&
options
);
parse_options
(
argc
,
argv
,
&
settings
,
&
options
);
if
(
options
.
action
==
ACT_LIST_DB
||
options
.
action
==
ACT_SHOW_VER
)
if
(
options
.
action
==
ACT_LIST_DB
||
options
.
action
==
ACT_SHOW_VER
)
options
.
dbname
=
"template1"
;
options
.
dbname
=
"template1"
;
if
(
options
.
username
)
{
if
(
options
.
username
)
if
(
strcmp
(
options
.
username
,
"?"
)
==
0
)
{
username
=
simple_prompt
(
"Username: "
,
100
,
true
);
if
(
strcmp
(
options
.
username
,
"?"
)
==
0
)
else
username
=
simple_prompt
(
"Username: "
,
100
,
true
);
username
=
strdup
(
options
.
username
);
else
}
username
=
strdup
(
options
.
username
);
if
(
settings
.
getPassword
)
password
=
simple_prompt
(
"Password: "
,
100
,
false
);
/* loop until we have a password if requested by backend */
do
{
need_pass
=
false
;
settings
.
db
=
PQsetdbLogin
(
options
.
host
,
options
.
port
,
NULL
,
NULL
,
options
.
dbname
,
username
,
password
);
if
(
PQstatus
(
settings
.
db
)
==
CONNECTION_BAD
&&
strcmp
(
PQerrorMessage
(
settings
.
db
),
"fe_sendauth: no password supplied
\n
"
)
==
0
)
{
need_pass
=
true
;
free
(
password
);
password
=
NULL
;
password
=
simple_prompt
(
"Password: "
,
100
,
false
);
}
}
}
while
(
need_pass
);
free
(
username
);
if
(
settings
.
getPassword
)
free
(
password
);
password
=
simple_prompt
(
"Password: "
,
100
,
false
);
if
(
PQstatus
(
settings
.
db
)
==
CONNECTION_BAD
)
{
/* loop until we have a password if requested by backend */
fprintf
(
stderr
,
"Connection to database '%s' failed.
\n
%s
\n
"
,
PQdb
(
settings
.
db
),
PQerrorMessage
(
settings
.
db
));
do
PQfinish
(
settings
.
db
);
{
exit
(
EXIT_BADCONN
);
need_pass
=
false
;
}
settings
.
db
=
PQsetdbLogin
(
options
.
host
,
options
.
port
,
NULL
,
NULL
,
options
.
dbname
,
username
,
password
);
if
(
PQstatus
(
settings
.
db
)
==
CONNECTION_BAD
&&
strcmp
(
PQerrorMessage
(
settings
.
db
),
"fe_sendauth: no password supplied
\n
"
)
==
0
)
{
need_pass
=
true
;
free
(
password
);
password
=
NULL
;
password
=
simple_prompt
(
"Password: "
,
100
,
false
);
}
}
while
(
need_pass
);
if
(
options
.
action
==
ACT_LIST_DB
)
{
free
(
username
);
int
success
=
listAllDbs
(
&
settings
);
free
(
password
);
PQfinish
(
settings
.
db
);
exit
(
!
success
);
if
(
PQstatus
(
settings
.
db
)
==
CONNECTION_BAD
)
}
{
fprintf
(
stderr
,
"Connection to database '%s' failed.
\n
%s
\n
"
,
PQdb
(
settings
.
db
),
PQerrorMessage
(
settings
.
db
));
PQfinish
(
settings
.
db
);
exit
(
EXIT_BADCONN
);
}
if
(
options
.
action
==
ACT_LIST_DB
)
{
int
success
=
listAllDbs
(
&
settings
);
PQfinish
(
settings
.
db
);
exit
(
!
success
);
}
if
(
options
.
action
==
ACT_SHOW_VER
)
{
showVersion
(
&
settings
,
true
);
PQfinish
(
settings
.
db
);
exit
(
EXIT_SUCCESS
);
}
if
(
!
GetVariable
(
settings
.
vars
,
"quiet"
)
&&
!
settings
.
notty
&&
!
options
.
action
)
{
puts
(
"Welcome to psql, the PostgreSQL interactive terminal.
\n
"
"(Please type
\\
copyright to see the distribution terms of PostgreSQL.)"
);
if
(
options
.
action
==
ACT_SHOW_VER
)
{
//showVersion(&settings, false);
showVersion
(
&
settings
,
true
);
puts
(
"
\n
"
"Type
\\
h for help with SQL commands,
\n
"
"
\\
? for help on internal slash commands,
\n
"
"
\\
q to quit,
\n
"
"
\\
g or terminate with semicolon to execute query."
);
}
process_psqlrc
(
&
settings
);
initializeInput
(
options
.
no_readline
?
0
:
1
);
/* Now find something to do */
/* process file given by -f */
if
(
options
.
action
==
ACT_FILE
)
successResult
=
process_file
(
options
.
action_string
,
&
settings
)
?
0
:
1
;
/* process slash command if one was given to -c */
else
if
(
options
.
action
==
ACT_SINGLE_SLASH
)
successResult
=
HandleSlashCmds
(
&
settings
,
options
.
action_string
,
NULL
,
NULL
)
!=
CMD_ERROR
?
0
:
1
;
/* If the query given to -c was a normal one, send it */
else
if
(
options
.
action
==
ACT_SINGLE_QUERY
)
successResult
=
SendQuery
(
&
settings
,
options
.
action_string
)
?
0
:
1
;
/* or otherwise enter interactive main loop */
else
successResult
=
MainLoop
(
&
settings
,
stdin
);
/* clean up */
finishInput
();
PQfinish
(
settings
.
db
);
PQfinish
(
settings
.
db
);
exit
(
EXIT_SUCCESS
);
setQFout
(
NULL
,
&
settings
);
}
DestroyVariableSpace
(
settings
.
vars
);
return
successResult
;
if
(
!
GetVariable
(
settings
.
vars
,
"quiet"
)
&&
!
settings
.
notty
&&
!
options
.
action
)
{
puts
(
"Welcome to psql, the PostgreSQL interactive terminal.
\n
"
"(Please type
\\
copyright to see the distribution terms of PostgreSQL.)"
);
// showVersion(&settings, false);
puts
(
"
\n
"
"Type
\\
h for help with SQL commands,
\n
"
"
\\
? for help on internal slash commands,
\n
"
"
\\
q to quit,
\n
"
"
\\
g or terminate with semicolon to execute query."
);
}
process_psqlrc
(
&
settings
);
initializeInput
(
options
.
no_readline
?
0
:
1
);
/* Now find something to do */
/* process file given by -f */
if
(
options
.
action
==
ACT_FILE
)
successResult
=
process_file
(
options
.
action_string
,
&
settings
)
?
0
:
1
;
/* process slash command if one was given to -c */
else
if
(
options
.
action
==
ACT_SINGLE_SLASH
)
successResult
=
HandleSlashCmds
(
&
settings
,
options
.
action_string
,
NULL
,
NULL
)
!=
CMD_ERROR
?
0
:
1
;
/* If the query given to -c was a normal one, send it */
else
if
(
options
.
action
==
ACT_SINGLE_QUERY
)
successResult
=
SendQuery
(
&
settings
,
options
.
action_string
)
?
0
:
1
;
/* or otherwise enter interactive main loop */
else
successResult
=
MainLoop
(
&
settings
,
stdin
);
/* clean up */
finishInput
();
PQfinish
(
settings
.
db
);
setQFout
(
NULL
,
&
settings
);
DestroyVariableSpace
(
settings
.
vars
);
return
successResult
;
}
}
...
@@ -214,215 +224,231 @@ main(int argc, char **argv)
...
@@ -214,215 +224,231 @@ main(int argc, char **argv)
#ifdef WIN32
#ifdef WIN32
/* getopt is not in the standard includes on Win32 */
/* getopt is not in the standard includes on Win32 */
int
getopt
(
int
,
char
*
const
[],
const
char
*
);
int
getopt
(
int
,
char
*
const
[],
const
char
*
);
#endif
#endif
static
void
static
void
parse_options
(
int
argc
,
char
*
argv
[],
PsqlSettings
*
pset
,
struct
adhoc_opts
*
options
)
parse_options
(
int
argc
,
char
*
argv
[],
PsqlSettings
*
pset
,
struct
adhoc_opts
*
options
)
{
{
#ifdef HAVE_GETOPT_LONG
#ifdef HAVE_GETOPT_LONG
static
struct
option
long_options
[]
=
{
static
struct
option
long_options
[]
=
{
{
"no-align"
,
no_argument
,
NULL
,
'A'
},
{
"no-align"
,
no_argument
,
NULL
,
'A'
},
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"database"
,
required_argument
,
NULL
,
'd'
},
{
"database"
,
required_argument
,
NULL
,
'd'
},
{
"dbname"
,
required_argument
,
NULL
,
'd'
},
{
"dbname"
,
required_argument
,
NULL
,
'd'
},
{
"echo"
,
no_argument
,
NULL
,
'e'
},
{
"echo"
,
no_argument
,
NULL
,
'e'
},
{
"echo-queries"
,
no_argument
,
NULL
,
'e'
},
{
"echo-queries"
,
no_argument
,
NULL
,
'e'
},
{
"echo-all"
,
no_argument
,
NULL
,
'E'
},
{
"echo-all"
,
no_argument
,
NULL
,
'E'
},
{
"echo-all-queries"
,
no_argument
,
NULL
,
'E'
},
{
"echo-all-queries"
,
no_argument
,
NULL
,
'E'
},
{
"file"
,
required_argument
,
NULL
,
'f'
},
{
"file"
,
required_argument
,
NULL
,
'f'
},
{
"field-sep"
,
required_argument
,
NULL
,
'F'
},
{
"field-sep"
,
required_argument
,
NULL
,
'F'
},
{
"host"
,
required_argument
,
NULL
,
'h'
},
{
"host"
,
required_argument
,
NULL
,
'h'
},
{
"html"
,
no_argument
,
NULL
,
'H'
},
{
"html"
,
no_argument
,
NULL
,
'H'
},
{
"list"
,
no_argument
,
NULL
,
'l'
},
{
"list"
,
no_argument
,
NULL
,
'l'
},
{
"no-readline"
,
no_argument
,
NULL
,
'n'
},
{
"no-readline"
,
no_argument
,
NULL
,
'n'
},
{
"out"
,
required_argument
,
NULL
,
'o'
},
{
"out"
,
required_argument
,
NULL
,
'o'
},
{
"to-file"
,
required_argument
,
NULL
,
'o'
},
{
"to-file"
,
required_argument
,
NULL
,
'o'
},
{
"port"
,
required_argument
,
NULL
,
'p'
},
{
"port"
,
required_argument
,
NULL
,
'p'
},
{
"pset"
,
required_argument
,
NULL
,
'P'
},
{
"pset"
,
required_argument
,
NULL
,
'P'
},
{
"quiet"
,
no_argument
,
NULL
,
'q'
},
{
"quiet"
,
no_argument
,
NULL
,
'q'
},
{
"single-step"
,
no_argument
,
NULL
,
's'
},
{
"single-step"
,
no_argument
,
NULL
,
's'
},
{
"single-line"
,
no_argument
,
NULL
,
'S'
},
{
"single-line"
,
no_argument
,
NULL
,
'S'
},
{
"tuples-only"
,
no_argument
,
NULL
,
't'
},
{
"tuples-only"
,
no_argument
,
NULL
,
't'
},
{
"table-attr"
,
required_argument
,
NULL
,
'T'
},
{
"table-attr"
,
required_argument
,
NULL
,
'T'
},
{
"username"
,
required_argument
,
NULL
,
'U'
},
{
"username"
,
required_argument
,
NULL
,
'U'
},
{
"expanded"
,
no_argument
,
NULL
,
'x'
},
{
"expanded"
,
no_argument
,
NULL
,
'x'
},
{
"set"
,
required_argument
,
NULL
,
'v'
},
{
"set"
,
required_argument
,
NULL
,
'v'
},
{
"variable"
,
required_argument
,
NULL
,
'v'
},
{
"variable"
,
required_argument
,
NULL
,
'v'
},
{
"version"
,
no_argument
,
NULL
,
'V'
},
{
"version"
,
no_argument
,
NULL
,
'V'
},
{
"password"
,
no_argument
,
NULL
,
'W'
},
{
"password"
,
no_argument
,
NULL
,
'W'
},
{
"help"
,
no_argument
,
NULL
,
'?'
},
{
"help"
,
no_argument
,
NULL
,
'?'
},
};
};
int
optindex
;
int
optindex
;
#endif
#endif
extern
char
*
optarg
;
extern
char
*
optarg
;
extern
int
optind
;
extern
int
optind
;
int
c
;
int
c
;
MemSet
(
options
,
0
,
sizeof
*
options
);
MemSet
(
options
,
0
,
sizeof
*
options
);
#ifdef HAVE_GETOPT_LONG
#ifdef HAVE_GETOPT_LONG
while
((
c
=
getopt_long
(
argc
,
argv
,
"Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?"
,
long_options
,
&
optindex
))
!=
-
1
)
while
((
c
=
getopt_long
(
argc
,
argv
,
"Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?"
,
long_options
,
&
optindex
))
!=
-
1
)
#else
#else
/* Be sure to leave the '-' in here, so we can catch accidental long options. */
while
((
c
=
getopt
(
argc
,
argv
,
"Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-"
))
!=
-
1
)
/*
* Be sure to leave the '-' in here, so we can catch accidental long
* options.
*/
while
((
c
=
getopt
(
argc
,
argv
,
"Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-"
))
!=
-
1
)
#endif
#endif
{
switch
(
c
)
{
case
'A'
:
pset
->
popt
.
topt
.
format
=
PRINT_UNALIGNED
;
break
;
case
'c'
:
options
->
action_string
=
optarg
;
if
(
optarg
[
0
]
==
'\\'
)
options
->
action
=
ACT_SINGLE_SLASH
;
else
options
->
action
=
ACT_SINGLE_QUERY
;
break
;
case
'd'
:
options
->
dbname
=
optarg
;
break
;
case
'e'
:
SetVariable
(
pset
->
vars
,
"echo"
,
""
);
break
;
case
'E'
:
SetVariable
(
pset
->
vars
,
"echo_secret"
,
""
);
break
;
case
'f'
:
options
->
action
=
ACT_FILE
;
options
->
action_string
=
optarg
;
break
;
case
'F'
:
pset
->
popt
.
topt
.
fieldSep
=
strdup
(
optarg
);
break
;
case
'h'
:
options
->
host
=
optarg
;
break
;
case
'H'
:
pset
->
popt
.
topt
.
format
=
PRINT_HTML
;
break
;
case
'l'
:
options
->
action
=
ACT_LIST_DB
;
break
;
case
'n'
:
options
->
no_readline
=
true
;
break
;
case
'o'
:
setQFout
(
optarg
,
pset
);
break
;
case
'p'
:
options
->
port
=
optarg
;
break
;
case
'P'
:
{
char
*
value
;
char
*
equal_loc
;
bool
result
;
value
=
xstrdup
(
optarg
);
equal_loc
=
strchr
(
value
,
'='
);
if
(
!
equal_loc
)
result
=
do_pset
(
value
,
NULL
,
&
pset
->
popt
,
true
);
else
{
*
equal_loc
=
'\0'
;
result
=
do_pset
(
value
,
equal_loc
+
1
,
&
pset
->
popt
,
true
);
}
if
(
!
result
)
{
fprintf
(
stderr
,
"Couldn't set printing paramter %s.
\n
"
,
value
);
exit
(
EXIT_FAILURE
);
}
free
(
value
);
break
;
}
case
'q'
:
SetVariable
(
pset
->
vars
,
"quiet"
,
""
);
break
;
case
's'
:
SetVariable
(
pset
->
vars
,
"singlestep"
,
""
);
break
;
case
'S'
:
SetVariable
(
pset
->
vars
,
"singleline"
,
""
);
break
;
case
't'
:
pset
->
popt
.
topt
.
tuples_only
=
true
;
break
;
case
'T'
:
pset
->
popt
.
topt
.
tableAttr
=
xstrdup
(
optarg
);
break
;
case
'u'
:
pset
->
getPassword
=
true
;
options
->
username
=
"?"
;
break
;
case
'U'
:
options
->
username
=
optarg
;
break
;
case
'x'
:
pset
->
popt
.
topt
.
expanded
=
true
;
break
;
case
'v'
:
{
{
char
*
value
;
switch
(
c
)
char
*
equal_loc
;
{
case
'A'
:
value
=
xstrdup
(
optarg
);
pset
->
popt
.
topt
.
format
=
PRINT_UNALIGNED
;
equal_loc
=
strchr
(
value
,
'='
);
break
;
if
(
!
equal_loc
)
{
case
'c'
:
if
(
!
DeleteVariable
(
pset
->
vars
,
value
))
{
options
->
action_string
=
optarg
;
fprintf
(
stderr
,
"Couldn't delete variable %s.
\n
"
,
value
);
if
(
optarg
[
0
]
==
'\\'
)
exit
(
EXIT_FAILURE
);
options
->
action
=
ACT_SINGLE_SLASH
;
}
else
}
options
->
action
=
ACT_SINGLE_QUERY
;
else
{
break
;
*
equal_loc
=
'\0'
;
case
'd'
:
if
(
!
SetVariable
(
pset
->
vars
,
value
,
equal_loc
+
1
))
{
options
->
dbname
=
optarg
;
fprintf
(
stderr
,
"Couldn't set variable %s to %s.
\n
"
,
value
,
equal_loc
);
break
;
exit
(
EXIT_FAILURE
);
case
'e'
:
}
SetVariable
(
pset
->
vars
,
"echo"
,
""
);
}
break
;
case
'E'
:
free
(
value
);
SetVariable
(
pset
->
vars
,
"echo_secret"
,
""
);
break
;
break
;
}
case
'f'
:
case
'V'
:
options
->
action
=
ACT_FILE
;
options
->
action
=
ACT_SHOW_VER
;
options
->
action_string
=
optarg
;
break
;
break
;
case
'W'
:
case
'F'
:
pset
->
getPassword
=
true
;
pset
->
popt
.
topt
.
fieldSep
=
strdup
(
optarg
);
break
;
break
;
case
'?'
:
case
'h'
:
usage
();
options
->
host
=
optarg
;
exit
(
EXIT_SUCCESS
);
break
;
break
;
case
'H'
:
pset
->
popt
.
topt
.
format
=
PRINT_HTML
;
break
;
case
'l'
:
options
->
action
=
ACT_LIST_DB
;
break
;
case
'n'
:
options
->
no_readline
=
true
;
break
;
case
'o'
:
setQFout
(
optarg
,
pset
);
break
;
case
'p'
:
options
->
port
=
optarg
;
break
;
case
'P'
:
{
char
*
value
;
char
*
equal_loc
;
bool
result
;
value
=
xstrdup
(
optarg
);
equal_loc
=
strchr
(
value
,
'='
);
if
(
!
equal_loc
)
result
=
do_pset
(
value
,
NULL
,
&
pset
->
popt
,
true
);
else
{
*
equal_loc
=
'\0'
;
result
=
do_pset
(
value
,
equal_loc
+
1
,
&
pset
->
popt
,
true
);
}
if
(
!
result
)
{
fprintf
(
stderr
,
"Couldn't set printing paramter %s.
\n
"
,
value
);
exit
(
EXIT_FAILURE
);
}
free
(
value
);
break
;
}
case
'q'
:
SetVariable
(
pset
->
vars
,
"quiet"
,
""
);
break
;
case
's'
:
SetVariable
(
pset
->
vars
,
"singlestep"
,
""
);
break
;
case
'S'
:
SetVariable
(
pset
->
vars
,
"singleline"
,
""
);
break
;
case
't'
:
pset
->
popt
.
topt
.
tuples_only
=
true
;
break
;
case
'T'
:
pset
->
popt
.
topt
.
tableAttr
=
xstrdup
(
optarg
);
break
;
case
'u'
:
pset
->
getPassword
=
true
;
options
->
username
=
"?"
;
break
;
case
'U'
:
options
->
username
=
optarg
;
break
;
case
'x'
:
pset
->
popt
.
topt
.
expanded
=
true
;
break
;
case
'v'
:
{
char
*
value
;
char
*
equal_loc
;
value
=
xstrdup
(
optarg
);
equal_loc
=
strchr
(
value
,
'='
);
if
(
!
equal_loc
)
{
if
(
!
DeleteVariable
(
pset
->
vars
,
value
))
{
fprintf
(
stderr
,
"Couldn't delete variable %s.
\n
"
,
value
);
exit
(
EXIT_FAILURE
);
}
}
else
{
*
equal_loc
=
'\0'
;
if
(
!
SetVariable
(
pset
->
vars
,
value
,
equal_loc
+
1
))
{
fprintf
(
stderr
,
"Couldn't set variable %s to %s.
\n
"
,
value
,
equal_loc
);
exit
(
EXIT_FAILURE
);
}
}
free
(
value
);
break
;
}
case
'V'
:
options
->
action
=
ACT_SHOW_VER
;
break
;
case
'W'
:
pset
->
getPassword
=
true
;
break
;
case
'?'
:
usage
();
exit
(
EXIT_SUCCESS
);
break
;
#ifndef HAVE_GETOPT_LONG
#ifndef HAVE_GETOPT_LONG
case
'-'
:
case
'-'
:
fprintf
(
stderr
,
"This version of psql was compiled without support for long options.
\n
"
fprintf
(
stderr
,
"This version of psql was compiled without support for long options.
\n
"
"Use -? for help on invocation options.
\n
"
);
"Use -? for help on invocation options.
\n
"
);
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
break
;
break
;
#endif
#endif
default:
default:
usage
();
usage
();
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
break
;
break
;
}
}
}
}
/* if we still have arguments, use it as the database name and username */
while
(
argc
-
optind
>=
1
)
{
if
(
!
options
->
dbname
)
options
->
dbname
=
argv
[
optind
];
else
if
(
!
options
->
username
)
options
->
username
=
argv
[
optind
];
else
fprintf
(
stderr
,
"Warning: extra option %s ignored.
\n
"
,
argv
[
optind
]);
optind
++
;
/*
}
* if we still have arguments, use it as the database name and
* username
*/
while
(
argc
-
optind
>=
1
)
{
if
(
!
options
->
dbname
)
options
->
dbname
=
argv
[
optind
];
else
if
(
!
options
->
username
)
options
->
username
=
argv
[
optind
];
else
fprintf
(
stderr
,
"Warning: extra option %s ignored.
\n
"
,
argv
[
optind
]);
optind
++
;
}
}
}
...
@@ -431,41 +457,44 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
...
@@ -431,41 +457,44 @@ parse_options(int argc, char *argv[], PsqlSettings * pset, struct adhoc_opts * o
* Load /etc/psqlrc or .psqlrc file, if found.
* Load /etc/psqlrc or .psqlrc file, if found.
*/
*/
static
void
static
void
process_psqlrc
(
PsqlSettings
*
pset
)
process_psqlrc
(
PsqlSettings
*
pset
)
{
{
char
*
psqlrc
;
char
*
psqlrc
;
char
*
home
;
char
*
home
;
#ifdef WIN32
#ifdef WIN32
#define R_OK 0
#define R_OK 0
#endif
#endif
/* System-wide startup file */
/* System-wide startup file */
if
(
access
(
"/etc/psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
R_OK
)
==
0
)
if
(
access
(
"/etc/psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
R_OK
)
==
0
)
process_file
(
"/etc/psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
pset
);
process_file
(
"/etc/psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
pset
);
else
if
(
access
(
"/etc/psqlrc"
,
R_OK
)
==
0
)
else
if
(
access
(
"/etc/psqlrc"
,
R_OK
)
==
0
)
process_file
(
"/etc/psqlrc"
,
pset
);
process_file
(
"/etc/psqlrc"
,
pset
);
/* Look for one in the home dir */
/* Look for one in the home dir */
home
=
getenv
(
"HOME"
);
home
=
getenv
(
"HOME"
);
if
(
home
)
{
if
(
home
)
psqlrc
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
{
if
(
!
psqlrc
)
{
psqlrc
=
(
char
*
)
malloc
(
strlen
(
home
)
+
20
);
perror
(
"malloc"
);
if
(
!
psqlrc
)
exit
(
EXIT_FAILURE
);
{
}
perror
(
"malloc"
);
exit
(
EXIT_FAILURE
);
}
sprintf
(
psqlrc
,
"%s/.psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
home
);
sprintf
(
psqlrc
,
"%s/.psqlrc-"
PG_RELEASE
"."
PG_VERSION
"."
PG_SUBVERSION
,
home
);
if
(
access
(
psqlrc
,
R_OK
)
==
0
)
if
(
access
(
psqlrc
,
R_OK
)
==
0
)
process_file
(
psqlrc
,
pset
);
process_file
(
psqlrc
,
pset
);
else
{
else
sprintf
(
psqlrc
,
"%s/.psqlrc"
,
home
);
{
if
(
access
(
psqlrc
,
R_OK
)
==
0
)
sprintf
(
psqlrc
,
"%s/.psqlrc"
,
home
);
process_file
(
psqlrc
,
pset
);
if
(
access
(
psqlrc
,
R_OK
)
==
0
)
process_file
(
psqlrc
,
pset
);
}
free
(
psqlrc
);
}
}
free
(
psqlrc
);
}
}
}
...
@@ -482,62 +511,68 @@ process_psqlrc(PsqlSettings * pset)
...
@@ -482,62 +511,68 @@ process_psqlrc(PsqlSettings * pset)
static
void
static
void
showVersion
(
PsqlSettings
*
pset
,
bool
verbose
)
showVersion
(
PsqlSettings
*
pset
,
bool
verbose
)
{
{
PGresult
*
res
;
PGresult
*
res
;
char
*
versionstr
=
NULL
;
char
*
versionstr
=
NULL
;
long
int
release
=
0
,
version
=
0
,
subversion
=
0
;
long
int
release
=
0
,
version
=
0
,
/* get backend version */
subversion
=
0
;
res
=
PSQLexec
(
pset
,
"SELECT version()"
);
if
(
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
)
/* get backend version */
versionstr
=
PQgetvalue
(
res
,
0
,
0
);
res
=
PSQLexec
(
pset
,
"SELECT version()"
);
if
(
PQresultStatus
(
res
)
==
PGRES_TUPLES_OK
)
if
(
!
verbose
)
{
versionstr
=
PQgetvalue
(
res
,
0
,
0
);
if
(
versionstr
)
puts
(
versionstr
);
PQclear
(
res
);
if
(
!
verbose
)
return
;
{
}
if
(
versionstr
)
puts
(
versionstr
);
PQclear
(
res
);
return
;
}
if
(
strncmp
(
versionstr
,
"PostgreSQL "
,
11
)
==
0
)
{
char
*
tmp
;
if
(
strncmp
(
versionstr
,
"PostgreSQL "
,
11
)
==
0
)
{
release
=
strtol
(
&
versionstr
[
11
],
&
tmp
,
10
);
char
*
tmp
;
version
=
strtol
(
tmp
+
1
,
&
tmp
,
10
);
release
=
strtol
(
&
versionstr
[
11
],
&
tmp
,
10
);
subversion
=
strtol
(
tmp
+
1
,
&
tmp
,
10
);
version
=
strtol
(
tmp
+
1
,
&
tmp
,
10
);
}
subversion
=
strtol
(
tmp
+
1
,
&
tmp
,
10
);
}
printf
(
"Server: %s
\n
psql"
,
versionstr
?
versionstr
:
"(could not connected)"
);
printf
(
"Server: %s
\n
psql"
,
versionstr
?
versionstr
:
"(could not connected)"
);
if
(
strcmp
(
versionstr
,
PG_VERSION_STR
)
!=
0
)
if
(
strcmp
(
versionstr
,
PG_VERSION_STR
)
!=
0
)
printf
(
&
PG_VERSION_STR
[
strcspn
(
PG_VERSION_STR
,
" "
)]);
printf
(
&
PG_VERSION_STR
[
strcspn
(
PG_VERSION_STR
,
" "
)]);
printf
(
" ("
__DATE__
" "
__TIME__
")"
);
printf
(
" ("
__DATE__
" "
__TIME__
")"
);
#ifdef MULTIBYTE
#ifdef MULTIBYTE
printf
(
", multibyte"
);
printf
(
", multibyte"
);
#endif
#endif
#ifdef HAVE_GETOPT_LONG
#ifdef HAVE_GETOPT_LONG
printf
(
", long options"
);
printf
(
", long options"
);
#endif
#endif
#ifdef USE_READLINE
#ifdef USE_READLINE
printf
(
", readline"
);
printf
(
", readline"
);
#endif
#endif
#ifdef USE_HISTORY
#ifdef USE_HISTORY
printf
(
", history"
);
printf
(
", history"
);
#endif
#endif
#ifdef USE_LOCALE
#ifdef USE_LOCALE
printf
(
", locale"
);
printf
(
", locale"
);
#endif
#endif
#ifdef PSQL_ALWAYS_GET_PASSWORDS
#ifdef PSQL_ALWAYS_GET_PASSWORDS
printf
(
", always password"
);
printf
(
", always password"
);
#endif
#endif
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
printf
(
", assert checks"
);
printf
(
", assert checks"
);
#endif
#endif
puts
(
""
);
puts
(
""
);
if
(
release
<
6
||
(
release
==
6
&&
version
<
5
))
if
(
release
<
6
||
(
release
==
6
&&
version
<
5
))
puts
(
"
\n
Warning: The server you are connected to is potentially too old for this client
\n
"
puts
(
"
\n
Warning: The server you are connected to is potentially too old for this client
\n
"
"version. You should ideally be using clients and servers from the same
\n
"
"version. You should ideally be using clients and servers from the same
\n
"
"distribution."
);
"distribution."
);
PQclear
(
res
);
PQclear
(
res
);
}
}
src/bin/psql/stringutils.c
View file @
0e6652e6
...
@@ -2,11 +2,13 @@
...
@@ -2,11 +2,13 @@
#include <c.h>
#include <c.h>
#include "stringutils.h"
#include "stringutils.h"
//#include <ctype.h>
//
#include <ctype.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <assert.h>
#include <assert.h>
//#include <stdio.h>
//
#include <stdio.h>
#include <postgres.h>
#include <postgres.h>
#ifndef HAVE_STRDUP
#ifndef HAVE_STRDUP
...
@@ -17,118 +19,130 @@
...
@@ -17,118 +19,130 @@
static
void
static
void
unescape_quotes
(
char
*
source
,
char
quote
,
char
escape
);
unescape_quotes
(
char
*
source
,
char
quote
,
char
escape
);
/*
/*
* Replacement for strtok() (a.k.a. poor man's flex)
* Replacement for strtok() (a.k.a. poor man's flex)
*
*
* The calling convention is similar to that of strtok.
* The calling convention is similar to that of strtok.
* s -
string to parse, if NULL continue parsing the last string
* s -
string to parse, if NULL continue parsing the last string
* delim -
set of characters that delimit tokens (usually whitespace)
* delim -
set of characters that delimit tokens (usually whitespace)
* quote -
set of characters that quote stuff, they're not part of the token
* quote -
set of characters that quote stuff, they're not part of the token
* escape -
character than can quote quotes
* escape -
character than can quote quotes
* was_quoted - if not NULL, stores the quoting character if any was encountered
* was_quoted - if not NULL, stores the quoting character if any was encountered
* token_pos -
if not NULL, receives a count to the start of the token in the
* token_pos -
if not NULL, receives a count to the start of the token in the
*
parsed string
*
parsed string
*
*
* Note that the string s is _not_ overwritten in this implementation.
* Note that the string s is _not_ overwritten in this implementation.
*/
*/
char
*
strtokx
(
const
char
*
s
,
char
*
const
char
*
delim
,
strtokx
(
const
char
*
s
,
const
char
*
quote
,
const
char
*
delim
,
char
escape
,
const
char
*
quote
,
char
*
was_quoted
,
char
escape
,
unsigned
int
*
token_pos
)
char
*
was_quoted
,
unsigned
int
*
token_pos
)
{
{
static
char
*
storage
=
NULL
;
/* store the local copy of the users string here */
static
char
*
storage
=
NULL
;
/* store the local copy of the users
static
char
*
string
=
NULL
;
/* pointer into storage where to continue on next call */
* string here */
/* variously abused variables: */
static
char
*
string
=
NULL
;
/* pointer into storage where to continue
unsigned
int
offset
;
* on next call */
char
*
start
;
char
*
cp
=
NULL
;
/* variously abused variables: */
unsigned
int
offset
;
if
(
s
)
{
char
*
start
;
free
(
storage
);
char
*
cp
=
NULL
;
storage
=
strdup
(
s
);
string
=
storage
;
if
(
s
)
}
{
free
(
storage
);
if
(
!
storage
)
storage
=
strdup
(
s
);
return
NULL
;
string
=
storage
;
}
/* skip leading "whitespace" */
offset
=
strspn
(
string
,
delim
);
/* end of string reached */
if
(
string
[
offset
]
==
'\0'
)
{
/* technically we don't need to free here, but we're nice */
free
(
storage
);
storage
=
NULL
;
string
=
NULL
;
return
NULL
;
}
/* test if quoting character */
if
(
quote
)
cp
=
strchr
(
quote
,
string
[
offset
]);
if
(
cp
)
{
/* okay, we have a quoting character, now scan for the closer */
char
*
p
;
start
=
&
string
[
offset
+
1
];
if
(
token_pos
)
if
(
!
storage
)
*
token_pos
=
start
-
storage
;
return
NULL
;
/* skip leading "whitespace" */
offset
=
strspn
(
string
,
delim
);
/* end of string reached */
if
(
string
[
offset
]
==
'\0'
)
{
/* technically we don't need to free here, but we're nice */
free
(
storage
);
storage
=
NULL
;
string
=
NULL
;
return
NULL
;
}
for
(
p
=
start
;
/* test if quoting character */
*
p
&&
(
*
p
!=
*
cp
||
*
(
p
-
1
)
==
escape
)
;
if
(
quote
)
cp
=
strchr
(
quote
,
string
[
offset
]);
if
(
cp
)
{
/* okay, we have a quoting character, now scan for the closer */
char
*
p
;
start
=
&
string
[
offset
+
1
];
if
(
token_pos
)
*
token_pos
=
start
-
storage
;
for
(
p
=
start
;
*
p
&&
(
*
p
!=
*
cp
||
*
(
p
-
1
)
==
escape
);
#ifdef MULTIBYTE
#ifdef MULTIBYTE
p
+=
PQmblen
(
p
)
p
+=
PQmblen
(
p
)
#else
#else
p
++
p
++
#endif
#endif
);
);
/* not yet end of string? */
/* not yet end of string? */
if
(
*
p
!=
'\0'
)
{
if
(
*
p
!=
'\0'
)
*
p
=
'\0'
;
{
string
=
p
+
1
;
*
p
=
'\0'
;
if
(
was_quoted
)
string
=
p
+
1
;
*
was_quoted
=
*
cp
;
if
(
was_quoted
)
unescape_quotes
(
start
,
*
cp
,
escape
);
*
was_quoted
=
*
cp
;
return
start
;
unescape_quotes
(
start
,
*
cp
,
escape
);
return
start
;
}
else
{
if
(
was_quoted
)
*
was_quoted
=
*
cp
;
string
=
p
;
unescape_quotes
(
start
,
*
cp
,
escape
);
return
start
;
}
}
}
else
{
if
(
was_quoted
)
*
was_quoted
=
*
cp
;
string
=
p
;
unescape_quotes
(
start
,
*
cp
,
escape
);
/* otherwise no quoting character. scan till next delimiter */
return
start
;
start
=
&
string
[
offset
];
}
}
/* otherwise no quoting character. scan till next delimiter */
if
(
token_pos
)
start
=
&
string
[
offset
];
*
token_pos
=
start
-
storage
;
if
(
token_pos
)
*
token_pos
=
start
-
storage
;
offset
=
strcspn
(
start
,
delim
);
offset
=
strcspn
(
start
,
delim
);
if
(
was_quoted
)
if
(
was_quoted
)
*
was_quoted
=
0
;
*
was_quoted
=
0
;
if
(
start
[
offset
]
!=
'\0'
)
{
if
(
start
[
offset
]
!=
'\0'
)
start
[
offset
]
=
'\0'
;
{
string
=
&
start
[
offset
]
+
1
;
start
[
offset
]
=
'\0'
;
string
=
&
start
[
offset
]
+
1
;
return
start
;
return
start
;
}
}
else
{
else
string
=
&
start
[
offset
];
{
return
start
;
string
=
&
start
[
offset
];
}
return
start
;
}
}
}
...
@@ -142,38 +156,41 @@ char * strtokx(const char *s,
...
@@ -142,38 +156,41 @@ char * strtokx(const char *s,
static
void
static
void
unescape_quotes
(
char
*
source
,
char
quote
,
char
escape
)
unescape_quotes
(
char
*
source
,
char
quote
,
char
escape
)
{
{
char
*
p
;
char
*
p
;
char
*
destination
,
*
tmp
;
char
*
destination
,
*
tmp
;
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
source
);
assert
(
source
);
#endif
#endif
destination
=
(
char
*
)
calloc
(
1
,
strlen
(
source
)
+
1
);
destination
=
(
char
*
)
calloc
(
1
,
strlen
(
source
)
+
1
);
if
(
!
destination
)
{
if
(
!
destination
)
perror
(
"calloc"
);
{
exit
(
EXIT_FAILURE
);
perror
(
"calloc"
);
}
exit
(
EXIT_FAILURE
);
}
tmp
=
destination
;
tmp
=
destination
;
for
(
p
=
source
;
*
p
;
p
++
)
for
(
p
=
source
;
*
p
;
p
++
)
{
{
char
c
;
char
c
;
if
(
*
p
==
escape
&&
*
(
p
+
1
)
&&
quote
==
*
(
p
+
1
))
{
if
(
*
p
==
escape
&&
*
(
p
+
1
)
&&
quote
==
*
(
p
+
1
))
c
=
*
(
p
+
1
);
{
p
++
;
c
=
*
(
p
+
1
);
}
p
++
;
else
}
c
=
*
p
;
else
c
=
*
p
;
*
tmp
=
c
;
*
tmp
=
c
;
tmp
++
;
tmp
++
;
}
}
/* Terminating null character */
/* Terminating null character */
*
tmp
=
'\0'
;
*
tmp
=
'\0'
;
strcpy
(
source
,
destination
);
strcpy
(
source
,
destination
);
}
}
src/bin/psql/stringutils.h
View file @
0e6652e6
...
@@ -3,12 +3,11 @@
...
@@ -3,12 +3,11 @@
/* The cooler version of strtok() which knows about quotes and doesn't
/* The cooler version of strtok() which knows about quotes and doesn't
* overwrite your input */
* overwrite your input */
extern
char
*
extern
char
*
strtokx
(
const
char
*
s
,
strtokx
(
const
char
*
s
,
const
char
*
delim
,
const
char
*
delim
,
const
char
*
quote
,
const
char
*
quote
,
char
escape
,
char
escape
,
char
*
was_quoted
,
char
*
was_quoted
,
unsigned
int
*
token_pos
);
unsigned
int
*
token_pos
);
#endif
/* STRINGUTILS_H */
#endif
/* STRINGUTILS_H */
src/bin/psql/variables.c
View file @
0e6652e6
...
@@ -6,127 +6,145 @@
...
@@ -6,127 +6,145 @@
#include <assert.h>
#include <assert.h>
VariableSpace
CreateVariableSpace
(
void
)
VariableSpace
CreateVariableSpace
(
void
)
{
{
struct
_variable
*
ptr
;
struct
_variable
*
ptr
;
ptr
=
calloc
(
1
,
sizeof
*
ptr
);
ptr
=
calloc
(
1
,
sizeof
*
ptr
);
if
(
!
ptr
)
return
NULL
;
if
(
!
ptr
)
return
NULL
;
ptr
->
name
=
strdup
(
"@"
);
ptr
->
value
=
strdup
(
""
);
if
(
!
ptr
->
name
||
!
ptr
->
value
)
{
free
(
ptr
->
name
);
free
(
ptr
->
value
);
free
(
ptr
);
return
NULL
;
}
ptr
->
name
=
strdup
(
"@"
);
return
ptr
;
ptr
->
value
=
strdup
(
""
);
if
(
!
ptr
->
name
||
!
ptr
->
value
)
{
free
(
ptr
->
name
);
free
(
ptr
->
value
);
free
(
ptr
);
return
NULL
;
}
return
ptr
;
}
}
const
char
*
GetVariable
(
VariableSpace
space
,
const
char
*
name
)
const
char
*
GetVariable
(
VariableSpace
space
,
const
char
*
name
)
{
{
struct
_variable
*
current
;
struct
_variable
*
current
;
if
(
!
space
)
if
(
!
space
)
return
NULL
;
return
NULL
;
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
NULL
;
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
NULL
;
for
(
current
=
space
;
current
;
current
=
current
->
next
)
{
for
(
current
=
space
;
current
;
current
=
current
->
next
)
{
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
current
->
name
);
assert
(
current
->
name
);
assert
(
current
->
value
);
assert
(
current
->
value
);
#endif
#endif
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
return
current
->
value
;
return
current
->
value
;
}
}
return
NULL
;
return
NULL
;
}
}
bool
GetVariableBool
(
VariableSpace
space
,
const
char
*
name
)
bool
GetVariableBool
(
VariableSpace
space
,
const
char
*
name
)
{
{
return
GetVariable
(
space
,
name
)
!=
NULL
?
true
:
false
;
return
GetVariable
(
space
,
name
)
!=
NULL
?
true
:
false
;
}
}
bool
SetVariable
(
VariableSpace
space
,
const
char
*
name
,
const
char
*
value
)
bool
SetVariable
(
VariableSpace
space
,
const
char
*
name
,
const
char
*
value
)
{
{
struct
_variable
*
current
,
*
previous
;
struct
_variable
*
current
,
*
previous
;
if
(
!
space
)
if
(
!
space
)
return
false
;
return
false
;
if
(
!
value
)
if
(
!
value
)
return
DeleteVariable
(
space
,
name
);
return
DeleteVariable
(
space
,
name
);
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
false
;
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
false
;
for
(
current
=
space
;
current
;
previous
=
current
,
current
=
current
->
next
)
{
for
(
current
=
space
;
current
;
previous
=
current
,
current
=
current
->
next
)
{
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
current
->
name
);
assert
(
current
->
name
);
assert
(
current
->
value
);
assert
(
current
->
value
);
#endif
#endif
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
{
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
free
(
current
->
value
);
{
current
->
value
=
strdup
(
value
);
free
(
current
->
value
);
return
current
->
value
?
true
:
false
;
current
->
value
=
strdup
(
value
);
return
current
->
value
?
true
:
false
;
}
}
}
}
previous
->
next
=
calloc
(
1
,
sizeof
*
(
previous
->
next
));
previous
->
next
=
calloc
(
1
,
sizeof
*
(
previous
->
next
));
if
(
!
previous
->
next
)
if
(
!
previous
->
next
)
return
false
;
return
false
;
previous
->
next
->
name
=
strdup
(
name
);
previous
->
next
->
name
=
strdup
(
name
);
if
(
!
previous
->
next
->
name
)
if
(
!
previous
->
next
->
name
)
return
false
;
return
false
;
previous
->
next
->
value
=
strdup
(
value
);
previous
->
next
->
value
=
strdup
(
value
);
return
previous
->
next
->
value
?
true
:
false
;
return
previous
->
next
->
value
?
true
:
false
;
}
}
bool
DeleteVariable
(
VariableSpace
space
,
const
char
*
name
)
bool
DeleteVariable
(
VariableSpace
space
,
const
char
*
name
)
{
{
struct
_variable
*
current
,
*
previous
;
struct
_variable
*
current
,
*
previous
;
if
(
!
space
)
if
(
!
space
)
return
false
;
return
false
;
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
false
;
if
(
strspn
(
name
,
VALID_VARIABLE_CHARS
)
!=
strlen
(
name
))
return
false
;
for
(
current
=
space
,
previous
=
NULL
;
current
;
previous
=
current
,
current
=
current
->
next
)
{
for
(
current
=
space
,
previous
=
NULL
;
current
;
previous
=
current
,
current
=
current
->
next
)
{
#ifdef USE_ASSERT_CHECKING
#ifdef USE_ASSERT_CHECKING
assert
(
current
->
name
);
assert
(
current
->
name
);
assert
(
current
->
value
);
assert
(
current
->
value
);
#endif
#endif
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
{
if
(
strcmp
(
current
->
name
,
name
)
==
0
)
free
(
current
->
name
);
{
free
(
current
->
value
);
free
(
current
->
name
);
if
(
previous
)
free
(
current
->
value
);
previous
->
next
=
current
->
next
;
if
(
previous
)
free
(
current
);
previous
->
next
=
current
->
next
;
return
true
;
free
(
current
);
return
true
;
}
}
}
}
return
true
;
return
true
;
}
}
void
DestroyVariableSpace
(
VariableSpace
space
)
void
DestroyVariableSpace
(
VariableSpace
space
)
{
{
if
(
!
space
)
if
(
!
space
)
return
;
return
;
DestroyVariableSpace
(
space
->
next
);
DestroyVariableSpace
(
space
->
next
);
free
(
space
);
free
(
space
);
}
}
src/bin/psql/variables.h
View file @
0e6652e6
...
@@ -13,21 +13,22 @@
...
@@ -13,21 +13,22 @@
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
struct
_variable
{
struct
_variable
char
*
name
;
{
char
*
value
;
char
*
name
;
struct
_variable
*
next
;
char
*
value
;
struct
_variable
*
next
;
};
};
typedef
struct
_variable
*
VariableSpace
;
typedef
struct
_variable
*
VariableSpace
;
VariableSpace
CreateVariableSpace
(
void
);
VariableSpace
CreateVariableSpace
(
void
);
const
char
*
GetVariable
(
VariableSpace
space
,
const
char
*
name
);
const
char
*
GetVariable
(
VariableSpace
space
,
const
char
*
name
);
bool
GetVariableBool
(
VariableSpace
space
,
const
char
*
name
);
bool
GetVariableBool
(
VariableSpace
space
,
const
char
*
name
);
bool
SetVariable
(
VariableSpace
space
,
const
char
*
name
,
const
char
*
value
);
bool
SetVariable
(
VariableSpace
space
,
const
char
*
name
,
const
char
*
value
);
bool
DeleteVariable
(
VariableSpace
space
,
const
char
*
name
);
bool
DeleteVariable
(
VariableSpace
space
,
const
char
*
name
);
void
DestroyVariableSpace
(
VariableSpace
space
);
void
DestroyVariableSpace
(
VariableSpace
space
);
#endif
/* VARIABLES_H */
#endif
/* VARIABLES_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment