Anleitung für den IBM-i-Administrator

Den Export erstellen

Etwa 20 Minuten. An Ihren Daten, Objekten und Programmen wird nichts verändert, es wird nichts installiert und kein fremder Code ausgeführt. Geschrieben wird ausschließlich in ein temporäres Verzeichnis (/tmp/export) und nach QTEMP; beides löschen Sie hinterher, QTEMP verschwindet von selbst. Sie brauchen eine SQL-Sitzung (ACS, STRSQL oder RUNSQL) und Leserecht auf die Bibliotheken, die untersucht werden sollen.

← Zurück zur Bestandsanalyse

Der eine Schritt, auf den es ankommt

Die Katalogauszüge (Schritt 1 und 2) beschreiben Ihren Bestand: Bibliotheken, Dateien, Felder, Programme und das eingebettete SQL. Damit entsteht ein vollständiger Strukturbefund.

Was die Programme tun, steht in den Quellmembern — und die kommen erst mit Schritt 3. Ohne ihn liefert der Bericht Struktur ohne Logik, und die wichtigste Frage („zu welchem Programm gibt es keinen Quelltext mehr?") lässt sich nicht beantworten: es sähe so aus, als fehle er überall.

Der Bericht sagt Ihnen, wenn Schritt 3 gefehlt hat — aber ein zweiter Export kostet Sie wieder zwanzig Minuten.

Schritt 1 — Katalogauszüge erzeugen

In einer SQL-Sitzung ausführen. Die Tabellen entstehen in QTEMPund verschwinden mit der Sitzung — auf Ihrer Maschine bleibt nichts zurück.

-- Katalogauszüge. &LIB gegen Ihre Bibliothek tauschen; für mehrere
-- Bibliotheken den Block je Bibliothek wiederholen oder IN ('LIB1','LIB2').
--
-- WICHTIG: dieser Block UND der CPYTOIMPF-Block darunter gehören in
-- DIESELBE Sitzung. QTEMP gehört zum Job -- wer die Abfragen in ACS
-- laufen lässt und die Kopierbefehle danach auf einer 5250-Befehlszeile,
-- findet dort nichts mehr. Läuft beides getrennt, ersetzen Sie QTEMP
-- durch eine eigene Bibliothek (nicht QGPL) und räumen sie hinterher auf.

DROP TABLE QTEMP/EXPBIB;
CREATE TABLE QTEMP/EXPBIB AS (
  SELECT SCHEMA_NAME, SCHEMA_TEXT
    FROM QSYS2.SYSSCHEMAS
   WHERE SCHEMA_NAME IN ('&LIB')
) WITH DATA;

DROP TABLE QTEMP/EXPTAB;
CREATE TABLE QTEMP/EXPTAB AS (
  SELECT TABLE_SCHEMA, SYSTEM_TABLE_NAME, TABLE_NAME, TABLE_TEXT, TABLE_TYPE
    FROM QSYS2.SYSTABLES
   WHERE TABLE_SCHEMA IN ('&LIB')
) WITH DATA;

DROP TABLE QTEMP/EXPSPA;
CREATE TABLE QTEMP/EXPSPA AS (
  SELECT TABLE_SCHEMA, TABLE_NAME, SYSTEM_COLUMN_NAME, COLUMN_NAME,
         DATA_TYPE, LENGTH, NUMERIC_SCALE, IS_NULLABLE, COLUMN_TEXT, CCSID
    FROM QSYS2.SYSCOLUMNS
   WHERE TABLE_SCHEMA IN ('&LIB')
) WITH DATA;

-- OBJECT_STATISTICS liefert auch LAST_USED_TIMESTAMP: wann ein Objekt
-- zuletzt BENUTZT wurde. Das beantwortet die Frage nach ungenutzten
-- Programmen -- und es gibt sie auf keinem anderen System.
DROP TABLE QTEMP/EXPOBJ;
CREATE TABLE QTEMP/EXPOBJ AS (
  SELECT OBJLIB, OBJNAME, OBJTYPE, OBJATTRIBUTE, OBJTEXT, OBJOWNER,
         CHANGE_TIMESTAMP, LAST_USED_TIMESTAMP
    FROM TABLE(QSYS2.OBJECT_STATISTICS('&LIB', '*ALL')) AS O
) WITH DATA;

-- Eingebettetes SQL -- steht IM Programmobjekt und ist damit auch dann
-- lesbar, wenn der Quelltext fehlt.
DROP TABLE QTEMP/EXPSQL;
CREATE TABLE QTEMP/EXPSQL AS (
  SELECT PROGRAM_SCHEMA, PROGRAM_NAME, STATEMENT_NUMBER, STATEMENT_TEXT
    FROM QSYS2.SYSPROGRAMSTMTSTAT
   WHERE PROGRAM_SCHEMA IN ('&LIB')
) WITH DATA;

Es sind genau die Sichten, die auch eine spätere Live-Anbindung abfragen würde. Wer den Export prüfen will, sieht an jeder Spalte, woher sie kommt.

Schritt 2 — als CSV in den IFS schreiben

ADDCOLNAM(*SQL) schreibt die Spaltennamen in die Kopfzeile — ohne sie ist die Datei nicht zuzuordnen. STMFCCSID(1208) ist UTF-8; ohne diese Angabe kommen Umlaute als Datenmüll an.

/* Als CSV in den IFS schreiben. Verzeichnis vorher anlegen:      */
/*   MKDIR DIR('/tmp/export')                                     */
/*   MKDIR DIR('/tmp/export/quelltext')                           */

CPYTOIMPF FROMFILE(QTEMP/EXPBIB) TOSTMF('/tmp/export/bibliotheken.csv') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*CRLF) +
            DTAFMT(*DLM) STRDLM(*DBLQUOTE) ADDCOLNAM(*SQL)

