comparison Framework/PostgreSQL/PostgreSQLLargeObject.cpp @ 0:7cea966b6829

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 04 Jul 2018 08:16:29 +0200
parents
children d17b2631bb67
comparison
equal deleted inserted replaced
-1:000000000000 0:7cea966b6829
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2018 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21
22 // http://www.postgresql.org/docs/9.1/static/lo-interfaces.html#AEN33102
23
24 #include "PostgreSQLLargeObject.h"
25
26 #include <Core/Logging.h>
27 #include <Core/OrthancException.h>
28
29 #include <boost/lexical_cast.hpp>
30 #include <libpq/libpq-fs.h>
31
32
33 namespace OrthancDatabases
34 {
35 void PostgreSQLLargeObject::Create()
36 {
37 PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_);
38
39 oid_ = lo_creat(pg, INV_WRITE);
40 if (oid_ == 0)
41 {
42 LOG(ERROR) << "PostgreSQL: Cannot create a large object";
43 database_.ThrowException(false);
44 }
45 }
46
47
48 void PostgreSQLLargeObject::Write(const void* data,
49 size_t size)
50 {
51 static int MAX_CHUNK_SIZE = 16 * 1024 * 1024;
52
53 PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_);
54
55 int fd = lo_open(pg, oid_, INV_WRITE);
56 if (fd < 0)
57 {
58 database_.ThrowException(true);
59 }
60
61 const char* position = reinterpret_cast<const char*>(data);
62 while (size > 0)
63 {
64 int chunk = (size > static_cast<size_t>(MAX_CHUNK_SIZE) ?
65 MAX_CHUNK_SIZE : static_cast<int>(size));
66 int nbytes = lo_write(pg, fd, position, chunk);
67 if (nbytes <= 0)
68 {
69 lo_close(pg, fd);
70 database_.ThrowException(true);
71 }
72
73 size -= nbytes;
74 position += nbytes;
75 }
76
77 lo_close(pg, fd);
78 }
79
80
81 PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database,
82 const void* data,
83 size_t size) :
84 database_(database)
85 {
86 Create();
87 Write(data, size);
88 }
89
90
91 PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database,
92 const std::string& s) :
93 database_(database)
94 {
95 Create();
96
97 if (s.size() != 0)
98 {
99 Write(s.c_str(), s.size());
100 }
101 else
102 {
103 Write(NULL, 0);
104 }
105 }
106
107
108 class PostgreSQLLargeObject::Reader
109 {
110 private:
111 PostgreSQLDatabase& database_;
112 int fd_;
113 size_t size_;
114
115 public:
116 Reader(PostgreSQLDatabase& database,
117 const std::string& oid) :
118 database_(database)
119 {
120 PGconn* pg = reinterpret_cast<PGconn*>(database.pg_);
121 Oid id = boost::lexical_cast<Oid>(oid);
122
123 fd_ = lo_open(pg, id, INV_READ);
124
125 if (fd_ < 0 ||
126 lo_lseek(pg, fd_, 0, SEEK_END) < 0)
127 {
128 LOG(ERROR) << "PostgreSQL: No such large object in the database; "
129 << "Make sure you use a transaction";
130 database.ThrowException(false);
131 }
132
133 // Get the size of the large object
134 int size = lo_tell(pg, fd_);
135 if (size < 0)
136 {
137 database.ThrowException(true);
138 }
139 size_ = static_cast<size_t>(size);
140
141 // Go to the first byte of the object
142 lo_lseek(pg, fd_, 0, SEEK_SET);
143 }
144
145 ~Reader()
146 {
147 lo_close(reinterpret_cast<PGconn*>(database_.pg_), fd_);
148 }
149
150 size_t GetSize() const
151 {
152 return size_;
153 }
154
155 void Read(char* target)
156 {
157 for (size_t position = 0; position < size_; )
158 {
159 size_t remaining = size_ - position;
160
161 int nbytes = lo_read(reinterpret_cast<PGconn*>(database_.pg_), fd_, target + position, remaining);
162 if (nbytes < 0)
163 {
164 LOG(ERROR) << "PostgreSQL: Unable to read the large object in the database";
165 database_.ThrowException(false);
166 }
167
168 position += static_cast<size_t>(nbytes);
169 }
170 }
171 };
172
173
174 void PostgreSQLLargeObject::Read(std::string& target,
175 PostgreSQLDatabase& database,
176 const std::string& oid)
177 {
178 Reader reader(database, oid);
179 target.resize(reader.GetSize());
180
181 if (target.size() > 0)
182 {
183 reader.Read(&target[0]);
184 }
185 }
186
187
188 void PostgreSQLLargeObject::Read(void*& target,
189 size_t& size,
190 PostgreSQLDatabase& database,
191 const std::string& oid)
192 {
193 Reader reader(database, oid);
194 size = reader.GetSize();
195
196 if (size == 0)
197 {
198 target = NULL;
199 }
200 else
201 {
202 target = malloc(size);
203 reader.Read(reinterpret_cast<char*>(target));
204 }
205 }
206
207
208 std::string PostgreSQLLargeObject::GetOid() const
209 {
210 return boost::lexical_cast<std::string>(oid_);
211 }
212
213
214 void PostgreSQLLargeObject::Delete(PostgreSQLDatabase& database,
215 const std::string& oid)
216 {
217 PGconn* pg = reinterpret_cast<PGconn*>(database.pg_);
218 Oid id = boost::lexical_cast<Oid>(oid);
219
220 if (lo_unlink(pg, id) < 0)
221 {
222 LOG(ERROR) << "PostgreSQL: Unable to delete the large object from the database";
223 database.ThrowException(false);
224 }
225 }
226 }