Query Constraint Clauses With Schema and Table (Postgres) - postgresql

I am trying to query the constraint clauses along with schema and table in postgres. I've gotten as far as identifying information_schema.check_constraints as a useful table. The problem is that doing
select *
from information_schema.check_constraints
Results in constraint_catalog, constraint_schema, constraint_name, check_clause. The check_clause is what I want and this table also gives me the constraint_schema. However, it does not give the table that this constraint is defined on. In my current database, I have constraints with the same name defined on different tables within the same schema (which is in it of itself perhaps poor design but what I need to deal with). Is it possible to get the table name here as well?

select
conname,
connamespace::regnamespace as schemaname,
conrelid::regclass as tablename,
consrc as checkclause,
pg_get_constraintdef(oid) as definition
from
pg_constraint
where
contype = 'c'
and conrelid <> 0; -- to get only table constraints
About pg_constraint
About Object Identifier Types

Related

PostgreSQL n_distinct statistics setting

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

Temporarily deactivate constraints for updating datatype of PKs and FKs

I have to update all columns of type "uuid" to "varchar(38)". I created all the necessary queries with:
SELECT format(
'ALTER TABLE %I.%I.%I ALTER COLUMN %I SET DATA TYPE varchar(38);',
table_catalog,
table_schema,
table_name,
column_name
)
FROM information_schema.columns
WHERE data_type = 'uuid'
AND table_schema NOT LIKE 'pg_%'
AND lower(table_schema) <> 'information_schema'
AND is_updatable = 'YES';
Obviously, I can't execute the resulting queries because of all the existing PK and FK constraints involving the uuid columns.
Is there a way to temporarily disable the constraints, then executing all the queries and reactivating the constraints afterwards without dropping the constraints?
Or if I have to drop all the constraints first, is there a way to set them all up again after the updates? I am not the creator of the database so I don't have all necessary queries to create the constraints again.
I found a way to create all queries for dropping and creating all constraints of the database.
So first I have to save the output of the first query
SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" DROP CONSTRAINT "'||conname||'";'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END,contype,nspname,relname,conname;
and of the second query
SELECT 'ALTER TABLE "'||nspname||'"."'||relname||'" ADD CONSTRAINT "'||conname||'" "'||
pg_get_constraintdef(pg_constraint.oid)||'";'
FROM pg_constraint
INNER JOIN pg_class ON conrelid=pg_class.oid
INNER JOIN pg_namespace ON pg_namespace.oid=pg_class.relnamespace
ORDER BY CASE WHEN contype='f' THEN 0 ELSE 1 END DESC,contype DESC,nspname DESC,relname DESC,conname DESC;
When I have all the queries, I first dropped every constrained, updated the tables and then executed the queries for adding the constraints again. Worked perfectly!

How to introspect materialized views

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.

Why are my view's columns nullable?

