No partition pruning when initializing query plan with LATERAL JOIN and aggregates

2020-01-13 Thread Marcin Barczyński
I wonder why partition pruning doesn't work with LATERAL JOIN and
aggregates.
Below is my example tested on PostgreSQL 12.1 (Ubuntu 12.1-1.pgdg18.04+1):

CREATE TABLE demo(key BIGINT) PARTITION BY LIST (key);
CREATE TABLE demo_key_1 PARTITION OF demo FOR VALUES IN (1);
CREATE TABLE demo_key_2 PARTITION OF demo FOR VALUES IN (2);
INSERT INTO demo(key) VALUES (1), (2);
ANALYZE demo;

CREATE TABLE demo2(key BIGINT) PARTITION BY LIST (key);
CREATE TABLE demo2_key_1 PARTITION OF demo2 FOR VALUES IN (1);
CREATE TABLE demo2_key_2 PARTITION OF demo2 FOR VALUES IN (2);
INSERT INTO demo2(key) VALUES (1), (2);
ANALYZE demo2;

Now, if there are no aggregates in SELECT under LATERAL JOIN, everything
works as expected - only a single partition of each table is scanned:

EXPLAIN ANALYZE
 SELECT * FROM demo
 JOIN LATERAL (
  SELECT key AS key2
  FROM demo2
  WHERE demo2.key = demo.key
 ) d ON TRUE
 WHERE demo.key = 1;
QUERY PLAN
---
 Nested Loop  (cost=0.00..2.03 rows=1 width=16) (actual time=0.007..0.008
rows=1 loops=1)
   ->  Seq Scan on demo_key_1  (cost=0.00..1.01 rows=1 width=8) (actual
time=0.004..0.005 rows=1 loops=1)
 Filter: (key = 1)
   ->  Seq Scan on demo2_key_1  (cost=0.00..1.01 rows=1 width=8) (actual
time=0.001..0.001 rows=1 loops=1)
 Filter: (key = 1)
 Planning Time: 0.191 ms
 Execution Time: 0.025 ms
(7 rows)

However, when I try a very similar query that contains an aggregate
function, partitions of demo2 are not pruned from the query plan:

EXPLAIN ANALYZE
 SELECT * FROM demo
 JOIN LATERAL (
  SELECT sum(demo2.key) AS sum2
  FROM demo2
  WHERE demo2.key = demo.key
 ) d ON TRUE
 WHERE demo.key = 1;
  QUERY PLAN
---
 Nested Loop  (cost=2.03..3.07 rows=1 width=40) (actual time=0.018..0.018
rows=1 loops=1)
   ->  Seq Scan on demo_key_1  (cost=0.00..1.01 rows=1 width=8) (actual
time=0.005..0.005 rows=1 loops=1)
 Filter: (key = 1)
   ->  Aggregate  (cost=2.03..2.04 rows=1 width=32) (actual
time=0.011..0.011 rows=1 loops=1)
 ->  Append  (cost=0.00..2.03 rows=2 width=8) (actual
time=0.004..0.005 rows=1 loops=1)
   ->  Seq Scan on demo2_key_1  (cost=0.00..1.01 rows=1
width=8) (actual time=0.002..0.002 rows=1 loops=1)
 Filter: (key = demo_key_1.key)
   ->  Seq Scan on demo2_key_2  (cost=0.00..1.01 rows=1
width=8) (never executed)
 Filter: (key = demo_key_1.key)
 Planning Time: 0.174 ms
 Execution Time: 0.082 ms
(11 rows)

Of course, Seq Scan on demo2_key_2 was never executed, but why wasn't it
pruned from the query plan? More complex queries with hundreds of
partitions are affected badly by that.

The workaround is to add a redundant condition to the subquery:

EXPLAIN ANALYZE
 SELECT * FROM demo
 JOIN LATERAL (
  SELECT sum(demo2.key) AS sum2
  FROM demo2
  WHERE demo2.key = 1 AND demo2.key = demo.key
 ) d ON TRUE
 WHERE demo.key = 1;
  QUERY PLAN
---
 Nested Loop  (cost=1.01..2.05 rows=1 width=40) (actual time=0.011..0.011
rows=1 loops=1)
   ->  Seq Scan on demo_key_1  (cost=0.00..1.01 rows=1 width=8) (actual
time=0.004..0.004 rows=1 loops=1)
 Filter: (key = 1)
   ->  Aggregate  (cost=1.01..1.02 rows=1 width=32) (actual
time=0.006..0.006 rows=1 loops=1)
 ->  Result  (cost=0.00..1.01 rows=1 width=8) (actual
time=0.002..0.002 rows=1 loops=1)
   One-Time Filter: (demo_key_1.key = 1)
   ->  Seq Scan on demo2_key_1  (cost=0.00..1.01 rows=1
width=8) (actual time=0.001..0.002 rows=1 loops=1)
 Filter: (key = 1)
 Planning Time: 0.079 ms
 Execution Time: 0.031 ms
