Skip to content

Commit 744e4ee

Browse files
committed
Get rid of the execute_statement API for SQL
We were providing two APIs two execute SQL queries. - `execute` with a string query and parameters - `execute_statement` with `SqlStatement` parameter two have more control over the query execution. We decided to get rid of the `execute_statement` and `SqlStatement` and provide ability to pass keyword arguments to `execute` to customize the behavior of the queries. With that change, the properties of `SqlStatement` is now keyword arguments. Also, upgraded the server version to 5.0 as 5.0 is released.
1 parent d98ffc9 commit 744e4ee

File tree

5 files changed

+162
-265
lines changed

5 files changed

+162
-265
lines changed

examples/sql/sql_example.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import hazelcast
44
from hazelcast.serialization.api import Portable
5-
from hazelcast.sql import SqlStatement
65

76

87
class Customer(Portable):
@@ -93,16 +92,13 @@ def __repr__(self):
9392

9493
print(name, age, is_active)
9594

96-
# Construct a statement object to control the properties of the query
9795
# Special keywords __key and this can be used to refer to key and value.
9896
# Also, a placeholder parameters can be specified
99-
statement = SqlStatement("SELECT __key, age FROM customers WHERE name LIKE ?")
97+
query = "SELECT __key, age FROM customers WHERE name LIKE ?"
10098

101-
# Parameters will replace the placeholders on the server side
102-
statement.add_parameter("Jo%")
103-
statement.timeout = 5
104-
105-
with client.sql.execute_statement(statement).result() as result:
99+
# Parameters will replace the placeholders on the server side.
100+
# Properties of the query can be configured with keyword arguments.
101+
with client.sql.execute(query, "Jo%", timeout=5).result() as result:
106102
# Row metadata can also be retrieved from the result
107103
row_metadata = result.get_row_metadata()
108104

@@ -128,3 +124,5 @@ def __repr__(self):
128124

129125
# Query can be closed explicitly
130126
result.close().result()
127+
128+
client.shutdown()

hazelcast/sql.py

Lines changed: 80 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -81,41 +81,77 @@ class SqlService(object):
8181
def __init__(self, internal_sql_service):
8282
self._service = internal_sql_service
8383

