Skip to content

Commit 74e7785

Browse files
committed
IGNITE-12867 Inital attempt at DBAPI support
1 parent dd3b280 commit 74e7785

File tree

5 files changed

+334
-0
lines changed

5 files changed

+334
-0
lines changed

pyignite/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@
1616
from pyignite.client import Client
1717
from pyignite.aio_client import AioClient
1818
from pyignite.binary import GenericObjectMeta
19+
from .dbapi import connect
1920

2021
__version__ = '0.6.0-dev'
22+
23+
__all__ = [ 'Client', 'connect' ]

pyignite/dbapi/__init__.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# Copyright 2021 GridGain Systems, Inc. and Contributors.
3+
#
4+
# Licensed under the GridGain Community Edition License (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
from .dbclient import DBClient
18+
from .. import constants
19+
20+
apiLevel = '2.0'
21+
threadsafety = 2
22+
paramstyle = 'qmark'
23+
24+
def connect(dsn=None,
25+
user=None, password=None,
26+
host=constants.IGNITE_DEFAULT_HOST, port=constants.IGNITE_DEFAULT_PORT,
27+
**kwargs):
28+
"""
29+
Create a new database connection.
30+
31+
The connection can be specified via DSN:
32+
33+
``conn = connect("ignite://localhost/test?param1=value1&...")``
34+
35+
or using database and credentials arguments:
36+
37+
``conn = connect(database="test", user="default", password="default",
38+
host="localhost", **kwargs)``
39+
40+
The basic connection parameters are:
41+
42+
- *host*: host with running Ignite server.
43+
- *port*: port Ignite server is bound to.
44+
- *database*: database connect to.
45+
- *user*: database user.
46+
- *password*: user's password.
47+
48+
See defaults in :data:`~pygridgain.connection.Connection`
49+
constructor.
50+
51+
DSN or host is required.
52+
53+
Any other keyword parameter will be passed to the underlying Connection
54+
class.
55+
56+
:return: a new connection.
57+
"""
58+
59+
if dsn is None and host is None:
60+
raise ValueError('host or dsn is required')
61+
62+
# TODO: implement connection using DSN
63+
if host is None:
64+
raise ValueError('dsn connection is not currently implemented')
65+
66+
client = DBClient()
67+
client.connect(host, port)
68+
69+
return client
70+
71+
__all__ = [
72+
'connect',
73+
'Warning', 'Error', 'DataError', 'DatabaseError', 'ProgrammingError',
74+
'IntegrityError', 'InterfaceError', 'InternalError', 'NotSupportedError',
75+
'OperationalError'
76+
]

pyignite/dbapi/dbclient.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#
2+
# Copyright 2021 GridGain Systems, Inc. and Contributors.
3+
#
4+
# Licensed under the GridGain Community Edition License (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
from ..client import Client
18+
from .dbcursor import DBCursor
19+
20+
class DBClient (Client):
21+
22+
23+
def close(self):
24+
"""
25+
"""
26+
# TODO: close ope cursors
27+
super.close()
28+
29+
def commit(self):
30+
"""
31+
Ignite doesn't have SQL transactions
32+
"""
33+
pass
34+
35+
def rollback(self):
36+
"""
37+
Ignite doesn't have SQL transactions
38+
"""
39+
pass
40+
41+
def cursor(self):
42+
"""
43+
Cursors work slightly differently in Ignite versus DBAPI, so
44+
we map from one to the other
45+
"""
46+
return DBCursor(self)
47+