CPYTOIMPF FROMFILE(QTEMP/EXPTAB) TOSTMF('/tmp/export/tabellen.csv') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*CRLF) +
            DTAFMT(*DLM) STRDLM(*DBLQUOTE) ADDCOLNAM(*SQL)

CPYTOIMPF FROMFILE(QTEMP/EXPSPA) TOSTMF('/tmp/export/spalten.csv') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*CRLF) +
            DTAFMT(*DLM) STRDLM(*DBLQUOTE) ADDCOLNAM(*SQL)

CPYTOIMPF FROMFILE(QTEMP/EXPOBJ) TOSTMF('/tmp/export/objekte.csv') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*CRLF) +
            DTAFMT(*DLM) STRDLM(*DBLQUOTE) ADDCOLNAM(*SQL)

CPYTOIMPF FROMFILE(QTEMP/EXPSQL) TOSTMF('/tmp/export/sql-im-programm.csv') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*CRLF) +
            DTAFMT(*DLM) STRDLM(*DBLQUOTE) ADDCOLNAM(*SQL)

/* Enthält eine Bibliothek kein Programm mit eingebettetem SQL, bleibt   */
/* EXPSQL leer, und der letzte Befehl bricht mit CPF2803/CPF2817 ab.     */
/* Das ist in Ordnung: schicken Sie den Export ohne diese eine Datei.    */

Schritt 3 — die Quellmember

Der Schritt aus dem Kasten oben. Ohne ihn gibt es keine Programmlogik im Befund. Er läuft in zwei Teilen: erst lässt SQL die Kopierbefehle erzeugen, dann arbeitet eine Zeile in QShell sie ab. Es entsteht dabei kein Objekt auf Ihrer Maschine — kein CL-Programm, nichts, was kompiliert werden müsste.

/* --- 3a: die Befehle erzeugen lassen -----------------------------  */
/* In DERSELBEN SQL-Sitzung wie Schritt 1. Das Ergebnis ist eine        */
/* Textdatei mit je einem Befehl pro Zeile -- niemand tippt hier etwas. */

