Deling door nul afhandeling van Oracle-uitzonderingen. Kenmerken van foutafhandeling van de Oracle-databaseserver. Fouten veroorzaakt door beperkingen van externe sleutels

Ph.D. Vladimir Likhachev, Kaluga Pedagogische Universiteit vernoemd naar K.E

Voor programma's die met databases werken, is het niet alleen belangrijk om fouten correct te verwerken, maar ook om informatieve berichten over deze fouten te genereren. Door de aanwezigheid van dergelijke berichten kunt u snel oorzaken identificeren en fouten corrigeren. Dit geldt vooral als hij met een eindgebruikersprogramma werkt, omdat hij in de meeste gevallen niet alleen de structuur van een specifieke database kent, maar ook de theoretische grondslagen van relationele databases.

Vreemd genoeg is de situatie bij het genereren van foutmeldingen in programma's vaak heel anders dan de afhandeling van fouten zelf. Bij het afhandelen van fouten is het meestal mogelijk een algemene strategie te ontwikkelen, die het mogelijk maakt de afhandeling ervan in één of meerdere functies te lokaliseren. Een soortgelijke aanpak voor foutmeldingen kan worden geïmplementeerd op basis van het feit dat de Oracle-server in de foutmelding het type fout aangeeft en het databaseobject dat deze heeft veroorzaakt. Dergelijke objecten zijn meestal beperkingen, zoals primaire, unieke en externe sleutels, unieke indexen, "niet nul"-beperkingen, enz. Uit systeemtabellen en databaseweergaven kan gedetailleerde informatie over deze beperkingen worden verkregen en kunnen waarden worden bepaald die kunnen gewijzigd worden, resulteerde in een fout. Maar het probleem is dat de implementatie van een dergelijk mechanisme voor het genereren van foutmeldingen in echte toepassingen op een aantal problemen stuit:

  • Afhankelijkheid van de foutmelding van het doel van het programma. Zelfs programma's die met dezelfde database werken, moeten mogelijk verschillende berichten over dezelfde fout genereren. In een programma voor het bewerken van gegevens door de gebruiker zou de melding bijvoorbeeld moeten zijn: "Er is al een product met dezelfde naam geregistreerd! Controleer de productnaam!" Maar het gegevensimportprogramma vereist een bericht met een geheel andere inhoud: "De geïmporteerde gegevens zijn gedupliceerd - controleer de datum waarvoor de gegevens worden geïmporteerd!"
  • Moeilijkheden bij het genereren van berichten voor sommige fouten veroorzaakt door databasebeperkingen. CHECK-beperkingen voor tabellen kunnen bijvoorbeeld vrij complexe query's en voorwaarden gebruiken. Daarom kan het genereren van berichten op basis van hun analyse een behoorlijk moeilijke taak zijn.
  • Aangepaste tabel- en kolomnamen gebruiken in clientprogramma's die verschillen van hun namen in de database. De tabel heeft bijvoorbeeld de naam "GOODS" en in de clienttoepassing kunnen de gegevens uit deze tabel in de directory worden weergegeven als "Producten" of "Producten".

De combinatie van deze factoren leidt er meestal toe dat het genereren van berichten, zelfs over fouten van hetzelfde type, voor elke transactie afzonderlijk wordt geïmplementeerd. Als gevolg hiervan wordt de code voor het genereren van foutmeldingen verspreid over de applicatie, waardoor deze moeilijk te onderhouden is. Vanwege de noodzaak om voor bijna elke mogelijke fout code te schrijven, eindigen sommige fouten waarvan de ontwikkelaar op de hoogte is, zonder overeenkomstige berichten voor de gebruiker. Als resultaat worden er slechts voor een deel van de fouten voldoende informatieve berichten voor de eindgebruiker gegenereerd; in andere gevallen moet hij op zijn best tevreden zijn met berichten van de databaseserver zelf. In de meeste gevallen is de informatie-inhoud van dergelijke berichten voor de gemiddelde gebruiker onvoldoende om de oorzaak van het probleem te identificeren en op te lossen.

De in het artikel besproken methode voor het genereren van informatieve foutmeldingen voor de gebruiker is vrij universeel en kan zowel in clientapplicaties als aan de Oracle-serverzijde worden geïmplementeerd. Het kan in verschillende soorten programma's worden gebruikt, zoals:

  • Programma's die een speciale interface gebruiken om databasegegevens in te voeren en te wijzigen. In de meeste gevallen kunnen informatieve foutmeldingen worden verkregen door de databasestructuur te analyseren. Hierdoor wordt de gebruiker met minimale inspanning van de kant van ontwikkelaars en software op de hoogte gebracht van de oorzaak.
  • Programma's waarmee de gebruiker willekeurige SQL-query's kan construeren. Het genereren van berichten op basis van analyse van de databasestructuur kan vooral relevant zijn voor programma's die gericht zijn op een breed scala aan gebruikers, inclusief gebruikers met een laag kennisniveau op dit gebied. Hierdoor worden foutmeldingen in SQL-query's begrijpelijker voor de gebruiker.
  • Onderwerpplatforms. Door de methoden te gebruiken die in het artikel worden beschreven, kan het onderwerpplatform zelf informatieve berichten over databasefouten genereren op basis van een analyse van de structuur ervan. Dit maakt het mogelijk om de code in de platformtaal die wordt gebruikt om foutsituaties af te handelen, te verminderen. En fouten waarvoor speciale berichten nodig zijn, maar die er niet bleken te zijn, zullen informatief genoeg zijn om het identificeren van de oorzaak ervan veel gemakkelijker te maken.

De hierboven beschreven problemen bij het genereren van berichten kunnen worden opgelost als foutmeldingen in twee groepen worden verdeeld:

  • universele berichten die worden gegenereerd op basis van analyse van de databasestructuur;
  • speciale berichten die voor elke fout afzonderlijk worden gedefinieerd.

De methode voor het genereren van databasefoutmeldingen die in dit artikel wordt beschreven, kan op veel relationele databaseservers worden toegepast. Een voorbeeld van het gebruik ervan voor Firebird-serverdatabases wordt in het artikel besproken. Als de clientapplicatie is ontwikkeld in Object Pascal (Delphi, Kylix, Free Pascal), kunnen de mogelijkheden van de JEDI-bibliotheek nuttig zijn om de oorzaken van onverwachte fouten te identificeren.

