Skip to content

Commit c9b8d17

Browse files
authored
Merge pull request #16 from henadzit/parameterization-changes
Parameterization support
2 parents def1617 + d196b33 commit c9b8d17

10 files changed

+263
-247
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# ChangeLog
22

3+
## 0.3
4+
5+
## 0.3.0
6+
- Add `Parameterizer`
7+
- Uppdate `Parameter` to be dialect-aware
8+
- Remove `ListParameter`, `DictParameter`, `QmarkParameter`, etc.
9+
- Wrap query's offset and limit with ValueWrapper so they can be parametrized
10+
- Fix a missing whitespace for MSSQL when pagination without ordering is used
11+
312
## 0.2
413

514
### 0.2.2

pypika/__init__.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,16 @@
2121
CustomFunction,
2222
EmptyCriterion,
2323
Field,
24-
FormatParameter,
2524
Index,
2625
Interval,
27-
NamedParameter,
2826
Not,
2927
NullValue,
30-
NumericParameter,
3128
Parameter,
32-
PyformatParameter,
33-
QmarkParameter,
29+
Parameterizer,
3430
Rollup,
3531
SystemTimeValue,
3632
Tuple,
33+
ValueWrapper,
3734
)
3835

3936
NULL = NullValue()

pypika/dialects/mssql.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from __future__ import annotations
22

3-
from typing import Any
3+
from typing import Any, cast
44

55
from pypika.enums import Dialects
66
from pypika.exceptions import QueryException
77
from pypika.queries import Query, QueryBuilder
8+
from pypika.terms import ValueWrapper
89
from pypika.utils import builder
910

1011

@@ -42,25 +43,29 @@ def top(self, value: str | int) -> MSSQLQueryBuilder: # type:ignore[return]
4243
@builder
4344
def fetch_next(self, limit: int) -> MSSQLQueryBuilder: # type:ignore[return]
4445
# Overridden to provide a more domain-specific API for T-SQL users
45-
self._limit = limit
46+
self._limit = cast(ValueWrapper, self.wrap_constant(limit))
4647

47-
def _offset_sql(self) -> str:
48+
def _offset_sql(self, **kwargs) -> str:
4849
order_by = ""
4950
if not self._orderbys:
50-
order_by = "ORDER BY (SELECT 0)"
51-
return order_by + " OFFSET {offset} ROWS".format(offset=self._offset or 0)
51+
order_by = " ORDER BY (SELECT 0)"
52+
return order_by + " OFFSET {offset} ROWS".format(
53+
offset=self._offset.get_sql(**kwargs) if self._offset is not None else 0
54+
)
5255

53-
def _limit_sql(self) -> str:
54-
return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit)
56+
def _limit_sql(self, **kwargs) -> str:
57+
if self._limit is None:
58+
return ""
59+
return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit.get_sql(**kwargs))
5560

56-
def _apply_pagination(self, querystring: str) -> str:
61+
def _apply_pagination(self, querystring: str, **kwargs) -> str:
5762
# Note: Overridden as MSSQL specifies offset before the fetch next limit
5863
if self._limit is not None or self._offset:
5964
# Offset has to be present if fetch next is specified in a MSSQL query
60-
querystring += self._offset_sql()
65+
querystring += self._offset_sql(**kwargs)
6166

6267
if self._limit is not None:
63-
querystring += self._limit_sql()
68+
querystring += self._limit_sql(**kwargs)
6469

6570
return querystring
6671

pypika/dialects/oracle.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ def get_sql(self, *args: Any, **kwargs: Any) -> str:
2828
kwargs["groupby_alias"] = False
2929
return super().get_sql(*args, **kwargs)
3030

