PostgreSQL Datenbankpraxis für Anwender, Administratoren und Entwickler

Anhang

Seite 487

Beispiel mit wenigen Zeilen für EXPLAIN

CREATE TABLE planer_test (
  id         SERIAL        NOT NULL UNIQUE,
  inhalt     VARCHAR       NOT NULL
);
INSERT INTO planer_test (inhalt) VALUES ('Inhalt 001');
INSERT INTO planer_test (inhalt) VALUES ('Inhalt 002');
INSERT INTO planer_test (inhalt) VALUES ('Inhalt 003');
INSERT INTO planer_test (inhalt) VALUES ('Inhalt 004');
INSERT INTO planer_test (inhalt) VALUES ('Inhalt 005');

Seite 488

Statistiken der Datenbank aktualisieren

ANALYZE VERBOSE planer_test;


SELECT id, inhalt FROM planer_test ORDER BY id;


einen bestimmten Wert suchen

EXPLAIN SELECT id, inhalt FROM planer_test WHERE id=1;


Ausführungszeit der Anfrage messen

EXPLAIN ANALYZE SELECT id, inhalt FROM planer_test WHERE id=1;


Beispiel mit vielen Zeilen für EXPLAIN

CREATE TABLE planer_test2 (
  id         SERIAL        NOT NULL UNIQUE,
  inhalt     VARCHAR       NOT NULL
);


Beispieltabelle mit vielen Inhalten füllen dies dauert einige Zeit …

INSERT INTO planer_test2 (id, inhalt)
            SELECT generate_series (1, 10000000), RANDOM();


Statistiken der Datenbank aktualisieren

ANALYZE VERBOSE planer_test2;


einen bestimmten Wert suchen

EXPLAIN SELECT id, inhalt FROM planer_test WHERE id=8500000;


Ausführungszeit der Anfrage messen

EXPLAIN ANALYZE SELECT id, inhalt FROM planer_test WHERE id=8500000;


Die gleiche Messung ohne Index die Nutzung aller vorhandenen Indicies verbieten

SET enable_indexscan = off;
SET enable_bitmapscan = off;


Messungen wiederholen

EXPLAIN SELECT id, inhalt FROM planer_test2 WHERE id=8500000;
EXPLAIN ANALYZE SELECT id, inhalt FROM planer_test2 WHERE id=8500000;

– Einstellung zurücksetzen

RESET enable_indexscan;
RESET enable_bitmapscan;

Seite 489

Beispieltabelle für Deadlock

CREATE TABLE lock_test (
  id             INTEGER      NOT NULL
                              PRIMARY KEY,
  content        VARCHAR      NOT NULL
);
INSERT INTO lock_test (id, content)
            VALUES ('1', 'eingetragen von a');
INSERT INTO lock_test (id, content)
            VALUES ('2', 'eingetragen von b');


Deadlock erzeugen

T 1: BEGIN;
T 2: BEGIN;
T 1: UPDATE lock_test SET content='geaendert von 1' WHERE id=1;
T 2: UPDATE lock_test SET content='geaendert von 2' WHERE id=2;
T 2: UPDATE lock_test SET content='geaendert von 2' WHERE id=1;
T 1: UPDATE lock_test SET content='geaendert von 1' WHERE id=2;

Beispiel für Verbindungsaufbau mit PQconnectdb

#include <postgresql/libpq-fe.h>
#include <stdio.h>

int main (void) {
  const char *connstring = "host=localhost";
  PGconn *conninfo;

  conninfo = PQconnectdb(connstring);

  if (PQstatus(conninfo) == CONNECTION_BAD) {
    printf("Fehler beim Verbinden!\n");
  } else if (PQstatus(conninfo) == CONNECTION_OK) {
    printf("Verbindung in Ordnung\n");
  } else {
    printf("Unbekannter Fehler!\n");
  }

  return 0;
}

Compilieren mit:

gcc -Wall -lpq -L`pg_config --libdir` -I`pg_config --includedir` -o connect connect.c