(10 rows)

-- 
M.B.


Inconsistent permission enforcement for schemas

2022-08-01 Thread Marcin Barczyński
Hello,

Why can a user access a table from a forbidden schema if the table is
explicitly specified in the definition of a view in an allowed schema? And
not if the table is accessed by a function used in the view.

Example:
```
DROP SCHEMA IF EXISTS forbidden_schema CASCADE;
DROP SCHEMA IF EXISTS allowed_schema CASCADE;
DROP FUNCTION IF EXISTS func CASCADE;
DROP USER IF EXISTS pinky;

CREATE SCHEMA forbidden_schema;
CREATE TABLE forbidden_schema.demo AS SELECT 1;

CREATE SCHEMA allowed_schema;

CREATE OR REPLACE FUNCTION func() RETURNS BIGINT AS
$$
DECLARE
 i BIGINT;
BEGIN
SELECT * INTO i FROM forbidden_schema.demo;
RETURN i;
END;
$$ LANGUAGE plpgsql;

CREATE VIEW allowed_schema.can_see AS SELECT * FROM forbidden_schema.demo;
CREATE VIEW allowed_schema.cannot_see AS SELECT * FROM func();

CREATE ROLE pinky LOGIN NOSUPERUSER PASSWORD 'pinky';
GRANT ALL PRIVILEGES ON SCHEMA allowed_schema TO pinky;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA allowed_schema TO pinky;
```

And now, as user pinky:
```
q=> SELECT * FROM allowed_schema.can_see;
 ?column?
--
1
(1 row)

q=> SELECT * FROM allowed_schema.cannot_see;
ERROR:  permission denied for schema forbidden_schema
LINE 1: SELECT *FROM forbidden_schema.demo
 ^
QUERY:  SELECT *FROM forbidden_schema.demo
CONTEXT:  PL/pgSQL function func() line 5 at SQL statement
```

Best regards,
Marcin Barczyński


How to change NUMERIC type under a domain without rewriting a table?

2024-05-08 Thread Marcin Barczyński
Hello!

In the following setup:

DROP DOMAIN IF EXISTS uint64 CASCADE;
DROP TABLE IF EXISTS demo;

CREATE DOMAIN uint64 AS NUMERIC(20, 0);
CREATE TABLE demo(key uint64);
INSERT INTO demo SELECT g FROM generate_series(1, 1000) g;

I would like to change the type of "key" column to NUMERIC(40, 0).
When I run,

ALTER TABLE demo ALTER COLUMN key TYPE NUMERIC(40, 0);

the whole table gets rewritten. Due to the table size it's not an option in
my case.
But, if there was no domain in the middle, and the column type was
NUMERIC(20, 0), the command above would complete in-place without rewriting
any rows.
I attempted to modify the definition uint64 in pg_type table:

UPDATE pg_type SET typtypmod = 2621444 WHERE typname = 'uint64';

It seems to work. Is it safe? Does it have any unintended consequences?
Or maybe there is another way to achieve this?

--
Marcin Barczyński


How to troubleshoot: ERROR: cache lookup failed for type?

2024-10-04 Thread Marcin Barczyński
Hello!

When reindexing a table, PostgreSQL returns the following errors for
two of its indexes:

Index foo_idx1 on foo failed with error: failed to find parent tuple
for heap-only tuple at (1162372,1) in table "foo"
Index foo_idx2 on foo failed with error: failed to find parent tuple
for heap-only tuple at (1162372,1) in table "foo"

When trying to show the tuple, there is another error message:

SET enable_indexscan = OFF;
SET enable_tidscan = OFF;
SELECT ctid, xmin, xmax, * FROM sf.dir_current_part_8 WHERE ctid =
'(1162372,1)';

ERROR:  cache lookup failed for type 1769235301

Searching for oid=1769235301 in pg_class, pg_type, pg_namespace and
pg_proc returned no results.

It appears to be data corruption.
It's PostgreSQL 13 and unfortunately, data_checksums are turned off.

I would appreciate any suggestions for troubleshooting this issue.

--
Marcin Barczyński