Perhaps a possible new feature to a future PostgreSQL release

2023-11-20 Thread Erki Eessaar
Hello

Let me assume that there is a table T with columns a, b, c, d, e, f, g, and h.

If one wants to select data from all the columns except d and e, then one has 
to write

SELECT a, b, c, f, g, h
FROM T;

instead of writing

SELECT ALL BUT (d, e)
FROM T;

or something similar (perhaps by using keywords EXCEPT or EXCLUDE).

The more a table has columns, the more one has to write the column names.

There are systems that support this kind of shorthand syntax in SQL:

BigQuery: 
https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select-modifiers<https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select-modifiers>

Databricks: 
https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select.html#syntax

DuckDB: https://duckdb.org/docs/sql/query_syntax/select

Snowflake:https://stephenallwright.com/select-columns-except-snowflake/

I think that such syntax would be useful and if more and more DBMS-s start to 
offer it, then perhaps one day it will be in the SQL standard as well.

What do you think, is it something that could be added to PostgreSQL?

People are interested of this feature. The following links are just some 
examples:
http://www.postgresonline.com/journal/archives/41-How-to-SELECT-ALL-EXCEPT-some-columns-in-a-table.html

https://stackoverflow.com/questions/729197/exclude-a-column-using-select-except-columna-from-tablea

https://dba.stackexchange.com/questions/1957/sql-select-all-columns-except-some

https://www.reddit.com/r/SQL/comments/15x97kw/sql_is_there_a_way_to_just_exclude_1_column_in/


Best regards
Erki Eessaar




Re: Issues with Information_schema.views

2023-10-29 Thread Erki Eessaar
Hello

Thank you! I know that.

DO INSTEAD NOTHING rules on updatable views could be used as a way to implement 
WITH READ ONLY constraint (one can define such constraint in Oracle).  However, 
one could accidentally add such rule to non-updatable view as well.

I tried to construct a system-catalog based query to find database rules that 
are unnecessary. Thus, for the testing purposes I added a DO INSTEAD NOTHING 
rule to already non-updatable view and was a bit surprised that 
INFORMATION_SCHEMA-based check showed that the view had become updatable. A 
possible reasoning is that I can update the view without getting an error.  
However, I still cannot change data in base tables.

Secondly, the rule you demonstrated does not alone change IS_UPDATABLE value to 
YES. I have to create two rules:

 CREATE OR REPLACE RULE emps_update AS ON UPDATE
TO Emps
DO INSTEAD UPDATE emp SET
empno = NEW.empno,
ename = NEW.ename,
deptno = NEW.deptno;

 CREATE OR REPLACE RULE emps_delete AS ON DELETE
TO Emps
DO INSTEAD DELETE FROM Emp WHERE empno=OLD.empno;

My question is - is all of this the intended behaviour by the implementers?

Best regards
Erki Eessaar


From: jian he 
Sent: Saturday, October 28, 2023 13:38
To: Erki Eessaar 
Cc: pgsql-hackers@lists.postgresql.org 
Subject: Re: Issues with Information_schema.views

On Sat, Oct 28, 2023 at 5:27 PM Erki Eessaar  wrote:
>
> Hello
>
>
> /*After that: is_updatable=YES*/
>
> 1. Indeed, now I can execute INSERT/UPDATE/DELETE against the view without 
> getting an error. However, I still cannot change the data in the database 
> through the views.

https://www.postgresql.org/docs/current/sql-createview.html
"
A more complex view that does not satisfy all these conditions is
read-only by default: the system will not allow an insert, update, or
delete on the view. You can get the effect of an updatable view by
creating INSTEAD OF triggers on the view, which must convert attempted
inserts, etc. on the view into appropriate actions on other tables.
For more information see CREATE TRIGGER. Another possibility is to
create rules (see CREATE RULE), but in practice triggers are easier to
understand and use correctly.
"
You CAN get the effect of an updateable view. But you need to make the
rule/triggers correct.

the following RULE can get the expected result.
CREATE OR REPLACE RULE emps_update AS ON UPDATE
TO Emps
DO INSTEAD UPDATE emp SET
empno = NEW.empno,
ename = NEW.ename,
deptno = NEW.deptno;
you can also look at src/test/regress/sql/triggers.sql,
src/test/regress/sql/rules.sql for more test cases.


