Deutsch English
Blog
Home
Über tdbengine
Newsletter
Abonnement
November 2003
August 2002
Dezember 2001
Mai 2001
Dezember 2000
August 2000
Juni 2000
Dezember 1999
Download
Helpware
Chat
Dokumentation
Einführungskurs
Befehlsreferenz
HOWTO - Wie kann ich...?
Projekte
Links
Benchmarks
Bug Reporting
Supportanfrage
 
Home    Überblick    Suche    Impressum    Kontakt    Mitglieder
tdbengine - news vom 20.08.2002

Datenbank-Einsatzgebiete und SQL
Im Usenet (bei de.comp.datenbanken.misc) gab es kürzlich eine recht eigen- , mitunter sogar bösartige Diskussion zum Thema tdbengine: Ein Anwender fragte nach einer Internet-fähigen Datenbank und ein anderer wies dabei auf die tdbengine hin. Der Tippgeber wurde daraufhin NG-typisch niedergebügelt, weil sein Hinweis ein wenig euphorisch ausfiel.

Ich will hier keine Stellung zu den einzelnen Pro- und Kontrapositionen beziehen. Nur zweierlei hat mich gestört: Die mangelnde Differenzierung des Einsatzbereiches der angesprochenen Datenbanken und die ausschließliche Propagierung des alleinseligmachenden SQL.

Zugegeben, die großen Datenbanken (Oracle & Co) bieten tolle Transaktionsmechanismen und hervorragende Parallelisierungen, die weit über die Möglichkeiten der tdbengine hinausgehen. Aber werden solche Features immer und überall benötigt? Ist es nicht eher so, dass mehr als 90 % aller Datenbankapplikionen davon niemals Gebrauch machen? In weit vernetzten Strukturen mit stark verteilten Systemen, wo der Aufbau der einzelnen Verbindungen zwischen den Knoten sehr lange dauern bzw. fehlschlagen kann, sind umfangreiche Konsistenzschutzstrategien unerlässlich (und machen solche Systeme auch angemessen teuer). In unserem Fall aber liegen alle Daten üblicherweise auf einem einzelnen Server (bzw. dessen Filesystem), und der meist ebenfalls auf dem Rechner installierte Web-Server sorgt (via CGI) für die Kommunikation. Ein solches System ist leicht zu administrieren (Backup!), performant und erschlägt mit Sicherheit die allermeisten Anforderungen.

SQL ist eine genormte Abfrage- und Datenmanipulationssprache für relationale Datenbanken. Eine, und nicht die einzige. Warum gibt es so viele SQL-Dialekte, wie es relationale Datenbanken gibt? Warum bauen die meisten Datenbank-Anbieter neben SQL noch eine eigene Abragesprache ein? Ich will hier nicht auf grundsätzliche Schwächen von SQL eingehen (wird die relevante Teilmenge des Relationen-Kalküls respektive der zugrunde liegenden Mengenlehre adäquat abgebildet?), sondern nur festellen, dass SQL nicht die einzige Möglichkeit ist, mit relationalen Datenbanken zu kommunizieren. Auf die Frage, warum nach nunmehr 17 Jahren TDB-Entwicklung immer noch keine SQL-Schnittstelle geschaffen wurde, gibt es eine verblüffende Antwort: Der Leidensdruck war einfach noch nicht groß genug, alle diesbezüglichen Probleme wurden mit Bordmitteln gelöst. Aber jetzt hat sich die TDB GmbH entschlossen, eine SQL- und, darauf aufsetzend, eine JDBC-Schnittstelle zu schaffen. Und sicher wird es auch hier einige SQL-Erweiterungen geben, denn manche der tdbengine-Features (wie etwa der feldbezogene Volltextindex) lassen sich ANSI-SQL nicht vernünftig abbilden.

Schulungen

Die TDB-GmbH in Schwabach bietet jetzt auch Schulungen zu verschiedenen Themenbereichen rund um tdbengine und VDP an.

Für weitere Informationen wenden Sie sich bitte an Hermann Götz ( hg@tdb.de ).

Themen: tdbengine Grundkurs
Beschreibung: Client/Server Anwendungsentwicklung mit tdbengine; typische Probleme und deren Lösungswege
Vorkenntnisse: Grundkenntnisse der Programmierung mit einer beliebigen Sprache

Visual Data Publisher Grundkurs
Beschreibung: Datenbank/Anwendungsentwicklung mit VDP
Vorkenntnisse: Umgang mit Windows

eforia CMS Anwenderkurs
Beschreibung: Einrichtung und Pflege einer WebSite mit eforia CMS
Vorkenntnisse: HTML-Grundkenntnisse sind von Vorteil

eforia CMS Modulentwicklung
Beschreibung: Wie werden eigene Module für eforia CMS erstellt; Z.B. der Aufbau einer Produktdatenbank mit nahtloser Integration;
Vorkenntnisse: tdbengine Programmierung; eforia CMS Anwendung

eforia Anwendungsentwicklung
Beschreibung: Webanwendungen mit dem eforia Framework realisieren; Beschreibung der Module und deren Zusammenspiel;
Vorkenntnisse: tdbengine Programmierung

Version 6.2.8
Ein paar Erweiterungen und Verbesserungen sind wieder eingeflossen, so dass wir nun bei Version 6.2.8 angelangt sind:

Untypisierte Dateien

Mehrere Anwender haben sich den Zugriff auf untypisierte Dateien (Streams) gewünscht. Mit Erfolg, denn jetzt stehen folgende Funktionen zur Verfügung:

f_create(filename : string) : integer
f_open(filename : string) : integer
f_close(handle : integer) : integer
f_seek(handle, pos : integer) : integer
f_size(handle : integer) : integer
f_read(handle : integer; var array [; nbytes : integer) : integer
f_write(handle : integer; var array [; nbytes : integer) : integer
f_error : integer

Alle Funktionen betreffen externe Dateien.

f_create erzeugt eine neue Datei, f_open öffnet eine bestehende Datei. Beide
Funktionen liefern im Erfolgsfall ein Handle (Integer>0), über den alle
weiteren Operationen aufgerufen werden. Wird 0 geliefert, so konnte die Datei
nicht geöffnet werden, und in f_error steht der Fehlercoe des Betriebssystems.

f_close schließt eine Datei. Alle geöffneten Dateien werden am Ende des
Programms automatisch geschlossen.

f_seek positioniert byteweise in der Datei. Die erste Position ist 0. Die
Funktion liefert den Fehlercode des Betriebssystems, also 0, wenn in Ordnung.

f_read und f_write erlauben das Lesen und Schreiben in die Datei. Der zweite
Parameter muss ein Array zur Datenübertragung sein. Wird kein dritter
Parameter angegeben, so wird das gesamte Feld gelesen bzw. geschrieben. Das
Funktionergebnis ist die Anzahl der wirklich übertragenen Bytes.

Beispiel zum Schreiben und Lesen der in einem TBits-Array gespeicherten
Markierungen:

PROCEDURE SaveMarks(VAR marks : TBITS[])
VAR hdl : INTEGER
  IF hdl:=f_create('marks/selection')>0 THEN
    f_write(hdl,marks); f_close(hdl)
  END
ENDPROC

PROCEDURE RestoreMarks(VAR marks : TBITS[])
VAR hdl : INTEGER
  IF hdl:=f_open('marks/selection')>0 THEN
    f_read(hdl,marks); f_close(hdl)
  END
ENDPROC


Direkt ausführbare Scripten

In der Linux- und FreeBSD-Version gibt es nun die Möglichkeit, direkt ausführbare Scripten zu erzeugen. Dazu muss in der Haupt-Konfigurationsdatei (tdbengine.ini im Programmverzeichnis) unter globals folgender Eintrag stehen:

[globals]
direct_scripting=1

Im Anschluss daran ergänzt die tdbengine jedes von Ihr erzeugte Programm (prg-Datei) um die Zeile

#!/Pfad/zur/tdbengine

Als Pfad zur tdbengine verwendet die tdbengine
  • den Pfad zu sich selbst, wenn nichts anderes angegeben ist
  • den Eintrag unter globals.interpreter in der tdbengine.ini, wenn im Script kein anderer Pfad angegeben ist, oder
  • den Pfad, der in der ersten Zeile der mod-Datei in der Form #!Pfad/zur/tdbengine steht
Ausserdem macht die tdbengine die erzeugten prg-Dateien ausführbar.

Das hat folgenden Vorteil: Die tdbengine kann nun auch auf solchen Servern eingesetzt werden, die keine Installation im HTTP-Server zulassen. Mit anderen Worten: Auf jedem Linux- oder FreeBSD-Server, der eigene CGI's zulässt, kann die tdbengine ohne weitere Installation durch den Administrator verwendet werden.

Dazu gehen Sie so vor:

Kopieren Sie die tdbengine in Ihr CGI-Verzeichnis (bzw. ein Unterverzeichnis) , beispielsweise /cgi-bin/tdbengine.
Testen Sie, ob die tdbengine ausgeführt wird, indem Sie folgende URL aufrufen: http://www.ihre_domain.de/cgi-bin/tdbengine/tdbengine.
Kopieren Sie die mitgelieferten Tools (pdk, dmk etc.) in das selbe Verzeichnis wie die tdbengine.
Starten Sie das Programm-Entwicklungstool mit http://www.ihre_domain.de/cgi-bin/tdbengine/pdk.prg.

Hinweis: Die mitgelieferten Tools sind mit dem Interpreter-Pfad ./tdbengine kompiliert.

Die mit der Einstellung "direct_scripting=1" kompilierten Pogramme können nicht auf tdbengine-Versionen vor 6.2.8 ausgeführt werden.

Sortierung ein- und zweidimensionaler String-Arrays

Eine Funktion wurde beträchtlich erweitert: StrSort

Bisher sortierte StrSort nur eindimensionale String-Felder aufsteigend im Indexbereich 0 bis max, wobei max der zweite Parameter nach dem Feldnamen ist.

Jetzt sortiert StrSort auch zweidimensionale String-Felder:

StrSort(StringArray,MaxIndex,StartIndex,SortOrder)

Das Feld wird zeilenweise vom StartIndex bis zum EndIndex sortiert.
Wird keine SortOrder angegeben, so wird das Feld aufsteigend nach der ersten Spalte (j=0) sortiert.
SortOrder ist ein String mit folgendem Aufbau:

SpaltenDesc{,SpaltenDesc}
SpaltenDesc=[-][%]Spaltenindex

Wird bei SpaltenDesc ein '-'-Zeichen angegeben, so wird nach dieser Spalte absteigen sortiert, sonst aufsteigend.
Wird bei SpaltenDesc ein '%'-Zeichen angegeben, so wird der Spalteninhalt als Zahl interpretiert und die Spalte numerisch sortiert.

Beispiel:

VAR Ergebnis : STRING[10000,10]
...
StrSort(Ergebnis,10000,1,'5,-%8')

Damit wird das Feld "Ergebnis" nach der Spalte mit dem Index 5 (also eigentlich der sechsten Spalte) aufsteigen sortiert. Zeilen mit gleichem Inhalt in dieser Spalte werden wiederum absteigen nach der Spalte 8 sortiert, wobei diese Spalte numerisch interpretiert wird.

Die Sortierung erfolgt sehr schnell und kann hervorragend eingesetzt werden, wenn Selektionen über mehrere Tabellen erfolgen, wobei eine Ergebnismenge erzeugt wird.

Dazu noch ein kleines Beispiel, das eine Auswertung von zwei relational verknüpften Tabellen liefert:

VAR db_1 : INTEGER = opendb('database/kunden.dat')
VAR db_2 : INTEGER = opendb('database/bestellung.dat')
VAR query : STRING = '$kunden.KDNR=$bestellung.KDNR, $bestellung.Datum<=today-14'
VAR result : STRING[,]
VAR i,j : INTEGER

InitArray(result[maxlabel(db_1)+maxlabel(db_2),filesize(db_1)*filesize(db_2)]

SUB _query
  nloop(i,maxlabel(db_1)-1,result[j,i]:=getfield(db_1,i+1))
  nloop(i,maxlabel(db_2)-1,result[j,maxlabel(db_1)+i]:=getfield(db_2,i+1))
  j++
ENDSUB

StrSort(result,j-1,0,'1,2')


Neue Funktionen zum ausprobieren (nur Linux-Version)

Zwei ganz neue Funktionen bietet die Linux-Version: TimeOut und Server. Beide bieten in Verbindung mit den bisher bereits eingebauten Socket-Funktionen neue Funktionalitäten.

Timeout(sec : INTEGER) : INTEGER

Diese Funktion ruft die Linux-Funktion Alarm mit dem übergeben Argument auf. Der Linux-Kernel sendet dann nach Ablauf der angebenen Zeit (in Sekunden) ein Signal (SIGALRM) an den aufrufenden Prozess. Die tdbengine verbeitet dieses Signal, indem sie diejenige Prozedur, die zum Zeitpunkt des Signals gerade abgearbeitet wird, mit RETURN 0 (bzw. RETURN '' bei einer STRING-Funktion) verlässt.
Wird vorher, (also bevor das Signal gesendet wird) die Funktion TimeOut mit dem Parameter 0 aufgerufen, so wird kein Signal gesendet.

Mit diesem Mechanismus kann man sich recht einfach TimeOuts basteln. Hierzu ein kleines Beispiel:

PROCEDURE Eingabe : INTEGER
VAR id : STRING
  Timeout(10)
  writeln(0,'Bitte Ihre User-Kennung: '
  id:=readln(0)
  TimeOut(0)
  RETURN 1
ENDPROC

PROCEDURE Main
  IF Eingabe
  THEN writeln(0,'Ihre Eingabe wird bearbeitet...')
  ELSE writeln(0,'Abbruch wegen Zeitüberschreitung')
  END
ENDPROC


Wenn man das kleine Programm an der Konsole startet, so kann man innerhalb von 10 Sekunden seine ID angeben, und das Programm macht weiter, als wenn nicht gewesen wäre. Andernfalls erhalten Sie Meldung 'Abbruch wegen Zeitüberschreitung'.

Für Bastler vielleicht noch interessanter ist die Funktion Server:

Server(port : INTEGER; proc : Prozedur; maxconnections : INTEGER) : INTEGER
Damit installieren Sie einen Multi-Prozess-Server, der auf dem angegebenen Port wartet und Verbindungen vom Typ Internet-Socket annimmt. Bei erfolgreicher Verbindung wird ein Kind-Prozess gestartet , der die Verbindung übernimmt. Der Vaterprozess steht somit sofort wieder bereit, weitere Verbindungen anzunehmen. Zusätzlich wird das Pseudo-Texthandle 128 zur Verfügung gestellt, über das Sie die Kommunikation mit dem Klienten abwickeln können (in dieses Handle können Sie schreiben und lesen). Schließlich wird die Prozedur proc ausgeführt. Wird diese beendet, so wird auch die Verbindung zum Klienten geschlossen und der Kind-Prozess beendet. Dieser bleibt allerdings zunächst als Zombie im Speicher. Allerdings räumt die tdbengine bei jedem neuen Verbindungsaufbau eventuel vorhandene Zombies auf.

Normalerweise terminiert die Funktion Server nicht. Sie läuft in einer Endlosschleife (das machen Dämonen nun mal so). Falls Sie doch terminiert, liefert das Funktionsergebnis den Fehlercode des Betriebssystems (wenn beispielsweise der Prot schon von einem anderen Dämonen überwacht wird.

Hier ein kleines Beispiel:

PROCEDURE try
VAR s : STRING
  writeln(128,'Hello '+getenv('REMOTE_ADDR')+', here is the server...')
  REPEAT
    s:=readln(128)
    writeln(128,s)
  UNTIL s='quit'
ENDPROC

PROCEDURE Main
  cgiclosebuffer
  server(3444,try,1)
ENDPROC



Wie Sie vielleicht daraus ersehen, setzt die tdbengine beim Aufbau einer Verbindung die Environment-Variable 'REMOTE_ADDR', in der die IP-Nummer des Klienten abgelegt wird.

Starten Sie dieses Programm auf den Konsole. Mit Telnet können Sie den "Server" testen:

telnet 192.168.1.2 3444
Trying 192.168.1.2...
Connected to linux_engine
Escape character is '^]'.
Hello 192.168.1.3, here is the server...
Hello
Hello
quit
quit
Connection closed by foreign host.

Dieser kleine Demoserver schickt alle Zeichenketten zurück an den Klienten, bis dieser "quit" eingibt.

Wichtig: Jede Verbindung führt zu einem eigenen, vollkommen insolierten Prozess. Jeder Prozess hat seine eigenen Variablen, die Synchronisation muss über geeignete Semaphoren sichergestellt werden.

Erweiterungen von anderen Funktionen

Subst wurde um den Modus 128 erweitert. In diesem Fall wird die Groß-/Kleinschreibung beim Target (der zu ersetzenden Zeichenfolge) ignoriert. Man sollte diesen Modus jedoch nur wählen, wenn er wirklich erforderlich ist, da die zusätzlich benötigte Rechenleistung doch enorm ist.

Den gleichen Modus mit der gleichen Wirkung gibt es jetzt auch für Ramtext_Find.

TAppend funktioniert nun auch für Ramtexte.

Korrekturen

Zum Export einer Tabelle in das DBF-Format steht schon seit langem die bisher undokumentierte Funktion ToDBF zur Verfügung:

ToDBF(dat : INTEGER; fn_dbf : STRING; lastfield, ansi : INTEGER)
dat : Handle von OpenDB
fn_dbf : Name der DBF-Datei
lastfield : es werden die Felder von 1 bis lastfield exportiert (0 = alle Felder)
ansi : 0 = ASCII in DBF, 1 = ANSI in DBF (für FoxPro)

Es wird eine Tabelle im DBase-4-Format erzeugt, mit der kein anderes Programm Probleme haben sollte.

Die Funktion kommt jetzt auch mit den erweiterten Datentypen der tdbengine (NUMBER,8 bzw. NUMBER,4) und langen Memos zurecht.

Neue Funktionen

BitNot(VAR T _ TBits[]) : INTEGER

Mit BitNot besteht jetzt die Möglichkeit, ein Bitarray zu invertieren, also aus jeder 0 eine eine 1 und aus jeder 1 ein 0 zu machen. Das Ergebnis der Funktion ist die Anzahl der gesetzten Bits nach der Operation.

CGITestParam(parameter : STRING) : 0|1

CGITestParam  liefert eine 1, wenn ein CGI-Parameter entsprechenden Namens vom Browser (mit der Methode POST) übertragen wurde, andernfalls 0. Es gilt jedoch zu beachten, dass bei vielen HTML-Input-Konstrukten (beispielsweise Checkboxen oder Radiobuttons) überhaupt nichts übertragen wird, wenn der Anwender solche Elemente nicht auswählt.

ExecProg(FileName : STRING) : INTEGER

Bei dieser Funktion handelt es sich eigentlich um eine alte TDB-Funktion, die jetzt in der tdbengine reaktiviert wurde. Der Parameter FileName  verweist auf eine Textdatei, die EASY-Code enthält. Dieser Code wird zunächst kompiliert und dann sofort ausgeführt. Gerade in Verbindung mit Ramtexten ergeben sich sehr schöne Möglichkeiten: Man setzt sich komplexe Datenbankabfragen in einem Ramtext zusammen und führt diesen dann aus.

Beim Einsatz von ExecProg ist zweierlei zu beachten: Innerhalb des auszuführenden Codes kann man zwar auf alle Variablen des aufrufenden Programmes zugreifen, nicht aber auf die Prozeduren und Funktionen. Und im Code verwendete Variablen werden durch den Compiler verändert, müssen also im Code explizit auf den aktuellen Wert  gesetzt werden.


tdbengine Anwendungen im Web:

Open-Source Web CMS


Open-Source Bug-Tracking


Free wiki hosting

Open-Source Wiki-System

Kostenloses Foren-Hosting

Diät mit tdbengine 8-)

tdbengine chat
irc.tdbengine.org
#tdbengine

   Copyright © 2003-2004 tdb Software Service GmbH
   Alle rechte vorbehalten. / All rights reserved
   Letzte Änderung: 05.05.2004


ranking-charts.de

Programmers Heaven - Where programmers go!