1. Algemene foutmeldingen veroorzaakt door databasebeperkingen

Zoals hierboven vermeld is het hoofdidee van het maken van universele berichten het creëren van een bericht dat voldoende informatief en begrijpelijk is voor de eindgebruiker op basis van gegevens uit de foutmelding van Oracle en over de databasestructuur. Laten we aannemen dat de gebruiker in de tabel "GOEDEREN" (script 1.1) een product probeert toe te voegen met een titel (kolom "TITLE") die al in de tabel staat.

MAAK TABEL DEMO.GOODS (CODE INTEGER NOT NULL , TITEL VARCHAR2(50 byte) NOT NULL , PRICE NUMBER(16, 2) NOT NULL , CONSTRAINT CK_PRICE CHECK (PRIJS > 0), CONSTRAINT PK_GOODS PRIMARY KEY (CODE)); REAGEER OP TAFEL DEMO.GOODS is "Producten"; REACTIE OP KOLOM DEMO.GOODS.CODE is "Productcode"; COMMENTAAR OP KOLOM DEMO.GOODS.TITLE is "Titel"; REAGEER BIJ KOLOM DEMO.GOODS.PRICE is "Prijs"; MAAK EEN UNIEKE INDEX DEMO.IDX_GOODS_TITLE OP DEMO.GOODS (TITEL);

Script 1.1. De tabel "GOEDEREN" maken.

In dit geval genereert de server een fout, omdat de kolom "TITLE", waarin de productnaam wordt opgeslagen, is opgenomen in de unieke index "DEMO.IDX_GOODS_TITLE":

In plaats van dit bericht voor de gebruiker kan bijvoorbeeld één van de volgende berichten worden gegenereerd:

  • De waarde van het veld "Naam" in de tabel "Producten" moet uniek zijn!
  • Een product met deze naam is al geregistreerd! Controleer de productnaam!
  • De productdirectory mag geen producten met dezelfde naam bevatten!

Hoewel deze berichten verschillen, bevatten ze allemaal informatie over het object waarvoor de uniciteitsbeperking wordt geschonden - dit is het veld "Naam" van de tabel "Producten".

Een van de problemen bij het genereren van dit soort berichten is dat de gebruikersnamen van velden en tabellen verschillen van de namen van tabellen en kolommen in de database. Om ervoor te zorgen dat de gebruiker het foutbericht begrijpt, moet het aangepaste namen gebruiken. Een aparte tabel of opmerkingen voor tabellen en kolommen kunnen worden gebruikt om tabel- en veldnamen en hun aangepaste namen met elkaar te matchen. De laatste optie kan als meer de voorkeur worden beschouwd, omdat u hiermee tegelijkertijd de database kunt documenteren. Dat is de reden waarom in script 1.1 hun aangepaste namen worden gegeven als commentaar voor de tabel en de bijbehorende kolommen. Als u de bovenstaande berichten en opmerkingen voor de tabel en kolommen vergelijkt, zult u merken dat het genereren van het eerste bericht de eenvoudigste optie is. Om de andere twee berichten te genereren kan lexicale synthese nodig zijn, maar dit is een aparte taak. Ik zou uw aandacht willen vestigen op het feit dat verderop in het artikel slechts één van de mogelijke berichtopties voor elk foutgeval wordt gegeven. In de praktijk kan de keuze voor berichtstijl en inhoud afhankelijk zijn van een aantal factoren en zal worden bepaald door de systeemontwerper.

Uiteraard kunnen we een situatie niet uitsluiten waarin er geen opmerkingen zijn over een tabel of kolom die in het bericht zou moeten worden opgenomen. In deze situatie kan het foutbericht de tabel- of kolomnaam rechtstreeks weergeven.

2. De waarde van een verplicht veld is niet gespecificeerd (NIET NULL-beperking)

Deze fout wordt in verschillende gevallen door de server gegenereerd:

  • de beperking 'niet null' die voor de kolom is ingesteld, is geschonden;
  • De waarde van een kolom die deel uitmaakt van een unieke index, master of unieke sleutel is niet opgegeven.

In al deze gevallen genereert de server een fout:

Om een ​​tabel- en kolombeschrijving uit een foutmelding te verkrijgen, kunt u Query 2.1 gebruiken.

selecteer tc.comments als table_comment, cc.comments als column_comment van all_tab_columns c, all_tab_comments tc, all_col_comments cc waarbij c.owner = :eigenaar en c.table_name = :tabelnaam en c.column_name = :column_name en tc.owner = c.owner en tc.table_name = c.table_name en cc.owner = c.owner en cc.table_name = c.table_name en cc.column_name = c.column_name

Verzoek 2.1. Tabel- en kolombeschrijvingen ophalen

Als queryparameters "eigenaar", "tabelnaam", "kolomnaam" moet u respectievelijk de naam van het schema, de tabel en de kolom uit het foutbericht opgeven. De query retourneert opmerkingen voor de tabel en kolom.

Met behulp van de resultaten van dit verzoek kan een foutmelding worden gegenereerd met bijvoorbeeld de volgende inhoud:

U moet de kolomwaarde opgeven "<Описание поля>"in tafel"<Описание таблицы>" bij<добавлении новой/изменении>records.

3. Het unieke karakter van de waarde van een veld of reeks kolommen wordt geschonden

De noodzaak om een ​​unieke kolomwaarde in te voeren kan voornamelijk in drie gevallen vereist zijn:

  • de kolom is opgenomen in de hoofdsleutel;
  • de kolom is opgenomen in een unieke sleutel;
  • de kolom is opgenomen in een unieke index.

In alle drie de gevallen genereert Oracle Database dezelfde fout:
ORA-00001: unieke beperking geschonden (<Схема>.<Ограничение>)

Het foutbericht identificeert de beperking die de fout heeft veroorzaakt. Om informatie te verkrijgen over de kolommen die zijn opgenomen in de hoofd- of unieke sleutels, kunt u query 3.1 gebruiken om informatie over de index te verkrijgen - query 3.2.