Issues with Information_schema.views

2023-10-28 Thread Erki Eessaar
Hello

The following was tested in a PostgreSQL (16) database. In my opinion queries 
based on Information_schema.views sometimes give unexpected results.

CREATE TABLE Dept(deptno SMALLINT NOT NULL,
dname VARCHAR(50) NOT NULL,
CONSTRAINT pk_dept PRIMARY KEY (deptno));

CREATE TABLE Emp(empno INTEGER NOT NULL,
ename VARCHAR(50) NOT NULL,
deptno SMALLINT NOT NULL,
CONSTRAINT pk_emp PRIMARY KEY (empno),
CONSTRAINT fk_emp_dept FOREIGN KEY (deptno) REFERENCES Dept(deptno) ON UPDATE 
CASCADE);

CREATE VIEW emps AS SELECT *
FROM Dept INNER JOIN Emp USING (deptno);

UPDATE Emps SET ename=Upper(ename);
/*ERROR:  cannot update view "emps"
DETAIL:  Views that do not select from a single table or view are not 
automatically updatable.
HINT:  To enable updating the view, provide an INSTEAD OF UPDATE trigger or an 
unconditional ON UPDATE DO INSTEAD rule.*/

SELECT table_schema AS schema, table_name AS view, is_updatable, 
is_insertable_into
FROM Information_schema.views
WHERE table_name='emps';

/*is_updatable=NO and is_insertable_into=NO*/

CREATE OR REPLACE RULE emps_insert AS ON INSERT
TO Emps
DO INSTEAD NOTHING;

/*After that: is_insertable_into=YES*/

CREATE OR REPLACE RULE emps_update AS ON UPDATE
TO Emps
DO INSTEAD NOTHING;

/*After that: is_updatable=NO*/

CREATE OR REPLACE RULE emps_delete AS ON DELETE
TO Emps
DO INSTEAD NOTHING;

/*After that: is_updatable=YES*/

1. Indeed, now I can execute INSERT/UPDATE/DELETE against the view without 
getting an error. However, I still cannot change the data in the database 
through the views.
2. is_updatable=YES only after I add both UPDATE and DELETE DO INSTEAD NOTHING 
rules.

My question is: are 1 and 2 the expected behaviour or is there a mistake in the 
implementation of the information_schema view?

Best regards
Erki Eessaar


Re: PostgreSQL domains and NOT NULL constraint

2023-10-15 Thread Erki Eessaar
Hello

Similarly, PostgreSQL does not enforce CHECK constraints of domains that try to 
enforce NOT NULL in the same situations where it does not enforce NOT NULL 
constraints - see example in the end.

Thus, in my base tables can be rows that violate domain NOT NULL and CHECK 
constraints. For me, it is not a "feature", it is a bug.

By the way, my small applications use domain NOT NULL constraints. This was the 
reason why I asked are there any other examples in addition to those that I 
provided that allow NULL's to NOT NULL columns.

Best regards
Erki Eessaar


DROP TABLE IF EXISTS Product;
DROP TABLE IF EXISTS Product_state_type;
DROP DOMAIN IF EXISTS d_name;

CREATE DOMAIN d_name VARCHAR(50)
CONSTRAINT chk_d_name CHECK (VALUE IS NOT NULL);

CREATE TABLE Product_state_type (product_state_type_code SMALLINT NOT NULL,
name d_name,
CONSTRAINT pk_product_state_type PRIMARY KEY (product_state_type_code),
CONSTRAINT ak_product_state_type_name UNIQUE (name));

CREATE TABLE Product (product_code INTEGER NOT NULL,
name d_name,
product_state_type_code SMALLINT NOT NULL,
CONSTRAINT pk_product PRIMARY KEY (product_code),
CONSTRAINT fk_product_product_state_type FOREIGN KEY (product_state_type_code)
REFERENCES Product_state_type(product_state_type_code) ON UPDATE CASCADE);

INSERT INTO Product_state_type (product_state_type_code, name)
VALUES (1, (SELECT name FROM Product_state_type WHERE FALSE));
/*Insertion succeeds, name is NULL!*/

