mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 11:38:19 +02:00
438 lines
14 KiB
ObjectPascal
438 lines
14 KiB
ObjectPascal
{
|
|
Copyright (c) 1999-2000 by Pavel Stingl <stingp1.eti@mail.cez.cz>
|
|
|
|
|
|
OCI workaround
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
unit oraclew;
|
|
|
|
interface
|
|
|
|
{$H+}
|
|
{$mode objfpc}
|
|
|
|
uses OCI, oratypes,Classes, SysUtils;
|
|
|
|
{ all pos parameters are indexed from 1..x! }
|
|
|
|
procedure OraInit;
|
|
procedure OraFin;
|
|
procedure OraLogin(name, pass, server: AnsiString);
|
|
procedure OraLogout;
|
|
procedure OraSQLExec(sql: AnsiString);
|
|
function OraGetFieldAsString(pos : integer) : AnsiString;
|
|
function OraGetFieldAsInteger(pos : integer) : longint;
|
|
function OraNext: boolean;
|
|
function OraGetFieldCount: integer;
|
|
function OraGetFieldName(pos : integer) : AnsiString;
|
|
function OraGetFieldType(pos : integer) : longint;
|
|
function IsFieldDate(Pos : integer): boolean;
|
|
procedure OraError(errcode: integer; err: POCIError; msg : AnsiString);
|
|
|
|
const
|
|
cDescribeBuf = 1024;
|
|
cPCharBufLen = 4097;
|
|
cPrefetchCnt = 100;
|
|
|
|
type
|
|
|
|
PDescribeRec = ^TDescribeRec;
|
|
TDescribeRec = record
|
|
dbsize : sb4;
|
|
dbtype : sb2;
|
|
buf : array [0..cDescribeBuf] of AnsiChar;
|
|
buflen : sb4;
|
|
dsize : sb4;
|
|
precision : sb2;
|
|
scale : sb2;
|
|
nullok : sb2;
|
|
|
|
// Define part
|
|
valbuf : array [0..cDescribeBuf] of AnsiChar;
|
|
flt_buf : double;
|
|
int_buf : cardinal;
|
|
int64_buf : int64;
|
|
indp : sb2;
|
|
col_retlen: ub2;
|
|
col_retcode: ub2;
|
|
end;
|
|
|
|
var
|
|
Env : POCIEnv;
|
|
Err : POCIError;
|
|
Svc : POCISvcCtx;
|
|
Stmt: POCIStmt;
|
|
FieldList : TList;
|
|
|
|
ecode : integer;
|
|
|
|
implementation
|
|
|
|
function DecodeDataType(dtype : longint): AnsiString;
|
|
begin
|
|
case dtype of
|
|
SQLT_CHR : DecodeDataType := '(ORANET TYPE) character AnsiString';
|
|
SQLT_NUM : DecodeDataType := '(ORANET TYPE) oracle numeric';
|
|
SQLT_INT : DecodeDataType := '(ORANET TYPE) integer';
|
|
SQLT_FLT : DecodeDataType := '(ORANET TYPE) Floating point number';
|
|
SQLT_STR : DecodeDataType := 'zero terminated AnsiString';
|
|
SQLT_VNU : DecodeDataType := 'NUM with preceding length byte';
|
|
SQLT_PDN : DecodeDataType := '(ORANET TYPE) Packed Decimal Numeric';
|
|
SQLT_LNG : DecodeDataType := 'long';
|
|
SQLT_VCS : DecodeDataType := 'Variable character AnsiString';
|
|
SQLT_NON : DecodeDataType := 'Null/empty PCC Descriptor entry';
|
|
SQLT_RID : DecodeDataType := 'rowid';
|
|
SQLT_DAT : DecodeDataType := 'date in oracle format';
|
|
SQLT_VBI : DecodeDataType := 'binary in VCS format';
|
|
SQLT_BIN : DecodeDataType := 'binary data(DTYBIN)';
|
|
SQLT_LBI : DecodeDataType := 'long binary';
|
|
SQLT_UIN : DecodeDataType := 'unsigned integer';
|
|
SQLT_SLS : DecodeDataType := 'Display sign leading separate';
|
|
SQLT_LVC : DecodeDataType := 'Longer longs (AnsiChar)';
|
|
SQLT_LVB : DecodeDataType := 'Longer long binary';
|
|
SQLT_AFC : DecodeDataType := 'Ansi fixed AnsiChar';
|
|
SQLT_AVC : DecodeDataType := 'Ansi Var AnsiChar';
|
|
SQLT_CUR : DecodeDataType := 'cursor type';
|
|
SQLT_RDD : DecodeDataType := 'rowid descriptor';
|
|
SQLT_LAB : DecodeDataType := 'label type';
|
|
SQLT_OSL : DecodeDataType := 'oslabel type';
|
|
SQLT_NTY : DecodeDataType := 'named object type';
|
|
SQLT_REF : DecodeDataType := 'ref type';
|
|
SQLT_CLOB : DecodeDataType := 'character lob';
|
|
SQLT_BLOB : DecodeDataType := 'binary lob';
|
|
SQLT_BFILEE : DecodeDataType := 'binary file lob';
|
|
SQLT_CFILEE : DecodeDataType := 'character file lob';
|
|
SQLT_RSET : DecodeDataType := 'result set type';
|
|
SQLT_NCO : DecodeDataType := 'named collection type (varray or nested table)';
|
|
SQLT_VST : DecodeDataType := 'OCIString type';
|
|
SQLT_ODT : DecodeDataType := 'OCIDate type';
|
|
else DecodeDataType := 'Unknown';
|
|
end;
|
|
end;
|
|
|
|
procedure FieldListClear;
|
|
var
|
|
x: longint;
|
|
PDesc: PDescribeRec;
|
|
begin
|
|
if FieldList.Count = 0 then Exit;
|
|
for x := 0 to FieldList.Count - 1 do
|
|
begin
|
|
PDesc := FieldList[x];
|
|
Dispose(PDesc);
|
|
end;
|
|
FieldList.Clear;
|
|
end;
|
|
|
|
procedure Describe;
|
|
var
|
|
fldc : longint;
|
|
paramd : POCIParam;
|
|
colname : PAnsiChar;
|
|
colsize : ub4;
|
|
Rec : PDescribeRec;
|
|
begin
|
|
fldc := 1;
|
|
|
|
FieldListClear;
|
|
ecode := OCIParamGet(Stmt, OCI_HTYPE_STMT, Err, paramd, fldc);
|
|
if ecode <> OCI_SUCCESS then
|
|
ORAError(ecode, Err, 'OCIParamGetError');
|
|
while ecode = OCI_SUCCESS do
|
|
begin
|
|
New(Rec);
|
|
FillChar(Rec^.buf, sizeof(Rec^.buf), #0);
|
|
ecode := OCIAttrGet(paramd, OCI_DTYPE_PARAM, @Rec^.dbtype, nil,
|
|
OCI_ATTR_DATA_TYPE, Err);
|
|
if ecode <> 0 then
|
|
begin
|
|
ORAError(ecode, Err, 'Retrieving DTYPE_PARAM:');
|
|
end;
|
|
colsize := 0;
|
|
colname := nil;
|
|
ecode := OCIAttrGet(paramd, OCI_DTYPE_PARAM, @colname, @colsize,
|
|
OCI_ATTR_NAME, Err);
|
|
if ecode <> 0 then
|
|
begin
|
|
ORAError(ecode, Err, 'Retrieving DTYPE_PARAM:');
|
|
end;
|
|
Move(Colname^,Rec^.buf, colsize);
|
|
Rec^.buflen := colsize;
|
|
// WriteLn('Column: ',Rec^.buf:15,' DataType: ',DecodeDataType(Rec^.dbtype));
|
|
inc(fldc);
|
|
|
|
FieldList.Add(Rec);
|
|
ecode := OCIParamGet(Stmt, OCI_HTYPE_STMT, Err, paramd, fldc);
|
|
end;
|
|
end;
|
|
|
|
procedure Define;
|
|
var
|
|
x : longint;
|
|
def: POCIDefine;
|
|
PDesc : PDescribeRec;
|
|
defptr: pointer;
|
|
deflen: sword;
|
|
deftyp: sword;
|
|
begin
|
|
def := nil;
|
|
for x := 0 to FieldList.Count - 1 do
|
|
begin
|
|
PDesc := FieldList[x];
|
|
case PDesc^.dbtype of
|
|
SQLT_NUM: begin
|
|
if PDesc^.scale <> 0 then
|
|
begin
|
|
defptr := @PDesc^.flt_buf;
|
|
deflen := SizeOf(PDesc^.flt_buf);
|
|
deftyp := SQLT_FLT;
|
|
PDesc^.dbtype := SQLT_FLT;
|
|
end
|
|
else begin
|
|
if PDesc^.dbsize > 4 then
|
|
begin
|
|
// WriteLn('BIG FAT WARNING!!!! dbsize int > 4 (',PDesc^.dbsize,')');
|
|
defptr := @PDesc^.int64_buf;
|
|
deflen := SizeOf(PDesc^.int64_buf);
|
|
deftyp := SQLT_INT;
|
|
PDesc^.dbtype := SQLT_INT;
|
|
end
|
|
else begin
|
|
defptr := @PDesc^.int_buf;
|
|
deflen := SizeOf(PDesc^.int_buf);
|
|
deftyp := SQLT_INT;
|
|
PDesc^.dbtype := SQLT_INT;
|
|
end;
|
|
end;
|
|
end;
|
|
else begin
|
|
defptr := @PDesc^.valbuf;
|
|
deflen := cDescribeBuf;
|
|
deftyp := PDesc^.dbtype;
|
|
end;
|
|
end;
|
|
ecode := OCIDefineByPos(Stmt, def, Err, x + 1, defptr,
|
|
deflen, deftyp, @PDesc^.indp, @PDesc^.col_retlen,
|
|
@PDesc^.col_retcode, OCI_DEFAULT);
|
|
if ecode <> 0 then
|
|
begin
|
|
OraError(ecode, Err, 'OCIDefineByPos: ');
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure OraError( errcode : integer; err: POCIError; msg : AnsiString );
|
|
var
|
|
buff : array [0..1024] of AnsiChar;
|
|
|
|
begin
|
|
if err <> nil then
|
|
begin
|
|
case errcode of
|
|
OCI_INVALID_HANDLE: Msg := Msg + ' OCI_INVALID_HANDLE';
|
|
end;
|
|
OCIErrorGet( err, 1, nil, errcode, @buff[0], 1024, OCI_HTYPE_ERROR);
|
|
writeln(stderr, msg, ' ', buff);
|
|
end
|
|
else begin
|
|
WriteLn(stderr, msg);
|
|
Halt(1);
|
|
end;
|
|
end;
|
|
|
|
procedure OraInit;
|
|
begin
|
|
ecode := OCIInitialize({OCI_DEFAULT or }OCI_OBJECT, nil, nil, nil, nil);
|
|
if ecode <> 0 then OraError( ecode, nil, 'Error initializing OCI');
|
|
ecode := OCIEnvInit(Env, OCI_DEFAULT, 0, nil);
|
|
if ecode <> 0 then OraError( ecode, nil, 'Error initializing OCI environment');
|
|
ecode := OCIHandleAlloc(Env, Err, OCI_HTYPE_ERROR, 0, nil);
|
|
if ecode <> 0 then OraError( ecode, nil, 'Error allocating error handle');
|
|
ecode := OCIHandleAlloc(Env, Stmt, OCI_HTYPE_STMT, 0, nil);
|
|
if ecode <> 0 then OraError( ecode, nil, 'Error allocating statement handle');
|
|
end;
|
|
|
|
procedure OraLogin(name, pass, server: AnsiString);
|
|
begin
|
|
ecode := OCILogon(Env, Err, Svc, @name[1], Length(name),
|
|
@pass[1], Length(pass), @server[1], Length(server));
|
|
if ecode <> 0 then OraError(ecode, Err, '');
|
|
end;
|
|
|
|
procedure OraLogout;
|
|
begin
|
|
ecode := OCILogoff(Svc, Err);
|
|
if ecode <> 0 then
|
|
OraError(ecode, Err, 'OCILogoff: ');
|
|
end;
|
|
|
|
procedure OraFin;
|
|
begin
|
|
OCIHandleFree(Stmt, OCI_HTYPE_STMT);
|
|
OCIHandleFree(Err, OCI_HTYPE_ERROR);
|
|
end;
|
|
|
|
procedure OraSQLExec(sql: AnsiString);
|
|
var
|
|
dtype: longint;
|
|
begin
|
|
// writeLn(Length(sql));
|
|
ecode := OCIStmtPrepare(Stmt, Err, @sql[1], Length(sql),
|
|
OCI_NTV_SYNTAX, OCI_DEFAULT);
|
|
if ecode <> 0 then
|
|
begin
|
|
OraError(ecode, Err, 'OCIStmtPrepare:');
|
|
Exit;
|
|
end;
|
|
|
|
dtype := cPrefetchCnt;
|
|
ecode := OCIAttrSet(Stmt, OCI_HTYPE_STMT, @dtype,
|
|
SizeOf(dtype), OCI_ATTR_PREFETCH_ROWS, Err);
|
|
if ecode <> 0 then
|
|
begin
|
|
OraError(ecode, Err, 'ociattrset:');
|
|
Exit;
|
|
end;
|
|
|
|
dtype := 0;
|
|
ecode := OCIAttrGet(Stmt, OCI_HTYPE_STMT, @dtype, nil,
|
|
OCI_ATTR_STMT_TYPE, Err);
|
|
if ecode <> 0 then
|
|
begin
|
|
OraError(ecode, Err, 'ociattrget:');
|
|
Exit;
|
|
end;
|
|
|
|
ecode := 0;
|
|
if dtype = OCI_STMT_SELECT then
|
|
ecode := OCIStmtExecute(Svc, Stmt, Err, 0, 0, nil, nil, OCI_DEFAULT)
|
|
else ecode := OCIStmtExecute(Svc, Stmt, Err, 1, 0, nil, nil, OCI_DEFAULT);
|
|
if ecode <> 0 then
|
|
begin
|
|
OraError(ecode, Err, 'OCIStmtExecute:');
|
|
Exit;
|
|
end;
|
|
|
|
if dtype = OCI_STMT_SELECT then
|
|
begin
|
|
Describe;
|
|
Define;
|
|
end;
|
|
end;
|
|
|
|
function OraGetFieldCount : integer;
|
|
begin
|
|
OraGetFieldCount := FieldList.Count;
|
|
end;
|
|
|
|
function IsFieldDate(Pos : integer): boolean;
|
|
var
|
|
Desc : TDescribeRec;
|
|
begin
|
|
Result := False;
|
|
if (Pos > FieldList.Count) or (Pos < 1) then
|
|
Exit;
|
|
Desc := TDescribeRec(FieldList[Pos-1]^);
|
|
Result := (Desc.dbtype = SQLT_DAT);
|
|
end;
|
|
|
|
function OraGetFieldAsString(pos : integer) : AnsiString;
|
|
var
|
|
Desc : TDescribeRec;
|
|
Date : array [0..6] of byte;
|
|
begin
|
|
if (Pos > FieldList.Count) or (Pos < 1) then
|
|
Exit;
|
|
Desc := TDescribeRec(FieldList[pos-1]^);
|
|
if Desc.indp < 0 then
|
|
begin
|
|
OraGetFieldAsString := 'null';
|
|
Exit;
|
|
end;
|
|
if Desc.dbtype = SQLT_STR then
|
|
begin
|
|
Desc.valbuf[Desc.col_retlen] := #0;
|
|
OraGetFieldAsString := strpas(Desc.valbuf);
|
|
end
|
|
else if Desc.dbtype = SQLT_CHR then
|
|
begin
|
|
Desc.valbuf[Desc.col_retlen] := #0;
|
|
OraGetFieldAsString := strpas(Desc.valbuf);
|
|
end
|
|
else if Desc.dbtype = SQLT_INT then
|
|
begin
|
|
OraGetFieldAsString := IntToStr(Desc.int_buf);
|
|
end
|
|
else if Desc.dbtype = SQLT_FLT then
|
|
OraGetFieldAsString := FloatToStr(Desc.flt_buf)
|
|
else if Desc.dbtype = SQLT_DAT then
|
|
begin
|
|
Move(Desc.valbuf,Date,SizeOf(Date));
|
|
OraGetFieldAsString :=
|
|
Format('%0.2d.%0.2d.%0.4d %0.2d:%0.2d:%0.2d',
|
|
[Date[3],Date[2],(((Date[0]-100)*100)+(Date[1] - 100)),
|
|
Date[4]-1,
|
|
Date[5]-1,
|
|
Date[6]-1]);
|
|
end
|
|
else if Desc.dbtype = SQLT_AFC then
|
|
begin
|
|
Desc.valbuf[Desc.col_retlen] := #0;
|
|
OraGetFieldAsString := strpas(Desc.valbuf);
|
|
end
|
|
else OraGetFieldAsString := 'dbtype not implemented ' + IntToStr(Desc.dbtype);
|
|
end;
|
|
|
|
function OraGetFieldAsInteger(pos : integer) : longint;
|
|
begin
|
|
OraGetFieldAsInteger := 0;
|
|
end;
|
|
|
|
function OraNext: boolean;
|
|
begin
|
|
ecode := OCIStmtFetch(Stmt, Err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
|
|
if ecode = 0 then
|
|
OraNext := true
|
|
else if ecode = OCI_SUCCESS_WITH_INFO then
|
|
OraNext := false
|
|
else if ecode = OCI_NO_DATA then
|
|
OraNext := false
|
|
else begin
|
|
OraNext := false;
|
|
OraError(ecode, err, 'OCIStmtFetch:');
|
|
end;
|
|
end;
|
|
|
|
function OraGetFieldType(pos : integer) : longint;
|
|
begin
|
|
if (Pos > FieldList.Count) or (pos < 1) then
|
|
Exit;
|
|
OraGetFieldType := TDescribeRec(FieldList[pos-1]^).dbtype;
|
|
end;
|
|
|
|
function OraGetFieldName(pos : integer) : AnsiString;
|
|
begin
|
|
if (Pos > FieldList.Count) or (Pos < 1) then
|
|
Exit;
|
|
OraGetFieldName := strpas(TDescribeRec(FieldList[pos-1]^).buf);
|
|
end;
|
|
|
|
initialization
|
|
|
|
FieldList := TList.Create;
|
|
|
|
finalization
|
|
|
|
FieldListClear;
|
|
FieldList.Free;
|
|
|
|
end.
|