Data Science

Erweiterung der Oracle Datenbank zur Oracle MLE Datenbank

Implementierung von Scriptsprachen in der Oracle MLE Datenbank

Ein aktueller Trend in der Entwicklung von Datenbank gestützten Webanwendungen ist, (möglichst) keine Businesslogik in der Datenbank zu implementieren. Obwohl die Nähe und leichte Verfügbarkeit hohe Performance erwarten lassen, schreckt der Zwang, bei der Implementierung die proprietäre Sprache der jeweiligen Datenbank verwenden zu müssen, doch so sehr ab, dass diese Möglichkeit immer seltener genutzt wird.
Was aber nun, wenn genau dieses Hindernis entfällt und eine Businessregel genauso in JavaScript für die Verwendung clientseitig im Browser wie serverseitig in der Datenbank implementiert werden kann – also die Datenbank JavaScript genauso gut versteht wie der Browser?

Oracle plant seine Datenbank zu erweitern, so dass neben den beiden schon bekannten Sprachen PL/SQL und Java weitere Sprachen im Datenbankserver selbst ausgeführt werden können (MLE steht dann für „multiple language engine“, also für die rweiterte PL/SQL Engine). Geplant – soweit bekannt – ist die Implementierung von drei Scriptsprachen:

  1. R
  2. JavaScript
  3. Python

Ziel ist es, nicht nur die Sprachen selber, sondern auch Module (Libraries), die zu diesen Sprachen gehören, verwenden zu können.
Auf diese Weise können bewährte Funktionalitäten auf einfache Weise in die Datenbank übernommen werden.

Im folgenden werde ich diese neuen Features am Beispiel der JavaScript Integration in die Oracle MLE Datenbank vorstellen.

JavaScript: Übersicht

(Die Implementierung von JavaScript befindet sich noch im Entwicklungsstadium, alle Beschreibungen beziehen sich auf die z.Z. aktuellste zur Verfügung stehende Version 0.3.0)
JavaScript kann auf drei Arten in der Oracle Datenbank verwendet werden:

  1. Als Funktion (UDF – User defined function) zur Erweiterung des Funktionsumfangs von SQL und PL/SQL
  2. Als Prozedur (Stored Procedure) zur Implementierung von Datenbearbeitungslogik
  3. Als JavaScript zur Laufzeit (Dynamic JavaScript) in PL/SQL erzeugt und ausgeführt

JavaScript Funktionen (UDFs – User defined functions)

Ziel ist die Erstellung von Funktionen komplett in JavaScript, die wie PL/SQL-Funktionen in SQL und PL/SQL verwendet werden können und so den Funktionsumfang erweitern helfen. In Verbindung mit der Nutzung etablierter „node.js“ Libraries können so aus einfachem Wege komplexe Funktionalitäten implementiert werden

Ein Beispiel

In JavaScript NodeJS Umfeld gibt es die Library „validator“, die Dutzende Funktionen zur String Bearbeitung und -validierung beinhaltet. Eine davon ist die Funktion „isEmail“, die einen übergebenen String dahingehend überprüft, ob er eine korrekte Email-Adresse darstellt.
Diese Funktion kann jetzt als JavaScript Funktion in der Datenbank hinterlegt und als UDF veröffentlicht werden und steht damit wie jede SQL-Standardfunktion zur Nutzung in DML-Statements zur Verfügung, z.B. als Filter (WHERE-Klausel) in einem Select-Statement bei einer Adressabfrage, bei der nur Adressen mit gültigen Email-Adressen zurückgegeben werden sollen, oder um alle Datensätze mit fehlerhafter Email-Adresse mit einem entsprechenden Fehlerstatus zu versehen.

JavaScript Prozeduren (Stored Procedures)

Prozeduren werden i.A. dazu verwendet, komplexe Regeln der Datenverarbeitung zu implementieren, die nicht oder nur sehr schwer in SQL implementiert werden können. Dazu benötigt die verwendete Prozedursprache Zugriff auf die Daten, um CRUD-Operationen zur Datenverarbeitung durchführen zu können.
Dem in der Datenbank laufenden JavaScript wird zu diesem Zweck ein spezielles SQL-Objekt zur Verfügung gestellt, das ähnlich einer Library oder einem „NodeJS“-Modul geladen werden kann und die Durchführung von CRUD-Operationen in JavaScript ermöglicht.
Serverseitige Ausführung von Prozeduren bedeutet auch, dass es keine Rückmeldungen oder Datenrückgaben nach außen gibt. Oracle hat für dieses Problem ein eigenes PL/SQL-Package entwickelt, das über einen speziellen Ausgabekanal Ausgaben in Form von Textzeilen ermöglicht. Oracle hat JavaScript nun so integriert, dass die Sprache diesen Ausgabekanal von PL/SQL direkt durch Verwendung der Debug-Console nutzen kann. Ausgaben mit „console.log(text …);“ werden wie PL-SQL „dbms_output.put_line“-Aufrufe ausgegeben.

