28. Januar 2008

Java in der Datenbank

English title: Java in the database ...

In der Vergangenheit habe ich ja bereits einige Posts dem Thema Java in der Datenbank gewidmet. Nun, es ist weigehend bekannt, dass die Oracle-Datenbank seit Oracle8i ("i" für "Internet") mit einer Java-Engine ausgestattet ist ... Diese Java-Engine ...
  • ... ist wie PL/SQL Bestandteil der Datenbank und wird nicht als separater Betriebssystem-Prozeß gestartet
  • ... kann nicht durch den Nutzer ausgetauscht werden
  • ... führt bis Oracle10g den Java-Code grundsätzlich interpretiert aus.
    • Man kann den Code mit dem ncomp-Werkzeug native kompilieren - dazu ist ein separater C-Compiler nötig und die so erstellten shared objects bzw. DLL's liegen dann im Dateisystem
    • Auch die Java-Systemklassen werden interpretiert ausgeführt, es sei denn, man spielt die nativ vorkompilierte Variante ein (in Oracle10g auf der Companion CD). Für Java in Oracle10g ist es daher ein MUSS, die Companion CD einzuspielen.
  • ... ab Oracle11g ist ein Just-In-Time-Compiler (JIT) dabei. Java-Klassen werden also von der Datenbank automatisch nativ kompiliert - mit dem Paket DBMS_JAVA kann man das allerdings auch manuell initiieren. Das gilt auch für die Systemklassen.
Manchmal fragt man sich allerdings, welche Java-Version eigentlich in der Datenbank vorhanden ist. Nun, da die Java-Engine ja nicht ausgetauscht werden kann, lässt sich diese Frage leicht beantworten:
  • Oracle 8.1.5: Java 1.1
  • Oracle 8.1.7: Java 1.2
  • Oracle 9.2: Java 1.3
  • Oracle 10.1: Java 1.4.1
  • Oracle 10.2: Java 1.4.2
  • Oracle 11.1: Java 1.5
Es ist auch recht einfach, es selbst herauszufinden (dieses Beispiel lief in einer Oracle Datenbank 11.1.0.6):
create or replace function get_java_version(p_name in varchar2) return varchar2
is language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';
/
sho err

select get_java_version('java.version') from dual
/

GET_JAVA_VERSION('JAVA.VERSION')
--------------------------------------------------------------------------------
1.5.0_10
In the past I published some more posts about using the JVM inside the oracle database. Most people know that Oracle contains this JVM since Oracle8i ("i" for "internet") ... This server-side JVM ...
  • ... is part of the database (like PL/SQL) and does not run as a separate operating system process
  • ... cannot be exchanged or solely upgraded by the user
  • ... executes java code until Oracle10g (inclusive) basically in interpreted mode
    • Using the ncomp-Utility one can compile the code natively - a C compiler is required for that and the resulting shared objects / DLL's then reside in the file system dann
    • This is also true for the provided Java system classes. But these are provided in native compiled form by Oracle. In Oracle10g these native classes are contained in the Companion CD, so if you are going to execute Java in an Oracle10g database ... installing the Companion CD is a MUST.
  • ... beginning with Oracle11g the JVM has a Just-In-Time-Compiler (JIT). So the java classes are native-compiled automatically by the database (using the DBMS_JAVA package) one can also compile them manually. A separate C compiler is no longer required. This applies also to the system classes
Some times I get questions which java version runs in a particalar database. Since the JVM cannot be changed or upgraded solely this question is easy to answer:
  • Oracle 8.1.5: Java 1.1
  • Oracle 8.1.7: Java 1.2
  • Oracle 9.2: Java 1.3
  • Oracle 10.1: Java 1.4.1
  • Oracle 10.2: Java 1.4.2
  • Oracle 11.1: Java 1.5
