13. Dezember 2007

Talk2Gerd in Deutsch

Heute lernte ich von Patrick Wolf, wie einfach es sein kann, einen Blog in mehreren Sprachen zu schreiben. Dies hier ist nun mein guter alter Talk2Gerd auf Deutsch. Die englische Version findet man hier.

Abschliessend noch ein Foto von meiner Firma im tiefen Schnee... und wir haben hier ne Menge Schnee in den letzten Wintern gehabt:

12. Dezember 2007

Libraries: PLL vs. PLX

Ein grosses Problem beim Erstellen von Forms-Anwendungen ist: Soll ich Libraries auf Basis von *.pll oder *.plx attachen?

Schauen wir uns einmal ein paar typische Fälle an:

A) Für die Forms-Entwicklung werden PLL und PLX genutzt. Der Forms Builder benutzt das Arbeitsverzeichnis C:\Forms und der FORMS_PATH hat einen Pfad auf C:\Forms\Lib. Die Sourcecodes heissen Lib.pll, Menu.mmb und EMP.fmb.

Wenn es eine generierte Lib.plx in c:\forms oder c:\forms\lib gibt, dann ist dies die Version, die zur Runtime benutzt wird.

Die Reihenfolge, in der Forms Libraries nutzt ist folgende:
1) Wenn Lib.plx im Arbeitsverzeichnis liegt wird es genutzt
2) Wenn es nicht gefunden wird, suche im FORMS_PATH nach Lib.plx
3) Wenn es nicht gefunden wird, suche im Arbeitsverzeichnis nach Lib.pll
4) Wenn es nicht gefunden wird, suche im FORMS_PATH nach Lib.pll
5) Wenn die Library nicht gefunden wird, erzeugt Forms eine Fehlermeldung

Dies gilt auch für Forms und Menüs



Wenn das PLX im lokalen Verzeichnis eine ältere Version ist als die entsprechende PLL, dann führt dies zu grossen Problemen während der Laufzeit. Fehler zu finden ist dann nahezu unmöglich, solange der Entwickler die Timestamps der Libraries nicht überprüft.

B) Wenn in der Anwendung nur PLL's benutzt werden:

Während der Entwicklung und zur Laufzeit hat man keine Probleme. Wenn eine Library nicht im Arbeitsverzeichnis liegt, wird sie im FORMS_PATH gefunden.

Zusammenfassung:

Ich selber nutze keine plx-Libraries mehr, da ich in viel zu vielen Projekten mit den Problemen konfrontiert war. Fehlersuche wird immens aufwändig, wenn man grössere Forms-Pfade hat und irgendwo eine plx-Library existiert, die nicht die richtige Version besitzt.

Viele Entwickler sagen: plx ist schneller. Dies konnte ich bisher nicht nachvollziehen und ich hatte auch noch nie Performance-Probleme damit.

Andere Entwickler sagen: plx ist der kompilierte Code, somit kann niemand den Sourcecode stehlen - in diesem Fall sollte man jedoch einmal folgendes ausprobieren:

Erzeuge eine Library mit einem Package und einer Konstanten:
PACKAGE Const IS
HiddenPW                     CONSTANT Varchar2 (100) := 'HiddenPW';
END Const;

Erzeuge die plx und öffne sie in einem Editor. Suche nach "HiddenPW". Der Name der Konstanten und ihr Wert sind auf diese Weise direkt zu finden. Das ist alles andere als sicher. Nur der Sourcecode ist unleserlich. Meines Erachtens ist eine Forms-Anwendung auf einem Oracle Application Server sicher. Somit braucht man den lesbaren Code der pll nicht in eine plx umzuwandeln

Dies gilt natürlich ausdrücklich nicht für Standard-Software, die man verkauft und deren Sourcecode man nicht ausliefern möchte. Hier ist die plx die einzige Alternative und auf der Datenbank sollte man über ge-wrappte Packages nachdenken.

11. Dezember 2007

DOAG Top News: Forms 11g

Die DOAG hat als neue Top News einen meiner Forms 11g Abstracts abgedruckt.

Dort schrieb ich über die neuen Features von Forms 11g und den vielfältigen Möglichkeiten, die uns in der künftigen SOA Welt zur Verfügung stehen.

Forms 11g unterwegs in Richtung SOA :

Donnerstag, 6. Dezember 2007 (wta) – Nach über sechs Jahren bringt die neue Forms-Version 11g erstmals wieder Neuerungen im Forms Builder. Die DOAG hat vorab einen Blick darauf geworfen und zwei extrem wichtige Funktionalitäten entdeckt.

Die erste betrifft die Kommunikation mit Advanced Qeueing aus der Datenbank. Es gibt einen neuen Objekttyp "Event", der mit einer Queue zusammenarbeitet. Werden Daten in eine Datenbank-Queue gestellt, startet in der Formsmaske sofort ein Event, der abgearbeitet wird.

