Skip to content

Commit

Permalink
Release/sql types (#141)
Browse files Browse the repository at this point in the history
* chore: Update makefile for tests-coverage command with database support

* chore: Update pyproject.toml packages formatting

* chore: Update VSCode settings for Python linting and formatting

* feat: Add database types for various data types

* feat: Add PySQLXJsonEnconder class for JSON encoding

* chore: Refactor import statements in pysqlx_engine

* Refactor import statements in pysqlx_engine

* remove benchmark tests

* chore: Update PySQLXEngineSync test_query.py with type casting and tuple parameter

* feat: Add type casting to accept None value

* chore: Update test_query.py with type casting for None value

* Refactor param_converter.py to handle NULL values in type conversion

---------

Co-authored-by: carlos.rian <carlos.rian@quartile.com>
  • Loading branch information
carlos-rian and carlos-rian-qd authored Jun 13, 2024
1 parent ab44f90 commit d19f01f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 51 deletions.
4 changes: 3 additions & 1 deletion pysqlx_engine/_core/param_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ def convert(provider: PROVIDER, value: SupportedValueType, field: str = "") -> U
return "NULL"

elif isinstance(value, AbstractDatabaseType):
return value.convert(provider=provider, field=field)
v = value.convert(provider=provider, field=field)
return "NULL" if v is None else v


elif isinstance(value, Enum):
return try_enum(provider, value, field)
Expand Down
95 changes: 47 additions & 48 deletions pysqlx_engine/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,25 @@ class BooleanType(AbstractDatabaseType):
"""

def __init__(self, value: bool):
assert isinstance(value, bool), "value must be a boolean."
def __init__(self, value: Union[bool, None]):
assert isinstance(value, (bool, type(None))), "value must be a boolean."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_bool(provider, self.value)
return self.value if self.value is None else param.try_bool(provider, self.value)


class StringType(AbstractDatabaseType):
"""String type
Database type: char|varchar|text|char|varchar|text|etc
"""

def __init__(self, value: str):
assert isinstance(value, str), "value must be a string."
def __init__(self, value: Union[str, None]):
assert isinstance(value, (str, type(None))), "value must be a string."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_str(provider, self.value)
return self.value if self.value is None else param.try_str(provider, self.value)


class NStringType(StringType):
Expand All @@ -69,33 +69,33 @@ class NStringType(StringType):
"""

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_nstr(provider, self.value)
return self.value if self.value is None else param.try_nstr(provider, self.value)


class IntegerType(AbstractDatabaseType):
"""Integer type
Database type: int|integer|smallint|bigint|tinyint|etc
"""

def __init__(self, value: int):
assert isinstance(value, int), "value must be an integer."
def __init__(self, value: Union[int, None]):
assert isinstance(value, (int, type(None))), "value must be an integer."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_int(provider, self.value)
return self.value if self.value is None else param.try_int(provider, self.value)


class JsonType(AbstractDatabaseType):
"""Json type
Database type: json|jsonb|nvarchar|varchar|string|etc
"""

def __init__(self, value: Union[dict, list]):
assert isinstance(value, (dict, list)), "value must be a dictionary or list."
def __init__(self, value: Union[dict, list, None]):
assert isinstance(value, (dict, list, type(None))), "value must be a dictionary or list."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_json(provider, self.value)
return self.value if self.value is None else param.try_json(provider, self.value)


class NJsonType(JsonType):
Expand All @@ -104,124 +104,124 @@ class NJsonType(JsonType):
"""

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_njson(provider, self.value)
return self.value if self.value is None else param.try_njson(provider, self.value)


class UUIDType(AbstractDatabaseType):
"""UUID type
Database type: uuid|varchar|text|nvarchar|etc
"""

def __init__(self, value: UUID):
assert isinstance(value, UUID), "value must be an UUID"
def __init__(self, value: Union[UUID, None]):
assert isinstance(value, (UUID, type(None))), "value must be an UUID"
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_uuid(provider, self.value)
return self.value if self.value is None else param.try_uuid(provider, self.value)


class TimeType(AbstractDatabaseType):
"""Time type
Database type: time|varchar|string|etc
"""

def __init__(self, value: time):
assert isinstance(value, time), "value must be a datetime.time."
def __init__(self, value: Union[time, None]):
assert isinstance(value, (time, type(None))), "value must be a datetime.time."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_time(provider, self.value)
return self.value if self.value is None else param.try_time(provider, self.value)


class DateType(AbstractDatabaseType):
"""Date type
Database type: date|varchar|string|etc
"""

def __init__(self, value: date):
assert isinstance(value, date), "value must be a datetime.date."
def __init__(self, value: Union[date, None]):
assert isinstance(value, (date, type(None))), "value must be a datetime.date."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_date(provider, self.value)
return self.value if self.value is None else param.try_date(provider, self.value)


class DateTimeType(AbstractDatabaseType):
"""DateTime type
Database type: datetime|timestamp|varchar|string|etc
"""

def __init__(self, value: datetime):
assert isinstance(value, datetime), "value must be a datetime.datetime."
def __init__(self, value: Union[datetime, None]):
assert isinstance(value, (datetime, type(None))), "value must be a datetime.datetime."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_datetime(provider, self.value)
return self.value if self.value is None else param.try_datetime(provider, self.value)


class FloatType(AbstractDatabaseType):
"""Float type
Database type: float|double|decimal|numeric|real|etc
"""

def __init__(self, value: float):
assert isinstance(value, float), "value must be a float."
def __init__(self, value: Union[float, None]):
assert isinstance(value, (float, type(None))), "value must be a float."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_float(provider, self.value)

return self.value if self.value is None else param.try_float(provider, self.value)

class BytesType(AbstractDatabaseType):
"""Bytes type
Database type: bytea|blob|varbinary|etc
"""

def __init__(self, value: bytes):
assert isinstance(value, bytes), "value must be a bytes."
def __init__(self, value: Union[bytes, None]):
assert isinstance(value, (bytes, type(None))), "value must be a bytes."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_bytes(provider, self.value)
return self.value if self.value is None else param.try_bytes(provider, self.value)



class DecimalType(AbstractDatabaseType):
"""Decimal type
Database type: decimal|numeric|money|etc
"""

def __init__(self, value: Decimal):
assert isinstance(value, Decimal), "value must be a float."
def __init__(self, value: Union[Decimal, None]):
assert isinstance(value, (Decimal, type(None))), "value must be a float."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_decimal(provider, self.value)
return self.value if self.value is None else param.try_decimal(provider, self.value)


class EnumType(AbstractDatabaseType):
"""Enum type
Database type: enum|varchar|text|nvarchar|etc
"""

def __init__(self, value: Enum):
assert isinstance(value, Enum), "value must be a enum.Enum."
def __init__(self, value: Union[Enum, None]):
assert isinstance(value, (Enum, type(None))), "value must be a enum.Enum."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_enum(provider, self.value, field)
return self.value if self.value is None else param.try_enum(provider, self.value, field)


class TupleType(AbstractDatabaseType):
"""Tuple type - Only for PostgreSQL
Database type: array(Postgres Native), another database: error.
"""

def __init__(self, value: tuple):
assert isinstance(value, tuple), "value must be a tuple."
def __init__(self, value: Union[tuple, None]):
assert isinstance(value, (tuple, type(None))), "value must be a tuple."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_tuple(provider, self.value, field)
return self.value if self.value is None else param.try_tuple(provider, self.value, field)


class NTupleType(TupleType):
Expand All @@ -230,18 +230,17 @@ class NTupleType(TupleType):
"""

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_ntuple(provider, self.value, field)

return self.value if self.value is None else param.try_ntuple(provider, self.value, field)

class TupleEnumType(AbstractDatabaseType):
"""Tuple Enum type - Only for PostgreSQL
Database type: array(Postgres Native), another database: error.
"""

def __init__(self, *value):
assert isinstance(value, tuple), "value must be a tuple."
assert all(isinstance(v, Enum) for v in value), "value must be a tuple of enum.Enum."
def __init__(self, value: Union[tuple, None]):
assert isinstance(value, (tuple, type(None))), "value must be a tuple."
self.value = value

def convert(self, provider: PROVIDER, field: str = "") -> T:
return param.try_tuple_enum(provider, self.value, field)
return self.value if self.value is None else param.try_tuple_enum(provider, self.value, field)

2 changes: 1 addition & 1 deletion tests/unittest/sync/test_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ class Enum2(Enum):

assert (
param_converter.convert(
provider=provider, value=types.TupleEnumType(Enum2("black"), Enum2("white")), field="xpto"
provider=provider, value=types.TupleEnumType((Enum2("black"), Enum2("white"))), field="xpto"
)
== "'{black,white}'"
)
5 changes: 4 additions & 1 deletion tests/unittest/sync/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,8 @@ def test_sample_query_first_with_param_db_pgsql(db: PySQLXEngineSync = db_pgsql)
CAST(:created_at AS TIMESTAMP) AS created_at,
CAST(:updated_at AS TIMESTAMP) AS updated_at,
CAST(:date AS DATE) AS date,
CAST(:tup AS INT[]) AS tup;
CAST(:tup AS INT[]) AS tup,
:tup_none AS tup_none;
"""
parameters = {
"id": 1,
Expand All @@ -553,6 +554,7 @@ def test_sample_query_first_with_param_db_pgsql(db: PySQLXEngineSync = db_pgsql)
"updated_at": datetime.fromisoformat("2021-01-01 00:00:00"),
"date": date.fromisoformat("2021-01-01"),
"tup": types.TupleType((1, 2, 3)),
"tup_none": types.TupleType(None),
}

resp = conn.query_first(sql=sql, parameters=parameters)
Expand All @@ -567,6 +569,7 @@ def test_sample_query_first_with_param_db_pgsql(db: PySQLXEngineSync = db_pgsql)
assert isinstance(resp.updated_at, datetime)
assert isinstance(resp.date, (date, datetime))
assert isinstance(resp.tup, tuple)
assert resp.tup_none is None

conn.close()
assert conn.connected is False
Expand Down

0 comments on commit d19f01f

Please # to comment.