Commit 2a14b960 authored by Peter Eisentraut's avatar Peter Eisentraut

psql: Update \d sequence display

For \d sequencename, the psql code just did SELECT * FROM sequencename
to get the information to display, but this does not contain much
interesting information anymore in PostgreSQL 10, because the metadata
has been moved to a separate system catalog.

This patch creates a newly designed sequence display that is not merely
an extension of the general relation/table display as it was previously.

Example:

PostgreSQL 9.6:

=> \d foobar
           Sequence "public.foobar"
    Column     |  Type   |        Value
---------------+---------+---------------------
 sequence_name | name    | foobar
 last_value    | bigint  | 1
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 0
 is_cycled     | boolean | f
 is_called     | boolean | f

PostgreSQL 10 before this change:

=> \d foobar
   Sequence "public.foobar"
   Column   |  Type   | Value
------------+---------+-------
 last_value | bigint  | 1
 log_cnt    | bigint  | 0
 is_called  | boolean | f

New:

=> \d foobar
                           Sequence "public.foobar"
  Type  | Start | Minimum |       Maximum       | Increment | Cycles? | Cache
--------+-------+---------+---------------------+-----------+---------+-------
 bigint |     1 |       1 | 9223372036854775807 |         1 | no      |     1
Reviewed-by: default avatarFabien COELHO <coelho@cri.ensmp.fr>
parent 136ab7c5
...@@ -1380,8 +1380,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -1380,8 +1380,6 @@ describeOneTableDetails(const char *schemaname,
int i; int i;
char *view_def = NULL; char *view_def = NULL;
char *headers[11]; char *headers[11];
char **seq_values = NULL;
char **ptr;
PQExpBufferData title; PQExpBufferData title;
PQExpBufferData tmpbuf; PQExpBufferData tmpbuf;
int cols; int cols;
...@@ -1563,27 +1561,125 @@ describeOneTableDetails(const char *schemaname, ...@@ -1563,27 +1561,125 @@ describeOneTableDetails(const char *schemaname,
res = NULL; res = NULL;
/* /*
* If it's a sequence, fetch its values and store into an array that will * If it's a sequence, deal with it here separately.
* be used later.
*/ */
if (tableinfo.relkind == RELKIND_SEQUENCE) if (tableinfo.relkind == RELKIND_SEQUENCE)
{ {
printfPQExpBuffer(&buf, "SELECT * FROM %s", fmtId(schemaname)); PGresult *result = NULL;
printQueryOpt myopt = pset.popt;
char *footers[2] = {NULL, NULL};
if (pset.sversion >= 100000)
{
printfPQExpBuffer(&buf,
"SELECT pg_catalog.format_type(seqtypid, NULL) AS \"%s\",\n"
" seqstart AS \"%s\",\n"
" seqmin AS \"%s\",\n"
" seqmax AS \"%s\",\n"
" seqincrement AS \"%s\",\n"
" CASE WHEN seqcycle THEN '%s' ELSE '%s' END AS \"%s\",\n"
" seqcache AS \"%s\"\n",
gettext_noop("Type"),
gettext_noop("Start"),
gettext_noop("Minimum"),
gettext_noop("Maximum"),
gettext_noop("Increment"),
gettext_noop("yes"),
gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf,
"FROM pg_catalog.pg_sequence\n"
"WHERE seqrelid = '%s';",
oid);
}
else
{
printfPQExpBuffer(&buf,
"SELECT pg_catalog.format_type('bigint'::regtype, NULL) AS \"%s\",\n"
" start_value AS \"%s\",\n"
" min_value AS \"%s\",\n"
" max_value AS \"%s\",\n"
" increment_by AS \"%s\",\n"
" CASE WHEN is_cycled THEN '%s' ELSE '%s' END AS \"%s\",\n"
" cache_value AS \"%s\"\n",
gettext_noop("Type"),
gettext_noop("Start"),
gettext_noop("Minimum"),
gettext_noop("Maximum"),
gettext_noop("Increment"),
gettext_noop("yes"),
gettext_noop("no"),
gettext_noop("Cycles?"),
gettext_noop("Cache"));
appendPQExpBuffer(&buf, "FROM %s", fmtId(schemaname));
/* must be separate because fmtId isn't reentrant */ /* must be separate because fmtId isn't reentrant */
appendPQExpBuffer(&buf, ".%s;", fmtId(relationname)); appendPQExpBuffer(&buf, ".%s;", fmtId(relationname));
}
res = PSQLexec(buf.data); res = PSQLexec(buf.data);
if (!res) if (!res)
goto error_return; goto error_return;
seq_values = pg_malloc((PQnfields(res) + 1) * sizeof(*seq_values)); /* Footer information about a sequence */
/* Get the column that owns this sequence */
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
"\n pg_catalog.quote_ident(relname) || '.' ||"
"\n pg_catalog.quote_ident(attname),"
"\n d.deptype"
"\nFROM pg_catalog.pg_class c"
"\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
"\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
"\nINNER JOIN pg_catalog.pg_attribute a ON ("
"\n a.attrelid=c.oid AND"
"\n a.attnum=d.refobjsubid)"
"\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.objid='%s'"
"\n AND d.deptype IN ('a', 'i')",
oid);
result = PSQLexec(buf.data);
/*
* If we get no rows back, don't show anything (obviously). We should
* never get more than one row back, but if we do, just ignore it and
* don't print anything.
*/
if (!result)
goto error_return;
else if (PQntuples(result) == 1)
{
switch (PQgetvalue(result, 0, 1)[0])
{
case 'a':
footers[0] = psprintf(_("Owned by: %s"),
PQgetvalue(result, 0, 0));
break;
case 'i':
footers[0] = psprintf(_("Sequence for identity column: %s"),
PQgetvalue(result, 0, 0));
break;
}
}
PQclear(result);
for (i = 0; i < PQnfields(res); i++) printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
seq_values[i] = pg_strdup(PQgetvalue(res, 0, i)); schemaname, relationname);
seq_values[i] = NULL;
PQclear(res); myopt.footers = footers;
res = NULL; myopt.topt.default_footer = false;
myopt.title = title.data;
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
if (footers[0])
free(footers[0]);
retval = true;
goto error_return; /* not an error, just return early */
} }
/* /*
...@@ -1667,10 +1763,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -1667,10 +1763,6 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""), printfPQExpBuffer(&title, _("Materialized view \"%s.%s\""),
schemaname, relationname); schemaname, relationname);
break; break;
case RELKIND_SEQUENCE:
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
schemaname, relationname);
break;
case RELKIND_INDEX: case RELKIND_INDEX:
if (tableinfo.relpersistence == 'u') if (tableinfo.relpersistence == 'u')
printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""), printfPQExpBuffer(&title, _("Unlogged index \"%s.%s\""),
...@@ -1729,9 +1821,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -1729,9 +1821,6 @@ describeOneTableDetails(const char *schemaname,
show_column_details = true; show_column_details = true;
} }
if (tableinfo.relkind == RELKIND_SEQUENCE)
headers[cols++] = gettext_noop("Value");
if (tableinfo.relkind == RELKIND_INDEX) if (tableinfo.relkind == RELKIND_INDEX)
headers[cols++] = gettext_noop("Definition"); headers[cols++] = gettext_noop("Definition");
...@@ -1814,10 +1903,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -1814,10 +1903,6 @@ describeOneTableDetails(const char *schemaname,
printTableAddCell(&cont, default_str, false, false); printTableAddCell(&cont, default_str, false, false);
} }
/* Value: for sequences only */
if (tableinfo.relkind == RELKIND_SEQUENCE)
printTableAddCell(&cont, seq_values[i], false, false);
/* Expression for index column */ /* Expression for index column */
if (tableinfo.relkind == RELKIND_INDEX) if (tableinfo.relkind == RELKIND_INDEX)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false); printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
...@@ -2030,55 +2115,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -2030,55 +2115,6 @@ describeOneTableDetails(const char *schemaname,
PQclear(result); PQclear(result);
} }
else if (tableinfo.relkind == RELKIND_SEQUENCE)
{
/* Footer information about a sequence */
PGresult *result = NULL;
/* Get the column that owns this sequence */
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
"\n pg_catalog.quote_ident(relname) || '.' ||"
"\n pg_catalog.quote_ident(attname),"
"\n d.deptype"
"\nFROM pg_catalog.pg_class c"
"\nINNER JOIN pg_catalog.pg_depend d ON c.oid=d.refobjid"
"\nINNER JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace"
"\nINNER JOIN pg_catalog.pg_attribute a ON ("
"\n a.attrelid=c.oid AND"
"\n a.attnum=d.refobjsubid)"
"\nWHERE d.classid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.refclassid='pg_catalog.pg_class'::pg_catalog.regclass"
"\n AND d.objid='%s'"
"\n AND d.deptype IN ('a', 'i')",
oid);
result = PSQLexec(buf.data);
if (!result)
goto error_return;
else if (PQntuples(result) == 1)
{
switch (PQgetvalue(result, 0, 1)[0])
{
case 'a':
printfPQExpBuffer(&buf, _("Owned by: %s"),
PQgetvalue(result, 0, 0));
printTableAddFooter(&cont, buf.data);
break;
case 'i':
printfPQExpBuffer(&buf, _("Sequence for identity column: %s"),
PQgetvalue(result, 0, 0));
printTableAddFooter(&cont, buf.data);
break;
}
}
/*
* If we get no rows back, don't show anything (obviously). We should
* never get more than one row back, but if we do, just ignore it and
* don't print anything.
*/
PQclear(result);
}
else if (tableinfo.relkind == RELKIND_RELATION || else if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_MATVIEW || tableinfo.relkind == RELKIND_MATVIEW ||
tableinfo.relkind == RELKIND_FOREIGN_TABLE || tableinfo.relkind == RELKIND_FOREIGN_TABLE ||
...@@ -2963,13 +2999,6 @@ error_return: ...@@ -2963,13 +2999,6 @@ error_return:
termPQExpBuffer(&title); termPQExpBuffer(&title);
termPQExpBuffer(&tmpbuf); termPQExpBuffer(&tmpbuf);
if (seq_values)
{
for (ptr = seq_values; *ptr; ptr++)
free(*ptr);
free(seq_values);
}
if (view_def) if (view_def)
free(view_def); free(view_def);
......
...@@ -32,6 +32,13 @@ SELECT pg_get_serial_sequence('itest1', 'a'); ...@@ -32,6 +32,13 @@ SELECT pg_get_serial_sequence('itest1', 'a');
public.itest1_a_seq public.itest1_a_seq
(1 row) (1 row)
\d itest1_a_seq
Sequence "public.itest1_a_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
---------+-------+---------+------------+-----------+---------+-------
integer | 1 | 1 | 2147483647 | 1 | no | 1
Sequence for identity column: public.itest1.a
CREATE TABLE itest4 (a int, b text); CREATE TABLE itest4 (a int, b text);
ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL
ERROR: column "a" of relation "itest4" must be declared NOT NULL before identity can be added ERROR: column "a" of relation "itest4" must be declared NOT NULL before identity can be added
......
...@@ -535,6 +535,19 @@ SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); ...@@ -535,6 +535,19 @@ SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass);
-1 | -9223372036854775808 | -1 | -1 | f | 1 | 20 -1 | -9223372036854775808 | -1 | -1 | f | 1 | 20
(1 row) (1 row)
\d sequence_test4
Sequence "public.sequence_test4"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
--------+-------+----------------------+---------+-----------+---------+-------
bigint | -1 | -9223372036854775808 | -1 | -1 | no | 1
\d serialtest2_f2_seq
Sequence "public.serialtest2_f2_seq"
Type | Start | Minimum | Maximum | Increment | Cycles? | Cache
---------+-------+---------+------------+-----------+---------+-------
integer | 1 | 1 | 2147483647 | 1 | no | 1
Owned by: public.serialtest2.f2
-- Test comments -- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work'; COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist ERROR: relation "asdf" does not exist
......
...@@ -14,6 +14,8 @@ SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE ...@@ -14,6 +14,8 @@ SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE
SELECT pg_get_serial_sequence('itest1', 'a'); SELECT pg_get_serial_sequence('itest1', 'a');
\d itest1_a_seq
CREATE TABLE itest4 (a int, b text); CREATE TABLE itest4 (a int, b text);
ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL
ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL; ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
......
...@@ -246,6 +246,10 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest']) ...@@ -246,6 +246,10 @@ WHERE sequencename ~ ANY(ARRAY['sequence_test', 'serialtest'])
SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass); SELECT * FROM pg_sequence_parameters('sequence_test4'::regclass);
\d sequence_test4
\d serialtest2_f2_seq
-- Test comments -- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work'; COMMENT ON SEQUENCE asdf IS 'won''t work';
COMMENT ON SEQUENCE sequence_test2 IS 'will work'; COMMENT ON SEQUENCE sequence_test2 IS 'will work';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment