Datenbank

Überblick

Für die Speicherung der Daten verwendet der VLS die Datenbank Firebird. Bei Firebird handelt es sich um ein Open-Source relationales DBMS, das für unterschiedliche Betriebssysteme verfügbar ist, u.a. Linux und Windows. Außerdem läßt es sich leicht installieren und verwalten. Zum Einsatz kommt die aktuelle Version 2.5.x.

Weitere Informationen zu Firebird findet man auf der Projektseite unter http://www.firebirdsql.org/ .

Für den direkten Zugriff auf Firebird kommt u.a. Flame Robin in Frage: http://www.flamerobin.org/ , das ebenfalls für Linux und Windows verfügbar ist.

Verbindung

Die Verbindungseigenschaften von VLS zur Firebird-Datenbank lassen sich in der server.ini konfigurieren:

[database]
    # activate database component
    on = boolean(default=true)

    # skip tcp-ip server use direct file connection
    direct = boolean(default=False)

    # host/ip of firebird server
    host = string(default=127.0.0.1)

    # file to the .fdb file
    file = path(noCreate, default=/this/is/not/a/valid/fdb/path)

    role = string(default=vlServer)
    user = string(default=sysdba)
    password = password(default=masterkey)
    charset = string(default=AUTO)

    # we will create the db, if its local and doesnt exist
    isLocal = checkLocalDatabase(default='')

    # minimum needed database version - will trigger autoupdate on startup
    minVersion = string(default=3.5.6)

    # ping database server periodically - and reconnect if connection problem
    ping = boolean(default=false)

    # cp thread independent connection pool size
    poolSize = integer(default=10)

    # supported: dirtyInsertTS (allow modification of OPR_Content.OC_InsertTS
    behaviors = set(default='')

Datenstruktur

Bezeichnungen

Für Tabellennamen gibt es ein Bezeichnungsschema, um Daten aus unterschiedlichen Bereichen zu separieren. Ein Tabellenname besteht aus: <Bereich>_<Name>.

Als Bereiche sind bisher definiert:

  • laufende beim Betrieb anfallende Daten = OPeRational (OPR)
  • Benutzerdaten = PRiVate (PRV)
  • Systemdaten = SYStem (SYS)
  • Konfigurationsdaten = ConFiGuration (CFG)
  • Temporäre Daten = TeMPorary (TMP)

Bei Tabellen, die Daten versionieren, endet die Kurzbeschreibung auf History.

Alle Feldnamen werden mit jeweils einem eindeutigen Namen benannt. Der Feldname besteht aus: <Prefix>_<Name>. Der Prefix besteht aus dem ersten Buchstaben des Bereichskürzel und einer Abkürzung der Tabellen-Kurzbeschreibung (In der Regel aus dem ersten Buchstaben davon. Mehrere Buchstaben werden verwendet, wenn die Abkürzung bereits vergeben wurde). Für History-Tabellen kommt als letzter Buchstabe ein H dazu.

Beispiele:

  • Tabelle OPR_Tree -> Prefix OT
  • Tabelle OPR_TopStructure -> Prefix OTS
  • Tabelle PRV_UserHistory -> Prefix PUH

OPR_Tree

Die zentrale Tabelle für die operativen Daten ist OPR_Tree. Die meisten anderen OPR-Tabellen verbinden ihre Datensätze über einen Fremdschlüssel mit dieser Tabelle und ergänzen die Daten.

Über den Inhalt von OT_OT_Parent wird eine Baumstruktur aufgebaut. Ein Datensatz auf oberster Ebene innerhalb einer Domain hat keinen Parent (OT_OT_Parent is NULL). OT_Path enthält den ausgeflachten Pfad in der Form |ID1||ID2||ID3||ID4|. Über OT_Path lassen sich Abfragen ausführen, die alle Nachfahren ab einem bestimmten Baumknoten zurückliefern:

select
    *
from
    OPR_Tree
where
    OT_Path starting with '|ID1||ID2||'

