oracleconnection.pp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  1. unit oracleconnection;
  2. {
  3. Copyright (c) 2006-2014 by Joost van der Sluis, FPC contributors
  4. Oracle RDBMS connector using the OCI protocol
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. **********************************************************************}
  8. {$mode objfpc}{$H+}
  9. {$Define LinkDynamically}
  10. interface
  11. uses
  12. Classes, SysUtils, db, dbconst, sqldb, bufdataset,
  13. {$IfDef LinkDynamically}
  14. ocidyn,
  15. {$ELSE}
  16. oci,
  17. {$ENDIF}
  18. oratypes;
  19. const
  20. DefaultTimeOut = 60;
  21. type
  22. EOraDatabaseError = class(ESQLDatabaseError)
  23. public
  24. property ORAErrorCode: integer read ErrorCode; deprecated 'Please use ErrorCode instead of ORAErrorCode'; // June 2014
  25. end;
  26. TOracleTrans = Class(TSQLHandle)
  27. protected
  28. FOciSvcCtx : POCISvcCtx;
  29. FOciTrans : POCITrans;
  30. FOciFlags : ub4;
  31. public
  32. destructor Destroy(); override;
  33. end;
  34. TOraFieldBuf = record
  35. DescType : ub4; // descriptor type
  36. Buffer : pointer;
  37. Ind : sb2; // indicator
  38. Len : ub4;
  39. Size : ub4;
  40. end;
  41. TOracleCursor = Class(TSQLCursor)
  42. protected
  43. FOciStmt : POCIStmt;
  44. FieldBuffers : array of TOraFieldBuf;
  45. ParamBuffers : array of TOraFieldBuf;
  46. end;
  47. { TOracleConnection }
  48. TOracleConnection = class (TSQLConnection)
  49. private
  50. FOciEnvironment : POciEnv;
  51. FOciError : POCIError;
  52. FOciServer : POCIServer;
  53. FOciUserSession : POCISession;
  54. FUserMem : pointer;
  55. procedure HandleError;
  56. procedure GetParameters(cursor : TSQLCursor; ATransaction : TSQLTransaction; AParams : TParams);
  57. procedure SetParameters(cursor : TSQLCursor; ATransaction : TSQLTransaction; AParams : TParams);
  58. protected
  59. // - Connect/disconnect
  60. procedure DoInternalConnect; override;
  61. procedure DoInternalDisconnect; override;
  62. // - Handle (de)allocation
  63. function AllocateCursorHandle:TSQLCursor; override;
  64. procedure DeAllocateCursorHandle(var cursor:TSQLCursor); override;
  65. function AllocateTransactionHandle:TSQLHandle; override;
  66. // - Statement handling
  67. procedure PrepareStatement(cursor:TSQLCursor; ATransaction:TSQLTransaction; buf:string; AParams:TParams); override;
  68. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  69. // - Transaction handling
  70. procedure InternalStartDBTransaction(trans:TOracleTrans);
  71. function GetTransactionHandle(trans:TSQLHandle):pointer; override;
  72. function StartDBTransaction(trans:TSQLHandle; AParams:string):boolean; override;
  73. function Commit(trans:TSQLHandle):boolean; override;
  74. function Rollback(trans:TSQLHandle):boolean; override;
  75. procedure CommitRetaining(trans:TSQLHandle); override;
  76. procedure RollbackRetaining(trans:TSQLHandle); override;
  77. // - Statement execution
  78. procedure Execute(cursor:TSQLCursor; ATransaction:TSQLTransaction; AParams:TParams); override;
  79. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  80. // - Result retrieval
  81. procedure AddFieldDefs(cursor:TSQLCursor; FieldDefs:TFieldDefs); override;
  82. function Fetch(cursor:TSQLCursor):boolean; override;
  83. function LoadField(cursor:TSQLCursor; FieldDef:TFieldDef; buffer:pointer; out CreateBlob : boolean):boolean; override;
  84. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction); override;
  85. procedure FreeFldBuffers(cursor:TSQLCursor); override;
  86. procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override;
  87. function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
  88. public
  89. constructor Create(AOwner : TComponent); override;
  90. end;
  91. { TOracleConnectionDef }
  92. TOracleConnectionDef = Class(TConnectionDef)
  93. Class Function TypeName : String; override;
  94. Class Function ConnectionClass : TSQLConnectionClass; override;
  95. Class Function Description : String; override;
  96. Class Function DefaultLibraryName : String; override;
  97. Class Function LoadFunction : TLibraryLoadFunction; override;
  98. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  99. Class Function LoadedLibraryName: string; override;
  100. end;
  101. implementation
  102. uses
  103. math, StrUtils, FmtBCD;
  104. const
  105. ObjectQuote='"'; //beginning and ending quote for objects such as table names. Note: can be different from quotes around field names
  106. ResourceString
  107. SErrEnvCreateFailed = 'The creation of an Oracle environment failed.';
  108. SErrHandleAllocFailed = 'The allocation of the error handle failed.';
  109. SErrOracle = 'Oracle returned error %s:';
  110. type
  111. TODateTime = record
  112. year : sb2;
  113. month : ub1;
  114. day : ub1;
  115. hour : ub1;
  116. min : ub1;
  117. sec : ub1;
  118. fsec : ub4;
  119. end;
  120. // Callback functions
  121. function cbf_no_data(ictxp:Pdvoid; bindp:POCIBind; iter:ub4; index:ub4; bufpp:PPdvoid;
  122. alenp:Pub4; piecep:Pub1; indp:PPdvoid):sb4;cdecl;
  123. begin
  124. bufpp^ := nil;
  125. alenp^ := 0;
  126. indp^ := nil;
  127. piecep^ := OCI_ONE_PIECE;
  128. result:=OCI_CONTINUE;
  129. end;
  130. function cbf_get_data(octxp:Pdvoid; bindp:POCIBind; iter:ub4; index:ub4; bufpp:PPdvoid;
  131. alenp:PPub4; piecep:Pub1; indp:PPdvoid; rcodep:PPub2):sb4;cdecl;
  132. begin
  133. // Only 1 row can be stored. No support for multiple rows: only the last row is kept.
  134. bufpp^:=TOraFieldBuf(octxp^).Buffer;
  135. indp^ := @TOraFieldBuf(octxp^).Ind;
  136. TOraFieldBuf(octxp^).Len:=TOraFieldBuf(octxp^).Size; //reset size to full buffer
  137. alenp^ := @TOraFieldBuf(octxp^).Len;
  138. rcodep^:=nil;
  139. piecep^ := OCI_ONE_PIECE;
  140. result:=OCI_CONTINUE;
  141. end;
  142. // Conversions
  143. Procedure FmtBCD2Nvu(bcd:tBCD;b:pByte);
  144. var
  145. i,j,cnt : integer;
  146. nibbles : array [0..maxfmtbcdfractionsize-1] of byte;
  147. exp : shortint;
  148. bb : byte;
  149. begin
  150. fillchar(b[0],22,#0);
  151. if BCDPrecision(bcd)=0 then // zero, special case
  152. begin
  153. b[0]:=1;
  154. b[1]:=$80;
  155. end
  156. else
  157. begin
  158. if (BCDPrecision(bcd)-BCDScale(bcd)) mod 2 <>0 then // odd number before decimal point
  159. begin
  160. nibbles[0]:=0;
  161. j:=1;
  162. end
  163. else
  164. j:=0;
  165. for i:=0 to bcd.Precision -1 do
  166. if i mod 2 =0 then
  167. nibbles[i+j]:=bcd.Fraction[i div 2] shr 4
  168. else
  169. nibbles[i+j]:=bcd.Fraction[i div 2] and $0f;
  170. nibbles[bcd.Precision+j]:=0; // make sure last nibble is also 0 in case we have odd scale
  171. exp:=(BCDPrecision(bcd)-BCDScale(bcd)+1) div 2;
  172. cnt:=exp+(BCDScale(bcd)+1) div 2;
  173. // to avoid "ora 01438: value larger than specified precision allowed for this column"
  174. // remove trailing zeros (scale < 0)...
  175. while (nibbles[cnt*2-2]*10+nibbles[cnt*2-1])=0 do
  176. cnt:=cnt-1;
  177. // ... and remove leading zeros (scale > precision)
  178. j:=0;
  179. while (nibbles[j*2]*10+nibbles[j*2+1])=0 do
  180. begin
  181. j:=j+1;
  182. exp:=exp-1;
  183. end;
  184. if IsBCDNegative(bcd) then
  185. begin
  186. b[0]:=cnt-j+1;
  187. b[1]:=not(exp+64) and $7f ;
  188. for i:=j to cnt-1 do
  189. begin
  190. bb:=nibbles[i*2]*10+nibbles[i*2+1];
  191. b[2+i-j]:=101-bb;
  192. end;
  193. if 2+cnt-j<22 then // add a 102 at the end of the number if place left.
  194. begin
  195. b[0]:=b[0]+1;
  196. b[2+cnt-j]:=102;
  197. end;
  198. end
  199. else
  200. begin
  201. b[0]:=cnt-j+1;
  202. b[1]:=(exp+64) or $80 ;
  203. for i:=j to cnt-1 do
  204. begin
  205. bb:=nibbles[i*2]*10+nibbles[i*2+1];
  206. b[2+i-j]:=1+bb;
  207. end;
  208. end;
  209. end;
  210. end;
  211. function Nvu2FmtBCD(b:pbyte):tBCD;
  212. var
  213. i,j : integer;
  214. bb,size : byte;
  215. exp : shortint;
  216. nibbles : array [0..maxfmtbcdfractionsize-1] of byte;
  217. scale : integer;
  218. begin
  219. size := b[0];
  220. if (size=1) and (b[1]=$80) then // special representation for 0
  221. result:=IntegerToBCD(0)
  222. else
  223. begin
  224. result.SignSpecialPlaces:=0; //sign positive, non blank, scale 0
  225. result.Precision:=1; //BCDNegate works only if Precision <>0
  226. if (b[1] and $80)=$80 then // then the number is positive
  227. begin
  228. exp := (b[1] and $7f)-65;
  229. for i := 0 to size-2 do
  230. begin
  231. bb := b[i+2]-1;
  232. nibbles[i*2]:=bb div 10;
  233. nibbles[i*2+1]:=(bb mod 10);
  234. end;
  235. end
  236. else
  237. begin
  238. BCDNegate(result);
  239. exp := (not(b[1]) and $7f)-65;
  240. if b[size]=102 then // last byte doesn't count if = 102
  241. size:=size-1;
  242. for i := 0 to size-2 do
  243. begin
  244. bb := 101-b[i+2];
  245. nibbles[i*2]:=bb div 10;
  246. nibbles[i*2+1]:=(bb mod 10);
  247. end;
  248. end;
  249. nibbles[(size-1)*2]:=0;
  250. result.Precision:=(size-1)*2;
  251. scale:=result.Precision-(exp*2+2);
  252. if scale>=0 then
  253. begin
  254. if (scale>result.Precision) then // need to add leading 0s
  255. begin
  256. for i:=0 to (scale-result.Precision+1) div 2 do
  257. result.Fraction[i]:=0;
  258. i:=scale-result.Precision;
  259. result.Precision:=scale;
  260. end
  261. else
  262. i:=0;
  263. j:=i;
  264. if (i=0) and (nibbles[0]=0) then // get rid of leading zero received from oci
  265. begin
  266. result.Precision:=result.Precision-1;
  267. j:=-1;
  268. end;
  269. while i<=result.Precision do // copy nibbles
  270. begin
  271. if i mod 2 =0 then
  272. result.Fraction[i div 2]:=nibbles[i-j] shl 4
  273. else
  274. result.Fraction[i div 2]:=result.Fraction[i div 2] or nibbles[i-j];
  275. i:=i+1;
  276. end;
  277. result.SignSpecialPlaces:=result.SignSpecialPlaces or scale;
  278. end
  279. else
  280. begin // add trailing zeroes, increase precision to take them into account
  281. i:=0;
  282. while i<=result.Precision do // copy nibbles
  283. begin
  284. if i mod 2 =0 then
  285. result.Fraction[i div 2]:=nibbles[i] shl 4
  286. else
  287. result.Fraction[i div 2]:=result.Fraction[i div 2] or nibbles[i];
  288. i:=i+1;
  289. end;
  290. result.Precision:=result.Precision-scale;
  291. for i := size -1 to High(result.Fraction) do
  292. result.Fraction[i] := 0;
  293. end;
  294. end;
  295. end;
  296. // TOracleConnection
  297. procedure TOracleConnection.HandleError;
  298. var
  299. errcode : sb4;
  300. buf : array[0..1023] of char;
  301. begin
  302. OCIErrorGet(FOciError,1,nil,errcode,@buf[0],1024,OCI_HTYPE_ERROR);
  303. raise EOraDatabaseError.CreateFmt(pchar(buf), [], Self, errcode, '')
  304. end;
  305. procedure TOracleConnection.GetParameters(cursor: TSQLCursor; ATransaction : TSQLTransaction; AParams: TParams);
  306. var
  307. i : integer;
  308. odt : TODateTime;
  309. s : string;
  310. begin
  311. with cursor as TOracleCursor do for i := 0 to High(ParamBuffers) do
  312. with AParams[i] do
  313. if ParamType=ptOutput then
  314. begin
  315. if ParamBuffers[i].ind = -1 then
  316. Value:=null;
  317. case DataType of
  318. ftInteger : AsInteger := PInteger(ParamBuffers[i].buffer)^;
  319. ftFloat : AsFloat := PDouble(ParamBuffers[i].buffer)^;
  320. ftString : begin
  321. SetLength(s,ParamBuffers[i].Len);
  322. move(ParamBuffers[i].buffer^,s[1],length(s)+1);
  323. AsString:=s;
  324. end;
  325. ftDate, ftDateTime: begin
  326. OCIDateTimeGetDate(FOciUserSession, FOciError, ParamBuffers[i].buffer, @odt.year, @odt.month, @odt.day);
  327. OCIDateTimeGetTime(FOciUserSession, FOciError, ParamBuffers[i].buffer, @odt.hour, @odt.min, @odt.sec, @odt.fsec);
  328. AsDateTime := ComposeDateTime(EncodeDate(odt.year,odt.month,odt.day), EncodeTime(odt.hour,odt.min,odt.sec,odt.fsec div 1000000));
  329. end;
  330. ftFMTBcd : begin
  331. AsFMTBCD:=Nvu2FmtBCD(ParamBuffers[i].buffer);
  332. end;
  333. end;
  334. end;
  335. end;
  336. procedure TOracleConnection.DoInternalConnect;
  337. var
  338. ConnectString : string;
  339. TempServiceContext : POCISvcCtx;
  340. IsConnected : boolean;
  341. CharSetId: ub2;
  342. begin
  343. {$IfDef LinkDynamically}
  344. InitialiseOCI;
  345. {$EndIf}
  346. inherited DoInternalConnect;
  347. //ToDo: get rid of FUserMem, as it isn't used
  348. FUserMem := nil;
  349. IsConnected := false;
  350. try
  351. case GetConnectionCharSet of
  352. 'utf8': CharSetId := 873;
  353. else CharSetId := 0; // if it is 0, the NLS_LANG and NLS_NCHAR environment variables are used
  354. end;
  355. // Create environment handle
  356. if OCIEnvNlsCreate(FOciEnvironment,OCI_DEFAULT,nil,nil,nil,nil,0,FUserMem,CharSetId,CharSetId) <> OCI_SUCCESS then
  357. DatabaseError(SErrEnvCreateFailed,self);
  358. // Create error handle
  359. if OciHandleAlloc(FOciEnvironment,FOciError,OCI_HTYPE_ERROR,0,FUserMem) <> OCI_SUCCESS then
  360. DatabaseError(SErrHandleAllocFailed,self);
  361. // Create server handle
  362. if OciHandleAlloc(FOciEnvironment,FOciServer,OCI_HTYPE_SERVER,0,FUserMem) <> OCI_SUCCESS then
  363. DatabaseError(SErrHandleAllocFailed,self);
  364. // Initialize server handle
  365. if HostName='' then
  366. ConnectString := DatabaseName
  367. else
  368. ConnectString := '//'+HostName+'/'+DatabaseName;
  369. if OCIServerAttach(FOciServer,FOciError,@(ConnectString[1]),Length(ConnectString),OCI_DEFAULT) <> OCI_SUCCESS then
  370. HandleError();
  371. try
  372. // Create temporary service-context handle for user authentication
  373. if OciHandleAlloc(FOciEnvironment,TempServiceContext,OCI_HTYPE_SVCCTX,0,FUserMem) <> OCI_SUCCESS then
  374. DatabaseError(SErrHandleAllocFailed,self);
  375. try
  376. // Create user-session handle
  377. if OciHandleAlloc(FOciEnvironment,FOciUserSession,OCI_HTYPE_SESSION,0,FUserMem) <> OCI_SUCCESS then
  378. DatabaseError(SErrHandleAllocFailed,self);
  379. try
  380. // Set the server-handle in the service-context handle
  381. if OCIAttrSet(TempServiceContext,OCI_HTYPE_SVCCTX,FOciServer,0,OCI_ATTR_SERVER,FOciError) <> OCI_SUCCESS then
  382. HandleError();
  383. // Set username and password in the user-session handle
  384. if OCIAttrSet(FOciUserSession,OCI_HTYPE_SESSION,@(Self.UserName[1]),Length(Self.UserName),OCI_ATTR_USERNAME,FOciError) <> OCI_SUCCESS then
  385. HandleError();
  386. if OCIAttrSet(FOciUserSession,OCI_HTYPE_SESSION,@(Self.Password[1]),Length(Self.Password),OCI_ATTR_PASSWORD,FOciError) <> OCI_SUCCESS then
  387. HandleError();
  388. // Authenticate
  389. if OCISessionBegin(TempServiceContext,FOciError,FOcIUserSession,OCI_CRED_RDBMS,OCI_DEFAULT) <> OCI_SUCCESS then
  390. HandleError();
  391. IsConnected := true;
  392. finally
  393. if not IsConnected then
  394. begin
  395. OCIHandleFree(FOciUserSession,OCI_HTYPE_SESSION);
  396. FOciUserSession := nil;
  397. end;
  398. end;
  399. finally
  400. // Free temporary service-context handle
  401. OCIHandleFree(TempServiceContext,OCI_HTYPE_SVCCTX);
  402. end;
  403. finally
  404. if not IsConnected then
  405. OCIServerDetach(FOciServer,FOciError,OCI_DEFAULT);
  406. end;
  407. finally
  408. if not IsConnected then
  409. begin
  410. if assigned(FOciServer) then
  411. OCIHandleFree(FOciServer,OCI_HTYPE_SERVER);
  412. if assigned(FOciError) then
  413. OCIHandleFree(FOciError,OCI_HTYPE_ERROR);
  414. if assigned(FOciEnvironment) then
  415. OCIHandleFree(FOciEnvironment,OCI_HTYPE_ENV);
  416. FOciEnvironment := nil;
  417. FOciError := nil;
  418. FOciServer := nil;
  419. end;
  420. end;
  421. end;
  422. procedure TOracleConnection.DoInternalDisconnect;
  423. var
  424. TempServiceContext : POCISvcCtx;
  425. begin
  426. inherited DoInternalDisconnect;
  427. if assigned(FOciEnvironment) then
  428. begin
  429. if assigned(FOciError) then
  430. begin
  431. if assigned(FOciServer) then
  432. begin
  433. if assigned(FOciUserSession) then
  434. begin
  435. try
  436. // Create temporary service-context handle for user-disconnect
  437. if OciHandleAlloc(FOciEnvironment,TempServiceContext,OCI_HTYPE_SVCCTX,0,FUserMem) <> OCI_SUCCESS then
  438. DatabaseError(SErrHandleAllocFailed,self);
  439. // Set the server handle in the service-context handle
  440. if OCIAttrSet(TempServiceContext,OCI_HTYPE_SVCCTX,FOciServer,0,OCI_ATTR_SERVER,FOciError) <> OCI_SUCCESS then
  441. HandleError();
  442. // Set the user session handle in the service-context handle
  443. if OCIAttrSet(TempServiceContext,OCI_HTYPE_SVCCTX,FOciUserSession,0,OCI_ATTR_SESSION,FOciError) <> OCI_SUCCESS then
  444. HandleError();
  445. // Disconnect uses-session handle
  446. if OCISessionEnd(TempServiceContext,FOciError,FOcIUserSession,OCI_DEFAULT) <> OCI_SUCCESS then
  447. HandleError();
  448. finally
  449. // Free user-session handle
  450. OCIHandleFree(FOciUserSession,OCI_HTYPE_SESSION);
  451. // Free temporary service-context handle
  452. OCIHandleFree(TempServiceContext,OCI_HTYPE_SVCCTX);
  453. FOciUserSession := nil;
  454. end;
  455. end;
  456. try
  457. // Disconnect server handle
  458. if OCIServerDetach(FOciServer,FOciError,OCI_DEFAULT) <> OCI_SUCCESS then
  459. HandleError();
  460. finally
  461. // Free connection handles
  462. OCIHandleFree(FOciServer,OCI_HTYPE_SERVER);
  463. FOciServer := nil;
  464. end;
  465. end;
  466. OCIHandleFree(FOciError,OCI_HTYPE_ERROR);
  467. FOciError := nil;
  468. end;
  469. OCIHandleFree(FOciEnvironment,OCI_HTYPE_ENV);
  470. FOciEnvironment := nil;
  471. end;
  472. {$IfDef LinkDynamically}
  473. ReleaseOCI;
  474. {$EndIf}
  475. end;
  476. function TOracleConnection.AllocateCursorHandle: TSQLCursor;
  477. var
  478. Cursor : TOracleCursor;
  479. begin
  480. Cursor:=TOracleCursor.Create;
  481. Result := cursor;
  482. end;
  483. procedure TOracleConnection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  484. procedure FreeOraFieldBuffers(b: array of TOraFieldBuf);
  485. var i : integer;
  486. begin
  487. if Length(b) > 0 then
  488. for i := low(b) to high(b) do
  489. if b[i].DescType <> 0 then
  490. OciDescriptorFree(b[i].buffer, b[i].DescType)
  491. else
  492. freemem(b[i].buffer);
  493. end;
  494. begin
  495. with cursor as TOracleCursor do
  496. begin
  497. FreeOraFieldBuffers(FieldBuffers);
  498. FreeOraFieldBuffers(ParamBuffers);
  499. end;
  500. FreeAndNil(cursor);
  501. end;
  502. function TOracleConnection.AllocateTransactionHandle: TSQLHandle;
  503. var
  504. locRes : TOracleTrans;
  505. begin
  506. locRes := TOracleTrans.Create();
  507. try
  508. // Allocate service-context handle
  509. if OciHandleAlloc(FOciEnvironment,locRes.FOciSvcCtx,OCI_HTYPE_SVCCTX,0,FUserMem) <> OCI_SUCCESS then
  510. DatabaseError(SErrHandleAllocFailed,self);
  511. // Set the server-handle in the service-context handle
  512. if OCIAttrSet(locRes.FOciSvcCtx,OCI_HTYPE_SVCCTX,FOciServer,0,OCI_ATTR_SERVER,FOciError) <> OCI_SUCCESS then
  513. HandleError();
  514. // Set the user-session handle in the service-context handle
  515. if OCIAttrSet(locRes.FOciSvcCtx,OCI_HTYPE_SVCCTX,FOciUserSession,0,OCI_ATTR_SESSION,FOciError) <> OCI_SUCCESS then
  516. HandleError();
  517. // Allocate transaction handle
  518. if OciHandleAlloc(FOciEnvironment,locRes.FOciTrans,OCI_HTYPE_TRANS,0,FUserMem) <> OCI_SUCCESS then
  519. DatabaseError(SErrHandleAllocFailed,self);
  520. // Set the transaction handle in the service-context handle
  521. if OCIAttrSet(locRes.FOciSvcCtx,OCI_HTYPE_SVCCTX,locRes.FOciTrans,0,OCI_ATTR_TRANS,FOciError) <> OCI_SUCCESS then
  522. HandleError();
  523. except
  524. locRes.Free();
  525. raise;
  526. end;
  527. Result := locRes;
  528. end;
  529. procedure TOracleConnection.PrepareStatement(cursor: TSQLCursor;
  530. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  531. var i : integer;
  532. FOcibind : POCIDefine;
  533. OFieldType : ub2;
  534. OFieldSize : sb4;
  535. ODescType : ub4;
  536. OBuffer : pointer;
  537. stmttype : ub2;
  538. begin
  539. with cursor as TOracleCursor do
  540. begin
  541. if LogEvent(detActualSQL) then
  542. Log(detActualSQL,Buf);
  543. if OCIStmtPrepare2(TOracleTrans(ATransaction.Handle).FOciSvcCtx,FOciStmt,FOciError,@buf[1],length(buf),nil,0,OCI_NTV_SYNTAX,OCI_DEFAULT) = OCI_ERROR then
  544. HandleError;
  545. // Get statement type
  546. if OCIAttrGet(FOciStmt,OCI_HTYPE_STMT,@stmttype,nil,OCI_ATTR_STMT_TYPE,FOciError) = OCI_ERROR then
  547. HandleError;
  548. case stmttype of
  549. OCI_STMT_SELECT: FStatementType := stSelect;
  550. OCI_STMT_UPDATE: FStatementType := stUpdate;
  551. OCI_STMT_DELETE: FStatementType := stDelete;
  552. OCI_STMT_INSERT: FStatementType := stInsert;
  553. OCI_STMT_CREATE,
  554. OCI_STMT_DROP,
  555. OCI_STMT_DECLARE,
  556. OCI_STMT_ALTER: FStatementType := stDDL;
  557. else
  558. FStatementType := stUnknown;
  559. end;
  560. if FStatementType in [stUpdate,stDelete,stInsert,stDDL] then
  561. FSelectable:=false;
  562. if assigned(AParams) then
  563. begin
  564. setlength(ParamBuffers,AParams.Count);
  565. for i := 0 to AParams.Count-1 do
  566. begin
  567. ODescType := 0;
  568. case AParams[i].DataType of
  569. ftSmallInt, ftInteger :
  570. begin OFieldType := SQLT_INT; OFieldSize := sizeof(integer); end;
  571. ftLargeInt :
  572. begin OFieldType := SQLT_INT; OFieldSize := sizeof(int64); end;
  573. ftFloat :
  574. begin OFieldType := SQLT_FLT; OFieldSize := sizeof(double); end;
  575. ftDate, ftDateTime :
  576. begin OFieldType := SQLT_TIMESTAMP; OFieldSize := sizeof(pointer); ODescType := OCI_DTYPE_TIMESTAMP; end;
  577. ftFixedChar, ftString :
  578. begin OFieldType := SQLT_STR; OFieldSize := 4000; end;
  579. ftFMTBcd, ftBCD :
  580. begin OFieldType := SQLT_VNU; OFieldSize := 22; end;
  581. ftBlob :
  582. //begin OFieldType := SQLT_LVB; OFieldSize := 65535; end;
  583. begin OFieldType := SQLT_BLOB; OFieldSize := sizeof(pointer); ODescType := OCI_DTYPE_LOB; end;
  584. ftMemo :
  585. begin OFieldType := SQLT_LVC; OFieldSize := 65535; end;
  586. else
  587. DatabaseErrorFmt(SUnsupportedParameter,[Fieldtypenames[AParams[i].DataType]],self);
  588. end;
  589. ParamBuffers[i].DescType := ODescType;
  590. ParamBuffers[i].Len := OFieldSize;
  591. ParamBuffers[i].Size := OFieldSize;
  592. if ODescType <> 0 then
  593. begin
  594. OBuffer := @ParamBuffers[i].buffer;
  595. OCIDescriptorAlloc(FOciEnvironment, OBuffer, ODescType, 0, nil);
  596. end
  597. else
  598. begin
  599. OBuffer := getmem(OFieldSize);
  600. ParamBuffers[i].buffer := OBuffer;
  601. end;
  602. FOciBind := nil;
  603. if AParams[i].ParamType=ptInput then
  604. begin
  605. 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
  606. HandleError;
  607. end
  608. else if AParams[i].ParamType=ptOutput then
  609. begin
  610. 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
  611. HandleError;
  612. if OCIBindDynamic(FOcibind, FOciError, nil, @cbf_no_data, @parambuffers[i], @cbf_get_data) <> OCI_SUCCESS then
  613. HandleError;
  614. end;
  615. end;
  616. end;
  617. FPrepared := True;
  618. end;
  619. end;
  620. procedure TOracleConnection.SetParameters(cursor : TSQLCursor; ATransaction : TSQLTransaction; AParams : TParams);
  621. var i : integer;
  622. year, month, day, hour, min, sec, msec : word;
  623. s : string;
  624. LobBuffer : TBytes;
  625. LobLength : ub4;
  626. begin
  627. with cursor as TOracleCursor do for i := 0 to High(ParamBuffers) do with AParams[i] do
  628. if ParamType=ptInput then
  629. begin
  630. if IsNull then ParamBuffers[i].ind := -1 else ParamBuffers[i].ind := 0;
  631. case DataType of
  632. ftSmallInt,
  633. ftInteger : PInteger(ParamBuffers[i].buffer)^ := AsInteger;
  634. ftLargeInt : PInt64(ParamBuffers[i].buffer)^ := AsLargeInt;
  635. ftFloat : PDouble(ParamBuffers[i].buffer)^ := AsFloat;
  636. ftString,
  637. ftFixedChar : begin
  638. s := asString+#0;
  639. move(s[1],parambuffers[i].buffer^,length(s)+1);
  640. end;
  641. ftDate, ftDateTime: begin
  642. DecodeDate(asDateTime,year,month,day);
  643. DecodeTime(asDateTime,hour,min,sec,msec);
  644. if OCIDateTimeConstruct(FOciUserSession, FOciError, ParamBuffers[i].buffer, year, month, day, hour, min, sec, msec*1000000, nil, 0) = OCI_ERROR then
  645. HandleError;
  646. { pb := ParamBuffers[i].buffer;
  647. pb[0] := (year div 100)+100;
  648. pb[1] := (year mod 100)+100;
  649. pb[2] := month;
  650. pb[3] := day;
  651. pb[4] := hour+1;
  652. pb[5] := minute+1;
  653. pb[6] := second+1;
  654. }
  655. end;
  656. ftFmtBCD, ftBCD : begin
  657. FmtBCD2Nvu(asFmtBCD,parambuffers[i].buffer);
  658. end;
  659. ftBlob : begin
  660. LobBuffer := AsBlob; // todo: use AsBytes
  661. LobLength := length(LobBuffer);
  662. // create empty temporary LOB with zero length
  663. if OciLobCreateTemporary(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, ParamBuffers[i].Buffer, OCI_DEFAULT, OCI_DEFAULT, OCI_TEMP_BLOB, False, OCI_DURATION_SESSION) = OCI_ERROR then
  664. HandleError;
  665. if (LobLength > 0) and (OciLobWrite(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, ParamBuffers[i].Buffer, @LobLength, 1, @LobBuffer[0], LobLength, OCI_ONE_PIECE, nil, nil, 0, SQLCS_IMPLICIT) = OCI_ERROR) then
  666. HandleError;
  667. end;
  668. ftMemo : begin
  669. LobBuffer := AsBytes;
  670. LobLength := length(LobBuffer);
  671. if LobLength > 65531 then LobLength := 65531;
  672. PInteger(ParamBuffers[i].Buffer)^ := LobLength;
  673. Move(LobBuffer[0], (ParamBuffers[i].Buffer+sizeof(integer))^, LobLength);
  674. end;
  675. else
  676. DatabaseErrorFmt(SUnsupportedParameter,[DataType],self);
  677. end;
  678. end;
  679. end;
  680. procedure TOracleConnection.UnPrepareStatement(cursor: TSQLCursor);
  681. begin
  682. if OCIStmtRelease(TOracleCursor(cursor).FOciStmt,FOciError,nil,0,OCI_DEFAULT)<> OCI_SUCCESS then
  683. HandleError();
  684. cursor.FPrepared:=False;
  685. end;
  686. procedure TOracleConnection.InternalStartDBTransaction(trans : TOracleTrans);
  687. begin
  688. if OCITransStart(trans.FOciSvcCtx,FOciError,DefaultTimeOut,trans.FOciFlags) <> OCI_SUCCESS then
  689. HandleError();
  690. end;
  691. function TOracleConnection.GetTransactionHandle(trans: TSQLHandle): pointer;
  692. begin
  693. Result := trans;
  694. end;
  695. function TOracleConnection.StartDBTransaction(trans: TSQLHandle; AParams: string): boolean;
  696. var
  697. flags : ub4;
  698. i : Integer;
  699. s : string;
  700. locTrans : TOracleTrans;
  701. begin
  702. flags := OCI_TRANS_READWRITE;
  703. if AParams <> '' then begin
  704. i := 1;
  705. s := ExtractSubStr(AParams,i,StdWordDelims);
  706. while ( s <> '' ) do begin
  707. if ( s = 'readonly' ) then
  708. flags := OCI_TRANS_READONLY
  709. else if ( s = 'serializable' ) then
  710. flags := OCI_TRANS_SERIALIZABLE
  711. else if ( s = 'readwrite' ) then
  712. flags := OCI_TRANS_READWRITE;
  713. s := ExtractSubStr(AParams,i,StdWordDelims);
  714. end;
  715. end;
  716. locTrans := TOracleTrans(trans);
  717. locTrans.FOciFlags := flags or OCI_TRANS_NEW;
  718. InternalStartDBTransaction(locTrans);
  719. Result := True;
  720. end;
  721. function TOracleConnection.Commit(trans: TSQLHandle): boolean;
  722. begin
  723. if OCITransCommit(TOracleTrans(trans).FOciSvcCtx,FOciError,OCI_DEFAULT) <> OCI_SUCCESS then
  724. HandleError();
  725. Result := True;
  726. end;
  727. function TOracleConnection.Rollback(trans: TSQLHandle): boolean;
  728. begin
  729. if OCITransRollback(TOracleTrans(trans).FOciSvcCtx,FOciError,OCI_DEFAULT) <> OCI_SUCCESS then
  730. HandleError();
  731. Result := True;
  732. end;
  733. procedure TOracleConnection.CommitRetaining(trans: TSQLHandle);
  734. begin
  735. Commit(trans);
  736. InternalStartDBTransaction(TOracleTrans(trans));
  737. end;
  738. procedure TOracleConnection.RollbackRetaining(trans: TSQLHandle);
  739. begin
  740. Rollback(trans);
  741. InternalStartDBTransaction(TOracleTrans(trans));
  742. end;
  743. procedure TOracleConnection.Execute(cursor: TSQLCursor; ATransaction: TSQLTransaction; AParams: TParams);
  744. procedure FreeParameters;
  745. var i: integer;
  746. begin
  747. with cursor as TOracleCursor do
  748. for i:=0 to high(ParamBuffers) do
  749. if ParamBuffers[i].DescType = OCI_DTYPE_LOB then
  750. if OciLobFreeTemporary(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, ParamBuffers[i].Buffer) = OCI_ERROR then
  751. HandleError;
  752. end;
  753. begin
  754. if Assigned(AParams) and (AParams.Count > 0) then SetParameters(cursor, ATransaction, AParams);
  755. if LogEvent(detParamValue) then
  756. LogParams(AParams);
  757. if cursor.FStatementType = stSelect then
  758. begin
  759. if OCIStmtExecute(TOracleTrans(ATransaction.Handle).FOciSvcCtx,(cursor as TOracleCursor).FOciStmt,FOciError,0,0,nil,nil,OCI_DEFAULT) = OCI_ERROR then
  760. HandleError;
  761. end
  762. else
  763. begin
  764. if OCIStmtExecute(TOracleTrans(ATransaction.Handle).FOciSvcCtx,(cursor as TOracleCursor).FOciStmt,FOciError,1,0,nil,nil,OCI_DEFAULT) = OCI_ERROR then
  765. HandleError;
  766. if Assigned(AParams) and (AParams.Count > 0) then GetParameters(cursor, ATransaction, AParams);
  767. end;
  768. FreeParameters;
  769. end;
  770. function TOracleConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  771. var rowcount: ub4;
  772. begin
  773. if Assigned(cursor) and (OCIAttrGet((cursor as TOracleCursor).FOciStmt, OCI_HTYPE_STMT, @rowcount, nil, OCI_ATTR_ROW_COUNT, FOciError) = OCI_SUCCESS) then
  774. Result:=rowcount
  775. else
  776. Result:=inherited RowsAffected(cursor);
  777. end;
  778. procedure TOracleConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs: TFieldDefs);
  779. var Param : POCIParam;
  780. counter : ub4;
  781. FieldType : TFieldType;
  782. FieldName : string;
  783. FieldSize : cardinal;
  784. OFieldType : ub2;
  785. OFieldName : Pchar;
  786. OFieldSize : ub4;
  787. OFNameLength : ub4;
  788. NumCols : ub4;
  789. FOciDefine : POCIDefine;
  790. OPrecision : sb2;
  791. OScale : sb1;
  792. ODescType : ub4;
  793. OBuffer : pointer;
  794. begin
  795. Param := nil;
  796. with cursor as TOracleCursor do
  797. begin
  798. if OCIAttrGet(FOciStmt,OCI_HTYPE_STMT,@numcols,nil,OCI_ATTR_PARAM_COUNT,FOciError) = OCI_ERROR then
  799. HandleError;
  800. // Note: needs to be cleared then allocated in one go.
  801. Setlength(FieldBuffers,numcols);
  802. for counter := 1 to numcols do
  803. begin
  804. // Clear OFieldSize. Oracle 9i, 10g doc says *ub4 but some clients use *ub2 leaving
  805. // high 16 bit untouched resulting in huge values and ORA-01062
  806. // WARNING: this does not work on big endian systems !!!!
  807. // To be tested if BE systems have this *ub2<->*ub4 problem
  808. OFieldSize:=0;
  809. ODescType :=0;
  810. if OCIParamGet(FOciStmt,OCI_HTYPE_STMT,FOciError,Param,counter) = OCI_ERROR then
  811. HandleError;
  812. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldType,nil,OCI_ATTR_DATA_TYPE,FOciError) = OCI_ERROR then
  813. HandleError;
  814. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldSize,nil,OCI_ATTR_DATA_SIZE,FOciError) = OCI_ERROR then
  815. HandleError;
  816. FieldSize := 0;
  817. case OFieldType of
  818. OCI_TYPECODE_NUMBER : begin
  819. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@Oprecision,nil,OCI_ATTR_PRECISION,FOciError) = OCI_ERROR then
  820. HandleError;
  821. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@Oscale,nil,OCI_ATTR_SCALE,FOciError) = OCI_ERROR then
  822. HandleError;
  823. if (Oscale = 0) and (Oprecision < 10) then
  824. begin
  825. if Oprecision=0 then //Number(0,0) = number(32,4)
  826. begin
  827. FieldType := ftFMTBCD;
  828. FieldSize := 4;
  829. OFieldType := SQLT_VNU;
  830. OFieldSize:= 22;
  831. end
  832. else if Oprecision < 5 then
  833. begin
  834. FieldType := ftSmallint;
  835. OFieldType := SQLT_INT;
  836. OFieldSize := sizeof(smallint);
  837. end
  838. else // OPrecision=5..9, OScale=0
  839. begin
  840. FieldType := ftInteger;
  841. OFieldType := SQLT_INT;
  842. OFieldSize:= sizeof(integer);
  843. end;
  844. end
  845. else if (Oscale = -127) {and (OPrecision=0)} then
  846. begin
  847. FieldType := ftFloat;
  848. OFieldType := SQLT_FLT;
  849. OFieldSize:=sizeof(double);
  850. end
  851. else if (Oscale >=0) and (Oscale <=4) and (OPrecision<=12) then
  852. begin
  853. FieldType := ftBCD;
  854. FieldSize := oscale;
  855. OFieldType := SQLT_VNU;
  856. OFieldSize:= 22;
  857. end
  858. else if (OPrecision-Oscale<64) and (Oscale < 64) then // limited to 63 digits before or after decimal point
  859. begin
  860. FieldType := ftFMTBCD;
  861. FieldSize := oscale;
  862. OFieldType := SQLT_VNU;
  863. OFieldSize:= 22;
  864. end
  865. else // approximation with double, best we can do
  866. begin
  867. FieldType := ftFloat;
  868. OFieldType := SQLT_FLT;
  869. OFieldSize:=sizeof(double);
  870. end;
  871. end;
  872. SQLT_LNG : begin
  873. FieldType := ftString;
  874. FieldSize := MaxSmallint; // OFieldSize is zero for LONG data type
  875. OFieldSize:= MaxSmallint+1;
  876. OFieldType:=SQLT_STR;
  877. end;
  878. OCI_TYPECODE_CHAR,
  879. OCI_TYPECODE_VARCHAR,
  880. OCI_TYPECODE_VARCHAR2 : begin
  881. FieldType := ftString;
  882. FieldSize := OFieldSize;
  883. inc(OFieldSize);
  884. OFieldType:=SQLT_STR;
  885. end;
  886. OCI_TYPECODE_DATE : FieldType := ftDate;
  887. OCI_TYPECODE_TIMESTAMP,
  888. OCI_TYPECODE_TIMESTAMP_LTZ,
  889. OCI_TYPECODE_TIMESTAMP_TZ :
  890. begin
  891. FieldType := ftDateTime;
  892. OFieldType := SQLT_TIMESTAMP;
  893. ODescType := OCI_DTYPE_TIMESTAMP;
  894. end;
  895. OCI_TYPECODE_BFLOAT,
  896. OCI_TYPECODE_BDOUBLE : begin
  897. FieldType := ftFloat;
  898. OFieldType := SQLT_BDOUBLE;
  899. OFieldSize := sizeof(double);
  900. end;
  901. SQLT_BLOB : begin
  902. FieldType := ftBlob;
  903. ODescType := OCI_DTYPE_LOB;
  904. end;
  905. SQLT_CLOB : begin
  906. FieldType := ftMemo;
  907. ODescType := OCI_DTYPE_LOB;
  908. end
  909. else
  910. FieldType := ftUnknown;
  911. end;
  912. FieldBuffers[counter-1].DescType := ODescType;
  913. if ODescType <> 0 then
  914. begin
  915. OBuffer := @FieldBuffers[counter-1].buffer;
  916. OCIDescriptorAlloc(FOciEnvironment, OBuffer, ODescType, 0, nil);
  917. OFieldSize := sizeof(pointer);
  918. end
  919. else
  920. begin
  921. OBuffer := getmem(OFieldSize);
  922. FieldBuffers[counter-1].buffer := OBuffer;
  923. end;
  924. if FieldType <> ftUnknown then
  925. begin
  926. FOciDefine := nil;
  927. if OciDefineByPos(FOciStmt,FOciDefine,FOciError,counter,OBuffer,OFieldSize,OFieldType,@FieldBuffers[counter-1].ind,nil,nil,OCI_DEFAULT) = OCI_ERROR then
  928. HandleError;
  929. end;
  930. if OCIAttrGet(Param,OCI_DTYPE_PARAM,@OFieldName,@OFNameLength,OCI_ATTR_NAME,FOciError) <> OCI_SUCCESS then
  931. HandleError;
  932. setlength(Fieldname,OFNameLength);
  933. move(OFieldName^,Fieldname[1],OFNameLength);
  934. FieldDefs.Add(FieldDefs.MakeNameUnique(FieldName), FieldType, FieldSize, False, counter);
  935. end;
  936. end;
  937. end;
  938. function TOracleConnection.Fetch(cursor: TSQLCursor): boolean;
  939. begin
  940. case OCIStmtFetch2((cursor as TOracleCursor).FOciStmt,FOciError,1,OCI_FETCH_NEXT,1,OCI_DEFAULT) of
  941. OCI_ERROR : begin
  942. Result := False;
  943. HandleError;
  944. end;
  945. OCI_NO_DATA : Result := False;
  946. OCI_SUCCESS : Result := True;
  947. OCI_SUCCESS_WITH_INFO : Begin
  948. Result := True;
  949. HandleError;
  950. end;
  951. end; {case}
  952. end;
  953. function TOracleConnection.LoadField(cursor: TSQLCursor; FieldDef: TFieldDef; buffer: pointer; out CreateBlob : boolean): boolean;
  954. var
  955. b : pbyte;
  956. size,i : byte;
  957. exp : shortint;
  958. cur : Currency;
  959. odt : TODateTime;
  960. begin
  961. CreateBlob := False;
  962. with cursor as TOracleCursor do if fieldbuffers[FieldDef.FieldNo-1].ind = -1 then
  963. Result := False
  964. else
  965. begin
  966. Result := True;
  967. case FieldDef.DataType of
  968. ftString :
  969. move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,FieldDef.Size);
  970. ftBCD :
  971. begin
  972. b := fieldbuffers[FieldDef.FieldNo-1].buffer;
  973. size := b[0];
  974. cur := 0;
  975. if (b[1] and $80)=$80 then // the number is positive
  976. begin
  977. exp := (b[1] and $7f)-65;
  978. for i := 2 to size do
  979. cur := cur + (b[i]-1) * intpower(100,-(i-2)+exp);
  980. end
  981. else
  982. begin
  983. exp := (not(b[1]) and $7f)-65;
  984. for i := 2 to size-1 do
  985. cur := cur + (101-b[i]) * intpower(100,-(i-2)+exp);
  986. cur := -cur;
  987. end;
  988. move(cur,buffer^,SizeOf(Currency));
  989. end;
  990. ftFmtBCD :
  991. pBCD(buffer)^:= Nvu2FmtBCD(fieldbuffers[FieldDef.FieldNo-1].buffer);
  992. ftFloat :
  993. move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,sizeof(double));
  994. ftSmallInt :
  995. move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,sizeof(smallint));
  996. ftInteger :
  997. move(fieldbuffers[FieldDef.FieldNo-1].buffer^,buffer^,sizeof(integer));
  998. ftDate :
  999. begin
  1000. b := fieldbuffers[FieldDef.FieldNo-1].buffer;
  1001. PDateTime(buffer)^ := ComposeDateTime(EncodeDate((b[0]-100)*100+(b[1]-100),b[2],b[3]), EncodeTime(b[4]-1, b[5]-1, b[6]-1, 0));
  1002. end;
  1003. ftDateTime :
  1004. begin
  1005. OCIDateTimeGetDate(FOciUserSession, FOciError, FieldBuffers[FieldDef.FieldNo-1].buffer, @odt.year, @odt.month, @odt.day);
  1006. OCIDateTimeGetTime(FOciUserSession, FOciError, FieldBuffers[FieldDef.FieldNo-1].buffer, @odt.hour, @odt.min, @odt.sec, @odt.fsec);
  1007. PDateTime(buffer)^ := ComposeDateTime(EncodeDate(odt.year,odt.month,odt.day), EncodeTime(odt.hour,odt.min,odt.sec,odt.fsec div 1000000));
  1008. end;
  1009. ftBlob,
  1010. ftMemo :
  1011. CreateBlob := True;
  1012. else
  1013. Result := False;
  1014. end;
  1015. end;
  1016. end;
  1017. procedure TOracleConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef; ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  1018. var LobLocator: pointer;
  1019. LobCharSetForm: ub1;
  1020. LobLength, LobSize: ub4;
  1021. begin
  1022. LobLocator := (cursor as TOracleCursor).FieldBuffers[FieldDef.FieldNo-1].Buffer;
  1023. //if OCILobLocatorIsInit(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @is_init) = OCI_ERROR then
  1024. // HandleError;
  1025. // For character LOBs, it is the number of characters, for binary LOBs and BFILEs it is the number of bytes
  1026. if OciLobGetLength(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @LobLength) = OCI_ERROR then
  1027. HandleError;
  1028. if OCILobCharSetForm(FOciEnvironment, FOciError, LobLocator, @LobCharSetForm) = OCI_ERROR then
  1029. HandleError;
  1030. // Adjust initial buffer size (in bytes), while LobLength can be in characters
  1031. case LobCharSetForm of
  1032. 0: ; // BLOB
  1033. SQLCS_IMPLICIT, // CLOB
  1034. SQLCS_NCHAR: // NCLOB
  1035. LobLength := LobLength*4;
  1036. end;
  1037. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, LobLength);
  1038. LobSize := 0;
  1039. // For CLOBs and NCLOBs the total amount of data which should be readed is on input in characters, but on output is in bytes if client character set is varying-width
  1040. // The application must call OCILobRead() (in streamed mode) over and over again to read more pieces of the LOB until the OCI_NEED_DATA error code is not returned.
  1041. // If the LOB is a BLOB, the csid and csfrm parameters are ignored.
  1042. if (LobLength > 0) and (OciLobRead(TOracleTrans(ATransaction.Handle).FOciSvcCtx, FOciError, LobLocator, @LobSize, 1, ABlobBuf^.BlobBuffer^.Buffer, LobLength, nil, nil, 0, LobCharSetForm) = OCI_ERROR) then
  1043. HandleError;
  1044. // Shrink initial buffer if needed (we assume that LobSize is in bytes, what is true for CLOB,NCLOB if client character set is varying-width, but if client character set is fixed-width then it is in characters)
  1045. if LobSize <> LobLength then
  1046. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, LobSize);
  1047. ABlobBuf^.BlobBuffer^.Size := LobSize;
  1048. end;
  1049. procedure TOracleConnection.FreeFldBuffers(cursor: TSQLCursor);
  1050. begin
  1051. // inherited FreeFldBuffers(cursor);
  1052. end;
  1053. procedure TOracleConnection.UpdateIndexDefs(IndexDefs: TIndexDefs;
  1054. TableName: string);
  1055. var qry : TSQLQuery;
  1056. begin
  1057. if not assigned(Transaction) then
  1058. DatabaseError(SErrConnTransactionnSet);
  1059. // Get table name into canonical format
  1060. if (length(TableName)>2) and (TableName[1]=ObjectQuote) and (TableName[length(TableName)]=ObjectQuote) then
  1061. TableName := AnsiDequotedStr(TableName, ObjectQuote)
  1062. else
  1063. TableName := UpperCase(TableName); //ANSI SQL: the name of an identifier (such as table names) are implicitly converted to uppercase, unless double quotes are used when referring to the identifier.
  1064. qry := tsqlquery.Create(nil);
  1065. qry.transaction := Transaction;
  1066. qry.database := Self;
  1067. with qry do
  1068. begin
  1069. ReadOnly := True;
  1070. sql.clear;
  1071. sql.add('SELECT '+
  1072. 'i.INDEX_NAME, '+
  1073. 'c.COLUMN_NAME, '+
  1074. 'p.CONSTRAINT_TYPE '+
  1075. 'FROM ALL_INDEXES i, ALL_IND_COLUMNS c,ALL_CONSTRAINTS p '+
  1076. 'WHERE '+
  1077. 'i.OWNER=c.INDEX_OWNER AND '+
  1078. 'i.INDEX_NAME=c.INDEX_NAME AND '+
  1079. 'p.INDEX_NAME(+)=i.INDEX_NAME AND '+
  1080. 'c.TABLE_NAME = ''' + TableName + ''' '+
  1081. 'ORDER by i.INDEX_NAME,c.COLUMN_POSITION');
  1082. open;
  1083. end;
  1084. while not qry.eof do with IndexDefs.AddIndexDef do
  1085. begin
  1086. Name := trim(qry.fields[0].asstring);
  1087. Fields := trim(qry.Fields[1].asstring);
  1088. If UpperCase(qry.fields[2].asstring)='P' then options := options + [ixPrimary];
  1089. If UpperCase(qry.fields[2].asstring)='U' then options := options + [ixUnique];
  1090. qry.next;
  1091. while (name = qry.fields[0].asstring) and (not qry.eof) do
  1092. begin
  1093. Fields := Fields + ';' + trim(qry.Fields[1].asstring);
  1094. qry.next;
  1095. end;
  1096. end;
  1097. qry.close;
  1098. qry.free;
  1099. end;
  1100. function TOracleConnection.GetSchemaInfoSQL(SchemaType: TSchemaType;
  1101. SchemaObjectName, SchemaPattern: string): string;
  1102. var
  1103. s : string;
  1104. begin
  1105. case SchemaType of
  1106. stTables : s := 'SELECT '+
  1107. '''' + DatabaseName + ''' as catalog_name, '+
  1108. 'sys_context( ''userenv'', ''current_schema'' ) as schema_name, '+
  1109. 'TABLE_NAME,'+
  1110. 'TABLE_TYPE '+
  1111. 'FROM USER_CATALOG ' +
  1112. 'WHERE '+
  1113. 'TABLE_TYPE<>''SEQUENCE'' '+
  1114. 'ORDER BY TABLE_NAME';
  1115. stSysTables : s := 'SELECT '+
  1116. '''' + DatabaseName + ''' as catalog_name, '+
  1117. 'OWNER as schema_name, '+
  1118. 'TABLE_NAME,'+
  1119. 'TABLE_TYPE '+
  1120. 'FROM ALL_CATALOG ' +
  1121. 'WHERE '+
  1122. 'TABLE_TYPE<>''SEQUENCE'' '+
  1123. 'ORDER BY TABLE_NAME';
  1124. stColumns : s := 'SELECT '+
  1125. 'OWNER as schema_name, '+
  1126. 'COLUMN_NAME, '+
  1127. 'DATA_TYPE as column_datatype, '+
  1128. 'CHARACTER_SET_NAME, '+
  1129. 'NULLABLE as column_nullable, '+
  1130. 'DATA_LENGTH as column_length, '+
  1131. 'DATA_PRECISION as column_precision, '+
  1132. 'DATA_SCALE as column_scale, '+
  1133. 'DATA_DEFAULT as column_default '+
  1134. 'FROM ALL_TAB_COLUMNS '+
  1135. 'WHERE Upper(TABLE_NAME) = '''+UpperCase(SchemaObjectName)+''' '+
  1136. 'ORDER BY COLUMN_NAME';
  1137. // Columns of tables, views and clusters accessible to user; hidden columns are filtered out.
  1138. stProcedures : s := 'SELECT '+
  1139. 'case when PROCEDURE_NAME is null then OBJECT_NAME ELSE OBJECT_NAME || ''.'' || PROCEDURE_NAME end AS procedure_name '+
  1140. 'FROM USER_PROCEDURES ';
  1141. else
  1142. DatabaseError(SMetadataUnavailable)
  1143. end; {case}
  1144. result := s;
  1145. end;
  1146. constructor TOracleConnection.Create(AOwner: TComponent);
  1147. begin
  1148. inherited Create(AOwner);
  1149. FConnOptions := FConnOptions + [sqEscapeRepeat];
  1150. FOciEnvironment := nil;
  1151. FOciError := nil;
  1152. FOciServer := nil;
  1153. FOciUserSession := nil;
  1154. FUserMem := nil;
  1155. end;
  1156. { TOracleConnectionDef }
  1157. class function TOracleConnectionDef.TypeName: String;
  1158. begin
  1159. Result:='Oracle';
  1160. end;
  1161. class function TOracleConnectionDef.ConnectionClass: TSQLConnectionClass;
  1162. begin
  1163. Result:=TOracleConnection;
  1164. end;
  1165. class function TOracleConnectionDef.Description: String;
  1166. begin
  1167. Result:='Connect to an Oracle database directly via the client library';
  1168. end;
  1169. class function TOracleConnectionDef.DefaultLibraryName: String;
  1170. begin
  1171. {$IfDef LinkDynamically}
  1172. Result:=ocilib;
  1173. {$else}
  1174. Result:='';
  1175. {$endif}
  1176. end;
  1177. class function TOracleConnectionDef.LoadFunction: TLibraryLoadFunction;
  1178. begin
  1179. {$IfDef LinkDynamically}
  1180. Result:=@InitialiseOCI;
  1181. {$else}
  1182. Result:=Nil;
  1183. {$endif}
  1184. end;
  1185. class function TOracleConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  1186. begin
  1187. {$IfDef LinkDynamically}
  1188. Result:=@ReleaseOCI;
  1189. {$else}
  1190. Result:=Nil;
  1191. {$endif}
  1192. end;
  1193. class function TOracleConnectionDef.LoadedLibraryName: string;
  1194. begin
  1195. {$IfDef LinkDynamically}
  1196. Result:=OCILoadedLibrary;
  1197. {$else}
  1198. Result:='';
  1199. {$endif}
  1200. end;
  1201. { TOracleTrans }
  1202. destructor TOracleTrans.Destroy();
  1203. begin
  1204. OCIHandleFree(FOciTrans,OCI_HTYPE_TRANS);
  1205. OCIHandleFree(FOciSvcCtx,OCI_HTYPE_SVCCTX);
  1206. inherited Destroy();
  1207. end;
  1208. initialization
  1209. RegisterConnection(TOracleConnectionDef);
  1210. finalization
  1211. UnRegisterConnection(TOracleConnectionDef);
  1212. end.