pyignite/dbapi/dbcursor.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#
2+
# Copyright 2021 GridGain Systems, Inc. and Contributors.
3+
#
4+
# Licensed under the GridGain Community Edition License (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
from ..cursors import SqlCursor
18+
19+
class DBCursor:
20+
21+
22+
def __init__(self, connection):
23+
self.connection = connection
24+
self.cursor = None
25+
26+
@property
27+
def description(self):
28+
if self._state == self._states.NONE:
29+
return None
30+
31+
columns = self._columns or []
32+
types = self._types or []
33+
34+
return [
35+
Column(name, type_code, None, None, None, None, True)
36+
for name, type_code in zip(columns, types)
37+
]
38+
39+
@property
40+
def rowcount(self):
41+
"""
42+
:return: the number of rows that the last .execute*() produced.
43+
"""
44+
return self._rowcount
45+
46+
def close(self):
47+
"""
48+
Close the cursor now. The cursor will be unusable from this point
49+
forward; an :data:`~clickhouse_driver.dbapi.Error` (or subclass)
50+
exception will be raised if any operation is attempted with the
51+
cursor.
52+
"""
53+
self._client.disconnect()
54+
self._state = self._states.CURSOR_CLOSED
55+
56+
try:
57+
# cursor can be already closed
58+
self._connection.cursors.remove(self)
59+
except ValueError:
60+
pass
61+
62+
def execute(self, operation, parameters=None):
63+
"""
64+
Prepare and execute a database operation (query or command).
65+
66+
:param operation: query or command to execute.
67+
:param parameters: sequence or mapping that will be bound to
68+
variables in the operation.
69+
:return: None
70+
"""
71+
self.cursor = self.connection.sql(operation, query_args=parameters)
72+
73+
def executemany(self, operation, seq_of_parameters):
74+
"""
75+
Prepare a database operation (query or command) and then execute it
76+
against all parameter sequences found in the sequence
77+
`seq_of_parameters`.
78+
79+
:param operation: query or command to execute.
80+
:param seq_of_parameters: sequences or mappings for execution.
81+
:return: None
82+
"""
83+
pass
84+
85+
def fetchone(self):
86+
"""
87+
Fetch the next row of a query result set, returning a single sequence,
88+
or None when no more data is available.
89+
90+
:return: the next row of a query result set or None.
91+
"""
92+
self._check_query_started()
93+
94+
if self._stream_results:
95+
return next(self._rows, None)
96+
97+
else:
98+
if not self._rows:
99+
return None
100+
101+
return self._rows.pop(0)
102+
103+
def fetchmany(self, size=None):
104+
"""
105+
Fetch the next set of rows of a query result, returning a sequence of
106+
sequences (e.g. a list of tuples). An empty sequence is returned when
107+
no more rows are available.
108+
109+
:param size: amount of rows to return.
110+
:return: list of fetched rows or empty list.
111+
"""
112+
self._check_query_started()
113+
114+
if size is None:
115+
size = self.arraysize
116+
117+
if self._stream_results:
118+
if size == -1:
119+
return list(self._rows)
120+
else:
121+
return list(islice(self._rows, size))
122+
123+
if size < 0:
124+
rv = self._rows
125+
self._rows = []
126+
else:
127+
rv = self._rows[:size]
128+
self._rows = self._rows[size:]
129+
130+
return rv
131+
132+
def fetchall(self):
133+
"""
134+
Fetch all (remaining) rows of a query result, returning them as a
135+
sequence of sequences (e.g. a list of tuples).
136+
137+
:return: list of fetched rows.
138+
"""
139+
if self.cursor != None:
140+
rows = []
141+
for row in self.cursor:
142+
rows.append(row)
143+
else:
144+
return None # error?
145+
146+
return rows
147+
148+
def setinputsizes(self, sizes):
149+
# Do nothing.
150+
pass
151+
152+
def setoutputsize(self, size, column=None):
153+
# Do nothing.
154+
pass

pyignite/dbapi/errors.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#
2+
# Copyright 2021 GridGain Systems, Inc. and Contributors.
3+
#
4+
# Licensed under the GridGain Community Edition License (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
class Warning(Exception):
18+
pass
19+
20+
21+
class Error(Exception):
22+
pass
23+
24+
25+
class InterfaceError(Error):
26+
pass
27+
28+
29+
class DatabaseError(Error):
30+
pass
31+
32+
33+
class InternalError(DatabaseError):
34+
pass
35+
36+
37+
class OperationalError(DatabaseError):
38+
pass
39+
40+
41+
class ProgrammingError(DatabaseError):
42+
pass
43+
44+
45+
class IntegrityError(DatabaseError):
46+
pass
47+
48+
49+
class DataError(DatabaseError):
50+
pass
51+
52+
53+
class NotSupportedError(DatabaseError):
54+
pass

0 commit comments

Comments
 (0)