45.6. Ejemplos #

Esta sección contiene un ejemplo muy simple de uso de SPI. La función C execq toma un comando SQL como su primer argumento y un recuento de filas como el segundo, ejecuta el comando usando SPI_exec y devuelve el número de filas que fueron procesadas por el comando. Puede encontrar ejemplos más complejos para SPI en el árbol de fuentes en src/test/regress/regress.c y en el módulo 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);
 }
 

Así es como se declara la función después de haberla compilado en una biblioteca compartida (los detalles están en Section 36.10.5):

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

Aquí hay una sesión de ejemplo:

 => 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    -- insertado por execq
 INFO:  EXECQ:  1    -- devuelto por execq e insertado por el INSERT superior

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

 => SELECT execq('INSERT INTO a SELECT x + 2 FROM a RETURNING *', 1);
 INFO:  EXECQ:  2    -- 0 + 2, luego la ejecución se detuvo por el recuento
  execq
 -------
      1
 (1 row)

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

  execq
 -------
      3              -- 10 es solo el valor máximo, 3 es el número real de filas
 (1 row)

 => SELECT execq('INSERT INTO a SELECT x + 10 FROM a', 1);
  execq
 -------
      3              -- todas las filas procesadas; el recuento no lo detiene, porque no se devuelve nada
 (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 (sin filas en a) + 1
 (1 row)

 => INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1);
 INFO:  EXECQ:  1
 INSERT 0 1
 => SELECT * FROM a;
  x
 ---
  1
  2                  -- 1 (había una fila en a) + 1
 (2 rows)

 -- Esto demuestra la regla de visibilidad de cambios de datos.
 -- execq es llamado dos veces y ve diferentes números de filas cada vez:

 => INSERT INTO a SELECT execq('SELECT * FROM a', 0) * x FROM a;
 INFO:  EXECQ:  1    -- resultados del primer execq
 INFO:  EXECQ:  2
 INFO:  EXECQ:  1    -- resultados del segundo execq
 INFO:  EXECQ:  2
 INFO:  EXECQ:  2
 INSERT 0 2
 => SELECT * FROM a;
  x
 ---
  1
  2
  2                  -- 2 filas * 1 (x en la primera fila)
  6                  -- 3 filas (2 + 1 recién insertada) * 2 (x en la segunda fila)
 (4 rows)