44.2. pl/sql(兼容Oracle PL/SQL) 的结构

44.2.1. 匿名块
44.2.2. 注释

pl/sql(兼容Oracle PL/SQL) 是一种块结构语言。函数、过程、包、触发器或匿名块都包含一个块结构,我们称所有包含在 匿名块 中的部分为 结构。

44.2.1. 匿名块

定义匿名块如下:

[ DECLARE
    declarations ]
BEGIN
    statements
[ EXCEPTION 
    Exception-handling part ]
END;
/

块内每个声明和语句都以分号结尾。出现在另一个块内的块必须在 END 后面加上分号,如上所示;然而,结束函数体的最终 END 也需要一个分号,其中 / 是 pl/sql 低层块结构的结束字符。

Tip

一个常见的错误是在 BEGIN 后立即写一个分号。这是不正确的,会导致语法错误。匿名块目前不支持使用 <<label>>

匿名块中支持将空格转成NUMERIC,在Oracle兼容模式下生效。

例如,使用匿名块输出“quantity”的值:

DECLARE
  quantity integer := 80;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Quantity here is '||quantity);  -- Prints 80
END;
/

在使用 DBMS_OUTPUT.PUT_LINE 函数之前,需要打开打印权限。

 select dbms_output.serveroutput(true);

当然,此函数只能在 Oracle 兼容模式下使用。

从 LightDB 23.4 版本开始,匿名块通过在 libpq 使用时支持绑定变量。 需要注意的是,使用绑定参数时客户端需要传递参数类型。 绑定参数的数量限制为65535。

下面是通过 libpq 执行使用绑定参数的匿名块的用例:


/*
 * src/test/examples/lt_testlibpq.c
 *
 *
 * lt_testlibpq.c
 *		this test program shows to use LIBPQ to exec dostmt with bind param
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include "libpq-fe.h"

static void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

int
main(int argc, char **argv)
{
    const char *conninfo;
    PGconn     *conn;
    PGresult   *res;
    const char *paramValues[10];
    Oid paramTypes[10];
	int			nFields;
	int			i,
				j;

    if (argc > 1)
        conninfo = argv[1];
    else
        conninfo = "dbname = postgres";

    
    conn = PQconnectdb(conninfo);

    
    if (PQstatus(conn) != CONNECTION_OK)
    {
        fprintf(stderr, "Connection to database failed: %s",
                PQerrorMessage(conn));
        exit_nicely(conn);
    }


    res = PQexec(conn, "create table test_dostmt(key1 int, key2 text);");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    res = PQexec(conn, "insert into test_dostmt values(1,'a');");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    paramTypes[0] = 23;
    paramValues[0] = "1";

    printf("update test_dostmt key2 to 'test_dostmt'\n");
    res = PQexecParams(conn, "begin\
                 begin\
                        update test_dostmt set key2 =  'test_dostmt'  where key1 =  $1  ;\
                 end;\
         end;", 1, paramTypes, paramValues, NULL, NULL, 0);

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    printf("get test_dostmt key2 \n");
    res = PQexec(conn, "select key2 from test_dostmt where key1=1;");
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

	nFields = PQnfields(res);

	/* print out the instances */
	for (i = 0; i < PQntuples(res); i++)
	{
		for (j = 0; j < nFields; j++)
			printf("%s: %s", PQfname(res, j), PQgetvalue(res, i, j));
		printf("\n");
	}

    PQclear(res);


    printf("update test_dostmt key2 to 'test_dostmt_new'\n");
    paramTypes[0] = 23;
    paramValues[0] = "1";
    paramTypes[1] = 25;
    paramValues[1] = "test_dostmt1";
    res = PQexecParams(conn, " \
                declare\
                    id int := $1; \
                    val text := $2; \
                begin\
                    begin\
                        val = val || '_new'; \
                        update  test_dostmt set key2 = val where key1 = id  ;\
                    end;\
         end;", 2, paramTypes, paramValues, NULL, NULL, 0);

    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    printf("get test_dostmt key2 \n");
    res = PQexec(conn, "select key2 from test_dostmt where key1=1;");
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }


	nFields = PQnfields(res);
	/* print out the instances */
	for (i = 0; i < PQntuples(res); i++)
	{
		for (j = 0; j < nFields; j++)
			printf("%s: %s", PQfname(res, j), PQgetvalue(res, i, j));
		printf("\n");
	}

    PQclear(res);


    res = PQexec(conn, "drop table test_dostmt;");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }

    PQclear(res);

    PQfinish(conn);

    return 0;
}

44.2.2. 注释

pl/sql(兼容Oracle PL/SQL) 编译器会忽略注释。它们的目的是帮助其他应用程序开发人员理解您的源文本。通常,您使用注释来描述每个代码段的目的和用途。您还可以通过将它们变成注释来禁用过时或未完成的代码片段。

44.2.2.1. 单行注释

单行注释以 -- 开始,延伸到行末。这个例子有三个单行注释。

DECLARE
  howmany     NUMBER;
  num_tables  NUMBER;
BEGIN
  -- Begin processing
  SELECT COUNT(*) INTO howmany
  FROM USER_OBJECTS
  WHERE OBJECT_TYPE = 'TABLE'; -- Check number of tables
  num_tables := howmany;       -- Compute another value
END;
/

44.2.2.2. 多行注释

多行注释以 /* 开始,以 */ 结束,可以跨多行。

BEGIN
  /*
  IF 2 + 2 = 4 THEN
    some_condition := TRUE;
  END IF;
  */
  NULL;
END;
/