全部產品
Search
文件中心

Hologres:預存程序

更新時間:May 21, 2025

預存程序(PROCEDURE)是由一組先行編譯的SQL語句組成的集合,可以在資料庫中進行儲存並反覆調用。本文為您介紹在Hologres中使用預存程序的方法。

使用限制

  • Hologres從V3.0版本起支援PL/pgSQL文法的預存程序,PL/pgSQL文法詳情請參見SQL Procedural Language

  • 在Hologres的預存程序中,支援多條DDL語句事務,支援多條DML混合事務,暫不支援DDL和DML混合事務,詳情請參見SQL事務能力

  • 預存程序不支援設定傳回值,無法作為UDF(User-defined Functions)使用。

許可權說明

  • CREATE PROCEDURE需要使用者有Database中的Create許可權,與建立表許可權一致,詳情請參見SQL-CREATE PROCEDURE

  • CREATE OR REPLACE需要使用者同時擁有Database的Create許可權和目標預存程序的OWNER許可權,詳見SQL-CREATE PROCEDURE

  • 執行預存程序需要使用者有預存程序的EXECUTE許可權,詳見SQL-CALL

命令參考

Hologres支援的預存程序文法相容PostgreSQL,具體文法如下:

建立預存程序

CREATE [ OR REPLACE ] PROCEDURE
    <procedure_name> ([<argname> <argtype>])
LANGUAGE 'plpgsql'
AS <definition>;

參數

說明

procedure_name

預存程序名稱。

argname

參數名稱。參數可選,取決於預存程序設計。

argtype

參數類型。

definition

定義預存程序的具體實現,可以是一個SQL語句或者代碼塊。

更多參數詳情請參見SQL-CREATE PROCEDURE

修改預存程序

ALTER PROCEDURE <procedure_name> ([<argname> <argtype>])
    OWNER TO <new_owner> | CURRENT_USER | SESSION_USER;

參數

說明

new_owner

新使用者名稱。

CURRENT_USER

目前使用者。

SESSION_USER

會話使用者。

參數詳情請參見SQL-ALTER PROCEDURE

刪除預存程序

DROP PROCEDURE [ IF EXISTS ] <procedure_name> ([<argname> <argtype>]); 

參數詳情請參見SQL-DROP PROCEDURE

執行預存程序

CALL <procedure_name> ([<argument>]);

參數

說明

argument

預存程序所需的參數。參數可選,取決於預存程序設計。

參數詳情請參見SQL-CALL