It's also very easy to determine (this example ran in an Oracle database 11.1.0.6) ...
create or replace function get_java_version(p_name in varchar2) return varchar2
is language java name 'java.lang.System.getProperty(java.lang.String) return java.lang.String';
/
sho err

select get_java_version('java.version') from dual
/

GET_JAVA_VERSION('JAVA.VERSION')
--------------------------------------------------------------------------------
1.5.0_10
Es gibt noch ein paar Besonderheiten ...
  • Threading ist ausgeschaltet: Java-Programme, die Threading verwenden, laufen zwar, allerdings serialisiert die Datenbank alle Threads. Möchte man Java-Code in der Datenbank parallelisierem, muss man wie mit PL/SQL mit dem Paket DBMS_JOB bzw. DBMS_SCHEDULER arbeiten.
  • Die Klassen des AWT für grafische Features können ab Oracle10g ("headless" AWT) genutzt werden; natürlich kann die Datenbank keine Fenster öffnen.
  • Datenbankaktionen (SQL, DML, DDL) erfolgen wie in allen Java-Programmen per JDBC. Es gibt einen speziellen Connection-String für den server-side internal driver ("jdbc:default:connection:"), mit dem die Java-Klasse in "ihre eigene" Session verbunden wird. Username und Passwort werden nicht benötigt. Alle JDBC-Operationen sind Teil des Transaktions-Kontext dieser Session.
There some special characteristics ...
  • Threading is deactivated: Java classes using threading will run, but the database will serialize the execution of threads so that there is no parallelism. To really parallelize the execution of java code in the database you have to use DBMS_JOB or DBMS_SCHEDULER (like PL/SQL).
  • The GUI classes (AWT) are usable within Oracle10g and higher ("headless" AWT), but the database cannot open windows (of course).
  • Database actions (SQL, DML, DDL) are done via JDBC calls as in every java program. There is a special connection string for the server-side internal driver ("jdbc:default:connection:"), which connects the java class to the database session it is running in. Username and password are not required. All JDBC calls are executed in this session's transaction context.
Und das wichtigste zum Schluß: Mit den Call Specifications können PL/SQL-Wrapper für Methoden in Java-Klassen geschrieben werden. Damit sieht Java-Funktionalität nach außen hin aus wie eine PL/SQL-Prozedur, eine -Funktion oder ein Package ... und kann entsprechend von SQL oder PL/SQL aus genutzt werden. Damit kann (und die vergangenen Posts beschäftigen sich genau damit) Java sehr gut für Dinge verwendet werden, die PL/SQL nicht kann (bspw. bessere Interaktion mit dem Dateisystem). Übrigens: Obiger Code zum Feststellen der Java-Version ist eine eben solche "Call Specification".
And finally the the best thing: Using the Call Specifications one can create "PL/SQL-Wrappers" for java methods in the database. The java functionality then looks like a PL/SQL procedure, a function or a package. And it can be used like that within other PL/SQL code or within SQL queries or DML commands. So java in the database can complement PL/SQL very nicely when using it for requiremts PL/SQL cannot suffice (e.g. better filesystem interaction). BTW: The above code to determine the java version is such a "call specification".

21. Januar 2008

Weniger Tippen mit SQL*Plus: Miniskripts und SQLPATH

English title: PATH variable for SQL scripts: SQLPATH>

Ich bin mehr oder weniger zufällig drauf gestoßen - werde das folgende aber nun intensiver nutzen: Für SQL-Skripte bietet SQL*Plus auch eine Pfad-Variable (wie im Betriebssystem) an: die Umgebungsvariable SQLPATH. Wenn man mit start oder @ ein SQL-Skript startet, sucht SQL*Plus nicht nur im aktuellen Verzeichnis, sondern anschließend auch in den darin angegebenen Verzeichnissen. Und wie bei der PATH-Variablen im Betriebssystem werden die einzelnen Verzeichnisse durch Doppelpunkte (:) auf Lunix/UNIX bzw. durch Semikolons (;) auf Windows getrennt.
Das ist also ganz hilfreich für Abkürzungen. So kann man leicht ein Verzeichnis anlegen, dort häufig genutzte SQL-Kommandos unter kurzen Dateinamen (bspw. PURGE RECYCLEBIN als pr.sql) speichern und so immer wiederverwenden, egal von wo aus man SQL*Plus gestartet hat.
Hier ein paar Kommando-Abkürzungen, die ich in den letzten Tagen erstellt habe und die mir jetzt schon jede Menge Tipparbeit sparen:
-- dbapw.sql [password]
-- Als SYS AS SYSDBA verbinden; insbesondere gut bei langen TNSNAMES 