Das zweite Highlight bezieht sich auf die Javascript-Integration. HTML-Seiten können nun direkt per Javascript mit Oracle Forms kommunizieren. Früher war dies nur über Umwege oder Java-Applets möglich. Mit der neuen Technik werden in Forms "WHEN CUSTOM JAVASCRIPT EVENT"-Trigger gestartet, die vom Entwickler benutzt werden können. Die Kommunikation verläuft bidirektional. Somit können auch Änderungen in der HTML-Seite direkt von Forms via Javascript vorgenommen werden.

Diese beiden neuen Techniken sind deutliche Anzeichen dafür, dass Oracle Forms mit neuem Schwung in die Zukunft durchstartet. In der neuen Oracle Fusion Middleware 11g ist Forms einer der zentralen SOA-Bestandteile.

Weitere Informationen zu Forms 11g finden Sie bei der Deutschen Forms Community sowie im Blog des Betatesters für die DOAG.


Vielen Dank an die DOAG !

1. Dezember 2007

Neues Statement of Direction, Nov. 2007

Im aktuellsten SoD wird Oracle Forms bis mindestens 2013 supported:

26. November 2007

Forms 11g New Features: Events

Mein Vortrag auf der DOAG Konferenz 2007 war "Forms 11g: Ein Blick hinter die Kulissen"

Die zwei neuen Features sind: Advanced Queuing und Javascript-API. Hier beschreibe ich nun die AQ-Technik mit den neuen Forms-Events.

Events - Interaktion mit Advanced Queuing

Dies ist die erste grosse Änderung im Forms Builder seit 6 Jahren. Wir können Events erstellen und Queues zuweisen. Sobald neue Daten in einer AQ ankommen, wird der neue Trigger WHEN-EVENT-RAISED gestartet.


Erlaubte Properties für Events

Event Type : Data Base, User Defined
Subscription Name : AQ-Name (LOV)
Scope : Application, Form
Auto Subscribe : Yes, No
Correlation ID :
View Mode : Browse, Locked, Removed

formsweb.cfg

maxEventWait=1000

Der neue Parameter maxEventWait ist sehr wichtig. Der Wert gibt in Millisekunden das Zeitintervall an, in dem die AQ's überprüft werden. Ansonsten würde die Formsmaske nur noch Refreshs ausführen, wenn der Anwender in der Maske etwas anklickt.

Code-Beispiel

BEGIN
:CONTROL.TI_Payload := get_event_object_property ('EV_Default', Event_Payload);
IF upper (:CONTROL.TI_Payload) = 'ATTACH DEBUG' THEN
DEBUG.Attach;
...
END IF;
END;

Dieses Beispiel zeigt eine Fernsteuerung für ein Remote Debugging. Auf der Datenbank füllt man einfach in einer Queue einen Datensatz ein, der dem Forms-Event EV_DEFAULT zugeordnet ist. Jedesmal, wenn die Payload den Wert ATTACH DEBUG hat, startet man in der Forms Runtime das Debug.

In meinem Download-Bereich gibt es einen Link zum Vortrag.

22. November 2007

DOAG Konferenz 2007

Heute war der letzte Tag der diesjährigen DOAG-Konferenz. Die DOAG (Deutsche Oracle Anwender Gruppe) feierte ihren 20. Geburtstag und feierte diesen gebührend im neuen Conference-Center in Nürnberg. Interessante Vorträge wie z.B. "Oracle 5.0 New Features" gab es :-)

Dies war eine phantastische Präsentation von Dierk Lenz. Er benutzte einfach seine 20 Jahre alten Folien und zeigte uns die neuesten Datenbank-Features aus dem Jahre 1988.

Dies konnte man live miterleben in einer vmWare, in der ein DOS 6.22 mit einem SQL*Forms 2 installiert waren.



In diesem Vortrag waren alleine schon 200 Leute. Einfach nur genial!

1. Oktober 2007

Forms Start-Up (Teil 1)

Dies ist der erste Artikel einer kleinen Serie. Ich zeige an dieser Stelle Methoden und Verbesserungen des Forms-Starts. In fast allen Web-Projekten lernte ich in den vergangenen Jahren Techniken kennen, in denen das Browser-HTML verändert wurde um zum Beispiel das versehentliche Schliessen des Browser-Fensters zu verhindern.

Mein erstes Beispiel kommt von Francois Degrelle. Er gab mir den Tipp mit einem automatisch versteckenden Internet Explorer beim Forms Startup. Der einzige Parameter der geändert werden musste ist der HTMLbodyAttrs in der formsweb.cfg:

HTMLbodyAttrs=onLoad='javascript:self.moveTo(2000,2000)'

Nach dem Forms-Start wandert das Browser-Fenster an die X,Y-Position 2000, 2000. Das ist normalerweise ausserhalb des sichtbaren Bereichs. Dies lässt den Anwender glauben, dass der Browser nun geschlossen ist. Das Icon in der Taskbar ist zwar noch sichtbar, aber der Browser ist nicht zu sehen, selbst wenn der Anwender auf das Icon klickt.

Pro:
- Der Anwender glaubt, dass der Browser sich selber versteckt
- Nur die Forms-Anwendung ist sichtbar (bei separateFrame=True)

