1
|
|
|
// Copyright 2017, 2022 The Godror Authors |
2
|
|
|
// |
3
|
|
|
// |
4
|
|
|
// SPDX-License-Identifier: UPL-1.0 OR Apache-2.0 |
5
|
|
|
|
6
|
|
|
package godror |
7
|
|
|
|
8
|
|
|
/* |
9
|
|
|
#include "dpiImpl.h" |
10
|
|
|
*/ |
11
|
|
|
import "C" |
12
|
|
|
import ( |
13
|
|
|
"bufio" |
14
|
|
|
"errors" |
15
|
|
|
"fmt" |
16
|
|
|
"io" |
17
|
|
|
"runtime" |
18
|
|
|
"strings" |
19
|
|
|
"sync" |
20
|
|
|
"unicode/utf8" |
21
|
|
|
"unsafe" |
22
|
|
|
) |
23
|
|
|
|
24
|
|
|
// Lob is for reading/writing a LOB. |
25
|
|
|
type Lob struct { |
26
|
|
|
io.Reader |
27
|
|
|
IsClob bool |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
var _ = (io.Reader)((*Lob)(nil)) |
31
|
|
|
var _ = (io.ReaderAt)((*Lob)(nil)) |
32
|
|
|
|
33
|
|
|
// Hijack the underlying lob reader/writer, and |
34
|
|
|
// return a DirectLob for reading/writing the lob directly. |
35
|
|
|
// |
36
|
|
|
// After this, the Lob is unusable! |
37
|
|
|
func (lob *Lob) Hijack() (*DirectLob, error) { |
38
|
|
|
if lob == nil || lob.Reader == nil { |
39
|
|
|
return nil, errors.New("lob is nil") |
40
|
|
|
} |
41
|
|
|
lr, ok := lob.Reader.(*dpiLobReader) |
42
|
|
|
if !ok { |
43
|
|
|
return nil, fmt.Errorf("Lob.Reader is %T, not *dpiLobReader", lob.Reader) |
44
|
|
|
} |
45
|
|
|
lob.Reader = nil |
46
|
|
|
return &DirectLob{drv: lr.drv, dpiLob: lr.dpiLob}, nil |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
// WriteTo writes data to w until there's no more data to write or when an error occurs. |
50
|
|
|
// The return value n is the number of bytes written. Any error encountered during the write is also returned. |
51
|
|
|
// |
52
|
|
|
// Will use Lob.Reader.WriteTo, if Lob.Reader implements io.WriterTo. |
53
|
|
|
func (lob *Lob) WriteTo(w io.Writer) (n int64, err error) { |
54
|
|
|
if wt, ok := lob.Reader.(io.WriterTo); ok { |
55
|
|
|
return wt.WriteTo(w) |
56
|
|
|
} |
57
|
|
|
return io.CopyBuffer(w, lob.Reader, make([]byte, 1<<20)) |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
// NewBufferedReader returns a new bufio.Reader with the given size (or 1M if 0). |
61
|
|
|
func (lob *Lob) NewBufferedReader(size int) *bufio.Reader { |
62
|
|
|
if size <= 0 { |
63
|
|
|
size = 1 << 20 |
64
|
|
|
} |
65
|
|
|
return bufio.NewReaderSize(lob.Reader, size) |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
// Size exposes the underlying Reader's Size method, if it is supported. |
69
|
|
|
func (lob *Lob) Size() (int64, error) { |
70
|
|
|
if lr, ok := lob.Reader.(interface{ Size() (int64, error) }); ok { |
71
|
|
|
return lr.Size() |
72
|
|
|
} |
73
|
|
|
return 0, ErrNotSupported |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
// ReadAt exposes the underlying Reader's ReadAt method, if it is supported. |
77
|
|
|
func (lob *Lob) ReadAt(p []byte, off int64) (int, error) { |
78
|
|
|
if lr, ok := lob.Reader.(io.ReaderAt); ok { |
79
|
|
|
return lr.ReadAt(p, off) |
80
|
|
|
} |
81
|
|
|
return 0, ErrNotSupported |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
// Scan assigns a value from a database driver. |
85
|
|
|
// |
86
|
|
|
// The src value will be of one of the following types: |
87
|
|
|
// |
88
|
|
|
// int64 |
89
|
|
|
// float64 |
90
|
|
|
// bool |
91
|
|
|
// []byte |
92
|
|
|
// string |
93
|
|
|
// time.Time |
94
|
|
|
// nil - for NULL values |
95
|
|
|
// |
96
|
|
|
// An error should be returned if the value cannot be stored |
97
|
|
|
// without loss of information. |
98
|
|
|
func (dlr *dpiLobReader) Scan(src interface{}) error { |
99
|
|
|
b, ok := src.([]byte) |
100
|
|
|
if !ok { |
101
|
|
|
return fmt.Errorf("cannot convert LOB to %T", src) |
102
|
|
|
} |
103
|
|
|
_ = b |
104
|
|
|
return nil |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
var _ = io.ReadCloser((*dpiLobReader)(nil)) |
108
|
|
|
var _ = io.ReaderAt((*dpiLobReader)(nil)) |
109
|
|
|
|
110
|
|
|
type dpiLobReader struct { |
111
|
|
|
*drv |
112
|
|
|
dpiLob *C.dpiLob |
113
|
|
|
buf []byte |
114
|
|
|
offset, sizePlusOne C.uint64_t |
115
|
|
|
mu sync.Mutex |
116
|
|
|
chunkSize C.uint32_t |
117
|
|
|
bufR, bufW int |
118
|
|
|
finished bool |
119
|
|
|
IsClob bool |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
// WriteTo writes data to w until there's no more data to write or when an error occurs. |
123
|
|
|
// The return value n is the number of bytes written. Any error encountered during the write is also returned. |
124
|
|
|
// |
125
|
|
|
// Uses efficient, multiple-of-LOB-chunk-size buffered reads. |
126
|
|
|
func (dlr *dpiLobReader) WriteTo(w io.Writer) (n int64, err error) { |
127
|
|
|
size := dlr.ChunkSize() |
128
|
|
|
const minBufferSize = 1 << 20 |
129
|
|
|
if size <= 0 { |
130
|
|
|
size = minBufferSize |
131
|
|
|
} else { |
132
|
|
|
for size < minBufferSize/2 { // at most 1M |
133
|
|
|
size *= 2 |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
return io.CopyBuffer( |
137
|
|
|
w, |
138
|
|
|
// Mask WriteTo method |
139
|
|
|
io.Reader(struct { |
140
|
|
|
io.Reader |
141
|
|
|
}{dlr}), |
142
|
|
|
make([]byte, size)) |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
// ChunkSize returns the LOB's native chunk size. Reads/writes with a multiply of this size is the most performant. |
146
|
|
|
func (dlr *dpiLobReader) ChunkSize() int { |
147
|
|
|
dlr.mu.Lock() |
148
|
|
|
defer dlr.mu.Unlock() |
149
|
|
|
if dlr.chunkSize != 0 { |
150
|
|
|
return int(dlr.chunkSize) |
151
|
|
|
} |
152
|
|
|
if err := dlr.checkExec(func() C.int { |
153
|
|
|
return C.dpiLob_getChunkSize(dlr.dpiLob, &dlr.chunkSize) |
154
|
|
|
}); err != nil { |
155
|
|
|
dlr.chunkSize = 0 |
156
|
|
|
return -1 |
157
|
|
|
} |
158
|
|
|
return int(dlr.chunkSize) |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// Read from LOB. It does buffer the reading internally against short buffers (io.ReadAll). |
162
|
|
|
func (dlr *dpiLobReader) Read(p []byte) (int, error) { |
163
|
|
|
dlr.mu.Lock() |
164
|
|
|
defer dlr.mu.Unlock() |
165
|
|
|
logger := getLogger() |
166
|
|
|
if logger != nil { |
167
|
|
|
logger.Log("msg", "Read", "bufR", dlr.bufR, "bufW", dlr.bufW, "buf", cap(dlr.buf)) |
168
|
|
|
} |
169
|
|
|
if dlr.buf == nil { |
170
|
|
|
if dlr.chunkSize == 0 { |
171
|
|
|
if err := dlr.checkExec(func() C.int { |
172
|
|
|
return C.dpiLob_getChunkSize(dlr.dpiLob, &dlr.chunkSize) |
173
|
|
|
}); err != nil { |
174
|
|
|
return 0, fmt.Errorf("getChunkSize: %w", err) |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
// If the dest buffer is big enough, avoid copying. |
178
|
|
|
if ulen := C.uint64_t(len(p)); ulen >= C.uint64_t(dlr.chunkSize) || dlr.sizePlusOne != 0 && ulen+1 >= dlr.sizePlusOne { |
179
|
|
|
if logger != nil { |
180
|
|
|
logger.Log("msg", "direct read", "p", len(p), "chunkSize", dlr.chunkSize) |
181
|
|
|
} |
182
|
|
|
return dlr.read(p) |
183
|
|
|
} |
184
|
|
|
cs := int(dlr.chunkSize) |
185
|
|
|
dlr.buf = make([]byte, (maxI(len(p), 1<<20-cs)/cs+1)*cs) |
186
|
|
|
dlr.bufR, dlr.bufW = 0, 0 |
187
|
|
|
} else if dlr.bufW != 0 && cap(dlr.buf) != 0 { |
188
|
|
|
var n int |
189
|
|
|
if dlr.bufR < dlr.bufW { |
190
|
|
|
n = copy(p, dlr.buf[dlr.bufR:dlr.bufW]) |
191
|
|
|
dlr.bufR += n |
192
|
|
|
} |
193
|
|
|
if dlr.bufR == dlr.bufW { |
194
|
|
|
dlr.bufR, dlr.bufW = 0, 0 |
195
|
|
|
} |
196
|
|
|
if n != 0 { |
197
|
|
|
return n, nil |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
var err error |
201
|
|
|
// We only read into dlr.buf when it's empty, dlr.bufR == dlr.bufW == 0 |
202
|
|
|
dlr.bufW, err = dlr.read(dlr.buf) |
203
|
|
|
if logger != nil { |
204
|
|
|
logger.Log("msg", "dlr.read", "bufR", dlr.bufR, "bufW", dlr.bufW, "chunkSize", dlr.chunkSize, "error", err) |
205
|
|
|
} |
206
|
|
|
dlr.bufR = copy(p, dlr.buf[:dlr.bufW]) |
207
|
|
|
if err == io.EOF && dlr.bufW != dlr.bufR { |
208
|
|
|
err = nil |
209
|
|
|
} |
210
|
|
|
return dlr.bufR, err |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
var ErrCLOB = errors.New("CLOB is not supported") |
214
|
|
|
|
215
|
|
|
// Size returns the LOB's size. It returns ErrCLOB for CLOB, |
216
|
|
|
// (works only for BLOBs), as Oracle reports CLOB size in runes, not in bytes! |
217
|
|
|
func (dlr *dpiLobReader) Size() (int64, error) { |
218
|
|
|
dlr.mu.Lock() |
219
|
|
|
err := dlr.getSize() |
220
|
|
|
size := dlr.sizePlusOne - 1 |
221
|
|
|
isClob := dlr.IsClob |
222
|
|
|
dlr.mu.Unlock() |
223
|
|
|
if err == nil && isClob { |
224
|
|
|
err = ErrCLOB |
225
|
|
|
} |
226
|
|
|
return int64(size), err |
227
|
|
|
} |
228
|
|
|
func (dlr *dpiLobReader) getSize() error { |
229
|
|
|
if dlr.sizePlusOne != 0 { |
230
|
|
|
return nil |
231
|
|
|
} |
232
|
|
|
var err error |
233
|
|
|
runtime.LockOSThread() |
234
|
|
|
if err = dlr.checkExecNoLOT(func() C.int { |
235
|
|
|
return C.dpiLob_getSize(dlr.dpiLob, &dlr.sizePlusOne) |
236
|
|
|
}); err != nil { |
237
|
|
|
err = fmt.Errorf("getSize: %w", err) |
238
|
|
|
C.dpiLob_close(dlr.dpiLob) |
239
|
|
|
dlr.dpiLob = nil |
240
|
|
|
} |
241
|
|
|
runtime.UnlockOSThread() |
242
|
|
|
dlr.sizePlusOne++ |
243
|
|
|
return err |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
// read does the real LOB reading. |
247
|
|
|
func (dlr *dpiLobReader) read(p []byte) (int, error) { |
248
|
|
|
if dlr == nil { |
249
|
|
|
return 0, errors.New("read on nil dpiLobReader") |
250
|
|
|
} |
251
|
|
|
logger := getLogger() |
252
|
|
|
if logger != nil { |
253
|
|
|
logger.Log("msg", "LOB Read", "dlr", fmt.Sprintf("%p", dlr), "offset", dlr.offset, "size", dlr.sizePlusOne, "finished", dlr.finished, "clob", dlr.IsClob) |
254
|
|
|
} |
255
|
|
|
if dlr.finished || dlr.dpiLob == nil { |
256
|
|
|
return 0, io.EOF |
257
|
|
|
} |
258
|
|
|
if len(p) < 1 || dlr.IsClob && len(p) < 4 { |
259
|
|
|
return 0, io.ErrShortBuffer |
260
|
|
|
} |
261
|
|
|
runtime.LockOSThread() |
262
|
|
|
defer runtime.UnlockOSThread() |
263
|
|
|
// For CLOB, sizePlusOne and offset counts the CHARACTERS! |
264
|
|
|
// See https://oracle.github.io/odpi/doc/public_functions/dpiLob.html dpiLob_readBytes |
265
|
|
|
if dlr.sizePlusOne == 0 { // first read |
266
|
|
|
// never read size before |
267
|
|
|
if err := dlr.getSize(); err != nil { |
268
|
|
|
var coder interface{ Code() int } |
269
|
|
|
if errors.As(err, &coder) && coder.Code() == 22922 || strings.Contains(err.Error(), "invalid dpiLob handle") { |
270
|
|
|
return 0, io.EOF |
271
|
|
|
} |
272
|
|
|
return 0, err |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
var lobType C.dpiOracleTypeNum |
276
|
|
|
if err := dlr.checkExecNoLOT(func() C.int { |
277
|
|
|
return C.dpiLob_getType(dlr.dpiLob, &lobType) |
278
|
|
|
}); err == nil && |
279
|
|
|
(2017 <= lobType && lobType <= 2019) { |
280
|
|
|
dlr.IsClob = lobType == 2017 || lobType == 2018 // CLOB and NCLOB |
281
|
|
|
} |
282
|
|
|
} |
283
|
|
|
n := C.uint64_t(len(p)) |
284
|
|
|
amount := n |
285
|
|
|
if dlr.IsClob { |
286
|
|
|
amount /= 4 // dpiLob_readBytes' amount is the number of CHARACTERS for CLOBs. |
287
|
|
|
} |
288
|
|
|
// fmt.Printf("%p.Read offset=%d sizePlusOne=%d n=%d\n", dlr.dpiLob, dlr.offset, dlr.sizePlusOne, n) |
289
|
|
|
if logger != nil { |
290
|
|
|
logger.Log("msg", "Read", "offset", dlr.offset, "sizePlusOne", dlr.sizePlusOne, "n", n, "amount", amount) |
291
|
|
|
} |
292
|
|
|
if !dlr.IsClob && dlr.offset+1 >= dlr.sizePlusOne { |
293
|
|
|
if logger != nil { |
294
|
|
|
logger.Log("msg", "LOB reached end", "offset", dlr.offset, "size", dlr.sizePlusOne) |
295
|
|
|
} |
296
|
|
|
return 0, io.EOF |
297
|
|
|
} |
298
|
|
|
if err := dlr.drv.checkExecNoLOT(func() C.int { |
299
|
|
|
return C.dpiLob_readBytes(dlr.dpiLob, dlr.offset+1, amount, (*C.char)(unsafe.Pointer(&p[0])), &n) |
300
|
|
|
}); err != nil { |
301
|
|
|
if logger != nil { |
302
|
|
|
logger.Log("msg", "readBytes", "error", err) |
303
|
|
|
} |
304
|
|
|
C.dpiLob_close(dlr.dpiLob) |
305
|
|
|
dlr.dpiLob = nil |
306
|
|
|
if logger != nil { |
307
|
|
|
logger.Log("msg", "LOB read", "error", err) |
308
|
|
|
} |
309
|
|
|
var codeErr interface{ Code() int } |
310
|
|
|
if dlr.finished = errors.As(err, &codeErr) && codeErr.Code() == 1403; dlr.finished { |
311
|
|
|
dlr.offset += n |
312
|
|
|
return int(n), io.EOF |
313
|
|
|
} |
314
|
|
|
return int(n), fmt.Errorf("dpiLob_readbytes(lob=%p offset=%d n=%d): %w", dlr.dpiLob, dlr.offset, len(p), err) |
315
|
|
|
} |
316
|
|
|
if logger != nil { |
317
|
|
|
logger.Log("msg", "read", "n", n) |
318
|
|
|
} |
319
|
|
|
if dlr.IsClob { |
320
|
|
|
dlr.offset += C.uint64_t(utf8.RuneCount(p[:n])) |
321
|
|
|
} else { |
322
|
|
|
dlr.offset += n |
323
|
|
|
} |
324
|
|
|
var err error |
325
|
|
|
if amount != 0 && n == 0 || !dlr.IsClob && dlr.offset+1 >= dlr.sizePlusOne { |
326
|
|
|
C.dpiLob_close(dlr.dpiLob) |
327
|
|
|
dlr.dpiLob = nil |
328
|
|
|
dlr.finished = true |
329
|
|
|
err = io.EOF |
330
|
|
|
} |
331
|
|
|
if logger != nil { |
332
|
|
|
logger.Log("msg", "LOB", "n", n, "offset", dlr.offset, "size", dlr.sizePlusOne, "finished", dlr.finished, "clob", dlr.IsClob, "error", err) |
333
|
|
|
} |
334
|
|
|
return int(n), err |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
// ReadAt reads at the specified offset (in bytes). |
338
|
|
|
// Works only for BLOBs! |
339
|
|
|
func (dlr *dpiLobReader) ReadAt(p []byte, off int64) (int, error) { |
340
|
|
|
dlr.mu.Lock() |
341
|
|
|
defer dlr.mu.Unlock() |
342
|
|
|
if dlr.IsClob { |
343
|
|
|
return 0, ErrCLOB |
344
|
|
|
} |
345
|
|
|
n := C.uint64_t(len(p)) |
346
|
|
|
err := dlr.checkExec(func() C.int { |
347
|
|
|
return C.dpiLob_readBytes(dlr.dpiLob, C.uint64_t(off+1), n, (*C.char)(unsafe.Pointer(&p[0])), &n) |
348
|
|
|
}) |
349
|
|
|
if err != nil { |
350
|
|
|
err = fmt.Errorf("readBytes at %d for %d: %w", off, n, dlr.getError()) |
351
|
|
|
} |
352
|
|
|
return int(n), err |
353
|
|
|
} |
354
|
|
|
func (dlr *dpiLobReader) Close() error { |
355
|
|
|
if dlr == nil || dlr.dpiLob == nil { |
356
|
|
|
return nil |
357
|
|
|
} |
358
|
|
|
lob := dlr.dpiLob |
359
|
|
|
dlr.dpiLob = nil |
360
|
|
|
return closeLob(dlr, lob) |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
type dpiLobWriter struct { |
364
|
|
|
*drv |
365
|
|
|
dpiLob *C.dpiLob |
366
|
|
|
offset C.uint64_t |
367
|
|
|
opened bool |
368
|
|
|
isClob bool |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
func (dlw *dpiLobWriter) Write(p []byte) (int, error) { |
372
|
|
|
runtime.LockOSThread() |
373
|
|
|
defer runtime.UnlockOSThread() |
374
|
|
|
|
375
|
|
|
lob := dlw.dpiLob |
376
|
|
|
if !dlw.opened { |
377
|
|
|
// fmt.Printf("open %p\n", lob) |
378
|
|
|
if err := dlw.drv.checkExecNoLOT(func() C.int { |
379
|
|
|
return C.dpiLob_openResource(lob) |
380
|
|
|
}); err != nil { |
381
|
|
|
return 0, fmt.Errorf("openResources(%p): %w", lob, err) |
382
|
|
|
} |
383
|
|
|
dlw.opened = true |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
n := C.uint64_t(len(p)) |
387
|
|
|
if err := dlw.drv.checkExecNoLOT(func() C.int { |
388
|
|
|
return C.dpiLob_writeBytes(lob, dlw.offset+1, (*C.char)(unsafe.Pointer(&p[0])), n) |
389
|
|
|
}); err != nil { |
390
|
|
|
err = fmt.Errorf("writeBytes(%p, offset=%d, data=%d): %w", lob, dlw.offset, n, err) |
391
|
|
|
dlw.dpiLob = nil |
392
|
|
|
closeLob(dlw, lob) |
393
|
|
|
return 0, err |
394
|
|
|
} |
395
|
|
|
// fmt.Printf("written %q into %p@%d\n", p[:n], lob, dlw.offset) |
396
|
|
|
dlw.offset += n |
397
|
|
|
|
398
|
|
|
return int(n), nil |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
func (dlw *dpiLobWriter) Close() error { |
402
|
|
|
if dlw == nil || dlw.dpiLob == nil { |
403
|
|
|
return nil |
404
|
|
|
} |
405
|
|
|
lob := dlw.dpiLob |
406
|
|
|
dlw.dpiLob = nil |
407
|
|
|
return closeLob(dlw, lob) |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
func closeLob(d interface{ getError() error }, lob *C.dpiLob) error { |
411
|
|
|
if lob == nil { |
412
|
|
|
return nil |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
runtime.LockOSThread() |
416
|
|
|
defer runtime.UnlockOSThread() |
417
|
|
|
|
418
|
|
|
var isOpen C.int |
419
|
|
|
if C.dpiLob_getIsResourceOpen(lob, &isOpen) != C.DPI_FAILURE && isOpen == 1 { |
420
|
|
|
if C.dpiLob_closeResource(lob) == C.DPI_FAILURE { |
421
|
|
|
if err := d.getError(); err != nil { |
422
|
|
|
var codeErr interface{ Code() int } |
423
|
|
|
if errors.As(err, &codeErr) && codeErr.Code() != 22289 { // cannot perform %s operation on an unopened file or LOB |
424
|
|
|
return fmt.Errorf("closeResource(%p): %w", lob, err) |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
} |
429
|
|
|
C.dpiLob_release(lob) |
430
|
|
|
return nil |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
// DirectLob holds a Lob and allows direct (Read/WriteAt, not streaming Read/Write) operations on it. |
434
|
|
|
type DirectLob struct { |
435
|
|
|
drv *drv |
436
|
|
|
dpiLob *C.dpiLob |
437
|
|
|
opened, isClob bool |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
var _ = io.ReaderAt((*DirectLob)(nil)) |
441
|
|
|
var _ = io.WriterAt((*DirectLob)(nil)) |
442
|
|
|
|
443
|
|
|
// NewTempLob returns a temporary LOB as DirectLob. |
444
|
|
|
func (c *conn) NewTempLob(isClob bool) (*DirectLob, error) { |
445
|
|
|
typ := C.uint(C.DPI_ORACLE_TYPE_BLOB) |
446
|
|
|
if isClob { |
447
|
|
|
typ = C.DPI_ORACLE_TYPE_CLOB |
448
|
|
|
} |
449
|
|
|
lob := DirectLob{drv: c.drv, isClob: isClob} |
450
|
|
|
if err := c.checkExec(func() C.int { return C.dpiConn_newTempLob(c.dpiConn, typ, &lob.dpiLob) }); err != nil { |
451
|
|
|
return nil, fmt.Errorf("newTempLob: %w", err) |
452
|
|
|
} |
453
|
|
|
return &lob, nil |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
// Close the Lob. |
457
|
|
|
func (dl *DirectLob) Close() error { |
458
|
|
|
if !dl.opened { |
459
|
|
|
return nil |
460
|
|
|
} |
461
|
|
|
lob := dl.dpiLob |
462
|
|
|
dl.opened, dl.dpiLob = false, nil |
463
|
|
|
return closeLob(dl.drv, lob) |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
// Size returns the size of the LOB. |
467
|
|
|
// |
468
|
|
|
// WARNING: for historical reasons, Oracle stores CLOBs and NCLOBs using the UTF-16 encoding, |
469
|
|
|
// regardless of what encoding is otherwise in use by the database. |
470
|
|
|
// The number of characters, however, is defined by the number of UCS-2 codepoints. |
471
|
|
|
// For this reason, if a character requires more than one UCS-2 codepoint, |
472
|
|
|
// the size returned will be inaccurate and care must be taken to account for the difference! |
473
|
|
|
func (dl *DirectLob) Size() (int64, error) { |
474
|
|
|
var n C.uint64_t |
475
|
|
|
if dl.dpiLob == nil { |
476
|
|
|
return 0, nil |
477
|
|
|
} |
478
|
|
|
if err := dl.drv.checkExec(func() C.int { |
479
|
|
|
return C.dpiLob_getSize(dl.dpiLob, &n) |
480
|
|
|
}); err != nil { |
481
|
|
|
var coder interface{ Code() int } |
482
|
|
|
if errors.As(err, &coder) && coder.Code() == 22922 || strings.Contains(err.Error(), "invalid dpiLob handle") { |
483
|
|
|
return 0, nil |
484
|
|
|
} |
485
|
|
|
return int64(n), fmt.Errorf("getSize: %w", err) |
486
|
|
|
} |
487
|
|
|
return int64(n), nil |
488
|
|
|
} |
489
|
|
|
|
490
|
|
|
// Trim the LOB to the given size. |
491
|
|
|
func (dl *DirectLob) Trim(size int64) error { |
492
|
|
|
if err := dl.drv.checkExec(func() C.int { |
493
|
|
|
return C.dpiLob_trim(dl.dpiLob, C.uint64_t(size)) |
494
|
|
|
}); err != nil { |
495
|
|
|
return fmt.Errorf("trim: %w", err) |
496
|
|
|
} |
497
|
|
|
return nil |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
// Set the contents of the LOB to the given byte slice. |
501
|
|
|
// The LOB is cleared first. |
502
|
|
|
func (dl *DirectLob) Set(p []byte) error { |
503
|
|
|
if err := dl.drv.checkExec(func() C.int { |
504
|
|
|
return C.dpiLob_setFromBytes(dl.dpiLob, (*C.char)(unsafe.Pointer(&p[0])), C.uint64_t(len(p))) |
505
|
|
|
}); err != nil { |
506
|
|
|
return fmt.Errorf("setFromBytes: %w", err) |
507
|
|
|
} |
508
|
|
|
return nil |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
// ReadAt reads at most len(p) bytes into p at offset. |
512
|
|
|
// |
513
|
|
|
// CLOB's offset must be in amount of characters, and does not work reliably! |
514
|
|
|
// |
515
|
|
|
// WARNING: for historical reasons, Oracle stores CLOBs and NCLOBs using the UTF-16 encoding, |
516
|
|
|
// regardless of what encoding is otherwise in use by the database. |
517
|
|
|
// The number of characters, however, is defined by the number of UCS-2 codepoints. |
518
|
|
|
// For this reason, if a character requires more than one UCS-2 codepoint, |
519
|
|
|
// the size returned will be inaccurate and care must be taken to account for the difference! |
520
|
|
|
func (dl *DirectLob) ReadAt(p []byte, offset int64) (int, error) { |
521
|
|
|
n := C.uint64_t(len(p)) |
522
|
|
|
if dl.dpiLob == nil { |
523
|
|
|
return 0, io.EOF |
524
|
|
|
} |
525
|
|
|
amount := n |
526
|
|
|
if dl.isClob { |
527
|
|
|
amount /= 4 |
528
|
|
|
if amount == 0 { |
529
|
|
|
return 0, io.ErrShortBuffer |
530
|
|
|
} |
531
|
|
|
} |
532
|
|
|
if err := dl.drv.checkExec(func() C.int { |
533
|
|
|
return C.dpiLob_readBytes(dl.dpiLob, C.uint64_t(offset)+1, n, (*C.char)(unsafe.Pointer(&p[0])), &n) |
534
|
|
|
}); err != nil { |
535
|
|
|
return int(n), fmt.Errorf("readBytes: %w", err) |
536
|
|
|
} |
537
|
|
|
return int(n), nil |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
// WriteAt writes p starting at offset. |
541
|
|
|
func (dl *DirectLob) WriteAt(p []byte, offset int64) (int, error) { |
542
|
|
|
runtime.LockOSThread() |
543
|
|
|
defer runtime.UnlockOSThread() |
544
|
|
|
|
545
|
|
|
if !dl.opened { |
546
|
|
|
// fmt.Printf("open %p\n", lob) |
547
|
|
|
if err := dl.drv.checkExecNoLOT(func() C.int { |
548
|
|
|
return C.dpiLob_openResource(dl.dpiLob) |
549
|
|
|
}); err != nil { |
550
|
|
|
return 0, fmt.Errorf("openResources(%p): %w", dl.dpiLob, err) |
551
|
|
|
} |
552
|
|
|
dl.opened = true |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
n := C.uint64_t(len(p)) |
556
|
|
|
if err := dl.drv.checkExecNoLOT(func() C.int { |
557
|
|
|
return C.dpiLob_writeBytes(dl.dpiLob, C.uint64_t(offset)+1, (*C.char)(unsafe.Pointer(&p[0])), n) |
558
|
|
|
}); err != nil { |
559
|
|
|
return int(n), fmt.Errorf("writeBytes: %w", err) |
560
|
|
|
} |
561
|
|
|
return int(n), nil |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
// GetFileName Return directory alias and file name for a BFILE type LOB. |
565
|
|
|
func (dl *DirectLob) GetFileName() (dir, file string, err error) { |
566
|
|
|
var directoryAliasLength, fileNameLength C.uint32_t |
567
|
|
|
var directoryAlias, fileName *C.char |
568
|
|
|
if err := dl.drv.checkExec(func() C.int { |
569
|
|
|
return C.dpiLob_getDirectoryAndFileName(dl.dpiLob, |
570
|
|
|
&directoryAlias, |
571
|
|
|
&directoryAliasLength, |
572
|
|
|
&fileName, |
573
|
|
|
&fileNameLength, |
574
|
|
|
) |
575
|
|
|
}); err != nil { |
576
|
|
|
return dir, file, fmt.Errorf("GetFileName: %w", err) |
577
|
|
|
} |
578
|
|
|
dir = C.GoStringN(directoryAlias, C.int(directoryAliasLength)) |
579
|
|
|
file = C.GoStringN(fileName, C.int(fileNameLength)) |
580
|
|
|
return dir, file, nil |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
func maxI(a, b int) int { |
584
|
|
|
if a < b { |
585
|
|
|
return b |
586
|
|
|
} |
587
|
|
|
return a |
588
|
|
|
} |
589
|
|
|
|