diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs')
18 files changed, 1888 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/__init__.pxd b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/__init__.pxd new file mode 100644 index 00000000..2dbcbd3c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/__init__.pxd @@ -0,0 +1,157 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef class CodecContext: + + cpdef get_text_codec(self) + cdef is_encoding_utf8(self) + cpdef get_json_decoder(self) + cdef is_decoding_json(self) + cpdef get_json_encoder(self) + cdef is_encoding_json(self) + + +ctypedef object (*encode_func)(CodecContext settings, + WriteBuffer buf, + object obj) + +ctypedef object (*decode_func)(CodecContext settings, + FRBuffer *buf) + + +# Datetime +cdef date_encode(CodecContext settings, WriteBuffer buf, obj) +cdef date_decode(CodecContext settings, FRBuffer * buf) +cdef date_encode_tuple(CodecContext settings, WriteBuffer buf, obj) +cdef date_decode_tuple(CodecContext settings, FRBuffer * buf) +cdef timestamp_encode(CodecContext settings, WriteBuffer buf, obj) +cdef timestamp_decode(CodecContext settings, FRBuffer * buf) +cdef timestamp_encode_tuple(CodecContext settings, WriteBuffer buf, obj) +cdef timestamp_decode_tuple(CodecContext settings, FRBuffer * buf) +cdef timestamptz_encode(CodecContext settings, WriteBuffer buf, obj) +cdef timestamptz_decode(CodecContext settings, FRBuffer * buf) +cdef time_encode(CodecContext settings, WriteBuffer buf, obj) +cdef time_decode(CodecContext settings, FRBuffer * buf) +cdef time_encode_tuple(CodecContext settings, WriteBuffer buf, obj) +cdef time_decode_tuple(CodecContext settings, FRBuffer * buf) +cdef timetz_encode(CodecContext settings, WriteBuffer buf, obj) +cdef timetz_decode(CodecContext settings, FRBuffer * buf) +cdef timetz_encode_tuple(CodecContext settings, WriteBuffer buf, obj) +cdef timetz_decode_tuple(CodecContext settings, FRBuffer * buf) +cdef interval_encode(CodecContext settings, WriteBuffer buf, obj) +cdef interval_decode(CodecContext settings, FRBuffer * buf) +cdef interval_encode_tuple(CodecContext settings, WriteBuffer buf, tuple obj) +cdef interval_decode_tuple(CodecContext settings, FRBuffer * buf) + + +# Bits +cdef bits_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef bits_decode(CodecContext settings, FRBuffer * buf) + + +# Bools +cdef bool_encode(CodecContext settings, WriteBuffer buf, obj) +cdef bool_decode(CodecContext settings, FRBuffer * buf) + + +# Geometry +cdef box_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef box_decode(CodecContext settings, FRBuffer * buf) +cdef line_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef line_decode(CodecContext settings, FRBuffer * buf) +cdef lseg_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef lseg_decode(CodecContext settings, FRBuffer * buf) +cdef point_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef point_decode(CodecContext settings, FRBuffer * buf) +cdef path_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef path_decode(CodecContext settings, FRBuffer * buf) +cdef poly_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef poly_decode(CodecContext settings, FRBuffer * buf) +cdef circle_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef circle_decode(CodecContext settings, FRBuffer * buf) + + +# Hstore +cdef hstore_encode(CodecContext settings, WriteBuffer buf, obj) +cdef hstore_decode(CodecContext settings, FRBuffer * buf) + + +# Ints +cdef int2_encode(CodecContext settings, WriteBuffer buf, obj) +cdef int2_decode(CodecContext settings, FRBuffer * buf) +cdef int4_encode(CodecContext settings, WriteBuffer buf, obj) +cdef int4_decode(CodecContext settings, FRBuffer * buf) +cdef uint4_encode(CodecContext settings, WriteBuffer buf, obj) +cdef uint4_decode(CodecContext settings, FRBuffer * buf) +cdef int8_encode(CodecContext settings, WriteBuffer buf, obj) +cdef int8_decode(CodecContext settings, FRBuffer * buf) +cdef uint8_encode(CodecContext settings, WriteBuffer buf, obj) +cdef uint8_decode(CodecContext settings, FRBuffer * buf) + + +# Floats +cdef float4_encode(CodecContext settings, WriteBuffer buf, obj) +cdef float4_decode(CodecContext settings, FRBuffer * buf) +cdef float8_encode(CodecContext settings, WriteBuffer buf, obj) +cdef float8_decode(CodecContext settings, FRBuffer * buf) + + +# JSON +cdef jsonb_encode(CodecContext settings, WriteBuffer buf, obj) +cdef jsonb_decode(CodecContext settings, FRBuffer * buf) + + +# JSON path +cdef jsonpath_encode(CodecContext settings, WriteBuffer buf, obj) +cdef jsonpath_decode(CodecContext settings, FRBuffer * buf) + + +# Text +cdef as_pg_string_and_size( + CodecContext settings, obj, char **cstr, ssize_t *size) +cdef text_encode(CodecContext settings, WriteBuffer buf, obj) +cdef text_decode(CodecContext settings, FRBuffer * buf) + +# Bytea +cdef bytea_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef bytea_decode(CodecContext settings, FRBuffer * buf) + + +# UUID +cdef uuid_encode(CodecContext settings, WriteBuffer wbuf, obj) +cdef uuid_decode(CodecContext settings, FRBuffer * buf) + + +# Numeric +cdef numeric_encode_text(CodecContext settings, WriteBuffer buf, obj) +cdef numeric_decode_text(CodecContext settings, FRBuffer * buf) +cdef numeric_encode_binary(CodecContext settings, WriteBuffer buf, obj) +cdef numeric_decode_binary(CodecContext settings, FRBuffer * buf) +cdef numeric_decode_binary_ex(CodecContext settings, FRBuffer * buf, + bint trail_fract_zero) + + +# Void +cdef void_encode(CodecContext settings, WriteBuffer buf, obj) +cdef void_decode(CodecContext settings, FRBuffer * buf) + + +# tid +cdef tid_encode(CodecContext settings, WriteBuffer buf, obj) +cdef tid_decode(CodecContext settings, FRBuffer * buf) + + +# Network +cdef cidr_encode(CodecContext settings, WriteBuffer buf, obj) +cdef cidr_decode(CodecContext settings, FRBuffer * buf) +cdef inet_encode(CodecContext settings, WriteBuffer buf, obj) +cdef inet_decode(CodecContext settings, FRBuffer * buf) + + +# pg_snapshot +cdef pg_snapshot_encode(CodecContext settings, WriteBuffer buf, obj) +cdef pg_snapshot_decode(CodecContext settings, FRBuffer * buf) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bits.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bits.pyx new file mode 100644 index 00000000..14f7bb0b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bits.pyx @@ -0,0 +1,47 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef bits_encode(CodecContext settings, WriteBuffer wbuf, obj): + cdef: + Py_buffer pybuf + bint pybuf_used = False + char *buf + ssize_t len + ssize_t bitlen + + if cpython.PyBytes_CheckExact(obj): + buf = cpython.PyBytes_AS_STRING(obj) + len = cpython.Py_SIZE(obj) + bitlen = len * 8 + elif isinstance(obj, pgproto_types.BitString): + cpython.PyBytes_AsStringAndSize(obj.bytes, &buf, &len) + bitlen = obj.__len__() + else: + cpython.PyObject_GetBuffer(obj, &pybuf, cpython.PyBUF_SIMPLE) + pybuf_used = True + buf = <char*>pybuf.buf + len = pybuf.len + bitlen = len * 8 + + try: + if bitlen > _MAXINT32: + raise ValueError('bit value too long') + wbuf.write_int32(4 + <int32_t>len) + wbuf.write_int32(<int32_t>bitlen) + wbuf.write_cstr(buf, len) + finally: + if pybuf_used: + cpython.PyBuffer_Release(&pybuf) + + +cdef bits_decode(CodecContext settings, FRBuffer *buf): + cdef: + int32_t bitlen = hton.unpack_int32(frb_read(buf, 4)) + ssize_t buf_len = buf.len + + bytes_ = cpython.PyBytes_FromStringAndSize(frb_read_all(buf), buf_len) + return pgproto_types.BitString.frombytes(bytes_, bitlen) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bytea.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bytea.pyx new file mode 100644 index 00000000..15818258 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/bytea.pyx @@ -0,0 +1,34 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef bytea_encode(CodecContext settings, WriteBuffer wbuf, obj): + cdef: + Py_buffer pybuf + bint pybuf_used = False + char *buf + ssize_t len + + if cpython.PyBytes_CheckExact(obj): + buf = cpython.PyBytes_AS_STRING(obj) + len = cpython.Py_SIZE(obj) + else: + cpython.PyObject_GetBuffer(obj, &pybuf, cpython.PyBUF_SIMPLE) + pybuf_used = True + buf = <char*>pybuf.buf + len = pybuf.len + + try: + wbuf.write_int32(<int32_t>len) + wbuf.write_cstr(buf, len) + finally: + if pybuf_used: + cpython.PyBuffer_Release(&pybuf) + + +cdef bytea_decode(CodecContext settings, FRBuffer *buf): + cdef ssize_t buf_len = buf.len + return cpython.PyBytes_FromStringAndSize(frb_read_all(buf), buf_len) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/context.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/context.pyx new file mode 100644 index 00000000..c4d4416e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/context.pyx @@ -0,0 +1,26 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef class CodecContext: + + cpdef get_text_codec(self): + raise NotImplementedError + + cdef is_encoding_utf8(self): + raise NotImplementedError + + cpdef get_json_decoder(self): + raise NotImplementedError + + cdef is_decoding_json(self): + return False + + cpdef get_json_encoder(self): + raise NotImplementedError + + cdef is_encoding_json(self): + return False diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/datetime.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/datetime.pyx new file mode 100644 index 00000000..bed0b9e9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/datetime.pyx @@ -0,0 +1,423 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cimport cpython.datetime +import datetime + +cpython.datetime.import_datetime() + +utc = datetime.timezone.utc +date_from_ordinal = datetime.date.fromordinal +timedelta = datetime.timedelta + +pg_epoch_datetime = datetime.datetime(2000, 1, 1) +cdef int32_t pg_epoch_datetime_ts = \ + <int32_t>cpython.PyLong_AsLong(int(pg_epoch_datetime.timestamp())) + +pg_epoch_datetime_utc = datetime.datetime(2000, 1, 1, tzinfo=utc) +cdef int32_t pg_epoch_datetime_utc_ts = \ + <int32_t>cpython.PyLong_AsLong(int(pg_epoch_datetime_utc.timestamp())) + +pg_epoch_date = datetime.date(2000, 1, 1) +cdef int32_t pg_date_offset_ord = \ + <int32_t>cpython.PyLong_AsLong(pg_epoch_date.toordinal()) + +# Binary representations of infinity for datetimes. +cdef int64_t pg_time64_infinity = 0x7fffffffffffffff +cdef int64_t pg_time64_negative_infinity = <int64_t>0x8000000000000000 +cdef int32_t pg_date_infinity = 0x7fffffff +cdef int32_t pg_date_negative_infinity = <int32_t>0x80000000 + +infinity_datetime = datetime.datetime( + datetime.MAXYEAR, 12, 31, 23, 59, 59, 999999) + +cdef int32_t infinity_datetime_ord = <int32_t>cpython.PyLong_AsLong( + infinity_datetime.toordinal()) + +cdef int64_t infinity_datetime_ts = 252455615999999999 + +negative_infinity_datetime = datetime.datetime( + datetime.MINYEAR, 1, 1, 0, 0, 0, 0) + +cdef int32_t negative_infinity_datetime_ord = <int32_t>cpython.PyLong_AsLong( + negative_infinity_datetime.toordinal()) + +cdef int64_t negative_infinity_datetime_ts = -63082281600000000 + +infinity_date = datetime.date(datetime.MAXYEAR, 12, 31) + +cdef int32_t infinity_date_ord = <int32_t>cpython.PyLong_AsLong( + infinity_date.toordinal()) + +negative_infinity_date = datetime.date(datetime.MINYEAR, 1, 1) + +cdef int32_t negative_infinity_date_ord = <int32_t>cpython.PyLong_AsLong( + negative_infinity_date.toordinal()) + + +cdef inline _local_timezone(): + d = datetime.datetime.now(datetime.timezone.utc).astimezone() + return datetime.timezone(d.utcoffset()) + + +cdef inline _encode_time(WriteBuffer buf, int64_t seconds, + int32_t microseconds): + # XXX: add support for double timestamps + # int64 timestamps, + cdef int64_t ts = seconds * 1000000 + microseconds + + if ts == infinity_datetime_ts: + buf.write_int64(pg_time64_infinity) + elif ts == negative_infinity_datetime_ts: + buf.write_int64(pg_time64_negative_infinity) + else: + buf.write_int64(ts) + + +cdef inline int32_t _decode_time(FRBuffer *buf, int64_t *seconds, + int32_t *microseconds): + cdef int64_t ts = hton.unpack_int64(frb_read(buf, 8)) + + if ts == pg_time64_infinity: + return 1 + elif ts == pg_time64_negative_infinity: + return -1 + else: + seconds[0] = ts // 1000000 + microseconds[0] = <int32_t>(ts % 1000000) + return 0 + + +cdef date_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + int32_t ordinal = <int32_t>cpython.PyLong_AsLong(obj.toordinal()) + int32_t pg_ordinal + + if ordinal == infinity_date_ord: + pg_ordinal = pg_date_infinity + elif ordinal == negative_infinity_date_ord: + pg_ordinal = pg_date_negative_infinity + else: + pg_ordinal = ordinal - pg_date_offset_ord + + buf.write_int32(4) + buf.write_int32(pg_ordinal) + + +cdef date_encode_tuple(CodecContext settings, WriteBuffer buf, obj): + cdef: + int32_t pg_ordinal + + if len(obj) != 1: + raise ValueError( + 'date tuple encoder: expecting 1 element ' + 'in tuple, got {}'.format(len(obj))) + + pg_ordinal = obj[0] + buf.write_int32(4) + buf.write_int32(pg_ordinal) + + +cdef date_decode(CodecContext settings, FRBuffer *buf): + cdef int32_t pg_ordinal = hton.unpack_int32(frb_read(buf, 4)) + + if pg_ordinal == pg_date_infinity: + return infinity_date + elif pg_ordinal == pg_date_negative_infinity: + return negative_infinity_date + else: + return date_from_ordinal(pg_ordinal + pg_date_offset_ord) + + +cdef date_decode_tuple(CodecContext settings, FRBuffer *buf): + cdef int32_t pg_ordinal = hton.unpack_int32(frb_read(buf, 4)) + + return (pg_ordinal,) + + +cdef timestamp_encode(CodecContext settings, WriteBuffer buf, obj): + if not cpython.datetime.PyDateTime_Check(obj): + if cpython.datetime.PyDate_Check(obj): + obj = datetime.datetime(obj.year, obj.month, obj.day) + else: + raise TypeError( + 'expected a datetime.date or datetime.datetime instance, ' + 'got {!r}'.format(type(obj).__name__) + ) + + delta = obj - pg_epoch_datetime + cdef: + int64_t seconds = cpython.PyLong_AsLongLong(delta.days) * 86400 + \ + cpython.PyLong_AsLong(delta.seconds) + int32_t microseconds = <int32_t>cpython.PyLong_AsLong( + delta.microseconds) + + buf.write_int32(8) + _encode_time(buf, seconds, microseconds) + + +cdef timestamp_encode_tuple(CodecContext settings, WriteBuffer buf, obj): + cdef: + int64_t microseconds + + if len(obj) != 1: + raise ValueError( + 'timestamp tuple encoder: expecting 1 element ' + 'in tuple, got {}'.format(len(obj))) + + microseconds = obj[0] + + buf.write_int32(8) + buf.write_int64(microseconds) + + +cdef timestamp_decode(CodecContext settings, FRBuffer *buf): + cdef: + int64_t seconds = 0 + int32_t microseconds = 0 + int32_t inf = _decode_time(buf, &seconds, µseconds) + + if inf > 0: + # positive infinity + return infinity_datetime + elif inf < 0: + # negative infinity + return negative_infinity_datetime + else: + return pg_epoch_datetime.__add__( + timedelta(0, seconds, microseconds)) + + +cdef timestamp_decode_tuple(CodecContext settings, FRBuffer *buf): + cdef: + int64_t ts = hton.unpack_int64(frb_read(buf, 8)) + + return (ts,) + + +cdef timestamptz_encode(CodecContext settings, WriteBuffer buf, obj): + if not cpython.datetime.PyDateTime_Check(obj): + if cpython.datetime.PyDate_Check(obj): + obj = datetime.datetime(obj.year, obj.month, obj.day, + tzinfo=_local_timezone()) + else: + raise TypeError( + 'expected a datetime.date or datetime.datetime instance, ' + 'got {!r}'.format(type(obj).__name__) + ) + + buf.write_int32(8) + + if obj == infinity_datetime: + buf.write_int64(pg_time64_infinity) + return + elif obj == negative_infinity_datetime: + buf.write_int64(pg_time64_negative_infinity) + return + + utc_dt = obj.astimezone(utc) + + delta = utc_dt - pg_epoch_datetime_utc + cdef: + int64_t seconds = cpython.PyLong_AsLongLong(delta.days) * 86400 + \ + cpython.PyLong_AsLong(delta.seconds) + int32_t microseconds = <int32_t>cpython.PyLong_AsLong( + delta.microseconds) + + _encode_time(buf, seconds, microseconds) + + +cdef timestamptz_decode(CodecContext settings, FRBuffer *buf): + cdef: + int64_t seconds = 0 + int32_t microseconds = 0 + int32_t inf = _decode_time(buf, &seconds, µseconds) + + if inf > 0: + # positive infinity + return infinity_datetime + elif inf < 0: + # negative infinity + return negative_infinity_datetime + else: + return pg_epoch_datetime_utc.__add__( + timedelta(0, seconds, microseconds)) + + +cdef time_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + int64_t seconds = cpython.PyLong_AsLong(obj.hour) * 3600 + \ + cpython.PyLong_AsLong(obj.minute) * 60 + \ + cpython.PyLong_AsLong(obj.second) + int32_t microseconds = <int32_t>cpython.PyLong_AsLong(obj.microsecond) + + buf.write_int32(8) + _encode_time(buf, seconds, microseconds) + + +cdef time_encode_tuple(CodecContext settings, WriteBuffer buf, obj): + cdef: + int64_t microseconds + + if len(obj) != 1: + raise ValueError( + 'time tuple encoder: expecting 1 element ' + 'in tuple, got {}'.format(len(obj))) + + microseconds = obj[0] + + buf.write_int32(8) + buf.write_int64(microseconds) + + +cdef time_decode(CodecContext settings, FRBuffer *buf): + cdef: + int64_t seconds = 0 + int32_t microseconds = 0 + + _decode_time(buf, &seconds, µseconds) + + cdef: + int64_t minutes = <int64_t>(seconds / 60) + int64_t sec = seconds % 60 + int64_t hours = <int64_t>(minutes / 60) + int64_t min = minutes % 60 + + return datetime.time(hours, min, sec, microseconds) + + +cdef time_decode_tuple(CodecContext settings, FRBuffer *buf): + cdef: + int64_t ts = hton.unpack_int64(frb_read(buf, 8)) + + return (ts,) + + +cdef timetz_encode(CodecContext settings, WriteBuffer buf, obj): + offset = obj.tzinfo.utcoffset(None) + + cdef: + int32_t offset_sec = \ + <int32_t>cpython.PyLong_AsLong(offset.days) * 24 * 60 * 60 + \ + <int32_t>cpython.PyLong_AsLong(offset.seconds) + + int64_t seconds = cpython.PyLong_AsLong(obj.hour) * 3600 + \ + cpython.PyLong_AsLong(obj.minute) * 60 + \ + cpython.PyLong_AsLong(obj.second) + + int32_t microseconds = <int32_t>cpython.PyLong_AsLong(obj.microsecond) + + buf.write_int32(12) + _encode_time(buf, seconds, microseconds) + # In Python utcoffset() is the difference between the local time + # and the UTC, whereas in PostgreSQL it's the opposite, + # so we need to flip the sign. + buf.write_int32(-offset_sec) + + +cdef timetz_encode_tuple(CodecContext settings, WriteBuffer buf, obj): + cdef: + int64_t microseconds + int32_t offset_sec + + if len(obj) != 2: + raise ValueError( + 'time tuple encoder: expecting 2 elements2 ' + 'in tuple, got {}'.format(len(obj))) + + microseconds = obj[0] + offset_sec = obj[1] + + buf.write_int32(12) + buf.write_int64(microseconds) + buf.write_int32(offset_sec) + + +cdef timetz_decode(CodecContext settings, FRBuffer *buf): + time = time_decode(settings, buf) + cdef int32_t offset = <int32_t>(hton.unpack_int32(frb_read(buf, 4)) / 60) + # See the comment in the `timetz_encode` method. + return time.replace(tzinfo=datetime.timezone(timedelta(minutes=-offset))) + + +cdef timetz_decode_tuple(CodecContext settings, FRBuffer *buf): + cdef: + int64_t microseconds = hton.unpack_int64(frb_read(buf, 8)) + int32_t offset_sec = hton.unpack_int32(frb_read(buf, 4)) + + return (microseconds, offset_sec) + + +cdef interval_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + int32_t days = <int32_t>cpython.PyLong_AsLong(obj.days) + int64_t seconds = cpython.PyLong_AsLongLong(obj.seconds) + int32_t microseconds = <int32_t>cpython.PyLong_AsLong(obj.microseconds) + + buf.write_int32(16) + _encode_time(buf, seconds, microseconds) + buf.write_int32(days) + buf.write_int32(0) # Months + + +cdef interval_encode_tuple(CodecContext settings, WriteBuffer buf, + tuple obj): + cdef: + int32_t months + int32_t days + int64_t microseconds + + if len(obj) != 3: + raise ValueError( + 'interval tuple encoder: expecting 3 elements ' + 'in tuple, got {}'.format(len(obj))) + + months = obj[0] + days = obj[1] + microseconds = obj[2] + + buf.write_int32(16) + buf.write_int64(microseconds) + buf.write_int32(days) + buf.write_int32(months) + + +cdef interval_decode(CodecContext settings, FRBuffer *buf): + cdef: + int32_t days + int32_t months + int32_t years + int64_t seconds = 0 + int32_t microseconds = 0 + + _decode_time(buf, &seconds, µseconds) + + days = hton.unpack_int32(frb_read(buf, 4)) + months = hton.unpack_int32(frb_read(buf, 4)) + + if months < 0: + years = -<int32_t>(-months // 12) + months = -<int32_t>(-months % 12) + else: + years = <int32_t>(months // 12) + months = <int32_t>(months % 12) + + return datetime.timedelta(days=days + months * 30 + years * 365, + seconds=seconds, microseconds=microseconds) + + +cdef interval_decode_tuple(CodecContext settings, FRBuffer *buf): + cdef: + int32_t days + int32_t months + int64_t microseconds + + microseconds = hton.unpack_int64(frb_read(buf, 8)) + days = hton.unpack_int32(frb_read(buf, 4)) + months = hton.unpack_int32(frb_read(buf, 4)) + + return (months, days, microseconds) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/float.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/float.pyx new file mode 100644 index 00000000..94eda03a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/float.pyx @@ -0,0 +1,34 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +from libc cimport math + + +cdef float4_encode(CodecContext settings, WriteBuffer buf, obj): + cdef double dval = cpython.PyFloat_AsDouble(obj) + cdef float fval = <float>dval + if math.isinf(fval) and not math.isinf(dval): + raise ValueError('value out of float32 range') + + buf.write_int32(4) + buf.write_float(fval) + + +cdef float4_decode(CodecContext settings, FRBuffer *buf): + cdef float f = hton.unpack_float(frb_read(buf, 4)) + return cpython.PyFloat_FromDouble(f) + + +cdef float8_encode(CodecContext settings, WriteBuffer buf, obj): + cdef double dval = cpython.PyFloat_AsDouble(obj) + buf.write_int32(8) + buf.write_double(dval) + + +cdef float8_decode(CodecContext settings, FRBuffer *buf): + cdef double f = hton.unpack_double(frb_read(buf, 8)) + return cpython.PyFloat_FromDouble(f) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/geometry.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/geometry.pyx new file mode 100644 index 00000000..44aac64b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/geometry.pyx @@ -0,0 +1,164 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef inline _encode_points(WriteBuffer wbuf, object points): + cdef object point + + for point in points: + wbuf.write_double(point[0]) + wbuf.write_double(point[1]) + + +cdef inline _decode_points(FRBuffer *buf): + cdef: + int32_t npts = hton.unpack_int32(frb_read(buf, 4)) + pts = cpython.PyTuple_New(npts) + int32_t i + object point + double x + double y + + for i in range(npts): + x = hton.unpack_double(frb_read(buf, 8)) + y = hton.unpack_double(frb_read(buf, 8)) + point = pgproto_types.Point(x, y) + cpython.Py_INCREF(point) + cpython.PyTuple_SET_ITEM(pts, i, point) + + return pts + + +cdef box_encode(CodecContext settings, WriteBuffer wbuf, obj): + wbuf.write_int32(32) + _encode_points(wbuf, (obj[0], obj[1])) + + +cdef box_decode(CodecContext settings, FRBuffer *buf): + cdef: + double high_x = hton.unpack_double(frb_read(buf, 8)) + double high_y = hton.unpack_double(frb_read(buf, 8)) + double low_x = hton.unpack_double(frb_read(buf, 8)) + double low_y = hton.unpack_double(frb_read(buf, 8)) + + return pgproto_types.Box( + pgproto_types.Point(high_x, high_y), + pgproto_types.Point(low_x, low_y)) + + +cdef line_encode(CodecContext settings, WriteBuffer wbuf, obj): + wbuf.write_int32(24) + wbuf.write_double(obj[0]) + wbuf.write_double(obj[1]) + wbuf.write_double(obj[2]) + + +cdef line_decode(CodecContext settings, FRBuffer *buf): + cdef: + double A = hton.unpack_double(frb_read(buf, 8)) + double B = hton.unpack_double(frb_read(buf, 8)) + double C = hton.unpack_double(frb_read(buf, 8)) + + return pgproto_types.Line(A, B, C) + + +cdef lseg_encode(CodecContext settings, WriteBuffer wbuf, obj): + wbuf.write_int32(32) + _encode_points(wbuf, (obj[0], obj[1])) + + +cdef lseg_decode(CodecContext settings, FRBuffer *buf): + cdef: + double p1_x = hton.unpack_double(frb_read(buf, 8)) + double p1_y = hton.unpack_double(frb_read(buf, 8)) + double p2_x = hton.unpack_double(frb_read(buf, 8)) + double p2_y = hton.unpack_double(frb_read(buf, 8)) + + return pgproto_types.LineSegment((p1_x, p1_y), (p2_x, p2_y)) + + +cdef point_encode(CodecContext settings, WriteBuffer wbuf, obj): + wbuf.write_int32(16) + wbuf.write_double(obj[0]) + wbuf.write_double(obj[1]) + + +cdef point_decode(CodecContext settings, FRBuffer *buf): + cdef: + double x = hton.unpack_double(frb_read(buf, 8)) + double y = hton.unpack_double(frb_read(buf, 8)) + + return pgproto_types.Point(x, y) + + +cdef path_encode(CodecContext settings, WriteBuffer wbuf, obj): + cdef: + int8_t is_closed = 0 + ssize_t npts + ssize_t encoded_len + int32_t i + + if cpython.PyTuple_Check(obj): + is_closed = 1 + elif cpython.PyList_Check(obj): + is_closed = 0 + elif isinstance(obj, pgproto_types.Path): + is_closed = obj.is_closed + + npts = len(obj) + encoded_len = 1 + 4 + 16 * npts + if encoded_len > _MAXINT32: + raise ValueError('path value too long') + + wbuf.write_int32(<int32_t>encoded_len) + wbuf.write_byte(is_closed) + wbuf.write_int32(<int32_t>npts) + + _encode_points(wbuf, obj) + + +cdef path_decode(CodecContext settings, FRBuffer *buf): + cdef: + int8_t is_closed = <int8_t>(frb_read(buf, 1)[0]) + + return pgproto_types.Path(*_decode_points(buf), is_closed=is_closed == 1) + + +cdef poly_encode(CodecContext settings, WriteBuffer wbuf, obj): + cdef: + bint is_closed + ssize_t npts + ssize_t encoded_len + int32_t i + + npts = len(obj) + encoded_len = 4 + 16 * npts + if encoded_len > _MAXINT32: + raise ValueError('polygon value too long') + + wbuf.write_int32(<int32_t>encoded_len) + wbuf.write_int32(<int32_t>npts) + _encode_points(wbuf, obj) + + +cdef poly_decode(CodecContext settings, FRBuffer *buf): + return pgproto_types.Polygon(*_decode_points(buf)) + + +cdef circle_encode(CodecContext settings, WriteBuffer wbuf, obj): + wbuf.write_int32(24) + wbuf.write_double(obj[0][0]) + wbuf.write_double(obj[0][1]) + wbuf.write_double(obj[1]) + + +cdef circle_decode(CodecContext settings, FRBuffer *buf): + cdef: + double center_x = hton.unpack_double(frb_read(buf, 8)) + double center_y = hton.unpack_double(frb_read(buf, 8)) + double radius = hton.unpack_double(frb_read(buf, 8)) + + return pgproto_types.Circle((center_x, center_y), radius) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/hstore.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/hstore.pyx new file mode 100644 index 00000000..09051c76 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/hstore.pyx @@ -0,0 +1,73 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef hstore_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + char *str + ssize_t size + ssize_t count + object items + WriteBuffer item_buf = WriteBuffer.new() + + count = len(obj) + if count > _MAXINT32: + raise ValueError('hstore value is too large') + item_buf.write_int32(<int32_t>count) + + if hasattr(obj, 'items'): + items = obj.items() + else: + items = obj + + for k, v in items: + if k is None: + raise ValueError('null value not allowed in hstore key') + as_pg_string_and_size(settings, k, &str, &size) + item_buf.write_int32(<int32_t>size) + item_buf.write_cstr(str, size) + if v is None: + item_buf.write_int32(<int32_t>-1) + else: + as_pg_string_and_size(settings, v, &str, &size) + item_buf.write_int32(<int32_t>size) + item_buf.write_cstr(str, size) + + buf.write_int32(item_buf.len()) + buf.write_buffer(item_buf) + + +cdef hstore_decode(CodecContext settings, FRBuffer *buf): + cdef: + dict result + uint32_t elem_count + int32_t elem_len + uint32_t i + str k + str v + + result = {} + + elem_count = <uint32_t>hton.unpack_int32(frb_read(buf, 4)) + if elem_count == 0: + return result + + for i in range(elem_count): + elem_len = hton.unpack_int32(frb_read(buf, 4)) + if elem_len < 0: + raise ValueError('null value not allowed in hstore key') + + k = decode_pg_string(settings, frb_read(buf, elem_len), elem_len) + + elem_len = hton.unpack_int32(frb_read(buf, 4)) + if elem_len < 0: + v = None + else: + v = decode_pg_string(settings, frb_read(buf, elem_len), elem_len) + + result[k] = v + + return result diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/int.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/int.pyx new file mode 100644 index 00000000..99972444 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/int.pyx @@ -0,0 +1,144 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef bool_encode(CodecContext settings, WriteBuffer buf, obj): + if not cpython.PyBool_Check(obj): + raise TypeError('a boolean is required (got type {})'.format( + type(obj).__name__)) + + buf.write_int32(1) + buf.write_byte(b'\x01' if obj is True else b'\x00') + + +cdef bool_decode(CodecContext settings, FRBuffer *buf): + return frb_read(buf, 1)[0] is b'\x01' + + +cdef int2_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef long val + + try: + if type(obj) is not int and hasattr(type(obj), '__int__'): + # Silence a Python warning about implicit __int__ + # conversion. + obj = int(obj) + val = cpython.PyLong_AsLong(obj) + except OverflowError: + overflow = 1 + + if overflow or val < INT16_MIN or val > INT16_MAX: + raise OverflowError('value out of int16 range') + + buf.write_int32(2) + buf.write_int16(<int16_t>val) + + +cdef int2_decode(CodecContext settings, FRBuffer *buf): + return cpython.PyLong_FromLong(hton.unpack_int16(frb_read(buf, 2))) + + +cdef int4_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef long val = 0 + + try: + if type(obj) is not int and hasattr(type(obj), '__int__'): + # Silence a Python warning about implicit __int__ + # conversion. + obj = int(obj) + val = cpython.PyLong_AsLong(obj) + except OverflowError: + overflow = 1 + + # "long" and "long long" have the same size for x86_64, need an extra check + if overflow or (sizeof(val) > 4 and (val < INT32_MIN or val > INT32_MAX)): + raise OverflowError('value out of int32 range') + + buf.write_int32(4) + buf.write_int32(<int32_t>val) + + +cdef int4_decode(CodecContext settings, FRBuffer *buf): + return cpython.PyLong_FromLong(hton.unpack_int32(frb_read(buf, 4))) + + +cdef uint4_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef unsigned long val = 0 + + try: + if type(obj) is not int and hasattr(type(obj), '__int__'): + # Silence a Python warning about implicit __int__ + # conversion. + obj = int(obj) + val = cpython.PyLong_AsUnsignedLong(obj) + except OverflowError: + overflow = 1 + + # "long" and "long long" have the same size for x86_64, need an extra check + if overflow or (sizeof(val) > 4 and val > UINT32_MAX): + raise OverflowError('value out of uint32 range') + + buf.write_int32(4) + buf.write_int32(<int32_t>val) + + +cdef uint4_decode(CodecContext settings, FRBuffer *buf): + return cpython.PyLong_FromUnsignedLong( + <uint32_t>hton.unpack_int32(frb_read(buf, 4))) + + +cdef int8_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef long long val + + try: + if type(obj) is not int and hasattr(type(obj), '__int__'): + # Silence a Python warning about implicit __int__ + # conversion. + obj = int(obj) + val = cpython.PyLong_AsLongLong(obj) + except OverflowError: + overflow = 1 + + # Just in case for systems with "long long" bigger than 8 bytes + if overflow or (sizeof(val) > 8 and (val < INT64_MIN or val > INT64_MAX)): + raise OverflowError('value out of int64 range') + + buf.write_int32(8) + buf.write_int64(<int64_t>val) + + +cdef int8_decode(CodecContext settings, FRBuffer *buf): + return cpython.PyLong_FromLongLong(hton.unpack_int64(frb_read(buf, 8))) + + +cdef uint8_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef unsigned long long val = 0 + + try: + if type(obj) is not int and hasattr(type(obj), '__int__'): + # Silence a Python warning about implicit __int__ + # conversion. + obj = int(obj) + val = cpython.PyLong_AsUnsignedLongLong(obj) + except OverflowError: + overflow = 1 + + # Just in case for systems with "long long" bigger than 8 bytes + if overflow or (sizeof(val) > 8 and val > UINT64_MAX): + raise OverflowError('value out of uint64 range') + + buf.write_int32(8) + buf.write_int64(<int64_t>val) + + +cdef uint8_decode(CodecContext settings, FRBuffer *buf): + return cpython.PyLong_FromUnsignedLongLong( + <uint64_t>hton.unpack_int64(frb_read(buf, 8)))
\ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/json.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/json.pyx new file mode 100644 index 00000000..97e6916b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/json.pyx @@ -0,0 +1,57 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef jsonb_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + char *str + ssize_t size + + if settings.is_encoding_json(): + obj = settings.get_json_encoder().encode(obj) + + as_pg_string_and_size(settings, obj, &str, &size) + + if size > 0x7fffffff - 1: + raise ValueError('string too long') + + buf.write_int32(<int32_t>size + 1) + buf.write_byte(1) # JSONB format version + buf.write_cstr(str, size) + + +cdef jsonb_decode(CodecContext settings, FRBuffer *buf): + cdef uint8_t format = <uint8_t>(frb_read(buf, 1)[0]) + + if format != 1: + raise ValueError('unexpected JSONB format: {}'.format(format)) + + rv = text_decode(settings, buf) + + if settings.is_decoding_json(): + rv = settings.get_json_decoder().decode(rv) + + return rv + + +cdef json_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + char *str + ssize_t size + + if settings.is_encoding_json(): + obj = settings.get_json_encoder().encode(obj) + + text_encode(settings, buf, obj) + + +cdef json_decode(CodecContext settings, FRBuffer *buf): + rv = text_decode(settings, buf) + + if settings.is_decoding_json(): + rv = settings.get_json_decoder().decode(rv) + + return rv diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/jsonpath.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/jsonpath.pyx new file mode 100644 index 00000000..610b30d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/jsonpath.pyx @@ -0,0 +1,29 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef jsonpath_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + char *str + ssize_t size + + as_pg_string_and_size(settings, obj, &str, &size) + + if size > 0x7fffffff - 1: + raise ValueError('string too long') + + buf.write_int32(<int32_t>size + 1) + buf.write_byte(1) # jsonpath format version + buf.write_cstr(str, size) + + +cdef jsonpath_decode(CodecContext settings, FRBuffer *buf): + cdef uint8_t format = <uint8_t>(frb_read(buf, 1)[0]) + + if format != 1: + raise ValueError('unexpected jsonpath format: {}'.format(format)) + + return text_decode(settings, buf) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/misc.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/misc.pyx new file mode 100644 index 00000000..99b19c99 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/misc.pyx @@ -0,0 +1,16 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef void_encode(CodecContext settings, WriteBuffer buf, obj): + # Void is zero bytes + buf.write_int32(0) + + +cdef void_decode(CodecContext settings, FRBuffer *buf): + # Do nothing; void will be passed as NULL so this function + # will never be called. + pass diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/network.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/network.pyx new file mode 100644 index 00000000..730c947f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/network.pyx @@ -0,0 +1,139 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +import ipaddress + + +# defined in postgresql/src/include/inet.h +# +DEF PGSQL_AF_INET = 2 # AF_INET +DEF PGSQL_AF_INET6 = 3 # AF_INET + 1 + + +_ipaddr = ipaddress.ip_address +_ipiface = ipaddress.ip_interface +_ipnet = ipaddress.ip_network + + +cdef inline uint8_t _ip_max_prefix_len(int32_t family): + # Maximum number of bits in the network prefix of the specified + # IP protocol version. + if family == PGSQL_AF_INET: + return 32 + else: + return 128 + + +cdef inline int32_t _ip_addr_len(int32_t family): + # Length of address in bytes for the specified IP protocol version. + if family == PGSQL_AF_INET: + return 4 + else: + return 16 + + +cdef inline int8_t _ver_to_family(int32_t version): + if version == 4: + return PGSQL_AF_INET + else: + return PGSQL_AF_INET6 + + +cdef inline _net_encode(WriteBuffer buf, int8_t family, uint32_t bits, + int8_t is_cidr, bytes addr): + + cdef: + char *addrbytes + ssize_t addrlen + + cpython.PyBytes_AsStringAndSize(addr, &addrbytes, &addrlen) + + buf.write_int32(4 + <int32_t>addrlen) + buf.write_byte(family) + buf.write_byte(<int8_t>bits) + buf.write_byte(is_cidr) + buf.write_byte(<int8_t>addrlen) + buf.write_cstr(addrbytes, addrlen) + + +cdef net_decode(CodecContext settings, FRBuffer *buf, bint as_cidr): + cdef: + int32_t family = <int32_t>frb_read(buf, 1)[0] + uint8_t bits = <uint8_t>frb_read(buf, 1)[0] + int prefix_len + int32_t is_cidr = <int32_t>frb_read(buf, 1)[0] + int32_t addrlen = <int32_t>frb_read(buf, 1)[0] + bytes addr + uint8_t max_prefix_len = _ip_max_prefix_len(family) + + if is_cidr != as_cidr: + raise ValueError('unexpected CIDR flag set in non-cidr value') + + if family != PGSQL_AF_INET and family != PGSQL_AF_INET6: + raise ValueError('invalid address family in "{}" value'.format( + 'cidr' if is_cidr else 'inet' + )) + + max_prefix_len = _ip_max_prefix_len(family) + + if bits > max_prefix_len: + raise ValueError('invalid network prefix length in "{}" value'.format( + 'cidr' if is_cidr else 'inet' + )) + + if addrlen != _ip_addr_len(family): + raise ValueError('invalid address length in "{}" value'.format( + 'cidr' if is_cidr else 'inet' + )) + + addr = cpython.PyBytes_FromStringAndSize(frb_read(buf, addrlen), addrlen) + + if as_cidr or bits != max_prefix_len: + prefix_len = cpython.PyLong_FromLong(bits) + + if as_cidr: + return _ipnet((addr, prefix_len)) + else: + return _ipiface((addr, prefix_len)) + else: + return _ipaddr(addr) + + +cdef cidr_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + object ipnet + int8_t family + + ipnet = _ipnet(obj) + family = _ver_to_family(ipnet.version) + _net_encode(buf, family, ipnet.prefixlen, 1, ipnet.network_address.packed) + + +cdef cidr_decode(CodecContext settings, FRBuffer *buf): + return net_decode(settings, buf, True) + + +cdef inet_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + object ipaddr + int8_t family + + try: + ipaddr = _ipaddr(obj) + except ValueError: + # PostgreSQL accepts *both* CIDR and host values + # for the host datatype. + ipaddr = _ipiface(obj) + family = _ver_to_family(ipaddr.version) + _net_encode(buf, family, ipaddr.network.prefixlen, 1, ipaddr.packed) + else: + family = _ver_to_family(ipaddr.version) + _net_encode(buf, family, _ip_max_prefix_len(family), 0, ipaddr.packed) + + +cdef inet_decode(CodecContext settings, FRBuffer *buf): + return net_decode(settings, buf, False) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/numeric.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/numeric.pyx new file mode 100644 index 00000000..b75d0961 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/numeric.pyx @@ -0,0 +1,356 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +from libc.math cimport abs, log10 +from libc.stdio cimport snprintf + +import decimal + +# defined in postgresql/src/backend/utils/adt/numeric.c +DEF DEC_DIGITS = 4 +DEF MAX_DSCALE = 0x3FFF +DEF NUMERIC_POS = 0x0000 +DEF NUMERIC_NEG = 0x4000 +DEF NUMERIC_NAN = 0xC000 +DEF NUMERIC_PINF = 0xD000 +DEF NUMERIC_NINF = 0xF000 + +_Dec = decimal.Decimal + + +cdef numeric_encode_text(CodecContext settings, WriteBuffer buf, obj): + text_encode(settings, buf, str(obj)) + + +cdef numeric_decode_text(CodecContext settings, FRBuffer *buf): + return _Dec(text_decode(settings, buf)) + + +cdef numeric_encode_binary(CodecContext settings, WriteBuffer buf, obj): + cdef: + object dec + object dt + int64_t exponent + int64_t i + int64_t j + tuple pydigits + int64_t num_pydigits + int16_t pgdigit + int64_t num_pgdigits + int16_t dscale + int64_t dweight + int64_t weight + uint16_t sign + int64_t padding_size = 0 + + if isinstance(obj, _Dec): + dec = obj + else: + dec = _Dec(obj) + + dt = dec.as_tuple() + + if dt.exponent == 'n' or dt.exponent == 'N': + # NaN + sign = NUMERIC_NAN + num_pgdigits = 0 + weight = 0 + dscale = 0 + elif dt.exponent == 'F': + # Infinity + if dt.sign: + sign = NUMERIC_NINF + else: + sign = NUMERIC_PINF + num_pgdigits = 0 + weight = 0 + dscale = 0 + else: + exponent = dt.exponent + if exponent < 0 and -exponent > MAX_DSCALE: + raise ValueError( + 'cannot encode Decimal value into numeric: ' + 'exponent is too small') + + if dt.sign: + sign = NUMERIC_NEG + else: + sign = NUMERIC_POS + + pydigits = dt.digits + num_pydigits = len(pydigits) + + dweight = num_pydigits + exponent - 1 + if dweight >= 0: + weight = (dweight + DEC_DIGITS) // DEC_DIGITS - 1 + else: + weight = -((-dweight - 1) // DEC_DIGITS + 1) + + if weight > 2 ** 16 - 1: + raise ValueError( + 'cannot encode Decimal value into numeric: ' + 'exponent is too large') + + padding_size = \ + (weight + 1) * DEC_DIGITS - (dweight + 1) + num_pgdigits = \ + (num_pydigits + padding_size + DEC_DIGITS - 1) // DEC_DIGITS + + if num_pgdigits > 2 ** 16 - 1: + raise ValueError( + 'cannot encode Decimal value into numeric: ' + 'number of digits is too large') + + # Pad decimal digits to provide room for correct Postgres + # digit alignment in the digit computation loop. + pydigits = (0,) * DEC_DIGITS + pydigits + (0,) * DEC_DIGITS + + if exponent < 0: + if -exponent > MAX_DSCALE: + raise ValueError( + 'cannot encode Decimal value into numeric: ' + 'exponent is too small') + dscale = <int16_t>-exponent + else: + dscale = 0 + + buf.write_int32(2 + 2 + 2 + 2 + 2 * <uint16_t>num_pgdigits) + buf.write_int16(<int16_t>num_pgdigits) + buf.write_int16(<int16_t>weight) + buf.write_int16(<int16_t>sign) + buf.write_int16(dscale) + + j = DEC_DIGITS - padding_size + + for i in range(num_pgdigits): + pgdigit = (pydigits[j] * 1000 + pydigits[j + 1] * 100 + + pydigits[j + 2] * 10 + pydigits[j + 3]) + j += DEC_DIGITS + buf.write_int16(pgdigit) + + +# The decoding strategy here is to form a string representation of +# the numeric var, as it is faster than passing an iterable of digits. +# For this reason the below code is pure overhead and is ~25% slower +# than the simple text decoder above. That said, we need the binary +# decoder to support binary COPY with numeric values. +cdef numeric_decode_binary_ex( + CodecContext settings, + FRBuffer *buf, + bint trail_fract_zero, +): + cdef: + uint16_t num_pgdigits = <uint16_t>hton.unpack_int16(frb_read(buf, 2)) + int16_t weight = hton.unpack_int16(frb_read(buf, 2)) + uint16_t sign = <uint16_t>hton.unpack_int16(frb_read(buf, 2)) + uint16_t dscale = <uint16_t>hton.unpack_int16(frb_read(buf, 2)) + int16_t pgdigit0 + ssize_t i + int16_t pgdigit + object pydigits + ssize_t num_pydigits + ssize_t actual_num_pydigits + ssize_t buf_size + int64_t exponent + int64_t abs_exponent + ssize_t exponent_chars + ssize_t front_padding = 0 + ssize_t num_fract_digits + ssize_t trailing_fract_zeros_adj + char smallbuf[_NUMERIC_DECODER_SMALLBUF_SIZE] + char *charbuf + char *bufptr + bint buf_allocated = False + + if sign == NUMERIC_NAN: + # Not-a-number + return _Dec('NaN') + elif sign == NUMERIC_PINF: + # +Infinity + return _Dec('Infinity') + elif sign == NUMERIC_NINF: + # -Infinity + return _Dec('-Infinity') + + if num_pgdigits == 0: + # Zero + return _Dec('0e-' + str(dscale)) + + pgdigit0 = hton.unpack_int16(frb_read(buf, 2)) + if weight >= 0: + if pgdigit0 < 10: + front_padding = 3 + elif pgdigit0 < 100: + front_padding = 2 + elif pgdigit0 < 1000: + front_padding = 1 + + # The number of fractional decimal digits actually encoded in + # base-DEC_DEIGITS digits sent by Postgres. + num_fract_digits = (num_pgdigits - weight - 1) * DEC_DIGITS + + # The trailing zero adjustment necessary to obtain exactly + # dscale number of fractional digits in output. May be negative, + # which indicates that trailing zeros in the last input digit + # should be discarded. + trailing_fract_zeros_adj = dscale - num_fract_digits + + # Maximum possible number of decimal digits in base 10. + # The actual number might be up to 3 digits smaller due to + # leading zeros in first input digit. + num_pydigits = num_pgdigits * DEC_DIGITS + if trailing_fract_zeros_adj > 0: + num_pydigits += trailing_fract_zeros_adj + + # Exponent. + exponent = (weight + 1) * DEC_DIGITS - front_padding + abs_exponent = abs(exponent) + if abs_exponent != 0: + # Number of characters required to render absolute exponent value + # in decimal. + exponent_chars = <ssize_t>log10(<double>abs_exponent) + 1 + else: + exponent_chars = 0 + + # Output buffer size. + buf_size = ( + 1 + # sign + 1 + # leading zero + 1 + # decimal dot + num_pydigits + # digits + 1 + # possible trailing zero padding + 2 + # exponent indicator (E-,E+) + exponent_chars + # exponent + 1 # null terminator char + ) + + if buf_size > _NUMERIC_DECODER_SMALLBUF_SIZE: + charbuf = <char *>cpython.PyMem_Malloc(<size_t>buf_size) + buf_allocated = True + else: + charbuf = smallbuf + + try: + bufptr = charbuf + + if sign == NUMERIC_NEG: + bufptr[0] = b'-' + bufptr += 1 + + bufptr[0] = b'0' + bufptr[1] = b'.' + bufptr += 2 + + if weight >= 0: + bufptr = _unpack_digit_stripping_lzeros(bufptr, pgdigit0) + else: + bufptr = _unpack_digit(bufptr, pgdigit0) + + for i in range(1, num_pgdigits): + pgdigit = hton.unpack_int16(frb_read(buf, 2)) + bufptr = _unpack_digit(bufptr, pgdigit) + + if dscale: + if trailing_fract_zeros_adj > 0: + for i in range(trailing_fract_zeros_adj): + bufptr[i] = <char>b'0' + + # If display scale is _less_ than the number of rendered digits, + # trailing_fract_zeros_adj will be negative and this will strip + # the excess trailing zeros. + bufptr += trailing_fract_zeros_adj + + if trail_fract_zero: + # Check if the number of rendered digits matches the exponent, + # and if so, add another trailing zero, so the result always + # appears with a decimal point. + actual_num_pydigits = bufptr - charbuf - 2 + if sign == NUMERIC_NEG: + actual_num_pydigits -= 1 + + if actual_num_pydigits == abs_exponent: + bufptr[0] = <char>b'0' + bufptr += 1 + + if exponent != 0: + bufptr[0] = b'E' + if exponent < 0: + bufptr[1] = b'-' + else: + bufptr[1] = b'+' + bufptr += 2 + snprintf(bufptr, <size_t>exponent_chars + 1, '%d', + <int>abs_exponent) + bufptr += exponent_chars + + bufptr[0] = 0 + + pydigits = cpythonx.PyUnicode_FromString(charbuf) + + return _Dec(pydigits) + + finally: + if buf_allocated: + cpython.PyMem_Free(charbuf) + + +cdef numeric_decode_binary(CodecContext settings, FRBuffer *buf): + return numeric_decode_binary_ex(settings, buf, False) + + +cdef inline char *_unpack_digit_stripping_lzeros(char *buf, int64_t pgdigit): + cdef: + int64_t d + bint significant + + d = pgdigit // 1000 + significant = (d > 0) + if significant: + pgdigit -= d * 1000 + buf[0] = <char>(d + <int32_t>b'0') + buf += 1 + + d = pgdigit // 100 + significant |= (d > 0) + if significant: + pgdigit -= d * 100 + buf[0] = <char>(d + <int32_t>b'0') + buf += 1 + + d = pgdigit // 10 + significant |= (d > 0) + if significant: + pgdigit -= d * 10 + buf[0] = <char>(d + <int32_t>b'0') + buf += 1 + + buf[0] = <char>(pgdigit + <int32_t>b'0') + buf += 1 + + return buf + + +cdef inline char *_unpack_digit(char *buf, int64_t pgdigit): + cdef: + int64_t d + + d = pgdigit // 1000 + pgdigit -= d * 1000 + buf[0] = <char>(d + <int32_t>b'0') + + d = pgdigit // 100 + pgdigit -= d * 100 + buf[1] = <char>(d + <int32_t>b'0') + + d = pgdigit // 10 + pgdigit -= d * 10 + buf[2] = <char>(d + <int32_t>b'0') + + buf[3] = <char>(pgdigit + <int32_t>b'0') + buf += 4 + + return buf diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/pg_snapshot.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/pg_snapshot.pyx new file mode 100644 index 00000000..d96107cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/pg_snapshot.pyx @@ -0,0 +1,63 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef pg_snapshot_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + ssize_t nxip + uint64_t xmin + uint64_t xmax + int i + WriteBuffer xip_buf = WriteBuffer.new() + + if not (cpython.PyTuple_Check(obj) or cpython.PyList_Check(obj)): + raise TypeError( + 'list or tuple expected (got type {})'.format(type(obj))) + + if len(obj) != 3: + raise ValueError( + 'invalid number of elements in txid_snapshot tuple, expecting 4') + + nxip = len(obj[2]) + if nxip > _MAXINT32: + raise ValueError('txid_snapshot value is too long') + + xmin = obj[0] + xmax = obj[1] + + for i in range(nxip): + xip_buf.write_int64( + <int64_t>cpython.PyLong_AsUnsignedLongLong(obj[2][i])) + + buf.write_int32(20 + xip_buf.len()) + + buf.write_int32(<int32_t>nxip) + buf.write_int64(<int64_t>xmin) + buf.write_int64(<int64_t>xmax) + buf.write_buffer(xip_buf) + + +cdef pg_snapshot_decode(CodecContext settings, FRBuffer *buf): + cdef: + int32_t nxip + uint64_t xmin + uint64_t xmax + tuple xip_tup + int32_t i + object xip + + nxip = hton.unpack_int32(frb_read(buf, 4)) + xmin = <uint64_t>hton.unpack_int64(frb_read(buf, 8)) + xmax = <uint64_t>hton.unpack_int64(frb_read(buf, 8)) + + xip_tup = cpython.PyTuple_New(nxip) + for i in range(nxip): + xip = cpython.PyLong_FromUnsignedLongLong( + <uint64_t>hton.unpack_int64(frb_read(buf, 8))) + cpython.Py_INCREF(xip) + cpython.PyTuple_SET_ITEM(xip_tup, i, xip) + + return (xmin, xmax, xip_tup) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/text.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/text.pyx new file mode 100644 index 00000000..79f375d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/text.pyx @@ -0,0 +1,48 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef inline as_pg_string_and_size( + CodecContext settings, obj, char **cstr, ssize_t *size): + + if not cpython.PyUnicode_Check(obj): + raise TypeError('expected str, got {}'.format(type(obj).__name__)) + + if settings.is_encoding_utf8(): + cstr[0] = <char*>cpythonx.PyUnicode_AsUTF8AndSize(obj, size) + else: + encoded = settings.get_text_codec().encode(obj)[0] + cpython.PyBytes_AsStringAndSize(encoded, cstr, size) + + if size[0] > 0x7fffffff: + raise ValueError('string too long') + + +cdef text_encode(CodecContext settings, WriteBuffer buf, obj): + cdef: + char *str + ssize_t size + + as_pg_string_and_size(settings, obj, &str, &size) + + buf.write_int32(<int32_t>size) + buf.write_cstr(str, size) + + +cdef inline decode_pg_string(CodecContext settings, const char* data, + ssize_t len): + + if settings.is_encoding_utf8(): + # decode UTF-8 in strict mode + return cpython.PyUnicode_DecodeUTF8(data, len, NULL) + else: + bytes = cpython.PyBytes_FromStringAndSize(data, len) + return settings.get_text_codec().decode(bytes)[0] + + +cdef text_decode(CodecContext settings, FRBuffer *buf): + cdef ssize_t buf_len = buf.len + return decode_pg_string(settings, frb_read_all(buf), buf_len) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/tid.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/tid.pyx new file mode 100644 index 00000000..b39bddc4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/tid.pyx @@ -0,0 +1,51 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef tid_encode(CodecContext settings, WriteBuffer buf, obj): + cdef int overflow = 0 + cdef unsigned long block, offset + + if not (cpython.PyTuple_Check(obj) or cpython.PyList_Check(obj)): + raise TypeError( + 'list or tuple expected (got type {})'.format(type(obj))) + + if len(obj) != 2: + raise ValueError( + 'invalid number of elements in tid tuple, expecting 2') + + try: + block = cpython.PyLong_AsUnsignedLong(obj[0]) + except OverflowError: + overflow = 1 + + # "long" and "long long" have the same size for x86_64, need an extra check + if overflow or (sizeof(block) > 4 and block > UINT32_MAX): + raise OverflowError('tuple id block value out of uint32 range') + + try: + offset = cpython.PyLong_AsUnsignedLong(obj[1]) + overflow = 0 + except OverflowError: + overflow = 1 + + if overflow or offset > 65535: + raise OverflowError('tuple id offset value out of uint16 range') + + buf.write_int32(6) + buf.write_int32(<int32_t>block) + buf.write_int16(<int16_t>offset) + + +cdef tid_decode(CodecContext settings, FRBuffer *buf): + cdef: + uint32_t block + uint16_t offset + + block = <uint32_t>hton.unpack_int32(frb_read(buf, 4)) + offset = <uint16_t>hton.unpack_int16(frb_read(buf, 2)) + + return (block, offset) diff --git a/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/uuid.pyx b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/uuid.pyx new file mode 100644 index 00000000..0bc45679 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/asyncpg/pgproto/codecs/uuid.pyx @@ -0,0 +1,27 @@ +# Copyright (C) 2016-present the asyncpg authors and contributors +# <see AUTHORS file> +# +# This module is part of asyncpg and is released under +# the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0 + + +cdef uuid_encode(CodecContext settings, WriteBuffer wbuf, obj): + cdef: + char buf[16] + + if type(obj) is pg_UUID: + wbuf.write_int32(<int32_t>16) + wbuf.write_cstr((<UUID>obj)._data, 16) + elif cpython.PyUnicode_Check(obj): + pg_uuid_bytes_from_str(obj, buf) + wbuf.write_int32(<int32_t>16) + wbuf.write_cstr(buf, 16) + else: + bytea_encode(settings, wbuf, obj.bytes) + + +cdef uuid_decode(CodecContext settings, FRBuffer *buf): + if buf.len != 16: + raise TypeError( + f'cannot decode UUID, expected 16 bytes, got {buf.len}') + return pg_uuid_from_buf(frb_read_all(buf)) |