What is the "classoid" column in pg_description table? I see it changing for different kinds of objects like table, function etc.,
The PostgreSQL Official documentations says classoid is "The OID of the system catalog this object appears in", but I don't understand it.
https://www.postgresql.org/docs/8.2/static/catalog-pg-description.html
If I'm trying to insert some comments into the pg_description table, is there a specific value based on the object? Like for table --> 1259 for Function --> 1259 etc.,
If that's true, may I know where can I find that list of classoids'?
They're oid values from pg_class, which is Postgres' internal list of tables (among other things). The simplest way to work with them is via the regclass type, e.g.:
/* Show catalog table name for all entries */
SELECT classoid::regclass, * FROM pg_description
/* Show all entries referencing pg_proc (i.e. functions) */
SELECT objoid::regprocedure, * FROM pg_description WHERE classoid = 'pg_proc'::regclass
However, you really, really shouldn't insert into the catalog tables directly. There may be associated entries to add, locks to acquire, validation to do, etc., and unless you know exactly what's going on under the hood, you could easily corrupt your database.
If you want to add an entry to pg_description, use a COMMENT statement.
The official docs and the accepted answer here did not add clarity for me, so I'm going to try to restate the answer in a way that might be helpful to others.
pg_description has four columns, objoid, classoid, objsubid, and description. description has the comment, but the other three values are needed to know exactly what is being commented on.
objoid tells whatyou specific thing in the DB this comment applies to, i.e. what actual table, column, procedure. But, there is way to know what this is refering to without consulting classoid.
classoid is the type of thing being described. This is a foreign key into pg_class. The relname column in pg_class tells you the type of thing.
pg_class is somewhat confusing. If you do select * from pg_class where relname = 'my_table' you will get a result, but this is not what pg_description.classoid refers to!
pg_description.classoid should refer to the entry in pg_class with the relname of…the string 'pg_class'. That is because the type of the thing being described is a class. And, of course, in this case, you then use objoid to look up the table in pg_class.
To clarify this, suppose pg_description's classoid referred to the entry in pg_class with the value for relname of 'pg_proc' That means that objoid is a reference to pg_proc (not to pg_class).
OK, so how do you figure out what's what?
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
WHERE
pg_class_classoid.relname = 'pg_class'
This says to join pg_description.objoid to pg_class.oid and also to make a separate join pg_description.classoid to pg_class.oid.
We then restrict the results to only those where classoid refers to the pg_class with the relname 'pg_class'.
Now, if you do this, you'll see a lot of results. You'll see comments on tables and columns. This is where objsubid comes in.
For the table comments, the objsubid is 0, thus:
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
WHERE
pg_class_classoid.relname = 'pg_class' AND
pg_description.objsubid = 0
Will show you what you want.
For completeness, the values of objsubid that are not 0 but where classoid indicates the type of thing is a table/view, the value for objsubid should correspond to the value pg_attribute.attnum where pg_attribute.attrelid equals pg_description.objoid. Thus, to see the table and column comments with their respective names:
SELECT
pg_class_objoid.relname AS "Table/View Name",
pg_attribute.attname AS "Column Name",
pg_description.description
FROM
pg_description
LEFT JOIN
pg_class AS pg_class_objoid ON pg_description.objoid = pg_class_objoid.oid
LEFT JOIN
pg_class AS pg_class_classoid ON pg_description.classoid = pg_class_classoid.oid
LEFT JOIN
pg_attribute ON pg_attribute.attnum = pg_description.objsubid AND
pg_attribute.attrelid = pg_description.objoid
WHERE
pg_class_classoid.relname = 'pg_class'
Related
I am trying to write a function to determine the sequences related to a given table name, in the query within the function, I went with something like this:
select s.relname as sec from pg_class s
join pg_depend d on d.objid=s.oid and d.classid='pg_class'::regclass
and d.refclassid='pg_class'::regclass
join pg_class t on t.oid=d.refobjid
join pg_namespace n on n.oid=t.relnamespace
join pg_attribute a on a.attrelid=t.oid and a.attnum=d.refobjsubid
where s.relkind='S' and t.relname = 'table_name'
However it doesn't seem to find all the sequences that exists, i.e. for some tables, it just doesn't find the related sequences, but for certain ones it does.
Are there multiple ways to set n_distinct in PostgreSQL? Both of these seem to be doing the same thing but end up changing a different value within pg_attribute. What is the difference between these two commands?
alter table my_table alter column my_column set (n_distinct = 500);
alter table my_table alter column my_column set statistics 1000;
select
c.relname,
a.attname,
a.attoptions,
a.attstattarget
from
pg_class c
inner join
pg_attribute a
on c.oid = a.attrelid
where
c.relname = 'my_table'
and
a.attname = 'my_column'
order by
c.relname,
a.attname;
Name |Value
-------------|----------------
relname |my_table
attname |my_column
attoptions |{n_distinct=500}
attstattarget|1000
Both of these seem to be doing the same thing
Why would you say that? Both commands are obviously distinct. Both are related to column statistics and query planning. But they do very different things.
The statistics target ...
controls the level of detail of statistics accumulated for this column by ANALYZE. See:
Check statistics targets in PostgreSQL
Basics in the manual.
Setting n_distinct is something completely different. It means hard-coding the number (or ratio) of distinct values to expect for the given column. (But only effective after the next ANALYZE.)
Related answer on dba.SE with more on n_distinct:
Very bad query plan in PostgreSQL 9.6
I have a utility that introspects columns of tables using:
select column_name, data_type from information_schema.columns
where table_name=%s
How can I extend this to introspect columns of materialized views?
Your query carries a few shortcomings / room for improvement:
A table name is not unique inside a database, you would have to narrow down to a specific schema, or could get surprising / misleading / totally incorrect results.
It's much more effective / convenient to cast the (optionally) schema-qualified table name to regclass ... see below.
A cast to regtype gives you generic type names instead of internal ones. But that's still only the base type.
Use the system catalog information functions format_type() instead to get an exact type name including modifiers.
With the above improvements you don't need to join to additional tables. Just pg_attribute.
Dropped columns reside in the catalog until the table is vacuumed (fully). You need to exclude those.
SELECT attname, atttypid::regtype AS base_type
, format_type(atttypid, atttypmod) AS full_type
FROM pg_attribute
WHERE attrelid = 'myschema.mytable'::regclass
AND attnum > 0
AND NOT attisdropped; -- no dead columns
As an aside: the views in the information schema are only good for standard compliance and portability (rarely works anyway). If you don't plan to switch your RDBMS, stick with the catalog tables, which are much faster - and more complete, apparently.
It would seem that postgres 9.3 has left materialized views out of the information_schema. (See http://postgresql.1045698.n5.nabble.com/Re-Materialized-views-WIP-patch-td5740513i40.html for a discussion.)
The following will work for introspection:
select attname, typname
from pg_attribute a
join pg_class c on a.attrelid = c.oid
join pg_type t on a.atttypid = t.oid
where relname = %s and attnum >= 1;
The clause attnum >= 1 suppresses system columns. The type names are pg_specific this way, I guess, but good enough for my purposes.
In the same vein as pg_dump without comments on objects?, is anyone aware of a command to quickly get rid of the comments (created with COMMENT ON) on all objects at once ?
For now, I resorted to bash generating a SQL script that would void one by one the comments on each table/view/column, but it is quite slow, especially with >4000 columns.
Example:
COMMENT ON COLUMN table1.column1 IS NULL;
COMMENT ON COLUMN table1.column2 IS NULL;
COMMENT ON COLUMN table1.column3 IS NULL;
...
I have faced a very similar problem some time ago and came up with a very simple solution: delete from the system catalog table pg_description directly. Comments are just "attached" to objects and don't interfere otherwise.
DELETE FROM pg_description WHERE description = 'something special';
Disclaimer:
Manipulating catalog tables directly is dangerous and a measure of last resort. You have to know what you are doing and you are doing it at your own risk! If you screw up, you may screw up your database (cluster).
I asked about the idea on pgsql-admin list and got an encouraging answer from Tom Lane:
> DELETE FROM pg_description WHERE description = 'My very special
> totally useless comment.';
> AFAICS, there are no side-effects. Are there any?
It's safe enough, as long as you don't delete the wrong comments.
There's no hidden infrastructure for a comment.
regards, tom lane
You should make sure that there aren't any comments you'd rather keep.
Inspect what your are going to delete first. Be aware that many built-in Postgres objects have comments, too.
For instance, to only delete all comments on table columns, you could use:
SELECT *
-- DELETE
FROM pg_description
WHERE objsubid > 0;
The manual informs about the column objsubid:
For a comment on a table column, this is the column number (the objoid and
classoid refer to the table itself). For all other object types, this column is zero.
Ok, thanks to your help, I found the following commands pretty useful:
To delete a comment from a given column position of a specific object (here, mytable), you could go:
DELETE FROM pg_description WHERE (SELECT relname FROM pg_class WHERE oid=objoid)='mytable' AND objsubid=2;
...but note that it's not more efficient than using COMMENT ON mytable.myfield IS NULL;
Now, to delete all comments from my user-defined views and underlying columns, here's what works very well:
DELETE FROM pg_description WHERE (SELECT relkind FROM pg_class WHERE oid=objoid)='v' AND (SELECT relname FROM pg_class WHERE oid=objoid) ~ 'v_';
where:
(SELECT relkind FROM pg_class WHERE oid=objoid)='v': all views
(SELECT relname FROM pg_class WHERE oid=objoid) ~ 'v_' : additional security, my views' names all start with 'v_'
If you want to do this without hacking a system table, then this will generate the statements for you:
SELECT 'COMMENT ON COLUMN ' || quote_ident(pg_namespace.nspname) || '.' || quote_ident(pg_class.relname) || '.' || quote_ident(columns.column_name) || ' IS NULL;'
FROM pg_description
JOIN pg_class ON pg_class.oid = pg_description.objoid
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
JOIN information_schema.columns
ON columns.table_schema = pg_namespace.nspname
AND columns.table_name = pg_class.relname
AND columns.ordinal_position = pg_description.objsubid
You should probably add a WHERE clause that constrains it to your schemas.
How to see all the built-in general-purpose data types, which support postgresql? For example from phppgadmin is possible to browse all types, but how to get types list via query, something like this:
SELECT data_types from ....
Something like this:
select ns.nspname as schema_name, t.typname as type_name
from pg_type t
join pg_namespace ns on ns.oid = t.typnamespace
where t.typtype in ('b')
and t.typelem = 0;
pg_type contains an entry for each and every type in the database, that includes the composite type that is created for a table and so on. The above query tries to filter out those that might not be interesting for you. You will have to play around with it to make it fit your needs.
pg_type is documented in the manual: http://www.postgresql.org/docs/current/static/catalog-pg-type.html