I'm running PostgreSQL 9.2 on Windows.
I have an existing table with some non nullable columns :
CREATE TABLE testtable
(
bkid serial NOT NULL,
bklabel character varying(128),
lacid integer NOT NULL
}
The I create a view on this table :
CREATE OR REPLACE VIEW test AS
SELECT testtable.bkid, testtable.lacid
from public.testtable;
I'm surprised that information_schema.columns for the view reports is_nullable to be YES for the selected columns ?
select * from information_schema.columns where table_name = 'test'
Reports :
"MyDatabase";"public";"test";"bkid";1;"";"YES";"integer";;;32;2;0;;"";;"";"";"";"";"";"";"";"";"";"MyDatabase";"pg_catalog";"int4";"";"";"";;"1";"NO";"NO";"";"";"";"";"";"";"NEVER";"";"NO"
"MyDatabase";"public";"test";"lacid";2;"";"YES";"integer";;;32;2;0;;"";;"";"";"";"";"";"";"";"";"";"MyDatabase";"pg_catalog";"int4";"";"";"";;"2";"NO";"NO";"";"";"";"";"";"";"NEVER";"";"NO"
Is it an expected behavior ?
My problem is that I'm trying to import such views in an Entity Framework Data Model and it fails because all columns are marked as nullable.
EDIT 1 :
The following query :
select attrelid, attname, attnotnull, pg_class.relname
from pg_attribute
inner join pg_class on attrelid = oid
where relname = 'test'
returns :
attrelid;attname;attnotnull;relname
271543;"bkid";f;"test"
271543;"lacid";f;"test"
As expected, attnotnull is 'false'.
As #Mike-Sherrill-Catcall suggested, I could manually set them to true :
update pg_attribute
set attnotnull = 't'
where attrelid = 271543
And the change is reflected in the information_schema.columns :
select * from information_schema.columns where table_name = 'test'
Output is :
"MyDatabase";"public";"test";"bkid";1;"";"NO";"integer";;;32;2;0;;"";;"";"";"";"";"";"";"";"";"";"MyDatabase";"pg_catalog";"int4";"";"";"";;"1";"NO";"NO";"";"";"";"";"";"";"NEVER";"";"NO"
"MyDatabase";"public";"test";"lacid";2;"";"NO";"integer";;;32;2;0;;"";;"";"";"";"";"";"";"";"";"";"MyDatabase";"pg_catalog";"int4";"";"";"";;"2";"NO";"NO";"";"";"";"";"";"";"NEVER";"";"NO"
I'll try to import the views in the Entity Framework data model.
EDIT 2 :
As guessed, it works, the view is now correctly imported in the Entity Framework Data Model.
Of course, I won't set all columns to be non nullable, as demonstrated above, only those non nullable in the underlying table.
I believe this is expected behavior, but I don't pretend to fully understand it. The columns in the base table seem to have the right attributes.
The column in the system tables underlying the information_schema here seems to be "attrnotnull". I see only one thread referring to "attnotnull" on the pgsql-hackers listserv: cataloguing NOT NULL constraints. (But that column might have had a different name in an earlier version. It's probably worth researching.)
You can see the behavior with this query. You'll need to work with the WHERE clause to get exactly what you need to see.
select attrelid, attname, attnotnull, pg_class.relname
from pg_attribute
inner join pg_class on attrelid = oid
where attname like 'something%'
On my system, columns that have a primary key constraint and columns that have a NOT NULL constraint have "attnotnull" set to 't'. The same columns in a view have "attnotnull" set to 'f'.
If you tilt your head and squint just right, that kind of makes sense. The column in the view isn't declared NOT NULL. Just the column in the base table.
The column pg_attribute.attnotnull is updatable. You can set it to TRUE, and that change seems to be reflected in the information_schema views. Although you can set it to TRUE directly, I think I'd be more comfortable setting it to match the value in the base table. (And by more comfortable, I don't mean to imply I'm comfortable at all with mucking about in the system tables.)
Why:
A view could be computed but references the column from a table. That computation could result in a NULL value on what is otherwise a non-null column. So basically, they put it into the too hard basket.
There is a way to see the underlying nullability for yourself with the following query:
select vcu.column_name, c.is_nullable, c.data_type
from information_schema.view_column_usage vcu
join information_schema."columns" c
on c.column_name = vcu.column_name
and c.table_name = vcu.table_name
and c.table_schema = vcu.table_schema
and c.table_catalog = vcu.table_catalog
where view_name = 'your_view_here'
If you know that you are only projecting the columns raw without functions, then it will work. Ideally, the Postgres provider for EF would use this view and also read the view definition to confirm nullability.
The nullability tracking in PostgreSQL is not developed very much at all. In most places, it will default to claiming everything is potentially nullable, which is in many cases allowed by the relevant standards. This is the case here as well: Nullability is not tracked through views. I wouldn't rely on it for an application.

How to determine the OID of a Postgres table?

Does anyone know how to find the OID of a table in Postgres 9.1?
I am writing an update script that needs to test for the existence of a column in a table before it tries to add the column. This is to prevent errors when running the script repeatedly.
To get a table OID, cast to the object identifier type regclass (while connected to the same DB):
SELECT 'mytbl'::regclass::oid;
This finds the first table (or view, etc.) with the given name along the search_path or raises an exception if not found.
Schema-qualify the table name to remove the dependency on the search path:
SELECT 'myschema.mytbl'::regclass::oid;
In Postgres 9.4 or later you can also use to_regclass('myschema.mytbl'), which doesn't raise an exception if the table is not found:
How to check if a table exists in a given schema
Then you only need to query the catalog table pg_attribute for the existence of the column:
SELECT TRUE AS col_exists
FROM pg_attribute
WHERE attrelid = 'myschema.mytbl'::regclass
AND attname = 'mycol'
AND NOT attisdropped -- no dropped (dead) columns
-- AND attnum > 0 -- no system columns (you may or may not want this)
;
The postgres catalog table pg_class is what you should look at. There should be one row per table, with the table name in the column relname, and the oid in the hidden column oid.
You may also be interested in the pg_attribute catalog table, which includes one row per table column.
See: http://www.postgresql.org/docs/current/static/catalog-pg-class.html and http://www.postgresql.org/docs/current/static/catalog-pg-attribute.html
SELECT oid FROM pg_class WHERE relname = 'tbl_name' AND relkind = 'r';
Just to complete the possibilities I'd like to add that there exists a syntax for dropping columns in order to no error out:
ALTER TABLE mytbl
DROP COLUMN IF EXISTS mycol
See http://www.postgresql.org/docs/9.0/static/sql-altertable.html
Then you can safely add your column.