DROP TABLE QTEMP/EXPCMD;
CREATE TABLE QTEMP/EXPCMD AS (
  SELECT BEFEHL FROM (
    SELECT 1 AS FOLGE,
           'MKDIR DIR(''/tmp/export/quelltext/' || TRIM(P.TABLE_SCHEMA) ||
           '/' || TRIM(P.TABLE_NAME) || ''')' AS BEFEHL
      FROM QSYS2.SYSPARTITIONSTAT P
      JOIN QSYS2.SYSTABLES T ON T.TABLE_SCHEMA = P.TABLE_SCHEMA
                            AND T.TABLE_NAME   = P.TABLE_NAME
                            AND T.FILE_TYPE    = 'S'
     WHERE P.TABLE_SCHEMA IN ('&LIB')
     GROUP BY P.TABLE_SCHEMA, P.TABLE_NAME
    UNION ALL
    SELECT 2,
           'CPYTOSTMF FROMMBR(''/QSYS.LIB/' || TRIM(P.TABLE_SCHEMA) || '.LIB/' ||
           TRIM(P.TABLE_NAME) || '.FILE/' || TRIM(P.TABLE_PARTITION) ||
           '.MBR'') TOSTMF(''/tmp/export/quelltext/' || TRIM(P.TABLE_SCHEMA) ||
           '/' || TRIM(P.TABLE_NAME) || '/' || TRIM(P.TABLE_PARTITION) || '.' ||
           COALESCE(NULLIF(TRIM(P.SOURCE_TYPE), ''), 'TXT') ||
           ''') STMFOPT(*REPLACE) STMFCCSID(1208) ENDLINFMT(*LF)'
      FROM QSYS2.SYSPARTITIONSTAT P
      JOIN QSYS2.SYSTABLES T ON T.TABLE_SCHEMA = P.TABLE_SCHEMA
                            AND T.TABLE_NAME   = P.TABLE_NAME
                            AND T.FILE_TYPE    = 'S'
     WHERE P.TABLE_SCHEMA IN ('&LIB')
  ) X ORDER BY FOLGE
) WITH DATA;

/* Als reine Textdatei ausschreiben. DTAFMT(*FIXED) ist hier wichtig:   */
/* es schreibt weder Anführungszeichen noch eine Kopfzeile hinein.      */
CPYTOIMPF FROMFILE(QTEMP/EXPCMD) TOSTMF('/tmp/export/befehle.txt') +
            MBROPT(*REPLACE) STMFCCSID(1208) RCDDLM(*LF) DTAFMT(*FIXED)

Jetzt die erzeugte Liste abarbeiten — in QShell (STRQSH).

/* --- 3b: die Befehle abarbeiten -------------------------------- */
/* In QShell (Befehl STRQSH). Zwei Zeilen, sonst nichts.              */

mkdir -p /tmp/export/quelltext/&LIB

while read -r c; do system "$c" </dev/null; done < /tmp/export/befehle.txt

Zwei Kleinigkeiten, an denen es sonst scheitert

Das </dev/null hinter system ist nicht optional. Ohne es liest der Befehl selbst aus der Liste, die die Schleife gerade abarbeitet — und nach wenigen Zeilen ist Schluss. An einer echten Maschine gemessen: mit der Angabe 965 von 965 Zeilen, ohne sie 24.

MKDIR legt keine übergeordneten Verzeichnisse an. Deshalb steht das mkdir -p davor — sonst laufen alle Befehle durch und es entsteht keine einzige Datei.

Fehlt zu einem Programm das Member, ist das kein Fehler — es ist der Befund, um den es geht. Er landet in Anhang A des Berichts.

So muss das ZIP aussehen

✓ Richtig

export.zip
├── bibliotheken.csv
├── tabellen.csv
├── spalten.csv
├── objekte.csv
├── sql-im-programm.csv
└── quelltext/
    ├── KREDIT/
    │   ├── QRPGLESRC/
    │   │   ├── KRD001.RPGLE
    │   │   └── KRD002.RPGLE
    │   └── QCLSRC/
    │       └── KRD003.CLLE
    └── KUNDEN/
        └── QRPGLESRC/
            └── KDS001.RPGLE