JavaScript zur Laufzeit (Dynamic JavaScript) in PL/SQL

Die Ausführung von JavaScript (das als Text vorliegt) zur Laufzeit in PL/SQL wird erfolgt mit Hilfe des neuen PL/SQL-Package DBMS_MLE, mit dem ebenso als Text vorliegender Python-Code ausgeführt werden kann. Die Vorgehensweise erinnert stark an das Package DBMS_SQL zur Ausführung von (als Text vorliegendem) SQL zur Laufzeit in PL/SQL:

  1. Mit Funktion DBMS_MLE.CREATE_SCRIPT(‚PYTHON’|’JS‘, ‚Script Text in Python oder JavaScript‘) wird der Skript Text übergeben und ein Handle erhalten, das dann in allen Prozeduren des Packages DBMS_MLE verwendet werden muss.
  2. Enthält der Skript Text Bind-Variablen, muss für jede Bind-Variabel mit dem Aufruf von DBMS_MLE.BIND_VARIABLE(handle, ‚Bind-Variabel Name‘, ‚Bind-Variabel Wert‘) ein Wert gesetzt werden.
  3. Das Script wird mit dem Aufruf DBMS_MLE.EXECUTE_SCRIPT(handle) ausgeführt.
  4. Mögliche Rückgabewerte können nun je Wert mit einem Aufruf von DBMS_MLE.VARIABLE_VALUE(handle, ‚Bind-Variabel Name‘, variabel_zur_aufnahme_des_wertes) ausgelesen werden.
  5. Mit dem Aufruf von DBMS_MLE.DROP_SCRIPT(handle) wird die Bearbeitung des Scripts beendet und alle Ressourcen werden freigegeben.

Die Schritte 2) bis 4) können beliebig häufig wiederholt werden, um ein Script mit verschiedenen Ausgangswerten auszuführen.

JavaScript: Deployment

Zur Nutzung von JavaScript Funktionen und Prozeduren müssen zwei Objekte in der Datenbank angelegt werden:

  1. Anlegen eines Source Objektes mit dem eigentlichen JavaScript Code
  2. Anlegen eines Call Objektes mit der Definition der Aufrufform in SQL und PL/SQL für jede JavaScript Funktion und Prozedur

JavaScript Source Objekte

Der JavaScript Code von JavaScript Funktionen und Prozeduren wird über JavaScript-Libraries im Module-Format (am einfachsten im CommonJS-Format) in die Datenbank geladen. Jede Funktion, die veröffentlicht werden soll, muss vom Modul exportiert werden.

Beispiel CommonJS-Format:

mit „javascript_function_name“ als dem Namen der exportierten Funktion in JavaScript (wird bei der Definition der JavaScript Call Objekte benötigt).

Zur Übernahme in die Datenbank gibt es zwei Möglichkeiten:

  1. JavaScript-Libraries können direkt wie PL/SQL-Definitionen als DDL-Statements in die Datenbank geladen und kompiliert werden.
  2. Eine als JS-Module erstellt Codedatei wird als CLOB in eine temporäre Tabelle in die Datenbank geladen und dann über ein spezielles DDL-Statement kompiliert.
Syntax: Direkte Erstellung

Kürzt man den gesamten Text einer JavaScript Module Definition mit [JS-MODULE-TEXT] ab, lautet die Syntax des JavaScript Source Objekt Direkt DDL Statements:

mit „user_name“ als den Namen des Datenbankschemas/-users, in dem das JavaScript Source Objekt angelegt werden soll und mit „library_name“ als JavaScript Namen des JavaScript Source Objektes (wird bei der Definition der JavaScript Call Objekte benötigt).

Syntax: Erstellung mit Hilfe eines CLOB

