codekingpro/portable-devtools
114k
1"""2The Column object in Cursor.description3"""4 5# Copyright (C) 2020 The Psycopg Team6 7from typing import Any, NamedTuple, Optional, Sequence, TYPE_CHECKING8from operator import attrgetter9 10if TYPE_CHECKING:11 from .cursor import BaseCursor12 13 14class ColumnData(NamedTuple):15 ftype: int16 fmod: int17 fsize: int18 19 20class Column(Sequence[Any]):21 __module__ = "psycopg"22 23 def __init__(self, cursor: "BaseCursor[Any, Any]", index: int):24 res = cursor.pgresult25 assert res26 27 fname = res.fname(index)28 if fname:29 self._name = fname.decode(cursor._encoding)30 else:31 # COPY_OUT results have columns but no name32 self._name = f"column_{index + 1}"33 34 self._data = ColumnData(35 ftype=res.ftype(index),36 fmod=res.fmod(index),37 fsize=res.fsize(index),38 )39 self._type = cursor.adapters.types.get(self._data.ftype)40 41 _attrs = tuple(42 attrgetter(attr)43 for attr in """44 name type_code display_size internal_size precision scale null_ok45 """.split()46 )47 48 def __repr__(self) -> str:49 return (50 f"<Column {self.name!r},"51 f" type: {self._type_display()} (oid: {self.type_code})>"52 )53 54 def __len__(self) -> int:55 return 756 57 def _type_display(self) -> str:58 parts = []59 parts.append(self._type.name if self._type else str(self.type_code))60 61 mod1 = self.precision62 if mod1 is None:63 mod1 = self.display_size64 if mod1:65 parts.append(f"({mod1}")66 if self.scale:67 parts.append(f", {self.scale}")68 parts.append(")")69 70 if self._type and self.type_code == self._type.array_oid:71 parts.append("[]")72 73 return "".join(parts)74 75 def __getitem__(self, index: Any) -> Any:76 if isinstance(index, slice):77 return tuple(getter(self) for getter in self._attrs[index])78 else:79 return self._attrs[index](self)80 81 @property82 def name(self) -> str:83 """The name of the column."""84 return self._name85 86 @property87 def type_code(self) -> int:88 """The numeric OID of the column."""89 return self._data.ftype90 91 @property92 def display_size(self) -> Optional[int]:93 """The field size, for :sql:`varchar(n)`, None otherwise."""94 if not self._type:95 return None96 97 if self._type.name in ("varchar", "char"):98 fmod = self._data.fmod99 if fmod >= 0:100 return fmod - 4101 102 return None103 104 @property105 def internal_size(self) -> Optional[int]:106 """The internal field size for fixed-size types, None otherwise."""107 fsize = self._data.fsize108 return fsize if fsize >= 0 else None109 110 @property111 def precision(self) -> Optional[int]:112 """The number of digits for fixed precision types."""113 if not self._type:114 return None115 116 dttypes = ("time", "timetz", "timestamp", "timestamptz", "interval")117 if self._type.name == "numeric":118 fmod = self._data.fmod119 if fmod >= 0:120 return fmod >> 16121 122 elif self._type.name in dttypes:123 fmod = self._data.fmod124 if fmod >= 0:125 return fmod & 0xFFFF126 127 return None128 129 @property130 def scale(self) -> Optional[int]:131 """The number of digits after the decimal point if available."""132 if self._type and self._type.name == "numeric":133 fmod = self._data.fmod - 4134 if fmod >= 0:135 return fmod & 0xFFFF136 137 return None138 139 @property140 def null_ok(self) -> Optional[bool]:141 """Always `!None`"""142 return None143 