ODBCBench ist ein portables Beispielprogramm. Es funktioniert ohne Änderungen auf den Oberon-Systemen V4 und V3. Um das Programm auch unter Oberon/F übersetzen zu können sind lediglich Änderungen aufgrund der anderen Schnittstelle des Moduls Files notwendig.
Die wesentlichen Schritte, um Portabilität zu erreichen sind das Ermitteln der von der jeweiligen Datenbank unterstützten Datentypen und das Umsetzen der gewünschten auf die unterstützten Datentypen. Weiters wird in diesem Programm gezeigt, wie man lange (dateibasierte) Daten in eine Tabelle einfügen und wieder auslesen kann.
MODULE ODBCBench;
IMPORT SQL, SQLExt, In, Out, Files, Strings;
CONST
nofRows = 100;
VAR
conn: SQL.Connection;
connStr: ARRAY 255 OF CHAR;
mapType, mapScale: ARRAY 3 OF INTEGER; mapPrec: ARRAY 3 OF LONGINT;
f: Files.File;
PROCEDURE LongIntValueOf (f: SQL.Field): LONGINT;
VAR i, tmp: LONGINT;
BEGIN
WITH f: SQL.IntField DO RETURN f.i
| f: SQL.StringField DO i := 0; tmp := 0;
REPEAT
tmp := tmp * 10 + ORD(f.str[i]) - ORD("0"); INC(i)
UNTIL f.str[i] = 0X;
RETURN tmp
| f: SQL.RealField DO RETURN ENTIER(f.r)
END
END LongIntValueOf;
PROCEDURE Disconnect*;
BEGIN conn := NIL END Disconnect;
PROCEDURE Connect*;
BEGIN
IF conn # NIL THEN Disconnect END ;
conn := SQL.OpenUI(connStr, connStr);
IF ~SQL.Map(conn, SQL.OberonLongInt, 0, 0, mapType[0], mapPrec[0], mapScale[0]) OR
~SQL.Map(conn, SQL.OberonChar, 32, 0, mapType[1], mapPrec[1], mapScale[1]) OR
~SQL.Map(conn, SQL.SQLLVarChar, 50000, 0, mapType[2], mapPrec[2], mapScale[2]) THEN
HALT(44)
END
END Connect;
PROCEDURE DropTables*;
VAR stat: SQL.Statement;
BEGIN
stat := SQL.PrepareStatement(conn, "DROP table Bench1");
SQL.Execute(stat);
stat := SQL.PrepareStatement(conn, "DROP table Bench2");
SQL.Execute(stat);
stat := SQL.PrepareStatement(conn, "DROP table Bench3");
SQL.Execute(stat);
END DropTables;
PROCEDURE CreateTables*;
VAR stat: SQL.Statement; str: ARRAY 256 OF CHAR;
BEGIN
str := "CREATE TABLE Bench1 (i ";
SQLExt.AppendType(conn, str, mapType[0], mapPrec[0], mapScale[0]);
Strings.Append(", firstName ", str);
SQLExt.AppendType(conn, str, mapType[1], mapPrec[1], mapScale[1]);
Strings.Append(", val ", str);
SQLExt.AppendType(conn, str, mapType[0], mapPrec[0], mapScale[0]);
Strings.Append(")", str);
stat := SQL.PrepareStatement(conn, str);
SQL.Execute(stat);
Out.Ln; Out.String(str);
IF stat.ret # SQL.Success THEN Out.String(" not") END ;
Out.String(" created.");
str := "CREATE TABLE Bench2 (i ";
SQLExt.AppendType(conn, str, mapType[0], mapPrec[0], mapScale[0]);
Strings.Append(", lastName ", str);
SQLExt.AppendType(conn, str, mapType[1], mapPrec[1], mapScale[1]);
Strings.Append(")", str);
stat := SQL.PrepareStatement(conn, str);
SQL.Execute(stat);
Out.Ln; Out.String(str);
IF stat.ret # SQL.Success THEN Out.String(" not") END ;
Out.String(" created.");
str := "CREATE TABLE Bench3 (i ";
SQLExt.AppendType(conn, str, mapType[0], mapPrec[0], mapScale[0]);
Strings.Append(", longData ", str);
SQLExt.AppendType(conn, str, mapType[2], mapPrec[2], mapScale[2]);
Strings.Append(")", str);
stat := SQL.PrepareStatement(conn, str);
SQL.Execute(stat);
Out.Ln; Out.String(str);
IF stat.ret # SQL.Success THEN Out.String(" not") END ;
Out.String(" created.")
END CreateTables;
PROCEDURE GenerateName(i: INTEGER; VAR s: ARRAY OF CHAR);
BEGIN
s[2] := CHR((i MOD 10) + ORD("0"));
s[1] := CHR((i DIV 10 MOD 10) + ORD("0"));
s[0] := CHR((i DIV 100 MOD 10) + ORD("0"));
s[3] := 0X
END GenerateName;
PROCEDURE InsertIntoBench1*;
VAR types: ARRAY 3 OF SQL.ParamDesc; stat: SQL.Statement;
i: INTEGER; idx, firstName, value: SQL.Field;
BEGIN
types[0].oberonType := SQL.OberonLongInt; types[0].sqlType := mapType[0];
types[0].precision := mapPrec[0]; types[0].scale := mapScale[0];
types[0].inOut := SQL.InParam; types[0].name := "idx";
types[1].oberonType := SQL.OberonChar; types[1].sqlType := mapType[1];
types[1].precision := mapPrec[1]; types[1].scale := mapScale[1];
types[1].inOut := SQL.InParam; types[1].name := "firstName";
types[2] := types[0]; types[2].name := "value";
stat := SQL.PrepareStatement(conn,
"INSERT INTO Bench1 (i, firstName, val) values (?, ?, ?)");
SQL.BindParameters(stat, types, 3);
SQL.FindField(stat.params, "idx", idx);
SQL.FindField(stat.params, "firstName", firstName);
SQL.FindField(stat.params, "value", value);
FOR i := 0 TO nofRows DO
idx(SQL.IntField).i := i;
GenerateName(i, firstName(SQL.StringField).str);
value(SQL.IntField).i := LONG(i) * i;
SQL.Execute(stat);
IF stat.ret # SQL.Success THEN RETURN END
END
END InsertIntoBench1;
PROCEDURE AppendToBench1*;
VAR types: ARRAY 3 OF SQL.ParamDesc; stat: SQL.Statement;
i: INTEGER; idx, firstName, value: SQL.Field;
BEGIN
stat := SQL.PrepareStatement(conn, "SELECT MAX(i) FROM Bench1");
SQL.Execute(stat);
SQL.FirstField(stat.results, idx);
SQL.Fetch(stat);
i := SHORT(LongIntValueOf(idx)); INC(i);
types[0].oberonType := SQL.OberonLongInt; types[0].sqlType := mapType[0];
types[0].precision := mapPrec[0]; types[0].scale := mapScale[0];
types[0].inOut := SQL.InParam; types[0].name := "idx";
types[1].oberonType := SQL.OberonChar; types[1].sqlType := mapType[1];
types[1].precision := mapPrec[1]; types[1].scale := mapScale[1];
types[1].inOut := SQL.InParam; types[1].name := "firstName";
types[2] := types[0]; types[2].name := "value";
stat := SQL.PrepareStatement(conn,
"INSERT INTO Bench1 (i, firstName, val) values (?, ?, ?)");
SQL.BindParameters(stat, types, 3);
SQL.FindField(stat.params, "idx", idx);
SQL.FindField(stat.params, "firstName", firstName);
SQL.FindField(stat.params, "value", value);
REPEAT
idx(SQL.IntField).i := i;
GenerateName(i, firstName(SQL.StringField).str);
value(SQL.IntField).i := LONG(i) * i;
SQL.Execute(stat);
IF stat.ret # SQL.Success THEN RETURN END ;
INC(i)
UNTIL i MOD nofRows = 0
END AppendToBench1;
PROCEDURE InsertIntoBench2*;
VAR types: ARRAY 2 OF SQL.ParamDesc; stat: SQL.Statement;
i: INTEGER; idx, lastName: SQL.Field;
BEGIN
types[0].oberonType := SQL.OberonLongInt; types[0].sqlType := mapType[0];
types[0].precision := mapPrec[0]; types[0].scale := mapScale[0];
types[0].inOut := SQL.InParam; types[0].name := "idx";
types[1].oberonType := SQL.OberonChar; types[1].sqlType := mapType[1];
types[1].precision := mapPrec[1]; types[1].scale := mapScale[1];
types[1].inOut := SQL.InParam; types[1].name := "lastName";
stat := SQL.PrepareStatement(conn,
"INSERT INTO Bench2 (i, lastName) values (?, ?)");
SQL.BindParameters(stat, types, 2);
SQL.FindField(stat.params, "idx", idx);
SQL.FindField(stat.params, "lastName", lastName);
FOR i := 0 TO nofRows DO
idx(SQL.IntField).i := i;
GenerateName(i, lastName(SQL.StringField).str);
SQL.Execute(stat);
IF stat.ret # SQL.Success THEN RETURN END
END
END InsertIntoBench2;
PROCEDURE PutField* (VAR field: SQL.Field);
VAR r: Files.Rider; ch: CHAR; f: SQL.Field;
BEGIN
IF field.len = SQL.NullData THEN Out.String("NULL")
ELSE
f := field;
WITH f: SQL.IntField DO Out.Int(f.i, 0)
| f: SQL.StringField DO Out.String(f.str)
| f: SQL.RealField DO
CASE field.sqlType OF
SQL.SQLFloat, SQL.SQLDouble: Out.LongReal(f.r, 16)
| SQL.SQLReal: Out.Real(SHORT(f.r), 16)
END
| f: SQL.DateField DO Out.Int(f.year, 0); Out.Char(\-");
Out.Int(f.month, 0); Out.Char(\-"); Out.Int(f.day, 0)
| f: SQL.TimeField DO Out.Int(f.hour, 0); Out.Char(":");
Out.Int(f.minute, 0); Out.Char(":"); Out.Int(f.second, 0)
| f: SQL.TimeStampField DO Out.Int(f.year, 0); Out.Char(\-");
Out.Int(f.month, 0); Out.Char(\-"); Out.Int(f.day, 0);
Out.Char(" "); Out.Int(f.hour, 0); Out.Char(":");
Out.Int(f.minute, 0); Out.Char(":"); Out.Int(f.second, 0);
Out.Char("."); Out.Int(f.fraction, 0)
| f: SQL.BooleanField DO
IF f.b THEN Out.String("TRUE") ELSE Out.String("FALSE") END
| f: SQL.FileField DO
Out.String("Length of data: "); Out.Int(Files.Length(f.f), 0);
IF field.sqlType = SQL.SQLLVarChar THEN
Files.Set(r, f.f, 0); Files.Read(r, ch);
WHILE ~r.eof DO Out.Char(ch); Files.Read(r, ch) END
END
ELSE (* SQL.BinaryField *)
END
END
END PutField;
PROCEDURE ShowResults* (s: SQL.Statement);
VAR field: SQL.Field; rows: LONGINT;
BEGIN
IF s.ret = SQL.Success THEN
IF s.results # NIL THEN
Out.Ln; SQL.FirstField(s.results, field);
WHILE field # NIL DO
Out.String(field.name); Out.Char(9X); SQL.NextField(field)
END ;
REPEAT
SQL.Fetch(s);
IF s.ret = SQL.Success THEN
Out.Ln;
SQL.FirstField(s.results, field);
WHILE field # NIL DO
PutField(field); Out.Char(9X);
SQL.NextField(field)
END
END
UNTIL s.ret # SQL.Success
ELSE
SQL.RowCount(s, rows);
IF rows # -1 THEN
Out.Ln; Out.String("Number of rows affected: "); Out.Int(rows, 0)
END
END
ELSE
SQL.ShowError(s.ret, "ShowResults", SQL.env, conn.dbc, s.stmt)
END ;
END ShowResults;
PROCEDURE Execute*;
VAR stat: SQL.Statement; str: ARRAY 256 OF CHAR;
BEGIN
In.Open; In.String(str);
stat := SQL.PrepareStatement(conn, str);
SQL.Execute(stat);
ShowResults(stat)
END Execute;
PROCEDURE ExecuteIntParam*;
VAR stat: SQL.Statement; str: ARRAY 256 OF CHAR;
types: ARRAY 1 OF SQL.ParamDesc; par1: SQL.Field;
BEGIN
In.Open; In.String(str);
types[0].oberonType := SQL.OberonLongInt; types[0].sqlType := mapType[0];
types[0].precision := mapPrec[0]; types[0].scale := mapScale[0];
types[0].inOut := SQL.InParam; types[0].name := "par1";
stat := SQL.PrepareStatement(conn, str);
SQL.BindParameters(stat, types, 1);
SQL.FindField(stat.params, "par1", par1);
In.LongInt(par1(SQL.IntField).i);
SQL.Execute(stat);
ShowResults(stat)
END ExecuteIntParam;
PROCEDURE ExecuteStringParam*;
VAR stat: SQL.Statement; str: ARRAY 256 OF CHAR;
types: ARRAY 1 OF SQL.ParamDesc; par1: SQL.Field;
BEGIN
In.Open; In.String(str);
types[0].oberonType := SQL.SQLVarChar; types[0].sqlType := mapType[1];
types[0].precision := mapPrec[1]; types[0].scale := mapScale[1];
types[0].inOut := SQL.InParam; types[0].name := "par1";
stat := SQL.PrepareStatement(conn, str);
SQL.BindParameters(stat, types, 1);
SQL.FindField(stat.params, "par1", par1);
In.String(par1(SQL.StringField).str);
SQL.Execute(stat);
ShowResults(stat)
END ExecuteStringParam;
PROCEDURE CreateLongData*;
VAR r: Files.Rider; i: INTEGER;
BEGIN
f := Files.New(""); Files.Set(r, f, 0);
FOR i := 0 TO 19999 DO Files.Write(r, CHR((i MOD 32) + ORD(" "))) END ;
END CreateLongData;
PROCEDURE VerifyLongData(f: Files.File);
VAR r: Files.Rider; ch: CHAR; i: INTEGER;
BEGIN
Files.Set(r, f, 0);
FOR i := 0 TO 19999 DO
Files.Read(r, ch);
IF ch # CHR((i MOD 32) + ORD(" ")) THEN
Out.String("wrong character in file");
RETURN
END
END
END VerifyLongData;
PROCEDURE CopyFile(fIn, fOut: Files.File);
VAR r, w: Files.Rider; buffer: ARRAY 1024 OF CHAR; pos: LONGINT;
BEGIN
Files.Set(r, fIn, 0); Files.Set(w, fOut, 0);
REPEAT
Files.ReadBytes(r, buffer, 1024);
Files.WriteBytes(w, buffer, 1024 - r.res)
UNTIL r.eof;
END CopyFile;
PROCEDURE InsertLongData*;
VAR types: ARRAY 2 OF SQL.ParamDesc; stat: SQL.Statement;
i: INTEGER; idx, longData: SQL.Field;
BEGIN
types[0].oberonType := SQL.OberonLongInt; types[0].sqlType := mapType[0];
types[0].precision := mapPrec[0]; types[0].scale := mapScale[0];
types[0].inOut := SQL.InParam; types[0].name := "idx";
types[1].oberonType := SQL.SQLLVarChar; types[1].sqlType := mapType[2];
types[1].inOut := SQL.InParam; types[1].name := "longData";
types[1].precision := mapPrec[2]; types[1].scale := mapScale[2];
stat := SQL.PrepareStatement(conn,
"INSERT INTO Bench3 (i, longData) values (?, ?)");
SQL.BindParameters(stat, types, 2);
SQL.FindField(stat.params, "idx", idx);
SQL.FindField(stat.params, "longData", longData);
FOR i := 0 TO 10 DO
Out.Ln; Out.String("Inserting row number "); Out.Int(i, 0);
idx(SQL.IntField).i := i;
CopyFile(f, longData(SQL.FileField).f);
SQL.Execute(stat);
IF stat.ret # SQL.Success THEN RETURN END
END
END InsertLongData;
PROCEDURE GetLongData*;
VAR stat: SQL.Statement;
i: INTEGER; idx, longData: SQL.Field;
BEGIN
stat := SQL.PrepareStatement(conn, "SELECT * FROM Bench3");
SQL.FindField(stat.results, "i", idx);
SQL.FindField(stat.results, "longData", longData);
SQL.Execute(stat);
IF stat.ret = SQL.Success THEN
i := 0;
REPEAT
SQL.Fetch(stat);
IF stat.ret = SQL.Success THEN
Out.Ln; Out.String("Fetching row number "); Out.Int(i, 0);
IF idx(SQL.IntField).i # i THEN
Out.String("wrong index: "); Out.Int(idx(SQL.IntField).i, 0)
END ;
VerifyLongData(longData(SQL.FileField).f);
INC(i)
END
UNTIL stat.ret # SQL.Success;
END
END GetLongData;
BEGIN
connStr := ""
END ODBCBench.