【发布时间】:2016-05-13 04:23:00
【问题描述】:
我有一个包含大量重复逻辑的 Postgres 函数。如果我用 Ruby 等语言编写它,我会将重复的逻辑提取到一些私有辅助方法中。但是在 Postgres 中似乎没有等效的“私有方法”。
原始函数
CREATE OR REPLACE FUNCTION drop_create_idx_constraint(in_operation varchar, in_table_name_or_all_option varchar) RETURNS integer AS $$
DECLARE
cur_drop_for_specific_tab CURSOR (tab_name varchar) IS SELECT drop_stmt FROM table_indexes WHERE table_indexes.table_name = table_name_to_drop;
cur_drop_for_all_tab CURSOR IS SELECT drop_stmt FROM table_indexes;
cur_create_for_specific_tab CURSOR (tab_name varchar) IS SELECT recreate_stmt FROM table_indexes WHERE table_indexes.table_name = table_name_to_drop;
cur_create_for_all_tab CURSOR IS SELECT recreate_stmt FROM table_indexes;
BEGIN
IF upper(in_operation) = 'DROP' THEN
IF upper(in_table_name_or_all_option) ='ALL' THEN
FOR table_record IN cur_drop_for_all_tab LOOP
EXECUTE table_record.drop_stmt;
END LOOP;
ELSE
FOR table_record IN cur_drop_for_specific_tab(in_table_name_or_all_option) LOOP
EXECUTE table_record.drop_stmt;
END LOOP;
END IF;
ELSIF upper(in_operation) = 'CREATE' THEN
IF upper(in_table_name_or_all_option) ='ALL' THEN
FOR table_record IN cur_create_for_all_tab LOOP
EXECUTE table_record.recreate_stmt;
END LOOP;
ELSE
FOR table_record IN cur_create_for_specific_tab(in_table_name_or_all_option) LOOP
EXECUTE table_record.recreate_stmt;
END LOOP;
END IF;
END IF;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
重构函数
CREATE OR REPLACE FUNCTION execute_recreate_stmt_from_records(input_cursor refcursor) RETURNS integer AS $$
BEGIN
FOR table_record IN input_cursor LOOP
EXECUTE table_record.recreate_stmt;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION execute_drop_stmt_from_records(input_cursor refcursor) RETURNS integer AS $$
BEGIN
FOR table_record IN input_cursor LOOP
EXECUTE table_record.drop_stmt;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION drop_indexes_and_constraints(table_name_to_drop varchar) RETURNS integer AS $$
DECLARE
indexes_and_constraints CURSOR IS SELECT drop_stmt FROM table_indexes WHERE table_indexes.table_name = table_name_to_drop;
SELECT execute_drop_stmt_from_records(indexes_and_constraints);
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION drop_all_indexes_and_constraints() RETURNS integer AS $$
DECLARE
indexes_and_constraints CURSOR IS SELECT drop_stmt FROM table_indexes;
SELECT execute_drop_stmt_from_records(indexes_and_constraints);
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION recreate_indexes_and_constraints(table_name_to_recreate varchar) RETURNS integer AS $$
DECLARE
indexes_and_constraints CURSOR IS SELECT recreate_stmt FROM table_indexes WHERE table_indexes.table_name = table_name_to_recreate;
SELECT execute_recreate_stmt_from_records(indexes_and_constraints);
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION recreate_all_indexes_and_constraints() RETURNS integer AS $$
DECLARE
indexes_and_constraints CURSOR IS SELECT recreate_stmt FROM table_indexes;
SELECT execute_recreate_stmt_from_records(indexes_and_constraints);
$$ LANGUAGE plpgsql;
我相信我的重构的根本问题是辅助函数 execute_recreate_stmt_from_records 和 execute_drop_stmt_from_records 功能太强大而无法公开访问,特别是因为 Heroku(托管此 DB)只允许一个 DB 用户。当然,如果上述重构还有其他问题,欢迎指出。
【问题讨论】:
-
在 Postgres 的 Create Function 文档中,有关于 SECURITY DEFINER 的讨论。我从未使用过它,但它可能是您正在寻找的保护这些功能不被全局化的东西。
-
来自PG docs:“SECURITY INVOKER 表示该函数将以调用它的用户的权限执行。这是默认值。SECURITY DEFINER 指定要执行该函数具有创建它的用户的权限。”这些选项似乎都没有将函数限制为仅由这些其他函数调用。
标签: postgresql stored-procedures heroku encapsulation heroku-postgres