Mercurial > hg > orthanc-databases
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/PostgreSQL/PostgreSQLLargeObject.cpp Wed Jul 04 08:16:29 2018 +0200 @@ -0,0 +1,226 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +// http://www.postgresql.org/docs/9.1/static/lo-interfaces.html#AEN33102 + +#include "PostgreSQLLargeObject.h" + +#include <Core/Logging.h> +#include <Core/OrthancException.h> + +#include <boost/lexical_cast.hpp> +#include <libpq/libpq-fs.h> + + +namespace OrthancDatabases +{ + void PostgreSQLLargeObject::Create() + { + PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_); + + oid_ = lo_creat(pg, INV_WRITE); + if (oid_ == 0) + { + LOG(ERROR) << "PostgreSQL: Cannot create a large object"; + database_.ThrowException(false); + } + } + + + void PostgreSQLLargeObject::Write(const void* data, + size_t size) + { + static int MAX_CHUNK_SIZE = 16 * 1024 * 1024; + + PGconn* pg = reinterpret_cast<PGconn*>(database_.pg_); + + int fd = lo_open(pg, oid_, INV_WRITE); + if (fd < 0) + { + database_.ThrowException(true); + } + + const char* position = reinterpret_cast<const char*>(data); + while (size > 0) + { + int chunk = (size > static_cast<size_t>(MAX_CHUNK_SIZE) ? + MAX_CHUNK_SIZE : static_cast<int>(size)); + int nbytes = lo_write(pg, fd, position, chunk); + if (nbytes <= 0) + { + lo_close(pg, fd); + database_.ThrowException(true); + } + + size -= nbytes; + position += nbytes; + } + + lo_close(pg, fd); + } + + + PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database, + const void* data, + size_t size) : + database_(database) + { + Create(); + Write(data, size); + } + + + PostgreSQLLargeObject::PostgreSQLLargeObject(PostgreSQLDatabase& database, + const std::string& s) : + database_(database) + { + Create(); + + if (s.size() != 0) + { + Write(s.c_str(), s.size()); + } + else + { + Write(NULL, 0); + } + } + + + class PostgreSQLLargeObject::Reader + { + private: + PostgreSQLDatabase& database_; + int fd_; + size_t size_; + + public: + Reader(PostgreSQLDatabase& database, + const std::string& oid) : + database_(database) + { + PGconn* pg = reinterpret_cast<PGconn*>(database.pg_); + Oid id = boost::lexical_cast<Oid>(oid); + + fd_ = lo_open(pg, id, INV_READ); + + if (fd_ < 0 || + lo_lseek(pg, fd_, 0, SEEK_END) < 0) + { + LOG(ERROR) << "PostgreSQL: No such large object in the database; " + << "Make sure you use a transaction"; + database.ThrowException(false); + } + + // Get the size of the large object + int size = lo_tell(pg, fd_); + if (size < 0) + { + database.ThrowException(true); + } + size_ = static_cast<size_t>(size); + + // Go to the first byte of the object + lo_lseek(pg, fd_, 0, SEEK_SET); + } + + ~Reader() + { + lo_close(reinterpret_cast<PGconn*>(database_.pg_), fd_); + } + + size_t GetSize() const + { + return size_; + } + + void Read(char* target) + { + for (size_t position = 0; position < size_; ) + { + size_t remaining = size_ - position; + + int nbytes = lo_read(reinterpret_cast<PGconn*>(database_.pg_), fd_, target + position, remaining); + if (nbytes < 0) + { + LOG(ERROR) << "PostgreSQL: Unable to read the large object in the database"; + database_.ThrowException(false); + } + + position += static_cast<size_t>(nbytes); + } + } + }; + + + void PostgreSQLLargeObject::Read(std::string& target, + PostgreSQLDatabase& database, + const std::string& oid) + { + Reader reader(database, oid); + target.resize(reader.GetSize()); + + if (target.size() > 0) + { + reader.Read(&target[0]); + } + } + + + void PostgreSQLLargeObject::Read(void*& target, + size_t& size, + PostgreSQLDatabase& database, + const std::string& oid) + { + Reader reader(database, oid); + size = reader.GetSize(); + + if (size == 0) + { + target = NULL; + } + else + { + target = malloc(size); + reader.Read(reinterpret_cast<char*>(target)); + } + } + + + std::string PostgreSQLLargeObject::GetOid() const + { + return boost::lexical_cast<std::string>(oid_); + } + + + void PostgreSQLLargeObject::Delete(PostgreSQLDatabase& database, + const std::string& oid) + { + PGconn* pg = reinterpret_cast<PGconn*>(database.pg_); + Oid id = boost::lexical_cast<Oid>(oid); + + if (lo_unlink(pg, id) < 0) + { + LOG(ERROR) << "PostgreSQL: Unable to delete the large object from the database"; + database.ThrowException(false); + } + } +}