From abeec6fa36332f03e78cba0023664b6140a04f5a Mon Sep 17 00:00:00 2001 From: antoshkka Date: Fri, 11 Oct 2024 22:55:22 +0300 Subject: [PATCH] feat postgresql: clarify docs on timezones and move the pages to .md files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests: протестировано CI commit_hash:8b0380809bed21954a7a1cefe7d6b1dacab9ca06 --- .mapping.json | 1 + .../userver/storages/postgres/io/chrono.hpp | 61 +----- .../storages/postgres/io/integral_types.hpp | 5 +- .../storages/postgres/io/supported_types.hpp | 170 +-------------- .../userver/storages/postgres/postgres.hpp | 4 +- .../userver/storages/postgres/result_set.hpp | 7 +- .../storages/postgres/typed_result_set.hpp | 5 +- .../storages/postgres/tests/chrono_pgtest.cpp | 105 +++++++++ scripts/docs/en/index.md | 2 +- scripts/docs/en/userver/pg_types.md | 204 ++++++++++++++++++ scripts/docs/en/userver/pg_user_types.md | 6 +- 11 files changed, 338 insertions(+), 232 deletions(-) create mode 100644 scripts/docs/en/userver/pg_types.md diff --git a/.mapping.json b/.mapping.json index 7a894fca8d6f..f9fe6b590fcd 100644 --- a/.mapping.json +++ b/.mapping.json @@ -3299,6 +3299,7 @@ "scripts/docs/en/userver/os_signals.md":"taxi/uservices/userver/scripts/docs/en/userver/os_signals.md", "scripts/docs/en/userver/periodics.md":"taxi/uservices/userver/scripts/docs/en/userver/periodics.md", "scripts/docs/en/userver/pg_connlimit_mode_auto.md":"taxi/uservices/userver/scripts/docs/en/userver/pg_connlimit_mode_auto.md", + "scripts/docs/en/userver/pg_types.md":"taxi/uservices/userver/scripts/docs/en/userver/pg_types.md", "scripts/docs/en/userver/pg_user_types.md":"taxi/uservices/userver/scripts/docs/en/userver/pg_user_types.md", "scripts/docs/en/userver/profile_context_switches.md":"taxi/uservices/userver/scripts/docs/en/userver/profile_context_switches.md", "scripts/docs/en/userver/publications.md":"taxi/uservices/userver/scripts/docs/en/userver/publications.md", diff --git a/postgresql/include/userver/storages/postgres/io/chrono.hpp b/postgresql/include/userver/storages/postgres/io/chrono.hpp index b5af2af585dd..2def2c0154cb 100644 --- a/postgresql/include/userver/storages/postgres/io/chrono.hpp +++ b/postgresql/include/userver/storages/postgres/io/chrono.hpp @@ -30,11 +30,11 @@ using ClockType = std::chrono::system_clock; using TimePoint = ClockType::time_point; using IntervalType = std::chrono::microseconds; -/// @brief Corresponds to TIMESTAMP WITH TIME ZONE (value in absolute time (no -/// TZ offset)). Time zones are applied automatically. +/// @brief Corresponds to TIMESTAMP WITH TIME ZONE database type. /// -/// @warning It's important that no conversion to TIMESTAMP WITHOUT TIME ZONE is -/// performed on the DB side, see @ref pg_timestamp +/// @warning Make sure that no unwanted +/// `TIMESTAMP` <> `TIMESTAMP WITHOUT TIME ZONE` conversions are performed on +/// the DB side, see @ref pg_timestamp. /// /// @see @ref Now struct TimePointTz final @@ -52,11 +52,11 @@ logging::LogHelper& operator<<(logging::LogHelper&, TimePointTz); /// @brief gtest logging support for TimePointTz. std::ostream& operator<<(std::ostream&, TimePointTz); -/// @brief Corresponds to TIMESTAMP WITHOUT TIME ZONE (value in absolute time -/// (no TZ offset)). +/// @brief Corresponds to TIMESTAMP WITHOUT TIME ZONE database type. /// -/// @warning It's important that no conversion to TIMESTAMP WITH TIME ZONE is -/// performed on the DB side, see @ref pg_timestamp +/// @warning Make sure that no unwanted +/// `TIMESTAMP` <> `TIMESTAMP WITHOUT TIME ZONE` conversions are performed on +/// the DB side, see @ref pg_timestamp. /// /// @see @ref NowWithoutTz struct TimePointWithoutTz final @@ -98,51 +98,6 @@ inline constexpr TimePoint kTimestampPositiveInfinity = TimePoint::max(); /// https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE inline constexpr TimePoint kTimestampNegativeInfinity = TimePoint::min(); -/** - * @page pg_timestamp uPg timestamp support - * - * The driver provides mapping from C++ std::chrono::time_point template type to - * Postgres timestamp (without time zone) data type. - * - * To read/write timestamp with time zone Postgres data type a TimePointTz - * helper type is provided. - * - * Postgres internal timestamp resolution is microseconds. - * - * **Example:** - * - * @code - * namespace pg = storages::postgres; - * - * pg::Transaction trx = ...; - * - * auto now = std::chrono::system_clock::now(); - * // Send as TIMESTAMP WITH TIME ZONE - * res = trx.Execute("select $1", pg::TimePointTz{now}); - * // Send as TIMESTAMP WITHOUT TIME ZONE - * auto res = trx.Execute("select $1", pg::TimePointWithoutTz{now}); - * // For reading, there is no difference between these time types - * res[0].To(now); - * @endcode - * - * ## How not to get skewed times in the database - * - * Postgres has two types corresponding to absolute time - * (a.k.a. global time; Unix time; NOT local time): - * * `TIMESTAMP WITH TIME ZONE` - * * `TIMESTAMP` - * - * An unfortunate design decision on the Postgres side is that it allows - * implicit conversion between them, and it applies an offset to the time point - * when doing so, depending on the timezone of the Postgres database. - * - * Because of this you MUST ensure that you always use the correct type: - * * storages::postgres::TimePointTz for `TIMESTAMP WITH TIME ZONE`; - * * storages::postgres::TimePointWithoutTz for `TIMESTAMP`. - * - * Otherwise, you'll get skewed times in database! - */ - namespace io { namespace detail { diff --git a/postgresql/include/userver/storages/postgres/io/integral_types.hpp b/postgresql/include/userver/storages/postgres/io/integral_types.hpp index e3c6f9059abb..e79f0728d9fe 100644 --- a/postgresql/include/userver/storages/postgres/io/integral_types.hpp +++ b/postgresql/include/userver/storages/postgres/io/integral_types.hpp @@ -66,7 +66,10 @@ struct IntegralBinaryParser : BufferParserBase { this->value = IntegralBySizeParser<8>::ParseBuffer(buf); break; default: - throw InvalidInputBufferSize{buf.length, "for an integral value type"}; + throw InvalidInputBufferSize{ + buf.length, + "for an integral value type (expecting size 2, 4, or 8). Not an " + "integer was returned from query."}; } } }; diff --git a/postgresql/include/userver/storages/postgres/io/supported_types.hpp b/postgresql/include/userver/storages/postgres/io/supported_types.hpp index 2d7243f4f771..1e3d0a851667 100644 --- a/postgresql/include/userver/storages/postgres/io/supported_types.hpp +++ b/postgresql/include/userver/storages/postgres/io/supported_types.hpp @@ -1,172 +1,8 @@ #pragma once -// clang-format off -/// @page pg_types uPg: Supported data types -/// -/// uPg provides data type support with a system of buffer parsers and -/// formatters. -/// Please refer to @ref pg_io for more information about the system. -/// -/// @see @ref userver_postgres_parse_and_format -/// -/// @par Fundamental PostgreSQL types -/// -/// The fundamental PostgreSQL types support is provided by the driver. The -/// table below shows supported Postgres types and their mapping to C++ types -/// provided by the driver. Column "Default" marks the Postgres type to which -/// a C++ type is mapped when used as a parameter. Where the C++ type is N/A -/// it means that the PosgreSQL data type is not supported. When there is a -/// C++ type in parenthesis, it is a data type that will be supported later -/// and the C++ type is planned counterpart. -/// PG type | C++ type | Default | -/// ----------------- | --------------------------------------- | ------- | -/// smallint | std::int16_t | + | -/// integer | std::int32_t | + | -/// bigint | std::int64_t | + | -/// smallserial | std::int16_t | | -/// serial | std::int32_t | | -/// bigserial | std::int64_t | | -/// boolean | bool | + | -/// real | float | + | -/// double precision | double | + | -/// numeric(p) | decimal64::Decimal | + | -/// decimal(p) | decimal64::Decimal | + | -/// money | N/A | | -/// text | std::string | + | -/// char(n) | std::string | | -/// varchar(n) | std::string | | -/// "char" | char | + | -/// timestamp | std::chrono::system_clock::time_point | + | -/// timestamptz | storages::postgres::TimePointTz | + | -/// date | utils::datetime::Date | + | -/// time | utils::datetime::TimeOfDay | + | -/// timetz | N/A | | -/// interval | std::chrono::microseconds | | -/// bytea | container of one-byte type | | -/// bit(n) | utils::Flags | | -/// ^ | std::bitset | | -/// ^ | std::array | | -/// bit varying(n) | utils::Flags | | -/// ^ | std::bitset | | -/// ^ | std::array | | -/// uuid | boost::uuids::uuid | + | -/// json | formats::json::Value | | -/// jsonb | formats::json::Value | + | -/// int4range | storages::postgres::IntegerRange | | -/// ^ | storages::postgres::BoundedIntegerRange | | -/// int8range | storages::postgres::BigintRange | | -/// ^ | storages::postgres::BoundedBigintRange | | -/// inet | utils::ip::AddressV4 | | -/// ^ | utils::ip::AddressV6 | | -/// cidr | utils::ip::NetworkV4 | | -/// ^ | utils::ip::NetworkV6 | | -/// macaddr | utils::Macaddr | | -/// macaddr8 | utils::Macaddr8 | | -/// numrange | N/A | | -/// tsrange | N/A | | -/// tstzrange | N/A | | -/// daterange | N/A | | -/// -/// @warning The library doesn't provide support for C++ unsigned integral -/// types intentionally as PostgreSQL doesn't provide unsigned types and -/// using the types with the database is error-prone. -/// -/// For more information on timestamps and working with time zones please see -/// @ref pg_timestamp -/// -/// @anchor pg_arrays -/// @par Arrays -/// -/// The driver supports PostgreSQL arrays provided that the element type is -/// supported by the driver, including user types. -/// -/// Array parser will throw storages::postgres::DimensionMismatch if the -/// dimensions of C++ container do not match that of the buffer received from -/// the server. -/// -/// Array formatter will throw storages::postgres::InvalidDimensions if -/// containers on same level of depth have different sizes. -/// -/// @par User-defined PostgreSQL types -/// -/// The driver provides support for user-defined PostgreSQL types: -/// - domains -/// - enumerations -/// - composite types -/// - custom ranges -/// -/// For more information please see -/// @ref scripts/docs/en/userver/pg_user_types.md. -/// -/// @par C++ strong typedefs -/// -/// The driver provides support for C++ strong typedef idiom. For more -/// information see @ref pg_strong_typedef -/// -/// @par PostgreSQL ranges -/// -/// PostgreSQL range type support is provided by `storages::postgres::Range` -/// template. -/// -/// @par Geometry types -/// -/// For geometry types the driver provides parsing/formatting from/to -/// on-the-wire representation. The types provided do not define any calculus. -/// -/// @anchor pg_bytea -/// @par PostgreSQL bytea support -/// -/// The driver allows reading and writing raw binary data from/to PostgreSQL -/// `bytea` type. -/// -/// Reading and writing to PostgreSQL is implemented for `std::string`, -/// `std::string_view` and `std::vector` of `char` or `unsigned char`. -/// -/// @warning When reading to `std::string_view` the value MUST NOT be used after -/// the PostgreSQL result set is destroyed. -/// -/// -/// -/// -/// Bytea() is a helper function for reading and writing binary data from/to a database. -/// -/// Example usage of Bytea(): -/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_simple -/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_string -/// @snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_vector -/// -/// -/// -/// -/// -/// @par Network types -/// -/// The driver offers data types to store IPv4, IPv6, and MAC addresses, as -/// well as network specifications (CIDR). -/// -/// @par Bit string types -/// -/// The driver supports PostgreSQL `bit` and `bit varying` types. -/// -/// Parsing and formatting is implemented for integral values -/// (e.g. `uint32_t`, `uint64_t`), `utils::Flags`, `std::array` -/// and `std::bitset`. -/// -/// Example of using the bit types from tests: -/// @snippet storages/postgres/tests/bitstring_pgtest.cpp Bit string sample -/// -/// @par PostgreSQL types not covered above -/// -/// The types not covered above or marked as N/A in the table of fundamental -/// types will be eventually supported later, on request from the driver's -/// users. -/// -/// ---------- -/// -/// @htmlonly
@endhtmlonly -/// ⇦ @ref pg_process_results | @ref pg_user_row_types ⇨ -/// @htmlonly
@endhtmlonly -// clang-format on +/// @file userver/storages/postgres/io/supported_types.hpp +/// @brief Includes parsers and formatters for all supported data types; +/// prefer using the type specific include to avoid compilation time slowdown. //@{ /** @name Traits etc */ diff --git a/postgresql/include/userver/storages/postgres/postgres.hpp b/postgresql/include/userver/storages/postgres/postgres.hpp index 89bb5943015e..d629526ce013 100644 --- a/postgresql/include/userver/storages/postgres/postgres.hpp +++ b/postgresql/include/userver/storages/postgres/postgres.hpp @@ -55,8 +55,8 @@ /// - @ref pg_transactions /// - @ref pg_run_queries /// - @ref pg_process_results -/// - @ref pg_types -/// - @ref pg_user_types +/// - @ref scripts/docs/en/userver/pg_types.md +/// - @ref scripts/docs/en/userver/pg_user_types.md /// - @ref pg_errors /// - @ref pg_topology /// diff --git a/postgresql/include/userver/storages/postgres/result_set.hpp b/postgresql/include/userver/storages/postgres/result_set.hpp index b12cccda51e1..43fd96e0bf4f 100644 --- a/postgresql/include/userver/storages/postgres/result_set.hpp +++ b/postgresql/include/userver/storages/postgres/result_set.hpp @@ -73,8 +73,9 @@ namespace storages::postgres { /// @par Extracting field's data to variables /// /// A Field object provides an interface to convert underlying buffer to a -/// C++ variable of supported type. Please see @ref pg_types for more -/// information on supported types. +/// C++ variable of supported type. Please see +/// @ref scripts/docs/en/userver/pg_types.md for more information on supported +/// types. /// /// Functions Field::As and Field::To can throw an exception if the field /// value is `null`. Their Field::Coalesce counterparts instead set the result @@ -181,7 +182,7 @@ namespace storages::postgres { /// ---------- /// /// @htmlonly
@endhtmlonly -/// ⇦ @ref pg_run_queries | @ref pg_types ⇨ +/// ⇦ @ref pg_run_queries | @ref scripts/docs/en/userver/pg_types.md ⇨ /// @htmlonly
@endhtmlonly struct FieldDescription { diff --git a/postgresql/include/userver/storages/postgres/typed_result_set.hpp b/postgresql/include/userver/storages/postgres/typed_result_set.hpp index e6bad0688405..9e57aa8f04d5 100644 --- a/postgresql/include/userver/storages/postgres/typed_result_set.hpp +++ b/postgresql/include/userver/storages/postgres/typed_result_set.hpp @@ -39,7 +39,8 @@ namespace storages::postgres { /// - non-aggregate class with some augmentation. /// /// Data members of the tuple or the classes must be supported by the driver. -/// For more information on supported data types please see @ref pg_types +/// For more information on supported data types please see +/// @ref scripts/docs/en/userver/pg_types.md. /// /// @par std::tuple. /// @@ -143,7 +144,7 @@ namespace storages::postgres { /// ---------- /// /// @htmlonly
@endhtmlonly -/// ⇦ @ref pg_types | @ref pg_errors ⇨ +/// ⇦ @ref scripts/docs/en/userver/pg_types.md | @ref pg_errors ⇨ /// @htmlonly
@endhtmlonly template diff --git a/postgresql/src/storages/postgres/tests/chrono_pgtest.cpp b/postgresql/src/storages/postgres/tests/chrono_pgtest.cpp index da5b4e46aed5..1af1eeec4361 100644 --- a/postgresql/src/storages/postgres/tests/chrono_pgtest.cpp +++ b/postgresql/src/storages/postgres/tests/chrono_pgtest.cpp @@ -95,6 +95,111 @@ TEST(PostgreIO, ChronoTz) { EXPECT_TRUE(EqualToMicroseconds(now.GetUnderlying(), tgt.GetUnderlying())); } +UTEST_P(PostgreConnection, ChronoTzSample) { + CheckConnection(GetConn()); + auto& connection = GetConn(); + + /// [tz sample] + namespace pg = storages::postgres; + + // Postgres only supports microsecond resolution + const auto now = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + + connection->Execute(R"( + CREATE TABLE tz_sample( + with_tz TIMESTAMP WITH TIME ZONE, + without_tz TIMESTAMP WITHOUT TIME ZONE + ) + )"); + + // No conversion performed + constexpr auto kInsertQuery = R"( + INSERT INTO tz_sample(with_tz, without_tz) VALUES($1, $2) + RETURNING with_tz, without_tz + )"; + const auto res = connection->Execute(kInsertQuery, pg::TimePointTz{now}, + pg::TimePointWithoutTz{now}); + + // Both values contain the correct time point + EXPECT_EQ(res[0][0].As().GetUnderlying(), now); + EXPECT_EQ(res[0][1].As().GetUnderlying(), now); + /// [tz sample] + + // Repeating above checks with more diagnostics + EXPECT_EQ(std::chrono::time_point_cast( + res[0][0].As().GetUnderlying()) + .time_since_epoch() + .count(), + now.time_since_epoch().count()); + EXPECT_EQ(std::chrono::time_point_cast( + res[0][1].As().GetUnderlying()) + .time_since_epoch() + .count(), + now.time_since_epoch().count()); + + connection->Execute("DROP TABLE tz_sample"); +} + +template +auto ToSeconds(const T& res) { + const auto underlying = res[0].template As().GetUnderlying(); + const auto epoch = underlying.time_since_epoch(); + return std::chrono::duration_cast(epoch).count(); +} + +UTEST_P(PostgreConnection, ChronoTzConversions) { + CheckConnection(GetConn()); + auto& connection = GetConn(); + + /// [tz skewed] + namespace pg = storages::postgres; + const auto tz_offset_res = + connection->Execute("select extract(timezone from now())::integer"); + const auto tz_offset = std::chrono::seconds{tz_offset_res[0].As()}; + + const auto now = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + connection->Execute(R"( + CREATE TABLE tz_conversion_sample( + with_tz TIMESTAMP WITH TIME ZONE, + without_tz TIMESTAMP WITHOUT TIME ZONE + ) + )"); + + constexpr auto kInsertQuery = R"( + INSERT INTO tz_conversion_sample(with_tz, without_tz) VALUES($1, $2) + RETURNING with_tz, without_tz + )"; + // ERROR! Types missmatch and are implicitly converted: + // * TimePointWithoutTz is converted on the DB side and TZ substracted. + // * TimePointTz is converted on the DB side and TZ added. + const auto res = connection->Execute( + kInsertQuery, pg::TimePointWithoutTz{now}, pg::TimePointTz{now}); + + // Both values were skewed + using time_point = std::chrono::system_clock::time_point; + EXPECT_EQ(res[0][0].As() + tz_offset, now); + EXPECT_EQ(res[0][1].As() - tz_offset, now); + /// [tz skewed] + + // Repeating above checks with more diagnostics + EXPECT_EQ(std::chrono::time_point_cast( + res[0][0].As() + tz_offset) + .time_since_epoch() + .count(), + now.time_since_epoch().count()) + << "Offset is " << tz_offset.count(); + EXPECT_EQ(std::chrono::time_point_cast( + res[0][1].As() - tz_offset) + .time_since_epoch() + .count(), + now.time_since_epoch().count()) + << "Offset is " << tz_offset.count(); + + connection->Execute("DROP TABLE tz_conversion_sample"); +} + // RAII class to set TZ environment variable. Not for use with threads. class TemporaryTZ { public: diff --git a/scripts/docs/en/index.md b/scripts/docs/en/index.md index 199b105c8475..d26c68c177ec 100644 --- a/scripts/docs/en/index.md +++ b/scripts/docs/en/index.md @@ -113,7 +113,7 @@ are available at the * @ref pg_transactions * @ref pg_run_queries * @ref pg_process_results -* @ref pg_types +* @ref scripts/docs/en/userver/pg_types.md * @ref pg_user_row_types * @ref pg_errors * @ref pg_topology diff --git a/scripts/docs/en/userver/pg_types.md b/scripts/docs/en/userver/pg_types.md new file mode 100644 index 000000000000..e83a27ddfe1e --- /dev/null +++ b/scripts/docs/en/userver/pg_types.md @@ -0,0 +1,204 @@ +# uPg: Supported data types + +PostgreSQL provides data type support with a system of buffer parsers and +formatters. Please refer to @ref pg_io for more information about the system. + +@see @ref userver_postgres_parse_and_format + + +@anchor pg_user_types +## Fundamental PostgreSQL types + +The fundamental PostgreSQL types support is provided by the driver. The +table below shows supported PostgreSQL types and their mapping to C++ types +provided by the driver. Column "Default" marks the PostgreSQL type to which +a C++ type is mapped when used as a parameter. Where the C++ type is N/A +it means that the PostgreSQL data type is not supported. When there is a +C++ type in parenthesis, it is a data type that will be supported later +and the C++ type is planned counterpart. +PG type | C++ type | Default | +----------------- | --------------------------------------- | ------- | +smallint | std::int16_t | + | +integer | std::int32_t | + | +bigint | std::int64_t | + | +smallserial | std::int16_t | | +serial | std::int32_t | | +bigserial | std::int64_t | | +boolean | bool | + | +real | float | + | +double precision | double | + | +numeric(p) | decimal64::Decimal | + | +decimal(p) | decimal64::Decimal | + | +money | N/A | | +text | std::string | + | +char(n) | std::string | | +varchar(n) | std::string | | +"char" | char | + | +timestamp | storages::postgres::TimePointWithoutTz | + | +timestamptz | storages::postgres::TimePointTz | + | +date | utils::datetime::Date | + | +time | utils::datetime::TimeOfDay | + | +timetz | N/A | | +interval | std::chrono::microseconds | | +bytea | container of one-byte type | | +bit(n) | utils::Flags | | +^ | std::bitset | | +^ | std::array | | +bit varying(n) | utils::Flags | | +^ | std::bitset | | +^ | std::array | | +uuid | boost::uuids::uuid | + | +json | formats::json::Value | | +jsonb | formats::json::Value | + | +int4range | storages::postgres::IntegerRange | | +^ | storages::postgres::BoundedIntegerRange | | +int8range | storages::postgres::BigintRange | | +^ | storages::postgres::BoundedBigintRange | | +inet | utils::ip::AddressV4 | | +^ | utils::ip::AddressV6 | | +cidr | utils::ip::NetworkV4 | | +^ | utils::ip::NetworkV6 | | +macaddr | utils::Macaddr | | +macaddr8 | utils::Macaddr8 | | +numrange | N/A | | +tsrange | N/A | | +tstzrange | N/A | | +daterange | N/A | | + +@warning The library doesn't provide support for C++ unsigned integral +types intentionally as PostgreSQL doesn't provide unsigned types and +using the types with the database is error-prone. + + +@anchor pg_timestamp +## Timestamp Support aka TimePointTz and TimePointWithoutTz + +The driver provides mapping from C++ std::chrono::time_point template type to +Postgres timestamp (without time zone) data type. + +To read/write timestamp with time zone Postgres data type a +storages::postgres::TimePointTz helper type is provided. + +PostgreSQL internal timestamp resolution is microseconds. + +**Example:** + +@snippet postgresql/src/storages/postgres/tests/chrono_pgtest.cpp tz sample + +### How not to get skewed times in the PostgreSQL + +Postgres has two types corresponding to absolute time +(a.k.a. global time; Unix time; NOT local time): + * `TIMESTAMP WITH TIME ZONE` + * `TIMESTAMP` + +An unfortunate design decision on the PostgreSQL side is that it allows +**implicit conversion** between them, and database applies an offset to the +time point when doing so, depending on the timezone of the Postgres database. + +Because of this you MUST ensure that you always use the correct type: + * storages::postgres::TimePointTz for `TIMESTAMP WITH TIME ZONE`; + * storages::postgres::TimePointWithoutTz for `TIMESTAMP`. + +Otherwise, you'll get skewed times in database: + +@snippet postgresql/src/storages/postgres/tests/chrono_pgtest.cpp tz skewed + +There is no way to detect that issue on the userver side, as the implicit +conversion is performed by the database itself and it provides no information +that the conversion happened. + + +@anchor pg_arrays +## Arrays in PostgreSQL + +The driver supports PostgreSQL arrays provided that the element type is +supported by the driver, including user types. + +Array parser will throw storages::postgres::DimensionMismatch if the +dimensions of C++ container do not match that of the buffer received from +the server. + +Array formatter will throw storages::postgres::InvalidDimensions if +containers on same level of depth have different sizes. + + +## User-defined PostgreSQL types + +The driver provides support for user-defined PostgreSQL types: +- domains +- enumerations +- composite types +- custom ranges + +For more information please see +@ref scripts/docs/en/userver/pg_user_types.md. + + +## C++ strong typedefs in PostgreSQL + +The driver provides support for C++ strong typedef idiom. For more +information see @ref pg_strong_typedef + + +## PostgreSQL ranges + +PostgreSQL range type support is provided by `storages::postgres::Range` +template. + + +## Geometry types in PostgreSQL + +For geometry types the driver provides parsing/formatting from/to +on-the-wire representation. The types provided do not define any calculus. + + +@anchor pg_bytea +## PostgreSQL bytea support + +The driver allows reading and writing raw binary data from/to PostgreSQL +`bytea` type. + +Reading and writing to PostgreSQL is implemented for `std::string`, +`std::string_view` and `std::vector` of `char` or `unsigned char`. + +@warning When reading to `std::string_view` the value MUST NOT be used after +the PostgreSQL result set is destroyed. + +Bytea() is a helper function for reading and writing binary data from/to a database. + +Example usage of Bytea(): +@snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_simple +@snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_string +@snippet postgresql/src/storages/postgres/tests/bytea_pgtest.cpp bytea_vector + + +## Network types in PostgreSQL + +The driver offers data types to store IPv4, IPv6, and MAC addresses, as +well as network specifications (CIDR). + + +## Bit string types in PostgreSQL + +The driver supports PostgreSQL `bit` and `bit varying` types. + +Parsing and formatting is implemented for integral values +(e.g. `uint32_t`, `uint64_t`), `utils::Flags`, `std::array` +and `std::bitset`. + +Example of using the bit types from tests: +@snippet storages/postgres/tests/bitstring_pgtest.cpp Bit string sample + + +## PostgreSQL types not covered above + +The types not covered above or marked as N/A in the table of fundamental +types will be eventually supported later, on request from the driver's +users. + +---------- + +@htmlonly
@endhtmlonly +⇦ @ref pg_process_results | @ref pg_user_row_types ⇨ +@htmlonly
@endhtmlonly diff --git a/scripts/docs/en/userver/pg_user_types.md b/scripts/docs/en/userver/pg_user_types.md index 704d23ffdcd6..da0130d744a5 100644 --- a/scripts/docs/en/userver/pg_user_types.md +++ b/scripts/docs/en/userver/pg_user_types.md @@ -5,7 +5,8 @@ user-povided database types. For a basic information on querying data see @ref pg_run_queries and @ref pg_process_results. A list of supported fundamental PostgreSQL types -and their mappings to C++ types is available at @ref pg_types. +and their mappings to C++ types is available at +@ref scripts/docs/en/userver/pg_types.md. In PosgtgreSQL database the following kinds of user types are available: - @ref pg_composite_types "composite (row) types" @@ -14,7 +15,6 @@ In PosgtgreSQL database the following kinds of user types are available: - @ref pg_user_types "domains" -@anchor pg_user_types ## Mapping a C++ type to PostgreSQL domain user type Domains are essentially some database data types with database constraints @@ -142,7 +142,7 @@ The type could be used in code in the following way: @snippet storages/postgres/tests/user_types_pgtest.cpp User domainrange type usage -### Time Range and Other Widely Used Types +### Time Range and Other Widely Used Types in PostgreSQL If you need a range of PostgreSQL `float` type or `time` type (actually any type mapped to C++ type that is highly likely used by other developers),