Skip to content

Commit e6f21e7

Browse files
authored
Merge pull request #69 from passren/0.7.4
0.7.4
2 parents 4a7ffff + 4172ab1 commit e6f21e7

15 files changed

Lines changed: 538 additions & 55 deletions

.github/workflows/run-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,6 @@ jobs:
8383
with:
8484
fetch-depth: 0
8585
- name: SonarCloud Scan
86-
uses: SonarSource/sonarqube-scan-action@v5.3.1
86+
uses: SonarSource/sonarqube-scan-action@v6.0.0
8787
env:
8888
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

pydynamodb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
if TYPE_CHECKING:
77
from .connection import Connection
88

9-
__version__: str = "0.7.3"
9+
__version__: str = "0.7.4"
1010

1111
# Globals https://www.python.org/dev/peps/pep-0249/#globals
1212
apilevel: str = "2.0"

pydynamodb/sql/common.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ class KeyWords:
9292
SPACE,
9393
LBRACKET,
9494
RBRACKET,
95-
) = map(Suppress, "(),;:.'\"`= []")
95+
LCURLYBRACKET,
96+
RCURLYBRACKET,
97+
) = map(Suppress, "(),;:.'\"`= []{}")
9698
STAR, QUESTION = "*", "?"
9799
SUPPRESS_QUOTE = (SINGLEQUOTE | BACKQUOTE | DOUBLEQUOTE).set_name("suppress_quote")
98100
QUOTE = one_of("' \" `").set_name("quote")
@@ -149,18 +151,23 @@ class KeyWords:
149151
FROM,
150152
WHERE,
151153
INSERT,
154+
INTO,
155+
VALUE,
152156
GLOBAL,
153157
PARTITION_KEY,
154158
SORT_KEY,
155159
HASH,
156160
RANGE,
157161
UPDATE,
162+
SET,
163+
REMOVE,
158164
DELETE,
159165
INDEX,
160166
REPLICA,
161167
ORDER_BY,
162168
LIMIT,
163169
AS,
170+
RETURNING,
164171
) = map(
165172
CaselessKeyword,
166173
[
@@ -172,18 +179,23 @@ class KeyWords:
172179
"FROM",
173180
"WHERE",
174181
"INSERT",
182+
"INTO",
183+
"VALUE",
175184
"GLOBAL",
176185
"PARTITION KEY",
177186
"SORT KEY",
178187
"HASH",
179188
"RANGE",
180189
"UPDATE",
190+
"SET",
191+
"REMOVE",
181192
"DELETE",
182193
"INDEX",
183194
"REPLICA",
184195
"ORDER BY",
185196
"Limit",
186197
"AS",
198+
"RETURNING",
187199
],
188200
)
189201

