Adam Ierymenko 0e5651f353
1.12.0 merge to main (#2104)
* add note about forceTcpRelay

* Create a sample systemd unit for tcp proxy

* set gitattributes for rust & cargo so hashes dont conflict on Windows

* Revert "set gitattributes for rust & cargo so hashes dont conflict on Windows"

This reverts commit 032dc5c108195f6bbc2e224f00da5b785df4b7f9.

* Turn off autocrlf for rust source

Doesn't appear to play nice well when it comes to git and vendored cargo package hashes

* Fix #1883 (#1886)

Still unknown as to why, but the call to `nc->GetProperties()` can fail
when setting a friendly name on the Windows virtual ethernet adapter.
Ensure that `ncp` is not null before continuing and accessing the device
GUID.

* Don't vendor packages for zeroidc (#1885)

* Added docker environment way to join networks (#1871)

* add StringUtils

* fix headers
use recommended headers and remove unused headers

* move extern "C"
only JNI functions need to be exported

* cleanup

* fix ANDROID-50: RESULT_ERROR_BAD_PARAMETER typo

* fix typo in log message

* fix typos in JNI method signatures

* fix typo

* fix ANDROID-51: fieldName is uninitialized

* fix ANDROID-35: memory leak

* fix missing DeleteLocalRef in loops

* update to use unique error codes

* add GETENV macro

* add LOG_TAG defines

* ANDROID-48: add ZT_jnicache.cpp

* ANDROID-48: use ZT_jnicache.cpp and remove ZT_jnilookup.cpp and ZT_jniarray.cpp

* add Event.fromInt

* add PeerRole.fromInt

* add ResultCode.fromInt

* fix ANDROID-36: issues with ResultCode

* add VirtualNetworkConfigOperation.fromInt

* fix ANDROID-40: VirtualNetworkConfigOperation out-of-sync with ZT_VirtualNetworkConfigOperation enum

* add VirtualNetworkStatus.fromInt

* fix ANDROID-37: VirtualNetworkStatus out-of-sync with ZT_VirtualNetworkStatus enum

* add VirtualNetworkType.fromInt

* make NodeStatus a plain data class

* fix ANDROID-52: synchronization bug with nodeMap

* Node init work: separate Node construction and init

* add Node.toString

* make PeerPhysicalPath a plain data class

* remove unused PeerPhysicalPath.fixed

* add array functions

* make Peer a plain data class

* make Version a plain data class

* fix ANDROID-42: copy/paste error

* fix ANDROID-49: VirtualNetworkConfig.equals is wrong

* reimplement VirtualNetworkConfig.equals

* reimplement VirtualNetworkConfig.compareTo

* add VirtualNetworkConfig.hashCode

* make VirtualNetworkConfig a plain data class

* remove unused VirtualNetworkConfig.enabled

* reimplement VirtualNetworkDNS.equals

* add VirtualNetworkDNS.hashCode

* make VirtualNetworkDNS a plain data class

* reimplement VirtualNetworkRoute.equals

* reimplement VirtualNetworkRoute.compareTo

* reimplement VirtualNetworkRoute.toString

* add VirtualNetworkRoute.hashCode

* make VirtualNetworkRoute a plain data class

* add isSocketAddressEmpty

* add addressPort

* add fromSocketAddressObject

* invert logic in a couple of places and return early

* newInetAddress and newInetSocketAddress work
allow newInetSocketAddress to return NULL if given empty address

* fix ANDROID-38: stack corruption in onSendPacketRequested

* use GETENV macro

* JniRef work
JniRef does not use callbacks struct, so remove
fix NewGlobalRef / DeleteGlobalRef mismatch

* use PRId64 macros

* switch statement work

* comments and logging

* Modifier 'public' is redundant for interface members

* NodeException can be made a checked Exception

* 'NodeException' does not define a 'serialVersionUID' field

* 'finalize()' should not be overridden
this is fine to do because ZeroTierOneService calls close() when it is done

* error handling, error reporting, asserts, logging

* simplify loadLibrary

* rename Node.networks -> Node.networkConfigs

* Windows file permissions fix (#1887)

* Allow macOS interfaces to use multiple IP addresses (#1879)

Co-authored-by: Sean OMeara <someara@users.noreply.github.com>
Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* Fix condition where full HELLOs might not be sent when necessary (#1877)

Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* 1.10.4 version bumps

* Add security policy to repo (#1889)

* [+] add e2k64 arch (#1890)

* temp fix for ANDROID-56: crash inside newNetworkConfig from too many args

* 1.10.4 release notes

* Windows 1.10.4 Advanced Installer bump

* Revert "temp fix for ANDROID-56: crash inside newNetworkConfig from too many args"

This reverts commit dd627cd7f44ad623a110bb14f72d0bea72a09e30.

* actual fix for ANDROID-56: crash inside newNetworkConfig
cast all arguments to varargs functions as good style

* Fix addIp being called with applied ips (#1897)

This was getting called outside of the check for existing ips
Because of the added ifdef and a brace getting moved to the
wrong place.

```
if (! n.tap()->addIp(*ip)) {
	fprintf(stderr, "ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
WinFWHelper::newICMPRule(*ip, n.config().nwid);

```

* 1.10.5 (#1905)

* 1.10.5 bump

* 1.10.5 for Windows

* 1.10.5

* Prevent path-learning loops (#1914)

* Prevent path-learning loops

* Only allow new overwrite if not bonded

* fix binding temporary ipv6 addresses on macos (#1910)

The check code wasn't running.

I don't know why !defined(TARGET_OS_IOS) would exclude code on
desktop macOS. I did a quick search and changed it to defined(TARGET_OS_MAC).
Not 100% sure what the most correct solution there is.

You can verify the old and new versions with

`ifconfig | grep temporary`

plus

`zerotier-cli info -j` -> listeningOn

* 1.10.6 (#1929)

* 1.10.5 bump

* 1.10.6

* 1.10.6 AIP for Windows.

* Release notes for 1.10.6 (#1931)

* Minor tweak to Synology Docker image script (#1936)

* Change if_def again so ios can build (#1937)

All apple's variables are "defined"
but sometimes they are defined as "0"

* move begin/commit into try/catch block (#1932)

Thread was exiting in some cases

* Bump openssl from 0.10.45 to 0.10.48 in /zeroidc (#1938)

Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.45 to 0.10.48.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.45...openssl-v0.10.48)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* new drone bits

* Fix multiple network join from environment entrypoint.sh.release (#1961)

* _bond_m guards _bond, not _paths_m (#1965)

* Fix: warning: mutex '_aqm_m' is not held on every path through here [-Wthread-safety-analysis] (#1964)

* Bump h2 from 0.3.16 to 0.3.17 in /zeroidc (#1963)

Bumps [h2](https://github.com/hyperium/h2) from 0.3.16 to 0.3.17.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.16...v0.3.17)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* Add note that binutils is required on FreeBSD (#1968)

* Add prometheus metrics for Central controllers (#1969)

* add header-only prometheus lib to ext

* rename folder

* Undo rename directory

* prometheus simpleapi included on mac & linux

* wip

* wire up some controller stats

* Get windows building with prometheus

* bsd build flags for prometheus

* Fix multiple network join from environment entrypoint.sh.release (#1961)

* _bond_m guards _bond, not _paths_m (#1965)

* Fix: warning: mutex '_aqm_m' is not held on every path through here [-Wthread-safety-analysis] (#1964)

* Serve prom metrics from /metrics endpoint

* Add prom metrics for Central controller specific things

* reorganize metric initialization

* testing out a labled gauge on Networks

* increment error counter on throw

* Consolidate metrics definitions

Put all metric definitions into node/Metrics.hpp.  Accessed as needed
from there.

* Revert "testing out a labled gauge on Networks"

This reverts commit 499ed6d95e11452019cdf48e32ed4cd878c2705b.

* still blows up but adding to the record for completeness right now

* Fix runtime issues with metrics

* Add metrics files to visual studio project

* Missed an "extern"

* add copyright headers to new files

* Add metrics for sent/received bytes (total)

* put /metrics endpoint behind auth

* sendto returns int on Win32

---------

Co-authored-by: Leonardo Amaral <leleobhz@users.noreply.github.com>
Co-authored-by: Brenton Bostick <bostick@gmail.com>

* Central startup update (#1973)

* allow specifying authtoken in central startup

* set allowManagedFrom

* move redis_mem_notification to the correct place

* add node checkins metric

* wire up min/max connection pool size metrics

* x86_64-unknown-linux-gnu on ubuntu runner (#1975)

* adding incoming zt packet type metrics (#1976)

* use cpp-httplib for HTTP control plane (#1979)

refactored the old control plane code to use [cpp-httplib](https://github.com/yhirose/cpp-httplib) instead of a hand rolled HTTP server.  Makes the control plane code much more legible.  Also no longer randomly stops responding.

* Outgoing Packet Metrics (#1980)

add tx/rx labels to packet counters and add metrics for outgoing packets

* Add short-term validation test workflow (#1974)

Add short-term validation test workflow

* Brenton/curly braces (#1971)

* fix formatting

* properly adjust various lines
breakup multiple statements onto multiple lines

* insert {} around if, for, etc.

* Fix rust dependency caching (#1983)

* fun with rust caching

* kick

* comment out invalid yaml keys for now

* Caching should now work

* re-add/rename key directives

* bump

* bump

* bump

* Don't force rebuild on Windows build GH Action (#1985)

Switching `/t:ZeroTierOne:Rebuild` to just `/t:ZeroTierOne` allows the Windows build to use the rust cache.  `/t:ZeroTierOne:Rebuild` cleared the cache before building.

* More packet metrics (#1982)

* found path negotation sends that weren't accounted for

* Fix histogram so it will actually compile

* Found more places for packet metrics

* separate the bind & listen calls on the http backplane (#1988)

* fix memory leak (#1992)

* fix a couple of metrics (#1989)

* More aggressive CLI spamming (#1993)

* fix type signatures (#1991)

* Network-metrics (#1994)

* Add a couple quick functions for converting a uint64_t network ID/node ID into std::string

* Network metrics

* Peer metrics (#1995)

* Adding peer metrics

still need to be wired up for use

* per peer packet metrics

* Fix crash from bad instantiation of histogram

* separate alive & dead path counts

* Add peer metric update block

* add peer latency values in doPingAndKeepalive

* prevent deadlock

* peer latency histogram actually works now

* cleanup

* capture counts of packets to specific peers

---------

Co-authored-by: Joseph Henry <joseph.henry@zerotier.com>

* Metrics consolidation (#1997)

* Rename zt_packet_incoming -> zt_packet

Also consolidate zt_peer_packets into a single metric with tx and rx labels.  Same for ztc_tcp_data and ztc_udp_data

* Further collapse tcp & udp into metric labels for zt_data

* Fix zt_data metric description

* zt_peer_packets description fix

* Consolidate incoming/outgoing network packets to a single metric

* zt_incoming_packet_error -> zt_packet_error

* Disable peer metrics for central controllers

Can change in the future if needed, but given the traffic our controllers serve, that's going to be a *lot* of data

* Disable peer metrics for controllers pt 2

* Update readme files for metrics (#2000)

* Controller Metrics & Network Config Request Fix (#2003)

* add new metrics for network config request queue size and sso expirations
* move sso expiration to its own thread in the controller
* fix potential undefined behavior when modifying a set

* Enable RTTI in Windows build

The new prometheus histogram stuff needs it.

Access violation - no RTTI data!INVALID packet 636ebd9ee8cac6c0 from cafe9efeb9(2605:9880:200:1200:30:571:e34:51/9993) (unexpected exception in tryDecode())

* Don't re-apply routes on BSD

See issue #1986

* Capture setContent by-value instead of by-reference (#2006)

Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* fix typos (#2010)

* central controller metrics & request path updates (#2012)

* internal db metrics

* use shared mutexes for read/write locks

* remove this lock. only used for a metric

* more metrics

* remove exploratory metrics

place controller request benchmarks behind ifdef

* Improve validation test (#2013)

* fix init order for EmbeddedNetworkController (#2014)

* add constant for getifaddrs cache time

* cache getifaddrs - mac

* cache getifaddrs - linux

* cache getifaddrs - bsd

* cache getifaddrs - windows

* Fix oidc client lookup query

join condition referenced the wrong table.  Worked fine unless there were multiple identical client IDs

* Fix udp sent metric

was only incrementing by 1 for each packet sent

* Allow sending all surface addresses to peer in low-bandwidth mode

* allow enabling of low bandwidth mode on controllers

* don't unborrow bad connections

pool will clean them up later

* Multi-arch controller container (#2037)

create arm64 & amd64 images for central controller

* Update README.md

issue #2009

* docker tags change

* fix oidc auth url memory leak (#2031)

getAuthURL() was not calling zeroidc::free_cstr(url);

the only place authAuthURL is called, the url can be retrieved
from the network config instead.

You could alternatively copy the string and call free_cstr in getAuthURL.
If that's better we can change the PR.

Since now there are no callers of getAuthURL I deleted it.

Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* Bump openssl from 0.10.48 to 0.10.55 in /zeroidc (#2034)

Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* zeroidc cargo warnings (#2029)

* fix unused struct member cargo warning

* fix unused import cargo warning

* fix unused return value cargo warning

---------

Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* fix memory leak in macos ipv6/dns helper (#2030)

Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>

* Consider ZEROTIER_JOIN_NETWORKS in healthcheck (#1978)

* Add a 2nd auth token only for access to /metrics (#2043)

* Add a 2nd auth token for /metrics

Allows administrators to distribute a token that only has access to read
metrics and nothing else.

Also added support for using bearer auth tokens for both types of tokens

Separate endpoint for metrics #2041

* Update readme

* fix a couple of cases of writing the wrong token

* Add warning to cli for allow default on FreeBSD

It doesn't work.
Not possible to fix with deficient network
stack and APIs.

ZeroTierOne-freebsd # zerotier-cli set 9bee8941b5xxxxxx allowDefault=1
400 set Allow Default does not work properly on FreeBSD. See #580
root@freebsd13-a:~/ZeroTierOne-freebsd # zerotier-cli get 9bee8941b5xxxxxx allowDefault
1

* ARM64 Support for TapDriver6 (#1949)

* Release memory previously allocated by UPNP_GetValidIGD

* Fix ifdef that breaks libzt on iOS (#2050)

* less drone (#2060)

* Exit if loading an invalid identity from disk (#2058)

* Exit if loading an invalid identity from disk

Previously, if an invalid identity was loaded from disk, ZeroTier would
generate a new identity & chug along and generate a brand new identity
as if nothing happened.  When running in containers, this introduces the
possibility for key matter loss; especially when running in containers
where the identity files are mounted in the container read only.  In
this case, ZT will continue chugging along with a brand new identity
with no possibility of recovering the private key.

ZeroTier should exit upon loading of invalid identity.public/identity.secret #2056

* add validation test for #2056

* tcp-proxy: fix build

* Adjust tcp-proxy makefile to support metrics

There's no way to get the metrics yet. Someone will
have to add the http service.

* remove ZT_NO_METRIC ifdef

* Implement recvmmsg() for Linux to reduce syscalls. (#2046)

Between 5% and 40% speed improvement on Linux, depending on system configuration and load.

* suppress warnings: comparison of integers of different signs: 'int64_t' (aka 'long') and 'uint64_t' (aka 'unsigned long') [-Wsign-compare] (#2063)

* fix warning: 'OS_STRING' macro redefined [-Wmacro-redefined] (#2064)

Even though this is in ext, these particular chunks of code were added
by us, so are ok to modify.

* Apply default route a different way - macOS

The original way we applied default route, by forking
0.0.0.0/0 into 0/1 and 128/1 works, but if mac os has any networking
hiccups -if you change SSIDs or sleep/wake- macos erases the system default route.
And then all networking on the computer is broken.

to summarize the new way:
allowDefault=1
```
sudo route delete default 192.168.82.1
sudo route add default 10.2.0.2
sudo route add -ifscope en1 default 192.168.82.1
```

gives us this routing table
```
Destination        Gateway            RT_IFA             Flags        Refs      Use    Mtu          Netif Expire    rtt(ms) rttvar(ms)
default            10.2.0.2           10.2.0.18          UGScg          90        1   2800       feth4823
default            192.168.82.1       192.168.82.217     UGScIg
```

allowDefault=0
```
sudo route delete default
sudo route delete -ifscope en1 default
sudo route add default 192.168.82.1
```

Notice the I flag, for -ifscope, on the physical default route.

route change does not seem to work reliably.

* fix docker tag for controllers (#2066)

* Update build.sh (#2068)

fix mkwork compilation errors

* Fix network DNS on macOS

It stopped working for ipv4 only networks in Monterey.
See #1696

We add some config like so to System Configuration

```
scutil
show State:/Network/Service/9bee8941b5xxxxxx/IPv4
<dictionary> {
  Addresses : <array> {
    0 : 10.2.1.36
  }
  InterfaceName : feth4823
  Router : 10.2.1.36
  ServerAddress : 127.0.0.1
}

```

* Add search domain to macos dns configuration

Stumbled upon this while debugging something else.
If we add search domain to our system configuration for
network DNS, then search domains work:

```
ping server1                                                                                                                                                                                    ~
PING server1.my.domain (10.123.3.1): 56 data bytes
64 bytes from 10.123.3.1
```

* Fix reporting of secondaryPort and tertiaryPort See: #2039

* Fix typos (#2075)

* Disable executable stacks on assembly objects (#2071)

Add `--noexecstack` to the assembler flags so the resulting binary
will link with a non-executable stack.

Fixes zerotier/ZeroTierOne#1179

Co-authored-by: Joseph Henry <joseph.henry@zerotier.com>

* Test that starting zerotier before internet works

* Don't skip hellos when there are no paths available

working on #2082

* Update validate-1m-linux.sh

* Save zt node log files on abort

* Separate test and summary step in validator script

* Don't apply default route until zerotier is "online"

I was running into issues with restarting the zerotier service while
"full tunnel" mode is enabled.
When zerotier first boots, it gets network state from the cache
on disk. So it immediately applies all the routes it knew about
before it shutdown.
The network config may have change in this time.
If it has, then your default route is via a route
you are blocked from talking on. So you  can't get the current
network config, so your internet does not work.

Other options include
- don't use cached network state on boot
- find a better criteria than "online"

* Fix node time-to-online counter in validator script

* Export variables so that they are accessible by exit function

* Fix PortMapper issue on ZeroTier startup

See issue #2082

We use a call to libnatpmp::ininatpp to make sure the computer
has working network sockets before we go into the main
nat-pmp/upnp logic.

With basic exponenetial delay up to 30 seconds.

* testing

* Comment out PortMapper debug

this got left turned on in a confusing merge previously

* fix macos default route again

see commit fb6af1971 * Fix network DNS on macOS
adding that stuff to System Config causes this extra route to be added
which breaks ipv4 default route.
We figured out a weird System Coniguration setting
that works.

--- old
couldn't figure out how to fix it in SystemConfiguration
so here we are# Please enter the commit message for your changes. Lines starting

We also moved the dns setter to before the syncIps stuff
to help with a race condition. It didn't always work when
you re-joined a network with default route enabled.

* Catch all conditions in switch statement, remove trailing whitespaces

* Add setmtu command, fix bond lifetime issue

* Basic cleanups

* Check if null is passed to VirtualNetworkConfig.equals and name fixes

* ANDROID-96: Simplify and use return code from node_init directly

* Windows arm64 (#2099)

* ARM64 changes for 1.12

* 1.12 Windows advanced installer updates and updates for ARM64

* 1.12.0

* Linux build fixes for old distros.

* release notes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: travis laduke <travisladuke@gmail.com>
Co-authored-by: Grant Limberg <grant.limberg@zerotier.com>
Co-authored-by: Grant Limberg <glimberg@users.noreply.github.com>
Co-authored-by: Leonardo Amaral <leleobhz@users.noreply.github.com>
Co-authored-by: Brenton Bostick <bostick@gmail.com>
Co-authored-by: Sean OMeara <someara@users.noreply.github.com>
Co-authored-by: Joseph Henry <joseph-henry@users.noreply.github.com>
Co-authored-by: Roman Peshkichev <roman.peshkichev@gmail.com>
Co-authored-by: Joseph Henry <joseph.henry@zerotier.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stavros Kois <47820033+stavros-k@users.noreply.github.com>
Co-authored-by: Jake Vis <jakevis@outlook.com>
Co-authored-by: Jörg Thalheim <joerg@thalheim.io>
Co-authored-by: lison <imlison@foxmail.com>
Co-authored-by: Kenny MacDermid <kenny@macdermid.ca>
2023-08-23 14:24:21 -04:00

1262 lines
46 KiB
C++

/* Definition of the connection class.
*
* pqxx::connection encapsulates a connection to a database.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/connection instead.
*
* Copyright (c) 2000-2022, 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_CONNECTION
#define PQXX_H_CONNECTION
#if !defined(PQXX_HEADER_PRE)
# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
#endif
#include <cstddef>
#include <ctime>
#include <functional>
#include <initializer_list>
#include <list>
#include <map>
#include <memory>
#include <string_view>
#include <tuple>
#include <utility>
// Double-check in order to suppress an overzealous Visual C++ warning (#418).
#if defined(PQXX_HAVE_CONCEPTS) && __has_include(<ranges>)
# include <ranges>
#endif
#include "pqxx/errorhandler.hxx"
#include "pqxx/except.hxx"
#include "pqxx/internal/concat.hxx"
#include "pqxx/params.hxx"
#include "pqxx/separated_list.hxx"
#include "pqxx/strconv.hxx"
#include "pqxx/types.hxx"
#include "pqxx/util.hxx"
#include "pqxx/zview.hxx"
/**
* @addtogroup connections
*
* Use of the libpqxx library starts here.
*
* Everything that can be done with a database through libpqxx must go through
* a @ref pqxx::connection object. It connects to a database when you create
* it, and it terminates that communication during destruction.
*
* Many things come together in this class. Handling of error and warning
* messages, for example, is defined by @ref pqxx::errorhandler objects in the
* context of a connection. Prepared statements are also defined here.
*
* When you connect to a database, you pass a connection string containing any
* parameters and options, such as the server address and the database name.
*
* These are identical to the ones in libpq, the C language binding upon which
* libpqxx itself is built:
*
* https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
*
* There are also environment variables you can set to provide defaults, again
* as defined by libpq:
*
* https://www.postgresql.org/docs/current/libpq-envars.html
*
* You can also create a database connection _asynchronously_ using an
* intermediate @ref pqxx::connecting object.
*/
namespace pqxx::internal
{
class sql_cursor;
#if defined(PQXX_HAVE_CONCEPTS)
/// Concept: T is a range of pairs of zero-terminated strings.
template<typename T>
concept ZKey_ZValues = std::ranges::input_range<T> and requires(T t)
{
{std::cbegin(t)};
{
std::get<0>(*std::cbegin(t))
} -> ZString;
{
std::get<1>(*std::cbegin(t))
} -> ZString;
} and std::tuple_size_v<typename std::ranges::iterator_t<T>::value_type>
== 2;
#endif // PQXX_HAVE_CONCEPTS
} // namespace pqxx::internal
namespace pqxx::internal::gate
{
class connection_dbtransaction;
class connection_errorhandler;
class connection_largeobject;
class connection_notification_receiver;
class connection_pipeline;
class connection_sql_cursor;
class connection_stream_from;
class connection_stream_to;
class connection_transaction;
class const_connection_largeobject;
} // namespace pqxx::internal::gate
namespace pqxx
{
/// Representation of a PostgreSQL table path.
/** A "table path" consists of a table name, optionally prefixed by a schema
* name, which in turn is optionally prefixed by a database name.
*
* A minimal example of a table path would be `{mytable}`. But a table path
* may also take the forms `{myschema,mytable}` or
* `{mydb,myschema,mytable}`.
*/
using table_path = std::initializer_list<std::string_view>;
/// Encrypt a password. @deprecated Use connection::encrypt_password instead.
[[nodiscard,
deprecated("Use connection::encrypt_password instead.")]] std::string
PQXX_LIBEXPORT
encrypt_password(char const user[], char const password[]);
/// Encrypt password. @deprecated Use connection::encrypt_password instead.
[[nodiscard,
deprecated("Use connection::encrypt_password instead.")]] inline std::string
encrypt_password(zview user, zview password)
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
return encrypt_password(user.c_str(), password.c_str());
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
/// Error verbosity levels.
enum class error_verbosity : int
{
// These values must match those in libpq's PGVerbosity enum.
terse = 0,
normal = 1,
verbose = 2
};
/// Connection to a database.
/** This is the first class to look at when you wish to work with a database
* through libpqxx. The connection opens during construction, and closes upon
* destruction.
*
* When creating a connection, you can pass a connection URI or a postgres
* connection string, to specify the database server's address, a login
* username, and so on. If you don't, the connection will try to obtain them
* from certain environment variables. If those are not set either, the
* default is to try and connect to the local system's port 5432.
*
* Find more about connection strings here:
*
* https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
*
* The variables are documented here:
*
* https://www.postgresql.org/docs/current/libpq-envars.html
*
* To query or manipulate the database once connected, use one of the
* transaction classes (see pqxx/transaction_base.hxx) and perhaps also the
* transactor framework (see pqxx/transactor.hxx).
*
* When a connection breaks, you will typically get a @ref broken_connection
* exception. This can happen at almost any point.
*
* @warning On Unix-like systems, including GNU and BSD systems, your program
* may receive the SIGPIPE signal when the connection to the backend breaks. By
* default this signal will abort your program. Use "signal(SIGPIPE, SIG_IGN)"
* if you want your program to continue running after a connection fails.
*/
class PQXX_LIBEXPORT connection
{
public:
connection() : connection{""} {}
/// Connect to a database, using `options` string.
explicit connection(char const options[])
{
check_version();
init(options);
}
/// Connect to a database, using `options` string.
explicit connection(zview options) : connection{options.c_str()}
{
// (Delegates to other constructor which calls check_version for us.)
}
/// Move constructor.
/** Moving a connection is not allowed if it has an open transaction, or has
* error handlers or notification receivers registered on it. In those
* situations, other objects may hold references to the old object which
* would become invalid and might produce hard-to-diagnose bugs.
*/
connection(connection &&rhs);
#if defined(PQXX_HAVE_CONCEPTS)
/// Connect to a database, passing options as a range of key/value pairs.
/** @warning Experimental. Requires C++20 "concepts" support. Define
* `PQXX_HAVE_CONCEPTS` to enable it.
*
* There's no need to escape the parameter values.
*
* See the PostgreSQL libpq documentation for the full list of possible
* options:
*
* https://postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
*
* The options can be anything that can be iterated as a series of pairs of
* zero-terminated strings: `std::pair<std::string, std::string>`, or
* `std::tuple<pqxx::zview, char const *>`, or
* `std::map<std::string, pqxx::zview>`, and so on.
*/
template<internal::ZKey_ZValues MAPPING>
inline connection(MAPPING const &params);
#endif // PQXX_HAVE_CONCEPTS
~connection()
{
try
{
close();
}
catch (std::exception const &)
{}
}
/// Move assignment.
/** Neither connection can have an open transaction, registered error
* handlers, or registered notification receivers.
*/
connection &operator=(connection &&rhs);
connection(connection const &) = delete;
connection &operator=(connection const &) = delete;
/// Is this connection open at the moment?
/** @warning This function is **not** needed in most code. Resist the
* temptation to check it after opening a connection. The `connection`
* constructor will throw a @ref broken_connection exception if can't connect
* to the database.
*/
[[nodiscard]] bool PQXX_PURE is_open() const noexcept;
/// Invoke notice processor function. The message should end in newline.
void process_notice(char const[]) noexcept;
/// 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;
/**
* @name Connection properties
*
* These are probably not of great interest, since most are derived from
* information supplied by the client program itself, but they are included
* for completeness.
*
* The connection needs to be currently active for these to work.
*/
//@{
/// Name of database we're connected to, if any.
[[nodiscard]] char const *dbname() const;
/// Database user ID we're connected under, if any.
[[nodiscard]] char const *username() const;
/// Address of server, or nullptr if none specified (i.e. default or local)
[[nodiscard]] char const *hostname() const;
/// Server port number we're connected to.
[[nodiscard]] char const *port() const;
/// Process ID for backend process, or 0 if inactive.
[[nodiscard]] int PQXX_PURE backendpid() const &noexcept;
/// Socket currently used for connection, or -1 for none. Use with care!
/** Query the current socket number. This is intended for event loops based
* on functions such as select() or poll(), where you're waiting for any of
* multiple file descriptors to become ready for communication.
*
* Please try to stay away from this function. It is really only meant for
* event loops that need to wait on more than one file descriptor. If all
* you need is to block until a notification arrives, for instance, use
* await_notification(). If you want to issue queries and retrieve results
* in nonblocking fashion, check out the pipeline class.
*/
[[nodiscard]] int PQXX_PURE sock() const &noexcept;
/// What version of the PostgreSQL protocol is this connection using?
/** The answer can be 0 (when there is no connection); 3 for protocol 3.0; or
* possibly higher values as newer protocol versions come into use.
*/
[[nodiscard]] int PQXX_PURE protocol_version() const noexcept;
/// What version of the PostgreSQL server are we connected to?
/** The result is a bit complicated: each of the major, medium, and minor
* release numbers is written as a two-digit decimal number, and the three
* are then concatenated. Thus server version 9.4.2 will be returned as the
* decimal number 90402. If there is no connection to the server, this
* returns zero.
*
* @warning When writing version numbers in your code, don't add zero at the
* beginning! Numbers beginning with zero are interpreted as octal (base-8)
* in C++. Thus, 070402 is not the same as 70402, and 080000 is not a number
* at all because there is no digit "8" in octal notation. Use strictly
* decimal notation when it comes to these version numbers.
*/
[[nodiscard]] int PQXX_PURE server_version() const noexcept;
//@}
/// @name Text encoding
/**
* Each connection is governed by a "client encoding," which dictates how
* strings and other text is represented in bytes. The database server will
* send text data to you in this encoding, and you should use it for the
* queries and data which you send to the server.
*
* Search the PostgreSQL documentation for "character set encodings" to find
* out more about the available encodings, how to extend them, and how to use
* them. Not all server-side encodings are compatible with all client-side
* encodings or vice versa.
*
* Encoding names are case-insensitive, so e.g. "UTF8" is equivalent to
* "utf8".
*
* You can change the client encoding, but this may not work when the
* connection is in a special state, such as when streaming a table. It's
* not clear what happens if you change the encoding during a transaction,
* and then abort the transaction.
*/
//@{
/// Get client-side character encoding, by name.
[[nodiscard]] std::string get_client_encoding() const;
/// Set client-side character encoding, by name.
/**
* @param encoding Name of the character set encoding to use.
*/
void set_client_encoding(zview encoding) &
{
set_client_encoding(encoding.c_str());
}
/// Set client-side character encoding, by name.
/**
* @param encoding Name of the character set encoding to use.
*/
void set_client_encoding(char const encoding[]) &;
/// Get the connection's encoding, as a PostgreSQL-defined code.
[[nodiscard]] int PQXX_PRIVATE encoding_id() const;
//@}
/// Set session variable, using SQL's `SET` command.
/** @deprecated To set a session variable, use @ref set_session_var. To set
* a transaction-local variable, execute an SQL `SET` command.
*
* @warning When setting a string value, you must escape and quote it first.
* Use the @ref quote() function to do that.
*
* @warning This executes an SQL query, so do not get or set variables while
* a table stream or pipeline is active on the same connection.
*
* @param var Variable to set.
* @param value New value for Var. This can be any SQL expression. If it's
* a string, be sure that it's properly escaped and quoted.
*/
[[deprecated("To set session variables, use set_session_var.")]] void
set_variable(std::string_view var, std::string_view value) &;
/// Set one of the session variables to a new value.
/** This executes SQL, so do not do it while a pipeline or stream is active
* on the connection.
*
* The value you set here will last for the rest of the connection's
* duration, or until you set a new value.
*
* If you set the value while in a @ref dbtransaction (i.e. any transaction
* that is not a @ref nontransaction), then rolling back the transaction will
* undo the change.
*
* All applies to setting _session_ variables. You can also set the same
* variables as _local_ variables, in which case they will always revert to
* their previous value when the transaction ends (or when you overwrite them
* of course). To set a local variable, simply execute an SQL statement
* along the lines of "`SET LOCAL var = 'value'`" inside your transaction.
*
* @param var The variable to set.
* @param value The new value for the variable.
* @throw @ref variable_set_to_null if the value is null; this is not
* allowed.
*/
template<typename TYPE>
void set_session_var(std::string_view var, TYPE const &value) &
{
if constexpr (nullness<TYPE>::has_null)
{
if (nullness<TYPE>::is_null(value))
throw variable_set_to_null{
internal::concat("Attempted to set variable ", var, " to null.")};
}
exec(internal::concat("SET ", quote_name(var), "=", quote(value)));
}
/// Read session variable, using SQL's `SHOW` command.
/** @warning This executes an SQL query, so do not get or set variables while
* a table stream or pipeline is active on the same connection.
*/
[[deprecated("Use get_var instead.")]] std::string
get_variable(std::string_view);
/// Read currently applicable value of a variable.
/** This function executes an SQL statement, so it won't work while a
* @ref pipeline or query stream is active on the connection.
*
* @return a blank `std::optional` if the variable's value is null, or its
* string value otherwise.
*/
std::string get_var(std::string_view var);
/// Read currently applicable value of a variable.
/** This function executes an SQL statement, so it won't work while a
* @ref pipeline or query stream is active on the connection.
*
* If there is any possibility that the variable is null, ensure that `TYPE`
* can represent null values.
*/
template<typename TYPE> TYPE get_var_as(std::string_view var)
{
return from_string<TYPE>(get_var(var));
}
/**
* @name Notifications and Receivers
*/
//@{
/// Check for pending notifications and take appropriate action.
/** This does not block. To wait for incoming notifications, either call
* await_notification() (it calls this function); or wait for incoming data
* on the connection's socket (i.e. wait to read), and then call this
* function repeatedly until it returns zero. After that, there are no more
* pending notifications so you may want to wait again.
*
* If any notifications are pending when you call this function, it
* processes them by finding any receivers that match the notification string
* and invoking those. If no receivers match, there is nothing to invoke but
* we do consider the notification processed.
*
* If any of the client-registered receivers throws an exception, the
* function will report it using the connection's errorhandlers. It does not
* re-throw the exceptions.
*
* @return Number of notifications processed.
*/
int get_notifs();
/// Wait for a notification to come in.
/** There are other events that will also terminate the wait, such as the
* backend failing. It will also wake up periodically.
*
* If a notification comes in, the call will process it, along with any other
* notifications that may have been pending.
*
* To wait for notifications into your own event loop instead, wait until
* there is incoming data on the connection's socket to be read, then call
* @ref get_notifs() repeatedly until it returns zero.
*
* @return Number of notifications processed.
*/
int await_notification();
/// Wait for a notification to come in, or for given timeout to pass.
/** There are other events that will also terminate the wait, such as the
* backend failing, or timeout expiring.
*
* If a notification comes in, the call will process it, along with any other
* notifications that may have been pending.
*
* To wait for notifications into your own event loop instead, wait until
* there is incoming data on the connection's socket to be read, then call
* @ref get_notifs repeatedly until it returns zero.
*
* @return Number of notifications processed
*/
int await_notification(std::time_t seconds, long microseconds);
//@}
/**
* @name Password encryption
*
* Use this when setting a new password for the user if password encryption
* is enabled. Inputs are the SQL name for the user for whom you with to
* encrypt a password; the plaintext password; and the hash algorithm.
*
* The algorithm must be one of "md5", "scram-sha-256" (introduced in
* PostgreSQL 10), or `nullptr`. If the pointer is null, this will query
* the `password_encryption setting` from the server, and use the default
* algorithm as defined there.
*
* @return encrypted version of the password, suitable for encrypted
* PostgreSQL authentication.
*
* Thus you can change a user's password with:
* ```cxx
* void setpw(transaction_base &t, string const &user, string const &pw)
* {
* t.exec0("ALTER USER " + user + " "
* "PASSWORD '" + t.conn().encrypt_password(user,pw) + "'");
* }
* ```
*
* When building this against a libpq older than version 10, this will use
* an older function which only supports md5. In that case, requesting a
* different algorithm than md5 will result in a @ref feature_not_supported
* exception.
*/
//@{
/// Encrypt a password for a given user.
[[nodiscard]] std::string
encrypt_password(zview user, zview password, zview algorithm)
{
return encrypt_password(user.c_str(), password.c_str(), algorithm.c_str());
}
/// Encrypt a password for a given user.
[[nodiscard]] std::string encrypt_password(
char const user[], char const password[], char const *algorithm = nullptr);
//@}
/**
* @name Prepared statements
*
* PostgreSQL supports prepared SQL statements, i.e. statements that you can
* register under a name you choose, optimized once by the backend, and
* executed any number of times under the given name.
*
* Prepared statement definitions are not sensitive to transaction
* boundaries. A statement defined inside a transaction will remain defined
* outside that transaction, even if the transaction itself is subsequently
* aborted. Once a statement has been prepared, it will only go away if you
* close the connection or explicitly "unprepare" the statement.
*
* Use the `pqxx::transaction_base::exec_prepared` functions to execute a
* prepared statement. See @ref prepared for a full discussion.
*
* @warning Using prepared statements can save time, but if your statement
* takes parameters, it may also make your application significantly slower!
* The reason is that the server works out a plan for executing the query
* when you prepare it. At that time, of course it does not know the values
* for the parameters that you will pass. If you execute a query without
* preparing it, then the server works out the plan on the spot, with full
* knowledge of the parameter values.
*
* A statement's definition can refer to its parameters as `$1`, `$2`, etc.
* The first parameter you pass to the call provides a value for `$1`, and
* so on.
*
* Here's an example of how to use prepared statements.
*
* ```cxx
* using namespace pqxx;
* void foo(connection &c)
* {
* c.prepare("findtable", "select * from pg_tables where name=$1");
* work tx{c};
* result r = tx.exec_prepared("findtable", "mytable");
* if (std::empty(r)) throw runtime_error{"mytable not found!"};
* }
* ```
*/
//@{
/// Define a prepared statement.
/**
* @param name unique name for the new prepared statement.
* @param definition SQL statement to prepare.
*/
void prepare(zview name, zview definition) &
{
prepare(name.c_str(), definition.c_str());
}
/**
* @param name unique name for the new prepared statement.
* @param definition SQL statement to prepare.
*/
void prepare(char const name[], char const definition[]) &;
/// Define a nameless prepared statement.
/**
* This can be useful if you merely want to pass large binary parameters to a
* statement without otherwise wishing to prepare it. If you use this
* feature, always keep the definition and the use close together to avoid
* the nameless statement being redefined unexpectedly by code somewhere
* else.
*/
void prepare(char const definition[]) &;
void prepare(zview definition) & { return prepare(definition.c_str()); }
/// Drop prepared statement.
void unprepare(std::string_view name);
//@}
// C++20: constexpr. Breaks ABI.
/// Suffix unique number to name to make it unique within session context.
/** Used internally to generate identifiers for SQL objects (such as cursors
* and nested transactions) based on a given human-readable base name.
*/
[[nodiscard]] std::string adorn_name(std::string_view);
/**
* @defgroup escaping-functions String-escaping functions
*/
//@{
/// Escape string for use as SQL string literal on this connection.
/** @warning This accepts a length, and it does not require a terminating
* zero byte. But if there is a zero byte, escaping stops there even if
* it's not at the end of the string!
*/
[[deprecated("Use std::string_view or pqxx:zview.")]] std::string
esc(char const text[], std::size_t maxlen) const
{
return esc(std::string_view{text, maxlen});
}
/// Escape string for use as SQL string literal on this connection.
[[nodiscard]] std::string esc(char const text[]) const
{
return esc(std::string_view{text});
}
#if defined(PQXX_HAVE_SPAN)
/// Escape string for use as SQL string literal, into `buffer`.
/** Use this variant when you want to re-use the same buffer across multiple
* calls. If that's not the case, or convenience and simplicity are more
* important, use the single-argument variant.
*
* For every byte in `text`, there must be at least 2 bytes of space in
* `buffer`; plus there must be one byte of space for a trailing zero.
* Throws @ref range_error if this space is not available.
*
* Returns a reference to the escaped string, which is actually stored in
* `buffer`.
*/
[[nodiscard]] std::string_view
esc(std::string_view text, std::span<char> buffer)
{
auto const size{std::size(text)}, space{std::size(buffer)};
auto const needed{2 * size + 1};
if (space < needed)
throw range_error{internal::concat(
"Not enough room to escape string of ", size, " byte(s): need ",
needed, " bytes of buffer space, but buffer size is ", space, ".")};
auto const data{buffer.data()};
return {data, esc_to_buf(text, data)};
}
#endif
/// Escape string for use as SQL string literal on this connection.
/** @warning This is meant for text strings only. It cannot contain bytes
* whose value is zero ("nul bytes").
*/
[[nodiscard]] std::string esc(std::string_view text) const;
#if defined(PQXX_HAVE_CONCEPTS)
/// Escape binary string for use as SQL string literal on this connection.
/** This is identical to `esc_raw(data)`. */
template<binary DATA> [[nodiscard]] std::string esc(DATA const &data) const
{
return esc_raw(data);
}
#endif
#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
/// Escape binary string for use as SQL string literal, into `buffer`.
/** Use this variant when you want to re-use the same buffer across multiple
* calls. If that's not the case, or convenience and simplicity are more
* important, use the single-argument variant.
*
* For every byte in `data`, there must be at least two bytes of space in
* `buffer`; plus there must be two bytes of space for a header and one for
* a trailing zero. Throws @ref range_error if this space is not available.
*
* Returns a reference to the escaped string, which is actually stored in
* `buffer`.
*/
template<binary DATA>
[[nodiscard]] zview esc(DATA const &data, std::span<char> buffer) const
{
auto const size{std::size(data)}, space{std::size(buffer)};
auto const needed{internal::size_esc_bin(std::size(data))};
if (space < needed)
throw range_error{internal::concat(
"Not enough room to escape binary string of ", size, " byte(s): need ",
needed, " bytes of buffer space, but buffer size is ", space, ".")};
std::basic_string_view<std::byte> view{std::data(data), std::size(data)};
auto const out{std::data(buffer)};
// Actually, in the modern format, we know beforehand exactly how many
// bytes we're going to fill. Just leave out the trailing zero.
internal::esc_bin(view, out);
return zview{out, needed - 1};
}
#endif
/// Escape binary string for use as SQL string literal on this connection.
[[deprecated("Use std::byte for binary data.")]] std::string
esc_raw(unsigned char const bin[], std::size_t len) const;
/// Escape binary string for use as SQL string literal on this connection.
/** You can also just use @ref esc with a binary string. */
[[nodiscard]] std::string esc_raw(std::basic_string_view<std::byte>) const;
#if defined(PQXX_HAVE_SPAN)
/// Escape binary string for use as SQL string literal, into `buffer`.
/** You can also just use @ref esc with a binary string. */
[[nodiscard]] std::string
esc_raw(std::basic_string_view<std::byte>, std::span<char> buffer) const;
#endif
#if defined(PQXX_HAVE_CONCEPTS)
/// Escape binary string for use as SQL string literal on this connection.
/** You can also just use @ref esc with a binary string. */
template<binary DATA>
[[nodiscard]] std::string esc_raw(DATA const &data) const
{
return esc_raw(
std::basic_string_view<std::byte>{std::data(data), std::size(data)});
}
#endif
#if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
/// Escape binary string for use as SQL string literal, into `buffer`.
template<binary DATA>
[[nodiscard]] zview esc_raw(DATA const &data, std::span<char> buffer) const
{
return this->esc(binary_cast(data), buffer);
}
#endif
/// Unescape binary data, e.g. from a table field or notification payload.
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
* copy of the original binary data.
*/
[[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
unesc_raw(zview text) const
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
return unesc_raw(text.c_str());
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
/// Unescape binary data, e.g. from a table field or notification payload.
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
* copy of the original binary data.
*/
[[nodiscard, deprecated("Use unesc_bin() instead.")]] std::string
unesc_raw(char const text[]) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Unescape binary data, e.g. from a table field or notification payload.
/** Takes a binary string as escaped by PostgreSQL, and returns a restored
* copy of the original binary data.
*
* (The data must be encoded in PostgreSQL's "hex" format. The legacy
* "bytea" escape format, used prior to PostgreSQL 9.0, is no longer
* supported.)
*/
[[nodiscard]] std::basic_string<std::byte>
unesc_bin(std::string_view text) const
{
std::basic_string<std::byte> buf;
buf.resize(pqxx::internal::size_unesc_bin(std::size(text)));
pqxx::internal::unesc_bin(text, buf.data());
return buf;
}
/// Escape and quote a string of binary data.
[[deprecated("Use quote(std::basic_string_view<std::byte>).")]] std::string
quote_raw(unsigned char const bin[], std::size_t len) const;
/// Escape and quote a string of binary data.
std::string quote_raw(std::basic_string_view<std::byte>) const;
#if defined(PQXX_HAVE_CONCEPTS)
/// Escape and quote a string of binary data.
/** You can also just use @ref quote with binary data. */
template<binary DATA>
[[nodiscard]] std::string quote_raw(DATA const &data) const
{
return quote_raw(
std::basic_string_view<std::byte>{std::data(data), std::size(data)});
}
#endif
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Escape and quote an SQL identifier for use in a query.
[[nodiscard]] std::string quote_name(std::string_view identifier) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Escape and quote a table name.
/** When passing just a table name, this is just another name for
* @ref quote_name.
*/
[[nodiscard]] std::string quote_table(std::string_view name) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Escape and quote a table path.
/** A table path consists of a table name, optionally prefixed by a schema
* name; and if both are given, they are in turn optionally prefixed by a
* database name.
*
* Each portion of the path (database name, schema name, table name) will be
* quoted separately, and they will be joined together by dots. So for
* example, `myschema.mytable` will become `"myschema"."mytable"`.
*/
[[nodiscard]] std::string quote_table(table_path) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Quote and comma-separate a series of column names.
/** Use this to save a bit of work in cases where you repeatedly need to pass
* the same list of column names, e.g. with @ref stream_to and @ref
* stream_from. Some functions that need to quote the columns list
* internally, will have a "raw" alternative which let you do the quoting
* yourself. It's a bit of extra work, but it can in rare cases let you
* eliminate some duplicate work in quoting them repeatedly.
*/
template<PQXX_CHAR_STRINGS_ARG STRINGS>
inline std::string quote_columns(STRINGS const &columns) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Represent object as SQL string, including quoting & escaping.
/**
* Recognises nulls and represents them as SQL nulls. They get no quotes.
*/
template<typename T>
[[nodiscard]] inline std::string quote(T const &t) const;
[[deprecated("Use std::byte for binary data.")]] std::string
quote(binarystring const &) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Escape and quote binary data for use as a BYTEA value in SQL statement.
[[nodiscard]] std::string
quote(std::basic_string_view<std::byte> bytes) const;
// TODO: Make "into buffer" variant to eliminate a string allocation.
/// Escape string for literal LIKE match.
/** Use this when part of an SQL "LIKE" pattern should match only as a
* literal string, not as a pattern, even if it contains "%" or "_"
* characters that would normally act as wildcards.
*
* The string does not get string-escaped or quoted. You do that later.
*
* For instance, let's say you have a string `name` entered by the user,
* and you're searching a `file` column for items that match `name`
* followed by a dot and three letters. Even if `name` contains wildcard
* characters "%" or "_", you only want those to match literally, so "_"
* only matches "_" and "%" only matches a single "%".
*
* You do that by "like-escaping" `name`, appending the wildcard pattern
* `".___"`, and finally, escaping and quoting the result for inclusion in
* your query:
*
* ```cxx
* tx.exec(
* "SELECT file FROM item WHERE file LIKE " +
* tx.quote(tx.esc_like(name) + ".___"));
* ```
*
* The SQL "LIKE" operator also lets you choose your own escape character.
* This is supported, but must be a single-byte character.
*/
[[nodiscard]] std::string
esc_like(std::string_view text, char escape_char = '\\') const;
//@}
/// Attempt to cancel the ongoing query, if any.
/** You can use this from another thread, and/or while a query is executing
* in a pipeline, but it's up to you to ensure that you're not canceling the
* wrong query. This may involve locking.
*/
void cancel_query();
#if defined(_WIN32) || __has_include(<fcntl.h>)
/// Set socket to blocking (true) or nonblocking (false).
/** @warning Do not use this unless you _really_ know what you're doing.
* @warning This function is available on most systems, but not necessarily
* all.
*/
void set_blocking(bool block) &;
#endif // defined(_WIN32) || __has_include(<fcntl.h>)
/// Set session verbosity.
/** Set the verbosity of error messages to "terse", "normal" (the default),
* or "verbose."
*
* If "terse", returned messages include severity, primary text, and
* position only; this will normally fit on a single line. "normal" produces
* messages that include the above plus any detail, hint, or context fields
* (these might span multiple lines). "verbose" includes all available
* fields.
*/
void set_verbosity(error_verbosity verbosity) &noexcept;
/// Return pointers to the active errorhandlers.
/** The entries are ordered from oldest to newest handler.
*
* You may use this to find errorhandlers that your application wants to
* delete when destroying the connection. Be aware, however, that libpqxx
* may also add errorhandlers of its own, and those will be included in the
* list. If this is a problem for you, derive your errorhandlers from a
* custom base class derived from pqxx::errorhandler. Then use dynamic_cast
* to find which of the error handlers are yours.
*
* The pointers point to the real errorhandlers. The container it returns
* however is a copy of the one internal to the connection, not a reference.
*/
[[nodiscard]] std::vector<errorhandler *> get_errorhandlers() const;
/// Return a connection string encapsulating this connection's options.
/** The connection must be currently open for this to work.
*
* Returns a reconstruction of this connection's connection string. It may
* not exactly match the connection string you passed in when creating this
* connection.
*/
[[nodiscard]] std::string connection_string() const;
/// Explicitly close the connection.
/** The destructor will do this for you automatically. Still, there is a
* reason to `close()` objects explicitly where possible: if an error should
* occur while closing, `close()` can throw an exception. A destructor
* cannot.
*
* Closing a connection is idempotent. Closing a connection that's already
* closed does nothing.
*/
void close();
/// Seize control of a raw libpq connection.
/** @warning Do not do this. Please. It's for very rare, very specific
* use-cases. The mechanism may change (or break) in unexpected ways in
* future versions.
*
* @param raw_conn a raw libpq `PQconn` pointer.
*/
static connection seize_raw_connection(internal::pq::PGconn *raw_conn)
{
return connection{raw_conn};
}
/// Release the raw connection without closing it.
/** @warning Do not do this. It's for very rare, very specific use-cases.
* The mechanism may change (or break) in unexpected ways in future versions.
*
* The `connection` object becomes unusable after this.
*/
internal::pq::PGconn *release_raw_connection() &&
{
return std::exchange(m_conn, nullptr);
}
private:
friend class connecting;
enum connect_mode
{
connect_nonblocking
};
connection(connect_mode, zview connection_string);
/// For use by @ref seize_raw_connection.
explicit connection(internal::pq::PGconn *raw_conn) : m_conn{raw_conn} {}
/// Poll for ongoing connection, try to progress towards completion.
/** Returns a pair of "now please wait to read data from socket" and "now
* please wait to write data to socket." Both will be false when done.
*
* Throws an exception if polling indicates that the connection has failed.
*/
std::pair<bool, bool> poll_connect();
// Initialise based on connection string.
void init(char const options[]);
// Initialise based on parameter names and values.
void init(char const *params[], char const *values[]);
void complete_init();
result make_result(
internal::pq::PGresult *pgr, std::shared_ptr<std::string> const &query,
std::string_view desc = ""sv);
void PQXX_PRIVATE set_up_state();
int PQXX_PRIVATE PQXX_PURE status() const noexcept;
/// Escape a string, into a buffer allocated by the caller.
/** The buffer must have room for at least `2*std::size(text) + 1` bytes.
*
* Returns the number of bytes written, including the trailing zero.
*/
std::size_t esc_to_buf(std::string_view text, char *buf) const;
friend class internal::gate::const_connection_largeobject;
char const *PQXX_PURE err_msg() const noexcept;
void PQXX_PRIVATE process_notice_raw(char const msg[]) noexcept;
result exec_prepared(std::string_view statement, internal::c_params const &);
/// Throw @ref usage_error if this connection is not in a movable state.
void check_movable() const;
/// Throw @ref usage_error if not in a state where it can be move-assigned.
void check_overwritable() const;
friend class internal::gate::connection_errorhandler;
void PQXX_PRIVATE register_errorhandler(errorhandler *);
void PQXX_PRIVATE unregister_errorhandler(errorhandler *) noexcept;
friend class internal::gate::connection_transaction;
result exec(std::string_view, std::string_view = ""sv);
result
PQXX_PRIVATE exec(std::shared_ptr<std::string>, std::string_view = ""sv);
void PQXX_PRIVATE register_transaction(transaction_base *);
void PQXX_PRIVATE unregister_transaction(transaction_base *) noexcept;
friend class internal::gate::connection_stream_from;
std::pair<std::unique_ptr<char, std::function<void(char *)>>, std::size_t>
PQXX_PRIVATE read_copy_line();
friend class internal::gate::connection_stream_to;
void PQXX_PRIVATE write_copy_line(std::string_view);
void PQXX_PRIVATE end_copy_write();
friend class internal::gate::connection_largeobject;
internal::pq::PGconn *raw_connection() const { return m_conn; }
friend class internal::gate::connection_notification_receiver;
void add_receiver(notification_receiver *);
void remove_receiver(notification_receiver *) noexcept;
friend class internal::gate::connection_pipeline;
void PQXX_PRIVATE start_exec(char const query[]);
bool PQXX_PRIVATE consume_input() noexcept;
bool PQXX_PRIVATE is_busy() const noexcept;
internal::pq::PGresult *get_result();
friend class internal::gate::connection_dbtransaction;
friend class internal::gate::connection_sql_cursor;
result exec_params(std::string_view query, internal::c_params const &args);
/// Connection handle.
internal::pq::PGconn *m_conn = nullptr;
/// Active transaction on connection, if any.
/** We don't use this for anything, except to check for open transactions
* when we close the connection or start a new transaction.
*
* We also don't allow move construction or move assignment while there's a
* transaction, since moving the connection in that case would leave one or
* more pointers back from the transaction to the connection dangling.
*/
transaction_base const *m_trans = nullptr;
std::list<errorhandler *> m_errorhandlers;
using receiver_list =
std::multimap<std::string, pqxx::notification_receiver *>;
/// Notification receivers.
receiver_list m_receivers;
/// Unique number to use as suffix for identifiers (see adorn_name()).
int m_unique_id = 0;
};
/// @deprecated Old base class for connection. They are now the same class.
using connection_base = connection;
/// An ongoing, non-blocking stepping stone to a connection.
/** Use this when you want to create a connection to the database, but without
* blocking your whole thread. It is only available on systems that have
* the `<fcntl.h>` header, and Windows.
*
* Connecting in this way is probably not "faster" (it's more complicated and
* has some extra overhead), but in some situations you can use it to make your
* application as a whole faster. It all depends on having other useful work
* to do in the same thread, and being able to wait on a socket. If you have
* other I/O going on at the same time, your event loop can wait for both the
* libpqxx socket and your own sockets, and wake up whenever any of them is
* ready to do work.
*
* Connecting in this way is not properly "asynchronous;" it's merely
* "nonblocking." This means it's not a super-high-performance mechanism like
* you might get with e.g. `io_uring`. In particular, if we need to look up
* the database hostname in DNS, that will happen synchronously.
*
* To use this, create the `connecting` object, passing a connection string.
* Then loop: If @ref wait_to_read returns true, wait for the socket to have
* incoming data on it. If @ref wait_to_write returns true, wait for the
* socket to be ready for writing. Then call @ref process to process any
* incoming or outgoing data. Do all of this until @ref done returns true (or
* there is an exception). Finally, call @ref produce to get the completed
* connection.
*
* For example:
*
* ```cxx
* pqxx::connecting cg{};
*
* // Loop until we're done connecting.
* while (!cg.done())
* {
* wait_for_fd(cg.sock(), cg.wait_to_read(), cg.wait_to_write());
* cg.process();
* }
*
* pqxx::connection conn = std::move(cg).produce();
*
* // At this point, conn is a working connection. You can no longer use
* // cg at all.
* ```
*/
class PQXX_LIBEXPORT connecting
{
public:
/// Start connecting.
connecting(zview connection_string = ""_zv);
connecting(connecting const &) = delete;
connecting(connecting &&) = default;
connecting &operator=(connecting const &) = delete;
connecting &operator=(connecting &&) = default;
/// Get the socket. The socket may change during the connection process.
[[nodiscard]] int sock() const &noexcept { return m_conn.sock(); }
/// Should we currently wait to be able to _read_ from the socket?
[[nodiscard]] constexpr bool wait_to_read() const &noexcept
{
return m_reading;
}
/// Should we currently wait to be able to _write_ to the socket?
[[nodiscard]] constexpr bool wait_to_write() const &noexcept
{
return m_writing;
}
/// Progress towards completion (but don't block).
void process() &;
/// Is our connection finished?
[[nodiscard]] constexpr bool done() const &noexcept
{
return not m_reading and not m_writing;
}
/// Produce the completed connection object.
/** Use this only once, after @ref done returned `true`. Once you have
* called this, the `connecting` instance has no more use or meaning. You
* can't call any of its member functions afterwards.
*
* This member function is rvalue-qualified, meaning that you can only call
* it on an rvalue instance of the class. If what you have is not an rvalue,
* turn it into one by wrapping it in `std::move()`.
*/
[[nodiscard]] connection produce() &&;
private:
connection m_conn;
bool m_reading{false};
bool m_writing{true};
};
template<typename T> inline std::string connection::quote(T const &t) const
{
if constexpr (nullness<T>::always_null)
{
return "NULL";
}
else
{
if (is_null(t))
return "NULL";
auto const text{to_string(t)};
// Okay, there's an easy way to do this and there's a hard way. The easy
// way was "quote, esc(to_string(t)), quote". I'm going with the hard way
// because it's going to save some string manipulation that will probably
// incur some unnecessary memory allocations and deallocations.
std::string buf{'\''};
buf.resize(2 + 2 * std::size(text) + 1);
auto const content_bytes{esc_to_buf(text, buf.data() + 1)};
auto const closing_quote{1 + content_bytes};
buf[closing_quote] = '\'';
auto const end{closing_quote + 1};
buf.resize(end);
return buf;
}
}
template<PQXX_CHAR_STRINGS_ARG STRINGS>
inline std::string connection::quote_columns(STRINGS const &columns) const
{
return separated_list(
","sv, std::cbegin(columns), std::cend(columns),
[this](auto col) { return this->quote_name(*col); });
}
#if defined(PQXX_HAVE_CONCEPTS)
template<internal::ZKey_ZValues MAPPING>
inline connection::connection(MAPPING const &params)
{
check_version();
std::vector<char const *> keys, values;
if constexpr (std::ranges::sized_range<MAPPING>)
{
auto const size{std::ranges::size(params) + 1};
keys.reserve(size);
values.reserve(size);
}
for (auto const &[key, value] : params)
{
keys.push_back(internal::as_c_string(key));
values.push_back(internal::as_c_string(value));
}
keys.push_back(nullptr);
values.push_back(nullptr);
init(std::data(keys), std::data(values));
}
#endif // PQXX_HAVE_CONCEPTS
} // namespace pqxx
#endif