ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBBlob.pas
Revision: 35
Committed: Tue Jan 26 14:38:47 2016 UTC (8 years, 2 months ago) by tony
Content type: text/x-pascal
File size: 13870 byte(s)
Log Message:
Committing updates for Release R1-3-2

File Contents

# Content
1 {************************************************************************}
2 { }
3 { Borland Delphi Visual Component Library }
4 { InterBase Express core components }
5 { }
6 { Copyright (c) 1998-2000 Inprise Corporation }
7 { }
8 { InterBase Express is based in part on the product }
9 { Free IB Components, written by Gregory H. Deatz for }
10 { Hoagland, Longo, Moran, Dunst & Doukas Company. }
11 { Free IB Components is used under license. }
12 { }
13 { The contents of this file are subject to the InterBase }
14 { Public License Version 1.0 (the "License"); you may not }
15 { use this file except in compliance with the License. You }
16 { may obtain a copy of the License at http://www.Inprise.com/IPL.html }
17 { Software distributed under the License is distributed on }
18 { an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either }
19 { express or implied. See the License for the specific language }
20 { governing rights and limitations under the License. }
21 { The Original Code was created by InterBase Software Corporation }
22 { and its successors. }
23 { Portions created by Inprise Corporation are Copyright (C) Inprise }
24 { Corporation. All Rights Reserved. }
25 { Contributor(s): Jeff Overcash }
26 { }
27 { IBX For Lazarus (Firebird Express) }
28 { Contributor: Tony Whyman, MWA Software http://www.mwasoftware.co.uk }
29 { Portions created by MWA Software are copyright McCallum Whyman }
30 { Associates Ltd 2011 }
31 {************************************************************************}
32
33 unit IBBlob;
34
35 {$Mode Delphi}
36
37 interface
38
39 uses
40 SysUtils, Classes, IBHeader, IBErrorCodes, IBExternals, DB, IB, IBDatabase;
41
42
43 const
44 DefaultBlobSegmentSize = 16 * 1024;
45
46 type
47 { TIBBlobStream }
48 TIBBlobStream = class(TStream)
49 private
50 FBase: TIBBase;
51 FBlobID: TISC_QUAD;
52 FBlobMaxSegmentSize: Int64;
53 FBlobNumSegments: Int64;
54 FBlobSize: Int64;
55 FBlobType: Short; { 0 = segmented, 1 = streamed }
56 FBuffer: PChar;
57 FBlobInitialized: Boolean;
58 FHandle: TISC_BLOB_HANDLE;
59 FMode: TBlobStreamMode;
60 FModified: Boolean;
61 FPosition: Int64;
62 protected
63 procedure CloseBlob;
64 procedure CreateBlob;
65 procedure EnsureBlobInitialized;
66 procedure GetBlobInfo;
67 function GetDatabase: TIBDatabase;
68 function GetDBHandle: PISC_DB_HANDLE;
69 function GetTransaction: TIBTransaction;
70 function GetTRHandle: PISC_TR_HANDLE;
71 procedure OpenBlob;
72 procedure SetBlobID(Value: TISC_QUAD);
73 procedure SetDatabase(Value: TIBDatabase);
74 procedure SetMode(Value: TBlobStreamMode);
75 procedure SetTransaction(Value: TIBTransaction);
76 public
77 constructor Create;
78 destructor Destroy; override;
79 function Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
80 procedure CheckReadable;
81 procedure CheckWritable;
82 procedure Finalize;
83 procedure LoadFromFile(Filename: string);
84 procedure LoadFromStream(Stream: TStream);
85 function Read(var Buffer; Count: Longint): Longint; override;
86 procedure SaveToFile(Filename: string);
87 procedure SaveToStream(Stream: TStream);
88 function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
89 procedure SetSize(const NewSize: Int64); override;
90 procedure SetSize(NewSize: Longint); override;
91 procedure Truncate;
92 function Write(const Buffer; Count: Longint): Longint; override;
93 property Handle: TISC_BLOB_HANDLE read FHandle;
94 property BlobID: TISC_QUAD read FBlobID write SetBlobID;
95 property BlobMaxSegmentSize: Int64 read FBlobMaxSegmentSize;
96 property BlobNumSegments: Int64 read FBlobNumSegments;
97 property BlobSize: Int64 read FBlobSize;
98 property BlobType: Short read FBlobType;
99 property Database: TIBDatabase read GetDatabase write SetDatabase;
100 property DBHandle: PISC_DB_HANDLE read GetDBHandle;
101 property Mode: TBlobStreamMode read FMode write SetMode;
102 property Modified: Boolean read FModified;
103 property Transaction: TIBTransaction read GetTransaction write SetTransaction;
104 property TRHandle: PISC_TR_HANDLE read GetTRHandle;
105 end;
106
107 procedure GetBlobInfo(hBlobHandle: PISC_BLOB_HANDLE; var NumSegments: Int64; var MaxSegmentSize,
108 TotalSize: Int64; var BlobType: Short);
109 function GetBlobCharSetID(hDB_Handle: TISC_DB_HANDLE; hTR_Handle: TISC_TR_HANDLE;
110 tableName, columnName: PChar): short;
111 procedure ReadBlob(hBlobHandle: PISC_BLOB_HANDLE; Buffer: PChar; BlobSize: Int64);
112 procedure WriteBlob(hBlobHandle: PISC_BLOB_HANDLE; Buffer: PChar; BlobSize: Int64);
113
114 implementation
115
116 uses IBIntf;
117
118 procedure GetBlobInfo(hBlobHandle: PISC_BLOB_HANDLE; var NumSegments: Int64; var MaxSegmentSize,
119 TotalSize: Int64; var BlobType: Short);
120 var
121 items: array[0..3] of Char;
122 results: array[0..99] of Char;
123 i, item_length: Integer;
124 item: Integer;
125 begin
126 items[0] := Char(isc_info_blob_num_segments);
127 items[1] := Char(isc_info_blob_max_segment);
128 items[2] := Char(isc_info_blob_total_length);
129 items[3] := Char(isc_info_blob_type);
130
131 if isc_blob_info(StatusVector, hBlobHandle, 4, @items[0], SizeOf(results),
132 @results[0]) > 0 then
133 IBDatabaseError;
134
135 i := 0;
136 while (i < SizeOf(results)) and (results[i] <> Char(isc_info_end)) do
137 begin
138 item := Integer(results[i]); Inc(i);
139 item_length := isc_vax_integer(@results[i], 2); Inc(i, 2);
140 case item of
141 isc_info_blob_num_segments:
142 NumSegments := isc_portable_integer(@results[i], item_length);
143 isc_info_blob_max_segment:
144 MaxSegmentSize := isc_portable_integer(@results[i], item_length);
145 isc_info_blob_total_length:
146 TotalSize := isc_portable_integer(@results[i], item_length);
147 isc_info_blob_type:
148 BlobType := isc_portable_integer(@results[i], item_length);
149 end;
150 Inc(i, item_length);
151 end;
152 end;
153
154 function GetBlobCharSetID(hDB_Handle: TISC_DB_HANDLE;
155 hTR_Handle: TISC_TR_HANDLE; tableName, columnName: PChar): short;
156 var desc: TISC_BLOB_DESC;
157 uGlobal: array [0..31] of char;
158 begin
159 if isc_blob_lookup_desc(StatusVector,@hDB_Handle,@hTR_Handle,
160 tableName,columnName,@desc,@uGlobal) > 0 then
161 IBDatabaseError;
162
163 Result := desc.blob_desc_charset;
164 end;
165
166 procedure ReadBlob(hBlobHandle: PISC_BLOB_HANDLE; Buffer: PChar; BlobSize: Int64);
167 var
168 CurPos: Int64;
169 BytesRead, SegLen: UShort;
170 LocalBuffer: PChar;
171 begin
172 CurPos := 0;
173 LocalBuffer := Buffer;
174 SegLen := UShort(DefaultBlobSegmentSize);
175 while (CurPos < BlobSize) do
176 begin
177 if (CurPos + SegLen > BlobSize) then
178 SegLen := BlobSize - CurPos;
179 if not ((isc_get_segment(StatusVector, hBlobHandle, @BytesRead, SegLen,
180 LocalBuffer) = 0) or
181 (StatusVectorArray[1] = isc_segment)) then
182 IBDatabaseError;
183 Inc(LocalBuffer, BytesRead);
184 Inc(CurPos, BytesRead);
185 BytesRead := 0;
186 end;
187 end;
188
189 procedure WriteBlob(hBlobHandle: PISC_BLOB_HANDLE; Buffer: PChar;
190 BlobSize: Int64);
191 var
192 CurPos: Int64;
193 SegLen: Long;
194 begin
195 CurPos := 0;
196 SegLen := DefaultBlobSegmentSize;
197 while (CurPos < BlobSize) do
198 begin
199 if (CurPos + SegLen > BlobSize) then
200 SegLen := BlobSize - CurPos;
201 if isc_put_segment(StatusVector, hBlobHandle, SegLen,
202 PChar(@Buffer[CurPos])) > 0 then
203 IBDatabaseError;
204 Inc(CurPos, SegLen);
205 end;
206 end;
207
208
209 { TIBBlobStream }
210 constructor TIBBlobStream.Create;
211 begin
212 inherited Create;
213 FBase := TIBBase.Create(Self);
214 FBuffer := nil;
215 FBlobSize := 0;
216 end;
217
218 destructor TIBBlobStream.Destroy;
219 begin
220 if (FHandle <> nil) and
221 (Call(isc_close_blob(StatusVector, @FHandle), False) > 0) then
222 IBDataBaseError;
223 FBase.Free;
224 SetSize(0);
225 inherited Destroy;
226 end;
227
228 function TIBBlobStream.Call(ErrCode: ISC_STATUS; RaiseError: Boolean): ISC_STATUS;
229 begin
230 result := 0;
231 if Transaction <> nil then
232 result := Transaction.Call(ErrCode, RaiseError)
233 else if RaiseError and (ErrCode > 0) then
234 IBDataBaseError;
235 end;
236
237 procedure TIBBlobStream.CheckReadable;
238 begin
239 if FMode = bmWrite then IBError(ibxeBlobCannotBeRead, [nil]);
240 end;
241
242 procedure TIBBlobStream.CheckWritable;
243 begin
244 if FMode = bmRead then IBError(ibxeBlobCannotBeWritten, [nil]);
245 end;
246
247 procedure TIBBlobStream.CloseBlob;
248 begin
249 Finalize;
250 if (FHandle <> nil) and
251 (Call(isc_close_blob(StatusVector, @FHandle), False) > 0) then
252 IBDataBaseError;
253 end;
254
255 procedure TIBBlobStream.CreateBlob;
256 begin
257 CheckWritable;
258 FBlobID.gds_quad_high := 0;
259 FBlobID.gds_quad_low := 0;
260 Truncate;
261 end;
262
263 procedure TIBBlobStream.EnsureBlobInitialized;
264 begin
265 if not FBlobInitialized then
266 case FMode of
267 bmWrite:
268 CreateBlob;
269 bmReadWrite: begin
270 if (FBlobID.gds_quad_high = 0) and
271 (FBlobID.gds_quad_low = 0) then
272 CreateBlob
273 else
274 OpenBlob;
275 end;
276 else
277 OpenBlob;
278 end;
279 FBlobInitialized := True;
280 end;
281
282 procedure TIBBlobStream.Finalize;
283 begin
284 if (not FBlobInitialized) or (FMode = bmRead) or (not FModified) then
285 exit;
286 { need to start writing to a blob, create one }
287 Call(isc_create_blob2(StatusVector, DBHandle, TRHandle, @FHandle, @FBlobID,
288 0, nil), True);
289 IBBlob.WriteBlob(@FHandle, FBuffer, FBlobSize);
290 Call(isc_close_blob(StatusVector, @FHandle), True);
291 FModified := False;
292 end;
293
294 procedure TIBBlobStream.GetBlobInfo;
295 var
296 iBlobSize: Int64;
297 begin
298 IBBlob.GetBlobInfo(@FHandle, FBlobNumSegments, FBlobMaxSegmentSize,
299 iBlobSize, FBlobType);
300 SetSize(iBlobSize);
301 end;
302
303 function TIBBlobStream.GetDatabase: TIBDatabase;
304 begin
305 result := FBase.Database;
306 end;
307
308 function TIBBlobStream.GetDBHandle: PISC_DB_HANDLE;
309 begin
310 result := FBase.DBHandle;
311 end;
312
313 function TIBBlobStream.GetTransaction: TIBTransaction;
314 begin
315 result := FBase.Transaction;
316 end;
317
318 function TIBBlobStream.GetTRHandle: PISC_TR_HANDLE;
319 begin
320 result := FBase.TRHandle;
321 end;
322
323 procedure TIBBlobStream.LoadFromFile(Filename: string);
324 var
325 Stream: TStream;
326 begin
327 Stream := TFileStream.Create(FileName, fmOpenRead);
328 try
329 LoadFromStream(Stream);
330 finally
331 Stream.Free;
332 end;
333 end;
334
335 procedure TIBBlobStream.LoadFromStream(Stream: TStream);
336 begin
337 CheckWritable;
338 EnsureBlobInitialized;
339 Stream.Position := 0;
340 SetSize(Stream.Size);
341 if FBlobSize <> 0 then
342 Stream.ReadBuffer(FBuffer^, FBlobSize);
343 FModified := True;
344 end;
345
346 procedure TIBBlobStream.OpenBlob;
347 begin
348 CheckReadable;
349 Call(isc_open_blob2(StatusVector, DBHandle, TRHandle, @FHandle,
350 @FBlobID, 0, nil), True);
351 try
352 GetBlobInfo;
353 SetSize(FBlobSize);
354 IBBlob.ReadBlob(@FHandle, FBuffer, FBlobSize);
355 except
356 Call(isc_close_blob(StatusVector, @FHandle), False);
357 raise;
358 end;
359 Call(isc_close_blob(StatusVector, @FHandle), True);
360 end;
361
362 function TIBBlobStream.Read(var Buffer; Count: Longint): Longint;
363 begin
364 CheckReadable;
365 EnsureBlobInitialized;
366 if (Count <= 0) then
367 begin
368 result := 0;
369 exit;
370 end;
371 if (FPosition + Count > FBlobSize) then
372 result := FBlobSize - FPosition
373 else
374 result := Count;
375 Move(FBuffer[FPosition], Buffer, result);
376 Inc(FPosition, Result);
377 end;
378
379 procedure TIBBlobStream.SaveToFile(Filename: string);
380 var
381 Stream: TStream;
382 begin
383 Stream := TFileStream.Create(FileName, fmCreate);
384 try
385 SaveToStream(Stream);
386 finally
387 Stream.Free;
388 end;
389 end;
390
391 procedure TIBBlobStream.SaveToStream(Stream: TStream);
392 begin
393 CheckReadable;
394 EnsureBlobInitialized;
395 if FBlobSize <> 0 then
396 begin
397 Seek(0, soFromBeginning);
398 Stream.WriteBuffer(FBuffer^, FBlobSize);
399 end;
400 end;
401
402 function TIBBlobStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
403 begin
404 EnsureBlobInitialized;
405 case Origin of
406 soBeginning : FPosition := Offset;
407 soCurrent : Inc(FPosition, Offset);
408 soEnd : FPosition := FBlobSize + Offset;
409 end;
410 result := FPosition;
411 end;
412
413 procedure TIBBlobStream.SetBlobID(Value: TISC_QUAD);
414 begin
415 System.Move(Value, FBlobID, SizeOf(TISC_QUAD));
416 FBlobInitialized := False;
417 end;
418
419 procedure TIBBlobStream.SetDatabase(Value: TIBDatabase);
420 begin
421 FBase.Database := Value;
422 FBlobInitialized := False;
423 end;
424
425 procedure TIBBlobStream.SetMode(Value: TBlobStreamMode);
426 begin
427 FMode := Value;
428 FBlobInitialized := False;
429 end;
430
431 procedure TIBBlobStream.SetSize(const NewSize: Int64);
432 begin
433 if (NewSize <> FBlobSize) then
434 begin
435 ReallocMem(FBuffer, NewSize);
436 FBlobSize := NewSize;
437 if NewSize = 0 then
438 FBuffer := nil;
439 end;
440 end;
441
442 procedure TIBBlobStream.SetSize(NewSize: Longint);
443 begin
444 SetSize(Int64(NewSize));
445 end;
446
447 procedure TIBBlobStream.SetTransaction(Value: TIBTransaction);
448 begin
449 FBase.Transaction := Value;
450 FBlobInitialized := False;
451 end;
452
453 procedure TIBBlobStream.Truncate;
454 begin
455 SetSize(0);
456 end;
457
458 function TIBBlobStream.Write(const Buffer; Count: Longint): Longint;
459 begin
460 CheckWritable;
461 EnsureBlobInitialized;
462 result := Count;
463 if Count <= 0 then
464 exit;
465 if (FPosition + Count > FBlobSize) then
466 SetSize(FPosition + Count);
467 Move(Buffer, FBuffer[FPosition], Count);
468 Inc(FPosition, Count);
469 FModified := True;
470 end;
471
472 end.