Skip to content

Commit

Permalink
Give zview its own header. Re-do process_notice.
Browse files Browse the repository at this point in the history
The `process_notice` functions now work a little less hard to do
useful work in catastrophic situations.  If worse comes to worst,
they'll just omit the trailing newline.  There's a moer efficient
variant taking a `zview`.

Moving `zview` into its own header fixed some dependency ordering
issues.
  • Loading branch information
jtv committed Jan 10, 2020
1 parent 198091c commit d3e0f3c
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 72 deletions.
2 changes: 1 addition & 1 deletion config/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ am__can_run_installinfo = \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
am__DIST_COMMON = $(srcdir)/Makefile.in compile config.guess \
config.sub depcomp install-sh ltmain.sh missing mkinstalldirs
config.sub install-sh ltmain.sh missing mkinstalldirs
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
Expand Down
2 changes: 2 additions & 0 deletions include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ install(
PATTERN util
PATTERN version.hxx
PATTERN version
PATTERN zview.hxx
PATTERN zview
PATTERN internal/callgate.hxx
PATTERN internal/compiler-internal-post.hxx
PATTERN internal/compiler-internal-pre.hxx
Expand Down
9 changes: 7 additions & 2 deletions include/pqxx/connection.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "pqxx/prepared_statement.hxx"
#include "pqxx/strconv.hxx"
#include "pqxx/util.hxx"
#include "pqxx/zview.hxx"


/**
Expand Down Expand Up @@ -190,11 +191,15 @@ public:
*/
bool PQXX_PURE is_open() const noexcept;

// XXX: zview?
/// Invoke notice processor function. The message should end in newline.
void process_notice(const char[]) noexcept;
/// Invoke notice processor function. Newline at end is recommended.
void process_notice(const std::string &) noexcept;
void process_notice(const std::string &msg) noexcept { process_notice(zview{msg}); }
/// Invoke notice processor function. Newline at end is recommended.
/** The zview variant, with a message ending in newline, is the most
* efficient way to call process_notice.
*/
void process_notice(zview) noexcept;

/// Enable tracing to a given output stream, or nullptr to disable.
void trace(std::FILE *) noexcept;
Expand Down
25 changes: 1 addition & 24 deletions include/pqxx/strconv.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "pqxx/except.hxx"
#include "pqxx/util.hxx"
#include "pqxx/zview.hxx"


namespace pqxx::internal
Expand All @@ -37,30 +38,6 @@ PQXX_LIBEXPORT std::string demangle_type_name(const char[]);

namespace pqxx
{
/// Marker-type wrapper: zero-terminated @c std::string_view.
/** @warning Use this only if the underlying string is zero-terminated.
*
* This is basically a @c std::string_view, but it adds the guarantee that
* if its data pointer is non-null, there is a terminating zero byte right
* after the contents.
*
* This means that it can also be used as a C-style string, which often matters
* since libpqxx builds on top of a C library. Therefore, it also adds a
* @c c_str method.
*/
class zview : public std::string_view
{
public:
template<typename... Args>
explicit constexpr zview(Args &&... args) :
std::string_view(std::forward<Args>(args)...)
{}

/// Either a null pointer, or a zero-terminated text buffer.
constexpr const char *c_str() const noexcept { return data(); }
};


/**
* @defgroup stringconversion String conversion
*
Expand Down
4 changes: 4 additions & 0 deletions include/pqxx/zview
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** Zero-terminated string view class.
*/
// Actual definitions in .hxx file so editors and such recognize file type.
#include "pqxx/zview.hxx"
45 changes: 45 additions & 0 deletions include/pqxx/zview.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* Zero-terminated string view.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/stringconv instead.
*
* Copyright (c) 2000-2020, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_ZVIEW
#define PQXX_H_ZVIEW

#include "pqxx/compiler-public.hxx"

#include <string_view>

namespace pqxx
{
/// Marker-type wrapper: zero-terminated @c std::string_view.
/** @warning Use this only if the underlying string is zero-terminated.
*
* When you construct a zview, you are promising that the underlying string is
* zero-terminated. It otherwise behaves exactly like a std::string_view.
* The terminating zero is not "in" the string, so it does not count as part of
* the view's length.
*
* The added guarantee lets the view be used as a C-style string, which often
* matters since libpqxx builds on top of a C library. For this reason, zview
* also adds a @c c_str method.
*/
class zview : public std::string_view
{
public:
template<typename... Args>
explicit constexpr zview(Args &&... args) :
std::string_view(std::forward<Args>(args)...)
{}

/// Either a null pointer, or a zero-terminated text buffer.
constexpr const char *c_str() const noexcept { return data(); }
};
} // namespace pqxx

#endif
64 changes: 19 additions & 45 deletions src/connection.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -282,65 +282,37 @@ void pqxx::connection::process_notice(const char msg[]) noexcept
{
if (msg == nullptr)
return;
const auto len = strlen(msg);
if (len == 0)
const zview view{msg};
if (view.empty())
return;
if (msg[len - 1] == '\n')
{
else if (msg[view.size() - 1] == '\n')
process_notice_raw(msg);
}
else
try
{
// Newline is missing. Try the C++ string version of this function.
process_notice(std::string{msg});
}
catch (const std::exception &)
{
// If we can't even do that, use plain old buffer copying instead
// (unavoidably, this will break up overly long messages!)
const char separator[] = "[...]\n";
char buf[1007];
size_t bytes = sizeof(buf) - sizeof(separator) - 1;
size_t written;
memcpy(&buf[bytes], separator, sizeof(separator));
// Write all chunks but last. Each will fill the buffer exactly.
for (written = 0; (written + bytes) < len; written += bytes)
{
memcpy(buf, &msg[written], bytes);
process_notice_raw(buf);
}
// Write any remaining bytes (which won't fill an entire buffer)
bytes = len - written;
memcpy(buf, &msg[written], bytes);
// Add trailing nul byte, plus newline unless there already is one
if (buf[bytes - 1] != '\n')
{
buf[bytes++] = '\n';
buf[bytes++] = '\0';
}
process_notice_raw(buf);
}
// Newline is missing. Let the zview version of the code add it.
process_notice(view);
}


void pqxx::connection::process_notice(const std::string &msg) noexcept
void pqxx::connection::process_notice(zview msg) noexcept
{
// Ensure that message passed to errorhandler ends in newline
if (msg[msg.size() - 1] == '\n')
if (msg.empty())
return;
else if (msg[msg.size() - 1] == '\n')
process_notice_raw(msg.c_str());
else
try
{
const std::string nl = msg + "\n";
process_notice_raw(nl.c_str());
// Add newline.
std::string buf;
buf.reserve(msg.size() + 1);
buf.assign(msg);
buf.push_back('\n');
process_notice_raw(buf.c_str());
}
catch (const std::exception &)
{
// If nothing else works, try writing the message without the newline
// If nothing else works, try writing the message without the newline.
process_notice_raw(msg.c_str());
// This is ugly.
process_notice_raw("\n");
}
}

Expand Down Expand Up @@ -743,13 +715,14 @@ void pqxx::connection::unregister_transaction(transaction_base *T) noexcept
}


// XXX: string_view? zview?
bool pqxx::connection::read_copy_line(std::string &Line)
{
// XXX: Does std::string::erase() preserve existing storage?
Line.erase();
bool Result;

char *Buf = nullptr;
// XXX: Allocate once, and just issue a fresh shared_ptr.
const auto q{std::make_shared<std::string>("[END COPY]")};
const auto line_len = PQgetCopyData(m_conn, &Buf, false);
switch (line_len)
Expand All @@ -772,6 +745,7 @@ bool pqxx::connection::read_copy_line(std::string &Line)
{
std::unique_ptr<char, std::function<void(char *)>> PQA(
Buf, pqxx::internal::freepqmem_templated<char>);
// XXX: Does std::string::assign() preserve existing storage?
Line.assign(Buf, unsigned(line_len));
}
Result = true;
Expand Down

0 comments on commit d3e0f3c

Please sign in to comment.