From ba53bde604ef5de54d8fd5b162b68f4ba6edffaf Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Wed, 29 May 2024 14:34:40 +0000 Subject: [PATCH 1/6] Initial support of SQLite Cloud to API-DB 2.0/SQLite3. - Compatibility with Python DB-API 2.0 - Aim for full compatibility with SQLite3 API --- .devcontainer/devcontainer.json | 13 +- .pre-commit-config.yaml | 12 +- src/sqlitecloud/__init__.py | 8 + src/sqlitecloud/client.py | 102 ++-- src/sqlitecloud/dbapi2.py | 462 +++++++++++++++++++ src/sqlitecloud/driver.py | 58 ++- src/sqlitecloud/resultset.py | 55 ++- src/sqlitecloud/types.py | 71 ++- src/tests/conftest.py | 19 + src/tests/integration/test_client.py | 31 +- src/tests/integration/test_dbapi2.py | 248 ++++++++++ src/tests/integration/test_sqlite3_parity.py | 1 + src/tests/unit/test_dbapi2.py | 306 ++++++++++++ src/tests/unit/test_driver.py | 123 +++++ src/tests/unit/test_resultset.py | 103 +++-- 15 files changed, 1494 insertions(+), 118 deletions(-) create mode 100644 src/sqlitecloud/dbapi2.py create mode 100644 src/tests/integration/test_dbapi2.py create mode 100644 src/tests/integration/test_sqlite3_parity.py create mode 100644 src/tests/unit/test_dbapi2.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2e0b7ac..a6e1021 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -20,11 +20,22 @@ // "postCreateCommand": "pip3 install --user -r requirements.txt", // Configure tool-specific properties. + // Py3.6 support (switch extensions to `pre-release` and `install another version`): + // Pylance v2022.6.30 + // Python v2022.8.1 + // Python Debugger v2023.1.XXX (pre-release version | debugpy v1.5.1) + // Black Formatter v2022.2.0 + // Isort v2022.1.11601002 (pre-release) "customizations": { "vscode": { "extensions": [ "littlefoxteam.vscode-python-test-adapter", - "jkillian.custom-local-formatters" + "jkillian.custom-local-formatters", + "ms-python.vscode-pylance", + "ms-python.python", + "ms-python.debugpy", + "ms-python.black-formatter", + "ms-python.isort" ] } } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6f5c64..da41096 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,11 @@ repos: - id: detect-private-key - id: check-merge-conflict # Using this mirror lets us use mypyc-compiled black, which is about 2x faster + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + name: isort - repo: https://github.com/psf/black-pre-commit-mirror rev: 22.8.0 hooks: @@ -19,11 +24,6 @@ repos: # pre-commit's default_language_version, see # https://pre-commit.com/#top_level-default_language_version language_version: python3.6 - - repo: https://github.com/pycqa/isort - rev: 5.10.1 - hooks: - - id: isort - name: isort - repo: https://github.com/PyCQA/autoflake rev: v1.4 hooks: @@ -34,7 +34,7 @@ repos: - "--expand-star-imports" - "--remove-duplicate-keys" - "--remove-unused-variables" - - "--remove-unused-variables" + - "--remove-all-unused-imports" - repo: https://github.com/pycqa/flake8 rev: 5.0.4 hooks: diff --git a/src/sqlitecloud/__init__.py b/src/sqlitecloud/__init__.py index 640940d..7318b7b 100644 --- a/src/sqlitecloud/__init__.py +++ b/src/sqlitecloud/__init__.py @@ -1 +1,9 @@ +# To replicate the public interface of sqlite3, we need to import +# the classes and functions from the dbapi2 module. +# eg: sqlite3.connect() -> sqlitecloud.connect() +# +from .dbapi2 import Connection, Cursor, connect + +__all__ = ["VERSION", "Connection", "Cursor", "connect"] + VERSION = "0.0.77" diff --git a/src/sqlitecloud/client.py b/src/sqlitecloud/client.py index cff4824..ead54cd 100644 --- a/src/sqlitecloud/client.py +++ b/src/sqlitecloud/client.py @@ -1,17 +1,16 @@ """ Module to interact with remote SqliteCloud database """ -from typing import Optional -from urllib import parse +from typing import Dict, Optional, Tuple, Union from sqlitecloud.driver import Driver from sqlitecloud.resultset import SqliteCloudResultSet from sqlitecloud.types import ( - SQCLOUD_DEFAULT, SQCloudConfig, SQCloudConnect, SQCloudException, SqliteCloudAccount, + SQLiteCloudDataTypes, ) @@ -39,10 +38,11 @@ def __init__( self.config = SQCloudConfig() if connection_str: - self.config = self._parse_connection_string(connection_str) + self.config = SQCloudConfig(connection_str) elif cloud_account: self.config.account = cloud_account - else: + + if self.config.account is None: raise SQCloudException("Missing connection parameters") def open_connection(self) -> SQCloudConnect: @@ -80,7 +80,7 @@ def exec_query(self, query: str, conn: SQCloudConnect) -> SqliteCloudResultSet: """Executes a SQL query on the SQLite Cloud database. Args: - query (str): The SQL query to be executed. + query (str): The SQL query to execute. Returns: SqliteCloudResultSet: The result set of the executed query. @@ -92,6 +92,41 @@ def exec_query(self, query: str, conn: SQCloudConnect) -> SqliteCloudResultSet: return SqliteCloudResultSet(result) + def exec_statement( + self, + query: str, + parameters: Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ], + conn: SQCloudConnect, + ) -> SqliteCloudResultSet: + """ + Prepare and execute a SQL statement (either a query or command) to the SQLite Cloud database. + This function supports two styles of parameter markers: + + 1. Question Mark Style: Parameters are passed as a tuple. For example: + "SELECT * FROM table WHERE id = ?" + + 2. Named Style: Parameters are passed as a dictionary. For example: + "SELECT * FROM table WHERE id = :id" + + In both cases, the parameters replace the placeholders in the SQL statement. + + Args: + query (str): The SQL query to execute. + parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): + The parameters to be used in the query. It can be a tuple or a dictionary. + conn (SQCloudConnect): The connection object to use for executing the query. + + Returns: + SqliteCloudResultSet: The result set obtained from executing the query. + """ + prepared_statement = self._driver.prepare_statement(query, parameters) + + result = self._driver.execute(prepared_statement, conn) + + return SqliteCloudResultSet(result) + def sendblob(self, blob: bytes, conn: SQCloudConnect) -> SqliteCloudResultSet: """Sends a blob to the SQLite database. @@ -100,58 +135,3 @@ def sendblob(self, blob: bytes, conn: SQCloudConnect) -> SqliteCloudResultSet: conn (SQCloudConnect): The connection to the database. """ return self._driver.send_blob(blob, conn) - - def _parse_connection_string(self, connection_string) -> SQCloudConfig: - # URL STRING FORMAT - # sqlitecloud://user:pass@host.com:port/dbname?timeout=10&key2=value2&key3=value3 - # or sqlitecloud://host.sqlite.cloud:8860/dbname?apikey=zIiAARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1A4m4xBA - - config = SQCloudConfig() - config.account = SqliteCloudAccount() - - try: - params = parse.urlparse(connection_string) - - options = {} - query = params.query - options = parse.parse_qs(query) - for option, values in options.items(): - opt = option.lower() - value = values.pop() - - if value.lower() in ["true", "false"]: - value = bool(value) - elif value.isdigit(): - value = int(value) - else: - value = value - - if hasattr(config, opt): - setattr(config, opt, value) - elif hasattr(config.account, opt): - setattr(config.account, opt, value) - - # apikey or username/password is accepted - if not config.account.apikey: - config.account.username = ( - parse.unquote(params.username) if params.username else "" - ) - config.account.password = ( - parse.unquote(params.password) if params.password else "" - ) - - path = params.path - database = path.strip("/") - if database: - config.account.dbname = database - - config.account.hostname = params.hostname - config.account.port = ( - int(params.port) if params.port else SQCLOUD_DEFAULT.PORT.value - ) - - return config - except Exception as e: - raise SQCloudException( - f"Invalid connection string {connection_string}" - ) from e diff --git a/src/sqlitecloud/dbapi2.py b/src/sqlitecloud/dbapi2.py new file mode 100644 index 0000000..6ee26ce --- /dev/null +++ b/src/sqlitecloud/dbapi2.py @@ -0,0 +1,462 @@ +# DB-API 2.0 interface to SQLiteCloud. +# +# PEP 249 – Python Database API Specification v2.0 +# https://peps.python.org/pep-0249/ +# +from typing import ( + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Tuple, + Union, + overload, +) + +from sqlitecloud.driver import Driver +from sqlitecloud.resultset import SQCloudResult +from sqlitecloud.types import ( + SQCLOUD_RESULT_TYPE, + SQCloudConfig, + SQCloudConnect, + SqliteCloudAccount, + SQLiteCloudDataTypes, +) + +# Question mark style, e.g. ...WHERE name=? +# Module also supports Named style, e.g. ...WHERE name=:name +paramstyle = "qmark" + +# Threads may share the module, but not connections +threadsafety = 1 + +# DB API level +apilevel = "2.0" + + +@overload +def connect(connection_str: str) -> "Connection": + """ + Establishes a connection to the SQLite Cloud database. + + Args: + connection_str (str): The connection string for the database. + It may include SQCloudConfig'options like timeout, apikey, etc. in the url query string. + Eg: sqlitecloud://myhost.sqlitecloud.io:8860/mydb?apikey=abc123&compression=true&timeout=10 + + Returns: + Connection: A connection object representing the database connection. + + Raises: + SQCloudException: If an error occurs while establishing the connection. + """ + ... + + +@overload +def connect( + cloud_account: SqliteCloudAccount, config: Optional[SQCloudConfig] = None +) -> "Connection": + """ + Establishes a connection to the SQLite Cloud database using the provided cloud account and configuration. + + Args: + cloud_account (SqliteCloudAccount): The cloud account used to authenticate and access the database. + config (Optional[SQCloudConfig]): Additional configuration options for the connection (default: None). + + Returns: + Connection: A connection object representing the connection to the SQLite Cloud database. + + Raises: + SQCloudException: If an error occurs while establishing the connection. + """ + ... + + +def connect( + connection_info: Union[str, SqliteCloudAccount], + config: Optional[SQCloudConfig] = None, +) -> "Connection": + """ + Establishes a connection to the SQLite Cloud database. + + Args: + connection_info (Union[str, SqliteCloudAccount]): The connection information. + It can be either a connection string or a `SqliteCloudAccount` object. + config (Optional[SQCloudConfig]): The configuration options for the connection. + Defaults to None. + + Returns: + Connection: A DB-API 2.0 connection object representing the connection to the database. + + Raises: + SQCloudException: If an error occurs while establishing the connection. + """ + driver = Driver() + + if isinstance(connection_info, SqliteCloudAccount): + if not config: + config = SQCloudConfig() + config.account = connection_info + else: + config = SQCloudConfig(connection_info) + + return Connection( + driver.connect(config.account.hostname, config.account.port, config) + ) + + +class Connection: + """ + Represents a DB-APi 2.0 connection to the SQLite Cloud database. + + Args: + sqcloud_connection (SQCloudConnect): The SQLiteCloud connection object. + + Attributes: + _driver (Driver): The driver object used for database operations. + sqcloud_connection (SQCloudConnect): The SQLiteCloud connection object. + """ + + row_factory: Optional[Callable[["Cursor", Tuple], object]] = None + + def __init__(self, sqcloud_connection: SQCloudConnect) -> None: + self._driver = Driver() + self.row_factory = None + self.sqcloud_connection = sqcloud_connection + + @property + def sqlcloud_connection(self) -> SQCloudConnect: + """ + Returns the SQLite Cloud connection object. + + Returns: + SQCloudConnect: The SQLiteCloud connection object. + """ + return self.sqcloud_connection + + def execute( + self, + sql: str, + parameters: Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ] = (), + ) -> "Cursor": + """ + Shortcut for cursor.execute(). + See the docstring of Cursor.execute() for more information. + + Args: + sql (str): The SQL query to execute. + parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): + The parameters to be used in the query. It can be a tuple or a dictionary. (Default ()) + conn (SQCloudConnect): The connection object to use for executing the query. + + Returns: + Cursor: The cursor object. + """ + cursor = self.cursor() + return cursor.execute(sql, parameters) + + def executemany( + self, + sql: str, + seq_of_parameters: Iterable[ + Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ] + ], + ) -> "Cursor": + """ + Shortcut for cursor.executemany(). + See the docstring of Cursor.executemany() for more information. + + Args: + sql (str): The SQL statement to execute. + seq_of_parameters (Iterable[Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]]): + The sequence of parameter sets to bind to the SQL statement. + + Returns: + Cursor: The cursor object. + """ + cursor = self.cursor() + return cursor.executemany(sql, seq_of_parameters) + + def close(self): + """ + Closes the database connection. + All cursors created with this connection will become unusable after calling this method. + + Note: + DB-API 2.0 interface does not manage the Sqlite Cloud PubSub feature. + Therefore, only the main socket is closed. + """ + self._driver.disconnect(self.sqcloud_connection, True) + + def commit(self): + """ + This method is a no-op as SQLite Cloud is currently not supporting transactions. + """ + + def rollback(self): + """ + This method is a no-op as SQLite Cloud is currently not supporting transactions. + """ + + def cursor(self): + """ + Creates a new cursor object. + + Returns: + Cursor: The cursor object. + """ + cursor = Cursor(self) + cursor.row_factory = self.row_factory + return cursor + + def __del__(self) -> None: + self.close() + + +class Cursor(Iterator[Any]): + """ + The DB-API 2.0 Cursor class represents a database cursor, which is used to interact with the database. + It provides methods to execute SQL statements, fetch results, and manage the cursor state. + + Attributes: + arraysize (int): The number of rows to fetch at a time with fetchmany(). Default is 1. + """ + + arraysize: int = 1 + + row_factory: Optional[Callable[["Cursor", Tuple], object]] = None + + def __init__(self, connection: Connection) -> None: + self._driver = Driver() + self.row_factory = None + self._connection = connection + self._iter_row: int = 0 + self._resultset: SQCloudResult = None + + @property + def connection(self) -> Connection: + """ + Returns the connection object associated with the database. + + Returns: + Connection: The DB-API 2.0 connection object. + """ + return self._connection + + @property + def description( + self, + ) -> Optional[Tuple[Tuple[str, None, None, None, None, None, None], ...]]: + """ + Each sequence contains information describing one result column. + Only the first value of the tuple is set which represents the column name. + """ + if not self._is_result_rowset(): + return None + + description = () + for i in range(self._resultset.ncols): + description += ( + ( + self._resultset.colname[i], + None, + None, + None, + None, + None, + None, + ), + ) + + return description + + @property + def rowcount(self) -> int: + """ + The number of rows that the last .execute*() produced (for DQL statements like SELECT) + + The number of rows affected by DML statements like UPDATE or INSERT is not supported. + + Returns: + int: The number of rows in the result set or -1 if no result set is available. + """ + return self._resultset.nrows if self._is_result_rowset() else -1 + + def close(self) -> None: + """ + Closes the database connection use to create the cursor. + + Note: + DB-API 2.0 interface does not manage the Sqlite Cloud PubSub feature. + Therefore, only the main socket is closed. + """ + self._driver.disconnect(self.connection.sqlcloud_connection, True) + + def execute( + self, + sql: str, + parameters: Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ] = (), + ) -> "Cursor": + """ + Prepare and execute a SQL statement (either a query or command) to the SQLite Cloud database. + This function supports two styles of parameter markers: + + 1. Question Mark Style: Parameters are passed as a tuple. For example: + "SELECT * FROM table WHERE id = ?" + + 2. Named Style: Parameters are passed as a dictionary. For example: + "SELECT * FROM table WHERE id = :id" + + In both cases, the parameters replace the placeholders in the SQL statement. + + Shortcut for cursor.execute(). + + Args: + sql (str): The SQL query to execute. + parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): + The parameters to be used in the query. It can be a tuple or a dictionary. (Default ()) + conn (SQCloudConnect): The connection object to use for executing the query. + + Returns: + Cursor: The cursor object. + """ + prepared_statement = self._driver.prepare_statement(sql, parameters) + result = self._driver.execute( + prepared_statement, self.connection.sqlcloud_connection + ) + + self._resultset = result + + return self + + def executemany( + self, + sql: str, + seq_of_parameters: Iterable[ + Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ] + ], + ) -> "Cursor": + """ + Executes a SQL statement multiple times, each with a different set of parameters. + The entire statement is transmitted to the SQLite Cloud server in a single operation. + This method is useful for executing the same query repeatedly with different values. + + Args: + sql (str): The SQL statement to execute. + seq_of_parameters (Iterable[Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]]): + The sequence of parameter sets to bind to the SQL statement. + + Returns: + Cursor: The cursor object. + """ + commands = "" + for parameters in seq_of_parameters: + prepared_statement = self._driver.prepare_statement(sql, parameters) + commands += prepared_statement + ";" + + self.execute(commands) + + return self + + def fetchone(self) -> Optional[Any]: + """ + Fetches the next row of a result set, returning it as a single sequence, + or None if no more rows are available. + + Returns: + The next row of the query result set as a tuple, + or None if no more rows are available. + """ + if not self._is_result_rowset(): + return None + + return next(self, None) + + def fetchmany(self, size=None) -> List[Any]: + """ + Fetches the next set of rows from the result set. + + Args: + size (int, optional): The maximum number of rows to fetch. + If not specified, it uses the `arraysize` attribute. + + Returns: + List[Tuple]: A list of rows, where each row is represented as a tuple. + """ + if not self._is_result_rowset(): + return [] + + if size is None: + size = self.arraysize + + results = [] + for _ in range(size): + next_result = self.fetchone() + if next_result is None: + break + results.append(next_result) + + return results + + def fetchall(self) -> List[Any]: + """ + Fetches all remaining rows of a query result set. + + Returns: + A list of rows, where each row is represented as a tuple. + """ + if not self._is_result_rowset(): + return [] + + return self.fetchmany(self.rowcount) + + def setinputsizes(self, sizes) -> None: + pass + + def setoutputsize(self, size, column=None) -> None: + pass + + def _call_row_factory(self, row: Tuple) -> object: + if self.row_factory is None: + return row + + return self.row_factory(self, row) + + def _is_result_rowset(self) -> bool: + return ( + self._resultset and self._resultset.tag == SQCLOUD_RESULT_TYPE.RESULT_ROWSET + ) + + def __iter__(self) -> "Cursor": + return self + + def __next__(self) -> Optional[Tuple[Any]]: + if ( + not self._resultset.is_result + and self._resultset.data + and self._iter_row < self._resultset.nrows + ): + out: tuple[Any] = () + + for col in range(self._resultset.ncols): + out += (self._resultset.get_value(self._iter_row, col),) + self._iter_row += 1 + + return self._call_row_factory(out) + + raise StopIteration + + def __len__(self) -> int: + return self.rowcount diff --git a/src/sqlitecloud/driver.py b/src/sqlitecloud/driver.py index c1843dd..ef50fcb 100644 --- a/src/sqlitecloud/driver.py +++ b/src/sqlitecloud/driver.py @@ -1,10 +1,11 @@ +import json import logging import select import socket import ssl import threading from io import BufferedReader, BufferedWriter -from typing import Callable, Optional, Union +from typing import Callable, Dict, Optional, Tuple, Union import lz4.block @@ -21,6 +22,7 @@ SQCloudNumber, SQCloudRowsetSignature, SQCloudValue, + SQLiteCloudDataTypes, ) @@ -88,6 +90,27 @@ def send_blob(self, blob: bytes, conn: SQCloudConnect) -> SQCloudResult: finally: conn.isblob = False + def prepare_statement( + self, + query: str, + parameters: Union[ + Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] + ], + ) -> str: + # TODO: check for right exception is case of wrong number of parameters + + # If parameters is a dictionary, replace the keys in the query with the values + if isinstance(parameters, dict): + for key, value in parameters.items(): + query = query.replace(":" + str(key), self.escape_sql_parameter(value)) + + # If parameters is a tuple, replace each '?' in the query with a value from the tuple + elif isinstance(parameters, tuple): + for value in parameters: + query = query.replace("?", self.escape_sql_parameter(value), 1) + + return query + def is_connected( self, connection: SQCloudConnect, main_socket: bool = True ) -> bool: @@ -105,6 +128,33 @@ def is_connected( return True + def escape_sql_parameter(self, param): + if param is None or param is None: + return "NULL" + + if isinstance(param, bool): + return "1" if param else "0" + + if isinstance(param, str): + # replace single quote with two single quotes + param = param.replace("'", "''") + return f"'{param}'" + + if isinstance(param, (int, float)): + return str(param) + + # serialize buffer as X'...' hex encoded string + if isinstance(param, bytes): + return f"X'{param.hex()}'" + + if isinstance(param, dict) or isinstance(param, list): + # serialize json then escape single quotes + json_string = json.dumps(param) + json_string = json_string.replace("'", "''") + return f"'{json_string}'" + + raise SQCloudException(f"Unsupported parameter type: {type(param)}") + def _internal_connect( self, hostname: str, port: int, config: SQCloudConfig ) -> socket: @@ -422,6 +472,12 @@ def _internal_run_command( command: Union[str, bytes], main_socket: bool = True, ) -> SQCloudResult: + if not self.is_connected(connection, main_socket): + raise SQCloudException( + "The connection is closed.", + SQCLOUD_INTERNAL_ERRCODE.NETWORK, + ) + self._internal_socket_write(connection, command, main_socket) return self._internal_socket_read(connection, main_socket) diff --git a/src/sqlitecloud/resultset.py b/src/sqlitecloud/resultset.py index 0220665..38e84f1 100644 --- a/src/sqlitecloud/resultset.py +++ b/src/sqlitecloud/resultset.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional -from sqlitecloud.types import SQCLOUD_RESULT_TYPE +from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCLOUD_VALUE_TYPE class SQCloudResult: @@ -31,6 +31,43 @@ def init_data(self, result: any) -> None: self.data = [result] self.is_result = True + def _compute_index(self, row: int, col: int) -> int: + if row < 0 or row >= self.nrows: + return -1 + if col < 0 or col >= self.ncols: + return -1 + return row * self.ncols + col + + def get_value(self, row: int, col: int, convert: bool = True) -> Optional[any]: + index = self._compute_index(row, col) + if index < 0 or not self.data or index >= len(self.data): + return None + + value = self.data[index] + return self._convert(value, col) if convert else value + + def get_name(self, col: int) -> Optional[str]: + if col < 0 or col >= self.ncols: + return None + return self.colname[col] + + def _convert(self, value: str, col: int) -> any: + if col < 0 or col >= len(self.decltype): + return value + + decltype = self.decltype[col] + if decltype == SQCLOUD_VALUE_TYPE.INTEGER.value: + return int(value) + if decltype == SQCLOUD_VALUE_TYPE.FLOAT.value: + return float(value) + if decltype == SQCLOUD_VALUE_TYPE.BLOB.value: + # values are received as bytes before being strings + return bytes(value) + if decltype == SQCLOUD_VALUE_TYPE.NULL.value: + return None + + return value + class SqliteCloudResultSet: def __init__(self, result: SQCloudResult) -> None: @@ -59,23 +96,11 @@ def __next__(self): raise StopIteration - def _compute_index(self, row: int, col: int) -> int: - if row < 0 or row >= self._result.nrows: - return -1 - if col < 0 or col >= self._result.ncols: - return -1 - return row * self._result.ncols + col - def get_value(self, row: int, col: int) -> Optional[any]: - index = self._compute_index(row, col) - if index < 0 or not self._result.data or index >= len(self._result.data): - return None - return self._result.data[index] + return self._result.get_value(row, col) def get_name(self, col: int) -> Optional[str]: - if col < 0 or col >= self._result.ncols: - return None - return self._result.colname[col] + return self._result.get_name(col) def get_result(self) -> Optional[any]: return self.get_value(0, 0) diff --git a/src/sqlitecloud/types.py b/src/sqlitecloud/types.py index 242d085..e8676e4 100644 --- a/src/sqlitecloud/types.py +++ b/src/sqlitecloud/types.py @@ -1,7 +1,11 @@ import types from asyncio import AbstractEventLoop from enum import Enum -from typing import Callable, Optional +from typing import Any, Callable, Dict, Optional, Union +from urllib import parse + +# Basic types supported by SQLiteCloud APIs +SQLiteCloudDataTypes = Union[str, int, bool, Dict[Union[str, int], Any], bytes, None] class SQCLOUD_DEFAULT(Enum): @@ -33,6 +37,14 @@ class SQCLOUD_ROWSET(Enum): CHUNKS_END = b"/6 0 0 0 " +class SQCLOUD_VALUE_TYPE(Enum): + INTEGER = "INTEGER" + FLOAT = "FLOAT" + TEXT = "TEXT" + BLOB = "BLOB" + NULL = "NULL" + + class SQCLOUD_INTERNAL_ERRCODE(Enum): """ Clients error codes. @@ -136,7 +148,7 @@ def __init__(self): class SQCloudConfig: - def __init__(self) -> None: + def __init__(self, connection_str: Optional[str] = None) -> None: self.account: SqliteCloudAccount = None # Optional query timeout passed directly to TLS socket @@ -173,6 +185,61 @@ def __init__(self) -> None: # Server should limit total number of rows in a set to maxRowset self.maxrowset = 0 + if connection_str is not None: + self._parse_connection_string(connection_str) + + def _parse_connection_string(self, connection_string) -> None: + # URL STRING FORMAT + # sqlitecloud://user:pass@host.com:port/dbname?timeout=10&key2=value2&key3=value3 + # or sqlitecloud://host.sqlite.cloud:8860/dbname?apikey=zIiAARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1A4m4xBA + + self.account = SqliteCloudAccount() + + try: + params = parse.urlparse(connection_string) + + options = {} + query = params.query + options = parse.parse_qs(query) + for option, values in options.items(): + opt = option.lower() + value = values.pop() + + if value.lower() in ["true", "false"]: + value = bool(value) + elif value.isdigit(): + value = int(value) + else: + value = value + + if hasattr(self, opt): + setattr(self, opt, value) + elif hasattr(self.account, opt): + setattr(self.account, opt, value) + + # apikey or username/password is accepted + if not self.account.apikey: + self.account.username = ( + parse.unquote(params.username) if params.username else "" + ) + self.account.password = ( + parse.unquote(params.password) if params.password else "" + ) + + path = params.path + database = path.strip("/") + if database: + self.account.dbname = database + + self.account.hostname = params.hostname + self.account.port = ( + int(params.port) if params.port else SQCLOUD_DEFAULT.PORT.value + ) + except Exception as e: + raise SQCloudException( + f"Invalid connection string {connection_string}" + ) from e + class SQCloudException(Exception): def __init__(self, message: str, code: int = -1, xerrcode: int = 0) -> None: diff --git a/src/tests/conftest.py b/src/tests/conftest.py index da4f982..39c61ce 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -3,6 +3,7 @@ import pytest from dotenv import load_dotenv +import sqlitecloud from sqlitecloud.client import SqliteCloudClient from sqlitecloud.types import SQCloudConnect, SqliteCloudAccount @@ -30,3 +31,21 @@ def sqlitecloud_connection(): yield (connection, client) client.disconnect(connection) + + +@pytest.fixture() +def sqlitecloud_dbapi2_connection(): + account = SqliteCloudAccount() + account.username = os.getenv("SQLITE_USER") + account.password = os.getenv("SQLITE_PASSWORD") + account.dbname = os.getenv("SQLITE_DB") + account.hostname = os.getenv("SQLITE_HOST") + account.port = int(os.getenv("SQLITE_PORT")) + + connection = sqlitecloud.connect(account) + + assert isinstance(connection, sqlitecloud.Connection) + + yield connection + + connection.close() diff --git a/src/tests/integration/test_client.py b/src/tests/integration/test_client.py index 273bb19..80bf1d2 100644 --- a/src/tests/integration/test_client.py +++ b/src/tests/integration/test_client.py @@ -143,9 +143,9 @@ def test_get_value(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("SELECT * FROM albums", connection) - assert "1" == result.get_value(0, 0) + assert 1 == result.get_value(0, 0) assert "For Those About To Rock We Salute You" == result.get_value(0, 1) - assert "2" == result.get_value(1, 0) + assert 2 == result.get_value(1, 0) def test_select_utf8_value_and_column_name(self, sqlitecloud_connection): connection, client = sqlitecloud_connection @@ -650,3 +650,30 @@ def test_compression_multiple_columns(self): assert rowset.nrows > 0 assert rowset.ncols > 0 assert rowset.get_name(0) == "AlbumId" + + def test_exec_statement_with_named_placeholder(self, sqlitecloud_connection): + connection, client = sqlitecloud_connection + + result = client.exec_statement( + "SELECT * FROM albums WHERE AlbumId = :id and Title = :title", + {"id": 1, "title": "For Those About To Rock We Salute You"}, + connection, + ) + + assert result.nrows == 1 + assert result.get_value(0, 0) == 1 + + def test_exec_statement_with_qmarks(self, sqlitecloud_connection): + connection, client = sqlitecloud_connection + + result = client.exec_statement( + "SELECT * FROM albums WHERE AlbumId = ? and Title = ?", + ( + 1, + "For Those About To Rock We Salute You", + ), + connection, + ) + + assert result.nrows == 1 + assert result.get_value(0, 0) == 1 diff --git a/src/tests/integration/test_dbapi2.py b/src/tests/integration/test_dbapi2.py new file mode 100644 index 0000000..b92ceb0 --- /dev/null +++ b/src/tests/integration/test_dbapi2.py @@ -0,0 +1,248 @@ +import os +import uuid + +import pytest + +import sqlitecloud +from sqlitecloud.types import ( + SQCLOUD_INTERNAL_ERRCODE, + SQCloudException, + SqliteCloudAccount, +) + + +class TestDBAPI2: + def test_connect_with_account(self): + account = SqliteCloudAccount( + os.getenv("SQLITE_USER"), + os.getenv("SQLITE_PASSWORD"), + os.getenv("SQLITE_HOST"), + os.getenv("SQLITE_DB"), + int(os.getenv("SQLITE_PORT")), + ) + + connection = sqlitecloud.connect(account) + + connection.close() + assert isinstance(connection, sqlitecloud.Connection) + + def test_connect_with_connection_string(self): + connection_str = f"{os.getenv('SQLITE_CONNECTION_STRING')}/{os.getenv('SQLITE_DB')}?apikey={os.getenv('SQLITE_API_KEY')}" + + connection = sqlitecloud.connect(connection_str) + + connection.close() + assert isinstance(connection, sqlitecloud.Connection) + + def test_disconnect(self): + account = SqliteCloudAccount( + os.getenv("SQLITE_USER"), + os.getenv("SQLITE_PASSWORD"), + os.getenv("SQLITE_HOST"), + os.getenv("SQLITE_DB"), + int(os.getenv("SQLITE_PORT")), + ) + + connection = sqlitecloud.connect(account) + + connection.close() + + assert isinstance(connection, sqlitecloud.Connection) + + with pytest.raises(SQCloudException) as e: + connection.execute("SELECT 1") + + assert e.value.errcode == SQCLOUD_INTERNAL_ERRCODE.NETWORK + assert e.value.errmsg == "The connection is closed." + + def test_select(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.cursor() + cursor.execute("SELECT 'Hello'") + + result = cursor.fetchone() + + assert result == ("Hello",) + + def test_connection_execute(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.execute("SELECT 'Hello'") + + result = cursor.fetchone() + + assert result == ("Hello",) + + def test_column_not_found(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + with pytest.raises(SQCloudException) as e: + connection.execute("SELECT not_a_column FROM albums") + + assert e.value.errcode == 1 + assert e.value.errmsg == "no such column: not_a_column" + + def test_rowset_data(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + cursor = connection.execute("SELECT AlbumId FROM albums LIMIT 2") + + assert cursor.rowcount == 2 + assert len(cursor.description) == 1 + + def test_fetch_one_row(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.execute("SELECT * FROM albums") + + row = cursor.fetchone() + + assert len(row) == 3 + assert row == (1, "For Those About To Rock We Salute You", 1) + + def test_select_utf8_value_and_column_name(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + cursor = connection.execute("SELECT 'Minha História'") + + assert cursor.rowcount == 1 + assert len(cursor.description) == 1 + assert "Minha História" == cursor.fetchone()[0] + assert "'Minha História'" == cursor.description[0][0] + + def test_column_name(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + cursor = connection.execute("SELECT * FROM albums") + + assert "AlbumId" == cursor.description[0][0] + assert "Title" == cursor.description[1][0] + + def test_integer(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + cursor = connection.execute("TEST INTEGER") + + assert cursor.rowcount == -1 + assert cursor.fetchone() is None + + def test_error(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + with pytest.raises(SQCloudException) as e: + connection.execute("TEST ERROR") + + assert e.value.errcode == 66666 + assert e.value.errmsg == "This is a test error message with a devil error code." + + def test_execute_with_named_placeholder(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.execute( + "SELECT * FROM albums WHERE AlbumId = :id and Title like :title", + {"id": 1, "title": "For Those About%"}, + ) + + assert cursor.rowcount == 1 + assert cursor.fetchone() == (1, "For Those About To Rock We Salute You", 1) + + def test_execute_with_qmarks(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.execute( + "SELECT * FROM albums WHERE AlbumId = ? and Title like ?", + ( + 1, + "For Those About%", + ), + ) + + assert cursor.rowcount == 1 + assert cursor.fetchone() == (1, "For Those About To Rock We Salute You", 1) + + def test_execute_two_updates_using_the_cursor(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.cursor() + new_name1 = "Jazz" + str(uuid.uuid4()) + new_name2 = "Jazz" + str(uuid.uuid4()) + genreId = 2 + + cursor.execute( + "UPDATE genres SET Name = ? WHERE GenreId = ?;", + ( + new_name1, + genreId, + ), + ) + cursor.execute( + "UPDATE genres SET Name = ? WHERE GenreId = ?;", + ( + new_name2, + genreId, + ), + ) + + cursor.execute( + "SELECT Name, GenreID FROM genres WHERE GenreId = :id", {"id": genreId} + ) + + assert cursor.fetchone() == ( + new_name2, + genreId, + ) + + def test_executemany_updates(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + new_name1 = "Jazz" + str(uuid.uuid4()) + new_name2 = "Jazz" + str(uuid.uuid4()) + genreId = 2 + + cursor = connection.executemany( + "UPDATE genres SET Name = ? WHERE GenreId = ?;", + [(new_name1, genreId), (new_name2, genreId)], + ) + + cursor.execute( + "SELECT Name, GenreID FROM genres WHERE GenreId = :id", {"id": genreId} + ) + + assert cursor.fetchone() == ( + new_name2, + genreId, + ) + + def test_executemany_updates_using_the_cursor(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + cursor = connection.cursor() + new_name1 = "Jazz" + str(uuid.uuid4()) + new_name2 = "Jazz" + str(uuid.uuid4()) + genreId = 2 + + cursor.executemany( + "UPDATE genres SET Name = ? WHERE GenreId = ?;", + [(new_name1, genreId), (new_name2, genreId)], + ) + + cursor.execute( + "SELECT Name, GenreID FROM genres WHERE GenreId = :id", {"id": genreId} + ) + + assert cursor.fetchone() == ( + new_name2, + genreId, + ) + + def test_row_factory(self, sqlitecloud_dbapi2_connection): + connection = sqlitecloud_dbapi2_connection + + connection.row_factory = lambda cursor, row: { + description[0]: row[i] for i, description in enumerate(cursor.description) + } + + cursor = connection.execute("SELECT * FROM albums") + + row = cursor.fetchone() + + assert row["AlbumId"] == 1 + assert row["Title"] == "For Those About To Rock We Salute You" + assert row["ArtistId"] == 1 diff --git a/src/tests/integration/test_sqlite3_parity.py b/src/tests/integration/test_sqlite3_parity.py new file mode 100644 index 0000000..c3740c4 --- /dev/null +++ b/src/tests/integration/test_sqlite3_parity.py @@ -0,0 +1 @@ +# TODO: sqlite3 to sqlitecloud comparison diff --git a/src/tests/unit/test_dbapi2.py b/src/tests/unit/test_dbapi2.py new file mode 100644 index 0000000..3df6077 --- /dev/null +++ b/src/tests/unit/test_dbapi2.py @@ -0,0 +1,306 @@ +from pytest_mock import MockerFixture + +import sqlitecloud +from sqlitecloud import Cursor +from sqlitecloud.dbapi2 import Connection +from sqlitecloud.driver import Driver +from sqlitecloud.resultset import SQCloudResult +from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCloudConfig, SqliteCloudAccount + + +def test_connect_with_account_and_config(mocker: MockerFixture): + mock_connect = mocker.patch("sqlitecloud.driver.Driver.connect") + + account = SqliteCloudAccount() + account.hostname = "myhost" + account.port = 1234 + + config = SQCloudConfig() + config.timeout = 99 + config.memory = True + + sqlitecloud.connect(account, config) + + mock_connect.assert_called_once_with(account.hostname, account.port, config) + + +def test_connect_with_connection_string_and_parameters(mocker: MockerFixture): + mock_connect = mocker.patch("sqlitecloud.driver.Driver.connect") + + sqlitecloud.connect( + "sqlitecloud://user:pass@myhost:1234/dbname?timeout=99&memory=true" + ) + + mock_connect.assert_called_once() + assert mock_connect.call_args[0][0] == "myhost" + assert mock_connect.call_args[0][1] == 1234 + # config + assert mock_connect.call_args[0][2].timeout == 99 + assert mock_connect.call_args[0][2].memory + + +class TestCursor: + def test_description_empty(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + description = cursor.description + + assert description is None + + def test_description_with_resultset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 1 + result.data = ["myname"] + result.colname = ["column1"] + cursor._resultset = result + + assert cursor.description == (("column1", None, None, None, None, None, None),) + + def test_description_with_resultset_multiple_rows(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 2 + result.nrows = 1 + result.data = ["myname"] + result.colname = ["name", "id"] + cursor._resultset = result + + assert cursor.description == ( + ("name", None, None, None, None, None, None), + ("id", None, None, None, None, None, None), + ) + + def test_rowcount_with_rowset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 3 + result.data = ["myname1", "myname2", "myname3"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.rowcount == 3 + + def test_rowcount_with_result(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + cursor._resultset = result + + assert cursor.rowcount == -1 + + def test_rowcount_with_no_resultset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + assert cursor.rowcount == -1 + + def test_execute_escaped(self, mocker: MockerFixture): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + execute_mock = mocker.patch.object(Driver, "execute") + + sql = "SELECT * FROM users WHERE name = ?" + parameters = ("John's",) + + cursor.execute(sql, parameters) + + assert ( + execute_mock.call_args[0][0] == "SELECT * FROM users WHERE name = 'John''s'" + ) + + def test_executemany(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + execute_mock = mocker.patch.object(cursor, "execute") + + sql = "INSERT INTO users (name, age) VALUES (?, ?)" + seq_of_parameters = [("John", 25), ("Jane", 30), ("Bob", 40)] + + cursor.executemany(sql, seq_of_parameters) + + execute_mock.assert_called_once_with( + "INSERT INTO users (name, age) VALUES ('John', 25);INSERT INTO users (name, age) VALUES ('Jane', 30);INSERT INTO users (name, age) VALUES ('Bob', 40);" + ) + + def test_executemany_escaped(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + execute_mock = mocker.patch.object(cursor, "execute") + + sql = "INSERT INTO users (name, age) VALUES (?, ?)" + seq_of_parameters = [("O'Conner", 25)] + + cursor.executemany(sql, seq_of_parameters) + + execute_mock.assert_called_once_with( + "INSERT INTO users (name, age) VALUES ('O''Conner', 25);" + ) + + def test_fetchone_with_no_resultset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + assert cursor.fetchone() is None + + def test_fetchone_with_result(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + cursor._resultset = result + + assert cursor.fetchone() is None + + def test_fetchone_with_rowset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 1 + result.data = ["myname"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchone() == ("myname",) + + def test_fetchone_twice(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 1 + result.data = ["myname"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchone() is not None + assert cursor.fetchone() is None + + def test_fetchmany_with_no_resultset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + assert cursor.fetchmany() == [] + + def test_fetchmany_with_result(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + cursor._resultset = result + + assert cursor.fetchmany() == [] + + def test_fetchmany_with_rowset_and_default_size(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 3 + result.data = ["myname1", "myname2", "myname3"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchmany(None) == [("myname1",)] + + def test_fetchmany_twice_to_retrieve_whole_rowset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 2 + result.data = ["myname1", "myname2"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchmany(2) == [("myname1",), ("myname2",)] + assert cursor.fetchmany() == [] + + def test_fetchmany_with_size_higher_than_rowcount(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 1 + result.data = ["myname1"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchmany(2) == [("myname1",)] + + def test_fetchall_with_no_resultset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + assert cursor.fetchall() == [] + + def test_fetchall_with_result(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + cursor._resultset = result + + assert cursor.fetchall() == [] + + def test_fetchall_with_rowset(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 3 + result.data = ["myname1", "myname2", "myname3"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchall() == [("myname1",), ("myname2",), ("myname3",)] + + def test_fetchall_twice_and_expect_empty_list(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 2 + result.data = ["myname1", "myname2"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchall() == [("myname1",), ("myname2",)] + assert cursor.fetchall() == [] + + def test_fetchall_to_return_remaining_rows(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 2 + result.data = ["myname1", "myname2"] + result.colname = ["name"] + cursor._resultset = result + + assert cursor.fetchone() is not None + assert cursor.fetchall() == [("myname2",)] + + def test_iterator(self, mocker): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 2 + result.data = ["myname1", "myname2"] + result.colname = ["name"] + cursor._resultset = result + + assert list(cursor) == [("myname1",), ("myname2",)] + assert len(cursor) == 2 + + def test_row_factory(self, mocker): + conn = Connection(mocker.patch("sqlitecloud.types.SQCloudConnect")) + conn.row_factory = lambda x, y: {"name": y[0]} + + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 1 + result.nrows = 2 + result.data = ["myname1", "myname2"] + result.colname = ["name"] + + cursor = conn.cursor() + cursor._resultset = result + + assert cursor.fetchone() == {"name": "myname1"} diff --git a/src/tests/unit/test_driver.py b/src/tests/unit/test_driver.py index 8e6d819..4dac64b 100644 --- a/src/tests/unit/test_driver.py +++ b/src/tests/unit/test_driver.py @@ -89,3 +89,126 @@ def test_parse_rowset_signature(self): assert 1 == result.version assert 1 == result.nrows assert 2 == result.ncols + + def test_prepare_statement_with_tuple_parameters(self): + driver = Driver() + + query = "SELECT * FROM users WHERE age > ? AND name = ?" + parameters = (18, "John") + + expected_result = "SELECT * FROM users WHERE age > 18 AND name = 'John'" + result = driver.prepare_statement(query, parameters) + + assert expected_result == result + + def test_prepare_statement_with_dict_parameters(self): + driver = Driver() + + query = "INSERT INTO users (name, age) VALUES (:name, :age)" + parameters = {"name": "Alice", "age": 25} + + expected_result = "INSERT INTO users (name, age) VALUES ('Alice', 25)" + result = driver.prepare_statement(query, parameters) + + assert expected_result == result + + # TODO: should rise exception + def test_prepare_statement_with_missing_parameters(self): + driver = Driver() + + query = "UPDATE users SET name = :name, age = :age WHERE id = :id" + parameters = {"name": "Bob"} + + expected_result = "UPDATE users SET name = 'Bob', age = :age WHERE id = :id" + + result = driver.prepare_statement(query, parameters) + + assert expected_result == result + + def test_prepare_statement_with_extra_parameters(self): + driver = Driver() + + query = "SELECT * FROM users WHERE age > :age" + parameters = {"age": 30, "name": "Alice"} + + expected_result = "SELECT * FROM users WHERE age > 30" + + result = driver.prepare_statement(query, parameters) + + assert expected_result == result + + def test_prepare_statement_with_sql_injection_threat(self): + driver = Driver() + + query = "SELECT * FROM phone WHERE name = ?" + parameter = ("Jack's phone; DROP TABLE phone;",) + + expected_result = ( + "SELECT * FROM phone WHERE name = 'Jack''s phone; DROP TABLE phone;'" + ) + result = driver.prepare_statement(query, parameter) + + assert expected_result == result + + def test_escape_sql_parameter_with_string(self): + driver = Driver() + param = "John's SQL" + + expected_result = "'John''s SQL'" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_integer(self): + driver = Driver() + param = 123 + + expected_result = "123" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_float(self): + driver = Driver() + param = 3.14 + + expected_result = "3.14" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_none(self): + driver = Driver() + param = None + + expected_result = "NULL" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_bool(self): + driver = Driver() + param = True + + expected_result = "1" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_bytes(self): + driver = Driver() + param = b"Hello" + + expected_result = "X'48656c6c6f'" + result = driver.escape_sql_parameter(param) + + assert expected_result == result + + def test_escape_sql_parameter_with_dict(self): + driver = Driver() + param = {"name": "O'Conner", "age": 25} + + expected_result = '\'{"name": "O\'\'Conner", "age": 25}\'' + driver.escape_sql_parameter(param) + + assert expected_result diff --git a/src/tests/unit/test_resultset.py b/src/tests/unit/test_resultset.py index 28b1264..ac22483 100644 --- a/src/tests/unit/test_resultset.py +++ b/src/tests/unit/test_resultset.py @@ -1,7 +1,7 @@ import pytest from sqlitecloud.resultset import SQCloudResult, SqliteCloudResultSet -from sqlitecloud.types import SQCLOUD_RESULT_TYPE +from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCLOUD_VALUE_TYPE class TestSqCloudResult: @@ -30,6 +30,78 @@ def test_init_as_dataset(self): assert 0 == result.ncols assert 0 == result.version + def test_get_value_with_rowset(self): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.nrows = 2 + result.ncols = 2 + result.colname = ["name", "age"] + result.data = ["John", 42, "Doe", 24] + result.version = 2 + + assert "John" == result.get_value(0, 0) + assert 24 == result.get_value(1, 1) + assert result.get_value(2, 2) is None + + def test_get_value_array(self): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ARRAY, result=[1, 2, 3]) + + assert [1, 2, 3] == result.get_value(0, 0) + + def test_get_colname(self): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.ncols = 2 + result.colname = ["name", "age"] + + assert "name" == result.get_name(0) + assert "age" == result.get_name(1) + assert result.get_name(2) is None + + def test_get_value_with_empty_decltype(self): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.nrows = 2 + result.ncols = 2 + result.colname = [] + result.decltype = [] + result.data = ["John", "42", "Doe", "24"] + + assert "John" == result.get_value(0, 0) + assert "42" == result.get_value(0, 1) + assert "Doe" == result.get_value(1, 0) + assert "24" == result.get_value(1, 1) + + def test_get_value_with_convert_false(self): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.nrows = 1 + result.ncols = 2 + result.colname = ["name", "age"] + result.data = ["John", "42"] + result.decltype = ["TEXT", "INTEGER"] + + assert "John" == result.get_value(0, 0, convert=False) + assert "42" == result.get_value(0, 1, convert=False) + + @pytest.mark.parametrize( + "value_type, value, expected_value", + [ + (SQCLOUD_VALUE_TYPE.INTEGER.value, "24", 24), + (SQCLOUD_VALUE_TYPE.FLOAT.value, "3.14", 3.14), + (SQCLOUD_VALUE_TYPE.TEXT.value, "John", "John"), + (SQCLOUD_VALUE_TYPE.BLOB.value, b"hello", b"hello"), + (SQCLOUD_VALUE_TYPE.NULL.value, "NULL", None), + ], + ) + def test_get_value_to_convert_text(self, value_type, value, expected_value): + result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result.nrows = 1 + result.ncols = 1 + result.colname = ["mycol"] + result.data = [value] + result.decltype = [value_type] + + result_set = SqliteCloudResultSet(result) + + assert expected_value == result_set.get_value(0, 0) + class TestSqliteCloudResultSet: def test_next(self): @@ -63,35 +135,6 @@ def test_iter_rowset(self): assert {"name": "John", "age": 42} == out[0] assert {"name": "Doe", "age": 24} == out[1] - def test_get_value_with_rowset(self): - rowset = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) - rowset.nrows = 2 - rowset.ncols = 2 - rowset.colname = ["name", "age"] - rowset.data = ["John", 42, "Doe", 24] - rowset.version = 2 - result_set = SqliteCloudResultSet(rowset) - - assert "John" == result_set.get_value(0, 0) - assert 24 == result_set.get_value(1, 1) - assert result_set.get_value(2, 2) is None - - def test_get_value_array(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ARRAY, result=[1, 2, 3]) - result_set = SqliteCloudResultSet(result) - - assert [1, 2, 3] == result_set.get_value(0, 0) - - def test_get_colname(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) - result.ncols = 2 - result.colname = ["name", "age"] - result_set = SqliteCloudResultSet(result) - - assert "name" == result_set.get_name(0) - assert "age" == result_set.get_name(1) - assert result_set.get_name(2) is None - def test_get_result_with_single_value(self): result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) result_set = SqliteCloudResultSet(result) From d4b74f2eed95652c07c5eff213297e7660e0a5d1 Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Thu, 30 May 2024 14:00:31 +0000 Subject: [PATCH 2/6] Tests for SQLite3 feature parity and pandas support. --- .gitignore | 2 + requirements-dev.txt | 4 + src/sqlitecloud/dbapi2.py | 48 +++- src/sqlitecloud/types.py | 2 +- src/tests/assets/chinook.sqlite | Bin 0 -> 886784 bytes src/tests/assets/prices.csv | 22 ++ src/tests/integration/test_pandas.py | 67 +++++ src/tests/integration/test_sqlite3_parity.py | 246 ++++++++++++++++++- src/tests/unit/test_dbapi2.py | 32 ++- 9 files changed, 408 insertions(+), 15 deletions(-) create mode 100644 src/tests/assets/chinook.sqlite create mode 100644 src/tests/assets/prices.csv create mode 100644 src/tests/integration/test_pandas.py diff --git a/.gitignore b/.gitignore index 9ee7dce..6aae48d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ main.dSYM/ .idea SqliteCloud.egg-info src/sqlitecloud/libsqcloud.so + +playground.ipynb diff --git a/requirements-dev.txt b/requirements-dev.txt index 1f30ce7..207f9a6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,3 +14,7 @@ isort==5.10.1 autoflake==1.4 pre-commit==2.17.0 bandit==1.7.1 +# We can use the most recent compatible version because +# this package is only used for testing compatibility +# with pandas dataframe +pandas>=1.1.5 diff --git a/src/sqlitecloud/dbapi2.py b/src/sqlitecloud/dbapi2.py index 6ee26ce..f645fa6 100644 --- a/src/sqlitecloud/dbapi2.py +++ b/src/sqlitecloud/dbapi2.py @@ -22,6 +22,7 @@ SQCLOUD_RESULT_TYPE, SQCloudConfig, SQCloudConnect, + SQCloudException, SqliteCloudAccount, SQLiteCloudDataTypes, ) @@ -198,12 +199,12 @@ def close(self): def commit(self): """ - This method is a no-op as SQLite Cloud is currently not supporting transactions. + Not implementied yet. """ def rollback(self): """ - This method is a no-op as SQLite Cloud is currently not supporting transactions. + Not implemented yet. """ def cursor(self): @@ -290,15 +291,21 @@ def rowcount(self) -> int: """ return self._resultset.nrows if self._is_result_rowset() else -1 - def close(self) -> None: + @property + def lastrowid(self) -> Optional[int]: + """ + Not implemented yet in the library. """ - Closes the database connection use to create the cursor. + return None - Note: - DB-API 2.0 interface does not manage the Sqlite Cloud PubSub feature. - Therefore, only the main socket is closed. + def close(self) -> None: + """ + Just mark the cursors to be no more usable in SQLite Cloud database. + In sqlite the `close()` is used to free up resources: https://devpress.csdn.net/python/62fe355b7e668234661931d8.html """ - self._driver.disconnect(self.connection.sqlcloud_connection, True) + self._ensure_connection() + + self._connection = None def execute( self, @@ -330,6 +337,8 @@ def execute( Returns: Cursor: The cursor object. """ + self._ensure_connection() + prepared_statement = self._driver.prepare_statement(sql, parameters) result = self._driver.execute( prepared_statement, self.connection.sqlcloud_connection @@ -361,6 +370,8 @@ def executemany( Returns: Cursor: The cursor object. """ + self._ensure_connection() + commands = "" for parameters in seq_of_parameters: prepared_statement = self._driver.prepare_statement(sql, parameters) @@ -379,6 +390,8 @@ def fetchone(self) -> Optional[Any]: The next row of the query result set as a tuple, or None if no more rows are available. """ + self._ensure_connection() + if not self._is_result_rowset(): return None @@ -395,6 +408,8 @@ def fetchmany(self, size=None) -> List[Any]: Returns: List[Tuple]: A list of rows, where each row is represented as a tuple. """ + self._ensure_connection() + if not self._is_result_rowset(): return [] @@ -417,6 +432,8 @@ def fetchall(self) -> List[Any]: Returns: A list of rows, where each row is represented as a tuple. """ + self._ensure_connection() + if not self._is_result_rowset(): return [] @@ -439,10 +456,22 @@ def _is_result_rowset(self) -> bool: self._resultset and self._resultset.tag == SQCLOUD_RESULT_TYPE.RESULT_ROWSET ) + def _ensure_connection(self): + """ + Ensure the cursor is usable or has been closed. + + Raises: + SQCloudException: If the cursor is closed. + """ + if not self._connection: + raise SQCloudException("The cursor is closed.") + def __iter__(self) -> "Cursor": return self def __next__(self) -> Optional[Tuple[Any]]: + self._ensure_connection() + if ( not self._resultset.is_result and self._resultset.data @@ -457,6 +486,3 @@ def __next__(self) -> Optional[Tuple[Any]]: return self._call_row_factory(out) raise StopIteration - - def __len__(self) -> int: - return self.rowcount diff --git a/src/sqlitecloud/types.py b/src/sqlitecloud/types.py index e8676e4..41dee4d 100644 --- a/src/sqlitecloud/types.py +++ b/src/sqlitecloud/types.py @@ -39,7 +39,7 @@ class SQCLOUD_ROWSET(Enum): class SQCLOUD_VALUE_TYPE(Enum): INTEGER = "INTEGER" - FLOAT = "FLOAT" + FLOAT = "REAL" TEXT = "TEXT" BLOB = "BLOB" NULL = "NULL" diff --git a/src/tests/assets/chinook.sqlite b/src/tests/assets/chinook.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..60621b92aa374b3f6f6b9abff54534fd91eee758 GIT binary patch literal 886784 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

