OSDN Git Service

Change the name of export script samples
[pgdbmsstats/pg_dbms_stats.git] / pg_dbms_stats--1.3.2-9.2.sql
1 /* pg_dbms_stats/pg_dbms_stats--1.3.2.sql */
2
3 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
4 \echo Use "CREATE EXTENSION pg_dbms_stats" to load this file. \quit
5
6 -- define alias of anyarray type because parser does not allow to use
7 -- anyarray in type definitions.
8 --
9 CREATE FUNCTION dbms_stats.anyarray_in(cstring) RETURNS dbms_stats.anyarray
10     AS 'anyarray_in' LANGUAGE internal STRICT IMMUTABLE;
11 CREATE FUNCTION dbms_stats.anyarray_out(dbms_stats.anyarray) RETURNS cstring
12     AS 'anyarray_out' LANGUAGE internal STRICT IMMUTABLE;
13 CREATE FUNCTION dbms_stats.anyarray_recv(internal) RETURNS dbms_stats.anyarray
14     AS 'MODULE_PATHNAME', 'dbms_stats_array_recv' LANGUAGE C STRICT IMMUTABLE;
15 CREATE FUNCTION dbms_stats.anyarray_send(dbms_stats.anyarray) RETURNS bytea
16     AS 'anyarray_send' LANGUAGE internal STRICT IMMUTABLE;
17 CREATE TYPE dbms_stats.anyarray (
18     INPUT = dbms_stats.anyarray_in,
19     OUTPUT = dbms_stats.anyarray_out,
20     RECEIVE = dbms_stats.anyarray_recv,
21     SEND = dbms_stats.anyarray_send,
22     INTERNALLENGTH = VARIABLE,
23     ALIGNMENT = double,
24     STORAGE = extended,
25     CATEGORY = 'P'
26 );
27
28 --
29 -- User defined stats tables
30 --
31
32 CREATE TABLE dbms_stats._relation_stats_locked (
33     relid            oid    NOT NULL,
34     relname          text   NOT NULL,
35     relpages         int4,
36     reltuples        float4,
37     relallvisible    int4,
38     curpages         int4,
39     last_analyze     timestamp with time zone,
40     last_autoanalyze timestamp with time zone,
41     PRIMARY KEY (relid)
42 );
43
44 CREATE TABLE dbms_stats._column_stats_locked (
45     starelid    oid    NOT NULL,
46     staattnum   int2   NOT NULL,
47     stainherit  bool   NOT NULL,
48     stanullfrac float4,
49     stawidth    int4,
50     stadistinct float4,
51     stakind1    int2,
52     stakind2    int2,
53     stakind3    int2,
54     stakind4    int2,
55     stakind5    int2,
56     staop1      oid,
57     staop2      oid,
58     staop3      oid,
59     staop4      oid,
60     staop5      oid,
61     stanumbers1 float4[],
62     stanumbers2 float4[],
63     stanumbers3 float4[],
64     stanumbers4 float4[],
65     stanumbers5 float4[],
66     stavalues1  dbms_stats.anyarray,
67     stavalues2  dbms_stats.anyarray,
68     stavalues3  dbms_stats.anyarray,
69     stavalues4  dbms_stats.anyarray,
70     stavalues5  dbms_stats.anyarray,
71     PRIMARY KEY (starelid, staattnum, stainherit),
72     FOREIGN KEY (starelid) REFERENCES dbms_stats._relation_stats_locked (relid) ON DELETE CASCADE
73 );
74
75 --
76 -- Statistics backup tables
77 --
78
79 CREATE TABLE dbms_stats.backup_history (
80     id      serial8 PRIMARY KEY,
81     time    timestamp with time zone NOT NULL,
82     unit    char(1) NOT NULL,
83     comment text
84 );
85
86 CREATE TABLE dbms_stats.relation_stats_backup (
87     id               int8   NOT NULL,
88     relid            oid    NOT NULL,
89     relname          text   NOT NULL,
90     relpages         int4   NOT NULL,
91     reltuples        float4 NOT NULL,
92     relallvisible    int4   NOT NULL,
93     curpages         int4   NOT NULL,
94     last_analyze     timestamp with time zone,
95     last_autoanalyze timestamp with time zone,
96     PRIMARY KEY (id, relid),
97     FOREIGN KEY (id) REFERENCES dbms_stats.backup_history (id) ON DELETE CASCADE
98 );
99
100 CREATE TABLE dbms_stats.column_stats_backup (
101     id          int8   NOT NULL,
102     statypid    oid    NOT NULL,
103     starelid    oid    NOT NULL,
104     staattnum   int2   NOT NULL,
105     stainherit  bool   NOT NULL,
106     stanullfrac float4 NOT NULL,
107     stawidth    int4   NOT NULL,
108     stadistinct float4 NOT NULL,
109     stakind1    int2   NOT NULL,
110     stakind2    int2   NOT NULL,
111     stakind3    int2   NOT NULL,
112     stakind4    int2   NOT NULL,
113     stakind5    int2   NOT NULL,
114     staop1      oid    NOT NULL,
115     staop2      oid    NOT NULL,
116     staop3      oid    NOT NULL,
117     staop4      oid    NOT NULL,
118     staop5      oid    NOT NULL,
119     stanumbers1 float4[],
120     stanumbers2 float4[],
121     stanumbers3 float4[],
122     stanumbers4 float4[],
123     stanumbers5 float4[],
124     stavalues1  dbms_stats.anyarray,
125     stavalues2  dbms_stats.anyarray,
126     stavalues3  dbms_stats.anyarray,
127     stavalues4  dbms_stats.anyarray,
128     stavalues5  dbms_stats.anyarray,
129     PRIMARY KEY (id, starelid, staattnum, stainherit),
130     FOREIGN KEY (id) REFERENCES dbms_stats.backup_history (id) ON DELETE CASCADE,
131     FOREIGN KEY (id, starelid) REFERENCES dbms_stats.relation_stats_backup (id, relid) ON DELETE CASCADE
132 );
133
134 --
135 -- Functions
136 --
137
138 CREATE FUNCTION dbms_stats.relname(nspname text, relname text)
139 RETURNS text AS
140 $$SELECT quote_ident($1) || '.' || quote_ident($2)$$
141 LANGUAGE sql STABLE STRICT;
142
143 CREATE FUNCTION dbms_stats.is_system_schema(schemaname text)
144 RETURNS boolean AS
145 'MODULE_PATHNAME', 'dbms_stats_is_system_schema'
146 LANGUAGE C IMMUTABLE STRICT;
147
148 CREATE FUNCTION dbms_stats.is_system_catalog(relid regclass)
149 RETURNS boolean AS
150 'MODULE_PATHNAME', 'dbms_stats_is_system_catalog'
151 LANGUAGE C STABLE;
152
153 CREATE FUNCTION dbms_stats.is_target_relkind(relkind "char")
154 RETURNS boolean AS
155 $$SELECT $1 IN ('r', 'i', 'f')$$
156 LANGUAGE sql STABLE;
157
158 CREATE FUNCTION dbms_stats.merge(
159     lhs dbms_stats._column_stats_locked,
160     rhs pg_catalog.pg_statistic
161 ) RETURNS dbms_stats._column_stats_locked AS
162 'MODULE_PATHNAME', 'dbms_stats_merge'
163 LANGUAGE C STABLE;
164
165 --
166 -- Statistics views for internal use
167 --    These views are used to merge authentic stats and dummy stats by hook
168 --    function, so we don't grant SELECT privilege to PUBLIC.
169 --
170
171 CREATE VIEW dbms_stats.relation_stats_effective AS
172     SELECT
173         c.oid AS relid,
174         dbms_stats.relname(nspname, c.relname) AS relname,
175         COALESCE(v.relpages, c.relpages) AS relpages,
176         COALESCE(v.reltuples, c.reltuples) AS reltuples,
177         COALESCE(v.relallvisible, c.relallvisible) AS relallvisible,
178         COALESCE(v.curpages,
179             (pg_relation_size(c.oid) / current_setting('block_size')::int4)::int4)
180             AS curpages,
181         COALESCE(v.last_analyze,
182             pg_catalog.pg_stat_get_last_analyze_time(c.oid))
183             AS last_analyze,
184         COALESCE(v.last_autoanalyze,
185             pg_catalog.pg_stat_get_last_autoanalyze_time(c.oid))
186             AS last_autoanalyze
187       FROM pg_catalog.pg_class c
188       JOIN pg_catalog.pg_namespace n
189         ON c.relnamespace = n.oid
190       LEFT JOIN dbms_stats._relation_stats_locked v
191         ON v.relid = c.oid
192      WHERE dbms_stats.is_target_relkind(c.relkind)
193        AND NOT dbms_stats.is_system_schema(nspname);
194
195 CREATE VIEW dbms_stats.column_stats_effective AS
196     SELECT * FROM (
197         SELECT (dbms_stats.merge(v, s)).*
198           FROM pg_catalog.pg_statistic s
199           FULL JOIN dbms_stats._column_stats_locked v
200          USING (starelid, staattnum, stainherit)
201          WHERE NOT dbms_stats.is_system_catalog(starelid)
202                    AND EXISTS (
203                         SELECT NULL
204                           FROM pg_attribute a
205                          WHERE a.attrelid = starelid
206                            AND a.attnum = staattnum
207                            AND a.attisdropped  = false
208                         )
209         ) m
210      WHERE starelid IS NOT NULL;
211
212 --
213 -- Statistics views for user use (including non-superusers)
214 --    These views allow users to see dummy statistics about tables which the
215 --    user has SELECT privilege.
216 --
217
218 CREATE VIEW dbms_stats.relation_stats_locked
219     AS SELECT *
220          FROM dbms_stats._relation_stats_locked;
221
222 GRANT SELECT ON dbms_stats.relation_stats_locked TO PUBLIC;
223
224 CREATE VIEW dbms_stats.column_stats_locked
225     AS SELECT *
226          FROM dbms_stats._column_stats_locked
227         WHERE has_column_privilege(starelid, staattnum, 'SELECT');
228
229 GRANT SELECT ON dbms_stats.column_stats_locked TO PUBLIC;
230
231 --
232 -- Note: This view is copied from pg_stats in
233 -- src/backend/catalog/system_views.sql in core source tree of version
234 -- 9.2, and customized for pg_dbms_stats.  Changes from orignal one are:
235 --   - rename from pg_stats to dbms_stats.stats by a view name.
236 --   - changed the table name from pg_statistic to dbms_stats.column_stats_effective.
237 --
238 CREATE VIEW dbms_stats.stats AS
239     SELECT
240         nspname AS schemaname,
241         relname AS tablename,
242         attname AS attname,
243         stainherit AS inherited,
244         stanullfrac AS null_frac,
245         stawidth AS avg_width,
246         stadistinct AS n_distinct,
247         CASE
248             WHEN stakind1 = 1 THEN stavalues1
249             WHEN stakind2 = 1 THEN stavalues2
250             WHEN stakind3 = 1 THEN stavalues3
251             WHEN stakind4 = 1 THEN stavalues4
252             WHEN stakind5 = 1 THEN stavalues5
253         END AS most_common_vals,
254         CASE
255             WHEN stakind1 = 1 THEN stanumbers1
256             WHEN stakind2 = 1 THEN stanumbers2
257             WHEN stakind3 = 1 THEN stanumbers3
258             WHEN stakind4 = 1 THEN stanumbers4
259             WHEN stakind5 = 1 THEN stanumbers5
260         END AS most_common_freqs,
261         CASE
262             WHEN stakind1 = 2 THEN stavalues1
263             WHEN stakind2 = 2 THEN stavalues2
264             WHEN stakind3 = 2 THEN stavalues3
265             WHEN stakind4 = 2 THEN stavalues4
266             WHEN stakind5 = 2 THEN stavalues5
267         END AS histogram_bounds,
268         CASE
269             WHEN stakind1 = 3 THEN stanumbers1[1]
270             WHEN stakind2 = 3 THEN stanumbers2[1]
271             WHEN stakind3 = 3 THEN stanumbers3[1]
272             WHEN stakind4 = 3 THEN stanumbers4[1]
273             WHEN stakind5 = 3 THEN stanumbers5[1]
274         END AS correlation,
275         CASE
276             WHEN stakind1 = 4 THEN stavalues1
277             WHEN stakind2 = 4 THEN stavalues2
278             WHEN stakind3 = 4 THEN stavalues3
279             WHEN stakind4 = 4 THEN stavalues4
280             WHEN stakind5 = 4 THEN stavalues5
281         END AS most_common_elems,
282         CASE
283             WHEN stakind1 = 4 THEN stanumbers1
284             WHEN stakind2 = 4 THEN stanumbers2
285             WHEN stakind3 = 4 THEN stanumbers3
286             WHEN stakind4 = 4 THEN stanumbers4
287             WHEN stakind5 = 4 THEN stanumbers5
288         END AS most_common_elem_freqs,
289         CASE
290             WHEN stakind1 = 5 THEN stanumbers1
291             WHEN stakind2 = 5 THEN stanumbers2
292             WHEN stakind3 = 5 THEN stanumbers3
293             WHEN stakind4 = 5 THEN stanumbers4
294             WHEN stakind5 = 5 THEN stanumbers5
295         END AS elem_count_histogram
296     FROM dbms_stats.column_stats_effective s JOIN pg_class c ON (c.oid = s.starelid)
297          JOIN pg_attribute a ON (c.oid = attrelid AND attnum = s.staattnum)
298          LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
299     WHERE NOT attisdropped AND has_column_privilege(c.oid, a.attnum, 'select');
300
301 GRANT SELECT ON dbms_stats.stats TO PUBLIC;
302
303 --
304 -- Utility functions
305 --
306
307 CREATE FUNCTION dbms_stats.invalidate_relation_cache()
308     RETURNS trigger AS
309     'MODULE_PATHNAME', 'dbms_stats_invalidate_relation_cache'
310     LANGUAGE C;
311
312 -- Invalidate cached plans when dbms_stats._relation_stats_locked is modified.
313 CREATE TRIGGER invalidate_relation_cache
314     BEFORE INSERT OR DELETE OR UPDATE
315     ON dbms_stats._relation_stats_locked
316    FOR EACH ROW EXECUTE PROCEDURE dbms_stats.invalidate_relation_cache();
317
318 CREATE FUNCTION dbms_stats.invalidate_column_cache()
319     RETURNS trigger AS
320     'MODULE_PATHNAME', 'dbms_stats_invalidate_column_cache'
321     LANGUAGE C;
322
323 -- Invalidate cached plans when dbms_stats._column_stats_locked is modified.
324 CREATE TRIGGER invalidate_column_cache
325     BEFORE INSERT OR DELETE OR UPDATE
326     ON dbms_stats._column_stats_locked
327     FOR EACH ROW EXECUTE PROCEDURE dbms_stats.invalidate_column_cache();
328
329 --
330 -- BACKUP_STATS: Statistic backup functions
331 --
332
333 CREATE FUNCTION dbms_stats.backup(
334     backup_id int8,
335     relid regclass,
336     attnum int2
337 ) RETURNS int8 AS
338 $$
339 INSERT INTO dbms_stats.relation_stats_backup
340     SELECT $1, v.relid, v.relname, v.relpages, v.reltuples, v.relallvisible,
341            v.curpages, v.last_analyze, v.last_autoanalyze
342       FROM pg_catalog.pg_class c,
343            dbms_stats.relation_stats_effective v
344      WHERE c.oid = v.relid
345        AND dbms_stats.is_target_relkind(relkind)
346        AND NOT dbms_stats.is_system_catalog(v.relid)
347        AND (v.relid = $2 OR $2 IS NULL);
348
349 INSERT INTO dbms_stats.column_stats_backup
350     SELECT $1, atttypid, s.*
351       FROM pg_catalog.pg_class c,
352            dbms_stats.column_stats_effective s,
353            pg_catalog.pg_attribute a
354      WHERE c.oid = starelid
355        AND starelid = attrelid
356        AND staattnum = attnum
357        AND dbms_stats.is_target_relkind(relkind)
358        AND NOT dbms_stats.is_system_catalog(c.oid)
359        AND ($2 IS NULL OR starelid = $2)
360        AND ($3 IS NULL OR staattnum = $3);
361
362 SELECT $1;
363 $$
364 LANGUAGE sql;
365
366 CREATE FUNCTION dbms_stats.backup(
367     relid regclass DEFAULT NULL,
368     attname text DEFAULT NULL,
369     comment text DEFAULT NULL
370 ) RETURNS int8 AS
371 $$
372 DECLARE
373     backup_id       int8;
374     backup_relkind  "char";
375     set_attnum      int2;
376     unit_type       char;
377 BEGIN
378     IF $1 IS NULL AND $2 IS NOT NULL THEN
379         RAISE EXCEPTION 'relation is required';
380     END IF;
381     IF $1 IS NOT NULL THEN
382         SELECT relkind INTO backup_relkind
383           FROM pg_catalog.pg_class WHERE oid = $1;
384         IF NOT FOUND THEN
385             RAISE EXCEPTION 'relation "%" does not exist', $1;
386         END IF;
387         IF NOT dbms_stats.is_target_relkind(backup_relkind) THEN
388             RAISE EXCEPTION 'can not backup statistics of "%" with relkind "%"',
389                                 $1, backup_relkind
390                                 USING HINT = 'only tables and indexes are supported';
391         END IF;
392         IF dbms_stats.is_system_catalog($1) THEN
393             RAISE EXCEPTION 'can not backup statistics of system catalog "%"', $1;
394         END IF;
395         IF $2 IS NOT NULL THEN
396             SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
397              WHERE a.attrelid = $1 AND a.attname = $2;
398             IF set_attnum IS NULL THEN
399                 RAISE EXCEPTION 'column "%" of "%" does not exist', $2, $1;
400             END IF;
401             IF NOT EXISTS(SELECT * FROM dbms_stats.column_stats_effective WHERE starelid = $1 AND staattnum = set_attnum) THEN
402                 RAISE EXCEPTION 'no statistic for column "%" of "%" exists', $2, $1;
403             END IF;
404             unit_type = 'c';
405         ELSE
406             unit_type = 't';
407         END IF;
408     ELSE
409         unit_type = 'd';
410     END IF;
411
412     INSERT INTO dbms_stats.backup_history(time, unit, comment)
413         VALUES (current_timestamp, unit_type, $3)
414         RETURNING dbms_stats.backup(id, $1, set_attnum) INTO backup_id;
415     RETURN backup_id;
416 END;
417 $$
418 LANGUAGE plpgsql;
419
420 CREATE FUNCTION dbms_stats.backup_database_stats(
421     comment text
422 ) RETURNS int8 AS
423 $$
424 SELECT dbms_stats.backup(NULL, NULL, $1)
425 $$
426 LANGUAGE sql;
427
428 CREATE FUNCTION dbms_stats.backup_schema_stats(
429     schemaname text,
430     comment text
431 ) RETURNS int8 AS
432 $$
433 DECLARE
434     backup_id       int8;
435 BEGIN
436     IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
437         RAISE EXCEPTION 'schema "%" does not exist', $1;
438     END IF;
439     IF dbms_stats.is_system_schema($1) THEN
440         RAISE EXCEPTION 'can not backup statistics of relation in system schema "%"', $1;
441     END IF;
442
443     INSERT INTO dbms_stats.backup_history(time, unit, comment)
444         VALUES (current_timestamp, 's', comment)
445         RETURNING id INTO backup_id;
446
447     PERFORM dbms_stats.backup(backup_id, cn.oid, NULL)
448       FROM (SELECT c.oid
449               FROM pg_catalog.pg_class c,
450                    pg_catalog.pg_namespace n
451              WHERE n.nspname = schemaname
452                AND c.relnamespace = n.oid
453                AND dbms_stats.is_target_relkind(c.relkind)
454              ORDER BY c.oid
455            ) cn;
456
457     RETURN backup_id;
458 END;
459 $$
460 LANGUAGE plpgsql;
461
462 CREATE FUNCTION dbms_stats.backup_table_stats(
463     relid regclass,
464     comment text
465 ) RETURNS int8 AS
466 $$
467 SELECT dbms_stats.backup($1, NULL, $2)
468 $$
469 LANGUAGE sql;
470
471 CREATE FUNCTION dbms_stats.backup_table_stats(
472     schemaname text,
473     tablename text,
474     comment text
475 ) RETURNS int8 AS
476 $$
477 SELECT dbms_stats.backup(dbms_stats.relname($1, $2)::regclass, NULL, $3)
478 $$
479 LANGUAGE sql;
480
481 CREATE FUNCTION dbms_stats.backup_column_stats(
482     relid regclass,
483     attname text,
484     comment text
485 ) RETURNS int8 AS
486 $$
487 SELECT dbms_stats.backup($1, $2, $3)
488 $$
489 LANGUAGE sql;
490
491 CREATE FUNCTION dbms_stats.backup_column_stats(
492     schemaname text,
493     tablename text,
494     attname text,
495     comment text
496 ) RETURNS int8 AS
497 $$
498 SELECT dbms_stats.backup(dbms_stats.relname($1, $2)::regclass, $3, $4)
499 $$
500 LANGUAGE sql;
501
502 --
503 -- RESTORE_STATS: Statistic restore functions
504 --
505 CREATE FUNCTION dbms_stats.restore(
506     backup_id int8,
507     relid regclass DEFAULT NULL,
508     attname text DEFAULT NULL
509 ) RETURNS SETOF regclass AS
510 $$
511 DECLARE
512     restore_id      int8;
513     restore_relid   regclass;
514     restore_attnum  int2;
515     set_attnum      int2;
516     restore_attname text;
517     restore_type    regtype;
518     cur_type        regtype;
519 BEGIN
520     IF $1 IS NULL THEN
521         RAISE EXCEPTION 'backup id is required';
522     END IF;
523     IF $2 IS NULL AND $3 IS NOT NULL THEN
524         RAISE EXCEPTION 'relation is required';
525     END IF;
526     IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id <= $1) THEN
527         RAISE EXCEPTION 'backup id % does not exist', $1;
528     END IF;
529     IF $2 IS NOT NULL THEN
530         IF NOT EXISTS(SELECT * FROM pg_catalog.pg_class WHERE oid = $2) THEN
531             RAISE EXCEPTION 'relation "%" does not exist', $2;
532         END IF;
533         IF NOT EXISTS(SELECT * FROM dbms_stats.relation_stats_backup b
534                        WHERE b.id <= $1 AND b.relid = $2) THEN
535             RAISE EXCEPTION 'relation "%" does not exist in previous backup', $2;
536         END IF;
537         IF $3 IS NOT NULL THEN
538             SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
539              WHERE a.attrelid = $2 AND a.attname = $3;
540             IF set_attnum IS NULL THEN
541                                 RAISE EXCEPTION 'column "%" of "%" does not exist', $3, $2;
542             END IF;
543             IF NOT EXISTS(SELECT * FROM dbms_stats.column_stats_backup WHERE id <= $1 AND starelid = $2 AND staattnum = set_attnum) THEN
544                 RAISE EXCEPTION 'column "%" of "%" does not exist in previous backup',$3, $2;
545             END IF;
546         END IF;
547     END IF;
548
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
552     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
562     LOOP
563         UPDATE dbms_stats._relation_stats_locked r
564            SET relid = b.relid,
565                relname = b.relname,
566                relpages = b.relpages,
567                reltuples = b.reltuples,
568                relallvisible = b.relallvisible,
569                curpages = b.curpages,
570                last_analyze = b.last_analyze,
571                last_autoanalyze = b.last_autoanalyze
572           FROM dbms_stats.relation_stats_backup b
573          WHERE r.relid = restore_relid
574            AND b.id = restore_id
575            AND b.relid = restore_relid;
576         IF NOT FOUND THEN
577             INSERT INTO dbms_stats._relation_stats_locked
578             SELECT b.relid,
579                    b.relname,
580                    b.relpages,
581                    b.reltuples,
582                    b.relallvisible,
583                    b.curpages,
584                    b.last_analyze,
585                    b.last_autoanalyze
586               FROM dbms_stats.relation_stats_backup b
587              WHERE b.id = restore_id
588                AND b.relid = restore_relid;
589         END IF;
590         RETURN NEXT restore_relid;
591     END LOOP;
592
593     FOR restore_id, restore_relid, restore_attnum, restore_type, cur_type IN
594         SELECT t.id, t.oid, t.attnum, b.statypid, a.atttypid
595           FROM pg_attribute a,
596                dbms_stats.column_stats_backup b,
597                (SELECT max(b.id) AS id, c.oid, a.attnum
598                   FROM pg_class c, pg_attribute a, dbms_stats.column_stats_backup b
599                  WHERE (c.oid = $2 OR $2 IS NULL)
600                    AND c.oid = a.attrelid
601                    AND c.oid = b.starelid
602                    AND (a.attnum = set_attnum OR set_attnum IS NULL)
603                    AND a.attnum = b.staattnum
604                    AND NOT a.attisdropped
605                    AND dbms_stats.is_target_relkind(c.relkind)
606                    AND b.id <= $1
607                  GROUP BY c.oid, a.attnum) t
608          WHERE a.attrelid = t.oid
609            AND a.attnum = t.attnum
610            AND b.id = t.id
611            AND b.starelid = t.oid
612            AND b.staattnum = t.attnum
613     LOOP
614         IF restore_type <> cur_type THEN
615             SELECT a.attname INTO restore_attname
616               FROM pg_catalog.pg_attribute a
617              WHERE a.attrelid = restore_relid
618                AND a.attnum = restore_attnum;
619             RAISE WARNING 'skip "%.%" because of type mismatch: "%" in backup and "%" in database',
620                 restore_relid, restore_attname, restore_type, cur_type;
621         ELSE
622             DELETE FROM dbms_stats._column_stats_locked
623              WHERE starelid = restore_relid
624                AND staattnum = restore_attnum;
625             INSERT INTO dbms_stats._column_stats_locked
626                 SELECT starelid, staattnum, stainherit,
627                        stanullfrac, stawidth, stadistinct,
628                        stakind1, stakind2, stakind3, stakind4, stakind5,
629                        staop1, staop2, staop3, staop4, staop5,
630                        stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,
631                        stavalues1, stavalues2, stavalues3, stavalues4, stavalues5
632                   FROM dbms_stats.column_stats_backup
633                  WHERE id = restore_id
634                    AND starelid = restore_relid
635                    AND staattnum = restore_attnum;
636         END IF;
637     END LOOP;
638 END;
639 $$
640 LANGUAGE plpgsql;
641
642 CREATE FUNCTION dbms_stats.restore_database_stats(
643     as_of_timestamp timestamp with time zone
644 ) RETURNS SETOF regclass AS
645 $$
646 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;
656 $$
657 LANGUAGE sql STRICT;
658
659 CREATE FUNCTION dbms_stats.restore_schema_stats(
660     schemaname text,
661     as_of_timestamp timestamp with time zone
662 ) RETURNS SETOF regclass AS
663 $$
664 BEGIN
665     IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
666         RAISE EXCEPTION 'schema "%" does not exist', $1;
667     END IF;
668     IF dbms_stats.is_system_schema($1) THEN
669                 RAISE EXCEPTION 'can not restore statistics of relation in system schema "%"', $1;
670     END IF;
671
672     RETURN QUERY
673         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;
686 END;
687 $$
688 LANGUAGE plpgsql STRICT;
689
690 CREATE FUNCTION dbms_stats.restore_table_stats(
691     relid regclass,
692     as_of_timestamp timestamp with time zone
693 ) RETURNS SETOF regclass AS
694 $$
695 SELECT dbms_stats.restore(max(id), $1, NULL)
696   FROM dbms_stats.backup_history WHERE time <= $2
697 $$
698 LANGUAGE sql STRICT;
699
700 CREATE FUNCTION dbms_stats.restore_table_stats(
701     schemaname text,
702     tablename text,
703     as_of_timestamp timestamp with time zone
704 ) RETURNS SETOF regclass AS
705 $$
706 SELECT dbms_stats.restore_table_stats(dbms_stats.relname($1, $2)::regclass, $3)
707 $$
708 LANGUAGE sql STRICT;
709
710 CREATE FUNCTION dbms_stats.restore_column_stats(
711     relid regclass,
712     attname text,
713     as_of_timestamp timestamp with time zone
714 ) RETURNS SETOF regclass AS
715 $$
716 SELECT dbms_stats.restore(max(id), $1, $2)
717   FROM dbms_stats.backup_history WHERE time <= $3
718 $$
719 LANGUAGE sql STRICT;
720
721 CREATE FUNCTION dbms_stats.restore_column_stats(
722     schemaname text,
723     tablename text,
724     attname text,
725     as_of_timestamp timestamp with time zone
726 ) RETURNS SETOF regclass AS
727 $$
728 SELECT dbms_stats.restore(max(id), dbms_stats.relname($1, $2)::regclass, $3)
729   FROM dbms_stats.backup_history WHERE time <= $4
730 $$
731 LANGUAGE sql STRICT;
732
733 CREATE FUNCTION dbms_stats.restore_stats(
734     backup_id int8
735 ) RETURNS SETOF regclass AS
736 $$
737 DECLARE
738     restore_relid   regclass;
739     restore_attnum  int2;
740     restore_attname text;
741     restore_type    regtype;
742     cur_type        regtype;
743 BEGIN
744     IF NOT EXISTS(SELECT * FROM dbms_stats.backup_history WHERE id = $1) THEN
745         RAISE EXCEPTION 'backup id % does not exist', $1;
746     END IF;
747
748     LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
749     LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
750
751     FOR restore_relid IN
752         SELECT b.relid
753           FROM pg_class c
754           JOIN dbms_stats.relation_stats_backup b ON (c.oid = b.relid)
755          WHERE b.id = $1
756          ORDER BY c.oid::regclass::text
757     LOOP
758         UPDATE dbms_stats._relation_stats_locked r
759            SET relid = b.relid,
760                relname = b.relname,
761                relpages = b.relpages,
762                reltuples = b.reltuples,
763                relallvisible = b.relallvisible,
764                curpages = b.curpages,
765                last_analyze = b.last_analyze,
766                last_autoanalyze = b.last_autoanalyze
767           FROM dbms_stats.relation_stats_backup b
768          WHERE r.relid = restore_relid
769            AND b.id = $1
770            AND b.relid = restore_relid;
771         IF NOT FOUND THEN
772             INSERT INTO dbms_stats._relation_stats_locked
773             SELECT b.relid,
774                    b.relname,
775                    b.relpages,
776                    b.reltuples,
777                    b.relallvisible,
778                    b.curpages,
779                    b.last_analyze,
780                    b.last_autoanalyze
781               FROM dbms_stats.relation_stats_backup b
782              WHERE b.id = $1
783                AND b.relid = restore_relid;
784         END IF;
785         RETURN NEXT restore_relid;
786     END LOOP;
787
788     FOR restore_relid, restore_attnum, restore_type, cur_type  IN
789         SELECT c.oid, a.attnum, b.statypid, a.atttypid
790           FROM pg_class c
791           JOIN dbms_stats.column_stats_backup b ON (c.oid = b.starelid)
792           JOIN pg_attribute a ON (b.starelid = attrelid
793                               AND b.staattnum = a.attnum)
794          WHERE b.id = $1
795     LOOP
796         IF restore_type <> cur_type THEN
797             SELECT attname INTO restore_attname
798               FROM pg_catalog.pg_attribute
799              WHERE attrelid = restore_relid
800                AND attnum = restore_attnum;
801             RAISE WARNING 'skip "%.%" because of type mismatch: "%" in backup and "%" in database',
802                 restore_relid, restore_attname, restore_type, cur_type;
803         ELSE
804             DELETE FROM dbms_stats._column_stats_locked
805              WHERE starelid = restore_relid
806                AND staattnum = restore_attnum;
807             INSERT INTO dbms_stats._column_stats_locked
808                 SELECT starelid, staattnum, stainherit,
809                        stanullfrac, stawidth, stadistinct,
810                        stakind1, stakind2, stakind3, stakind4, stakind5,
811                        staop1, staop2, staop3, staop4, staop5,
812                        stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,
813                        stavalues1, stavalues2, stavalues3, stavalues4, stavalues5
814                   FROM dbms_stats.column_stats_backup
815                  WHERE id = $1
816                    AND starelid = restore_relid
817                    AND staattnum = restore_attnum;
818         END IF;
819     END LOOP;
820
821 END;
822 $$
823 LANGUAGE plpgsql STRICT;
824
825 --
826 -- LOCK_STATS: Statistic lock functions
827 --
828
829 CREATE FUNCTION dbms_stats.lock(
830     relid regclass,
831     attname text
832 ) RETURNS regclass AS
833 $$
834 DECLARE
835     lock_relkind "char";
836     set_attnum   int2;
837     r            record;
838 BEGIN
839     IF $1 IS NULL THEN
840         RAISE EXCEPTION 'relation is required';
841     END IF;
842     IF $2 IS NULL THEN
843         RETURN dbms_stats.lock($1);
844     END IF;
845     SELECT relkind INTO lock_relkind FROM pg_catalog.pg_class WHERE oid = $1;
846     IF NOT FOUND THEN
847         RAISE EXCEPTION 'relation "%" does not exist', $1;
848     END IF;
849     IF NOT dbms_stats.is_target_relkind(lock_relkind) THEN
850         RAISE EXCEPTION '"%" is not a table nor an index', $1;
851     END IF;
852     IF EXISTS(SELECT * FROM pg_catalog.pg_index WHERE lock_relkind = 'i' AND indexrelid = $1 AND indexprs IS NULL) THEN
853         RAISE EXCEPTION '"%" is not an indexes on expressions', $1;
854     END IF;
855     IF dbms_stats.is_system_catalog($1) THEN
856                 RAISE EXCEPTION 'can not lock statistics of system catalog "%"', $1;
857     END IF;
858     SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
859      WHERE a.attrelid = $1 AND a.attname = $2;
860     IF set_attnum IS NULL THEN
861         RAISE EXCEPTION 'column "%" of "%" does not exist', $2, $1;
862     END IF;
863
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
867         /*
868          * If we don't have per-table statistics, create new one which has NULL for
869          * every statistic column_stats_effective.
870          */
871     IF NOT EXISTS(SELECT * FROM dbms_stats._relation_stats_locked ru
872                    WHERE ru.relid = $1) THEN
873         INSERT INTO dbms_stats._relation_stats_locked
874             SELECT $1, dbms_stats.relname(nspname, relname),
875                    NULL, NULL, NULL, NULL, NULL
876               FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n
877              WHERE c.relnamespace = n.oid
878                AND c.oid = $1;
879     END IF;
880
881         /*
882          * Process for per-column statistics
883          */
884     FOR r IN
885         SELECT stainherit, stanullfrac, stawidth, stadistinct,
886                stakind1, stakind2, stakind3, stakind4, stakind5,
887                staop1, staop2, staop3, staop4, staop5,
888                stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,
889                stavalues1, stavalues2, stavalues3, stavalues4, stavalues5
890           FROM dbms_stats.column_stats_effective
891          WHERE starelid = $1
892            AND staattnum = set_attnum
893     LOOP
894         UPDATE dbms_stats._column_stats_locked c
895            SET stanullfrac = r.stanullfrac,
896                stawidth = r.stawidth,
897                stadistinct = r.stadistinct,
898                stakind1 = r.stakind1,
899                stakind2 = r.stakind2,
900                stakind3 = r.stakind3,
901                stakind4 = r.stakind4,
902                stakind5 = r.stakind5,
903                staop1 = r.staop1,
904                staop2 = r.staop2,
905                staop3 = r.staop3,
906                staop4 = r.staop4,
907                staop5 = r.staop5,
908                stanumbers1 = r.stanumbers1,
909                stanumbers2 = r.stanumbers2,
910                stanumbers3 = r.stanumbers3,
911                stanumbers4 = r.stanumbers4,
912                stanumbers5 = r.stanumbers5,
913                stavalues1 = r.stavalues1,
914                stavalues2 = r.stavalues2,
915                stavalues3 = r.stavalues3,
916                stavalues4 = r.stavalues4,
917                stavalues5 = r.stavalues5
918          WHERE c.starelid = $1
919            AND c.staattnum = set_attnum
920            AND c.stainherit = r.stainherit;
921
922         IF NOT FOUND THEN
923             INSERT INTO dbms_stats._column_stats_locked
924                  VALUES ($1,
925                          set_attnum,
926                          r.stainherit,
927                          r.stanullfrac,
928                          r.stawidth,
929                          r.stadistinct,
930                          r.stakind1,
931                          r.stakind2,
932                          r.stakind3,
933                          r.stakind4,
934                          r.stakind5,
935                          r.staop1,
936                          r.staop2,
937                          r.staop3,
938                          r.staop4,
939                          r.staop5,
940                          r.stanumbers1,
941                          r.stanumbers2,
942                          r.stanumbers3,
943                          r.stanumbers4,
944                          r.stanumbers5,
945                          r.stavalues1,
946                          r.stavalues2,
947                          r.stavalues3,
948                          r.stavalues4,
949                          r.stavalues5);
950         END IF;
951         END LOOP;
952
953                 /* If we don't have statistic at all, raise error. */
954         IF NOT FOUND THEN
955                         RAISE EXCEPTION 'no statistic for column "%" of "%" exists', $2, $1::regclass;
956                 END IF;
957
958     RETURN $1;
959 END;
960 $$
961 LANGUAGE plpgsql;
962
963 CREATE FUNCTION dbms_stats.lock(relid regclass)
964     RETURNS regclass AS
965 $$
966 DECLARE
967     lock_relkind "char";
968     i            record;
969 BEGIN
970     IF $1 IS NULL THEN
971         RAISE EXCEPTION 'relation is required';
972     END IF;
973     SELECT relkind INTO lock_relkind FROM pg_catalog.pg_class WHERE oid = $1;
974     IF NOT FOUND THEN
975         RAISE EXCEPTION 'relation "%" does not exist', $1;
976     END IF;
977     IF NOT dbms_stats.is_target_relkind(lock_relkind) THEN
978         RAISE EXCEPTION 'can not lock statistics of "%" with relkind "%"', $1, lock_relkind
979                         USING HINT = 'only tables and indexes are supported';
980     END IF;
981     IF dbms_stats.is_system_catalog($1) THEN
982                 RAISE EXCEPTION 'can not lock statistics of system catalog "%"', $1;
983     END IF;
984
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
988     UPDATE dbms_stats._relation_stats_locked r
989        SET relname = dbms_stats.relname(nspname, c.relname),
990            relpages = v.relpages,
991            reltuples = v.reltuples,
992            relallvisible = v.relallvisible,
993            curpages = v.curpages,
994            last_analyze = v.last_analyze,
995            last_autoanalyze = v.last_autoanalyze
996       FROM pg_catalog.pg_class c,
997            pg_catalog.pg_namespace n,
998            dbms_stats.relation_stats_effective v
999      WHERE r.relid = $1
1000        AND c.oid = $1
1001        AND c.relnamespace = n.oid
1002        AND v.relid = $1;
1003     IF NOT FOUND THEN
1004         INSERT INTO dbms_stats._relation_stats_locked
1005         SELECT $1, dbms_stats.relname(nspname, c.relname),
1006                v.relpages, v.reltuples, v.relallvisible, v.curpages,
1007                v.last_analyze, v.last_autoanalyze
1008           FROM pg_catalog.pg_class c,
1009                pg_catalog.pg_namespace n,
1010                dbms_stats.relation_stats_effective v
1011          WHERE c.oid = $1
1012            AND c.relnamespace = n.oid
1013            AND v.relid = $1;
1014     END IF;
1015
1016     IF EXISTS(SELECT *
1017                 FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index ind
1018                   ON c.oid = ind.indexrelid
1019                WHERE c.oid = $1
1020                  AND c.relkind = 'i'
1021                  AND ind.indexprs IS NULL) THEN
1022         RETURN $1;
1023     END IF;
1024
1025     FOR i IN
1026         SELECT staattnum, stainherit, stanullfrac,
1027                stawidth, stadistinct,
1028                stakind1, stakind2, stakind3, stakind4, stakind5,
1029                staop1, staop2, staop3, staop4, staop5,
1030                stanumbers1, stanumbers2, stanumbers3, stanumbers4, stanumbers5,
1031                stavalues1, stavalues2, stavalues3, stavalues4, stavalues5
1032           FROM dbms_stats.column_stats_effective
1033          WHERE starelid = $1
1034     LOOP
1035         UPDATE dbms_stats._column_stats_locked c
1036            SET stanullfrac = i.stanullfrac,
1037                stawidth = i.stawidth,
1038                stadistinct = i.stadistinct,
1039                stakind1 = i.stakind1,
1040                stakind2 = i.stakind2,
1041                stakind3 = i.stakind3,
1042                stakind4 = i.stakind4,
1043                stakind5 = i.stakind5,
1044                staop1 = i.staop1,
1045                staop2 = i.staop2,
1046                staop3 = i.staop3,
1047                staop4 = i.staop4,
1048                staop5 = i.staop5,
1049                stanumbers1 = i.stanumbers1,
1050                stanumbers2 = i.stanumbers2,
1051                stanumbers3 = i.stanumbers3,
1052                stanumbers4 = i.stanumbers4,
1053                stanumbers5 = i.stanumbers5,
1054                stavalues1 = i.stavalues1,
1055                stavalues2 = i.stavalues2,
1056                stavalues3 = i.stavalues3,
1057                stavalues4 = i.stavalues4,
1058                stavalues5 = i.stavalues5
1059          WHERE c.starelid = $1
1060            AND c.staattnum = i.staattnum
1061            AND c.stainherit = i.stainherit;
1062
1063         IF NOT FOUND THEN
1064             INSERT INTO dbms_stats._column_stats_locked
1065                  VALUES ($1,
1066                          i.staattnum,
1067                          i.stainherit,
1068                          i.stanullfrac,
1069                          i.stawidth,
1070                          i.stadistinct,
1071                          i.stakind1,
1072                          i.stakind2,
1073                          i.stakind3,
1074                          i.stakind4,
1075                          i.stakind5,
1076                          i.staop1,
1077                          i.staop2,
1078                          i.staop3,
1079                          i.staop4,
1080                          i.staop5,
1081                          i.stanumbers1,
1082                          i.stanumbers2,
1083                          i.stanumbers3,
1084                          i.stanumbers4,
1085                          i.stanumbers5,
1086                          i.stavalues1,
1087                          i.stavalues2,
1088                          i.stavalues3,
1089                          i.stavalues4,
1090                          i.stavalues5);
1091             END IF;
1092         END LOOP;
1093
1094     RETURN $1;
1095 END;
1096 $$
1097 LANGUAGE plpgsql;
1098
1099 CREATE FUNCTION dbms_stats.lock_database_stats()
1100   RETURNS SETOF regclass AS
1101 $$
1102 SELECT dbms_stats.lock(c.oid)
1103   FROM (SELECT oid
1104           FROM pg_catalog.pg_class
1105          WHERE NOT dbms_stats.is_system_catalog(oid)
1106            AND dbms_stats.is_target_relkind(relkind)
1107          ORDER BY pg_class.oid
1108        ) c;
1109 $$
1110 LANGUAGE sql;
1111
1112 CREATE FUNCTION dbms_stats.lock_schema_stats(
1113     schemaname text
1114 ) RETURNS SETOF regclass AS
1115 $$
1116 BEGIN
1117     IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
1118         RAISE EXCEPTION 'schema "%" does not exist', $1;
1119     END IF;
1120     IF dbms_stats.is_system_schema($1) THEN
1121         RAISE EXCEPTION 'can not lock statistics of relation in system schema "%"', $1;
1122     END IF;
1123
1124     RETURN QUERY
1125         SELECT dbms_stats.lock(cn.oid)
1126           FROM (SELECT c.oid
1127                   FROM pg_class c, pg_namespace n
1128                  WHERE c.relnamespace = n.oid
1129                    AND dbms_stats.is_target_relkind(c.relkind)
1130                    AND n.nspname = $1
1131                  ORDER BY c.oid
1132                ) cn;
1133 END;
1134 $$
1135 LANGUAGE plpgsql STRICT;
1136
1137 CREATE FUNCTION dbms_stats.lock_table_stats(relid regclass)
1138   RETURNS regclass AS
1139 $$
1140 SELECT dbms_stats.lock($1)
1141 $$
1142 LANGUAGE sql STRICT;
1143
1144 CREATE FUNCTION dbms_stats.lock_table_stats(
1145     schemaname text,
1146     tablename text
1147 ) RETURNS regclass AS
1148 $$
1149 SELECT dbms_stats.lock(dbms_stats.relname($1, $2)::regclass)
1150 $$
1151 LANGUAGE sql STRICT;
1152
1153 CREATE FUNCTION dbms_stats.lock_column_stats(
1154     relid regclass,
1155     attname text
1156 ) RETURNS regclass AS
1157 $$
1158 SELECT dbms_stats.lock($1, $2)
1159 $$
1160 LANGUAGE sql STRICT;
1161
1162 CREATE FUNCTION dbms_stats.lock_column_stats(
1163     schemaname text,
1164     tablename text,
1165     attname text
1166 ) RETURNS regclass AS
1167 $$
1168 SELECT dbms_stats.lock(dbms_stats.relname($1, $2)::regclass, $3)
1169 $$
1170 LANGUAGE sql STRICT;
1171
1172 --
1173 -- UNLOCK_STATS: Statistic unlock functions
1174 --
1175
1176 CREATE FUNCTION dbms_stats.unlock(
1177     relid regclass DEFAULT NULL,
1178     attname text DEFAULT NULL
1179 ) RETURNS SETOF regclass AS
1180 $$
1181 DECLARE
1182     set_attnum int2;
1183     unlock_id  int8;
1184 BEGIN
1185     IF $1 IS NULL AND $2 IS NOT NULL THEN
1186         RAISE EXCEPTION 'relation is required';
1187     END IF;
1188     SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
1189      WHERE a.attrelid = $1 AND a.attname = $2;
1190     IF $2 IS NOT NULL AND set_attnum IS NULL THEN
1191         RAISE EXCEPTION 'column "%" of "%" does not exist', $2, $1;
1192     END IF;
1193
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
1197     DELETE FROM dbms_stats._column_stats_locked
1198      WHERE (starelid = $1 OR $1 IS NULL)
1199        AND (staattnum = set_attnum OR $2 IS NULL);
1200
1201     IF $1 IS NOT NULL AND $2 IS NOT NULL THEN
1202         RETURN QUERY
1203             SELECT $1;
1204     END IF;
1205     FOR unlock_id IN
1206         SELECT ru.relid
1207           FROM dbms_stats._relation_stats_locked ru
1208          WHERE (ru.relid = $1 OR $1 IS NULL) AND ($2 IS NULL)
1209          ORDER BY ru.relid
1210     LOOP
1211         DELETE FROM dbms_stats._relation_stats_locked ru
1212          WHERE ru.relid = unlock_id;
1213         RETURN NEXT unlock_id;
1214     END LOOP;
1215 END;
1216 $$
1217 LANGUAGE plpgsql;
1218
1219 CREATE FUNCTION dbms_stats.unlock_database_stats()
1220   RETURNS SETOF regclass AS
1221 $$
1222 DECLARE
1223     unlock_id int8;
1224 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;
1227
1228     FOR unlock_id IN
1229         SELECT relid
1230           FROM dbms_stats._relation_stats_locked
1231          ORDER BY relid
1232     LOOP
1233         DELETE FROM dbms_stats._relation_stats_locked
1234          WHERE relid = unlock_id;
1235         RETURN NEXT unlock_id;
1236     END LOOP;
1237 END;
1238 $$
1239 LANGUAGE plpgsql STRICT;
1240
1241 CREATE FUNCTION dbms_stats.unlock_schema_stats(
1242     schemaname text
1243 ) RETURNS SETOF regclass AS
1244 $$
1245 DECLARE
1246     unlock_id int8;
1247 BEGIN
1248     IF NOT EXISTS(SELECT * FROM pg_namespace WHERE nspname = $1) THEN
1249         RAISE EXCEPTION 'schema "%" does not exist', $1;
1250     END IF;
1251     IF dbms_stats.is_system_schema($1) THEN
1252         RAISE EXCEPTION 'can not unlock statistics of relation in system schema "%"', $1;
1253     END IF;
1254
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
1258     FOR unlock_id IN
1259         SELECT relid
1260           FROM dbms_stats._relation_stats_locked, pg_class c, pg_namespace n
1261          WHERE relid = c.oid
1262            AND c.relnamespace = n.oid
1263            AND n.nspname = $1
1264          ORDER BY relid
1265     LOOP
1266         DELETE FROM dbms_stats._relation_stats_locked
1267          WHERE relid = unlock_id;
1268         RETURN NEXT unlock_id;
1269     END LOOP;
1270 END;
1271 $$
1272 LANGUAGE plpgsql STRICT;
1273
1274 CREATE FUNCTION dbms_stats.unlock_table_stats(relid regclass)
1275   RETURNS SETOF regclass AS
1276 $$
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
1281 DELETE FROM dbms_stats._relation_stats_locked
1282  WHERE relid = $1
1283  RETURNING relid::regclass
1284 $$
1285 LANGUAGE sql STRICT;
1286
1287 CREATE FUNCTION dbms_stats.unlock_table_stats(
1288     schemaname text,
1289     tablename text
1290 ) RETURNS SETOF regclass AS
1291 $$
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
1296 DELETE FROM dbms_stats._relation_stats_locked
1297  WHERE relid = dbms_stats.relname($1, $2)::regclass
1298  RETURNING relid::regclass
1299 $$
1300 LANGUAGE sql STRICT;
1301
1302 CREATE FUNCTION dbms_stats.unlock_column_stats(
1303     relid regclass,
1304     attname text
1305 ) RETURNS SETOF regclass AS
1306 $$
1307 DECLARE
1308     set_attnum int2;
1309 BEGIN
1310     SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
1311      WHERE a.attrelid = $1 AND a.attname = $2;
1312     IF $2 IS NOT NULL AND set_attnum IS NULL THEN
1313         RAISE EXCEPTION 'column "%" of "%" does not exist', $2, $1;
1314     END IF;
1315
1316     LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1317     LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1318
1319         DELETE FROM dbms_stats._column_stats_locked
1320          WHERE starelid = $1
1321            AND staattnum = set_attnum;
1322     RETURN QUERY
1323         SELECT $1;
1324 END;
1325 $$
1326 LANGUAGE plpgsql STRICT;
1327
1328 CREATE FUNCTION dbms_stats.unlock_column_stats(
1329     schemaname text,
1330     tablename text,
1331     attname text
1332 ) RETURNS SETOF regclass AS
1333 $$
1334 DECLARE
1335     set_attnum int2;
1336 BEGIN
1337     SELECT a.attnum INTO set_attnum FROM pg_catalog.pg_attribute a
1338      WHERE a.attrelid = dbms_stats.relname($1, $2)::regclass
1339        AND a.attname = $3;
1340     IF $3 IS NOT NULL AND set_attnum IS NULL THEN
1341                 RAISE EXCEPTION 'column "%" of "%.%" does not exist', $3, $1, $2;
1342     END IF;
1343
1344     LOCK dbms_stats._relation_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1345     LOCK dbms_stats._column_stats_locked IN SHARE UPDATE EXCLUSIVE MODE;
1346
1347         DELETE FROM dbms_stats._column_stats_locked
1348          WHERE starelid = dbms_stats.relname($1, $2)::regclass
1349            AND staattnum = set_attnum;
1350     RETURN QUERY
1351         SELECT dbms_stats.relname($1, $2)::regclass;
1352 END;
1353 $$
1354 LANGUAGE plpgsql STRICT;
1355
1356 --
1357 -- IMPORT_STATS: Statistic import functions
1358 --
1359
1360 CREATE FUNCTION dbms_stats.import(
1361     nspname text DEFAULT NULL,
1362     relid regclass DEFAULT NULL,
1363     attname text DEFAULT NULL,
1364     src text DEFAULT NULL
1365 ) RETURNS void AS
1366 'MODULE_PATHNAME', 'dbms_stats_import'
1367 LANGUAGE C;
1368
1369 CREATE FUNCTION dbms_stats.import_database_stats(src text)
1370   RETURNS void AS
1371 $$
1372 SELECT dbms_stats.import(NULL, NULL, NULL, $1)
1373 $$
1374 LANGUAGE sql;
1375
1376 CREATE FUNCTION dbms_stats.import_schema_stats(
1377     schemaname text,
1378     src text
1379 ) RETURNS void AS
1380 $$
1381 SELECT dbms_stats.import($1, NULL, NULL, $2)
1382 $$
1383 LANGUAGE sql;
1384
1385 CREATE FUNCTION dbms_stats.import_table_stats(
1386     relid regclass,
1387     src text
1388 ) RETURNS void AS
1389 $$
1390 SELECT dbms_stats.import(NULL, $1, NULL, $2)
1391 $$
1392 LANGUAGE sql;
1393
1394 CREATE FUNCTION dbms_stats.import_table_stats(
1395     schemaname text,
1396     tablename text,
1397     src text
1398 ) RETURNS void AS
1399 $$
1400 SELECT dbms_stats.import(NULL, dbms_stats.relname($1, $2)::regclass, NULL, $3)
1401 $$
1402 LANGUAGE sql;
1403
1404 CREATE FUNCTION dbms_stats.import_column_stats(
1405     relid regclass,
1406     attname text,
1407     src text
1408 ) RETURNS void AS
1409 $$
1410 SELECT dbms_stats.import(NULL, $1, $2, $3)
1411 $$
1412 LANGUAGE sql;
1413
1414 CREATE FUNCTION dbms_stats.import_column_stats(
1415     schemaname text,
1416     tablename text,
1417     attname text,
1418     src text
1419 ) RETURNS void AS
1420 $$
1421 SELECT dbms_stats.import(NULL, dbms_stats.relname($1, $2)::regclass, $3, $4)
1422 $$
1423 LANGUAGE sql;
1424
1425 --
1426 -- PURGE_STATS: Statistic purge function
1427 --
1428
1429 CREATE OR REPLACE FUNCTION dbms_stats.purge_stats(
1430     backup_id int8,
1431     force bool DEFAULT false
1432 ) RETURNS SETOF dbms_stats.backup_history AS
1433 $$
1434 DECLARE
1435     delete_id int8;
1436     deleted   dbms_stats.backup_history;
1437 BEGIN
1438     IF $1 IS NULL THEN
1439         RAISE EXCEPTION 'backup id is required';
1440     END IF;
1441     IF $2 IS NULL THEN
1442         RAISE EXCEPTION 'force is not null';
1443     END IF;
1444
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
1450         RAISE EXCEPTION 'backup id % does not exist', $1;
1451     END IF;
1452     IF NOT $2 AND NOT EXISTS(SELECT *
1453                                FROM dbms_stats.backup_history
1454                               WHERE unit = 'd'
1455                                 AND id > $1) THEN
1456         RAISE WARNING 'at least one database-wise backup must be remain'
1457                         USING HINT = 'use true as 2nd parameter, if you want to purge forcibly';
1458         RETURN;
1459     END IF;
1460
1461     FOR deleted IN
1462         SELECT * FROM dbms_stats.backup_history
1463          WHERE id <= $1
1464          ORDER BY id
1465     LOOP
1466         DELETE FROM dbms_stats.backup_history
1467          WHERE id = deleted.id;
1468         RETURN NEXT deleted;
1469     END LOOP;
1470 END;
1471 $$
1472 LANGUAGE plpgsql;
1473
1474 --
1475 -- CLEAN_STATS: Clean orphan dummy statistic
1476 --
1477
1478 CREATE OR REPLACE FUNCTION dbms_stats.clean_up_stats() RETURNS SETOF text AS
1479 $$
1480 DECLARE
1481         clean_relid             Oid;
1482         clean_attnum    int2;
1483         clean_inherit   bool;
1484         clean_rel_col   text;
1485 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
1489         -- We don't have to check that table-level dummy statistic of the table
1490         -- exists here, because the foreign key constraints defined on column-level
1491         -- dummy static table eusures that.
1492         FOR clean_rel_col, clean_relid, clean_attnum, clean_inherit IN
1493                 SELECT r.relname || ', ' || v.staattnum::text,
1494                            v.starelid, v.staattnum, v.stainherit
1495                   FROM dbms_stats._column_stats_locked v
1496                   JOIN dbms_stats._relation_stats_locked r ON (v.starelid = r.relid)
1497                  WHERE NOT EXISTS (
1498                         SELECT NULL
1499                           FROM pg_attribute a
1500                          WHERE a.attrelid = v.starelid
1501                            AND a.attnum = v.staattnum
1502                            AND a.attisdropped  = false
1503                 )
1504         LOOP
1505                 DELETE FROM dbms_stats._column_stats_locked
1506                  WHERE starelid = clean_relid
1507                    AND staattnum = clean_attnum
1508                    AND stainherit = clean_inherit;
1509                 RETURN NEXT clean_rel_col;
1510         END LOOP;
1511
1512         RETURN QUERY
1513                 DELETE FROM dbms_stats._relation_stats_locked r
1514                  WHERE NOT EXISTS (
1515                         SELECT NULL
1516                           FROM pg_class c
1517                          WHERE c.oid = r.relid)
1518                  RETURNING relname || ',';
1519         RETURN;
1520 END
1521 $$
1522 LANGUAGE plpgsql;
1523
1524 GRANT USAGE ON schema dbms_stats TO PUBLIC;
1525 --