1 /** Implementation of the Large Objects interface.
2 *
3 * Allows direct access to large objects, as well as though I/O streams.
4 *
5 * Copyright (c) 2000-2020, Jeroen T. Vermeulen.
6 *
7 * See COPYING for copyright license. If you did not receive a file called
8 * COPYING with this source code, please notify the distributor of this
9 * mistake, or contact the author.
10 */
11 #include "pqxx-source.hxx"
12
13 #include <algorithm>
14 #include <cerrno>
15 #include <stdexcept>
16
17 extern "C"
18 {
19 #include <libpq-fe.h>
20 }
21
22 #include "pqxx/largeobject"
23
24 #include "pqxx/internal/gates/connection-largeobject.hxx"
25
26
27 namespace
28 {
std_mode_to_pq_mode(std::ios::openmode mode)29 constexpr inline int std_mode_to_pq_mode(std::ios::openmode mode)
30 {
31 /// Mode bits, copied from libpq-fs.h so that we no longer need that header.
32 constexpr int INV_WRITE{0x00020000}, INV_READ{0x00040000};
33
34 return ((mode & std::ios::in) ? INV_READ : 0) |
35 ((mode & std::ios::out) ? INV_WRITE : 0);
36 }
37
38
std_dir_to_pq_dir(std::ios::seekdir dir)39 constexpr int std_dir_to_pq_dir(std::ios::seekdir dir) noexcept
40 {
41 if constexpr (
42 static_cast<int>(std::ios::beg) == int(SEEK_SET) and
43 static_cast<int>(std::ios::cur) == int(SEEK_CUR) and
44 static_cast<int>(std::ios::end) == int(SEEK_END))
45 {
46 // Easy optimisation: they're the same constants. This is actually the
47 // case for the gcc I'm using.
48 return dir;
49 }
50 else
51 switch (dir)
52 {
53 case std::ios::beg: return SEEK_SET; break;
54 case std::ios::cur: return SEEK_CUR; break;
55 case std::ios::end: return SEEK_END; break;
56
57 // Shouldn't happen, but may silence compiler warning.
58 default: return dir; break;
59 }
60 }
61 } // namespace
62
63
largeobject(dbtransaction & t)64 pqxx::largeobject::largeobject(dbtransaction &t)
65 {
66 // (Mode is ignored as of postgres 8.1.)
67 m_id = lo_creat(raw_connection(t), 0);
68 if (m_id == oid_none)
69 {
70 int const err{errno};
71 if (err == ENOMEM)
72 throw std::bad_alloc{};
73 throw failure{"Could not create large object: " + reason(t.conn(), err)};
74 }
75 }
76
77
largeobject(dbtransaction & t,std::string_view file)78 pqxx::largeobject::largeobject(dbtransaction &t, std::string_view file)
79 {
80 m_id = lo_import(raw_connection(t), file.data());
81 if (m_id == oid_none)
82 {
83 int const err{errno};
84 if (err == ENOMEM)
85 throw std::bad_alloc{};
86 throw failure{
87 "Could not import file '" + std::string{file} +
88 "' to large object: " + reason(t.conn(), err)};
89 }
90 }
91
92
largeobject(largeobjectaccess const & o)93 pqxx::largeobject::largeobject(largeobjectaccess const &o) noexcept :
94 m_id{o.id()}
95 {}
96
97
to_file(dbtransaction & t,std::string_view file) const98 void pqxx::largeobject::to_file(dbtransaction &t, std::string_view file) const
99 {
100 if (id() == oid_none)
101 throw usage_error{"No object selected."};
102 if (lo_export(raw_connection(t), id(), file.data()) == -1)
103 {
104 int const err{errno};
105 if (err == ENOMEM)
106 throw std::bad_alloc{};
107 throw failure{
108 "Could not export large object " + to_string(m_id) +
109 " "
110 "to file '" +
111 std::string{file} + "': " + reason(t.conn(), err)};
112 }
113 }
114
115
remove(dbtransaction & t) const116 void pqxx::largeobject::remove(dbtransaction &t) const
117 {
118 if (id() == oid_none)
119 throw usage_error{"No object selected."};
120 if (lo_unlink(raw_connection(t), id()) == -1)
121 {
122 int const err{errno};
123 if (err == ENOMEM)
124 throw std::bad_alloc{};
125 throw failure{
126 "Could not delete large object " + to_string(m_id) + ": " +
127 reason(t.conn(), err)};
128 }
129 }
130
131
132 pqxx::internal::pq::PGconn *
raw_connection(dbtransaction const & t)133 pqxx::largeobject::raw_connection(dbtransaction const &t)
134 {
135 return pqxx::internal::gate::connection_largeobject{t.conn()}
136 .raw_connection();
137 }
138
139
reason(connection const & c,int err) const140 std::string pqxx::largeobject::reason(connection const &c, int err) const
141 {
142 if (err == ENOMEM)
143 return "Out of memory";
144 return pqxx::internal::gate::const_connection_largeobject{c}.error_message();
145 }
146
147
largeobjectaccess(dbtransaction & t,openmode mode)148 pqxx::largeobjectaccess::largeobjectaccess(dbtransaction &t, openmode mode) :
149 largeobject{t}, m_trans{t}
150 {
151 open(mode);
152 }
153
154
largeobjectaccess(dbtransaction & t,oid o,openmode mode)155 pqxx::largeobjectaccess::largeobjectaccess(
156 dbtransaction &t, oid o, openmode mode) :
157 largeobject{o}, m_trans{t}
158 {
159 open(mode);
160 }
161
162
largeobjectaccess(dbtransaction & t,largeobject o,openmode mode)163 pqxx::largeobjectaccess::largeobjectaccess(
164 dbtransaction &t, largeobject o, openmode mode) :
165 largeobject{o}, m_trans{t}
166 {
167 open(mode);
168 }
169
170
largeobjectaccess(dbtransaction & t,std::string_view file,openmode mode)171 pqxx::largeobjectaccess::largeobjectaccess(
172 dbtransaction &t, std::string_view file, openmode mode) :
173 largeobject{t, file}, m_trans{t}
174 {
175 open(mode);
176 }
177
178
179 pqxx::largeobjectaccess::size_type
seek(size_type dest,seekdir dir)180 pqxx::largeobjectaccess::seek(size_type dest, seekdir dir)
181 {
182 auto const res{cseek(dest, dir)};
183 if (res == -1)
184 {
185 int const err{errno};
186 if (err == ENOMEM)
187 throw std::bad_alloc{};
188 if (id() == oid_none)
189 throw usage_error{"No object selected."};
190 throw failure{"Error seeking in large object: " + reason(err)};
191 }
192
193 return res;
194 }
195
196
197 pqxx::largeobjectaccess::pos_type
cseek(off_type dest,seekdir dir)198 pqxx::largeobjectaccess::cseek(off_type dest, seekdir dir) noexcept
199 {
200 return lo_lseek64(raw_connection(), m_fd, dest, std_dir_to_pq_dir(dir));
201 }
202
203
204 pqxx::largeobjectaccess::pos_type
cwrite(char const buf[],std::size_t len)205 pqxx::largeobjectaccess::cwrite(char const buf[], std::size_t len) noexcept
206 {
207 return std::max(lo_write(raw_connection(), m_fd, buf, len), -1);
208 }
209
210
211 pqxx::largeobjectaccess::pos_type
cread(char buf[],std::size_t len)212 pqxx::largeobjectaccess::cread(char buf[], std::size_t len) noexcept
213 {
214 return std::max(lo_read(raw_connection(), m_fd, buf, len), -1);
215 }
216
217
218 pqxx::largeobjectaccess::pos_type
ctell() const219 pqxx::largeobjectaccess::ctell() const noexcept
220 {
221 return lo_tell64(raw_connection(), m_fd);
222 }
223
224
write(char const buf[],std::size_t len)225 void pqxx::largeobjectaccess::write(char const buf[], std::size_t len)
226 {
227 if (id() == oid_none)
228 throw usage_error{"No object selected."};
229 if (auto const bytes{cwrite(buf, len)}; bytes < static_cast<off_type>(len))
230 {
231 int const err{errno};
232 if (err == ENOMEM)
233 throw std::bad_alloc{};
234 if (bytes < 0)
235 throw failure{
236 "Error writing to large object #" + to_string(id()) + ": " +
237 reason(err)};
238 if (bytes == 0)
239 throw failure{
240 "Could not write to large object #" + to_string(id()) + ": " +
241 reason(err)};
242
243 throw failure{
244 "Wanted to write " + to_string(len) + " bytes to large object #" +
245 to_string(id()) +
246 "; "
247 "could only write " +
248 to_string(bytes)};
249 }
250 }
251
252
253 pqxx::largeobjectaccess::size_type
read(char buf[],std::size_t len)254 pqxx::largeobjectaccess::read(char buf[], std::size_t len)
255 {
256 if (id() == oid_none)
257 throw usage_error{"No object selected."};
258 auto const bytes{cread(buf, len)};
259 if (bytes < 0)
260 {
261 int const err{errno};
262 if (err == ENOMEM)
263 throw std::bad_alloc{};
264 throw failure{
265 "Error reading from large object #" + to_string(id()) + ": " +
266 reason(err)};
267 }
268 return bytes;
269 }
270
271
open(openmode mode)272 void pqxx::largeobjectaccess::open(openmode mode)
273 {
274 if (id() == oid_none)
275 throw usage_error{"No object selected."};
276 m_fd = lo_open(raw_connection(), id(), std_mode_to_pq_mode(mode));
277 if (m_fd < 0)
278 {
279 int const err{errno};
280 if (err == ENOMEM)
281 throw std::bad_alloc{};
282 throw failure{
283 "Could not open large object " + to_string(id()) + ": " + reason(err)};
284 }
285 }
286
287
close()288 void pqxx::largeobjectaccess::close() noexcept
289 {
290 if (m_fd >= 0)
291 lo_close(raw_connection(), m_fd);
292 }
293
294
tell() const295 pqxx::largeobjectaccess::size_type pqxx::largeobjectaccess::tell() const
296 {
297 auto const res{ctell()};
298 if (res == -1)
299 throw failure{reason(errno)};
300 return res;
301 }
302
303
reason(int err) const304 std::string pqxx::largeobjectaccess::reason(int err) const
305 {
306 if (m_fd == -1)
307 return "No object opened.";
308 return largeobject::reason(m_trans.conn(), err);
309 }
310
311
process_notice(std::string const & s)312 void pqxx::largeobjectaccess::process_notice(std::string const &s) noexcept
313 {
314 m_trans.process_notice(s);
315 }
316