Skip to content

Commit

Permalink
Catch errors on closeStatement from SQREAM (#28)
Browse files Browse the repository at this point in the history
* Catch errors on closeStatement from SQREAM
  • Loading branch information
andreyd-sqream committed Jun 7, 2023
1 parent f756c98 commit 70d661a
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 4 deletions.
35 changes: 31 additions & 4 deletions pysqream/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from pysqream.column_buffer import ColumnBuffer
from pysqream.ping import PingLoop, _start_ping_loop, _end_ping_loop
from pysqream.logger import *
from pysqream.utils import NotSupportedError, ProgrammingError, InternalError, IntegrityError, OperationalError, DataError, \
DatabaseError, InterfaceError, Warning, Error
# So it won't silently merge with array feature branch
from pysqream.utils import NotSupportedError, ProgrammingError, OperationalError
from pysqream.casting import lengths_to_pairs, sq_date_to_py_date, sq_datetime_to_py_datetime, sq_numeric_to_decimal
from pysqream.SQSocket import Client
import time
Expand Down Expand Up @@ -455,9 +455,36 @@ def setoutputsize(self, size, column=None):

## Closing

def close_stmt(self):
def close_stmt(self) -> None:
"""Closes open statement with SQREAM
Raises:
ProgrammingError: If server responds with invalid JSON
ProgrammingError: If server responds with valid JSON,
but it is not object
OperationalError: If server responds with "error" key in JSON
"""
if self.open_statement:
self.client.send_string('{"closeStatement": "closeStatement"}')
raw = self.client.send_string(
'{"closeStatement": "closeStatement"}')

# Check errors in response of the server
if raw:
try:
response = json.loads(raw)
except json.decoder.JSONDecodeError:
log_and_raise(
ProgrammingError,
f"Could not parse server response: {raw}"
)
if not isinstance(response, dict):
log_and_raise(
ProgrammingError,
f"Unexpected server response: {raw}"
)
if "error" in response:
log_and_raise(OperationalError, response["error"])

self.open_statement = False

if logger.isEnabledFor(logging.INFO):
Expand Down
Empty file added tests/cursor/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions tests/cursor/test_close_stmt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Tests for catching errors on closeStatement
In the past, the closeStatement operation remained silent, causing interference
with SQ-13979. As a result, when inserting sequences (TEXT / ARRAY) that
exceeded the limited size, the process would silently close, giving the
impression of success.
"""
import pytest

from pysqream.cursor import Cursor
from pysqream.SQSocket import Client
from pysqream.utils import OperationalError, ProgrammingError


class ConnectionMock:
"""Mock of pysqream.connection.Connection to prevent real connection"""
# pylint: disable=too-few-public-methods; they are not need for Mocks
s = None
version = 'Mock1'


def test_raise_on_error_from_sqream(monkeypatch):
"""JSON with error from SQREAM on closeStatement raises OperationalError"""
monkeypatch.setattr(
Client, "send_string", lambda *_: '{"error": "mock SQREAM error"}')
cur = Cursor(ConnectionMock(), [])
cur.open_statement = True
with pytest.raises(OperationalError):
cur.close_stmt()


def test_raise_on_invalid_json(monkeypatch):
"""
Test if SQREAM sends invalid json on closeStatement raises ProgrammingError
"""
monkeypatch.setattr(Client, "send_string", lambda *_: "I'm invalid json")
cur = Cursor(ConnectionMock(), [])
cur.open_statement = True
with pytest.raises(ProgrammingError):
cur.close_stmt()


def test_raise_if_json_not_obj(monkeypatch):
"""
Test if SQREAM sends not object on closeStatement raises ProgrammingError
"""
monkeypatch.setattr(Client, "send_string", lambda *_: '["valid array"]')
cur = Cursor(ConnectionMock(), [])
cur.open_statement = True
with pytest.raises(ProgrammingError):
cur.close_stmt()

0 comments on commit 70d661a

Please sign in to comment.