84-
def execute(self, sql, *params):
85-
"""Convenient method to execute a distributed query with the given
86-
parameters.
87-
88-
Converts passed SQL string and parameters into an :class:`SqlStatement`
89-
object and invokes :func:`execute_statement`.
84+
def execute(self, sql, *params, **kwargs):
85+
"""Executes an SQL statement.
9086
9187
Args:
9288
sql (str): SQL string.
93-
*params: Query parameters that will be passed to
94-
:func:`SqlStatement.add_parameter`.
89+
*params: Query parameters that will replaces the placeholders at
90+
the server-side. You may define parameter placeholders in the
91+
query with the ``?`` character. For every placeholder, a
92+
parameter value must be provided.
9593
96-
Returns:
97-
hazelcast.future.Future[SqlResult]: The execution result.
94+
Keyword Args:
95+
cursor_buffer_size (int): The cursor buffer size measured in the
96+
number of rows.
9897
99-
Raises:
100-
HazelcastSqlError: In case of execution error.
101-
AssertionError: If the SQL parameter is not a string.
102-
ValueError: If the SQL parameter is an empty string.
103-
"""
104-
return self._service.execute(sql, *params)
98+
When a statement is submitted for execution, a
99+
:class:`SqlResult` is returned as a result. When rows are ready
100+
to be consumed, they are put into an internal buffer of the
101+
cursor. This parameter defines the maximum number of rows in
102+
that buffer. When the threshold is reached, the backpressure
103+
mechanism will slow down the execution, possibly to a complete
104+
halt, to prevent out-of-memory.
105105
106-
def execute_statement(self, statement):
107-
"""Executes an SQL statement.
106+
Only positive values are allowed.
108107
109-
Args:
110-
statement (SqlStatement): Statement to be executed
108+
The default value is expected to work well for most workloads.
109+
A bigger buffer size may give you a slight performance boost
110+
for queries with large result sets at the cost of increased
111+
memory consumption.
112+
113+
Defaults to ``4096``.
114+
timeout (float or int): The execution timeout in seconds.
115+
116+
If the timeout is reached for a running statement, it will be
117+
cancelled forcefully.
118+
119+
Zero value means no timeout. ``-1`` means that the value from
120+
the server-side config will be used. Other negative values are
121+
prohibited.
122+
123+
Defaults to ``-1``.
124+
expected_result_type (SqlExpectedResultType): The expected result
125+
type.
126+
schema (str or None): The schema name.
127+
128+
The engine will try to resolve the non-qualified object
129+
identifiers from the statement in the given schema. If not
130+
found, the default search path will be used.
131+
132+
The schema name is case sensitive. For example, ``foo`` and
133+
``Foo`` are different schemas.
134+
135+
The default value is ``None`` meaning only the default search
136+
path is used.
111137
112138
Returns:
113139
hazelcast.future.Future[SqlResult]: The execution result.
114140
115141
Raises:
116142
HazelcastSqlError: In case of execution error.
143+
AssertionError: If the ``sql`` parameter is not a string, the
144+
``schema`` is not a string or ``None``, the ``timeout`` is not
145+
an integer or float, or the ``cursor_buffer_size`` is not an
146+
integer.
147+
ValueError: If the ``sql`` parameter is an empty string, the
148+
``timeout`` is negative and not equal to ``-1``, the
149+
``cursor_buffer_size`` is not positive.
150+
TypeError: If the ``expected_result_type`` does not equal to one of
151+
the values or names of the members of the
152+
:class:`SqlExpectedResultType`.
117153
"""
118-
return self._service.execute_statement(statement)
154+
return self._service.execute(sql, params, kwargs)
119155

120156

121157
class _SqlQueryId(object):
@@ -1167,32 +1203,18 @@ def __init__(self, connection_manager, serialization_service, invocation_service
11671203
self._serialization_service = serialization_service
11681204
self._invocation_service = invocation_service
11691205

1170-
def execute(self, sql, *params):
1206+
def execute(self, sql, params, kwargs):
11711207
"""Constructs a statement and executes it.
11721208
11731209
Args:
11741210
sql (str): SQL string.
1175-
*params: Query parameters.
1176-
1177-
Returns:
1178-
hazelcast.future.Future[SqlResult]: The execution result.
1179-
"""
1180-
statement = SqlStatement(sql)
1181-
1182-
for param in params:
1183-
statement.add_parameter(param)
1184-
1185-
return self.execute_statement(statement)
1186-
1187-
def execute_statement(self, statement):
1188-
"""Executes the given statement.
1189-
1190-
Args:
1191-
statement (SqlStatement): The statement to execute.
1211+
params (tuple): Query parameters.
1212+
kwargs (dict): Arguments to customize the query.
11921213
11931214
Returns:
11941215
hazelcast.future.Future[SqlResult]: The execution result.
11951216
"""
1217+
statement = _SqlStatement(sql, params, **kwargs)
11961218

11971219
connection = None
11981220
try:
@@ -1393,39 +1415,31 @@ class SqlExpectedResultType(object):
13931415
"""
13941416

13951417

1396-
class SqlStatement(object):
1397-
"""Definition of an SQL statement.
1398-
1399-
This object is mutable. Properties are read once before the execution
1400-
is started. Changes to properties do not affect the behavior of already
1401-
running statements.
1402-
"""
1403-
1404-
TIMEOUT_NOT_SET = -1
1405-
1406-
TIMEOUT_DISABLED = 0
1418+
_TIMEOUT_NOT_SET = -1
1419+
_DEFAULT_CURSOR_BUFFER_SIZE = 4096
14071420

