• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

修訂0fea6a73a629a56fbef2e3fbf8950d6aedd544e5 (tree)
時間2014-08-08 09:48:52
作者Kyotaro Horiguchi <horiguchi.kyotaro@lab....>
CommiterKyotaro Horiguchi

Log Message

Raise granularity of locks.

Almost all sql functions of pg_dbms_stats takes "share update
exclusive" locks on internal tables before they does their work. As
the result, it serializes dbms_stats operations even if they're on
different tables. This commit replaces such giant locks with the more
narrowed locks so that operations on different tables basically can
run in parallel. As the exchange for the concurrency, multiple (first
time) statistics lock operations on the same table at the same time
may result in failure of either of them. But it does no matter under
usual usage.

Change Summary

差異

--- a/expected/ut-9.1.out
+++ b/expected/ut-9.1.out
@@ -1245,7 +1245,7 @@ SELECT starelid::regclass, staattnum FROM dbms_stats.column_stats_backup
12451245 DELETE FROM dbms_stats.relation_stats_backup;
12461246 SELECT dbms_stats.backup(NULL, 's0.st0'::regclass, NULL);
12471247 ERROR: null value in column "id" violates not-null constraint
1248-CONTEXT: SQL function "backup" statement 1
1248+CONTEXT: SQL function "backup" statement 2
12491249 SELECT count(*) FROM dbms_stats.relation_stats_backup;
12501250 count
12511251 -------
@@ -1530,7 +1530,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15301530 AND dbms_stats.is_target_relkind(c.relkind)
15311531 ORDER BY c.oid
15321532 ) cn"
1533-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1533+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15341534 NOTICE: arguments are 9, s0.st0_idx, <NULL>
15351535 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15361536 FROM (SELECT c.oid
@@ -1541,7 +1541,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15411541 AND dbms_stats.is_target_relkind(c.relkind)
15421542 ORDER BY c.oid
15431543 ) cn"
1544-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1544+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15451545 NOTICE: arguments are 9, s0.st1, <NULL>
15461546 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15471547 FROM (SELECT c.oid
@@ -1552,7 +1552,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15521552 AND dbms_stats.is_target_relkind(c.relkind)
15531553 ORDER BY c.oid
15541554 ) cn"
1555-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1555+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15561556 NOTICE: arguments are 9, s0.st1_idx, <NULL>
15571557 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15581558 FROM (SELECT c.oid
@@ -1563,7 +1563,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15631563 AND dbms_stats.is_target_relkind(c.relkind)
15641564 ORDER BY c.oid
15651565 ) cn"
1566-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1566+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15671567 NOTICE: arguments are 9, s0.st2, <NULL>
15681568 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15691569 FROM (SELECT c.oid
@@ -1574,7 +1574,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15741574 AND dbms_stats.is_target_relkind(c.relkind)
15751575 ORDER BY c.oid
15761576 ) cn"
1577-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1577+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15781578 NOTICE: arguments are 9, s0.st2_idx, <NULL>
15791579 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15801580 FROM (SELECT c.oid
@@ -1585,7 +1585,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
15851585 AND dbms_stats.is_target_relkind(c.relkind)
15861586 ORDER BY c.oid
15871587 ) cn"
1588-PL/pgSQL function "backup_schema_stats" line 16 at PERFORM
1588+PL/pgSQL function "backup_schema_stats" line 17 at PERFORM
15891589 backup_schema_stats
15901590 ---------------------
15911591 9
@@ -1832,17 +1832,16 @@ SELECT dbms_stats.restore(2, 's0.st0', NULL);
18321832 (1 row)
18331833
18341834 SELECT * FROM internal_locks;
1835- relation | mode
1836------------------------------------+--------------------------
1837- dbms_stats.backup_history | AccessShareLock
1835+ relation | mode
1836+-----------------------------------+------------------
1837+ dbms_stats.backup_history | RowShareLock
18381838 dbms_stats.column_stats_backup | AccessShareLock
18391839 dbms_stats._column_stats_locked | RowExclusiveLock
1840- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
18411840 dbms_stats.relation_stats_backup | AccessShareLock
1841+ dbms_stats.relation_stats_backup | RowShareLock
18421842 dbms_stats._relation_stats_locked | RowExclusiveLock
18431843 dbms_stats._relation_stats_locked | RowShareLock
1844- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
1845-(8 rows)
1844+(7 rows)
18461845
18471846 COMMIT;
18481847 SELECT relid::regclass FROM dbms_stats.relation_stats_locked
@@ -2812,16 +2811,16 @@ SELECT dbms_stats.restore_stats(2);
28122811 (13 rows)
28132812
28142813 SELECT * FROM internal_locks;
2815- relation | mode
2816------------------------------------+--------------------------
2814+ relation | mode
2815+-----------------------------------+------------------
28172816 dbms_stats.backup_history | AccessShareLock
28182817 dbms_stats.column_stats_backup | AccessShareLock
28192818 dbms_stats._column_stats_locked | RowExclusiveLock
2820- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
28212819 dbms_stats.relation_stats_backup | AccessShareLock
2820+ dbms_stats.relation_stats_backup | RowShareLock
2821+ dbms_stats._relation_stats_locked | ExclusiveLock
28222822 dbms_stats._relation_stats_locked | RowExclusiveLock
28232823 dbms_stats._relation_stats_locked | RowShareLock
2824- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
28252824 (8 rows)
28262825
28272826 COMMIT;
@@ -3265,16 +3264,14 @@ SELECT * FROM columns_locked_v c;
32653264 (4 rows)
32663265
32673266 SELECT * FROM internal_locks;
3268- relation | mode
3269------------------------------------+--------------------------
3267+ relation | mode
3268+-----------------------------------+------------------
32703269 dbms_stats._column_stats_locked | AccessShareLock
32713270 dbms_stats._column_stats_locked | RowExclusiveLock
3272- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
32733271 dbms_stats._relation_stats_locked | AccessShareLock
32743272 dbms_stats._relation_stats_locked | RowExclusiveLock
32753273 dbms_stats._relation_stats_locked | RowShareLock
3276- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
3277-(7 rows)
3274+(5 rows)
32783275
32793276 COMMIT;
32803277 -- No.11-2-2
@@ -4651,14 +4648,13 @@ SELECT dbms_stats.unlock();
46514648 (14 rows)
46524649
46534650 SELECT * FROM internal_locks;
4654- relation | mode
4655------------------------------------+--------------------------
4651+ relation | mode
4652+-----------------------------------+------------------
46564653 dbms_stats._column_stats_locked | RowExclusiveLock
4657- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
46584654 dbms_stats._relation_stats_locked | AccessShareLock
46594655 dbms_stats._relation_stats_locked | RowExclusiveLock
4660- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
4661-(5 rows)
4656+ dbms_stats._relation_stats_locked | RowShareLock
4657+(4 rows)
46624658
46634659 COMMIT;
46644660 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -4930,14 +4926,13 @@ SELECT dbms_stats.unlock_database_stats();
49304926 (14 rows)
49314927
49324928 SELECT * FROM internal_locks;
4933- relation | mode
4934------------------------------------+--------------------------
4929+ relation | mode
4930+-----------------------------------+------------------
49354931 dbms_stats._column_stats_locked | RowExclusiveLock
4936- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
49374932 dbms_stats._relation_stats_locked | AccessShareLock
4933+ dbms_stats._relation_stats_locked | ExclusiveLock
49384934 dbms_stats._relation_stats_locked | RowExclusiveLock
4939- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
4940-(5 rows)
4935+(4 rows)
49414936
49424937 COMMIT;
49434938 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -5668,14 +5663,12 @@ SELECT dbms_stats.unlock_schema_stats('s0');
56685663 (6 rows)
56695664
56705665 SELECT * FROM internal_locks;
5671- relation | mode
5672------------------------------------+--------------------------
5666+ relation | mode
5667+-----------------------------------+------------------
56735668 dbms_stats._column_stats_locked | RowExclusiveLock
5674- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
5675- dbms_stats._relation_stats_locked | AccessShareLock
56765669 dbms_stats._relation_stats_locked | RowExclusiveLock
5677- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
5678-(5 rows)
5670+ dbms_stats._relation_stats_locked | RowShareLock
5671+(3 rows)
56795672
56805673 COMMIT;
56815674 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -6423,13 +6416,11 @@ SELECT dbms_stats.unlock_table_stats('s0.st0');
64236416 (1 row)
64246417
64256418 SELECT * FROM internal_locks;
6426- relation | mode
6427------------------------------------+--------------------------
6419+ relation | mode
6420+-----------------------------------+------------------
64286421 dbms_stats._column_stats_locked | RowExclusiveLock
6429- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
64306422 dbms_stats._relation_stats_locked | RowExclusiveLock
6431- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
6432-(4 rows)
6423+(2 rows)
64336424
64346425 COMMIT;
64356426 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -6886,7 +6877,7 @@ SELECT starelid::regclass, count(*) FROM dbms_stats._column_stats_locked
68866877
68876878 SELECT dbms_stats.unlock_table_stats('s00', 's0');
68886879 ERROR: schema "s00" does not exist
6889-CONTEXT: SQL function "unlock_table_stats" statement 3
6880+CONTEXT: SQL function "unlock_table_stats" statement 1
68906881 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
68916882 GROUP BY relid
68926883 ORDER BY relid;
@@ -7184,13 +7175,11 @@ SELECT dbms_stats.unlock_table_stats('s0', 'st0');
71847175 (1 row)
71857176
71867177 SELECT * FROM internal_locks;
7187- relation | mode
7188------------------------------------+--------------------------
7178+ relation | mode
7179+-----------------------------------+------------------
71897180 dbms_stats._column_stats_locked | RowExclusiveLock
7190- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
71917181 dbms_stats._relation_stats_locked | RowExclusiveLock
7192- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
7193-(4 rows)
7182+(2 rows)
71947183
71957184 COMMIT;
71967185 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -8183,12 +8172,11 @@ SELECT dbms_stats.unlock_column_stats('s0.st0', 'id');
81838172 (1 row)
81848173
81858174 SELECT * FROM internal_locks;
8186- relation | mode
8187------------------------------------+--------------------------
8175+ relation | mode
8176+-----------------------------------+------------------
81888177 dbms_stats._column_stats_locked | RowExclusiveLock
8189- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
8190- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
8191-(3 rows)
8178+ dbms_stats._relation_stats_locked | RowShareLock
8179+(2 rows)
81928180
81938181 COMMIT;
81948182 SELECT starelid, attname, stainherit FROM columns_locked_v c;
@@ -9067,12 +9055,11 @@ SELECT dbms_stats.unlock_column_stats('s0', 'st0', 'id');
90679055 (1 row)
90689056
90699057 SELECT * FROM internal_locks;
9070- relation | mode
9071------------------------------------+--------------------------
9058+ relation | mode
9059+-----------------------------------+------------------
90729060 dbms_stats._column_stats_locked | RowExclusiveLock
9073- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
9074- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
9075-(3 rows)
9061+ dbms_stats._relation_stats_locked | RowShareLock
9062+(2 rows)
90769063
90779064 COMMIT;
90789065 SELECT starelid, attname, stainherit FROM columns_locked_v c;
--- a/expected/ut-9.2.out
+++ b/expected/ut-9.2.out
@@ -1790,7 +1790,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
17901790 AND dbms_stats.is_target_relkind(c.relkind)
17911791 ORDER BY c.oid
17921792 ) cn"
1793-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1793+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
17941794 NOTICE: arguments are 9, s0.st0_idx, <NULL>
17951795 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
17961796 FROM (SELECT c.oid
@@ -1801,7 +1801,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18011801 AND dbms_stats.is_target_relkind(c.relkind)
18021802 ORDER BY c.oid
18031803 ) cn"
1804-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1804+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18051805 NOTICE: arguments are 9, s0.st1, <NULL>
18061806 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18071807 FROM (SELECT c.oid
@@ -1812,7 +1812,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18121812 AND dbms_stats.is_target_relkind(c.relkind)
18131813 ORDER BY c.oid
18141814 ) cn"
1815-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1815+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18161816 NOTICE: arguments are 9, s0.st1_idx, <NULL>
18171817 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18181818 FROM (SELECT c.oid
@@ -1823,7 +1823,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18231823 AND dbms_stats.is_target_relkind(c.relkind)
18241824 ORDER BY c.oid
18251825 ) cn"
1826-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1826+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18271827 NOTICE: arguments are 9, s0.st2, <NULL>
18281828 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18291829 FROM (SELECT c.oid
@@ -1834,7 +1834,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18341834 AND dbms_stats.is_target_relkind(c.relkind)
18351835 ORDER BY c.oid
18361836 ) cn"
1837-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1837+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18381838 NOTICE: arguments are 9, s0.st2_idx, <NULL>
18391839 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18401840 FROM (SELECT c.oid
@@ -1845,7 +1845,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18451845 AND dbms_stats.is_target_relkind(c.relkind)
18461846 ORDER BY c.oid
18471847 ) cn"
1848-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1848+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18491849 NOTICE: arguments are 9, s0.sft0, <NULL>
18501850 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18511851 FROM (SELECT c.oid
@@ -1856,7 +1856,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18561856 AND dbms_stats.is_target_relkind(c.relkind)
18571857 ORDER BY c.oid
18581858 ) cn"
1859-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1859+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18601860 backup_schema_stats
18611861 ---------------------
18621862 9
@@ -2105,17 +2105,16 @@ SELECT dbms_stats.restore(2, 's0.st0', NULL);
21052105 (1 row)
21062106
21072107 SELECT * FROM internal_locks;
2108- relation | mode
2109------------------------------------+--------------------------
2110- dbms_stats.backup_history | AccessShareLock
2108+ relation | mode
2109+-----------------------------------+------------------
2110+ dbms_stats.backup_history | RowShareLock
21112111 dbms_stats.column_stats_backup | AccessShareLock
21122112 dbms_stats._column_stats_locked | RowExclusiveLock
2113- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
21142113 dbms_stats.relation_stats_backup | AccessShareLock
2114+ dbms_stats.relation_stats_backup | RowShareLock
21152115 dbms_stats._relation_stats_locked | RowExclusiveLock
21162116 dbms_stats._relation_stats_locked | RowShareLock
2117- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
2118-(8 rows)
2117+(7 rows)
21192118
21202119 COMMIT;
21212120 SELECT relid::regclass FROM dbms_stats.relation_stats_locked
@@ -3114,16 +3113,16 @@ SELECT dbms_stats.restore_stats(2);
31143113 (14 rows)
31153114
31163115 SELECT * FROM internal_locks;
3117- relation | mode
3118------------------------------------+--------------------------
3116+ relation | mode
3117+-----------------------------------+------------------
31193118 dbms_stats.backup_history | AccessShareLock
31203119 dbms_stats.column_stats_backup | AccessShareLock
31213120 dbms_stats._column_stats_locked | RowExclusiveLock
3122- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
31233121 dbms_stats.relation_stats_backup | AccessShareLock
3122+ dbms_stats.relation_stats_backup | RowShareLock
3123+ dbms_stats._relation_stats_locked | ExclusiveLock
31243124 dbms_stats._relation_stats_locked | RowExclusiveLock
31253125 dbms_stats._relation_stats_locked | RowShareLock
3126- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
31273126 (8 rows)
31283127
31293128 COMMIT;
@@ -3586,16 +3585,14 @@ SELECT * FROM columns_locked_v c;
35863585 (4 rows)
35873586
35883587 SELECT * FROM internal_locks;
3589- relation | mode
3590------------------------------------+--------------------------
3588+ relation | mode
3589+-----------------------------------+------------------
35913590 dbms_stats._column_stats_locked | AccessShareLock
35923591 dbms_stats._column_stats_locked | RowExclusiveLock
3593- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
35943592 dbms_stats._relation_stats_locked | AccessShareLock
35953593 dbms_stats._relation_stats_locked | RowExclusiveLock
35963594 dbms_stats._relation_stats_locked | RowShareLock
3597- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
3598-(7 rows)
3595+(5 rows)
35993596
36003597 COMMIT;
36013598 -- No.11-2-2
@@ -5027,14 +5024,13 @@ SELECT dbms_stats.unlock();
50275024 (15 rows)
50285025
50295026 SELECT * FROM internal_locks;
5030- relation | mode
5031------------------------------------+--------------------------
5027+ relation | mode
5028+-----------------------------------+------------------
50325029 dbms_stats._column_stats_locked | RowExclusiveLock
5033- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
50345030 dbms_stats._relation_stats_locked | AccessShareLock
50355031 dbms_stats._relation_stats_locked | RowExclusiveLock
5036- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
5037-(5 rows)
5032+ dbms_stats._relation_stats_locked | RowShareLock
5033+(4 rows)
50385034
50395035 COMMIT;
50405036 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -5312,14 +5308,13 @@ SELECT dbms_stats.unlock_database_stats();
53125308 (15 rows)
53135309
53145310 SELECT * FROM internal_locks;
5315- relation | mode
5316------------------------------------+--------------------------
5311+ relation | mode
5312+-----------------------------------+------------------
53175313 dbms_stats._column_stats_locked | RowExclusiveLock
5318- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
53195314 dbms_stats._relation_stats_locked | AccessShareLock
5315+ dbms_stats._relation_stats_locked | ExclusiveLock
53205316 dbms_stats._relation_stats_locked | RowExclusiveLock
5321- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
5322-(5 rows)
5317+(4 rows)
53235318
53245319 COMMIT;
53255320 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -6080,14 +6075,12 @@ SELECT dbms_stats.unlock_schema_stats('s0');
60806075 (7 rows)
60816076
60826077 SELECT * FROM internal_locks;
6083- relation | mode
6084------------------------------------+--------------------------
6078+ relation | mode
6079+-----------------------------------+------------------
60856080 dbms_stats._column_stats_locked | RowExclusiveLock
6086- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
6087- dbms_stats._relation_stats_locked | AccessShareLock
60886081 dbms_stats._relation_stats_locked | RowExclusiveLock
6089- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
6090-(5 rows)
6082+ dbms_stats._relation_stats_locked | RowShareLock
6083+(3 rows)
60916084
60926085 COMMIT;
60936086 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -6866,13 +6859,11 @@ SELECT dbms_stats.unlock_table_stats('s0.st0');
68666859 (1 row)
68676860
68686861 SELECT * FROM internal_locks;
6869- relation | mode
6870------------------------------------+--------------------------
6862+ relation | mode
6863+-----------------------------------+------------------
68716864 dbms_stats._column_stats_locked | RowExclusiveLock
6872- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
68736865 dbms_stats._relation_stats_locked | RowExclusiveLock
6874- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
6875-(4 rows)
6866+(2 rows)
68766867
68776868 COMMIT;
68786869 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -7347,7 +7338,7 @@ SELECT starelid::regclass, count(*) FROM dbms_stats._column_stats_locked
73477338
73487339 SELECT dbms_stats.unlock_table_stats('s00', 's0');
73497340 ERROR: schema "s00" does not exist
7350-CONTEXT: SQL function "unlock_table_stats" statement 3
7341+CONTEXT: SQL function "unlock_table_stats" statement 1
73517342 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
73527343 GROUP BY relid
73537344 ORDER BY relid;
@@ -7660,13 +7651,11 @@ SELECT dbms_stats.unlock_table_stats('s0', 'st0');
76607651 (1 row)
76617652
76627653 SELECT * FROM internal_locks;
7663- relation | mode
7664------------------------------------+--------------------------
7654+ relation | mode
7655+-----------------------------------+------------------
76657656 dbms_stats._column_stats_locked | RowExclusiveLock
7666- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
76677657 dbms_stats._relation_stats_locked | RowExclusiveLock
7668- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
7669-(4 rows)
7658+(2 rows)
76707659
76717660 COMMIT;
76727661 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -8702,12 +8691,11 @@ SELECT dbms_stats.unlock_column_stats('s0.st0', 'id');
87028691 (1 row)
87038692
87048693 SELECT * FROM internal_locks;
8705- relation | mode
8706------------------------------------+--------------------------
8694+ relation | mode
8695+-----------------------------------+------------------
87078696 dbms_stats._column_stats_locked | RowExclusiveLock
8708- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
8709- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
8710-(3 rows)
8697+ dbms_stats._relation_stats_locked | RowShareLock
8698+(2 rows)
87118699
87128700 COMMIT;
87138701 SELECT starelid, attname, stainherit FROM columns_locked_v c;
@@ -9624,12 +9612,11 @@ SELECT dbms_stats.unlock_column_stats('s0', 'st0', 'id');
96249612 (1 row)
96259613
96269614 SELECT * FROM internal_locks;
9627- relation | mode
9628------------------------------------+--------------------------
9615+ relation | mode
9616+-----------------------------------+------------------
96299617 dbms_stats._column_stats_locked | RowExclusiveLock
9630- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
9631- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
9632-(3 rows)
9618+ dbms_stats._relation_stats_locked | RowShareLock
9619+(2 rows)
96339620
96349621 COMMIT;
96359622 SELECT starelid, attname, stainherit FROM columns_locked_v c;
--- a/expected/ut-9.3.out
+++ b/expected/ut-9.3.out
@@ -1865,7 +1865,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18651865 AND dbms_stats.is_target_relkind(c.relkind)
18661866 ORDER BY c.oid
18671867 ) cn"
1868-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1868+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18691869 NOTICE: arguments are 9, s0.st0_idx, <NULL>
18701870 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18711871 FROM (SELECT c.oid
@@ -1876,7 +1876,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18761876 AND dbms_stats.is_target_relkind(c.relkind)
18771877 ORDER BY c.oid
18781878 ) cn"
1879-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1879+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18801880 NOTICE: arguments are 9, s0.st1, <NULL>
18811881 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18821882 FROM (SELECT c.oid
@@ -1887,7 +1887,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18871887 AND dbms_stats.is_target_relkind(c.relkind)
18881888 ORDER BY c.oid
18891889 ) cn"
1890-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1890+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
18911891 NOTICE: arguments are 9, s0.st1_idx, <NULL>
18921892 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18931893 FROM (SELECT c.oid
@@ -1898,7 +1898,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
18981898 AND dbms_stats.is_target_relkind(c.relkind)
18991899 ORDER BY c.oid
19001900 ) cn"
1901-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1901+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
19021902 NOTICE: arguments are 9, s0.st2, <NULL>
19031903 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19041904 FROM (SELECT c.oid
@@ -1909,7 +1909,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19091909 AND dbms_stats.is_target_relkind(c.relkind)
19101910 ORDER BY c.oid
19111911 ) cn"
1912-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1912+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
19131913 NOTICE: arguments are 9, s0.st2_idx, <NULL>
19141914 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19151915 FROM (SELECT c.oid
@@ -1920,7 +1920,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19201920 AND dbms_stats.is_target_relkind(c.relkind)
19211921 ORDER BY c.oid
19221922 ) cn"
1923-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1923+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
19241924 NOTICE: arguments are 9, s0.sft0, <NULL>
19251925 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19261926 FROM (SELECT c.oid
@@ -1931,7 +1931,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19311931 AND dbms_stats.is_target_relkind(c.relkind)
19321932 ORDER BY c.oid
19331933 ) cn"
1934-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1934+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
19351935 NOTICE: arguments are 9, s0.smv0, <NULL>
19361936 CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19371937 FROM (SELECT c.oid
@@ -1942,7 +1942,7 @@ CONTEXT: SQL statement "SELECT dbms_stats.backup(backup_id, cn.oid, NULL)
19421942 AND dbms_stats.is_target_relkind(c.relkind)
19431943 ORDER BY c.oid
19441944 ) cn"
1945-PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 16 at PERFORM
1945+PL/pgSQL function dbms_stats.backup_schema_stats(text,text) line 17 at PERFORM
19461946 backup_schema_stats
19471947 ---------------------
19481948 9
@@ -2195,17 +2195,16 @@ SELECT dbms_stats.restore(2, 's0.st0', NULL);
21952195 (1 row)
21962196
21972197 SELECT * FROM internal_locks;
2198- relation | mode
2199------------------------------------+--------------------------
2200- dbms_stats.backup_history | AccessShareLock
2198+ relation | mode
2199+-----------------------------------+------------------
2200+ dbms_stats.backup_history | RowShareLock
22012201 dbms_stats.column_stats_backup | AccessShareLock
22022202 dbms_stats._column_stats_locked | RowExclusiveLock
2203- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
22042203 dbms_stats.relation_stats_backup | AccessShareLock
2204+ dbms_stats.relation_stats_backup | RowShareLock
22052205 dbms_stats._relation_stats_locked | RowExclusiveLock
22062206 dbms_stats._relation_stats_locked | RowShareLock
2207- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
2208-(8 rows)
2207+(7 rows)
22092208
22102209 COMMIT;
22112210 SELECT relid::regclass FROM dbms_stats.relation_stats_locked
@@ -3243,16 +3242,16 @@ SELECT dbms_stats.restore_stats(2);
32433242 (15 rows)
32443243
32453244 SELECT * FROM internal_locks;
3246- relation | mode
3247------------------------------------+--------------------------
3245+ relation | mode
3246+-----------------------------------+------------------
32483247 dbms_stats.backup_history | AccessShareLock
32493248 dbms_stats.column_stats_backup | AccessShareLock
32503249 dbms_stats._column_stats_locked | RowExclusiveLock
3251- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
32523250 dbms_stats.relation_stats_backup | AccessShareLock
3251+ dbms_stats.relation_stats_backup | RowShareLock
3252+ dbms_stats._relation_stats_locked | ExclusiveLock
32533253 dbms_stats._relation_stats_locked | RowExclusiveLock
32543254 dbms_stats._relation_stats_locked | RowShareLock
3255- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
32563255 (8 rows)
32573256
32583257 COMMIT;
@@ -3741,16 +3740,14 @@ SELECT * FROM columns_locked_v c;
37413740 (4 rows)
37423741
37433742 SELECT * FROM internal_locks;
3744- relation | mode
3745------------------------------------+--------------------------
3743+ relation | mode
3744+-----------------------------------+------------------
37463745 dbms_stats._column_stats_locked | AccessShareLock
37473746 dbms_stats._column_stats_locked | RowExclusiveLock
3748- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
37493747 dbms_stats._relation_stats_locked | AccessShareLock
37503748 dbms_stats._relation_stats_locked | RowExclusiveLock
37513749 dbms_stats._relation_stats_locked | RowShareLock
3752- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
3753-(7 rows)
3750+(5 rows)
37543751
37553752 COMMIT;
37563753 -- No.11-2-2
@@ -5262,14 +5259,13 @@ SELECT dbms_stats.unlock();
52625259 (16 rows)
52635260
52645261 SELECT * FROM internal_locks;
5265- relation | mode
5266------------------------------------+--------------------------
5262+ relation | mode
5263+-----------------------------------+------------------
52675264 dbms_stats._column_stats_locked | RowExclusiveLock
5268- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
52695265 dbms_stats._relation_stats_locked | AccessShareLock
52705266 dbms_stats._relation_stats_locked | RowExclusiveLock
5271- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
5272-(5 rows)
5267+ dbms_stats._relation_stats_locked | RowShareLock
5268+(4 rows)
52735269
52745270 COMMIT;
52755271 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -5553,14 +5549,13 @@ SELECT dbms_stats.unlock_database_stats();
55535549 (16 rows)
55545550
55555551 SELECT * FROM internal_locks;
5556- relation | mode
5557------------------------------------+--------------------------
5552+ relation | mode
5553+-----------------------------------+------------------
55585554 dbms_stats._column_stats_locked | RowExclusiveLock
5559- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
55605555 dbms_stats._relation_stats_locked | AccessShareLock
5556+ dbms_stats._relation_stats_locked | ExclusiveLock
55615557 dbms_stats._relation_stats_locked | RowExclusiveLock
5562- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
5563-(5 rows)
5558+(4 rows)
55645559
55655560 COMMIT;
55665561 SELECT count(*) FROM dbms_stats._relation_stats_locked;
@@ -6351,14 +6346,12 @@ SELECT dbms_stats.unlock_schema_stats('s0');
63516346 (8 rows)
63526347
63536348 SELECT * FROM internal_locks;
6354- relation | mode
6355------------------------------------+--------------------------
6349+ relation | mode
6350+-----------------------------------+------------------
63566351 dbms_stats._column_stats_locked | RowExclusiveLock
6357- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
6358- dbms_stats._relation_stats_locked | AccessShareLock
63596352 dbms_stats._relation_stats_locked | RowExclusiveLock
6360- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
6361-(5 rows)
6353+ dbms_stats._relation_stats_locked | RowShareLock
6354+(3 rows)
63626355
63636356 COMMIT;
63646357 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -7168,13 +7161,11 @@ SELECT dbms_stats.unlock_table_stats('s0.st0');
71687161 (1 row)
71697162
71707163 SELECT * FROM internal_locks;
7171- relation | mode
7172------------------------------------+--------------------------
7164+ relation | mode
7165+-----------------------------------+------------------
71737166 dbms_stats._column_stats_locked | RowExclusiveLock
7174- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
71757167 dbms_stats._relation_stats_locked | RowExclusiveLock
7176- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
7177-(4 rows)
7168+(2 rows)
71787169
71797170 COMMIT;
71807171 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -7667,7 +7658,7 @@ SELECT starelid::regclass, count(*) FROM dbms_stats._column_stats_locked
76677658
76687659 SELECT dbms_stats.unlock_table_stats('s00', 's0');
76697660 ERROR: schema "s00" does not exist
7670-CONTEXT: SQL function "unlock_table_stats" statement 3
7661+CONTEXT: SQL function "unlock_table_stats" statement 1
76717662 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
76727663 GROUP BY relid
76737664 ORDER BY relid;
@@ -7995,13 +7986,11 @@ SELECT dbms_stats.unlock_table_stats('s0', 'st0');
79957986 (1 row)
79967987
79977988 SELECT * FROM internal_locks;
7998- relation | mode
7999------------------------------------+--------------------------
7989+ relation | mode
7990+-----------------------------------+------------------
80007991 dbms_stats._column_stats_locked | RowExclusiveLock
8001- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
80027992 dbms_stats._relation_stats_locked | RowExclusiveLock
8003- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
8004-(4 rows)
7993+(2 rows)
80057994
80067995 COMMIT;
80077996 SELECT relid::regclass FROM dbms_stats._relation_stats_locked
@@ -9110,12 +9099,11 @@ SELECT dbms_stats.unlock_column_stats('s0.st0', 'id');
91109099 (1 row)
91119100
91129101 SELECT * FROM internal_locks;
9113- relation | mode
9114------------------------------------+--------------------------
9102+ relation | mode
9103+-----------------------------------+------------------
91159104 dbms_stats._column_stats_locked | RowExclusiveLock
9116- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
9117- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
9118-(3 rows)
9105+ dbms_stats._relation_stats_locked | RowShareLock
9106+(2 rows)
91199107
91209108 COMMIT;
91219109 SELECT starelid, attname, stainherit FROM columns_locked_v c;
@@ -10098,12 +10086,11 @@ SELECT dbms_stats.unlock_column_stats('s0', 'st0', 'id');
1009810086 (1 row)
1009910087
1010010088 SELECT * FROM internal_locks;
10101- relation | mode
10102------------------------------------+--------------------------
10089+ relation | mode
10090+-----------------------------------+------------------
1010310091 dbms_stats._column_stats_locked | RowExclusiveLock
10104- dbms_stats._column_stats_locked | ShareUpdateExclusiveLock
10105- dbms_stats._relation_stats_locked | ShareUpdateExclusiveLock
10106-(3 rows)
10092+ dbms_stats._relation_stats_locked | RowShareLock
10093+(2 rows)
1010710094
1010810095 COMMIT;
1010910096 SELECT starelid, attname, stainherit FROM columns_locked_v c;
--- a/expected/ut-common.out
+++ b/expected/ut-common.out
@@ -2077,16 +2077,14 @@ SELECT relation::regclass, mode
20772077 OR relation::regclass::text LIKE 'dbms_stats.backup_history'
20782078 OR relation::regclass::text LIKE 'dbms_stats.%\_backup'
20792079 ORDER BY relation::regclass::text, mode;
2080- relation | mode
2081-----------------------------------+--------------------------
2080+ relation | mode
2081+----------------------------------+------------------
20822082 dbms_stats.backup_history | AccessShareLock
20832083 dbms_stats.backup_history | RowExclusiveLock
2084- dbms_stats.backup_history | ShareUpdateExclusiveLock
2084+ dbms_stats.backup_history | RowShareLock
20852085 dbms_stats.column_stats_backup | RowExclusiveLock
2086- dbms_stats.column_stats_backup | ShareUpdateExclusiveLock
20872086 dbms_stats.relation_stats_backup | RowExclusiveLock
2088- dbms_stats.relation_stats_backup | ShareUpdateExclusiveLock
2089-(7 rows)
2087+(5 rows)
20902088
20912089 COMMIT;
20922090 SELECT * FROM dbms_stats.backup_history;
--- a/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.1.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.1.sql
@@ -4,6 +4,43 @@
44 \echo Use "ALTER EXTENSION pg_dbms_stats UPDATE TO '1.3.3'" to load this file. \quit
55
66 CREATE OR REPLACE FUNCTION dbms_stats.backup(
7+ backup_id int8,
8+ relid regclass,
9+ attnum int2
10+) RETURNS int8 AS
11+$$
12+/* Lock the backup id */
13+SELECT * from dbms_stats.backup_history
14+ WHERE id = $1 FOR UPDATE;
15+
16+INSERT INTO dbms_stats.relation_stats_backup
17+ SELECT $1, v.relid, v.relname, v.relpages, v.reltuples,
18+ v.curpages, v.last_analyze, v.last_autoanalyze
19+ FROM pg_catalog.pg_class c,
20+ dbms_stats.relation_stats_effective v
21+ WHERE c.oid = v.relid
22+ AND dbms_stats.is_target_relkind(relkind)
23+ AND NOT dbms_stats.is_system_catalog(v.relid)
24+ AND (v.relid = $2 OR $2 IS NULL);
25+
26+INSERT INTO dbms_stats.column_stats_backup
27+ SELECT $1, atttypid, s.*
28+ FROM pg_catalog.pg_class c,
29+ dbms_stats.column_stats_effective s,
30+ pg_catalog.pg_attribute a
31+ WHERE c.oid = starelid
32+ AND starelid = attrelid
33+ AND staattnum = attnum
34+ AND dbms_stats.is_target_relkind(relkind)
35+ AND NOT dbms_stats.is_system_catalog(c.oid)
36+ AND ($2 IS NULL OR starelid = $2)
37+ AND ($3 IS NULL OR staattnum = $3);
38+
39+SELECT $1;
40+$$
41+LANGUAGE sql;
42+
43+CREATE OR REPLACE FUNCTION dbms_stats.backup(
744 relid regclass DEFAULT NULL,
845 attname text DEFAULT NULL,
946 comment text DEFAULT NULL
@@ -20,7 +57,7 @@ BEGIN
2057 END IF;
2158 IF $1 IS NOT NULL THEN
2259 SELECT relkind INTO backup_relkind
23- FROM pg_catalog.pg_class WHERE oid = $1;
60+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
2461 IF NOT FOUND THEN
2562 RAISE EXCEPTION 'relation "%" not found', $1;
2663 END IF;
@@ -34,7 +71,7 @@ BEGIN
3471 END IF;
3572 IF $2 IS NOT NULL THEN
3673 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
37- WHERE a.attrelid = $1 AND a.attname = $2;
74+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
3875 IF set_attnum IS NULL THEN
3976 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
4077 END IF;
@@ -65,7 +102,8 @@ $$
65102 DECLARE
66103 backup_id int8;
67104 BEGIN
68- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
105+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
106+ THEN
69107 RAISE EXCEPTION 'schema "%" not found', $1;
70108 END IF;
71109 IF dbms_stats.is_system_schema($1) THEN
@@ -112,15 +150,18 @@ BEGIN
112150 IF $2 IS NULL AND $3 IS NOT NULL THEN
113151 RAISE EXCEPTION 'relation required';
114152 END IF;
115- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
153+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
154+ WHERE id <= $1 FOR SHARE) THEN
116155 RAISE EXCEPTION 'backup id % not found', $1;
117156 END IF;
118157 IF $2 IS NOT NULL THEN
119- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
158+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
159+ WHERE oid = $2 FOR SHARE) THEN
120160 RAISE EXCEPTION 'relation "%" not found', $2;
121161 END IF;
162+ -- Grabbing all backups for the relation which is not used in restore.
122163 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
123- WHERE b.id <= $1 AND b.relid = $2) THEN
164+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
124165 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
125166 END IF;
126167 IF $3 IS NOT NULL THEN
@@ -133,21 +174,25 @@ BEGIN
133174 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
134175 END IF;
135176 END IF;
177+ PERFORM * FROM dbms_stats._relation_stats_locked r
178+ WHERE r.relid = $2 FOR UPDATE;
179+ ELSE
180+ /* Lock the whole relation stats if relation is not specified.*/
181+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
136182 END IF;
137183
138- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
139- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
140-
141184 FOR restore_id, restore_relid IN
142- SELECT max(b.id), c.oid
143- FROM pg_class c, dbms_stats.relation_stats_backup b
144- WHERE (c.oid = $2 OR $2 IS NULL)
145- AND c.oid = b.relid
146- AND dbms_stats.is_target_relkind(c.relkind)
147- AND NOT dbms_stats.is_system_catalog(c.oid)
148- AND b.id <= $1
149- GROUP BY c.oid
150- ORDER BY c.oid::regclass::text
185+ SELECT max(id), coid FROM
186+ (SELECT b.id as id, c.oid as coid
187+ FROM pg_class c, dbms_stats.relation_stats_backup b
188+ WHERE (c.oid = $2 OR $2 IS NULL)
189+ AND c.oid = b.relid
190+ AND dbms_stats.is_target_relkind(c.relkind)
191+ AND NOT dbms_stats.is_system_catalog(c.oid)
192+ AND b.id <= $1
193+ FOR SHARE) t
194+ GROUP BY coid
195+ ORDER BY coid::regclass::text
151196 LOOP
152197 UPDATE dbms_stats._relation_stats_locked r
153198 SET relid = b.relid,
@@ -222,10 +267,31 @@ BEGIN
222267 AND staattnum = restore_attnum;
223268 END IF;
224269 END LOOP;
270+EXCEPTION
271+ WHEN unique_violation THEN
272+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
225273 END;
226274 $$
227275 LANGUAGE plpgsql;
228276
277+CREATE OR REPLACE FUNCTION dbms_stats.restore_database_stats(
278+ as_of_timestamp timestamp with time zone
279+) RETURNS SETOF regclass AS
280+$$
281+SELECT dbms_stats.restore(m.id, m.relid)
282+ FROM (SELECT max(id) AS id, relid
283+ FROM (SELECT r.id, r.relid
284+ FROM pg_class c, dbms_stats.relation_stats_backup r,
285+ dbms_stats.backup_history b
286+ WHERE c.oid = r.relid
287+ AND r.id = b.id
288+ AND b.time <= $1
289+ FOR SHARE) t1
290+ GROUP BY t1.relid
291+ ORDER BY t1.relid) m;
292+$$
293+LANGUAGE sql STRICT;
294+
229295 CREATE OR REPLACE FUNCTION dbms_stats.restore_schema_stats(
230296 schemaname text,
231297 as_of_timestamp timestamp with time zone
@@ -241,18 +307,19 @@ BEGIN
241307
242308 RETURN QUERY
243309 SELECT dbms_stats.restore(m.id, m.relid)
244- FROM (SELECT max(r.id) AS id, r.relid
245- FROM pg_class c, pg_namespace n,
246- dbms_stats.relation_stats_backup r,
247- dbms_stats.backup_history b
248- WHERE c.oid = r.relid
249- AND c.relnamespace = n.oid
250- AND n.nspname = $1
251- AND r.id = b.id
252- AND b.time <= $2
253- GROUP BY r.relid
254- ORDER BY r.relid
255- ) m;
310+ FROM (SELECT max(id) AS id, relid
311+ FROM (SELECT r.id, r.relid
312+ FROM pg_class c, pg_namespace n,
313+ dbms_stats.relation_stats_backup r,
314+ dbms_stats.backup_history b
315+ WHERE c.oid = r.relid
316+ AND c.relnamespace = n.oid
317+ AND n.nspname = $1
318+ AND r.id = b.id
319+ AND b.time <= $2
320+ FOR SHARE) t1
321+ GROUP BY t1.relid
322+ ORDER BY t1.relid) m;
256323 END;
257324 $$
258325 LANGUAGE plpgsql STRICT;
@@ -272,8 +339,12 @@ BEGIN
272339 RAISE EXCEPTION 'backup id % not found', $1;
273340 END IF;
274341
275- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
276- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
342+ /* Lock the backup */
343+ PERFORM * from dbms_stats.relation_stats_backup b
344+ WHERE id = $1 FOR SHARE;
345+
346+ /* Locking only _relation_stats_locked is sufficient */
347+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
277348
278349 FOR restore_relid IN
279350 SELECT b.relid
@@ -382,15 +453,12 @@ BEGIN
382453 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
383454 END IF;
384455
385- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
386- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
387-
388456 /*
389457 * If we don't have per-table statistics, create new one which has NULL for
390- * every statistic column_stats_effective.
458+ * every statistic value for column_stats_effective.
391459 */
392460 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
393- WHERE ru.relid = $1) THEN
461+ WHERE ru.relid = $1 FOR SHARE) THEN
394462 INSERT INTO dbms_stats._relation_stats_locked
395463 SELECT $1, dbms_stats.relname(nspname, relname),
396464 NULL, NULL, NULL, NULL, NULL
@@ -463,12 +531,15 @@ BEGIN
463531 END IF;
464532 END LOOP;
465533
466- /* If we don't have statistic at all, raise error. */
534+ /* If we don't have statistics at all, raise error. */
467535 IF NOT FOUND THEN
468536 RAISE EXCEPTION 'no statistics available for column "%" of relation "%"', $2, $1::regclass;
469537 END IF;
470538
471539 RETURN $1;
540+EXCEPTION
541+ WHEN unique_violation THEN
542+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
472543 END;
473544 $$
474545 LANGUAGE plpgsql;
@@ -495,9 +566,6 @@ BEGIN
495566 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
496567 END IF;
497568
498- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
499- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
500-
501569 UPDATE dbms_stats._relation_stats_locked r
502570 SET relname = dbms_stats.relname(nspname, c.relname),
503571 relpages = v.relpages,
@@ -596,6 +664,9 @@ BEGIN
596664 END LOOP;
597665
598666 RETURN $1;
667+EXCEPTION
668+ WHEN unique_violation THEN
669+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
599670 END;
600671 $$
601672 LANGUAGE plpgsql;
@@ -637,15 +708,19 @@ BEGIN
637708 IF $1 IS NULL AND $2 IS NOT NULL THEN
638709 RAISE EXCEPTION 'relation required';
639710 END IF;
711+
712+ /*
713+ * Lock the target relation to prevent conflicting with stats lock/restore
714+ */
715+ PERFORM * FROM dbms_stats._relation_stats_locked ru
716+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
717+
640718 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
641719 WHERE a.attrelid = $1 AND a.attname = $2;
642720 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
643721 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
644722 END IF;
645723
646- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
647- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
648-
649724 DELETE FROM dbms_stats._column_stats_locked
650725 WHERE (starelid = $1 OR $1 IS NULL)
651726 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -668,6 +743,27 @@ END;
668743 $$
669744 LANGUAGE plpgsql;
670745
746+CREATE OR REPLACE FUNCTION dbms_stats.unlock_database_stats()
747+ RETURNS SETOF regclass AS
748+$$
749+DECLARE
750+ unlock_id int8;
751+BEGIN
752+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
753+
754+ FOR unlock_id IN
755+ SELECT relid
756+ FROM dbms_stats._relation_stats_locked
757+ ORDER BY relid
758+ LOOP
759+ DELETE FROM dbms_stats._relation_stats_locked
760+ WHERE relid = unlock_id;
761+ RETURN NEXT unlock_id;
762+ END LOOP;
763+END;
764+$$
765+LANGUAGE plpgsql STRICT;
766+
671767 CREATE OR REPLACE FUNCTION dbms_stats.unlock_schema_stats(
672768 schemaname text
673769 ) RETURNS SETOF regclass AS
@@ -682,16 +778,14 @@ BEGIN
682778 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
683779 END IF;
684780
685- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
686- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
687-
688781 FOR unlock_id IN
689- SELECT relid
690- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
782+ SELECT r.relid
783+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
691784 WHERE relid = c.oid
692785 AND c.relnamespace = n.oid
693786 AND n.nspname = $1
694787 ORDER BY relid
788+ FOR UPDATE
695789 LOOP
696790 DELETE FROM dbms_stats._relation_stats_locked
697791 WHERE relid = unlock_id;
@@ -701,6 +795,26 @@ END;
701795 $$
702796 LANGUAGE plpgsql STRICT;
703797
798+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
799+ RETURNS SETOF regclass AS
800+$$
801+DELETE FROM dbms_stats._relation_stats_locked
802+ WHERE relid = $1
803+ RETURNING relid::regclass
804+$$
805+LANGUAGE sql STRICT;
806+
807+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(
808+ schemaname text,
809+ tablename text
810+) RETURNS SETOF regclass AS
811+$$
812+DELETE FROM dbms_stats._relation_stats_locked
813+ WHERE relid = dbms_stats.relname($1, $2)::regclass
814+ RETURNING relid::regclass
815+$$
816+LANGUAGE sql STRICT;
817+
704818 CREATE OR REPLACE FUNCTION dbms_stats.unlock_column_stats(
705819 relid regclass,
706820 attname text
@@ -715,12 +829,14 @@ BEGIN
715829 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
716830 END IF;
717831
718- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
719- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
832+ /* Lock the locked table stats */
833+ PERFORM * from dbms_stats.relation_stats_locked r
834+ WHERE r.relid = $1 FOR SHARE;
835+
836+ DELETE FROM dbms_stats._column_stats_locked
837+ WHERE starelid = $1
838+ AND staattnum = set_attnum;
720839
721- DELETE FROM dbms_stats._column_stats_locked
722- WHERE starelid = $1
723- AND staattnum = set_attnum;
724840 RETURN QUERY
725841 SELECT $1;
726842 END;
@@ -743,12 +859,14 @@ BEGIN
743859 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
744860 END IF;
745861
746- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
747- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
862+ /* Lock the locked table stats */
863+ PERFORM * from dbms_stats.relation_stats_locked r
864+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
865+
866+ DELETE FROM dbms_stats._column_stats_locked
867+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
868+ AND staattnum = set_attnum;
748869
749- DELETE FROM dbms_stats._column_stats_locked
750- WHERE starelid = dbms_stats.relname($1, $2)::regclass
751- AND staattnum = set_attnum;
752870 RETURN QUERY
753871 SELECT dbms_stats.relname($1, $2)::regclass;
754872 END;
@@ -771,11 +889,8 @@ BEGIN
771889 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
772890 END IF;
773891
774- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
775- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
776- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
777-
778- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
892+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
893+ WHERE id = $1 FOR UPDATE) THEN
779894 RAISE EXCEPTION 'backup id % not found', $1;
780895 END IF;
781896 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -790,7 +905,7 @@ BEGIN
790905 FOR todelete IN
791906 SELECT * FROM dbms_stats.backup_history
792907 WHERE id <= $1
793- ORDER BY id
908+ ORDER BY id FOR UPDATE
794909 LOOP
795910 DELETE FROM dbms_stats.backup_history
796911 WHERE id = todelete.id;
@@ -799,3 +914,47 @@ BEGIN
799914 END;
800915 $$
801916 LANGUAGE plpgsql;
917+
918+CREATE OR REPLACE FUNCTION dbms_stats.clean_up_stats() RETURNS SETOF text AS
919+$$
920+DECLARE
921+ clean_relid Oid;
922+ clean_attnum int2;
923+ clean_inherit bool;
924+ clean_rel_col text;
925+BEGIN
926+ -- We don't have to check that table-level dummy statistics of the table
927+ -- exists here, because the foreign key constraints defined on column-level
928+ -- dummy static table ensures that.
929+ FOR clean_rel_col, clean_relid, clean_attnum, clean_inherit IN
930+ SELECT r.relname || ', ' || v.staattnum::text,
931+ v.starelid, v.staattnum, v.stainherit
932+ FROM dbms_stats._column_stats_locked v
933+ JOIN dbms_stats._relation_stats_locked r ON (v.starelid = r.relid)
934+ WHERE NOT EXISTS (
935+ SELECT NULL
936+ FROM pg_attribute a
937+ WHERE a.attrelid = v.starelid
938+ AND a.attnum = v.staattnum
939+ AND a.attisdropped = false
940+ FOR UPDATE
941+ )
942+ LOOP
943+ DELETE FROM dbms_stats._column_stats_locked
944+ WHERE starelid = clean_relid
945+ AND staattnum = clean_attnum
946+ AND stainherit = clean_inherit;
947+ RETURN NEXT clean_rel_col;
948+ END LOOP;
949+
950+ RETURN QUERY
951+ DELETE FROM dbms_stats._relation_stats_locked r
952+ WHERE NOT EXISTS (
953+ SELECT NULL
954+ FROM pg_class c
955+ WHERE c.oid = r.relid)
956+ RETURNING relname || ',';
957+ RETURN;
958+END
959+$$
960+LANGUAGE plpgsql;
--- a/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.2.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.2.sql
@@ -4,6 +4,43 @@
44 \echo Use "ALTER EXTENSION pg_dbms_stats UPDATE TO '1.3.3'" to load this file. \quit
55
66 CREATE OR REPLACE FUNCTION dbms_stats.backup(
7+ backup_id int8,
8+ relid regclass,
9+ attnum int2
10+) RETURNS int8 AS
11+$$
12+/* Lock the backup id */
13+SELECT * from dbms_stats.backup_history
14+ WHERE id = $1 FOR UPDATE;
15+
16+INSERT INTO dbms_stats.relation_stats_backup
17+ SELECT $1, v.relid, v.relname, v.relpages, v.reltuples, v.relallvisible,
18+ v.curpages, v.last_analyze, v.last_autoanalyze
19+ FROM pg_catalog.pg_class c,
20+ dbms_stats.relation_stats_effective v
21+ WHERE c.oid = v.relid
22+ AND dbms_stats.is_target_relkind(relkind)
23+ AND NOT dbms_stats.is_system_catalog(v.relid)
24+ AND (v.relid = $2 OR $2 IS NULL);
25+
26+INSERT INTO dbms_stats.column_stats_backup
27+ SELECT $1, atttypid, s.*
28+ FROM pg_catalog.pg_class c,
29+ dbms_stats.column_stats_effective s,
30+ pg_catalog.pg_attribute a
31+ WHERE c.oid = starelid
32+ AND starelid = attrelid
33+ AND staattnum = attnum
34+ AND dbms_stats.is_target_relkind(relkind)
35+ AND NOT dbms_stats.is_system_catalog(c.oid)
36+ AND ($2 IS NULL OR starelid = $2)
37+ AND ($3 IS NULL OR staattnum = $3);
38+
39+SELECT $1;
40+$$
41+LANGUAGE sql;
42+
43+CREATE OR REPLACE FUNCTION dbms_stats.backup(
744 relid regclass DEFAULT NULL,
845 attname text DEFAULT NULL,
946 comment text DEFAULT NULL
@@ -20,7 +57,7 @@ BEGIN
2057 END IF;
2158 IF $1 IS NOT NULL THEN
2259 SELECT relkind INTO backup_relkind
23- FROM pg_catalog.pg_class WHERE oid = $1;
60+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
2461 IF NOT FOUND THEN
2562 RAISE EXCEPTION 'relation "%" not found', $1;
2663 END IF;
@@ -34,7 +71,7 @@ BEGIN
3471 END IF;
3572 IF $2 IS NOT NULL THEN
3673 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
37- WHERE a.attrelid = $1 AND a.attname = $2;
74+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
3875 IF set_attnum IS NULL THEN
3976 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
4077 END IF;
@@ -65,7 +102,8 @@ $$
65102 DECLARE
66103 backup_id int8;
67104 BEGIN
68- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
105+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
106+ THEN
69107 RAISE EXCEPTION 'schema "%" not found', $1;
70108 END IF;
71109 IF dbms_stats.is_system_schema($1) THEN
@@ -112,15 +150,18 @@ BEGIN
112150 IF $2 IS NULL AND $3 IS NOT NULL THEN
113151 RAISE EXCEPTION 'relation required';
114152 END IF;
115- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
153+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
154+ WHERE id <= $1 FOR SHARE) THEN
116155 RAISE EXCEPTION 'backup id % not found', $1;
117156 END IF;
118157 IF $2 IS NOT NULL THEN
119- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
158+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
159+ WHERE oid = $2 FOR SHARE) THEN
120160 RAISE EXCEPTION 'relation "%" not found', $2;
121161 END IF;
162+ -- Grabbing all backups for the relation which is not used in restore.
122163 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
123- WHERE b.id <= $1 AND b.relid = $2) THEN
164+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
124165 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
125166 END IF;
126167 IF $3 IS NOT NULL THEN
@@ -133,21 +174,25 @@ BEGIN
133174 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
134175 END IF;
135176 END IF;
177+ PERFORM * FROM dbms_stats._relation_stats_locked r
178+ WHERE r.relid = $2 FOR UPDATE;
179+ ELSE
180+ /* Lock the whole relation stats if relation is not specified.*/
181+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
136182 END IF;
137183
138- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
139- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
140-
141184 FOR restore_id, restore_relid IN
142- SELECT max(b.id), c.oid
143- FROM pg_class c, dbms_stats.relation_stats_backup b
144- WHERE (c.oid = $2 OR $2 IS NULL)
145- AND c.oid = b.relid
146- AND dbms_stats.is_target_relkind(c.relkind)
147- AND NOT dbms_stats.is_system_catalog(c.oid)
148- AND b.id <= $1
149- GROUP BY c.oid
150- ORDER BY c.oid::regclass::text
185+ SELECT max(id), coid FROM
186+ (SELECT b.id as id, c.oid as coid
187+ FROM pg_class c, dbms_stats.relation_stats_backup b
188+ WHERE (c.oid = $2 OR $2 IS NULL)
189+ AND c.oid = b.relid
190+ AND dbms_stats.is_target_relkind(c.relkind)
191+ AND NOT dbms_stats.is_system_catalog(c.oid)
192+ AND b.id <= $1
193+ FOR SHARE) t
194+ GROUP BY coid
195+ ORDER BY coid::regclass::text
151196 LOOP
152197 UPDATE dbms_stats._relation_stats_locked r
153198 SET relid = b.relid,
@@ -224,10 +269,31 @@ BEGIN
224269 AND staattnum = restore_attnum;
225270 END IF;
226271 END LOOP;
272+EXCEPTION
273+ WHEN unique_violation THEN
274+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
227275 END;
228276 $$
229277 LANGUAGE plpgsql;
230278
279+CREATE OR REPLACE FUNCTION dbms_stats.restore_database_stats(
280+ as_of_timestamp timestamp with time zone
281+) RETURNS SETOF regclass AS
282+$$
283+SELECT dbms_stats.restore(m.id, m.relid)
284+ FROM (SELECT max(id) AS id, relid
285+ FROM (SELECT r.id, r.relid
286+ FROM pg_class c, dbms_stats.relation_stats_backup r,
287+ dbms_stats.backup_history b
288+ WHERE c.oid = r.relid
289+ AND r.id = b.id
290+ AND b.time <= $1
291+ FOR SHARE) t1
292+ GROUP BY t1.relid
293+ ORDER BY t1.relid) m;
294+$$
295+LANGUAGE sql STRICT;
296+
231297 CREATE OR REPLACE FUNCTION dbms_stats.restore_schema_stats(
232298 schemaname text,
233299 as_of_timestamp timestamp with time zone
@@ -243,18 +309,19 @@ BEGIN
243309
244310 RETURN QUERY
245311 SELECT dbms_stats.restore(m.id, m.relid)
246- FROM (SELECT max(r.id) AS id, r.relid
247- FROM pg_class c, pg_namespace n,
248- dbms_stats.relation_stats_backup r,
249- dbms_stats.backup_history b
250- WHERE c.oid = r.relid
251- AND c.relnamespace = n.oid
252- AND n.nspname = $1
253- AND r.id = b.id
254- AND b.time <= $2
255- GROUP BY r.relid
256- ORDER BY r.relid
257- ) m;
312+ FROM (SELECT max(id) AS id, relid
313+ FROM (SELECT r.id, r.relid
314+ FROM pg_class c, pg_namespace n,
315+ dbms_stats.relation_stats_backup r,
316+ dbms_stats.backup_history b
317+ WHERE c.oid = r.relid
318+ AND c.relnamespace = n.oid
319+ AND n.nspname = $1
320+ AND r.id = b.id
321+ AND b.time <= $2
322+ FOR SHARE) t1
323+ GROUP BY t1.relid
324+ ORDER BY t1.relid) m;
258325 END;
259326 $$
260327 LANGUAGE plpgsql STRICT;
@@ -274,8 +341,12 @@ BEGIN
274341 RAISE EXCEPTION 'backup id % not found', $1;
275342 END IF;
276343
277- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
278- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
344+ /* Lock the backup */
345+ PERFORM * from dbms_stats.relation_stats_backup b
346+ WHERE id = $1 FOR SHARE;
347+
348+ /* Locking only _relation_stats_locked is sufficient */
349+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
279350
280351 FOR restore_relid IN
281352 SELECT b.relid
@@ -386,15 +457,12 @@ BEGIN
386457 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
387458 END IF;
388459
389- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
390- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
391-
392460 /*
393461 * If we don't have per-table statistics, create new one which has NULL for
394- * every statistic column_stats_effective.
462+ * every statistic value for column_stats_effective.
395463 */
396464 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
397- WHERE ru.relid = $1) THEN
465+ WHERE ru.relid = $1 FOR SHARE) THEN
398466 INSERT INTO dbms_stats._relation_stats_locked
399467 SELECT $1, dbms_stats.relname(nspname, relname),
400468 NULL, NULL, NULL, NULL, NULL
@@ -475,12 +543,15 @@ BEGIN
475543 END IF;
476544 END LOOP;
477545
478- /* If we don't have statistic at all, raise error. */
546+ /* If we don't have statistics at all, raise error. */
479547 IF NOT FOUND THEN
480548 RAISE EXCEPTION 'no statistics available for column "%" of relation "%"', $2, $1::regclass;
481549 END IF;
482550
483551 RETURN $1;
552+EXCEPTION
553+ WHEN unique_violation THEN
554+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
484555 END;
485556 $$
486557 LANGUAGE plpgsql;
@@ -507,9 +578,6 @@ BEGIN
507578 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
508579 END IF;
509580
510- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
511- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
512-
513581 UPDATE dbms_stats._relation_stats_locked r
514582 SET relname = dbms_stats.relname(nspname, c.relname),
515583 relpages = v.relpages,
@@ -617,6 +685,9 @@ BEGIN
617685 END LOOP;
618686
619687 RETURN $1;
688+EXCEPTION
689+ WHEN unique_violation THEN
690+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
620691 END;
621692 $$
622693 LANGUAGE plpgsql;
@@ -658,15 +729,19 @@ BEGIN
658729 IF $1 IS NULL AND $2 IS NOT NULL THEN
659730 RAISE EXCEPTION 'relation required';
660731 END IF;
732+
733+ /*
734+ * Lock the target relation to prevent conflicting with stats lock/restore
735+ */
736+ PERFORM * FROM dbms_stats._relation_stats_locked ru
737+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
738+
661739 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
662740 WHERE a.attrelid = $1 AND a.attname = $2;
663741 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
664742 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
665743 END IF;
666744
667- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
668- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
669-
670745 DELETE FROM dbms_stats._column_stats_locked
671746 WHERE (starelid = $1 OR $1 IS NULL)
672747 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -689,6 +764,27 @@ END;
689764 $$
690765 LANGUAGE plpgsql;
691766
767+CREATE OR REPLACE FUNCTION dbms_stats.unlock_database_stats()
768+ RETURNS SETOF regclass AS
769+$$
770+DECLARE
771+ unlock_id int8;
772+BEGIN
773+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
774+
775+ FOR unlock_id IN
776+ SELECT relid
777+ FROM dbms_stats._relation_stats_locked
778+ ORDER BY relid
779+ LOOP
780+ DELETE FROM dbms_stats._relation_stats_locked
781+ WHERE relid = unlock_id;
782+ RETURN NEXT unlock_id;
783+ END LOOP;
784+END;
785+$$
786+LANGUAGE plpgsql STRICT;
787+
692788 CREATE OR REPLACE FUNCTION dbms_stats.unlock_schema_stats(
693789 schemaname text
694790 ) RETURNS SETOF regclass AS
@@ -703,16 +799,14 @@ BEGIN
703799 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
704800 END IF;
705801
706- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
707- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
708-
709802 FOR unlock_id IN
710- SELECT relid
711- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
803+ SELECT r.relid
804+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
712805 WHERE relid = c.oid
713806 AND c.relnamespace = n.oid
714807 AND n.nspname = $1
715808 ORDER BY relid
809+ FOR UPDATE
716810 LOOP
717811 DELETE FROM dbms_stats._relation_stats_locked
718812 WHERE relid = unlock_id;
@@ -722,6 +816,26 @@ END;
722816 $$
723817 LANGUAGE plpgsql STRICT;
724818
819+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
820+ RETURNS SETOF regclass AS
821+$$
822+DELETE FROM dbms_stats._relation_stats_locked
823+ WHERE relid = $1
824+ RETURNING relid::regclass
825+$$
826+LANGUAGE sql STRICT;
827+
828+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(
829+ schemaname text,
830+ tablename text
831+) RETURNS SETOF regclass AS
832+$$
833+DELETE FROM dbms_stats._relation_stats_locked
834+ WHERE relid = dbms_stats.relname($1, $2)::regclass
835+ RETURNING relid::regclass
836+$$
837+LANGUAGE sql STRICT;
838+
725839 CREATE OR REPLACE FUNCTION dbms_stats.unlock_column_stats(
726840 relid regclass,
727841 attname text
@@ -736,12 +850,14 @@ BEGIN
736850 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
737851 END IF;
738852
739- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
740- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
853+ /* Lock the locked table stats */
854+ PERFORM * from dbms_stats.relation_stats_locked r
855+ WHERE r.relid = $1 FOR SHARE;
856+
857+ DELETE FROM dbms_stats._column_stats_locked
858+ WHERE starelid = $1
859+ AND staattnum = set_attnum;
741860
742- DELETE FROM dbms_stats._column_stats_locked
743- WHERE starelid = $1
744- AND staattnum = set_attnum;
745861 RETURN QUERY
746862 SELECT $1;
747863 END;
@@ -764,12 +880,14 @@ BEGIN
764880 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
765881 END IF;
766882
767- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
768- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
883+ /* Lock the locked table stats */
884+ PERFORM * from dbms_stats.relation_stats_locked r
885+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
886+
887+ DELETE FROM dbms_stats._column_stats_locked
888+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
889+ AND staattnum = set_attnum;
769890
770- DELETE FROM dbms_stats._column_stats_locked
771- WHERE starelid = dbms_stats.relname($1, $2)::regclass
772- AND staattnum = set_attnum;
773891 RETURN QUERY
774892 SELECT dbms_stats.relname($1, $2)::regclass;
775893 END;
@@ -792,11 +910,8 @@ BEGIN
792910 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
793911 END IF;
794912
795- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
796- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
797- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
798-
799- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
913+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
914+ WHERE id = $1 FOR UPDATE) THEN
800915 RAISE EXCEPTION 'backup id % not found', $1;
801916 END IF;
802917 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -811,7 +926,7 @@ BEGIN
811926 FOR todelete IN
812927 SELECT * FROM dbms_stats.backup_history
813928 WHERE id <= $1
814- ORDER BY id
929+ ORDER BY id FOR UPDATE
815930 LOOP
816931 DELETE FROM dbms_stats.backup_history
817932 WHERE id = todelete.id;
@@ -820,3 +935,47 @@ BEGIN
820935 END;
821936 $$
822937 LANGUAGE plpgsql;
938+
939+CREATE OR REPLACE FUNCTION dbms_stats.clean_up_stats() RETURNS SETOF text AS
940+$$
941+DECLARE
942+ clean_relid Oid;
943+ clean_attnum int2;
944+ clean_inherit bool;
945+ clean_rel_col text;
946+BEGIN
947+ -- We don't have to check that table-level dummy statistics of the table
948+ -- exists here, because the foreign key constraints defined on column-level
949+ -- dummy static table ensures that.
950+ FOR clean_rel_col, clean_relid, clean_attnum, clean_inherit IN
951+ SELECT r.relname || ', ' || v.staattnum::text,
952+ v.starelid, v.staattnum, v.stainherit
953+ FROM dbms_stats._column_stats_locked v
954+ JOIN dbms_stats._relation_stats_locked r ON (v.starelid = r.relid)
955+ WHERE NOT EXISTS (
956+ SELECT NULL
957+ FROM pg_attribute a
958+ WHERE a.attrelid = v.starelid
959+ AND a.attnum = v.staattnum
960+ AND a.attisdropped = false
961+ FOR UPDATE
962+ )
963+ LOOP
964+ DELETE FROM dbms_stats._column_stats_locked
965+ WHERE starelid = clean_relid
966+ AND staattnum = clean_attnum
967+ AND stainherit = clean_inherit;
968+ RETURN NEXT clean_rel_col;
969+ END LOOP;
970+
971+ RETURN QUERY
972+ DELETE FROM dbms_stats._relation_stats_locked r
973+ WHERE NOT EXISTS (
974+ SELECT NULL
975+ FROM pg_class c
976+ WHERE c.oid = r.relid)
977+ RETURNING relname || ',';
978+ RETURN;
979+END
980+$$
981+LANGUAGE plpgsql;
--- a/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.3.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.2--1.3.3-9.3.sql
@@ -4,6 +4,43 @@
44 \echo Use "ALTER EXTENSION pg_dbms_stats UPDATE TO '1.3.3'" to load this file. \quit
55
66 CREATE OR REPLACE FUNCTION dbms_stats.backup(
7+ backup_id int8,
8+ relid regclass,
9+ attnum int2
10+) RETURNS int8 AS
11+$$
12+/* Lock the backup id */
13+SELECT * from dbms_stats.backup_history
14+ WHERE id = $1 FOR UPDATE;
15+
16+INSERT INTO dbms_stats.relation_stats_backup
17+ SELECT $1, v.relid, v.relname, v.relpages, v.reltuples, v.relallvisible,
18+ v.curpages, v.last_analyze, v.last_autoanalyze
19+ FROM pg_catalog.pg_class c,
20+ dbms_stats.relation_stats_effective v
21+ WHERE c.oid = v.relid
22+ AND dbms_stats.is_target_relkind(relkind)
23+ AND NOT dbms_stats.is_system_catalog(v.relid)
24+ AND (v.relid = $2 OR $2 IS NULL);
25+
26+INSERT INTO dbms_stats.column_stats_backup
27+ SELECT $1, atttypid, s.*
28+ FROM pg_catalog.pg_class c,
29+ dbms_stats.column_stats_effective s,
30+ pg_catalog.pg_attribute a
31+ WHERE c.oid = starelid
32+ AND starelid = attrelid
33+ AND staattnum = attnum
34+ AND dbms_stats.is_target_relkind(relkind)
35+ AND NOT dbms_stats.is_system_catalog(c.oid)
36+ AND ($2 IS NULL OR starelid = $2)
37+ AND ($3 IS NULL OR staattnum = $3);
38+
39+SELECT $1;
40+$$
41+LANGUAGE sql;
42+
43+CREATE OR REPLACE FUNCTION dbms_stats.backup(
744 relid regclass DEFAULT NULL,
845 attname text DEFAULT NULL,
946 comment text DEFAULT NULL
@@ -20,7 +57,7 @@ BEGIN
2057 END IF;
2158 IF $1 IS NOT NULL THEN
2259 SELECT relkind INTO backup_relkind
23- FROM pg_catalog.pg_class WHERE oid = $1;
60+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
2461 IF NOT FOUND THEN
2562 RAISE EXCEPTION 'relation "%" not found', $1;
2663 END IF;
@@ -34,7 +71,7 @@ BEGIN
3471 END IF;
3572 IF $2 IS NOT NULL THEN
3673 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
37- WHERE a.attrelid = $1 AND a.attname = $2;
74+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
3875 IF set_attnum IS NULL THEN
3976 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
4077 END IF;
@@ -65,7 +102,8 @@ $$
65102 DECLARE
66103 backup_id int8;
67104 BEGIN
68- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
105+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
106+ THEN
69107 RAISE EXCEPTION 'schema "%" not found', $1;
70108 END IF;
71109 IF dbms_stats.is_system_schema($1) THEN
@@ -112,15 +150,18 @@ BEGIN
112150 IF $2 IS NULL AND $3 IS NOT NULL THEN
113151 RAISE EXCEPTION 'relation required';
114152 END IF;
115- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
153+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
154+ WHERE id <= $1 FOR SHARE) THEN
116155 RAISE EXCEPTION 'backup id % not found', $1;
117156 END IF;
118157 IF $2 IS NOT NULL THEN
119- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
158+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
159+ WHERE oid = $2 FOR SHARE) THEN
120160 RAISE EXCEPTION 'relation "%" not found', $2;
121161 END IF;
162+ -- Grabbing all backups for the relation which is not used in restore.
122163 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
123- WHERE b.id <= $1 AND b.relid = $2) THEN
164+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
124165 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
125166 END IF;
126167 IF $3 IS NOT NULL THEN
@@ -133,21 +174,25 @@ BEGIN
133174 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
134175 END IF;
135176 END IF;
177+ PERFORM * FROM dbms_stats._relation_stats_locked r
178+ WHERE r.relid = $2 FOR UPDATE;
179+ ELSE
180+ /* Lock the whole relation stats if relation is not specified.*/
181+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
136182 END IF;
137183
138- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
139- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
140-
141184 FOR restore_id, restore_relid IN
142- SELECT max(b.id), c.oid
143- FROM pg_class c, dbms_stats.relation_stats_backup b
144- WHERE (c.oid = $2 OR $2 IS NULL)
145- AND c.oid = b.relid
146- AND dbms_stats.is_target_relkind(c.relkind)
147- AND NOT dbms_stats.is_system_catalog(c.oid)
148- AND b.id <= $1
149- GROUP BY c.oid
150- ORDER BY c.oid::regclass::text
185+ SELECT max(id), coid FROM
186+ (SELECT b.id as id, c.oid as coid
187+ FROM pg_class c, dbms_stats.relation_stats_backup b
188+ WHERE (c.oid = $2 OR $2 IS NULL)
189+ AND c.oid = b.relid
190+ AND dbms_stats.is_target_relkind(c.relkind)
191+ AND NOT dbms_stats.is_system_catalog(c.oid)
192+ AND b.id <= $1
193+ FOR SHARE) t
194+ GROUP BY coid
195+ ORDER BY coid::regclass::text
151196 LOOP
152197 UPDATE dbms_stats._relation_stats_locked r
153198 SET relid = b.relid,
@@ -224,10 +269,31 @@ BEGIN
224269 AND staattnum = restore_attnum;
225270 END IF;
226271 END LOOP;
272+EXCEPTION
273+ WHEN unique_violation THEN
274+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
227275 END;
228276 $$
229277 LANGUAGE plpgsql;
230278
279+CREATE OR REPLACE FUNCTION dbms_stats.restore_database_stats(
280+ as_of_timestamp timestamp with time zone
281+) RETURNS SETOF regclass AS
282+$$
283+SELECT dbms_stats.restore(m.id, m.relid)
284+ FROM (SELECT max(id) AS id, relid
285+ FROM (SELECT r.id, r.relid
286+ FROM pg_class c, dbms_stats.relation_stats_backup r,
287+ dbms_stats.backup_history b
288+ WHERE c.oid = r.relid
289+ AND r.id = b.id
290+ AND b.time <= $1
291+ FOR SHARE) t1
292+ GROUP BY t1.relid
293+ ORDER BY t1.relid) m;
294+$$
295+LANGUAGE sql STRICT;
296+
231297 CREATE OR REPLACE FUNCTION dbms_stats.restore_schema_stats(
232298 schemaname text,
233299 as_of_timestamp timestamp with time zone
@@ -243,18 +309,19 @@ BEGIN
243309
244310 RETURN QUERY
245311 SELECT dbms_stats.restore(m.id, m.relid)
246- FROM (SELECT max(r.id) AS id, r.relid
247- FROM pg_class c, pg_namespace n,
248- dbms_stats.relation_stats_backup r,
249- dbms_stats.backup_history b
250- WHERE c.oid = r.relid
251- AND c.relnamespace = n.oid
252- AND n.nspname = $1
253- AND r.id = b.id
254- AND b.time <= $2
255- GROUP BY r.relid
256- ORDER BY r.relid
257- ) m;
312+ FROM (SELECT max(id) AS id, relid
313+ FROM (SELECT r.id, r.relid
314+ FROM pg_class c, pg_namespace n,
315+ dbms_stats.relation_stats_backup r,
316+ dbms_stats.backup_history b
317+ WHERE c.oid = r.relid
318+ AND c.relnamespace = n.oid
319+ AND n.nspname = $1
320+ AND r.id = b.id
321+ AND b.time <= $2
322+ FOR SHARE) t1
323+ GROUP BY t1.relid
324+ ORDER BY t1.relid) m;
258325 END;
259326 $$
260327 LANGUAGE plpgsql STRICT;
@@ -274,8 +341,12 @@ BEGIN
274341 RAISE EXCEPTION 'backup id % not found', $1;
275342 END IF;
276343
277- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
278- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
344+ /* Lock the backup */
345+ PERFORM * from dbms_stats.relation_stats_backup b
346+ WHERE id = $1 FOR SHARE;
347+
348+ /* Locking only _relation_stats_locked is sufficient */
349+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
279350
280351 FOR restore_relid IN
281352 SELECT b.relid
@@ -386,15 +457,12 @@ BEGIN
386457 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
387458 END IF;
388459
389- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
390- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
391-
392460 /*
393461 * If we don't have per-table statistics, create new one which has NULL for
394- * every statistic column_stats_effective.
462+ * every statistic value for column_stats_effective.
395463 */
396464 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
397- WHERE ru.relid = $1) THEN
465+ WHERE ru.relid = $1 FOR SHARE) THEN
398466 INSERT INTO dbms_stats._relation_stats_locked
399467 SELECT $1, dbms_stats.relname(nspname, relname),
400468 NULL, NULL, NULL, NULL, NULL
@@ -475,12 +543,15 @@ BEGIN
475543 END IF;
476544 END LOOP;
477545
478- /* If we don't have statistic at all, raise error. */
546+ /* If we don't have statistics at all, raise error. */
479547 IF NOT FOUND THEN
480548 RAISE EXCEPTION 'no statistics available for column "%" of relation "%"', $2, $1::regclass;
481549 END IF;
482550
483551 RETURN $1;
552+EXCEPTION
553+ WHEN unique_violation THEN
554+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
484555 END;
485556 $$
486557 LANGUAGE plpgsql;
@@ -507,9 +578,6 @@ BEGIN
507578 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
508579 END IF;
509580
510- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
511- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
512-
513581 UPDATE dbms_stats._relation_stats_locked r
514582 SET relname = dbms_stats.relname(nspname, c.relname),
515583 relpages = v.relpages,
@@ -617,6 +685,9 @@ BEGIN
617685 END LOOP;
618686
619687 RETURN $1;
688+EXCEPTION
689+ WHEN unique_violation THEN
690+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
620691 END;
621692 $$
622693 LANGUAGE plpgsql;
@@ -658,15 +729,19 @@ BEGIN
658729 IF $1 IS NULL AND $2 IS NOT NULL THEN
659730 RAISE EXCEPTION 'relation required';
660731 END IF;
732+
733+ /*
734+ * Lock the target relation to prevent conflicting with stats lock/restore
735+ */
736+ PERFORM * FROM dbms_stats._relation_stats_locked ru
737+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
738+
661739 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
662740 WHERE a.attrelid = $1 AND a.attname = $2;
663741 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
664742 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
665743 END IF;
666744
667- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
668- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
669-
670745 DELETE FROM dbms_stats._column_stats_locked
671746 WHERE (starelid = $1 OR $1 IS NULL)
672747 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -689,6 +764,105 @@ END;
689764 $$
690765 LANGUAGE plpgsql;
691766
767+CREATE OR REPLACE FUNCTION dbms_stats.unlock_database_stats()
768+ RETURNS SETOF regclass AS
769+$$
770+DECLARE
771+ unlock_id int8;
772+BEGIN
773+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
774+
775+ FOR unlock_id IN
776+ SELECT relid
777+ FROM dbms_stats._relation_stats_locked
778+ ORDER BY relid
779+ LOOP
780+ DELETE FROM dbms_stats._relation_stats_locked
781+ WHERE relid = unlock_id;
782+ RETURN NEXT unlock_id;
783+ END LOOP;
784+END;
785+$$
786+LANGUAGE plpgsql STRICT;
787+
788+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
789+ RETURNS SETOF regclass AS
790+$$
791+DELETE FROM dbms_stats._relation_stats_locked
792+ WHERE relid = $1
793+ RETURNING relid::regclass
794+$$
795+LANGUAGE sql STRICT;
796+
797+CREATE OR REPLACE FUNCTION dbms_stats.unlock_table_stats(
798+ schemaname text,
799+ tablename text
800+) RETURNS SETOF regclass AS
801+$$
802+DELETE FROM dbms_stats._relation_stats_locked
803+ WHERE relid = dbms_stats.relname($1, $2)::regclass
804+ RETURNING relid::regclass
805+$$
806+LANGUAGE sql STRICT;
807+
808+CREATE OR REPLACE FUNCTION dbms_stats.unlock_column_stats(
809+ relid regclass,
810+ attname text
811+) RETURNS SETOF regclass AS
812+$$
813+DECLARE
814+ set_attnum int2;
815+BEGIN
816+ SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
817+ WHERE a.attrelid = $1 AND a.attname = $2;
818+ IF $2 IS NOT NULL AND set_attnum IS NULL THEN
819+ RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
820+ END IF;
821+
822+ /* Lock the locked table stats */
823+ PERFORM * from dbms_stats.relation_stats_locked r
824+ WHERE r.relid = $1 FOR SHARE;
825+
826+ DELETE FROM dbms_stats._column_stats_locked
827+ WHERE starelid = $1
828+ AND staattnum = set_attnum;
829+
830+ RETURN QUERY
831+ SELECT $1;
832+END;
833+$$
834+LANGUAGE plpgsql STRICT;
835+
836+CREATE OR REPLACE FUNCTION dbms_stats.unlock_column_stats(
837+ schemaname text,
838+ tablename text,
839+ attname text
840+) RETURNS SETOF regclass AS
841+$$
842+DECLARE
843+ set_attnum int2;
844+BEGIN
845+ SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
846+ WHERE a.attrelid = dbms_stats.relname($1, $2)::regclass
847+ AND a.attname = $3;
848+ IF $3 IS NOT NULL AND set_attnum IS NULL THEN
849+ RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
850+ END IF;
851+
852+ /* Lock the locked table stats */
853+ PERFORM * from dbms_stats.relation_stats_locked r
854+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
855+
856+ DELETE FROM dbms_stats._column_stats_locked
857+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
858+ AND staattnum = set_attnum;
859+
860+ RETURN QUERY
861+ SELECT dbms_stats.relname($1, $2)::regclass;
862+END;
863+$$
864+LANGUAGE plpgsql STRICT;
865+
692866 CREATE OR REPLACE FUNCTION dbms_stats.unlock_schema_stats(
693867 schemaname text
694868 ) RETURNS SETOF regclass AS
@@ -703,16 +877,14 @@ BEGIN
703877 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
704878 END IF;
705879
706- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
707- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
708-
709880 FOR unlock_id IN
710- SELECT relid
711- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
881+ SELECT r.relid
882+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
712883 WHERE relid = c.oid
713884 AND c.relnamespace = n.oid
714885 AND n.nspname = $1
715886 ORDER BY relid
887+ FOR UPDATE
716888 LOOP
717889 DELETE FROM dbms_stats._relation_stats_locked
718890 WHERE relid = unlock_id;
@@ -736,12 +908,14 @@ BEGIN
736908 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
737909 END IF;
738910
739- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
740- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
911+ /* Lock the locked table stats */
912+ PERFORM * from dbms_stats.relation_stats_locked r
913+ WHERE r.relid = $1 FOR SHARE;
914+
915+ DELETE FROM dbms_stats._column_stats_locked
916+ WHERE starelid = $1
917+ AND staattnum = set_attnum;
741918
742- DELETE FROM dbms_stats._column_stats_locked
743- WHERE starelid = $1
744- AND staattnum = set_attnum;
745919 RETURN QUERY
746920 SELECT $1;
747921 END;
@@ -764,12 +938,14 @@ BEGIN
764938 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
765939 END IF;
766940
767- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
768- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
941+ /* Lock the locked table stats */
942+ PERFORM * from dbms_stats.relation_stats_locked r
943+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
944+
945+ DELETE FROM dbms_stats._column_stats_locked
946+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
947+ AND staattnum = set_attnum;
769948
770- DELETE FROM dbms_stats._column_stats_locked
771- WHERE starelid = dbms_stats.relname($1, $2)::regclass
772- AND staattnum = set_attnum;
773949 RETURN QUERY
774950 SELECT dbms_stats.relname($1, $2)::regclass;
775951 END;
@@ -783,7 +959,7 @@ CREATE OR REPLACE FUNCTION dbms_stats.purge_stats(
783959 $$
784960 DECLARE
785961 delete_id int8;
786- deleted dbms_stats.backup_history;
962+ todelete dbms_stats.backup_history;
787963 BEGIN
788964 IF $1 IS NULL THEN
789965 RAISE EXCEPTION 'backup id required';
@@ -792,11 +968,8 @@ BEGIN
792968 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
793969 END IF;
794970
795- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
796- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
797- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
798-
799- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
971+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
972+ WHERE id = $1 FOR UPDATE) THEN
800973 RAISE EXCEPTION 'backup id % not found', $1;
801974 END IF;
802975 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -808,15 +981,59 @@ BEGIN
808981 RETURN;
809982 END IF;
810983
811- FOR deleted IN
984+ FOR todelete IN
812985 SELECT * FROM dbms_stats.backup_history
813986 WHERE id <= $1
814- ORDER BY id
987+ ORDER BY id FOR UPDATE
815988 LOOP
816989 DELETE FROM dbms_stats.backup_history
817- WHERE id = deleted.id;
818- RETURN NEXT deleted;
990+ WHERE id = todelete.id;
991+ RETURN NEXT todelete;
819992 END LOOP;
820993 END;
821994 $$
822995 LANGUAGE plpgsql;
996+
997+CREATE OR REPLACE FUNCTION dbms_stats.clean_up_stats() RETURNS SETOF text AS
998+$$
999+DECLARE
1000+ clean_relid Oid;
1001+ clean_attnum int2;
1002+ clean_inherit bool;
1003+ clean_rel_col text;
1004+BEGIN
1005+ -- We don't have to check that table-level dummy statistics of the table
1006+ -- exists here, because the foreign key constraints defined on column-level
1007+ -- dummy static table ensures that.
1008+ FOR clean_rel_col, clean_relid, clean_attnum, clean_inherit IN
1009+ SELECT r.relname || ', ' || v.staattnum::text,
1010+ v.starelid, v.staattnum, v.stainherit
1011+ FROM dbms_stats._column_stats_locked v
1012+ JOIN dbms_stats._relation_stats_locked r ON (v.starelid = r.relid)
1013+ WHERE NOT EXISTS (
1014+ SELECT NULL
1015+ FROM pg_attribute a
1016+ WHERE a.attrelid = v.starelid
1017+ AND a.attnum = v.staattnum
1018+ AND a.attisdropped = false
1019+ FOR UPDATE
1020+ )
1021+ LOOP
1022+ DELETE FROM dbms_stats._column_stats_locked
1023+ WHERE starelid = clean_relid
1024+ AND staattnum = clean_attnum
1025+ AND stainherit = clean_inherit;
1026+ RETURN NEXT clean_rel_col;
1027+ END LOOP;
1028+
1029+ RETURN QUERY
1030+ DELETE FROM dbms_stats._relation_stats_locked r
1031+ WHERE NOT EXISTS (
1032+ SELECT NULL
1033+ FROM pg_class c
1034+ WHERE c.oid = r.relid)
1035+ RETURNING relname || ',';
1036+ RETURN;
1037+END
1038+$$
1039+LANGUAGE plpgsql;
--- a/ext_scripts/pg_dbms_stats--1.3.3-9.1.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.3-9.1.sql
@@ -300,6 +300,10 @@ CREATE FUNCTION dbms_stats.backup(
300300 attnum int2
301301 ) RETURNS int8 AS
302302 $$
303+/* Lock the backup id */
304+SELECT * from dbms_stats.backup_history
305+ WHERE id = $1 FOR UPDATE;
306+
303307 INSERT INTO dbms_stats.relation_stats_backup
304308 SELECT $1, v.relid, v.relname, v.relpages, v.reltuples,
305309 v.curpages, v.last_analyze, v.last_autoanalyze
@@ -344,7 +348,7 @@ BEGIN
344348 END IF;
345349 IF $1 IS NOT NULL THEN
346350 SELECT relkind INTO backup_relkind
347- FROM pg_catalog.pg_class WHERE oid = $1;
351+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
348352 IF NOT FOUND THEN
349353 RAISE EXCEPTION 'relation "%" not found', $1;
350354 END IF;
@@ -358,7 +362,7 @@ BEGIN
358362 END IF;
359363 IF $2 IS NOT NULL THEN
360364 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
361- WHERE a.attrelid = $1 AND a.attname = $2;
365+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
362366 IF set_attnum IS NULL THEN
363367 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
364368 END IF;
@@ -397,7 +401,8 @@ $$
397401 DECLARE
398402 backup_id int8;
399403 BEGIN
400- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
404+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
405+ THEN
401406 RAISE EXCEPTION 'schema "%" not found', $1;
402407 END IF;
403408 IF dbms_stats.is_system_schema($1) THEN
@@ -487,15 +492,18 @@ BEGIN
487492 IF $2 IS NULL AND $3 IS NOT NULL THEN
488493 RAISE EXCEPTION 'relation required';
489494 END IF;
490- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
495+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
496+ WHERE id <= $1 FOR SHARE) THEN
491497 RAISE EXCEPTION 'backup id % not found', $1;
492498 END IF;
493499 IF $2 IS NOT NULL THEN
494- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
500+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
501+ WHERE oid = $2 FOR SHARE) THEN
495502 RAISE EXCEPTION 'relation "%" not found', $2;
496503 END IF;
504+ -- Grabbing all backups for the relation which is not used in restore.
497505 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
498- WHERE b.id <= $1 AND b.relid = $2) THEN
506+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
499507 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
500508 END IF;
501509 IF $3 IS NOT NULL THEN
@@ -508,21 +516,25 @@ BEGIN
508516 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
509517 END IF;
510518 END IF;
519+ PERFORM * FROM dbms_stats._relation_stats_locked r
520+ WHERE r.relid = $2 FOR UPDATE;
521+ ELSE
522+ /* Lock the whole relation stats if relation is not specified.*/
523+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
511524 END IF;
512525
513- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
514- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
515-
516526 FOR restore_id, restore_relid IN
517- SELECT max(b.id), c.oid
518- FROM pg_class c, dbms_stats.relation_stats_backup b
519- WHERE (c.oid = $2 OR $2 IS NULL)
520- AND c.oid = b.relid
521- AND dbms_stats.is_target_relkind(c.relkind)
522- AND NOT dbms_stats.is_system_catalog(c.oid)
523- AND b.id <= $1
524- GROUP BY c.oid
525- ORDER BY c.oid::regclass::text
527+ SELECT max(id), coid FROM
528+ (SELECT b.id as id, c.oid as coid
529+ FROM pg_class c, dbms_stats.relation_stats_backup b
530+ WHERE (c.oid = $2 OR $2 IS NULL)
531+ AND c.oid = b.relid
532+ AND dbms_stats.is_target_relkind(c.relkind)
533+ AND NOT dbms_stats.is_system_catalog(c.oid)
534+ AND b.id <= $1
535+ FOR SHARE) t
536+ GROUP BY coid
537+ ORDER BY coid::regclass::text
526538 LOOP
527539 UPDATE dbms_stats._relation_stats_locked r
528540 SET relid = b.relid,
@@ -597,6 +609,9 @@ BEGIN
597609 AND staattnum = restore_attnum;
598610 END IF;
599611 END LOOP;
612+EXCEPTION
613+ WHEN unique_violation THEN
614+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
600615 END;
601616 $$
602617 LANGUAGE plpgsql;
@@ -606,15 +621,16 @@ CREATE FUNCTION dbms_stats.restore_database_stats(
606621 ) RETURNS SETOF regclass AS
607622 $$
608623 SELECT dbms_stats.restore(m.id, m.relid)
609- FROM (SELECT max(r.id) AS id, r.relid
610- FROM pg_class c, dbms_stats.relation_stats_backup r,
611- dbms_stats.backup_history b
612- WHERE c.oid = r.relid
613- AND r.id = b.id
614- AND b.time <= $1
615- GROUP BY r.relid
616- ORDER BY r.relid
617- ) m;
624+ FROM (SELECT max(id) AS id, relid
625+ FROM (SELECT r.id, r.relid
626+ FROM pg_class c, dbms_stats.relation_stats_backup r,
627+ dbms_stats.backup_history b
628+ WHERE c.oid = r.relid
629+ AND r.id = b.id
630+ AND b.time <= $1
631+ FOR SHARE) t1
632+ GROUP BY t1.relid
633+ ORDER BY t1.relid) m;
618634 $$
619635 LANGUAGE sql STRICT;
620636
@@ -633,18 +649,19 @@ BEGIN
633649
634650 RETURN QUERY
635651 SELECT dbms_stats.restore(m.id, m.relid)
636- FROM (SELECT max(r.id) AS id, r.relid
637- FROM pg_class c, pg_namespace n,
638- dbms_stats.relation_stats_backup r,
639- dbms_stats.backup_history b
640- WHERE c.oid = r.relid
641- AND c.relnamespace = n.oid
642- AND n.nspname = $1
643- AND r.id = b.id
644- AND b.time <= $2
645- GROUP BY r.relid
646- ORDER BY r.relid
647- ) m;
652+ FROM (SELECT max(id) AS id, relid
653+ FROM (SELECT r.id, r.relid
654+ FROM pg_class c, pg_namespace n,
655+ dbms_stats.relation_stats_backup r,
656+ dbms_stats.backup_history b
657+ WHERE c.oid = r.relid
658+ AND c.relnamespace = n.oid
659+ AND n.nspname = $1
660+ AND r.id = b.id
661+ AND b.time <= $2
662+ FOR SHARE) t1
663+ GROUP BY t1.relid
664+ ORDER BY t1.relid) m;
648665 END;
649666 $$
650667 LANGUAGE plpgsql STRICT;
@@ -707,8 +724,12 @@ BEGIN
707724 RAISE EXCEPTION 'backup id % not found', $1;
708725 END IF;
709726
710- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
711- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
727+ /* Lock the backup */
728+ PERFORM * from dbms_stats.relation_stats_backup b
729+ WHERE id = $1 FOR SHARE;
730+
731+ /* Locking only _relation_stats_locked is sufficient */
732+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
712733
713734 FOR restore_relid IN
714735 SELECT b.relid
@@ -821,15 +842,12 @@ BEGIN
821842 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
822843 END IF;
823844
824- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
825- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
826-
827845 /*
828846 * If we don't have per-table statistics, create new one which has NULL for
829847 * every statistic value for column_stats_effective.
830848 */
831849 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
832- WHERE ru.relid = $1) THEN
850+ WHERE ru.relid = $1 FOR SHARE) THEN
833851 INSERT INTO dbms_stats._relation_stats_locked
834852 SELECT $1, dbms_stats.relname(nspname, relname),
835853 NULL, NULL, NULL, NULL, NULL
@@ -908,6 +926,9 @@ BEGIN
908926 END IF;
909927
910928 RETURN $1;
929+EXCEPTION
930+ WHEN unique_violation THEN
931+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
911932 END;
912933 $$
913934 LANGUAGE plpgsql;
@@ -934,9 +955,6 @@ BEGIN
934955 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
935956 END IF;
936957
937- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
938- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
939-
940958 UPDATE dbms_stats._relation_stats_locked r
941959 SET relname = dbms_stats.relname(nspname, c.relname),
942960 relpages = v.relpages,
@@ -1035,6 +1053,9 @@ BEGIN
10351053 END LOOP;
10361054
10371055 RETURN $1;
1056+EXCEPTION
1057+ WHEN unique_violation THEN
1058+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
10381059 END;
10391060 $$
10401061 LANGUAGE plpgsql;
@@ -1128,15 +1149,19 @@ BEGIN
11281149 IF $1 IS NULL AND $2 IS NOT NULL THEN
11291150 RAISE EXCEPTION 'relation required';
11301151 END IF;
1152+
1153+ /*
1154+ * Lock the target relation to prevent conflicting with stats lock/restore
1155+ */
1156+ PERFORM * FROM dbms_stats._relation_stats_locked ru
1157+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
1158+
11311159 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
11321160 WHERE a.attrelid = $1 AND a.attname = $2;
11331161 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
11341162 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
11351163 END IF;
11361164
1137- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1138- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1139-
11401165 DELETE FROM dbms_stats._column_stats_locked
11411166 WHERE (starelid = $1 OR $1 IS NULL)
11421167 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -1165,8 +1190,7 @@ $$
11651190 DECLARE
11661191 unlock_id int8;
11671192 BEGIN
1168- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1169- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1193+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
11701194
11711195 FOR unlock_id IN
11721196 SELECT relid
@@ -1195,16 +1219,14 @@ BEGIN
11951219 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
11961220 END IF;
11971221
1198- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1199- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1200-
12011222 FOR unlock_id IN
1202- SELECT relid
1203- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
1223+ SELECT r.relid
1224+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
12041225 WHERE relid = c.oid
12051226 AND c.relnamespace = n.oid
12061227 AND n.nspname = $1
12071228 ORDER BY relid
1229+ FOR UPDATE
12081230 LOOP
12091231 DELETE FROM dbms_stats._relation_stats_locked
12101232 WHERE relid = unlock_id;
@@ -1217,10 +1239,6 @@ LANGUAGE plpgsql STRICT;
12171239 CREATE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
12181240 RETURNS SETOF regclass AS
12191241 $$
1220-
1221-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1222-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1223-
12241242 DELETE FROM dbms_stats._relation_stats_locked
12251243 WHERE relid = $1
12261244 RETURNING relid::regclass
@@ -1232,10 +1250,6 @@ CREATE FUNCTION dbms_stats.unlock_table_stats(
12321250 tablename text
12331251 ) RETURNS SETOF regclass AS
12341252 $$
1235-
1236-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1237-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1238-
12391253 DELETE FROM dbms_stats._relation_stats_locked
12401254 WHERE relid = dbms_stats.relname($1, $2)::regclass
12411255 RETURNING relid::regclass
@@ -1256,12 +1270,14 @@ BEGIN
12561270 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
12571271 END IF;
12581272
1259- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1260- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1273+ /* Lock the locked table stats */
1274+ PERFORM * from dbms_stats.relation_stats_locked r
1275+ WHERE r.relid = $1 FOR SHARE;
1276+
1277+ DELETE FROM dbms_stats._column_stats_locked
1278+ WHERE starelid = $1
1279+ AND staattnum = set_attnum;
12611280
1262- DELETE FROM dbms_stats._column_stats_locked
1263- WHERE starelid = $1
1264- AND staattnum = set_attnum;
12651281 RETURN QUERY
12661282 SELECT $1;
12671283 END;
@@ -1284,12 +1300,14 @@ BEGIN
12841300 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
12851301 END IF;
12861302
1287- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1288- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1303+ /* Lock the locked table stats */
1304+ PERFORM * from dbms_stats.relation_stats_locked r
1305+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
1306+
1307+ DELETE FROM dbms_stats._column_stats_locked
1308+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
1309+ AND staattnum = set_attnum;
12891310
1290- DELETE FROM dbms_stats._column_stats_locked
1291- WHERE starelid = dbms_stats.relname($1, $2)::regclass
1292- AND staattnum = set_attnum;
12931311 RETURN QUERY
12941312 SELECT dbms_stats.relname($1, $2)::regclass;
12951313 END;
@@ -1384,11 +1402,8 @@ BEGIN
13841402 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
13851403 END IF;
13861404
1387- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
1388- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1389- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1390-
1391- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
1405+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
1406+ WHERE id = $1 FOR UPDATE) THEN
13921407 RAISE EXCEPTION 'backup id % not found', $1;
13931408 END IF;
13941409 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -1403,7 +1418,7 @@ BEGIN
14031418 FOR todelete IN
14041419 SELECT * FROM dbms_stats.backup_history
14051420 WHERE id <= $1
1406- ORDER BY id
1421+ ORDER BY id FOR UPDATE
14071422 LOOP
14081423 DELETE FROM dbms_stats.backup_history
14091424 WHERE id = todelete.id;
@@ -1438,6 +1453,7 @@ BEGIN
14381453 WHERE a.attrelid = v.starelid
14391454 AND a.attnum = v.staattnum
14401455 AND a.attisdropped = false
1456+ FOR UPDATE
14411457 )
14421458 LOOP
14431459 DELETE FROM dbms_stats._column_stats_locked
--- a/ext_scripts/pg_dbms_stats--1.3.3-9.2.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.3-9.2.sql
@@ -336,6 +336,10 @@ CREATE FUNCTION dbms_stats.backup(
336336 attnum int2
337337 ) RETURNS int8 AS
338338 $$
339+/* Lock the backup id */
340+SELECT * from dbms_stats.backup_history
341+ WHERE id = $1 FOR UPDATE;
342+
339343 INSERT INTO dbms_stats.relation_stats_backup
340344 SELECT $1, v.relid, v.relname, v.relpages, v.reltuples, v.relallvisible,
341345 v.curpages, v.last_analyze, v.last_autoanalyze
@@ -380,7 +384,7 @@ BEGIN
380384 END IF;
381385 IF $1 IS NOT NULL THEN
382386 SELECT relkind INTO backup_relkind
383- FROM pg_catalog.pg_class WHERE oid = $1;
387+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
384388 IF NOT FOUND THEN
385389 RAISE EXCEPTION 'relation "%" not found', $1;
386390 END IF;
@@ -394,7 +398,7 @@ BEGIN
394398 END IF;
395399 IF $2 IS NOT NULL THEN
396400 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
397- WHERE a.attrelid = $1 AND a.attname = $2;
401+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
398402 IF set_attnum IS NULL THEN
399403 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
400404 END IF;
@@ -433,7 +437,8 @@ $$
433437 DECLARE
434438 backup_id int8;
435439 BEGIN
436- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
440+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
441+ THEN
437442 RAISE EXCEPTION 'schema "%" not found', $1;
438443 END IF;
439444 IF dbms_stats.is_system_schema($1) THEN
@@ -523,15 +528,18 @@ BEGIN
523528 IF $2 IS NULL AND $3 IS NOT NULL THEN
524529 RAISE EXCEPTION 'relation required';
525530 END IF;
526- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
531+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
532+ WHERE id <= $1 FOR SHARE) THEN
527533 RAISE EXCEPTION 'backup id % not found', $1;
528534 END IF;
529535 IF $2 IS NOT NULL THEN
530- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
536+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
537+ WHERE oid = $2 FOR SHARE) THEN
531538 RAISE EXCEPTION 'relation "%" not found', $2;
532539 END IF;
540+ -- Grabbing all backups for the relation which is not used in restore.
533541 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
534- WHERE b.id <= $1 AND b.relid = $2) THEN
542+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
535543 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
536544 END IF;
537545 IF $3 IS NOT NULL THEN
@@ -544,21 +552,25 @@ BEGIN
544552 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
545553 END IF;
546554 END IF;
555+ PERFORM * FROM dbms_stats._relation_stats_locked r
556+ WHERE r.relid = $2 FOR UPDATE;
557+ ELSE
558+ /* Lock the whole relation stats if relation is not specified.*/
559+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
547560 END IF;
548561
549- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
550- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
551-
552562 FOR restore_id, restore_relid IN
553- SELECT max(b.id), c.oid
554- FROM pg_class c, dbms_stats.relation_stats_backup b
555- WHERE (c.oid = $2 OR $2 IS NULL)
556- AND c.oid = b.relid
557- AND dbms_stats.is_target_relkind(c.relkind)
558- AND NOT dbms_stats.is_system_catalog(c.oid)
559- AND b.id <= $1
560- GROUP BY c.oid
561- ORDER BY c.oid::regclass::text
563+ SELECT max(id), coid FROM
564+ (SELECT b.id as id, c.oid as coid
565+ FROM pg_class c, dbms_stats.relation_stats_backup b
566+ WHERE (c.oid = $2 OR $2 IS NULL)
567+ AND c.oid = b.relid
568+ AND dbms_stats.is_target_relkind(c.relkind)
569+ AND NOT dbms_stats.is_system_catalog(c.oid)
570+ AND b.id <= $1
571+ FOR SHARE) t
572+ GROUP BY coid
573+ ORDER BY coid::regclass::text
562574 LOOP
563575 UPDATE dbms_stats._relation_stats_locked r
564576 SET relid = b.relid,
@@ -635,6 +647,9 @@ BEGIN
635647 AND staattnum = restore_attnum;
636648 END IF;
637649 END LOOP;
650+EXCEPTION
651+ WHEN unique_violation THEN
652+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
638653 END;
639654 $$
640655 LANGUAGE plpgsql;
@@ -644,15 +659,16 @@ CREATE FUNCTION dbms_stats.restore_database_stats(
644659 ) RETURNS SETOF regclass AS
645660 $$
646661 SELECT dbms_stats.restore(m.id, m.relid)
647- FROM (SELECT max(r.id) AS id, r.relid
648- FROM pg_class c, dbms_stats.relation_stats_backup r,
649- dbms_stats.backup_history b
650- WHERE c.oid = r.relid
651- AND r.id = b.id
652- AND b.time <= $1
653- GROUP BY r.relid
654- ORDER BY r.relid
655- ) m;
662+ FROM (SELECT max(id) AS id, relid
663+ FROM (SELECT r.id, r.relid
664+ FROM pg_class c, dbms_stats.relation_stats_backup r,
665+ dbms_stats.backup_history b
666+ WHERE c.oid = r.relid
667+ AND r.id = b.id
668+ AND b.time <= $1
669+ FOR SHARE) t1
670+ GROUP BY t1.relid
671+ ORDER BY t1.relid) m;
656672 $$
657673 LANGUAGE sql STRICT;
658674
@@ -671,18 +687,19 @@ BEGIN
671687
672688 RETURN QUERY
673689 SELECT dbms_stats.restore(m.id, m.relid)
674- FROM (SELECT max(r.id) AS id, r.relid
675- FROM pg_class c, pg_namespace n,
676- dbms_stats.relation_stats_backup r,
677- dbms_stats.backup_history b
678- WHERE c.oid = r.relid
679- AND c.relnamespace = n.oid
680- AND n.nspname = $1
681- AND r.id = b.id
682- AND b.time <= $2
683- GROUP BY r.relid
684- ORDER BY r.relid
685- ) m;
690+ FROM (SELECT max(id) AS id, relid
691+ FROM (SELECT r.id, r.relid
692+ FROM pg_class c, pg_namespace n,
693+ dbms_stats.relation_stats_backup r,
694+ dbms_stats.backup_history b
695+ WHERE c.oid = r.relid
696+ AND c.relnamespace = n.oid
697+ AND n.nspname = $1
698+ AND r.id = b.id
699+ AND b.time <= $2
700+ FOR SHARE) t1
701+ GROUP BY t1.relid
702+ ORDER BY t1.relid) m;
686703 END;
687704 $$
688705 LANGUAGE plpgsql STRICT;
@@ -745,8 +762,12 @@ BEGIN
745762 RAISE EXCEPTION 'backup id % not found', $1;
746763 END IF;
747764
748- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
749- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
765+ /* Lock the backup */
766+ PERFORM * from dbms_stats.relation_stats_backup b
767+ WHERE id = $1 FOR SHARE;
768+
769+ /* Locking only _relation_stats_locked is sufficient */
770+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
750771
751772 FOR restore_relid IN
752773 SELECT b.relid
@@ -861,15 +882,12 @@ BEGIN
861882 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
862883 END IF;
863884
864- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
865- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
866-
867885 /*
868886 * If we don't have per-table statistics, create new one which has NULL for
869887 * every statistic value for column_stats_effective.
870888 */
871889 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
872- WHERE ru.relid = $1) THEN
890+ WHERE ru.relid = $1 FOR SHARE) THEN
873891 INSERT INTO dbms_stats._relation_stats_locked
874892 SELECT $1, dbms_stats.relname(nspname, relname),
875893 NULL, NULL, NULL, NULL, NULL
@@ -956,6 +974,9 @@ BEGIN
956974 END IF;
957975
958976 RETURN $1;
977+EXCEPTION
978+ WHEN unique_violation THEN
979+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
959980 END;
960981 $$
961982 LANGUAGE plpgsql;
@@ -982,9 +1003,6 @@ BEGIN
9821003 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
9831004 END IF;
9841005
985- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
986- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
987-
9881006 UPDATE dbms_stats._relation_stats_locked r
9891007 SET relname = dbms_stats.relname(nspname, c.relname),
9901008 relpages = v.relpages,
@@ -1092,6 +1110,9 @@ BEGIN
10921110 END LOOP;
10931111
10941112 RETURN $1;
1113+EXCEPTION
1114+ WHEN unique_violation THEN
1115+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
10951116 END;
10961117 $$
10971118 LANGUAGE plpgsql;
@@ -1185,15 +1206,19 @@ BEGIN
11851206 IF $1 IS NULL AND $2 IS NOT NULL THEN
11861207 RAISE EXCEPTION 'relation required';
11871208 END IF;
1209+
1210+ /*
1211+ * Lock the target relation to prevent conflicting with stats lock/restore
1212+ */
1213+ PERFORM * FROM dbms_stats._relation_stats_locked ru
1214+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
1215+
11881216 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
11891217 WHERE a.attrelid = $1 AND a.attname = $2;
11901218 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
11911219 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
11921220 END IF;
11931221
1194- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1195- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1196-
11971222 DELETE FROM dbms_stats._column_stats_locked
11981223 WHERE (starelid = $1 OR $1 IS NULL)
11991224 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -1222,8 +1247,7 @@ $$
12221247 DECLARE
12231248 unlock_id int8;
12241249 BEGIN
1225- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1226- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1250+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
12271251
12281252 FOR unlock_id IN
12291253 SELECT relid
@@ -1252,16 +1276,14 @@ BEGIN
12521276 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
12531277 END IF;
12541278
1255- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1256- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1257-
12581279 FOR unlock_id IN
1259- SELECT relid
1260- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
1280+ SELECT r.relid
1281+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
12611282 WHERE relid = c.oid
12621283 AND c.relnamespace = n.oid
12631284 AND n.nspname = $1
12641285 ORDER BY relid
1286+ FOR UPDATE
12651287 LOOP
12661288 DELETE FROM dbms_stats._relation_stats_locked
12671289 WHERE relid = unlock_id;
@@ -1274,10 +1296,6 @@ LANGUAGE plpgsql STRICT;
12741296 CREATE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
12751297 RETURNS SETOF regclass AS
12761298 $$
1277-
1278-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1279-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1280-
12811299 DELETE FROM dbms_stats._relation_stats_locked
12821300 WHERE relid = $1
12831301 RETURNING relid::regclass
@@ -1289,10 +1307,6 @@ CREATE FUNCTION dbms_stats.unlock_table_stats(
12891307 tablename text
12901308 ) RETURNS SETOF regclass AS
12911309 $$
1292-
1293-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1294-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1295-
12961310 DELETE FROM dbms_stats._relation_stats_locked
12971311 WHERE relid = dbms_stats.relname($1, $2)::regclass
12981312 RETURNING relid::regclass
@@ -1313,12 +1327,14 @@ BEGIN
13131327 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
13141328 END IF;
13151329
1316- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1317- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1330+ /* Lock the locked table stats */
1331+ PERFORM * from dbms_stats.relation_stats_locked r
1332+ WHERE r.relid = $1 FOR SHARE;
1333+
1334+ DELETE FROM dbms_stats._column_stats_locked
1335+ WHERE starelid = $1
1336+ AND staattnum = set_attnum;
13181337
1319- DELETE FROM dbms_stats._column_stats_locked
1320- WHERE starelid = $1
1321- AND staattnum = set_attnum;
13221338 RETURN QUERY
13231339 SELECT $1;
13241340 END;
@@ -1341,12 +1357,14 @@ BEGIN
13411357 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
13421358 END IF;
13431359
1344- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1345- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1360+ /* Lock the locked table stats */
1361+ PERFORM * from dbms_stats.relation_stats_locked r
1362+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
1363+
1364+ DELETE FROM dbms_stats._column_stats_locked
1365+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
1366+ AND staattnum = set_attnum;
13461367
1347- DELETE FROM dbms_stats._column_stats_locked
1348- WHERE starelid = dbms_stats.relname($1, $2)::regclass
1349- AND staattnum = set_attnum;
13501368 RETURN QUERY
13511369 SELECT dbms_stats.relname($1, $2)::regclass;
13521370 END;
@@ -1442,11 +1460,8 @@ BEGIN
14421460 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
14431461 END IF;
14441462
1445- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
1446- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1447- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1448-
1449- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
1463+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
1464+ WHERE id = $1 FOR UPDATE) THEN
14501465 RAISE EXCEPTION 'backup id % not found', $1;
14511466 END IF;
14521467 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -1461,7 +1476,7 @@ BEGIN
14611476 FOR todelete IN
14621477 SELECT * FROM dbms_stats.backup_history
14631478 WHERE id <= $1
1464- ORDER BY id
1479+ ORDER BY id FOR UPDATE
14651480 LOOP
14661481 DELETE FROM dbms_stats.backup_history
14671482 WHERE id = todelete.id;
@@ -1483,9 +1498,6 @@ DECLARE
14831498 clean_inherit bool;
14841499 clean_rel_col text;
14851500 BEGIN
1486- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1487- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1488-
14891501 -- We don't have to check that table-level dummy statistics of the table
14901502 -- exists here, because the foreign key constraints defined on column-level
14911503 -- dummy static table ensures that.
@@ -1500,6 +1512,7 @@ BEGIN
15001512 WHERE a.attrelid = v.starelid
15011513 AND a.attnum = v.staattnum
15021514 AND a.attisdropped = false
1515+ FOR UPDATE
15031516 )
15041517 LOOP
15051518 DELETE FROM dbms_stats._column_stats_locked
--- a/ext_scripts/pg_dbms_stats--1.3.3-9.3.sql
+++ b/ext_scripts/pg_dbms_stats--1.3.3-9.3.sql
@@ -336,6 +336,10 @@ CREATE FUNCTION dbms_stats.backup(
336336 attnum int2
337337 ) RETURNS int8 AS
338338 $$
339+/* Lock the backup id */
340+SELECT * from dbms_stats.backup_history
341+ WHERE id = $1 FOR UPDATE;
342+
339343 INSERT INTO dbms_stats.relation_stats_backup
340344 SELECT $1, v.relid, v.relname, v.relpages, v.reltuples, v.relallvisible,
341345 v.curpages, v.last_analyze, v.last_autoanalyze
@@ -380,7 +384,7 @@ BEGIN
380384 END IF;
381385 IF $1 IS NOT NULL THEN
382386 SELECT relkind INTO backup_relkind
383- FROM pg_catalog.pg_class WHERE oid = $1;
387+ FROM pg_catalog.pg_class WHERE oid = $1 FOR SHARE;
384388 IF NOT FOUND THEN
385389 RAISE EXCEPTION 'relation "%" not found', $1;
386390 END IF;
@@ -394,7 +398,7 @@ BEGIN
394398 END IF;
395399 IF $2 IS NOT NULL THEN
396400 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
397- WHERE a.attrelid = $1 AND a.attname = $2;
401+ WHERE a.attrelid = $1 AND a.attname = $2 FOR SHARE;
398402 IF set_attnum IS NULL THEN
399403 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
400404 END IF;
@@ -433,7 +437,8 @@ $$
433437 DECLARE
434438 backup_id int8;
435439 BEGIN
436- IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
440+ IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1 FOR SHARE)
441+ THEN
437442 RAISE EXCEPTION 'schema "%" not found', $1;
438443 END IF;
439444 IF dbms_stats.is_system_schema($1) THEN
@@ -523,15 +528,18 @@ BEGIN
523528 IF $2 IS NULL AND $3 IS NOT NULL THEN
524529 RAISE EXCEPTION 'relation required';
525530 END IF;
526- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
531+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
532+ WHERE id <= $1 FOR SHARE) THEN
527533 RAISE EXCEPTION 'backup id % not found', $1;
528534 END IF;
529535 IF $2 IS NOT NULL THEN
530- IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
536+ IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class
537+ WHERE oid = $2 FOR SHARE) THEN
531538 RAISE EXCEPTION 'relation "%" not found', $2;
532539 END IF;
540+ -- Grabbing all backups for the relation which is not used in restore.
533541 IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
534- WHERE b.id <= $1 AND b.relid = $2) THEN
542+ WHERE b.id <= $1 AND b.relid = $2 FOR SHARE) THEN
535543 RAISE EXCEPTION 'statistics of relation "%" not found in any backups before backup id = %', $2, $1;
536544 END IF;
537545 IF $3 IS NOT NULL THEN
@@ -544,21 +552,25 @@ BEGIN
544552 RAISE EXCEPTION 'statistics of column "%" of relation "%" are not found in any backups before',$3, $2, $1;
545553 END IF;
546554 END IF;
555+ PERFORM * FROM dbms_stats._relation_stats_locked r
556+ WHERE r.relid = $2 FOR UPDATE;
557+ ELSE
558+ /* Lock the whole relation stats if relation is not specified.*/
559+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
547560 END IF;
548561
549- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
550- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
551-
552562 FOR restore_id, restore_relid IN
553- SELECT max(b.id), c.oid
554- FROM pg_class c, dbms_stats.relation_stats_backup b
555- WHERE (c.oid = $2 OR $2 IS NULL)
556- AND c.oid = b.relid
557- AND dbms_stats.is_target_relkind(c.relkind)
558- AND NOT dbms_stats.is_system_catalog(c.oid)
559- AND b.id <= $1
560- GROUP BY c.oid
561- ORDER BY c.oid::regclass::text
563+ SELECT max(id), coid FROM
564+ (SELECT b.id as id, c.oid as coid
565+ FROM pg_class c, dbms_stats.relation_stats_backup b
566+ WHERE (c.oid = $2 OR $2 IS NULL)
567+ AND c.oid = b.relid
568+ AND dbms_stats.is_target_relkind(c.relkind)
569+ AND NOT dbms_stats.is_system_catalog(c.oid)
570+ AND b.id <= $1
571+ FOR SHARE) t
572+ GROUP BY coid
573+ ORDER BY coid::regclass::text
562574 LOOP
563575 UPDATE dbms_stats._relation_stats_locked r
564576 SET relid = b.relid,
@@ -635,6 +647,9 @@ BEGIN
635647 AND staattnum = restore_attnum;
636648 END IF;
637649 END LOOP;
650+EXCEPTION
651+ WHEN unique_violation THEN
652+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
638653 END;
639654 $$
640655 LANGUAGE plpgsql;
@@ -644,15 +659,16 @@ CREATE FUNCTION dbms_stats.restore_database_stats(
644659 ) RETURNS SETOF regclass AS
645660 $$
646661 SELECT dbms_stats.restore(m.id, m.relid)
647- FROM (SELECT max(r.id) AS id, r.relid
648- FROM pg_class c, dbms_stats.relation_stats_backup r,
649- dbms_stats.backup_history b
650- WHERE c.oid = r.relid
651- AND r.id = b.id
652- AND b.time <= $1
653- GROUP BY r.relid
654- ORDER BY r.relid
655- ) m;
662+ FROM (SELECT max(id) AS id, relid
663+ FROM (SELECT r.id, r.relid
664+ FROM pg_class c, dbms_stats.relation_stats_backup r,
665+ dbms_stats.backup_history b
666+ WHERE c.oid = r.relid
667+ AND r.id = b.id
668+ AND b.time <= $1
669+ FOR SHARE) t1
670+ GROUP BY t1.relid
671+ ORDER BY t1.relid) m;
656672 $$
657673 LANGUAGE sql STRICT;
658674
@@ -671,18 +687,19 @@ BEGIN
671687
672688 RETURN QUERY
673689 SELECT dbms_stats.restore(m.id, m.relid)
674- FROM (SELECT max(r.id) AS id, r.relid
675- FROM pg_class c, pg_namespace n,
676- dbms_stats.relation_stats_backup r,
677- dbms_stats.backup_history b
678- WHERE c.oid = r.relid
679- AND c.relnamespace = n.oid
680- AND n.nspname = $1
681- AND r.id = b.id
682- AND b.time <= $2
683- GROUP BY r.relid
684- ORDER BY r.relid
685- ) m;
690+ FROM (SELECT max(id) AS id, relid
691+ FROM (SELECT r.id, r.relid
692+ FROM pg_class c, pg_namespace n,
693+ dbms_stats.relation_stats_backup r,
694+ dbms_stats.backup_history b
695+ WHERE c.oid = r.relid
696+ AND c.relnamespace = n.oid
697+ AND n.nspname = $1
698+ AND r.id = b.id
699+ AND b.time <= $2
700+ FOR SHARE) t1
701+ GROUP BY t1.relid
702+ ORDER BY t1.relid) m;
686703 END;
687704 $$
688705 LANGUAGE plpgsql STRICT;
@@ -745,8 +762,12 @@ BEGIN
745762 RAISE EXCEPTION 'backup id % not found', $1;
746763 END IF;
747764
748- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
749- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
765+ /* Lock the backup */
766+ PERFORM * from dbms_stats.relation_stats_backup b
767+ WHERE id = $1 FOR SHARE;
768+
769+ /* Locking only _relation_stats_locked is sufficient */
770+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
750771
751772 FOR restore_relid IN
752773 SELECT b.relid
@@ -861,15 +882,12 @@ BEGIN
861882 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
862883 END IF;
863884
864- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
865- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
866-
867885 /*
868886 * If we don't have per-table statistics, create new one which has NULL for
869887 * every statistic value for column_stats_effective.
870888 */
871889 IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
872- WHERE ru.relid = $1) THEN
890+ WHERE ru.relid = $1 FOR SHARE) THEN
873891 INSERT INTO dbms_stats._relation_stats_locked
874892 SELECT $1, dbms_stats.relname(nspname, relname),
875893 NULL, NULL, NULL, NULL, NULL
@@ -956,6 +974,9 @@ BEGIN
956974 END IF;
957975
958976 RETURN $1;
977+EXCEPTION
978+ WHEN unique_violation THEN
979+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock or restore operation on the same relation.';
959980 END;
960981 $$
961982 LANGUAGE plpgsql;
@@ -982,9 +1003,6 @@ BEGIN
9821003 RAISE EXCEPTION 'locking statistics is not allowed for system catalogs: "%"', $1;
9831004 END IF;
9841005
985- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
986- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
987-
9881006 UPDATE dbms_stats._relation_stats_locked r
9891007 SET relname = dbms_stats.relname(nspname, c.relname),
9901008 relpages = v.relpages,
@@ -1092,6 +1110,9 @@ BEGIN
10921110 END LOOP;
10931111
10941112 RETURN $1;
1113+EXCEPTION
1114+ WHEN unique_violation THEN
1115+ RAISE EXCEPTION 'This operation is canceled by simultaneous lock operation on the same relation.';
10951116 END;
10961117 $$
10971118 LANGUAGE plpgsql;
@@ -1185,15 +1206,19 @@ BEGIN
11851206 IF $1 IS NULL AND $2 IS NOT NULL THEN
11861207 RAISE EXCEPTION 'relation required';
11871208 END IF;
1209+
1210+ /*
1211+ * Lock the target relation to prevent conflicting with stats lock/restore
1212+ */
1213+ PERFORM * FROM dbms_stats._relation_stats_locked ru
1214+ WHERE (ru.relid = $1 OR $1 IS NULL) FOR UPDATE;
1215+
11881216 SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
11891217 WHERE a.attrelid = $1 AND a.attname = $2;
11901218 IF $2 IS NOT NULL AND set_attnum IS NULL THEN
11911219 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
11921220 END IF;
11931221
1194- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1195- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1196-
11971222 DELETE FROM dbms_stats._column_stats_locked
11981223 WHERE (starelid = $1 OR $1 IS NULL)
11991224 AND (staattnum = set_attnum OR $2 IS NULL);
@@ -1222,8 +1247,7 @@ $$
12221247 DECLARE
12231248 unlock_id int8;
12241249 BEGIN
1225- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1226- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1250+ LOCK dbms_stats._relation_stats_locked IN EXCLUSIVE MODE;
12271251
12281252 FOR unlock_id IN
12291253 SELECT relid
@@ -1252,16 +1276,14 @@ BEGIN
12521276 RAISE EXCEPTION 'unlocking statistics is not allowed for system schemas: "%"', $1;
12531277 END IF;
12541278
1255- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1256- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1257-
12581279 FOR unlock_id IN
1259- SELECT relid
1260- FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
1280+ SELECT r.relid
1281+ FROM dbms_stats._relation_stats_locked r, pg_class c, pg_namespace n
12611282 WHERE relid = c.oid
12621283 AND c.relnamespace = n.oid
12631284 AND n.nspname = $1
12641285 ORDER BY relid
1286+ FOR UPDATE
12651287 LOOP
12661288 DELETE FROM dbms_stats._relation_stats_locked
12671289 WHERE relid = unlock_id;
@@ -1274,10 +1296,6 @@ LANGUAGE plpgsql STRICT;
12741296 CREATE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
12751297 RETURNS SETOF regclass AS
12761298 $$
1277-
1278-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1279-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1280-
12811299 DELETE FROM dbms_stats._relation_stats_locked
12821300 WHERE relid = $1
12831301 RETURNING relid::regclass
@@ -1289,10 +1307,6 @@ CREATE FUNCTION dbms_stats.unlock_table_stats(
12891307 tablename text
12901308 ) RETURNS SETOF regclass AS
12911309 $$
1292-
1293-LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1294-LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1295-
12961310 DELETE FROM dbms_stats._relation_stats_locked
12971311 WHERE relid = dbms_stats.relname($1, $2)::regclass
12981312 RETURNING relid::regclass
@@ -1313,12 +1327,14 @@ BEGIN
13131327 RAISE EXCEPTION 'column "%" not found in relation "%"', $2, $1;
13141328 END IF;
13151329
1316- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1317- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1330+ /* Lock the locked table stats */
1331+ PERFORM * from dbms_stats.relation_stats_locked r
1332+ WHERE r.relid = $1 FOR SHARE;
1333+
1334+ DELETE FROM dbms_stats._column_stats_locked
1335+ WHERE starelid = $1
1336+ AND staattnum = set_attnum;
13181337
1319- DELETE FROM dbms_stats._column_stats_locked
1320- WHERE starelid = $1
1321- AND staattnum = set_attnum;
13221338 RETURN QUERY
13231339 SELECT $1;
13241340 END;
@@ -1341,12 +1357,14 @@ BEGIN
13411357 RAISE EXCEPTION 'column "%" not found in relation "%.%"', $3, $1, $2;
13421358 END IF;
13431359
1344- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1345- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1360+ /* Lock the locked table stats */
1361+ PERFORM * from dbms_stats.relation_stats_locked r
1362+ WHERE relid = dbms_stats.relname($1, $2)::regclass FOR SHARE;
1363+
1364+ DELETE FROM dbms_stats._column_stats_locked
1365+ WHERE starelid = dbms_stats.relname($1, $2)::regclass
1366+ AND staattnum = set_attnum;
13461367
1347- DELETE FROM dbms_stats._column_stats_locked
1348- WHERE starelid = dbms_stats.relname($1, $2)::regclass
1349- AND staattnum = set_attnum;
13501368 RETURN QUERY
13511369 SELECT dbms_stats.relname($1, $2)::regclass;
13521370 END;
@@ -1441,11 +1459,8 @@ BEGIN
14411459 RAISE EXCEPTION 'NULL is not allowed as the second parameter';
14421460 END IF;
14431461
1444- LOCK dbms_stats.backup_history IN SHARE UPDATE EXCLUSIVE MODE;
1445- LOCK dbms_stats.relation_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1446- LOCK dbms_stats.column_stats_backup IN SHARE UPDATE EXCLUSIVE MODE;
1447-
1448- IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
1462+ IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history
1463+ WHERE id = $1 FOR UPDATE) THEN
14491464 RAISE EXCEPTION 'backup id % not found', $1;
14501465 END IF;
14511466 IF NOT $2 AND NOT EXISTS(SELECT *
@@ -1460,7 +1475,7 @@ BEGIN
14601475 FOR todelete IN
14611476 SELECT * FROM dbms_stats.backup_history
14621477 WHERE id <= $1
1463- ORDER BY id
1478+ ORDER BY id FOR UPDATE
14641479 LOOP
14651480 DELETE FROM dbms_stats.backup_history
14661481 WHERE id = todelete.id;
@@ -1481,9 +1496,6 @@ DECLARE
14811496 clean_inherit bool;
14821497 clean_rel_col text;
14831498 BEGIN
1484- LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1485- LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1486-
14871499 -- We don't have to check that table-level dummy statistics of the table
14881500 -- exists here, because the foreign key constraints defined on column-level
14891501 -- dummy static table ensures that.
@@ -1498,6 +1510,7 @@ BEGIN
14981510 WHERE a.attrelid = v.starelid
14991511 AND a.attnum = v.staattnum
15001512 AND a.attisdropped = false
1513+ FOR UPDATE
15011514 )
15021515 LOOP
15031516 DELETE FROM dbms_stats._column_stats_locked