Contra:
- Die Task des Browsers ist sichtbar und kann nicht aktiviert werden. Sie kann nur geschlossen werden

Das ist zur Zeit meine Lieblingsvariante

17. September 2007

Bigger. taller. better :-)

Nachdem ich ein wenig am Template-HTML herumgespielt habe fand ich ein paar sehr wichtige Properties für meinen Blog.

Die ehemalige Breite des Blogs war ein echtes Hindernis und sorgte dafür, dass ich Sourcecodes oft kürzen musste, damit sie in eine Zeile passten. Dieses Problem ist jetzt ein für alle Mal gelöst. Nun habe ich 50% mehr Platz für meine Snippets :-)

13. September 2007

Einfaches Logging und Debugging in Forms

Jede Forms-Anwendung braucht eine simple Methode um Fehler wegzuschreiben. Diese Technik kann desweiteren dazu benutzt werden um Forms, Reports und PL/SQL zu debuggen.

Zuerst einmal erstellen wir die Tabelle, Sequence und eine View um die Logging-Information zu speichern:
CREATE TABLE Logging (
  ID                         NUMBER(8,0) NOT NULL,
  SESSION_ID                 NUMBER(8,0),
  INSERT_DATE                DATE NOT NULL,
  TEXT                       VARCHAR2(2000) NOT NULL);

CREATE SEQUENCE Logging_SEQ;

CREATE OR REPLACE VIEW V_Logging_desc 
         (ID, SESSION_ID, INSERT_DATE, TEXT)
AS SELECT ID, SESSION_ID, INSERT_DATE, TEXT
     FROM Logging
ORDER BY SESSION_ID DESC, ID DESC;