Ein Datensatz wird anhand von zwei Merkmalen typisiert: Datentyp/(Unter-)Typ (OT_RCV_Datatype, OT_RCV_Type). Hauptsächlich bestimmt der Datentyp, welche anderen OPR-Tabellen zusätzliche Daten enhalten müssen oder enthalten können. Zum Beispiel sind Seiten vom Datentyp 3 und enthalten genau einen Eintrag in OPR_Page. Metadaten (Bücher, Zeitschriften, …) sind vom Datentyp 1 und enthalten genau einen Eintrag in OPR_Content.

Welche Kombinationen aus Datentyp/Typ erlaubt sind wird auch von der Serverkonfiguation der Types (config/types.ini, typesspec.ini) bestimmt.

Struktur von OPR_Tree

Feldname Beschreibung
OT_ID Primärschlüsssel
OT_OD_ID Domain (Sammlung), in die dieser Datensatz enthalten ist. Fremdschlüssel auf OPR_Domain.OD_ID.
OT_OT_Parent Elternknoten innerhalb der Baumstruktur. Fremdschlüssel auf OT_ID (Null erlaubt).
OT_OT_Link Verlinkter Datensatz. Zeigt auf die OT_ID des Originals.
OT_RCV_Datatype Datentyp
OT_RCV_Type Typ innerhalb des Datentyps
OT_Caption Text zur Anzeige im Baum
OT_Sort Sortierreihenfolge innerhalb von Geschwisterknoten (Daten mit gleichem Elternknoten).
OT_DelState Lösch-Status: 0 nicht gelöscht, 1 gelöscht (im Papierkorb)
OT_Path Flache Baumpfad Information mit dem aufgelösten Inhalt von OT_ID und OT_Parent. Inhalt: |<oberster OT_Parent>||…||<OT_Parent>||<OT_ID>| Beispiel: |23||144||6008|
OT_State Freigabestatus: 0 nicht freigegeben, 1 freigegeben
OT_InsertTS Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde.
OT_UpdateTS Zeitpunkt, an dem der Datensatz zuletzt geändert wurde.

OPR_Content

Die Tabelle OPR_Content verwaltet bibliographische Daten (Metadaten) für Datensätze vom Datentyp 1. Die Verbindung zu OPR_Tree wird über den Fremdschlüssel OC_OT_ID hergestellt. Änderungen an OPR_Content werden abhängig von den geänderten Feldern in OPR_ContentHistory, OPR_ContentXmlHistory oder OPR_Comment versioniert.

Struktur von OPR_Content

