to be run at the start of every session.
+
psql can be used in a pipe sequence, and
+ automatically detects when it is not used interactively.
+
+
1998-09-26
libpq client library, upon which
psql is built, will choose defaults.
(This will usually mean the environment variables PGDATABASE,
- PGHOST, PGPORT, PQUSER,
+ PGHOST, PGPORT, PGUSER,
respectively, if they are set. Otherwise the default host is the local host
via Unix domain sockets, the default port is decided at compile time,
the default user is the system user name, and the default database is
In normal operation,
psql provides a prompt with
the name of the database that
psql is currently
connected to followed by the string "=>". For example,
$ psql testdb
Welcome to psql, the PostgreSQL interactive terminal.
\q to quit
testdb=>
-
+
and
.
-
-
psql can be used in a pipe sequence, and
- automatically detects when it is not used interactively.
-
- Shows all column of relation
+ Shows all columns of relation
(which could be a table, view, index, or sequence),
their types, and any special attributes such as NOT NULL
or defaults, if any.
+ If the relation is, in fact, a table, any defined indices are also listed.
+ If the relation is a view, the view definition is also shown.
- If the relation is, in fact, a table, any defined indices are also listed.
- If the relation is a view, the view definition is also shown.
- If the variable description is set, any comments associated
- with a table columns are shown as well.
+ The command form \d? is identical, but any comments
+ associated with the table columns are shown as well.
Lists all available aggregate functions, together with the data type they operate on.
If pattern
(a regular expression) is specified, only matching aggregates are shown.
- If the variable description is set, comments are listed for
- each function as well.
+ If the alternative command form \da? is used,
+ comments are listed for each function as well. The command form
+ \da+ will show more information about each aggregate
+ function, which is usually not of general interest.
(which can be a regular expression), or of all objects if no argument is given.
(Object
covers aggregates, functions, operators, types, relations
(tables, views, indices, sequences, large objects), rules, and triggers.) For example:
=> \dd version
Object descriptions
Name | What | Description
---------+----------+---------------------------
version | function | PostgreSQL version string
(1 row)
-
+
Lists available functions, together with their argument and return types.
If pattern
(a regular expression) is specified, only matching functions are shown.
- If the variable description is set, comments are listed for
- each function as well.
+ If the form \df+ is used, additional information about
+ each function is shown. Comments for each function can be shown with
+ the \df? form.
If pattern is specified,
it is a regular expression restricts the listing to those objects
- whose name matches. If the variable description is set,
+ whose name matches. If one appends a ?
to the command name,
each object is listed with its associated description, if any.
- \do [ name ]
+ \do [ pattern ]
Lists available operators with their operand and return types.
- If name
+ If pattern
is specified, only operators with that name will be shown.
- (Note that, unlike with similar commands, this is not a regular expression
- because operator names were likely to interfere with regular expression
- meta-characters.)
+ (Since this is a regular expression, be sure to quote all special
+ characters in you operator name with backslashes. To prevent
+ interpretation of the backslash as a new command, you might also
+ wish to quote the argument.)
- If the variable description is set, comments are listed for
+ If the form \do? is used, comments are listed for
each operator.
\dT [ pattern ]
- List all data types or only those that match pattern.
- If the variable description is set, each type is listed with
- its associated description.
+ Lists all data types or only those that match pattern.
+ The command forms \dT+ and \dT? show extra information
+ and the associated descriptions of the types, respectively.
The new query buffer is then re-parsed according to the normal rules of
psql, where the whole buffer is treated as
a single line. (Thus you cannot make scripts
this way,
- use \i for that.) In particular, this means that
- if the query ends (or rather contains) a semicolon, it is immediately
+ use \i for that.) This means also that
+ if the query ends with (or rather contains) a semicolon, it is immediately
executed. In other cases it will merely wait in the query buffer.
Prints the arguments to the standard output. This can be useful to
intersperse information in the output of scripts. For example:
=> \echo `date`
Tue Oct 26 21:40:57 CEST 1999
-
+
Sends the current query input buffer to the backend and optionally
saves the output in filename
or pipes the output into a separate Unix shell to execute
- command. A blank \g
+ command. A bare \g
is virtually equivalent to a semicolon. A \g with argument
is a one-shot
alternative to the \o command.
Give syntax help on the specified
SQL command.
- If
command is not a defined
SQL command
- or if command is not specified,
+ If command is not specified,
list all the commands for which syntax help is
available. If command
Reads input from the file filename
- and executes it as though it has been typed on the keyboard.
+ and executes it as though it had been typed on the keyboard.
\l (or \list)
- List all the databases in the server as well as their owners. If the
- variable description is set, any descriptions for
-
the databases are shown as well. If your
PostgreSQL
+ List all the databases in the server as well as their owners. Append a
+ ?
(question mark) to the command name to see any descriptions
+
for the databases as well. If your
PostgreSQL
installation was
compiled with multibyte encoding support, the encoding scheme of each
database is shown as well.
Stores the file into a
PostgreSQL large object
.
Optionally, it associates the given comment with the object. Example:
foo=> \lo_import '/home/me/pictures/photo.xcf' 'a picture of me'
lo_import 152801
-
+
The response indicates that the large object received object id 152801
- which one ought to remember if one wants to access the object every again.
+ which one ought to remember if one wants to access the object ever again.
For that reason it is recommended to always associate a human-readable
comment with every object. Those can then be seen with the
- \lo_list command.
+ \lo_list? command.
Shows a list of all
PostgreSQL large
objects currently stored in the database along with their owners.
- If the variable description is set, the associated
- comments are shown as well.
+ Append a question mark to the command name (\lo_list?) to
+ see the the associated comments as well.
The second argument is a string that should be printed whenever a field
is null. The default is not to print anything, which can easily be mistaken
- for, say, an empty string. There one might choose to write
+ for, say, an empty string. Thus, one might choose to write
\pset null "(null)".
- As of
psql version
6.6 it is no longer
+ As of
psql version
7.0 it is no longer
necessary, in fact, to save the command history as that will be done
automatically on program termination. The history is then
also automatically loaded every time
psql
Sets the internal variable name
to value. If no second argument
is given, the variable is unset (which is different from setting it to,
- for example, and empty string: \set foo ''). If no
+ for example, an empty string: \set foo ''). If no
arguments are given, all currently defined variables are listed with their
values.
- Valid variable names can contain lower-case characters, digits, and
- underscores. In particular, no upper-case characters are allowed, as
- those are reserved for certain magic
variables and
- environment variables. See the section about
psql
- variables for details.
+ Valid variable names can contain characters, digits, and underscores.
+ See the section about
psql variables for details.
test=> \z
Access permissions for database "test"
Relation | Access permissions
----------+-------------------------------------
my_table | {"=r","joe=arwR", "group staff=ar"}
(1 row )
-
+
Read this as follows:
Escapes to a separate Unix shell or executes the Unix command
- command.
+ command. The arguments
+ are not further interpreted, the shell will see them as is. If you wish
+ to capture the output of a shell command and/or use
psql's
+ variable substitution features, use the backticks (`).
Use the file filename
as the source of queries instead of reading queries interactively.
- After the file is processed,
terminates.
+ After the file is processed,
psql terminates.
This in many ways equivalent to the internal command \i.
The output looks similar to this:
~$ psql -V
Server: PostgreSQL 6.5.2 on i586-pc-linux-gnu, compiled by egcs
psql 6.6.0 on i586-pc-linux-gnu, compiled by gcc 2.8.1 (Oct 27 1999 15:15:04), long options,
readline, history, locale, assert checks
-
+
The Server
line is identical to the one returned by the
backend function version() and thus might vary
if you query different servers by using different connection
- As of version
6.6,
psql automatically issues a
+ As of version
7.0,
psql automatically issues a
password prompt whenever the backend requests password authentication.
Because this is currently based on a hack
the automatic
recognition might mysteriously fail, hence this option to force a prompt.
psql provides variable substitution features
- similar to common Unix command shells. Variables are simply name/values
+ similar to common Unix command shells. Variables are simply name/value
pairs, where the value can be any string of any length. To set variables,
use the
psql meta-command
\set:
testdb=> \set foo bar
-
+
sets the variable foo
to the value bar
. To retrieve
the content of the variable, precede the name with a dollar-sign and use it
as the argument of any slash command:
testdb=> \echo $foo
bar
-
+
Alternatively, the value can also be interpolated into a double-quoted (or backtick-quoted)
string, like so:
testdb=> \echo "foo is now ${foo}."
foo is now bar.
-
+
(The curly braces are required. This is not
Perl.) No variable substitution
will be performed in single-quoted strings or in any of the backslash commands
- that have special parsing rules (\copy, \help).
+ that have special parsing rules (e.g., \copy).
The arguments of \set are subject to the same substitution
rules as with other commands. Thus you can construct interesting references
- such as \set "${foo}bar" 'something' and get variable
- variables of
Perl or
PHP
- fame. Unfortunately (or fortunately?), there is not way to do anything useful
+ such as \set "${foo}bar" 'something' and get soft
+ links or
variable variables
of
Perl
+ or
PHP fame, respectively.
+ Unfortunately (or fortunately?), there is not way to do anything useful
with these constructs. (\echo ${${foo}} doesn't work.) On the
other hand, \set bar $foo is a perfectly valid way to copy
a variable.
psql's internal variable names can consist of
- lower-case letters, numbers, and underscores in any order and any number of
- them. Upper-case letters are not allowed. (There is a reason for that. Keep reading.)
- If you attempt to refer to a variable that does not consist of those
- characters
psql first checks if it is the name of
- one of several defined magic
variables. Those variables you cannot
- set but they always have a value. By convention they all start with an
- upper-case letter. Finally, if no match is found that way, the value of
- the respective environment variable is substituted.
+ letters, numbers, and underscores in any order and any number of them.
+ It is recommended, however, that you stick to lower-case letters and do not
+ begin with a digit. The partial rationale for this follows.
+
+
+ If you attempt to refer to a variable that is not set,
+
psql first checks if it is the name of one of
+ several defined magic
variables. Those variables are
+ maintained internally and always have a value (at least when their semantics
+ permit it). By convention they all start with an upper-case letter. You can
+ set those variables manually, but that will shadow
their
+ special meaning, until you unset your personal copy. Finally, if no match is
+ found that way, the value of the respective environment variable is
+ substituted.
Version which contains a string with the version of
Port, User are the currently active
- connection options.
+ connection options. LastOid contains the oid that was the
+ result of the last INSERT or \lo_import
+ command. If the last command was not one of those two, the value
+ is undefined.
only care whether or not are they set, not what to. A list of all specially
treated variables follows.
-
- description
-
- If set, the various \d* commands as well as
- \l and \lo_list show object
- descriptions along with the normal information. (Except for
- \dd which always shows descriptions as this
- is its very purpose.)
-
-
-
-
die_on_error
lo_transaction
- If you use the
PostgreSQL large object interface to store
- data that does not fit into one tuple specially all the operations must be contained
- in a transaction block. (See the documentation of the large object interface for
- more information.) Since
psql has no way to keep track if
- you already have a transaction in progress when you call one of its internal commands
- \lo_export, \lo_import, \lo_unlink
- it must take some arbitrary action. This action could either be to roll back any transaction
- that might already be in progress, or to commit any such transaction, or to do nothing
- at all. In the latter case you must provide you own BEGIN/END
- block or the results are unpredictable (usually resulting in the desired action not being
- performed anyway).
+ If you use the
PostgreSQL large object
+ interface to specially store data that does not fit into one tuple,
+ all the operations must be contained in a transaction block. (See the
+ documentation of the large object interface for more information.) Since
+
psql has no way to keep track if you already
+ have a transaction in progress when you call one of its internal
+ commands \lo_export, \lo_import,
+ \lo_unlink it must take some arbitrary action. This
+ action could either be to roll back any transaction that might already
+ be in progress, or to commit any such transaction, or to do nothing at
+ all. In the latter case you must provide you own
+ BEGIN TRANSACTION/COMMIT block or
+ the results will be unpredictable (usually resulting in the desired
+ action not being performed anyway).
To choose what you want to do you set this variable to one of
- rollback
, commit
, or nothing
. The default is
- to roll back the transaction. If you just want to load one or a few objects this is fine.
- However, if you intend to transfer many large objects, it might be advisable to
- provide one explicit transaction block around all commands.
+ rollback
, commit
, or nothing
.
+ The default is to roll back the transaction. If you just want to load one
+ or a few objects this is fine. However, if you intend to transfer many
+ large objects, it might be advisable to provide one explicit transaction
+ block around all commands.
-
-
- sql_interpol
-
- The escape character for
SQL variable interpolation. See below.
-
-
-
-
An additional useful feature of
psql variables
is that you can substitute (interpolate
) them into
- regular
SQL statements. In order not to break existing
-
SQL statements, you must choose your own special
- character that tells
psql that you wish to
- interpolate the value of a variable here. You do this by setting the
- variable sql_interpol. Only the first character will be
- looked at. You can set this variable to anything you want but, for instance,
- letters, numbers, semicolons, or backslashes will not make your life easier.
- Reasonable choices include the dollar ($
) and pound
- (#
) signs.
-testdb=> \set sql_interpol '#'
-
-
-
- Once this is set up, whenever
psql sees the
- magic character where it would expect a query, it will continue scanning
- until it sees the same character again and will interpret anything in
- between as a variable name.
+ regular
SQL statements. The syntax for this is to prepend
+ the variable name with a colon (:).
testdb=> \set foo 'my_table'
-testdb=> SELECT * FROM #foo#;
-
+testdb=> SELECT * FROM :foo;
+
would then query the table my_table. The value of the
variable is copied literally, so it can even contain unbalanced quotes or
backslash commands. You must make sure that it makes sense where you put it.
+ Variable interpolation will not be performed into quoted
SQL
+ entities.
- One possible application of this mechanism is to copy the contents of a file
+ A popular application of this facility is to refer to the last inserted
+
OID in subsequent statement to build a foreign key
+ scenario.
+ Another possible use of this mechanism is to copy the contents of a file
into a field. First load the file into a variable and then proceed as above.
testdb=> \set content `cat my_file.txt`
-testdb=> INSERT INTO my_table VALUES ('#content#');
-
+testdb=> \set content "'${content}'"
+testdb=> INSERT INTO my_table VALUES (:content);
+
One possible problem with this approach is that my_file.txt
might contain single quotes. These need to be escaped so that
- they don't cause a syntax error when the second line is processed. This
+ they don't cause a syntax error when the third line is processed. This
could be done with the program
sed:
testdb=> \set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`
-
+
Observe the correct number of backslashes (6)! You can resolve it this way: After
psql has parsed this line, it passes
sed -e "s/'/\\\'/g" < my_file.txt to the shell. The shell
The first example shows how to spread a query over several lines of input.
Notice the changing prompt.
testdb=> CREATE TABLE my_table (
testdb-> first int4 not null default 0,
testdb-> second text
testdb-> );
CREATE
-
+
Now look at the table definition again:
testdb=> \d my_table
Table "my_table"
Attribute | Type | Info
first | int4 | not null default 0
second | text |
-
+
At this point you decide to change the prompt to something more
interesting:
testdb=> \set prompt1 '%n@%m %~%R%# '
peter@localhost testdb=>
-
+
Let's assume you have filled the table with data and want to take a look at it:
peter@localhost testdb=> SELECT * FROM my_table;
first | second
-------+--------
4 | four
(4 rows)
-
+
Notice how the int4 colums in right aligned while the text column in left aligned.
You can make this table look differently by using the \pset
command.
peter@localhost testdb=> \pset border 2
Border style is 2.
peter@localhost testdb=> SELECT * FROM my_table;
two,2
three,3
four,4
-
+
Alternatively, use the short commands:
peter@localhost testdb=> \a \t \x
Output format is aligned.
Tuples only is off.
-[ RECORD 4 ]-
first | 4
second | four
-
+
compatibility this is still supported to some extent but I am not
going to explain the details here as this use is discouraged. But
if you get strange messages, keep this in mind. For example
testdb=> \foo
Field separator is "oo".
-
+
is perhaps not what one would expect.
and attempting to get along with each other. Sometimes they do, sometimes
they don't. An excellent example of this can be seen in section
.
- Changing this situation, however, is beyond feasability.
+ There are vague dreams of using
flex in the future,
+ but it won't happen soon.
these limits sooner rather than later.
+
+
+ The number of options for a backslash command is limited, probably to 16.
+ You can easily change this in the source code, and perhaps I will get around
+ to fixing this one day (see previous item). Not that there is any command
+ that actually uses that many options though.
+
+
The present version is the result of a major clean-up and re-write in 1999 by
Peter Eisentraut in preparation for release 7.0.
- Many people had again contributed their ideas. The author would also like
-
to recognize the influence of tcsh at a number
- of places.
+ Many people had again contributed their ideas. A bunch of features were stolen
+ from various shells (in case you hadn't noticed), in particular
readline/history.h) in appropriate directories. If
you have the library and header files installed in an obscure place you
must tell configure about them, for example:
$ ./configure --with-includes=/opt/gnu/include --with-libraries=/opt/gnu/lib ...
-
+
Then you have to recompile
psql (not necessarily
the entire code tree).
#endif
-/* functions for use in this file only */
+/* functions for use in this file */
static backslashResult exec_command(const char *cmd,
char *const * options,
PQExpBuffer query_buf,
PsqlSettings *pset);
-static bool
- do_edit(const char *filename_arg, PQExpBuffer query_buf);
+static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
-static char *
- unescape(const char *source, PsqlSettings *pset);
+static char * unescape(const char *source, PsqlSettings *pset);
+
+static bool do_connect(const char *new_dbname,
+ const char *new_user,
+ PsqlSettings *pset);
-static bool
- do_shell(const char *command);
+static bool do_shell(const char *command);
+
+/*
+ * Perhaps this should be changed to "infinity",
+ * but there is no convincing reason to bother
+ * at this point.
+ */
+#define NR_OPTIONS 16
/*----------
{
backslashResult status = CMD_SKIP_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[NR_OPTIONS+1];
char *token;
const char *options_string = NULL;
const char *cmd;
i = 0;
token = strtokx(options_string, " \t", "\"'`", '\\', "e, &pos);
- for (i = 0; token && i < 16; i++)
+ for (i = 0; token && i < NR_OPTIONS; i++)
{
switch (quote)
{
options[i] = xstrdup(interpolate_var(token + 1, pset));
else
options[i] = xstrdup(token);
- break;
}
if (continue_parse)
break;
token = strtokx(NULL, " \t", "\"'`", '\\', "e, &pos);
- }
+ } /* for */
+
+ options[i] = NULL;
}
cmd = my_line;
* arguments to start immediately after the command, but that is
* no longer encouraged.
*/
- const char *new_options[17];
+ const char *new_options[NR_OPTIONS+1];
char new_cmd[2];
int i;
- for (i = 1; i < 17; i++)
+ for (i = 1; i < NR_OPTIONS+1; i++)
new_options[i] = options[i - 1];
new_options[0] = cmd + 1;
}
/* clean up */
- for (i = 0; i < 16 && options[i]; i++)
+ for (i = 0; i < NR_OPTIONS && options[i]; i++)
free(options[i]);
free(my_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 makes little sense but we keep it around. */
if (strcmp(cmd, "a") == 0)
{
if (pset->popt.topt.format != PRINT_ALIGNED)
}
- /*
- * \C -- override table title (formerly change HTML caption) This is
- * deprecated.
- */
+ /* \C -- override table title (formerly change HTML caption) */
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
+ * \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)
{
/* \d* commands */
else if (cmd[0] == 'd')
{
+ bool show_verbose = strchr(cmd, '+') ? true : false;
+ bool show_desc = strchr(cmd, '?') ? true : false;
+
switch (cmd[1])
{
case '\0':
+ case '?':
if (options[0])
- success = describeTableDetails(options[0], pset);
+ success = describeTableDetails(options[0], pset, show_desc);
else
- success = listTables("tvs", NULL, pset); /* standard listing of
- * interesting things */
+ /* standard listing of interesting things */
+ success = listTables("tvs", NULL, pset, show_desc);
break;
case 'a':
- success = describeAggregates(options[0], pset);
+ success = describeAggregates(options[0], pset, show_verbose, show_desc);
break;
case 'd':
success = objectDescription(options[0], pset);
break;
case 'f':
- success = describeFunctions(options[0], pset);
+ success = describeFunctions(options[0], pset, show_verbose, show_desc);
break;
case 'l':
- success = do_lo_list(pset);
+ success = do_lo_list(pset, show_desc);
break;
case 'o':
- success = describeOperators(options[0], pset);
+ success = describeOperators(options[0], pset, show_verbose, show_desc);
break;
case 'p':
success = permissionsList(options[0], pset);
break;
case 'T':
- success = describeTypes(options[0], pset);
+ success = describeTypes(options[0], pset, show_verbose, show_desc);
break;
case 't':
case 'v':
case 's':
case 'S':
if (cmd[1] == 'S' && cmd[2] == '\0')
- success = listTables("Stvs", NULL, pset);
+ success = listTables("Stvs", NULL, pset, show_desc);
else
- success = listTables(&cmd[1], options[0], pset);
+ success = listTables(&cmd[1], options[0], pset, show_desc);
break;
default:
status = CMD_UNKNOWN;
fputs("\n", stdout);
}
- /*
- * \f -- change field separator (This is deprecated in favour of
- * \pset.)
- */
+ /* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
success = do_pset("fieldsep", options[0], &pset->popt, quiet);
-
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
/* help */
else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
- helpSQL(options_string);
-
+ {
+ char buf[256] = "";
+ int i;
+ for (i=0; options && options[i] && strlen(buf)<255; i++)
+ {
+ strncat(buf, options[i], 255 - strlen(buf));
+ if (strlen(buf)<255 && options[i+1])
+ strcat(buf, " ");
+ }
+ buf[255] = '\0';
+ helpSQL(buf);
+ }
/* HTML mode */
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
- success = do_pset("format", "html", &pset->popt, quiet);
+ {
+ if (pset->popt.topt.format != PRINT_HTML)
+ success = do_pset("format", "html", &pset->popt, quiet);
+ else
+ success = do_pset("format", "aligned", &pset->popt, quiet);
+ }
/* \i is include file */
success = process_file(options[0], pset);
}
+
/* \l is list databases */
else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
- success = listAllDbs(pset);
+ success = listAllDbs(pset, false);
+ else if (strcmp(cmd, "l?") == 0 || strcmp(cmd, "list?") == 0)
+ success = listAllDbs(pset, true);
/* large object things */
}
else if (strcmp(cmd + 3, "list") == 0)
- success = do_lo_list(pset);
+ success = do_lo_list(pset, false);
+ else if (strcmp(cmd + 3, "list?") == 0)
+ success = do_lo_list(pset, true);
else if (strcmp(cmd + 3, "unlink") == 0)
{
* Returns true if all ok, false if the new connection couldn't be established
* but the old one was set back. Otherwise it terminates the program.
*/
-bool
+static bool
do_connect(const char *new_dbname, const char *new_user, PsqlSettings *pset)
{
PGconn *oldconn = pset->db;
* takes an optional regexp to match specific aggregates by name
*/
bool
-describeAggregates(const char *name, PsqlSettings *pset)
+describeAggregates(const char *name, PsqlSettings *pset, bool verbose, bool desc)
{
- char descbuf[384 + 2 * REGEXP_CUTOFF]; /* observe/adjust this
- * if you change the
- * query */
+ char buf[384 + 2 * REGEXP_CUTOFF];
PGresult *res;
- bool description = GetVariableBool(pset->vars, "description");
printQueryOpt myopt = pset->popt;
- descbuf[0] = '\0';
-
/*
* There are two kinds of aggregates: ones that work on particular
* types ones that work on all
*/
- strcat(descbuf,
+ strcpy(buf,
"SELECT a.aggname AS \"Name\", t.typname AS \"Type\"");
- if (description)
- strcat(descbuf,
- ",\n obj_description(a.oid) as \"Description\"");
- strcat(descbuf,
- "\nFROM pg_aggregate a, pg_type t\n"
- "WHERE a.aggbasetype = t.oid\n");
+ if (verbose)
+ strcat(buf, " ,u.usename as \"Owner\"");
+ if (desc)
+ strcat(buf, ",\n obj_description(a.oid) as \"Description\"");
+ strcat(buf, !verbose ?
+ ("\nFROM pg_aggregate a, pg_type t\n"
+ "WHERE a.aggbasetype = t.oid\n") :
+ ("\nFROM pg_aggregate a, pg_type t, pg_user u\n"
+ "WHERE a.aggbasetype = t.oid AND a.aggowner = u.usesysid\n")
+ );
+
if (name)
{
- strcat(descbuf, " AND a.aggname ~* '^");
- strncat(descbuf, name, REGEXP_CUTOFF);
- strcat(descbuf, "'\n");
+ strcat(buf, " AND a.aggname ~* '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf,
+ strcat(buf,
"UNION\n"
"SELECT a.aggname AS \"Name\", '(all types)' as \"Type\"");
- if (description)
- strcat(descbuf,
+ if (verbose)
+ strcat(buf, " ,u.usename as \"Owner\"");
+ if (desc)
+ strcat(buf,
",\n obj_description(a.oid) as \"Description\"");
- strcat(descbuf,
- "\nFROM pg_aggregate a\n"
- "WHERE a.aggbasetype = 0\n");
+ strcat(buf, !verbose ?
+ ("\nFROM pg_aggregate a\n"
+ "WHERE a.aggbasetype = 0\n") :
+ ("\nFROM pg_aggregate a, pg_user u\n"
+ "WHERE a.aggbasetype = 0 AND a.aggowner = u.usesysid\n")
+ );
if (name)
{
- strcat(descbuf, " AND a.aggname ~* '^");
- strncat(descbuf, name, REGEXP_CUTOFF);
- strcat(descbuf, "'\n");
+ strcat(buf, " AND a.aggname ~* '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf, "ORDER BY \"Name\", \"Type\"");
+ strcat(buf, "ORDER BY \"Name\", \"Type\"");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
/* \df
- * takes an optional regexp to narrow down the function name
+ * Takes an optional regexp to narrow down the function name
*/
bool
-describeFunctions(const char *name, PsqlSettings *pset)
+describeFunctions(const char *name, PsqlSettings *pset, bool verbose, bool desc)
{
- char descbuf[384 + REGEXP_CUTOFF];
+ char buf[384 + REGEXP_CUTOFF];
PGresult *res;
printQueryOpt myopt = pset->popt;
* 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\", p.proname as \"Function\",\n"
+ strcpy(buf,
+ "SELECT t.typname as \"Result\", p.proname as \"Function\",\n"
" oid8types(p.proargtypes) as \"Arguments\"");
- if (GetVariableBool(pset->vars, "description"))
- strcat(descbuf, "\n, obj_description(p.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_proc p, pg_type t\n"
- "WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')\n");
+ if (verbose)
+ strcat(buf, ",\n u.usename as \"Owner\", l.lanname as \"Language\", p.prosrc as \"Source\"");
+ if (desc)
+ strcat(buf, ",\n obj_description(p.oid) as \"Description\"");
+
+ if (!verbose)
+ strcat(buf,
+ "\nFROM pg_proc p, pg_type t\n"
+ "WHERE p.prorettype = t.oid and (pronargs = 0 or oid8types(p.proargtypes) != '')\n");
+ else
+ strcat(buf,
+ "\nFROM pg_proc p, pg_type t, pg_language l, pg_user u\n"
+ "WHERE p.prorettype = t.oid AND p.prolang = l.oid AND p.proowner = u.usesysid\n"
+ " AND (pronargs = 0 or oid8types(p.proargtypes) != '')\n");
+
if (name)
{
- strcat(descbuf, " AND p.proname ~* '^");
- strncat(descbuf, name, REGEXP_CUTOFF);
- strcat(descbuf, "'\n");
+ strcat(buf, " AND p.proname ~* '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf, "ORDER BY \"Function\", \"Result\", \"Arguments\"");
+ strcat(buf, "ORDER BY \"Function\", \"Result\", \"Arguments\"");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
/*
- * describeTypes
- *
- * for \dT
+ * \dT
+ * describe types
*/
bool
-describeTypes(const char *name, PsqlSettings *pset)
+describeTypes(const char *name, PsqlSettings *pset, bool verbose, bool desc)
{
- char descbuf[256 + REGEXP_CUTOFF];
+ char buf[256 + REGEXP_CUTOFF];
PGresult *res;
printQueryOpt myopt = pset->popt;
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT typname AS \"Type\"");
- if (GetVariableBool(pset->vars, "description"))
- strcat(descbuf, ", obj_description(oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_type\n"
- "WHERE typrelid = 0 AND typname !~ '^_.*'\n");
+ strcpy(buf, "SELECT t.typname AS \"Type\"");
+ if (verbose)
+ strcat(buf,
+ ",\n (CASE WHEN t.typlen=-1 THEN 'var'::text ELSE t.typlen::text END) as \"Length\""
+ ",\n u.usename as \"Owner\""
+ );
+ if (desc)
+ strcat(buf, ",\n obj_description(t.oid) as \"Description\"");
+ /*
+ * do not include array types (start with underscore),
+ * do not include user relations (typrelid!=0)
+ */
+ strcat(buf, !verbose ?
+ ("\nFROM pg_type t\n"
+ "WHERE t.typrelid = 0 AND t.typname !~ '^_.*'\n") :
+ ("\nFROM pg_type t, pg_user u\n"
+ "WHERE t.typrelid = 0 AND t.typname !~ '^_.*' AND t.typowner = u.usesysid\n")
+ );
if (name)
{
- strcat(descbuf, " AND typname ~* '^");
- strncat(descbuf, name, REGEXP_CUTOFF);
- strcat(descbuf, "' ");
+ strcat(buf, " AND t.typname ~* '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "' ");
}
- strcat(descbuf, "ORDER BY typname;");
+ strcat(buf, "ORDER BY t.typname;");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
/* \do
- * NOTE: The (optional) argument here is _not_ a regexp since with all the
- * funny chars floating around that would probably confuse people. It's an
- * exact match string.
*/
bool
-describeOperators(const char *name, PsqlSettings *pset)
+describeOperators(const char *name, PsqlSettings *pset, bool verbose, bool desc)
{
- char descbuf[1536 + 3 * 32]; /* 32 is max length for operator
- * name */
+ char buf[1536 + 3 * REGEXP_CUTOFF];
PGresult *res;
- bool description = GetVariableBool(pset->vars, "description");
printQueryOpt myopt = pset->popt;
- descbuf[0] = '\0';
+ /* Not used right now. Maybe later. */
+ (void)verbose;
- strcat(descbuf, "SELECT o.oprname AS \"Op\",\n"
+ /* FIXME: Use outer joins here when ready */
+
+ strcpy(buf,
+ "SELECT o.oprname AS \"Op\",\n"
" t1.typname AS \"Left arg\",\n"
" t2.typname AS \"Right arg\",\n"
" t0.typname AS \"Result\"");
- if (description)
- strcat(descbuf, ",\n obj_description(p.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_proc p, pg_type t0,\n"
+ if (desc)
+ strcat(buf, ",\n obj_description(p.oid) as \"Description\"");
+ strcat(buf,
+ "\nFROM pg_proc p, pg_type t0,\n"
" pg_type t1, pg_type t2,\n"
" pg_operator o\n"
"WHERE p.prorettype = t0.oid AND\n"
" o.oprright = t2.oid\n");
if (name)
{
- strcat(descbuf, " AND o.oprname = '");
- strncat(descbuf, name, 32);
- strcat(descbuf, "'\n");
+ strcat(buf, " AND o.oprname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf, "\nUNION\n\n"
+ strcat(buf, "\nUNION\n\n"
"SELECT o.oprname as \"Op\",\n"
" ''::name AS \"Left arg\",\n"
" t1.typname AS \"Right arg\",\n"
" t0.typname AS \"Result\"");
- if (description)
- strcat(descbuf, ",\n obj_description(p.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n"
+ if (desc)
+ strcat(buf, ",\n obj_description(p.oid) as \"Description\"");
+ strcat(buf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n"
"WHERE RegprocToOid(o.oprcode) = p.oid AND\n"
" o.oprresult = t0.oid AND\n"
" o.oprkind = 'l' AND\n"
" o.oprright = t1.oid\n");
if (name)
{
- strcat(descbuf, "AND o.oprname = '");
- strncat(descbuf, name, 32);
- strcat(descbuf, "'\n");
+ strcat(buf, "AND o.oprname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf, "\nUNION\n\n"
+ strcat(buf, "\nUNION\n\n"
"SELECT o.oprname as \"Op\",\n"
" t1.typname AS \"Left arg\",\n"
" ''::name AS \"Right arg\",\n"
" t0.typname AS \"Result\"");
- if (description)
- strcat(descbuf, ",\n obj_description(p.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n"
+ if (desc)
+ strcat(buf, ",\n obj_description(p.oid) as \"Description\"");
+ strcat(buf, "\nFROM pg_operator o, pg_proc p, pg_type t0, pg_type t1\n"
"WHERE RegprocToOid(o.oprcode) = p.oid AND\n"
" o.oprresult = t0.oid AND\n"
" o.oprkind = 'r' AND\n"
" o.oprleft = t1.oid\n");
if (name)
{
- strcat(descbuf, "AND o.oprname = '");
- strncat(descbuf, name, 32);
- strcat(descbuf, "'\n");
+ strcat(buf, "AND o.oprname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
- strcat(descbuf, "\nORDER BY \"Op\", \"Left arg\", \"Right arg\", \"Result\"");
+ strcat(buf, "\nORDER BY \"Op\", \"Left arg\", \"Right arg\", \"Result\"");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
* for \l, \list, and -l switch
*/
bool
-listAllDbs(PsqlSettings *pset)
+listAllDbs(PsqlSettings *pset, bool desc)
{
PGresult *res;
- char descbuf[256];
+ char buf[256];
printQueryOpt myopt = pset->popt;
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT pg_database.datname as \"Database\",\n"
+ strcpy(buf,
+ "SELECT pg_database.datname as \"Database\",\n"
" pg_user.usename as \"Owner\""
#ifdef MULTIBYTE
",\n pg_database.encoding as \"Encoding\""
#endif
);
- if (GetVariableBool(pset->vars, "description"))
- strcat(descbuf, ",\n obj_description(pg_database.oid) as \"Description\"\n");
- strcat(descbuf, "FROM pg_database, pg_user\n"
+ if (desc)
+ strcat(buf, ",\n obj_description(pg_database.oid) as \"Description\"\n");
+ strcat(buf, "FROM pg_database, pg_user\n"
"WHERE pg_database.datdba = pg_user.usesysid\n"
"ORDER BY \"Database\"");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
" relname !~ '^pg_'\n");
if (name)
{
- strcat(descbuf, " AND rename ~ '^");
+ strcat(descbuf, " AND rename ~ '");
strncat(descbuf, name, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE a.oid = d.objoid\n");
if (object)
{
- strcat(descbuf, " AND a.aggname ~* '^");
+ strcat(descbuf, " AND a.aggname ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE p.oid = d.objoid AND (p.pronargs = 0 or oid8types(p.proargtypes) != '')\n");
if (object)
{
- strcat(descbuf, " AND p.proname ~* '^");
+ strcat(descbuf, " AND p.proname ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
strcat(descbuf, "\nUNION ALL\n\n");
strcat(descbuf, "SELECT DISTINCT o.oprname as \"Name\", 'operator'::text as \"What\", d.description as \"Description\"\n"
"FROM pg_operator o, pg_description d\n"
- // must get comment via associated function
+ /* must get comment via associated function */
"WHERE RegprocToOid(o.oprcode) = d.objoid\n");
if (object)
{
- strcat(descbuf, " AND o.oprname = '");
+ strcat(descbuf, " AND o.oprname ~ '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE t.oid = d.objoid\n");
if (object)
{
- strcat(descbuf, " AND t.typname ~* '^");
+ strcat(descbuf, " AND t.typname ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE c.oid = d.objoid\n");
if (object)
{
- strcat(descbuf, " AND c.relname ~* '^");
+ strcat(descbuf, " AND c.relname ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'\n");
if (object)
{
- strcat(descbuf, " AND r.rulename ~* '^");
+ strcat(descbuf, " AND r.rulename ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
"WHERE t.oid = d.objoid\n");
if (object)
{
- strcat(descbuf, " AND t.tgname ~* '^");
+ strcat(descbuf, " AND t.tgname ~* '");
strncat(descbuf, object, REGEXP_CUTOFF);
strcat(descbuf, "'\n");
}
bool
-describeTableDetails(const char *name, PsqlSettings *pset)
+describeTableDetails(const char *name, PsqlSettings *pset, bool desc)
{
- char descbuf[512 + NAMEDATALEN];
- PGresult *res = NULL,
- *res2 = NULL,
- *res3 = NULL;
+ char buf[512 + 8 * NAMEDATALEN];
+ PGresult *res = NULL;
printTableOpt myopt = pset->popt.topt;
- bool description = GetVariableBool(pset->vars, "description");
int i;
- const char *view_def = NULL;
+ const char *view_def = NULL;
const char *headers[5];
char **cells = NULL;
char *title = NULL;
char **footers = NULL;
char **ptr;
unsigned int cols;
+ struct { bool hasindex; char relkind; int16 checks; int16 triggers; bool hasrules; } tableinfo;
+ bool error = false;
- cols = 3 + (description ? 1 : 0);
-
- headers[0] = "Attribute";
- headers[1] = "Type";
- headers[2] = "Info";
- if (description)
- {
- headers[3] = "Description";
- headers[4] = NULL;
- }
- else
- headers[3] = NULL;
+ /* truncate table name */
+ if (strlen(name) > NAMEDATALEN) {
+ char *my_name = xmalloc(NAMEDATALEN+1);
+ strncpy(my_name, name, NAMEDATALEN);
+ my_name[NAMEDATALEN] = '\0';
+ name = my_name;
+ }
/* 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, "\nFROM 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;
+ sprintf(buf,
+ "SELECT relhasindex, relkind, relchecks, reltriggers, relhasrules\n"
+ "FROM pg_class WHERE relname='%s'",
+ name);
+ res = PSQLexec(pset, buf);
+ 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);
+ fprintf(stdout, "Did not find any relation 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;
+ /* FIXME: check for null pointers here? */
+ tableinfo.hasindex = strcmp(PQgetvalue(res,0,0),"t")==0;
+ tableinfo.relkind = *(PQgetvalue(res,0,1));
+ tableinfo.checks = atoi(PQgetvalue(res,0,2));
+ tableinfo.triggers = atoi(PQgetvalue(res,0,3));
+ tableinfo.hasrules = strcmp(PQgetvalue(res,0,4),"t")==0;
+ PQclear(res);
- if (PQntuples(res2) > 0)
- view_def = PQgetvalue(res2, 0, 0);
+ headers[0] = "Attribute";
+ headers[1] = "Type";
+ cols = 2;
+ if (tableinfo.relkind == 'r' || tableinfo.relkind == 's')
+ {
+ cols++;
+ headers[cols-1] = "Extra";
+ }
- /* Generate table cells to be printed */
- cells = calloc(PQntuples(res) * cols + 1, sizeof(*cells));
- if (!cells)
+ if (desc)
{
- perror("calloc");
- exit(EXIT_FAILURE);
+ cols++;
+ headers[cols-1] = "Description";
}
+ headers[cols] = NULL;
+
+
+ /* Get column info */
+ strcpy(buf, "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum");
+ if (desc)
+ strcat(buf, ", obj_description(a.oid)");
+ strcat(buf, "\nFROM pg_class c, pg_attribute a, pg_type t\n"
+ "WHERE c.relname = '");
+ strncat(buf, name, NAMEDATALEN);
+ strcat(buf, "'\n AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid\n"
+ "ORDER BY a.attnum");
+
+ res = PSQLexec(pset, buf);
+ if (!res)
+ return false;
+
+ /* Check if table is a view */
+ if (tableinfo.hasrules) {
+ PGresult *result;
+ sprintf(buf, "SELECT definition FROM pg_views WHERE viewname = '%s'", name);
+ result = PSQLexec(pset, buf);
+ if (!result)
+ {
+ PQclear(res);
+ PQclear(result);
+ return false;
+ }
+
+ if (PQntuples(result) > 0)
+ view_def = xstrdup(PQgetvalue(result, 0, 0));
+ PQclear(result);
+ }
+
+
+ /* Generate table cells to be printed */
+ cells = xmalloc((PQntuples(res) * cols + 1) * sizeof(*cells));
+ cells[PQntuples(res) * cols] = NULL; /* end of list */
+
for (i = 0; i < PQntuples(res); i++)
{
int4 attypmod = atoi(PQgetvalue(res, i, 3));
const char *attype = PQgetvalue(res, i, 1);
/* Name */
- cells[i * cols + 0] = (char*)PQgetvalue(res, i, 0); /* don't free this afterwards */
-
+ cells[i * cols + 0] = (char *)PQgetvalue(res, i, 0); /* don't free this afterwards */
+
/* Type */
+ /* (convert some internal type names to "readable") */
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
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));
- }
+
+ /* Extra: not null and default */
+ /* (I'm cutting off the 'default' string at 128) */
+ if (tableinfo.relkind == 'r' || tableinfo.relkind == 's')
+ {
+ cells[i * cols + 2] = xmalloc(128 + 128);
+ cells[i * cols + 2][0] = '\0';
+ if (strcmp(PQgetvalue(res, i, 4), "t") == 0)
+ strcat(cells[i * cols + 2], "not null");
+
+ /* handle "default" here */
+ if (strcmp(PQgetvalue(res, i, 5), "t") == 0)
+ {
+ PGresult *result;
+
+ sprintf(buf, "SELECT substring(d.adsrc for 128) FROM pg_attrdef d, pg_class c\n"
+ "WHERE c.relname = '%s' AND c.oid = d.adrelid AND d.adnum = %s",
+ name, PQgetvalue(res, i, 6));
+
+ result = PSQLexec(pset, buf);
+ if (!result)
+ error = true;
+ else
+ {
+ if (cells[i * cols + 2][0])
+ strcat(cells[i * cols + 2], " ");
+ strcat(cells[i * cols + 2], "default ");
+ strcat(cells[i * cols + 2], PQgetvalue(result, 0, 0));
+ PQclear(result);
+ }
+ }
+ }
+
+ if (error)
+ break;
/* Description */
- if (description)
- cells[i * cols + 3] = (char*)PQgetvalue(res, i, 7);
+ if (desc)
+ cells[i * cols + cols-1] = (char*)PQgetvalue(res, i, 7);
}
/* Make title */
- title = xmalloc(10 + strlen(name));
- if (view_def)
- sprintf(title, "View \"%s\"", name);
- else
- sprintf(title, "Table \"%s\"", name);
+ title = xmalloc(20 + strlen(name));
+ switch (tableinfo.relkind) {
+ case 'r':
+ if (view_def)
+ sprintf(title, "View \"%s\"", name);
+ else
+ sprintf(title, "Table \"%s\"", name);
+ break;
+ case 'S':
+ sprintf(title, "Sequence \"%s\"", name);
+ break;
+ case 'i':
+ sprintf(title, "Index \"%s\"", name);
+ break;
+ case 's':
+ sprintf(title, "System table \"%s\"", name);
+ break;
+ default:
+ sprintf(title, "?%c?", tableinfo.relkind);
+ }
/* Make footers */
- if (view_def)
+ /* Information about the index */
+ if (tableinfo.relkind == 'i')
+ {
+ PGresult * result;
+
+ sprintf(buf, "SELECT i.indisunique, i.indisprimary, a.amname\n"
+ "FROM pg_index i, pg_class c, pg_am a\n"
+ "WHERE i.indexrelid = c.oid AND c.relname = '%s' AND c.relam = a.oid",
+ name);
+
+ result = PSQLexec(pset, buf);
+ if (!result)
+ error = true;
+ else
+ {
+ footers = xmalloc(2 * sizeof(*footers));
+ footers[0] = xmalloc(NAMEDATALEN + 32);
+ sprintf(footers[0], "%s%s",
+ strcmp(PQgetvalue(result, 0, 0), "t")==0 ? "unique " : "",
+ PQgetvalue(result, 0, 2)
+ );
+ if (strcmp(PQgetvalue(result, 0, 1), "t")==0)
+ strcat(footers[0], " (primary key)");
+ footers[1] = NULL;
+ }
+ }
+ /* Information about the view */
+ else if (tableinfo.relkind == 'r' && 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
+
+ /* Information about the table */
+ else if (tableinfo.relkind == 'r')
{
- /* 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;
+ PGresult *result1=NULL, *result2=NULL, *result3=NULL, *result4=NULL;
+ int index_count=0, constr_count=0, rule_count=0, trigger_count=0;
+ int count_footers=0;
+
+ /* count indices */
+ if (!error && tableinfo.hasindex)
+ {
+ sprintf(buf, "SELECT c2.relname\n"
+ "FROM pg_class c, pg_class c2, pg_index i\n"
+ "WHERE c.relname = '%s' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\n"
+ "ORDER BY c2.relname",
+ name);
+ result1 = PSQLexec(pset, buf);
+ if (!result1)
+ error = true;
+ else
+ index_count = PQntuples(result1);
+ }
+
+ /* count table (and column) constraints */
+ if (!error && tableinfo.checks)
+ {
+ sprintf(buf, "SELECT rcsrc\n"
+ "FROM pg_relcheck r, pg_class c\n"
+ "WHERE c.relname='%s' AND c.oid = r.rcrelid",
+ name);
+ result2 = PSQLexec(pset, buf);
+ if (!result2)
+ error = true;
+ else
+ constr_count = PQntuples(result2);
+ }
+
+ /* count rules */
+ if (!error && tableinfo.hasrules)
+ {
+ sprintf(buf,
+ "SELECT r.rulename\n"
+ "FROM pg_rewrite r, pg_class c\n"
+ "WHERE c.relname='%s' AND c.oid = r.ev_class",
+ name);
+ result3 = PSQLexec(pset, buf);
+ if (!result3)
+ error = true;
+ else
+ rule_count = PQntuples(result3);
+ }
+
+ /* count triggers */
+ if (!error && tableinfo.hasrules)
+ {
+ sprintf(buf,
+ "SELECT t.tgname\n"
+ "FROM pg_trigger t, pg_class c\n"
+ "WHERE c.relname='%s' AND c.oid = t.tgrelid",
+ name);
+ result4 = PSQLexec(pset, buf);
+ if (!result4)
+ error = true;
+ else
+ trigger_count = PQntuples(result4);
+ }
+
+ footers = xmalloc((index_count + constr_count + rule_count + trigger_count + 1) * sizeof(*footers));
+
+ /* print indices */
+ for (i = 0; i < index_count; i++)
+ {
+ sprintf(buf, "%s %s",
+ index_count==1 ? "Index:" : (i==0 ? "Indices:" : " "),
+ PQgetvalue(result1, i, 0)
+ );
+ if (i < index_count-1)
+ strcat(buf, ",");
+
+ footers[count_footers++] = xstrdup(buf);
+ }
+
+ /* print contraints */
+ for (i = 0; i < constr_count; i++)
+ {
+ sprintf(buf, "%s %s",
+ constr_count==1 ? "Constraint:" : (i==0 ? "Constraints:" : " "),
+ PQgetvalue(result2, i, 0)
+ );
+ footers[count_footers++] = xstrdup(buf);
+ }
+
+ /* print rules */
+ for (i = 0; i < rule_count; i++)
+ {
+ sprintf(buf, "%s %s",
+ rule_count==1 ? "Rule:" : (i==0 ? "Rules:" : " "),
+ PQgetvalue(result3, i, 0)
+ );
+ if (i < rule_count-1)
+ strcat(buf, ",");
+
+ footers[count_footers++] = xstrdup(buf);
+ }
+
+ /* print triggers */
+ for (i = 0; i < trigger_count; i++)
+ {
+ sprintf(buf, "%s %s",
+ trigger_count==1 ? "Trigger:" : (i==0 ? "Triggers:" : " "),
+ PQgetvalue(result4, i, 0)
+ );
+ if (i < trigger_count-1)
+ strcat(buf, ",");
+
+ footers[count_footers++] = xstrdup(buf);
}
- }
+ /* end of list marker */
+ footers[count_footers] = NULL;
+
+ PQclear(result1);
+ PQclear(result2);
+ PQclear(result3);
+ PQclear(result4);
+ }
- myopt.tuples_only = false;
- printTable(title, headers, (const char**)cells, (const char**)footers, "llll", &myopt, pset->queryFout);
+ if (!error) {
+ myopt.tuples_only = false;
+ printTable(title, headers, (const char**)cells, (const char**)footers, "llll", &myopt, pset->queryFout);
+ }
/* clean up */
free(title);
for (i = 0; i < PQntuples(res); i++)
{
free(cells[i * cols + 1]);
- free(cells[i * cols + 2]);
+ if (tableinfo.relkind == 'r' || tableinfo.relkind == 's')
+ free(cells[i * cols + 2]);
}
free(cells);
free(footers);
PQclear(res);
- PQclear(res2);
- PQclear(res3);
- return true;
+ return !error;
}
* i - indices
* v - views
* s - sequences
- * S - systems tables (~'^pg_')
+ * S - systems tables (~ '^pg_')
* (any order of the above is fine)
*/
bool
-listTables(const char *infotype, const char *name, PsqlSettings *pset)
+listTables(const char *infotype, const char *name, PsqlSettings *pset, bool desc)
{
bool showTables = strchr(infotype, 't') != NULL;
bool showIndices = strchr(infotype, 'i') != NULL;
bool showSeq = strchr(infotype, 's') != NULL;
bool showSystem = strchr(infotype, 'S') != NULL;
- bool description = GetVariableBool(pset->vars, "description");
-
- char descbuf[1536 + 4 * REGEXP_CUTOFF];
+ char buf[1536 + 4 * REGEXP_CUTOFF];
PGresult *res;
printQueryOpt myopt = pset->popt;
- descbuf[0] = '\0';
+ buf[0] = '\0';
/* tables */
if (showTables)
{
- strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'table'::text as \"Type\"");
- if (description)
- strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_class c, pg_user u\n"
+ strcat(buf, "SELECT c.relname as \"Name\", 'table'::text as \"Type\", u.usename as \"Owner\"");
+ if (desc)
+ strcat(buf, ", obj_description(c.oid) as \"Description\"");
+ strcat(buf, "\nFROM pg_class c, pg_user u\n"
"WHERE c.relowner = u.usesysid AND c.relkind = 'r'\n"
" AND not 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(buf, 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");
+ strcat(buf, " AND c.relname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
}
/* views */
if (showViews)
{
- if (descbuf[0])
- strcat(descbuf, "\nUNION\n\n");
+ if (buf[0])
+ strcat(buf, "\nUNION\n\n");
- strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'view'::text as \"Type\"");
- if (description)
- strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_class c, pg_user u\n"
+ strcat(buf, "SELECT c.relname as \"Name\", 'view'::text as \"Type\", u.usename as \"Owner\"");
+ if (desc)
+ strcat(buf, ", obj_description(c.oid) as \"Description\"");
+ strcat(buf, "\nFROM pg_class c, pg_user u\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");
+ strcat(buf, 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");
+ strcat(buf, " AND c.relname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
}
/* indices, sequences */
if (showIndices || showSeq)
{
- if (descbuf[0])
- strcat(descbuf, "\nUNION\n\n");
-
- strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\",\n"
- " (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as \"Type\"");
- if (description)
- strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_class c, pg_user u\n"
+ if (buf[0])
+ strcat(buf, "\nUNION\n\n");
+
+ strcat(buf,
+ "SELECT c.relname as \"Name\",\n"
+ " (CASE WHEN relkind = 'S' THEN 'sequence'::text ELSE 'index'::text END) as \"Type\",\n"
+ " u.usename as \"Owner\""
+ );
+ if (desc)
+ strcat(buf, ", obj_description(c.oid) as \"Description\"");
+ strcat(buf, "\nFROM pg_class c, pg_user u\n"
"WHERE c.relowner = u.usesysid AND relkind in (");
if (showIndices && showSeq)
- strcat(descbuf, "'i', 'S'");
+ strcat(buf, "'i', 'S'");
else if (showIndices)
- strcat(descbuf, "'i'");
+ strcat(buf, "'i'");
else
- strcat(descbuf, "'S'");
- strcat(descbuf, ")\n");
+ strcat(buf, "'S'");
+ strcat(buf, ")\n");
/* ignore large-obj indices */
if (showIndices)
- strcat(descbuf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n");
+ strcat(buf, " AND (c.relkind != 'i' OR c.relname !~ '^xinx')\n");
- strcat(descbuf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n");
+ strcat(buf, 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");
+ strcat(buf, " AND c.relname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
}
/* real system catalogue tables */
if (showSystem && showTables)
{
- if (descbuf[0])
- strcat(descbuf, "\nUNION\n\n");
+ if (buf[0])
+ strcat(buf, "\nUNION\n\n");
- strcat(descbuf, "SELECT u.usename as \"Owner\", c.relname as \"Name\", 'system'::text as \"Type\"");
- if (description)
- strcat(descbuf, ", obj_description(c.oid) as \"Description\"");
- strcat(descbuf, "\nFROM pg_class c, pg_user u\n"
+ strcat(buf, "SELECT c.relname as \"Name\", 'system'::text as \"Type\", u.usename as \"Owner\"");
+ if (desc)
+ strcat(buf, ", obj_description(c.oid) as \"Description\"");
+ strcat(buf, "\nFROM 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(buf, " AND c.relname ~ '");
+ strncat(buf, name, REGEXP_CUTOFF);
+ strcat(buf, "'\n");
}
}
- strcat(descbuf, "\nORDER BY \"Name\"");
+ strcat(buf, "\nORDER BY \"Name\"");
- res = PSQLexec(pset, descbuf);
+ res = PSQLexec(pset, buf);
if (!res)
return false;
if (PQntuples(res) == 0)
- fprintf(pset->queryFout, "No matching classes found.\n");
+ fprintf(pset->queryFout, "No matching relations found.\n");
else
{
myopt.topt.tuples_only = false;
myopt.nullPrint = NULL;
- myopt.title = "List of classes";
+ myopt.title = "List of relations";
printQuery(res, &myopt, pset->queryFout);
}
}
-/* the end */
+/* end of file */