pydynamodb/sql/dml_delete.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Syntax of PartiQL
4+
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.delete.html
5+
------------------
6+
DELETE FROM table
7+
WHERE condition [RETURNING returnvalues]
8+
<returnvalues> ::= ALL OLD *
9+
10+
Sample SQL of Deleting:
11+
------------------------
12+
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks'
13+
14+
DELETE FROM "Music" WHERE "Artist" = 'Acme Band' AND "SongTitle" = 'PartiQL Rocks' RETURNING ALL OLD *
15+
"""
16+
import logging
17+
from .dml_sql import DmlBase
18+
from .common import KeyWords, Tokens
19+
from pyparsing import (
20+
Forward,
21+
Opt,
22+
)
23+
from typing import Any, Dict
24+
25+
_logger = logging.getLogger(__name__) # type: ignore
26+
27+
28+
class DmlDelete(DmlBase):
29+
30+
_DELETE_STATEMENT = (
31+
KeyWords.DELETE
32+
+ KeyWords.FROM
33+
+ Tokens.TABLE_NAME
34+
+ KeyWords.WHERE
35+
+ DmlBase._WHERE_CONDITIONS
36+
+ Opt(DmlBase._RETURNING_CLAUSE)
37+
)("delete_statement").set_name("delete_statement")
38+
39+
_DML_DELETE_EXPR = Forward()
40+
_DML_DELETE_EXPR <<= _DELETE_STATEMENT
41+
42+
def __init__(self, statement: str) -> None:
43+
super().__init__(statement)
44+
45+
@property
46+
def syntax_def(self) -> Forward:
47+
return DmlDelete._DML_DELETE_EXPR
48+
49+
def transform(self) -> Dict[str, Any]:
50+
table_name_ = self.root_parse_results["table"]
51+
52+
table_ = '"%s"' % table_name_
53+
54+
# Build the statement
55+
statement_ = "DELETE FROM {table} {where_conditions}"
56+
57+
where_conditions = self.root_parse_results.get("where_conditions", None)
58+
if where_conditions is not None:
59+
where_conditions_ = self._construct_where_conditions(where_conditions)
60+
else:
61+
where_conditions_ = ""
62+
63+
statement_ = statement_.format(
64+
table=table_, where_conditions=str(where_conditions_)
65+
)
66+
67+
# Add RETURNING clause if present
68+
if "returning_clause" in self.root_parse_results:
69+
returning = self.root_parse_results["returning_clause"]
70+
return_content = returning.get("return_content", "").strip()
71+
statement_ += " RETURNING %s" % return_content
72+
73+
request = {"Statement": statement_.strip()}
74+
return request

pydynamodb/sql/dml_insert.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Syntax of PartiQL
4+
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.insert.html
5+
------------------
6+
INSERT INTO table VALUE item
7+
8+
Sample SQL of Inserting:
9+
------------------------
10+
INSERT INTO "Music" value {'Artist' : 'Acme Band','SongTitle' : 'PartiQL Rocks'}
11+
"""
12+
import logging
13+
from .dml_sql import DmlBase
14+
from .common import KeyWords, Tokens
15+
from pyparsing import Forward, Group, Regex
16+
from typing import Any, Dict
17+
18+
_logger = logging.getLogger(__name__) # type: ignore
19+
20+
21+
class DmlInsert(DmlBase):
22+
23+
# Parse everything inside curly brackets as a single string
24+
_ITEM = Group(
25+
KeyWords.LCURLYBRACKET
26+
+ Regex(r"[^}]*")("item_content")
27+
+ KeyWords.RCURLYBRACKET
28+
)("item").set_name("item")
29+
30+
_INSERT_STATEMENT = (
31+
KeyWords.INSERT + KeyWords.INTO + Tokens.TABLE_NAME + KeyWords.VALUE + _ITEM
32+
)("insert_statement").set_name("insert_statement")
33+
34+
_DML_INSERT_EXPR = Forward()
35+
_DML_INSERT_EXPR <<= _INSERT_STATEMENT
36+
37+
def __init__(self, statement: str) -> None:
38+
super().__init__(statement)
39+
40+
@property
41+
def syntax_def(self) -> Forward:
42+
return DmlInsert._DML_INSERT_EXPR
43+
44+
def transform(self) -> Dict[str, Any]:
45+
table_name_ = self.root_parse_results["table"]
46+
item_content_ = self.root_parse_results["item"]["item_content"]
47+
48+
table_ = '"%s"' % table_name_
49+
item_ = "{%s}" % item_content_
50+
51+
statement_ = "INSERT INTO {table} VALUE {item}"
52+
statement_ = statement_.format(
53+
table=table_,
54+
item=item_,
55+
)
56+
request = {"Statement": statement_.strip()}
57+
58+
return request

