oracleconnection.pp 16 KB

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