selecteer dcs.constraint_type, cc.table_name, tc.comments als table_comment, cc.column_name, ccom.comments als column_comment van all_cons_columns cc sluit zich aan bij all_tab_comments tc op (tc.owner = cc.owner en tc.table_name = cc.table_name) sluit zich aan op all_col_comments ccom op (ccom.owner = cc.owner en ccom.table_name = cc.table_name en ccom.column_name = cc. column_name) voeg alle_constraints dcs toe op (dcs.constraint_name = cc.constraint_name) waarbij cc.owner =:eigenaar en cc.constraint_name =:sleutel_naam
Verzoek 3.1. Informatie verkrijgen over tabelkolommen die zijn opgenomen in de hoofd- of unieke sleutels.
selecteer ic.table_name, tc.comments als table_comment, ic.column_name, ccom.comments als column_comment van all_ind_columns ic sluit zich aan bij all_tab_comments tc op (tc.owner = ic.table_owner en tc.table_name = ic.table_name) sluit zich aan op all_col_comments ccom op (ccom.owner = ic.table_owner en ccom.table_name = ic.table_name en ccom.column_name = ic. kolom_naam) waarbij tabel_eigenaar = :eigenaar en index_naam = :index_naam
Verzoek 3.2. Informatie verkrijgen over de tabelkolommen die in de index zijn opgenomen.

Als parameters krijgen query's de schemanaam ("eigenaar"), sleutelnaam ("sleutel_naam") of indexnaam ("index_naam") doorgegeven. Query's retourneren namen en opmerkingen voor de tabellen en kolommen die in de beperking zijn opgenomen. Query 3.1 retourneert ook het type beperking ("constraint_type"): "P" is de hoofdsleutel, "U" is de unieke sleutel. Het aantal records dat door query's wordt geretourneerd, komt overeen met het aantal kolommen in de unieke beperking.

Op basis van de ontvangen informatie over de uniciteitsbeperking kunnen varianten van foutmeldingen voor de gebruiker worden gegenereerd, bijvoorbeeld de berichten die in Hoofdstuk 1 worden gegeven.

4. Fouten veroorzaakt door beperkingen van externe sleutels

Bij het uitvoeren van bewerkingen op tabelgegevens die zijn gekoppeld door externe sleutels, zijn er verschillende redenen die tot fouten leiden:

1. Er wordt een record toegevoegd aan de slave-tabel waarin de kolom in de refererende sleutel geen overeenkomstige waarde heeft in de hoofdtabel. Een soortgelijke situatie doet zich voor bij het wijzigen van de waarde van een kolom van een ondergeschikte tabel als de nieuwe kolomwaarde niet in de hoofdtabel staat. Oracle Database genereert in dit geval een fout:

  1. De hoofdtabel probeert de waarde te wijzigen van een kolom waarnaar wordt verwezen in een onderliggende tabel. In dit geval genereert Oracle Database een fout:
  1. De mastertabel probeert gegevens te verwijderen waarnaar wordt verwezen in de slavetabel. Als de definitie van een relatie tussen tabellen een beperking "NO ACTION" specificeert voor een gegevensverwijderingsbewerking, staat Oracle niet toe dat gegevens uit de hoofdtabel worden verwijderd als de onderliggende tabel records bevat die zijn gekoppeld aan het record dat wordt verwijderd. Voor deze situatie genereert Oracle Database een fout die vergelijkbaar is met het vorige geval.

Om informatie te verkrijgen over de kolommen van de hoofd- en onderliggende tabellen die in de refererende sleutel zijn opgenomen, kunt u de volgende query 4.1 gebruiken.

selecteer a.constraint_name, a.table_name, tc1.comments als table_comment, a2.column_name, cc1.comments als column_comment, b.owner als r_owner, b.table_name als r_table_name, tc2.comments als r_table_comment, b2.column_name als r_column_name, cc2 .comments als r_column_comment van all_constraints a, all_constraints b, all_cons_columns a2, all_cons_columns b2, all_tab_comments tc1, all_col_comments cc1, all_tab_comments tc2, all_col_comments cc2 waarbij a.owner =:eigenaar en a.constraint_type = "R" en a.constraint_name =:foreign_key en b.cons train_type in ("P", "U") en b.constraint_name = a.r_constraint_name en b.owner = a.r_owner en a2.constraint_name = a.constraint_name en a2.table_name = a.table_name en a2.owner = a.owner en b2.constraint_name = b.constraint_name en b2.table_name = b.table_name en b2.owner = b.owner en b2.position = a2.position en tc1.owner = a.owner en tc1.table_name = a.table_name en cc1. eigenaar = a2.eigenaar en cc1.tabelnaam = a2.tabelnaam en cc1.column_naam = a2.kolomnaam en tc2.eigenaar = b.eigenaar en tc2.tabelnaam = b.tabelnaam en cc2.eigenaar = b2.eigenaar en cc2.tabelnaam = b2.tabelnaam en cc2.kolomnaam = b2.kolomnaam
Verzoek 4.1. Informatie verkrijgen over een externe sleutel.

Het verzoek heeft twee parameters: "eigenaar" en "foreign_key" - het schema en de externe sleutel waarover u informatie moet verkrijgen. Het retourneert informatie over de kolommen die zijn opgenomen in de externe sleutel: "table_name", "table_comment" - de naam en beschrijving van de ondergeschikte tabel; "column_name", "column_comment" - naam en beschrijving van de kolom van de ondergeschikte tabel. Querykolommen met het voorvoegsel 'r_' retourneren informatie over de hoofdtabel. Het aantal records dat door de query wordt geretourneerd, komt overeen met het aantal kolommen in de externe sleutel.

Op basis van deze informatie kunnen foutmeldingen voor externe sleutels voor de gebruiker worden gegenereerd.

5. Speciale foutmeldingen veroorzaakt door databasebeperkingen

De noodzaak om speciale berichten te gebruiken kan zich voordoen als een universele foutmelding om welke reden dan ook niet kan worden gebruikt of kan worden gegenereerd. Een voorbeeld van het laatste geval zijn CHECK-beperkingen voor tabellen. Beperkingen kunnen vragen en voorwaarden met zich meebrengen die behoorlijk lastig te analyseren zijn. Daarom is het voor deze beperkingen vaak handiger om berichten te gebruiken die tijdens het ontwerp zijn gedefinieerd.

