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