comparison Framework/PostgreSQL/PostgreSQLLargeObject.cpp @ 248:7a4f9bcb0bc2

PostgreSQL: Support of range reads from the storage area
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 14 Apr 2021 09:46:44 +0200
parents 3236894320d6
children 16aac0287485
comparison
equal deleted inserted replaced
247:e57a5313ffe5 248:7a4f9bcb0bc2
78 lo_close(pg, fd); 78 lo_close(pg, fd);
79 } 79 }
80 80
81 81
82 PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database, 82 PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database,
83 const void* data,
84 size_t size) :
85 database_(database)
86 {
87 Create();
88 Write(data, size);
89 }
90
91
92 PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database,
93 const std::string& s) : 83 const std::string& s) :
94 database_(database) 84 database_(database)
95 { 85 {
96 Create(); 86 Create();
97 87
111 private: 101 private:
112 PostgreSQLDatabase& database_; 102 PostgreSQLDatabase& database_;
113 int fd_; 103 int fd_;
114 size_t size_; 104 size_t size_;
115 105
116 public: 106 void ReadInternal(PGconn* pg,
117 Reader(PostgreSQLDatabase& database, 107 std::string& target)
118 const std::string& oid) : 108 {
119 database_(database) 109 for (size_t position = 0; position < target.size(); )
120 { 110 {
121 PGconn* pg = reinterpret_cast<PGconn*>(database.pg_); 111 size_t remaining = target.size() - position;
122 Oid id = boost::lexical_cast<Oid>(oid); 112
123 113 int nbytes = lo_read(pg, fd_, &target[position], remaining);
124 fd_ = lo_open(pg, id, INV_READ);
125
126 if (fd_ < 0 ||
127 lo_lseek(pg, fd_, 0, SEEK_END) < 0)
128 {
129 LOG(ERROR) << "PostgreSQL: No such large object in the database; "
130 << "Make sure you use a transaction";
131 database.ThrowException(false);
132 }
133
134 // Get the size of the large object
135 int size = lo_tell(pg, fd_);
136 if (size < 0)
137 {
138 database.ThrowException(true);
139 }
140 size_ = static_cast<size_t>(size);
141
142 // Go to the first byte of the object
143 lo_lseek(pg, fd_, 0, SEEK_SET);
144 }
145
146 ~Reader()
147 {
148 lo_close(reinterpret_cast<PGconn*>(database_.pg_), fd_);
149 }
150
151 size_t GetSize() const
152 {
153 return size_;
154 }
155
156 void Read(char* target)
157 {
158 for (size_t position = 0; position < size_; )
159 {
160 size_t remaining = size_ - position;
161
162 int nbytes = lo_read(reinterpret_cast<PGconn*>(database_.pg_), fd_, target + position, remaining);
163 if (nbytes < 0) 114 if (nbytes < 0)
164 { 115 {
165 LOG(ERROR) << "PostgreSQL: Unable to read the large object in the database"; 116 LOG(ERROR) << "PostgreSQL: Unable to read the large object in the database";
166 database_.ThrowException(false); 117 database_.ThrowException(false);
167 } 118 }
168 119
169 position += static_cast<size_t>(nbytes); 120 position += static_cast<size_t>(nbytes);
170 } 121 }
171 } 122 }
123
124 public:
125 Reader(PostgreSQLDatabase& database,
126 const std::string& oid) :
127 database_(database)
128 {
129 PGconn* pg = reinterpret_cast<PGconn*>(database.pg_);
130 Oid id = boost::lexical_cast<Oid>(oid);
131
132 fd_ = lo_open(pg, id, INV_READ);
133
134 if (fd_ < 0 ||
135 lo_lseek(pg, fd_, 0, SEEK_END) < 0)
136 {
137 LOG(ERROR) << "PostgreSQL: No such large object in the database; "
138 << "Make sure you use a transaction";
139 database.ThrowException(false);
140 }
141
142 // Get the size of the large object
143 int size = lo_tell(pg, fd_);
144 if (size < 0)
145 {
146 database.ThrowException(true);
147 }
148 size_ = static_cast<size_t>(size);
149 }
150
151 ~Reader()
152 {
153 lo_close(reinterpret_cast<PGconn*>(database_.pg_), fd_);
154 }
155
156 size_t GetSize() const
157 {
158 return size_;
159 }
160
161 void ReadWhole(std::string& target)
162 {
163 if (target.size() != size_)
164 {
165 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
166 }
167
168 PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_);
169
170 // Go to the first byte of the object
171 lo_lseek(pg, fd_, 0, SEEK_SET);
172
173 ReadInternal(pg, target);
174 }
175
176 void ReadRange(std::string& target,
177 uint64_t start)
178 {
179 PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_);
180
181 // Go to the first byte of the object
182 lo_lseek(pg, fd_, start, SEEK_SET);
183
184 ReadInternal(pg, target);
185 }
172 }; 186 };
173 187
174 188
175 void PostgreSQLLargeObject::Read(std::string& target, 189 void PostgreSQLLargeObject::ReadWhole(std::string& target,
176 PostgreSQLDatabase& database, 190 PostgreSQLDatabase& database,
177 const std::string& oid) 191 const std::string& oid)
178 { 192 {
179 Reader reader(database, oid); 193 Reader reader(database, oid);
180 target.resize(reader.GetSize()); 194 target.resize(reader.GetSize());
181 195
182 if (target.size() > 0) 196 if (target.size() > 0)
183 { 197 {
184 reader.Read(&target[0]); 198 reader.ReadWhole(target);
185 } 199 }
186 } 200 }
187 201
188 202
189 void PostgreSQLLargeObject::Read(void*& target, 203 void PostgreSQLLargeObject::ReadRange(std::string& target,
190 size_t& size, 204 PostgreSQLDatabase& database,
191 PostgreSQLDatabase& database, 205 const std::string& oid,
192 const std::string& oid) 206 uint64_t start,
207 size_t length)
193 { 208 {
194 Reader reader(database, oid); 209 Reader reader(database, oid);
195 size = reader.GetSize(); 210
196 211 if (start >= reader.GetSize() ||
197 if (size == 0) 212 start + length > reader.GetSize())
198 { 213 {
199 target = NULL; 214 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRange);
200 } 215 }
201 else 216
202 { 217 target.resize(length);
203 target = malloc(size); 218
204 if (target == NULL) 219 if (target.size() > 0)
205 { 220 {
206 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory); 221 reader.ReadRange(target, start);
207 }
208
209 reader.Read(reinterpret_cast<char*>(target));
210 } 222 }
211 } 223 }
212 224
213 225
214 std::string PostgreSQLLargeObject::GetOid() const 226 std::string PostgreSQLLargeObject::GetOid() const