mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
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: Fabien COELHO <coelho@cri.ensmp.fr>
This commit is contained in:
parent
136ab7c5a5
commit
2a14b9609d
@ -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,
|
|||||||
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 */
|
||||||
|
|
||||||
for (i = 0; i < PQnfields(res); i++)
|
/* Get the column that owns this sequence */
|
||||||
seq_values[i] = pg_strdup(PQgetvalue(res, 0, i));
|
printfPQExpBuffer(&buf, "SELECT pg_catalog.quote_ident(nspname) || '.' ||"
|
||||||
seq_values[i] = NULL;
|
"\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);
|
||||||
|
|
||||||
PQclear(res);
|
result = PSQLexec(buf.data);
|
||||||
res = NULL;
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
printfPQExpBuffer(&title, _("Sequence \"%s.%s\""),
|
||||||
|
schemaname, relationname);
|
||||||
|
|
||||||
|
myopt.footers = footers;
|
||||||
|
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
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:
|
|||||||
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');
|
|||||||
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);
|
|||||||
-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
|
|||||||
|
|
||||||
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'])
|
|||||||
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';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user