1408-
DEFAULT_TIMEOUT = TIMEOUT_NOT_SET
14091421

1410-
DEFAULT_CURSOR_BUFFER_SIZE = 4096
1422+
class _SqlStatement(object):
1423+
"""Definition of an SQL statement."""
14111424

1412-
def __init__(self, sql):
1425+
def __init__(
1426+
self,
1427+
sql,
1428+
parameters,
1429+
timeout=_TIMEOUT_NOT_SET,
1430+
cursor_buffer_size=_DEFAULT_CURSOR_BUFFER_SIZE,
1431+
schema=None,
1432+
expected_result_type=SqlExpectedResultType.ANY,
1433+
):
14131434
self.sql = sql
1414-
self._parameters = []
1415-
self._timeout = SqlStatement.DEFAULT_TIMEOUT
1416-
self._cursor_buffer_size = SqlStatement.DEFAULT_CURSOR_BUFFER_SIZE
1417-
self._schema = None
1418-
self._expected_result_type = SqlExpectedResultType.ANY
1435+
self.parameters = parameters
1436+
self.timeout = timeout
1437+
self.cursor_buffer_size = cursor_buffer_size
1438+
self.schema = schema
1439+
self.expected_result_type = expected_result_type
14191440

14201441
@property
14211442
def sql(self):
1422-
"""str: The SQL string to be executed.
1423-
1424-
The setter raises:
1425-
1426-
- **AssertionError**: If the SQL parameter is not a string.
1427-
- **ValueError**: If the SQL parameter is an empty string.
1428-
"""
14291443
return self._sql
14301444

14311445
@sql.setter
@@ -1439,22 +1453,6 @@ def sql(self, sql):
14391453

14401454
@property
14411455
def schema(self):
1442-
"""str: The schema name. The engine will try to resolve the
1443-
non-qualified object identifiers from the statement in the given
1444-
schema. If not found, the default search path will be used, which
1445-
looks for objects in the predefined schemas ``partitioned`` and
1446-
``public``.
1447-
1448-
The schema name is case sensitive. For example, ``foo`` and ``Foo``
1449-
are different schemas.
1450-
1451-
The default value is ``None`` meaning only the default search path is
1452-
used.
1453-
1454-
The setter raises:
1455-
1456-
- **AssertionError**: If the schema is not a string or ``None``.
1457-
"""
14581456
return self._schema
14591457

14601458
@schema.setter
@@ -1465,80 +1463,20 @@ def schema(self, schema):
14651463
)
14661464
self._schema = schema
14671465

1468-
@property
1469-
def parameters(self):
1470-
"""list: Sets the statement parameters.
1471-
1472-
You may define parameter placeholders in the statement with the ``?``
1473-
character. For every placeholder, a parameter value must be provided.
1474-
1475-
When the setter is called, the content of the parameters list is copied.
1476-
Subsequent changes to the original list don't change the statement parameters.
1477-
1478-
The setter raises:
1479-
1480-
- **AssertionError**: If the parameter is not a list.
1481-
"""
1482-
return self._parameters
1483-
1484-
@parameters.setter
1485-
def parameters(self, parameters):
1486-
check_true(isinstance(parameters, list), "Parameters must be a list")
1487-
self._parameters = list(parameters)
1488-
14891466
@property
14901467
def timeout(self):
1491-
"""float or int: The execution timeout in seconds.
1492-
1493-
If the timeout is reached for a running statement, it will be
1494-
cancelled forcefully.
1495-
1496-
Zero value means no timeout. :const:`TIMEOUT_NOT_SET` means that
1497-
the value from the server-side config will be used. Other negative
1498-
values are prohibited.
1499-
1500-
Defaults to :const:`TIMEOUT_NOT_SET`.
1501-
1502-
The setter raises:
1503-
1504-
- **AssertionError**: If the timeout is not an integer or float.
1505-
- **ValueError**: If the timeout is negative and not equal to
1506-
:const:`TIMEOUT_NOT_SET`.
1507-
"""
15081468
return self._timeout
15091469

