2012-04-07 7 views
1

Ich entwickle ein Framework, das dynamisch Tabellen für die Speicherung von Inhalten auf PostgreSQL 9.1 erstellt. Eine der API-Funktionen ermöglicht dem Aufrufer das Speichern eines neuen Inhaltseintrags durch Angabe aller Felder innerhalb eines bestimmten Objekts (z. B. Webformular). Um eine Reihe von Feldern zu erhalten, erstellt Framework einen zusammengesetzten Typ.Ist es möglich, explizite Umwandlungen für zusammengesetzte Typen in plpgsql-Funktionen zu vermeiden?

Betrachten Sie den folgenden Code ein:

CREATE SEQUENCE seq_contents MINVALUE 10000; 
CREATE TABLE contents (
    content_id  int8  not null, 
    is_edited  boolean  not null default false, 
    is_published boolean  not null default false, 
    "Input1"  varchar(60), 
    "CheckBox1"  int2, 
    "TheBox"  varchar(60), 
    "Slider1"  varchar(60) 
); 
CREATE TYPE "contentsType" AS (
    "Input1"  varchar(60), 
    "CheckBox1"  int2, 
    "TheBox"  varchar(60), 
    "Slider1"  varchar(60) 
); 
CREATE OR REPLACE FUNCTION push(in_all anyelement) RETURNS int8 AS $push$ 
DECLARE 
    _c_id int8; 
BEGIN 
    SELECT nextval('seq_contents') INTO _c_id; 

    EXECUTE $$INSERT INTO contents 
    SELECT a.*, b.* 
     FROM (SELECT $1, true, false) AS a, 
      (SELECT $2.*) AS b$$ USING _c_id, in_all; 

    RETURN _c_id; 
END; 
$push$ LANGUAGE plpgsql; 

Nun, um diese Funktion ich so explizite Umwandlung, hinzufügen müssen, rufen:

SELECT push(('input1',1,'thebox','slider1')::"contentsType"); 

Gibt es eine Möglichkeit explizite Umwandlung zu vermeiden? Da ich möchte, dass externe Anrufer sich nicht mit Umwandlungen befassen, d. H. Die Logik hinter den PostgreSQL-Funktionen verbergen. Zur Zeit habe ich einen solchen Fehler:

SELECT push(('input1',1,'thebox','slider1')); 
ERROR: PL/pgSQL functions cannot accept type record 
CONTEXT: compilation of PL/pgSQL function "push" near line 1 
+0

Basierend auf dem Fehler 'PL/pgSQL Funktionen können Typ record nicht annehmen 'nehme ich an, dass dies PL/pgSQL Problem ist. Also könnte eine der Optionen diese Funktion in C machen. – vyegorov

Antwort

1

Haben Sie in Betracht gezogen, die Datensatzvariable Textdarstellung zu übergeben? Theoretisch kann jede Datensatzvariable mit dem normalen CAST-Operator in und aus Text umgewandelt werden.Hier

ist die Funktion modifiziert, so dass in_all hat Text eingeben und wird zu "contentsType" in der USING-Klausel gegossen:

CREATE OR REPLACE FUNCTION push(in_all text) RETURNS int8 AS $push$ 
DECLARE 
    _c_id int8; 
BEGIN 
    SELECT nextval('seq_contents') INTO _c_id; 

    EXECUTE $$INSERT INTO contents 
    SELECT a.*, b.* 
     FROM (SELECT $1, true, false) AS a, 
      (SELECT $2.*) AS b$$ USING _c_id, in_all::"contentsType"; 

    RETURN _c_id; 
END; 
$push$ LANGUAGE plpgsql; 

Dann kann es wie folgt aus (kein expliziter Hinweis auf die Art) aufgerufen werden

select push('(input1,1,thebox,slider1)'); 

oder wie die

SELECT push(('input1',1,'thebox','slider1')::"contentsType"::text); 
(explizite Aufzeichnung in Text gegossen)

