Skip to content

Commit d19f01f

Browse files
Release/sql types (#141)
* 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>
1 parent ab44f90 commit d19f01f

File tree

4 files changed

+55
-51
lines changed

4 files changed

+55
-51
lines changed

pysqlx_engine/_core/param_converter.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ def convert(provider: PROVIDER, value: SupportedValueType, field: str = "") -> U
1212
return "NULL"
1313

1414
elif isinstance(value, AbstractDatabaseType):
15-
return value.convert(provider=provider, field=field)
15+
v = value.convert(provider=provider, field=field)
16+
return "NULL" if v is None else v
17+
1618

1719
elif isinstance(value, Enum):
1820
return try_enum(provider, value, field)

pysqlx_engine/types.py

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,25 @@ class BooleanType(AbstractDatabaseType):
4242
4343
"""
4444

45-
def __init__(self, value: bool):
46-
assert isinstance(value, bool), "value must be a boolean."
45+
def __init__(self, value: Union[bool, None]):
46+
assert isinstance(value, (bool, type(None))), "value must be a boolean."
4747
self.value = value
4848

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

5252

5353
class StringType(AbstractDatabaseType):
5454
"""String type
5555
Database type: char|varchar|text|char|varchar|text|etc
5656
"""
5757

58-
def __init__(self, value: str):
59-
assert isinstance(value, str), "value must be a string."
58+
def __init__(self, value: Union[str, None]):
59+
assert isinstance(value, (str, type(None))), "value must be a string."
6060
self.value = value
6161

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

6565

6666
class NStringType(StringType):
@@ -69,33 +69,33 @@ class NStringType(StringType):
6969
"""
7070

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

7474

7575
class IntegerType(AbstractDatabaseType):
7676
"""Integer type
7777
Database type: int|integer|smallint|bigint|tinyint|etc
7878
"""
7979

80-
def __init__(self, value: int):
81-
assert isinstance(value, int), "value must be an integer."
80+
def __init__(self, value: Union[int, None]):
81+
assert isinstance(value, (int, type(None))), "value must be an integer."
8282
self.value = value
8383

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

8787

8888
class JsonType(AbstractDatabaseType):
8989
"""Json type
9090
Database type: json|jsonb|nvarchar|varchar|string|etc
9191
"""
9292

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

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

100100

101101
class NJsonType(JsonType):
@@ -104,124 +104,124 @@ class NJsonType(JsonType):
104104
"""
105105

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

109109

110110
class UUIDType(AbstractDatabaseType):
111111
"""UUID type
112112
Database type: uuid|varchar|text|nvarchar|etc
113113
"""
114114

115-
def __init__(self, value: UUID):
116-
assert isinstance(value, UUID), "value must be an UUID"
115+
def __init__(self, value: Union[UUID, None]):
116+
assert isinstance(value, (UUID, type(None))), "value must be an UUID"
117117
self.value = value
118118

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

122122

123123
class TimeType(AbstractDatabaseType):
124124
"""Time type
125125
Database type: time|varchar|string|etc
126126
"""
127127

128-
def __init__(self, value: time):
129-
assert isinstance(value, time), "value must be a datetime.time."
128+
def __init__(self, value: Union[time, None]):
129+
assert isinstance(value, (time, type(None))), "value must be a datetime.time."
130130
self.value = value
131131

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

135135

136136
class DateType(AbstractDatabaseType):
137137
"""Date type
138138
Database type: date|varchar|string|etc
139139
"""
140140

141-
def __init__(self, value: date):
142-
assert isinstance(value, date), "value must be a datetime.date."
141+
def __init__(self, value: Union[date, None]):
142+
assert isinstance(value, (date, type(None))), "value must be a datetime.date."
143143
self.value = value
144144

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

148148

149149
class DateTimeType(AbstractDatabaseType):
150150
"""DateTime type
151151
Database type: datetime|timestamp|varchar|string|etc
152152
"""
153153

154-
def __init__(self, value: datetime):
155-
assert isinstance(value, datetime), "value must be a datetime.datetime."
154+
def __init__(self, value: Union[datetime, None]):
155+
assert isinstance(value, (datetime, type(None))), "value must be a datetime.datetime."
156156
self.value = value
157157

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

161161

162162
class FloatType(AbstractDatabaseType):
163163
"""Float type
164164
Database type: float|double|decimal|numeric|real|etc
165165
"""
166166

167-
def __init__(self, value: float):
168-
assert isinstance(value, float), "value must be a float."
167+
def __init__(self, value: Union[float, None]):
168+
assert isinstance(value, (float, type(None))), "value must be a float."
169169
self.value = value
170170

171171
def convert(self, provider: PROVIDER, field: str = "") -> T:
172-
return param.try_float(provider, self.value)
173-
172+
return self.value if self.value is None else param.try_float(provider, self.value)
174173

175174
class BytesType(AbstractDatabaseType):
176175
"""Bytes type
177176
Database type: bytea|blob|varbinary|etc
178177
"""
179178

180-
def __init__(self, value: bytes):
181-
assert isinstance(value, bytes), "value must be a bytes."
179+
def __init__(self, value: Union[bytes, None]):
180+
assert isinstance(value, (bytes, type(None))), "value must be a bytes."
182181
self.value = value
183182

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

187187

188188
class DecimalType(AbstractDatabaseType):
189189
"""Decimal type
190190
Database type: decimal|numeric|money|etc
191191
"""
192192

193-
def __init__(self, value: Decimal):
194-
assert isinstance(value, Decimal), "value must be a float."
193+
def __init__(self, value: Union[Decimal, None]):
194+
assert isinstance(value, (Decimal, type(None))), "value must be a float."
195195
self.value = value
196196

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

200200

201201
class EnumType(AbstractDatabaseType):
202202
"""Enum type
203203
Database type: enum|varchar|text|nvarchar|etc
204204
"""
205205

206-
def __init__(self, value: Enum):
207-
assert isinstance(value, Enum), "value must be a enum.Enum."
206+
def __init__(self, value: Union[Enum, None]):
207+
assert isinstance(value, (Enum, type(None))), "value must be a enum.Enum."
208208
self.value = value
209209

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

213213

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

219-
def __init__(self, value: tuple):
220-
assert isinstance(value, tuple), "value must be a tuple."
219+
def __init__(self, value: Union[tuple, None]):
220+
assert isinstance(value, (tuple, type(None))), "value must be a tuple."
221221
self.value = value
222222

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

226226

227227
class NTupleType(TupleType):
@@ -230,18 +230,17 @@ class NTupleType(TupleType):
230230
"""
231231

232232
def convert(self, provider: PROVIDER, field: str = "") -> T:
233-
return param.try_ntuple(provider, self.value, field)
234-
233+
return self.value if self.value is None else param.try_ntuple(provider, self.value, field)
235234

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

241-
def __init__(self, *value):
242-
assert isinstance(value, tuple), "value must be a tuple."
243-
assert all(isinstance(v, Enum) for v in value), "value must be a tuple of enum.Enum."
240+
def __init__(self, value: Union[tuple, None]):
241+
assert isinstance(value, (tuple, type(None))), "value must be a tuple."
244242
self.value = value
245243

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

tests/unittest/sync/test_default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ class Enum2(Enum):
306306

307307
assert (
308308
param_converter.convert(
309-
provider=provider, value=types.TupleEnumType(Enum2("black"), Enum2("white")), field="xpto"
309+
provider=provider, value=types.TupleEnumType((Enum2("black"), Enum2("white"))), field="xpto"
310310
)
311311
== "'{black,white}'"
312312
)

tests/unittest/sync/test_query.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,8 @@ def test_sample_query_first_with_param_db_pgsql(db: PySQLXEngineSync = db_pgsql)
540540
CAST(:created_at AS TIMESTAMP) AS created_at,
541541
CAST(:updated_at AS TIMESTAMP) AS updated_at,
542542
CAST(:date AS DATE) AS date,
543-
CAST(:tup AS INT[]) AS tup;
543+
CAST(:tup AS INT[]) AS tup,
544+
:tup_none AS tup_none;
544545
"""
545546
parameters = {
546547
"id": 1,
@@ -553,6 +554,7 @@ def test_sample_query_first_with_param_db_pgsql(db: PySQLXEngineSync = db_pgsql)
553554
"updated_at": datetime.fromisoformat("2021-01-01 00:00:00"),
554555
"date": date.fromisoformat("2021-01-01"),
555556
"tup": types.TupleType((1, 2, 3)),
557+
"tup_none": types.TupleType(None),
556558
}
557559

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

571574
conn.close()
572575
assert conn.connected is False

0 commit comments

Comments
 (0)