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

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 04 Jul 2018 08:16:29 +0200
parents
children b2ff1cd2907a
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 #include "PostgreSQLStatement.h"
23
24 #include "../Common/BinaryStringValue.h"
25 #include "../Common/FileValue.h"
26 #include "../Common/Integer64Value.h"
27 #include "../Common/NullValue.h"
28 #include "../Common/ResultBase.h"
29 #include "../Common/Utf8StringValue.h"
30 #include "PostgreSQLResult.h"
31
32 #include <Core/Logging.h>
33 #include <Core/OrthancException.h>
34 #include <Core/Toolbox.h>
35
36 #include <cassert>
37
38 // PostgreSQL includes
39 #include <libpq-fe.h>
40 #include <c.h>
41 #include <catalog/pg_type.h>
42
43 #include <Core/Endianness.h>
44
45
46 namespace OrthancDatabases
47 {
48 class PostgreSQLStatement::Inputs : public boost::noncopyable
49 {
50 private:
51 std::vector<char*> values_;
52 std::vector<int> sizes_;
53
54 static char* Allocate(const void* source, int size)
55 {
56 if (size == 0)
57 {
58 return NULL;
59 }
60 else
61 {
62 char* ptr = reinterpret_cast<char*>(malloc(size));
63
64 if (source != NULL)
65 {
66 memcpy(ptr, source, size);
67 }
68
69 return ptr;
70 }
71 }
72
73 void Resize(size_t size)
74 {
75 // Shrinking of the vector
76 for (size_t i = size; i < values_.size(); i++)
77 {
78 if (values_[i] != NULL)
79 free(values_[i]);
80 }
81
82 values_.resize(size, NULL);
83 sizes_.resize(size, 0);
84 }
85
86 void EnlargeForIndex(size_t index)
87 {
88 if (index >= values_.size())
89 {
90 // The vector is too small
91 Resize(index + 1);
92 }
93 }
94
95 public:
96 Inputs()
97 {
98 }
99
100 ~Inputs()
101 {
102 Resize(0);
103 }
104
105 void SetItem(size_t pos, const void* source, int size)
106 {
107 EnlargeForIndex(pos);
108
109 if (sizes_[pos] == size)
110 {
111 if (source && size != 0)
112 {
113 memcpy(values_[pos], source, size);
114 }
115 }
116 else
117 {
118 if (values_[pos] != NULL)
119 {
120 free(values_[pos]);
121 }
122
123 values_[pos] = Allocate(source, size);
124 sizes_[pos] = size;
125 }
126 }
127
128 void SetItem(size_t pos, int size)
129 {
130 SetItem(pos, NULL, size);
131 }
132
133 void* GetItem(size_t pos) const
134 {
135 if (pos >= values_.size())
136 {
137 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
138 }
139
140 return values_[pos];
141 }
142
143 const std::vector<char*>& GetValues() const
144 {
145 return values_;
146 }
147
148 const std::vector<int>& GetSizes() const
149 {
150 return sizes_;
151 }
152 };
153
154
155 void PostgreSQLStatement::Prepare()
156 {
157 if (id_.size() > 0)
158 {
159 // Already prepared
160 return;
161 }
162
163 for (size_t i = 0; i < oids_.size(); i++)
164 {
165 if (oids_[i] == 0)
166 {
167 // The type of an input parameter was not set
168 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
169 }
170 }
171
172 id_ = Orthanc::Toolbox::GenerateUuid();
173
174 const unsigned int* tmp = oids_.size() ? &oids_[0] : NULL;
175
176 PGresult* result = PQprepare(reinterpret_cast<PGconn*>(database_.pg_),
177 id_.c_str(), sql_.c_str(), oids_.size(), tmp);
178
179 if (result == NULL)
180 {
181 id_.clear();
182 database_.ThrowException(true);
183 }
184
185 bool ok = (PQresultStatus(result) == PGRES_COMMAND_OK);
186 if (ok)
187 {
188 PQclear(result);
189 }
190 else
191 {
192 std::string message = PQresultErrorMessage(result);
193 PQclear(result);
194 id_.clear();
195 LOG(ERROR) << "PostgreSQL error: " << message;
196 database_.ThrowException(false);
197 }
198 }
199
200
201 void PostgreSQLStatement::Unprepare()
202 {
203 if (id_.size() > 0)
204 {
205 // "Although there is no libpq function for deleting a
206 // prepared statement, the SQL DEALLOCATE statement can be
207 // used for that purpose."
208 //database_.Execute("DEALLOCATE " + id_);
209 }
210
211 id_.clear();
212 }
213
214
215 void PostgreSQLStatement::DeclareInputInternal(unsigned int param,
216 unsigned int /*Oid*/ type)
217 {
218 Unprepare();
219
220 if (oids_.size() <= param)
221 {
222 oids_.resize(param + 1, 0);
223 binary_.resize(param + 1);
224 }
225
226 oids_[param] = type;
227 binary_[param] = (type == TEXTOID || type == BYTEAOID || type == OIDOID) ? 0 : 1;
228 }
229
230
231 void PostgreSQLStatement::DeclareInputInteger(unsigned int param)
232 {
233 DeclareInputInternal(param, INT4OID);
234 }
235
236
237 void PostgreSQLStatement::DeclareInputInteger64(unsigned int param)
238 {
239 DeclareInputInternal(param, INT8OID);
240 }
241
242
243 void PostgreSQLStatement::DeclareInputString(unsigned int param)
244 {
245 DeclareInputInternal(param, TEXTOID);
246 }
247
248
249 void PostgreSQLStatement::DeclareInputBinary(unsigned int param)
250 {
251 DeclareInputInternal(param, BYTEAOID);
252 }
253
254
255 void PostgreSQLStatement::DeclareInputLargeObject(unsigned int param)
256 {
257 DeclareInputInternal(param, OIDOID);
258 }
259
260
261 void* /* PGresult* */ PostgreSQLStatement::Execute()
262 {
263 Prepare();
264
265 PGresult* result;
266
267 if (oids_.size() == 0)
268 {
269 // No parameter
270 result = PQexecPrepared(reinterpret_cast<PGconn*>(database_.pg_),
271 id_.c_str(), 0, NULL, NULL, NULL, 1);
272 }
273 else
274 {
275 // At least 1 parameter
276 result = PQexecPrepared(reinterpret_cast<PGconn*>(database_.pg_),
277 id_.c_str(),
278 oids_.size(),
279 &inputs_->GetValues()[0],
280 &inputs_->GetSizes()[0],
281 &binary_[0],
282 1);
283 }
284
285 if (result == NULL)
286 {
287 database_.ThrowException(true);
288 }
289
290 return result;
291 }
292
293
294 PostgreSQLStatement::PostgreSQLStatement(PostgreSQLDatabase& database,
295 const std::string& sql,
296 bool readOnly) :
297 database_(database),
298 readOnly_(readOnly),
299 sql_(sql),
300 inputs_(new Inputs),
301 formatter_(Dialect_PostgreSQL)
302 {
303 LOG(TRACE) << "PostgreSQL: " << sql;
304 }
305
306
307 PostgreSQLStatement::PostgreSQLStatement(PostgreSQLDatabase& database,
308 const Query& query) :
309 database_(database),
310 readOnly_(query.IsReadOnly()),
311 inputs_(new Inputs),
312 formatter_(Dialect_PostgreSQL)
313 {
314 query.Format(sql_, formatter_);
315 LOG(TRACE) << "PostgreSQL: " << sql_;
316
317 for (size_t i = 0; i < formatter_.GetParametersCount(); i++)
318 {
319 switch (formatter_.GetParameterType(i))
320 {
321 case ValueType_Integer64:
322 DeclareInputInteger64(i);
323 break;
324
325 case ValueType_Utf8String:
326 DeclareInputString(i);
327 break;
328
329 case ValueType_BinaryString:
330 DeclareInputBinary(i);
331 break;
332
333 case ValueType_File:
334 DeclareInputLargeObject(i);
335 break;
336
337 case ValueType_Null:
338 default:
339 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
340 }
341 }
342 }
343
344
345 void PostgreSQLStatement::Run()
346 {
347 PGresult* result = reinterpret_cast<PGresult*>(Execute());
348 assert(result != NULL); // An exception would have been thrown otherwise
349
350 bool ok = (PQresultStatus(result) == PGRES_COMMAND_OK ||
351 PQresultStatus(result) == PGRES_TUPLES_OK);
352 if (ok)
353 {
354 PQclear(result);
355 }
356 else
357 {
358 std::string error = PQresultErrorMessage(result);
359 PQclear(result);
360 LOG(ERROR) << "PostgreSQL error: " << error;
361 database_.ThrowException(false);
362 }
363 }
364
365
366 void PostgreSQLStatement::BindNull(unsigned int param)
367 {
368 if (param >= oids_.size())
369 {
370 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
371 }
372
373 inputs_->SetItem(param, 0);
374 }
375
376
377 void PostgreSQLStatement::BindInteger(unsigned int param,
378 int value)
379 {
380 if (param >= oids_.size())
381 {
382 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
383 }
384
385 if (oids_[param] != INT4OID)
386 {
387 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType);
388 }
389
390 assert(sizeof(int32_t) == 4);
391 int32_t v = htobe32(static_cast<int32_t>(value));
392 inputs_->SetItem(param, &v, sizeof(int32_t));
393 }
394
395
396 void PostgreSQLStatement::BindInteger64(unsigned int param,
397 int64_t value)
398 {
399 if (param >= oids_.size())
400 {
401 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
402 }
403
404 if (oids_[param] != INT8OID)
405 {
406 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType);
407 }
408
409 assert(sizeof(int64_t) == 8);
410 int64_t v = htobe64(value);
411 inputs_->SetItem(param, &v, sizeof(int64_t));
412 }
413
414
415 void PostgreSQLStatement::BindString(unsigned int param,
416 const std::string& value)
417 {
418 if (param >= oids_.size())
419 {
420 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
421 }
422
423 if (oids_[param] != TEXTOID && oids_[param] != BYTEAOID)
424 {
425 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType);
426 }
427
428 if (value.size() == 0)
429 {
430 inputs_->SetItem(param, "", 1 /* end-of-string character */);
431 }
432 else
433 {
434 inputs_->SetItem(param, value.c_str(),
435 value.size() + 1); // "+1" for end-of-string character
436 }
437 }
438
439
440 void PostgreSQLStatement::BindLargeObject(unsigned int param,
441 const PostgreSQLLargeObject& value)
442 {
443 if (param >= oids_.size())
444 {
445 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
446 }
447
448 if (oids_[param] != OIDOID)
449 {
450 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadParameterType);
451 }
452
453 inputs_->SetItem(param, value.GetOid().c_str(),
454 value.GetOid().size() + 1); // "+1" for end-of-string character
455 }
456
457
458 class PostgreSQLStatement::ResultWrapper : public ResultBase
459 {
460 private:
461 std::auto_ptr<PostgreSQLResult> result_;
462
463 protected:
464 virtual IValue* FetchField(size_t index)
465 {
466 return result_->GetValue(index);
467 }
468
469 public:
470 ResultWrapper(PostgreSQLStatement& statement) :
471 result_(new PostgreSQLResult(statement))
472 {
473 SetFieldsCount(result_->GetColumnsCount());
474 FetchFields();
475 }
476
477 virtual void Next()
478 {
479 result_->Next();
480 FetchFields();
481 }
482
483 virtual bool IsDone() const
484 {
485 return result_->IsDone();
486 }
487 };
488
489
490 IResult* PostgreSQLStatement::Execute(PostgreSQLTransaction& transaction,
491 const Dictionary& parameters)
492 {
493 for (size_t i = 0; i < formatter_.GetParametersCount(); i++)
494 {
495 const std::string& name = formatter_.GetParameterName(i);
496
497 switch (formatter_.GetParameterType(i))
498 {
499 case ValueType_Integer64:
500 BindInteger64(i, dynamic_cast<const Integer64Value&>(parameters.GetValue(name)).GetValue());
501 break;
502
503 case ValueType_Null:
504 BindNull(i);
505 break;
506
507 case ValueType_Utf8String:
508 BindString(i, dynamic_cast<const Utf8StringValue&>
509 (parameters.GetValue(name)).GetContent());
510 break;
511
512 case ValueType_BinaryString:
513 BindString(i, dynamic_cast<const BinaryStringValue&>
514 (parameters.GetValue(name)).GetContent());
515 break;
516
517 case ValueType_File:
518 {
519 const FileValue& blob =
520 dynamic_cast<const FileValue&>(parameters.GetValue(name));
521
522 PostgreSQLLargeObject largeObject(database_, blob.GetContent());
523 BindLargeObject(i, largeObject);
524 break;
525 }
526
527 default:
528 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
529 }
530 }
531
532 return new ResultWrapper(*this);
533 }
534
535
536 void PostgreSQLStatement::ExecuteWithoutResult(PostgreSQLTransaction& transaction,
537 const Dictionary& parameters)
538 {
539 std::auto_ptr<IResult> dummy(Execute(transaction, parameters));
540 }
541 }