Seite 490

Beispiel für Verbindungsaufbau mit PQconnectStart

#include <postgresql/libpq-fe.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main (void) {
  const char *connstring = "host=localhost dbname=template1";
  PGconn *conninfo;
  PostgresPollingStatusType pstat;
  int count;

  conninfo = PQconnectStart(connstring);

  if (PQstatus(conninfo) == CONNECTION_BAD) {
    printf("Fehler beim Verbinden!\n");
  }
  pstat = PQconnectPoll(conninfo);

  sleep(10);

  for (count = 1; count <= 10; count++) {

    pstat = PQconnectPoll(conninfo);
    switch (pstat) {
      case PGRES_POLLING_FAILED:
        {
          char *errorm = PQerrorMessage(conninfo);
          printf("Fehler: %s\n", errorm);
          exit(1);
        }
      case PGRES_POLLING_OK:
        printf("Polling OK\n");
        break;
      case PGRES_POLLING_READING:
        printf("Polling lesend\n");
        break;
      case PGRES_POLLING_WRITING:
        printf("Polling schreibend\n");
        break;
      case PGRES_POLLING_ACTIVE:
        printf("Polling aktiv\n");
        break;
      default:
        printf("Unbekannter Polling Status Code: %08x\n", pstat);
    }
    sleep(1);
  }

  return 0;
}

Compilieren mit:

gcc -Wall -lpq -L`pg_config --libdir` -I`pg_config --includedir` -o connect connect.c

Seite 491

Verbindungsaufbau mit pg_pconnect

