-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Fixes #123, fixes #113 Prepare and setup workspace for release(s) * Update setup.py - Update author(s) and author_email - Reformat setup.py - Update metadata [classifiers, links, ...] * Update outdated dependency markers in setup.py * Update README.rst * Add additional python versions to CI/CD build matrix * Add nox session(s) for checking and listing documentation links - Add link check to CI/CD pipeline * Fix broken and outdated links in the documentation * Update deployment token * Pin version of deployment action * Update changelog
- Loading branch information
Showing
11 changed files
with
193 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,8 @@ SQLAlchemy Dialect for EXASOL DB | |
================================ | ||
|
||
|
||
.. image:: https://github.com/blue-yonder/sqlalchemy_exasol/workflows/CI-CD/badge.svg?branch=master | ||
:target: https://github.com/blue-yonder/sqlalchemy_exasol/actions?query=workflow%3ACI-CD | ||
.. image:: https://github.com/exasol/sqlalchemy_exasol/workflows/CI-CD/badge.svg?branch=master | ||
:target: https://github.com/exasol/sqlalchemy_exasol/actions?query=workflow%3ACI-CD | ||
.. image:: https://img.shields.io/pypi/v/sqlalchemy_exasol | ||
:target: https://pypi.org/project/sqlalchemy-exasol/ | ||
:alt: PyPI Version | ||
|
@@ -20,93 +20,96 @@ How to get started | |
------------------ | ||
|
||
We assume you have a good understanding of (unix)ODBC. If not, make sure you | ||
read their documentation carefully - there are lot's of traps to step into. | ||
|
||
Get the EXASolution database | ||
```````````````````````````` | ||
|
||
If you do not have access to an EXASolution database, download EXASolo for free | ||
from EXASOL: http://www.exasol.com/en/test-drive/ | ||
|
||
The database is a VM image. You will need VirtualBox, VMWare Player, or KVM to | ||
run the database. Start the database and make sure you can connect to it as | ||
described in the How-To from EXASOL. | ||
read their documentation carefully - there are lot's of traps 🪤 to step into. | ||
|
||
Meet the system requirements | ||
```````````````````````````` | ||
|
||
On Linux/Unix like systems you need: | ||
|
||
- the packages unixODBC and unixODBC-dev >= 2.2.14 | ||
- Python >= 2.7 | ||
- Download and install the ODBC client drivers from EXASOL >= 5 | ||
- configure ODBC.ini and ODBCINST.ini | ||
- An Exasol DB (e.g. `docker-db <test_docker_image_>`_ or a `cloud instance <test_drive_>`_) | ||
|
||
- >= 7.1.6 | ||
- >= 7.0.16 | ||
|
||
- The packages unixODBC and unixODBC-dev >= 2.2.14 | ||
- Python >= 3.6 | ||
- The Exasol `ODBC driver <odbc_driver_>`_ | ||
- The ODBC.ini and ODBCINST.ini configurations files setup | ||
|
||
Turbodbc support | ||
```````````````` | ||
|
||
- Turbodbc and sqlalchemy_exasol as well do now support python 2.7, 3.4 and 3.6. | ||
- You can use Turbodbc with sqlalchemy_exasol if you use a python version >= 3.6. | ||
- Multi row update is not supported, see | ||
`test/test_update.py <test/test_update.py>`_ for an example | ||
|
||
|
||
Setup you python project and install sqlalchemy-exasol | ||
`````````````````````````````````````````````````````` | ||
Setup your python project and install sqlalchemy-exasol | ||
``````````````````````````````````````````````````````` | ||
|
||
:: | ||
.. code-block:: shell | ||
> pip install sqlalchemy-exasol | ||
$ pip install sqlalchemy-exasol | ||
for turbodbc support: | ||
|
||
:: | ||
.. code-block:: shell | ||
> pip install sqlalchemy-exasol[turbodbc] | ||
$ pip install sqlalchemy-exasol[turbodbc] | ||
Talk to EXASolution using SQLAlchemy | ||
```````````````````````````````````` | ||
Talk to the EXASOL DB using SQLAlchemy | ||
`````````````````````````````````````` | ||
|
||
:: | ||
.. code-block:: python | ||
from sqlalchemy import create_engine | ||
e = create_engine("exa+pyodbc://A_USER:[email protected]:1234/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC") | ||
url = "exa+pyodbc://A_USER:[email protected]:1234/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC" | ||
e = create_engine(url) | ||
r = e.execute("select 42 from dual").fetchall() | ||
to use turbodbc as driver: | ||
|
||
:: | ||
.. code-block:: python | ||
from sqlalchemy import create_engine | ||
e = create_engine("exa+turbodbc://A_USER:[email protected]:1234/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC") | ||
url = "exa+turbodbc://A_USER:[email protected]:1234/my_schema?CONNECTIONLCALL=en_US.UTF-8&driver=EXAODBC" | ||
e = create_engine(url) | ||
r = e.execute("select 42 from dual").fetchall() | ||
The dialect supports two connection urls for create_engine. A DSN (Data Source Name) mode and a host mode: | ||
The dialect supports two types of connection urls creating an engine. A DSN (Data Source Name) mode and a host mode: | ||
|
||
.. list-table:: | ||
:widths: 50 50 | ||
:header-rows: 1 | ||
|
||
======== ==================================================================== | ||
DSN url 'exa+pyodbc://USER:PWD@exa_test' | ||
Host url 'exa+pyodbc://USER:[email protected]:1234/my_schema?parameter' | ||
======== ==================================================================== | ||
* - Type | ||
- Example | ||
* - DSN URL | ||
- 'exa+pyodbc://USER:PWD@exa_test' | ||
* - HOST URL | ||
- 'exa+pyodbc://USER:[email protected]:1234/my_schema?parameter' | ||
|
||
*Features*: | ||
Features | ||
++++++++ | ||
|
||
- SELECT, INSERT, UPDATE, DELETE statements | ||
- you can even use the MERGE statement (see unit tests for examples) | ||
|
||
*Note*: | ||
Notes | ||
+++++ | ||
|
||
- Schema name and parameters are optional for the host url string | ||
- Schema name and parameters are optional for the host url | ||
- At least on Linux/Unix systems it has proven valuable to pass 'CONNECTIONLCALL=en_US.UTF-8' as a url parameter. This will make sure that the client process (Python) and the EXASOL driver (UTF-8 internal) know how to interpret code pages correctly. | ||
- Always use all lower-case identifiers for schema, table and column names. SQLAlchemy treats all lower-case identifiers as case-insensitive, the dialect takes care of transforming the identifier into a case-insensitive representation of the specific database (in case of EXASol this is upper-case as for Oracle) | ||
- As of EXASol client driver version 4.1.2 you can pass the flag 'INTTYPESINRESULTSIFPOSSIBLE=y' in the connection string (or configure it in your DSN). This will convert DECIMAL data types to Integer-like data types. Creating integers is a factor three faster in Python than creating Decimals. | ||
|
||
Troubleshooting | ||
``````````````` | ||
|
||
The unixodbc Stack is not the most friendly for programmers. If you get strange errors from the driver mangager, you might have an issue with the names of the unixodbc libs. Have a look at https://github.com/blue-yonder/sqlalchemy_exasol/blob/master/fix_unixodbc_so.sh to find ideas on how to fix this on Ubuntu. Good luck! | ||
- As of Exasol client driver version 4.1.2 you can pass the flag 'INTTYPESINRESULTSIFPOSSIBLE=y' in the connection string (or configure it in your DSN). This will convert DECIMAL data types to Integer-like data types. Creating integers is a factor three faster in Python than creating Decimals. | ||
|
||
Development & Testing | ||
````````````````````` | ||
See `developer guide`_ | ||
|
||
.. _developer guide: https://github.com/exasol/sqlalchemy-exasol/blob/doc/developer_guide/developer_guide.rst | ||
.. _developer guide: https://github.com/exasol/sqlalchemy-exasol/blob/master/doc/developer_guide/developer_guide.rst | ||
.. _odbc_driver: https://docs.exasol.com/db/latest/connect_exasol/drivers/odbc/odbc_linux.htm | ||
.. _test_drive: https://www.exasol.com/test-it-now/cloud/ | ||
.. _test_docker_image: https://github.com/exasol/docker-db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[ODBC] | ||
Trace=yes | ||
TraceFile=/home/nic/Projects/sqla-playground/odbc.trace | ||
|
||
[EXAODBC] | ||
#Driver location will be appended in build environment: | ||
DRIVER=/home/nic/Projects/sqla-playground/driver/libexaodbc-uo2214lv1.so | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
-r requirements_test.txt | ||
nox>=2022.1.7 | ||
urlscan>=0.9.9 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import urllib.error | ||
import subprocess | ||
from itertools import repeat | ||
from pathlib import Path | ||
from typing import Iterable, Tuple | ||
from urllib import request | ||
|
||
|
||
def documentation(root: Path) -> Iterable[Path]: | ||
"""Returns an iterator over all documentation files of the project""" | ||
docs = Path(root).glob("**/*.rst") | ||
|
||
def _deny_filter(path): | ||
return not ("venv" in path.parts) | ||
|
||
return filter(lambda path: _deny_filter(path), docs) | ||
|
||
|
||
def urls(files: Iterable[Path]) -> Iterable[Tuple[Path, str]]: | ||
"""Returns an iterable over all urls contained in the provided files""" | ||
|
||
def should_filter(url): | ||
_filtered = [] | ||
return url.startswith("mailto") or url in _filtered | ||
|
||
for file in files: | ||
cmd = ['python', '-m', 'urlscan', '-n', f'{file}'] | ||
result = subprocess.run(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE) | ||
if result.returncode != 0: | ||
stderr = result.stderr.decode('utf8') | ||
msg = f"Could not retrieve url's from file: {file}, details: {stderr}" | ||
raise Exception(msg) | ||
stdout = result.stdout.decode('utf8').strip() | ||
_urls = (url.strip() for url in stdout.split('\n')) | ||
yield from zip( | ||
repeat(file), filter(lambda url: not should_filter(url), _urls) | ||
) | ||
|
||
|
||
def check(url: str) -> Tuple[int, str]: | ||
"""Checks if an url is still working (can be accessed)""" | ||
try: | ||
# User-Agent needs to be faked otherwise some webpages will deny access with a 403 | ||
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) | ||
result = request.urlopen(req) | ||
return result.code, f"{result.msg}" | ||
except urllib.error.HTTPError as ex: | ||
return ex.status, f"{ex}" |
Oops, something went wrong.