Objektzugriff über Cursor

Der Zugriff auf Listenobjekte über einen Cursor [a] bietet eine schnelle Möglichkeit, um auf den Inhalt von Sätzen in Listenobjekten zuzugreifen. Dies bringt zur Laufzeit Performance-Vorteile. Allerdings ist bei der Nutzung dieser Funktionalität einiges zu berücksichtigen und zu beachten, um Programmabstürze und Anwendungsfehler zu vermeiden.

Der Hauptunterschied bei dem Zugriff auf die Daten über einen Cursor gegenüber dem „normalen“ GET-Zugriff ist, dass Sie mit Cursor direkt auf den Datenbereich des Objekts in der Infrastruktur zugreifen, während Sie ohne Cursor auf einer Kopie des Objektinhalts arbeiten. Das bedeutet, dass mit Cursor alle Änderungen, die Sie in der Datenstruktur vornehmen, direkt auch in der Infrastruktur - und somit auch für Folge- oder Vorgängermodule - wirksam werden.

Die Infrastruktur hat keine Kontrolle über die Änderungen, die Sie bei Zugriff mit Cursor in der Datenstruktur vornehmen. Sie kann auch nicht verhindern, dass an Objekten mit der Rolle REF auf diesem Weg trotzdem Änderungen durchgeführt werden. Solche Änderungen können jedoch in unterschiedlichen Umgebungen und in zukünftigen TAA-Releases unterschiedliche Auswirkungen haben, weshalb wir dringend davon abraten.

Das bedeutet aber auch, dass, solange für ein Listenobjekt ein Cursor existiert, für dieses Objekt keine Operationen erlaubt sind, durch die Sätze in dem Objekt hinzugefügt oder gelöscht werden. Andernfalls könnte es nach einer solchen Operation zu Laufzeitfehlern kommen wegen Zugriffen auf nicht (mehr) vorhandene Adressen. Wenn Sie, während ein Cursor für ein Objekt existiert, Operationen wie ADD oder DELETE auf dieses Objekt versuchen, setzt die Infrastruktur eine Condition und den Zustand OM-RC-NOT-OK. Dies gilt auch für Operationen in aufgerufenen Modulen, die das betreffende Objekt als Parameterobjekt empfangen haben.

Achtung:

Der Zugriff über Cursor ist nur sinnvoll, wenn

  • Sie große Listenobjekte verarbeiten oder häufig auf unterschiedliche Elemente eines Listenobjekts zugreifen;
  • Sie überschaubare Änderungen an den einzelnen Einträgen vornehmen;
  • Sie in dem Listenobjekt während der Verarbeitung keine Einträge lhinzufügen brauchen;
  • die Verarbeitung innerhalb des Moduls stattfindet, d.h. möglichst ohne zwischenzeitliche Modulaufrufe, vor allem keine Aufrufe von Modulen, die das Listobjekt als Parameter erwarten und dieses mglw. verändern wollen.

Beim Zugriff über Cursor müssen Sie selbst darauf achten, dass

  • vor der GET-Operation ein CREATE CURSOR erfolgreich stattgefunden hat;
  • der Cursor nicht zwischenzeitlich durch einen DESTROY CURSOR zerstört wurde;
  • die vom Cursor gelieferten Adressen nicht durch andere Aktionen zerstört werden können.

Fehler beim Zugriff über Cursor führen i.d.R. zu COBOL-Laufzeitfehlern; Sie können solche Fehler nicht über TAA-Returncodes oder Conditions abfangen.

Wenn Sie für ein Objekt im Wechsel Zugriffe mit und ohne Cursor codieren, vor allem bei Zugriffen ohne Cursor, während ein Cursor existiert (nach CREATE CURSOR), versucht die TAA, dies abzufangen. Unerwünschte Nebeneffekte können dabei aber nicht ganz ausgeschlossen werden. Sie sollten deshalb nach einem CREATE CURSOR möglichst bis zum DESTROY CURSOR auf das betreffende Objekt nur mit dem Cursor zugreifen.

Sie sollten auf keinen Fall alle Zugriffe auf List-Objekte auf Cursor-Operationen umstellen! Der „normale“ Objektzugriff über die GET-; PUT- und DELETE-Anweisungen bietet ein deutlich höheres Maß an Sicherheit und Programmierkomfort.