15101470
@timeout.setter
15111471
def timeout(self, timeout):
15121472
check_is_number(timeout, "Timeout must be an integer or float")
1513-
if timeout < 0 and timeout != SqlStatement.TIMEOUT_NOT_SET:
1473+
if timeout < 0 and timeout != _TIMEOUT_NOT_SET:
15141474
raise ValueError("Timeout must be non-negative or -1, not %s" % timeout)
15151475

15161476
self._timeout = timeout
15171477

15181478
@property
15191479
def cursor_buffer_size(self):
1520-
"""int: The cursor buffer size (measured in the number of rows).
1521-
1522-
When a statement is submitted for execution, a :class:`SqlResult`
1523-
is returned as a result. When rows are ready to be consumed,
1524-
they are put into an internal buffer of the cursor. This parameter
1525-
defines the maximum number of rows in that buffer. When the threshold
1526-
is reached, the backpressure mechanism will slow down the execution,
1527-
possibly to a complete halt, to prevent out-of-memory.
1528-
1529-
Only positive values are allowed.
1530-
1531-
The default value is expected to work well for most workloads. A bigger
1532-
buffer size may give you a slight performance boost for queries with
1533-
large result sets at the cost of increased memory consumption.
1534-
1535-
Defaults to :const:`DEFAULT_CURSOR_BUFFER_SIZE`.
1536-
1537-
The setter raises:
1538-
1539-
- **AssertionError**: If the cursor buffer size is not an integer.
1540-
- **ValueError**: If the cursor buffer size is not positive.
1541-
"""
15421480
return self._cursor_buffer_size
15431481

15441482
@cursor_buffer_size.setter
@@ -1550,14 +1488,6 @@ def cursor_buffer_size(self, cursor_buffer_size):
15501488

15511489
@property
15521490
def expected_result_type(self):
1553-
"""SqlExpectedResultType: The expected result type.
1554-
1555-
The setter raises:
1556-
1557-
- **TypeError**: If the expected result type does not equal to one of
1558-
the values or names of the members of the
1559-
:class:`SqlExpectedResultType`.
1560-
"""
15611491
return self._expected_result_type
15621492

15631493
@expected_result_type.setter
@@ -1566,37 +1496,6 @@ def expected_result_type(self, expected_result_type):
15661496
expected_result_type, SqlExpectedResultType
15671497
)
15681498

1569-
def add_parameter(self, parameter):
1570-
"""Adds a single parameter to the end of the parameters list.
1571-
1572-
Args:
1573-
parameter: The parameter.
1574-
1575-
See Also:
1576-
:attr:`parameters`
1577-
1578-
:func:`clear_parameters`
1579-
"""
1580-
self._parameters.append(parameter)
1581-
1582-
def clear_parameters(self):
1583-
"""Clears statement parameters."""
1584-
self._parameters = []
1585-
1586-
def copy(self):
1587-
"""Creates a copy of this instance.
1588-
1589-
Returns:
1590-
SqlStatement: The new copy.
1591-
"""
1592-
copied = SqlStatement(self.sql)
1593-
copied.parameters = list(self.parameters)
1594-
copied.timeout = self.timeout
1595-
copied.cursor_buffer_size = self.cursor_buffer_size
1596-
copied.schema = self.schema
1597-
copied.expected_result_type = self.expected_result_type
1598-
return copied
1599-
16001499
def __repr__(self):
16011500
return (
16021501
"SqlStatement(schema=%s, sql=%s, parameters=%s, timeout=%s,"

start_rc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
from os.path import isfile
55

6-
SERVER_VERSION = "5.0-SNAPSHOT"
6+
SERVER_VERSION = "5.0"
77
RC_VERSION = "0.8-SNAPSHOT"
88

99
RELEASE_REPO = "http://repo1.maven.apache.org/maven2"

0 commit comments

Comments
 (0)