$db = pg_pconnect("host=einserver port=5432 dbname=meine_datenbank
                   user=pascal password=geheim");
if (!$db) {
  ... Fehlerbehandlung
}

pg_close($db);


PHP-kompatibler BOOLEAN-Ersatz

--
-- erstelle einen PHP (native) kompatiblen BOOLEAN Datentyp
--

-- Datentyp löschen, falls dieser existiert:
-- das löscht alle Funktionen und Spalten, die diesen Typ nutzen!
-- DROP TYPE IF EXISTS boolean2 CASCADE;

-- Ein- und Ausgabefunktionen
-- dafür können bereits existierende Funktionen genutzt werden
CREATE FUNCTION boolean2_in(cstring)
   RETURNS boolean2
   AS 'boolin'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolean2_out(boolean2)
   RETURNS cstring
   AS 'int2out'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolean2_recv(internal)
   RETURNS boolean2
   AS 'boolrecv'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolean2_send(boolean2)
   RETURNS bytea
   AS 'boolsend'
   LANGUAGE internal STRICT;

-- den eigentlichen Datentyp erstellen
CREATE TYPE boolean2 (
   input = boolean2_in,
   output = boolean2_out,
   receive = boolean2_recv,
   send = boolean2_send,
   internallength = 1,
   alignment = char,
   storage = plain,
   passedbyvalue
);
COMMENT ON TYPE boolean2 IS 'boolean, ''1''/''0''';


-- da BOOLEAN2 binär.kompatibel mit BOOLEAN ist, kann dieser Typ
-- in beide Richtungen gecastet werden
CREATE CAST (boolean2 AS boolean)
    WITHOUT FUNCTION
         AS IMPLICIT;
CREATE CAST (boolean AS boolean2)
    WITHOUT FUNCTION
         AS IMPLICIT;


-- CASTing Funktionen für INTEGER vs BOOLEAN2
CREATE FUNCTION int4(boolean2)
   RETURNS int4
   AS 'bool_int4'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolean2(int4)
   RETURNS boolean2
   AS 'int4_bool'
   LANGUAGE internal STRICT;

-- der eigentliche CAST für INTEGER vs BOOLEAN2
CREATE CAST (boolean2 AS int4)
       WITH FUNCTION int4(boolean2)
         AS ASSIGNMENT;
CREATE CAST (int4 AS boolean2)
       WITH FUNCTION boolean2(int4)
         AS ASSIGNMENT;




-- Operatoren und Hilfsfunktionen
CREATE FUNCTION boollt(boolean2, boolean)
   RETURNS boolean
   AS 'boollt'
   LANGUAGE internal STRICT;
CREATE FUNCTION boollt(boolean, boolean2)
   RETURNS boolean
   AS 'boollt'
   LANGUAGE internal STRICT;
CREATE FUNCTION boollt(boolean2, boolean2)
   RETURNS boolean
   AS 'boollt'
   LANGUAGE internal STRICT;
CREATE OPERATOR < (
    PROCEDURE = boollt,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = >,
    NEGATOR = >=,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);
CREATE OPERATOR < (
    PROCEDURE = boollt,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = >,
    NEGATOR = >=,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);
CREATE OPERATOR < (
    PROCEDURE = boollt,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = >,
    NEGATOR = >=,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);

CREATE FUNCTION boolle(boolean2, boolean)
   RETURNS boolean
   AS 'boolle'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolle(boolean, boolean2)
   RETURNS boolean
   AS 'boolle'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolle(boolean2, boolean2)
   RETURNS boolean
   AS 'boolle'
   LANGUAGE internal STRICT;
CREATE OPERATOR <= (
    PROCEDURE = boolle,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = >=,
    NEGATOR = >,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
    PROCEDURE = boolle,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = >=,
    NEGATOR = >,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);
CREATE OPERATOR <= (
    PROCEDURE = boolle,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = >=,
    NEGATOR = >,
    RESTRICT = scalarltsel,
    JOIN = scalarltjoinsel
);

CREATE FUNCTION boolne(boolean2, boolean)
   RETURNS boolean
   AS 'boolne'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolne(boolean, boolean2)
   RETURNS boolean
   AS 'boolne'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolne(boolean2, boolean2)
   RETURNS boolean
   AS 'boolne'
   LANGUAGE internal STRICT;
CREATE OPERATOR <> (
    PROCEDURE = boolne,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = <>,
    NEGATOR = =,
    RESTRICT = neqsel,
    JOIN = neqjoinsel
);
CREATE OPERATOR <> (
    PROCEDURE = boolne,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = <>,
    NEGATOR = =,
    RESTRICT = neqsel,
    JOIN = neqjoinsel
);
CREATE OPERATOR <> (
    PROCEDURE = boolne,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = <>,
    NEGATOR = =,
    RESTRICT = neqsel,
    JOIN = neqjoinsel
);

CREATE FUNCTION booleq(boolean2, boolean)
   RETURNS boolean
   AS 'booleq'
   LANGUAGE internal STRICT;
CREATE FUNCTION booleq(boolean, boolean2)
   RETURNS boolean
   AS 'booleq'
   LANGUAGE internal STRICT;
CREATE FUNCTION booleq(boolean2, boolean2)
   RETURNS boolean
   AS 'booleq'
   LANGUAGE internal STRICT;
CREATE OPERATOR = (
    PROCEDURE = booleq,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = =,
    NEGATOR = <>,
    HASHES,
    RESTRICT = eqsel,
    JOIN = eqjoinsel,
    SORT1 = <,
    SORT2 = <,
    LTCMP = <,
    GTCMP = >
);
CREATE OPERATOR = (
    PROCEDURE = booleq,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = =,
    NEGATOR = <>,
    HASHES,
    RESTRICT = eqsel,
    JOIN = eqjoinsel,
    SORT1 = <,
    SORT2 = <,
    LTCMP = <,
    GTCMP = >
);
CREATE OPERATOR = (
    PROCEDURE = booleq,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = =,
    NEGATOR = <>,
    HASHES,
    RESTRICT = eqsel,
    JOIN = eqjoinsel,
    SORT1 = <,
    SORT2 = <,
    LTCMP = <,
    GTCMP = >
);

CREATE FUNCTION boolgt(boolean2, boolean)
   RETURNS boolean
   AS 'boolgt'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolgt(boolean, boolean2)
   RETURNS boolean
   AS 'boolgt'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolgt(boolean2, boolean2)
   RETURNS boolean
   AS 'boolgt'
   LANGUAGE internal STRICT;
CREATE OPERATOR > (
    PROCEDURE = boolgt,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = <,
    NEGATOR = <=,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);
CREATE OPERATOR > (
    PROCEDURE = boolgt,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = <,
    NEGATOR = <=,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);
CREATE OPERATOR > (
    PROCEDURE = boolgt,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = <,
    NEGATOR = <=,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);

CREATE FUNCTION boolge(boolean2, boolean)
   RETURNS boolean
   AS 'boolge'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolge(boolean, boolean2)
   RETURNS boolean
   AS 'boolge'
   LANGUAGE internal STRICT;
CREATE FUNCTION boolge(boolean2, boolean2)
   RETURNS boolean
   AS 'boolge'
   LANGUAGE internal STRICT;
CREATE OPERATOR >= (
    PROCEDURE = boolge,
    LEFTARG = boolean2,
    RIGHTARG = boolean,
    COMMUTATOR = <=,
    NEGATOR = <,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);
CREATE OPERATOR >= (
    PROCEDURE = boolge,
    LEFTARG = boolean,
    RIGHTARG = boolean2,
    COMMUTATOR = <=,
    NEGATOR = <,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);
CREATE OPERATOR >= (
    PROCEDURE = boolge,
    LEFTARG = boolean2,
    RIGHTARG = boolean2,
    COMMUTATOR = <=,
    NEGATOR = <,
    RESTRICT = scalargtsel,
    JOIN = scalargtjoinsel
);
-- fertig mit den Operatoren


-- Funktionen zum Unterstützen der Operatorenklassen
CREATE FUNCTION btboolcmp(boolean2, boolean)
   RETURNS int4
   AS 'btboolcmp'
   LANGUAGE internal STRICT;
CREATE FUNCTION btboolcmp(boolean, boolean2)
   RETURNS int4
   AS 'btboolcmp'
   LANGUAGE internal STRICT;
CREATE FUNCTION btboolcmp(boolean2, boolean2)
   RETURNS int4
   AS 'btboolcmp'
   LANGUAGE internal STRICT;


-- erstelle Operatorenklassen
-- das ist notwendig für die Index Unterstützung
CREATE OPERATOR CLASS _bool2_ops
    DEFAULT FOR TYPE boolean2[] USING gin AS
    STORAGE boolean2 ,
    OPERATOR 1 &&(anyarray,anyarray) ,
    OPERATOR 2 @>(anyarray,anyarray) ,
    OPERATOR 3 <@(anyarray,anyarray) RECHECK ,
    OPERATOR 4 =(anyarray,anyarray) RECHECK ,
    FUNCTION 1 btboolcmp(boolean2,boolean2) ,
    FUNCTION 2 ginarrayextract(anyarray,internal) ,
    FUNCTION 3 ginarrayextract(anyarray,internal) ,
    FUNCTION 4 ginarrayconsistent(internal,smallint,internal);

CREATE OPERATOR CLASS bool2_ops
    DEFAULT FOR TYPE boolean2 USING btree AS
    OPERATOR 1 <(boolean2,boolean2) ,
    OPERATOR 2 <=(boolean2,boolean2) ,
    OPERATOR 3 =(boolean2,boolean2) ,
    OPERATOR 4 >=(boolean2,boolean2) ,
    OPERATOR 5 >(boolean2,boolean2) ,
    FUNCTION 1 btboolcmp(boolean2,boolean2);

CREATE OPERATOR CLASS bool2_ops
    DEFAULT FOR TYPE boolean2 USING hash AS
    OPERATOR 1 =(boolean2,boolean2) ,
    FUNCTION 1 hashchar("char");

Seite 499

Beispiel für die Nutzung des SQL-Präprozessors

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


/* Exceptions müssen innerhalb des Quellcodes vor der Nutzung
 * definiert werden. Da die Exceptions vom Präprozessor
 * abgearbeitet werden, müssen die Defintionen jedoch nicht
 * innerhalb einer Funktion stehen. */
EXEC SQL WHENEVER SQLERROR SQLPRINT;


void ausgabe (void) {
  EXEC SQL BEGIN DECLARE SECTION;
  int id;
  VARCHAR inhalt[20];
  EXEC SQL END DECLARE SECTION;

  /* Bei einem Fehler gilt weiterhin die SQLPRINT Exception */

  EXEC SQL SELECT id, inhalt INTO :id, :inhalt FROM test;

  printf("\%i: \%s\n", id, inhalt.arr);


}

int main (void) {

  EXEC SQL CONNECT TO datenbank;


  /* Die SQLPRINT Exception hat keine Auswirkung auf die 
   * Funktion ausgabe(), da die Exception im Quellcode 
   * hinter der Funktion definiert wird */
  EXEC SQL WHENEVER SQLERROR STOP;

  /* allerdings hat die Änderung von SQLPRINT auf STOP Auswirkung
   * auf die nachfolgenden Anweisungen:
   * wenn die Tabelle "test" existiert, 
   * wird das Programm abgebrochen */

  EXEC SQL CREATE TABLE test (id SERIAL, inhalt VARCHAR(20));
  EXEC SQL INSERT INTO test (inhalt) VALUES ('Beispiel 1');
  EXEC SQL COMMIT;

  ausgabe();

  EXEC SQL DISCONNECT ALL;

  return 0;
}

Compilieren mit:

ecpg programm.pgc
gcc -Wall -lecpg -L`pg_config --libdir` -I`pg_config --includedir` -o programm programm.c

Seite 500

Ersatzfunktion für COUNT(*) Datentabelle

CREATE TABLE trigger_test (
  id        SERIAL         PRIMARY KEY,
  daten     TEXT           NOT NULL
);

Tabelle mit Zähler

CREATE TABLE trigger_count (
  id        SMALLINT       PRIMARY KEY
                           CHECK (id = 1),
  max_wert  INTEGER        NOT NULL,
  anzahl    INTEGER        NOT NULL
);

Zähler einfügen:

INSERT INTO trigger_count (id, max_wert, anzahl)
     VALUES (1, COALESCE(
(SELECT MAX(id) FROM trigger_test), 0),
             COALESCE(
(SELECT COUNT(id) FROM trigger_test), 0));

und überprüfen:

SELECT id, max_wert, anzahl FROM trigger_count;

Triggerfunktion zum Zählen erstellen

CREATE OR REPLACE FUNCTION
          trigger_count()
          RETURNS TRIGGER
AS $$
DECLARE
  loop_record RECORD;
BEGIN

  -- höchsten Wert herausfinden
  SELECT MAX(id) AS max_id,
         COUNT(id) AS anzahl
    INTO loop_record FROM trigger_test;
  IF loop_record.anzahl IS NULL THEN
    -- es gibt keine Einträge in der Tabelle
    UPDATE trigger_count SET max_wert = 0, anzahl = 0;
  ELSE
    -- den Maximalwert in die zweite Tabelle übertragen
    UPDATE trigger_count
       SET max_wert = loop_record.max_id,
           anzahl = loop_record.anzahl;
  END IF;

  RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

Trigger erstellen

 CREATE TRIGGER trigger_update
  AFTER INSERT OR UPDATE OR DELETE
     ON trigger_test FOR EACH ROW
EXECUTE PROCEDURE trigger_count();

Testdaten einfügen

INSERT INTO trigger_test (daten) VALUES ('Zeile 1');
INSERT INTO trigger_test (daten) VALUES ('Zeile 2');
INSERT INTO trigger_test (daten) VALUES ('Zeile 3');

Daten und Counter anschauen

SELECT id, daten FROM trigger_test;
SELECT id, max_wert, anzahl FROM trigger_count;

alle Einträge löschen

DELETE FROM trigger_test;

Daten und Counter anschauen

SELECT id, daten FROM trigger_test;
SELECT id, max_wert, anzahl FROM trigger_count;

Seite 502

Setzen der aktuellen Zeit

CREATE TABLE zeitstempel_demo (
  id             SERIAL       PRIMARY KEY,
  eingefuegt     TIMESTAMPTZ
                 NOT NULL,
  aktualisiert   TIMESTAMPTZ
                 NOT NULL,
  daten          VARCHAR,
  ...
);
CREATE OR REPLACE FUNCTION aktuelle_zeit_setzen()
                  RETURNS trigger
AS $$
BEGIN
    IF TG_OP = 'INSERT' THEN
        NEW.eingefuegt := NOW();
    ELSE
        -- ursprünglichen Wert behalten beim Update
        NEW.eingefuegt := OLD.eingefuegt;
    END IF;

    -- Zeit der letzten Änderung setzen
    NEW.aktualisiert := NOW(); 

    RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
 CREATE TRIGGER addiere_aktuelle_zeit
 BEFORE INSERT OR UPDATE
     ON zeitstempel_demo
    FOR EACH ROW
EXECUTE PROCEDURE aktuelle_zeit_setzen();

INSERT testen

INSERT INTO zeitstempel_demo (daten) VALUES ('abc');
SELECT id, eingefuegt, aktualisiert, daten FROM zeitstempel_demo;

UPDATE testen

UPDATE zeitstempel_demo
   SET eingefuegt = '2020-01-01 00:00:00',
       aktualisiert = '2020-01-01 00:00:00',
       daten = 'def'
 WHERE id = 1;
SELECT id, eingefuegt, aktualisiert, daten FROM zeitstempel_demo;

Seite 503

Implizite Casts aus PostgreSQL 8.2

  CREATE FUNCTION pg_catalog.text(integer)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(int4out($1));';

  CREATE CAST (integer AS text)
    WITH FUNCTION pg_catalog.text(integer)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(smallint)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(int2out($1));';

  CREATE CAST (smallint AS text)
    WITH FUNCTION pg_catalog.text(smallint)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(oid)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(oidout($1));';

  CREATE CAST (oid AS text)
    WITH FUNCTION pg_catalog.text(oid)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(date)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(date_out($1));';

  CREATE CAST (date AS text)
    WITH FUNCTION pg_catalog.text(date)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(double precision)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(float8out($1));';

  CREATE CAST (double precision AS text)
    WITH FUNCTION pg_catalog.text(double precision)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(real)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(float4out($1));';

  CREATE CAST (real AS text)
    WITH FUNCTION pg_catalog.text(real)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(time with time zone)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(timetz_out($1));';

  CREATE CAST (time with time zone AS text)
    WITH FUNCTION pg_catalog.text(time with time zone)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(time without time zone)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(time_out($1));';

  CREATE CAST (time without time zone AS text)
    WITH FUNCTION pg_catalog.text(time without time zone)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(timestamp with time zone)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(timestamptz_out($1));';

  CREATE CAST (timestamp with time zone AS text)
    WITH FUNCTION pg_catalog.text(timestamp with time zone)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(interval)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(interval_out($1));';

  CREATE CAST (interval AS text)
    WITH FUNCTION pg_catalog.text(interval)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(bigint)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(int8out($1));';

  CREATE CAST (bigint AS text)
    WITH FUNCTION pg_catalog.text(bigint)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(numeric)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(numeric_out($1));';

  CREATE CAST (numeric AS text)
    WITH FUNCTION pg_catalog.text(numeric)
      AS IMPLICIT;

  CREATE FUNCTION pg_catalog.text(timestamp without time zone)
 RETURNS text STRICT IMMUTABLE
LANGUAGE SQL
      AS 'SELECT textin(timestamp_out($1));';

  CREATE CAST (timestamp without time zone AS text)
     WITH FUNCTION pg_catalog.text(timestamp without time zone)
       AS IMPLICIT;

Die ursprüngliche Version dieser CASTs und Funktionen wurde von Peter Eisentraut in seinem Blog veröffentlicht.