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