CLOSE
-BeispieleIN AUTONOMOUS TRANSACTION
Ausführen einer Anweisung oder eines Anweisungsblocks in einer autonomen Transaktion
PSQL
IN AUTONOMOUS TRANSACTION DO <compound_statement>
Argument | Beschreibung |
---|---|
compound_statement |
Ein Statement oder ein Block von Statements |
Eine Anweisung IN AUTONOMOUS TRANSACTION
ermöglicht die Ausführung einer Anweisung oder eines Anweisungsblocks in einer autonomen Transaktion.Code, der in einer autonomen Transaktion ausgeführt wird, wird unmittelbar nach seiner erfolgreichen Ausführung unabhängig vom Status seiner übergeordneten Transaktion festgeschrieben.Dies kann erforderlich sein, wenn bestimmte Vorgänge nicht zurückgesetzt werden sollen, auch wenn in der übergeordneten Transaktion ein Fehler auftritt.
Eine autonome Transaktion hat dieselbe Isolationsstufe wie ihre übergeordnete Transaktion.Jede Ausnahme, die im Block des autonomen Transaktionscodes ausgelöst wird, führt dazu, dass die autonome Transaktion zurückgesetzt wird und alle vorgenommenen Änderungen storniert werden.Wenn der Code erfolgreich ausgeführt wird, wird die autonome Transaktion festgeschrieben.
IN AUTONOMOUS TRANSACTION
-BeispieleVerwendung einer autonomen Transaktion in einem Trigger für das Datenbankereignis ON CONNECT
, um alle Verbindungsversuche, einschließlich der fehlgeschlagenen, zu protokollieren:
CREATE TRIGGER TR_CONNECT ON CONNECT
AS
BEGIN
-- Logging all attempts to connect to the database
IN AUTONOMOUS TRANSACTION DO
INSERT INTO LOG(MSG)
VALUES ('USER ' || CURRENT_USER || ' CONNECTS.');
IF (EXISTS(SELECT *
FROM BLOCKED_USERS
WHERE USERNAME = CURRENT_USER)) THEN
BEGIN
-- Logging that the attempt to connect
-- to the database failed and sending
-- a message about the event
IN AUTONOMOUS TRANSACTION DO
BEGIN
INSERT INTO LOG(MSG)
VALUES ('USER ' || CURRENT_USER || ' REFUSED.');
POST_EVENT 'CONNECTION ATTEMPT BY BLOCKED USER!';
END
-- now calling an exception
EXCEPTION EX_BADUSER;
END
END
POST_EVENT
Benachrichtigung von Listening-Clients über Datenbankereignisse in einem Modul
PSQL
POST_EVENT event_name;
Argument | Beschreibung |
---|---|
event_name |
Ereignisname (Nachricht) ist auf 127 Byte beschränkt |
Die Anweisung POST_EVENT
benachrichtigt den Ereignismanager über das Ereignis, das es in einer Ereignistabelle speichert.Wenn die Transaktion festgeschrieben ist, benachrichtigt der Ereignismanager Anwendungen, die ihr Interesse an dem Ereignis signalisieren.
Der Ereignisname kann eine Art Code oder eine kurze Nachricht sein: Die Auswahl ist offen, da es sich nur um eine Zeichenfolge mit bis zu 127 Bytes handelt.
Der Inhalt des Strings kann ein Stringliteral, eine Variable oder ein beliebiger gültiger SQL-Ausdruck sein, der in einen String aufgelöst wird.
POST_EVENT
-BeispieleBenachrichtigen der Listening-Anwendungen über das Einfügen eines Datensatzes in die SALES
-Tabelle:
CREATE TRIGGER POST_NEW_ORDER FOR SALES
ACTIVE AFTER INSERT POSITION 0
AS
BEGIN
POST_EVENT 'new_order';
END
RETURN
Einen Wert aus einer gespeicherten Funktion zurückgeben
PSQL
RETURN value;
Argument | Beschreibung |
---|---|
value |
Ausdruck mit dem zurückzugebenden Wert;Kann jeder Ausdruckstyp sein, der mit dem Rückgabetyp der Funktion kompatibel ist |
Die RETURN
-Anweisung beendet die Ausführung einer Funktion und gibt den Wert des Ausdrucks value zurück.
RETURN
kann nur in PSQL-Funktionen (gespeicherte und lokale Funktionen) verwendet werden.
RETURN
-BeispieleSiehe auch CREATE FUNCTION
-Beispiele
DECLARE VARIABLE
Eine lokale Variable deklarieren
PSQL
DECLARE [VARIABLE] varname <domain_or_non_array_type> [NOT NULL] [COLLATE collation] [{DEFAULT | = } <initvalue>]; <domain_or_non_array_type> ::= !! Siehe auch Skalardatentypen !! <initvalue> ::= <literal> | <context_var>
Argument | Beschreibung |
---|---|
varname |
Name der lokalen Variablen |
collation |
Sortierreihenfolge |
initvalue |
Anfangswert für diese Variable |
literal |
Literal eines Typs, der mit dem Typ der lokalen Variablen kompatibel ist |
context_var |
Jede Kontextvariable, deren Typ mit dem Typ der lokalen Variablen kompatibel ist |
Die Anweisung DECLARE [VARIABLE]
wird verwendet, um eine lokale Variable zu deklarieren.Das Schlüsselwort VARIABLE
kann weggelassen werden.Für jede lokale Variable ist eine DECLARE [VARIABLE]
-Anweisung erforderlich.Es können beliebig viele DECLARE [VARIABLE]
-Anweisungen in beliebiger Reihenfolge eingefügt werden.Der Name einer lokalen Variablen muss unter den Namen der für das Modul deklarierten lokalen Variablen und Ein- und Ausgabeparameter eindeutig sein.
Note
|
Ein Sonderfall von |
Eine lokale Variable kann einen beliebigen SQL-Typ außer einem Array sein.
Als Typ kann ein Domainname angegeben werden;die Variable erbt alle ihre Attribute.
Wenn stattdessen die Klausel TYPE OF domain
verwendet wird, erbt die Variable nur den Datentyp der Domäne und gegebenenfalls deren Zeichensatz- und Kollatierungsattribute.Alle Standardwerte oder Einschränkungen wie NOT NULL
oder CHECK
Einschränkungen werden nicht vererbt.
Wenn die Option TYPE OF COLUMN relation.column>
verwendet wird, um aus einer Spalte in einer Tabelle oder Ansicht zu „borgen“, erbt die Variable nur den Datentyp der Spalte und gegebenenfalls den Zeichensatz und die Kollatierung Attribute.Alle anderen Attribute werden ignoriert.
NICHT NULL
-BeschränkungFür lokale Variablen können Sie die Einschränkung NOT NULL
angeben, wodurch NULL
-Werte für die Variable nicht zugelassen werden.Wenn als Datentyp eine Domäne angegeben wurde und die Domäne bereits die Einschränkung NOT NULL
hat, ist die Deklaration unnötig.Für andere Formen, einschließlich der Verwendung einer Domäne, die null zulässt, kann die Einschränkung NOT NULL
bei Bedarf eingefügt werden.
CHARACTER SET
- und COLLATE
-KlauselnSofern nicht anders angegeben, sind der Zeichensatz und die Kollatierungssequenz einer String-Variablen die Datenbank-Standardwerte.Eine CHARACTER SET
-Klausel kann bei Bedarf eingefügt werden, um Zeichenfolgendaten zu verarbeiten, die in einem anderen Zeichensatz vorliegen.Eine gültige Kollatierungssequenz (COLLATE
-Klausel) kann auch mit oder ohne Zeichensatz-Klausel eingeschlossen werden.
Lokale Variablen sind NULL
, wenn die Ausführung des Moduls beginnt.Sie können initialisiert werden, so dass ein Start- oder Standardwert verfügbar ist, wenn sie zum ersten Mal referenziert werden.Es kann die Form DEFAULT <initvalue>
verwendet werden oder nur der Zuweisungsoperator
.Der Wert kann ein beliebiges typkompatibles Literal oder eine Kontextvariable sein, einschließlich `=
’: `= <initvalue>NULL
.
Tip
|
Stellen Sie sicher, dass Sie diese Klausel für alle Variablen verwenden, die eine |
CREATE OR ALTER PROCEDURE SOME_PROC
AS
-- Deklaration einer Variablen vom Typ INT
DECLARE I INT;
-- Eine Variable vom Typ INT deklarieren, die NULL nicht zulässt
DECLARE VARIABLE J INT NOT NULL;
-- Deklarieren einer Variablen vom Typ INT mit dem Standardwert 0
DECLARE VARIABLE K INT DEFAULT 0;
-- Deklarieren einer Variablen vom Typ INT mit dem Standardwert 1
DECLARE VARIABLE L INT = 1;
-- Deklarieren einer Variablen basierend auf der COUNTRYNAME-Domain
DECLARE FARM_COUNTRY COUNTRYNAME;
-- Deklarieren einer Variablen des Typs gleich der Domäne COUNTRYNAME
DECLARE FROM_COUNTRY TYPE OF COUNTRYNAME;
-- Deklarieren einer Variablen mit dem Typ der Spalte CAPITAL in der Tabelle COUNTRY
DECLARE CAPITAL TYPE OF COLUMN COUNTRY.CAPITAL;
BEGIN
/* PSQL-Anweisungen */
END
DECLARE .. CURSOR
Deklarieren eines benannten Cursors
PSQL
DECLARE [VARIABLE] cursor_name [[NO] SCROLL] CURSOR FOR (<select>);
Argument | Beschreibung |
---|---|
cursorname |
Name des Cursors |
select |
|
Die DECLARE … CURSOR … FOR
-Anweisung bindet einen benannten Cursor an die Ergebnismenge, die in der SELECT
-Anweisung erhalten wurde, die in der FOR
-Klausel angegeben ist.Im Body-Code kann der Cursor geöffnet, zum zeilenweisen Durchlaufen der Ergebnismenge verwendet und geschlossen werden.Während der Cursor geöffnet ist, kann der Code positionierte Aktualisierungen und Löschungen durchführen, indem das WHERE CURRENT OF
in der UPDATE
- oder DELETE
-Anweisung verwendet wird.
Note
|
Syntaktisch ist die |
Der Cursor kann nur vorwärts (unidirektional) oder scrollbar sein.Die optionale Klausel SCROLL
macht den Cursor scrollbar, die NO SCROLL
Klausel nur vorwärts.Standardmäßig sind Cursor nur vorwärts.
Nur-Vorwärts-Cursor können sich – wie der Name schon sagt – im Datensatz nur vorwärts bewegen.Vorwärtscursor unterstützen nur die Anweisung FETCH [NEXT FROM]
, andere Befehle geben einen Fehler aus.Scrollbare Cursor ermöglichen es Ihnen, sich im Datensatz nicht nur vorwärts, sondern auch rückwärts zu bewegen, sowie N Positionen relativ zur aktuellen Position.
Warning
|
Scrollbare Cursor werden als temporäres Dataset materialisiert und verbrauchen daher zusätzlichen Speicher oder Festplattenspeicher. Verwenden Sie sie also nur, wenn Sie sie wirklich brauchen. |
Die optionale FOR UPDATE
-Klausel kann in die SELECT
-Anweisung aufgenommen werden, ihr Fehlen verhindert jedoch nicht die erfolgreiche Ausführung eines positionierten Updates oder Deletes
Es sollte darauf geachtet werden, dass die Namen deklarierter Cursor nicht mit Namen kollidieren, die später in Anweisungen für AS CURSOR
-Klauseln verwendet werden
Wenn der Cursor nur zum Durchlaufen der Ergebnismenge benötigt wird, ist es fast immer einfacher und weniger fehleranfällig, eine FOR SELECT
-Anweisung mit der AS CURSOR
-Klausel zu verwenden.Deklarierte Cursor müssen explizit geöffnet, zum Abrufen von Daten verwendet und geschlossen werden.Die Kontextvariable ROW_COUNT
muss nach jedem Fetch überprüft werden und wenn ihr Wert null ist, muss die Schleife beendet werden.Eine FOR SELECT
-Anweisung macht dies automatisch.
Dennoch bieten deklarierte Cursor ein hohes Maß an Kontrolle über sequentielle Ereignisse und ermöglichen die parallele Verwaltung mehrerer Cursor.
Die SELECT
-Anweisung kann Parameter enthalten. Zum Beispiel:
SELECT NAME || :SFX FROM NAMES WHERE NUMBER = :NUM
Jeder Parameter muss zuvor als PSQL-Variable deklariert worden sein, auch wenn sie als Ein- und Ausgabeparameter stammen.Beim Öffnen des Cursors wird dem Parameter der aktuelle Wert der Variablen zugewiesen.
Warning
|
Instabile Variablen und Cursors
Wenn sich der Wert der PSQL-Variablen, die in der Beachten Sie insbesondere, dass das Verhalten vom Abfrageplan abhängen kann, insbesondere von den verwendeten Indizes.Derzeit gibt es keine strengen Regeln für dieses Verhalten, und dies kann sich in zukünftigen Versionen von Firebird ändern. |
Deklarieren eines benannten Cursors im Trigger.
CREATE OR ALTER TRIGGER TBU_STOCK
BEFORE UPDATE ON STOCK
AS
DECLARE C_COUNTRY CURSOR FOR (
SELECT
COUNTRY,
CAPITAL
FROM COUNTRY
);
BEGIN
/* PSQL statements */
END
Einen scrollbaren Cursor deklarieren
EXECUTE BLOCK
RETURNS (
N INT,
RNAME CHAR(63))
AS
- Declaring a scrollable cursor
DECLARE C SCROLL CURSOR FOR (
SELECT
ROW_NUMBER() OVER (ORDER BY RDB$RELATION_NAME) AS N,
RDB$RELATION_NAME
FROM RDB$RELATIONS
ORDER BY RDB$RELATION_NAME);
BEGIN
/ * PSQL-Anweisungen * /
END
Eine Sammlung von Skripten zum Erstellen von Ansichten mit einem PSQL-Block unter Verwendung von benannten Cursorn.
EXECUTE BLOCK
RETURNS (
SCRIPT BLOB SUB_TYPE TEXT)
AS
DECLARE VARIABLE FIELDS VARCHAR(8191);
DECLARE VARIABLE FIELD_NAME TYPE OF RDB$FIELD_NAME;
DECLARE VARIABLE RELATION RDB$RELATION_NAME;
DECLARE VARIABLE SOURCE TYPE OF COLUMN RDB$RELATIONS.RDB$VIEW_SOURCE;
DECLARE VARIABLE CUR_R CURSOR FOR (
SELECT
RDB$RELATION_NAME,
RDB$VIEW_SOURCE
FROM
RDB$RELATIONS
WHERE
RDB$VIEW_SOURCE IS NOT NULL);
-- Declaring a named cursor where
-- a local variable is used
DECLARE CUR_F CURSOR FOR (
SELECT
RDB$FIELD_NAME
FROM
RDB$RELATION_FIELDS
WHERE
-- It is important that the variable must be declared earlier
RDB$RELATION_NAME = :RELATION);
BEGIN
OPEN CUR_R;
WHILE (1 = 1) DO
BEGIN
FETCH CUR_R
INTO :RELATION, :SOURCE;
IF (ROW_COUNT = 0) THEN
LEAVE;
FIELDS = NULL;
-- The CUR_F cursor will use the value
-- of the RELATION variable initiated above
OPEN CUR_F;
WHILE (1 = 1) DO
BEGIN
FETCH CUR_F
INTO :FIELD_NAME;
IF (ROW_COUNT = 0) THEN
LEAVE;
IF (FIELDS IS NULL) THEN
FIELDS = TRIM(FIELD_NAME);
ELSE
FIELDS = FIELDS || ', ' || TRIM(FIELD_NAME);
END
CLOSE CUR_F;
SCRIPT = 'CREATE VIEW ' || RELATION;
IF (FIELDS IS NOT NULL) THEN
SCRIPT = SCRIPT || ' (' || FIELDS || ')';
SCRIPT = SCRIPT || ' AS ' || ASCII_CHAR(13);
SCRIPT = SCRIPT || SOURCE;
SUSPEND;
END
CLOSE CUR_R;
END
DECLARE FUNCTION
Deklaration einer lokalen Variablen
PSQL
<declare-subfunc> ::= <subfunc-forward> | <subfunc-def>
<subfunc-forward> ::= <subfunc-header>;
<subfunc-def> ::= <subfunc-header> <psql-module-body>
<subfunc-header> ::=
DECLARE FUNCTION subfuncname [ ( [ <in_params> ] ) ]
RETURNS <domain_or_non_array_type> [COLLATE collation]
[DETERMINISTIC]
<in_params> ::=
!! Siehe CREATE FUNCTION
-Syntax !!
<domain_or_non_array_type> ::=
!! Siehe Syntax für skalare Datentypen !!
<psql-module-body> ::=
!! Siehe Syntax des Modulbodys !!
Argument | Beschreibung |
---|---|
subfuncname |
Unterfunktionsname |
collation |
Kollationsname |
Die Anweisung DECLARE FUNCTION
deklariert eine Unterfunktion.Eine Unterfunktion ist nur für das PSQL-Modul sichtbar, das die Unterfunktion definiert hat.
Unterfunktionen haben eine Reihe von Einschränkungen:
Eine Unterfunktion kann nicht in eine andere Unterroutine eingebettet werden.Unterroutinen werden nur in PSQL-Modulen der obersten Ebene unterstützt (gespeicherte Prozeduren, gespeicherte Funktionen, Trigger und anonyme PSQL-Blöcke).Diese Einschränkung wird durch die Syntax nicht erzwungen, aber Versuche, verschachtelte Unterfunktionen zu erstellen, führen zu einem Fehler “feature is not supported” mit der Detailmeldung “nested sub function”.
Derzeit hat die Unterfunktion keinen direkten Zugriff auf Variablen und Cursor aus ihrem Elternmodul.Es kann jedoch von seinen Elternmodulen auf andere Routinen zugreifen, einschließlich rekursiver Aufrufe an sich selbst.In einigen Fällen kann eine Vorwärtsdeklaration der Routine erforderlich sein.
Eine Unterfunktion kann vorwärts deklariert werden, um gegenseitige Abhängigkeiten zwischen Unterprogrammen aufzulösen, und muss von ihrer tatsächlichen Definition gefolgt werden.Wenn eine Unterfunktion forward-deklariert ist und Parameter mit Standardwerten hat, sollten die Standardwerte nur in der forward-Deklaration angegeben und nicht in subfunc_def wiederholt werden.
Note
|
Wenn Sie eine Unterfunktion mit demselben Namen wie eine gespeicherte Funktion deklarieren, wird diese gespeicherte Funktion aus Ihrem Modul ausgeblendet.Es ist nicht möglich, diese gespeicherte Funktion aufzurufen. |
Note
|
Im Gegensatz zu |
Unterfunktion innerhalb einer gespeicherten Funktion
CREATE OR ALTER FUNCTION FUNC1 (n1 INTEGER, n2 INTEGER)
RETURNS INTEGER
AS
- Subfunction
DECLARE FUNCTION SUBFUNC (n1 INTEGER, n2 INTEGER)
RETURNS INTEGER
AS
BEGIN
RETURN n1 + n2;
END
BEGIN
RETURN SUBFUNC (n1, n2);
END
Rekursiver Funktionsaufruf
execute block returns (i integer, o integer)
as
-- Rekursive Funktion ohne Vorwärtsdeklaration.
declare function fibonacci(n integer) returns integer
as
begin
if (n = 0 or n = 1) then
return n;
else
return fibonacci(n - 1) + fibonacci(n - 2);
end
begin
i = 0;
while (i < 10)
do
begin
o = fibonacci(i);
suspend;
i = i + 1;
end
end
DECLARE PROCEDURE
Deklaration eines Unterverfahrens
PSQL
<declare-subproc> ::= <subproc-forward> | <subproc-def>
<subproc-forward> ::= <subproc-header>;
<subproc-def> ::= <subproc-header> <psql-module-body>
<subproc-header> ::=
DECLARE subprocname [ ( [ <in_params> ] ) ]
[RETURNS (<out_params>)]
<in_params> ::=
!! Siehe auch CREATE PROCEDURE
-Syntax !!
<domain_or_non_array_type> ::=
!! Siehe auch Syntax für skalare Datentypen !!
<psql-module-body> ::=
!! Siehe auch Syntax des Modul-Bodys !!
Argument | Beschreibung |
---|---|
subprocname |
Name des Unterverfahrens |
collation |
Kollationsname |
Die Anweisung DECLARE PROCEDURE
deklariert eine Unterprozedur.Eine Unterprozedur ist nur für das PSQL-Modul sichtbar, das die Unterprozedur definiert hat.
Unterverfahren haben eine Reihe von Einschränkungen:
Eine Unterprozedur kann nicht in eine andere Unterroutine geschachtelt werden.Unterroutinen werden nur in PSQL-Modulen der obersten Ebene unterstützt (gespeicherte Prozeduren, gespeicherte Funktionen, Trigger und anonyme PSQL-Blöcke).Diese Einschränkung wird durch die Syntax nicht erzwungen, aber Versuche, verschachtelte Unterprozeduren zu erstellen, führen zu einem Fehler “feature is not supported” mit der Detailmeldung “nested sub procedure”.
Derzeit hat die Unterprozedur keinen direkten Zugriff, um Variablen und Cursor aus ihrem Elternmodul zu verwenden.Es kann von seinen Elternmodulen auf andere Routinen zugreifen.In einigen Fällen kann eine Voranmeldung erforderlich sein.
Eine Unterprozedur kann vorwärts deklariert werden, um gegenseitige Abhängigkeiten zwischen Unterroutinen aufzulösen, und muss von ihrer tatsächlichen Definition gefolgt werden.Wenn eine Unterprozedur vorwärts deklariert ist und Parameter mit Standardwerten hat, sollten die Standardwerte nur in der Vorwärtsdeklaration angegeben und nicht in subproc_def wiederholt werden.
Note
|
Wenn Sie eine Unterprozedur mit demselben Namen wie eine gespeicherte Prozedur, Tabelle oder Ansicht deklarieren, wird diese gespeicherte Prozedur, Tabelle oder Ansicht von Ihrem Modul ausgeblendet.Es ist nicht möglich, diese gespeicherte Prozedur, Tabelle oder Ansicht aufzurufen. |
Note
|
Im Gegensatz zu |
EXECUTE BLOCK
+
EXECUTE BLOCK
RETURNS (name VARCHAR(63))
AS
-- Unterprozedur, die eine Liste von Tabellen zurückgibt
DECLARE PROCEDURE get_tables
RETURNS (table_name VARCHAR(63))
AS
BEGIN
FOR SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$VIEW_BLR IS NULL
INTO table_name
DO SUSPEND;
END
-- Unterprozedur, die eine Liste von Ansichten zurückgibt
DECLARE PROCEDURE get_views
RETURNS (view_name VARCHAR(63))
AS
BEGIN
FOR SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$VIEW_BLR IS NOT NULL
INTO view_name
DO SUSPEND;
END
BEGIN
FOR SELECT table_name
FROM get_tables
UNION ALL
SELECT view_name
FROM get_views
INTO name
DO SUSPEND;
END
Mit Vorwärtsdeklaration und Parameter mit Standardwert
execute block returns (o integer)
as
-- Vorwärtsdeklaration von P1.
declare procedure p1(i integer = 1) returns (o integer);
-- Vorwärtsdeklaration von P2.
declare procedure p2(i integer) returns (o integer);
-- Die Implementierung von P1 sollte den Parameterstandardwert nicht neu deklarieren.
declare procedure p1(i integer) returns (o integer)
as
begin
execute procedure p2(i) returning_values o;
end
declare procedure p2(i integer) returns (o integer)
as
begin
o = i;
end
begin
execute procedure p1 returning_values o;
suspend;
end
BEGIN … END
Einen Block von Anweisungen abgrenzen
PSQL
<block> ::= BEGIN [<compound_statement> ...] END <compound_statement> ::= {<block> | <statement>}
Das Konstrukt BEGIN … END
ist eine zweiteilige Anweisung, die einen Block von Anweisungen umschließt, die als eine Codeeinheit ausgeführt werden.Jeder Block beginnt mit der Halbanweisung "BEGIN" und endet mit der anderen Halbanweisung "END".Blöcke können mit einer maximalen Tiefe von 512 verschachtelten Blöcken verschachtelt werden.Ein Block kann leer sein, sodass sie als Stubs fungieren können, ohne dass Dummy-Anweisungen geschrieben werden müssen.
Die Anweisungen BEGIN und END haben keine Zeilenabschlusszeichen (Semikolon).Beim Definieren oder Ändern eines PSQL-Moduls im Dienstprogramm isql erfordert diese Anwendung jedoch, dass der letzten END
-Anweisung ein eigenes Abschlusszeichen folgt, das zuvor mit SET TERM
in eine andere Zeichenfolge als umgestellt wurde ein Semikolon.Dieser Terminator ist nicht Teil der PSQL-Syntax.
Die letzte oder äußerste END
-Anweisung in einem Trigger beendet den Trigger.Was die letzte END
-Anweisung in einer Stored Procedure macht, hängt vom Prozedurtyp ab:
In einer auswählbaren Prozedur gibt die letzte END
-Anweisung die Kontrolle an den Aufrufer zurück und gibt SQLCODE 100 zurück, was angibt, dass keine weiteren Zeilen zum Abrufen vorhanden sind
In einer ausführbaren Prozedur gibt die letzte END
-Anweisung die Kontrolle an den Aufrufer zurück, zusammen mit den aktuellen Werten aller definierten Ausgabeparameter.
BEGIN … END
-Beispieleemployee.fdb
, die die einfache Verwendung von BEGIN…END
-Blöcken zeigt:SET TERM ^;
CREATE OR ALTER PROCEDURE DEPT_BUDGET (
DNO CHAR(3))
RETURNS (
TOT DECIMAL(12,2))
AS
DECLARE VARIABLE SUMB DECIMAL(12,2);
DECLARE VARIABLE RDNO CHAR(3);
DECLARE VARIABLE CNT INTEGER;
BEGIN
TOT = 0;
SELECT BUDGET
FROM DEPARTMENT
WHERE DEPT_NO = :DNO
INTO :TOT;
SELECT COUNT(BUDGET)
FROM DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :CNT;
IF (CNT = 0) THEN
SUSPEND;
FOR SELECT DEPT_NO
FROM DEPARTMENT
WHERE HEAD_DEPT = :DNO
INTO :RDNO
DO
BEGIN
EXECUTE PROCEDURE DEPT_BUDGET(:RDNO)
RETURNING_VALUES :SUMB;
TOT = TOT + SUMB;
END
SUSPEND;
END^
SET TERM ;^
IF … THEN … ELSE
Bedingte Verzweigung
PSQL
IF (<condition>) THEN <compound_statement> [ELSE <compound_statement>]
Argument | Beschreibung |
---|---|
condition |
Eine logische Bedingung, die TRUE, FALSE oder UNKNOWN zurückgibt |
compound_statement |
Eine einzelne Anweisung oder zwei oder mehr Anweisungen, die in |
Die bedingte Sprunganweisung IF … THEN
wird verwendet, um den Ausführungsprozess in einem PSQL-Modul zu verzweigen.Die Bedingung ist immer in Klammern eingeschlossen.Wenn es den Wert TRUE zurückgibt, verzweigt die Ausführung in die Anweisung oder den Anweisungsblock nach dem Schlüsselwort THEN
.Wenn eine ELSE
vorhanden ist und die Bedingung FALSE oder UNKNOWN zurückgibt, verzweigt die Ausführung in die Anweisung oder den Anweisungsblock danach.
PSQL bietet keine fortgeschritteneren Multi-Branch-Sprünge wie CASE
oder SWITCH
.Es ist jedoch möglich, IF … THEN … ELSE
-Anweisungen zu verketten, siehe den Beispielabschnitt unten.Alternativ steht die CASE
-Anweisung von DSQL in PSQL zur Verfügung und kann zumindest einige Anwendungsfälle nach Art eines Schalters erfüllen:
CASE <test_expr> WHEN <expr> THEN <result> [WHEN <expr> THEN <result> ...] [ELSE <defaultresult>] END CASE WHEN <bool_expr> THEN <result> [WHEN <bool_expr> THEN <result> ...] [ELSE <defaultresult>] END
...
C = CASE
WHEN A=2 THEN 1
WHEN A=1 THEN 3
ELSE 0
END;
...
IF
-BeispieleEin Beispiel mit der IF
-Anweisung.Angenommen, die Variablen FIRST
, LINE2
und LAST
wurden früher deklariert.
...
IF (FIRST IS NOT NULL) THEN
LINE2 = FIRST || ' ' || LAST;
ELSE
LINE2 = LAST;
...
Da IF … THEN … ELSE
eine Anweisung ist, ist es möglich, sie miteinander zu verketten.Angenommen, die Variablen INT_VALUE
und STRING_VALUE
wurden früher deklariert.
IF (INT_VALUE = 1) THEN
STRING_VALUE = 'one';
ELSE IF (INT_VALUE = 2) THEN
STRING_VALUE = 'two';
ELSE IF (INT_VALUE = 3) THEN
STRING_VALUE = 'three';
ELSE
STRING_VALUE = 'too much';
Dieses spezielle Beispiel kann durch ein Einfaches CASE
oder die Funktion DECODE
ersetzt werden.
WHILE … DO
Schleifenkonstrukte
PSQL
[label:] WHILE <condition> DO <compound_statement>
Argument | Beschreibung |
---|---|
label |
Optionales Label für |
condition |
Eine logische Bedingung, die TRUE, FALSE oder UNKNOWN zurückgibt |
compound_statement |
Zwei oder mehr Anweisungen, die in |
Eine WHILE
-Anweisung implementiert das Schleifenkonstrukt in PSQL.Die Anweisung oder der Anweisungsblock wird ausgeführt, bis die Bedingung TRUE zurückgibt.Schleifen können beliebig tief verschachtelt werden.
WHILE … DO
-BeispieleEine Prozedur, die die Summe der Zahlen von 1 bis I berechnet, zeigt, wie das Schleifenkonstrukt verwendet wird.
CREATE PROCEDURE SUM_INT (I INTEGER)
RETURNS (S INTEGER)
AS
BEGIN
s = 0;
WHILE (i > 0) DO
BEGIN
s = s + i;
i = i - 1;
END
END
Ausführen der Prozedur in isql:
EXECUTE PROCEDURE SUM_INT(4);
Das Ergebnis ist:
S
==========
10
Firebird verfügt über ein nützliches Lexikon von PSQL-Anweisungen und -Ressourcen zum Abfangen von Fehlern in Modulen und deren Behandlung.Firebird verwendet integrierte Ausnahmen, die bei Fehlern ausgelöst werden, die beim Arbeiten mit DML- und DDL-Anweisungen auftreten.
Im PSQL-Code werden Ausnahmen mit der WHEN
-Anweisung behandelt.Das Behandeln einer Ausnahme im Code beinhaltet entweder das Beheben des Problems vor Ort oder das Überwinden des Problems.Bei beiden Lösungen kann die Ausführung fortgesetzt werden, ohne dass eine Ausnahmenachricht an den Client zurückgegeben wird.
Eine Ausnahme führt dazu, dass die Ausführung im aktuellen Block beendet wird.Anstatt die Ausführung an die END
-Anweisung zu übergeben, bewegt sich die Prozedur ausgehend von dem Block, in dem die Ausnahme abgefangen wurde, durch Ebenen verschachtelter Blöcke nach außen und sucht nach dem Code des Handlers, der diese Ausnahme „kennt“.Es stoppt die Suche, wenn es die erste WHEN
-Anweisung findet, die diese Ausnahme behandeln kann.
Eine Ausnahme ist eine Nachricht, die generiert wird, wenn ein Fehler auftritt.
Alle von Firebird behandelten Ausnahmen haben vordefinierte numerische Werte für Kontextvariablen (Symbole) und damit verbundene Textnachrichten.Fehlermeldungen werden standardmäßig in Englisch ausgegeben.Es sind lokalisierte Firebird-Builds verfügbar, bei denen Fehlermeldungen in andere Sprachen übersetzt werden.
Vollständige Auflistungen der Systemausnahmen finden Sie in Anhang B: Ausnahmecodes und Meldungen:
Benutzerdefinierte Ausnahmen können in der Datenbank als persistente Objekte deklariert und im PSQL-Code aufgerufen werden, um bestimmte Fehler zu signalisieren;B. um bestimmte Geschäftsregeln durchzusetzen.Eine benutzerdefinierte Ausnahme besteht aus einem Bezeichner und einer Standardnachricht von 1021 Byte.Weitere Informationen finden Sie unter CREATE EXCEPTION
.
EXCEPTION
Eine benutzerdefinierte Ausnahme auslösen oder eine Ausnahme erneut auslösen
PSQL
EXCEPTION [ exception_name [ custom_message | USING (<value_list>)] ] <value_list> ::= <val> [, <val> ...]
Argument | Beschreibung |
---|---|
exception_name |
Name der Ausnahme |
custom_message |
Alternativer Nachrichtentext, der an die Aufruferschnittstelle zurückgegeben wird, wenn eine Ausnahme ausgelöst wird.Die maximale Länge der Textnachricht beträgt 1.021 Byte |
val |
Wertausdruck, der Parameter-Slots im Ausnahmenachrichtentext ersetzt |
Eine Anweisung EXCEPTION
löst die benutzerdefinierte Ausnahme mit dem angegebenen Namen aus.Ein alternativer Nachrichtentext von bis zu 1.021 Byte kann optional den Standardnachrichtentext der Ausnahme überschreiben.
Die Standardausnahmenachricht kann Slots für Parameter enthalten, die beim Auslösen einer Ausnahme gefüllt werden können.Um Parameterwerte an eine Ausnahme zu übergeben, verwenden Sie die USING
-Klausel.Betrachten wir in der Reihenfolge von links nach rechts, dass jeder Parameter, der in der Anweisung zum Auslösen von Ausnahmen als “the Nth” übergeben wird, mit N beginnend bei 1:
Wenn der Nte Parameter nicht übergeben wird, wird sein Slot nicht ersetzt
Wird ein NULL
Parameter übergeben, wird der Slot durch den String “*** null ***
” ersetzt
Werden mehr Parameter übergeben, als in der Ausnahmemeldung definiert sind, werden die überzähligen ignoriert
Die maximale Anzahl von Parametern beträgt 9
Die maximale Nachrichtenlänge einschließlich Parameterwerten beträgt 1053 Byte
Note
|
Der Statusvektor wird durch diese Codekombination Der verwendete Fehlercode ( |
Warning
|
Wenn die message eine Parameter-Slot-Nummer enthält, die größer als 9 ist, werden die zweite und die nachfolgenden Ziffern als Literaltext behandelt.Zum Beispiel wird Als Beispiel:
Dies erzeugt die folgende Ausgabe Statement failed, SQLSTATE = HY000 exception 1 -EX1 -something wrong in abcdefghi a0 a1 |
Ausnahmen können in einer [fblangref40-psql-when-de]-Anweisung behandelt werden.Wenn eine Ausnahme in einem Modul nicht behandelt wird, werden die Auswirkungen der in diesem Modul ausgeführten Aktionen aufgehoben und das aufrufende Programm empfängt die Ausnahme (entweder den Standardtext oder den benutzerdefinierten Text).
Innerhalb des Ausnahmebehandlungsblocks — und nur darin — kann die abgefangene Ausnahme erneut ausgelöst werden, indem die EXCEPTION
-Anweisung ohne Parameter ausgeführt wird.Wenn er sich außerhalb des Blocks befindet, hat der erneut ausgelöste EXCEPTION
-Aufruf keine Wirkung.
Note
|
Custom exceptions are stored in the system table |
EXCEPTION
-BeispieleAuslösen einer Ausnahme bei einer Bedingung in der gespeicherten Prozedur SHIP_ORDER
:
CREATE OR ALTER PROCEDURE SHIP_ORDER (
PO_NUM CHAR(8))
AS
DECLARE VARIABLE ord_stat CHAR(7);
DECLARE VARIABLE hold_stat CHAR(1);
DECLARE VARIABLE cust_no INTEGER;
DECLARE VARIABLE any_po CHAR(8);
BEGIN
SELECT
s.order_status,
c.on_hold,
c.cust_no
FROM
sales s, customer c
WHERE
po_number = :po_num AND
s.cust_no = c.cust_no
INTO :ord_stat,
:hold_stat,
:cust_no;
IF (ord_stat = 'shipped') THEN
EXCEPTION order_already_shipped;
/* Other statements */
END
Eine Ausnahme bei einer Bedingung auslösen und die ursprüngliche Nachricht durch eine alternative Nachricht ersetzen:
CREATE OR ALTER PROCEDURE SHIP_ORDER (
PO_NUM CHAR(8))
AS
DECLARE VARIABLE ord_stat CHAR(7);
DECLARE VARIABLE hold_stat CHAR(1);
DECLARE VARIABLE cust_no INTEGER;
DECLARE VARIABLE any_po CHAR(8);
BEGIN
SELECT
s.order_status,
c.on_hold,
c.cust_no
FROM
sales s, customer c
WHERE
po_number = :po_num AND
s.cust_no = c.cust_no
INTO :ord_stat,
:hold_stat,
:cust_no;
IF (ord_stat = 'shipped') THEN
EXCEPTION order_already_shipped
'Order status is "' || ord_stat || '"';
/* Other statements */
END
Verwenden einer parametrisierten Ausnahme:
CREATE EXCEPTION EX_BAD_SP_NAME
'Name of procedures must start with' '@ 1' ':' '@ 2' '' ;
...
CREATE TRIGGER TRG_SP_CREATE BEFORE CREATE PROCEDURE
AS
DECLARE SP_NAME VARCHAR(255);
BEGIN
SP_NAME = RDB$GET_CONTEXT ('DDL_TRIGGER' , 'OBJECT_NAME');
IF (SP_NAME NOT STARTING 'SP_') THEN
EXCEPTION EX_BAD_SP_NAME USING ('SP_', SP_NAME);
END
Logging an error and re-throwing it in the WHEN
block:
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10))
AS
BEGIN
INSERT INTO country (country,
currency)
VALUES (:ACountryName,
:ACurrency);
WHEN ANY DO
BEGIN
-- write an error in log
IN AUTONOMOUS TRANSACTION DO
INSERT INTO ERROR_LOG (PSQL_MODULE,
GDS_CODE,
SQL_CODE,
SQL_STATE)
VALUES ('ADD_COUNTRY',
GDSCODE,
SQLCODE,
SQLSTATE);
-- Re-throw exception
EXCEPTION;
END
END
WHEN … DO
Eine Ausnahme abfangen und den Fehler behandeln
PSQL
WHEN {<error> [, <error> ...] | ANY} DO <compound_statement> <error> ::= { EXCEPTION exception_name | SQLCODE number | GDSCODE errcode | SQLSTATE sqlstate_code }
Argument | Beschreibung |
---|---|
exception_name |
Name der Ausnahme |
number |
SQLCODE-Fehlercode |
errcode |
Symbolischer GDSCODE-Fehlername |
sqlstate_code |
String-Literal mit dem SQLSTATE-Fehlercode |
compound_statement |
Eine einzelne Anweisung oder ein Block von Anweisungen |
Die Anweisung WHEN … DO
wird verwendet, um Fehler und benutzerdefinierte Ausnahmen zu behandeln.Die Anweisung erfasst alle Fehler und benutzerdefinierten Ausnahmen, die nach dem Schlüsselwort WHEN
aufgeführt sind.Wenn WHEN
das Schlüsselwort ANY
folgt, fängt die Anweisung jeden Fehler oder jede benutzerdefinierte Ausnahme ab, auch wenn sie bereits in einer WHEN
-Anweisung weiter oben im Block behandelt wurden.
Der WHEN … DO
-Block muss sich am Ende eines Anweisungsblocks befinden, vor der Anweisung END
des Blocks.
Auf das Schlüsselwort DO
folgt eine Anweisung oder ein Anweisungsblock innerhalb eines BEGIN … END
-Wrappers, der die Ausnahme behandelt.Die Kontextvariablen SQLCODE
, GDSCODE
und SQLSTATE
stehen im Kontext dieser Anweisung oder dieses Blocks zur Verfügung.Die Funktion RDB$ERROR
kann verwendet werden, um den SQLCODE, GDSCODE, SQLSTATE, den benutzerdefinierten Ausnahmenamen und die Ausnahmemeldung abzurufen.Die Anweisung EXCEPTION
ohne Parameter kann auch in diesem Kontext verwendet werden, um den Fehler oder die Ausnahme erneut zu werfen.
GDSCODE
Das Argument für die Klausel WHEN GDSCODE
ist der symbolische Name, der der intern definierten Ausnahme zugeordnet ist, z.B. grant_obj_notfound
für den GDS-Fehler 335544551.
In einer Anweisung oder einem Anweisungsblock der DO
-Klausel wird eine GDSCODE
-Kontextvariable verfügbar, die den numerischen Code enthält.Dieser numerische Code ist erforderlich, wenn Sie eine GDSCODE
-Ausnahme mit einem gezielten Fehler vergleichen möchten.Um ihn mit einem bestimmten Fehler zu vergleichen, müssen Sie einen numerischen Wert verwenden, zum Beispiel 335544551
für grant_obj_notfound
.
Ähnliche Kontextvariablen sind für SQLCODE
und SQLSTATE
verfügbar.
Die Anweisung oder der Block WHEN … DO
wird nur ausgeführt, wenn eines der von seinen Bedingungen betroffenen Ereignisse zur Laufzeit eintritt.Wenn die Anweisung WHEN … DO
ausgeführt wird, wird die Ausführung auch dann fortgesetzt, als ob kein Fehler aufgetreten wäre: Der Fehler oder die benutzerdefinierte Ausnahme beendet weder die Operationen des Triggers oder der gespeicherten Prozedur noch setzt sie diese zurück.
Wenn jedoch die WHEN … DO
-Anweisung oder der Block nichts zur Behandlung oder Behebung des Fehlers tut, wird die DML-Anweisung (SELECT
, INSERT
, UPDATE
, DELETE
, MERGE
), die den Fehler verursacht hat, error wird zurückgesetzt und keine der Anweisungen darunter im selben Anweisungsblock wird ausgeführt.
Important
|
|
WHEN … DO
-AnweisungEine Anweisung WHEN … DO
fängt Fehler und Ausnahmen im aktuellen Anweisungsblock ab.Es fängt auch ähnliche Ausnahmen in verschachtelten Blöcken ab, wenn diese Ausnahmen nicht in ihnen behandelt wurden.
Alle Änderungen, die vor der Anweisung vorgenommen wurden, die den Fehler verursacht hat, sind für eine WHEN … DO
-Anweisung sichtbar.Wenn Sie jedoch versuchen, sie in einer autonomen Transaktion zu protokollieren, sind diese Änderungen nicht verfügbar, da die Transaktion, bei der die Änderungen stattfanden, zu dem Zeitpunkt, zu dem die autonome Transaktion gestartet wird, nicht festgeschrieben ist.Das untere Beispiel 4 zeigt dieses Verhalten.
Tip
|
Bei der Behandlung von Ausnahmen ist es manchmal wünschenswert, die Ausnahme zu behandeln, indem eine Protokollnachricht geschrieben wird, um den Fehler zu markieren und die Ausführung über den fehlerhaften Datensatz hinaus fortsetzen zu lassen.Logs können in reguläre Tabellen geschrieben werden, aber dabei gibt es ein Problem: Die Log-Records werden “verschwinden”, wenn ein nicht behandelter Fehler dazu führt, dass das Modul nicht mehr ausgeführt wird und ein Rollback durchgeführt wird.Die Verwendung von external tables kann hier sinnvoll sein, da die Daten, die in diese geschrieben werden, transaktionsunabhängig sind.Die verknüpfte externe Datei ist weiterhin vorhanden, unabhängig davon, ob der Gesamtprozess erfolgreich ist oder nicht. |
WHEN…DO
Ersetzen des Standardfehlers durch einen benutzerdefinierten Fehler:
CREATE EXCEPTION COUNTRY_EXIST '';
SET TERM ^;
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10) )
AS
BEGIN
INSERT INTO country (country, currency)
VALUES (:ACountryName, :ACurrency);
WHEN SQLCODE -803 DO
EXCEPTION COUNTRY_EXIST 'Country already exists!';
END^
SET TERM ^;
Einen Fehler protokollieren und erneut in den WHEN
-Block werfen:
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10) )
AS
BEGIN
INSERT INTO country (country,
currency)
VALUES (:ACountryName,
:ACurrency);
WHEN ANY DO
BEGIN
-- write an error in log
IN AUTONOMOUS TRANSACTION DO
INSERT INTO ERROR_LOG (PSQL_MODULE,
GDS_CODE,
SQL_CODE,
SQL_STATE,
MESSAGE)
VALUES ('ADD_COUNTRY',
GDSCODE,
SQLCODE,
SQLSTATE,
RDB$ERROR(MESSAGE));
-- Re-throw exception
EXCEPTION;
END
END
Behandeln mehrerer Fehler in einem WHEN
-Block
...
WHEN GDSCODE GRANT_OBJ_NOTFOUND,
GDSCODE GRANT_FLD_NOTFOUND,
GDSCODE GRANT_NOPRIV,
GDSCODE GRANT_NOPRIV_ON_BASE
DO
BEGIN
EXECUTE PROCEDURE LOG_GRANT_ERROR(GDSCODE,
RDB$ERROR(MESSAGE);
EXIT;
END
...
Abfangen von Fehlern mit dem SQLSTATE-Code
EXECUTE BLOCK
AS
DECLARE VARIABLE I INT;
BEGIN
BEGIN
I = 1/0;
WHEN SQLSTATE '22003' DO
EXCEPTION E_CUSTOM_EXCEPTION
'Numeric value out of range.';
WHEN SQLSTATE '22012' DO
EXCEPTION E_CUSTOM_EXCEPTION
'Division by zero.';
WHEN SQLSTATE '23000' DO
EXCEPTION E_CUSTOM_EXCEPTION
'Integrity constraint violation.';
END
END