fcl-db: oracle:

- bind date and datetime params using Oracle external data type SQLT_TIMESTAMP instead of SQLT_ODT to support fraction seconds
- introduce support for ftBlob and ftMemo params, but only with max.length up to 64K

git-svn-id: trunk@28041 -
This commit is contained in:
lacak 2014-06-24 08:57:37 +00:00
parent 57b233e724
commit 742faaed92

View File

@ -58,8 +58,8 @@ type
FOciUserSession : POCISession;
FUserMem : pointer;
procedure HandleError;
procedure GetParameters(cursor : TSQLCursor;AParams : TParams);
procedure SetParameters(cursor : TSQLCursor;AParams : TParams);
procedure GetParameters(cursor : TSQLCursor; AParams : TParams);
procedure SetParameters(cursor : TSQLCursor; AParams : TParams);
protected
// - Connect/disconnect
procedure DoInternalConnect; override;
@ -117,6 +117,17 @@ ResourceString
SErrHandleAllocFailed = 'The allocation of the error handle failed.';
SErrOracle = 'Oracle returned error %s:';
type
TODateTime = record
year : sb2;
month : ub1;
day : ub1;
hour : ub1;
min : ub1;
sec : ub1;
fsec : ub4;
end;
// Callback functions
function cbf_no_data(ictxp:Pdvoid; bindp:POCIBind; iter:ub4; index:ub4; bufpp:PPdvoid;
@ -324,46 +335,35 @@ begin
Raise E;
end;
procedure TOracleConnection.GetParameters(cursor: TSQLCursor; AParams: TParams
);
var SQLVarNr : integer;
i : integer;
f : double;
year,month,day : word;
pb : pbyte;
s : string;
procedure TOracleConnection.GetParameters(cursor: TSQLCursor; AParams: TParams);
var
i : integer;
odt : TODateTime;
s : string;
begin
with cursor as TOracleCursor do for SQLVarNr := 0 to High(ParamBuffers) do
with AParams[SQLVarNr] do
with cursor as TOracleCursor do for i := 0 to High(ParamBuffers) do
with AParams[i] do
if ParamType=ptOutput then
begin
if parambuffers[SQLVarNr].ind = -1 then
if ParamBuffers[i].ind = -1 then
Value:=null;
case DataType of
ftInteger : begin
move(parambuffers[SQLVarNr].buffer^,i,sizeof(integer));
asInteger := i;
end;
ftFloat : begin
move(parambuffers[SQLVarNr].buffer^,f,sizeof(double));
asFloat := f;
end;
ftInteger : AsInteger := PInteger(ParamBuffers[i].buffer)^;
ftFloat : AsFloat := PDouble(ParamBuffers[i].buffer)^;
ftString : begin
SetLength(s,parambuffers[SQLVarNr].Len);
move(parambuffers[SQLVarNr].buffer^,s[1],length(s)+1);
asString:=s;
SetLength(s,ParamBuffers[i].Len);
move(ParamBuffers[i].buffer^,s[1],length(s)+1);
AsString:=s;
end;
ftDate, ftDateTime: begin
pb := parambuffers[SQLVarNr].buffer;
year:=(pb[0]-100)*100+pb[1]-100;
month:=pb[2];
day:=pb[3];
asDateTime:=EncodeDate(year,month,day);
OCIDateTimeGetDate(FOciUserSession, FOciError, ParamBuffers[i].buffer, @odt.year, @odt.month, @odt.day);
OCIDateTimeGetTime(FOciUserSession, FOciError, ParamBuffers[i].buffer, @odt.hour, @odt.min, @odt.sec, @odt.fsec);
AsDateTime := ComposeDateTime(EncodeDate(odt.year,odt.month,odt.day), EncodeTime(odt.hour,odt.min,odt.sec,odt.fsec div 1000000));
end;
ftFMTBcd : begin
AsFMTBCD:=Nvu2FmtBCE(parambuffers[SQLVarNr].buffer);
AsFMTBCD:=Nvu2FmtBCE(ParamBuffers[i].buffer);
end;
end;
@ -524,11 +524,13 @@ end;
procedure TOracleConnection.PrepareStatement(cursor: TSQLCursor;
ATransaction: TSQLTransaction; buf: string; AParams: TParams);
var counter : integer;
var i : integer;
FOcibind : POCIDefine;
OFieldType : ub2;
OFieldSize : sb4;
ODescType : ub4;
OBuffer : pointer;
stmttype : ub2;
@ -541,14 +543,14 @@ begin
if OCIAttrGet(FOciStmt,OCI_HTYPE_STMT,@stmttype,nil,OCI_ATTR_STMT_TYPE,FOciError) = OCI_ERROR then
HandleError;
case stmttype of
OCI_STMT_SELECT:FStatementType := stSelect;
OCI_STMT_UPDATE:FStatementType := stUpdate;
OCI_STMT_DELETE:FStatementType := stDelete;
OCI_STMT_INSERT:FStatementType := stInsert;
OCI_STMT_SELECT: FStatementType := stSelect;
OCI_STMT_UPDATE: FStatementType := stUpdate;
OCI_STMT_DELETE: FStatementType := stDelete;
OCI_STMT_INSERT: FStatementType := stInsert;
OCI_STMT_CREATE,
OCI_STMT_DROP,
OCI_STMT_DECLARE,
OCI_STMT_ALTER:FStatementType := stDDL;
OCI_STMT_ALTER: FStatementType := stDDL;
else
FStatementType := stUnknown;
end;
@ -557,10 +559,10 @@ begin
if assigned(AParams) then
begin
setlength(ParamBuffers,AParams.Count);
for counter := 0 to AParams.Count-1 do
for i := 0 to AParams.Count-1 do
begin
case AParams[counter].DataType of
ODescType := 0;
case AParams[i].DataType of
ftSmallInt, ftInteger :
begin OFieldType := SQLT_INT; OFieldSize := sizeof(integer); end;
ftLargeInt :
@ -568,31 +570,45 @@ begin
ftFloat :
begin OFieldType := SQLT_FLT; OFieldSize := sizeof(double); end;
ftDate, ftDateTime :
begin OFieldType := SQLT_DAT; OFieldSize := 7; end;
begin OFieldType := SQLT_TIMESTAMP; OFieldSize := sizeof(pointer); ODescType := OCI_DTYPE_TIMESTAMP; end;
ftFixedChar, ftString :
begin OFieldType := SQLT_STR; OFieldSize := 4000; end;
ftFMTBcd, ftBCD :
begin OFieldType := SQLT_VNU; OFieldSize := 22; end;
ftBlob :
begin OFieldType := SQLT_LVB; OFieldSize := 65535; end;
ftMemo :
begin OFieldType := SQLT_LVC; OFieldSize := 65535; end;
else
DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[AParams[counter].DataType]],self);
DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[AParams[i].DataType]],self);
end;
parambuffers[counter].buffer := getmem(OFieldSize);
parambuffers[counter].Len := OFieldSize;
parambuffers[counter].Size := OFieldSize;
ParamBuffers[i].DescType := ODescType;
ParamBuffers[i].Len := OFieldSize;
ParamBuffers[i].Size := OFieldSize;
if ODescType <> 0 then
begin
OBuffer := @ParamBuffers[i].buffer;
OCIDescriptorAlloc(FOciEnvironment, OBuffer, ODescType, 0, nil);
end
else
begin
OBuffer := getmem(OFieldSize);
ParamBuffers[i].buffer := OBuffer;
end;
FOciBind := nil;
if AParams[counter].ParamType=ptInput then
if AParams[i].ParamType=ptInput then
begin
if OCIBindByName(FOciStmt,FOcibind,FOciError,pchar(AParams[counter].Name),length(AParams[counter].Name),ParamBuffers[counter].buffer,OFieldSize,OFieldType,@ParamBuffers[counter].ind,nil,nil,0,nil,OCI_DEFAULT )= OCI_ERROR then
if OCIBindByName(FOciStmt,FOcibind,FOciError,pchar(AParams[i].Name),length(AParams[i].Name),OBuffer,OFieldSize,OFieldType,@ParamBuffers[i].ind,nil,nil,0,nil,OCI_DEFAULT )= OCI_ERROR then
HandleError;
end
else if AParams[counter].ParamType=ptOutput then
else if AParams[i].ParamType=ptOutput then
begin
if OCIBindByName(FOciStmt,FOcibind,FOciError,pchar(AParams[counter].Name),length(AParams[counter].Name),nil,OFieldSize,OFieldType,nil,nil,nil,0,nil,OCI_DATA_AT_EXEC )= OCI_ERROR then
if OCIBindByName(FOciStmt,FOcibind,FOciError,pchar(AParams[i].Name),length(AParams[i].Name),nil,OFieldSize,OFieldType,nil,nil,nil,0,nil,OCI_DATA_AT_EXEC )= OCI_ERROR then
HandleError;
if OCIBindDynamic(FOcibind, FOciError, nil, @cbf_no_data, @parambuffers[counter], @cbf_get_data) <> OCI_SUCCESS then
if OCIBindDynamic(FOcibind, FOciError, nil, @cbf_no_data, @parambuffers[i], @cbf_get_data) <> OCI_SUCCESS then
HandleError;
end;
end;
@ -601,42 +617,36 @@ begin
end;
end;
procedure TOracleConnection.SetParameters(cursor : TSQLCursor;AParams : TParams);
procedure TOracleConnection.SetParameters(cursor : TSQLCursor; AParams : TParams);
var SQLVarNr : integer;
i : integer;
f : double;
year, month, day, hour, minute, second, millisecond : word;
pb : pbyte;
var i : integer;
year, month, day, hour, min, sec, msec : word;
s : string;
blobbuf : string;
bloblen : ub4;
begin
with cursor as TOracleCursor do for SQLVarNr := 0 to High(ParamBuffers) do with AParams[SQLVarNr] do
with cursor as TOracleCursor do for i := 0 to High(ParamBuffers) do with AParams[i] do
if ParamType=ptInput then
begin
if IsNull then parambuffers[SQLVarNr].ind := -1 else
parambuffers[SQLVarNr].ind := 0;
if IsNull then ParamBuffers[i].ind := -1 else ParamBuffers[i].ind := 0;
case DataType of
ftSmallInt,
ftInteger : begin
i := asInteger;
move(i,parambuffers[SQLVarNr].buffer^,sizeof(integer));
end;
ftLargeInt : PInt64(parambuffers[SQLVarNr].buffer)^ := AsLargeInt;
ftFloat : begin
f := asFloat;
move(f,parambuffers[SQLVarNr].buffer^,sizeof(double));
end;
ftInteger : PInteger(ParamBuffers[i].buffer)^ := AsInteger;
ftLargeInt : PInt64(ParamBuffers[i].buffer)^ := AsLargeInt;
ftFloat : PDouble(ParamBuffers[i].buffer)^ := AsFloat;
ftString,
ftFixedChar : begin
s := asString+#0;
move(s[1],parambuffers[SQLVarNr].buffer^,length(s)+1);
move(s[1],parambuffers[i].buffer^,length(s)+1);
end;
ftDate, ftDateTime: begin
DecodeDate(asDateTime,year,month,day);
DecodeTime(asDateTime,hour,minute,second,millisecond);
pb := parambuffers[SQLVarNr].buffer;
DecodeTime(asDateTime,hour,min,sec,msec);
if OCIDateTimeConstruct(FOciUserSession, FOciError, ParamBuffers[i].buffer, year, month, day, hour, min, sec, msec*1000000, nil, 0) = OCI_ERROR then
HandleError;
{ pb := ParamBuffers[i].buffer;
pb[0] := (year div 100)+100;
pb[1] := (year mod 100)+100;
pb[2] := month;
@ -644,9 +654,19 @@ begin
pb[4] := hour+1;
pb[5] := minute+1;
pb[6] := second+1;
}
end;
ftFmtBCD,ftBCD : begin
FmtBCD2Nvu(asFmtBCD,parambuffers[SQLVarNr].buffer);
ftFmtBCD, ftBCD : begin
FmtBCD2Nvu(asFmtBCD,parambuffers[i].buffer);
end;
ftBlob, ftMemo : begin
blobbuf := AsBlob; // todo: use AsBytes
bloblen := length(blobbuf);
if bloblen > 65531 then bloblen := 65531;
PInteger(ParamBuffers[i].Buffer)^ := bloblen;
Move(blobbuf[1], (ParamBuffers[i].Buffer+sizeof(integer))^, bloblen);
//if OciLobWrite(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, ParamBuffers[i].buffer, @bloblen, 1, @blobbuf[1], bloblen, OCI_ONE_PIECE, nil, nil, 0, SQLCS_IMPLICIT) = OCI_ERROR then
// HandleError;
end;
else
DatabaseErrorFmt(SUnsupportedParameter,[DataType],self);
@ -945,17 +965,6 @@ end;
function TOracleConnection.LoadField(cursor: TSQLCursor; FieldDef: TFieldDef; buffer: pointer; out CreateBlob : boolean): boolean;
type
TODateTime = record
year : sb2;
month : ub1;
day : ub1;
hour : ub1;
min : ub1;
sec : ub1;
fsec : ub4;
end;
var
b : pbyte;
size,i : byte;