Er kunnen twee groepen bijzondere foutmeldingen worden onderscheiden. Het eerste type speciale berichten is bedoeld voor gebruik in alle toepassingen die met een gemeenschappelijke database werken. Deze kunnen losjes 'database-niveau-specifieke foutmeldingen' worden genoemd. De tweede groep berichten is specifiek voor een bepaalde toepassing. Ze kunnen nodig zijn wanneer verschillende applicaties verschillende berichten over dezelfde fout aan de gebruiker moeten presenteren. Ze kunnen ook wel 'speciale foutmeldingen op applicatieniveau' worden genoemd. Het is handig om informatie over de eerste groep berichten in de database zelf op te slaan en hiervoor een aparte tabel te gebruiken. Berichten die specifiek zijn voor een programma kunnen in de bronnen ervan worden opgeslagen, bijvoorbeeld in de vorm van een afzonderlijk bestand of ook in een database. Identificatie van speciale berichten kan worden gedaan op basis van de foutcode, de schemanaam en een of meer trefwoorden uit de foutmelding.

6. Foutmeldingen voor CHECK-beperkingen voor tabellen

Wanneer er een fout optreedt als gevolg van een CHECK-beperking op een tafel, genereert de server een fout:
ORA-02290: CONTROLEER integriteitsbeperking geschonden (<Схема>.<Имя ограничения>)

Zoals hierboven vermeld, is het vaak handig om speciale berichten voor dergelijke fouten te gebruiken. De beperking 'CK_PRICE' van de tabel 'GOODS' kan bijvoorbeeld een speciaal bericht gebruiken dat is opgeslagen in de speciale berichtentabel:

7. Geïntegreerd gebruik van speciale en generieke foutmeldingen

Het flexibele mechanisme voor het genereren van informatieve foutmeldingen voor de gebruiker wordt in verschillende fasen geïmplementeerd (Fig. 1):

1. Geef een speciaal foutbericht op applicatieniveau weer. Het programma zoekt eerst naar een foutmelding tussen applicatiespecifieke berichten. Als een dergelijk bericht wordt gevonden, wordt het weergegeven en is het genereren van het bericht voltooid.

2. Geef een speciaal bericht weer over een fout op databaseniveau. Als er in stap 1 geen bericht wordt gevonden, wordt er gezocht naar een specifiek foutbericht op databaseniveau. Indien gevonden, wordt deze aan de gebruiker getoond en eindigt het genereren van de foutmelding hier.

3. Het uitvoeren van een bericht op basis van analyse van de databasestructuur (universeel bericht). Als er in de voorgaande fasen geen speciale berichten zijn gedetecteerd, worden deze gegenereerd op basis van een analyse van de databasestructuur. Het wordt aan de gebruiker getoond en het genereren van het bericht is voltooid.

4. Voer een bericht uit vanaf de databaseserver. Als er in de drie voorgaande fasen geen melding voor de gebruiker is gegenereerd, wordt er een foutmelding van Oracle weergegeven. Deze situatie kan om verschillende redenen ontstaan. Wanneer er bijvoorbeeld een aangepaste fout optreedt die opzettelijk is gegenereerd in een opgeslagen procedure of trigger met behulp van de functie RAISE_APPLICATION_ERROR, en waarvan de inhoud van het bericht niet hoeft te worden gewijzigd.

Er zijn complexere gevallen mogelijk dan die in dit artikel worden gegeven. Bijvoorbeeld als een bericht wordt gegenereerd in een opgeslagen procedure, die op zijn beurt kan worden aangeroepen vanuit een trigger of een andere opgeslagen procedure. In dit geval hebt u mogelijk ook informatie nodig over hoe de procedure die de foutmelding heeft gegenereerd, is aangeroepen. En daardoor kan het oorspronkelijke bericht worden aangevuld of aangepast, bijvoorbeeld op basis van informatie over de call stack van opgeslagen procedures en triggers.

In sommige gevallen kunnen dergelijke berichten zelfs nog informatiever zijn dan de berichten die in de voorgaande fasen zijn gegenereerd. In plaats van de CK_PRICE-beperking voor de DEMO.GOODS-tabel (script 1.1), kunt u bijvoorbeeld de noodzakelijke controle in een trigger uitvoeren voordat u een record invoegt en bijwerkt, en een bericht voor de gebruiker genereren in een 'klaar'-formulier:

Als de productprijs kleiner of gelijk is aan nul, genereert de server een foutmelding, bijvoorbeeld:

De clientapplicatie kan dit bericht onmiddellijk en zonder wijziging doorgeven aan de gebruiker.

Een andere reden kan het optreden van een fout zijn waarvoor geen berichtgeneratie mogelijk is.

Rijst. 1. Volgorde voor het genereren van een databasefoutmelding.

Ik wil erop wijzen dat zelfs als de applicatie alleen speciale foutmeldingen gebruikt, het gebruik van een algemene functie voor het genereren van berichten de structuur van het programma zal verbeteren. Indien nodig kan het formaat van speciale berichten koppelingen naar het helpsysteem, afbeeldingen, enz. ondersteunen. De beschreven werkwijze voor het genereren van databasefoutmeldingen is meer gericht op implementatie in een clientapplicatie. Tegelijkertijd kan het aan de serverzijde worden gebruikt in opgeslagen procedures, tabeltriggers, maar ook in systeemtriggers voor de SERVERERROR-gebeurtenis van een database of schema.

Conclusie

Het doel van dit artikel is om de basisideeën te laten zien van een methode die kan worden gebruikt om informatieve Oracle-databasefoutmeldingen voor de eindgebruiker te genereren. Hoewel sommige aspecten van de implementatie buiten de reikwijdte van het artikel zijn gelaten, zou ik graag willen hopen dat de in het artikel beschreven aanpak de arbeidskosten bij softwareontwikkeling zal verlagen en de betrouwbaarheid en kwaliteit ervan zal vergroten.

Wanneer zich uitzonderingen voordoen, is het belangrijk om gebruiksvriendelijke foutmeldingen te bieden. Uitzonderingen werden al genoemd in de sectie over basis PL/SQL-blokken. Nu is het tijd om ze in meer detail te bekijken.

Uitzonderingen

