Major fixes and new features
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-09-25 15:51:48 +09:00
parent dd7349bb4c
commit ddce9f5125
5586 changed files with 1470941 additions and 0 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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, &microseconds)
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, &microseconds)
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, &microseconds)
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, &microseconds)
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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)))

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))