使用樣本

  • 樣本1:含多條DDL語句事務的預存程序。

    1. 建立預存程序。

      CREATE OR REPLACE PROCEDURE procedure_1()
      LANGUAGE 'plpgsql'
      AS $$
      BEGIN
          --- TXN1 --- 
          CREATE TABLE a1(key int);
          CREATE TABLE a2(key int);
          COMMIT; 
      
          --- TXN2 ---
          CREATE TABLE a3(key int);
          CREATE TABLE a4(key int);
          ROLLBACK;
      END; 
      $$;
    2. 調用預存程序:表a1、a2建立成功,a3、a4未建立。

      CALL procedure_1();
  • 樣本2:含多條DML語句事務的預存程序。

    1. 建立預存程序

      CREATE OR REPLACE PROCEDURE procedure_2()
      LANGUAGE 'plpgsql'
      AS $$
      BEGIN
          INSERT INTO a1 VALUES(1);
          INSERT INTO a2 VALUES(2);
          ROLLBACK;
      END;
      $$;
      
      CREATE OR REPLACE PROCEDURE procedure_3()
      LANGUAGE 'plpgsql'
      AS $$
      BEGIN
          INSERT INTO a1 VALUES(1);
          INSERT INTO a2 VALUES(2);
      END;
      $$;
    2. 執行預存程序

      • 執行procedure_2:支援ROLLBACK,資料未成功寫入

        -- 開啟DML事務功能
        SET hg_experimental_enable_transaction = ON;
        
        -- 執行預存程序
        CALL procedure_2();
      • 執行procedure_3:資料成功寫入

        -- 開啟DML事務功能
        SET hg_experimental_enable_transaction = ON;
        
        -- 執行預存程序
        CALL procedure_3();
  • 樣本3:同時含DDL和DML的預存程序。

    1. 建立預存程序:Hologres暫不支援DDL和DML混合事務,因此在預存程序中,需要對DDL和DML分別執行COMMIT。

      CREATE OR REPLACE PROCEDURE procedure_4()
      LANGUAGE 'plpgsql'
      AS $$
      BEGIN
          INSERT INTO a1 VALUES(1);
          COMMIT;	
          CREATE TABLE bb(key int);
          COMMIT;	
          INSERT INTO a1 VALUES(2);
          INSERT INTO bb VALUES(1);    
          COMMIT;	
      END;
      $$;
    2. 執行預存程序:建表和資料寫入均成功。

      -- 開啟DML事務功能
      SET hg_experimental_enable_transaction = ON;
      
      -- 執行預存程序
      CALL procedure_4();
  • 樣本4:含常見用法的預存程序包括定義入參、定義中間變數、定義迴圈、定義IF條件、定義EXCEPTION等。

    1. 建立預存程序。

      CREATE OR REPLACE PROCEDURE procedure_5(input text)
      LANGUAGE 'plpgsql'
      AS $$
      -- 定義中間變數
      DECLARE
      sql1 text;
      BEGIN
          -- 向入參的表裡寫入一行資料
          EXECUTE 'insert into ' || input || ' values(1);';
          COMMIT;
      
          -- 建a3表
          CREATE TABLE a3(key int);
          COMMIT;
      
          -- 使用中間變數,向a3表寫入一條資料
          sql1 = 'insert into a3 values(1);';
          EXECUTE sql1;
      
          -- 定義FOR迴圈
          FOR i IN 1..10 LOOP
              BEGIN
                  -- i=1已存在表中,所以只打一條日誌
                  IF i IN (SELECT KEY FROM a3) THEN
                      RAISE NOTICE 'Data already exists.';
                  -- 其他數字不存在表中,所以嘗試寫入,同時RAISE EXCEPTION,而後COMMIT
                  ELSE
                      INSERT INTO a3 VALUES(i);
                      RAISE EXCEPTION 'HG_PLPGSQL_NEED_RETRY';
                      COMMIT; 
                  END IF;
              -- 針對RAISE的EXCEPTION,打一條日誌
              EXCEPTION 
                  WHEN OTHERS THEN
                      RAISE NOTICE 'Catch error.';
              END;
          END LOOP;
      
      END;
      $$;
    2. 執行預存程序:a3表中寫入資料1、其餘資料不寫入,相關日誌全部列印。

      -- 開啟DML事務功能
      SET hg_experimental_enable_transaction = ON;
      
      -- 執行預存程序
      CALL procedure_5('a1');

管理預存程序

  • 查看已建立的預存程序。

    SELECT
        p.proname AS procedure_name,
        pg_get_function_identity_arguments(p.oid) AS argument_types,
        REPLACE(pg_get_functiondef(p.oid),'$procedure$','$$') AS procedure_detail,
        n.nspname AS schema_name,
        r.rolname AS owner_name,
        d.description AS description
    FROM
        pg_proc p
        INNER JOIN pg_namespace n ON p.pronamespace = n.oid
        INNER JOIN pg_roles r ON p.proowner = r.oid
        LEFT JOIN pg_description d ON p.oid = d.objoid
    WHERE
        r.rolname != 'holo_admin'
        AND p.prokind = 'p'
    ORDER BY
        n.nspname,
        p.proname;
  • 查看預存程序定義。

    SELECT pg_get_functiondef('<procedure_name>'::regproc);

常見問題

由於Hologres是分布式系統,其中接入節點FE也是分布式的。當表發生DDL變更時,不同接入節點之間需要即時同步中繼資料,如果中繼資料未同步完成,DDL變更可能會失敗。針對上述情境,Hologres在大部分情況下會自動重試,無需手動重複提交DDL變更。但在預存程序中,無法支援自動重試,上述情境會直接返回錯誤資訊為“HG_PLPGSQL_NEED_RETRY”的報錯。

針對高頻DDL變更的表,建議在預存程序中手動定義重試邏輯,以免預存程序頻繁報錯。重試邏輯如下:

CREATE OR REPLACE PROCEDURE procedure_6()
LANGUAGE 'plpgsql'
AS $$
BEGIN
    WHILE TRUE LOOP
        BEGIN
            -- 嘗試執行DDL語句,如果成功,則退出迴圈
            CREATE TABLE a3(key int);
            COMMIT;
            EXIT;
        EXCEPTION
            -- 如果遇到HG_PLPGSQL_NEED_RETRY報錯,則列印日誌,並自動重試
            WHEN HG_PLPGSQL_NEED_RETRY THEN 
                RAISE NOTICE 'DDL need retry';
        END;
    END LOOP;
END;
$$;