From acfe51d49d001f7c5ef1bd22f56c44610703551d Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Tue, 29 Oct 2024 22:08:15 -0500 Subject: [PATCH 01/15] [#5200]Update the type.py for python client. --- clients/client-python/gravitino/api/type.py | 148 ++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 clients/client-python/gravitino/api/type.py diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py new file mode 100644 index 00000000000..c2399aa2901 --- /dev/null +++ b/clients/client-python/gravitino/api/type.py @@ -0,0 +1,148 @@ +from abc import ABC, abstractmethod +from enum import Enum + +from enum import Enum + +class Name(Enum): + """ + The root type name of this type, representing all data types supported. + """ + + BOOLEAN = "BOOLEAN" + """ The boolean type. """ + + BYTE = "BYTE" + """ The byte type. """ + + SHORT = "SHORT" + """ The short type. """ + + INTEGER = "INTEGER" + """ The integer type. """ + + LONG = "LONG" + """ The long type. """ + + FLOAT = "FLOAT" + """ The float type. """ + + DOUBLE = "DOUBLE" + """ The double type. """ + + DECIMAL = "DECIMAL" + """ The decimal type. """ + + DATE = "DATE" + """ The date type. """ + + TIME = "TIME" + """ The time type. """ + + TIMESTAMP = "TIMESTAMP" + """ The timestamp type. """ + + INTERVAL_YEAR = "INTERVAL_YEAR" + """ The interval year type. """ + + INTERVAL_DAY = "INTERVAL_DAY" + """ The interval day type. """ + + STRING = "STRING" + """ The string type. """ + + VARCHAR = "VARCHAR" + """ The varchar type. """ + + FIXEDCHAR = "FIXEDCHAR" + """ The char type with fixed length. """ + + UUID = "UUID" + """ The UUID type. """ + + FIXED = "FIXED" + """ The binary type with fixed length. """ + + BINARY = "BINARY" + """ The binary type with variable length. The length is specified in the type itself. """ + + STRUCT = "STRUCT" + """ + The struct type. + A struct type is a complex type that contains a set of named fields, each with a type, + and optionally a comment. + """ + + LIST = "LIST" + """ + The list type. + A list type is a complex type that contains a set of elements, each with the same type. + """ + + MAP = "MAP" + """ + The map type. + A map type is a complex type that contains a set of key-value pairs, each with a key type + and a value type. + """ + + UNION = "UNION" + """ + The union type. + A union type is a complex type that contains a set of types. + """ + + NULL = "NULL" + """ The null type. A null type represents a value that is null. """ + + UNPARSED = "UNPARSED" + """ The unparsed type. An unparsed type represents an unresolvable type. """ + + EXTERNAL = "EXTERNAL" + """ The external type. An external type represents a type that is not supported. """ + +# Define the Type interface (abstract base class) +class Type(ABC): + @abstractmethod + def name(self) -> Name: + """ Returns the generic name of the type. """ + pass + + @abstractmethod + def simpleString(self) -> str: + """ Returns a readable string representation of the type. """ + pass + +# Define base classes +class PrimitiveType(Type, ABC): + """ Base class for all primitive types. """ + pass + +class NumericType(PrimitiveType, ABC): + """ Base class for all numeric types. """ + pass + +class DateTimeType(PrimitiveType, ABC): + """ Base class for all date/time types. """ + pass + +class IntervalType(PrimitiveType, ABC): + """ Base class for all interval types. """ + pass + +class ComplexType(Type, ABC): + """ Base class for all complex types, including struct, list, map, and union. """ + pass + +# Define IntegralType class +class IntegralType(NumericType, ABC): + def __init__(self, signed: bool): + self._signed = signed + + def signed(self) -> bool: + """ Returns True if the integer type is signed, False otherwise. """ + return self._signed + +# Define FractionType class +class FractionType(NumericType, ABC): + """ Base class for all fractional types. """ + pass From aff35d6941a25cd45684ca007edc0777de83fa49 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Tue, 29 Oct 2024 23:18:33 -0500 Subject: [PATCH 02/15] Update the types.java in python client --- clients/client-python/gravitino/api/type.py | 1238 ++++++++++++++++++- 1 file changed, 1236 insertions(+), 2 deletions(-) diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index c2399aa2901..760692304b8 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -1,7 +1,6 @@ from abc import ABC, abstractmethod from enum import Enum - -from enum import Enum +from typing import Optional class Name(Enum): """ @@ -146,3 +145,1238 @@ def signed(self) -> bool: class FractionType(NumericType, ABC): """ Base class for all fractional types. """ pass + +""" The helper class for Type. """ +class Types: + + """ The data type representing `NULL` values. """ + class NullType(Type): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of NullType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Null type. + """ + return Name.NULL + + def simpleString(self) -> str: + """ + @return A readable string representation of the Null type. + """ + return "null" + + """ The boolean type in Gravitino. """ + class BooleanType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of BooleanType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Boolean type. + """ + return Name.BOOLEAN + + def simpleString(self) -> str: + """ + @return A readable string representation of the Boolean type. + """ + return "boolean" + + """ The byte type in Gravitino. """ + class ByteType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of ByteType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned ByteType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the byte type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Byte type. + """ + return Name.BYTE + + def simpleString(self) -> str: + """ + @return A readable string representa + """ + return "byte" if self.signed() else "byte unsigned" + + """ The short type in Gravitino. """ + class ShortType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of ShortType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned ShortType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the short type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Short type. + """ + return Name.SHORT + + def simpleString(self) -> str: + """ + @return A readable string representation of the Short type. + """ + return "short" if self.signed() else "short unsigned" + + """ The integer type in Gravitino. """ + class IntegerType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of IntegerType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned IntegerType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the integer type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Integer type. + """ + return Name.INTEGER + + def simpleString(self) -> str: + """ + @return A readable string representation of the Integer type. + """ + return "integer" if self.signed() else "integer unsigned" + + """ The long type in Gravitino. """ + class LongType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of LongType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned LongType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the long type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Long type. + """ + return Name.LONG + + def simpleString(self) -> str: + """ + @return A readable string representation of the Long type. + """ + return "long" if self.signed() else "long unsigned" + + """ The float type in Gravitino. """ + class FloatType(FractionType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of FloatType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Float type. + """ + return Name.FLOAT + + def simpleString(self) -> str: + """ + @return A readable string representation of the Float type. + """ + return "float" + + """ The double type in Gravitino. """ + class DoubleType(FractionType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of DoubleType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Double type. + """ + return Name.DOUBLE + + def simpleString(self) -> str: + """ + @return A readable string representation of the Double type. + """ + return "double" + + """ The decimal type in Gravitino. """ + class DecimalType(FractionType): + @classmethod + def of(cls, precision: int, scale: int): + """ + @param precision: The precision of the decimal type. + @param scale: The scale of the decimal type. + @return A DecimalType with the given precision and scale. + """ + return cls(precision, scale) + + def __init__(self, precision: int, scale: int): + """ + @param precision: The precision of the decimal type. + @param scale: The scale of the decimal type. + """ + self.check_precision_scale(precision, scale) + self._precision = precision + self._scale = scale + + @staticmethod + def check_precision_scale(precision: int, scale: int): + """ + Ensures the precision and scale values are within valid range. + @param precision: The precision of the decimal. + @param scale: The scale of the decimal. + """ + if not (1 <= precision <= 38): + raise ValueError(f"Decimal precision must be in range [1, 38]: {precision}") + if not (0 <= scale <= precision): + raise ValueError(f"Decimal scale must be in range [0, precision ({precision})]: {scale}") + + def name(self) -> Name: + """ + @return The name of the Decimal type. + """ + return Name.DECIMAL + + def precision(self) -> int: + """ + @return The precision of the decimal type. + """ + return self._precision + + def scale(self) -> int: + """ + @return The scale of the decimal type. + """ + return self._scale + + def simpleString(self) -> str: + """ + @return A readable string representation of the Decimal type. + """ + return f"decimal({self._precision},{self._scale})" + + def __eq__(self, other): + """ + Compares two DecimalType objects for equality. + + @param other: The other DecimalType to compare with. + @return: True if both objects have the same precision and scale, False otherwise. + """ + if not isinstance(other, Types.DecimalType): + return False + return self._precision == other._precision and self._scale == other._scale + + def __hash__(self): + """ + @return: A hash code for the DecimalType based on its precision and scale. + """ + return hash((self._precision, self._scale)) + + """ The date time type in Gravitino. """ + class DateType(DateTimeType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of DateType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Date type. + """ + return Name.DATE + + def simpleString(self) -> str: + """ + @return A readable string representation of the Date type. + """ + return "date" + + """ The time type in Gravitino. """ + class TimeType(DateTimeType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of TimeType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Time type. + """ + return Name.TIME + + def simpleString(self) -> str: + """ + @return A readable string representation of the Time type. + """ + return "time" + + """ The timestamp type in Gravitino. """ + class TimestampType(DateTimeType): + _instance_with_tz = None + _instance_without_tz = None + + @classmethod + def withTimeZone(cls): + """ + @return A TimestampType with time zone. + """ + if cls._instance_with_tz is None: + cls._instance_with_tz = cls(True) + return cls._instance_with_tz + + @classmethod + def withoutTimeZone(cls): + """ + @return A TimestampType without time zone. + """ + if cls._instance_without_tz is None: + cls._instance_without_tz = cls(False) + return cls._instance_without_tz + + def __init__(self, with_time_zone: bool): + """ + @param with_time_zone: True if the timestamp type has a time zone, False otherwise. + """ + self._with_time_zone = with_time_zone + + def hasTimeZone(self) -> bool: + """ + @return True if the timestamp type has a time zone, False otherwise. + """ + return self._with_time_zone + + def name(self) -> Name: + """ + @return The name of the Timestamp type. + """ + return Name.TIMESTAMP + + def simpleString(self) -> str: + """ + @return A readable string representation of the Timestamp type. + """ + return "timestamp_tz" if self._with_time_zone else "timestamp" + + """ The interval year type in Gravitino. """ + class IntervalYearType(IntervalType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of IntervalYearType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the IntervalYear type. + """ + return Name.INTERVAL_YEAR + + def simpleString(self) -> str: + """ + @return A readable string representation of the IntervalYear type. + """ + return "interval_year" + + """ The interval day type in Gravitino. """ + class IntervalDayType(IntervalType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of IntervalDayType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the interval day type. + """ + return Name.INTERVAL_DAY + + def simpleString(self) -> str: + """ + @return: A readable string representation of the interval day type. + """ + return "interval_day" + + """ The string type in Gravitino, equivalent to varchar(MAX), where the MAX is determined by the underlying catalog. """ + class StringType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of StringType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the string type. + """ + return Name.STRING + + def simpleString(self) -> str: + """ + @return: A readable string representation of the string type. + """ + return "string" + + """ The UUID type in Gravitino. """ + class UUIDType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of UUIDType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the UUID type. + """ + return Name.UUID + + def simpleString(self) -> str: + """ + @return: A readable string representation of the UUID type. + """ + return "uuid" + + """ Fixed-length byte array type. Use BinaryType for variable-length byte arrays. """ + class FixedType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the FixedType with the given length. + + @param length: The length of the fixed type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the fixed type. + @return: A FixedType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the fixed type. + """ + return Name.FIXED + + def length(self) -> int: + """ + @return: The length of the fixed type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the fixed type. + """ + return f"fixed({self._length})" + + def __eq__(self, other): + """ + Compares two FixedType objects for equality. + + @param other: The other FixedType object to compare with. + @return: True if both FixedType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.FixedType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the FixedType based on its length. + """ + return hash(self._length) + + """ The varchar type in Gravitino. """ + class VarCharType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the VarCharType with the given length. + + @param length: The length of the varchar type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the varchar type. + @return: A VarCharType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the varchar type. + """ + return Name.VARCHAR + + def length(self) -> int: + """ + @return: The length of the varchar type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the varchar type. + """ + return f"varchar({self._length})" + + def __eq__(self, other): + """ + Compares two VarCharType objects for equality. + + @param other: The other VarCharType object to compare with. + @return: True if both VarCharType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.VarCharType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the VarCharType based on its length. + """ + return hash(self._length) + + """ The fixed char type in Gravitino. """ + class FixedCharType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the FixedCharType with the given length. + + @param length: The length of the fixed char type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the fixed char type. + @return: A FixedCharType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the fixed char type. + """ + return Name.FIXEDCHAR + + def length(self) -> int: + """ + @return: The length of the fixed char type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the fixed char type. + """ + return f"char({self._length})" + + def __eq__(self, other): + """ + Compares two FixedCharType objects for equality. + + @param other: The other FixedCharType object to compare with. + @return: True if both FixedCharType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.FixedCharType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the FixedCharType based on its length. + """ + return hash(self._length) + + """ The binary type in Gravitino. """ + class BinaryType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of BinaryType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the binary type. + """ + return Name.BINARY + + def simpleString(self) -> str: + """ + @return: A readable string representation of the binary type. + """ + return "binary" + + """ The struct type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" + class StructType(ComplexType): + def __init__(self, fields): + """ + Initializes the StructType with the given fields. + + @param fields: The fields of the struct type. + """ + if not fields or len(fields) == 0: + raise ValueError("fields cannot be null or empty") + self._fields = fields + + @classmethod + def of(cls, *fields): + """ + @param fields: The fields of the struct type. + @return: A StructType instance with the given fields. + """ + return cls(fields) + + def fields(self): + """ + @return: The fields of the struct type. + """ + return self._fields + + def name(self) -> Name: + """ + @return: The name of the struct type. + """ + return Name.STRUCT + + def simpleString(self) -> str: + """ + @return: A readable string representation of the struct type. + """ + return f"struct<{', '.join(field.simpleString() for field in self._fields)}>" + + def __eq__(self, other): + """ + Compares two StructType objects for equality. + + @param other: The other StructType object to compare with. + @return: True if both StructType objects have the same fields, False otherwise. + """ + if not isinstance(other, Types.StructType): + return False + return self._fields == other._fields + + def __hash__(self): + """ + @return: A hash code for the StructType based on its fields. + """ + return hash(tuple(self._fields)) + + """ A field of a struct type. """ + class Field: + def __init__(self, name, type, nullable, comment=None): + """ + Initializes the Field with the given name, type, nullable flag, and comment. + + @param name: The name of the field. + @param type: The type of the field. + @param nullable: Whether the field is nullable. + @param comment: The comment of the field (optional). + """ + if name is None: + raise ValueError("name cannot be null") + if type is None: + raise ValueError("type cannot be null") + self._name = name + self._type = type + self._nullable = nullable + self._comment = comment + + @classmethod + def notNullField(cls, name, type, comment=None): + """ + @param name: The name of the field. + @param type: The type of the field. + @param comment: The comment of the field. + @return: A NOT NULL Field instance with the given name, type, and comment. + """ + return cls(name, type, False, comment) + + @classmethod + def nullableField(cls, name, type, comment=None): + """ + @param name: The name of the field. + @param type: The type of the field. + @param comment: The comment of the field. + @return: A nullable Field instance with the given name, type, and comment. + """ + return cls(name, type, True, comment) + + def name(self): + """ + @return: The name of the field. + """ + return self._name + + def type(self): + """ + @return: The type of the field. + """ + return self._type + + def nullable(self): + """ + @return: Whether the field is nullable. + """ + return self._nullable + + def comment(self): + """ + @return: The comment of the field, or None if not set. + """ + return self._comment + + def __eq__(self, other): + """ + Compares two Field objects for equality. + + @param other: The other Field object to compare with. + @return: True if both Field objects have the same attributes, False otherwise. + """ + if not isinstance(other, Types.StructType.Field): + return False + return (self._name == other._name and self._type == other._type and + self._nullable == other._nullable and self._comment == other._comment) + + def __hash__(self): + """ + @return: A hash code for the Field based on its attributes. + """ + return hash((self._name, self._type, self._nullable)) + + def simpleString(self) -> str: + """ + @return: The simple string representation of the field. + """ + nullable_str = "NULL" if self._nullable else "NOT NULL" + comment_str = f" COMMENT '{self._comment}'" if self._comment else "" + return f"{self._name}: {self._type.simpleString()} {nullable_str}{comment_str}" + + """ A list type. Note, this type is not supported in the current version of Gravitino. """ + class ListType(ComplexType): + def __init__(self, elementType: Type, elementNullable: bool): + """ + Create a new ListType with the given element type and the type is nullable. + + @param elementType: The element type of the list. + @param elementNullable: Whether the element of the list is nullable. + """ + if elementType is None: + raise ValueError("elementType cannot be null") + self._elementType = elementType + self._elementNullable = elementNullable + + @classmethod + def nullable(cls, elementType: Type): + """ + Create a new ListType with the given element type and the type is nullable. + + @param elementType: The element type of the list. + @return: A new ListType instance. + """ + return cls.of(elementType, True) + + @classmethod + def notNull(cls, elementType: Type): + """ + Create a new ListType with the given element type. + + @param elementType: The element type of the list. + @return: A new ListType instance. + """ + return cls.of(elementType, False) + + @classmethod + def of(cls, elementType: Type, elementNullable: bool): + """ + Create a new ListType with the given element type and whether the element is nullable. + + @param elementType: The element type of the list. + @param elementNullable: Whether the element of the list is nullable. + @return: A new ListType instance. + """ + return cls(elementType, elementNullable) + + def elementType(self) -> Type: + """ + @return: The element type of the list. + """ + return self._elementType + + def elementNullable(self) -> bool: + """ + @return: Whether the element of the list is nullable. + """ + return self._elementNullable + + def name(self) -> Name: + """ + @return: The name of the list type. + """ + return Name.LIST + + def simpleString(self) -> str: + """ + @return: A readable string representation of the list type. + """ + return f"list<{self._elementType.simpleString()}>" if self._elementNullable else f"list<{self._elementType.simpleString()}, NOT NULL>" + + def __eq__(self, other): + """ + Compares two ListType objects for equality. + + @param other: The other ListType object to compare with. + @return: True if both ListType objects have the same element type and nullability, False otherwise. + """ + if not isinstance(other, Types.ListType): + return False + return self._elementNullable == other._elementNullable and self._elementType == other._elementType + + def __hash__(self): + """ + @return: A hash code for the ListType based on its element type and nullability. + """ + return hash((self._elementType, self._elementNullable)) + + """ The map type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class MapType(ComplexType): + def __init__(self, keyType: Type, valueType: Type, valueNullable: bool): + """ + Create a new MapType with the given key type, value type and the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @param valueNullable: Whether the value of the map is nullable. + """ + self._keyType = keyType + self._valueType = valueType + self._valueNullable = valueNullable + + @classmethod + def valueNullable(cls, keyType: Type, valueType: Type): + """ + Create a new MapType with the given key type, value type, and the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @return: A new MapType instance. + """ + return cls.of(keyType, valueType, True) + + @classmethod + def valueNotNull(cls, keyType: Type, valueType: Type): + """ + Create a new MapType with the given key type, value type, and the value is not nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @return: A new MapType instance. + """ + return cls.of(keyType, valueType, False) + + @classmethod + def of(cls, keyType: Type, valueType: Type, valueNullable: bool): + """ + Create a new MapType with the given key type, value type, and whether the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @param valueNullable: Whether the value of the map is nullable. + @return: A new MapType instance. + """ + return cls(keyType, valueType, valueNullable) + + def keyType(self) -> Type: + """ + @return: The key type of the map. + """ + return self._keyType + + def valueType(self) -> Type: + """ + @return: The value type of the map. + """ + return self._valueType + + def valueNullable(self) -> bool: + """ + @return: Whether the value of the map is nullable. + """ + return self._valueNullable + + def name(self) -> Name: + """ + @return: The name of the map type. + """ + return Name.MAP + + def simpleString(self) -> str: + """ + @return: A readable string representation of the map type. + """ + return f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" + + def __eq__(self, other): + """ + Compares two MapType objects for equality. + + @param other: The other MapType object to compare with. + @return: True if both MapType objects have the same key type, value type, and nullability, False otherwise. + """ + if not isinstance(other, Types.MapType): + return False + return self._valueNullable == other._valueNullable and self._keyType == other._keyType and self._valueType == other._valueType + + def __hash__(self): + """ + @return: A hash code for the MapType based on its key type, value type, and nullability. + """ + return hash((self._keyType, self._valueType, self._valueNullable)) + + """ The union type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class UnionType(ComplexType): + def __init__(self, types: list): + """ + Create a new UnionType with the given types. + + @param types: The types of the union. + """ + self._types = types + + @classmethod + def of(cls, *types: Type): + """ + Create a new UnionType with the given types. + + @param types: The types of the union. + @return: A new UnionType instance. + """ + return cls(types) + + def types(self) -> list: + """ + @return: The types of the union. + """ + return self._types + + def name(self) -> Name: + """ + @return: The name of the union type. + """ + return Name.UNION + + def simpleString(self) -> str: + """ + @return: A readable string representation of the union type. + """ + return f"union<{', '.join(t.simpleString() for t in self._types)}>" + + def __eq__(self, other): + """ + Compares two UnionType objects for equality. + + @param other: The other UnionType object to compare with. + @return: True if both UnionType objects have the same types, False otherwise. + """ + if not isinstance(other, Types.UnionType): + return False + return self._types == other._types + + def __hash__(self): + """ + @return: A hash code for the UnionType based on its types. + """ + return hash(tuple(self._types)) + + """ Represents a type that is not parsed yet. The parsed type is represented by other types of Types. """ + class UnparsedType(Type): + def __init__(self, unparsedType: str): + """ + Initializes an UnparsedType instance. + + @param unparsedType: The unparsed type as a string. + """ + self._unparsedType = unparsedType + + @classmethod + def of(cls, unparsedType: str): + """ + Creates a new UnparsedType with the given unparsed type. + + @param unparsedType: The unparsed type. + @return: A new UnparsedType instance. + """ + return cls(unparsedType) + + def unparsedType(self) -> str: + """ + @return: The unparsed type as a string. + """ + return self._unparsedType + + def name(self) -> Name: + """ + @return: The name of the unparsed type. + """ + return Name.UNPARSED + + def simpleString(self) -> str: + """ + @return: A readable string representation of the unparsed type. + """ + return f"unparsed({self._unparsedType})" + + def __eq__(self, other): + """ + Compares two UnparsedType objects for equality. + + @param other: The other UnparsedType object to compare with. + @return: True if both UnparsedType objects have the same unparsed type string, False otherwise. + """ + if not isinstance(other, Types.UnparsedType): + return False + return self._unparsedType == other._unparsedType + + def __hash__(self): + """ + @return: A hash code for the UnparsedType based on its unparsed type string. + """ + return hash(self._unparsedType) + + def __str__(self): + """ + @return: The unparsed type string representation. + """ + return self._unparsedType + + """ Represents a type that is defined in an external catalog. """ + class ExternalType(Type): + def __init__(self, catalogString: str): + """ + Initializes an ExternalType instance. + + @param catalogString: The string representation of this type in the catalog. + """ + self._catalogString = catalogString + + @classmethod + def of(cls, catalogString: str): + """ + Creates a new ExternalType with the given catalog string. + + @param catalogString: The string representation of this type in the catalog. + @return: A new ExternalType instance. + """ + return cls(catalogString) + + def catalogString(self) -> str: + """ + @return: The string representation of this type in external catalog. + """ + return self._catalogString + + def name(self) -> Name: + """ + @return: The name of the external type. + """ + return Name.EXTERNAL + + def simpleString(self) -> str: + """ + @return: A readable string representation of the external type. + """ + return f"external({self._catalogString})" + + def __eq__(self, other): + """ + Compares two ExternalType objects for equality. + + @param other: The other ExternalType object to compare with. + @return: True if both ExternalType objects have the same catalog string, False otherwise. + """ + if not isinstance(other, Types.ExternalType): + return False + return self._catalogString == other._catalogString + + def __hash__(self): + """ + @return: A hash code for the ExternalType based on its catalog string. + """ + return hash(self._catalogString) + + def __str__(self): + """ + @return: The string representation of the external type. + """ + return self.simpleString() + + @staticmethod + def allowAutoIncrement(dataType: Type) -> bool: + """ + Checks if the given data type is allowed to be an auto-increment column. + + @param dataType: The data type to check. + @return: True if the given data type is allowed to be an auto-increment column, False otherwise. + """ + return isinstance(dataType, (Types.IntegerType, Types.LongType)) + From 2e5c47b6d798a3d0602b7029d600afa73689b83d Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Tue, 29 Oct 2024 23:31:25 -0500 Subject: [PATCH 03/15] Separate to type.py and types.py --- clients/client-python/gravitino/api/type.py | 1258 +----------------- clients/client-python/gravitino/api/types.py | 1254 +++++++++++++++++ 2 files changed, 1276 insertions(+), 1236 deletions(-) create mode 100644 clients/client-python/gravitino/api/types.py diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index 760692304b8..c32b6215bdc 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -1,7 +1,27 @@ +""" +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +""" + from abc import ABC, abstractmethod from enum import Enum from typing import Optional + class Name(Enum): """ The root type name of this type, representing all data types supported. @@ -144,1239 +164,5 @@ def signed(self) -> bool: # Define FractionType class class FractionType(NumericType, ABC): """ Base class for all fractional types. """ - pass - -""" The helper class for Type. """ -class Types: - - """ The data type representing `NULL` values. """ - class NullType(Type): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of NullType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Null type. - """ - return Name.NULL - - def simpleString(self) -> str: - """ - @return A readable string representation of the Null type. - """ - return "null" - - """ The boolean type in Gravitino. """ - class BooleanType(PrimitiveType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of BooleanType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Boolean type. - """ - return Name.BOOLEAN - - def simpleString(self) -> str: - """ - @return A readable string representation of the Boolean type. - """ - return "boolean" - - """ The byte type in Gravitino. """ - class ByteType(IntegralType): - _instance = None - _unsigned_instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of ByteType. - """ - if cls._instance is None: - cls._instance = cls(True) - return cls._instance - - @classmethod - def unsigned(cls): - """ - @return The singleton instance of unsigned ByteType. - """ - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance - - def __init__(self, signed: bool): - """ - @param signed: True if the byte type is signed, False otherwise. - """ - super().__init__(signed) - - def name(self) -> Name: - """ - @return The name of the Byte type. - """ - return Name.BYTE - - def simpleString(self) -> str: - """ - @return A readable string representa - """ - return "byte" if self.signed() else "byte unsigned" - - """ The short type in Gravitino. """ - class ShortType(IntegralType): - _instance = None - _unsigned_instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of ShortType. - """ - if cls._instance is None: - cls._instance = cls(True) - return cls._instance - - @classmethod - def unsigned(cls): - """ - @return The singleton instance of unsigned ShortType. - """ - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance - - def __init__(self, signed: bool): - """ - @param signed: True if the short type is signed, False otherwise. - """ - super().__init__(signed) - - def name(self) -> Name: - """ - @return The name of the Short type. - """ - return Name.SHORT - - def simpleString(self) -> str: - """ - @return A readable string representation of the Short type. - """ - return "short" if self.signed() else "short unsigned" - - """ The integer type in Gravitino. """ - class IntegerType(IntegralType): - _instance = None - _unsigned_instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of IntegerType. - """ - if cls._instance is None: - cls._instance = cls(True) - return cls._instance - - @classmethod - def unsigned(cls): - """ - @return The singleton instance of unsigned IntegerType. - """ - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance - - def __init__(self, signed: bool): - """ - @param signed: True if the integer type is signed, False otherwise. - """ - super().__init__(signed) - - def name(self) -> Name: - """ - @return The name of the Integer type. - """ - return Name.INTEGER - - def simpleString(self) -> str: - """ - @return A readable string representation of the Integer type. - """ - return "integer" if self.signed() else "integer unsigned" - - """ The long type in Gravitino. """ - class LongType(IntegralType): - _instance = None - _unsigned_instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of LongType. - """ - if cls._instance is None: - cls._instance = cls(True) - return cls._instance - - @classmethod - def unsigned(cls): - """ - @return The singleton instance of unsigned LongType. - """ - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance - - def __init__(self, signed: bool): - """ - @param signed: True if the long type is signed, False otherwise. - """ - super().__init__(signed) - - def name(self) -> Name: - """ - @return The name of the Long type. - """ - return Name.LONG - - def simpleString(self) -> str: - """ - @return A readable string representation of the Long type. - """ - return "long" if self.signed() else "long unsigned" - - """ The float type in Gravitino. """ - class FloatType(FractionType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of FloatType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Float type. - """ - return Name.FLOAT - - def simpleString(self) -> str: - """ - @return A readable string representation of the Float type. - """ - return "float" - - """ The double type in Gravitino. """ - class DoubleType(FractionType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of DoubleType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Double type. - """ - return Name.DOUBLE - - def simpleString(self) -> str: - """ - @return A readable string representation of the Double type. - """ - return "double" - - """ The decimal type in Gravitino. """ - class DecimalType(FractionType): - @classmethod - def of(cls, precision: int, scale: int): - """ - @param precision: The precision of the decimal type. - @param scale: The scale of the decimal type. - @return A DecimalType with the given precision and scale. - """ - return cls(precision, scale) - - def __init__(self, precision: int, scale: int): - """ - @param precision: The precision of the decimal type. - @param scale: The scale of the decimal type. - """ - self.check_precision_scale(precision, scale) - self._precision = precision - self._scale = scale - - @staticmethod - def check_precision_scale(precision: int, scale: int): - """ - Ensures the precision and scale values are within valid range. - @param precision: The precision of the decimal. - @param scale: The scale of the decimal. - """ - if not (1 <= precision <= 38): - raise ValueError(f"Decimal precision must be in range [1, 38]: {precision}") - if not (0 <= scale <= precision): - raise ValueError(f"Decimal scale must be in range [0, precision ({precision})]: {scale}") - - def name(self) -> Name: - """ - @return The name of the Decimal type. - """ - return Name.DECIMAL - - def precision(self) -> int: - """ - @return The precision of the decimal type. - """ - return self._precision - - def scale(self) -> int: - """ - @return The scale of the decimal type. - """ - return self._scale - - def simpleString(self) -> str: - """ - @return A readable string representation of the Decimal type. - """ - return f"decimal({self._precision},{self._scale})" - - def __eq__(self, other): - """ - Compares two DecimalType objects for equality. - - @param other: The other DecimalType to compare with. - @return: True if both objects have the same precision and scale, False otherwise. - """ - if not isinstance(other, Types.DecimalType): - return False - return self._precision == other._precision and self._scale == other._scale - - def __hash__(self): - """ - @return: A hash code for the DecimalType based on its precision and scale. - """ - return hash((self._precision, self._scale)) - - """ The date time type in Gravitino. """ - class DateType(DateTimeType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of DateType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Date type. - """ - return Name.DATE - - def simpleString(self) -> str: - """ - @return A readable string representation of the Date type. - """ - return "date" - - """ The time type in Gravitino. """ - class TimeType(DateTimeType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of TimeType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the Time type. - """ - return Name.TIME - - def simpleString(self) -> str: - """ - @return A readable string representation of the Time type. - """ - return "time" - - """ The timestamp type in Gravitino. """ - class TimestampType(DateTimeType): - _instance_with_tz = None - _instance_without_tz = None - - @classmethod - def withTimeZone(cls): - """ - @return A TimestampType with time zone. - """ - if cls._instance_with_tz is None: - cls._instance_with_tz = cls(True) - return cls._instance_with_tz - - @classmethod - def withoutTimeZone(cls): - """ - @return A TimestampType without time zone. - """ - if cls._instance_without_tz is None: - cls._instance_without_tz = cls(False) - return cls._instance_without_tz - - def __init__(self, with_time_zone: bool): - """ - @param with_time_zone: True if the timestamp type has a time zone, False otherwise. - """ - self._with_time_zone = with_time_zone - - def hasTimeZone(self) -> bool: - """ - @return True if the timestamp type has a time zone, False otherwise. - """ - return self._with_time_zone - - def name(self) -> Name: - """ - @return The name of the Timestamp type. - """ - return Name.TIMESTAMP - - def simpleString(self) -> str: - """ - @return A readable string representation of the Timestamp type. - """ - return "timestamp_tz" if self._with_time_zone else "timestamp" - - """ The interval year type in Gravitino. """ - class IntervalYearType(IntervalType): - _instance = None - - @classmethod - def get(cls): - """ - @return The singleton instance of IntervalYearType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return The name of the IntervalYear type. - """ - return Name.INTERVAL_YEAR - - def simpleString(self) -> str: - """ - @return A readable string representation of the IntervalYear type. - """ - return "interval_year" - - """ The interval day type in Gravitino. """ - class IntervalDayType(IntervalType): - _instance = None - - @classmethod - def get(cls): - """ - @return: The singleton instance of IntervalDayType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return: The name of the interval day type. - """ - return Name.INTERVAL_DAY - - def simpleString(self) -> str: - """ - @return: A readable string representation of the interval day type. - """ - return "interval_day" - - """ The string type in Gravitino, equivalent to varchar(MAX), where the MAX is determined by the underlying catalog. """ - class StringType(PrimitiveType): - _instance = None - - @classmethod - def get(cls): - """ - @return: The singleton instance of StringType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return: The name of the string type. - """ - return Name.STRING - - def simpleString(self) -> str: - """ - @return: A readable string representation of the string type. - """ - return "string" - - """ The UUID type in Gravitino. """ - class UUIDType(PrimitiveType): - _instance = None - - @classmethod - def get(cls): - """ - @return: The singleton instance of UUIDType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return: The name of the UUID type. - """ - return Name.UUID - - def simpleString(self) -> str: - """ - @return: A readable string representation of the UUID type. - """ - return "uuid" - - """ Fixed-length byte array type. Use BinaryType for variable-length byte arrays. """ - class FixedType(PrimitiveType): - def __init__(self, length: int): - """ - Initializes the FixedType with the given length. - - @param length: The length of the fixed type. - """ - self._length = length - - @classmethod - def of(cls, length: int): - """ - @param length: The length of the fixed type. - @return: A FixedType instance with the given length. - """ - return cls(length) - - def name(self) -> Name: - """ - @return: The name of the fixed type. - """ - return Name.FIXED - - def length(self) -> int: - """ - @return: The length of the fixed type. - """ - return self._length - - def simpleString(self) -> str: - """ - @return: A readable string representation of the fixed type. - """ - return f"fixed({self._length})" - - def __eq__(self, other): - """ - Compares two FixedType objects for equality. - - @param other: The other FixedType object to compare with. - @return: True if both FixedType objects have the same length, False otherwise. - """ - if not isinstance(other, Types.FixedType): - return False - return self._length == other._length - - def __hash__(self): - """ - @return: A hash code for the FixedType based on its length. - """ - return hash(self._length) - - """ The varchar type in Gravitino. """ - class VarCharType(PrimitiveType): - def __init__(self, length: int): - """ - Initializes the VarCharType with the given length. - - @param length: The length of the varchar type. - """ - self._length = length - - @classmethod - def of(cls, length: int): - """ - @param length: The length of the varchar type. - @return: A VarCharType instance with the given length. - """ - return cls(length) - - def name(self) -> Name: - """ - @return: The name of the varchar type. - """ - return Name.VARCHAR - - def length(self) -> int: - """ - @return: The length of the varchar type. - """ - return self._length - - def simpleString(self) -> str: - """ - @return: A readable string representation of the varchar type. - """ - return f"varchar({self._length})" - - def __eq__(self, other): - """ - Compares two VarCharType objects for equality. - - @param other: The other VarCharType object to compare with. - @return: True if both VarCharType objects have the same length, False otherwise. - """ - if not isinstance(other, Types.VarCharType): - return False - return self._length == other._length - - def __hash__(self): - """ - @return: A hash code for the VarCharType based on its length. - """ - return hash(self._length) - - """ The fixed char type in Gravitino. """ - class FixedCharType(PrimitiveType): - def __init__(self, length: int): - """ - Initializes the FixedCharType with the given length. - - @param length: The length of the fixed char type. - """ - self._length = length - - @classmethod - def of(cls, length: int): - """ - @param length: The length of the fixed char type. - @return: A FixedCharType instance with the given length. - """ - return cls(length) - - def name(self) -> Name: - """ - @return: The name of the fixed char type. - """ - return Name.FIXEDCHAR - - def length(self) -> int: - """ - @return: The length of the fixed char type. - """ - return self._length - - def simpleString(self) -> str: - """ - @return: A readable string representation of the fixed char type. - """ - return f"char({self._length})" - - def __eq__(self, other): - """ - Compares two FixedCharType objects for equality. - - @param other: The other FixedCharType object to compare with. - @return: True if both FixedCharType objects have the same length, False otherwise. - """ - if not isinstance(other, Types.FixedCharType): - return False - return self._length == other._length - - def __hash__(self): - """ - @return: A hash code for the FixedCharType based on its length. - """ - return hash(self._length) - - """ The binary type in Gravitino. """ - class BinaryType(PrimitiveType): - _instance = None - - @classmethod - def get(cls): - """ - @return: The singleton instance of BinaryType. - """ - if cls._instance is None: - cls._instance = cls() - return cls._instance - - def name(self) -> Name: - """ - @return: The name of the binary type. - """ - return Name.BINARY - - def simpleString(self) -> str: - """ - @return: A readable string representation of the binary type. - """ - return "binary" - - """ The struct type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" - class StructType(ComplexType): - def __init__(self, fields): - """ - Initializes the StructType with the given fields. - - @param fields: The fields of the struct type. - """ - if not fields or len(fields) == 0: - raise ValueError("fields cannot be null or empty") - self._fields = fields - - @classmethod - def of(cls, *fields): - """ - @param fields: The fields of the struct type. - @return: A StructType instance with the given fields. - """ - return cls(fields) - - def fields(self): - """ - @return: The fields of the struct type. - """ - return self._fields - - def name(self) -> Name: - """ - @return: The name of the struct type. - """ - return Name.STRUCT - - def simpleString(self) -> str: - """ - @return: A readable string representation of the struct type. - """ - return f"struct<{', '.join(field.simpleString() for field in self._fields)}>" - - def __eq__(self, other): - """ - Compares two StructType objects for equality. - - @param other: The other StructType object to compare with. - @return: True if both StructType objects have the same fields, False otherwise. - """ - if not isinstance(other, Types.StructType): - return False - return self._fields == other._fields - - def __hash__(self): - """ - @return: A hash code for the StructType based on its fields. - """ - return hash(tuple(self._fields)) - - """ A field of a struct type. """ - class Field: - def __init__(self, name, type, nullable, comment=None): - """ - Initializes the Field with the given name, type, nullable flag, and comment. - - @param name: The name of the field. - @param type: The type of the field. - @param nullable: Whether the field is nullable. - @param comment: The comment of the field (optional). - """ - if name is None: - raise ValueError("name cannot be null") - if type is None: - raise ValueError("type cannot be null") - self._name = name - self._type = type - self._nullable = nullable - self._comment = comment - - @classmethod - def notNullField(cls, name, type, comment=None): - """ - @param name: The name of the field. - @param type: The type of the field. - @param comment: The comment of the field. - @return: A NOT NULL Field instance with the given name, type, and comment. - """ - return cls(name, type, False, comment) - - @classmethod - def nullableField(cls, name, type, comment=None): - """ - @param name: The name of the field. - @param type: The type of the field. - @param comment: The comment of the field. - @return: A nullable Field instance with the given name, type, and comment. - """ - return cls(name, type, True, comment) - - def name(self): - """ - @return: The name of the field. - """ - return self._name - - def type(self): - """ - @return: The type of the field. - """ - return self._type - - def nullable(self): - """ - @return: Whether the field is nullable. - """ - return self._nullable - - def comment(self): - """ - @return: The comment of the field, or None if not set. - """ - return self._comment - - def __eq__(self, other): - """ - Compares two Field objects for equality. - - @param other: The other Field object to compare with. - @return: True if both Field objects have the same attributes, False otherwise. - """ - if not isinstance(other, Types.StructType.Field): - return False - return (self._name == other._name and self._type == other._type and - self._nullable == other._nullable and self._comment == other._comment) - - def __hash__(self): - """ - @return: A hash code for the Field based on its attributes. - """ - return hash((self._name, self._type, self._nullable)) - - def simpleString(self) -> str: - """ - @return: The simple string representation of the field. - """ - nullable_str = "NULL" if self._nullable else "NOT NULL" - comment_str = f" COMMENT '{self._comment}'" if self._comment else "" - return f"{self._name}: {self._type.simpleString()} {nullable_str}{comment_str}" - - """ A list type. Note, this type is not supported in the current version of Gravitino. """ - class ListType(ComplexType): - def __init__(self, elementType: Type, elementNullable: bool): - """ - Create a new ListType with the given element type and the type is nullable. - - @param elementType: The element type of the list. - @param elementNullable: Whether the element of the list is nullable. - """ - if elementType is None: - raise ValueError("elementType cannot be null") - self._elementType = elementType - self._elementNullable = elementNullable - - @classmethod - def nullable(cls, elementType: Type): - """ - Create a new ListType with the given element type and the type is nullable. - - @param elementType: The element type of the list. - @return: A new ListType instance. - """ - return cls.of(elementType, True) - - @classmethod - def notNull(cls, elementType: Type): - """ - Create a new ListType with the given element type. - - @param elementType: The element type of the list. - @return: A new ListType instance. - """ - return cls.of(elementType, False) - - @classmethod - def of(cls, elementType: Type, elementNullable: bool): - """ - Create a new ListType with the given element type and whether the element is nullable. - - @param elementType: The element type of the list. - @param elementNullable: Whether the element of the list is nullable. - @return: A new ListType instance. - """ - return cls(elementType, elementNullable) - - def elementType(self) -> Type: - """ - @return: The element type of the list. - """ - return self._elementType - - def elementNullable(self) -> bool: - """ - @return: Whether the element of the list is nullable. - """ - return self._elementNullable - - def name(self) -> Name: - """ - @return: The name of the list type. - """ - return Name.LIST - - def simpleString(self) -> str: - """ - @return: A readable string representation of the list type. - """ - return f"list<{self._elementType.simpleString()}>" if self._elementNullable else f"list<{self._elementType.simpleString()}, NOT NULL>" - - def __eq__(self, other): - """ - Compares two ListType objects for equality. - - @param other: The other ListType object to compare with. - @return: True if both ListType objects have the same element type and nullability, False otherwise. - """ - if not isinstance(other, Types.ListType): - return False - return self._elementNullable == other._elementNullable and self._elementType == other._elementType - - def __hash__(self): - """ - @return: A hash code for the ListType based on its element type and nullability. - """ - return hash((self._elementType, self._elementNullable)) - - """ The map type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ - class MapType(ComplexType): - def __init__(self, keyType: Type, valueType: Type, valueNullable: bool): - """ - Create a new MapType with the given key type, value type and the value is nullable. - - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @param valueNullable: Whether the value of the map is nullable. - """ - self._keyType = keyType - self._valueType = valueType - self._valueNullable = valueNullable - - @classmethod - def valueNullable(cls, keyType: Type, valueType: Type): - """ - Create a new MapType with the given key type, value type, and the value is nullable. - - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @return: A new MapType instance. - """ - return cls.of(keyType, valueType, True) - - @classmethod - def valueNotNull(cls, keyType: Type, valueType: Type): - """ - Create a new MapType with the given key type, value type, and the value is not nullable. - - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @return: A new MapType instance. - """ - return cls.of(keyType, valueType, False) - - @classmethod - def of(cls, keyType: Type, valueType: Type, valueNullable: bool): - """ - Create a new MapType with the given key type, value type, and whether the value is nullable. - - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @param valueNullable: Whether the value of the map is nullable. - @return: A new MapType instance. - """ - return cls(keyType, valueType, valueNullable) - - def keyType(self) -> Type: - """ - @return: The key type of the map. - """ - return self._keyType - - def valueType(self) -> Type: - """ - @return: The value type of the map. - """ - return self._valueType - - def valueNullable(self) -> bool: - """ - @return: Whether the value of the map is nullable. - """ - return self._valueNullable - - def name(self) -> Name: - """ - @return: The name of the map type. - """ - return Name.MAP - - def simpleString(self) -> str: - """ - @return: A readable string representation of the map type. - """ - return f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" - - def __eq__(self, other): - """ - Compares two MapType objects for equality. - - @param other: The other MapType object to compare with. - @return: True if both MapType objects have the same key type, value type, and nullability, False otherwise. - """ - if not isinstance(other, Types.MapType): - return False - return self._valueNullable == other._valueNullable and self._keyType == other._keyType and self._valueType == other._valueType - - def __hash__(self): - """ - @return: A hash code for the MapType based on its key type, value type, and nullability. - """ - return hash((self._keyType, self._valueType, self._valueNullable)) - - """ The union type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ - class UnionType(ComplexType): - def __init__(self, types: list): - """ - Create a new UnionType with the given types. - - @param types: The types of the union. - """ - self._types = types - - @classmethod - def of(cls, *types: Type): - """ - Create a new UnionType with the given types. - - @param types: The types of the union. - @return: A new UnionType instance. - """ - return cls(types) - - def types(self) -> list: - """ - @return: The types of the union. - """ - return self._types - - def name(self) -> Name: - """ - @return: The name of the union type. - """ - return Name.UNION - - def simpleString(self) -> str: - """ - @return: A readable string representation of the union type. - """ - return f"union<{', '.join(t.simpleString() for t in self._types)}>" - - def __eq__(self, other): - """ - Compares two UnionType objects for equality. - - @param other: The other UnionType object to compare with. - @return: True if both UnionType objects have the same types, False otherwise. - """ - if not isinstance(other, Types.UnionType): - return False - return self._types == other._types - - def __hash__(self): - """ - @return: A hash code for the UnionType based on its types. - """ - return hash(tuple(self._types)) - - """ Represents a type that is not parsed yet. The parsed type is represented by other types of Types. """ - class UnparsedType(Type): - def __init__(self, unparsedType: str): - """ - Initializes an UnparsedType instance. - - @param unparsedType: The unparsed type as a string. - """ - self._unparsedType = unparsedType - - @classmethod - def of(cls, unparsedType: str): - """ - Creates a new UnparsedType with the given unparsed type. - - @param unparsedType: The unparsed type. - @return: A new UnparsedType instance. - """ - return cls(unparsedType) - - def unparsedType(self) -> str: - """ - @return: The unparsed type as a string. - """ - return self._unparsedType - - def name(self) -> Name: - """ - @return: The name of the unparsed type. - """ - return Name.UNPARSED - - def simpleString(self) -> str: - """ - @return: A readable string representation of the unparsed type. - """ - return f"unparsed({self._unparsedType})" - - def __eq__(self, other): - """ - Compares two UnparsedType objects for equality. - - @param other: The other UnparsedType object to compare with. - @return: True if both UnparsedType objects have the same unparsed type string, False otherwise. - """ - if not isinstance(other, Types.UnparsedType): - return False - return self._unparsedType == other._unparsedType - - def __hash__(self): - """ - @return: A hash code for the UnparsedType based on its unparsed type string. - """ - return hash(self._unparsedType) - - def __str__(self): - """ - @return: The unparsed type string representation. - """ - return self._unparsedType - - """ Represents a type that is defined in an external catalog. """ - class ExternalType(Type): - def __init__(self, catalogString: str): - """ - Initializes an ExternalType instance. - - @param catalogString: The string representation of this type in the catalog. - """ - self._catalogString = catalogString - - @classmethod - def of(cls, catalogString: str): - """ - Creates a new ExternalType with the given catalog string. - - @param catalogString: The string representation of this type in the catalog. - @return: A new ExternalType instance. - """ - return cls(catalogString) - - def catalogString(self) -> str: - """ - @return: The string representation of this type in external catalog. - """ - return self._catalogString - - def name(self) -> Name: - """ - @return: The name of the external type. - """ - return Name.EXTERNAL - - def simpleString(self) -> str: - """ - @return: A readable string representation of the external type. - """ - return f"external({self._catalogString})" - - def __eq__(self, other): - """ - Compares two ExternalType objects for equality. - - @param other: The other ExternalType object to compare with. - @return: True if both ExternalType objects have the same catalog string, False otherwise. - """ - if not isinstance(other, Types.ExternalType): - return False - return self._catalogString == other._catalogString - - def __hash__(self): - """ - @return: A hash code for the ExternalType based on its catalog string. - """ - return hash(self._catalogString) - - def __str__(self): - """ - @return: The string representation of the external type. - """ - return self.simpleString() - - @staticmethod - def allowAutoIncrement(dataType: Type) -> bool: - """ - Checks if the given data type is allowed to be an auto-increment column. - - @param dataType: The data type to check. - @return: True if the given data type is allowed to be an auto-increment column, False otherwise. - """ - return isinstance(dataType, (Types.IntegerType, Types.LongType)) - + def __init__(self): + super().__init__() diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py new file mode 100644 index 00000000000..60650c53e53 --- /dev/null +++ b/clients/client-python/gravitino/api/types.py @@ -0,0 +1,1254 @@ +""" +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +""" +from .type import Type, Name, PrimitiveType, IntegralType, FractionType, DateTimeType, IntervalType, ComplexType + +""" The helper class for Type. """ +class Types: + + """ The data type representing `NULL` values. """ + class NullType(Type): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of NullType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Null type. + """ + return Name.NULL + + def simpleString(self) -> str: + """ + @return A readable string representation of the Null type. + """ + return "null" + + """ The boolean type in Gravitino. """ + class BooleanType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of BooleanType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Boolean type. + """ + return Name.BOOLEAN + + def simpleString(self) -> str: + """ + @return A readable string representation of the Boolean type. + """ + return "boolean" + + """ The byte type in Gravitino. """ + class ByteType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of ByteType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned ByteType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the byte type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Byte type. + """ + return Name.BYTE + + def simpleString(self) -> str: + """ + @return A readable string representa + """ + return "byte" if self.signed() else "byte unsigned" + + """ The short type in Gravitino. """ + class ShortType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of ShortType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned ShortType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the short type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Short type. + """ + return Name.SHORT + + def simpleString(self) -> str: + """ + @return A readable string representation of the Short type. + """ + return "short" if self.signed() else "short unsigned" + + """ The integer type in Gravitino. """ + class IntegerType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of IntegerType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned IntegerType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the integer type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Integer type. + """ + return Name.INTEGER + + def simpleString(self) -> str: + """ + @return A readable string representation of the Integer type. + """ + return "integer" if self.signed() else "integer unsigned" + + """ The long type in Gravitino. """ + class LongType(IntegralType): + _instance = None + _unsigned_instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of LongType. + """ + if cls._instance is None: + cls._instance = cls(True) + return cls._instance + + @classmethod + def unsigned(cls): + """ + @return The singleton instance of unsigned LongType. + """ + if cls._unsigned_instance is None: + cls._unsigned_instance = cls(False) + return cls._unsigned_instance + + def __init__(self, signed: bool): + """ + @param signed: True if the long type is signed, False otherwise. + """ + super().__init__(signed) + + def name(self) -> Name: + """ + @return The name of the Long type. + """ + return Name.LONG + + def simpleString(self) -> str: + """ + @return A readable string representation of the Long type. + """ + return "long" if self.signed() else "long unsigned" + + """ The float type in Gravitino. """ + class FloatType(FractionType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of FloatType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Float type. + """ + return Name.FLOAT + + def simpleString(self) -> str: + """ + @return A readable string representation of the Float type. + """ + return "float" + + """ The double type in Gravitino. """ + class DoubleType(FractionType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of DoubleType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Double type. + """ + return Name.DOUBLE + + def simpleString(self) -> str: + """ + @return A readable string representation of the Double type. + """ + return "double" + + """ The decimal type in Gravitino. """ + class DecimalType(FractionType): + @classmethod + def of(cls, precision: int, scale: int): + """ + @param precision: The precision of the decimal type. + @param scale: The scale of the decimal type. + @return A DecimalType with the given precision and scale. + """ + return cls(precision, scale) + + def __init__(self, precision: int, scale: int): + """ + @param precision: The precision of the decimal type. + @param scale: The scale of the decimal type. + """ + self.check_precision_scale(precision, scale) + self._precision = precision + self._scale = scale + + @staticmethod + def check_precision_scale(precision: int, scale: int): + """ + Ensures the precision and scale values are within valid range. + @param precision: The precision of the decimal. + @param scale: The scale of the decimal. + """ + if not (1 <= precision <= 38): + raise ValueError(f"Decimal precision must be in range [1, 38]: {precision}") + if not (0 <= scale <= precision): + raise ValueError(f"Decimal scale must be in range [0, precision ({precision})]: {scale}") + + def name(self) -> Name: + """ + @return The name of the Decimal type. + """ + return Name.DECIMAL + + def precision(self) -> int: + """ + @return The precision of the decimal type. + """ + return self._precision + + def scale(self) -> int: + """ + @return The scale of the decimal type. + """ + return self._scale + + def simpleString(self) -> str: + """ + @return A readable string representation of the Decimal type. + """ + return f"decimal({self._precision},{self._scale})" + + def __eq__(self, other): + """ + Compares two DecimalType objects for equality. + + @param other: The other DecimalType to compare with. + @return: True if both objects have the same precision and scale, False otherwise. + """ + if not isinstance(other, Types.DecimalType): + return False + return self._precision == other._precision and self._scale == other._scale + + def __hash__(self): + """ + @return: A hash code for the DecimalType based on its precision and scale. + """ + return hash((self._precision, self._scale)) + + """ The date time type in Gravitino. """ + class DateType(DateTimeType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of DateType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Date type. + """ + return Name.DATE + + def simpleString(self) -> str: + """ + @return A readable string representation of the Date type. + """ + return "date" + + """ The time type in Gravitino. """ + class TimeType(DateTimeType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of TimeType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the Time type. + """ + return Name.TIME + + def simpleString(self) -> str: + """ + @return A readable string representation of the Time type. + """ + return "time" + + """ The timestamp type in Gravitino. """ + class TimestampType(DateTimeType): + _instance_with_tz = None + _instance_without_tz = None + + @classmethod + def withTimeZone(cls): + """ + @return A TimestampType with time zone. + """ + if cls._instance_with_tz is None: + cls._instance_with_tz = cls(True) + return cls._instance_with_tz + + @classmethod + def withoutTimeZone(cls): + """ + @return A TimestampType without time zone. + """ + if cls._instance_without_tz is None: + cls._instance_without_tz = cls(False) + return cls._instance_without_tz + + def __init__(self, with_time_zone: bool): + """ + @param with_time_zone: True if the timestamp type has a time zone, False otherwise. + """ + self._with_time_zone = with_time_zone + + def hasTimeZone(self) -> bool: + """ + @return True if the timestamp type has a time zone, False otherwise. + """ + return self._with_time_zone + + def name(self) -> Name: + """ + @return The name of the Timestamp type. + """ + return Name.TIMESTAMP + + def simpleString(self) -> str: + """ + @return A readable string representation of the Timestamp type. + """ + return "timestamp_tz" if self._with_time_zone else "timestamp" + + """ The interval year type in Gravitino. """ + class IntervalYearType(IntervalType): + _instance = None + + @classmethod + def get(cls): + """ + @return The singleton instance of IntervalYearType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return The name of the IntervalYear type. + """ + return Name.INTERVAL_YEAR + + def simpleString(self) -> str: + """ + @return A readable string representation of the IntervalYear type. + """ + return "interval_year" + + """ The interval day type in Gravitino. """ + class IntervalDayType(IntervalType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of IntervalDayType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the interval day type. + """ + return Name.INTERVAL_DAY + + def simpleString(self) -> str: + """ + @return: A readable string representation of the interval day type. + """ + return "interval_day" + + """ The string type in Gravitino, equivalent to varchar(MAX), where the MAX is determined by the underlying catalog. """ + class StringType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of StringType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the string type. + """ + return Name.STRING + + def simpleString(self) -> str: + """ + @return: A readable string representation of the string type. + """ + return "string" + + """ The UUID type in Gravitino. """ + class UUIDType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of UUIDType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the UUID type. + """ + return Name.UUID + + def simpleString(self) -> str: + """ + @return: A readable string representation of the UUID type. + """ + return "uuid" + + """ Fixed-length byte array type. Use BinaryType for variable-length byte arrays. """ + class FixedType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the FixedType with the given length. + + @param length: The length of the fixed type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the fixed type. + @return: A FixedType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the fixed type. + """ + return Name.FIXED + + def length(self) -> int: + """ + @return: The length of the fixed type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the fixed type. + """ + return f"fixed({self._length})" + + def __eq__(self, other): + """ + Compares two FixedType objects for equality. + + @param other: The other FixedType object to compare with. + @return: True if both FixedType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.FixedType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the FixedType based on its length. + """ + return hash(self._length) + + """ The varchar type in Gravitino. """ + class VarCharType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the VarCharType with the given length. + + @param length: The length of the varchar type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the varchar type. + @return: A VarCharType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the varchar type. + """ + return Name.VARCHAR + + def length(self) -> int: + """ + @return: The length of the varchar type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the varchar type. + """ + return f"varchar({self._length})" + + def __eq__(self, other): + """ + Compares two VarCharType objects for equality. + + @param other: The other VarCharType object to compare with. + @return: True if both VarCharType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.VarCharType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the VarCharType based on its length. + """ + return hash(self._length) + + """ The fixed char type in Gravitino. """ + class FixedCharType(PrimitiveType): + def __init__(self, length: int): + """ + Initializes the FixedCharType with the given length. + + @param length: The length of the fixed char type. + """ + self._length = length + + @classmethod + def of(cls, length: int): + """ + @param length: The length of the fixed char type. + @return: A FixedCharType instance with the given length. + """ + return cls(length) + + def name(self) -> Name: + """ + @return: The name of the fixed char type. + """ + return Name.FIXEDCHAR + + def length(self) -> int: + """ + @return: The length of the fixed char type. + """ + return self._length + + def simpleString(self) -> str: + """ + @return: A readable string representation of the fixed char type. + """ + return f"char({self._length})" + + def __eq__(self, other): + """ + Compares two FixedCharType objects for equality. + + @param other: The other FixedCharType object to compare with. + @return: True if both FixedCharType objects have the same length, False otherwise. + """ + if not isinstance(other, Types.FixedCharType): + return False + return self._length == other._length + + def __hash__(self): + """ + @return: A hash code for the FixedCharType based on its length. + """ + return hash(self._length) + + """ The binary type in Gravitino. """ + class BinaryType(PrimitiveType): + _instance = None + + @classmethod + def get(cls): + """ + @return: The singleton instance of BinaryType. + """ + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def name(self) -> Name: + """ + @return: The name of the binary type. + """ + return Name.BINARY + + def simpleString(self) -> str: + """ + @return: A readable string representation of the binary type. + """ + return "binary" + + """ The struct type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" + class StructType(ComplexType): + def __init__(self, fields): + """ + Initializes the StructType with the given fields. + + @param fields: The fields of the struct type. + """ + if not fields or len(fields) == 0: + raise ValueError("fields cannot be null or empty") + self._fields = fields + + @classmethod + def of(cls, *fields): + """ + @param fields: The fields of the struct type. + @return: A StructType instance with the given fields. + """ + return cls(fields) + + def fields(self): + """ + @return: The fields of the struct type. + """ + return self._fields + + def name(self) -> Name: + """ + @return: The name of the struct type. + """ + return Name.STRUCT + + def simpleString(self) -> str: + """ + @return: A readable string representation of the struct type. + """ + return f"struct<{', '.join(field.simpleString() for field in self._fields)}>" + + def __eq__(self, other): + """ + Compares two StructType objects for equality. + + @param other: The other StructType object to compare with. + @return: True if both StructType objects have the same fields, False otherwise. + """ + if not isinstance(other, Types.StructType): + return False + return self._fields == other._fields + + def __hash__(self): + """ + @return: A hash code for the StructType based on its fields. + """ + return hash(tuple(self._fields)) + + """ A field of a struct type. """ + class Field: + def __init__(self, name, type, nullable, comment=None): + """ + Initializes the Field with the given name, type, nullable flag, and comment. + + @param name: The name of the field. + @param type: The type of the field. + @param nullable: Whether the field is nullable. + @param comment: The comment of the field (optional). + """ + if name is None: + raise ValueError("name cannot be null") + if type is None: + raise ValueError("type cannot be null") + self._name = name + self._type = type + self._nullable = nullable + self._comment = comment + + @classmethod + def notNullField(cls, name, type, comment=None): + """ + @param name: The name of the field. + @param type: The type of the field. + @param comment: The comment of the field. + @return: A NOT NULL Field instance with the given name, type, and comment. + """ + return cls(name, type, False, comment) + + @classmethod + def nullableField(cls, name, type, comment=None): + """ + @param name: The name of the field. + @param type: The type of the field. + @param comment: The comment of the field. + @return: A nullable Field instance with the given name, type, and comment. + """ + return cls(name, type, True, comment) + + def name(self): + """ + @return: The name of the field. + """ + return self._name + + def type(self): + """ + @return: The type of the field. + """ + return self._type + + def nullable(self): + """ + @return: Whether the field is nullable. + """ + return self._nullable + + def comment(self): + """ + @return: The comment of the field, or None if not set. + """ + return self._comment + + def __eq__(self, other): + """ + Compares two Field objects for equality. + + @param other: The other Field object to compare with. + @return: True if both Field objects have the same attributes, False otherwise. + """ + if not isinstance(other, Types.StructType.Field): + return False + return (self._name == other._name and self._type == other._type and + self._nullable == other._nullable and self._comment == other._comment) + + def __hash__(self): + """ + @return: A hash code for the Field based on its attributes. + """ + return hash((self._name, self._type, self._nullable)) + + def simpleString(self) -> str: + """ + @return: The simple string representation of the field. + """ + nullable_str = "NULL" if self._nullable else "NOT NULL" + comment_str = f" COMMENT '{self._comment}'" if self._comment else "" + return f"{self._name}: {self._type.simpleString()} {nullable_str}{comment_str}" + + """ A list type. Note, this type is not supported in the current version of Gravitino. """ + class ListType(ComplexType): + def __init__(self, elementType: Type, elementNullable: bool): + """ + Create a new ListType with the given element type and the type is nullable. + + @param elementType: The element type of the list. + @param elementNullable: Whether the element of the list is nullable. + """ + if elementType is None: + raise ValueError("elementType cannot be null") + self._elementType = elementType + self._elementNullable = elementNullable + + @classmethod + def nullable(cls, elementType: Type): + """ + Create a new ListType with the given element type and the type is nullable. + + @param elementType: The element type of the list. + @return: A new ListType instance. + """ + return cls.of(elementType, True) + + @classmethod + def notNull(cls, elementType: Type): + """ + Create a new ListType with the given element type. + + @param elementType: The element type of the list. + @return: A new ListType instance. + """ + return cls.of(elementType, False) + + @classmethod + def of(cls, elementType: Type, elementNullable: bool): + """ + Create a new ListType with the given element type and whether the element is nullable. + + @param elementType: The element type of the list. + @param elementNullable: Whether the element of the list is nullable. + @return: A new ListType instance. + """ + return cls(elementType, elementNullable) + + def elementType(self) -> Type: + """ + @return: The element type of the list. + """ + return self._elementType + + def elementNullable(self) -> bool: + """ + @return: Whether the element of the list is nullable. + """ + return self._elementNullable + + def name(self) -> Name: + """ + @return: The name of the list type. + """ + return Name.LIST + + def simpleString(self) -> str: + """ + @return: A readable string representation of the list type. + """ + return f"list<{self._elementType.simpleString()}>" if self._elementNullable else f"list<{self._elementType.simpleString()}, NOT NULL>" + + def __eq__(self, other): + """ + Compares two ListType objects for equality. + + @param other: The other ListType object to compare with. + @return: True if both ListType objects have the same element type and nullability, False otherwise. + """ + if not isinstance(other, Types.ListType): + return False + return self._elementNullable == other._elementNullable and self._elementType == other._elementType + + def __hash__(self): + """ + @return: A hash code for the ListType based on its element type and nullability. + """ + return hash((self._elementType, self._elementNullable)) + + """ The map type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class MapType(ComplexType): + def __init__(self, keyType: Type, valueType: Type, valueNullable: bool): + """ + Create a new MapType with the given key type, value type and the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @param valueNullable: Whether the value of the map is nullable. + """ + self._keyType = keyType + self._valueType = valueType + self._valueNullable = valueNullable + + @classmethod + def valueNullable(cls, keyType: Type, valueType: Type): + """ + Create a new MapType with the given key type, value type, and the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @return: A new MapType instance. + """ + return cls.of(keyType, valueType, True) + + @classmethod + def valueNotNull(cls, keyType: Type, valueType: Type): + """ + Create a new MapType with the given key type, value type, and the value is not nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @return: A new MapType instance. + """ + return cls.of(keyType, valueType, False) + + @classmethod + def of(cls, keyType: Type, valueType: Type, valueNullable: bool): + """ + Create a new MapType with the given key type, value type, and whether the value is nullable. + + @param keyType: The key type of the map. + @param valueType: The value type of the map. + @param valueNullable: Whether the value of the map is nullable. + @return: A new MapType instance. + """ + return cls(keyType, valueType, valueNullable) + + def keyType(self) -> Type: + """ + @return: The key type of the map. + """ + return self._keyType + + def valueType(self) -> Type: + """ + @return: The value type of the map. + """ + return self._valueType + + def valueNullable(self) -> bool: + """ + @return: Whether the value of the map is nullable. + """ + return self._valueNullable + + def name(self) -> Name: + """ + @return: The name of the map type. + """ + return Name.MAP + + def simpleString(self) -> str: + """ + @return: A readable string representation of the map type. + """ + return f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" + + def __eq__(self, other): + """ + Compares two MapType objects for equality. + + @param other: The other MapType object to compare with. + @return: True if both MapType objects have the same key type, value type, and nullability, False otherwise. + """ + if not isinstance(other, Types.MapType): + return False + return self._valueNullable == other._valueNullable and self._keyType == other._keyType and self._valueType == other._valueType + + def __hash__(self): + """ + @return: A hash code for the MapType based on its key type, value type, and nullability. + """ + return hash((self._keyType, self._valueType, self._valueNullable)) + + """ The union type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class UnionType(ComplexType): + def __init__(self, types: list): + """ + Create a new UnionType with the given types. + + @param types: The types of the union. + """ + self._types = types + + @classmethod + def of(cls, *types: Type): + """ + Create a new UnionType with the given types. + + @param types: The types of the union. + @return: A new UnionType instance. + """ + return cls(types) + + def types(self) -> list: + """ + @return: The types of the union. + """ + return self._types + + def name(self) -> Name: + """ + @return: The name of the union type. + """ + return Name.UNION + + def simpleString(self) -> str: + """ + @return: A readable string representation of the union type. + """ + return f"union<{', '.join(t.simpleString() for t in self._types)}>" + + def __eq__(self, other): + """ + Compares two UnionType objects for equality. + + @param other: The other UnionType object to compare with. + @return: True if both UnionType objects have the same types, False otherwise. + """ + if not isinstance(other, Types.UnionType): + return False + return self._types == other._types + + def __hash__(self): + """ + @return: A hash code for the UnionType based on its types. + """ + return hash(tuple(self._types)) + + """ Represents a type that is not parsed yet. The parsed type is represented by other types of Types. """ + class UnparsedType(Type): + def __init__(self, unparsedType: str): + """ + Initializes an UnparsedType instance. + + @param unparsedType: The unparsed type as a string. + """ + self._unparsedType = unparsedType + + @classmethod + def of(cls, unparsedType: str): + """ + Creates a new UnparsedType with the given unparsed type. + + @param unparsedType: The unparsed type. + @return: A new UnparsedType instance. + """ + return cls(unparsedType) + + def unparsedType(self) -> str: + """ + @return: The unparsed type as a string. + """ + return self._unparsedType + + def name(self) -> Name: + """ + @return: The name of the unparsed type. + """ + return Name.UNPARSED + + def simpleString(self) -> str: + """ + @return: A readable string representation of the unparsed type. + """ + return f"unparsed({self._unparsedType})" + + def __eq__(self, other): + """ + Compares two UnparsedType objects for equality. + + @param other: The other UnparsedType object to compare with. + @return: True if both UnparsedType objects have the same unparsed type string, False otherwise. + """ + if not isinstance(other, Types.UnparsedType): + return False + return self._unparsedType == other._unparsedType + + def __hash__(self): + """ + @return: A hash code for the UnparsedType based on its unparsed type string. + """ + return hash(self._unparsedType) + + def __str__(self): + """ + @return: The unparsed type string representation. + """ + return self._unparsedType + + """ Represents a type that is defined in an external catalog. """ + class ExternalType(Type): + def __init__(self, catalogString: str): + """ + Initializes an ExternalType instance. + + @param catalogString: The string representation of this type in the catalog. + """ + self._catalogString = catalogString + + @classmethod + def of(cls, catalogString: str): + """ + Creates a new ExternalType with the given catalog string. + + @param catalogString: The string representation of this type in the catalog. + @return: A new ExternalType instance. + """ + return cls(catalogString) + + def catalogString(self) -> str: + """ + @return: The string representation of this type in external catalog. + """ + return self._catalogString + + def name(self) -> Name: + """ + @return: The name of the external type. + """ + return Name.EXTERNAL + + def simpleString(self) -> str: + """ + @return: A readable string representation of the external type. + """ + return f"external({self._catalogString})" + + def __eq__(self, other): + """ + Compares two ExternalType objects for equality. + + @param other: The other ExternalType object to compare with. + @return: True if both ExternalType objects have the same catalog string, False otherwise. + """ + if not isinstance(other, Types.ExternalType): + return False + return self._catalogString == other._catalogString + + def __hash__(self): + """ + @return: A hash code for the ExternalType based on its catalog string. + """ + return hash(self._catalogString) + + def __str__(self): + """ + @return: The string representation of the external type. + """ + return self.simpleString() + + @staticmethod + def allowAutoIncrement(dataType: Type) -> bool: + """ + Checks if the given data type is allowed to be an auto-increment column. + + @param dataType: The data type to check. + @return: True if the given data type is allowed to be an auto-increment column, False otherwise. + """ + return isinstance(dataType, (Types.IntegerType, Types.LongType)) + From 666a0b36f6c686723f2db32744387500643a5f88 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Tue, 29 Oct 2024 23:41:15 -0500 Subject: [PATCH 04/15] Update the unit test for the types --- clients/client-python/gravitino/api/type.py | 35 ++-- clients/client-python/gravitino/api/types.py | 35 ++-- .../tests/unittests/test_types.py | 151 ++++++++++++++++++ 3 files changed, 184 insertions(+), 37 deletions(-) create mode 100644 clients/client-python/tests/unittests/test_types.py diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index c32b6215bdc..5855fe78670 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -1,22 +1,19 @@ -""" -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -""" - +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. from abc import ABC, abstractmethod from enum import Enum from typing import Optional diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index 60650c53e53..5f10fdaf6d7 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -1,23 +1,22 @@ -""" -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -""" +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. from .type import Type, Name, PrimitiveType, IntegralType, FractionType, DateTimeType, IntervalType, ComplexType + """ The helper class for Type. """ class Types: diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py new file mode 100644 index 00000000000..d0cb5b5227f --- /dev/null +++ b/clients/client-python/tests/unittests/test_types.py @@ -0,0 +1,151 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import unittest + +from gravitino.api.types import Types, Name + + +class TestTypes(unittest.TestCase): + + # Test NullType singleton and methods + def test_null_type(self): + null_type_1 = Types.NullType.get() + null_type_2 = Types.NullType.get() + + self.assertIs(null_type_1, null_type_2, "NullType should return the same singleton instance") + self.assertEqual(null_type_1.name(), Name.NULL) + self.assertEqual(null_type_1.simpleString(), "null") + + # Test BooleanType singleton and methods + def test_boolean_type(self): + boolean_type_1 = Types.BooleanType.get() + boolean_type_2 = Types.BooleanType.get() + + self.assertIs(boolean_type_1, boolean_type_2, "BooleanType should return the same singleton instance") + self.assertEqual(boolean_type_1.name(), Name.BOOLEAN) + self.assertEqual(boolean_type_1.simpleString(), "boolean") + + # Test ByteType for signed and unsigned versions + def test_byte_type(self): + signed_byte = Types.ByteType.get() + unsigned_byte = Types.ByteType.unsigned() + + self.assertEqual(signed_byte.name(), Name.BYTE) + self.assertEqual(signed_byte.simpleString(), "byte") + self.assertEqual(unsigned_byte.simpleString(), "byte unsigned") + + # Test ShortType for signed and unsigned versions + def test_short_type(self): + signed_short = Types.ShortType.get() + unsigned_short = Types.ShortType.unsigned() + + self.assertEqual(signed_short.name(), Name.SHORT) + self.assertEqual(signed_short.simpleString(), "short") + self.assertEqual(unsigned_short.simpleString(), "short unsigned") + + # Test IntegerType for signed and unsigned versions + def test_integer_type(self): + signed_int = Types.IntegerType.get() + unsigned_int = Types.IntegerType.unsigned() + + self.assertEqual(signed_int.name(), Name.INTEGER) + self.assertEqual(signed_int.simpleString(), "integer") + self.assertEqual(unsigned_int.simpleString(), "integer unsigned") + + # Test DecimalType and its methods + def test_decimal_type(self): + decimal_type_1 = Types.DecimalType.of(10, 2) + decimal_type_2 = Types.DecimalType.of(10, 2) + decimal_type_3 = Types.DecimalType.of(12, 3) + + self.assertEqual(decimal_type_1.name(), Name.DECIMAL) + self.assertEqual(decimal_type_1.simpleString(), "decimal(10,2)") + self.assertEqual(decimal_type_1, decimal_type_2, "Decimal types with the same precision and scale should be equal") + self.assertNotEqual(decimal_type_1, decimal_type_3, "Decimal types with different precision/scale should not be equal") + self.assertNotEqual(hash(decimal_type_1), hash(decimal_type_3), "Different decimal types should have different hash codes") + + # Test DateType singleton and methods + def test_date_type(self): + date_type_1 = Types.DateType.get() + date_type_2 = Types.DateType.get() + + self.assertIs(date_type_1, date_type_2, "DateType should return the same singleton instance") + self.assertEqual(date_type_1.name(), Name.DATE) + self.assertEqual(date_type_1.simpleString(), "date") + + # Test TimeType singleton and methods + def test_time_type(self): + time_type_1 = Types.TimeType.get() + time_type_2 = Types.TimeType.get() + + self.assertIs(time_type_1, time_type_2, "TimeType should return the same singleton instance") + self.assertEqual(time_type_1.name(), Name.TIME) + self.assertEqual(time_type_1.simpleString(), "time") + + # Test StringType singleton and methods + def test_string_type(self): + string_type_1 = Types.StringType.get() + string_type_2 = Types.StringType.get() + + self.assertIs(string_type_1, string_type_2, "StringType should return the same singleton instance") + self.assertEqual(string_type_1.name(), Name.STRING) + self.assertEqual(string_type_1.simpleString(), "string") + + # Test UUIDType singleton and methods + def test_uuid_type(self): + uuid_type_1 = Types.UUIDType.get() + uuid_type_2 = Types.UUIDType.get() + + self.assertIs(uuid_type_1, uuid_type_2, "UUIDType should return the same singleton instance") + self.assertEqual(uuid_type_1.name(), Name.UUID) + self.assertEqual(uuid_type_1.simpleString(), "uuid") + + # Test FixedType creation and equality + def test_fixed_type(self): + fixed_type_1 = Types.FixedType.of(16) + fixed_type_2 = Types.FixedType.of(16) + fixed_type_3 = Types.FixedType.of(32) + + self.assertEqual(fixed_type_1.name(), Name.FIXED) + self.assertEqual(fixed_type_1.simpleString(), "fixed(16)") + self.assertEqual(fixed_type_1, fixed_type_2) + self.assertNotEqual(fixed_type_1, fixed_type_3) + + # Test VarCharType creation and equality + def test_varchar_type(self): + varchar_type_1 = Types.VarCharType.of(255) + varchar_type_2 = Types.VarCharType.of(255) + varchar_type_3 = Types.VarCharType.of(512) + + self.assertEqual(varchar_type_1.name(), Name.VARCHAR) + self.assertEqual(varchar_type_1.simpleString(), "varchar(255)") + self.assertEqual(varchar_type_1, varchar_type_2) + self.assertNotEqual(varchar_type_1, varchar_type_3) + + # Test allowAutoIncrement method for IntegerType and LongType + def test_allow_auto_increment(self): + integer_type = Types.IntegerType.get() + long_type = Types.LongType.get() + boolean_type = Types.BooleanType.get() + + self.assertTrue(Types.allowAutoIncrement(integer_type)) + self.assertTrue(Types.allowAutoIncrement(long_type)) + self.assertFalse(Types.allowAutoIncrement(boolean_type)) + + +if __name__ == "__main__": + unittest.main() From 5abeb8cabfe7cc09d57af2840dae263e203a8df9 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Tue, 29 Oct 2024 23:42:36 -0500 Subject: [PATCH 05/15] Update unit test file --- clients/client-python/tests/unittests/test_types.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py index d0cb5b5227f..270bfdf8113 100644 --- a/clients/client-python/tests/unittests/test_types.py +++ b/clients/client-python/tests/unittests/test_types.py @@ -144,8 +144,4 @@ def test_allow_auto_increment(self): self.assertTrue(Types.allowAutoIncrement(integer_type)) self.assertTrue(Types.allowAutoIncrement(long_type)) - self.assertFalse(Types.allowAutoIncrement(boolean_type)) - - -if __name__ == "__main__": - unittest.main() + self.assertFalse(Types.allowAutoIncrement(boolean_type)) \ No newline at end of file From 28bfa05db7498a89b4016418f0b6563e23657c53 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Wed, 30 Oct 2024 09:34:00 -0500 Subject: [PATCH 06/15] Update the format --- clients/client-python/gravitino/api/type.py | 32 +++++-- clients/client-python/gravitino/api/types.py | 89 +++++++++++++++---- .../tests/unittests/test_types.py | 72 +++++++++++---- 3 files changed, 151 insertions(+), 42 deletions(-) diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index 5855fe78670..cf9d9003cc0 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -116,50 +116,64 @@ class Name(Enum): EXTERNAL = "EXTERNAL" """ The external type. An external type represents a type that is not supported. """ + # Define the Type interface (abstract base class) class Type(ABC): @abstractmethod def name(self) -> Name: - """ Returns the generic name of the type. """ + """Returns the generic name of the type.""" pass @abstractmethod def simpleString(self) -> str: - """ Returns a readable string representation of the type. """ + """Returns a readable string representation of the type.""" pass + # Define base classes class PrimitiveType(Type, ABC): - """ Base class for all primitive types. """ + """Base class for all primitive types.""" + pass + class NumericType(PrimitiveType, ABC): - """ Base class for all numeric types. """ + """Base class for all numeric types.""" + pass + class DateTimeType(PrimitiveType, ABC): - """ Base class for all date/time types. """ + """Base class for all date/time types.""" + pass + class IntervalType(PrimitiveType, ABC): - """ Base class for all interval types. """ + """Base class for all interval types.""" + pass + class ComplexType(Type, ABC): - """ Base class for all complex types, including struct, list, map, and union. """ + """Base class for all complex types, including struct, list, map, and union.""" + pass + # Define IntegralType class class IntegralType(NumericType, ABC): def __init__(self, signed: bool): self._signed = signed def signed(self) -> bool: - """ Returns True if the integer type is signed, False otherwise. """ + """Returns True if the integer type is signed, False otherwise.""" return self._signed + # Define FractionType class class FractionType(NumericType, ABC): - """ Base class for all fractional types. """ + """Base class for all fractional types.""" + def __init__(self): super().__init__() diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index 5f10fdaf6d7..601a198f564 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -14,13 +14,24 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from .type import Type, Name, PrimitiveType, IntegralType, FractionType, DateTimeType, IntervalType, ComplexType +from .type import ( + Type, + Name, + PrimitiveType, + IntegralType, + FractionType, + DateTimeType, + IntervalType, + ComplexType, +) """ The helper class for Type. """ + + class Types: + """The data type representing `NULL` values.""" - """ The data type representing `NULL` values. """ class NullType(Type): _instance = None @@ -46,6 +57,7 @@ def simpleString(self) -> str: return "null" """ The boolean type in Gravitino. """ + class BooleanType(PrimitiveType): _instance = None @@ -71,6 +83,7 @@ def simpleString(self) -> str: return "boolean" """ The byte type in Gravitino. """ + class ByteType(IntegralType): _instance = None _unsigned_instance = None @@ -112,6 +125,7 @@ def simpleString(self) -> str: return "byte" if self.signed() else "byte unsigned" """ The short type in Gravitino. """ + class ShortType(IntegralType): _instance = None _unsigned_instance = None @@ -153,6 +167,7 @@ def simpleString(self) -> str: return "short" if self.signed() else "short unsigned" """ The integer type in Gravitino. """ + class IntegerType(IntegralType): _instance = None _unsigned_instance = None @@ -194,6 +209,7 @@ def simpleString(self) -> str: return "integer" if self.signed() else "integer unsigned" """ The long type in Gravitino. """ + class LongType(IntegralType): _instance = None _unsigned_instance = None @@ -235,6 +251,7 @@ def simpleString(self) -> str: return "long" if self.signed() else "long unsigned" """ The float type in Gravitino. """ + class FloatType(FractionType): _instance = None @@ -260,6 +277,7 @@ def simpleString(self) -> str: return "float" """ The double type in Gravitino. """ + class DoubleType(FractionType): _instance = None @@ -285,6 +303,7 @@ def simpleString(self) -> str: return "double" """ The decimal type in Gravitino. """ + class DecimalType(FractionType): @classmethod def of(cls, precision: int, scale: int): @@ -312,9 +331,13 @@ def check_precision_scale(precision: int, scale: int): @param scale: The scale of the decimal. """ if not (1 <= precision <= 38): - raise ValueError(f"Decimal precision must be in range [1, 38]: {precision}") + raise ValueError( + f"Decimal precision must be in range [1, 38]: {precision}" + ) if not (0 <= scale <= precision): - raise ValueError(f"Decimal scale must be in range [0, precision ({precision})]: {scale}") + raise ValueError( + f"Decimal scale must be in range [0, precision ({precision})]: {scale}" + ) def name(self) -> Name: """ @@ -339,11 +362,11 @@ def simpleString(self) -> str: @return A readable string representation of the Decimal type. """ return f"decimal({self._precision},{self._scale})" - + def __eq__(self, other): """ Compares two DecimalType objects for equality. - + @param other: The other DecimalType to compare with. @return: True if both objects have the same precision and scale, False otherwise. """ @@ -358,6 +381,7 @@ def __hash__(self): return hash((self._precision, self._scale)) """ The date time type in Gravitino. """ + class DateType(DateTimeType): _instance = None @@ -383,6 +407,7 @@ def simpleString(self) -> str: return "date" """ The time type in Gravitino. """ + class TimeType(DateTimeType): _instance = None @@ -408,6 +433,7 @@ def simpleString(self) -> str: return "time" """ The timestamp type in Gravitino. """ + class TimestampType(DateTimeType): _instance_with_tz = None _instance_without_tz = None @@ -455,6 +481,7 @@ def simpleString(self) -> str: return "timestamp_tz" if self._with_time_zone else "timestamp" """ The interval year type in Gravitino. """ + class IntervalYearType(IntervalType): _instance = None @@ -478,8 +505,9 @@ def simpleString(self) -> str: @return A readable string representation of the IntervalYear type. """ return "interval_year" - + """ The interval day type in Gravitino. """ + class IntervalDayType(IntervalType): _instance = None @@ -505,6 +533,7 @@ def simpleString(self) -> str: return "interval_day" """ The string type in Gravitino, equivalent to varchar(MAX), where the MAX is determined by the underlying catalog. """ + class StringType(PrimitiveType): _instance = None @@ -530,6 +559,7 @@ def simpleString(self) -> str: return "string" """ The UUID type in Gravitino. """ + class UUIDType(PrimitiveType): _instance = None @@ -555,6 +585,7 @@ def simpleString(self) -> str: return "uuid" """ Fixed-length byte array type. Use BinaryType for variable-length byte arrays. """ + class FixedType(PrimitiveType): def __init__(self, length: int): """ @@ -608,6 +639,7 @@ def __hash__(self): return hash(self._length) """ The varchar type in Gravitino. """ + class VarCharType(PrimitiveType): def __init__(self, length: int): """ @@ -661,6 +693,7 @@ def __hash__(self): return hash(self._length) """ The fixed char type in Gravitino. """ + class FixedCharType(PrimitiveType): def __init__(self, length: int): """ @@ -714,6 +747,7 @@ def __hash__(self): return hash(self._length) """ The binary type in Gravitino. """ + class BinaryType(PrimitiveType): _instance = None @@ -739,6 +773,7 @@ def simpleString(self) -> str: return "binary" """ The struct type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" + class StructType(ComplexType): def __init__(self, fields): """ @@ -774,7 +809,9 @@ def simpleString(self) -> str: """ @return: A readable string representation of the struct type. """ - return f"struct<{', '.join(field.simpleString() for field in self._fields)}>" + return ( + f"struct<{', '.join(field.simpleString() for field in self._fields)}>" + ) def __eq__(self, other): """ @@ -794,6 +831,7 @@ def __hash__(self): return hash(tuple(self._fields)) """ A field of a struct type. """ + class Field: def __init__(self, name, type, nullable, comment=None): """ @@ -866,8 +904,12 @@ def __eq__(self, other): """ if not isinstance(other, Types.StructType.Field): return False - return (self._name == other._name and self._type == other._type and - self._nullable == other._nullable and self._comment == other._comment) + return ( + self._name == other._name + and self._type == other._type + and self._nullable == other._nullable + and self._comment == other._comment + ) def __hash__(self): """ @@ -884,6 +926,7 @@ def simpleString(self) -> str: return f"{self._name}: {self._type.simpleString()} {nullable_str}{comment_str}" """ A list type. Note, this type is not supported in the current version of Gravitino. """ + class ListType(ComplexType): def __init__(self, elementType: Type, elementNullable: bool): """ @@ -950,7 +993,11 @@ def simpleString(self) -> str: """ @return: A readable string representation of the list type. """ - return f"list<{self._elementType.simpleString()}>" if self._elementNullable else f"list<{self._elementType.simpleString()}, NOT NULL>" + return ( + f"list<{self._elementType.simpleString()}>" + if self._elementNullable + else f"list<{self._elementType.simpleString()}, NOT NULL>" + ) def __eq__(self, other): """ @@ -961,7 +1008,10 @@ def __eq__(self, other): """ if not isinstance(other, Types.ListType): return False - return self._elementNullable == other._elementNullable and self._elementType == other._elementType + return ( + self._elementNullable == other._elementNullable + and self._elementType == other._elementType + ) def __hash__(self): """ @@ -970,6 +1020,7 @@ def __hash__(self): return hash((self._elementType, self._elementNullable)) """ The map type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class MapType(ComplexType): def __init__(self, keyType: Type, valueType: Type, valueNullable: bool): """ @@ -1045,7 +1096,9 @@ def simpleString(self) -> str: """ @return: A readable string representation of the map type. """ - return f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" + return ( + f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" + ) def __eq__(self, other): """ @@ -1056,7 +1109,11 @@ def __eq__(self, other): """ if not isinstance(other, Types.MapType): return False - return self._valueNullable == other._valueNullable and self._keyType == other._keyType and self._valueType == other._valueType + return ( + self._valueNullable == other._valueNullable + and self._keyType == other._keyType + and self._valueType == other._valueType + ) def __hash__(self): """ @@ -1065,6 +1122,7 @@ def __hash__(self): return hash((self._keyType, self._valueType, self._valueNullable)) """ The union type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + class UnionType(ComplexType): def __init__(self, types: list): """ @@ -1120,6 +1178,7 @@ def __hash__(self): return hash(tuple(self._types)) """ Represents a type that is not parsed yet. The parsed type is represented by other types of Types. """ + class UnparsedType(Type): def __init__(self, unparsedType: str): """ @@ -1181,6 +1240,7 @@ def __str__(self): return self._unparsedType """ Represents a type that is defined in an external catalog. """ + class ExternalType(Type): def __init__(self, catalogString: str): """ @@ -1250,4 +1310,3 @@ def allowAutoIncrement(dataType: Type) -> bool: @return: True if the given data type is allowed to be an auto-increment column, False otherwise. """ return isinstance(dataType, (Types.IntegerType, Types.LongType)) - diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py index 270bfdf8113..f4210c6314a 100644 --- a/clients/client-python/tests/unittests/test_types.py +++ b/clients/client-python/tests/unittests/test_types.py @@ -20,25 +20,33 @@ class TestTypes(unittest.TestCase): - + # Test NullType singleton and methods def test_null_type(self): null_type_1 = Types.NullType.get() null_type_2 = Types.NullType.get() - - self.assertIs(null_type_1, null_type_2, "NullType should return the same singleton instance") + + self.assertIs( + null_type_1, + null_type_2, + "NullType should return the same singleton instance", + ) self.assertEqual(null_type_1.name(), Name.NULL) self.assertEqual(null_type_1.simpleString(), "null") - + # Test BooleanType singleton and methods def test_boolean_type(self): boolean_type_1 = Types.BooleanType.get() boolean_type_2 = Types.BooleanType.get() - self.assertIs(boolean_type_1, boolean_type_2, "BooleanType should return the same singleton instance") + self.assertIs( + boolean_type_1, + boolean_type_2, + "BooleanType should return the same singleton instance", + ) self.assertEqual(boolean_type_1.name(), Name.BOOLEAN) self.assertEqual(boolean_type_1.simpleString(), "boolean") - + # Test ByteType for signed and unsigned versions def test_byte_type(self): signed_byte = Types.ByteType.get() @@ -56,7 +64,7 @@ def test_short_type(self): self.assertEqual(signed_short.name(), Name.SHORT) self.assertEqual(signed_short.simpleString(), "short") self.assertEqual(unsigned_short.simpleString(), "short unsigned") - + # Test IntegerType for signed and unsigned versions def test_integer_type(self): signed_int = Types.IntegerType.get() @@ -65,7 +73,7 @@ def test_integer_type(self): self.assertEqual(signed_int.name(), Name.INTEGER) self.assertEqual(signed_int.simpleString(), "integer") self.assertEqual(unsigned_int.simpleString(), "integer unsigned") - + # Test DecimalType and its methods def test_decimal_type(self): decimal_type_1 = Types.DecimalType.of(10, 2) @@ -74,34 +82,58 @@ def test_decimal_type(self): self.assertEqual(decimal_type_1.name(), Name.DECIMAL) self.assertEqual(decimal_type_1.simpleString(), "decimal(10,2)") - self.assertEqual(decimal_type_1, decimal_type_2, "Decimal types with the same precision and scale should be equal") - self.assertNotEqual(decimal_type_1, decimal_type_3, "Decimal types with different precision/scale should not be equal") - self.assertNotEqual(hash(decimal_type_1), hash(decimal_type_3), "Different decimal types should have different hash codes") + self.assertEqual( + decimal_type_1, + decimal_type_2, + "Decimal types with the same precision and scale should be equal", + ) + self.assertNotEqual( + decimal_type_1, + decimal_type_3, + "Decimal types with different precision/scale should not be equal", + ) + self.assertNotEqual( + hash(decimal_type_1), + hash(decimal_type_3), + "Different decimal types should have different hash codes", + ) # Test DateType singleton and methods def test_date_type(self): date_type_1 = Types.DateType.get() date_type_2 = Types.DateType.get() - self.assertIs(date_type_1, date_type_2, "DateType should return the same singleton instance") + self.assertIs( + date_type_1, + date_type_2, + "DateType should return the same singleton instance", + ) self.assertEqual(date_type_1.name(), Name.DATE) self.assertEqual(date_type_1.simpleString(), "date") - + # Test TimeType singleton and methods def test_time_type(self): time_type_1 = Types.TimeType.get() time_type_2 = Types.TimeType.get() - self.assertIs(time_type_1, time_type_2, "TimeType should return the same singleton instance") + self.assertIs( + time_type_1, + time_type_2, + "TimeType should return the same singleton instance", + ) self.assertEqual(time_type_1.name(), Name.TIME) self.assertEqual(time_type_1.simpleString(), "time") - + # Test StringType singleton and methods def test_string_type(self): string_type_1 = Types.StringType.get() string_type_2 = Types.StringType.get() - self.assertIs(string_type_1, string_type_2, "StringType should return the same singleton instance") + self.assertIs( + string_type_1, + string_type_2, + "StringType should return the same singleton instance", + ) self.assertEqual(string_type_1.name(), Name.STRING) self.assertEqual(string_type_1.simpleString(), "string") @@ -110,7 +142,11 @@ def test_uuid_type(self): uuid_type_1 = Types.UUIDType.get() uuid_type_2 = Types.UUIDType.get() - self.assertIs(uuid_type_1, uuid_type_2, "UUIDType should return the same singleton instance") + self.assertIs( + uuid_type_1, + uuid_type_2, + "UUIDType should return the same singleton instance", + ) self.assertEqual(uuid_type_1.name(), Name.UUID) self.assertEqual(uuid_type_1.simpleString(), "uuid") @@ -144,4 +180,4 @@ def test_allow_auto_increment(self): self.assertTrue(Types.allowAutoIncrement(integer_type)) self.assertTrue(Types.allowAutoIncrement(long_type)) - self.assertFalse(Types.allowAutoIncrement(boolean_type)) \ No newline at end of file + self.assertFalse(Types.allowAutoIncrement(boolean_type)) From a4323d76916f67fb26db4c630b9e469bf8fcc603 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Wed, 30 Oct 2024 22:44:37 -0500 Subject: [PATCH 07/15] Update the format to pass pylint --- clients/client-python/gravitino/api/type.py | 6 +- clients/client-python/gravitino/api/types.py | 701 ++++--------------- 2 files changed, 157 insertions(+), 550 deletions(-) diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index cf9d9003cc0..4d077dab44f 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -16,7 +16,6 @@ # under the License. from abc import ABC, abstractmethod from enum import Enum -from typing import Optional class Name(Enum): @@ -125,7 +124,7 @@ def name(self) -> Name: pass @abstractmethod - def simpleString(self) -> str: + def simple_string(self) -> str: """Returns a readable string representation of the type.""" pass @@ -175,5 +174,4 @@ def signed(self) -> bool: class FractionType(NumericType, ABC): """Base class for all fractional types.""" - def __init__(self): - super().__init__() + pass diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index 601a198f564..ebd1cd295a0 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -26,292 +26,175 @@ ) -""" The helper class for Type. """ - - class Types: - """The data type representing `NULL` values.""" class NullType(Type): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of NullType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Null type. - """ return Name.NULL - def simpleString(self) -> str: - """ - @return A readable string representation of the Null type. - """ + def simple_string(self) -> str: return "null" - """ The boolean type in Gravitino. """ - class BooleanType(PrimitiveType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of BooleanType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Boolean type. - """ return Name.BOOLEAN - def simpleString(self) -> str: - """ - @return A readable string representation of the Boolean type. - """ + def simple_string(self) -> str: return "boolean" - """ The byte type in Gravitino. """ - class ByteType(IntegralType): _instance = None _unsigned_instance = None + def __init__(self, signed: bool): + super().__init__(signed) + self._signed = signed + @classmethod def get(cls): - """ - @return The singleton instance of ByteType. - """ if cls._instance is None: cls._instance = cls(True) return cls._instance @classmethod def unsigned(cls): - """ - @return The singleton instance of unsigned ByteType. - """ if cls._unsigned_instance is None: cls._unsigned_instance = cls(False) return cls._unsigned_instance - def __init__(self, signed: bool): - """ - @param signed: True if the byte type is signed, False otherwise. - """ - super().__init__(signed) - def name(self) -> Name: - """ - @return The name of the Byte type. - """ return Name.BYTE - def simpleString(self) -> str: - """ - @return A readable string representa - """ + def simple_string(self) -> str: return "byte" if self.signed() else "byte unsigned" - """ The short type in Gravitino. """ - class ShortType(IntegralType): _instance = None _unsigned_instance = None + def __init__(self, signed: bool): + super().__init__(signed) + self._signed = signed + @classmethod def get(cls): - """ - @return The singleton instance of ShortType. - """ if cls._instance is None: cls._instance = cls(True) return cls._instance @classmethod def unsigned(cls): - """ - @return The singleton instance of unsigned ShortType. - """ if cls._unsigned_instance is None: cls._unsigned_instance = cls(False) return cls._unsigned_instance - def __init__(self, signed: bool): - """ - @param signed: True if the short type is signed, False otherwise. - """ - super().__init__(signed) - def name(self) -> Name: - """ - @return The name of the Short type. - """ return Name.SHORT - def simpleString(self) -> str: - """ - @return A readable string representation of the Short type. - """ + def simple_string(self) -> str: return "short" if self.signed() else "short unsigned" - """ The integer type in Gravitino. """ - class IntegerType(IntegralType): _instance = None _unsigned_instance = None + def __init__(self, signed: bool): + super().__init__(signed) + self._signed = signed + @classmethod def get(cls): - """ - @return The singleton instance of IntegerType. - """ if cls._instance is None: cls._instance = cls(True) return cls._instance @classmethod def unsigned(cls): - """ - @return The singleton instance of unsigned IntegerType. - """ if cls._unsigned_instance is None: cls._unsigned_instance = cls(False) return cls._unsigned_instance - def __init__(self, signed: bool): - """ - @param signed: True if the integer type is signed, False otherwise. - """ - super().__init__(signed) - def name(self) -> Name: - """ - @return The name of the Integer type. - """ return Name.INTEGER - def simpleString(self) -> str: - """ - @return A readable string representation of the Integer type. - """ + def simple_string(self) -> str: return "integer" if self.signed() else "integer unsigned" - """ The long type in Gravitino. """ - class LongType(IntegralType): _instance = None _unsigned_instance = None + def __init__(self, signed: bool): + super().__init__(signed) + self._signed = signed + @classmethod def get(cls): - """ - @return The singleton instance of LongType. - """ if cls._instance is None: cls._instance = cls(True) return cls._instance @classmethod def unsigned(cls): - """ - @return The singleton instance of unsigned LongType. - """ if cls._unsigned_instance is None: cls._unsigned_instance = cls(False) return cls._unsigned_instance - def __init__(self, signed: bool): - """ - @param signed: True if the long type is signed, False otherwise. - """ - super().__init__(signed) - def name(self) -> Name: - """ - @return The name of the Long type. - """ return Name.LONG - def simpleString(self) -> str: - """ - @return A readable string representation of the Long type. - """ + def simple_string(self) -> str: return "long" if self.signed() else "long unsigned" - """ The float type in Gravitino. """ - class FloatType(FractionType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of FloatType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Float type. - """ return Name.FLOAT - def simpleString(self) -> str: - """ - @return A readable string representation of the Float type. - """ + def simple_string(self) -> str: return "float" - """ The double type in Gravitino. """ - class DoubleType(FractionType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of DoubleType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Double type. - """ return Name.DOUBLE - def simpleString(self) -> str: - """ - @return A readable string representation of the Double type. - """ + def simple_string(self) -> str: return "double" - """ The decimal type in Gravitino. """ - class DecimalType(FractionType): @classmethod def of(cls, precision: int, scale: int): - """ - @param precision: The precision of the decimal type. - @param scale: The scale of the decimal type. - @return A DecimalType with the given precision and scale. - """ return cls(precision, scale) def __init__(self, precision: int, scale: int): @@ -319,6 +202,7 @@ def __init__(self, precision: int, scale: int): @param precision: The precision of the decimal type. @param scale: The scale of the decimal type. """ + super().__init__() self.check_precision_scale(precision, scale) self._precision = precision self._scale = scale @@ -330,37 +214,25 @@ def check_precision_scale(precision: int, scale: int): @param precision: The precision of the decimal. @param scale: The scale of the decimal. """ - if not (1 <= precision <= 38): + if not 1 <= precision <= 38: raise ValueError( f"Decimal precision must be in range [1, 38]: {precision}" ) - if not (0 <= scale <= precision): + if not 0 <= scale <= precision: raise ValueError( f"Decimal scale must be in range [0, precision ({precision})]: {scale}" ) def name(self) -> Name: - """ - @return The name of the Decimal type. - """ return Name.DECIMAL def precision(self) -> int: - """ - @return The precision of the decimal type. - """ return self._precision def scale(self) -> int: - """ - @return The scale of the decimal type. - """ return self._scale - def simpleString(self) -> str: - """ - @return A readable string representation of the Decimal type. - """ + def simple_string(self) -> str: return f"decimal({self._precision},{self._scale})" def __eq__(self, other): @@ -375,217 +247,126 @@ def __eq__(self, other): return self._precision == other._precision and self._scale == other._scale def __hash__(self): - """ - @return: A hash code for the DecimalType based on its precision and scale. - """ return hash((self._precision, self._scale)) - """ The date time type in Gravitino. """ - class DateType(DateTimeType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of DateType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Date type. - """ return Name.DATE - def simpleString(self) -> str: - """ - @return A readable string representation of the Date type. - """ + def simple_string(self) -> str: return "date" - """ The time type in Gravitino. """ - class TimeType(DateTimeType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of TimeType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the Time type. - """ return Name.TIME - def simpleString(self) -> str: - """ - @return A readable string representation of the Time type. - """ + def simple_string(self) -> str: return "time" - """ The timestamp type in Gravitino. """ - class TimestampType(DateTimeType): _instance_with_tz = None _instance_without_tz = None @classmethod - def withTimeZone(cls): - """ - @return A TimestampType with time zone. - """ + def with_time_zone(cls): if cls._instance_with_tz is None: cls._instance_with_tz = cls(True) return cls._instance_with_tz @classmethod - def withoutTimeZone(cls): - """ - @return A TimestampType without time zone. - """ + def without_time_zone(cls): if cls._instance_without_tz is None: cls._instance_without_tz = cls(False) return cls._instance_without_tz def __init__(self, with_time_zone: bool): - """ - @param with_time_zone: True if the timestamp type has a time zone, False otherwise. - """ self._with_time_zone = with_time_zone - def hasTimeZone(self) -> bool: - """ - @return True if the timestamp type has a time zone, False otherwise. - """ + def has_time_zone(self) -> bool: return self._with_time_zone def name(self) -> Name: - """ - @return The name of the Timestamp type. - """ return Name.TIMESTAMP - def simpleString(self) -> str: - """ - @return A readable string representation of the Timestamp type. - """ + def simple_string(self) -> str: return "timestamp_tz" if self._with_time_zone else "timestamp" - """ The interval year type in Gravitino. """ - class IntervalYearType(IntervalType): _instance = None @classmethod def get(cls): - """ - @return The singleton instance of IntervalYearType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return The name of the IntervalYear type. - """ return Name.INTERVAL_YEAR - def simpleString(self) -> str: - """ - @return A readable string representation of the IntervalYear type. - """ + def simple_string(self) -> str: return "interval_year" - """ The interval day type in Gravitino. """ - class IntervalDayType(IntervalType): _instance = None @classmethod def get(cls): - """ - @return: The singleton instance of IntervalDayType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return: The name of the interval day type. - """ return Name.INTERVAL_DAY - def simpleString(self) -> str: - """ - @return: A readable string representation of the interval day type. - """ + def simple_string(self) -> str: return "interval_day" - """ The string type in Gravitino, equivalent to varchar(MAX), where the MAX is determined by the underlying catalog. """ - class StringType(PrimitiveType): _instance = None @classmethod def get(cls): - """ - @return: The singleton instance of StringType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return: The name of the string type. - """ return Name.STRING - def simpleString(self) -> str: - """ - @return: A readable string representation of the string type. - """ + def simple_string(self) -> str: return "string" - """ The UUID type in Gravitino. """ - class UUIDType(PrimitiveType): _instance = None @classmethod def get(cls): - """ - @return: The singleton instance of UUIDType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return: The name of the UUID type. - """ return Name.UUID - def simpleString(self) -> str: - """ - @return: A readable string representation of the UUID type. - """ + def simple_string(self) -> str: return "uuid" - """ Fixed-length byte array type. Use BinaryType for variable-length byte arrays. """ - class FixedType(PrimitiveType): def __init__(self, length: int): """ @@ -604,21 +385,12 @@ def of(cls, length: int): return cls(length) def name(self) -> Name: - """ - @return: The name of the fixed type. - """ return Name.FIXED def length(self) -> int: - """ - @return: The length of the fixed type. - """ return self._length - def simpleString(self) -> str: - """ - @return: A readable string representation of the fixed type. - """ + def simple_string(self) -> str: return f"fixed({self._length})" def __eq__(self, other): @@ -633,13 +405,8 @@ def __eq__(self, other): return self._length == other._length def __hash__(self): - """ - @return: A hash code for the FixedType based on its length. - """ return hash(self._length) - """ The varchar type in Gravitino. """ - class VarCharType(PrimitiveType): def __init__(self, length: int): """ @@ -658,21 +425,12 @@ def of(cls, length: int): return cls(length) def name(self) -> Name: - """ - @return: The name of the varchar type. - """ return Name.VARCHAR def length(self) -> int: - """ - @return: The length of the varchar type. - """ return self._length - def simpleString(self) -> str: - """ - @return: A readable string representation of the varchar type. - """ + def simple_string(self) -> str: return f"varchar({self._length})" def __eq__(self, other): @@ -687,13 +445,8 @@ def __eq__(self, other): return self._length == other._length def __hash__(self): - """ - @return: A hash code for the VarCharType based on its length. - """ return hash(self._length) - """ The fixed char type in Gravitino. """ - class FixedCharType(PrimitiveType): def __init__(self, length: int): """ @@ -712,21 +465,12 @@ def of(cls, length: int): return cls(length) def name(self) -> Name: - """ - @return: The name of the fixed char type. - """ return Name.FIXEDCHAR def length(self) -> int: - """ - @return: The length of the fixed char type. - """ return self._length - def simpleString(self) -> str: - """ - @return: A readable string representation of the fixed char type. - """ + def simple_string(self) -> str: return f"char({self._length})" def __eq__(self, other): @@ -741,39 +485,23 @@ def __eq__(self, other): return self._length == other._length def __hash__(self): - """ - @return: A hash code for the FixedCharType based on its length. - """ return hash(self._length) - """ The binary type in Gravitino. """ - class BinaryType(PrimitiveType): _instance = None @classmethod def get(cls): - """ - @return: The singleton instance of BinaryType. - """ if cls._instance is None: cls._instance = cls() return cls._instance def name(self) -> Name: - """ - @return: The name of the binary type. - """ return Name.BINARY - def simpleString(self) -> str: - """ - @return: A readable string representation of the binary type. - """ + def simple_string(self) -> str: return "binary" - """ The struct type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" - class StructType(ComplexType): def __init__(self, fields): """ @@ -794,23 +522,14 @@ def of(cls, *fields): return cls(fields) def fields(self): - """ - @return: The fields of the struct type. - """ return self._fields def name(self) -> Name: - """ - @return: The name of the struct type. - """ return Name.STRUCT - def simpleString(self) -> str: - """ - @return: A readable string representation of the struct type. - """ + def simple_string(self) -> str: return ( - f"struct<{', '.join(field.simpleString() for field in self._fields)}>" + f"struct<{', '.join(field.simple_string() for field in self._fields)}>" ) def __eq__(self, other): @@ -825,20 +544,15 @@ def __eq__(self, other): return self._fields == other._fields def __hash__(self): - """ - @return: A hash code for the StructType based on its fields. - """ return hash(tuple(self._fields)) - """ A field of a struct type. """ - class Field: - def __init__(self, name, type, nullable, comment=None): + def __init__(self, name, field_type, nullable, comment=None): """ Initializes the Field with the given name, type, nullable flag, and comment. @param name: The name of the field. - @param type: The type of the field. + @param field_type: The type of the field. @param nullable: Whether the field is nullable. @param comment: The comment of the field (optional). """ @@ -847,52 +561,40 @@ def __init__(self, name, type, nullable, comment=None): if type is None: raise ValueError("type cannot be null") self._name = name - self._type = type + self._field_type = field_type self._nullable = nullable self._comment = comment @classmethod - def notNullField(cls, name, type, comment=None): + def not_null_field(cls, name, field_type, comment=None): """ @param name: The name of the field. - @param type: The type of the field. + @param field_type: The type of the field. @param comment: The comment of the field. - @return: A NOT NULL Field instance with the given name, type, and comment. + @return: A NOT NULL Field instance with the given name, field_type, and comment. """ - return cls(name, type, False, comment) + return cls(name, field_type, False, comment) @classmethod - def nullableField(cls, name, type, comment=None): + def nullable_field(cls, name, field_type, comment=None): """ @param name: The name of the field. - @param type: The type of the field. + @param field_type: The type of the field. @param comment: The comment of the field. - @return: A nullable Field instance with the given name, type, and comment. + @return: A nullable Field instance with the given name, field_type, and comment. """ - return cls(name, type, True, comment) + return cls(name, field_type, True, comment) def name(self): - """ - @return: The name of the field. - """ return self._name def type(self): - """ - @return: The type of the field. - """ return self._type def nullable(self): - """ - @return: Whether the field is nullable. - """ return self._nullable def comment(self): - """ - @return: The comment of the field, or None if not set. - """ return self._comment def __eq__(self, other): @@ -912,91 +614,71 @@ def __eq__(self, other): ) def __hash__(self): - """ - @return: A hash code for the Field based on its attributes. - """ return hash((self._name, self._type, self._nullable)) - def simpleString(self) -> str: - """ - @return: The simple string representation of the field. - """ + def simple_string(self) -> str: nullable_str = "NULL" if self._nullable else "NOT NULL" comment_str = f" COMMENT '{self._comment}'" if self._comment else "" - return f"{self._name}: {self._type.simpleString()} {nullable_str}{comment_str}" - - """ A list type. Note, this type is not supported in the current version of Gravitino. """ + return f"{self._name}: {self._type.simple_string()} {nullable_str}{comment_str}" class ListType(ComplexType): - def __init__(self, elementType: Type, elementNullable: bool): + def __init__(self, element_type: Type, element_nullable: bool): """ Create a new ListType with the given element type and the type is nullable. - @param elementType: The element type of the list. - @param elementNullable: Whether the element of the list is nullable. + @param element_type: The element type of the list. + @param element_nullable: Whether the element of the list is nullable. """ - if elementType is None: - raise ValueError("elementType cannot be null") - self._elementType = elementType - self._elementNullable = elementNullable + if element_type is None: + raise ValueError("element_type cannot be null") + self._element_type = element_type + self._element_nullable = element_nullable @classmethod - def nullable(cls, elementType: Type): + def nullable(cls, element_type: Type): """ Create a new ListType with the given element type and the type is nullable. - @param elementType: The element type of the list. + @param element_type: The element type of the list. @return: A new ListType instance. """ - return cls.of(elementType, True) + return cls.of(element_type, True) @classmethod - def notNull(cls, elementType: Type): + def not_null(cls, element_type: Type): """ Create a new ListType with the given element type. - @param elementType: The element type of the list. + @param element_type: The element type of the list. @return: A new ListType instance. """ - return cls.of(elementType, False) + return cls.of(element_type, False) @classmethod - def of(cls, elementType: Type, elementNullable: bool): + def of(cls, element_type: Type, element_nullable: bool): """ Create a new ListType with the given element type and whether the element is nullable. - @param elementType: The element type of the list. - @param elementNullable: Whether the element of the list is nullable. + @param element_type: The element type of the list. + @param element_nullable: Whether the element of the list is nullable. @return: A new ListType instance. """ - return cls(elementType, elementNullable) + return cls(element_type, element_nullable) - def elementType(self) -> Type: - """ - @return: The element type of the list. - """ - return self._elementType + def element_type(self) -> Type: + return self._element_type - def elementNullable(self) -> bool: - """ - @return: Whether the element of the list is nullable. - """ - return self._elementNullable + def element_nullable(self) -> bool: + return self._element_nullable def name(self) -> Name: - """ - @return: The name of the list type. - """ return Name.LIST - def simpleString(self) -> str: - """ - @return: A readable string representation of the list type. - """ + def simple_string(self) -> str: return ( - f"list<{self._elementType.simpleString()}>" - if self._elementNullable - else f"list<{self._elementType.simpleString()}, NOT NULL>" + f"list<{self._element_type.simple_string()}>" + if self._element_nullable + else f"list<{self._element_type.simple_string()}, NOT NULL>" ) def __eq__(self, other): @@ -1009,96 +691,74 @@ def __eq__(self, other): if not isinstance(other, Types.ListType): return False return ( - self._elementNullable == other._elementNullable - and self._elementType == other._elementType + self._element_nullable == other._element_nullable + and self._element_type == other._element_type ) def __hash__(self): - """ - @return: A hash code for the ListType based on its element type and nullability. - """ - return hash((self._elementType, self._elementNullable)) - - """ The map type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + return hash((self._element_type, self._element_nullable)) class MapType(ComplexType): - def __init__(self, keyType: Type, valueType: Type, valueNullable: bool): + def __init__(self, key_type: Type, value_type: Type, value_nullable: bool): """ Create a new MapType with the given key type, value type and the value is nullable. - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @param valueNullable: Whether the value of the map is nullable. + @param key_type: The key type of the map. + @param value_type: The value type of the map. + @param value_nullable: Whether the value of the map is nullable. """ - self._keyType = keyType - self._valueType = valueType - self._valueNullable = valueNullable + self._key_type = key_type + self._value_type = value_type + self._value_nullable = value_nullable @classmethod - def valueNullable(cls, keyType: Type, valueType: Type): + def value_nullable(cls, key_type: Type, value_type: Type): """ Create a new MapType with the given key type, value type, and the value is nullable. - @param keyType: The key type of the map. - @param valueType: The value type of the map. + @param key_type: The key type of the map. + @param value_type: The value type of the map. @return: A new MapType instance. """ - return cls.of(keyType, valueType, True) + return cls.of(key_type, value_type, True) @classmethod - def valueNotNull(cls, keyType: Type, valueType: Type): + def value_not_null(cls, key_type: Type, value_type: Type): """ Create a new MapType with the given key type, value type, and the value is not nullable. - @param keyType: The key type of the map. - @param valueType: The value type of the map. + @param key_type: The key type of the map. + @param value_type: The value type of the map. @return: A new MapType instance. """ - return cls.of(keyType, valueType, False) + return cls.of(key_type, value_type, False) @classmethod - def of(cls, keyType: Type, valueType: Type, valueNullable: bool): + def of(cls, key_type: Type, value_type: Type, value_nullable: bool): """ Create a new MapType with the given key type, value type, and whether the value is nullable. - @param keyType: The key type of the map. - @param valueType: The value type of the map. - @param valueNullable: Whether the value of the map is nullable. + @param key_type: The key type of the map. + @param value_type: The value type of the map. + @param value_nullable: Whether the value of the map is nullable. @return: A new MapType instance. """ - return cls(keyType, valueType, valueNullable) + return cls(key_type, value_type, value_nullable) - def keyType(self) -> Type: - """ - @return: The key type of the map. - """ - return self._keyType + def key_type(self) -> Type: + return self._key_type - def valueType(self) -> Type: - """ - @return: The value type of the map. - """ - return self._valueType + def value_type(self) -> Type: + return self._value_type - def valueNullable(self) -> bool: - """ - @return: Whether the value of the map is nullable. - """ - return self._valueNullable + def is_value_nullable(self) -> bool: + return self._value_nullable def name(self) -> Name: - """ - @return: The name of the map type. - """ return Name.MAP - def simpleString(self) -> str: - """ - @return: A readable string representation of the map type. - """ - return ( - f"map<{self._keyType.simpleString()}, {self._valueType.simpleString()}>" - ) + def simple_string(self) -> str: + return f"map<{self._key_type.simple_string()}, {self._value_type.simple_string()}>" def __eq__(self, other): """ @@ -1110,18 +770,13 @@ def __eq__(self, other): if not isinstance(other, Types.MapType): return False return ( - self._valueNullable == other._valueNullable - and self._keyType == other._keyType - and self._valueType == other._valueType + self._value_nullable == other._value_nullable + and self._key_type == other._key_type + and self._value_type == other._value_type ) def __hash__(self): - """ - @return: A hash code for the MapType based on its key type, value type, and nullability. - """ - return hash((self._keyType, self._valueType, self._valueNullable)) - - """ The union type in Gravitino. Note, this type is not supported in the current version of Gravitino. """ + return hash((self._key_type, self._value_type, self._value_nullable)) class UnionType(ComplexType): def __init__(self, types: list): @@ -1143,22 +798,13 @@ def of(cls, *types: Type): return cls(types) def types(self) -> list: - """ - @return: The types of the union. - """ return self._types def name(self) -> Name: - """ - @return: The name of the union type. - """ return Name.UNION - def simpleString(self) -> str: - """ - @return: A readable string representation of the union type. - """ - return f"union<{', '.join(t.simpleString() for t in self._types)}>" + def simple_string(self) -> str: + return f"union<{', '.join(t.simple_string() for t in self._types)}>" def __eq__(self, other): """ @@ -1172,111 +818,80 @@ def __eq__(self, other): return self._types == other._types def __hash__(self): - """ - @return: A hash code for the UnionType based on its types. - """ return hash(tuple(self._types)) - """ Represents a type that is not parsed yet. The parsed type is represented by other types of Types. """ - class UnparsedType(Type): - def __init__(self, unparsedType: str): + def __init__(self, unparsed_type: str): """ - Initializes an UnparsedType instance. + Initializes an unparsed_type instance. - @param unparsedType: The unparsed type as a string. + @param unparsed_type: The unparsed type as a string. """ - self._unparsedType = unparsedType + self._unparsed_type = unparsed_type @classmethod - def of(cls, unparsedType: str): + def of(cls, unparsed_type: str): """ - Creates a new UnparsedType with the given unparsed type. + Creates a new unparsed_type with the given unparsed type. - @param unparsedType: The unparsed type. - @return: A new UnparsedType instance. + @param unparsed_type: The unparsed type. + @return: A new unparsed_type instance. """ - return cls(unparsedType) + return cls(unparsed_type) - def unparsedType(self) -> str: - """ - @return: The unparsed type as a string. - """ - return self._unparsedType + def unparsed_type(self) -> str: + return self._unparsed_type def name(self) -> Name: - """ - @return: The name of the unparsed type. - """ return Name.UNPARSED - def simpleString(self) -> str: - """ - @return: A readable string representation of the unparsed type. - """ - return f"unparsed({self._unparsedType})" + def simple_string(self) -> str: + return f"unparsed({self._unparsed_type})" def __eq__(self, other): """ - Compares two UnparsedType objects for equality. + Compares two unparsed_type objects for equality. - @param other: The other UnparsedType object to compare with. - @return: True if both UnparsedType objects have the same unparsed type string, False otherwise. + @param other: The other unparsed_type object to compare with. + @return: True if both unparsed_type objects have the same unparsed type string, False otherwise. """ - if not isinstance(other, Types.UnparsedType): + if not isinstance(other, Types.unparsed_type): return False - return self._unparsedType == other._unparsedType + return self._unparsed_type == other._unparsed_type def __hash__(self): - """ - @return: A hash code for the UnparsedType based on its unparsed type string. - """ - return hash(self._unparsedType) + return hash(self._unparsed_type) def __str__(self): - """ - @return: The unparsed type string representation. - """ - return self._unparsedType - - """ Represents a type that is defined in an external catalog. """ + return self._unparsed_type class ExternalType(Type): - def __init__(self, catalogString: str): + def __init__(self, catalog_string: str): """ Initializes an ExternalType instance. - @param catalogString: The string representation of this type in the catalog. + @param catalog_string: The string representation of this type in the catalog. """ - self._catalogString = catalogString + self._catalog_string = catalog_string @classmethod - def of(cls, catalogString: str): + def of(cls, catalog_string: str): """ Creates a new ExternalType with the given catalog string. - @param catalogString: The string representation of this type in the catalog. + @param catalog_string: The string representation of this type in the catalog. @return: A new ExternalType instance. """ - return cls(catalogString) + return cls(catalog_string) - def catalogString(self) -> str: - """ - @return: The string representation of this type in external catalog. - """ - return self._catalogString + def catalog_string(self) -> str: + return self._catalog_string def name(self) -> Name: - """ - @return: The name of the external type. - """ return Name.EXTERNAL - def simpleString(self) -> str: - """ - @return: A readable string representation of the external type. - """ - return f"external({self._catalogString})" + def simple_string(self) -> str: + return f"external({self._catalog_string})" def __eq__(self, other): """ @@ -1287,26 +902,20 @@ def __eq__(self, other): """ if not isinstance(other, Types.ExternalType): return False - return self._catalogString == other._catalogString + return self._catalog_string == other._catalog_string def __hash__(self): - """ - @return: A hash code for the ExternalType based on its catalog string. - """ - return hash(self._catalogString) + return hash(self._catalog_string) def __str__(self): - """ - @return: The string representation of the external type. - """ - return self.simpleString() + return self.simple_string() @staticmethod - def allowAutoIncrement(dataType: Type) -> bool: + def allow_auto_increment(data_type: Type) -> bool: """ Checks if the given data type is allowed to be an auto-increment column. - @param dataType: The data type to check. + @param data_type: The data type to check. @return: True if the given data type is allowed to be an auto-increment column, False otherwise. """ - return isinstance(dataType, (Types.IntegerType, Types.LongType)) + return isinstance(data_type, (Types.IntegerType, Types.LongType)) From 8a513a7df2773a8066036d8bfecbf541dabf64d0 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Wed, 30 Oct 2024 23:00:10 -0500 Subject: [PATCH 08/15] Update the unite test --- .../tests/unittests/test_types.py | 283 +++++++++--------- 1 file changed, 146 insertions(+), 137 deletions(-) diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py index f4210c6314a..549fd8759ba 100644 --- a/clients/client-python/tests/unittests/test_types.py +++ b/clients/client-python/tests/unittests/test_types.py @@ -21,163 +21,172 @@ class TestTypes(unittest.TestCase): - # Test NullType singleton and methods def test_null_type(self): - null_type_1 = Types.NullType.get() - null_type_2 = Types.NullType.get() + instance = Types.NullType.get() + self.assertIsInstance(instance, Types.NullType) + self.assertEqual(instance.name(), Name.NULL) + self.assertEqual(instance.simple_string(), "null") + self.assertIs(instance, Types.NullType.get()) # Singleton check - self.assertIs( - null_type_1, - null_type_2, - "NullType should return the same singleton instance", - ) - self.assertEqual(null_type_1.name(), Name.NULL) - self.assertEqual(null_type_1.simpleString(), "null") - - # Test BooleanType singleton and methods def test_boolean_type(self): - boolean_type_1 = Types.BooleanType.get() - boolean_type_2 = Types.BooleanType.get() - - self.assertIs( - boolean_type_1, - boolean_type_2, - "BooleanType should return the same singleton instance", - ) - self.assertEqual(boolean_type_1.name(), Name.BOOLEAN) - self.assertEqual(boolean_type_1.simpleString(), "boolean") + instance = Types.BooleanType.get() + self.assertIsInstance(instance, Types.BooleanType) + self.assertEqual(instance.name(), Name.BOOLEAN) + self.assertEqual(instance.simple_string(), "boolean") + self.assertIs(instance, Types.BooleanType.get()) # Singleton check - # Test ByteType for signed and unsigned versions def test_byte_type(self): - signed_byte = Types.ByteType.get() - unsigned_byte = Types.ByteType.unsigned() + signed_instance = Types.ByteType.get() + unsigned_instance = Types.ByteType.unsigned() + self.assertIsInstance(signed_instance, Types.ByteType) + self.assertEqual(signed_instance.name(), Name.BYTE) + self.assertEqual(signed_instance.simple_string(), "byte") + self.assertEqual(unsigned_instance.simple_string(), "byte unsigned") - self.assertEqual(signed_byte.name(), Name.BYTE) - self.assertEqual(signed_byte.simpleString(), "byte") - self.assertEqual(unsigned_byte.simpleString(), "byte unsigned") - - # Test ShortType for signed and unsigned versions def test_short_type(self): - signed_short = Types.ShortType.get() - unsigned_short = Types.ShortType.unsigned() - - self.assertEqual(signed_short.name(), Name.SHORT) - self.assertEqual(signed_short.simpleString(), "short") - self.assertEqual(unsigned_short.simpleString(), "short unsigned") + signed_instance = Types.ShortType.get() + unsigned_instance = Types.ShortType.unsigned() + self.assertIsInstance(signed_instance, Types.ShortType) + self.assertEqual(signed_instance.simple_string(), "short") + self.assertEqual(unsigned_instance.simple_string(), "short unsigned") - # Test IntegerType for signed and unsigned versions def test_integer_type(self): - signed_int = Types.IntegerType.get() - unsigned_int = Types.IntegerType.unsigned() - - self.assertEqual(signed_int.name(), Name.INTEGER) - self.assertEqual(signed_int.simpleString(), "integer") - self.assertEqual(unsigned_int.simpleString(), "integer unsigned") + signed_instance = Types.IntegerType.get() + unsigned_instance = Types.IntegerType.unsigned() + self.assertIsInstance(signed_instance, Types.IntegerType) + self.assertEqual(signed_instance.simple_string(), "integer") + self.assertEqual(unsigned_instance.simple_string(), "integer unsigned") + + def test_long_type(self): + signed_instance = Types.LongType.get() + unsigned_instance = Types.LongType.unsigned() + self.assertIsInstance(signed_instance, Types.LongType) + self.assertEqual(signed_instance.simple_string(), "long") + self.assertEqual(unsigned_instance.simple_string(), "long unsigned") + + def test_float_type(self): + instance = Types.FloatType.get() + self.assertEqual(instance.name(), Name.FLOAT) + self.assertEqual(instance.simple_string(), "float") + + def test_double_type(self): + instance = Types.DoubleType.get() + self.assertEqual(instance.name(), Name.DOUBLE) + self.assertEqual(instance.simple_string(), "double") - # Test DecimalType and its methods def test_decimal_type(self): - decimal_type_1 = Types.DecimalType.of(10, 2) - decimal_type_2 = Types.DecimalType.of(10, 2) - decimal_type_3 = Types.DecimalType.of(12, 3) + instance = Types.DecimalType.of(10, 2) + self.assertEqual(instance.name(), Name.DECIMAL) + self.assertEqual(instance.precision(), 10) + self.assertEqual(instance.scale(), 2) + self.assertEqual(instance.simple_string(), "decimal(10,2)") + with self.assertRaises(ValueError): + Types.DecimalType.of(39, 2) # Precision out of range + with self.assertRaises(ValueError): + Types.DecimalType.of(10, 11) # Scale out of range - self.assertEqual(decimal_type_1.name(), Name.DECIMAL) - self.assertEqual(decimal_type_1.simpleString(), "decimal(10,2)") - self.assertEqual( - decimal_type_1, - decimal_type_2, - "Decimal types with the same precision and scale should be equal", - ) - self.assertNotEqual( - decimal_type_1, - decimal_type_3, - "Decimal types with different precision/scale should not be equal", - ) - self.assertNotEqual( - hash(decimal_type_1), - hash(decimal_type_3), - "Different decimal types should have different hash codes", - ) - - # Test DateType singleton and methods def test_date_type(self): - date_type_1 = Types.DateType.get() - date_type_2 = Types.DateType.get() - - self.assertIs( - date_type_1, - date_type_2, - "DateType should return the same singleton instance", - ) - self.assertEqual(date_type_1.name(), Name.DATE) - self.assertEqual(date_type_1.simpleString(), "date") + instance = Types.DateType.get() + self.assertEqual(instance.name(), Name.DATE) + self.assertEqual(instance.simple_string(), "date") - # Test TimeType singleton and methods def test_time_type(self): - time_type_1 = Types.TimeType.get() - time_type_2 = Types.TimeType.get() + instance = Types.TimeType.get() + self.assertEqual(instance.name(), Name.TIME) + self.assertEqual(instance.simple_string(), "time") + + def test_timestamp_type(self): + instance_with_tz = Types.TimestampType.with_time_zone() + instance_without_tz = Types.TimestampType.without_time_zone() + self.assertTrue(instance_with_tz.has_time_zone()) + self.assertFalse(instance_without_tz.has_time_zone()) + self.assertEqual(instance_with_tz.simple_string(), "timestamp_tz") + self.assertEqual(instance_without_tz.simple_string(), "timestamp") + + def test_interval_types(self): + year_instance = Types.IntervalYearType.get() + day_instance = Types.IntervalDayType.get() + self.assertEqual(year_instance.name(), Name.INTERVAL_YEAR) + self.assertEqual(day_instance.name(), Name.INTERVAL_DAY) + self.assertEqual(year_instance.simple_string(), "interval_year") + self.assertEqual(day_instance.simple_string(), "interval_day") - self.assertIs( - time_type_1, - time_type_2, - "TimeType should return the same singleton instance", - ) - self.assertEqual(time_type_1.name(), Name.TIME) - self.assertEqual(time_type_1.simpleString(), "time") - - # Test StringType singleton and methods def test_string_type(self): - string_type_1 = Types.StringType.get() - string_type_2 = Types.StringType.get() - - self.assertIs( - string_type_1, - string_type_2, - "StringType should return the same singleton instance", - ) - self.assertEqual(string_type_1.name(), Name.STRING) - self.assertEqual(string_type_1.simpleString(), "string") + instance = Types.StringType.get() + self.assertEqual(instance.name(), Name.STRING) + self.assertEqual(instance.simple_string(), "string") - # Test UUIDType singleton and methods def test_uuid_type(self): - uuid_type_1 = Types.UUIDType.get() - uuid_type_2 = Types.UUIDType.get() + instance = Types.UUIDType.get() + self.assertEqual(instance.name(), Name.UUID) + self.assertEqual(instance.simple_string(), "uuid") - self.assertIs( - uuid_type_1, - uuid_type_2, - "UUIDType should return the same singleton instance", - ) - self.assertEqual(uuid_type_1.name(), Name.UUID) - self.assertEqual(uuid_type_1.simpleString(), "uuid") - - # Test FixedType creation and equality def test_fixed_type(self): - fixed_type_1 = Types.FixedType.of(16) - fixed_type_2 = Types.FixedType.of(16) - fixed_type_3 = Types.FixedType.of(32) - - self.assertEqual(fixed_type_1.name(), Name.FIXED) - self.assertEqual(fixed_type_1.simpleString(), "fixed(16)") - self.assertEqual(fixed_type_1, fixed_type_2) - self.assertNotEqual(fixed_type_1, fixed_type_3) + instance = Types.FixedType.of(5) + self.assertEqual(instance.name(), Name.FIXED) + self.assertEqual(instance.length(), 5) + self.assertEqual(instance.simple_string(), "fixed(5)") - # Test VarCharType creation and equality def test_varchar_type(self): - varchar_type_1 = Types.VarCharType.of(255) - varchar_type_2 = Types.VarCharType.of(255) - varchar_type_3 = Types.VarCharType.of(512) - - self.assertEqual(varchar_type_1.name(), Name.VARCHAR) - self.assertEqual(varchar_type_1.simpleString(), "varchar(255)") - self.assertEqual(varchar_type_1, varchar_type_2) - self.assertNotEqual(varchar_type_1, varchar_type_3) - - # Test allowAutoIncrement method for IntegerType and LongType - def test_allow_auto_increment(self): - integer_type = Types.IntegerType.get() - long_type = Types.LongType.get() - boolean_type = Types.BooleanType.get() - - self.assertTrue(Types.allowAutoIncrement(integer_type)) - self.assertTrue(Types.allowAutoIncrement(long_type)) - self.assertFalse(Types.allowAutoIncrement(boolean_type)) + instance = Types.VarCharType.of(10) + self.assertEqual(instance.name(), Name.VARCHAR) + self.assertEqual(instance.length(), 10) + self.assertEqual(instance.simple_string(), "varchar(10)") + + def test_fixed_char_type(self): + instance = Types.FixedCharType.of(3) + self.assertEqual(instance.name(), Name.FIXEDCHAR) + self.assertEqual(instance.length(), 3) + self.assertEqual(instance.simple_string(), "char(3)") + + def test_binary_type(self): + instance = Types.BinaryType.get() + self.assertEqual(instance.name(), Name.BINARY) + self.assertEqual(instance.simple_string(), "binary") + + def test_struct_type(self): + field1 = Types.StructType.Field( + "name", Types.StringType.get(), True, "User's name" + ) + field2 = Types.StructType.Field( + "age", Types.IntegerType.get(), False, "User's age" + ) + struct = Types.StructType.of(field1, field2) + self.assertEqual( + struct.simple_string(), + "struct", + ) + + def test_list_type(self): + instance = Types.ListType.of(Types.StringType.get(), True) + self.assertEqual(instance.name(), Name.LIST) + self.assertTrue(instance.element_nullable()) + self.assertEqual(instance.simple_string(), "list") + + def test_map_type(self): + instance = Types.MapType.of( + Types.StringType.get(), Types.IntegerType.get(), True + ) + self.assertEqual(instance.name(), Name.MAP) + self.assertTrue(instance.is_value_nullable()) + self.assertEqual(instance.simple_string(), "map") + + def test_union_type(self): + instance = Types.UnionType.of(Types.StringType.get(), Types.IntegerType.get()) + self.assertEqual(instance.name(), Name.UNION) + self.assertEqual(instance.simple_string(), "union") + + def test_unparsed_type(self): + instance = Types.UnparsedType.of("custom_type") + self.assertEqual(instance.name(), Name.UNPARSED) + self.assertEqual(instance.simple_string(), "unparsed(custom_type)") + + def test_external_type(self): + instance = Types.ExternalType.of("external_type") + self.assertEqual(instance.name(), Name.EXTERNAL) + self.assertEqual(instance.simple_string(), "external(external_type)") + + def test_auto_increment_check(self): + self.assertTrue(Types.allow_auto_increment(Types.IntegerType.get())) + self.assertTrue(Types.allow_auto_increment(Types.LongType.get())) + self.assertFalse(Types.allow_auto_increment(Types.StringType.get())) From cc02a5487ab635373110454ed5f57fb4c0c9bfcd Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Thu, 31 Oct 2024 01:13:45 -0500 Subject: [PATCH 09/15] Fix the bug --- clients/client-python/gravitino/api/types.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index ebd1cd295a0..25e30bfef46 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -588,8 +588,8 @@ def nullable_field(cls, name, field_type, comment=None): def name(self): return self._name - def type(self): - return self._type + def field_type(self): + return self._field_type def nullable(self): return self._nullable @@ -608,18 +608,18 @@ def __eq__(self, other): return False return ( self._name == other._name - and self._type == other._type + and self._field_type == other._field_type and self._nullable == other._nullable and self._comment == other._comment ) def __hash__(self): - return hash((self._name, self._type, self._nullable)) + return hash((self._name, self._field_type, self._nullable)) def simple_string(self) -> str: nullable_str = "NULL" if self._nullable else "NOT NULL" comment_str = f" COMMENT '{self._comment}'" if self._comment else "" - return f"{self._name}: {self._type.simple_string()} {nullable_str}{comment_str}" + return f"{self._name}: {self._field_type.simple_string()} {nullable_str}{comment_str}" class ListType(ComplexType): def __init__(self, element_type: Type, element_nullable: bool): From fa4f903ead6560e21783ece50ad660506dc21a5e Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Mon, 4 Nov 2024 08:26:19 -0600 Subject: [PATCH 10/15] Convert Decimal.java to decimal.py with unit test --- .../client-python/gravitino/api/decimal.py | 59 +++++++++++++++ .../tests/unittests/test_decimal.py | 74 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 clients/client-python/gravitino/api/decimal.py create mode 100644 clients/client-python/tests/unittests/test_decimal.py diff --git a/clients/client-python/gravitino/api/decimal.py b/clients/client-python/gravitino/api/decimal.py new file mode 100644 index 00000000000..6b0e5b4ba5d --- /dev/null +++ b/clients/client-python/gravitino/api/decimal.py @@ -0,0 +1,59 @@ +from decimal import Decimal as PyDecimal, ROUND_HALF_UP +from typing import Union +from .types import Types + + +class Decimal: + def __init__( + self, value: Union[str, PyDecimal], precision: int = None, scale: int = None + ): + if isinstance(value, str): + value = PyDecimal(value) + + # If precision and scale are not provided, calculate them based on the value + if precision is None: + precision = len(value.as_tuple().digits) + if scale is None: + scale = -value.as_tuple().exponent + + # Validate precision and scale using DecimalType's rules + Types.DecimalType.check_precision_scale(precision, scale) + + if precision < value.adjusted() + 1: + raise ValueError( + f"Precision of value cannot be greater than precision of decimal: {value.adjusted() + 1} > {precision}" + ) + + # Set scale with rounding + self._value = value.quantize( + PyDecimal((0, (1,), -scale)), rounding=ROUND_HALF_UP + ) + self._precision = precision + self._scale = scale + + @classmethod + def of(cls, value: Union[str, PyDecimal], precision: int = None, scale: int = None): + return cls(value, precision, scale) + + @property + def value(self) -> PyDecimal: + return self._value + + @property + def precision(self) -> int: + return self._precision + + @property + def scale(self) -> int: + return self._scale + + def __str__(self) -> str: + return str(self._value) + + def __eq__(self, other) -> bool: + if not isinstance(other, Decimal): + return False + return self._scale == other._scale and self._value == other._value + + def __hash__(self) -> int: + return hash((self._value, self._precision, self._scale)) diff --git a/clients/client-python/tests/unittests/test_decimal.py b/clients/client-python/tests/unittests/test_decimal.py new file mode 100644 index 00000000000..abad4871e26 --- /dev/null +++ b/clients/client-python/tests/unittests/test_decimal.py @@ -0,0 +1,74 @@ +import unittest +from decimal import Decimal as PyDecimal, InvalidOperation +from gravitino.api.decimal import Decimal + + +class TestDecimal(unittest.TestCase): + def test_decimal_creation_with_precision_and_scale(self): + dec = Decimal.of("123.45", 5, 2) + self.assertEqual(dec.value, PyDecimal("123.45")) + self.assertEqual(dec.precision, 5) + self.assertEqual(dec.scale, 2) + + def test_decimal_creation_without_precision_and_scale(self): + # With the new precision calculation, this should yield precision 6 (includes trailing zero) + dec = Decimal.of("123.450") + self.assertEqual(dec.value, PyDecimal("123.450")) + self.assertEqual(dec.precision, 6) + self.assertEqual(dec.scale, 3) + + def test_decimal_precision_limit(self): + # Testing edge cases at precision limits + dec = Decimal.of("1.2345678901234567890123456789012345678", 38, 0) + self.assertEqual(dec.precision, 38) + self.assertEqual(dec.scale, 0) + with self.assertRaises(ValueError): + Decimal.of("1.23456789012345678901234567890123456789", 39, 0) + + def test_decimal_scale_limit(self): + # Testing maximum scale allowed by precision + dec = Decimal.of("0.12345678901234567890123456", 38, 28) + self.assertEqual(dec.precision, 38) + self.assertEqual(dec.scale, 28) + + def test_rounding_behavior(self): + dec = Decimal.of("123.4567", 6, 2) + self.assertEqual(dec.value, PyDecimal("123.46")) # rounded to scale 2 + + def test_invalid_precision_scale_combination(self): + with self.assertRaises(ValueError): + Decimal.of("12.345", 2, 3) # Scale exceeds precision + + def test_equality_and_hashing(self): + dec1 = Decimal.of("123.45", 5, 2) + dec2 = Decimal.of("123.45", 5, 2) + dec3 = Decimal.of("123.450", 6, 3) + self.assertEqual(dec1, dec2) + self.assertNotEqual(dec1, dec3) + self.assertEqual(hash(dec1), hash(dec2)) + + def test_string_representation(self): + dec = Decimal.of("123.45", 5, 2) + self.assertEqual(str(dec), "123.45") + + def test_invalid_value_raises_error(self): + with self.assertRaises(InvalidOperation): + Decimal.of("invalid") + + def test_large_and_small_values(self): + dec_small = Decimal.of("0.00000001", 10, 8) + dec_large = Decimal.of("12345678901234567890", 20, 0) + self.assertEqual(dec_small.value, PyDecimal("0.00000001")) + self.assertEqual(dec_large.value, PyDecimal("12345678901234567890")) + + def test_precision_scale_edge_cases(self): + dec = Decimal.of("1.1", 2, 1) # Adjusted to include trailing zero significance + self.assertEqual(dec.precision, 2) + self.assertEqual(dec.scale, 1) + with self.assertRaises(ValueError): + Decimal.of("1.1", 0, 1) # Invalid precision lower bound + + def test_decimal_with_set_scale(self): + # Test if value is correctly set to required scale with rounding + dec = Decimal.of("123.4", 5, 3) + self.assertEqual(dec.value, PyDecimal("123.400")) From c9ac8ef8cd21b2cece7b1d0b77a9af56f75930e9 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Mon, 4 Nov 2024 20:48:14 -0600 Subject: [PATCH 11/15] Update the license header --- clients/client-python/gravitino/api/decimal.py | 17 +++++++++++++++++ clients/client-python/gravitino/api/type.py | 1 + clients/client-python/gravitino/api/types.py | 1 + .../tests/unittests/test_decimal.py | 17 +++++++++++++++++ .../client-python/tests/unittests/test_types.py | 1 + 5 files changed, 37 insertions(+) diff --git a/clients/client-python/gravitino/api/decimal.py b/clients/client-python/gravitino/api/decimal.py index 6b0e5b4ba5d..276b3fbea68 100644 --- a/clients/client-python/gravitino/api/decimal.py +++ b/clients/client-python/gravitino/api/decimal.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + from decimal import Decimal as PyDecimal, ROUND_HALF_UP from typing import Union from .types import Types diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index 4d077dab44f..a6d312c2a53 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + from abc import ABC, abstractmethod from enum import Enum diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index 25e30bfef46..86197287b28 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + from .type import ( Type, Name, diff --git a/clients/client-python/tests/unittests/test_decimal.py b/clients/client-python/tests/unittests/test_decimal.py index abad4871e26..fb563e6a2a4 100644 --- a/clients/client-python/tests/unittests/test_decimal.py +++ b/clients/client-python/tests/unittests/test_decimal.py @@ -1,3 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + import unittest from decimal import Decimal as PyDecimal, InvalidOperation from gravitino.api.decimal import Decimal diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py index 549fd8759ba..977c25df5bf 100644 --- a/clients/client-python/tests/unittests/test_types.py +++ b/clients/client-python/tests/unittests/test_types.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + import unittest from gravitino.api.types import Types, Name From dce34c2055c5789c2322ecf0763fb88415ef8022 Mon Sep 17 00:00:00 2001 From: YUN SUN Date: Mon, 4 Nov 2024 20:58:11 -0600 Subject: [PATCH 12/15] Delete the change for decimal.py , we don't need it --- .../client-python/gravitino/api/decimal.py | 76 ---------------- .../tests/unittests/test_decimal.py | 91 ------------------- 2 files changed, 167 deletions(-) delete mode 100644 clients/client-python/gravitino/api/decimal.py delete mode 100644 clients/client-python/tests/unittests/test_decimal.py diff --git a/clients/client-python/gravitino/api/decimal.py b/clients/client-python/gravitino/api/decimal.py deleted file mode 100644 index 276b3fbea68..00000000000 --- a/clients/client-python/gravitino/api/decimal.py +++ /dev/null @@ -1,76 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -from decimal import Decimal as PyDecimal, ROUND_HALF_UP -from typing import Union -from .types import Types - - -class Decimal: - def __init__( - self, value: Union[str, PyDecimal], precision: int = None, scale: int = None - ): - if isinstance(value, str): - value = PyDecimal(value) - - # If precision and scale are not provided, calculate them based on the value - if precision is None: - precision = len(value.as_tuple().digits) - if scale is None: - scale = -value.as_tuple().exponent - - # Validate precision and scale using DecimalType's rules - Types.DecimalType.check_precision_scale(precision, scale) - - if precision < value.adjusted() + 1: - raise ValueError( - f"Precision of value cannot be greater than precision of decimal: {value.adjusted() + 1} > {precision}" - ) - - # Set scale with rounding - self._value = value.quantize( - PyDecimal((0, (1,), -scale)), rounding=ROUND_HALF_UP - ) - self._precision = precision - self._scale = scale - - @classmethod - def of(cls, value: Union[str, PyDecimal], precision: int = None, scale: int = None): - return cls(value, precision, scale) - - @property - def value(self) -> PyDecimal: - return self._value - - @property - def precision(self) -> int: - return self._precision - - @property - def scale(self) -> int: - return self._scale - - def __str__(self) -> str: - return str(self._value) - - def __eq__(self, other) -> bool: - if not isinstance(other, Decimal): - return False - return self._scale == other._scale and self._value == other._value - - def __hash__(self) -> int: - return hash((self._value, self._precision, self._scale)) diff --git a/clients/client-python/tests/unittests/test_decimal.py b/clients/client-python/tests/unittests/test_decimal.py deleted file mode 100644 index fb563e6a2a4..00000000000 --- a/clients/client-python/tests/unittests/test_decimal.py +++ /dev/null @@ -1,91 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -import unittest -from decimal import Decimal as PyDecimal, InvalidOperation -from gravitino.api.decimal import Decimal - - -class TestDecimal(unittest.TestCase): - def test_decimal_creation_with_precision_and_scale(self): - dec = Decimal.of("123.45", 5, 2) - self.assertEqual(dec.value, PyDecimal("123.45")) - self.assertEqual(dec.precision, 5) - self.assertEqual(dec.scale, 2) - - def test_decimal_creation_without_precision_and_scale(self): - # With the new precision calculation, this should yield precision 6 (includes trailing zero) - dec = Decimal.of("123.450") - self.assertEqual(dec.value, PyDecimal("123.450")) - self.assertEqual(dec.precision, 6) - self.assertEqual(dec.scale, 3) - - def test_decimal_precision_limit(self): - # Testing edge cases at precision limits - dec = Decimal.of("1.2345678901234567890123456789012345678", 38, 0) - self.assertEqual(dec.precision, 38) - self.assertEqual(dec.scale, 0) - with self.assertRaises(ValueError): - Decimal.of("1.23456789012345678901234567890123456789", 39, 0) - - def test_decimal_scale_limit(self): - # Testing maximum scale allowed by precision - dec = Decimal.of("0.12345678901234567890123456", 38, 28) - self.assertEqual(dec.precision, 38) - self.assertEqual(dec.scale, 28) - - def test_rounding_behavior(self): - dec = Decimal.of("123.4567", 6, 2) - self.assertEqual(dec.value, PyDecimal("123.46")) # rounded to scale 2 - - def test_invalid_precision_scale_combination(self): - with self.assertRaises(ValueError): - Decimal.of("12.345", 2, 3) # Scale exceeds precision - - def test_equality_and_hashing(self): - dec1 = Decimal.of("123.45", 5, 2) - dec2 = Decimal.of("123.45", 5, 2) - dec3 = Decimal.of("123.450", 6, 3) - self.assertEqual(dec1, dec2) - self.assertNotEqual(dec1, dec3) - self.assertEqual(hash(dec1), hash(dec2)) - - def test_string_representation(self): - dec = Decimal.of("123.45", 5, 2) - self.assertEqual(str(dec), "123.45") - - def test_invalid_value_raises_error(self): - with self.assertRaises(InvalidOperation): - Decimal.of("invalid") - - def test_large_and_small_values(self): - dec_small = Decimal.of("0.00000001", 10, 8) - dec_large = Decimal.of("12345678901234567890", 20, 0) - self.assertEqual(dec_small.value, PyDecimal("0.00000001")) - self.assertEqual(dec_large.value, PyDecimal("12345678901234567890")) - - def test_precision_scale_edge_cases(self): - dec = Decimal.of("1.1", 2, 1) # Adjusted to include trailing zero significance - self.assertEqual(dec.precision, 2) - self.assertEqual(dec.scale, 1) - with self.assertRaises(ValueError): - Decimal.of("1.1", 0, 1) # Invalid precision lower bound - - def test_decimal_with_set_scale(self): - # Test if value is correctly set to required scale with rounding - dec = Decimal.of("123.4", 5, 3) - self.assertEqual(dec.value, PyDecimal("123.400")) From aae67d2248ad94e641205a4faf6e92a713b9f67c Mon Sep 17 00:00:00 2001 From: Xun Date: Tue, 5 Nov 2024 21:35:05 +0800 Subject: [PATCH 13/15] improve code --- clients/client-python/gravitino/api/type.py | 6 +- clients/client-python/gravitino/api/types.py | 730 +++++++++++------- .../tests/unittests/test_gvfs_with_local.py | 1 + .../tests/unittests/test_types.py | 56 +- 4 files changed, 491 insertions(+), 302 deletions(-) diff --git a/clients/client-python/gravitino/api/type.py b/clients/client-python/gravitino/api/type.py index a6d312c2a53..9b089ea1873 100644 --- a/clients/client-python/gravitino/api/type.py +++ b/clients/client-python/gravitino/api/type.py @@ -161,8 +161,11 @@ class ComplexType(Type, ABC): pass -# Define IntegralType class class IntegralType(NumericType, ABC): + """Base class for all integral types.""" + + _signed: bool + def __init__(self, signed: bool): self._signed = signed @@ -171,7 +174,6 @@ def signed(self) -> bool: return self._signed -# Define FractionType class class FractionType(NumericType, ABC): """Base class for all fractional types.""" diff --git a/clients/client-python/gravitino/api/types.py b/clients/client-python/gravitino/api/types.py index 86197287b28..b82ac2b6844 100644 --- a/clients/client-python/gravitino/api/types.py +++ b/clients/client-python/gravitino/api/types.py @@ -14,7 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - +# pylint: disable=C0302 +from __future__ import annotations +from typing import List from .type import ( Type, Name, @@ -28,16 +30,22 @@ class Types: + """The helper class for Type. It contains all built-in types and provides utility methods.""" class NullType(Type): - _instance = None + """The data type representing `NULL` values.""" - @classmethod - def get(cls): + _instance: "NullType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.NullType, cls).__new__(cls) return cls._instance + @classmethod + def get(cls) -> "NullType": + return cls() + def name(self) -> Name: return Name.NULL @@ -45,14 +53,19 @@ def simple_string(self) -> str: return "null" class BooleanType(PrimitiveType): - _instance = None + """The boolean type in Gravitino.""" - @classmethod - def get(cls): + _instance: "BooleanType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.BooleanType, cls).__new__(cls) return cls._instance + @classmethod + def get(cls) -> "BooleanType": + return cls() + def name(self) -> Name: return Name.BOOLEAN @@ -60,24 +73,29 @@ def simple_string(self) -> str: return "boolean" class ByteType(IntegralType): - _instance = None - _unsigned_instance = None + """The byte type in Gravitino.""" + + _instance: "ByteType" = None + _unsigned_instance: "ByteType" = None - def __init__(self, signed: bool): - super().__init__(signed) - self._signed = signed + def __new__(cls, signed: bool = True): + if signed: + if cls._instance is None: + cls._instance = super(Types.ByteType, cls).__new__(cls) + cls._instance.__init__(signed) + return cls._instance + if cls._unsigned_instance is None: + cls._unsigned_instance = super(Types.ByteType, cls).__new__(cls) + cls._unsigned_instance.__init__(signed) + return cls._unsigned_instance @classmethod - def get(cls): - if cls._instance is None: - cls._instance = cls(True) - return cls._instance + def get(cls) -> "ByteType": + return cls(True) @classmethod - def unsigned(cls): - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance + def unsigned(cls) -> "ByteType": + return cls(False) def name(self) -> Name: return Name.BYTE @@ -86,24 +104,27 @@ def simple_string(self) -> str: return "byte" if self.signed() else "byte unsigned" class ShortType(IntegralType): - _instance = None - _unsigned_instance = None - - def __init__(self, signed: bool): - super().__init__(signed) - self._signed = signed + _instance: "ShortType" = None + _unsigned_instance: "ShortType" = None + + def __new__(cls, signed=True): + if signed: + if cls._instance is None: + cls._instance = super(Types.ShortType, cls).__new__(cls) + cls._instance.__init__(signed) + return cls._instance + if cls._unsigned_instance is None: + cls._unsigned_instance = super(Types.ShortType, cls).__new__(cls) + cls._unsigned_instance.__init__(signed) + return cls._unsigned_instance @classmethod - def get(cls): - if cls._instance is None: - cls._instance = cls(True) - return cls._instance + def get(cls) -> "ShortType": + return cls(True) @classmethod def unsigned(cls): - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance + return cls(False) def name(self) -> Name: return Name.SHORT @@ -112,24 +133,27 @@ def simple_string(self) -> str: return "short" if self.signed() else "short unsigned" class IntegerType(IntegralType): - _instance = None - _unsigned_instance = None - - def __init__(self, signed: bool): - super().__init__(signed) - self._signed = signed + _instance: "IntegerType" = None + _unsigned_instance: "IntegerType" = None + + def __new__(cls, signed=True): + if signed: + if cls._instance is None: + cls._instance = super(Types.IntegerType, cls).__new__(cls) + cls._instance.__init__(signed) + return cls._instance + if cls._unsigned_instance is None: + cls._unsigned_instance = super(Types.IntegerType, cls).__new__(cls) + cls._unsigned_instance.__init__(signed) + return cls._unsigned_instance @classmethod - def get(cls): - if cls._instance is None: - cls._instance = cls(True) - return cls._instance + def get(cls) -> "IntegerType": + return cls(True) @classmethod def unsigned(cls): - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance + return cls(False) def name(self) -> Name: return Name.INTEGER @@ -138,24 +162,27 @@ def simple_string(self) -> str: return "integer" if self.signed() else "integer unsigned" class LongType(IntegralType): - _instance = None - _unsigned_instance = None - - def __init__(self, signed: bool): - super().__init__(signed) - self._signed = signed + _instance: "LongType" = None + _unsigned_instance: "LongType" = None + + def __new__(cls, signed=True): + if signed: + if cls._instance is None: + cls._instance = super(Types.LongType, cls).__new__(cls) + cls._instance.__init__(signed) + return cls._instance + if cls._unsigned_instance is None: + cls._unsigned_instance = super(Types.LongType, cls).__new__(cls) + cls._unsigned_instance.__init__(signed) + return cls._unsigned_instance @classmethod - def get(cls): - if cls._instance is None: - cls._instance = cls(True) - return cls._instance + def get(cls) -> "LongType": + return cls(True) @classmethod def unsigned(cls): - if cls._unsigned_instance is None: - cls._unsigned_instance = cls(False) - return cls._unsigned_instance + return cls(False) def name(self) -> Name: return Name.LONG @@ -164,14 +191,18 @@ def simple_string(self) -> str: return "long" if self.signed() else "long unsigned" class FloatType(FractionType): - _instance = None + _instance: "FloatType" = None - @classmethod - def get(cls): + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.FloatType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "FloatType": + return cls() + def name(self) -> Name: return Name.FLOAT @@ -179,14 +210,18 @@ def simple_string(self) -> str: return "float" class DoubleType(FractionType): - _instance = None + _instance: "DoubleType" = None - @classmethod - def get(cls): + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.DoubleType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "DoubleType": + return cls() + def name(self) -> Name: return Name.DOUBLE @@ -194,14 +229,17 @@ def simple_string(self) -> str: return "double" class DecimalType(FractionType): - @classmethod - def of(cls, precision: int, scale: int): - return cls(precision, scale) + """The decimal type in Gravitino.""" + + MAX_PRECISION = 38 + _precision: int + _scale: int def __init__(self, precision: int, scale: int): """ - @param precision: The precision of the decimal type. - @param scale: The scale of the decimal type. + Args: + precision: The precision of the decimal type. + scale: The scale of the decimal type. """ super().__init__() self.check_precision_scale(precision, scale) @@ -212,10 +250,12 @@ def __init__(self, precision: int, scale: int): def check_precision_scale(precision: int, scale: int): """ Ensures the precision and scale values are within valid range. - @param precision: The precision of the decimal. - @param scale: The scale of the decimal. + + Args: + precision: The precision of the decimal. + scale: The scale of the decimal. """ - if not 1 <= precision <= 38: + if not 1 <= precision <= Types.DecimalType.MAX_PRECISION: raise ValueError( f"Decimal precision must be in range [1, 38]: {precision}" ) @@ -224,6 +264,10 @@ def check_precision_scale(precision: int, scale: int): f"Decimal scale must be in range [0, precision ({precision})]: {scale}" ) + @classmethod + def of(cls, precision: int, scale: int) -> "DecimalType": + return cls(precision, scale) + def name(self) -> Name: return Name.DECIMAL @@ -240,8 +284,11 @@ def __eq__(self, other): """ Compares two DecimalType objects for equality. - @param other: The other DecimalType to compare with. - @return: True if both objects have the same precision and scale, False otherwise. + Args: + other: The other DecimalType to compare with. + + Returns: + True if both objects have the same precision and scale, False otherwise. """ if not isinstance(other, Types.DecimalType): return False @@ -251,14 +298,20 @@ def __hash__(self): return hash((self._precision, self._scale)) class DateType(DateTimeType): - _instance = None + """The date time type in Gravitino.""" - @classmethod - def get(cls): + _instance: "DateType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.DateType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "DateType": + return cls() + def name(self) -> Name: return Name.DATE @@ -266,14 +319,18 @@ def simple_string(self) -> str: return "date" class TimeType(DateTimeType): - _instance = None + _instance: "TimeType" = None - @classmethod - def get(cls): + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.TimeType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "TimeType": + return cls() + def name(self) -> Name: return Name.TIME @@ -281,23 +338,32 @@ def simple_string(self) -> str: return "time" class TimestampType(DateTimeType): - _instance_with_tz = None - _instance_without_tz = None + _instance_with_tz: "TimestampType" = None + _instance_without_tz: "TimestampType" = None + _with_time_zone: bool + + def __new__(cls, with_time_zone: bool): + if with_time_zone: + if cls._instance_with_tz is None: + cls._instance_with_tz = super(Types.TimestampType, cls).__new__(cls) + cls._instance_with_tz.__init__(with_time_zone) + return cls._instance_with_tz + if cls._instance_without_tz is None: + cls._instance_without_tz = super(Types.TimestampType, cls).__new__(cls) + cls._instance_without_tz.__init__(with_time_zone) + return cls._instance_without_tz @classmethod - def with_time_zone(cls): - if cls._instance_with_tz is None: - cls._instance_with_tz = cls(True) - return cls._instance_with_tz + def with_time_zone(cls) -> "TimestampType": + return cls(True) @classmethod - def without_time_zone(cls): - if cls._instance_without_tz is None: - cls._instance_without_tz = cls(False) - return cls._instance_without_tz + def without_time_zone(cls) -> "TimestampType": + return cls(False) def __init__(self, with_time_zone: bool): self._with_time_zone = with_time_zone + super().__init__() def has_time_zone(self) -> bool: return self._with_time_zone @@ -309,14 +375,20 @@ def simple_string(self) -> str: return "timestamp_tz" if self._with_time_zone else "timestamp" class IntervalYearType(IntervalType): - _instance = None + """The interval year type in Gravitino.""" - @classmethod - def get(cls): + _instance: "IntervalYearType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.IntervalYearType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "IntervalYearType": + return cls() + def name(self) -> Name: return Name.INTERVAL_YEAR @@ -324,14 +396,20 @@ def simple_string(self) -> str: return "interval_year" class IntervalDayType(IntervalType): - _instance = None + """The interval day type in Gravitino.""" - @classmethod - def get(cls): + _instance: "IntervalDayType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.IntervalDayType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "IntervalDayType": + return cls() + def name(self) -> Name: return Name.INTERVAL_DAY @@ -339,14 +417,21 @@ def simple_string(self) -> str: return "interval_day" class StringType(PrimitiveType): - _instance = None + """The string type in Gravitino, equivalent to varchar(MAX), + which the MAX is determined by the underlying catalog.""" - @classmethod - def get(cls): + _instance: "StringType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.StringType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "StringType": + return cls() + def name(self) -> Name: return Name.STRING @@ -354,14 +439,20 @@ def simple_string(self) -> str: return "string" class UUIDType(PrimitiveType): - _instance = None + """The uuid type in Gravitino.""" - @classmethod - def get(cls): + _instance: "UUIDType" = None + + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.UUIDType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "UUIDType": + return cls() + def name(self) -> Name: return Name.UUID @@ -369,19 +460,28 @@ def simple_string(self) -> str: return "uuid" class FixedType(PrimitiveType): + """Fixed-length byte array type, if you want to use variable-length + byte array, use BinaryType instead.""" + + _length: int + def __init__(self, length: int): """ Initializes the FixedType with the given length. - @param length: The length of the fixed type. + Args: + length: The length of the fixed type. """ self._length = length @classmethod - def of(cls, length: int): + def of(cls, length: int) -> "FixedType": """ - @param length: The length of the fixed type. - @return: A FixedType instance with the given length. + Args: + length: The length of the fixed type. + + Returns: + A FixedType instance with the given length. """ return cls(length) @@ -398,8 +498,11 @@ def __eq__(self, other): """ Compares two FixedType objects for equality. - @param other: The other FixedType object to compare with. - @return: True if both FixedType objects have the same length, False otherwise. + Args: + other: The other FixedType object to compare with. + + Returns: + True if both FixedType objects have the same length, False otherwise. """ if not isinstance(other, Types.FixedType): return False @@ -409,20 +512,15 @@ def __hash__(self): return hash(self._length) class VarCharType(PrimitiveType): - def __init__(self, length: int): - """ - Initializes the VarCharType with the given length. + """The varchar type in Gravitino.""" - @param length: The length of the varchar type. - """ + _length: int + + def __init__(self, length: int): self._length = length @classmethod - def of(cls, length: int): - """ - @param length: The length of the varchar type. - @return: A VarCharType instance with the given length. - """ + def of(cls, length: int) -> "VarCharType": return cls(length) def name(self) -> Name: @@ -438,31 +536,29 @@ def __eq__(self, other): """ Compares two VarCharType objects for equality. - @param other: The other VarCharType object to compare with. - @return: True if both VarCharType objects have the same length, False otherwise. + Args: + other: The other VarCharType object to compare with. + + Returns: + True if both VarCharType objects have the same length, False otherwise. """ - if not isinstance(other, Types.VarCharType): - return False - return self._length == other._length + if isinstance(other, Types.VarCharType): + return self._length == other._length + return False def __hash__(self): return hash(self._length) class FixedCharType(PrimitiveType): - def __init__(self, length: int): - """ - Initializes the FixedCharType with the given length. + """The fixed char type in Gravitino.""" - @param length: The length of the fixed char type. - """ + _length: int + + def __init__(self, length: int): self._length = length @classmethod - def of(cls, length: int): - """ - @param length: The length of the fixed char type. - @return: A FixedCharType instance with the given length. - """ + def of(cls, length: int) -> "FixedCharType": return cls(length) def name(self) -> Name: @@ -475,12 +571,6 @@ def simple_string(self) -> str: return f"char({self._length})" def __eq__(self, other): - """ - Compares two FixedCharType objects for equality. - - @param other: The other FixedCharType object to compare with. - @return: True if both FixedCharType objects have the same length, False otherwise. - """ if not isinstance(other, Types.FixedCharType): return False return self._length == other._length @@ -489,14 +579,18 @@ def __hash__(self): return hash(self._length) class BinaryType(PrimitiveType): - _instance = None + _instance: "BinaryType" = None - @classmethod - def get(cls): + def __new__(cls): if cls._instance is None: - cls._instance = cls() + cls._instance = super(Types.BinaryType, cls).__new__(cls) + cls._instance.__init__() return cls._instance + @classmethod + def get(cls) -> "BinaryType": + return cls() + def name(self) -> Name: return Name.BINARY @@ -504,25 +598,28 @@ def simple_string(self) -> str: return "binary" class StructType(ComplexType): - def __init__(self, fields): - """ - Initializes the StructType with the given fields. + """The struct type in Gravitino. + Note, this type is not supported in the current version of Gravitino.""" - @param fields: The fields of the struct type. - """ + _fields: List["Field"] + + def __init__(self, fields: List["Field"]): if not fields or len(fields) == 0: raise ValueError("fields cannot be null or empty") self._fields = fields @classmethod - def of(cls, *fields): + def of(cls, *fields) -> "StructType": """ - @param fields: The fields of the struct type. - @return: A StructType instance with the given fields. + Args: + fields: The fields of the struct type. + + Returns: + A StructType instance with the given fields. """ return cls(fields) - def fields(self): + def fields(self) -> List["Field"]: return self._fields def name(self) -> Name: @@ -537,60 +634,78 @@ def __eq__(self, other): """ Compares two StructType objects for equality. - @param other: The other StructType object to compare with. - @return: True if both StructType objects have the same fields, False otherwise. + Args: + other: The other StructType object to compare with. + + Returns: + True if both StructType objects have the same fields, False otherwise. """ - if not isinstance(other, Types.StructType): - return False - return self._fields == other._fields + if isinstance(other, Types.StructType): + return self._fields == other._fields + return False def __hash__(self): return hash(tuple(self._fields)) class Field: - def __init__(self, name, field_type, nullable, comment=None): + _name: str + _type: Type + _nullable: bool + _comment: str + + def __init__( + self, name: str, field_type: Type, nullable: bool, comment: str + ) -> None: """ Initializes the Field with the given name, type, nullable flag, and comment. - @param name: The name of the field. - @param field_type: The type of the field. - @param nullable: Whether the field is nullable. - @param comment: The comment of the field (optional). + Args: + name: The name of the field. + field_type: The type of the field. + nullable: Whether the field is nullable. + comment: The comment of the field (optional). """ if name is None: raise ValueError("name cannot be null") if type is None: raise ValueError("type cannot be null") self._name = name - self._field_type = field_type + self._type = field_type self._nullable = nullable self._comment = comment @classmethod - def not_null_field(cls, name, field_type, comment=None): + def not_null_field( + cls, name: str, field_type: Type, comment: str = None + ) -> "Field": """ - @param name: The name of the field. - @param field_type: The type of the field. - @param comment: The comment of the field. - @return: A NOT NULL Field instance with the given name, field_type, and comment. + Args: + name: The name of the field. + field_type: The type of the field. + comment: The comment of the field (optional). """ return cls(name, field_type, False, comment) @classmethod - def nullable_field(cls, name, field_type, comment=None): + def nullable_field( + cls, name: str, field_type: Type, comment: str = None + ) -> "Field": """ - @param name: The name of the field. - @param field_type: The type of the field. - @param comment: The comment of the field. - @return: A nullable Field instance with the given name, field_type, and comment. + Args: + name: The name of the field. + field_type: The type of the field. + comment: The comment of the field (optional). + + Returns: + A nullable Field instance with the given name, field_type, and comment. """ return cls(name, field_type, True, comment) def name(self): return self._name - def field_type(self): - return self._field_type + def type(self): + return self._type def nullable(self): return self._nullable @@ -602,33 +717,42 @@ def __eq__(self, other): """ Compares two Field objects for equality. - @param other: The other Field object to compare with. - @return: True if both Field objects have the same attributes, False otherwise. + Args: + other: The other Field object to compare with. + + Returns: + True if both Field objects have the same attributes, False otherwise. """ - if not isinstance(other, Types.StructType.Field): - return False - return ( - self._name == other._name - and self._field_type == other._field_type - and self._nullable == other._nullable - and self._comment == other._comment - ) + if isinstance(other, Types.StructType.Field): + return ( + self._name == other._name + and self._type == other._type + and self._nullable == other._nullable + and self._comment == other._comment + ) + return False def __hash__(self): - return hash((self._name, self._field_type, self._nullable)) + return hash((self._name, self._type, self._nullable)) def simple_string(self) -> str: nullable_str = "NULL" if self._nullable else "NOT NULL" comment_str = f" COMMENT '{self._comment}'" if self._comment else "" - return f"{self._name}: {self._field_type.simple_string()} {nullable_str}{comment_str}" + return f"{self._name}: {self._type.simple_string()} {nullable_str}{comment_str}" class ListType(ComplexType): + """A list type. Note, this type is not supported in the current version of Gravitino.""" + + _element_type: Type + _element_nullable: bool + def __init__(self, element_type: Type, element_nullable: bool): """ Create a new ListType with the given element type and the type is nullable. - @param element_type: The element type of the list. - @param element_nullable: Whether the element of the list is nullable. + Args: + element_type: The element type of the list. + element_nullable: Whether the element of the list is nullable. """ if element_type is None: raise ValueError("element_type cannot be null") @@ -636,33 +760,42 @@ def __init__(self, element_type: Type, element_nullable: bool): self._element_nullable = element_nullable @classmethod - def nullable(cls, element_type: Type): + def nullable(cls, element_type: Type) -> "ListType": """ Create a new ListType with the given element type and the type is nullable. - @param element_type: The element type of the list. - @return: A new ListType instance. + Args: + element_type: The element type of the list. + + Returns: + A new ListType instance. """ return cls.of(element_type, True) @classmethod - def not_null(cls, element_type: Type): + def not_null(cls, element_type: Type) -> "ListType": """ Create a new ListType with the given element type. - @param element_type: The element type of the list. - @return: A new ListType instance. + Args: + element_type: The element type of the list. + + Returns: + A new ListType instance. """ return cls.of(element_type, False) @classmethod - def of(cls, element_type: Type, element_nullable: bool): + def of(cls, element_type: Type, element_nullable: bool) -> "ListType": """ Create a new ListType with the given element type and whether the element is nullable. - @param element_type: The element type of the list. - @param element_nullable: Whether the element of the list is nullable. - @return: A new ListType instance. + Args: + element_type: The element type of the list. + element_nullable: Whether the element of the list is nullable. + + Returns + A new ListType instance. """ return cls(element_type, element_nullable) @@ -683,66 +816,78 @@ def simple_string(self) -> str: ) def __eq__(self, other): - """ - Compares two ListType objects for equality. - - @param other: The other ListType object to compare with. - @return: True if both ListType objects have the same element type and nullability, False otherwise. - """ if not isinstance(other, Types.ListType): - return False - return ( - self._element_nullable == other._element_nullable - and self._element_type == other._element_type - ) + return ( + self._element_nullable == other.element_nullable() + and self._element_type == other.element_type() + ) + return False def __hash__(self): return hash((self._element_type, self._element_nullable)) class MapType(ComplexType): + """The map type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" + + _key_type: Type + _value_type: Type + _value_nullable: bool + def __init__(self, key_type: Type, value_type: Type, value_nullable: bool): """ Create a new MapType with the given key type, value type and the value is nullable. - @param key_type: The key type of the map. - @param value_type: The value type of the map. - @param value_nullable: Whether the value of the map is nullable. + Args: + key_type: The key type of the map. + value_type: The value type of the map. + value_nullable: Whether the value of the map is nullable. """ self._key_type = key_type self._value_type = value_type self._value_nullable = value_nullable @classmethod - def value_nullable(cls, key_type: Type, value_type: Type): + def value_nullable(cls, key_type: Type, value_type: Type) -> "MapType": """ Create a new MapType with the given key type, value type, and the value is nullable. - @param key_type: The key type of the map. - @param value_type: The value type of the map. - @return: A new MapType instance. + Args: + key_type: The key type of the map. + value_type: The value type of the map. + + Returns: + A new MapType instance. """ return cls.of(key_type, value_type, True) @classmethod - def value_not_null(cls, key_type: Type, value_type: Type): + def value_not_null(cls, key_type: Type, value_type: Type) -> "MapType": """ Create a new MapType with the given key type, value type, and the value is not nullable. - @param key_type: The key type of the map. - @param value_type: The value type of the map. - @return: A new MapType instance. + Args: + key_type: The key type of the map. + value_type: The value type of the map. + + Returns: + A new MapType instance. """ return cls.of(key_type, value_type, False) @classmethod - def of(cls, key_type: Type, value_type: Type, value_nullable: bool): + def of( + cls, key_type: Type, value_type: Type, value_nullable: bool + ) -> "MapType": """ Create a new MapType with the given key type, value type, and whether the value is nullable. - @param key_type: The key type of the map. - @param value_type: The value type of the map. - @param value_nullable: Whether the value of the map is nullable. - @return: A new MapType instance. + Args: + key_type: The key type of the map. + value_type: The value type of the map. + value_nullable: Whether the value of the map is nullable. + + Returns: + A new MapType instance. """ return cls(key_type, value_type, value_nullable) @@ -765,38 +910,49 @@ def __eq__(self, other): """ Compares two MapType objects for equality. - @param other: The other MapType object to compare with. - @return: True if both MapType objects have the same key type, value type, and nullability, False otherwise. + Args: + other The other MapType object to compare with. + + Returns: + True if both MapType objects have the same key type, value type, and nullability, False otherwise. """ - if not isinstance(other, Types.MapType): - return False - return ( - self._value_nullable == other._value_nullable - and self._key_type == other._key_type - and self._value_type == other._value_type - ) + if isinstance(other, Types.MapType): + return ( + self._value_nullable == other._value_nullable + and self._key_type == other._key_type + and self._value_type == other._value_type + ) + return False def __hash__(self): return hash((self._key_type, self._value_type, self._value_nullable)) class UnionType(ComplexType): - def __init__(self, types: list): + """The union type in Gravitino. Note, this type is not supported in the current version of Gravitino.""" + + _types: list[Type] + + def __init__(self, types: list[Type]): """ Create a new UnionType with the given types. - @param types: The types of the union. + Args: + types The types of the union. """ self._types = types @classmethod - def of(cls, *types: Type): + def of(cls, *types: Type) -> "UnionType": """ Create a new UnionType with the given types. - @param types: The types of the union. - @return: A new UnionType instance. + Args: + types: The types of the union. + + Returns: + A new UnionType instance. """ - return cls(types) + return Types.UnionType(list(types)) def types(self) -> list: return self._types @@ -811,32 +967,43 @@ def __eq__(self, other): """ Compares two UnionType objects for equality. - @param other: The other UnionType object to compare with. - @return: True if both UnionType objects have the same types, False otherwise. + Args: + other The other UnionType object to compare with. + + Returns: + True if both UnionType objects have the same types, False otherwise. """ if not isinstance(other, Types.UnionType): - return False - return self._types == other._types + return self._types == other.types() + return False def __hash__(self): return hash(tuple(self._types)) class UnparsedType(Type): + """Represents a type that is not parsed yet. The parsed type is represented by other types of types.""" + + _unparsed_type: str + def __init__(self, unparsed_type: str): """ Initializes an unparsed_type instance. - @param unparsed_type: The unparsed type as a string. + Args: + unparsed_type: The unparsed type as a string. """ self._unparsed_type = unparsed_type @classmethod - def of(cls, unparsed_type: str): + def of(cls, unparsed_type: str) -> "UnparsedType": """ Creates a new unparsed_type with the given unparsed type. - @param unparsed_type: The unparsed type. - @return: A new unparsed_type instance. + Args: + unparsed_type The unparsed type. + + Returns + A new unparsed_type instance. """ return cls(unparsed_type) @@ -853,12 +1020,15 @@ def __eq__(self, other): """ Compares two unparsed_type objects for equality. - @param other: The other unparsed_type object to compare with. - @return: True if both unparsed_type objects have the same unparsed type string, False otherwise. + Args: + other: The other unparsed_type object to compare with. + + Returns: + True if both unparsed_type objects have the same unparsed type string, False otherwise. """ - if not isinstance(other, Types.unparsed_type): - return False - return self._unparsed_type == other._unparsed_type + if not isinstance(other, Types.UnparsedType): + return self._unparsed_type == other.unparsed_type() + return False def __hash__(self): return hash(self._unparsed_type) @@ -867,21 +1037,29 @@ def __str__(self): return self._unparsed_type class ExternalType(Type): + """Represents a type that is defined in an external catalog.""" + + _catalog_string: str + def __init__(self, catalog_string: str): """ Initializes an ExternalType instance. - @param catalog_string: The string representation of this type in the catalog. + Args: + catalog_string The string representation of this type in the catalog. """ self._catalog_string = catalog_string @classmethod - def of(cls, catalog_string: str): + def of(cls, catalog_string: str) -> "ExternalType": """ Creates a new ExternalType with the given catalog string. - @param catalog_string: The string representation of this type in the catalog. - @return: A new ExternalType instance. + Args: + catalog_string The string representation of this type in the catalog. + + Returns: + A new ExternalType instance. """ return cls(catalog_string) @@ -898,8 +1076,11 @@ def __eq__(self, other): """ Compares two ExternalType objects for equality. - @param other: The other ExternalType object to compare with. - @return: True if both ExternalType objects have the same catalog string, False otherwise. + Args: + other: The other ExternalType object to compare with. + + Returns: + True if both ExternalType objects have the same catalog string, False otherwise. """ if not isinstance(other, Types.ExternalType): return False @@ -916,7 +1097,10 @@ def allow_auto_increment(data_type: Type) -> bool: """ Checks if the given data type is allowed to be an auto-increment column. - @param data_type: The data type to check. - @return: True if the given data type is allowed to be an auto-increment column, False otherwise. + Args: + data_type The data type to check. + + Returns: + True if the given data type is allowed to be an auto-increment column, False otherwise. """ return isinstance(data_type, (Types.IntegerType, Types.LongType)) diff --git a/clients/client-python/tests/unittests/test_gvfs_with_local.py b/clients/client-python/tests/unittests/test_gvfs_with_local.py index b4ce39e571a..6e8e2050253 100644 --- a/clients/client-python/tests/unittests/test_gvfs_with_local.py +++ b/clients/client-python/tests/unittests/test_gvfs_with_local.py @@ -49,6 +49,7 @@ ) +# pylint: disable=C0302 def generate_unique_random_string(length): characters = string.ascii_letters + string.digits random_string = "".join(random.sample(characters, length)) diff --git a/clients/client-python/tests/unittests/test_types.py b/clients/client-python/tests/unittests/test_types.py index 977c25df5bf..e241b420acc 100644 --- a/clients/client-python/tests/unittests/test_types.py +++ b/clients/client-python/tests/unittests/test_types.py @@ -23,21 +23,21 @@ class TestTypes(unittest.TestCase): def test_null_type(self): - instance = Types.NullType.get() + instance: Types.NullType = Types.NullType.get() self.assertIsInstance(instance, Types.NullType) self.assertEqual(instance.name(), Name.NULL) self.assertEqual(instance.simple_string(), "null") self.assertIs(instance, Types.NullType.get()) # Singleton check def test_boolean_type(self): - instance = Types.BooleanType.get() + instance: Types.BooleanType = Types.BooleanType.get() self.assertIsInstance(instance, Types.BooleanType) self.assertEqual(instance.name(), Name.BOOLEAN) self.assertEqual(instance.simple_string(), "boolean") self.assertIs(instance, Types.BooleanType.get()) # Singleton check def test_byte_type(self): - signed_instance = Types.ByteType.get() + signed_instance: Types.ByteType = Types.ByteType.get() unsigned_instance = Types.ByteType.unsigned() self.assertIsInstance(signed_instance, Types.ByteType) self.assertEqual(signed_instance.name(), Name.BYTE) @@ -45,38 +45,38 @@ def test_byte_type(self): self.assertEqual(unsigned_instance.simple_string(), "byte unsigned") def test_short_type(self): - signed_instance = Types.ShortType.get() + signed_instance: Types.ShortType = Types.ShortType.get() unsigned_instance = Types.ShortType.unsigned() self.assertIsInstance(signed_instance, Types.ShortType) self.assertEqual(signed_instance.simple_string(), "short") self.assertEqual(unsigned_instance.simple_string(), "short unsigned") def test_integer_type(self): - signed_instance = Types.IntegerType.get() + signed_instance: Types.IntegerType = Types.IntegerType.get() unsigned_instance = Types.IntegerType.unsigned() self.assertIsInstance(signed_instance, Types.IntegerType) self.assertEqual(signed_instance.simple_string(), "integer") self.assertEqual(unsigned_instance.simple_string(), "integer unsigned") def test_long_type(self): - signed_instance = Types.LongType.get() + signed_instance: Types.LongType = Types.LongType.get() unsigned_instance = Types.LongType.unsigned() self.assertIsInstance(signed_instance, Types.LongType) self.assertEqual(signed_instance.simple_string(), "long") self.assertEqual(unsigned_instance.simple_string(), "long unsigned") def test_float_type(self): - instance = Types.FloatType.get() + instance: Types.FloatType = Types.FloatType.get() self.assertEqual(instance.name(), Name.FLOAT) self.assertEqual(instance.simple_string(), "float") def test_double_type(self): - instance = Types.DoubleType.get() + instance: Types.DoubleType = Types.DoubleType.get() self.assertEqual(instance.name(), Name.DOUBLE) self.assertEqual(instance.simple_string(), "double") def test_decimal_type(self): - instance = Types.DecimalType.of(10, 2) + instance: Types.DecimalType = Types.DecimalType.of(10, 2) self.assertEqual(instance.name(), Name.DECIMAL) self.assertEqual(instance.precision(), 10) self.assertEqual(instance.scale(), 2) @@ -87,12 +87,12 @@ def test_decimal_type(self): Types.DecimalType.of(10, 11) # Scale out of range def test_date_type(self): - instance = Types.DateType.get() + instance: Types.DateType = Types.DateType.get() self.assertEqual(instance.name(), Name.DATE) self.assertEqual(instance.simple_string(), "date") def test_time_type(self): - instance = Types.TimeType.get() + instance: Types.TimeType = Types.TimeType.get() self.assertEqual(instance.name(), Name.TIME) self.assertEqual(instance.simple_string(), "time") @@ -105,67 +105,67 @@ def test_timestamp_type(self): self.assertEqual(instance_without_tz.simple_string(), "timestamp") def test_interval_types(self): - year_instance = Types.IntervalYearType.get() - day_instance = Types.IntervalDayType.get() + year_instance: Types.IntervalYearType = Types.IntervalYearType.get() + day_instance: Types.IntervalDayType = Types.IntervalDayType.get() self.assertEqual(year_instance.name(), Name.INTERVAL_YEAR) self.assertEqual(day_instance.name(), Name.INTERVAL_DAY) self.assertEqual(year_instance.simple_string(), "interval_year") self.assertEqual(day_instance.simple_string(), "interval_day") def test_string_type(self): - instance = Types.StringType.get() + instance: Types.StringType = Types.StringType.get() self.assertEqual(instance.name(), Name.STRING) self.assertEqual(instance.simple_string(), "string") def test_uuid_type(self): - instance = Types.UUIDType.get() + instance: Types.UUIDType = Types.UUIDType.get() self.assertEqual(instance.name(), Name.UUID) self.assertEqual(instance.simple_string(), "uuid") def test_fixed_type(self): - instance = Types.FixedType.of(5) + instance: Types.FixedType = Types.FixedType.of(5) self.assertEqual(instance.name(), Name.FIXED) self.assertEqual(instance.length(), 5) self.assertEqual(instance.simple_string(), "fixed(5)") def test_varchar_type(self): - instance = Types.VarCharType.of(10) + instance: Types.VarCharType = Types.VarCharType.of(10) self.assertEqual(instance.name(), Name.VARCHAR) self.assertEqual(instance.length(), 10) self.assertEqual(instance.simple_string(), "varchar(10)") def test_fixed_char_type(self): - instance = Types.FixedCharType.of(3) + instance: Types.FixedCharType = Types.FixedCharType.of(3) self.assertEqual(instance.name(), Name.FIXEDCHAR) self.assertEqual(instance.length(), 3) self.assertEqual(instance.simple_string(), "char(3)") def test_binary_type(self): - instance = Types.BinaryType.get() + instance: Types.BinaryType = Types.BinaryType.get() self.assertEqual(instance.name(), Name.BINARY) self.assertEqual(instance.simple_string(), "binary") def test_struct_type(self): - field1 = Types.StructType.Field( + field1: Types.StructType.Field = Types.StructType.Field( "name", Types.StringType.get(), True, "User's name" ) - field2 = Types.StructType.Field( + field2: Types.StructType.Field = Types.StructType.Field( "age", Types.IntegerType.get(), False, "User's age" ) - struct = Types.StructType.of(field1, field2) + struct: Types.StructType = Types.StructType.of(field1, field2) self.assertEqual( struct.simple_string(), "struct", ) def test_list_type(self): - instance = Types.ListType.of(Types.StringType.get(), True) + instance: Types.ListType = Types.ListType.of(Types.StringType.get(), True) self.assertEqual(instance.name(), Name.LIST) self.assertTrue(instance.element_nullable()) self.assertEqual(instance.simple_string(), "list") def test_map_type(self): - instance = Types.MapType.of( + instance: Types.MapType = Types.MapType.of( Types.StringType.get(), Types.IntegerType.get(), True ) self.assertEqual(instance.name(), Name.MAP) @@ -173,17 +173,19 @@ def test_map_type(self): self.assertEqual(instance.simple_string(), "map") def test_union_type(self): - instance = Types.UnionType.of(Types.StringType.get(), Types.IntegerType.get()) + instance: Types.UnionType = Types.UnionType.of( + Types.StringType.get(), Types.IntegerType.get() + ) self.assertEqual(instance.name(), Name.UNION) self.assertEqual(instance.simple_string(), "union") def test_unparsed_type(self): - instance = Types.UnparsedType.of("custom_type") + instance: Types.UnparsedType = Types.UnparsedType.of("custom_type") self.assertEqual(instance.name(), Name.UNPARSED) self.assertEqual(instance.simple_string(), "unparsed(custom_type)") def test_external_type(self): - instance = Types.ExternalType.of("external_type") + instance: Types.ExternalType = Types.ExternalType.of("external_type") self.assertEqual(instance.name(), Name.EXTERNAL) self.assertEqual(instance.simple_string(), "external(external_type)") From 19d4f00f6d638bb14bc802197c26d401e2fc2541 Mon Sep 17 00:00:00 2001 From: Xun Date: Wed, 6 Nov 2024 21:14:42 +0800 Subject: [PATCH 14/15] update doc --- api/src/test/java/org/apache/gravitino/rel/TestLiteral.java | 2 +- docs/manage-relational-metadata-using-gravitino.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java b/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java index cd71a2f3526..31687ab28a3 100644 --- a/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java +++ b/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java @@ -114,6 +114,6 @@ public void testLiterals() { literal = decimalLiteral(Decimal.of("0.00")); Assertions.assertEquals(Decimal.of(new BigDecimal("0.00")), literal.value()); - Assertions.assertEquals(Types.DecimalType.of(2, 2), literal.dataType()); + Assertions.assertEquals(Types.DecimalType(2, 2), literal.dataType()); } } diff --git a/docs/manage-relational-metadata-using-gravitino.md b/docs/manage-relational-metadata-using-gravitino.md index 6c2a741490c..833c6c00770 100644 --- a/docs/manage-relational-metadata-using-gravitino.md +++ b/docs/manage-relational-metadata-using-gravitino.md @@ -870,7 +870,7 @@ In order to create a table, you need to provide the following information: The following types that Gravitino supports: -| Type | Java | JSON | Description | +| Type | Java / Python | JSON | Description | |---------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Boolean | `Types.BooleanType.get()` | `boolean` | Boolean type | | Byte | `Types.ByteType.get()` | `byte` | Byte type, indicates a numerical value of 1 byte | From f19438d762e71ae349ea07fb596414f4ef6e989f Mon Sep 17 00:00:00 2001 From: Xun Date: Wed, 6 Nov 2024 21:16:27 +0800 Subject: [PATCH 15/15] ci --- api/src/test/java/org/apache/gravitino/rel/TestLiteral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java b/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java index 31687ab28a3..cd71a2f3526 100644 --- a/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java +++ b/api/src/test/java/org/apache/gravitino/rel/TestLiteral.java @@ -114,6 +114,6 @@ public void testLiterals() { literal = decimalLiteral(Decimal.of("0.00")); Assertions.assertEquals(Decimal.of(new BigDecimal("0.00")), literal.value()); - Assertions.assertEquals(Types.DecimalType(2, 2), literal.dataType()); + Assertions.assertEquals(Types.DecimalType.of(2, 2), literal.dataType()); } }