Feldname Beschreibung
OC_ID Primärschlüssel
OC_OT_ID 1:1 Fremdschlüssel nach OPR_Tree
OC_OT_MetaParent OT_ID des nächsten Eltern-Metadaten innerhalb der Baumstruktur in OPR_Tree
OC_MpMode Typ von OT_OT_MetaParent: 0 keine Eltern-Metadaten vorhanden, 1 Eltern-Metadaten vorhanden, 2 Kind-Metadaten vorhanden, 3 Eltern- und Kind-Metadaten vorhanden
OC_OT_Deliverer OT_ID des Deliverers (Ablieferer, Verlag) für diese Metadaten. Kann nur für Metadaten, die keinen Metadaten-Parent enthalten, eingestellt werden.
OC_OT_Titlepage OT_ID der Seite, die als Titelseite für diese Metadaten z. B. auf der Webseite Titelinfo angzeigt wird.
OC_OT_TitlepageMode Typ von OC_OT_Titlepage: 0 Eintrag automatisch gesetzt, 1 Eintrag von Hand gesetzt
OC_SourceIdent Primäre Katalog-ID (oder eine andere externe ID) dieser Metadaten
OC_Provider Wenn vorhanden, benutze den sekundären SRU-Provider aus dconfig.connectors.sru
OC_Signature Entweder manual ausgewählte/eingegebene Signatur (über VLM) oder beim Import aus mods:shelflocator extrahiert.
OC_PhysicalLocation Entweder manual ausgewählter/eingegebener Standort (über VLM) oder beim Import aus mods:location/mods:physicalLocation extrahiert.
OC_MediaNumber Aus mods:location/mods:electronicLocator extrahiert.
OC_Owner Besitzende Bibliothek
OC_PageCount Aus mods:extent extrahiert.
OC_PageCache Anzahl der enthaltenen Seiten (wird vom PagecountJob bestimmt. Zählt enthaltene nicht gelöschte Seiten(Knoten vom Datentyp 3))
OC_Comment Auftrags-Kommentar. Nicht sichtbar im Web-Frontend.
OC_OrgForm Web-Kommentar. Enthält reinen Text (ohne Html-Auszeichnungen).
OC_WebInfo1 HTML-Web-Kommentar. Enthält Html formatierten Text.
OC_XML Bibliographische Metadaten im Mods-Format.
OC_XmlOrigin Herkunft der bibliographischen Metadaten in OC_XML (s. u.)
OC_SC_OrderDate Über VLM gesetzt, OrderDateJob oder Import-Verhalten (Bedeutung: Startdatum des Digitalisierungs-Prozesses)
OC_SC_DeliveryDate Dieses Datum kann automatisch während des Import-Vorganges aus der dd_ISODATE Bezeichnung des Package Ordners gesetzt werden(Bedeutung: Importdatum der Digitalisate)
OC_ReleaseDate Das Datum, an dem dieses Metadatum zum ersten Mal freigegeben wurde (erstes Mal OT_State auf 1 gesetzt).
OC_MaxAccessCount Lehrbuchsammlung: Anzahl der vorhandenen Kopien. Maximale Anzahl der gleichzeitigen Nutzer, die sich die Seiten dieses Titels in der Lehrbuchanwendung anschauen können.
OC_PresentationUrl url to source presentation for harvested records (extracted from METS in dv:presentation)
OC_ReferenceUrl url to source reference for harvested records (extracted from METS in dv:reference)
OC_OaiIdent OAI-Id, falls der Datensatz über das OAI-Harvesting importiert wurde.
OC_EMail E-Mail Adresse für Hochschulschriften/Open Access Funktionalität
OC_GenerateIdentifier Die Generierung von IDs (URN, DOI, …) einschalten. Wird nur überprüft, wenn autogen ident nach einem Statuswechsel ausgelöst wird.
OC_TextState Volltext-Status von Kindknoten (in der Regel nur von Seiten): 0 keine Texte in Kindern enthalten, 1 Kinder enthalten Volltexte oder Wikitexte
OC_InsertTs Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde.
OC_UpdateTs Zeitpunkt, an dem der Datensatz zuletzt geändert wurde.
OC_XmlOrigin Bedeutung
0 unspecific
1 online catalogue (vlz)
2 batch catalogue (.mab, .pica, .mods, .marc)
3 oai harvest
4 fs-import (proto record)
5 csv/xls import
10 local manually edited
11 local autocreated from struct
12 simple file upload (SOURCEIDENT.pdf)
13 created via mods forms
14 vlm upload
15 identifier upload

OPR_Page

Die Tabelle OPR_Page verwaltet die Eigenschaften der Digitalisate der einzelnen Seiten eines Metadatums. Mit dem Feld OP_OT_ID wird eine 1:1 Verbindung zu den Daten aus OPR_Tree (mit dem Datentyp 3) hergestellt. Gleichbleibende Daten aus dem Scan-Prozess werden zusammengefasst in OPR_ImageProfile gespeichert.

Struktur von OPR_Page