Die mit [JS-MODULE-TEXT] bezeichnete komplette JavaScript Module Definition wird in eine CLOB-Spalte einer Tabelle geladen und dann mit einem Select-Befehl bereitgestellt. Dabei müssen die Tabelle und das Select-Statement so definiert werden, dass nur der CLOB zurückgegeben wird. Bezeichnet man das Select-Statement, das den CLOB zurückgibt, nun mit [CLOB-SELECT], lautet die Syntax des JavaScript Source Objekt CLOB DDL Statements:

JavaScript Call Objekte

Mit der Definition eines JavaScript Call Objektes wird die Schnittstelle zu SQL und PL/SQL einer JavaScript Funktion erstellt. Deshalb werden folgende Angaben benötigt

  • Verwendung der zuzuordnenden JavaScript Funktion als „User defined function“ oder „Stored Procedure“
  • Schema, in dem das JavaScript Source Objektes angelegt wurde („user_name“ im DDL-Statement)
  • Name des angelegten JavaScript Source Objektes in der Datenbank („library_name.js“ im DDL-Statement)
  • Name der zuzuordnenden JavaScript Funktion aus der JavaScript Module Definition (im CommonJS-Format die Angabe „javascript_function_name“ aus der Export-Definition)
  • Jeder Aufruf-Parameter der JavaScript Funktion mit der Angabe des Parameternamen und -Datentyp (getrennt durch ein Leerzeichen, z.Z. unterstützte Datentypen sind „string“, „number“, „date“)
  • Zu jedem angegebenen Aufruf-Parameter der JavaScript Funktion einen Parameter mit Namen und SQL Datentyp passend zum JavaScript Datentyp (VARCHAR2|CLOB -> string, NUMBER -> number, DATE -> date)
  • Wenn die zuzuordnende JavaScript Funktion als „User defined function“ verwendet werden soll, Angabe des JavaScript Datentyps des Rückgabewertes der JavaScript Funktion
  • Wenn die zuzuordnende JavaScript Funktion als „User defined function“ verwendet werden soll, Angabe des SQL Datentyps des Rückgabewertes

Das ergibt ein Minimum von 5 Angaben für eine als „Stored Procedure“ ohne Aufrufparameter zu verwendende JavaScript Funktion und einem Maximum von 7 + 4 * n (n Anzahl Parameter) für jede „User defined function“.

Syntax: Stored Procedure

Syntax: User defined function

JavaScript Texte in PL/SQL

Jeder gültige JavaScript Code kann als Text in PL/SQL mit Hilfe des Packages DBMS_MLE ausgeführt werden. Zusätzlich wird ein Objekt „mle“ im JavaScript Bereich zur Verfügung gestellt. Dieses Objekt hat zwei Eigenschaften „mle.sql“ und „mle.binds“.

„mle.sql“ ist das Objekt, das – wie auch schon bei der Beschreibung von JavaScript Prozeduren erwähnt – zur Interaktion mit der Datenbank bereitgestellt wird, um CRUD-Operationen in der Datenbank auszuführen.

„mle.binds“ dient dem Datenaustausch zwischen PL/SQL- und JavaScript Umgebung. Mit dem Aufruf DBMS_MLE.BIND_VARIABLE(handle, ‚ein_name‘, wert) vor der Ausführung des Skriptes wird in JavaScript eine Variabel „mle.binds.ein_name“ angelegt und ihr der Inhalt von Wert zugewiesen, der dann in JavaScript bei der Ausführung zur Verfügung steht.
Mit dem Aufruf DBMS_MLE.VARIABLE_VALUE(v_handle, ’noch_ein_name‘, v_plsql_variabel) nach der Ausführung des Skriptes kann der Wert der Variabel „mle.binds.noch_ein_name“ ausgelesen und der PL/SQL-Variabel v_plsql_variabel zugewiesen werden. Dabei kann diese Variabel vor der Ausführung des Skriptes mit DBMS_MLE.BIND_VARIABLE angelegt und mit einem Wert befüllt worden sein (der durch das Script geändert werden kann) oder die Variabel wird erst während der Ausführung des Scripts im Script angelegt und im Script mit einem Wert belegt.

Ein Beispiel

Script

Ausgabe

Ausblick

Als nächstes werde ich im zweiten Teil dieser Blogreihe die Integration von Python in die Oracle MLE Datenbank betrachten, wobei insbesondere die Möglichkeiten der Nutzung externer Module von Interesse sind. Oracle Labs ist wohl gerade dabei, hier die Oracle MLE Datenbank auszubauen.