Nutzung der Cursor-Anweisungen

Um einen Cursor nutzen zu können, müssen Sie diesen in der Working-Storage Section des COBOL-Moduls deklarieren. Innerhalb der Procedure Division müssen Sie zunächst den Cursor mit Daten versorgen lassen, bevor Sie ihn für GET-Operationen nutzen können. Sie können nun auch die Anzahl der Einträge in dem Cursor abfragen. Wenn Sie den Cursor nicht mehr benötigen, sollten Sie ihn freigeben, um damit wieder alle OM-Operationen für das Objekt, für das der Cursor angelegt wurde, zulassen.

Cursor deklarieren

EXEC TAA
   DECLARE CURSOR <cursorname> FOR <objektname>
END-EXEC

Diese Anweisung darf nur in der WORKING-STORAGE SECTION stehen. Sie muss sich auf ein bereits deklariertes Objekt beziehen, also entweder ein Parameter- oder globales Objekt, oder ein zuvor deklariertes lokales Objekt.

Der Cursor darf nur für dieses Objekt verwendet werden. Es ist auch nicht möglich, einen für ein Objekt deklarierten Cursor für ein anders Objekt mit gleicher Datenstruktur zu verwenden.

Der Name des Cursors sollte nicht länger als 8 Stellen sein.

  EXEC TAA DECLARE mysrc TYPE pgformate CLASS lst END-EXEC.
  EXEC TAA DECLARE CURSOR c-mysrc FOR mysrc END-EXEC.

Cursor füllen

EXEC TAA CREATE CURSOR <cursorname>
    [FOR {UPDATE | READ-ONLY | DELETE}]   
END-EXEC

Diese Anweisung bewirkt, dass von der Infrastruktur ein Cursor in Form einer Liste mit Verweisen auf alle Sätze in einem Listenobjekt erstellt wird. Aus technischen Gründen können nicht mehr als 1.000.000 (1 Million) Verweise übergeben werden.

Der so erstellte Cursor kann in GET-Operationen durch den Zusatz „USING CURSOR …“ benutzt werden. Er bleibt so lange gültig, bis entweder das Modul endet, oder er ausdrücklich durch DESTROY CURSOR freigegeben wird, oder der Cursor durch einen neuen CREATE neu gefüllt wird.

Die Angabe READ-ONLY teilt der Infrastruktur mit, dass Sie den Cursor ausschließlich zum Lesen der Sätze benutzen werden, und keine Veränderungen in den Daten vornehmen werden. Wenn Sie trotz der READ-ONLY-Angabe Satzinhalte verändern, kann es bedingt durch unterschiedliches Verhalten der Infrastrukturen zu unerwarteten Auswirkungen in anderen Objekten kommen, wohin oder woraus Sätze in das bzw. aus dem bearbeiteten Objekt kopiert wurden, z.B. im Zusammenhang mit der BY VALUE-Angabe in Modulaufrufen. Wenn Sie den Cursor mit der Angabe FOR UPDATE füllen, rechnet die Infrastruktur mit solchen Änderungen, was jedoch zur Laufzeit langsamer sein kann kann.

Die Angabe FOR DELETE [b] teilt der Infrastruktur mit, dass Sie nicht nur Änderungen an Satzinhalten vornehmen möchten, sondern u.U. Sätze auch löschen möchten (DELETE impliziert immer auch UPDATE). Zu den Besonderheiten beim Löschen s. unten.

Der Default für diese Angabe ist abhängig von der Objektrolle: Bei Objekten mit der Rolle REF wird immer READ-ONLY generiert, die Angabe FOR UPDATE wird für REF-Objekte ignoriert. Bei anderen Rollen sowie generell bei lokalen Objekten ist der Default FOR UPDATE.

Wenn der Cursor erfolgreich erstellt wurde, wird OM-RC-OK gesetzt, andernfalls OM-RC-NOTOK.

  EXEC TAA CREATE CURSOR CURREAD READ-ONLY END-EXEC.
  EXEC TAA CREATE CURSOR C-MYSRC FOR DELETE END-EXEC.

Mit Cursor lesen

Get

