123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- unit pqconnection;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, sqldb, db,postgres3;
-
- type
- TPQTrans = Class(TSQLHandle)
- protected
- TransactionHandle : PPGConn;
- end;
- TPQCursor = Class(TSQLHandle)
- protected
- Statement : string;
- tr : Pointer;
- nFields : integer;
- res : PPGresult;
- BaseRes : PPGresult;
- end;
- TPQConnection = class (TSQLConnection)
- private
- FConnectString : string;
- FSQLDatabaseHandle : pointer;
- function TranslateFldType(Type_Oid : integer) : TFieldType;
- protected
- procedure DoInternalConnect; override;
- procedure DoInternalDisconnect; override;
- function GetHandle : pointer; override;
- Function AllocateCursorHandle : TSQLHandle; override;
- Function AllocateTransactionHandle : TSQLHandle; override;
- procedure FreeStatement(cursor : TSQLHandle); override;
- procedure PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string); override;
- procedure FreeFldBuffers(cursor : TSQLHandle); override;
- procedure Execute(cursor: TSQLHandle;atransaction:tSQLtransaction); override;
- procedure AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs); override;
- function GetFieldSizes(cursor : TSQLHandle) : integer; override;
- function Fetch(cursor : TSQLHandle) : boolean; override;
- procedure LoadFieldsFromBuffer(cursor : TSQLHandle;buffer: pchar); override;
- function GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean; override;
- function GetTransactionHandle(trans : TSQLHandle): pointer; override;
- function RollBack(trans : TSQLHandle) : boolean; override;
- function Commit(trans : TSQLHandle) : boolean; override;
- procedure CommitRetaining(trans : TSQLHandle); override;
- function StartdbTransaction(trans : TSQLHandle) : boolean; override;
- procedure RollBackRetaining(trans : TSQLHandle); override;
- published
- property DatabaseName;
- property KeepConnection;
- property LoginPrompt;
- property Params;
- property OnLogin;
- end;
- implementation
- ResourceString
- SErrRollbackFailed = 'Rollback transaction failed';
- SErrCommitFailed = 'Commit transaction failed';
- SErrConnectionFailed = 'Connection to database failed';
- SErrTransactionFailed = 'Start of transacion failed';
- SErrClearSelection = 'Clear of selection failed';
- SErrExecuteFailed = 'Execution of query failed';
- SErrFieldDefsFailed = 'Can not extract field information from query';
- SErrFetchFailed = 'Fetch of data failed';
- SErrNoDatabaseName = 'Database connect string (DatabaseName) not filled in!';
-
- const Oid_Text = 25;
- Oid_Int8 = 20;
- Oid_int2 = 21;
- Oid_Int4 = 23;
- Oid_Float4 = 700;
- Oid_Float8 = 701;
- Oid_bpchar = 1042;
- Oid_varchar = 1043;
- function TPQConnection.GetTransactionHandle(trans : TSQLHandle): pointer;
- begin
- Result := (trans as TPQtrans).TransactionHandle;
- end;
- function TPQConnection.RollBack(trans : TSQLHandle) : boolean;
- var
- res : PPGresult;
- tr : TPQTrans;
- begin
- result := false;
- tr := trans as TPQTrans;
- res := PQexec(tr.TransactionHandle, 'ROLLBACK');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- result := false;
- DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
- end
- else
- begin
- PQclear(res);
- PQFinish(tr.TransactionHandle);
- result := true;
- end;
- end;
- function TPQConnection.Commit(trans : TSQLHandle) : boolean;
- var
- res : PPGresult;
- tr : TPQTrans;
- begin
- result := false;
- tr := trans as TPQTrans;
- res := PQexec(tr.TransactionHandle, 'COMMIT');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- result := false;
- DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
- end
- else
- begin
- PQclear(res);
- PQFinish(tr.TransactionHandle);
- result := true;
- end;
- end;
- function TPQConnection.StartdbTransaction(trans : TSQLHandle) : boolean;
- var
- res : PPGresult;
- tr : TPQTrans;
- msg : string;
- begin
- result := false;
- tr := trans as TPQTrans;
- tr.TransactionHandle := PQconnectdb(pchar(FConnectString));
- if (PQstatus(tr.TransactionHandle) = CONNECTION_BAD) then
- begin
- result := false;
- PQFinish(tr.TransactionHandle);
- DatabaseError(SErrConnectionFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
- end
- else
- begin
- res := PQexec(tr.TransactionHandle, 'BEGIN');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- result := false;
- PQclear(res);
- msg := PQerrorMessage(tr.transactionhandle);
- PQFinish(tr.TransactionHandle);
- DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
- end
- else
- begin
- PQclear(res);
- result := true;
- end;
- end;
- end;
- procedure TPQConnection.RollBackRetaining(trans : TSQLHandle);
- var
- res : PPGresult;
- tr : TPQTrans;
- msg : string;
- begin
- tr := trans as TPQTrans;
- res := PQexec(tr.TransactionHandle, 'ROLLBACK');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- DatabaseError(SErrRollbackFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
- end
- else
- begin
- PQclear(res);
- res := PQexec(tr.TransactionHandle, 'BEGIN');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- msg := PQerrorMessage(tr.transactionhandle);
- PQFinish(tr.TransactionHandle);
- DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
- end
- else
- PQclear(res);
- end;
- end;
- procedure TPQConnection.CommitRetaining(trans : TSQLHandle);
- var
- res : PPGresult;
- tr : TPQTrans;
- msg : string;
- begin
- tr := trans as TPQTrans;
- res := PQexec(tr.TransactionHandle, 'COMMIT');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- DatabaseError(SErrCommitFailed + ' (PostgreSQL: ' + PQerrorMessage(tr.transactionhandle) + ')',self);
- end
- else
- begin
- PQclear(res);
- res := PQexec(tr.TransactionHandle, 'BEGIN');
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- PQclear(res);
- msg := PQerrorMessage(tr.transactionhandle);
- PQFinish(tr.TransactionHandle);
- DatabaseError(sErrTransactionFailed + ' (PostgreSQL: ' + msg + ')',self);
- end
- else
- PQclear(res);
- end;
- end;
- procedure TPQConnection.DoInternalConnect;
- var msg : string;
- begin
- inherited dointernalconnect;
- if (DatabaseName = '') then
- DatabaseError(SErrNoDatabaseName,self);
- FConnectString := '';
- if (UserName <> '') then FConnectString := FConnectString + ' user=''' + UserName + '''';
- if (Password <> '') then FConnectString := FConnectString + ' password=''' + Password + '''';
- if (DatabaseName <> '') then FConnectString := FConnectString + ' dbname=''' + DatabaseName + '''';
- FSQLDatabaseHandle := PQconnectdb(pchar(FConnectString));
- if (PQstatus(FSQLDatabaseHandle) = CONNECTION_BAD) then
- begin
- msg := PQerrorMessage(FSQLDatabaseHandle);
- dointernaldisconnect;
- DatabaseError(sErrConnectionFailed + ' (PostgreSQL: ' + msg + ')',self);
- end;
- end;
- procedure TPQConnection.DoInternalDisconnect;
- begin
- PQfinish(FSQLDatabaseHandle);
- end;
- function TPQConnection.TranslateFldType(Type_Oid : integer) : TFieldType;
- begin
- case Type_Oid of
- Oid_varchar,Oid_bpchar : Result := ftstring;
- Oid_text : REsult := ftmemo;
- Oid_int8 : Result := ftLargeInt;
- Oid_int4 : Result := ftInteger;
- Oid_int2 : Result := ftSmallInt;
- Oid_Float4 : Result := ftFloat;
- Oid_Float8 : Result := ftFloat;
- end;
- end;
- Function TPQConnection.AllocateCursorHandle : TSQLHandle;
- begin
- result := TPQCursor.create;
- end;
- Function TPQConnection.AllocateTransactionHandle : TSQLHandle;
- begin
- result := TPQTrans.create;
- end;
- procedure TPQConnection.PrepareStatement(cursor: TSQLHandle;ATransaction : TSQLTransaction;buf : string);
- begin
- with (cursor as TPQCursor) do
- begin
- (cursor as TPQCursor).statement := buf;
- if StatementType = stselect then
- statement := 'DECLARE selectst' + name + ' BINARY CURSOR FOR ' + statement;
- end;
- end;
- procedure TPQConnection.FreeStatement(cursor : TSQLHandle);
- begin
- with cursor as TPQCursor do
- begin
- if StatementType = stselect then
- begin
- Res := pqexec(tr,pchar('CLOSE selectst' + name));
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- pqclear(res);
- DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self);
- end
- end;
- pqclear(baseres);
- pqclear(res);
- end;
- end;
- procedure TPQConnection.FreeFldBuffers(cursor : TSQLHandle);
- begin
- // Do nothing
- end;
- procedure TPQConnection.Execute(cursor: TSQLHandle;atransaction:tSQLtransaction);
- var st : string;
- begin
- with cursor as TPQCursor do
- begin
- tr := aTransaction.Handle;
- // res := pqexecParams(tr,pchar(statement),0,nil,nil,nil,nil,1);
- st := statement;
- res := pqexec(tr,pchar(st));
- if (PQresultStatus(res) <> PGRES_COMMAND_OK) then
- begin
- pqclear(res);
- DatabaseError(SErrExecuteFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
- end;
- end;
- end;
- procedure TPQConnection.AddFieldDefs(cursor: TSQLHandle; FieldDefs : TfieldDefs);
- var
- i : integer;
- size : integer;
- st : string;
- fieldtype : tfieldtype;
- begin
- with cursor as TPQCursor do
- begin
- // BaseRes := pqexecParams(tr,'FETCH 0 IN selectst' + pchar(name) ,0,nil,nil,nil,nil,1);
- st := 'FETCH 0 IN selectst' + pchar(name);
- BaseRes := pqexec(tr,pchar(st));
- if (PQresultStatus(BaseRes) <> PGRES_TUPLES_OK) then
- begin
- pqclear(BaseRes);
- DatabaseError(SErrFieldDefsFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
- end;
- nFields := PQnfields(BaseRes);
- for i := 0 to nFields-1 do
- begin
- size := PQfsize(BaseRes, i);
- fieldtype := TranslateFldType(PQftype(BaseRes, i));
- if fieldtype = ftstring then
- size := pqfmod(baseres,i)-4;
- TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
- end;
- end;
- end;
- function TPQConnection.GetFieldSizes(cursor : TSQLHandle) : integer;
- var
- x,recsize : integer;
- size : integer;
- begin
- recsize := 0;
- {$R-}
- with cursor as TPQCursor do
- for x := 0 to PQnfields(baseres)-1 do
- begin
- size := PQfsize(baseres, x);
- if TranslateFldType(PQftype(BaseRes, x)) = ftString then
- size := pqfmod(baseres,x);
-
- if size = -1 then size := sizeof(pchar);
- Inc(recsize, size);
- end;
- {$R+}
- result := recsize;
- end;
- function TPQConnection.GetHandle: pointer;
- begin
- Result := FSQLDatabaseHandle;
- end;
- function TPQConnection.Fetch(cursor : TSQLHandle) : boolean;
- var st : string;
- begin
- with cursor as TPQCursor do
- begin
- st := 'FETCH NEXT IN selectst' + pchar(name);
- Res := pqexec(tr,pchar(st));
- if (PQresultStatus(res) <> PGRES_TUPLES_OK) then
- begin
- pqclear(Res);
- DatabaseError(SErrfetchFailed + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
- end;
- Result := (PQntuples(res)<>0);
- end;
- end;
- procedure TPQConnection.LoadFieldsFromBuffer(cursor : TSQLHandle;buffer : pchar);
- var
- x,i : integer;
- begin
- {$R-}
- with cursor as TPQCursor do for x := 0 to PQnfields(res)-1 do
- begin
- i := PQfsize(res, x);
- buffer[0] := chr(pqgetisnull(res,0,x));
- inc(buffer);
- if i = -1 then
- begin
- i := pqgetlength(res,0,x);
- move(i,buffer^,sizeof(integer));
- inc(buffer,sizeof(integer));
-
- Move(pqgetvalue(res,0,x)^,Buffer^, i);
- inc(buffer,i);
- end
- else
- begin
- Move(pqgetvalue(res,0,x)^, Buffer^, i);
- Inc(Buffer, i);
- end;
- end;
- {$R+}
- end;
- function TPQConnection.GetFieldData(Cursor : TSQLHandle;Field: TField; FieldDefs : TfieldDefs; Buffer: Pointer;currbuff : pchar): Boolean;
- var
- x : longint;
- size : integer;
- tel : byte;
- begin
- Result := False;
- with cursor as TPQCursor do
- begin
- for x := 0 to Field.Fieldno-1 do
- begin
- size := PQfsize(BaseRes, x);
- inc(currbuff);
- if size = -1 then
- begin
- size := integer(CurrBuff^);
- inc(CurrBuff,sizeof(integer));
- end;
- if x < Field.Fieldno-1 then
- Inc(CurrBuff, size);
- end;
- dec(currbuff);
- if currbuff[0]<>#1 then
- begin
- inc(currbuff);
- case Field.DataType of
- ftInteger, ftSmallint, ftLargeInt,ftfloat :
- begin
- for tel := 1 to size do // postgres returns big-endian integers
- pchar(Buffer)[tel-1] := CurrBuff[size-tel];
- end;
- ftString :
- begin
- Move(CurrBuff^, Buffer^, size);
- PChar(Buffer + Size)^ := #0;
- end;
- end;
- Result := True;
- end
- end;
- end;
- end.
|