Desweiteren brauchen wir ein Package mit den Funktionen und Prozeduren, die für`s Logging benötigt werden.
CREATE OR REPLACE PACKAGE PK_DEBUG IS
  FUNCTION Debug_allowed RETURN BOOLEAN;
  FUNCTION Next_ID       RETURN NUMBER;

  PROCEDURE Disable;
  PROCEDURE Enable;
  PROCEDURE Destroy;
  PROCEDURE Init  (P_Debug_allowed IN BOOLEAN DEFAULT TRUE);
  PROCEDURE Write (P_Text IN VARCHAR2,
                   P_Session_ID IN NUMBER DEFAULT NULL);

  G_Debug_allowed BOOLEAN := TRUE;
  G_Session_ID    NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY PK_DEBUG IS
FUNCTION Debug_allowed RETURN BOOLEAN IS
BEGIN
  RETURN (G_Debug_allowed);
END;

FUNCTION Next_ID RETURN NUMBER IS
  V_ID NUMBER;
BEGIN
  SELECT Logging_SEQ.nextval
    INTO V_ID
    FROM DUAL;
  RETURN (V_ID);
END;

PROCEDURE Disable IS
BEGIN
  G_Debug_allowed := FALSE;
END;

PROCEDURE Enable IS
BEGIN
  G_Debug_allowed := TRUE;
END;

PROCEDURE Destroy IS
BEGIN
  Write ('----------------------stopp '
    || to_char (G_Session_ID) || '--');
  G_Session_ID := NULL;
END;

PROCEDURE Init (
  P_Debug_allowed IN BOOLEAN DEFAULT TRUE) IS
BEGIN
  G_Debug_allowed := P_Debug_allowed;
  G_Session_ID := Next_ID;
  Write ('--start ' || to_char (G_Session_ID)
    || '----------------------');
END;

PROCEDURE Write (
  P_Text       IN VARCHAR2,
  P_Session_ID IN NUMBER DEFAULT NULL) IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  IF Debug_allowed THEN
    IF G_Session_ID IS NULL THEN
      Init;
    END IF;
    INSERT INTO Logging (ID,
      Session_ID, Insert_Date, Text)
    VALUES (Next_ID,
      NVL (P_Session_ID, G_Session_ID),
      Sysdate, P_Text);
    COMMIT;
  END IF;
END;
END;
/

Das Debugging wird mit INIT gestartet und endet mit DESTROY. Fehlermeldungen werden in die Tabelle festgeschrieben mit WRITE. Zum Beispiel:
pk_Debug.Write ('Hello World - ' || V_Test);

Teile des Debuggings können deaktiviert werden mit DISABLE, so dass von dieser Zeile Code an keine Logging-Informationen mehr geschrieben werden, bis man ENABLE startet.

Die View V_Logging_desc zeigt die Logging-Daten, gruppiert nach der neuesten Session-ID.
ID Session Insert-Date     Text
============================================
24    21   10.09.-12:38:48 -------stopp 21--
23    21   10.09.-12:38:48 Hello World - 42
22    21   10.09.-12:38:48 --start 21-------


Viel Spass damit
Gerd

31. August 2007

San Francisco Impressionen

Hier sind einige Fotos von San Francisco, aufgenommen in der Zeit nach dem Test-Event
Lombard Street Richtung Coit Tower

17 Miles Drive

Muir Woods

Der Beweis: Second Life existiert doch in der Realität

Steinkunst in Sausalito

Die Golden Gate

24. August 2007

Oracle Fusion Middleware 11g Beta Test - Letzter Tag

Heute war der letzte Tag des Beta-Tests. Nach den letzten Tests startete die grosse Feedback-Runde im Oracle Conference Center.

Alle Kunden und 50 Oracle Experten trafen sich, desweiteren waren Dutzende Oracle Mitarbeiter per Web-Konferenz zugeschaltet. In den folgenden 3 Stunden wurden nacheinander alle Produkte des Fusion Middleware-Stacks angehört und wir gaben unser Feedback ab. Der einzige Forms-Tester (myself) gab folgendes Statement an Oracle:

Zuerst einmal: Forms ist eine sehr grosse Plattform in Deutschland und dieses Beta 2 Programm zeigte uns in den letzten Tagen das erste Oracle Forms, das nach vielen Jahren mit neuer Funktionalität im Forms Builder aufwartete.

Danach beschrieb ich die neuen Features und wie wichtig sie in der Forms Community sind.

Am Ende erklärte ich noch, welche Funktionalität ich persönlich gerne noch im Forms Builder sähe:

* ein Object namens Web-Service auf Form-Ebene
* die Integration von BI-Beans
* und am wichtigsten: einen neuen PL/SQL-Editor

Das war mein Statement und nun möchte ich mich vielmals bedanken bei Oracle für die Einladung zu diesem Event, Duncan und seiner Gruppe für einen phantastischen Support in der Woche. Es war klasse, solch eine Chance zu bekommen und mit diesen Profis arbeiten zu können.

Vielen Dank auch an Rolf (meinen Boss) der diesen Trip finanzierte, an Manuela (die mich gehen liess) und an Andreas, der eine wichtige Schulung beim Kunden übernahm, während der Testwoche. Vielen Dank euch allen!

Und hier ist der Beweis, dass Duncan seine Karriere in der NBA gestartet hat, bevor er zu Oracle kam (Ich bin 1,97):

PS: Falls sich jemand wundert, warum ich die neuen Features samt Funktionalität nicht beschreibe, dem sei gesagt, dass ich bis zur DOAG Konferenz Stillschweigen halten muss.

23. August 2007

Forms Beta-Test, Tag 4

Duncan und ich erfanden ein neues Feature vor ein paar Tagen. Heute war es soweit und ich konnte es in Forms 11g einbauen und testen:

Remote Debugging-Start mittels AQ

Die Idee ist einfach: Ein Entwickler stellt eine Information in eine Forms-AQ ein, auf der alle Formsmasken horchen. Diese Payload wird nun in den einzelnen Masken ausgelesen und startet einen neuartigen AQ-Trigger. Der Anwender wird nun zur Laufzeit gefragt, ob er seine Maske debuggen lassen möchte. Wenn er dies bestätigt startet die Maske ein debug.attach und der Entwickler kann seinen Forms Builder nutzen um die Client-Runtime zu debuggen.

Dies ist eine neuartige Form des Remote Debuggings das nur mit Forms 11g integriert werden kann.

22. August 2007

Forms Beta-Test, Tag 3

Heute haben Duncan und Phil Kuhn an einigen neuen Forms Features gearbeitet. Es ist grossartig zu sehen, wie sie und das Team im Hintergrund arbeiten.

Dies ist die erste Oracle Forms Version seit ca. sieben Jahren, in der neue Funktionalität (neue Objekte und Trigger) im Forms Builder integriert wurde!

All jene, die das Ende von Oracle Forms propagieren werden eines besseren belehrt, wenn das neue Release herauskommt.

21. August 2007

Forms Beta-Test, Tag 2

Der zweite Tag des Beta-Tests startete mit einem Überblick des neuen Oracle Application Servers. Vier Sessions wurden diesbezüglich heute präsentiert. Die restliche Zeit blieb zum Betatesten von Oracle Forms 11g.

Am Ende des Tages lud Oracle uns in ein nettes Restaurant nahe des Headquarters ein.

20. August 2007

Einladung zum Oracle Forms Beta-Test

Oracle HQ in Redwood Shores lud mich zum diesjährigen Beta-Test der Fusion Middleware ein.

Heute starteten wir im Headquarter zusammen mit 16 anderen eingeladenen Firmen. Zusätzlich zu uns Testern unterstützte Oracle diese Veranstaltung noch mit nahezu 50 Oracle Cracks. Zum Beispiel den Oracle Forms Chefentwickler Duncan Mills.

Oracle entwickelte umfangreiche Test-Szenarien für die kommenden 5 Tage. Somit werden wir in der Lage sein, tief in all die neuen Produkte reinzuschauen.

Die Stimmung in der Mannschaft ist grossartig!

3. August 2007

Setze Record-Status auf Query nach dem POST-QUERY

In Blöcken mit einem POST-QUERY-trigger hat man oft das Problem, dass der Record-Status auf CHANGED wechselt, wenn Non-Basetable-Items verändert wurden im POST-QUERY.

Wenn man nun den Record-Status zurück auf QUERY setzt, ist das auf jeden Fall schon mal eine gute Vorgehensweise. Dazu erstellen wir eine Prozedur zum Setzen des Record-Status auf QUERY:

PROCEDURE Set_Record_Query_Status IS
BEGIN
Set_Record_Property (NAME_IN ('SYSTEM.TRIGGER_RECORD'),
NAME_IN ('SYSTEM.TRIGGER_BLOCK'),
STATUS,
QUERY_STATUS);
END;

dieser wird dann im POST-QUERY-Trigger aufgerufen:

BEGIN
SELECT someColumns
INTO :myBlock.nonBasetable_Item
FROM myTable
WHERE someFilter;

Set_Record_Query_Status;
END;

Nachdem nun jeder Datensatz auf QUERY-Status gesetzt wurde hat man mit den Daten des Blockes auch kein Problem mehr.

Wichtig: Wenn der POST-QUERY Basetable-Items im Datensatz verändert muss man die erzeugten Datensatz-Sperren zurücksetzen, falls der Block-Locking-Modus auf Immediate gesetzt ist. Dieses Problem kann folgendermassen gefixt werden:


BEGIN
Set_Block_Property ('myBlock', LOCKING_MODE, Delayed);

SELECT someColumns
INTO :myBlock.nonBasetable_Item
FROM myTable
WHERE someFilter;

Set_Block_Property ('myBlock', LOCKING_MODE, Immediate);

Set_Record_Query_Status;
END;



try it
Gerd

1. August 2007

Oracle Certified Trainer

Seit Freitag bin ich nun auch Oracle Certified Trainer. Dies ist die Voraussetzung dafür, in der Oracle University arbeiten zu dürfen.

20. Juli 2007

Neustart meines Forms Framework-Projektes

Open-Source-Projekte sollte man dort hosten, wo sie

- einfach zu benutzen
- einfach zu administrieren
- einfach zu finden sind

und vor zwei Tagen fand ich Google Code. Dies ist eine phantastische Plattform, um Projekte zu sharen!

http://code.google.com/p/forms-framework/

Zur Zeit deploye ich gerade die Haupt-PL/SQL Library und das Forms Template

Viel Spass mit dem Projekt und seinen nächsten Releases
Gerd

19. Juli 2007

Check Form_Success für jedes Built-In

Alle Built-Ins haben das Problem, das sie keine echten Exceptions werfen. Nur im ON-ERROR

Beispiel: Sie möchten zum Block Customer navigieren. Dabei unterlief ihnen ein Schreibfehler:

Go_Block ('CUSTOMR');
Do_something_after_Go_Block;

Go_Block kann nicht zu dem angegebenen Block navigieren, da CUSTOMR nicht existiert. Es wird jedoch keine Exception für innerhalb des PL/SQL-Blockes erzeugt. Das heisst, der Code wird nicht unterbrochen und die Abarbeitung von Do_something_after_Go_Block startet. Das ist immer ein grosses Problem!

Lösung: Erzeuge eine Prozedur Check_Builtin

PROCEDURE Check_Builtin IS
BEGIN
IF NOT Form_Success THEN
RAISE Form_Trigger_Failure;
END IF;
END;

Benutze diese Prozedur nach jedem Built-In:

BEGIN
Go_Block ('CUSTOMR');
Check_Builtin;
Do_something_after_Go_Block;
EXCEPTION
WHEN FORM_TRIGGER_FAILURE THEN
-- do something ...
END;

Desweiteren kann man ein eigenes Built-In schreiben für Goto_Block anstelle von Go_Block: Diese neue Prozedur arbeitet intern mit dem neuen Check_Builtin

PROCEDURE Goto_Block (P_Block IN VARCHAR2) IS
BEGIN
Go_Block (P_Block);
Check_Builtin;
Do_something_after_Go_Block;
END;

als nächstes:

BEGIN
Goto_Block ('CUSTOMR');
Do_something_after_Go_Block;
EXCEPTION
WHEN FORM_TRIGGER_FAILURE THEN
-- do something ...
END;

Wichtig: Wenn sie diese Technik anwenden müssen sie ein Exception-Handling schreiben, dass den FORM_TRIGGER_FAILURE abfängt und behandelt.

Diese Technik ist identisch zu Oracle's Check_Package_Failure-Routine, diese kann jedoch nur genutzt werden, wenn in der Maske mindestens eine Master-Detail-Relation existiert.

benutze Check_Builtin
Gerd

10. Juli 2007

Zusammenführung grosser Default-Where-Bedingungen

Der einfachste Weg um Where-Bedingungen zu konkatinieren ist:

Beispiel: Eine leere Maske mit der Tabelle DEPT. In diesem Beispiel soll es drei Regeln geben, nach denen die Default-Where-Bedingung aufgebaut wird. Das Problem ist: Ab dem zweiten IF muss man jedesmal überprüfen, ob in V_Default_Where schon etwas drinsteht. Falls ja wird ein ' AND ' dran konkatiniert vor jedem neuen Teilstring.

DECLARE
V_Default_Where VARCHAR2 (2000);
BEGIN
IF Rule_1_is_TRUE THEN
V_Default_Where := 'DEPTNO IN (10, 20, 30)';
END IF;

IF Rule_2_is_TRUE THEN
IF V_Default_Where IS NOT NULL THEN
V_Default_Where := V_Default_Where ||
' AND DNAME != ''SALES'' ';
ELSE
V_Default_Where := 'DNAME != ''SALES'' ';
END IF;
END IF;

IF Rule_3_is_TRUE THEN
IF V_Default_Where IS NOT NULL THEN
V_Default_Where := V_Default_Where ||
' AND LOC IS NOT NULL';
ELSE
V_Default_Where := 'LOC IS NOT NULL';
END IF;
END IF;

Set_Block_Property ('DEPT', DEFAULT_WHERE,
V_Default_Where);
Go_Block ('DEPT');
Execute_Query;
END;

Diese IF's sind nicht wartbar. Änderungen in der Where-Bedingung bedeuten immer zwei Änderungen im Code:

...
V_Default_Where := V_Default_Where ||
' AND LOC IS NULL';
ELSE
V_Default_Where := 'LOC IS NULL';
...

Ein besserer Ansatz ist die Benutzung von ' AND ' bei jeder Konkatinierung:

DECLARE
V_Default_Where VARCHAR2 (2000);
BEGIN
IF Rule_1_is_TRUE THEN
V_Default_Where := ' AND DEPTNO IN (10, 20, 30)';
END IF;

IF Rule_2_is_TRUE THEN
V_Default_Where := V_Default_Where ||
' AND DNAME != ''SALES'' ';
END IF;

IF Rule_3_is_TRUE THEN
V_Default_Where := V_Default_Where ||
' AND LOC IS NOT NULL';
END IF;

Set_Block_Property ('DEPT', DEFAULT_WHERE,
Substr (V_Default_Where, 6));
Go_Block ('DEPT');
Execute_Query;
END;


der Substr (V_Default_Where, 6) eliminiert zum Schluss das führende ' AND '.

Sehr einfacher und wartbarer Code!

PS: In den englischen Kommentaren gibt es eine Erklärung dafür, dass ich nicht den Trick mit "1=1" benutze

27. Juni 2007

Deutscher Wochentag

Das grosse Problem von "to_char (sysdate, 'D')" ist, dass abhängig vom NLS unterschiedliche Resultate herauskommen:

Sonntag ist der erste Wochentag in den USA
Montag ist der erste Wochentag in Deutschland

Grün-Donnerstag 2000 zum Beispiel:

Green_Thursday := to_date ('23.03.2000', 'DD.MM.YYYY');
in den USA: to_char (Green_Thursday, 'D') = 5
in der BRD: to_char (Green_Thursday, 'D') = 4

Das ist sub-optimal, da die Formatmaske abhängig von der NLS ist.

Meine Lösung in diesem Fall ist: Die Funktion German_Weekday

FUNCTION German_Weekday (P_Date IN DATE)
RETURN NUMBER IS
V_Delta NUMBER;
BEGIN
-- Referenz-Tag: Grün Donnerstag 2000 = Tag 4 in Deutschland
V_Delta := TO_NUMBER (TO_CHAR (TO_DATE ('23.03.2000',
'DD.MM.YYYY'),
'D')) - 4;
RETURN (TO_NUMBER (TO_CHAR (P_Date-V_Delta, 'D')));
END;

und American_Weekday

FUNCTION American_Weekday (P_Date IN DATE)
RETURN NUMBER IS
V_Delta NUMBER;
BEGIN
-- Referenz-Tag: Grün Donnerstag 2000 = Tag 5 in USA
V_Delta := TO_NUMBER (TO_CHAR (TO_DATE ('23.03.2000',
'DD.MM.YYYY'),
'D')) - 5;
RETURN (TO_NUMBER (TO_CHAR (P_Date-V_Delta, 'D')));
END;

diese Funktion gibt nun immer den korrekten deutschen Wochentag zurück, unabhängig von der NLS.

viel Spass damit
Gerd

23. Juni 2007

Forms 11g Erscheinungsdatum

Keiner weiss, wann Forms 11g herauskommen wird.

Das neueste offizielle Statement zu diesem Thema ist:

"Version 11 von Forms wird in der Version des Application Server Version 11 enthalten sein, das im Fiscal Jahr 2008 erscheinen wird."

Nachzulesen im OTN-Diskussions-Forum

22. Juni 2007

Oracle Develop 2007 in München

Anfang der Woche besuchte ich die Oracle Develop im Arabella Sheraton München. Sehr schönes Hotel, jedoch mussten wir zwischen den Vorträgen immer zwischen unterschiedlichen Gebäuden hin und herlaufen. Und die Kaffeemaschine war natürlich in dem Gebäude in dem nur 1 der 5 parallelen Slots stattfand

Tag 1:

Montag früh startete ich mit Frank Nimphius' Vortrag über Forms, die Zukunft von Forms und die Integration in Java, SOA, ...

Marc Sewtz und sein "SQL Developer Features" war nett, da wir eine Fülle neuer Feature des aktuellen Releases zu sehen bekamen

Nach dem Mittagessen gab es einen phantastischen Bryn Llewellyn mit "PL/SQL Performance: Debunking the Myths". Einfach klasse! Das nächste Mal kriegt er hoffentlich zwei Stunden und nicht nur eine.

Parallel zu den Vorträgen gab es 3 Labs von Oracle. Montag nachmittag schaute ich mir die neue APEX 3.0 Version an.


Tag 2:


Schlechte Nachrichten an der Kaffee-Front. Wir mussten weiterhin ein paar 100m im Freien mit unserem Kaffee von Hotel A nach Hotel B laufen, wenn wir dort was trinken wollten.

Dienstags schaute ich mir das nächste Lab an: "Developing and Deploying Oracle and PHP". Sehr interessant zu sehen, wie einfach PHP in Oracle integriert werden kann.

Danach präsentierte Bryn "PL/SQL-Enhancements of the new Oracle DB 11g". Ich liebe die Compound Triggers! Zusammen mit all den anderen neuen Techniken. Sequencen, die nun direkt angesprochen werden können und nicht mehr über DUAL !!

Nach dem Mittagessen präsentierte Frank Nimphius "Building Rich UI using JavaServer Faces and AJAX". Dies war ein weiteres Highlight Konferenz.

Interessant, wieviel Microsoft-Themen in den Vorträgen adressiert wurden. "Microsoft Interoperability with Oracle Fusion Middleware" zeigte die SOA-Integration, die mit dem Office-Toolstack ermöglicht wird. Schön zu sehen, wie andere Firmen das machen.


Zusammenfassung:


Vorträge und Labs: TOP
Kein Kaffee: FLOP

10. Mai 2007

Neues Forms Look & Feel

Gute Nachrichten für alle Forms-Entwickler, die ihre Masken einmal in einem neuen Look and Feel sehen möchten.

In Grant's neuestem Interview sprach er mit Francois Degrelle, über sein Forms Look & Feel White Paper von April 2007.

17. April 2007

Schnellerer Forms-Start mit Synchronize

I konnte es gar nicht glauben, aber es klappt in manchen Fällen!

Wenn man in einer Forms-Anwendung den Eindruck hat, dass der Forms-Start zu langsam vonstatten geht, dann sollte man einen Synchronize einbauen um den Bildschirm beim Starten zu refreshen. Der Anwender denkt nun, dass die Maske schneller startet, intern wurde jedoch nur das Refreshen des Bildschirms vorgezogen.

WHEN-NEW-FORM-INSTANCE - trigger

BEGIN
synchronize;
-- your WHEN-NEW-FORM-INSTANCE-code
END;

probier es aus!

5. April 2007

EOUC 2007 wurde abgesagt

hmmmmmm...

die EMEA-Oracle-User-Council-Konferenz in Amsterdam wurde gerade abgesagt:

EOUC 2007

Update Dez. 2007: mittlerweile wurde sogar diese URL gecancelt

4. April 2007

Multi-Select from DUAL

Ein einfacher Weg um Datensätze aus dem Nichts zu erzeugen ist ein einfacher SELECT mit einem CONNECT BY gegen DUAL.

Wenn man zum Beispiel in einer Forms-LOV die letzten 12 Monate anzeigen möchte, dann benötigt man eine Record-Group, die genau 12 Datensätze zurückgibt. Danach kombiniert man das noch mit dem Sysdate:

SELECT Level LVL
FROM Dual
CONNECT BY Level <= 12;

jetzt bringt man Sysdate ins Statement:

SELECT add_months (trunc (sysdate, 'MM'), -1*Level) Month
FROM Dual
CONNECT BY Level <= 12;

MONTH
--------
01.03.07
01.02.07
01.01.07
01.12.06
01.11.06
01.10.06
01.09.06
01.08.06
01.07.06
01.06.06
01.05.06

ist das nicht eine genial einfache Lösung für die Selektion der letzten 12 Monate?

14. März 2007

Assertions

Assertions im Sourcecode sind Alltag in Java und anderen Programmiersprachen. Warum also nicht auch bei uns in PL/SQL?

Das ist eine gute Frage und ich löste sie für meine Fragestellungen mit dieser Technik:
DECLARE
e_Assertion EXCEPTION;
BEGIN
IF condition1 = 'value'
OR boolean = TRUE
OR something_else THEN
RAISE e_Assertion;
END IF;

-- weiterer Code:
...
EXCEPTION
WHEN e_Assertion THEN
NULL;
WHEN OTHERS THEN
-- when-others-exception-handling
END;

In diesem Beispiel schreibt man alle negativen Assertions untereinander und startet entsprechende Exceptions, die im Exception-Handling nichts tun.

z.B.
PROCEDURE Double_Manager_Salary (P_EMPNO IN NUMBER, P_JOB IN VARCHAR2) IS
e_Assertion EXCEPTION;
BEGIN
IF P_Job != 'MGR' THEN
RAISE e_Assertion;
END IF;

UPDATE EMP SET
SAL = SAL * 2
WHERE EMPNO = P_EMPNO;

EXCEPTION
WHEN e_Assertion THEN
NULL;
END;

was wir hier sehen ist sehr einfach: Wenn man annimmt, dass nur Manager das doppelte Gehalt bekommen, dann kann man die Prozedur direkt nach dem Start abbrechen. Man springt in die e_Assertion-Exception und tut nichts.

Try it
Gerd

12. Februar 2007

Einladung zur EOUC 2007

Die EMEA Oracle User Council Konferenz ist in diesem Jahr in Amsterdam vom 2.-3. Mai.

Mein Vortrag, den ich vor einigen Monaten eingesendet hatte lautet:

Oracle Forms 10g und die Integration in BPEL

Heute bekam ich die Einladung für den 3. Mai

20. Januar 2007

One Time Timer

Sehr oft muss ich während der Validierung eine Navigation starten. Restricted Built-Ins kann man jedoch nicht benutzen. Wir brauchen einen Workaround:

Dies ist der "One Time Timer":

Beispiel: Ich habe einen Control-Block mit ein paar Feldern. Unter diesem Block ist ein Multi-Record Block namens EMP. Der Control-Block dient als Filter.

Der Anwender gibt Filterkriterien ein und automatisch wird nach jedem ausgefüllten Feld eine Query im darunterliegenden EMP Block gestartet. Das ist jedoch unmöglich, da Navigation im Validierungstrigger nicht erlaubt ist und in diesem Fall ein Go_Block und Execute_Query benötigt würden.

Lösung: Erstelle einen Form-Ebene WHEN-TIMER-EXPIRED-Trigger:

DECLARE
V_Item VARCHAR2 (61);
BEGIN
V_Item := :SYSTEM.CURSOR_ITEM;

IF One_Time_Timer.Get_Value = Const.ott_Query_in_EMP THEN
Go_Block ('EMP');
Execute_Query;
Go_Item (V_Item);
ELSIF One_Time_Timer.Get_Value = Const.ott_Something_Else
THEN
-- if more One-Time-Timer are needed,
-- create one for each Branch
NULL;
END IF;
END;

Erstelle eine Konstanten-Package mit einigen Konstanten:

PACKAGE Const IS

-- Globals
gbl_One_Time_Timer CONSTANT VARCHAR2 (61) :=
upper ('global.One_Time_Timer');

-- One-Time-Timer
ott_Query_in_EMP CONSTANT VARCHAR2 (30) :=
'Filter EMP-Block';
ott_Something_Else CONSTANT VARCHAR2 (30) :=
'Something else';

END;

und ein Package mit einigen Funktionen:

PACKAGE One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2;
PROCEDURE Initialize (P_Event IN VARCHAR2);
END;

PACKAGE BODY One_Time_Timer IS
FUNCTION Get_Value RETURN VARCHAR2 IS
BEGIN
Default_Value (NULL, Const.gbl_One_Time_Timer);
RETURN (NAME_IN (Const.gbl_One_Time_Timer));
END;

PROCEDURE Initialize (P_Event IN VARCHAR2) IS
tm_id timer;
tm_name VARCHAR2 (30) := 'ONE_TIME_TIMER';
BEGIN
tm_id := Find_Timer (tm_name);
IF ID_Null (tm_id) THEN
tm_id := Create_Timer (tm_name, 10, NO_REPEAT);
COPY (p_Event, Const.gbl_One_Time_Timer);
END IF;
END;
END One_Time_Timer;

Der Control-Block (namens "Filter") hat z.B. zwei Items: ENAME und SAL

Erstelle einen WHEN-VALIDATE-ITEM auf ENAME:

BEGIN
One_Time_Timer.Initialize (Const.ott_Query_in_EMP);
END;

letzter Schritt: der EMP-Block benötigt einen PRE-QUERY-Trigger auf Block-Ebene:

BEGIN
IF :Filter.ENAME IS NOT NULL THEN
:EMP.ENAME := :Filter.ENAME;
END IF;
END;


was passiert nun?
Nach dem Ändern eines Wertes in ENAME im FILTER-Block startet der WHEN-VALIDATE-ITEM auf diesem Feld. Er initialisiert den One-Time-Timer. Die globale Variable "global.One_Time_Timer" bekommt den Wert Const.ott_Query_in_EMP zugewiesen (= "Filter EMP-Block"). Danach startet der Timer und feuert nach 10 ms.

10ms später:
Der WHEN-TIMER-EXPIRED startet den execute_query im EMP-block und springt danach zurück auf das Feld, in dem der Cursor stand. Im EMP-block startet der PRE-QUERY und die Daten des EMP-Blocks werden gefiltert durch ":EMP.ENAME := :Filter.ENAME"

das war's schon. Viel Spass damit!