conn sys/&1@&_CONNECT_IDENTIFIER. as sysdba
undefine 1
-- dba.sql
-- Sichere Variante von "dbapw.sql" für Vorführungen

conn sys@"&_CONNECT_IDENTIFIER." as sysdba
-- cru.sql [username]
-- Abkürzung für CREATE USER; "schnelles Anlegen" eines Users

create user &1 identified by &1;
grant connect, resource, create view, alter session to &1;

undefine 1
-- c.sql [ausdruck]
-- "Alleskönner"; gut als Taschenrechner zu gebrauchen
-- @c "100 * 1.9 + 9.90" 

select &1 as ergebnis from dual
/
undefine 1
-- pr.sql
-- PURGE RECYCLEBIN

purge recyclebin
/
-- sess [username]
-- Gibt alle Sessions für den angegenenen User zurück

select
  sid, serial#, program
from v$session
where username = upper('&1')
/
undefine 1
I was not really aware of that (until today): SQL*Plus offers (as the operating system) a PATH variable for SQL Scripts: SQLPATH! When starting a SQL Script with start or @ then SQL*Plus looks for the script not only in the working directory but after that also in the directories noted in SQLPATH. And similar to the PATH variable the directories are seprated with a colon (:) on Unix/Linux and with a semicolon on Windows.
This is very useful to implement shortcuts. Just create a directory containing some SQL scripts with short filenames doing frequent issued statements (say: PURGE RECYCLEBIN as pr.sql). These shortcuts can then be used from whereever SQL*Plus was started ...
Here are some shortcuts I created during the last few days - and with which I saved a lot of typing ...
-- dbapw.sql [password]
-- Connect as "SYS AS SYSDBA"; this is useful when your TNSNAME is somewhat longer

conn sys/&1@&_CONNECT_IDENTIFIER. as sysdba
undefine 1
-- dba.sql
-- More secure variant of "dbapw.sql" for presentations

conn sys@"&_CONNECT_IDENTIFIER." as sysdba
-- cru.sql [username]
-- shortcut for CREATE USER; "fast user creation" on the fly

create user &1 identified by &1;
grant connect, resource, create view, alter session to &1;

undefine 1
-- c.sql [ausdruck]
-- "allrounder;" usable for example as a calculator
-- @c "100 * 1.9 + 9.90" 

select &1 as ergebnis from dual
/
undefine 1
-- pr.sql
-- PURGE RECYCLEBIN

purge recyclebin
/
-- sess [username]
-- Select all database sessions of a given User

select
  sid, serial#, program
from v$session
where username = upper('&1')
/
undefine 1

16. Januar 2008

Download verfügbar: Betriebssystem Kommandos aus der Datenbank ausführen

Download available: Executing OS commands from the database

Bereits vor einiger Zeit habe ich einen Post mit etwas Code zum Ausführen von Betriebssystem-Kommandos aus der Datenbank heraus veröffentlicht. Diesen Code habe ich nun mit dem Code zum Anzeigen von Verzeichnisinhalten zusammengeführt und zum Download bereitgestellt. Die erste Version 0.2 ist bei weitem noch nicht vollständig - aber es wird weitere Versionen geben.
Feedback, damit das Paket immer besser wird, ist natürlich erwünscht ...
Some time ago I posted some code for executing operating system commands from the database - now I merged this code with another post about retrieving directory contents with SQL and made it available as a download. This first version (0.2) is not complete (of course) but there will be additions in the future.
I appreciate feedback - so that the package gets better and better ...