Das würde nicht nur mit "contentsType" funktionieren, sondern mit jedem anderen Datensatztyp, vorausgesetzt, die Funktion ist in der Lage, sie in diesen Typ zurück zu konvertieren.

Auch in plpgsql, nehme ich das so gut funktionieren sollte:

ret := push(r::text); 

wenn r eine variable Datensatz ist.

+0

Ich habe nicht selbst Textdarstellung verwenden. Danke, dass Sie darauf hingewiesen haben, ich werde mich für diese Lösung entscheiden und an der "C" -Funktion arbeiten, falls die Leistung nicht akzeptabel sein sollte. – vyegorov

1

Da Sie hart codierte den Tabellennamen, in die Sie einfügen möchten, und Sie haben eine feste Anzahl und Art der Parameter braucht es, ich bin mir nicht klar, warum Sie benötigen den Typ "contentsType" überhaupt. Warum nicht die zusätzliche Ebene der Klammern von der Funktion aufheben, und übergeben Sie einfach die vier Parameter direkt? Das hält alles einfacher.

CREATE OR REPLACE FUNCTION push(
    "Input1"  varchar(60), 
    "CheckBox1"  int2, 
    "TheBox"  varchar(60), 
    "Slider1"  varchar(60) 
) RETURNS int8 AS $push$ 
DECLARE 
    _c_id int8; 
BEGIN 
    SELECT nextval('seq_contents') INTO _c_id; 

    EXECUTE $$INSERT INTO contents 
    VALUES ($1, true, false, $2, %3, %4, $5) 
     $$ USING _c_id, "Input1", "CheckBox1", "TheBox", "Slider1"); 

    RETURN _c_id; 
END; 
$push$ LANGUAGE plpgsql; 

, dass die Funktion wie folgt aussehen macht Aufruf:

SELECT push('input1',1,'thebox','slider1'); 

Wenn Sie schauen, die push() Funktion verallgemeinern, so dass es für alle Tabellen funktioniert, werden Sie andere Probleme treffen, wenn Du kommst an diesem vorbei. Sie werden nicht darüber hinwegkommen können, dass die Funktion den Tabellennamen während der Ausführung kennen muss. Wenn Sie die Funktion überladen möchten, so dass Sie für jeden Datensatztyp einen separaten push() haben können, müssen Sie irgendwie Informationen zum Datensatztyp angeben. Also, wenn Sie etwas so machen wollen, ist die kurze Antwort auf Ihre Frage "Nein".

Auf der anderen Seite, machen Sie es vielleicht etwas schwieriger, als es sein muss. Ich hoffe, Ihnen ist bewusst, dass für jede Tabelle automatisch ein Typ mit demselben Namen wie die Tabelle erstellt wird. Sie könnten dies wahrscheinlich nutzen, um sowohl den Typ explizit zu deklarieren als auch einen Datensatz mit demselben Namen wie Ihre Tabelle zu übergeben - mit Dummy-Einträgen für die Werte, die die Funktion füllen wird. Ich denke, Sie könnten eine völlig generische Push-Funktion erstellen, obwohl es schwierig sein könnte, die starken Tippfehler in plpgsql zu überwinden. Das Schreiben der Funktion in C könnte einfacher sein, wenn Sie damit vertraut sind.

+0

Das war ein Beispiel. In Wirklichkeit unterscheidet sich der Name der Tabelle ('contents' in diesem Beispiel), ihre Struktur und Struktur vom zusammengesetzten Typ: nach Namen der Tabelle/Art, nach Anzahl der Spalten, nach Namen der Spalten, nach Typen der Säulen. – vyegorov

+0

Ein weiterer Kommentar. Ich möchte wirklich eine verallgemeinerte 'push()' Funktion haben. Und ich kann keinen Typ verwenden, der von PostgreSQL für die Tabelle erstellt wurde, da die Tabelle einige zusätzliche Felder enthält, die ich den externen Aufrufern nicht zugänglich mache. Ich möchte auch nicht überlasten. Also bleibe ich bei der C-Funktion. Danke für deine Antwort. – vyegorov

Verwandte Themen