Een uitzondering is een foutconditie die optreedt - of raakt opgewonden - wanneer er een probleem optreedt. Er zijn veel verschillende uitzonderingen, die elk verband houden met een specifiek type probleem. Wanneer er een uitzondering optreedt, stopt de uitvoering van de code bij de instructie die de uitzondering veroorzaakte, en wordt de controle overgedragen naar het deel van het blok dat de uitzondering afhandelt. Als het blok geen uitvoerbare sectie bevat, probeert PL/SQL een uitvoerbare sectie te vinden inbegrepen basiseenheid (omsluitend basisblok), d.w.z. in een blok dat extern is aan de code die de uitzondering veroorzaakte. Als er geen handler is voor een bepaalde uitzondering in het onmiddellijk omsluitende blok, gaat het zoeken door in blokken van opeenvolgende niveaus totdat een geschikte handler is gevonden. Als deze niet kan worden gevonden, wordt de programma-uitvoering beëindigd met een onverwerkte foutmelding.

Het van het blok is een ideale plek om informatieve foutmeldingen te geven en uit te voeren schoonmaak (opruimen), waarmee u alles kunt verwijderen dat later verwarring of problemen zou kunnen veroorzaken. Als er een uitzondering optreedt tijdens een procedure waarbij een rij in een tabel wordt ingevoegd, kan een typische opschoonprocedure een ROLLBACK-instructie bevatten.

Zodra de controle is overgedragen aan een uitzonderingshandler, wordt deze niet meer teruggestuurd naar de instructie die de uitzondering heeft veroorzaakt. In plaats daarvan wordt de controle overgedragen naar de omsluitende basisblokinstructie die onmiddellijk volgt op het geneste blok of de procedure/functie-aanroep.

Systeemuitzonderingen

U bent al bekend met de uitzondering ZERO_DIVIDE, die vooraf is gedefinieerd in PL/SQL. Er zijn nog een flink aantal andere systeemuitzonderingen die worden herkend en gegenereerd door PL/SQL of Oracle. Tabel 1 biedt een completere lijst met systeemuitzonderingen.

PL/SQL kan op twee manieren foutinformatie aan gebruikers verstrekken. De eerste manier is om de opdracht SQLCODE te gebruiken, die een foutcode retourneert. Deze code is een negatief getal, meestal gelijk aan het ORA-foutnummer dat wordt afgedrukt wanneer de toepassing wordt beëindigd als de uitzondering niet wordt afgehandeld. De tweede methode is het retourneren van een sms-bericht waarin de fout wordt beschreven. Het is niet verrassend dat het bijbehorende commando SQLERRM heet. U kunt SQLCODE of SQLERRM gebruiken in uw uitzonderingshandler. Opmerking: niet alle systeemuitzonderingen hebben een naam.

Tafel1 . Systeemuitzonderingen

Systeemuitzonderingfoutcode

Reden voor opwinding

CURSOR_ AL_ OPEN

ORA-06511

Er wordt geprobeerd een reeds geopende cursor te openen

DUP_VAL_ON_INDEX

ORA-00001

Er wordt geprobeerd een dubbele waarde in te voegen in een kolom die een unieke index heeft en dus een unieke beperking

ONGELDIG_ CURSOR

ORA-01001

Er wordt geprobeerd een cursor op te halen die niet is geopend, of er wordt geprobeerd een cursor te sluiten die niet is geopend

GEEN DATA GEVONDEN

ORA-01403

Proberen SELECT INTO uit te voeren wanneer SELECT nul rijen retourneert (en andere redenen die buiten het bestek van dit boek vallen)

PROGRAMMA_ FOUT

ORA-06501

Interne fout. Dit betekent doorgaans dat u contact moet opnemen met Oracle Support

OPSLAG_ FOUT

ORA-06500

Het programma heeft onvoldoende systeemgeheugen

TIMEOUT_ON_RESOURCE

ORA-00051

Het programma heeft te lang gewacht voordat een bepaalde bron beschikbaar kwam

TE_VEEL_RIJEN

ORA-01422

SELECT INTO in PL/SQL heeft meer dan één rij geretourneerd

WAARDE_ FOUT

ORA-06502

PL/SOL is een onjuiste gegevensconversie of -afkapping tegengekomen, of een onjuiste gegevensbeperking

NUL_ VERDELING

ORA-01476

Probeer te delen door nul

Alle overige uitzonderingen en interne fouten die niet vallen onder de uitzonderingen gedefinieerd in het basisblok. Wordt gebruikt in gevallen waarin u niet precies weet welke genoemde uitzondering moet worden afgehandeld en u eventuele uitzonderingen wilt afhandelen die worden gegenereerd

Laten we nu teruggaan naar het allereerste voorbeeld van dit hoofdstuk en daarin SQLCODE en SQLERRM gebruiken. Hieronder vindt u de broncode van het voorbeeld en de resultaten van de lancering ervan (Fig. 1).

Aantal_a NUMMER:= 6;

Aantal_b NUMMER;

Aantal_b:= 0;

Aantal_a:= Aantal_a / Aantal_b;

Aantal_b:= 7;

dbms_output.put_line(" Waarde van Num_b "|| Num_b);

UITZONDERING

WANNEER NUL_DIVIDE DAN

err_num NUMBER:= SQLCODE;

err_msg VARCHAR2(512) := SQLERRM;

dbms_output.put_line("ORA-foutnummer " || err_num);

dbms_output.put_line("ORA-foutmelding " || err_msg);

dbms_output.put_line("Waarde van Num_a " || Num_a);

dbms_output.put_line("Waarde van Num_b " || Num_b);

SQL> zet serveruitvoer aan

SQL>VERKLAREN

2 aantal_a NUMMER:= 6;

3 num_b NUMMER;

4 BEGIN

5 num_b:= 0;

6 aantal_a:= aantal_a / aantal_b;

7 aantal_b:= 7;

8 dbms_output.put_line(" Waarde van num_b "|| num_b);

9 UITZONDERING

10 WANNEER NUL_DIVIDE

11 DAN

13 err_num NUMMER:= SQLCODE;

14 err_msg VARCHAR2(512) := SQLERRM;

15 BEGIN

16 dbms_output.put_line("ORA-foutnummer "|| err_num);

17 dbms_output.put_line("ORA-foutmelding " || err_msg);

18 dbms_output.put_line("Waarde van num_a " || num_a);

19 dbms_output.put_line("Waarde van num_b " || num_b);

20 EINDE;

21 EINDE;

ORA-foutnummer -1476

