1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
|
/** Large Objects interface.
*
* Allows access to large objects directly, or through I/O streams.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead.
*
* Copyright (c) 2000-2019, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this mistake,
* or contact the author.
*/
#ifndef PQXX_H_LARGEOBJECT
#define PQXX_H_LARGEOBJECT
#include "pqxx/compiler-public.hxx"
#include "pqxx/compiler-internal-pre.hxx"
#include <streambuf>
#include "pqxx/dbtransaction.hxx"
namespace pqxx
{
/// Identity of a large object
/** This class encapsulates the identity of a large object. To access the
* contents of the object, create a largeobjectaccess, a largeobject_streambuf,
* or an ilostream, an olostream or a lostream around the largeobject.
*
* A largeobject must be accessed only from within a backend transaction, but
* the object's identity remains valid as long as the object exists.
*/
class PQXX_LIBEXPORT largeobject
{
public:
using size_type = large_object_size_type;
/// Refer to a nonexistent large object (similar to what a null pointer does)
largeobject() noexcept =default; //[t48]
/// Create new large object
/** @param T Backend transaction in which the object is to be created
*/
explicit largeobject(dbtransaction &T); //[t48]
/// Wrap object with given oid
/** Convert combination of a transaction and object identifier into a
* large object identity. Does not affect the database.
* @param O Object identifier for the given object
*/
explicit largeobject(oid O) noexcept : m_id{O} {} //[t48]
/// Import large object from a local file
/** Creates a large object containing the data found in the given file.
* @param T Backend transaction in which the large object is to be created
* @param File A filename on the client program's filesystem
*/
largeobject(dbtransaction &T, const std::string &File); //[t53]
/// Take identity of an opened large object
/** Copy identity of already opened large object. Note that this may be done
* as an implicit conversion.
* @param O Already opened large object to copy identity from
*/
largeobject(const largeobjectaccess &O) noexcept; //[t50]
/// Object identifier
/** The number returned by this function identifies the large object in the
* database we're connected to (or oid_none is returned if we refer to the
* null object).
*/
oid id() const noexcept { return m_id; } //[t48]
/**
* @name Identity comparisons
*
* These operators compare the object identifiers of large objects. This has
* nothing to do with the objects' actual contents; use them only for keeping
* track of containers of references to large objects and such.
*/
//@{
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator==(const largeobject &other) const //[t51]
{ return m_id == other.m_id; }
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator!=(const largeobject &other) const //[t51]
{ return m_id != other.m_id; }
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator<=(const largeobject &other) const //[t51]
{ return m_id <= other.m_id; }
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator>=(const largeobject &other) const //[t51]
{ return m_id >= other.m_id; }
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator<(const largeobject &other) const //[t51]
{ return m_id < other.m_id; }
/// Compare object identities
/** @warning Only valid between large objects in the same database. */
bool operator>(const largeobject &other) const //[t51]
{ return m_id > other.m_id; }
//@}
/// Export large object's contents to a local file
/** Writes the data stored in the large object to the given file.
* @param T Transaction in which the object is to be accessed
* @param File A filename on the client's filesystem
*/
void to_file(dbtransaction &T, const std::string &File) const; //[t52]
/// Delete large object from database
/** Unlike its low-level equivalent cunlink, this will throw an exception if
* deletion fails.
* @param T Transaction in which the object is to be deleted
*/
void remove(dbtransaction &T) const; //[t48]
protected:
PQXX_PURE static internal::pq::PGconn *raw_connection(
const dbtransaction &T);
PQXX_PRIVATE std::string reason(const connection_base &, int err) const;
private:
oid m_id = oid_none;
};
// TODO: New hierarchy with separate read / write / mixed-mode access
/// Accessor for large object's contents.
class PQXX_LIBEXPORT largeobjectaccess : private largeobject
{
public:
using largeobject::size_type;
using off_type = long;
using pos_type = size_type;
/// Open mode: @c in, @c out (can be combined with the "or" operator)
/** According to the C++ standard, these should be in @c std::ios_base. We
* take them from @c std::ios instead, which should be safe because it
* inherits the same definition, to accommodate gcc 2.95 & 2.96.
*/
using openmode = std::ios::openmode;
/// Seek direction: @c beg, @c cur, @c end
/** According to the C++ standard, these should be in @c std::ios_base. We
* take them from @c std::ios instead, which should be safe because it
* inherits the same definition, to accommodate gcc 2.95 & 2.96.
*/
using seekdir = std::ios::seekdir;
/// Create new large object and open it
/**
* @param T Backend transaction in which the object is to be created
* @param mode Access mode, defaults to ios_base::in | ios_base::out
*/
explicit largeobjectaccess( //[t51]
dbtransaction &T,
openmode mode=std::ios::in|std::ios::out);
/// Open large object with given oid
/** Convert combination of a transaction and object identifier into a
* large object identity. Does not affect the database.
* @param T Transaction in which the object is to be accessed
* @param O Object identifier for the given object
* @param mode Access mode, defaults to ios_base::in | ios_base::out
*/
largeobjectaccess( //[t52]
dbtransaction &T,
oid O,
openmode mode=std::ios::in|std::ios::out);
/// Open given large object
/** Open a large object with the given identity for reading and/or writing
* @param T Transaction in which the object is to be accessed
* @param O Identity for the large object to be accessed
* @param mode Access mode, defaults to ios_base::in | ios_base::out
*/
largeobjectaccess( //[t50]
dbtransaction &T,
largeobject O,
openmode mode=std::ios::in|std::ios::out);
/// Import large object from a local file and open it
/** Creates a large object containing the data found in the given file.
* @param T Backend transaction in which the large object is to be created
* @param File A filename on the client program's filesystem
* @param mode Access mode, defaults to ios_base::in | ios_base::out
*/
largeobjectaccess( //[t55]
dbtransaction &T,
const std::string &File,
openmode mode=std::ios::in|std::ios::out);
~largeobjectaccess() noexcept { close(); }
/// Object identifier
/** The number returned by this function uniquely identifies the large object
* in the context of the database we're connected to.
*/
using largeobject::id;
/// Export large object's contents to a local file
/** Writes the data stored in the large object to the given file.
* @param File A filename on the client's filesystem
*/
void to_file(const std::string &File) const //[t54]
{ largeobject::to_file(m_trans, File); }
using largeobject::to_file;
/**
* @name High-level access to object contents
*/
//@{
/// Write data to large object
/** If not all bytes could be written, an exception is thrown.
* @param Buf Data to write
* @param Len Number of bytes from Buf to write
*/
void write(const char Buf[], size_type Len); //[t51]
/// Write string to large object
/** If not all bytes could be written, an exception is thrown.
* @param Buf Data to write; no terminating zero is written
*/
void write(const std::string &Buf) //[t50]
{ write(Buf.c_str(), static_cast<size_type>(Buf.size())); }
/// Read data from large object
/** Throws an exception if an error occurs while reading.
* @param Buf Location to store the read data in
* @param Len Number of bytes to try and read
* @return Number of bytes read, which may be less than the number requested
* if the end of the large object is reached
*/
size_type read(char Buf[], size_type Len); //[t50]
/// Seek in large object's data stream
/** Throws an exception if an error occurs.
* @return The new position in the large object
*/
size_type seek(size_type dest, seekdir dir); //[t51]
/// Report current position in large object's data stream
/** Throws an exception if an error occurs.
* @return The current position in the large object
*/
size_type tell() const; //[t50]
//@}
/**
* @name Low-level access to object contents
*
* These functions provide a more "C-like" access interface, returning special
* values instead of throwing exceptions on error. These functions are
* generally best avoided in favour of the high-level access functions, which
* behave more like C++ functions should.
*/
//@{
/// Seek in large object's data stream
/** Does not throw exception in case of error; inspect return value and
* @c errno instead.
* @param dest Offset to go to
* @param dir Origin to which dest is relative: ios_base::beg (from beginning
* of the object), ios_base::cur (from current access position), or
* ios_base;:end (from end of object)
* @return New position in large object, or -1 if an error occurred.
*/
pos_type cseek(off_type dest, seekdir dir) noexcept; //[t50]
/// Write to large object's data stream
/** Does not throw exception in case of error; inspect return value and
* @c errno instead.
* @param Buf Data to write
* @param Len Number of bytes to write
* @return Number of bytes actually written, or -1 if an error occurred.
*/
off_type cwrite(const char Buf[], size_type Len) noexcept; //[t50]
/// Read from large object's data stream
/** Does not throw exception in case of error; inspect return value and
* @c errno instead.
* @param Buf Area where incoming bytes should be stored
* @param Len Number of bytes to read
* @return Number of bytes actually read, or -1 if an error occurred.
*/
off_type cread(char Buf[], size_type Len) noexcept; //[t50]
/// Report current position in large object's data stream
/** Does not throw exception in case of error; inspect return value and
* @c errno instead.
* @return Current position in large object, of -1 if an error occurred.
*/
pos_type ctell() const noexcept; //[t50]
//@}
/**
* @name Error/warning output
*/
//@{
/// Issue message to transaction's notice processor
void process_notice(const std::string &) noexcept; //[t50]
//@}
using largeobject::remove;
using largeobject::operator==;
using largeobject::operator!=;
using largeobject::operator<;
using largeobject::operator<=;
using largeobject::operator>;
using largeobject::operator>=;
private:
PQXX_PRIVATE std::string reason(int err) const;
internal::pq::PGconn *raw_connection() const
{ return largeobject::raw_connection(m_trans); }
PQXX_PRIVATE void open(openmode mode);
void close() noexcept;
dbtransaction &m_trans;
int m_fd = -1;
largeobjectaccess() =delete;
largeobjectaccess(const largeobjectaccess &) =delete;
largeobjectaccess operator=(const largeobjectaccess &) =delete;
};
/// Streambuf to use large objects in standard I/O streams
/** The standard streambuf classes provide uniform access to data storage such
* as files or string buffers, so they can be accessed using standard input or
* output streams. This streambuf implementation provides similar access to
* large objects, so they can be read and written using the same stream classes.
*
* @warning This class may not work properly in compiler environments that don't
* fully support Standard-compliant streambufs, such as g++ 2.95 or older.
*/
template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
class largeobject_streambuf :
public std::basic_streambuf<CHAR, TRAITS>
{
using size_type = long;
public:
using char_type = CHAR;
using traits_type = TRAITS;
using int_type = typename traits_type::int_type;
using pos_type = typename traits_type::pos_type;
using off_type = typename traits_type::off_type;
using openmode = largeobjectaccess::openmode;
using seekdir = largeobjectaccess::seekdir;
largeobject_streambuf( //[t48]
dbtransaction &T,
largeobject O,
openmode mode=std::ios::in|std::ios::out,
size_type BufSize=512) :
m_bufsize{BufSize},
m_obj{T, O, mode},
m_g{nullptr},
m_p{nullptr}
{ initialize(mode); }
largeobject_streambuf( //[t48]
dbtransaction &T,
oid O,
openmode mode=std::ios::in|std::ios::out,
size_type BufSize=512) :
m_bufsize{BufSize},
m_obj{T, O, mode},
m_g{nullptr},
m_p{nullptr}
{ initialize(mode); }
virtual ~largeobject_streambuf() noexcept
{ delete [] m_p; delete [] m_g; }
/// For use by large object stream classes
void process_notice(const std::string &s) { m_obj.process_notice(s); }
protected:
virtual int sync() override
{
// setg() sets eback, gptr, egptr
this->setg(this->eback(), this->eback(), this->egptr());
return overflow(EoF());
}
virtual pos_type seekoff(
off_type offset,
seekdir dir,
openmode)
override
{
return AdjustEOF(m_obj.cseek(largeobjectaccess::off_type(offset), dir));
}
virtual pos_type seekpos(pos_type pos, openmode) override
{
const largeobjectaccess::pos_type newpos = m_obj.cseek(
largeobjectaccess::off_type(pos),
std::ios::beg);
return AdjustEOF(newpos);
}
virtual int_type overflow(int_type ch = EoF()) override
{
char *const pp = this->pptr();
if (pp == nullptr) return EoF();
char *const pb = this->pbase();
int_type res = 0;
if (pp > pb) res = int_type(AdjustEOF(m_obj.cwrite(pb, pp-pb)));
this->setp(m_p, m_p + m_bufsize);
// Write that one more character, if it's there.
if (ch != EoF())
{
*this->pptr() = char(ch);
this->pbump(1);
}
return res;
}
virtual int_type underflow() override
{
if (this->gptr() == nullptr) return EoF();
char *const eb = this->eback();
const int_type res(static_cast<int_type>(
AdjustEOF(m_obj.cread(this->eback(), m_bufsize))));
this->setg(eb, eb, eb + ((res==EoF()) ? 0 : res));
return ((res == 0) or (res == EoF())) ? EoF() : *eb;
}
private:
/// Shortcut for traits_type::eof().
static int_type EoF() { return traits_type::eof(); }
/// Helper: change error position of -1 to EOF (probably a no-op).
template<typename INTYPE>
static std::streampos AdjustEOF(INTYPE pos)
{ return (pos==-1) ? std::streampos(EoF()) : std::streampos(pos); }
void initialize(openmode mode)
{
if (mode & std::ios::in)
{
m_g = new char_type[unsigned(m_bufsize)];
this->setg(m_g, m_g, m_g);
}
if (mode & std::ios::out)
{
m_p = new char_type[unsigned(m_bufsize)];
this->setp(m_p, m_p + m_bufsize);
}
}
const size_type m_bufsize;
largeobjectaccess m_obj;
/// Get & put buffers.
char_type *m_g, *m_p;
};
/// Input stream that gets its data from a large object.
/** Use this class exactly as you would any other istream to read data from a
* large object. All formatting and streaming operations of @c std::istream are
* supported. What you'll typically want to use, however, is the ilostream
* alias (which defines a basic_ilostream for @c char). This is similar to
* how e.g. @c std::ifstream relates to @c std::basic_ifstream.
*
* Currently only works for <tt><char, std::char_traits<char>></tt>.
*/
template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
class basic_ilostream :
public std::basic_istream<CHAR, TRAITS>
{
using super = std::basic_istream<CHAR, TRAITS>;
public:
using char_type = CHAR;
using traits_type = TRAITS;
using int_type = typename traits_type::int_type;
using pos_type = typename traits_type::pos_type;
using off_type = typename traits_type::off_type;
/// Create a basic_ilostream
/**
* @param T Transaction in which this stream is to exist
* @param O Large object to access
* @param BufSize Size of buffer to use internally (optional)
*/
basic_ilostream( //[t57]
dbtransaction &T,
largeobject O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::in, BufSize}
{ super::init(&m_buf); }
/// Create a basic_ilostream
/**
* @param T Transaction in which this stream is to exist
* @param O Identifier of a large object to access
* @param BufSize Size of buffer to use internally (optional)
*/
basic_ilostream( //[t48]
dbtransaction &T,
oid O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::in, BufSize}
{ super::init(&m_buf); }
private:
largeobject_streambuf<CHAR,TRAITS> m_buf;
};
using ilostream = basic_ilostream<char>;
/// Output stream that writes data back to a large object
/** Use this class exactly as you would any other ostream to write data to a
* large object. All formatting and streaming operations of @c std::ostream are
* supported. What you'll typically want to use, however, is the olostream
* alias (which defines a basic_olostream for @c char). This is similar to
* how e.g. @c std::ofstream is related to @c std::basic_ofstream.
*
* Currently only works for <tt><char, std::char_traits<char>></tt>.
*/
template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
class basic_olostream :
public std::basic_ostream<CHAR, TRAITS>
{
using super = std::basic_ostream<CHAR, TRAITS>;
public:
using char_type = CHAR;
using traits_type = TRAITS;
using int_type = typename traits_type::int_type;
using pos_type = typename traits_type::pos_type;
using off_type = typename traits_type::off_type;
/// Create a basic_olostream
/**
* @param T transaction in which this stream is to exist
* @param O a large object to access
* @param BufSize size of buffer to use internally (optional)
*/
basic_olostream( //[t48]
dbtransaction &T,
largeobject O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::out, BufSize}
{ super::init(&m_buf); }
/// Create a basic_olostream
/**
* @param T transaction in which this stream is to exist
* @param O a large object to access
* @param BufSize size of buffer to use internally (optional)
*/
basic_olostream( //[t57]
dbtransaction &T,
oid O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::out, BufSize}
{ super::init(&m_buf); }
~basic_olostream()
{
try
{
m_buf.pubsync(); m_buf.pubsync();
}
catch (const std::exception &e)
{
m_buf.process_notice(e.what());
}
}
private:
largeobject_streambuf<CHAR,TRAITS> m_buf;
};
using olostream = basic_olostream<char>;
/// Stream that reads and writes a large object
/** Use this class exactly as you would a std::iostream to read data from, or
* write data to a large object. All formatting and streaming operations of
* @c std::iostream are supported. What you'll typically want to use, however,
* is the lostream alias (which defines a basic_lostream for @c char). This
* is similar to how e.g. @c std::fstream is related to @c std::basic_fstream.
*
* Currently only works for <tt><char, std::char_traits<char>></tt>.
*/
template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
class basic_lostream :
public std::basic_iostream<CHAR, TRAITS>
{
using super = std::basic_iostream<CHAR, TRAITS>;
public:
using char_type = CHAR;
using traits_type = TRAITS;
using int_type = typename traits_type::int_type;
using pos_type = typename traits_type::pos_type;
using off_type = typename traits_type::off_type;
/// Create a basic_lostream
/**
* @param T Transaction in which this stream is to exist
* @param O Large object to access
* @param BufSize Size of buffer to use internally (optional)
*/
basic_lostream( //[t59]
dbtransaction &T,
largeobject O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::in | std::ios::out, BufSize}
{ super::init(&m_buf); }
/// Create a basic_lostream
/**
* @param T Transaction in which this stream is to exist
* @param O Large object to access
* @param BufSize Size of buffer to use internally (optional)
*/
basic_lostream( //[t59]
dbtransaction &T,
oid O,
largeobject::size_type BufSize=512) :
super{nullptr},
m_buf{T, O, std::ios::in | std::ios::out, BufSize}
{ super::init(&m_buf); }
~basic_lostream()
{
try
{
m_buf.pubsync(); m_buf.pubsync();
}
catch (const std::exception &e)
{
m_buf.process_notice(e.what());
}
}
private:
largeobject_streambuf<CHAR,TRAITS> m_buf;
};
using lostream = basic_lostream<char>;
} // namespace pqxx
#include "pqxx/compiler-internal-post.hxx"
#endif
|