oracleconnection.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. unit OracleConnection;
  2. {$mode objfpc}{$H+}
  3. {$Define LinkDynamically}
  4. interface
  5. uses
  6. Classes, SysUtils, sqldb,db,dbconst,
  7. {$IfDef LinkDynamically}
  8. ocidyn,
  9. {$ELSE}
  10. oci,
  11. {$ENDIF}
  12. oratypes;
  13. type
  14. TOracleTrans = Class(TSQLHandle)
  15. protected
  16. end;
  17. TOraFieldBuf = record
  18. Buffer : pointer;
  19. Ind : sb2;
  20. end;
  21. TOracleCursor = Class(TSQLCursor)
  22. protected
  23. FOciStmt : POCIStmt;
  24. FieldBuffers : array of TOraFieldBuf;
  25. ParamBuffers : array of TOraFieldBuf;
  26. end;
  27. { TOracleConnection }
  28. TOracleConnection = class (TSQLConnection)
  29. private
  30. FOciEnvironment : POciEnv;
  31. FOciError : POCIError;
  32. FOciSvcCtx : POCISvcCtx;
  33. FUserMem : pointer;
  34. procedure HandleError;
  35. procedure SetParameters(cursor : TSQLCursor;AParams : TParams);
  36. protected
  37. // - Connect/disconnect
  38. procedure DoInternalConnect; override;
  39. procedure DoInternalDisconnect; override;
  40. // - Handle (de)allocation
  41. function AllocateCursorHandle:TSQLCursor; override;
  42. procedure DeAllocateCursorHandle(var cursor:TSQLCursor); override;
  43. function AllocateTransactionHandle:TSQLHandle; override;
  44. // - Statement handling
  45. procedure PrepareStatement(cursor:TSQLCursor; ATransaction:TSQLTransaction; buf:string; AParams:TParams); override;
  46. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  47. // - Transaction handling
  48. function GetTransactionHandle(trans:TSQLHandle):pointer; override;
  49. function StartDBTransaction(trans:TSQLHandle; AParams:string):boolean; override;
  50. function Commit(trans:TSQLHandle):boolean; override;
  51. function Rollback(trans:TSQLHandle):boolean; override;
  52. procedure CommitRetaining(trans:TSQLHandle); override;
  53. procedure RollbackRetaining(trans:TSQLHandle); override;
  54. // - Statement execution
  55. procedure Execute(cursor:TSQLCursor; ATransaction:TSQLTransaction; AParams:TParams); override;
  56. // - Result retrieving
  57. procedure AddFieldDefs(cursor:TSQLCursor; FieldDefs:TFieldDefs); override;
  58. function Fetch(cursor:TSQLCursor):boolean; override;
  59. function LoadField(cursor:TSQLCursor; FieldDef:TFieldDef; buffer:pointer):boolean; override;
  60. function CreateBlobStream(Field:TField; Mode:TBlobStreamMode):TStream; override;
  61. procedure FreeFldBuffers(cursor:TSQLCursor); override;
  62. public
  63. constructor Create(AOwner : TComponent); override;
  64. end;
  65. implementation
  66. uses math;
  67. ResourceString
  68. SErrEnvCreateFailed = 'The creation of an Oracle environment failed.';
  69. SErrHandleAllocFailed = 'The allocation of the error handle failed.';
  70. SErrOracle = 'Oracle returned error %s:';
  71. procedure TOracleConnection.HandleError;
  72. var errcode : sb4;
  73. buf : array[0..1023] of char;
  74. begin
  75. OCIErrorGet(FOciError,1,nil,errcode,@buf[1],1023,OCI_HTYPE_ERROR);
  76. DatabaseErrorFmt(SErrOracle+LineEnding+buf,[inttostr(errcode)],self);
  77. end;
  78. procedure TOracleConnection.DoInternalConnect;
  79. begin
  80. {$IfDef LinkDynamically}
  81. InitialiseOCI;
  82. {$EndIf}
  83. inherited DoInternalConnect;
  84. FUserMem := nil;
  85. if OCIEnvCreate(FOciEnvironment,oci_default,nil,nil,nil,nil,0,FUserMem) <> OCI_SUCCESS then
  86. DatabaseError(SErrEnvCreateFailed,self);
  87. if OciHandleAlloc(FOciEnvironment,FOciError,OCI_HTYPE_ERROR,0,FUserMem) <> OCI_SUCCESS then
  88. DatabaseError(SErrHandleAllocFailed,self);
  89. if OCILogon2(FOciEnvironment,FOciError,FOciSvcCtx,@username[1],length(username),@password[1],length(password),@databasename[1],length(databasename),OCI_DEFAULT) = OCI_ERROR then
  90. HandleError;
  91. end;
  92. procedure TOracleConnection.DoInternalDisconnect;
  93. begin
  94. inherited DoInternalDisconnect;
  95. if OCILogoff(FOciSvcCtx,FOciError)<> OCI_SUCCESS then
  96. HandleError;
  97. OCIHandleFree(FOciSvcCtx,OCI_HTYPE_SVCCTX);
  98. OCIHandleFree(FOciError,OCI_HTYPE_ERROR);
  99. OCIHandleFree(FOciEnvironment,OCI_HTYPE_ENV);
  100. {$IfDef LinkDynamically}
  101. ReleaseOCI;
  102. {$EndIf}
  103. end;
  104. function TOracleConnection.AllocateCursorHandle: TSQLCursor;
  105. var Cursor : TOracleCursor;
  106. begin
  107. Cursor:=TOracleCursor.Create;
  108. OciHandleAlloc(FOciEnvironment,Cursor.FOciStmt,OCI_HTYPE_STMT,0,FUserMem);
  109. Result := cursor;
  110. end;
  111. procedure TOracleConnection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  112. var tel : word;
  113. begin
  114. with cursor as TOracleCursor do
  115. begin
  116. OCIHandleFree(FOciStmt,OCI_HTYPE_ERROR);
  117. if Length(FieldBuffers) > 0 then
  118. for tel := 0 to high(FieldBuffers) do freemem(FieldBuffers[tel].buffer);
  119. end;
  120. FreeAndNil(cursor);
  121. end;
  122. function TOracleConnection.AllocateTransactionHandle: TSQLHandle;
  123. begin
  124. Result:=nil;
  125. end;
  126. procedure TOracleConnection.PrepareStatement(cursor: TSQLCursor;
  127. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  128. var tel : integer;
  129. FOcibind : POCIDefine;
  130. OFieldType : ub2;
  131. OFieldSize : sb4;
  132. begin
  133. with cursor as TOracleCursor do
  134. begin
  135. if OCIStmtPrepare(FOciStmt,FOciError,@buf[1],length(buf),OCI_NTV_SYNTAX,OCI_DEFAULT) = OCI_ERROR then
  136. HandleError;
  137. if assigned(AParams) then
  138. begin
  139. setlength(ParamBuffers,AParams.Count);
  140. for tel := 0 to AParams.Count-1 do
  141. begin
  142. case AParams[tel].DataType of
  143. ftInteger : begin OFieldType := SQLT_INT; OFieldSize := sizeof(integer); end;
  144. ftFloat : begin OFieldType := SQLT_FLT; OFieldSize := sizeof(double); end;
  145. ftDate, ftDateTime : begin OFieldType := SQLT_DAT; OFieldSize := 7; end;
  146. ftString : begin OFieldType := SQLT_STR; OFieldSize := 4000; end;
  147. end;
  148. parambuffers[tel].buffer := getmem(OFieldSize);
  149. FOciBind := nil;
  150. if OCIBindByName(FOciStmt,FOcibind,FOciError,pchar(AParams[tel].Name),length(AParams[tel].Name),ParamBuffers[tel].buffer,OFieldSize,OFieldType,@ParamBuffers[tel].ind,nil,nil,0,nil,OCI_DEFAULT )= OCI_ERROR then
  151. HandleError;
  152. end;
  153. end;
  154. end;
  155. end;
  156. procedure TOracleConnection.SetParameters(cursor : TSQLCursor;AParams : TParams);
  157. var SQLVarNr : integer;
  158. i : integer;
  159. f : double;
  160. year,month,day : word;
  161. db : array[0..4] of byte;
  162. pb : pbyte;
  163. s : string;
  164. begin
  165. with cursor as TOracleCursor do for SQLVarNr := 0 to High(ParamBuffers) do with AParams[SQLVarNr] do
  166. begin
  167. if IsNull then parambuffers[SQLVarNr].ind := -1 else
  168. parambuffers[SQLVarNr].ind := 0;
  169. case DataType of
  170. ftInteger : begin
  171. i := asInteger;
  172. move(i,parambuffers[SQLVarNr].buffer^,sizeof(integer));
  173. end;
  174. ftFloat : begin
  175. f := asFloat;
  176. move(f,parambuffers[SQLVarNr].buffer^,sizeof(double));
  177. end;
  178. ftString : begin
  179. s := asString+#0;
  180. move(s[1],parambuffers[SQLVarNr].buffer^,length(s)+1);
  181. end;
  182. ftDate, ftDateTime: begin
  183. DecodeDate(asDateTime,year,month,day);
  184. pb := parambuffers[SQLVarNr].buffer;
  185. pb[0] := (year div 100)+100;
  186. pb[1] := (year mod 100)+100;
  187. pb[2] := month;
  188. pb[3] := day;
  189. pb[4] := 1;
  190. pb[5] := 1;
  191. pb[6] := 1;
  192. end;
  193. end;
  194. end;
  195. end;
  196. procedure TOracleConnection.UnPrepareStatement(cursor: TSQLCursor);
  197. begin
  198. //
  199. end;
  200. function TOracleConnection.GetTransactionHandle(trans: TSQLHandle): pointer;
  201. begin
  202. // Transactions not implemented yet
  203. end;
  204. function TOracleConnection.StartDBTransaction(trans: TSQLHandle; AParams: string): boolean;
  205. begin
  206. // Transactions not implemented yet
  207. end;
  208. function TOracleConnection.Commit(trans: TSQLHandle): boolean;
  209. begin
  210. // Transactions not implemented yet
  211. end;
  212. function TOracleConnection.Rollback(trans: TSQLHandle): boolean;
  213. begin
  214. // Transactions not implemented yet
  215. end;
  216. procedure TOracleConnection.CommitRetaining(trans: TSQLHandle);
  217. begin
  218. // Transactions not implemented yet
  219. end;
  220. procedure TOracleConnection.RollbackRetaining(trans: TSQLHandle);
  221. begin
  222. // Transactions not implemented yet
  223. end;
  224. procedure TOracleConnection.Execute(cursor: TSQLCursor; ATransaction: TSQLTransaction; AParams: TParams);
  225. begin
  226. if Assigned(APArams) and (AParams.count > 0) then SetParameters(cursor, AParams);
  227. if cursor.FStatementType = stSelect then
  228. begin
  229. if OCIStmtExecute(FOciSvcCtx,(cursor as TOracleCursor).FOciStmt,FOciError,0,0,nil,nil,OCI_DEFAULT) = OCI_ERROR then
  230. HandleError;
  231. end
  232. else
  233. begin
  234. if OCIStmtExecute(FOciSvcCtx,(cursor as TOracleCursor).FOciStmt,FOciError,1,0,nil,nil,OCI_DEFAULT) = OCI_ERROR then
  235. HandleError;
  236. end;
  237. end;
  238. procedure TOracleConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs: TFieldDefs);
  239. var Param : POCIParam;
  240. tel : ub4;
  241. FieldType : TFieldType;
  242. FieldName : string;
  243. FieldSize : word;
  244. OFieldType : ub2;
  245. OFieldName : Pchar;
  246. OFieldSize : sb4;
  247. OFNameLength : ub4;
  248. NumCols : ub4;
  249. FOciDefine : POCIDefine;
  250. OPrecision : sb2;
  251. OScale : sb1;
  252. begin
  253. Param := nil;
  254. with cursor as TOracleCursor do
  255. begin
  256. if OCIAttrGet(FOciStmt,OCI_HTYPE_STMT,@numcols,nil,OCI_ATTR_PARAM_COUNT,FOciError) = OCI_ERROR then
  257. HandleError;
  258. // Let op, moet gewist worden. En in een keer gealloceerd
  259. Setlength(FieldBuffers,numcols);
  260. for tel := 1 to numcols do
  261. begin
  262. if OCIParamGet(FOciStmt,OCI_HTYPE_STMT,FOciError,Param,tel) = OCI_ERROR then
  263. HandleError;
  264. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldType,nil,OCI_ATTR_DATA_TYPE,FOciError) = OCI_ERROR then
  265. HandleError;
  266. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldSize,nil,OCI_ATTR_DATA_SIZE,FOciError) = OCI_ERROR then
  267. HandleError;
  268. FieldSize := 0;
  269. case OFieldType of
  270. OCI_TYPECODE_NUMBER : begin
  271. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@Oprecision,nil,OCI_ATTR_PRECISION,FOciError) = OCI_ERROR then
  272. HandleError;
  273. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@Oscale,nil,OCI_ATTR_SCALE,FOciError) = OCI_ERROR then
  274. HandleError;
  275. if Oscale = 0 then
  276. begin
  277. FieldType := ftInteger;
  278. OFieldType := SQLT_INT;
  279. OFieldSize:= sizeof(integer);
  280. end
  281. else if (oscale = -127) {and (OPrecision=0)} then
  282. begin
  283. FieldType := ftFloat;
  284. OFieldType := SQLT_FLT;
  285. OFieldSize:=sizeof(double);
  286. end
  287. else if (oscale <=4) and (OPrecision<=12) then
  288. begin
  289. FieldType := ftBCD;
  290. FieldSize := sizeof(Currency);
  291. OFieldType := SQLT_VNU;
  292. OFieldSize:= 22;
  293. end
  294. else FieldType := ftUnknown;
  295. end;
  296. OCI_TYPECODE_CHAR,
  297. OCI_TYPECODE_VARCHAR,
  298. OCI_TYPECODE_VARCHAR2 : begin FieldType := ftString; FieldSize := OFieldSize; inc(OFieldsize) ;OFieldType:=SQLT_STR end;
  299. OCI_TYPECODE_DATE : FieldType := ftDate;
  300. OCI_TYPECODE_TIMESTAMP,
  301. OCI_TYPECODE_TIMESTAMP_LTZ,
  302. OCI_TYPECODE_TIMESTAMP_TZ : begin
  303. FieldType := ftDateTime;
  304. OFieldType := SQLT_ODT;
  305. end;
  306. else
  307. FieldType := ftUnknown;
  308. end;
  309. FieldBuffers[tel-1].buffer := getmem(OFieldSize);
  310. FOciDefine := nil;
  311. if OciDefineByPos(FOciStmt,FOciDefine,FOciError,tel,fieldbuffers[tel-1].buffer,OFieldSize,OFieldType,@(fieldbuffers[tel-1].ind),nil,nil,OCI_DEFAULT) = OCI_ERROR then
  312. HandleError;
  313. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldName,@OFNameLength,OCI_ATTR_NAME,FOciError) <> OCI_SUCCESS then
  314. HandleError;
  315. setlength(Fieldname,OFNameLength);
  316. move(OFieldName^,Fieldname[1],OFNameLength);
  317. TFieldDef.Create(FieldDefs, FieldName, FieldType, FieldSize, False, tel);
  318. end;
  319. end;
  320. end;
  321. function TOracleConnection.Fetch(cursor: TSQLCursor): boolean;
  322. begin
  323. case OCIStmtFetch2((cursor as TOracleCursor).FOciStmt,FOciError,1,OCI_FETCH_NEXT,1,OCI_DEFAULT) of
  324. OCI_ERROR : begin
  325. Result := False;
  326. HandleError;
  327. end;
  328. OCI_NO_DATA : Result := False;
  329. OCI_SUCCESS : Result := True;
  330. OCI_SUCCESS_WITH_INFO : Begin
  331. Result := True;
  332. HandleError;
  333. end;
  334. end; {case}
  335. end;
  336. function TOracleConnection.LoadField(cursor: TSQLCursor; FieldDef: TFieldDef; buffer: pointer): boolean;
  337. var dt : TDateTime;
  338. b : pbyte;
  339. size,i : byte;
  340. exp : shortint;
  341. cur : Currency;
  342. odt : POCIdateTime;
  343. begin
  344. with cursor as TOracleCursor do if fieldbuffers[FieldDef.FieldNo-1].ind = -1 then
  345. Result := False
  346. else
  347. begin
  348. result := True;
  349. case FieldDef.DataType of
  350. ftString : move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,FieldDef.Size);
  351. ftBCD : begin
  352. b := fieldbuffers[FieldDef.FieldNo-1].buffer;
  353. size := b[0];
  354. cur := 0;
  355. if (b[1] and $80)=$80 then // then the number is positive
  356. begin
  357. exp := (b[1] and $7f)-65;
  358. for i := 2 to size do
  359. cur := cur + (b[i]-1) * intpower(100,-(i-2)+exp);
  360. end
  361. else
  362. begin
  363. exp := (not(b[1]) and $7f)-65;
  364. for i := 2 to size-1 do
  365. cur := cur + (101-b[i]) * intpower(100,-(i-2)+exp);
  366. cur := -cur;
  367. end;
  368. move(cur,buffer^,FieldDef.Size);
  369. end;
  370. ftFloat : move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,sizeof(double));
  371. ftInteger : move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,sizeof(integer));
  372. ftDate : begin
  373. b := fieldbuffers[FieldDef.FieldNo-1].buffer;
  374. dt := EncodeDate((b[0]-100)*100+(b[1]-100),b[2],b[3]);
  375. move(dt,buffer^,sizeof(dt));
  376. end;
  377. ftDateTime : begin
  378. odt := fieldbuffers[FieldDef.FieldNo-1].buffer;
  379. dt := ComposeDateTime(EncodeDate(odt^.year,odt^.month,odt^.day), EncodeTime(odt^.hour,odt^.min,odt^.sec,0));
  380. move(dt,buffer^,sizeof(dt));
  381. end;
  382. else
  383. Result := False;
  384. end;
  385. end;
  386. end;
  387. function TOracleConnection.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  388. begin
  389. // Result:=inherited CreateBlobStream(Field, Mode);
  390. end;
  391. procedure TOracleConnection.FreeFldBuffers(cursor: TSQLCursor);
  392. begin
  393. // inherited FreeFldBuffers(cursor);
  394. end;
  395. constructor TOracleConnection.Create(AOwner: TComponent);
  396. begin
  397. inherited Create(AOwner);
  398. FUserMem := nil;
  399. end;
  400. end.