Feldname Beschreibung
OP_ID Primärschlüssel
OP_OT_ID 1:1 Fremdschlüssel nach OPR_Tree
OP_Url Url auf das Digitalisat im Archiv (vls.archive) oder Url zu einem Remote-Server Bild oder Verweis in das Quell-PDF-Dokument.
OP_State Status vom Digitalisat: 0 nicht definiert (z. B. kein Digitalisat vorhanden), 1 existiert (im lokalen Archiv), 2 gelöscht (nach Kapsel-Export), 3 gespiegelt (remote url, not implemented yet), 4 extrahierte PDF Seite
OP_Comment Bemerkung zur Seite
OP_OIP_ID Fremdschlüssel auf die Informationen in OPR_ImageProfile
OP_Ident Import-Identifier
OP_ImgWidth Breite in Pixel
OP_ImgHeight Höhe in Pixel
OP_ImgDateTime Zeitstempel vom Scan-Prozess, aus den Bild-Metadaten (tiff-metadatatags) extrahiert
OP_ImgPageName Seitenname vom Scan-Prozess aus den Bild-Metadaten (tiff-header)
OP_Filesize Dateigröße (in Bytes). Dieser Wert wird nicht verändert, wenn die Datei nach Kapsel-Export gelöscht wird (OP_State = 2)
OP_ModificationTimestamp Zeitpunkt der letzten Änderungen dieser Datei im Archiv
OP_Webcache Kommaseparierte Liste von Webcache Bildbreiten
OP_WebcacheSize Webcachegröße (in Bytes) als Summe über alle verfügbaren Webcache-Bilder
OP_PaginationCaption Textwert der Paginierung (Überschrift)
OP_PaginationType Typ der Paginatierung: siehe paginate.xml
OP_PaginationValue Wert des Paginierungs-Zählers (unabhängig von der Ausgabe/Formatierung des Zähler, der abhängig vom Paginierungstyp ist)
OP_IccState Status der ICC-Profil Anwendung: 0 nicht transformiert, 1 Webcache-Bilder sind in den sRGB-Farbraum transformiert
OP_Hashsum Hashsumme des Digitalisats im Format: {ALGO}hexdigest
OP_ChangeTS Änderungszeitpunkt für die Felder: OP_Ident, OP_Comment, OP_ModificationTimestamp, OP_PaginationCaption, OP_PaginationType, OP_PaginationValue, OP_Hashsum
OP_InsertTs Zeitpunkt, an dem der Datensatz in die Tabelle eingefügt wurde.
OP_UpdateTs Zeitpunkt, an dem der Datensatz zuletzt geändert wurde.

API

Die VLS-Datenbankschicht ist in vls.db implementiert. Mit folgendem Python-Import:

>>> from vls.api import db

steht der wichtigste Namespace im Modul zur Verfügung. Alternativ lässt sich die API auch über component.vls.db erreichen.

Ohne Transaktionskontext lässt sich die DB in Python über folgende API erreichen:

>>> sql = 'select first 2 OT_Caption from OPR_Tree where OT_RCV_DataType = :t'
>>> rs = db.selectQuery(sql, t=10)
>>> for r in rs: print(r)
<result set row with OT_CAPTION = domain obvsg>
<result set row with OT_CAPTION = domain ulbtirol>
>>> print('\n'.join(db.selectQuery(sql, t=10, map='OT_Caption')))
domain obvsg
domain ulbtirol
>>> db.updateQuery('delete from TMP_ID')
0
>>> db.batchQuery('insert into TMP_ID (TI_OT_ID) values(1);delete from TMP_ID')

Für Transaktionshandling stehen folgende Idiome zur Verfügung:

conn = db.getConn(autoCommit=False) # from pool or from request-thread
try:
    # TMP_ID has only transaction-level visibility
    conn.updateQuery('insert into TMP_ID (TI_OT_ID) values(1)')
    # still there
    affected = conn.updateQuery('delete from TMP_ID')
    assert affected == 1
    conn.commit()
finally:
    db.releaseConn(conn)

Oder in einem Kontextmanager:

with db.conn() as conn:
    # do something with conn
    # auto-commit() if from pool, do not commit if from request-thread
    # rollback on error

with db.conn(request=False) as conn:
    # got a non-cp-request conn, got one from the pool

Cursor-basiert lässt sich auf folgender API arbeiten:

with db.selectCursorQuery(sql, **params) as (cur, prep):
    for i, r in enumerate(cur.itermap()):
        print(i, r)

with db.conn() as conn:
    with conn.selectCursorQuery(sql, **params) as (cur, prep):
        # ..

        # beware! cursor will be closed if prepared
        # statement is garbage collected
        del prep # there goes the cursor

Wenn man gegen eine (große, >1499, Firebird “in ()” Limit) Liste von IDs operiert:

sql = '''select OT_ID from OPR_Tree
         inner join TMP_ID on TI_OT_ID = OT_ID'''
with db.conn() as conn:
    db.fillTempIds(conn, otids, clear=True)
    round_tripped = conn.selectQuery(sql, map='OT_ID')
    assert set(otids) == set(round_tripped)

Eine Connection, die readonly/non-blocking arbeitet:

readonly = dict(isolationLevel=(db.isc_tpb_read_committed, db.isc_tpb_rec_version),
                accessMode=db.isc_tpb_read)
with db.conn(request=False, **readonly) as conn:
    # got a readonly non blocking conn from pool