15. Januar 2008

Noch einige Anmerkungen zum Thema SQL NULL ...

English title: NULL = NULL or NULL IS NULL or what (contd) ...?

Heute noch ein paar Ergänzungen zum Thema NULL. Nachdem der letzte Post das grundsätzliche Verhalten von NULL betrachtet hat, fehlen nur noch ein paar Informationen über das Verhalten von NULL bei AND bzw. OR-Verknüpfungen - auch hier hilft eine Tabelle:
AusdruckErgebnis
(true) and (null)NULL
(false) and (null)false
(true) or (null)true
(false) or (null)NULL
  • Fangen wir mit der ersten AND-Verknüpfung an: Die linke Seite ist mit true bekannt, die rechte Seite ist unbekannt (NULL). Das Endergebnis hängt nun von der rechten Seite ab: Wenn die rechte Seite true wäre, wäre das Ergebnis ebenfalls true, wenn sie false wäre, dann ergäbe sich als Ergebnis false. Beides ist also möglich; die Datenbank weiss es nicht, also kommt NULL heraus.
  • Analog die zweite AND-Verknüpfung: Die linke Seite ist mit false bekannt, die rechte Seite ist unbekannt (NULL). Nun ist es egal, wie die rechte Seite aussieht; wenn bei einer AND-Verknüpfung eine Seite false ist, kommt immer false heraus. Die Datenbank kann also mit Sicherheit sagen, dass false herauskommt.
  • Die OR-Verknüpfungen lassen sich nun ähnlich erklären. OR reagiert mit true genauso wie AND mit false. Hat eine Seite den Wert true, kommt immer true heraus. Die Datenbank kann also mit Sicherheit sagen, dass true herauskommt.
  • Die letzte OR-Verknüpfung mit false erklärt sich wie die erste AND-Verküpfung. Das Ergebnis hängt von der rechten Seite ab; die Datenbank weiss es also nicht; das Ergebnis ist also NULL.
Today a few additional comments about NULL values. The previous post was about SQL NULL basically - this one is about the behaviour when using it in boolean expressions (AND, OR). First we create (again) a value table:
ExpressionResult
(true) and (null)NULL
(false) and (null)false
(true) or (null)true
(false) or (null)NULL
  • We begin with the first AND-Expression: The left side is known (true), die right is unknown (NULL). The result therefore depends on the right side: If the right side was true, the result would be also true, if it was false the result would be also false. Both is possible; the database does not know, the result is therefore NULL.
  • We'll evaluate the second AND-Expression accordingly: The left side is known (false), the right side is - again - unknown (NULL). Now, since this is logical AND the result is always false, regardless of the right side. So the database is sure, that the result is false.
  • The OR expressions are explained the same way: OR evaluates with true as AND with false. If one side is true, the result is always true, regardless of the other side. So the database returns true.
  • The last OR expression with false is explained as the first one with AND. The result depends on the right side; the database does not know - the result is therefore NULL.

11. Januar 2008

SQL: "NULL = NULL" oder "NULL is NULL" oder was ...?

English title: NULL = NULL or NULL IS NULL or what ...?