INSERT INTO Product (product_code, name, product_state_type_code)
SELECT 1 AS product_code, Product.name, 1 AS product_state_type_code
FROM Product_state_type LEFT JOIN Product USING (product_state_type_code);
/*Insertion succeeds, name is NULL!*/

DROP TABLE IF EXISTS Product;
DROP TABLE IF EXISTS Product_state_type;
DROP DOMAIN IF EXISTS d_name;

CREATE DOMAIN d_name VARCHAR(50)
CONSTRAINT chk_d_name CHECK (coalesce(VALUE,'')<>'');

CREATE TABLE Product_state_type (product_state_type_code SMALLINT NOT NULL,
name d_name,
CONSTRAINT pk_product_state_type PRIMARY KEY (product_state_type_code),
CONSTRAINT ak_product_state_type_name UNIQUE (name));

CREATE TABLE Product (product_code INTEGER NOT NULL,
name d_name,
product_state_type_code SMALLINT NOT NULL,
CONSTRAINT pk_product PRIMARY KEY (product_code),
CONSTRAINT fk_product_product_state_type FOREIGN KEY (product_state_type_code)
REFERENCES Product_state_type(product_state_type_code) ON UPDATE CASCADE);

INSERT INTO Product_state_type (product_state_type_code, name)
VALUES (1, (SELECT name FROM Product_state_type WHERE FALSE));
/*Insertion succeeds, name is NULL!*/

INSERT INTO Product (product_code, name, product_state_type_code)
SELECT 1 AS product_code, Product.name, 1 AS product_state_type_code
FROM Product_state_type LEFT JOIN Product USING (product_state_type_code);
/*Insertion succeeds, name is NULL!*/


From: Tom Lane 
Sent: Saturday, October 14, 2023 19:09
To: Vik Fearing 
Cc: Erki Eessaar ; pgsql-hackers@lists.postgresql.org 

Subject: Re: PostgreSQL domains and NOT NULL constraint

Vik Fearing  writes:
> On 10/13/23 06:37, Tom Lane wrote:
>> Hmph.  The really basic problem here, I think, is that the spec
>> wants to claim that a domain is a data type, but then it backs
>> off and limits where the domain's constraints need to hold.

> I don't think that is an accurate depiction of domains.
> First of all, I am not seeing where it says that a domain is a data
> type.  It allows domains to be used in some places where a data type is
> used, but that is not equivalent to a domain /being/ a data type.

Hmm, you are right.  This is something I'd never paid attention to
before, but they do seem to exclude domains from being the declared
type of any expression.  Most notably, not even a CAST to a domain
type produces the domain type.  Per SQL:2021 6.13 
syntax rules:

1) Case:
a) If a  is specified, then let TD be the data
type of the specified domain.

b) If a  is specified, then let TD be the data type
identified by .  shall not contain a
.

2) The declared type of the result of the  is TD.

Even more amusingly for our current purposes, CAST does not enforce
NOT NULL.   general rule 2:

2) Case:
a) If the  specifies NULL, then the result of CS
is the null value and no further General Rules of this
Subclause are applied.

b) If the  specifies an ,
then the result of CS is an empty collection of declared type
TD and no further General Rules of this Subclause are applied.

c) If SV is the null value, then the result of CS is the null
value and no further General Rules of this Subclause are
applied.

So for a null value the spec never reaches GR 23 that says to apply
the domain's constraints.

This is already a sufficient intellectual muddle that I'm not sure
we want to follow it slavishly.  I

Re: PostgreSQL domains and NOT NULL constraint

2023-10-14 Thread Erki Eessaar
>I doubt we'd consider doing anything about that.
>The whole business of domains with NOT NULL constraints
>is arguably a defect of the SQL standard, because
>there are multiple ways to produce a value that
>is NULL and yet must be considered to be of the domain type.

In my opinion it is inconsistent and illogical if a type sometimes contains a 
value and sometimes not.

CREATE DOMAIN d_int INTEGER NOT NULL;

All the following statements fail (and correctly so in my opinion).

SELECT (NULL)::d_int;
/*ERROR:  domain d_int does not allow null values*/

SELECT Cast(NULL AS d_int);
/*ERROR:  domain d_int does not allow null values*/