YV<~TCCx! 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

5(e(^zm66=a4EQ{qbDWBlXPc=qERxYkDdS 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%0D7pndIZ*iR9p>k02NmS>`%p2 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 `%T|c9$9(Vnes_0#rpHLOO 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 literal 0 HcmV?d00001 diff --git a/src/tests/assets/prices.csv b/src/tests/assets/prices.csv new file mode 100644 index 0000000..f55746f --- /dev/null +++ b/src/tests/assets/prices.csv @@ -0,0 +1,22 @@ +DATE,TICKER,CLOSE +20161125,GE,31.440001 +20161123,GE,31.34 +20161122,GE,31.18 +20161121,GE,30.870001 +20161118,GE,30.67 +20161117,GE,30.790001 +20161116,GE,30.74 +20161115,GE,30.75 +20161114,GE,30.51 +20161111,GE,30.709999 +20161110,GE,30.41 +20161109,GE,29.629999 +20161108,GE,29.42 +20161107,GE,29.309999 +20161104,GE,28.440001 +20161103,GE,28.280001 +20161102,GE,28.49 +20161101,GE,28.879999 +20161031,GE,29.1 +20161028,GE,29.219999 +20161027,GE,28.629999 diff --git a/src/tests/integration/test_pandas.py b/src/tests/integration/test_pandas.py new file mode 100644 index 0000000..4dfa136 --- /dev/null +++ b/src/tests/integration/test_pandas.py @@ -0,0 +1,67 @@ +import os + +import pandas as pd +from pandas.testing import assert_frame_equal + + +# Integration tests for sqlitecloud and pandas dataframe +class TestPanads: + def test_insert_from_dataframe(self, sqlitecloud_dbapi2_connection): + conn = sqlitecloud_dbapi2_connection + + dfprices = pd.read_csv( + os.path.join(os.path.dirname(__file__), "../assets/prices.csv") + ) + + dfmapping = pd.DataFrame( + { + "AXP": ["American Express Company"], + "GE": ["General Electric Company"], + "GS": ["Goldman Sachs Group Inc"], + "UTX": ["United Technologies Corporation"], + } + ) + + conn.executemany( + "DROP TABLE IF EXISTS ?", [("CLOSING_PRICES",), ("TICKER_MAPPING",)] + ) + + # arg if_exists="replace" raises the error + dfprices.to_sql("CLOSING_PRICES", conn, index=False) + dfmapping.to_sql("TICKER_MAPPING", conn, index=False) + + df_actual_tables = pd.read_sql( + "SELECT name FROM sqlite_master WHERE type='table'", conn + ) + df_actual_prices = pd.read_sql("SELECT * FROM CLOSING_PRICES", conn) + df_actual_mapping = pd.read_sql("SELECT * FROM TICKER_MAPPING", conn) + + assert "CLOSING_PRICES" in df_actual_tables["name"].to_list() + assert "TICKER_MAPPING" in df_actual_tables["name"].to_list() + assert_frame_equal( + df_actual_prices, + dfprices, + check_exact=False, + atol=1e-6, + check_dtype=False, + ) + assert_frame_equal( + df_actual_mapping, + dfmapping, + check_exact=False, + atol=1e-6, + check_dtype=False, + ) + + def test_select_into_dataframe(self, sqlitecloud_dbapi2_connection): + conn = sqlitecloud_dbapi2_connection + + query = "SELECT * FROM albums" + df = pd.read_sql_query(query, conn) + cursor = conn.execute(query) + + assert df.columns.to_list() == [ + description[0] for description in cursor.description + ] + # compare as tuples + assert list(df.itertuples(index=False, name=None)) == cursor.fetchall() diff --git a/src/tests/integration/test_sqlite3_parity.py b/src/tests/integration/test_sqlite3_parity.py index c3740c4..b322e0d 100644 --- a/src/tests/integration/test_sqlite3_parity.py +++ b/src/tests/integration/test_sqlite3_parity.py @@ -1 +1,245 @@ -# TODO: sqlite3 to sqlitecloud comparison +import os +import sqlite3 + +import pytest + +from sqlitecloud.types import SQCloudException + + +class TestSQLite3FeatureParity: + @pytest.fixture() + def sqlite3_connection(self): + connection = sqlite3.connect( + os.path.join(os.path.dirname(__file__), "../assets/chinook.sqlite") + ) + yield connection + connection.close() + + def test_connection_close(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + sqlitecloud_connection.close() + sqlite3_connection.close() + + with pytest.raises(SQCloudException) as e: + sqlitecloud_connection.execute("SELECT 1") + + assert isinstance(e.value, SQCloudException) + + with pytest.raises(sqlite3.ProgrammingError) as e: + sqlite3_connection.execute("SELECT 1") + + assert isinstance(e.value, sqlite3.ProgrammingError) + + @pytest.mark.skip( + reason="SQLite Cloud does not convert to int a column without an explicit SQLite Type" + ) + def test_ping_select(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + sqlitecloud_cursor = sqlitecloud_connection.execute("SELECT 1") + sqlite3_cursor = sqlite3_connection.execute("SELECT 1") + + sqlitecloud_cursor = sqlitecloud_cursor.fetchall() + sqlite3_cursor = sqlite3_cursor.fetchall() + + assert sqlitecloud_cursor == sqlite3_cursor + + def test_create_table_and_insert_many( + self, sqlitecloud_dbapi2_connection, sqlite3_connection + ): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + create_table_query = "CREATE TABLE IF NOT EXISTS sqlitetest (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)" + sqlitecloud_connection.execute(create_table_query) + sqlite3_connection.execute(create_table_query) + + truncate_table_query = "DELETE FROM sqlitetest" + sqlitecloud_connection.execute(truncate_table_query) + sqlite3_connection.execute(truncate_table_query) + + insert_query = "INSERT INTO sqlitetest (name, age) VALUES (?, ?)" + params = [("Alice", 25), ("Bob", 30)] + sqlitecloud_connection.executemany(insert_query, params) + sqlite3_connection.executemany(insert_query, params) + + select_query = "SELECT * FROM sqlitetest" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + + def test_execute_with_question_mark_style( + self, sqlitecloud_dbapi2_connection, sqlite3_connection + ): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums WHERE AlbumId = ?" + params = (1,) + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query, params) + sqlite3_cursor = sqlite3_connection.execute(select_query, params) + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + + def test_execute_with_named_param_style( + self, sqlitecloud_dbapi2_connection, sqlite3_connection + ): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums WHERE AlbumId = :id" + params = {"id": 1} + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query, params) + sqlite3_cursor = sqlite3_connection.execute(select_query, params) + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + + @pytest.mark.skip( + reason="Rowcount does not contain the number of inserted rows yet" + ) + def test_insert_result(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + insert_query = "INSERT INTO albums (Title, ArtistId) VALUES (?, ?)" + params = ("Test Album", 1) + sqlitecloud_cursor = sqlitecloud_connection.execute(insert_query, params) + sqlite3_cursor = sqlite3_connection.execute(insert_query, params) + + assert sqlitecloud_cursor.rowcount == sqlite3_cursor.rowcount + + def test_close_cursor_raises_exception( + self, sqlitecloud_dbapi2_connection, sqlite3_connection + ): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT 1" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_cursor.close() + sqlite3_cursor.close() + + with pytest.raises(SQCloudException) as e: + sqlitecloud_cursor.fetchall() + + assert isinstance(e.value, SQCloudException) + + with pytest.raises(sqlite3.ProgrammingError) as e: + sqlite3_cursor.fetchall() + + def test_row_factory(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + def simple_factory(cursor, row): + return { + description[0]: row[i] + for i, description in enumerate(cursor.description) + } + + sqlitecloud_connection.row_factory = simple_factory + sqlite3_connection.row_factory = simple_factory + + select_query = "SELECT * FROM albums WHERE AlbumId = 1" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + assert sqlitecloud_results[0]["Title"] == sqlite3_results[0]["Title"] + + def test_description(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums WHERE AlbumId = 1" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + assert sqlitecloud_cursor.description == sqlite3_cursor.description + assert sqlitecloud_cursor.description[1][0] == "Title" + assert sqlite3_cursor.description[1][0] == "Title" + + def test_fetch_one(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums WHERE AlbumId = 1" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_result = sqlitecloud_cursor.fetchone() + sqlite3_result = sqlite3_cursor.fetchone() + + assert sqlitecloud_result == sqlite3_result + + sqlitecloud_result = sqlitecloud_cursor.fetchone() + sqlite3_result = sqlite3_cursor.fetchone() + + assert sqlitecloud_result is None + assert sqlite3_result is None + + def test_fatchmany(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_results = sqlitecloud_cursor.fetchmany(2) + sqlite3_results = sqlite3_cursor.fetchmany(2) + + assert len(sqlitecloud_results) == 2 + assert len(sqlite3_results) == 2 + assert sqlitecloud_results == sqlite3_results + + def test_fetchmany_more_then_available( + self, sqlitecloud_dbapi2_connection, sqlite3_connection + ): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums LIMIT 3" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_results = sqlitecloud_cursor.fetchmany(100) + sqlite3_results = sqlite3_cursor.fetchmany(100) + + assert sqlitecloud_results == sqlite3_results + assert len(sqlitecloud_results) == 3 + assert len(sqlite3_results) == 3 + + sqlitecloud_results = sqlitecloud_cursor.fetchmany(100) + sqlite3_results = sqlite3_cursor.fetchmany(100) + + assert sqlitecloud_results == sqlite3_results + assert len(sqlitecloud_results) == 0 + assert len(sqlite3_results) == 0 + + def test_fetchall(self, sqlitecloud_dbapi2_connection, sqlite3_connection): + sqlitecloud_connection = sqlitecloud_dbapi2_connection + + select_query = "SELECT * FROM albums LIMIT 5" + sqlitecloud_cursor = sqlitecloud_connection.execute(select_query) + sqlite3_cursor = sqlite3_connection.execute(select_query) + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + assert len(sqlitecloud_results) == 5 + assert len(sqlite3_results) == 5 + + sqlitecloud_results = sqlitecloud_cursor.fetchall() + sqlite3_results = sqlite3_cursor.fetchall() + + assert sqlitecloud_results == sqlite3_results + assert len(sqlitecloud_results) == 0 + assert len(sqlite3_results) == 0 diff --git a/src/tests/unit/test_dbapi2.py b/src/tests/unit/test_dbapi2.py index 3df6077..23dd970 100644 --- a/src/tests/unit/test_dbapi2.py +++ b/src/tests/unit/test_dbapi2.py @@ -1,3 +1,4 @@ +import pytest from pytest_mock import MockerFixture import sqlitecloud @@ -5,7 +6,12 @@ from sqlitecloud.dbapi2 import Connection from sqlitecloud.driver import Driver from sqlitecloud.resultset import SQCloudResult -from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCloudConfig, SqliteCloudAccount +from sqlitecloud.types import ( + SQCLOUD_RESULT_TYPE, + SQCloudConfig, + SQCloudException, + SqliteCloudAccount, +) def test_connect_with_account_and_config(mocker: MockerFixture): @@ -288,7 +294,6 @@ def test_iterator(self, mocker): cursor._resultset = result assert list(cursor) == [("myname1",), ("myname2",)] - assert len(cursor) == 2 def test_row_factory(self, mocker): conn = Connection(mocker.patch("sqlitecloud.types.SQCloudConnect")) @@ -304,3 +309,26 @@ def test_row_factory(self, mocker): cursor._resultset = result assert cursor.fetchone() == {"name": "myname1"} + + @pytest.mark.parametrize( + "method, args", + [ + ("execute", ("",)), + ("executemany", ("", [])), + ("fetchone", ()), + ("fetchmany", ()), + ("fetchall", ()), + ("close", ()), + ], + ) + def test_close_raises_expected_exception_on_any_further_operation( + self, method, args, mocker + ): + cursor = Cursor(mocker.patch("sqlitecloud.Connection")) + + cursor.close() + + with pytest.raises(SQCloudException) as e: + getattr(cursor, method)(*args) + + assert e.value.args[0] == "The cursor is closed." From c1e31f504738f311df4677cde5d7c3fc3cc54024 Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Thu, 30 May 2024 15:39:16 +0000 Subject: [PATCH 3/6] Non-linearizable is executed before auth command --- src/sqlitecloud/driver.py | 7 +++--- src/tests/unit/test_driver.py | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/sqlitecloud/driver.py b/src/sqlitecloud/driver.py index ef50fcb..e8079e6 100644 --- a/src/sqlitecloud/driver.py +++ b/src/sqlitecloud/driver.py @@ -430,6 +430,10 @@ def _internal_config_apply( buffer = "" + # it must be executed before authentication command + if config.non_linearizable: + buffer += "SET CLIENT KEY NONLINEARIZABLE TO 1;" + if config.account.apikey: buffer += f"AUTH APIKEY {config.account.apikey};" @@ -448,9 +452,6 @@ def _internal_config_apply( if config.zerotext: buffer += "SET CLIENT KEY ZEROTEXT TO 1;" - if config.non_linearizable: - buffer += "SET CLIENT KEY NONLINEARIZABLE TO 1;" - if config.noblob: buffer += "SET CLIENT KEY NOBLOB TO 1;" diff --git a/src/tests/unit/test_driver.py b/src/tests/unit/test_driver.py index 4dac64b..d994ce3 100644 --- a/src/tests/unit/test_driver.py +++ b/src/tests/unit/test_driver.py @@ -1,6 +1,8 @@ import pytest +from pytest_mock import MockerFixture from sqlitecloud.driver import Driver +from sqlitecloud.types import SQCloudConfig, SqliteCloudAccount class TestDriver: @@ -212,3 +214,46 @@ def test_escape_sql_parameter_with_dict(self): driver.escape_sql_parameter(param) assert expected_result + + def test_nonlinearizable_command_before_auth_with_account( + self, mocker: MockerFixture + ): + driver = Driver() + + config = SQCloudConfig() + config.account = SqliteCloudAccount() + config.account.username = "pippo" + config.account.password = "pluto" + config.non_linearizable = True + + mocker.patch.object(driver, "_internal_connect", return_value=None) + run_command_mock = mocker.patch.object(driver, "_internal_run_command") + + driver.connect("myhost", 8860, config) + + expected_buffer = ( + "SET CLIENT KEY NONLINEARIZABLE TO 1;AUTH USER pippo PASSWORD pluto;" + ) + + run_command_mock.assert_called_once() + assert run_command_mock.call_args[0][1] == expected_buffer + + def test_nonlinearizable_command_before_auth_with_apikey( + self, mocker: MockerFixture + ): + driver = Driver() + + config = SQCloudConfig() + config.account = SqliteCloudAccount() + config.account.apikey = "abc123" + config.non_linearizable = True + + mocker.patch.object(driver, "_internal_connect", return_value=None) + run_command_mock = mocker.patch.object(driver, "_internal_run_command") + + driver.connect("myhost", 8860, config) + + expected_buffer = "SET CLIENT KEY NONLINEARIZABLE TO 1;AUTH APIKEY abc123;" + + run_command_mock.assert_called_once() + assert run_command_mock.call_args[0][1] == expected_buffer From f41245cf4655b13215e5f6e96f3f4e51fa1410a6 Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Fri, 31 May 2024 09:34:08 +0000 Subject: [PATCH 4/6] README and samples in Jupyter notebook --- README.md | 140 +++++++++++------ samples.ipynb | 217 +++++++++++++++++++++------ src/tests/assets/prices.csv | 41 +++-- src/tests/integration/test_pandas.py | 10 +- 4 files changed, 293 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 609995c..e60777c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Python SDK for SQLite Cloud +# Driver for SQLite Cloud

SQLite Cloud logo @@ -11,74 +11,124 @@ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqlitecloud?link=https%3A%2F%2Fpypi.org%2Fproject%2FSqliteCloud%2F) -[SQLiteCloud](https://sqlitecloud.io) is a powerful Python package that allows you to interact with the SQLite Cloud backend server seamlessly. It provides methods for various database operations. This package is designed to simplify database operations in Python applications, making it easier than ever to work with SQLite Cloud. +- [Driver for SQLite Cloud](#driver-for-sqlite-cloud) +- [Example](#example) +- [SQLite Cloud loves sqlite3](#sqlite-cloud-loves-sqlite3) +- [SQLite Cloud for Pandas DataFrame](#sqlite-cloud-for-pandas-dataframe) -- Site: [https://sqlitecloud.io](https://sqlitecloud.io/developers) -- Documentation: https://..._coming!_ -- Source: [https://github.com/sqlitecloud/python](https://github.com/sqlitecloud/python) +--- + +[SQLiteCloud](https://sqlitecloud.io) is a powerful Python package that allows you to interact with the SQLite Cloud database seamlessly. It provides methods for various database operations. This package is designed to simplify database operations in Python applications, making it easier than ever to work with SQLite Cloud. + + +#### Compatibility with sqlite3 API -## Installation +We aim for full compatibility with the Python built-in [sqlite3](https://docs.python.org/3.6/library/sqlite3.html) API (based on Python [PEP 249](https://peps.python.org/pep-0249)), with the primary distinction being that our driver connects to SQLite Cloud databases. This allows you to migrate your local SQLite databases to SQLite Cloud without needing to modify your existing Python code that uses the sqlite3 API. -You can install SqliteCloud Package using Python Package Index (PYPI): +- Documentation: Our API closely follows the sqlite3 API. You can refer to the sqlite3 documentation for most functionality. The list of implemented features are documented [here](https://github.com/sqlitecloud/python/issues/8). +- Source: [https://github.com/sqlitecloud/python](https://github.com/sqlitecloud/python) +- Site: [https://sqlitecloud.io](https://sqlitecloud.io/developers) + +## Example ```bash $ pip install sqlitecloud ``` -## Usage -


- ```python -from sqlitecloud.client import SqliteCloudClient -from sqlitecloud.types import SqliteCloudAccount -``` +import sqlitecloud -### _Init a connection_ +# Open the connection to SQLite Cloud +conn = sqlitecloud.connect("sqlitecloud://myhost.sqlite.cloud:8860?apikey=myapikey") -#### Using explicit configuration +# You can autoselect the database during the connect call +# by adding the database name as path of the SQLite Cloud +# connection string, eg: +# conn = sqlitecloud.connect("sqlitecloud://myhost.sqlite.cloud:8860/mydatabase?apikey=myapikey") +db_name = "chinook.sqlite" +conn.execute(f"USE DATABASE {db_name}") -```python -account = SqliteCloudAccount(user, password, host, db_name, port) -client = SqliteCloudClient(cloud_account=account) -conn = client.open_connection() -``` +cursor = conn.execute("SELECT * FROM albums WHERE AlbumId = ?", (1, )) +result = cursor.fetchone() -#### _Using string configuration_ +print(result) -```python -account = SqliteCloudAccount("sqlitecloud://user:pass@host.com:port/dbname?apikey=myapikey") -client = SqliteCloudClient(cloud_account=account) -conn = client.open_connection() +conn.close() ``` -### _Execute a query_ -You can bind values to parametric queries: you can pass parameters as positional values in an array +## sqlitecloud loves sqlite3 + +Is your project based on the `sqlite3` library to interact with a SQLite database? + +Just install `sqlitecloud` package from `pip` and change the module name! That's it! + +Try it yourself: + ```python -result = client.exec_query( - "SELECT * FROM table_name WHERE id = 1" - conn=conn +# import sqlitecloud +import sqlite3 + +# comment out the following line... +conn = sqlite3.connect(":memory:") + +# ... and uncomment this line and import the sqlitecloud package +# (add the database name like in this connection string) +# conn = sqlitecloud.connect("sqlitecloud://myhost.sqlite.cloud:8860/mydatabase.sqlite?apikey=myapikey") + +conn.execute("CREATE TABLE IF NOT EXISTS producers (ProducerId INTEGER PRIMARY KEY, name TEXT, year INTEGER)") +conn.executemany( + "INSERT INTO producers (name, year) VALUES (?, ?)", + [("Sony Music Entertainment", 2020), ("EMI Music Publishing", 2021)], ) -``` -### _Iterate result_ -result is an iterable object -```python -for row in result: +cursor = conn.execute("SELECT * FROM cars") + +for row in cursor: print(row) ``` -### _Specific value_ -```python -result.get_value(0, 0) -``` +## SQLite Cloud for Pandas DataFrame -### _Column name_ -```python -result.get_name(0) -``` +[Pandas](https://pypi.org/project/pandas/) is a Python package for data manipulation and analysis. It provides high-performance, easy-to-use data structures, such as DataFrame. + +Use the connection to SQLite Cloud to: +- Insert data from a DataFrame into a SQLite Cloud database. +- Query SQLite Cloud and fetch the results into a DataFrame for further analysis. -### _Close connection_ +Example: ```python -client.disconnect(conn) +import io + +import pandas as pd + +import sqlitecloud + +dfprices = pd.read_csv( + io.StringIO( + """DATE,CURRENCY,PRICE + 20230504,USD,201.23456 + 20230503,USD,12.34567 + 20230502,USD,23.45678 + 20230501,USD,34.56789""" + ) +) + +conn = sqlitecloud.connect("sqlitecloud://myhost.sqlite.cloud:8860/mydatabase.sqlite?apikey=myapikey") + +conn.executemany("DROP TABLE IF EXISTS ?", [("PRICES",)]) + +# Write the dataframe to the SQLite Cloud database as a table PRICES +dfprices.to_sql("PRICES", conn, index=False) + +# Create the dataframe from the table PRICES on the SQLite Cloud database +df_actual_prices = pd.read_sql("SELECT * FROM PRICES", conn) + +# Inspect the dataframe +print(df_actual_prices.head()) + +# Perform a simple query on the dataframe +query_result = df_actual_prices.query("PRICE > 50.00") + +print(query_result) ``` diff --git a/samples.ipynb b/samples.ipynb index 7f9d9e7..7a35b90 100644 --- a/samples.ipynb +++ b/samples.ipynb @@ -4,130 +4,263 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Import SqliteCloudClient and SqliteCloudAccount\n", + "# How to query SQLite Cloud database (SQLite3 API)\n", "\n", - "SqliteCloudAccount is the class rapresenting your auth data for SqliteCloud\n", + "Before start:\n", "\n", - "SqliteCloudClient is the class managing the connection for you" + "1. Create an account for free on [sqlitecloud.io](https://sqlitecloud.io/).\n", + "2. Copy your connection string (keep it secret! It contains your API KEY).\n", + "\n", + "Let's start!" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "import sys\n", - "\n", - "sys.path.append('/workspaces/python/src')\n", - "\n", - "from sqlitecloud.client import SqliteCloudClient\n", - "from sqlitecloud.types import SqliteCloudAccount" + "%pip install sqlitecloud" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 3, "metadata": {}, + "outputs": [], "source": [ - "## Init a connection\n", + "import sqlitecloud\n", "\n", - "Initialize the client with account connection info" + "# You can autoselect the database during the connection by adding the \n", + "# database name as path of the SQLite Cloud connection string, eg:\n", + "# conn = sqlitecloud.connect(\"sqlitecloud://myhost.sqlite.cloud:8860/mydatabase?apikey=myapikey\")\n", + "sqlitecloud_connection_string = \"sqlitecloud://myhost.sqlite.cloud:8860/mydatabase?apikey=myapikey\"\n" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "account = SqliteCloudAccount(user, password, host, db_name, 8860)\n", - "client = SqliteCloudClient(cloud_account=account)\n", - "conn = client.open_connection()" + "# Open the connection to SQLite Cloud\n", + "conn = sqlitecloud.connect(sqlitecloud_connection_string)\n" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'chinook.sqlite'" + "" ] }, - "execution_count": 10, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "db_name" + "# Select the database to use if not specified in the connection string\n", + "db_name = \"chinook.sqlite\"\n", + "conn.execute(f\"USE DATABASE {db_name}\")\n" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 6, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 'For Those About To Rock We Salute You', 1)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "Then execute the query" + "# The execution of the query generate the `Cursor` object\n", + "# to fetch the results.\n", + "cursor = conn.execute(\"SELECT * FROM albums\")\n", + "cursor.fetchone()\n" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(31, 'My brand new genre')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# You can use the cursor to perform other queries.\n", + "# Queries can be prepared with `question mark` and `named` style\n", + "cursor = conn.execute(\"INSERT INTO genres (Name) values (?)\", (\"My brand new genre\",))\n", + "\n", + "cursor.execute(\"SELECT * FROM genres WHERE Name like :name\", {\"name\": \"My brand%\"})\n", + "\n", + "cursor.fetchone()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "query = \"select * from employees;\"\n", - "result = client.exec_query(query, conn)" + "# When you are done clean up the connection\n", + "conn.close()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The result is an iterable" + "### sqlitecloud loves sqlite3\n", + "\n", + "Is your project based on the `sqlite3` library to interact with a SQLite database?\n", + "\n", + "Just install `sqlitecloud` package from `pip` and change the module name! That's it!\n", + "\n", + "Try it yourself:" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'EmployeeId': '1', 'LastName': 'Adams', 'FirstName': 'Andrew', 'Title': 'General Manager', 'ReportsTo': None, 'BirthDate': '1962-02-18 00:00:00', 'HireDate': '2002-08-14 00:00:00', 'Address': '11120 Jasper Ave NW', 'City': 'Edmonton', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T5K 2N1', 'Phone': '+1 (780) 428-9482', 'Fax': '+1 (780) 428-3457', 'Email': 'andrew@chinookcorp.com'}\n", - "{'EmployeeId': '2', 'LastName': 'Edwards', 'FirstName': 'Nancy', 'Title': 'Sales Manager', 'ReportsTo': '1', 'BirthDate': '1958-12-08 00:00:00', 'HireDate': '2002-05-01 00:00:00', 'Address': '825 8 Ave SW', 'City': 'Calgary', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T2P 2T3', 'Phone': '+1 (403) 262-3443', 'Fax': '+1 (403) 262-3322', 'Email': 'nancy@chinookcorp.com'}\n", - "{'EmployeeId': '3', 'LastName': 'Peacock', 'FirstName': 'Jane', 'Title': 'Sales Support Agent', 'ReportsTo': '2', 'BirthDate': '1973-08-29 00:00:00', 'HireDate': '2002-04-01 00:00:00', 'Address': '1111 6 Ave SW', 'City': 'Calgary', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T2P 5M5', 'Phone': '+1 (403) 262-3443', 'Fax': '+1 (403) 262-6712', 'Email': 'jane@chinookcorp.com'}\n", - "{'EmployeeId': '4', 'LastName': 'Park', 'FirstName': 'Margaret', 'Title': 'Sales Support Agent', 'ReportsTo': '2', 'BirthDate': '1947-09-19 00:00:00', 'HireDate': '2003-05-03 00:00:00', 'Address': '683 10 Street SW', 'City': 'Calgary', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T2P 5G3', 'Phone': '+1 (403) 263-4423', 'Fax': '+1 (403) 263-4289', 'Email': 'margaret@chinookcorp.com'}\n", - "{'EmployeeId': '5', 'LastName': 'Johnson', 'FirstName': 'Steve', 'Title': 'Sales Support Agent', 'ReportsTo': '2', 'BirthDate': '1965-03-03 00:00:00', 'HireDate': '2003-10-17 00:00:00', 'Address': '7727B 41 Ave', 'City': 'Calgary', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T3B 1Y7', 'Phone': '1 (780) 836-9987', 'Fax': '1 (780) 836-9543', 'Email': 'steve@chinookcorp.com'}\n", - "{'EmployeeId': '6', 'LastName': 'Mitchell', 'FirstName': 'Michael', 'Title': 'IT Manager', 'ReportsTo': '1', 'BirthDate': '1973-07-01 00:00:00', 'HireDate': '2003-10-17 00:00:00', 'Address': '5827 Bowness Road NW', 'City': 'Calgary', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T3B 0C5', 'Phone': '+1 (403) 246-9887', 'Fax': '+1 (403) 246-9899', 'Email': 'michael@chinookcorp.com'}\n", - "{'EmployeeId': '7', 'LastName': 'King', 'FirstName': 'Robert', 'Title': 'IT Staff', 'ReportsTo': '6', 'BirthDate': '1970-05-29 00:00:00', 'HireDate': '2004-01-02 00:00:00', 'Address': '590 Columbia Boulevard West', 'City': 'Lethbridge', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T1K 5N8', 'Phone': '+1 (403) 456-9986', 'Fax': '+1 (403) 456-8485', 'Email': 'robert@chinookcorp.com'}\n", - "{'EmployeeId': '8', 'LastName': 'Callahan', 'FirstName': 'Laura', 'Title': 'IT Staff', 'ReportsTo': '6', 'BirthDate': '1968-01-09 00:00:00', 'HireDate': '2004-03-04 00:00:00', 'Address': '923 7 ST NW', 'City': 'Lethbridge', 'State': 'AB', 'Country': 'Canada', 'PostalCode': 'T1H 1Y8', 'Phone': '+1 (403) 467-3351', 'Fax': '+1 (403) 467-8772', 'Email': 'laura@chinookcorp.com'}\n" + "(1, 'Sony Music Entertainment', 2020)\n", + "(2, 'EMI Music Publishing', 2021)\n" ] } ], "source": [ - "for r in result:\n", - " print(r)" + "import sqlite3\n", + "\n", + "import sqlitecloud\n", + "\n", + "# Comment out the following line\n", + "conn = sqlite3.connect(\":memory:\")\n", + "# and uncomment this one to use the sqlitecloud package\n", + "# conn = sqlitecloud.connect(sqlitecloud_connection_string)\n", + "\n", + "conn.execute(\n", + " \"CREATE TABLE IF NOT EXISTS producers (ProducerId INTEGER PRIMARY KEY, name TEXT, year INTEGER)\"\n", + ")\n", + "conn.executemany(\n", + " \"INSERT INTO producers (name, year) VALUES (?, ?)\",\n", + " [(\"Sony Music Entertainment\", 2020), (\"EMI Music Publishing\", 2021)],\n", + ")\n", + "\n", + "cursor = conn.execute(\"SELECT * FROM producers\")\n", + "\n", + "for row in cursor:\n", + " print(row)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Whe you are done clean up the connection" + "### SQLite Cloud for Pandas DataFrame\n", + "\n", + "[Pandas](https://pypi.org/project/pandas/) is a Python package for data manipulation and analysis. It provides high-performance, easy-to-use data structures, such as DataFrame.\n", + "\n", + "Use the connection to SQLite Cloud to:\n", + "- Insert data from a DataFrame into a SQLite Cloud database.\n", + "- Query SQLite Cloud and fetch the results into a DataFrame for further analysis.\n", + "\n", + "Example:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " DATE CURRENCY PRICE\n", + "0 20230504 USD 201.23456\n", + "1 20230503 USD 12.34567\n", + "2 20230502 USD 23.45678\n", + "3 20230501 USD 34.56789\n" + ] + } + ], "source": [ - "client.disconnect(conn)\n" + "import io\n", + "\n", + "import pandas as pd\n", + "\n", + "import sqlitecloud\n", + "\n", + "dfprices = pd.read_csv(\n", + " io.StringIO(\n", + " \"\"\"DATE,CURRENCY,PRICE\n", + " 20230504,USD,201.23456\n", + " 20230503,USD,12.34567\n", + " 20230502,USD,23.45678\n", + " 20230501,USD,34.56789\"\"\"\n", + " )\n", + ")\n", + "\n", + "conn = sqlitecloud.connect(sqlitecloud_connection_string)\n", + "\n", + "conn.executemany(\"DROP TABLE IF EXISTS ?\", [(\"PRICES\",)])\n", + "\n", + "# Write the dataframe to the SQLite Cloud database as a table PRICES\n", + "dfprices.to_sql(\"PRICES\", conn, index=False)\n", + "\n", + "# Create the dataframe from the table PRICES on the SQLite Cloud database\n", + "df_actual_prices = pd.read_sql(\"SELECT * FROM PRICES\", conn)\n", + "\n", + "# Inspect the dataframe\n", + "print(df_actual_prices.head())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " DATE CURRENCY PRICE\n", + "0 20230504 USD 201.23456\n" + ] + } + ], + "source": [ + "# Perform a simple query on the dataframe\n", + "query_result = df_actual_prices.query(\"PRICE > 50.00\")\n", + "\n", + "print(query_result)" ] } ], diff --git a/src/tests/assets/prices.csv b/src/tests/assets/prices.csv index f55746f..4c822e5 100644 --- a/src/tests/assets/prices.csv +++ b/src/tests/assets/prices.csv @@ -1,22 +1,19 @@ -DATE,TICKER,CLOSE -20161125,GE,31.440001 -20161123,GE,31.34 -20161122,GE,31.18 -20161121,GE,30.870001 -20161118,GE,30.67 -20161117,GE,30.790001 -20161116,GE,30.74 -20161115,GE,30.75 -20161114,GE,30.51 -20161111,GE,30.709999 -20161110,GE,30.41 -20161109,GE,29.629999 -20161108,GE,29.42 -20161107,GE,29.309999 -20161104,GE,28.440001 -20161103,GE,28.280001 -20161102,GE,28.49 -20161101,GE,28.879999 -20161031,GE,29.1 -20161028,GE,29.219999 -20161027,GE,28.629999 +DATE,CURRENCY,PRICE +20230504,USD,201.23456 +20230503,USD,12.34567 +20230502,USD,23.45678 +20230501,USD,34.56789 +20230430,USD,45.6789 +20230425,USD,90.12345 +20230424,USD,1.23456 +20230423,USD,2.34567 +20230422,USD,3.45678 +20230421,USD,4.56789 +20230416,USD,9.01234 +20230415,USD,0.12345 +20230414,USD,0.23456 +20230413,USD,0.34567 +20230412,USD,0.45678 +20230411,USD,0.56789 +20230410,USD,0.67890 +20230409,USD,0.78901 diff --git a/src/tests/integration/test_pandas.py b/src/tests/integration/test_pandas.py index 4dfa136..799627b 100644 --- a/src/tests/integration/test_pandas.py +++ b/src/tests/integration/test_pandas.py @@ -22,21 +22,19 @@ def test_insert_from_dataframe(self, sqlitecloud_dbapi2_connection): } ) - conn.executemany( - "DROP TABLE IF EXISTS ?", [("CLOSING_PRICES",), ("TICKER_MAPPING",)] - ) + conn.executemany("DROP TABLE IF EXISTS ?", [("PRICES",), ("TICKER_MAPPING",)]) # arg if_exists="replace" raises the error - dfprices.to_sql("CLOSING_PRICES", conn, index=False) + dfprices.to_sql("PRICES", conn, index=False) dfmapping.to_sql("TICKER_MAPPING", conn, index=False) df_actual_tables = pd.read_sql( "SELECT name FROM sqlite_master WHERE type='table'", conn ) - df_actual_prices = pd.read_sql("SELECT * FROM CLOSING_PRICES", conn) + df_actual_prices = pd.read_sql("SELECT * FROM PRICES", conn) df_actual_mapping = pd.read_sql("SELECT * FROM TICKER_MAPPING", conn) - assert "CLOSING_PRICES" in df_actual_tables["name"].to_list() + assert "PRICES" in df_actual_tables["name"].to_list() assert "TICKER_MAPPING" in df_actual_tables["name"].to_list() assert_frame_equal( df_actual_prices, From cd34018b17504eb080f8b09ba2fa015f263f8cdd Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Mon, 3 Jun 2024 07:41:46 +0000 Subject: [PATCH 5/6] feat(nonlinearizable): #13 - add alias for connection string --- src/sqlitecloud/driver.py | 2 -- src/sqlitecloud/types.py | 4 ++++ src/tests/unit/test_driver.py | 3 +-- src/tests/unit/test_types.py | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/tests/unit/test_types.py diff --git a/src/sqlitecloud/driver.py b/src/sqlitecloud/driver.py index e8079e6..4395b26 100644 --- a/src/sqlitecloud/driver.py +++ b/src/sqlitecloud/driver.py @@ -97,8 +97,6 @@ def prepare_statement( Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] ], ) -> str: - # TODO: check for right exception is case of wrong number of parameters - # If parameters is a dictionary, replace the keys in the query with the values if isinstance(parameters, dict): for key, value in parameters.items(): diff --git a/src/sqlitecloud/types.py b/src/sqlitecloud/types.py index 41dee4d..2a2a63a 100644 --- a/src/sqlitecloud/types.py +++ b/src/sqlitecloud/types.py @@ -212,6 +212,10 @@ def _parse_connection_string(self, connection_string) -> None: else: value = value + # alias + if opt == "nonlinearizable": + opt = "non_linearizable" + if hasattr(self, opt): setattr(self, opt, value) elif hasattr(self.account, opt): diff --git a/src/tests/unit/test_driver.py b/src/tests/unit/test_driver.py index d994ce3..fadb084 100644 --- a/src/tests/unit/test_driver.py +++ b/src/tests/unit/test_driver.py @@ -114,8 +114,7 @@ def test_prepare_statement_with_dict_parameters(self): assert expected_result == result - # TODO: should rise exception - def test_prepare_statement_with_missing_parameters(self): + def test_prepare_statement_with_missing_parameters_does_not_raise_exception(self): driver = Driver() query = "UPDATE users SET name = :name, age = :age WHERE id = :id" diff --git a/src/tests/unit/test_types.py b/src/tests/unit/test_types.py new file mode 100644 index 0000000..9b56754 --- /dev/null +++ b/src/tests/unit/test_types.py @@ -0,0 +1,19 @@ +import pytest + +from sqlitecloud.types import SQCloudConfig + + +class TestSQCloudConfig: + @pytest.mark.parametrize( + "param, value", + [ + ("non_linearizable", True), + ("nonlinearizable", True), + ], + ) + def test_parse_connection_string_with_nonlinarizable(self, param: str, value: any): + connection_string = f"sqlitecloud://myhost.sqlitecloud.io?{param}={value}" + + config = SQCloudConfig(connection_string) + + assert config.non_linearizable From 4b8f6193b785f5664f5019f4c4412cc795f07717 Mon Sep 17 00:00:00 2001 From: Daniele Briggi Date: Mon, 3 Jun 2024 08:04:32 +0000 Subject: [PATCH 6/6] refact: standard prefix for classes and consts --- .gitignore | 1 - bandit-baseline.json | 126 ++++-- src/setup.py | 2 +- src/sqlitecloud/client.py | 54 +-- src/sqlitecloud/dbapi2.py | 67 ++-- src/sqlitecloud/download.py | 6 +- src/sqlitecloud/driver.py | 401 ++++++++++--------- src/sqlitecloud/pubsub.py | 30 +- src/sqlitecloud/resultset.py | 24 +- src/sqlitecloud/types.py | 49 +-- src/sqlitecloud/upload.py | 8 +- src/tests/conftest.py | 12 +- src/tests/integration/test_client.py | 114 +++--- src/tests/integration/test_dbapi2.py | 18 +- src/tests/integration/test_download.py | 6 +- src/tests/integration/test_pubsub.py | 50 +-- src/tests/integration/test_sqlite3_parity.py | 10 +- src/tests/unit/test_client.py | 10 +- src/tests/unit/test_dbapi2.py | 52 +-- src/tests/unit/test_driver.py | 10 +- src/tests/unit/test_resultset.py | 54 +-- src/tests/unit/test_types.py | 6 +- 22 files changed, 595 insertions(+), 515 deletions(-) diff --git a/.gitignore b/.gitignore index 6aae48d..1593f97 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,5 @@ main.dSYM/ .idea SqliteCloud.egg-info -src/sqlitecloud/libsqcloud.so playground.ipynb diff --git a/bandit-baseline.json b/bandit-baseline.json index b53ebef..b3b2040 100644 --- a/bandit-baseline.json +++ b/bandit-baseline.json @@ -1,6 +1,6 @@ { "errors": [], - "generated_at": "2024-05-23T09:59:11Z", + "generated_at": "2024-06-03T07:52:17Z", "metrics": { "_totals": { "CONFIDENCE.HIGH": 0.0, @@ -11,7 +11,7 @@ "SEVERITY.LOW": 1.0, "SEVERITY.MEDIUM": 2.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 2096, + "loc": 3405, "nosec": 0 }, "src/setup.py": { @@ -23,7 +23,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 39, + "loc": 29, "nosec": 0 }, "src/sqlitecloud/__init__.py": { @@ -35,7 +35,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 1, + "loc": 3, "nosec": 0 }, "src/sqlitecloud/client.py": { @@ -47,10 +47,10 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 119, + "loc": 104, "nosec": 0 }, - "src/sqlitecloud/conn_info.py": { + "src/sqlitecloud/dbapi2.py": { "CONFIDENCE.HIGH": 0.0, "CONFIDENCE.LOW": 0.0, "CONFIDENCE.MEDIUM": 0.0, @@ -59,7 +59,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 6, + "loc": 377, "nosec": 0 }, "src/sqlitecloud/download.py": { @@ -83,7 +83,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 727, + "loc": 765, "nosec": 0 }, "src/sqlitecloud/pubsub.py": { @@ -107,7 +107,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 62, + "loc": 80, "nosec": 0 }, "src/sqlitecloud/types.py": { @@ -119,7 +119,7 @@ "SEVERITY.LOW": 1.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 143, + "loc": 194, "nosec": 0 }, "src/sqlitecloud/upload.py": { @@ -155,7 +155,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 22, + "loc": 35, "nosec": 0 }, "src/tests/integration/__init__.py": { @@ -179,7 +179,19 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 471, + "loc": 492, + "nosec": 0 + }, + "src/tests/integration/test_dbapi2.py": { + "CONFIDENCE.HIGH": 0.0, + "CONFIDENCE.LOW": 0.0, + "CONFIDENCE.MEDIUM": 0.0, + "CONFIDENCE.UNDEFINED": 0.0, + "SEVERITY.HIGH": 0.0, + "SEVERITY.LOW": 0.0, + "SEVERITY.MEDIUM": 0.0, + "SEVERITY.UNDEFINED": 0.0, + "loc": 181, "nosec": 0 }, "src/tests/integration/test_download.py": { @@ -206,6 +218,18 @@ "loc": 18, "nosec": 0 }, + "src/tests/integration/test_pandas.py": { + "CONFIDENCE.HIGH": 0.0, + "CONFIDENCE.LOW": 0.0, + "CONFIDENCE.MEDIUM": 0.0, + "CONFIDENCE.UNDEFINED": 0.0, + "SEVERITY.HIGH": 0.0, + "SEVERITY.LOW": 0.0, + "SEVERITY.MEDIUM": 0.0, + "SEVERITY.UNDEFINED": 0.0, + "loc": 50, + "nosec": 0 + }, "src/tests/integration/test_pubsub.py": { "CONFIDENCE.HIGH": 0.0, "CONFIDENCE.LOW": 1.0, @@ -215,7 +239,19 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 1.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 109, + "loc": 118, + "nosec": 0 + }, + "src/tests/integration/test_sqlite3_parity.py": { + "CONFIDENCE.HIGH": 0.0, + "CONFIDENCE.LOW": 0.0, + "CONFIDENCE.MEDIUM": 0.0, + "CONFIDENCE.UNDEFINED": 0.0, + "SEVERITY.HIGH": 0.0, + "SEVERITY.LOW": 0.0, + "SEVERITY.MEDIUM": 0.0, + "SEVERITY.UNDEFINED": 0.0, + "loc": 176, "nosec": 0 }, "src/tests/integration/test_upload.py": { @@ -242,6 +278,18 @@ "loc": 48, "nosec": 0 }, + "src/tests/unit/test_dbapi2.py": { + "CONFIDENCE.HIGH": 0.0, + "CONFIDENCE.LOW": 0.0, + "CONFIDENCE.MEDIUM": 0.0, + "CONFIDENCE.UNDEFINED": 0.0, + "SEVERITY.HIGH": 0.0, + "SEVERITY.LOW": 0.0, + "SEVERITY.MEDIUM": 0.0, + "SEVERITY.UNDEFINED": 0.0, + "loc": 242, + "nosec": 0 + }, "src/tests/unit/test_driver.py": { "CONFIDENCE.HIGH": 0.0, "CONFIDENCE.LOW": 0.0, @@ -251,7 +299,7 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 76, + "loc": 188, "nosec": 0 }, "src/tests/unit/test_resultset.py": { @@ -263,32 +311,32 @@ "SEVERITY.LOW": 0.0, "SEVERITY.MEDIUM": 0.0, "SEVERITY.UNDEFINED": 0.0, - "loc": 77, + "loc": 113, + "nosec": 0 + }, + "src/tests/unit/test_types.py": { + "CONFIDENCE.HIGH": 0.0, + "CONFIDENCE.LOW": 0.0, + "CONFIDENCE.MEDIUM": 0.0, + "CONFIDENCE.UNDEFINED": 0.0, + "SEVERITY.HIGH": 0.0, + "SEVERITY.LOW": 0.0, + "SEVERITY.MEDIUM": 0.0, + "SEVERITY.UNDEFINED": 0.0, + "loc": 14, "nosec": 0 } }, "results": [ { - "code": "95 class SqliteCloudAccount:\n96 def __init__(\n97 self,\n98 username: Optional[str] = \"\",\n99 password: Optional[str] = \"\",\n100 hostname: Optional[str] = \"\",\n101 dbname: Optional[str] = \"\",\n102 port: Optional[int] = SQCLOUD_DEFAULT.PORT.value,\n103 apikey: Optional[str] = \"\",\n104 ) -> None:\n105 # User name is required unless connectionstring is provided\n106 self.username = username\n107 # Password is required unless connection string is provided\n108 self.password = password\n109 # Password is hashed\n110 self.password_hashed = False\n111 # API key instead of username and password\n112 self.apikey = apikey\n113 # Name of database to open\n114 self.dbname = dbname\n115 # Like mynode.sqlitecloud.io\n116 self.hostname = hostname\n117 self.port = port\n118 \n", + "code": "107 class SQLiteCloudAccount:\n108 def __init__(\n109 self,\n110 username: Optional[str] = \"\",\n111 password: Optional[str] = \"\",\n112 hostname: str = \"\",\n113 dbname: Optional[str] = \"\",\n114 port: int = SQLITECLOUD_DEFAULT.PORT.value,\n115 apikey: Optional[str] = \"\",\n116 ) -> None:\n117 # User name is required unless connectionstring is provided\n118 self.username = username\n119 # Password is required unless connection string is provided\n120 self.password = password\n121 # Password is hashed\n122 self.password_hashed = False\n123 # API key instead of username and password\n124 self.apikey = apikey\n125 # Name of database to open\n126 self.dbname = dbname\n127 # Like mynode.sqlitecloud.io\n128 self.hostname = hostname\n129 self.port = port\n130 \n", "col_offset": 4, "filename": "src/sqlitecloud/types.py", "issue_confidence": "MEDIUM", "issue_severity": "LOW", "issue_text": "Possible hardcoded password: ''", - "line_number": 96, + "line_number": 108, "line_range": [ - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, 108, 109, 110, @@ -298,22 +346,34 @@ 114, 115, 116, - 117 + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129 ], "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b107_hardcoded_password_default.html", "test_id": "B107", "test_name": "hardcoded_password_default" }, { - "code": "155 client.exec_query(\n156 f\"UPDATE genres SET Name = '{new_name}' WHERE GenreId = 1;\", connection\n157 )\n", + "code": "164 client.exec_query(\n165 f\"UPDATE genres SET Name = '{new_name}' WHERE GenreId = 1;\", connection\n166 )\n", "col_offset": 12, "filename": "src/tests/integration/test_pubsub.py", "issue_confidence": "LOW", "issue_severity": "MEDIUM", "issue_text": "Possible SQL injection vector through string-based query construction.", - "line_number": 156, + "line_number": 165, "line_range": [ - 156 + 165 ], "more_info": "https://bandit.readthedocs.io/en/latest/plugins/b608_hardcoded_sql_expressions.html", "test_id": "B608", diff --git a/src/setup.py b/src/setup.py index 1760435..e653ac5 100644 --- a/src/setup.py +++ b/src/setup.py @@ -7,7 +7,7 @@ long_description = (Path(__file__).parent / "README.md").read_text() setup( - name="SqliteCloud", + name="sqlitecloud", version="0.0.77", author="sqlitecloud.io", description="A Python package for working with SQLite databases in the cloud.", diff --git a/src/sqlitecloud/client.py b/src/sqlitecloud/client.py index ead54cd..375fd36 100644 --- a/src/sqlitecloud/client.py +++ b/src/sqlitecloud/client.py @@ -4,24 +4,24 @@ from typing import Dict, Optional, Tuple, Union from sqlitecloud.driver import Driver -from sqlitecloud.resultset import SqliteCloudResultSet +from sqlitecloud.resultset import SQLiteCloudResultSet from sqlitecloud.types import ( - SQCloudConfig, - SQCloudConnect, - SQCloudException, - SqliteCloudAccount, + SQLiteCloudAccount, + SQLiteCloudConfig, + SQLiteCloudConnect, SQLiteCloudDataTypes, + SQLiteCloudException, ) -class SqliteCloudClient: +class SQLiteCloudClient: """ Client to interact with Sqlite Cloud """ def __init__( self, - cloud_account: Optional[SqliteCloudAccount] = None, + cloud_account: Optional[SQLiteCloudAccount] = None, connection_str: Optional[str] = None, ) -> None: """Initializes a new instance of the class with connection information. @@ -35,25 +35,25 @@ def __init__( """ self._driver = Driver() - self.config = SQCloudConfig() + self.config = SQLiteCloudConfig() if connection_str: - self.config = SQCloudConfig(connection_str) + self.config = SQLiteCloudConfig(connection_str) elif cloud_account: self.config.account = cloud_account if self.config.account is None: - raise SQCloudException("Missing connection parameters") + raise SQLiteCloudException("Missing connection parameters") - def open_connection(self) -> SQCloudConnect: - """Opens a connection to the SQCloud server. + def open_connection(self) -> SQLiteCloudConnect: + """Opens a connection to the SQLite Cloud server. Returns: - SQCloudConnect: An instance of the SQCloudConnect class representing - the connection to the SQCloud server. + SQLiteCloudConnect: An instance of the SQLiteCloudConnect class representing + the connection to the SQLite Cloud server. Raises: - SQCloudException: If an error occurs while opening the connection. + SQLiteCloudException: If an error occurs while opening the connection. """ connection = self._driver.connect( self.config.account.hostname, self.config.account.port, self.config @@ -61,22 +61,22 @@ def open_connection(self) -> SQCloudConnect: return connection - def disconnect(self, conn: SQCloudConnect) -> None: + def disconnect(self, conn: SQLiteCloudConnect) -> None: """Close the connection to the database.""" self._driver.disconnect(conn) - def is_connected(self, conn: SQCloudConnect) -> bool: + def is_connected(self, conn: SQLiteCloudConnect) -> bool: """Check if the connection is still open. Args: - conn (SQCloudConnect): The connection to the database. + conn (SQLiteCloudConnect): The connection to the database. Returns: bool: True if the connection is open, False otherwise. """ return self._driver.is_connected(conn) - def exec_query(self, query: str, conn: SQCloudConnect) -> SqliteCloudResultSet: + def exec_query(self, query: str, conn: SQLiteCloudConnect) -> SQLiteCloudResultSet: """Executes a SQL query on the SQLite Cloud database. Args: @@ -86,11 +86,11 @@ def exec_query(self, query: str, conn: SQCloudConnect) -> SqliteCloudResultSet: SqliteCloudResultSet: The result set of the executed query. Raises: - SQCloudException: If an error occurs while executing the query. + SQLiteCloudException: If an error occurs while executing the query. """ result = self._driver.execute(query, conn) - return SqliteCloudResultSet(result) + return SQLiteCloudResultSet(result) def exec_statement( self, @@ -98,8 +98,8 @@ def exec_statement( parameters: Union[ Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes] ], - conn: SQCloudConnect, - ) -> SqliteCloudResultSet: + conn: SQLiteCloudConnect, + ) -> SQLiteCloudResultSet: """ Prepare and execute a SQL statement (either a query or command) to the SQLite Cloud database. This function supports two styles of parameter markers: @@ -116,7 +116,7 @@ def exec_statement( query (str): The SQL query to execute. parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): The parameters to be used in the query. It can be a tuple or a dictionary. - conn (SQCloudConnect): The connection object to use for executing the query. + conn (SQLiteCloudConnect): The connection object to use for executing the query. Returns: SqliteCloudResultSet: The result set obtained from executing the query. @@ -125,13 +125,13 @@ def exec_statement( result = self._driver.execute(prepared_statement, conn) - return SqliteCloudResultSet(result) + return SQLiteCloudResultSet(result) - def sendblob(self, blob: bytes, conn: SQCloudConnect) -> SqliteCloudResultSet: + def sendblob(self, blob: bytes, conn: SQLiteCloudConnect) -> SQLiteCloudResultSet: """Sends a blob to the SQLite database. Args: blob (bytes): The blob to be sent to the database. - conn (SQCloudConnect): The connection to the database. + conn (SQLiteCloudConnect): The connection to the database. """ return self._driver.send_blob(blob, conn) diff --git a/src/sqlitecloud/dbapi2.py b/src/sqlitecloud/dbapi2.py index f645fa6..06ba402 100644 --- a/src/sqlitecloud/dbapi2.py +++ b/src/sqlitecloud/dbapi2.py @@ -1,4 +1,4 @@ -# DB-API 2.0 interface to SQLiteCloud. +# DB-API 2.0 interface to SQLite Cloud. # # PEP 249 – Python Database API Specification v2.0 # https://peps.python.org/pep-0249/ @@ -17,14 +17,14 @@ ) from sqlitecloud.driver import Driver -from sqlitecloud.resultset import SQCloudResult +from sqlitecloud.resultset import SQLiteCloudResult from sqlitecloud.types import ( - SQCLOUD_RESULT_TYPE, - SQCloudConfig, - SQCloudConnect, - SQCloudException, - SqliteCloudAccount, + SQLITECLOUD_RESULT_TYPE, + SQLiteCloudAccount, + SQLiteCloudConfig, + SQLiteCloudConnect, SQLiteCloudDataTypes, + SQLiteCloudException, ) # Question mark style, e.g. ...WHERE name=? @@ -45,41 +45,41 @@ def connect(connection_str: str) -> "Connection": Args: connection_str (str): The connection string for the database. - It may include SQCloudConfig'options like timeout, apikey, etc. in the url query string. + It may include SQLiteCloudConfig'options like timeout, apikey, etc. in the url query string. Eg: sqlitecloud://myhost.sqlitecloud.io:8860/mydb?apikey=abc123&compression=true&timeout=10 Returns: Connection: A connection object representing the database connection. Raises: - SQCloudException: If an error occurs while establishing the connection. + SQLiteCloudException: If an error occurs while establishing the connection. """ ... @overload def connect( - cloud_account: SqliteCloudAccount, config: Optional[SQCloudConfig] = None + cloud_account: SQLiteCloudAccount, config: Optional[SQLiteCloudConfig] = None ) -> "Connection": """ Establishes a connection to the SQLite Cloud database using the provided cloud account and configuration. Args: cloud_account (SqliteCloudAccount): The cloud account used to authenticate and access the database. - config (Optional[SQCloudConfig]): Additional configuration options for the connection (default: None). + config (Optional[SQLiteCloudConfig]): Additional configuration options for the connection (default: None). Returns: Connection: A connection object representing the connection to the SQLite Cloud database. Raises: - SQCloudException: If an error occurs while establishing the connection. + SQLiteCloudException: If an error occurs while establishing the connection. """ ... def connect( - connection_info: Union[str, SqliteCloudAccount], - config: Optional[SQCloudConfig] = None, + connection_info: Union[str, SQLiteCloudAccount], + config: Optional[SQLiteCloudConfig] = None, ) -> "Connection": """ Establishes a connection to the SQLite Cloud database. @@ -87,23 +87,23 @@ def connect( Args: connection_info (Union[str, SqliteCloudAccount]): The connection information. It can be either a connection string or a `SqliteCloudAccount` object. - config (Optional[SQCloudConfig]): The configuration options for the connection. + config (Optional[SQLiteCloudConfig]): The configuration options for the connection. Defaults to None. Returns: Connection: A DB-API 2.0 connection object representing the connection to the database. Raises: - SQCloudException: If an error occurs while establishing the connection. + SQLiteCloudException: If an error occurs while establishing the connection. """ driver = Driver() - if isinstance(connection_info, SqliteCloudAccount): + if isinstance(connection_info, SQLiteCloudAccount): if not config: - config = SQCloudConfig() + config = SQLiteCloudConfig() config.account = connection_info else: - config = SQCloudConfig(connection_info) + config = SQLiteCloudConfig(connection_info) return Connection( driver.connect(config.account.hostname, config.account.port, config) @@ -115,29 +115,29 @@ class Connection: Represents a DB-APi 2.0 connection to the SQLite Cloud database. Args: - sqcloud_connection (SQCloudConnect): The SQLiteCloud connection object. + SQLiteCloud_connection (SQLiteCloudConnect): The SQLite Cloud connection object. Attributes: _driver (Driver): The driver object used for database operations. - sqcloud_connection (SQCloudConnect): The SQLiteCloud connection object. + SQLiteCloud_connection (SQLiteCloudConnect): The SQLite Cloud connection object. """ row_factory: Optional[Callable[["Cursor", Tuple], object]] = None - def __init__(self, sqcloud_connection: SQCloudConnect) -> None: + def __init__(self, SQLiteCloud_connection: SQLiteCloudConnect) -> None: self._driver = Driver() self.row_factory = None - self.sqcloud_connection = sqcloud_connection + self.SQLiteCloud_connection = SQLiteCloud_connection @property - def sqlcloud_connection(self) -> SQCloudConnect: + def sqlcloud_connection(self) -> SQLiteCloudConnect: """ Returns the SQLite Cloud connection object. Returns: - SQCloudConnect: The SQLiteCloud connection object. + SQLiteCloudConnect: The SQLite Cloud connection object. """ - return self.sqcloud_connection + return self.SQLiteCloud_connection def execute( self, @@ -154,7 +154,7 @@ def execute( sql (str): The SQL query to execute. parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): The parameters to be used in the query. It can be a tuple or a dictionary. (Default ()) - conn (SQCloudConnect): The connection object to use for executing the query. + conn (SQLiteCloudConnect): The connection object to use for executing the query. Returns: Cursor: The cursor object. @@ -195,7 +195,7 @@ def close(self): DB-API 2.0 interface does not manage the Sqlite Cloud PubSub feature. Therefore, only the main socket is closed. """ - self._driver.disconnect(self.sqcloud_connection, True) + self._driver.disconnect(self.SQLiteCloud_connection, True) def commit(self): """ @@ -240,7 +240,7 @@ def __init__(self, connection: Connection) -> None: self.row_factory = None self._connection = connection self._iter_row: int = 0 - self._resultset: SQCloudResult = None + self._resultset: SQLiteCloudResult = None @property def connection(self) -> Connection: @@ -332,7 +332,7 @@ def execute( sql (str): The SQL query to execute. parameters (Union[Tuple[SQLiteCloudDataTypes], Dict[Union[str, int], SQLiteCloudDataTypes]]): The parameters to be used in the query. It can be a tuple or a dictionary. (Default ()) - conn (SQCloudConnect): The connection object to use for executing the query. + conn (SQLiteCloudConnect): The connection object to use for executing the query. Returns: Cursor: The cursor object. @@ -453,7 +453,8 @@ def _call_row_factory(self, row: Tuple) -> object: def _is_result_rowset(self) -> bool: return ( - self._resultset and self._resultset.tag == SQCLOUD_RESULT_TYPE.RESULT_ROWSET + self._resultset + and self._resultset.tag == SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET ) def _ensure_connection(self): @@ -461,10 +462,10 @@ def _ensure_connection(self): Ensure the cursor is usable or has been closed. Raises: - SQCloudException: If the cursor is closed. + SQLiteCloudException: If the cursor is closed. """ if not self._connection: - raise SQCloudException("The cursor is closed.") + raise SQLiteCloudException("The cursor is closed.") def __iter__(self) -> "Cursor": return self diff --git a/src/sqlitecloud/download.py b/src/sqlitecloud/download.py index 21ac3dd..37eb8b6 100644 --- a/src/sqlitecloud/download.py +++ b/src/sqlitecloud/download.py @@ -2,7 +2,7 @@ from io import BufferedWriter from sqlitecloud.driver import Driver -from sqlitecloud.types import SQCloudConnect +from sqlitecloud.types import SQLiteCloudConnect def xCallback( @@ -28,12 +28,12 @@ def xCallback( logging.log(logging.DEBUG, f"{(nprogress + blen) / ntot * 100:.2f}%") -def download_db(connection: SQCloudConnect, dbname: str, filename: str) -> None: +def download_db(connection: SQLiteCloudConnect, dbname: str, filename: str) -> None: """ Download a database from the server. Raises: - SQCloudException: If an error occurs while downloading the database. + SQLiteCloudException: If an error occurs while downloading the database. """ driver = Driver() diff --git a/src/sqlitecloud/driver.py b/src/sqlitecloud/driver.py index 4395b26..f951d68 100644 --- a/src/sqlitecloud/driver.py +++ b/src/sqlitecloud/driver.py @@ -9,50 +9,50 @@ import lz4.block -from sqlitecloud.resultset import SQCloudResult, SqliteCloudResultSet +from sqlitecloud.resultset import SQLiteCloudResult, SQLiteCloudResultSet from sqlitecloud.types import ( - SQCLOUD_CMD, - SQCLOUD_DEFAULT, - SQCLOUD_INTERNAL_ERRCODE, - SQCLOUD_RESULT_TYPE, - SQCLOUD_ROWSET, - SQCloudConfig, - SQCloudConnect, - SQCloudException, - SQCloudNumber, - SQCloudRowsetSignature, - SQCloudValue, + SQLITECLOUD_CMD, + SQLITECLOUD_DEFAULT, + SQLITECLOUD_INTERNAL_ERRCODE, + SQLITECLOUD_RESULT_TYPE, + SQLITECLOUD_ROWSET, + SQLiteCloudConfig, + SQLiteCloudConnect, SQLiteCloudDataTypes, + SQLiteCloudException, + SQLiteCloudNumber, + SQLiteCloudRowsetSignature, + SQLiteCloudValue, ) class Driver: - SQCLOUD_DEFAULT_UPLOAD_SIZE = 512 * 1024 + SQLiteCloud_DEFAULT_UPLOAD_SIZE = 512 * 1024 def __init__(self) -> None: # Used while parsing chunked rowset - self._rowset: SQCloudResult = None + self._rowset: SQLiteCloudResult = None def connect( - self, hostname: str, port: int, config: SQCloudConfig - ) -> SQCloudConnect: + self, hostname: str, port: int, config: SQLiteCloudConfig + ) -> SQLiteCloudConnect: """ Connect to the SQLite Cloud server. Args: hostname (str): The hostname of the server. port (int): The port number of the server. - config (SQCloudConfig): The configuration for the connection. + config (SQLiteCloudConfig): The configuration for the connection. Returns: - SQCloudConnect: The connection object. + SQLiteCloudConnect: The connection object. Raises: - SQCloudException: If an error occurs while connecting the socket. + SQLiteCloudException: If an error occurs while connecting the socket. """ sock = self._internal_connect(hostname, port, config) - connection = SQCloudConnect() + connection = SQLiteCloudConnect() connection.config = config connection.socket = sock @@ -60,7 +60,9 @@ def connect( return connection - def disconnect(self, conn: SQCloudConnect, only_main_socket: bool = False) -> None: + def disconnect( + self, conn: SQLiteCloudConnect, only_main_socket: bool = False + ) -> None: """ Disconnect from the SQLite Cloud server. """ @@ -74,13 +76,15 @@ def disconnect(self, conn: SQCloudConnect, only_main_socket: bool = False) -> No if not only_main_socket: conn.pubsub_socket = None - def execute(self, command: str, connection: SQCloudConnect) -> SQCloudResult: + def execute( + self, command: str, connection: SQLiteCloudConnect + ) -> SQLiteCloudResult: """ Execute a query on the SQLite Cloud server. """ return self._internal_run_command(connection, command) - def send_blob(self, blob: bytes, conn: SQCloudConnect) -> SQCloudResult: + def send_blob(self, blob: bytes, conn: SQLiteCloudConnect) -> SQLiteCloudResult: """ Send a blob to the SQLite Cloud server. """ @@ -110,7 +114,7 @@ def prepare_statement( return query def is_connected( - self, connection: SQCloudConnect, main_socket: bool = True + self, connection: SQLiteCloudConnect, main_socket: bool = True ) -> bool: """ Check if the connection is still open. @@ -151,10 +155,10 @@ def escape_sql_parameter(self, param): json_string = json_string.replace("'", "''") return f"'{json_string}'" - raise SQCloudException(f"Unsupported parameter type: {type(param)}") + raise SQLiteCloudException(f"Unsupported parameter type: {type(param)}") def _internal_connect( - self, hostname: str, port: int, config: SQCloudConfig + self, hostname: str, port: int, config: SQLiteCloudConfig ) -> socket: """ Create a socket connection to the SQLite Cloud server. @@ -180,14 +184,16 @@ def _internal_connect( sock.connect((hostname, port)) except Exception as e: errmsg = "An error occurred while initializing the socket." - raise SQCloudException(errmsg) from e + raise SQLiteCloudException(errmsg) from e return sock def _internal_reconnect(self, buffer: bytes) -> bool: return True - def _internal_setup_pubsub(self, connection: SQCloudConnect, buffer: bytes) -> bool: + def _internal_setup_pubsub( + self, connection: SQLiteCloudConnect, buffer: bytes + ) -> bool: """ Prepare the connection for PubSub. Opens a new specific socket and starts the thread to listen for incoming messages. @@ -196,7 +202,7 @@ def _internal_setup_pubsub(self, connection: SQCloudConnect, buffer: bytes) -> b return True if connection.pubsub_callback is None: - raise SQCloudException( + raise SQLiteCloudException( "A callback function must be provided to setup the PubSub connection." ) @@ -217,7 +223,7 @@ def _internal_setup_pubsub(self, connection: SQCloudConnect, buffer: bytes) -> b return True - def _internal_pubsub_thread(self, connection: SQCloudConnect) -> None: + def _internal_pubsub_thread(self, connection: SQLiteCloudConnect) -> None: blen = 2048 buffer: bytes = b"" @@ -247,7 +253,7 @@ def _internal_pubsub_thread(self, connection: SQCloudConnect) -> None: break except Exception as e: logging.error( - f"An error occurred while reading data: {SQCLOUD_INTERNAL_ERRCODE.NETWORK.value} ({e})." + f"An error occurred while reading data: {SQLITECLOUD_INTERNAL_ERRCODE.NETWORK.value} ({e})." ) break @@ -256,24 +262,24 @@ def _internal_pubsub_thread(self, connection: SQCloudConnect) -> None: blen -= nread buffer += data - sqcloud_number = self._internal_parse_number(buffer) - clen = sqcloud_number.value + SQLiteCloud_number = self._internal_parse_number(buffer) + clen = SQLiteCloud_number.value if clen == 0: continue # check if read is complete # clen is the lenght parsed in the buffer # cstart is the index of the first space - cstart = sqcloud_number.cstart + cstart = SQLiteCloud_number.cstart if clen + cstart != tread: continue result = self._internal_parse_buffer(connection, buffer, tread) - if result.tag == SQCLOUD_RESULT_TYPE.RESULT_STRING: - result.tag = SQCLOUD_RESULT_TYPE.RESULT_JSON + if result.tag == SQLITECLOUD_RESULT_TYPE.RESULT_STRING: + result.tag = SQLITECLOUD_RESULT_TYPE.RESULT_JSON connection.pubsub_callback( - connection, SqliteCloudResultSet(result), connection.pubsub_data + connection, SQLiteCloudResultSet(result), connection.pubsub_data ) except Exception as e: logging.error(f"An error occurred while parsing data: {e}.") @@ -283,7 +289,7 @@ def _internal_pubsub_thread(self, connection: SQCloudConnect) -> None: def upload_database( self, - connection: SQCloudConnect, + connection: SQLiteCloudConnect, dbname: str, key: Optional[str], is_file_transfer: bool, @@ -297,7 +303,7 @@ def upload_database( Uploads a database to the server. Args: - connection (SQCloudConnect): The connection object to the SQLite Cloud server. + connection (SQLiteCloudConnect): The connection object to the SQLite Cloud server. dbname (str): The name of the database to upload. key (Optional[str]): The encryption key for the database, if applicable. is_file_transfer (bool): Indicates whether the database is being transferred as a file. @@ -308,7 +314,7 @@ def upload_database( xCallback (Callable[[BufferedReader, int, int, int], bytes]): The callback function to read the buffer. Raises: - SQCloudException: If an error occurs during the upload process. + SQLiteCloudException: If an error occurs during the upload process. """ keyarg = "KEY " if key else "" @@ -325,7 +331,7 @@ def upload_database( # execute command on server side result = self._internal_run_command(connection, command) if not result.data[0]: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while initializing the upload of the database." ) @@ -335,12 +341,12 @@ def upload_database( try: while True: # execute callback to read buffer - blen = SQCLOUD_DEFAULT.UPLOAD_SIZE.value + blen = SQLITECLOUD_DEFAULT.UPLOAD_SIZE.value try: buffer = xCallback(fd, blen, dbsize, nprogress) blen = len(buffer) except Exception as e: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while reading the file." ) from e @@ -348,7 +354,7 @@ def upload_database( # send also the final confirmation blob of zero bytes self.send_blob(buffer, connection) except Exception as e: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while uploading the file." ) from e @@ -364,7 +370,7 @@ def upload_database( def download_database( self, - connection: SQCloudConnect, + connection: SQLiteCloudConnect, dbname: str, fd: BufferedWriter, xCallback: Callable[[BufferedWriter, int, int, int], bytes], @@ -374,14 +380,14 @@ def download_database( Downloads a database from the SQLite Cloud service. Args: - connection (SQCloudConnect): The connection object used to communicate with the SQLite Cloud service. + connection (SQLiteCloudConnect): The connection object used to communicate with the SQLite Cloud service. dbname (str): The name of the database to download. fd (BufferedWriter): The file descriptor to write the downloaded data to. xCallback (Callable[[BufferedWriter, int, int, int], bytes]): A callback function to write downloaded data with the download progress information. if_exists (bool): If True, the download won't rise an exception if database is missing. Raises: - SQCloudException: If an error occurs while downloading the database. + SQLiteCloudException: If an error occurs while downloading the database. """ exists_cmd = " IF EXISTS" if if_exists else "" @@ -390,7 +396,7 @@ def download_database( ) if result.nrows == 0: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while initializing the download of the database." ) @@ -421,7 +427,7 @@ def download_database( raise e def _internal_config_apply( - self, connection: SQCloudConnect, config: SQCloudConfig + self, connection: SQLiteCloudConnect, config: SQLiteCloudConfig ) -> None: if config.timeout > 0: connection.socket.settimeout(config.timeout) @@ -467,14 +473,14 @@ def _internal_config_apply( def _internal_run_command( self, - connection: SQCloudConnect, + connection: SQLiteCloudConnect, command: Union[str, bytes], main_socket: bool = True, - ) -> SQCloudResult: + ) -> SQLiteCloudResult: if not self.is_connected(connection, main_socket): - raise SQCloudException( + raise SQLiteCloudException( "The connection is closed.", - SQCLOUD_INTERNAL_ERRCODE.NETWORK, + SQLITECLOUD_INTERNAL_ERRCODE.NETWORK, ) self._internal_socket_write(connection, command, main_socket) @@ -482,7 +488,7 @@ def _internal_run_command( def _internal_socket_write( self, - connection: SQCloudConnect, + connection: SQLiteCloudConnect, command: Union[str, bytes], main_socket: bool = True, ) -> None: @@ -498,9 +504,9 @@ def _internal_socket_write( try: sock.sendall(header.encode()) except Exception as exc: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while writing header data.", - SQCLOUD_INTERNAL_ERRCODE.NETWORK, + SQLITECLOUD_INTERNAL_ERRCODE.NETWORK, ) from exc # write buffer @@ -509,14 +515,14 @@ def _internal_socket_write( try: sock.sendall(buffer) except Exception as exc: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while writing data.", - SQCLOUD_INTERNAL_ERRCODE.NETWORK, + SQLITECLOUD_INTERNAL_ERRCODE.NETWORK, ) from exc def _internal_socket_read( - self, connection: SQCloudConnect, main_socket: bool = True - ) -> SQCloudResult: + self, connection: SQLiteCloudConnect, main_socket: bool = True + ) -> SQLiteCloudResult: """ Read from the socket and parse the response. @@ -535,11 +541,11 @@ def _internal_socket_read( try: data = sock.recv(buffer_size) if not data: - raise SQCloudException("Incomplete response from server.") + raise SQLiteCloudException("Incomplete response from server.") except Exception as exc: - raise SQCloudException( + raise SQLiteCloudException( "An error occurred while reading data from the socket.", - SQCLOUD_INTERNAL_ERRCODE.NETWORK, + SQLITECLOUD_INTERNAL_ERRCODE.NETWORK, ) from exc # the expected data length to read @@ -551,23 +557,23 @@ def _internal_socket_read( c = chr(buffer[0]) if ( - c == SQCLOUD_CMD.INT.value - or c == SQCLOUD_CMD.FLOAT.value - or c == SQCLOUD_CMD.NULL.value + c == SQLITECLOUD_CMD.INT.value + or c == SQLITECLOUD_CMD.FLOAT.value + or c == SQLITECLOUD_CMD.NULL.value ): if not buffer.endswith(b" "): continue - elif c == SQCLOUD_CMD.ROWSET_CHUNK.value: - isEndOfChunk = buffer.endswith(SQCLOUD_ROWSET.CHUNKS_END.value) + elif c == SQLITECLOUD_CMD.ROWSET_CHUNK.value: + isEndOfChunk = buffer.endswith(SQLITECLOUD_ROWSET.CHUNKS_END.value) if not isEndOfChunk: continue else: - sqcloud_number = self._internal_parse_number(buffer) - n = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer) + n = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart can_be_zerolength = ( - c == SQCLOUD_CMD.BLOB.value or c == SQCLOUD_CMD.STRING.value + c == SQLITECLOUD_CMD.BLOB.value or c == SQLITECLOUD_CMD.STRING.value ) if n == 0 and not can_be_zerolength: continue @@ -576,9 +582,11 @@ def _internal_socket_read( return self._internal_parse_buffer(connection, buffer, len(buffer)) - def _internal_parse_number(self, buffer: bytes, index: int = 1) -> SQCloudNumber: - sqcloud_number = SQCloudNumber() - sqcloud_number.value = 0 + def _internal_parse_number( + self, buffer: bytes, index: int = 1 + ) -> SQLiteCloudNumber: + SQLiteCloud_number = SQLiteCloudNumber() + SQLiteCloud_number.value = 0 extvalue = 0 isext = False blen = len(buffer) @@ -594,9 +602,9 @@ def _internal_parse_number(self, buffer: bytes, index: int = 1) -> SQCloudNumber # check for end of value if c == " ": - sqcloud_number.cstart = i + 1 - sqcloud_number.extcode = extvalue - return sqcloud_number + SQLiteCloud_number.cstart = i + 1 + SQLiteCloud_number.extcode = extvalue + return SQLiteCloud_number val = int(c) if c.isdigit() else 0 @@ -604,14 +612,14 @@ def _internal_parse_number(self, buffer: bytes, index: int = 1) -> SQCloudNumber if isext: extvalue = (extvalue * 10) + val else: - sqcloud_number.value = (sqcloud_number.value * 10) + val + SQLiteCloud_number.value = (SQLiteCloud_number.value * 10) + val - sqcloud_number.value = 0 - return sqcloud_number + SQLiteCloud_number.value = 0 + return SQLiteCloud_number def _internal_parse_buffer( - self, connection: SQCloudConnect, buffer: bytes, blen: int - ) -> SQCloudResult: + self, connection: SQLiteCloudConnect, buffer: bytes, blen: int + ) -> SQLiteCloudResult: # possible return values: # True => OK # False => error @@ -624,15 +632,15 @@ def _internal_parse_buffer( # check OK value if buffer == b"+2 OK": - return SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_OK, True) + return SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_OK, True) cmd = chr(buffer[0]) # check for compressed result - if cmd == SQCLOUD_CMD.COMPRESSED.value: + if cmd == SQLITECLOUD_CMD.COMPRESSED.value: buffer = self._internal_uncompress_data(buffer) if buffer is None: - raise SQCloudException( + raise SQLiteCloudException( f"An error occurred while decompressing the input buffer of len {blen}." ) @@ -642,53 +650,54 @@ def _internal_parse_buffer( # first character contains command type if cmd in [ - SQCLOUD_CMD.ZEROSTRING.value, - SQCLOUD_CMD.RECONNECT.value, - SQCLOUD_CMD.PUBSUB.value, - SQCLOUD_CMD.COMMAND.value, - SQCLOUD_CMD.STRING.value, - SQCLOUD_CMD.ARRAY.value, - SQCLOUD_CMD.BLOB.value, - SQCLOUD_CMD.JSON.value, + SQLITECLOUD_CMD.ZEROSTRING.value, + SQLITECLOUD_CMD.RECONNECT.value, + SQLITECLOUD_CMD.PUBSUB.value, + SQLITECLOUD_CMD.COMMAND.value, + SQLITECLOUD_CMD.STRING.value, + SQLITECLOUD_CMD.ARRAY.value, + SQLITECLOUD_CMD.BLOB.value, + SQLITECLOUD_CMD.JSON.value, ]: sqlite_number = self._internal_parse_number(buffer) len_ = sqlite_number.value cstart = sqlite_number.cstart if len_ == 0: - return SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_STRING, "") + return SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_STRING, "") tag = ( - SQCLOUD_RESULT_TYPE.RESULT_JSON - if cmd == SQCLOUD_CMD.JSON.value - else SQCLOUD_RESULT_TYPE.RESULT_STRING + SQLITECLOUD_RESULT_TYPE.RESULT_JSON + if cmd == SQLITECLOUD_CMD.JSON.value + else SQLITECLOUD_RESULT_TYPE.RESULT_STRING ) - if cmd == SQCLOUD_CMD.ZEROSTRING.value: + if cmd == SQLITECLOUD_CMD.ZEROSTRING.value: len_ -= 1 clone = buffer[cstart : cstart + len_] - if cmd == SQCLOUD_CMD.COMMAND.value: + if cmd == SQLITECLOUD_CMD.COMMAND.value: return self._internal_run_command(connection, clone) - elif cmd == SQCLOUD_CMD.PUBSUB.value: - return SQCloudResult( - SQCLOUD_RESULT_TYPE.RESULT_OK, + elif cmd == SQLITECLOUD_CMD.PUBSUB.value: + return SQLiteCloudResult( + SQLITECLOUD_RESULT_TYPE.RESULT_OK, self._internal_setup_pubsub(connection, clone), ) - elif cmd == SQCLOUD_CMD.RECONNECT.value: - return SQCloudResult( - SQCLOUD_RESULT_TYPE.RESULT_OK, self._internal_reconnect(clone) + elif cmd == SQLITECLOUD_CMD.RECONNECT.value: + return SQLiteCloudResult( + SQLITECLOUD_RESULT_TYPE.RESULT_OK, self._internal_reconnect(clone) ) - elif cmd == SQCLOUD_CMD.ARRAY.value: - return SQCloudResult( - SQCLOUD_RESULT_TYPE.RESULT_ARRAY, self._internal_parse_array(clone) + elif cmd == SQLITECLOUD_CMD.ARRAY.value: + return SQLiteCloudResult( + SQLITECLOUD_RESULT_TYPE.RESULT_ARRAY, + self._internal_parse_array(clone), ) - elif cmd == SQCLOUD_CMD.BLOB.value: - tag = SQCLOUD_RESULT_TYPE.RESULT_BLOB + elif cmd == SQLITECLOUD_CMD.BLOB.value: + tag = SQLITECLOUD_RESULT_TYPE.RESULT_BLOB - clone = clone.decode() if cmd != SQCLOUD_CMD.BLOB.value else clone - return SQCloudResult(tag, clone) + clone = clone.decode() if cmd != SQLITECLOUD_CMD.BLOB.value else clone + return SQLiteCloudResult(tag, clone) - elif cmd == SQCLOUD_CMD.ERROR.value: + elif cmd == SQLITECLOUD_CMD.ERROR.value: # -LEN ERRCODE:EXTCODE ERRMSG sqlite_number = self._internal_parse_number(buffer) len_ = sqlite_number.value @@ -704,9 +713,9 @@ def _internal_parse_buffer( len_ -= cstart2 errmsg = clone[cstart2:] - raise SQCloudException(errmsg.decode(), errcode, xerrcode) + raise SQLiteCloudException(errmsg.decode(), errcode, xerrcode) - elif cmd in [SQCLOUD_CMD.ROWSET.value, SQCLOUD_CMD.ROWSET_CHUNK.value]: + elif cmd in [SQLITECLOUD_CMD.ROWSET.value, SQLITECLOUD_CMD.ROWSET_CHUNK.value]: # CMD_ROWSET: *LEN 0:VERSION ROWS COLS DATA # - When decompressed, LEN for ROWSET is *0 # @@ -714,7 +723,7 @@ def _internal_parse_buffer( # rowset_signature = self._internal_parse_rowset_signature(buffer) if rowset_signature.start < 0: - raise SQCloudException("Cannot parse rowset signature") + raise SQLiteCloudException("Cannot parse rowset signature") # check for end-of-chunk condition if rowset_signature.start == 0 and rowset_signature.version == 0: @@ -734,35 +743,35 @@ def _internal_parse_buffer( # continue parsing next chunk in the buffer sign_len = rowset_signature.len buffer = buffer[sign_len + len(f"/{sign_len} ") :] - if cmd == SQCLOUD_CMD.ROWSET_CHUNK.value and buffer: + if cmd == SQLITECLOUD_CMD.ROWSET_CHUNK.value and buffer: return self._internal_parse_buffer(connection, buffer, len(buffer)) return rowset - elif cmd == SQCLOUD_CMD.NULL.value: - return SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_NONE, None) + elif cmd == SQLITECLOUD_CMD.NULL.value: + return SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_NONE, None) - elif cmd in [SQCLOUD_CMD.INT.value, SQCLOUD_CMD.FLOAT.value]: - sqcloud_value = self._internal_parse_value(buffer) - clone = sqcloud_value.value + elif cmd in [SQLITECLOUD_CMD.INT.value, SQLITECLOUD_CMD.FLOAT.value]: + SQLiteCloud_value = self._internal_parse_value(buffer) + clone = SQLiteCloud_value.value tag = ( - SQCLOUD_RESULT_TYPE.RESULT_INTEGER - if cmd == SQCLOUD_CMD.INT.value - else SQCLOUD_RESULT_TYPE.RESULT_FLOAT + SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER + if cmd == SQLITECLOUD_CMD.INT.value + else SQLITECLOUD_RESULT_TYPE.RESULT_FLOAT ) if clone is None: - return SQCloudResult(tag, 0) + return SQLiteCloudResult(tag, 0) - if cmd == SQCLOUD_CMD.INT.value: - return SQCloudResult(tag, int(clone)) - return SQCloudResult(tag, float(clone)) + if cmd == SQLITECLOUD_CMD.INT.value: + return SQLiteCloudResult(tag, int(clone)) + return SQLiteCloudResult(tag, float(clone)) - elif cmd == SQCLOUD_CMD.RAWJSON.value: - return SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_NONE, None) + elif cmd == SQLITECLOUD_CMD.RAWJSON.value: + return SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_NONE, None) - return SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_NONE, None) + return SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_NONE, None) def _internal_uncompress_data(self, buffer: bytes) -> Optional[bytes]: """ @@ -812,62 +821,64 @@ def _internal_parse_array(self, buffer: bytes) -> list: r: str = [] for i in range(n): - sqcloud_value = self._internal_parse_value(buffer, start) - start += sqcloud_value.cellsize - r.append(sqcloud_value.value) + SQLiteCloud_value = self._internal_parse_value(buffer, start) + start += SQLiteCloud_value.cellsize + r.append(SQLiteCloud_value.value) return r - def _internal_parse_value(self, buffer: bytes, index: int = 0) -> SQCloudValue: - sqcloud_value = SQCloudValue() + def _internal_parse_value(self, buffer: bytes, index: int = 0) -> SQLiteCloudValue: + SQLiteCloud_value = SQLiteCloudValue() len = 0 cellsize = 0 # handle special NULL value case c = chr(buffer[index]) - if buffer is None or c == SQCLOUD_CMD.NULL.value: + if buffer is None or c == SQLITECLOUD_CMD.NULL.value: len = 0 if cellsize is not None: cellsize = 2 - sqcloud_value.len = len - sqcloud_value.cellsize = cellsize + SQLiteCloud_value.len = len + SQLiteCloud_value.cellsize = cellsize - return sqcloud_value + return SQLiteCloud_value - sqcloud_number = self._internal_parse_number(buffer, index + 1) - blen = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, index + 1) + blen = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart # handle decimal/float cases - if c == SQCLOUD_CMD.INT.value or c == SQCLOUD_CMD.FLOAT.value: + if c == SQLITECLOUD_CMD.INT.value or c == SQLITECLOUD_CMD.FLOAT.value: nlen = cstart - index len = nlen - 2 cellsize = nlen - sqcloud_value.value = (buffer[index + 1 : index + 1 + len]).decode() - sqcloud_value.len - sqcloud_value.cellsize = cellsize + SQLiteCloud_value.value = (buffer[index + 1 : index + 1 + len]).decode() + SQLiteCloud_value.len + SQLiteCloud_value.cellsize = cellsize - return sqcloud_value + return SQLiteCloud_value - len = blen - 1 if c == SQCLOUD_CMD.ZEROSTRING.value else blen + len = blen - 1 if c == SQLITECLOUD_CMD.ZEROSTRING.value else blen cellsize = blen + cstart - index - sqcloud_value.value = (buffer[cstart : cstart + len]).decode() - sqcloud_value.len = len - sqcloud_value.cellsize = cellsize + SQLiteCloud_value.value = (buffer[cstart : cstart + len]).decode() + SQLiteCloud_value.len = len + SQLiteCloud_value.cellsize = cellsize - return sqcloud_value + return SQLiteCloud_value - def _internal_parse_rowset_signature(self, buffer: bytes) -> SQCloudRowsetSignature: + def _internal_parse_rowset_signature( + self, buffer: bytes + ) -> SQLiteCloudRowsetSignature: # ROWSET: *LEN 0:VERS NROWS NCOLS DATA # ROWSET in CHUNK: /LEN IDX:VERS NROWS NCOLS DATA - signature = SQCloudRowsetSignature() + signature = SQLiteCloudRowsetSignature() # check for end-of-chunk condition - if buffer == SQCLOUD_ROWSET.CHUNKS_END.value: + if buffer == SQLITECLOUD_ROWSET.CHUNKS_END.value: signature.version = 0 signature.start = 0 return signature @@ -899,21 +910,21 @@ def _internal_parse_rowset_signature(self, buffer: bytes) -> SQCloudRowsetSignat return signature else: - return SQCloudRowsetSignature() - return SQCloudRowsetSignature() + return SQLiteCloudRowsetSignature() + return SQLiteCloudRowsetSignature() def _internal_parse_rowset( self, buffer: bytes, start: int, idx: int, version: int, nrows: int, ncols: int - ) -> SQCloudResult: + ) -> SQLiteCloudResult: rowset = None n = start - ischunk = chr(buffer[0]) == SQCLOUD_CMD.ROWSET_CHUNK.value + ischunk = chr(buffer[0]) == SQLITECLOUD_CMD.ROWSET_CHUNK.value # idx == 0 means first (and only) chunk for rowset # idx == 1 means first chunk for chunked rowset first_chunk = (ischunk and idx == 1) or (not ischunk and idx == 0) if first_chunk: - rowset = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + rowset = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) rowset.nrows = nrows rowset.ncols = ncols rowset.version = version @@ -922,7 +933,7 @@ def _internal_parse_rowset( self._rowset = rowset n = self._internal_parse_rowset_header(rowset, buffer, start) if n <= 0: - raise SQCloudException("Cannot parse rowset header") + raise SQLiteCloudException("Cannot parse rowset header") else: rowset = self._rowset rowset.nrows += nrows @@ -933,16 +944,16 @@ def _internal_parse_rowset( return rowset def _internal_parse_rowset_header( - self, rowset: SQCloudResult, buffer: bytes, start: int + self, rowset: SQLiteCloudResult, buffer: bytes, start: int ) -> int: ncols = rowset.ncols # parse column names rowset.colname = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - number_len = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + number_len = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart value = buffer[cstart : cstart + number_len] rowset.colname.append(value.decode()) start = cstart + number_len @@ -951,14 +962,16 @@ def _internal_parse_rowset_header( return start if rowset.version != 2: - raise SQCloudException(f"Rowset version {rowset.version} is not supported.") + raise SQLiteCloudException( + f"Rowset version {rowset.version} is not supported." + ) # parse declared types rowset.decltype = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - number_len = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + number_len = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart value = buffer[cstart : cstart + number_len] rowset.decltype.append(value.decode()) start = cstart + number_len @@ -966,9 +979,9 @@ def _internal_parse_rowset_header( # parse database names rowset.dbname = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - number_len = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + number_len = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart value = buffer[cstart : cstart + number_len] rowset.dbname.append(value.decode()) start = cstart + number_len @@ -976,9 +989,9 @@ def _internal_parse_rowset_header( # parse table names rowset.tblname = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - number_len = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + number_len = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart value = buffer[cstart : cstart + number_len] rowset.tblname.append(value.decode()) start = cstart + number_len @@ -986,9 +999,9 @@ def _internal_parse_rowset_header( # parse column original names rowset.origname = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - number_len = sqcloud_number.value - cstart = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + number_len = SQLiteCloud_number.value + cstart = SQLiteCloud_number.cstart value = buffer[cstart : cstart + number_len] rowset.origname.append(value.decode()) start = cstart + number_len @@ -996,31 +1009,31 @@ def _internal_parse_rowset_header( # parse not null flags rowset.notnull = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - rowset.notnull.append(sqcloud_number.value) - start = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + rowset.notnull.append(SQLiteCloud_number.value) + start = SQLiteCloud_number.cstart # parse primary key flags rowset.prikey = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - rowset.prikey.append(sqcloud_number.value) - start = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + rowset.prikey.append(SQLiteCloud_number.value) + start = SQLiteCloud_number.cstart # parse autoincrement flags rowset.autoinc = [] for i in range(ncols): - sqcloud_number = self._internal_parse_number(buffer, start) - rowset.autoinc.append(sqcloud_number.value) - start = sqcloud_number.cstart + SQLiteCloud_number = self._internal_parse_number(buffer, start) + rowset.autoinc.append(SQLiteCloud_number.value) + start = SQLiteCloud_number.cstart return start def _internal_parse_rowset_values( - self, rowset: SQCloudResult, buffer: bytes, start: int, bound: int + self, rowset: SQLiteCloudResult, buffer: bytes, start: int, bound: int ): # loop to parse each individual value for i in range(bound): - sqcloud_value = self._internal_parse_value(buffer, start) - start += sqcloud_value.cellsize - rowset.data.append(sqcloud_value.value) + SQLiteCloud_value = self._internal_parse_value(buffer, start) + start += SQLiteCloud_value.cellsize + rowset.data.append(SQLiteCloud_value.value) diff --git a/src/sqlitecloud/pubsub.py b/src/sqlitecloud/pubsub.py index 39277cf..7999908 100644 --- a/src/sqlitecloud/pubsub.py +++ b/src/sqlitecloud/pubsub.py @@ -1,21 +1,21 @@ from typing import Callable, Optional from sqlitecloud.driver import Driver -from sqlitecloud.resultset import SqliteCloudResultSet -from sqlitecloud.types import SQCLOUD_PUBSUB_SUBJECT, SQCloudConnect +from sqlitecloud.resultset import SQLiteCloudResultSet +from sqlitecloud.types import SQLITECLOUD_PUBSUB_SUBJECT, SQLiteCloudConnect -class SqliteCloudPubSub: +class SQLiteCloudPubSub: def __init__(self) -> None: self._driver = Driver() def listen( self, - connection: SQCloudConnect, - subject_type: SQCLOUD_PUBSUB_SUBJECT, + connection: SQLiteCloudConnect, + subject_type: SQLITECLOUD_PUBSUB_SUBJECT, subject_name: str, callback: Callable[ - [SQCloudConnect, Optional[SqliteCloudResultSet], Optional[any]], None + [SQLiteCloudConnect, Optional[SQLiteCloudResultSet], Optional[any]], None ], data: Optional[any] = None, ) -> None: @@ -28,8 +28,8 @@ def listen( def unlisten( self, - connection: SQCloudConnect, - subject_type: SQCLOUD_PUBSUB_SUBJECT, + connection: SQLiteCloudConnect, + subject_type: SQLITECLOUD_PUBSUB_SUBJECT, subject_name: str, ) -> None: subject = "TABLE " if subject_type.value == "TABLE" else "" @@ -40,17 +40,19 @@ def unlisten( connection.pubsub_data = None def create_channel( - self, connection: SQCloudConnect, name: str, if_not_exists: bool = False + self, connection: SQLiteCloudConnect, name: str, if_not_exists: bool = False ) -> None: if if_not_exists: self._driver.execute(f"CREATE CHANNEL {name} IF NOT EXISTS;", connection) else: self._driver.execute(f"CREATE CHANNEL {name};", connection) - def notify_channel(self, connection: SQCloudConnect, name: str, data: str) -> None: + def notify_channel( + self, connection: SQLiteCloudConnect, name: str, data: str + ) -> None: self._driver.execute(f"NOTIFY {name} '{data}';", connection) - def set_pubsub_only(self, connection: SQCloudConnect) -> None: + def set_pubsub_only(self, connection: SQLiteCloudConnect) -> None: """ Close the main socket, leaving only the pub/sub socket opened and ready to receive incoming notifications from subscripted channels and tables. @@ -60,10 +62,10 @@ def set_pubsub_only(self, connection: SQCloudConnect) -> None: self._driver.execute("PUBSUB ONLY;", connection) self._driver.disconnect(connection, only_main_socket=True) - def is_connected(self, connection: SQCloudConnect) -> bool: + def is_connected(self, connection: SQLiteCloudConnect) -> bool: return self._driver.is_connected(connection, False) - def list_connections(self, connection: SQCloudConnect) -> SqliteCloudResultSet: - return SqliteCloudResultSet( + def list_connections(self, connection: SQLiteCloudConnect) -> SQLiteCloudResultSet: + return SQLiteCloudResultSet( self._driver.execute("LIST PUBSUB CONNECTIONS;", connection) ) diff --git a/src/sqlitecloud/resultset.py b/src/sqlitecloud/resultset.py index 38e84f1..c6160d7 100644 --- a/src/sqlitecloud/resultset.py +++ b/src/sqlitecloud/resultset.py @@ -1,11 +1,13 @@ from typing import Any, Dict, List, Optional -from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCLOUD_VALUE_TYPE +from sqlitecloud.types import SQLITECLOUD_RESULT_TYPE, SQLITECLOUD_VALUE_TYPE -class SQCloudResult: - def __init__(self, tag: SQCLOUD_RESULT_TYPE, result: Optional[any] = None) -> None: - self.tag: SQCLOUD_RESULT_TYPE = tag +class SQLiteCloudResult: + def __init__( + self, tag: SQLITECLOUD_RESULT_TYPE, result: Optional[any] = None + ) -> None: + self.tag: SQLITECLOUD_RESULT_TYPE = tag self.nrows: int = 0 self.ncols: int = 0 self.version: int = 0 @@ -56,23 +58,23 @@ def _convert(self, value: str, col: int) -> any: return value decltype = self.decltype[col] - if decltype == SQCLOUD_VALUE_TYPE.INTEGER.value: + if decltype == SQLITECLOUD_VALUE_TYPE.INTEGER.value: return int(value) - if decltype == SQCLOUD_VALUE_TYPE.FLOAT.value: + if decltype == SQLITECLOUD_VALUE_TYPE.FLOAT.value: return float(value) - if decltype == SQCLOUD_VALUE_TYPE.BLOB.value: + if decltype == SQLITECLOUD_VALUE_TYPE.BLOB.value: # values are received as bytes before being strings return bytes(value) - if decltype == SQCLOUD_VALUE_TYPE.NULL.value: + if decltype == SQLITECLOUD_VALUE_TYPE.NULL.value: return None return value -class SqliteCloudResultSet: - def __init__(self, result: SQCloudResult) -> None: +class SQLiteCloudResultSet: + def __init__(self, result: SQLiteCloudResult) -> None: self._iter_row: int = 0 - self._result: SQCloudResult = result + self._result: SQLiteCloudResult = result def __getattr__(self, attr: str) -> Optional[Any]: return getattr(self._result, attr) diff --git a/src/sqlitecloud/types.py b/src/sqlitecloud/types.py index 2a2a63a..9e29142 100644 --- a/src/sqlitecloud/types.py +++ b/src/sqlitecloud/types.py @@ -4,17 +4,17 @@ from typing import Any, Callable, Dict, Optional, Union from urllib import parse -# Basic types supported by SQLiteCloud APIs +# Basic types supported by SQLite Cloud APIs SQLiteCloudDataTypes = Union[str, int, bool, Dict[Union[str, int], Any], bytes, None] -class SQCLOUD_DEFAULT(Enum): +class SQLITECLOUD_DEFAULT(Enum): PORT = 8860 TIMEOUT = 12 UPLOAD_SIZE = 512 * 1024 -class SQCLOUD_CMD(Enum): +class SQLITECLOUD_CMD(Enum): STRING = "+" ZEROSTRING = "!" ERROR = "-" @@ -33,11 +33,11 @@ class SQCLOUD_CMD(Enum): ARRAY = "=" -class SQCLOUD_ROWSET(Enum): +class SQLITECLOUD_ROWSET(Enum): CHUNKS_END = b"/6 0 0 0 " -class SQCLOUD_VALUE_TYPE(Enum): +class SQLITECLOUD_VALUE_TYPE(Enum): INTEGER = "INTEGER" FLOAT = "REAL" TEXT = "TEXT" @@ -45,7 +45,7 @@ class SQCLOUD_VALUE_TYPE(Enum): NULL = "NULL" -class SQCLOUD_INTERNAL_ERRCODE(Enum): +class SQLITECLOUD_INTERNAL_ERRCODE(Enum): """ Clients error codes. """ @@ -54,7 +54,7 @@ class SQCLOUD_INTERNAL_ERRCODE(Enum): NETWORK = 100005 -class SQCLOUD_ERRCODE(Enum): +class SQLITECLOUD_ERRCODE(Enum): """ Error codes from Sqlite Cloud. """ @@ -68,7 +68,7 @@ class SQCLOUD_ERRCODE(Enum): RAFT = 10006 -class SQCLOUD_RESULT_TYPE(Enum): +class SQLITECLOUD_RESULT_TYPE(Enum): RESULT_OK = 0 RESULT_ERROR = 1 RESULT_STRING = 2 @@ -81,7 +81,7 @@ class SQCLOUD_RESULT_TYPE(Enum): RESULT_BLOB = 9 -class SQCLOUD_PUBSUB_SUBJECT(Enum): +class SQLITECLOUD_PUBSUB_SUBJECT(Enum): """ Subjects that can be subscribed to by PubSub. """ @@ -90,7 +90,7 @@ class SQCLOUD_PUBSUB_SUBJECT(Enum): CHANNEL = "CHANNEL" -class SQCloudRowsetSignature: +class SQLiteCloudRowsetSignature: """ Represents the parsed signature for a rowset. """ @@ -104,14 +104,14 @@ def __init__(self) -> None: self.ncols: int = 0 -class SqliteCloudAccount: +class SQLiteCloudAccount: def __init__( self, username: Optional[str] = "", password: Optional[str] = "", hostname: str = "", dbname: Optional[str] = "", - port: int = SQCLOUD_DEFAULT.PORT.value, + port: int = SQLITECLOUD_DEFAULT.PORT.value, apikey: Optional[str] = "", ) -> None: # User name is required unless connectionstring is provided @@ -129,32 +129,33 @@ def __init__( self.port = port -class SQCloudConnect: +class SQLiteCloudConnect: """ Represents the connection information. """ def __init__(self): self.socket: any = None - self.config: SQCloudConfig + self.config: SQLiteCloudConfig self.isblob: bool = False self.pubsub_socket: any = None self.pubsub_callback: Callable[ - [SQCloudConnect, Optional[types.SqliteCloudResultSet], Optional[any]], None + [SQLiteCloudConnect, Optional[types.SqliteCloudResultSet], Optional[any]], + None, ] = None self.pubsub_data: any = None self.pubsub_thread: AbstractEventLoop = None -class SQCloudConfig: +class SQLiteCloudConfig: def __init__(self, connection_str: Optional[str] = None) -> None: - self.account: SqliteCloudAccount = None + self.account: SQLiteCloudAccount = None # Optional query timeout passed directly to TLS socket self.timeout = 0 # Socket connection timeout - self.connect_timeout = SQCLOUD_DEFAULT.TIMEOUT.value + self.connect_timeout = SQLITECLOUD_DEFAULT.TIMEOUT.value # Enable compression self.compression = False @@ -193,7 +194,7 @@ def _parse_connection_string(self, connection_string) -> None: # sqlitecloud://user:pass@host.com:port/dbname?timeout=10&key2=value2&key3=value3 # or sqlitecloud://host.sqlite.cloud:8860/dbname?apikey=zIiAARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1A4m4xBA - self.account = SqliteCloudAccount() + self.account = SQLiteCloudAccount() try: params = parse.urlparse(connection_string) @@ -237,22 +238,22 @@ def _parse_connection_string(self, connection_string) -> None: self.account.hostname = params.hostname self.account.port = ( - int(params.port) if params.port else SQCLOUD_DEFAULT.PORT.value + int(params.port) if params.port else SQLITECLOUD_DEFAULT.PORT.value ) except Exception as e: - raise SQCloudException( + raise SQLiteCloudException( f"Invalid connection string {connection_string}" ) from e -class SQCloudException(Exception): +class SQLiteCloudException(Exception): def __init__(self, message: str, code: int = -1, xerrcode: int = 0) -> None: self.errmsg = str(message) self.errcode = code self.xerrcode = xerrcode -class SQCloudNumber: +class SQLiteCloudNumber: """ Represents the parsed number or the error code. """ @@ -263,7 +264,7 @@ def __init__(self) -> None: self.extcode: int = None -class SQCloudValue: +class SQLiteCloudValue: """ Represents the parse value. """ diff --git a/src/sqlitecloud/upload.py b/src/sqlitecloud/upload.py index bea6578..3dd10e1 100644 --- a/src/sqlitecloud/upload.py +++ b/src/sqlitecloud/upload.py @@ -4,7 +4,7 @@ from typing import Optional from sqlitecloud.driver import Driver -from sqlitecloud.types import SQCloudConnect +from sqlitecloud.types import SQLiteCloudConnect def xCallback(fd: BufferedReader, blen: int, ntot: int, nprogress: int) -> bytes: @@ -32,19 +32,19 @@ def xCallback(fd: BufferedReader, blen: int, ntot: int, nprogress: int) -> bytes def upload_db( - connection: SQCloudConnect, dbname: str, key: Optional[str], filename: str + connection: SQLiteCloudConnect, dbname: str, key: Optional[str], filename: str ) -> None: """ Uploads a SQLite database to the SQLite Cloud node using the provided connection. Args: - connection (SQCloudConnect): The connection object used to connect to the node. + connection (SQLiteCloudConnect): The connection object used to connect to the node. dbname (str): The name of the database in SQLite Cloud. key (Optional[str]): The encryption key for the database. If None, no encryption is used. filename (str): The path to the SQLite database file to be uploaded. Raises: - SQCloudException: If an error occurs while uploading the database. + SQLiteCloudException: If an error occurs while uploading the database. """ diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 39c61ce..b1db511 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -4,8 +4,8 @@ from dotenv import load_dotenv import sqlitecloud -from sqlitecloud.client import SqliteCloudClient -from sqlitecloud.types import SQCloudConnect, SqliteCloudAccount +from sqlitecloud.client import SQLiteCloudClient +from sqlitecloud.types import SQLiteCloudAccount, SQLiteCloudConnect @pytest.fixture(autouse=True) @@ -15,17 +15,17 @@ def load_env_vars(): @pytest.fixture() def sqlitecloud_connection(): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_USER") account.password = os.getenv("SQLITE_PASSWORD") account.dbname = os.getenv("SQLITE_DB") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) connection = client.open_connection() - assert isinstance(connection, SQCloudConnect) + assert isinstance(connection, SQLiteCloudConnect) assert client.is_connected(connection) yield (connection, client) @@ -35,7 +35,7 @@ def sqlitecloud_connection(): @pytest.fixture() def sqlitecloud_dbapi2_connection(): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_USER") account.password = os.getenv("SQLITE_PASSWORD") account.dbname = os.getenv("SQLITE_DB") diff --git a/src/tests/integration/test_client.py b/src/tests/integration/test_client.py index 80bf1d2..f0a7a81 100644 --- a/src/tests/integration/test_client.py +++ b/src/tests/integration/test_client.py @@ -4,14 +4,14 @@ import pytest -from sqlitecloud.client import SqliteCloudClient +from sqlitecloud.client import SQLiteCloudClient from sqlitecloud.types import ( - SQCLOUD_ERRCODE, - SQCLOUD_INTERNAL_ERRCODE, - SQCLOUD_RESULT_TYPE, - SQCloudConnect, - SQCloudException, - SqliteCloudAccount, + SQLITECLOUD_ERRCODE, + SQLITECLOUD_INTERNAL_ERRCODE, + SQLITECLOUD_RESULT_TYPE, + SQLiteCloudAccount, + SQLiteCloudConnect, + SQLiteCloudException, ) @@ -23,69 +23,69 @@ class TestClient: EXPECT_SPEED_MS = 6 * 1000 def test_connection_with_credentials(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_USER") account.password = os.getenv("SQLITE_PASSWORD") account.dbname = os.getenv("SQLITE_DB") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) conn = client.open_connection() - assert isinstance(conn, SQCloudConnect) + assert isinstance(conn, SQLiteCloudConnect) client.disconnect(conn) def test_connection_with_apikey(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_API_KEY") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) conn = client.open_connection() - assert isinstance(conn, SQCloudConnect) + assert isinstance(conn, SQLiteCloudConnect) client.disconnect(conn) def test_connection_without_credentials_and_apikey(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.dbname = os.getenv("SQLITE_DB") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) - with pytest.raises(SQCloudException): + with pytest.raises(SQLiteCloudException): client.open_connection() def test_connect_with_string(self): connection_string = os.getenv("SQLITE_CONNECTION_STRING") - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) conn = client.open_connection() - assert isinstance(conn, SQCloudConnect) + assert isinstance(conn, SQLiteCloudConnect) client.disconnect(conn) def test_connect_with_string_with_credentials(self): connection_string = f"sqlitecloud://{os.getenv('SQLITE_USER')}:{os.getenv('SQLITE_PASSWORD')}@{os.getenv('SQLITE_HOST')}/{os.getenv('SQLITE_DB')}" - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) conn = client.open_connection() - assert isinstance(conn, SQCloudConnect) + assert isinstance(conn, SQLiteCloudConnect) client.disconnect(conn) def test_is_connected(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_API_KEY") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) conn = client.open_connection() assert client.is_connected(conn) @@ -94,12 +94,12 @@ def test_is_connected(self): assert not client.is_connected(conn) def test_disconnect(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.username = os.getenv("SQLITE_API_KEY") account.hostname = os.getenv("SQLITE_HOST") account.port = int(os.getenv("SQLITE_PORT")) - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) conn = client.open_connection() assert client.is_connected(conn) @@ -124,7 +124,7 @@ def test_select(self, sqlitecloud_connection): def test_column_not_found(self, sqlitecloud_connection): connection, client = sqlitecloud_connection - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: client.exec_query("SELECT not_a_column FROM albums", connection) assert e.value.errcode == 1 @@ -134,7 +134,7 @@ def test_rowset_data(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("SELECT AlbumId FROM albums LIMIT 2", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_ROWSET == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET == result.tag assert 2 == result.nrows assert 1 == result.ncols assert 2 == result.version @@ -194,28 +194,28 @@ def test_integer(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST INTEGER", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_INTEGER == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER == result.tag assert 123456 == result.get_result() def test_float(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST FLOAT", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_FLOAT == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_FLOAT == result.tag assert 3.1415926 == result.get_result() def test_string(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST STRING", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_STRING == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_STRING == result.tag assert result.get_result() == "Hello World, this is a test string." def test_zero_string(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST ZERO_STRING", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_STRING == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_STRING == result.tag assert ( result.get_result() == "Hello World, this is a zero-terminated test string." ) @@ -224,7 +224,7 @@ def test_empty_string(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST STRING0", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_STRING == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_STRING == result.tag assert result.get_result() == "" def test_command(self, sqlitecloud_connection): @@ -237,7 +237,7 @@ def test_json(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST JSON", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_JSON == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_JSON == result.tag assert { "msg-from": {"class": "soldier", "name": "Wixilav"}, "msg-to": {"class": "supreme-commander", "name": "[Redacted]"}, @@ -254,20 +254,20 @@ def test_blob(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST BLOB", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_BLOB == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_BLOB == result.tag assert len(result.get_result()) == 1000 def test_blob0(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST BLOB0", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_STRING == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_STRING == result.tag assert len(result.get_result()) == 0 def test_error(self, sqlitecloud_connection): connection, client = sqlitecloud_connection - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: client.exec_query("TEST ERROR", connection) assert e.value.errcode == 66666 @@ -276,7 +276,7 @@ def test_error(self, sqlitecloud_connection): def test_ext_error(self, sqlitecloud_connection): connection, client = sqlitecloud_connection - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: client.exec_query("TEST EXTERROR", connection) assert e.value.errcode == 66666 @@ -292,7 +292,7 @@ def test_array(self, sqlitecloud_connection): result_array = result.get_result() - assert SQCLOUD_RESULT_TYPE.RESULT_ARRAY == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_ARRAY == result.tag assert isinstance(result_array, list) assert len(result_array) == 5 assert result_array[0] == "Hello World" @@ -304,7 +304,7 @@ def test_rowset(self, sqlitecloud_connection): connection, client = sqlitecloud_connection result = client.exec_query("TEST ROWSET", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_ROWSET == result.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET == result.tag assert result.nrows >= 30 assert result.ncols == 2 assert result.version in [1, 2] @@ -312,12 +312,12 @@ def test_rowset(self, sqlitecloud_connection): assert result.get_name(1) == "value" def test_max_rows_option(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.dbname = os.getenv("SQLITE_DB") account.apikey = os.getenv("SQLITE_API_KEY") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.maxrows = 1 connection = client.open_connection() @@ -331,31 +331,31 @@ def test_max_rows_option(self): assert rowset.nrows > 100 def test_max_rowset_option_to_fail_when_rowset_is_bigger(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.dbname = os.getenv("SQLITE_DB") account.apikey = os.getenv("SQLITE_API_KEY") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.maxrowset = 1024 connection = client.open_connection() - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: client.exec_query("SELECT * FROM albums", connection) client.disconnect(connection) - assert SQCLOUD_ERRCODE.INTERNAL.value == e.value.errcode + assert SQLITECLOUD_ERRCODE.INTERNAL.value == e.value.errcode assert "RowSet too big to be sent (limit set to 1024 bytes)." == e.value.errmsg def test_max_rowset_option_to_succeed_when_rowset_is_lighter(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.dbname = os.getenv("SQLITE_DB") account.apikey = os.getenv("SQLITE_API_KEY") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.maxrowset = 1024 connection = client.open_connection() @@ -371,7 +371,7 @@ def test_chunked_rowset(self, sqlitecloud_connection): rowset = client.exec_query("TEST ROWSET_CHUNK", connection) - assert SQCLOUD_RESULT_TYPE.RESULT_ROWSET == rowset.tag + assert SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET == rowset.tag assert 147 == rowset.nrows assert 1 == rowset.ncols assert 147 == len(rowset.data) @@ -413,18 +413,18 @@ def test_serialized_operations(self, sqlitecloud_connection): assert rowset.version in [1, 2] def test_query_timeout(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.dbname = os.getenv("SQLITE_DB") account.apikey = os.getenv("SQLITE_API_KEY") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.timeout = 1 # 1 sec connection = client.open_connection() # this operation should take more than 1 sec - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: # just a long running query client.exec_query( """ @@ -440,7 +440,7 @@ def test_query_timeout(self): client.disconnect(connection) - assert e.value.errcode == SQCLOUD_INTERNAL_ERRCODE.NETWORK + assert e.value.errcode == SQLITECLOUD_INTERNAL_ERRCODE.NETWORK assert e.value.errmsg == "An error occurred while reading data from the socket." def test_XXL_query(self, sqlitecloud_connection): @@ -508,12 +508,12 @@ def test_select_long_formatted_string(self, sqlitecloud_connection): assert len(rowset.get_value(0, 0)) == 1000 def test_select_database(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.dbname = "" account.apikey = os.getenv("SQLITE_API_KEY") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) connection = client.open_connection() @@ -604,12 +604,12 @@ def test_stress_test_20x_batched_selects(self, sqlitecloud_connection): ), f"{num_queries}x batched selects, {query_ms}ms per query" def test_compression_single_column(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.apikey = os.getenv("SQLITE_API_KEY") account.dbname = os.getenv("SQLITE_DB") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.compression = True connection = client.open_connection() @@ -629,12 +629,12 @@ def test_compression_single_column(self): assert len(rowset.get_value(0, 0)) == blob_size * 2 def test_compression_multiple_columns(self): - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = os.getenv("SQLITE_HOST") account.apikey = os.getenv("SQLITE_API_KEY") account.dbname = os.getenv("SQLITE_DB") - client = SqliteCloudClient(cloud_account=account) + client = SQLiteCloudClient(cloud_account=account) client.config.compression = True connection = client.open_connection() diff --git a/src/tests/integration/test_dbapi2.py b/src/tests/integration/test_dbapi2.py index b92ceb0..e97f383 100644 --- a/src/tests/integration/test_dbapi2.py +++ b/src/tests/integration/test_dbapi2.py @@ -5,15 +5,15 @@ import sqlitecloud from sqlitecloud.types import ( - SQCLOUD_INTERNAL_ERRCODE, - SQCloudException, - SqliteCloudAccount, + SQLITECLOUD_INTERNAL_ERRCODE, + SQLiteCloudAccount, + SQLiteCloudException, ) class TestDBAPI2: def test_connect_with_account(self): - account = SqliteCloudAccount( + account = SQLiteCloudAccount( os.getenv("SQLITE_USER"), os.getenv("SQLITE_PASSWORD"), os.getenv("SQLITE_HOST"), @@ -35,7 +35,7 @@ def test_connect_with_connection_string(self): assert isinstance(connection, sqlitecloud.Connection) def test_disconnect(self): - account = SqliteCloudAccount( + account = SQLiteCloudAccount( os.getenv("SQLITE_USER"), os.getenv("SQLITE_PASSWORD"), os.getenv("SQLITE_HOST"), @@ -49,10 +49,10 @@ def test_disconnect(self): assert isinstance(connection, sqlitecloud.Connection) - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: connection.execute("SELECT 1") - assert e.value.errcode == SQCLOUD_INTERNAL_ERRCODE.NETWORK + assert e.value.errcode == SQLITECLOUD_INTERNAL_ERRCODE.NETWORK assert e.value.errmsg == "The connection is closed." def test_select(self, sqlitecloud_dbapi2_connection): @@ -77,7 +77,7 @@ def test_connection_execute(self, sqlitecloud_dbapi2_connection): def test_column_not_found(self, sqlitecloud_dbapi2_connection): connection = sqlitecloud_dbapi2_connection - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: connection.execute("SELECT not_a_column FROM albums") assert e.value.errcode == 1 @@ -126,7 +126,7 @@ def test_integer(self, sqlitecloud_dbapi2_connection): def test_error(self, sqlitecloud_dbapi2_connection): connection = sqlitecloud_dbapi2_connection - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: connection.execute("TEST ERROR") assert e.value.errcode == 66666 diff --git a/src/tests/integration/test_download.py b/src/tests/integration/test_download.py index e40825c..ddd5f46 100644 --- a/src/tests/integration/test_download.py +++ b/src/tests/integration/test_download.py @@ -4,7 +4,7 @@ import pytest from sqlitecloud import download -from sqlitecloud.types import SQCLOUD_ERRCODE, SQCloudException +from sqlitecloud.types import SQLITECLOUD_ERRCODE, SQLiteCloudException class TestDownload: @@ -25,8 +25,8 @@ def test_download_missing_database(self, sqlitecloud_connection): temp_file = tempfile.mkstemp(prefix="missing")[1] - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: download.download_db(connection, "missing.sqlite", temp_file) - assert e.value.errcode == SQCLOUD_ERRCODE.COMMAND.value + assert e.value.errcode == SQLITECLOUD_ERRCODE.COMMAND.value assert e.value.errmsg == "Database missing.sqlite does not exist." diff --git a/src/tests/integration/test_pubsub.py b/src/tests/integration/test_pubsub.py index 2b037ba..be4b98a 100644 --- a/src/tests/integration/test_pubsub.py +++ b/src/tests/integration/test_pubsub.py @@ -3,13 +3,13 @@ import pytest -from sqlitecloud.pubsub import SqliteCloudPubSub -from sqlitecloud.resultset import SqliteCloudResultSet +from sqlitecloud.pubsub import SQLiteCloudPubSub +from sqlitecloud.resultset import SQLiteCloudResultSet from sqlitecloud.types import ( - SQCLOUD_ERRCODE, - SQCLOUD_PUBSUB_SUBJECT, - SQCLOUD_RESULT_TYPE, - SQCloudException, + SQLITECLOUD_ERRCODE, + SQLITECLOUD_PUBSUB_SUBJECT, + SQLITECLOUD_RESULT_TYPE, + SQLiteCloudException, ) @@ -24,14 +24,14 @@ def assert_callback(conn, result, data): nonlocal callback_called nonlocal flag - if isinstance(result, SqliteCloudResultSet): - assert result.tag == SQCLOUD_RESULT_TYPE.RESULT_JSON + if isinstance(result, SQLiteCloudResultSet): + assert result.tag == SQLITECLOUD_RESULT_TYPE.RESULT_JSON assert data == ["somedata"] callback_called = True flag.set() - pubsub = SqliteCloudPubSub() - type = SQCLOUD_PUBSUB_SUBJECT.CHANNEL + pubsub = SQLiteCloudPubSub() + type = SQLITECLOUD_PUBSUB_SUBJECT.CHANNEL channel = "channel" + str(uuid.uuid4()) pubsub.create_channel(connection, channel) @@ -47,8 +47,8 @@ def assert_callback(conn, result, data): def test_unlisten_channel(self, sqlitecloud_connection): connection, _ = sqlitecloud_connection - pubsub = SqliteCloudPubSub() - type = SQCLOUD_PUBSUB_SUBJECT.CHANNEL + pubsub = SQLiteCloudPubSub() + type = SQLITECLOUD_PUBSUB_SUBJECT.CHANNEL channel_name = "channel" + str(uuid.uuid4()) pubsub.create_channel(connection, channel_name) @@ -68,24 +68,24 @@ def test_unlisten_channel(self, sqlitecloud_connection): def test_create_channel_to_fail_if_exists(self, sqlitecloud_connection): connection, _ = sqlitecloud_connection - pubsub = SqliteCloudPubSub() + pubsub = SQLiteCloudPubSub() channel_name = "channel" + str(uuid.uuid4()) pubsub.create_channel(connection, channel_name, if_not_exists=True) - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: pubsub.create_channel(connection, channel_name, if_not_exists=False) assert ( e.value.errmsg == f"Cannot create channel {channel_name} because it already exists." ) - assert e.value.errcode == SQCLOUD_ERRCODE.GENERIC.value + assert e.value.errcode == SQLITECLOUD_ERRCODE.GENERIC.value def test_is_connected(self, sqlitecloud_connection): connection, _ = sqlitecloud_connection - pubsub = SqliteCloudPubSub() + pubsub = SQLiteCloudPubSub() channel_name = "channel" + str(uuid.uuid4()) assert not pubsub.is_connected(connection) @@ -93,7 +93,7 @@ def test_is_connected(self, sqlitecloud_connection): pubsub.create_channel(connection, channel_name, if_not_exists=True) pubsub.listen( connection, - SQCLOUD_PUBSUB_SUBJECT.CHANNEL, + SQLITECLOUD_PUBSUB_SUBJECT.CHANNEL, channel_name, lambda conn, result, data: None, ) @@ -110,13 +110,13 @@ def assert_callback(conn, result, data): nonlocal callback_called nonlocal flag - if isinstance(result, SqliteCloudResultSet): + if isinstance(result, SQLiteCloudResultSet): assert result.get_result() is not None callback_called = True flag.set() - pubsub = SqliteCloudPubSub() - type = SQCLOUD_PUBSUB_SUBJECT.CHANNEL + pubsub = SQLiteCloudPubSub() + type = SQLITECLOUD_PUBSUB_SUBJECT.CHANNEL channel = "channel" + str(uuid.uuid4()) pubsub.create_channel(connection, channel, if_not_exists=True) @@ -128,7 +128,7 @@ def assert_callback(conn, result, data): assert pubsub.is_connected(connection) connection2 = client.open_connection() - pubsub2 = SqliteCloudPubSub() + pubsub2 = SQLiteCloudPubSub() pubsub2.notify_channel(connection2, channel, "message-in-a-bottle") client.disconnect(connection2) @@ -148,15 +148,15 @@ def assert_callback(conn, result, data): nonlocal callback_called nonlocal flag - if isinstance(result, SqliteCloudResultSet): - assert result.tag == SQCLOUD_RESULT_TYPE.RESULT_JSON + if isinstance(result, SQLiteCloudResultSet): + assert result.tag == SQLITECLOUD_RESULT_TYPE.RESULT_JSON assert new_name in result.get_result() assert data == ["somedata"] callback_called = True flag.set() - pubsub = SqliteCloudPubSub() - type = SQCLOUD_PUBSUB_SUBJECT.TABLE + pubsub = SQLiteCloudPubSub() + type = SQLITECLOUD_PUBSUB_SUBJECT.TABLE new_name = "Rock" + str(uuid.uuid4()) pubsub.listen(connection, type, "genres", assert_callback, ["somedata"]) diff --git a/src/tests/integration/test_sqlite3_parity.py b/src/tests/integration/test_sqlite3_parity.py index b322e0d..e80fc52 100644 --- a/src/tests/integration/test_sqlite3_parity.py +++ b/src/tests/integration/test_sqlite3_parity.py @@ -3,7 +3,7 @@ import pytest -from sqlitecloud.types import SQCloudException +from sqlitecloud.types import SQLiteCloudException class TestSQLite3FeatureParity: @@ -21,10 +21,10 @@ def test_connection_close(self, sqlitecloud_dbapi2_connection, sqlite3_connectio sqlitecloud_connection.close() sqlite3_connection.close() - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: sqlitecloud_connection.execute("SELECT 1") - assert isinstance(e.value, SQCloudException) + assert isinstance(e.value, SQLiteCloudException) with pytest.raises(sqlite3.ProgrammingError) as e: sqlite3_connection.execute("SELECT 1") @@ -127,10 +127,10 @@ def test_close_cursor_raises_exception( sqlitecloud_cursor.close() sqlite3_cursor.close() - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: sqlitecloud_cursor.fetchall() - assert isinstance(e.value, SQCloudException) + assert isinstance(e.value, SQLiteCloudException) with pytest.raises(sqlite3.ProgrammingError) as e: sqlite3_cursor.fetchall() diff --git a/src/tests/unit/test_client.py b/src/tests/unit/test_client.py index 844a456..7a561cc 100644 --- a/src/tests/unit/test_client.py +++ b/src/tests/unit/test_client.py @@ -1,10 +1,10 @@ -from sqlitecloud.client import SqliteCloudClient +from sqlitecloud.client import SQLiteCloudClient class TestClient: def test_parse_connection_string_with_apikey(self): connection_string = "sqlitecloud://user:pass@host.com:8860/dbname?apikey=abc123&timeout=10&compression=true" - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) assert not client.config.account.username assert not client.config.account.password @@ -17,7 +17,7 @@ def test_parse_connection_string_with_apikey(self): def test_parse_connection_string_with_credentials(self): connection_string = "sqlitecloud://user:pass@host.com:8860" - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) assert "user" == client.config.account.username assert "pass" == client.config.account.password @@ -27,7 +27,7 @@ def test_parse_connection_string_with_credentials(self): def test_parse_connection_string_without_credentials(self): connection_string = "sqlitecloud://host.com" - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) assert not client.config.account.username assert not client.config.account.password @@ -36,7 +36,7 @@ def test_parse_connection_string_without_credentials(self): def test_parse_connection_string_with_all_parameters(self): connection_string = "sqlitecloud://host.com:8860/dbname?apikey=abc123&compression=true&zerotext=true&memory=true&create=true&non_linearizable=true&insecure=true&no_verify_certificate=true&root_certificate=rootcert&certificate=cert&certificate_key=certkey&noblob=true&maxdata=10&maxrows=11&maxrowset=12" - client = SqliteCloudClient(connection_str=connection_string) + client = SQLiteCloudClient(connection_str=connection_string) assert "host.com" == client.config.account.hostname assert 8860 == client.config.account.port diff --git a/src/tests/unit/test_dbapi2.py b/src/tests/unit/test_dbapi2.py index 23dd970..c76aad5 100644 --- a/src/tests/unit/test_dbapi2.py +++ b/src/tests/unit/test_dbapi2.py @@ -5,23 +5,23 @@ from sqlitecloud import Cursor from sqlitecloud.dbapi2 import Connection from sqlitecloud.driver import Driver -from sqlitecloud.resultset import SQCloudResult +from sqlitecloud.resultset import SQLiteCloudResult from sqlitecloud.types import ( - SQCLOUD_RESULT_TYPE, - SQCloudConfig, - SQCloudException, - SqliteCloudAccount, + SQLITECLOUD_RESULT_TYPE, + SQLiteCloudAccount, + SQLiteCloudConfig, + SQLiteCloudException, ) def test_connect_with_account_and_config(mocker: MockerFixture): mock_connect = mocker.patch("sqlitecloud.driver.Driver.connect") - account = SqliteCloudAccount() + account = SQLiteCloudAccount() account.hostname = "myhost" account.port = 1234 - config = SQCloudConfig() + config = SQLiteCloudConfig() config.timeout = 99 config.memory = True @@ -56,7 +56,7 @@ def test_description_empty(self, mocker): def test_description_with_resultset(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 1 result.data = ["myname"] @@ -68,7 +68,7 @@ def test_description_with_resultset(self, mocker): def test_description_with_resultset_multiple_rows(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 2 result.nrows = 1 result.data = ["myname"] @@ -83,7 +83,7 @@ def test_description_with_resultset_multiple_rows(self, mocker): def test_rowcount_with_rowset(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 3 result.data = ["myname1", "myname2", "myname3"] @@ -95,7 +95,7 @@ def test_rowcount_with_rowset(self, mocker): def test_rowcount_with_result(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_BLOB) cursor._resultset = result assert cursor.rowcount == -1 @@ -152,7 +152,7 @@ def test_fetchone_with_no_resultset(self, mocker): def test_fetchone_with_result(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_BLOB) cursor._resultset = result assert cursor.fetchone() is None @@ -160,7 +160,7 @@ def test_fetchone_with_result(self, mocker): def test_fetchone_with_rowset(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 1 result.data = ["myname"] @@ -172,7 +172,7 @@ def test_fetchone_with_rowset(self, mocker): def test_fetchone_twice(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 1 result.data = ["myname"] @@ -190,7 +190,7 @@ def test_fetchmany_with_no_resultset(self, mocker): def test_fetchmany_with_result(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_BLOB) cursor._resultset = result assert cursor.fetchmany() == [] @@ -198,7 +198,7 @@ def test_fetchmany_with_result(self, mocker): def test_fetchmany_with_rowset_and_default_size(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 3 result.data = ["myname1", "myname2", "myname3"] @@ -210,7 +210,7 @@ def test_fetchmany_with_rowset_and_default_size(self, mocker): def test_fetchmany_twice_to_retrieve_whole_rowset(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 2 result.data = ["myname1", "myname2"] @@ -223,7 +223,7 @@ def test_fetchmany_twice_to_retrieve_whole_rowset(self, mocker): def test_fetchmany_with_size_higher_than_rowcount(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 1 result.data = ["myname1"] @@ -240,7 +240,7 @@ def test_fetchall_with_no_resultset(self, mocker): def test_fetchall_with_result(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_BLOB) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_BLOB) cursor._resultset = result assert cursor.fetchall() == [] @@ -248,7 +248,7 @@ def test_fetchall_with_result(self, mocker): def test_fetchall_with_rowset(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 3 result.data = ["myname1", "myname2", "myname3"] @@ -260,7 +260,7 @@ def test_fetchall_with_rowset(self, mocker): def test_fetchall_twice_and_expect_empty_list(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 2 result.data = ["myname1", "myname2"] @@ -273,7 +273,7 @@ def test_fetchall_twice_and_expect_empty_list(self, mocker): def test_fetchall_to_return_remaining_rows(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 2 result.data = ["myname1", "myname2"] @@ -286,7 +286,7 @@ def test_fetchall_to_return_remaining_rows(self, mocker): def test_iterator(self, mocker): cursor = Cursor(mocker.patch("sqlitecloud.Connection")) - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 2 result.data = ["myname1", "myname2"] @@ -296,10 +296,10 @@ def test_iterator(self, mocker): assert list(cursor) == [("myname1",), ("myname2",)] def test_row_factory(self, mocker): - conn = Connection(mocker.patch("sqlitecloud.types.SQCloudConnect")) + conn = Connection(mocker.patch("sqlitecloud.types.SQLiteCloudConnect")) conn.row_factory = lambda x, y: {"name": y[0]} - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 1 result.nrows = 2 result.data = ["myname1", "myname2"] @@ -328,7 +328,7 @@ def test_close_raises_expected_exception_on_any_further_operation( cursor.close() - with pytest.raises(SQCloudException) as e: + with pytest.raises(SQLiteCloudException) as e: getattr(cursor, method)(*args) assert e.value.args[0] == "The cursor is closed." diff --git a/src/tests/unit/test_driver.py b/src/tests/unit/test_driver.py index fadb084..f49ba32 100644 --- a/src/tests/unit/test_driver.py +++ b/src/tests/unit/test_driver.py @@ -2,7 +2,7 @@ from pytest_mock import MockerFixture from sqlitecloud.driver import Driver -from sqlitecloud.types import SQCloudConfig, SqliteCloudAccount +from sqlitecloud.types import SQLiteCloudAccount, SQLiteCloudConfig class TestDriver: @@ -219,8 +219,8 @@ def test_nonlinearizable_command_before_auth_with_account( ): driver = Driver() - config = SQCloudConfig() - config.account = SqliteCloudAccount() + config = SQLiteCloudConfig() + config.account = SQLiteCloudAccount() config.account.username = "pippo" config.account.password = "pluto" config.non_linearizable = True @@ -242,8 +242,8 @@ def test_nonlinearizable_command_before_auth_with_apikey( ): driver = Driver() - config = SQCloudConfig() - config.account = SqliteCloudAccount() + config = SQLiteCloudConfig() + config.account = SQLiteCloudAccount() config.account.apikey = "abc123" config.non_linearizable = True diff --git a/src/tests/unit/test_resultset.py b/src/tests/unit/test_resultset.py index ac22483..f89f274 100644 --- a/src/tests/unit/test_resultset.py +++ b/src/tests/unit/test_resultset.py @@ -1,12 +1,12 @@ import pytest -from sqlitecloud.resultset import SQCloudResult, SqliteCloudResultSet -from sqlitecloud.types import SQCLOUD_RESULT_TYPE, SQCLOUD_VALUE_TYPE +from sqlitecloud.resultset import SQLiteCloudResult, SQLiteCloudResultSet +from sqlitecloud.types import SQLITECLOUD_RESULT_TYPE, SQLITECLOUD_VALUE_TYPE -class TestSqCloudResult: +class TestSQLiteCloudResult: def test_init_data(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_INTEGER) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER) result.init_data(42) assert 1 == result.nrows assert 1 == result.ncols @@ -14,7 +14,7 @@ def test_init_data(self): assert True is result.is_result def test_init_data_with_array(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ARRAY) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ARRAY) result.init_data([42, 43, 44]) assert 1 == result.nrows @@ -23,7 +23,7 @@ def test_init_data_with_array(self): assert True is result.is_result def test_init_as_dataset(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) assert False is result.is_result assert 0 == result.nrows @@ -31,7 +31,7 @@ def test_init_as_dataset(self): assert 0 == result.version def test_get_value_with_rowset(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.nrows = 2 result.ncols = 2 result.colname = ["name", "age"] @@ -43,12 +43,14 @@ def test_get_value_with_rowset(self): assert result.get_value(2, 2) is None def test_get_value_array(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ARRAY, result=[1, 2, 3]) + result = SQLiteCloudResult( + SQLITECLOUD_RESULT_TYPE.RESULT_ARRAY, result=[1, 2, 3] + ) assert [1, 2, 3] == result.get_value(0, 0) def test_get_colname(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.ncols = 2 result.colname = ["name", "age"] @@ -57,7 +59,7 @@ def test_get_colname(self): assert result.get_name(2) is None def test_get_value_with_empty_decltype(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.nrows = 2 result.ncols = 2 result.colname = [] @@ -70,7 +72,7 @@ def test_get_value_with_empty_decltype(self): assert "24" == result.get_value(1, 1) def test_get_value_with_convert_false(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.nrows = 1 result.ncols = 2 result.colname = ["name", "age"] @@ -83,49 +85,49 @@ def test_get_value_with_convert_false(self): @pytest.mark.parametrize( "value_type, value, expected_value", [ - (SQCLOUD_VALUE_TYPE.INTEGER.value, "24", 24), - (SQCLOUD_VALUE_TYPE.FLOAT.value, "3.14", 3.14), - (SQCLOUD_VALUE_TYPE.TEXT.value, "John", "John"), - (SQCLOUD_VALUE_TYPE.BLOB.value, b"hello", b"hello"), - (SQCLOUD_VALUE_TYPE.NULL.value, "NULL", None), + (SQLITECLOUD_VALUE_TYPE.INTEGER.value, "24", 24), + (SQLITECLOUD_VALUE_TYPE.FLOAT.value, "3.14", 3.14), + (SQLITECLOUD_VALUE_TYPE.TEXT.value, "John", "John"), + (SQLITECLOUD_VALUE_TYPE.BLOB.value, b"hello", b"hello"), + (SQLITECLOUD_VALUE_TYPE.NULL.value, "NULL", None), ], ) def test_get_value_to_convert_text(self, value_type, value, expected_value): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) result.nrows = 1 result.ncols = 1 result.colname = ["mycol"] result.data = [value] result.decltype = [value_type] - result_set = SqliteCloudResultSet(result) + result_set = SQLiteCloudResultSet(result) assert expected_value == result_set.get_value(0, 0) class TestSqliteCloudResultSet: def test_next(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) - result_set = SqliteCloudResultSet(result) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) + result_set = SQLiteCloudResultSet(result) assert {"result": 42} == next(result_set) with pytest.raises(StopIteration): next(result_set) def test_iter_result(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) - result_set = SqliteCloudResultSet(result) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) + result_set = SQLiteCloudResultSet(result) for row in result_set: assert {"result": 42} == row def test_iter_rowset(self): - rowset = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_ROWSET) + rowset = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_ROWSET) rowset.nrows = 2 rowset.ncols = 2 rowset.colname = ["name", "age"] rowset.data = ["John", 42, "Doe", 24] rowset.version = 2 - result_set = SqliteCloudResultSet(rowset) + result_set = SQLiteCloudResultSet(rowset) out = [] for row in result_set: @@ -136,7 +138,7 @@ def test_iter_rowset(self): assert {"name": "Doe", "age": 24} == out[1] def test_get_result_with_single_value(self): - result = SQCloudResult(SQCLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) - result_set = SqliteCloudResultSet(result) + result = SQLiteCloudResult(SQLITECLOUD_RESULT_TYPE.RESULT_INTEGER, result=42) + result_set = SQLiteCloudResultSet(result) assert 42 == result_set.get_result() diff --git a/src/tests/unit/test_types.py b/src/tests/unit/test_types.py index 9b56754..e08e569 100644 --- a/src/tests/unit/test_types.py +++ b/src/tests/unit/test_types.py @@ -1,9 +1,9 @@ import pytest -from sqlitecloud.types import SQCloudConfig +from sqlitecloud.types import SQLiteCloudConfig -class TestSQCloudConfig: +class TestSQLiteCloudConfig: @pytest.mark.parametrize( "param, value", [ @@ -14,6 +14,6 @@ class TestSQCloudConfig: def test_parse_connection_string_with_nonlinarizable(self, param: str, value: any): connection_string = f"sqlitecloud://myhost.sqlitecloud.io?{param}={value}" - config = SQCloudConfig(connection_string) + config = SQLiteCloudConfig(connection_string) assert config.non_linearizable