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/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
run: uv pip install --system --requirement=requirements.txt --requirement=requirements-dev.txt

- name: Run linters and software tests
run: poe test
run: poe check
Comment on lines 57 to +58
Copy link
Member Author

@amotl amotl Oct 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB/FYI: poe check invokes both poe lint and poe test, in this order. That verifies that the code is always in the right shape, and will never include obstacles, mostly introduced by accidents, for example stray print statements or such.


# https://github.com/codecov/codecov-action
- name: Upload coverage results to Codecov
Expand Down
57 changes: 29 additions & 28 deletions cratedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import requests
except ImportError:
import urequests as requests

from base64 import b64encode

# IDs of CrateDB supported data types.
Expand Down Expand Up @@ -78,75 +78,76 @@
CRATEDB_ERROR_SNAPSHOT_CREATION_FAILED = 5004
CRATEDB_ERROR_QUERY_KILLED = 5030


class NetworkError(Exception):
pass


class CrateDBError(Exception):
pass


class CrateDB:
def __init__(self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True):
def __init__(
self, host, port=4200, user=None, password=None, schema="doc", use_ssl=True
):
self.user = user
self.password = password
self.schema = schema
self.host = host
self.port = port
self.use_ssl = use_ssl

self.cratedb_url = f"{'https' if self.use_ssl == True else 'http'}://{self.host}:{self.port}/_sql"
self.cratedb_url = f"{'https' if self.use_ssl is True else 'http'}://{self.host}:{self.port}/_sql"

if self.user is not None and self.password is not None:
self.encoded_credentials = self.__encode_credentials(self.user, self.password)


def __encode_credentials(self, user, password):
creds_str = f"{user}:{password}"
return b64encode(creds_str.encode("UTF-8")).decode("UTF-8")


def __make_request(self, sql, args=None, with_types = False, return_response = True):
headers = {
"Content-Type": "text/json",
"Default-Schema": self.schema
}
def __make_request(self, sql, args=None, with_types=False, return_response=True):
headers = {"Content-Type": "text/json", "Default-Schema": self.schema}

if hasattr(self, "encoded_credentials"):
headers["Authorization"] = f"Basic {self.encoded_credentials}"

request_url = self.cratedb_url if with_types == False else f"{self.cratedb_url}?types"
request_url = (
self.cratedb_url if with_types is False else f"{self.cratedb_url}?types"
)

payload = {
"stmt": sql
}
payload = {"stmt": sql}

if args is not None:
for arg in args:
if not isinstance(arg, list):
payload["args"] = args
break

if not "args" in payload:
if "args" not in payload:
payload["bulk_args"] = args

try:
response = requests.post(
request_url,
headers = headers,
json = payload
)
response = requests.post(request_url, headers=headers, json=payload)
except OSError as o:
raise NetworkError(o)
raise NetworkError(o) # noqa: B904

if response.status_code == 400 or response.status_code == 404 or response.status_code == 409:
if (
response.status_code == 400
or response.status_code == 404
or response.status_code == 409
):
error_doc = response.json()
raise CrateDBError(error_doc)
elif response.status_code != 200:
raise NetworkError(f"Error {response.status_code}: {response.reason.decode('UTF-8')}")
if response.status_code != 200:
raise NetworkError(
f"Error {response.status_code}: {response.reason.decode('UTF-8')}"
)

if return_response == True:
if return_response is True:
return response.json()
return None


def execute(self, sql, args = None, with_types = False, return_response = True):
def execute(self, sql, args=None, with_types=False, return_response=True):
return self.__make_request(sql, args, with_types, return_response)

25 changes: 13 additions & 12 deletions examples/example_usage.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ruff: noqa: W291 Trailing whitespace

# Example script showing different types of interactions with
# CrateDB. This has no hardware dependencies so should run
# CrateDB. This has no hardware dependencies so should run
# in any MicroPython environment. You will need to edit the
# code below to use your CrateDB credentials.

Expand All @@ -20,7 +22,9 @@
try:
# Create a table.
print("Create table.")
response = crate.execute("create table driver_test(id TEXT, val1 bigint, val2 bigint, val3 boolean)")
response = crate.execute(
"create table driver_test(id TEXT, val1 bigint, val2 bigint, val3 boolean)"
)
# response:
# {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 119.652275}
print(response)
Expand All @@ -29,10 +33,7 @@
print("Bulk insert.")
response = crate.execute(
"insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)",
[
[ "a", 2, 3, True ],
[ "b", 3, 4, False ]
]
[["a", 2, 3, True], ["b", 3, 4, False]],
)
# response:
# {'results': [{'rowcount': 1}, {'rowcount': 1}], 'cols': [], 'duration': 6.265751}
Expand All @@ -42,14 +43,14 @@
print("Select with column data types.")
response = crate.execute("select * from driver_test", with_types=True)
# response:
# {'col_types': [4, 10, 10, 3], 'cols': ['id', 'val1', 'val2', 'val3'], 'rowcount': 2, 'rows': [['b', 3, 4, False], ['a', 2, 3, True]], 'duration': 4.378391}
# {'col_types': [4, 10, 10, 3], 'cols': ['id', 'val1', 'val2', 'val3'], 'rowcount': 2,
# 'rows': [['b', 3, 4, False], ['a', 2, 3, True]], 'duration': 4.378391}
print(response)

# SELECT with parameter substitution.
print("Select with parameter substitution.")
response = crate.execute(
"select val1, val2 from driver_test where val1 > ? and val2 < ?",
[ 1, 4 ]
"select val1, val2 from driver_test where val1 > ? and val2 < ?", [1, 4]
)
# response:
# {'rows': [[2, 3]], 'rowcount': 1, 'cols': ['val1', 'val2'], 'duration': 3.266117}
Expand All @@ -59,7 +60,7 @@
print("Insert with parameter substitution.")
response = crate.execute(
"insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)",
[ "d", 1, 9, False ]
["d", 1, 9, False],
)
# response:
# {'rows': [[]], 'rowcount': 1, 'cols': [], 'duration': 5.195949}
Expand All @@ -69,8 +70,8 @@
print("Insert with parameter substitution and no response processing.")
response = crate.execute(
"insert into driver_test (id, val1, val2, val3) values (?, ?, ?, ?)",
[ "e", 4, 12, True ],
return_response=False
["e", 4, 12, True],
return_response=False,
)
# response:
# None
Expand Down
Loading
Loading