Postgres get out enough view information to reproduce it - postgresql

Basically - I want to get out enough information from postgres to reproduce a view - I know I can do a
select * from pg_get_viewdef('my_view')
which gets me the select statement, but not the create part of it - which could be a normal view or a materialized view - and then there could grants and for a materialized view indexes.
I know I can work through all of that myself, but I'll probably get it wrong or miss something - is there an easier way in postgres? ie something that I can get out that gives me the complete ddl for reconstructing the view?

The easiest way to get the CREATE VIEW statement and all GRANTs is to run pg_dump:
pg_dump -t viewschema.viewname dbname
If you cannot use that, collecting the information is more difficult:
I recommend that you use the views pg_views and pg_matviews. They contain a column definition which contains the result of pg_get_viewdef. Just prepend CREATE VIEW or CREATE MATERIALIZED VIEW for the complete statement.
Permissions for views can be conveniently got from information_schema.table_privileges, but this view does not contain materialized views.
To get permissions directly from the metadata, you can use:
SELECT c.oid::regclass AS table_or_view
p.grantor::regrole,
p.grantee::regrole,
p.privilege_type,
p.is_grantable
FROM pg_class AS c
CROSS JOIN LATERAL aclexplode(c.relacl) AS p
WHERE c.relname = 'v';
Here, a - as grantee means PUBLIC.

Related

DBeaver changes format of Redshift views

everytime I create a view in Amazon Redshift using the Tool "DBeaver" - it totaly meeses up my SQL-Code.
Is there any reason for this?
with CTE1 as (
SELECT DISTINCT
No_
FROM
snapshots."_de_contract_header"
) Select * From CTE1
Right now I'm creating a View in Dbeaver using the create or Replace View Command
Create or Replace view snapshots.TEST as
with CTE1 as (
SELECT DISTINCT
No_
FROM
snapshots."_de_contract_header"
) Select * From CTE1
When I try to view the source-code of this view , it looks like this:
With smaler statements like this it's no problem - functionality is the same. But if statements get bigger and more complex it's nearly impossible to read the source code. Any Ideas?
That's not dbeaver, that's your database (Redshift). It's rewritting the SQL into something it can run. It always does this with queries, behind the scenes.
Store your view in a .sql file and reference that, not the printout from dbeaver.

Join a view to another table

Is it possible to join a view with another table in SQL? If so, how?
I have a query on Oracle db which has specific fields. I need to re create the same query on PostgreSQL but some of the data in the PostgreSQL query are coming from a view... And that view has missing information. It's a pretty complex view, so I don't want to NOT use it for now.
For example, in Oracle I do this:
SELECT
d.dos_id,
trunc(d.dos_creation, 'MM') as Cohorte,
sum(v.ver_etude + v.ver_direct) as encaissé
from t_dossier d
left outer join v_versement v
on v.dos_id = d.dos_id
In the Postgres one, I'm using a view. But the view does not return "dos_id" so I cannot explicitly join v_versement with the view.
Is there a way to force a view to return specific fields at runtime which weren't there when creating the view?
You can't force it
to return specific fields at runtime which weren't there when creating
the view
You can create or replace it with limitation:
https://www.postgresql.org/docs/current/static/sql-createview.html
CREATE OR REPLACE VIEW is similar, but if a view of the same name
already exists, it is replaced. The new query must generate the same
columns that were generated by the existing view query (that is, the
same column names in the same order and with the same data types), but
it may add additional columns to the end of the list. The calculations
giving rise to the output columns may be completely different.
example:
t=# create view v2 as select now();
CREATE VIEW
Time: 36.488 ms
t=# create or replace view v2 as select now(),current_user;
CREATE VIEW
Time: 8.551 ms
t=# create or replace view v2 as select now()::text,current_user;
ERROR: cannot change data type of view column "now" from timestamp with time zone to text
Time: 0.430 ms
I guess I had not realised that I can actually use the view without creating it...
So I edited the SQL statement that makes up the view, added the fields that I needed and used the code of the view without having to create a new view (creating a new view would mean outsourcing it to another company, which would cost us money..)
Thanks :)