WITH val (v) AS (VALUES (1), (NULL))
SELECT Cast(v AS d_int) AS v
FROM Val;
/*ERROR:  domain d_int does not allow null values*/

In my opinion the confusion and related problems arise from the widespread 
practice of sometimes treating a domain as a type (which it is not) and 
sometimes treating NULL as  a value (which it is not).

Best regards
Erki Eessaar


Re: PostgreSQL domains and NOT NULL constraint

2023-10-13 Thread Erki Eessaar
Hello

Equaling a domain with a type is really confusing because why, for instance, in 
this case the following is possible without defining any additional operators.

CREATE DOMAIN d_name VARCHAR(50) NOT NULL;
CREATE DOMAIN d_description VARCHAR(1000) NOT NULL;
CREATE TABLE x(name d_name, description d_description);
SELECT *
FROM x
WHERE name=description;

Isn't it so that domains are not types and for this reason there are separate 
CREATE DOMAIN and CREATE TYPE statements?!

In my opinion the Notes section of CREATE DOMAIN documentation should offer 
better examples. The two examples that I provided in my demonstration seemed 
very far fetched and artificial. Frankly, I have difficulties in imagining why 
someone would like to write statements like that in a production environment 
and how the proper enforcement of NOT NULL constraints of domains could break 
things.

Lets say I have a column that I have declared mandatory by using a domain, but 
somehow I have added NULLs to the column, and if it is not possible any more, 
then things break down.

If I want to permit NULLs, then ALTER DOMAIN d DROP NOT NULL; will fix it with 
one stroke. If I do not want to permit NULLs but I have registered NULLs, then 
this is a data quality issue that has to be addressed.

Currently there is a feature (NOT NULL of domain) that the documentation 
explicitly suggests not to use. Isn't it in this case better to remove this 
feature completely?! If this would break something, then it would mean that 
systems actually rely on this constraint.

Best regards
Erki Eessaar


From: Tom Lane 
Sent: Friday, October 13, 2023 08:37
To: Vik Fearing 
Cc: Erki Eessaar ; pgsql-hackers@lists.postgresql.org 

Subject: Re: PostgreSQL domains and NOT NULL constraint

Vik Fearing  writes:
> Regardless of what the spec may or may not say about v1.d, it still
> remains that nulls should not be allowed in a *base table* if the domain
> says nulls are not allowed.  Not mentioned in this thread but the
> constraints are also applied when CASTing to the domain.

Hmph.  The really basic problem here, I think, is that the spec
wants to claim that a domain is a data type, but then it backs
off and limits where the domain's constraints need to hold.
That's fundamentally inconsistent.  It's like claiming that
'foobarbaz' is a valid value of type numeric as long as it's
only in flight within a query and you haven't tried to store it
into a table.

Practical problems with this include:

* If a function declares its argument as being of a domain type,
can it expect that the passed value obeys the constraints?

* If a function declares its result as being of a domain type,
is it required to return a result that obeys the constraints?
(This has particular force for RETURNS NULL ON NULL INPUT
functions, for which we just automatically return NULL given
a NULL input without any consideration of whether the result
type nominally prohibits that.)

* If a plpgsql function has a variable that is declared to be of
domain type, do we enforce the domain's constraints when assigning?

* If a composite type has a column of a domain type, do we enforce
the domain's constraints when assigning or casting to that?

AFAICS, the spec's position leaves all of these as judgment calls,
or else you might claim that none of the above cases are even allowed
to be declared per spec.  I don't find either of those satisfactory,
so I reiterate my position that the committee hasn't thought this
through.

> As you know, I am more than happy to (try to) amend the spec where
> needed, but Erki's complaint of a null value being allowed in a base
> table is clearly a bug in our implementation regardless of what we do
> with views.

I agree it's not a good behavior, but I still say it's traceable
to schizophenia in the spec.  If the result of a sub-select is
nominally of a domain type, we should not have to recheck the
domain constraints in order to assign it to a domain-typed target.
If it's not nominally of a domain type, please cite chapter and
verse that says it isn't.

regards, tom lane


PostgreSQL domains and NOT NULL constraint

2023-10-12 Thread Erki Eessaar
Hello