Das Verhalten von SQL NULL (nicht nur) in der Oracle-Datenbank führt immer wieder zu Erstaunen und zu Überraschungen ... und dabei ist eigentlich alles ganz einfach. Im folgenden daher ein kurzer Abriß über das Verhalten SQL NULL und wie man damit umgeht.
Zunächst ist NULL definiert als der "unbekannte" (und nicht der "fehlende") Wert - NULL bedeutet also soviel wie "weiss nicht". So weit - so gut. Das Besondere ist nun, dass Oracle bei Vergleichen mit "dreiwertiger" Logik arbeitet - das Ergebnis eines Vergleichs kann also nicht nur wahr (true) oder falsch (false) sein, sondern auch weiss nicht, also NULL. Am besten nähert man sich dem mit einer Tabelle:
VergleichErgebnis
1 = 0false
1 = 1true
1 = NULLNULL
NULL = NULLNULL
Die ersten beiden Zeilen sind klar; interessant ist die dritte. Der Vergleich 1 = NULL meint übersetzt soviel wie "Ist 1 gleich dem unbekannten Wert?" Die Antwort kann nur sein: "Weiss nicht", also NULL. Dasselbe gilt für den Vergleich NULL = NULL; das meint soviel wie "Ist ein unbekannter Wert gleich einem anderen unbekannten Wert?" Antwort wiederum: "Weiss nicht", also NULL. Achtung: Es meint nicht: "Gleicht der "fehlende" Wert dem "fehlenden" Wert?" - die Antwort darauf wäre: wahr. Aber NULLrepräsentiert eben den unbekannten, nicht den fehlenden Wert.
Das ist übrigens bei Negationen nicht anders:
VergleichErgebnis
1 != 0true
1 != 1false
1 != NULLNULL
NULL != NULLNULL
Der Aussagewert der Unbekannten ändert sich nicht, auch wenn man die Frage negiert. So meint 1 != NULL soviel wie "Ist 1 ungleich der Unbekannten?. Die Antwort lautet wieder mal "weiss nicht", könnte ja auch gleich sein. Nun, die meisten Entwickler wissen, dass man NULL-Werte nicht mit dem Gleichheitszeichen (=), sondern mit IS NULL abfragen muss. Wir erweitern die Tabelle ...
VergleichErgebnis
1 IS NULLfalse
NULL IS NULLtrue
Der erste Vergleich meint übersetzt soviel wie "Ist 1 ein unbekannter Wert?" - das kann man klar beantworten: "1 ist bekannt", also "nein". Der Vergleich liefert also false zurück. Die nächste Zeile fragt, ob "der unbekannte Wert unbekannt ist" - klaro: Das ist wahr; als Ergebnis kommt also true heraus.
Was bedeutet das also für den Entwickler?
  1. Vergleiche mit NULL niemals mit dem Gleichheitszeichen (=), sondern immer mit IS NULL durchführen!
  2. Wenn mit dem Gleichheitszeichen gearbeitet wird und eine Variable hat den Wert NULL, kommt (wie oben gesehen) auch NULL als Ergebnis heraus. Das ist weder wahr noch falsch. In einem PL/SQL if-then-else-Block landet man daher immer im else-Zweig.