TSQL Case on the From clause

in my LiveAppDB we have a load of views which reference a larger LiveProduction DB. What I'd like to do is switch the code to look at the TestProduction DB, depending on which Application DB the view is running on
-- VIEW CAN RUN ON LiveApplicationDB or TestApplicationDB
SELECT COL1, COL2
FROM (CASE WHEN DB_NAME() = ‘LiveApplicationDB‘ THEN LIVEPRODUCTION.DB.DBTABLE ELSE TESTPRODUCTION.DB.DBTABLE END) AS tabl1 -- CASE TO DETERMINE WHICH PRODUCTION DB TO USE
INNER JOIN dbo.ThisDBTable BRA
ON tabl1.product COLLATE Latin1_General_BIN = BRA.product
WHERE (tabl1.COL1 IS NOT NULL)
In a bid to help clarify this…
If the view LiveAppDB use LiveProductionDB else TestAppDB use TestProductionDB
Obviously you can’t use variables in a View.
Any help much appreciated.
Create a synonym in LiveProductionDB pointing to LIVEPRODUCTION.DB.DBTABLE and a synonym with the same exact name in TestAppDB pointing to TESTPRODUCTION.DB.DBTABLE. Use the synonym in your query. You probably should put something like this in your deployment script:
IF DB_NAME() = 'LiveApplicationDB'
CREATE SYNONYM dbo.DBTABLE FOR LIVEPRODUCTION.DB.DBTABLE;
IF DB_NAME() = 'LiveApplicationDB'
CREATE SYNONYM dbo.DBTABLE FOR TESTPRODUCTION.DB.DBTABLE;
FYI: Most compare tools I've tried ignore the destination of a synonym and thus this will not be considered a difference.
EDIT: I'd recommend never to use a object in an other database directly. For example: when dbA needs some objects in dbB, I'd create a schema in dbA called dbB and place synonyms in this schema referencing the objects in dbB from dbA. In most cases I'd also create a schema called dbA in dbB and place views and sprocs there only to be used by dbA. dbA is only allowed to use objects placed in the dbA schema in dbB. To futher explain the reasoning behind this approach:
All dependancies on dbB from dbA are clearly stated in both databases
Moving dbB to another server is a breeze, simply create a linked server and modify all synonyms. No sproc needs to be modified or tested.
Datamodel changes dbB don't necessarily mean you'd need to make changes in dbA, just make sure the interfaces of the objects in schema dbA in dbB remain the same.
All code that matters is exactly the same between test and production which benefits deployments and code compares
If the servers are linked, then you can use a four-part naming convention to point to your test instance - just qualify the name like so:
(CASE WHEN DB_NAME() = 'LiveApplicationDB' THEN LiveServer.LIVEPRODUCTION.DB.DBTABLE ELSE TestServer.TESTPRODUCTION.DB.DBTABLE END
But you'll have to get your DBA to agree to link the servers, or do it yourself, if you have the knowledge/power.
Donna
I'd approach this slightly differently to how you're doing it. You're always using the table dbo.ThisDBTable. Use this as the base table then join to the others with the db_name() check in the ON clause;
-- VIEW CAN RUN ON LiveApplicationDB or TestApplicationDB
SELECT BRA.COL1
,COLLATE(tab1.COL2, tab2.COL2) COL2
FROM dbo.ThisDBTable BRA
LEFT JOIN LIVEPRODUCTION.DB.DBTABLE tab1
ON BRA.product = tab1.product COLLATE Latin1_General_BIN
AND DB_NAME() = 'LiveApplicationDB'
LEFT JOIN TESTPRODUCTION.DB.DBTABLE tab2
ON BRA.product = tab2.product COLLATE Latin1_General_BIN
AND DB_NAME() = 'TestProductionDB'
WHERE tab1.COL1 IS NOT NULL

Postgres materialized view hides some data

First of all, I can not recreate this example in SQL fiddle, I got some error when I try select from view.
Problem:
I have a materialized view which fetches function parameters from my schema, based on information_schema. When I create it, it works just fine. When I refresh it, it works just fine. When I assign it to some role, and then refresh it - it loses about 75% of its contents, and refreshing does not work. Only thing that works is dropping and re creating the whole view.
Examples:
All examples was executed as superuser. Lets say that I have role:
CREATE ROLE table_owner NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE NOREPLICATION;
And I have materialized view like this:
CREATE MATERIALIZED VIEW function_def AS
SELECT
regexp_replace(r.specific_name::text, '^(.*)_[0-9]+$'::text, '\1'::text) AS function_name,
r.data_type AS output_type,
r.type_udt_name AS output_udt_name,
p.ordinal_position,
p.parameter_name,
p.data_type,
p.udt_schema,
regexp_replace(p.udt_name::text, '^_'::text, ''::text) AS udt_name
FROM information_schema.routines r
LEFT JOIN information_schema.parameters p ON p.specific_name::text = r.specific_name::text
WHERE 1 = 1 AND p.specific_schema::text = 'mySchema'::text
ORDER BY regexp_replace(r.specific_name::text, '^(.*)_[0-9]+$'::text, '\1'::text), p.ordinal_position
WITH DATA;
CREATE INDEX i_function_def_function_name ON function_def(function_name);
And lets say that in this point statement:
SElECT count(*) FROM function_def
returns 231 rows, which is correct number. Then I assign the ownership of view to some role:
ALTER TABLE function_def OWNER TO table_owner;
and select still returns 231 rows, which is correct number.
SElECT count(*) FROM function_def;
but when I refresh view like this:
REFRESH MATERIALIZED VIEW function_def WITH DATA;
and then:
SElECT count(*) FROM function_def;
the returned number of rows is constant 54, which is not correct.
I am quite puzzled here and would appreciate some help, or hint. Is this a postgres bug, or am I doing something wrong?
EDIT - solution:
As stated by Klin it is in fact a privilege issue! Because all my functions are owned by function_owner, this code has done the trick, and now everything is fine:
ALTER TABLE function_def OWNER TO function_owner;
GRANT SELECT ON TABLE function_def TO GROUP table_owner;
REFRESH MATERIALIZED VIEW is executed with the privileges of the view owner, i.e. table_owner in this case.
The user have no access to some functions and therefore he does not see some records in information_schema.routines.
You can check what functions are not accessible by table_owner by executing this query as superuser:
SELECT
regexp_replace(r.specific_name::text, '^(.*)_[0-9]+$'::text, '\1'::text) AS function_name,
r.data_type AS output_type,
r.type_udt_name AS output_udt_name,
p.ordinal_position,
p.parameter_name,
p.data_type,
p.udt_schema,
regexp_replace(p.udt_name::text, '^_'::text, ''::text) AS udt_name
FROM information_schema.routines r
LEFT JOIN information_schema.parameters p ON p.specific_name::text = r.specific_name::text
WHERE 1 = 1 AND p.specific_schema::text = 'mySchema'::text
EXCEPT
SELECT * FROM function_def;

Common table expression,WITH clause in PostgreSQL ;ERROR: relation "stkpos" does not exist

for example the following is my query
WITH stkpos as (
select * from mytbl
),
updt as (
update stkpos set field=(select sum(fieldn) from stkpos)
)
select * from stkpos
ERROR: relation "stkpos" does not exist
Unlike MS-SQL and some other DBs, PostgreSQL's CTE terms are not treated like a view. They're more like an implicit temp table - they get materialized, the planner can't push filters down into them or pull filters up out of them, etc.
One consequence of this is that you can't update them, because they're a copy of the original data, not just a view of it. You'll need to find another way to do what you want.
If you want help with that you'll need to post a new question that has a clear and self contained example (with create table statements, etc) showing a cut down version of your real problem. Enough to actually let us understand what you're trying to accomplish and why.