31-
def _offset_sql(self) -> str:
32-
return " OFFSET {offset} ROWS".format(offset=self._offset)
33-
34-
def _limit_sql(self) -> str:
35-
return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit)
31+
def _offset_sql(self, **kwargs) -> str:
32+
if self._offset is None:
33+
return ""
34+
return " OFFSET {offset} ROWS".format(offset=self._offset.get_sql(**kwargs))
35+
36+
def _limit_sql(self, **kwargs) -> str:
37+
if self._limit is None:
38+
return ""
39+
return " FETCH NEXT {limit} ROWS ONLY".format(limit=self._limit.get_sql(**kwargs))

pypika/queries.py

+36-32
Original file line numberDiff line numberDiff line change
@@ -535,8 +535,8 @@ def __init__(
535535
self._set_operation = [(set_operation, set_operation_query)]
536536
self._orderbys: list[tuple[Field, Order | None]] = []
537537

538-
self._limit: int | None = None
539-
self._offset: int | None = None
538+
self._limit: ValueWrapper | None = None
539+
self._offset: ValueWrapper | None = None
540540

541541
self._wrapper_cls = wrapper_cls
542542

@@ -553,11 +553,11 @@ def orderby(self, *fields: Field, **kwargs: Any) -> "Self": # type:ignore[retur
553553

554554
@builder
555555
def limit(self, limit: int) -> "Self": # type:ignore[return]
556-
self._limit = limit
556+
self._limit = cast(ValueWrapper, self.wrap_constant(limit))
557557

558558
@builder
559559
def offset(self, offset: int) -> "Self": # type:ignore[return]
560-
self._offset = offset
560+
self._offset = cast(ValueWrapper, self.wrap_constant(offset))
561561

562562
@builder
563563
def union(self, other: Selectable) -> "Self": # type:ignore[return]
@@ -624,11 +624,8 @@ def get_sql(self, with_alias: bool = False, subquery: bool = False, **kwargs: An
624624
if self._orderbys:
625625
querystring += self._orderby_sql(**kwargs)
626626

627-
if self._limit is not None:
628-
querystring += self._limit_sql()
629-
630-
if self._offset:
631-
querystring += self._offset_sql()
627+
querystring += self._limit_sql(**kwargs)
628+
querystring += self._offset_sql(**kwargs)
632629

633630
if subquery:
634631
querystring = "({query})".format(query=querystring, **kwargs)
@@ -668,11 +665,15 @@ def _orderby_sql(self, quote_char: str | None = None, **kwargs: Any) -> str:
668665

669666
return " ORDER BY {orderby}".format(orderby=",".join(clauses))
670667

671-
def _offset_sql(self) -> str:
672-
return " OFFSET {offset}".format(offset=self._offset)
668+
def _offset_sql(self, **kwargs) -> str:
669+
if self._offset is None:
670+
return ""
671+
return " OFFSET {offset}".format(offset=self._offset.get_sql(**kwargs))
673672

674-
def _limit_sql(self) -> str:
675-
return " LIMIT {limit}".format(limit=self._limit)
673+
def _limit_sql(self, **kwargs) -> str:
674+
if self._limit is None:
675+
return ""
676+
return " LIMIT {limit}".format(limit=self._limit.get_sql(**kwargs))
676677

677678

678679
class QueryBuilder(Selectable, Term): # type:ignore[misc]
@@ -725,8 +726,8 @@ def __init__(
725726
self._joins: list[Join] = []
726727
self._unions: list = []
727728

728-
self._limit: int | None = None
729-
self._offset: int | None = None
729+
self._limit: ValueWrapper | None = None
730+
self._offset: ValueWrapper | None = None
730731

731732
self._updates: list[tuple] = []
732733

@@ -1223,11 +1224,11 @@ def hash_join(self, item: Table | "QueryBuilder" | AliasedQuery) -> "Joiner":
12231224

12241225
@builder
12251226
def limit(self, limit: int) -> "Self": # type:ignore[return]
1226-
self._limit = limit
1227+
self._limit = cast(ValueWrapper, self.wrap_constant(limit))
12271228

12281229
@builder
12291230
def offset(self, offset: int) -> "Self": # type:ignore[return]
1230-
self._offset = offset
1231+
self._offset = cast(ValueWrapper, self.wrap_constant(offset))
12311232

12321233
@builder
12331234
def union(self, other: Self) -> _SetOperation:
@@ -1252,7 +1253,8 @@ def minus(self, other: Self) -> _SetOperation:
12521253
@builder
12531254
def set(self, field: Field | str, value: Any) -> "Self": # type:ignore[return]
12541255
field = Field(field) if not isinstance(field, Field) else field
1255-
self._updates.append((field, self._wrapper_cls(value)))
1256+
value = self.wrap_constant(value, wrapper_cls=self._wrapper_cls)
1257+
self._updates.append((field, value))
12561258

12571259
def __add__(self, other: Self) -> _SetOperation: # type:ignore[override]
12581260
return self.union(other)
@@ -1265,8 +1267,10 @@ def __sub__(self, other: Self) -> _SetOperation: # type:ignore[override]
12651267

12661268
@builder
12671269
def slice(self, slice: slice) -> "Self": # type:ignore[return]
1268-
self._offset = slice.start
1269-
self._limit = slice.stop
1270+
if slice.start is not None:
1271+
self._offset = cast(ValueWrapper, self.wrap_constant(slice.start))
1272+
if slice.stop is not None:
1273+
self._limit = cast(ValueWrapper, self.wrap_constant(slice.stop))
12701274

12711275
def __getitem__(self, item: Any) -> Self | Field: # type:ignore[override]
12721276
if not isinstance(item, slice):
@@ -1512,7 +1516,7 @@ def get_sql(self, with_alias: bool = False, subquery: bool = False, **kwargs: An
15121516
if self._orderbys:
15131517
querystring += self._orderby_sql(**kwargs)
15141518

1515-
querystring = self._apply_pagination(querystring)
1519+
querystring = self._apply_pagination(querystring, **kwargs)
15161520

15171521
if self._for_update:
15181522
querystring += self._for_update_sql(**kwargs)
@@ -1532,13 +1536,9 @@ def get_sql(self, with_alias: bool = False, subquery: bool = False, **kwargs: An
15321536

15331537
return querystring
15341538

1535-
def _apply_pagination(self, querystring: str) -> str:
1536-
if self._limit is not None:
1537-
querystring += self._limit_sql()
1538-
1539-
if self._offset:
1540-
querystring += self._offset_sql()
1541-
1539+
def _apply_pagination(self, querystring: str, **kwargs) -> str:
1540+
querystring += self._limit_sql(**kwargs)
1541+
querystring += self._offset_sql(**kwargs)
15421542
return querystring
15431543

15441544
def _with_sql(self, **kwargs: Any) -> str:
@@ -1750,11 +1750,15 @@ def _having_sql(self, quote_char: str | None = None, **kwargs: Any) -> str:
17501750
having = self._havings.get_sql(quote_char=quote_char, **kwargs) # type:ignore[union-attr]
17511751
return f" HAVING {having}"
17521752

1753-
def _offset_sql(self) -> str:
1754-
return " OFFSET {offset}".format(offset=self._offset)
1753+
def _offset_sql(self, **kwargs) -> str:
1754+
if self._offset is None:
1755+
return ""
1756+
return " OFFSET {offset}".format(offset=self._offset.get_sql(**kwargs))
17551757

1756-
def _limit_sql(self) -> str:
1757-
return " LIMIT {limit}".format(limit=self._limit)
1758+
def _limit_sql(self, **kwargs) -> str:
1759+
if self._limit is None:
1760+
return ""
1761+
return " LIMIT {limit}".format(limit=self._limit.get_sql(**kwargs))
17581762

17591763
def _set_sql(self, **kwargs: Any) -> str:
17601764
return " SET {set}".format(

0 commit comments

Comments
 (0)