EXEC TAA
   GET [ FIRST | NEXT | PREVIOUS | LAST | CURRENT ] <Objektname>
   USING CURSOR <cursorname>
END-EXEC

EXEC-TAA
   GET <Objektname> ( <Datenname> | <num.Literal>| FIRST | NEXT | PREVIOUS | LAST | CURRENT)
   USING CURSOR <cursorname>
END-EXEC

GetWhere

EXEC TAA
   GET { FIRST | NEXT | PREVIOUS | LAST | CURRENT } <Objektname>
   USING CURSOR <cursorname>
   WHERE <bedingung>
END-EXEC

EXEC-TAA
   GET <Objektname> ( { <Datenname> | <num.Literal>| FIRST | NEXT | PREVIOUS | LAST | CURRENT } )
   USING CURSOR <cursorname>
   WHERE <bedingung>
END-EXEC

Es gelten die für die bekannten GET- oder GET WHERE-Anweisungengültigen Regeln. Lediglich die technische Umsetzung der Anweisung unterscheidet sich, denn es wird nicht der gewünschte Satz aus der Infrastruktur beschafft, sondern innerhalb des angegebenen Cursors auf den angegebenen Satz positioniert. Der Satzinhalt steht nach dem GET bzw. GETWHERE wie gewohnt in der COBOL-Datenstruktur des Objekts zur Verfügung.

Wenn Sie auf einen Index zugreifen, der größer ist als die Anzahl Einträge im Cursor, wird OM-RC-EOL gesetzt, ebenso bei Angabe eines Index kleiner 1, und nach Erreichen des Endes oder Anfangs des Cursors (NEXT bzw. PREVIOUS).

Mit Cursor löschen

EXEC TAA
   DELETE [ FIRST | NEXT | PREVIOUS | LAST | CURRENT ] <Objektname>
   USING CURSOR <cursorname>
END-EXEC

EXEC-TAA
   DELETE <Objektname> ( <Datenname> | <num.Literal>| FIRST | NEXT | PREVIOUS | LAST | CURRENT)
   USING CURSOR <cursorname>
END-EXEC

Ebenso wie beim GET, gelten auch hier die bekannten Regeln für DELETE-Anweisungen, jedoch unterscheidet sich die technische Umsetzung der Anweisung.

Im Gegensatz zum Löschen ohne Cursor wird hier der gewählte Satz nicht direkt gelöscht, sondern lediglich zum Löschen markiert. Erst wenn der Cursor freigegeben wird, werden die in der Liste markierten Sätze tatsächlich aus dem Listenobjekt entfernt.

Dies bedeutet aber, dass der gelöschte (bzw. zum Löschen vorgemerkte) Satz für GET-Operationen weiterhin zur Verfügung steht. Bei Abarbeitung des Objekts z.B. über GET FIRST/NEXT erhalten Sie auch die zum Löschen vorgemerkten Sätze. Ebenso werden bei der Positionierung über GET(index) zum Löschen vorgemerkte Sätze nicht übersprungen.

Dies ist u.a. zu beachten bei der Bereitstellung von Sätzen nach dem Löschen, z.B. in Schleifen: Nach einem DELETE USING CURSOR positionieren Sie auf den folgenden Satz mit einem GET NEXT USING CURSOR, also genauso als wäre keine Löschung erfolgt!

Um bei Objektzugriffen nach Löschungen die gelöschten Sätze nicht mehr zu referenzieren, muss ein neuer EXEC TAA CREATE CURSOR ausgeführt werden.

Cursor freigeben

  EXEC TAA DESTROY CURSOR <cursorname> END-EXEC 

Diese Anweisung gibt den erstellten Cursor frei. Nach dem DESTROY sind Operationen wie ADD und DELETE auf das Objekt wieder zulässig.

Wenn Sie den Cursor nicht selbst freigeben, wird er automatisch bei Ende des aktuellen Moduls von der Infrastruktur freigegeben.


a) ab TAA Version 7.04
b) ab TAA Version 8.01
cobref:cob:cursor · Zuletzt geändert: 08.04.2020 10:37

Copyright © 1992-2024 TeamWiSE Gesellschaft für Softwaretechnik mbH         Adressen |  Kontakt |  AGB |  Datenschutzerklärung |  Impressum