PostgreSQL's CREATE DOMAIN documentation (section Notes) describes a way how 
one can add NULL's to a column that has a domain with the NOT NULL constraint.
https://www.postgresql.org/docs/current/sql-createdomain.html

To me it seems very strange and amounts to a bug because it defeats the purpose 
of domains (to be a reusable assets) and constraints (to avoid any bypassing of 
these).

Oracle 23c added the support of domains 
(https://docs.oracle.com/en/database/oracle/oracle-database/23/sqlrf/create-domain.html).
 I tested the same scenario both in PostgreSQL and Oracle 
(https://www.oracle.com/database/free/) and found out that in these situations 
Oracle does not allow NULL's to be added to the column. I do not know as to 
whether the behaviour that is implemented in PostgreSQL is specified by the 
standard. However, if it is not the case, then how it could be that Oracle can 
but PostgreSQL cannot.

Best regards
Erki Eessaar

The scenario that I tested both in PostgreSQL (16) and Oracle (23c).
***
/*PostgreSQL 16*/

CREATE DOMAIN d_name VARCHAR(50) NOT NULL;

CREATE TABLE Product_state_type (product_state_type_code SMALLINT NOT NULL,
name d_name,
CONSTRAINT pk_product_state_type PRIMARY KEY (product_state_type_code),
CONSTRAINT ak_product_state_type_name UNIQUE (name));

CREATE TABLE Product (product_code INTEGER NOT NULL,
name d_name,
product_state_type_code SMALLINT NOT NULL,
CONSTRAINT pk_product PRIMARY KEY (product_code),
CONSTRAINT fk_product_product_state_type FOREIGN KEY (product_state_type_code)
REFERENCES Product_state_type(product_state_type_code) ON UPDATE CASCADE);

INSERT INTO Product_state_type (product_state_type_code, name)
VALUES (1, (SELECT name FROM Product_state_type WHERE FALSE));
/*Insertion succeeds, name is NULL!*/

INSERT INTO Product (product_code, name, product_state_type_code)
SELECT 1 AS product_code, Product.name, 1 AS product_state_type_code
FROM Product_state_type LEFT JOIN Product USING (product_state_type_code);
/*Insertion succeeds, name is NULL!*/

/*Oracle 23c*/

CREATE DOMAIN d_name AS VARCHAR2(50) NOT NULL;

CREATE TABLE Product_state_type (product_state_type_code NUMBER(4) NOT NULL,
name d_name,
CONSTRAINT pk_product_state_type PRIMARY KEY (product_state_type_code),
CONSTRAINT ak_product_state_type_name UNIQUE (name));

CREATE TABLE Product (product_code NUMBER(8) NOT NULL,
name d_name,
product_state_type_code NUMBER(4) NOT NULL,
CONSTRAINT pk_product PRIMARY KEY (product_code),
CONSTRAINT fk_product_product_state_type FOREIGN KEY (product_state_type_code)
REFERENCES Product_state_type(product_state_type_code));


INSERT INTO Product_state_type (product_state_type_code, name)
VALUES (1, (SELECT name FROM Product_state_type WHERE FALSE));
/*Fails.
Error report -
SQL Error: ORA-01400: cannot insert NULL into
("SYSTEM"."PRODUCT_STATE_TYPE"."NAME")
Help: https://docs.oracle.com/error-help/db/ora-01400/
01400. 0 -  "cannot insert NULL into (%s)"
*Cause:An attempt was made to insert NULL into previously listed objects.
*Action:   These objects cannot accept NULL values.*/

INSERT INTO Product_state_type (product_state_type_code, name)
VALUES (1, 'Active');

INSERT INTO Product (product_code, name, product_state_type_code)
SELECT 1 AS product_code, Product.name, 1 AS product_state_type_code
FROM Product_state_type LEFT JOIN Product USING (product_state_type_code);
/*Fails.
SQL Error: ORA-01400: cannot insert NULL into
("SYSTEM"."PRODUCT"."NAME")
Help: https://docs.oracle.com/error-help/db/ora-01400/
01400. 0 -  "cannot insert NULL into (%s)"
*Cause:An attempt was made to insert NULL into previously listed objects.
*Action:   These objects cannot accept NULL values.*/