ORA-foutmelding ORA-01476: deler is gelijk aan nul

Waarde van num_a 6

Waarde van num_b 0

PL/SQL-procedure met succes voltooid.

Rijst. 1. Gebruik van SQLCODE en SQLERRM bij het afhandelen van systeemuitzonderingen

In dit hoofdstuk bespreken we uitzonderingen in PL/SQL. Een uitzondering is een foutconditie tijdens de uitvoering van een programma. PL/SQL ondersteunt programmeurs om dergelijke omstandigheden op te vangen met behulp van UITZONDERING blokkeren in het programma en er wordt passende actie ondernomen tegen de foutconditie. Er zijn twee soorten uitzonderingen −

  • Door het systeem gedefinieerde uitzonderingen
  • Door de gebruiker gedefinieerde uitzonderingen

Syntaxis voor de afhandeling van uitzonderingen

De algemene syntaxis voor de afhandeling van uitzonderingen is als volgt. Hier kunt u zoveel uitzonderingen opsommen als u aankan. De standaarduitzondering wordt afgehandeld met WANNEER anderen DAN

VERKLAREN BEGINNEN UITZONDERING WHEN uitzondering1 THEN uitzondering1-afhandelingsinstructies WHEN uitzondering2 THEN uitzondering2-afhandelingsinstructies WHEN uitzondering3 THEN uitzondering3-afhandelingsinstructies ........ WHEN anderen THEN uitzondering3-afhandelingsinstructies END;

Voorbeeld

Laten we een code schrijven om het concept te illustreren. We zullen de tabel CUSTOMERS gebruiken die we in de voorgaande hoofdstukken hebben gemaakt en gebruikt −

VERKLAREN c_id klanten.id%type:= 8; c_naam klant.Naam%type; c_addr klanten.adres%type; BEGIN SELECTEER naam, adres INTO c_name, c_addr FROM klanten WAAR id = c_id; DBMS_OUTPUT.PUT_LINE("Naam: "|| c_naam); DBMS_OUTPUT.PUT_LINE("Adres: " || c_addr); UITZONDERING WANNEER no_data_found DAN dbms_output.put_line("Geen dergelijke klant!"); WANNEER anderen DAN dbms_output.put_line("Fout!"); EINDE; /

Niet zo'n klant! PL/SQL-procedure met succes voltooid.

Het bovenstaande programma toont de naam en het adres van een klant wiens ID is opgegeven. Omdat er geen klant met ID-waarde 8 in onze database aanwezig is, verhoogt het programma de runtime-uitzondering GEEN DATA GEVONDEN die is vastgelegd in de UITZONDERING blok.

Uitzonderingen opwerpen

Uitzonderingen worden automatisch gegenereerd door de databaseserver wanneer er een interne databasefout optreedt, maar uitzonderingen kunnen expliciet door de programmeur worden gegenereerd met behulp van de opdracht SALARISVERHOGING. Hieronder volgt de eenvoudige syntaxis voor het genereren van een uitzondering −

VERKLAREN uitzonderingsnaam UITZONDERING; BEGIN ALS voorwaarde DAN RAISE uitzonderingsnaam; STOP ALS; UITZONDERING WHEN uitzonderingsnaam THEN-instructie; EINDE;

U kunt de bovenstaande syntaxis gebruiken bij het verhogen van de standaarduitzondering van Oracle of een door de gebruiker gedefinieerde uitzondering. In de volgende sectie geven we u een voorbeeld van het genereren van een door de gebruiker gedefinieerde uitzondering. U kunt de standaarduitzonderingen van Oracle op een vergelijkbare manier verhogen.

Door de gebruiker gedefinieerde uitzonderingen

Met PL/SQL kunt u uw eigen uitzonderingen definiëren, afhankelijk van de behoefte van uw programma. Een door de gebruiker gedefinieerde uitzondering moet worden gedeclareerd en vervolgens expliciet worden gegenereerd, met behulp van een RAISE-instructie of de procedure DBMS_STANDARD.RAISE_APPLICATION_ERROR.

De syntaxis voor het declareren van een uitzondering is −

VERKLAREN mijn uitzondering UITZONDERING;

Voorbeeld

Het volgende voorbeeld illustreert het concept. Dit programma vraagt ​​om een ​​klant-ID, wanneer de gebruiker een ongeldig ID invoert, de uitzondering ongeldig identiteitsbewijs wordt verhoogd.

VERKLAREN c_id klanten.id%type:= &cc_id; c_naam klant.Naam%type; c_addr klanten.adres%type; -- door de gebruiker gedefinieerde uitzondering ex_invalid_id UITZONDERING; BEGIN ALS c_id<= 0 THEN RAISE ex_invalid_id; ELSE SELECT name, address INTO c_name, c_addr FROM customers WHERE id = c_id; DBMS_OUTPUT.PUT_LINE ("Name: "|| c_name); DBMS_OUTPUT.PUT_LINE ("Address: " || c_addr); END IF; EXCEPTION WHEN ex_invalid_id THEN dbms_output.put_line("ID must be greater than zero!"); WHEN no_data_found THEN dbms_output.put_line("No such customer!"); WHEN others THEN dbms_output.put_line("Error!"); END; /

Wanneer de bovenstaande code wordt uitgevoerd bij de SQL-prompt, levert dit het volgende resultaat op −

Voer een waarde in voor cc_id: -6 (laten we een waarde invoeren -6) oud 2: c_id customers.id%type:= &cc_id; nieuw 2: c_id customers.id%type:= -6; ID moet groter zijn dan nul !PL/SQL-procedure succesvol afgerond.

Vooraf gedefinieerde uitzonderingen

PL/SQL biedt veel vooraf gedefinieerde uitzonderingen, die worden uitgevoerd wanneer een databaseregel door een programma wordt overtreden. De vooraf gedefinieerde uitzondering NO_DATA_FOUND wordt bijvoorbeeld gegenereerd wanneer een SELECT INTO-instructie geen rijen retourneert. De volgende tabel bevat enkele van de belangrijke vooraf gedefinieerde uitzonderingen −

