3

在 PostgreSQL 11 上,我定期转储备份快照,有时将它们导入具有相同设置的开发系统。那里没什么好看的:

# Dump
ps_dump -OU <user> <database> >dump.sql
# Restore
psql -U <user> -f dump.sql <database>

但是,转储中的两个索引在还原时会引发错误。我把它归结为以下转储,所有无关的东西都被剥离了:

--
-- PostgreSQL database dump
--

-- Dumped from database version 11.2
-- Dumped by pg_dump version 11.2

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET client_min_messages = warning;
SET row_security = off;

--
-- Name: add_days(timestamp without time zone, integer, text); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.add_days(timestamp without time zone, integer, text DEFAULT 'Europe/Zurich'::text) RETURNS timestamp without time zone
    LANGUAGE sql IMMUTABLE
    SET search_path TO 'public', 'pg_temp'
    AS $_$
          SELECT (($1::timestamp AT TIME ZONE 'UTC' AT TIME ZONE $3 + INTERVAL '1 day' * $2) AT TIME ZONE $3)::timestamp
        $_$;

SET default_tablespace = '';

SET default_with_oids = false;

--
-- Name: projects; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.projects (
    id integer NOT NULL,
    started_at timestamp without time zone,
    duration integer
);

--
-- Name: ended_at(public.projects); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.ended_at(public.projects) RETURNS timestamp without time zone
    LANGUAGE sql STABLE
    AS $_$
          SELECT add_days($1.started_at, $1.duration)
        $_$;

--
-- Name: index_projects_on_ended_at; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX index_projects_on_ended_at ON public.projects USING btree (public.ended_at(projects.*));

恢复此转储会产生以下错误:

psql:dumped.sql:60: ERROR:  function add_days(timestamp without time zone, integer) does not exist
LINE 2:           SELECT add_days($1.started_at, $1.duration)
                     ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
QUERY:
      SELECT add_days($1.started_at, $1.duration)

CONTEXT:  SQL function "ended_at" during inlining

以下解决了这个问题:add_days模式前缀如下:

CREATE FUNCTION public.ended_at(public.projects) RETURNS timestamp without time zone
    LANGUAGE sql STABLE
    AS $_$
          SELECT public.add_days($1.started_at, $1.duration)
        $_$;

结案?不完全的。我想了解这里的问题。

我猜,除非带有显式前缀前缀,否则SELECT pg_catalog.set_config('search_path', '', false);pg_dump 生成的行会阻止被发现。add_days

但是,为什么以下替代方法(添加 search_path)不起作用?

CREATE FUNCTION public.ended_at(public.projects) RETURNS timestamp without time zone
    LANGUAGE sql STABLE
    SET search_path TO 'public', 'pg_temp'
    AS $_$
          SELECT add_days($1.started_at, $1.duration)
        $_$;

这会触发一个完全不同的错误:

psql:dumped.sql:58: ERROR:  functions in index expression must be marked IMMUTABLE

好吧,现在我很困惑。有人能告诉我这里发生了什么吗?

4

1 回答 1

0

这里有两个不同的问题。

  1. 为什么在恢复转储时会出现错误?

    该问题是由修复 PostgreSQL 安全问题的补丁引起的。

    在此更改之前,pg_dump/pg_restore会将 设置search_path为如下所示:

    SET search_path = dumped_schema, pg_catalog;
    

    这样做的问题是索引定义(和其他地方)中使用的任何函数或运算符都将dumped_schema首先被搜索。

    恶意用户可以使用它在还原期间以超级用户权限执行其功能。

    你已经想出了如何解决这个问题。

    诚然,这很烦人,但鉴于到目前为止您的功能受当前search_path设置的支配(任何人都可以通过简单的SET命令进行更改),我认为它本身就是一种改进。

  2. 为什么它对SET search_path功能不起作用?

    这里的问题是索引中使用的任何函数都必须是IMMUTABLE(无论如何都必须为相同的参数返回相同的结果),否则索引可能会损坏。然而,ended_atSTABLE和不是IMMUTABLE

    这个问题在你原来的设置中被“掩盖”了,因为 PostgreSQL 可以内联函数,也就是说,用它的定义替换函数调用。现在add_days 标记IMMUTABLE了,所以很好。

    设置search_pathon 函数后,函数不能再被内联,所以现在你得到错误信息。

    我会说你也应该ended_at声明IMMUTABLE

于 2019-03-30T03:51:47.973 回答