"""Handle numeric type modifier."""
def get_modifier(self, typemod: int) -> tuple[int, ...] | None:
- rv = []
precision = self.get_precision(typemod)
- if precision is not None:
- rv.append(precision)
scale = self.get_scale(typemod)
- if scale is not None:
- rv.append(scale)
- return tuple(rv) if rv else None
+ return None if precision is None or scale is None else (precision, scale)
def get_precision(self, typemod: int) -> int | None:
return typemod >> 16 if typemod >= 0 else None
def get_scale(self, typemod: int) -> int | None:
- typemod -= 4
- return typemod & 0xFFFF if typemod >= 0 else None
+ if typemod < 0:
+ return None
+
+ scale = (typemod - 4) & 0xFFFF
+ if scale >= 0x400:
+ scale = scale - 0x800
+ return scale
class CharTypeModifier(TypeModifier):
"""Handle char/varchar type modifier."""
+ def get_modifier(self, typemod: int) -> tuple[int, ...] | None:
+ dsize = self.get_display_size(typemod)
+ return (dsize,) if dsize else None
+
def get_display_size(self, typemod: int) -> int | None:
return typemod - 4 if typemod >= 0 else None
class TimeTypeModifier(TypeModifier):
"""Handle time-related types modifier."""
+ def get_modifier(self, typemod: int) -> tuple[int, ...] | None:
+ prec = self.get_precision(typemod)
+ return (prec,) if prec is not None else None
+
def get_precision(self, typemod: int) -> int | None:
return typemod & 0xFFFF if typemod >= 0 else None
import pytest
from psycopg.postgres import types as builtins
-from .fix_crdb import is_crdb, crdb_encoding, crdb_time_precision
+from .fix_crdb import is_crdb, crdb_encoding
def test_description_attribs(conn):
[
("text", None, None, None, None),
("varchar", None, None, None, None),
+ ("varchar(1)", None, None, 1, None),
("varchar(42)", None, None, 42, None),
+ ("bpchar(42)", None, None, 42, None),
+ ("varchar(10485760)", None, None, 10485760, None),
("int4", None, None, None, 4),
("numeric", None, None, None, None),
- ("numeric(10)", 10, 0, None, None),
- ("numeric(10, 3)", 10, 3, None, None),
+ ("numeric(10,0)", 10, 0, None, None),
+ ("numeric(10,3)", 10, 3, None, None),
+ ("numeric(10,-1)", 10, -1, None, None),
+ ("numeric(1,-1000)", 1, -1000, None, None),
+ ("numeric(1,1000)", 1, 1000, None, None),
+ ("numeric(1000,1000)", 1000, 1000, None, None),
("time", None, None, None, 8),
- crdb_time_precision("time(4)", 4, None, None, 8),
- crdb_time_precision("time(10)", 6, None, None, 8),
+ ("time", None, None, None, 8),
+ ("timetz", None, None, None, 12),
+ ("timestamp", None, None, None, 8),
+ ("timestamptz", None, None, None, 8),
+ ("interval", None, None, None, 16),
],
)
def test_details(conn, type, precision, scale, dsize, isize):
cur = conn.cursor()
cur.execute(f"select null::{type}")
col = cur.description[0]
- repr(col)
+ assert type in (repr(col))
assert col.precision == precision
assert col.scale == scale
assert col.display_size == dsize
assert col.internal_size == isize
+@pytest.mark.crdb("skip", reason="time precision")
+@pytest.mark.parametrize("type", "time timetz timestamp timestamptz interval".split())
+@pytest.mark.parametrize("precision", [0, 2, 6])
+def test_details_time(conn, type, precision):
+ type = f"{type}({precision})"
+ cur = conn.cursor()
+ cur.execute(f"select null::{type}")
+ col = cur.description[0]
+ assert type in (repr(col))
+ assert col.precision == precision
+
+
def test_pickle(conn):
curs = conn.cursor()
curs.execute(