Uitzondering Oracle-fout SQLCODE Beschrijving
ACCESS_INTO_NULL 06530 -6530 Het wordt gegenereerd wanneer aan een nulobject automatisch een waarde wordt toegewezen.
CASE_NOT_FOUND 06592 -6592 Het treedt op als geen van de keuzes in de WHEN-clausule van een CASE-instructie is geselecteerd, en er geen ELSE-clausule is.
COLLECTION_IS_NULL 06531 -6531 Het treedt op wanneer een programma probeert andere verzamelmethoden dan EXISTS toe te passen op een niet-geïnitialiseerde geneste tabel of varray, of wanneer het programma probeert waarden toe te wijzen aan de elementen van een niet-geïnitialiseerde geneste tabel of varray.
DUP_VAL_ON_INDEX 00001 -1 Het treedt op wanneer wordt geprobeerd dubbele waarden op te slaan in een kolom met een unieke index.
INVALID_CURSOR 01001 -1001 Het treedt op wanneer wordt geprobeerd een cursorbewerking uit te voeren die niet is toegestaan, zoals het sluiten van een ongeopende cursor.
ONGELDIG NUMMER 01722 -1722 Het treedt op als de conversie van een tekenreeks naar een getal mislukt omdat de tekenreeks geen geldig getal vertegenwoordigt.
LOGIN_DENIED 01017 -1017 Het probleem treedt op wanneer een programma probeert in te loggen bij de database met een ongeldige gebruikersnaam of wachtwoord.
GEEN DATA GEVONDEN 01403 +100 Het wordt gegenereerd wanneer een SELECT INTO-instructie geen rijen retourneert.
NOT_LOGGED_ON 01012 -1012 Het wordt gegenereerd wanneer een databaseoproep wordt gedaan zonder dat er verbinding is met de database.
PROGRAM_ERROR 06501 -6501 Het treedt op als PL/SQL een intern probleem heeft.
ROWTYPE_MISMATCH 06504 -6504 Het wordt gegenereerd wanneer een cursor waarde ophaalt in een variabele met een incompatibel gegevenstype.
SELF_IS_NULL 30625 -30625 Het wordt gegenereerd wanneer een lidmethode wordt aangeroepen, maar de instantie van het objecttype niet is geïnitialiseerd.
OPSLAG_ERROR 06500 -6500 Het treedt op als PL/SQL onvoldoende geheugen heeft of als het geheugen beschadigd is.
TE_VEEL_RIJEN 01422 -1422 Het wordt gegenereerd wanneer een SELECT INTO-instructie meer dan één rij retourneert.
VALUE_ERROR 06502 -6502 Het treedt op wanneer er een rekenkundige, conversie-, afkappings- of groottebeperkingsfout optreedt.
ZERO_DIVIDE 01476 1476 Het wordt verhoogd wanneer wordt geprobeerd een getal door nul te delen.

Laten we een ontwerp bekijken voor het opvangen van uitzonderingen (fouten). Orakel

wanneer anderen dan- vangt alle fouten op die kunnen optreden als ze niet eerder zijn opgemerkt

serveruitvoer inschakelen;

verklaren
een GEHEEL:= 1;
b GEHEEL:= 0;
c CHAR(1) := "c";
beginnen
--Veroorzaakt geen fout
DBMS_OUTPUT.PUT_LINE(a / a);
--Zal een deling door 0 veroorzaken
--DBMS_OUTPUT.PUT_LINE(a / b);
--Zal alle andere fouten oproepen
--DBMS_OUTPUT.PUT_LINE(a / c);
uitzondering
wanneer zero_divide dan
DBMS_OUTPUT.PUT_LINE("zero_divide!");
wanneer anderen dan
DBMS_OUTPUT.PUT_LINE("wanneer anderen dan!");
einde;

We hebben een bord vertaald dat de fouten beschrijft: http://oracleplsql.ru/named-system-exceptions.html

/*
DUP_VAL_ON_INDEX U heeft geprobeerd een veld in te voegen of bij te werken waarvan de waarde de uniciteitsbeperking van het veld zou schenden.
TIMEOUT_ON_RESOURCE Wordt geactiveerd wanneer er een time-out optreedt terwijl ORACLE op een hulpbron wacht.
TRANSACTION_BACKED_OUT Draait het verwijderde deel van een transactie terug.
INVALID_CURSOR U probeert te verwijzen naar een cursor die nog niet bestaat. Dit kan gebeuren omdat u een cursor ophaalt die gesloten of niet geopend was.
NOT_LOGGED_ON U probeert Oracle te bellen zonder dat u met Oracle bent verbonden.
LOGIN_DENIED U probeert in te loggen bij Oracle met een onjuiste gebruikersnaam/wachtwoord.
NO_DATA_FOUND Je hebt een van de volgende pogingen geprobeerd:
1. U hebt SELECT INTO uitgevoerd en de query heeft geen rijen opgeleverd.
2. U verwijst naar een niet-geïnitialiseerde rij in de tabel.
3. U leest voorbij het einde van het UTL_FILE pakketbestand.
TOO_MANY_ROWS U heeft geprobeerd een SELECT INTO uit te voeren en de query retourneerde meer dan één rij.
ZERO_DIVIDE U heeft geprobeerd een getal door nul te delen.
INVALID_NUMBER U probeert een SQL-instructie uit te voeren die probeert een tekenreeks naar een getal te converteren.
STORAGE_ERROR Het beschikbare geheugen is uitgeput of het geheugen is beschadigd.
PROGRAM_ERROR Dit is een algemeen bericht voor contact met Oracle Support dat wordt weergegeven omdat er een interne fout is gedetecteerd.
VALUE_ERROR U heeft geprobeerd een bewerking uit te voeren, maar er is een fout opgetreden bij het converteren, afkappen of beperken van numerieke of tekengegevens.
CURSOR_ALREADY_OPEN U heeft geprobeerd een cursor te openen die al open is.
*/

Er kunnen fouten optreden tijdens het uitvoeren van PL/SQL-code, waardoor het PL/SQL-blok wordt beëindigd. Dergelijke fouten genereren uitzonderingen, die kunnen worden opgevangen en afgehandeld door een uitzonderingshandler.

Uitzondering – een PL/SQL-variabele die wordt gegenereerd tijdens de blokuitvoering en stopt met het uitvoeren van acties in de hoofdtekst van het blok. Als uw PL/SQL-blok een sectie voor het afhandelen van uitzonderingen bevat, kunt u acties definiëren die voor een bepaalde uitzondering moeten worden uitgevoerd voordat het blok wordt beëindigd.