Der Membername ist der Dateiname. Über ihn wird das Programmobjekt zugeordnet.

✗ Nicht ausreichend

export.zip
├── bibliotheken.csv
├── tabellen.csv
├── spalten.csv
├── objekte.csv
└── sql-im-programm.csv

Kein quelltext/. Der Bericht sagt das ausdrücklich — aber er kann dann nur Struktur und eingebettetes SQL auswerten.

Packen mit ZIP aus QShell oder über eine Netzwerkfreigabe vom Arbeitsplatz aus. Bis 200 MB entpackt.

Vor dem Hochladen: drei Kontrollen

  1. Kopfzeilen prüfen. Jede CSV muss in der ersten Zeile die Spaltennamen tragen (TABLE_SCHEMA, OBJNAME …). Fehlen sie, wurde ADDCOLNAM(*SQL) vergessen.
  2. Umlaute ansehen. Öffnen Sie tabellen.csv und suchen Sie einen Objekttext mit Umlaut. Steht dort Datenmüll, fehlte STMFCCSID(1208).
  3. Quellmember zählen. Sind unter quelltext/ ungefähr so viele Dateien wie Programme in objekte.csv? Große Abweichungen sind ein Befund — aber prüfen Sie, ob nicht einfach eine Quelldatei vergessen wurde.

Was gelesen wird — und was nicht

Wird gelesen

  • Bibliotheken, Dateien, Sichten, logische Dateien
  • Felder mit Typ, Länge, Nachkommastellen und CCSID
  • Programme, Serviceprogramme, Module
  • Eingebettetes SQL aus dem Programmobjekt
  • Quelltext: RPG, RPGLE, CL, COBOL, DDS
  • Änderungs- und Nutzungszeitpunkt der Objekte

Wird nicht gelesen

  • Geschäftsdaten — der Export enthält keine Datensätze aus Ihren Dateien
  • Benutzerprofile und Kennwörter
  • Spool-Dateien und Protokolle
  • Objekte, für die Sie kein Leserecht haben

Häufige Fragen

Läuft irgendetwas auf unserer Maschine?

Nein. Sie führen Katalogabfragen und Kopierbefehle aus, beides IBM-Bordmittel. Von uns kommt kein Code auf Ihre Maschine, und wir bekommen keinen Zugang zu ihr.

Werden Geschäftsdaten übertragen?

Nein. Der Export beschreibt Struktur und Programme — Bibliotheken, Dateien, Felder, Quelltext. Die Sätze in Ihren Dateien sind nicht enthalten; keine der Abfragen liest sie.

Wir haben zu vielen Programmen keinen Quelltext mehr.

Dann ist der Befund für Sie besonders wertvoll — genau das ist die Frage, die er beantwortet. Der Bericht listet die betroffenen Programme in Anhang A und nennt für jedes, welche Dateien es laut eingebettetem SQL anfasst. Das steht im Objekt, ist also auch ohne Quelltext lesbar.

Warum nicht gleich eine lesende Verbindung statt eines Exports?

Weil die schneller versprochen als eingerichtet ist. Netzwerkfreigabe, Benutzerprofil, Change-Genehmigung, Sicherheitsprüfung — in einem regulierten Haus sind das Wochen. Der Export beantwortet die erste Frage ohne all das. Wenn Sie danach weitermachen wollen, ist die Verbindung der nächste Schritt, nicht der erste.

Wir haben mehrere Bibliotheken.

Nehmen Sie alle mit, die fachlich zusammengehören. Der Bericht zeigt in Abschnitt 7, welche Bibliothek an welcher hängt — und das geht nur, wenn beide Seiten im Export liegen. Fehlt eine, erscheint der Bezug als „Ziel nicht im Export enthalten“.

Wie groß darf das Archiv sein?

Bis 200 MB entpackt. Katalogauszüge sind klein; die Größe kommt aus den Quellmembern. Ein Bestand mit zweitausend Programmen liegt erfahrungsgemäß deutlich darunter.

Export ist fertig — zur Bestandsanalyse →