zodbc 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zodbc-0.9.1/MANIFEST.in +10 -0
- zodbc-0.9.1/PKG-INFO +106 -0
- zodbc-0.9.1/PyFuncs.zig +60 -0
- zodbc-0.9.1/README.md +90 -0
- zodbc-0.9.1/_zodbc.zig +601 -0
- zodbc-0.9.1/arrow.zig +73 -0
- zodbc-0.9.1/fetch_arrow.zig +686 -0
- zodbc-0.9.1/fetch_py.zig +294 -0
- zodbc-0.9.1/fmt.zig +183 -0
- zodbc-0.9.1/put_arrow.zig +493 -0
- zodbc-0.9.1/put_common.zig +186 -0
- zodbc-0.9.1/put_py.zig +461 -0
- zodbc-0.9.1/pyproject.toml +28 -0
- zodbc-0.9.1/setup.cfg +4 -0
- zodbc-0.9.1/setup.py +32 -0
- zodbc-0.9.1/utils.zig +107 -0
- zodbc-0.9.1/zodbc.egg-info/PKG-INFO +106 -0
- zodbc-0.9.1/zodbc.egg-info/SOURCES.txt +21 -0
- zodbc-0.9.1/zodbc.egg-info/dependency_links.txt +1 -0
- zodbc-0.9.1/zodbc.egg-info/requires.txt +3 -0
- zodbc-0.9.1/zodbc.egg-info/top_level.txt +2 -0
- zodbc-0.9.1/zodbc.py +240 -0
zodbc-0.9.1/MANIFEST.in
ADDED
zodbc-0.9.1/PKG-INFO
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zodbc
|
|
3
|
+
Version: 0.9.1
|
|
4
|
+
Summary: DBAPI2-like ODBC client with Apache Arrow support
|
|
5
|
+
Author: Felix Graßl
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ffelixg/zodbc_py
|
|
8
|
+
Keywords: odbc,arrow,mssql,sql server
|
|
9
|
+
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Topic :: Database
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
Provides-Extra: pyarrow
|
|
15
|
+
Requires-Dist: pyarrow; extra == "pyarrow"
|
|
16
|
+
|
|
17
|
+
# zodbc
|
|
18
|
+
|
|
19
|
+
This odbc client tries to be compatible with the DB API 2 Specification, while also treating pyarrow compatibility as a first class citizen. At the moment it is tested with the msodbc driver. Compilation and installation is made easy by ziglang being a simple build dependency. The only system dependencies for compilation should be development headers for unixodbc (if on linux) and python.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install zodbc
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Features
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## DB API 2
|
|
33
|
+
|
|
34
|
+
```py
|
|
35
|
+
import zodbc
|
|
36
|
+
import os
|
|
37
|
+
|
|
38
|
+
con = zodbc.connect(os.environ["ODBC_CONSTR"]) # Connect with odbc connection string or DSN
|
|
39
|
+
cur = con.cursor() # Cursor, which translates to an odbc statement handle
|
|
40
|
+
|
|
41
|
+
# python parameters & return values, data types like str, int, bool, UUID, decimal, date, time, datetime
|
|
42
|
+
cur.execute("select ? a", [42])
|
|
43
|
+
assert cur.fetchone()[0] == 42
|
|
44
|
+
|
|
45
|
+
# Short hand alternative for getting column names from cur.description
|
|
46
|
+
assert [c[0] for c in cur.description] == cur.column_names == ["a"]
|
|
47
|
+
|
|
48
|
+
con.commit() # Transactions, autocommit is disabled by default
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Additional fetch methods
|
|
53
|
+
|
|
54
|
+
The DB API 2 fetch methods `fetchone`, `fetchmany` and `fetchall` return (lists of) tuples. Additionally, there is `fetch_named` which returns a list of named tuples, which allows accessing fields by attribute name like `row.a` as well as `fetch_dict` which returns a list of dicts. `fetch_dict` requires unique column names in the query. These methods are implemented directly in Zig and should therefore be more efficient than fetching tuples and converting them in python.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Apache Arrow
|
|
58
|
+
|
|
59
|
+
A query can be executed for every row in an Arrow RecordBatch.
|
|
60
|
+
|
|
61
|
+
```py
|
|
62
|
+
import pyarrow as pa
|
|
63
|
+
|
|
64
|
+
cur.execute("drop table if exists t1")
|
|
65
|
+
cur.execute("create table t1(a int)")
|
|
66
|
+
cur.executemany_arrow(
|
|
67
|
+
"insert into t1(a) values(?)",
|
|
68
|
+
pa.RecordBatch.from_arrays([pa.array([1, 2, 3])], ["a"])
|
|
69
|
+
)
|
|
70
|
+
cur.execute("select * from t1").fetchall() # [(1,), (2,), (3,)]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Arrow RecordBatches can be fetched from a query result.
|
|
74
|
+
|
|
75
|
+
```py
|
|
76
|
+
cur.execute("select 1 a").fetch_arrow()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Arrow RecordBatches can be used as a table valued parameter for MS SQL.
|
|
80
|
+
|
|
81
|
+
```py
|
|
82
|
+
cur.execute("drop type if exists test_tabletype")
|
|
83
|
+
cur.execute("create type test_tabletype as table(a int)")
|
|
84
|
+
con.commit()
|
|
85
|
+
|
|
86
|
+
assert cur.execute(
|
|
87
|
+
"select sum(a) from ? where a <= ?",
|
|
88
|
+
[
|
|
89
|
+
zodbc.ArrowTVP(
|
|
90
|
+
zodbc.ArrowTVPType.from_name("test_tabletype"),
|
|
91
|
+
pa.RecordBatch.from_arrays([pa.array([1, 2, 3])], ["a"]),
|
|
92
|
+
),
|
|
93
|
+
2,
|
|
94
|
+
]
|
|
95
|
+
).fetchone()[0] == 3
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# Differences with pyodbc
|
|
100
|
+
|
|
101
|
+
- UUID is always returned as a python UUID object (no pyodbc.native_uuid global variable)
|
|
102
|
+
- No output conversion (add_output_converter connection method)
|
|
103
|
+
- More explicit:
|
|
104
|
+
- No `execute(query, param1, param2)` => params must be passed as sequence
|
|
105
|
+
- No Cursor.commit, instead use Cursor.connection.commit
|
|
106
|
+
- Connection.getinfo takes a string instead of the odbc enum value
|
zodbc-0.9.1/PyFuncs.zig
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const c = @import("c");
|
|
2
|
+
const Obj = *c.PyObject;
|
|
3
|
+
|
|
4
|
+
cls_datetime: Obj,
|
|
5
|
+
cls_date: Obj,
|
|
6
|
+
cls_time: Obj,
|
|
7
|
+
cls_timezone: Obj,
|
|
8
|
+
cls_timedelta: Obj,
|
|
9
|
+
cls_decimal: Obj,
|
|
10
|
+
func_decimal_intratio: Obj,
|
|
11
|
+
cls_uuid: Obj,
|
|
12
|
+
|
|
13
|
+
pub fn init() !@This() {
|
|
14
|
+
const mod_datetime = c.PyImport_ImportModule("datetime") orelse return error.PyErr;
|
|
15
|
+
defer c.Py_DECREF(mod_datetime);
|
|
16
|
+
const cls_datetime = c.PyObject_GetAttrString(mod_datetime, "datetime") orelse return error.PyErr;
|
|
17
|
+
errdefer c.Py_DECREF(cls_datetime);
|
|
18
|
+
const cls_date = c.PyObject_GetAttrString(mod_datetime, "date") orelse return error.PyErr;
|
|
19
|
+
errdefer c.Py_DECREF(cls_date);
|
|
20
|
+
const cls_time = c.PyObject_GetAttrString(mod_datetime, "time") orelse return error.PyErr;
|
|
21
|
+
errdefer c.Py_DECREF(cls_time);
|
|
22
|
+
const cls_timezone = c.PyObject_GetAttrString(mod_datetime, "timezone") orelse return error.PyErr;
|
|
23
|
+
errdefer c.Py_DECREF(cls_timezone);
|
|
24
|
+
const cls_timedelta = c.PyObject_GetAttrString(mod_datetime, "timedelta") orelse return error.PyErr;
|
|
25
|
+
errdefer c.Py_DECREF(cls_timedelta);
|
|
26
|
+
|
|
27
|
+
const mod_decimal = c.PyImport_ImportModule("decimal") orelse return error.PyErr;
|
|
28
|
+
defer c.Py_DECREF(mod_decimal);
|
|
29
|
+
const cls_decimal = c.PyObject_GetAttrString(mod_decimal, "Decimal") orelse return error.PyErr;
|
|
30
|
+
errdefer c.Py_DECREF(cls_decimal);
|
|
31
|
+
const func_decimal_intratio = c.PyObject_GetAttrString(cls_decimal, "as_integer_ratio") orelse return error.PyErr;
|
|
32
|
+
errdefer c.Py_DECREF(func_decimal_intratio);
|
|
33
|
+
|
|
34
|
+
const mod_uuid = c.PyImport_ImportModule("uuid") orelse return error.PyErr;
|
|
35
|
+
defer c.Py_DECREF(mod_uuid);
|
|
36
|
+
const cls_uuid = c.PyObject_GetAttrString(mod_uuid, "UUID") orelse return error.PyErr;
|
|
37
|
+
errdefer c.Py_DECREF(cls_uuid);
|
|
38
|
+
|
|
39
|
+
return .{
|
|
40
|
+
.cls_datetime = cls_datetime,
|
|
41
|
+
.cls_date = cls_date,
|
|
42
|
+
.cls_time = cls_time,
|
|
43
|
+
.cls_timezone = cls_timezone,
|
|
44
|
+
.cls_timedelta = cls_timedelta,
|
|
45
|
+
.cls_decimal = cls_decimal,
|
|
46
|
+
.func_decimal_intratio = func_decimal_intratio,
|
|
47
|
+
.cls_uuid = cls_uuid,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub fn deinit(self: @This()) void {
|
|
52
|
+
c.Py_DECREF(self.cls_datetime);
|
|
53
|
+
c.Py_DECREF(self.cls_date);
|
|
54
|
+
c.Py_DECREF(self.cls_time);
|
|
55
|
+
c.Py_DECREF(self.cls_timezone);
|
|
56
|
+
c.Py_DECREF(self.cls_timedelta);
|
|
57
|
+
c.Py_DECREF(self.cls_decimal);
|
|
58
|
+
c.Py_DECREF(self.func_decimal_intratio);
|
|
59
|
+
c.Py_DECREF(self.cls_uuid);
|
|
60
|
+
}
|
zodbc-0.9.1/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# zodbc
|
|
2
|
+
|
|
3
|
+
This odbc client tries to be compatible with the DB API 2 Specification, while also treating pyarrow compatibility as a first class citizen. At the moment it is tested with the msodbc driver. Compilation and installation is made easy by ziglang being a simple build dependency. The only system dependencies for compilation should be development headers for unixodbc (if on linux) and python.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install zodbc
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Features
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## DB API 2
|
|
17
|
+
|
|
18
|
+
```py
|
|
19
|
+
import zodbc
|
|
20
|
+
import os
|
|
21
|
+
|
|
22
|
+
con = zodbc.connect(os.environ["ODBC_CONSTR"]) # Connect with odbc connection string or DSN
|
|
23
|
+
cur = con.cursor() # Cursor, which translates to an odbc statement handle
|
|
24
|
+
|
|
25
|
+
# python parameters & return values, data types like str, int, bool, UUID, decimal, date, time, datetime
|
|
26
|
+
cur.execute("select ? a", [42])
|
|
27
|
+
assert cur.fetchone()[0] == 42
|
|
28
|
+
|
|
29
|
+
# Short hand alternative for getting column names from cur.description
|
|
30
|
+
assert [c[0] for c in cur.description] == cur.column_names == ["a"]
|
|
31
|
+
|
|
32
|
+
con.commit() # Transactions, autocommit is disabled by default
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Additional fetch methods
|
|
37
|
+
|
|
38
|
+
The DB API 2 fetch methods `fetchone`, `fetchmany` and `fetchall` return (lists of) tuples. Additionally, there is `fetch_named` which returns a list of named tuples, which allows accessing fields by attribute name like `row.a` as well as `fetch_dict` which returns a list of dicts. `fetch_dict` requires unique column names in the query. These methods are implemented directly in Zig and should therefore be more efficient than fetching tuples and converting them in python.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Apache Arrow
|
|
42
|
+
|
|
43
|
+
A query can be executed for every row in an Arrow RecordBatch.
|
|
44
|
+
|
|
45
|
+
```py
|
|
46
|
+
import pyarrow as pa
|
|
47
|
+
|
|
48
|
+
cur.execute("drop table if exists t1")
|
|
49
|
+
cur.execute("create table t1(a int)")
|
|
50
|
+
cur.executemany_arrow(
|
|
51
|
+
"insert into t1(a) values(?)",
|
|
52
|
+
pa.RecordBatch.from_arrays([pa.array([1, 2, 3])], ["a"])
|
|
53
|
+
)
|
|
54
|
+
cur.execute("select * from t1").fetchall() # [(1,), (2,), (3,)]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Arrow RecordBatches can be fetched from a query result.
|
|
58
|
+
|
|
59
|
+
```py
|
|
60
|
+
cur.execute("select 1 a").fetch_arrow()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Arrow RecordBatches can be used as a table valued parameter for MS SQL.
|
|
64
|
+
|
|
65
|
+
```py
|
|
66
|
+
cur.execute("drop type if exists test_tabletype")
|
|
67
|
+
cur.execute("create type test_tabletype as table(a int)")
|
|
68
|
+
con.commit()
|
|
69
|
+
|
|
70
|
+
assert cur.execute(
|
|
71
|
+
"select sum(a) from ? where a <= ?",
|
|
72
|
+
[
|
|
73
|
+
zodbc.ArrowTVP(
|
|
74
|
+
zodbc.ArrowTVPType.from_name("test_tabletype"),
|
|
75
|
+
pa.RecordBatch.from_arrays([pa.array([1, 2, 3])], ["a"]),
|
|
76
|
+
),
|
|
77
|
+
2,
|
|
78
|
+
]
|
|
79
|
+
).fetchone()[0] == 3
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Differences with pyodbc
|
|
84
|
+
|
|
85
|
+
- UUID is always returned as a python UUID object (no pyodbc.native_uuid global variable)
|
|
86
|
+
- No output conversion (add_output_converter connection method)
|
|
87
|
+
- More explicit:
|
|
88
|
+
- No `execute(query, param1, param2)` => params must be passed as sequence
|
|
89
|
+
- No Cursor.commit, instead use Cursor.connection.commit
|
|
90
|
+
- Connection.getinfo takes a string instead of the odbc enum value
|