Er wordt automatisch een uitzondering gegenereerd door de Oracle-server wanneer er een Oracle-fout optreedt (TOO_MANY_ROWS, NO_DATA_FOUND). U kunt echter uw eigen uitzondering definiëren in de declaratieve sectie van een PL/SQL-blok en deze vervolgens expliciet verhogen in de uitvoerbare sectie van het blok.

Als er een uitzondering optreedt in de uitvoerbare sectie van een blok, wordt de controle overgedragen naar de uitzonderingsafhandelingssectie (EXCEPTION-sectie). Als de uitzondering succesvol wordt afgehandeld, wordt het PL/SQL-blok zonder fouten voltooid. Als er geen handler voor deze uitzondering is, wordt de uitvoering van het PL/SQL-blok abnormaal beëindigd.

Er zijn drie soorten uitzonderingen:

Uitzonderingen opvangen

Het opvangen van uitzonderingen wordt uitgevoerd in de uitzonderingsafhandelingssectie van het PL/SQL-blok.

UITZONDERING

WANNEER uitzondering_1 [ OF uitzondering_2 ...] DAN

exploitanten ;

WANNEER uitzondering_3 [ OF uitzondering_4 ...] DAN

exploitanten ;

uitzondering– de naam van een vooraf gedefinieerde uitzondering of uitzondering beschreven in een declaratief gedeelte

WANNEER ANDEREN – definieert acties om alle uitzonderingen af ​​te handelen waarvoor de afhandeling niet expliciet is gespecificeerd

De sectie voor het afhandelen van uitzonderingen begint met het trefwoord EXCEPTION. De sectie voor het afhandelen van uitzonderingen kan meerdere uitzonderingshandlers bevatten, die elk hun eigen groep instructies uitvoeren. Als er een uitzondering optreedt in de uitvoerbare sectie van een PL/SQL-blok, wordt de controle overgedragen naar de uitzonderingsafhandelingssectie naar de handler die is ontworpen om die specifieke uitzondering af te handelen. Na het voltooien van de acties die in deze handler zijn gespecificeerd, wordt de blokuitvoering zonder fouten beëindigd.

U kunt acties definiëren die moeten worden ondernomen als er uitzonderingen optreden waarvoor niet expliciet een handler is gedefinieerd. Gebruik hiervoor de WHEN OTHERS-clausule. Er kan slechts één WHEN OTHERS-clausule zijn, die na alle andere uitzonderingshandlers wordt geplaatst.

Vooraf gedefinieerde uitzonderingen van Oracle Server opvangen

Het opvangen van vooraf gedefinieerde Oracle-fouten wordt gedaan door te verwijzen naar de standaard uitzonderingsnaam in de sectie voor het afhandelen van uitzonderingen.

Naam van uitzondering

Foutnummer

Beschrijving

CURSOR_ALREADY_OPEN

Er wordt geprobeerd een cursor te openen die al open is

DUP_VAL_ON_INDEX

Poging om een ​​ongeoorloofde bewerking uit te voeren op een cursor (een ongeopende cursor sluiten)

Er wordt geprobeerd een tekenwaarde om te zetten naar een numerieke waarde in een SQL-instructie terwijl de tekenwaarde geen tekenrepresentatie van een getal is

Er wordt geprobeerd verbinding te maken met een database met een onjuiste gebruikersnaam en/of wachtwoord

De SELECT INTO-instructie heeft geen rijen geretourneerd

Proberen toegang te krijgen tot een database zonder er verbinding mee te maken

PL/SQL interne fout

Niet genoeg geheugen

SYS_INVALID_ROWID

Er wordt geprobeerd een tekenwaarde naar een ROWID te converteren terwijl de tekenwaarde geen geldige tekenrepresentatie van de ROWID vertegenwoordigt

TIMEOUT_ON_RESOURCE

Er is een time-out opgetreden voor de resource

De SELECT INTO-instructie heeft meer dan één rij geretourneerd

Rekenfout, typeconversie, dimensieschending

Probeer te delen door nul

Laten we eens kijken naar een voorbeeld. Laat er een batchbestand zijn behalve.sql om het salaris van een werknemer met een specifieke functie te berekenen. In dit geval is het noodzakelijk om te voorzien in afhandelingssituaties waarbij er geen medewerkers zijn met een dergelijke functie of meerdere medewerkers een dergelijke functie hebben.

v_sal emp.sal%TYPE;

SELECTEER sal IN v_sal VAN emp WHERE LOWER(job)=LOWER(:v_job);

DBMS_OUTPUT.put_line("Salaris van " || :v_job || " is " || TO_CHAR(v_sal));

WANNEER GEEN_DATA_GEVONDEN DAN

DBMS_OUTPUT.put_line(:v_job || " is geen titel van werknemers");

WANNEER TE_VEEL_RIJEN DAN

DBMS_OUTPUT.put_line(:v_job || " is een titel van veel werknemers");

WANNEER ANDEREN DAN

DBMS_OUTPUT.put_line("Er is een andere fout opgetreden");

Laten we nu naar verschillende opties kijken, waarbij we verschillende waarden aan de hostvariabele toewijzen v_functie.

SQL> VARIABELE v_job VARCHAR2(20)

SQL> EXECUTE:v_job:= "President"

SQL> @d:\gebruikers\behalve

Het salaris van de president is 5000

PL/SQL-procedure met succes voltooid.

SQL> EXECUTE:v_job:= "Bediende"

PL/SQL-procedure met succes voltooid.

SQL> @d:\gebruikers\behalve

Bediende is een titel van veel werknemers

PL/SQL-procedure met succes voltooid.

SQL> EXECUTE:v_job:= "Ingenieur"

PL/SQL-procedure met succes voltooid.

SQL> @d:\gebruikers\behalve

Ingenieur is geen titel van werknemers

PL/SQL-procedure met succes voltooid.

Oracle Undefinieerde uitzonderingen opvangen

Als u een standaardfout op de Oracle-server wilt afhandelen die geen vooraf gedefinieerde fout is, moet u eerst de uitzondering declareren in de declaratieve sectie, deze koppelen aan een standaardfoutnummer en naar de uitzondering verwijzen in de sectie voor het afhandelen van uitzonderingen.