Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:

- name: Run tests
run: |
python -m coverage run -m pytest -vvv -s
python -m coverage run -m pytest
python -m coverage lcov

- name: Python ${{ matrix.python-version }} Coveralls
Expand Down
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Changelog
All notable changes to this project are documented in this file.

## [Unreleased]

### Added

- Oracle databases are now supported via driver `oracledb`. [#5]
- (INTERNAL) `BaseDriver` subclasses can now optionally implement an `init_connection` method which is called right after a `PyDbAPIv2Connection` is created so as to perform any driver specific initializations on said connection. [#5]

### Changed

- (INTERNAL) Renamed certain functions to better convey their meaning. [#5]


## 0.1.1 [2025-11-26]

### Fixed
Expand All @@ -9,5 +21,5 @@ All notable changes to this project are documented in this file.




[#5]: https://github.com/manoss96/onlymaps/pull/5
[#6]: https://github.com/manoss96/onlymaps/pull/6
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
![OnlyMaps Logo](docs/source/onlymaps.png)


Onlymaps is a Python micro-ORM library that enables you to interact with a database
Onlymaps is a Python micro-ORM library that lets you interact with a database
through plain SQL queries while it takes care of mapping the results back to Python
objects. More specifically, it provides:

Expand Down Expand Up @@ -103,6 +103,7 @@ The `{DB_TYPE}` placeholder can take any of the following values depending on th
- `mysql`: MySQL
- `mssql`: Microsoft SQL Server
- `mariadb`: MariaDB
- `oraceldb`: Oracle Database
- `sqlite`: SQLite. More specifically, when connecting to a SQLite database, your connection string must be formatted as such: `sqlite:///{DB_NAME}`.

#### Using unsupported drivers
Expand Down Expand Up @@ -392,7 +393,7 @@ If you wish to retain that, you should use a model type:
```python
rows: list[dict] = db.fetch_many(dict, "SELECT id, label FROM my_table")

print(rows.keys()) # This prints `dict_keys(['id', 'label'])`.
print(rows[0].keys()) # This prints `dict_keys(['id', 'label'])`.
```

You can use whichever suits you best, as both container types and
Expand Down
82 changes: 42 additions & 40 deletions onlymaps/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(
self.__driver = driver if driver else UnknownDriver.create()
self.__handle_broken_conn = handle_broken_conn
self.__cursor_lock = Lock()
self.__open_lock = Lock()
self.__iter_lock = Lock()
self.__is_open = False
self.__iteration_id: int | None = None
Expand Down Expand Up @@ -200,56 +201,56 @@ def _safe_cursor(self) -> Iterator[PyDbAPIv2Cursor]: # <async>
if acquire_lock:
self.__cursor_lock.acquire() # <await>

if self.__handle_broken_conn and not self.__in_transaction:
try:
# Both obtain a cursor and ping the database by
# executing a no-result query to ensure that the
# connection has not been closed.
# fmt: off
cursor = (
self.__conn.cursor() # <replace:await co_exec(self.__conn.cursor)>
)
# fmt: on
cursor.execute("SELECT 1 WHERE FALSE") # <await>
except: # pylint: disable=bare-except
try:
# This block ensures a connection is not closed/broken,
# and if it is, attempts to open a new connection.
if self.__handle_broken_conn and not self.__in_transaction:
try:
# The connection is likely to be already closed
# at this point, though an attempt to close it
# doesn't hurt!
self.__conn.close() # <replace:await co_exec(self.__conn.close)>
# Both obtain a cursor and ping the database by
# executing a no-result query to ensure that the
# connection has not been closed.
# fmt: off
cursor = (
self.__conn.cursor() # <replace:await co_exec(self.__conn.cursor)>
)
# fmt: on
cursor.execute("SELECT 1 WHERE FALSE") # <await>
except: # pylint: disable=bare-except
pass
self.__conn = self.__conn_factory() # <await>
# fmt: off
cursor = (
self.__conn.cursor() # <replace:await co_exec(self.__conn.cursor)>
)
# fmt: on
else:
try:
# The connection is likely to be already closed
# at this point, though an attempt to close it
# doesn't hurt!
self.__is_open = False
self.__conn.close() # <replace:await co_exec(self.__conn.close)>
except: # pylint: disable=bare-except
pass
finally:
# Re-open connection.
self.open() # <await>

# fmt: off
cursor = (
self.__conn.cursor() # <replace:await co_exec(self.__conn.cursor)>
)
# fmt: on

try:
yield cursor
is_query_successful = True
except:
is_query_successful = False
if not self.__in_transaction:
self.__conn.rollback() # <await>
raise
finally:
try:
cursor.close() # <await>
yield cursor
is_query_successful = True
except:
is_query_successful = False
if not self.__in_transaction:
self.__conn.rollback() # <await>
raise
finally:
cursor.close() # <replace:await co_exec(cursor.close)>
# Commit only if query execution was successful
# and not in the middle of a transaction.
if is_query_successful and not self.__in_transaction:
self.__conn.commit() # <await>
finally:
if acquire_lock:
self.__cursor_lock.release()
finally:
if acquire_lock:
self.__cursor_lock.release()

@require_open
@__require_not_iter_same_ctx
Expand Down Expand Up @@ -416,15 +417,16 @@ def open(self) -> None: # <async>
if self.__is_open:
raise Error.DbOpenConnection

# Re-check om avoid the case where two or
# more threads call `open` at the same time
# Re-check if open to avoid the case where two
# or more threads call `open` at the same time
# waiting for the lock.
with self.__cursor_lock: # <async>
with self.__open_lock: # <async>
if self.__is_open:
raise Error.DbOpenConnection

try:
self.__conn = self.__conn_factory() # <await>
self.__driver.init_connection(self.__conn)
self.__is_open = True
except:
self.__is_open = False
Expand Down
Loading