Ошибки при импорте дампов SQL, содержащих функции, используемые в индексах - PullRequest
3 голосов
/ 29 марта 2019

В 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

Хорошо, теперь я запутался.Может кто-нибудь сказать мне, что здесь происходит?

1 Ответ

0 голосов
/ 30 марта 2019

Здесь есть два разных вопроса.

  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_at - это STABLE, а не IMMUTABLE.

    Проблема & ldquo; в маске & rdquo; в вашей первоначальной настройке, поскольку PostgreSQL может inline функции, то есть заменить вызов функции ее определением. Теперь add_days помечено IMMUTABLE, так что все в порядке.

    После установки search_path для функции, функция больше не может быть встроена, поэтому теперь вы получаете сообщение об ошибке.

    Я бы сказал, что вы должны объявить ended_at как IMMUTABLE.

...