So, das ist es eigentlich auch schon: Nur eines noch (ein Punkt bleibt immer): Seit Urzeiten setzt die Oracle-Datenbank den Leerstring ('') mit NULL gleich. Das ist nun vom Konzept her nicht so ganz sauber, denn der Leerstring ist keine Unbekannte, sondern klar definiert eben als Leerstring. Aber hier kann man nur sagen: isso - und da tausende Kunden mit abertausend Anwendungen sich u.U. genau hierauf verlassen, wird sich daran wohl auch so schnell nix ändern!
The behaviour of SQL NULL (not only) in the Oracle database leads to frequent questions and sometimes irritations ... but basically it's very easy. This post therefore contains a brief overview of SQL NULL and how to deal with it.
NULL is defined as the unknown value (and not as the "missing value") - NULL therefore means as much as "don't know". The trick is now that databases (and so Oracle) works with the "three-value-logic" - the result of an expression can be not only true or false but also "don't know (NULL). The best approach to understand this behaviour is to create a value table:
ComparisonResult
1 = 0false
1 = 1true
1 = NULLNULL
NULL = NULLNULL
The first two lines need no explanation; focus is on the following ones. The comparison 1 = NULL translated into "human language" means as much as "Equals '1' the unknown value?" The answer can only be: "don't know", say NULL. The same applies to the comparison NULL = NULL; this can be translated to"Equals on unknown value another unknown value?" Answer again: "don't know" (NULL).
Heads Up: It does not mean "Does missing value equal missing value? - Such a question would be answered with true; but NULL represents the unknown value. This also applies for negations:
ComparisonResult
1 != 0true
1 != 1false
1 != NULLNULL
NULL != NULLNULL
The negation of unkown is still unknown. 1 != NULL does therefore translate to "Does '1' not equal the unknown value?. The answer is again "don't know".
Most developers know that one cannot use the equal-sign (=) in a comparison with NULL - IS NULL has to be used. So we extend our table ...
ComparisonResult
1 IS NULLfalse
NULL IS NULLtrue
The first line translates to "Is '1' an unknown value?" - this leads to a clear answer: "no". The result is therefore false. The next line asks whether "the unknown value unknown is" - this is clear: true.
What does this mean to the developer?
  1. Never compare NULL values using the equal-sign (=); always use IS NULL!
  2. When equal-comparisons are performed and one value is NULL then the result is always NULL (as seen above) - neither true nor false. In a PL/SQL if-then-else-Block this always evaluates in the else branch.
So ... that's it! There is (as always) only one weak point: The oracle database sets the empty string ('') equal to NULL (since the annals). Following the concept strictly this is not correct: The empty string is a known value: empty. Here we can only say: That's life! - and since thousands of customers with again thousands of applications rely on this: this is not likely to change.

7. Januar 2008

SQL*Plus mit Kommandozeilen-Historie auch auf UNIX/Linux?

English title: SQL*Plus with Commandline history ... possible ...?

Erstmal ein gutes neues Jahr 2008 an alle ...
... und es geht auch schon gleich los: Ich selbst (und ich denke, viele andere auch) arbeite immer wieder mal mit SQL*Plus. Auf Windows-Umgebungen habe ich mich recht gut daran gewöhnt, dass die Kommandozeilen-Historie mit den Cursortasten dort funktioniert (also Cursor-Rauf = letztes Kommando). Auch das Editieren eines Kommandos mit Cursor-Links oder Cursor rechts funktioniert sehr gut. Nur auf der UNIX/Linux-Umgebung wird's dann wieder holprig - da bekomme ich irgendwelche Steuerzeichen, wenn ich die Cursortasten nutze - Editieren einer Zeile heißt im besten Fall, dass die Backspace-Taste geht. Und das, obwohl die Linux-bash-Shell das alles kann. Na gut, mit c (c.fr1om.from) kann man die letzte Zeile ebenfalls bearbeiten, aber bitte: Wir leben im Jahr 2008!
First things first: Happy new year to everyone!
... and we'll start immediately: I (and I thing others also) use SQL*Plus very frequently. In a Windows Enviroment I'm quite comfortable with the command line history and the possibility to edit the current sql command using the cursor keys. When then working on UNIX/Linux enviroment things get cumbersome - cursor-up leads to some kind of control character but not to the last sql commandline - editing means in the best case that the backspace key is working ... and all this regardless to the fact that the Lunix bash shell supports all this. OK using c you can also edit the last line (c.fr1om.from) but hey: we now write the year 2008!
Aber es gibt Abhilfe von Ljubomir J. Buturovic (San Francisco State University): Schaut euch mal gqlplus - ein kleiner Aufsatz für SQL*Plus auf UNIX/Linux, der eben diese Lücke schließt - darüber hinaus gibt es sogar "Namensvervollständigung" mit der TAB-Taste - einfach mal herunterladen und ausprobieren ...
But there is a solution for that. Have a look at gqlplus (by Ljubomir J. Buturovic of the San Francisco State University) - this is an add-on to Oracle SQL*Plus which adds all that functionality; plus name completion with the TAB key.

Beliebte Postings