From: Andres Freund on
Hi Simon,

Your patch implements part of a feature I desire greatly - thanks!

Some comments:

On Thursday 15 July 2010 11:24:27 Simon Riggs wrote:
>> ! LOCKMODE
>> ! AlterTableGreatestLockLevel(List *cmds)
>> ! {
>> ! ListCell *lcmd;
>> ! LOCKMODE lockmode = ShareUpdateExclusiveLock; /* default for compiler */
Actually its not only for the compiler, its necessary for correctness
as you omit the default at least in the AT_AddConstraint case.

>> !
>> ! foreach(lcmd, cmds)
>> ! {
>> ! AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
>> ! LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
>> !
>> ! switch (cmd->subtype)
>> ! {
>> ! /*
>> ! * Need AccessExclusiveLock for these subcommands because they
>> ! * affect or potentially affect both read and write operations.
>> ! *
>> ! * New subcommand types should be added here by default.
>> ! */
>> ! case AT_AddColumn: /* may rewrite heap, in some cases and visible to SELECT */
>> ! case AT_DropColumn: /* change visible to SELECT */
>> ! case AT_AddColumnToView: /* CREATE VIEW */
>> ! case AT_AlterColumnType: /* must rewrite heap */
>> ! case AT_DropConstraint: /* as DROP INDEX */
>> ! case AT_AddOids:
>> ! case AT_DropOids: /* calls AT_DropColumn */
>> ! case AT_EnableAlwaysRule: /* as DROP INDEX */
>> ! case AT_EnableReplicaRule: /* as DROP INDEX */
>> ! case AT_EnableRule: /* as DROP INDEX */
>> ! case AT_DisableRule: /* as DROP INDEX */
>> ! case AT_ChangeOwner: /* change visible to SELECT */
>> ! case AT_SetTableSpace: /* must rewrite heap */
>> ! case AT_DropNotNull: /* may change some SQL plans */
>> ! case AT_SetNotNull:
>> ! cmd_lockmode = AccessExclusiveLock;
>> ! break;
>> !
>> ! /*
>> ! * These subcommands affect write operations only.
>> ! */
>> ! case AT_ColumnDefault:
>> ! case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
>> ! case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
>> ! case AT_EnableTrig:
>> ! case AT_EnableAlwaysTrig:
>> ! case AT_EnableReplicaTrig:
>> ! case AT_EnableTrigAll:
>> ! case AT_EnableTrigUser:
>> ! case AT_DisableTrig:
>> ! case AT_DisableTrigAll:
>> ! case AT_DisableTrigUser:
>> ! case AT_AddIndex: /* from ADD CONSTRAINT */
>> ! cmd_lockmode = ShareRowExclusiveLock;
>> ! break;
>> !
>> ! case AT_AddConstraint:
>> ! if (IsA(cmd->def, Constraint))
>> ! {
>> ! Constraint *con = (Constraint *) cmd->def;
>> !
>> ! switch (con->contype)
>> ! {
>> ! case CONSTR_EXCLUSION:
>> ! case CONSTR_PRIMARY:
>> ! case CONSTR_UNIQUE:
>> ! /*
>> ! * Cases essentially the same as CREATE INDEX. We
>> ! * could reduce the lock strength to ShareLock if we
>> ! * can work out how to allow concurrent catalog updates.
>> ! */
>> ! cmd_lockmode = ShareRowExclusiveLock;
>> ! break;
>> ! case CONSTR_FOREIGN:
>> ! /*
>> ! * We add triggers to both tables when we add a
>> ! * Foreign Key, so the lock level must be at least
>> ! * as strong as CREATE TRIGGER.
>> ! */
>> ! cmd_lockmode = ShareRowExclusiveLock;
>> ! break;
>> !
>> ! default:
>> ! cmd_lockmode = ShareRowExclusiveLock;
>> ! }
>> ! }
>> ! break;

You argue above that you cant change SET [NOT] NULL to be less
restrictive because it might change plans - isnt that true for some of the
above cases as well?

For example UNIQUE/PRIMARY might make join removal possible - which could
only be valid after "invalid" tuples where deleted earlier in that
transaction. Another case which it influences are grouping plans...

So I think the only case where its actually possible to lower the
level is CONSTR_EXCLUSION and _FOREIGN.
The latter might get impossible soon by planner improvements (Peter's
functional dependencies patch for example).


Sorry, thats it for now...

Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Andres Freund on
On Friday 16 July 2010 20:41:44 Andres Freund wrote:
> >> ! */
> >> ! case AT_AddColumn: /* may
> >> rewrite heap, in some cases and visible to SELECT */ !
> >> case AT_DropColumn: /* change
> >> visible to SELECT */ ! case
> >> AT_AddColumnToView: /* CREATE VIEW */ ! case
> >> AT_AlterColumnType: /* must rewrite heap */ !
> >> case AT_DropConstraint: /* as DROP INDEX */
> >> ! case AT_AddOids:
> >> ! case AT_DropOids: /* calls
> >> AT_DropColumn */ ! case
> >> AT_EnableAlwaysRule: /* as DROP INDEX */ !
> >> case AT_EnableReplicaRule: /* as DROP INDEX */
> >> ! case AT_EnableRule: /* as DROP
> >> INDEX */
Another remark:

Imho it would be usefull to keep that list in same order as in the enum -
currently its hard to make sure no case is missing.

Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Andres Freund on
On Friday 16 July 2010 21:12:33 Simon Riggs wrote:
> On Fri, 2010-07-16 at 20:41 +0200, Andres Freund wrote:
> > You argue above that you cant change SET [NOT] NULL to be less
> > restrictive because it might change plans - isnt that true for some of
> > the above cases as well?
> >
> > For example UNIQUE/PRIMARY might make join removal possible - which could
> > only be valid after "invalid" tuples where deleted earlier in that
> > transaction. Another case which it influences are grouping plans...
>
> This is only for adding a constraint, not removing it. Join removal
> would be possible after the ALTER finishes, but won't change plans
> already in progress. The idea is to minimise the impact, not maximise
> the benefit of the newly added constraint; I don't think we should block
> all queries just because a few might benefit.
Its not about benefit, its about correctness:

CREATE TABLE testsnap(t int);
INSERT INTO testsnap VALUES(1),(1);


T1:
test=# BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN
Time: 0.853 ms
test=# explain analyze SELECT t1.* FROM testsnap t1 LEFT JOIN testsnap t2 USING(t); QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Merge Left Join (cost=337.49..781.49 rows=28800 width=4) (actual time=0.090..0.118 rows=4 loops=1)
Merge Cond: (t1.t = t2.t)
-> Sort (cost=168.75..174.75 rows=2400 width=4) (actual time=0.049..0.051 rows=2 loops=1)
Sort Key: t1.t
Sort Method: quicksort Memory: 25kB
-> Seq Scan on testsnap t1 (cost=0.00..34.00 rows=2400 width=4) (actual time=0.018..0.023 rows=2 loops=1)
-> Sort (cost=168.75..174.75 rows=2400 width=4) (actual time=0.026..0.033 rows=3 loops=1)
Sort Key: t2.t
Sort Method: quicksort Memory: 25kB
-> Seq Scan on testsnap t2 (cost=0.00..34.00 rows=2400 width=4) (actual time=0.005..0.009 rows=2 loops=1)
Total runtime: 0.279 ms
(11 rows)


T2:
test=# DELETE FROM testsnap;
DELETE 2
Time: 1.184 ms
test=# ALTER TABLE testsnap ADD CONSTRAINT t unique(t);
NOTICE: 00000: ALTER TABLE / ADD UNIQUE will create implicit index "t" for table "testsnap"
LOCATION: DefineIndex, indexcmds.c:471
ALTER TABLE
Time: 45.639 ms

T1:
Time: 1.948 ms
test=# explain analyze SELECT t1.* FROM testsnap t1 LEFT JOIN testsnap t2 USING(t);
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Seq Scan on testsnap t1 (cost=0.00..1.02 rows=2 width=4) (actual time=0.013..0.016 rows=2 loops=1)
Total runtime: 0.081 ms
(2 rows)

Time: 2.004 ms
test=#

boom.



Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Andres Freund on
On Friday 16 July 2010 21:15:44 Simon Riggs wrote:
> On Fri, 2010-07-16 at 21:10 +0200, Andres Freund wrote:
> > On Friday 16 July 2010 20:41:44 Andres Freund wrote:
> > > >> ! */
> > > >> ! case AT_AddColumn: /* may
> > > >> rewrite heap, in some cases and visible to SELECT */ !
> > > >>
> > > >> case AT_DropColumn: /* change
> > > >>
> > > >> visible to SELECT */ ! case
> > > >> AT_AddColumnToView: /* CREATE VIEW */ !
> > > >> case AT_AlterColumnType: /* must rewrite heap */ !
> > > >>
> > > >> case AT_DropConstraint: /* as DROP INDEX
> > > >> */
> > > >>
> > > >> ! case AT_AddOids:
> > > >> ! case AT_DropOids: /*
> > > >> calls AT_DropColumn */ ! case
> > > >> AT_EnableAlwaysRule: /* as DROP INDEX */ !
> > > >>
> > > >> case AT_EnableReplicaRule: /* as DROP INDEX
> > > >> */
> > > >>
> > > >> ! case AT_EnableRule: /* as
> > > >> DROP INDEX */
> >
> > Another remark:
> >
> > Imho it would be usefull to keep that list in same order as in the enum -
> > currently its hard to make sure no case is missing.
>
> Not really; the default case is to reject, so any full test suite will
> pick that up.
>
> The cases are ordered by resulting lock type, which seemed the best way
> to check we didn't accidentally assign an incorrect lock type.
Well, I meant ordering it correctly inside the locktypes, sorry for the
inprecision.

Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

From: Andres Freund on
On Friday 16 July 2010 22:24:32 Simon Riggs wrote:
> On Fri, 2010-07-16 at 21:38 +0200, Andres Freund wrote:
> > boom
>
> Your test case would still occur in the case where the first query had
> not been executed against the same table. So the test case illustrates a
> failing of join removal, not of this patch.
Well, yes. Thats a well known (and documented) issue of pg's serialized
transactions - which you can protect against quite easily (see the trunctate
docs for example).
The difference is that I know of no sensible way you sensibly could protect
against such issues with the patch applied while its easy before(LOCK TABLE
.... IN SHARE MODE for all used tables).
I know of several sites which have *long* running serialized transactions open
for analysis and I know there have been other cases of it.

Sure its not that bad, but at least it needs to get documented imho. Likely
others should chime in here ;-)

What could the join removal path (and similar places) *possibly* do against
such a case? Without stopping to use SnapshotNow I dont see any way :-(


Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers(a)postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers