Postgresql 中文操作指南

47.6. Examples #

本节包含 SPI 使用非常简单的示例。C 函数 _execq_将 SQL 命令作为它的第一个参数和行数作为它的第二个参数,使用 _SPI_exec_执行命令,并返回命令处理过的行数。您可以在源树的 _src/test/regress/regress.c_和 spi模块中找到 SPI 更复杂的示例。

#include "postgres.h"

#include "executor/spi.h"
#include "utils/builtins.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(execq);

Datum
execq(PG_FUNCTION_ARGS)
{
    char *command;
    int cnt;
    int ret;
    uint64 proc;

    /* Convert given text object to a C string */
    command = text_to_cstring(PG_GETARG_TEXT_PP(0));
    cnt = PG_GETARG_INT32(1);

    SPI_connect();

    ret = SPI_exec(command, cnt);

    proc = SPI_processed;

    /*
     * If some rows were fetched, print them via elog(INFO).
     */
    if (ret > 0 && SPI_tuptable != NULL)
    {
        SPITupleTable *tuptable = SPI_tuptable;
        TupleDesc tupdesc = tuptable->tupdesc;
        char buf[8192];
        uint64 j;

        for (j = 0; j < tuptable->numvals; j++)
        {
            HeapTuple tuple = tuptable->vals[j];
            int i;

            for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %s%s",
                        SPI_getvalue(tuple, tupdesc, i),
                        (i == tupdesc->natts) ? " " : " |");
            elog(INFO, "EXECQ: %s", buf);
        }
    }

    SPI_finish();
    pfree(command);

    PG_RETURN_INT64(proc);
}

经过编译生成共享库后(详情见 Section 38.10.5),您应该这样声明函数:

CREATE FUNCTION execq(text, integer) RETURNS int8
    AS 'filename'
    LANGUAGE C STRICT;

以下是一个示例会话:

=> SELECT execq('CREATE TABLE a (x integer)', 0);
 execq
-------
     0
(1 row)

=> INSERT INTO a VALUES (execq('INSERT INTO a VALUES (0)', 0));
INSERT 0 1
=> SELECT execq('SELECT * FROM a', 0);
INFO:  EXECQ:  0    -- inserted by execq
INFO:  EXECQ:  1    -- returned by execq and inserted by upper INSERT

 execq
-------
     2
(1 row)

=> SELECT execq('INSERT INTO a SELECT x + 2 FROM a RETURNING *', 1);
INFO:  EXECQ:  2    -- 0 + 2, then execution was stopped by count
 execq
-------
     1
(1 row)

=> SELECT execq('SELECT * FROM a', 10);
INFO:  EXECQ:  0
INFO:  EXECQ:  1
INFO:  EXECQ:  2

 execq
-------
     3              -- 10 is the max value only, 3 is the real number of rows
(1 row)

=> SELECT execq('INSERT INTO a SELECT x + 10 FROM a', 1);
 execq
-------
     3              -- all rows processed; count does not stop it, because nothing is returned
(1 row)

=> SELECT * FROM a;
 x
  0
  1
  2
 10
 11
 12
(6 rows)

⇒ DELETE FROM a;DELETE 6⇒ INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1);INSERT 0 1⇒ SELECT * FROM a; x--- 1  — 0 (a 中没有行) + 1(1 行)

⇒ INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1);INFO: EXECQ: 1INSERT 0 1⇒ SELECT * FROM a; x--- 1 2  — 1(a 中有一行)+ 1(2 行)

 — 这说明了数据更改的可见性规则。 — execq 被调用两次,每次看到了不同数量的行:

⇒ INSERT INTO a SELECT execq('SELECT * FROM a', 0) * x FROM a;INFO: EXECQ: 1  — 第一次 execq 的结果INFO: EXECQ: 2INFO: EXECQ: 1  — 第二次 execq 的结果INFO: EXECQ: 2INFO: EXECQ: 2INSERT 0 2⇒ SELECT * FROM a; x--- 1 2 2  — 2 行 * 1(第一行中的 x)6  — 3 行(刚插入的 2 + 1)* 2(第二行中的 x)(4 行)