pydynamodb/sql/dml_select.py

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
"""
33
Syntax of PartiQL
4-
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html#ql-reference.select.syntax
4+
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ql-reference.select.html
55
------------------
66
SELECT expression [, ...]
77
FROM table[.index]
@@ -37,8 +37,6 @@
3737
from collections import OrderedDict
3838
from .dml_sql import DmlBase, DmlFunction
3939
from .common import KeyWords, Tokens
40-
from .util import flatten_list
41-
from pyparsing import ParseResults
4240
from pyparsing import Opt, Forward, Group, ZeroOrMore, delimited_list, Regex
4341
from typing import Any, Dict, List, Optional
4442

@@ -147,18 +145,13 @@ class DmlSelect(DmlBase):
147145

148146
def __init__(self, statement: str) -> None:
149147
super().__init__(statement)
150-
self._columns = list()
151-
self._where_conditions = list()
148+
self._columns = []
152149
self._is_star_column = False
153150

154151
@property
155152
def columns(self) -> List[Optional[DmlSelectColumn]]:
156153
return self._columns
157154

158-
@property
159-
def where_conditions(self) -> List[Optional[str]]:
160-
return self._where_conditions
161-
162155
@property
163156
def is_star_column(self) -> bool:
164157
return self._is_star_column
@@ -218,7 +211,7 @@ def _construct_columns(self, columns: List[Any]) -> str:
218211
self._columns.clear()
219212
return "*"
220213

221-
column_ = list()
214+
column_ = []
222215
column_.append(column["column_name"])
223216

224217
for rcolumn in column["column_ops"]:
@@ -249,43 +242,11 @@ def _construct_columns(self, columns: List[Any]) -> str:
249242
columns_ = ",".join(columns_.keys())
250243
return columns_
251244

252-
def _construct_where_conditions(self, where_conditions: List[Any]) -> str:
253-
for condition in where_conditions:
254-
if not isinstance(condition, ParseResults):
255-
self._where_conditions.append(str(condition))
256-
else:
257-
function_ = condition.get("function", None)
258-
function_with_op_ = condition.get("function_with_op", None)
259-
if function_:
260-
flatted_func_params = ",".join(condition["function_params"])
261-
self._where_conditions.append(
262-
"%s(%s)" % (function_, flatted_func_params)
263-
)
264-
elif function_with_op_:
265-
flatted_func_params = ",".join(condition["function_params"])
266-
self._where_conditions.append(
267-
"%s(%s) %s %s"
268-
% (
269-
function_with_op_,
270-
flatted_func_params,
271-
condition["comparison_operators"],
272-
condition["column_rvalue"],
273-
)
274-
)
275-
else:
276-
where_conditions_ = condition.as_list()
277-
flatted_where = " ".join(
278-
str(c) for c in flatten_list(where_conditions_)
279-
)
280-
self._where_conditions.append(flatted_where)
281-
282-
return "WHERE %s" % " ".join(self.where_conditions)
283-
284245
def _construct_raw_options(self, options: List[Any]) -> Optional[List[Any]]:
285246
converted_ = None
286247
for option in options:
287248
if converted_ is None:
288-
converted_ = list()
249+
converted_ = []
289250

290251
converted_.append(" ".join(str(o) for o in option))
291252

@@ -295,7 +256,7 @@ def _construct_options(self, options: List[Any]) -> Optional[Dict[str, Any]]:
295256
converted_ = None
296257
for option in options:
297258
if converted_ is None:
298-
converted_ = dict()
259+
converted_ = {}
299260
option_name = option[0]
300261
option_value = option[1]
301262

pydynamodb/sql/dml_sql.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
import logging
33
from abc import ABCMeta
44
from .base import Base
5-
from typing import Any, Dict, List
5+
from typing import Any, Dict, List, Optional
66
from .common import KeyWords, Tokens
7+
from .util import flatten_list
78
from pyparsing import (
9+
ParseResults,
810
Word,
911
CaselessKeyword,
1012
alphanums,
@@ -19,6 +21,7 @@
1921
infix_notation,
2022
pyparsing_common as ppc,
2123
Combine,
24+
Regex,
2225
)
2326

2427
_logger = logging.getLogger(__name__) # type: ignore
@@ -121,12 +124,21 @@ class DmlBase(Base):
121124
)("option").set_name("option")
122125
)("options").set_name("options")
123126

127+
_RETURNING_CLAUSE = Group(
128+
KeyWords.RETURNING + Regex(r".*")("return_content").set_name("return_content")
129+
)("returning_clause").set_name("returning_clause")
130+
124131
def __init__(self, statement: str) -> None:
125132
super().__init__(statement)
133+
self._where_conditions = []
126134
self._limit = None
127135
self._consistent_read = False
128136
self._return_consumed_capacity = "NONE"
129137

138+
@property
139+
def where_conditions(self) -> List[Optional[str]]:
140+
return self._where_conditions
141+
130142
@property
131143
def limit(self) -> int:
132144
return self._limit
@@ -146,6 +158,38 @@ def syntax_def(self) -> None:
146158
def transform(self) -> Dict[str, Any]:
147159
return {"Statement": self._statement}
148160

161+
def _construct_where_conditions(self, where_conditions: List[Any]) -> str:
162+
for condition in where_conditions:
163+
if not isinstance(condition, ParseResults):
164+
self._where_conditions.append(str(condition))
165+
else:
166+
function_ = condition.get("function", None)
167+
function_with_op_ = condition.get("function_with_op", None)
168+
if function_:
169+
flatted_func_params = ",".join(condition["function_params"])
170+
self._where_conditions.append(
171+
"%s(%s)" % (function_, flatted_func_params)
172+
)
173+
elif function_with_op_:
174+
flatted_func_params = ",".join(condition["function_params"])
175+
self._where_conditions.append(
176+
"%s(%s) %s %s"
177+
% (
178+
function_with_op_,
179+
flatted_func_params,
180+
condition["comparison_operators"],
181+
condition["column_rvalue"],
182+
)
183+
)
184+
else:
185+
where_conditions_ = condition.as_list()
186+
flatted_where = " ".join(
187+
str(c) for c in flatten_list(where_conditions_)
188+
)
189+
self._where_conditions.append(flatted_where)
190+
191+
return "WHERE %s" % " ".join(self.where_conditions)
192+
149193

150194
class DmlFunction(metaclass=ABCMeta):
151195
def __init__(self, name: str, params: List[str] = None) -> None:

0 commit comments

Comments
 (0)