MODULE SqlDrivers;
(**
	project	= "BlackBox"
	organization	= "www.oberon.ch"
	contributors	= "Oberon microsystems"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "Docu/BB-License"
	changes	= "
	- 20151125, center #87, adding support for LONGINT in SqlDrivers.Table
	"
	issues	= "
	- ...
	"

**)

	IMPORT Meta, Dates, Dialog;

	CONST
		noDriverMod = 1;
		noDriverProc = 2;
		wrongDriverType = 3;
		connectionsExceeded = 4;
		outOfTables = 5;
		notExecutable = 6;
		cannotOpenDB = 7;
		wrongIdentification = 8;
		tooManyBlobs = 9;
		
		converted = 1;
		truncated = 2;
		overflow = 3;
		incompatible = 4;
		noData = 5;

	TYPE
		Driver* = POINTER TO ABSTRACT RECORD
			blobStr-: ARRAY 8 OF CHAR;
			open: BOOLEAN;
			tables: INTEGER;	(* number of currently open tables for this driver *)
		END;
		
		Table* = POINTER TO ABSTRACT RECORD
			(** an open table must anchor its driver; a closed table must not anchor it **)
			res*: INTEGER;	(** sticky result, replaced by higher values only **)
			open: BOOLEAN;
			driver: Driver	(* only used to prevent too early garbage collection of driver *)
		END;

		String* = POINTER TO ARRAY OF CHAR;
		
		Blob* = POINTER TO RECORD
			len*: INTEGER;
			data*: POINTER TO ARRAY OF BYTE;	(** not 0X terminated **)
			next*: Blob
		END;

		DriverAllocator = PROCEDURE (id, password, datasource: ARRAY OF CHAR;
													async, showErr: BOOLEAN; OUT d: Driver; OUT res: INTEGER);
		(* Post: res = 0 <=> connection has started and should be completed later with EndOpen
					res = 0 <=> d # NIL
					~async & (res = 0) => d.Ready() *)


	PROCEDURE (d: Driver) Ready* (): BOOLEAN, NEW, ABSTRACT;
	PROCEDURE (d: Driver) EndOpen* (OUT res: INTEGER), NEW, ABSTRACT;
	(** Pre: d.Ready()	20 **)

	PROCEDURE (d: Driver) BeginExec* (IN statement: ARRAY OF CHAR; data: Blob;
															async, showErrors: BOOLEAN; OUT res: INTEGER), NEW, ABSTRACT;
	(** asynchronous execution is optional, a driver may legally execute synchronously if async is TRUE **)
	(** Pre: statement # ""	20
				no other execution started	21 **)
	(** Post: res = 0 <=> execution has started and should be completed later with EndExec
				~async => d.Ready() **)

	PROCEDURE (d: Driver) EndExec* (VAR t: Table; OUT rows, columns, res: INTEGER), NEW, ABSTRACT;
	(** Pre: execution must have been started successfully with BeginExec	20
				d.Ready()	21 **)
	(** Post: res = 0  <=>  (t # NIL) OR (rows = 0) & (columns = 0) **)
	
	PROCEDURE (d: Driver) Commit* (accept: BOOLEAN; OUT res: INTEGER), NEW, ABSTRACT;
	
	PROCEDURE (d: Driver) Cleanup-, NEW, EMPTY;
	
	PROCEDURE (d: Driver) Close*, NEW;
	BEGIN
		IF d.open THEN
			IF d.tables = 0 THEN
				d.Cleanup	(* release driver info *)
			END;
			d.open := FALSE;
		END
	END Close;

	PROCEDURE (d: Driver) Init* (blobStr: ARRAY OF CHAR), NEW;
	BEGIN
		d.blobStr := blobStr$;
		d.open := TRUE
	END Init;
	
	PROCEDURE (d: Driver) FINALIZE-;
	BEGIN
		d.Close
	END FINALIZE;

	
	PROCEDURE (t: Table) ReadInteger* (row, column: INTEGER; OUT val: INTEGER), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadLong* (row, column: INTEGER; OUT val: LONGINT), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadReal* (row, column: INTEGER; OUT val: REAL), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadDate* (row, column: INTEGER; OUT val: Dates.Date), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadTime* (row, column: INTEGER; OUT val: Dates.Time), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadCurrency* (row, column: INTEGER; OUT val: Dialog.Currency), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadString* (row, column: INTEGER; OUT str: ARRAY OF CHAR), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadVarString* (row, column: INTEGER; OUT str: String), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadBlob* (row, column: INTEGER; OUT len: INTEGER;
														OUT data: POINTER TO ARRAY OF BYTE), NEW, ABSTRACT;
	(** Pre: row >= 0	20
				row < rows	21
				column >= 0	22
				column < columns	23 **)

	PROCEDURE (t: Table) ReadName* (column: INTEGER; OUT str: String), NEW, ABSTRACT;
	(** Pre: column >= 0	20
				column < columns	21 **)

	PROCEDURE (t: Table) ReadType* (column: INTEGER; OUT str: String), NEW, ABSTRACT;
	(** Pre: column >= 0	20
				column < columns	21 *)

	PROCEDURE (t: Table) Cleanup-, NEW, EMPTY;
	
	PROCEDURE (t: Table) Close*, NEW;
	BEGIN
		IF t.open THEN
			t.Cleanup;	(* release table info *)
			DEC(t.driver.tables);
			IF (t.driver.tables = 0) & ~t.driver.open THEN
				t.driver.Cleanup	(* release driver info *)
			END;
			t.driver := NIL;	(* allow for garbage collection of driver *)
			t.open := FALSE
		END
	END Close;
	
	PROCEDURE (t: Table) Init* (d: Driver), NEW;
	BEGIN
		ASSERT(d.open, 20);
		t.driver := d; INC(d.tables);
		t.open := TRUE
	END Init;
	
	PROCEDURE (t: Table) FINALIZE-;
	BEGIN
		t.Close
	END FINALIZE;
	

	PROCEDURE Open* (protocol, id, password, datasource: ARRAY OF CHAR;
											async, showErrors: BOOLEAN; OUT d: Driver; OUT res: INTEGER);
		VAR ok: BOOLEAN; m, p: Meta.Item; mod: Meta.Name;
			v: RECORD (Meta.Value)
				Open: DriverAllocator
			END;
	BEGIN
		ASSERT(protocol # "", 20);
		mod := protocol$; Meta.Lookup(mod, m); d := NIL; res := 0;
		IF m.obj = Meta.modObj THEN
			m.Lookup("Open", p);
			IF p.obj = Meta.procObj THEN
				p.GetVal(v, ok);
				IF ok THEN
					v.Open(id, password, datasource, async, showErrors, d, res);
				ELSE res := wrongDriverType
				END
			ELSE res := noDriverProc
			END
		ELSE res := noDriverMod
		END
	END Open;

END SqlDrivers.
