Source code for tableauhyperapi.sqltype

import datetime
import decimal
import enum
import functools

from typing import Optional, Union

from .date import Date
from .impl import hapi
from .impl.util import check_precondition
from .interval import Interval
from .timestamp import Timestamp


ScalarValue = Union[bool, bytearray, bytes, datetime.time, Date, decimal.Decimal, float, int, Interval, str, Timestamp]
NullableValue = Optional[ScalarValue]
"""
Return type used for single SQL values.

This lists all the possible types in :any:`TypeTag`.
Note that the SQL NULL value maps to None, and thus it is covered by the `Optional`.
"""


[docs] @enum.unique class TypeTag(enum.Enum): """ Constants which represent Hyper data types. These are used as values of the :any:`SqlType.tag` attribute. """ UNSUPPORTED = hapi.HYPER_UNSUPPORTED """ Unsupported type. Queries and tables may have columns of this type if the database was created by a newer version of the library. Values are represented by Python's ``bytes``. """ BOOL = hapi.HYPER_BOOL """ Boolean values. Represented by Python's ``bool``. """ BIG_INT = hapi.HYPER_BIG_INT """ Eight-byte integer values. Represented by Python's ``int``. """ SMALL_INT = hapi.HYPER_SMALL_INT """ Two-byte integer values. Represented by Python's ``int``. """ INT = hapi.HYPER_INT """ Four-byte integer values. Represented by Python's ``int``. """ NUMERIC = hapi.HYPER_NUMERIC """ Exact decimal numbers with user-specified precision. Represented by ``decimal.Decimal``. """ FLOAT = hapi.HYPER_FLOAT """ Single precision floating point values. Represented by Python's ``float``. """ DOUBLE = hapi.HYPER_DOUBLE """ Double precision floating point values. Represented by Python's ``float``. """ OID = hapi.HYPER_OID """ OID values. Represented by Python's ``int``. """ BYTES = hapi.HYPER_BYTE_A """ Byte array. These are returned as ``bytes`` objects from queries. When writing data, ``bytearray`` and ``bytes`` objects are accepted. """ TEXT = hapi.HYPER_TEXT """ Unicode text. These are returned as ``str`` or UTF8-encoded ``bytes`` objects, depending on the ``str_as_bytes`` parameter of :any:`execute_query`/:any:`execute_list_query`/:any:`execute_scalar_query` methods. When writing data, ``str``, ``bytearray``, and ``bytes`` objects are accepted. Bytes are assumed to be UTF8-encoded. """ VARCHAR = hapi.HYPER_VARCHAR """ Unicode text with maximum length. These are returned as ``str`` or UTF8-encoded ``bytes`` objects, depending on the ``str_as_bytes`` parameter of :any:`execute_query`/:any:`execute_list_query`/:any:`execute_scalar_query` methods. When writing data, ``str``, ``bytearray``, and ``bytes`` objects are accepted. Bytes are assumed to be UTF8-encoded. """ CHAR = hapi.HYPER_CHAR """ Space-padded unicode text. These are returned as ``str`` or UTF8-encoded ``bytes`` objects, depending on the ``str_as_bytes`` parameter of :any:`execute_query`/:any:`execute_list_query`/:any:`execute_scalar_query` methods. When writing data, ``str``, ``bytearray``, and ``bytes`` objects are accepted. Bytes are assumed to be UTF8-encoded. """ JSON = hapi.HYPER_JSON """ Unicode text. These are returned as ``str`` or UTF8-encoded ``bytes`` objects, depending on the ``str_as_bytes`` parameter of :any:`execute_query`/:any:`execute_list_query`/:any:`execute_scalar_query` methods. When writing data, ``str``, ``bytearray``, and ``bytes`` objects are accepted. Bytes are assumed to be UTF8-encoded. """ DATE = hapi.HYPER_DATE """ Date values. These are returned as :any:`Date` objects from queries. When writing data, ``datetime.date``, ``datetime.datetime`` are also accepted; time part in ``datetime.datetime`` is ignored. """ INTERVAL = hapi.HYPER_INTERVAL """ Time interval - union of logically independent months, days, and microseconds components. Represented by :any:`Interval` objects. """ TIME = hapi.HYPER_TIME """ Time of the day, from 00:00:00 to 23:59:59:999999. These are returned as ``datetime.time`` objects from queries. When writing data, ``datetime.time`` and ``datetime.datetime`` objects are accepted; date part in ``datetime.datetime`` is ignored. """ TIMESTAMP = hapi.HYPER_TIMESTAMP """ Timestamp - date and time of day. These are returned as :any:`Timestamp` objects from queries. When writing data, ``datetime.datetime`` objects are also accepted. """ TIMESTAMP_TZ = hapi.HYPER_TIMESTAMP_TZ """ UTC Timestamp - date and time of day. These are returned as :any:`Timestamp` objects from queries. When writing data, ``datetime.datetime`` objects are also accepted. """ GEOGRAPHY = hapi.HYPER_GEOGRAPHY """ Geography. These are returned as ``bytes`` objects from queries. When writing data, ``bytes`` and ``bytearray`` objects are accepted. """ @staticmethod def _from_value(value: int): for e in TypeTag: if e.value == value: return e raise ValueError('Invalid type tag value {}'.format(value))
# SqlType.int() shadows builtin int when 'int' is used as a type annotation _int_type = int # noinspection DuplicatedCode
[docs] @functools.total_ordering class SqlType: """ An object which represents a column type - type tag and optional type-specific modifiers. Do not use the constructor directly, instead use one of the factory methods. """ _UNUSED_MODIFIER = 0xFFFFFFFF __OID_MAP = { TypeTag.BOOL: hapi.HYPER_OID_BOOL, TypeTag.BIG_INT: hapi.HYPER_OID_BIG_INT, TypeTag.SMALL_INT: hapi.HYPER_OID_SMALL_INT, TypeTag.INT: hapi.HYPER_OID_INT, TypeTag.NUMERIC: hapi.HYPER_OID_NUMERIC, TypeTag.FLOAT: hapi.HYPER_OID_FLOAT, TypeTag.DOUBLE: hapi.HYPER_OID_DOUBLE, TypeTag.OID: hapi.HYPER_OID_OID, TypeTag.BYTES: hapi.HYPER_OID_BYTE_A, TypeTag.TEXT: hapi.HYPER_OID_TEXT, TypeTag.VARCHAR: hapi.HYPER_OID_VARCHAR, TypeTag.CHAR: hapi.HYPER_OID_CHAR, TypeTag.JSON: hapi.HYPER_OID_JSON, TypeTag.DATE: hapi.HYPER_OID_DATE, TypeTag.INTERVAL: hapi.HYPER_OID_INTERVAL, TypeTag.TIME: hapi.HYPER_OID_TIME, TypeTag.TIMESTAMP: hapi.HYPER_OID_TIMESTAMP, TypeTag.TIMESTAMP_TZ: hapi.HYPER_OID_TIMESTAMP_TZ, TypeTag.GEOGRAPHY: hapi.HYPER_OID_GEOGRAPHY, } def __init__(self, tag: TypeTag, modifier: int = _UNUSED_MODIFIER, oid: int = None): self.__tag = tag if oid is None: if tag == TypeTag.CHAR and modifier == hapi.hyper_encode_string_modifier(1): oid = hapi.HYPER_OID_CHAR1 else: oid = SqlType.__OID_MAP[tag] self.__oid = oid self.__modifier = modifier @property def tag(self) -> TypeTag: """ The underlying type tag, one of the :any:`TypeTag` constants. """ return self.__tag @property def internal_oid(self) -> int: """ The underlying type OID. This property is internal and may change or go away in the future versions. """ return self.__oid @property def internal_type_modifier(self) -> int: """ The underlying type modifier. This property is internal and may change or go away in the future versions. """ return self.__modifier @property def precision(self) -> Optional[int]: """ The precision, i.e., maximum number of digits, of a :any:`NUMERIC` value. """ if self.__tag == TypeTag.NUMERIC: return hapi.hyper_get_precision_from_modifier(self.__modifier) else: return None @property def scale(self) -> Optional[int]: """ The scale, i.e., number of fractional digits, of a :any:`NUMERIC` value. """ if self.__tag == TypeTag.NUMERIC: return hapi.hyper_get_scale_from_modifier(self.__modifier) else: return None @property def max_length(self) -> Optional[int]: """ The max length of this type if it is :any:`CHAR` or :any:`VARCHAR`, otherwise ``None``. """ if self.__tag in (TypeTag.CHAR, TypeTag.VARCHAR): return hapi.hyper_get_max_length_from_modifier(self.__modifier) else: return None
[docs] @staticmethod def bool() -> 'SqlType': """ Creates an instance of a :any:`BOOL` type. """ return SqlType(TypeTag.BOOL)
[docs] @staticmethod def small_int() -> 'SqlType': """ Creates an instance of a :any:`SMALL_INT` type. """ return SqlType(TypeTag.SMALL_INT)
[docs] @staticmethod def int() -> 'SqlType': """ Creates an instance of an :any:`INT` type. """ return SqlType(TypeTag.INT)
[docs] @staticmethod def big_int() -> 'SqlType': """ Creates an instance of a :any:`BIG_INT` type. """ return SqlType(TypeTag.BIG_INT)
[docs] @staticmethod def float() -> 'SqlType': """ Creates an instance of a :any:`FLOAT` type. """ return SqlType(TypeTag.FLOAT)
[docs] @staticmethod def double() -> 'SqlType': """ Creates an instance of a :any:`DOUBLE` type. """ return SqlType(TypeTag.DOUBLE)
[docs] @staticmethod def oid() -> 'SqlType': """ Creates an instance of an :any:`OID` type. """ return SqlType(TypeTag.OID)
[docs] @staticmethod def date() -> 'SqlType': """ Creates an instance of a :any:`DATE` type. """ return SqlType(TypeTag.DATE)
[docs] @staticmethod def time() -> 'SqlType': """ Creates an instance of a :any:`TIME` type. """ return SqlType(TypeTag.TIME)
[docs] @staticmethod def timestamp() -> 'SqlType': """ Creates an instance of a :any:`TIMESTAMP` type. """ return SqlType(TypeTag.TIMESTAMP)
[docs] @staticmethod def timestamp_tz() -> 'SqlType': """ Creates an instance of a :any:`TIMESTAMP_TZ` type. """ return SqlType(TypeTag.TIMESTAMP_TZ)
[docs] @staticmethod def interval() -> 'SqlType': """ Creates an instance of an :any:`INTERVAL` type. """ return SqlType(TypeTag.INTERVAL)
[docs] @staticmethod def text() -> 'SqlType': """ Creates an instance of a :any:`TEXT` type. """ return SqlType(TypeTag.TEXT)
[docs] @staticmethod def json() -> 'SqlType': """ Creates an instance of a :any:`JSON` type. """ return SqlType(TypeTag.JSON)
[docs] @staticmethod def geography() -> 'SqlType': """ Creates an instance of a :any:`GEOGRAPHY` type. """ return SqlType(TypeTag.GEOGRAPHY)
[docs] @staticmethod def bytes() -> 'SqlType': """ Creates an instance of a :any:`BYTES` type. """ return SqlType(TypeTag.BYTES)
[docs] @staticmethod def char(max_length: _int_type) -> 'SqlType': """ Creates an instance of a :any:`CHAR` type. """ check_precondition(max_length >= 1, "'max_length' must be positive") return SqlType(TypeTag.CHAR, hapi.hyper_encode_string_modifier(max_length))
[docs] @staticmethod def varchar(max_length: _int_type) -> 'SqlType': """ Creates an instance of a :any:`VARCHAR` type. """ check_precondition(max_length >= 1, "'max_length' must be positive") return SqlType(TypeTag.VARCHAR, hapi.hyper_encode_string_modifier(max_length))
[docs] @staticmethod def numeric(precision: _int_type, scale: _int_type) -> 'SqlType': """ Creates an instance of a :any:`NUMERIC` type. """ check_precondition(1 <= precision <= 38, "'precision' must be between 1 and 38") check_precondition(0 <= scale <= precision, "'scale' must be between 0 and 'precision'") return SqlType(TypeTag.NUMERIC, hapi.hyper_encode_numeric_modifier(precision, scale))
def __as_int_tuple(self): return self.__tag.value, self.__modifier, self.__oid def __eq__(self, other): if not isinstance(other, SqlType): return NotImplemented return self.__as_int_tuple() == other.__as_int_tuple() def __lt__(self, other): if not isinstance(other, SqlType): return NotImplemented return self.__as_int_tuple() < other.__as_int_tuple() def __hash__(self): return hash(self.__as_int_tuple()) def __str__(self): if self.__tag in (TypeTag.CHAR, TypeTag.VARCHAR): return f'{self.__tag.name}({self.max_length})' if self.__tag == TypeTag.NUMERIC: return f'NUMERIC({self.precision}, {self.scale})' return self.__tag.name def __repr__(self): cls_name = self.__class__.__name__ if self.__tag in (TypeTag.CHAR, TypeTag.VARCHAR): return f'{cls_name}.{self.__tag.name.lower()}({self.max_length})' if self.__tag == TypeTag.NUMERIC: return f'{cls_name}.numeric({self.precision}, {self.scale})' return f'{cls_name}.{self.__tag.name.lower()}()'