-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdatatypes.py
More file actions
259 lines (206 loc) · 7.11 KB
/
datatypes.py
File metadata and controls
259 lines (206 loc) · 7.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
from asyncio import AbstractEventLoop
from enum import Enum
from typing import Any, Callable, Dict, Optional, Union
from urllib import parse
from sqlitecloud.exceptions import SQLiteCloudException
from .resultset import SQLiteCloudResultSet
# SQLite supported data types
SQLiteDataTypes = Union[str, int, float, bytes, None]
# Basic types supported by SQLite Cloud APIs
SQLiteCloudDataTypes = Union[str, int, bool, Dict[Union[str, int], Any], bytes, None]
class SQLITECLOUD_DEFAULT(Enum):
PORT = 8860
TIMEOUT = 30
UPLOAD_SIZE = 512 * 1024
class SQLITECLOUD_CMD(Enum):
STRING = "+"
ZEROSTRING = "!"
ERROR = "-"
INT = ":"
FLOAT = ","
ROWSET = "*"
ROWSET_CHUNK = "/"
JSON = "#"
RAWJSON = "{"
NULL = "_"
BLOB = "$"
COMPRESSED = "%"
PUBSUB = "|"
COMMAND = "^"
RECONNECT = "@"
ARRAY = "="
class SQLITECLOUD_ROWSET(Enum):
CHUNKS_END = b"/6 0 0 0 "
class SQLITECLOUD_INTERNAL_ERRCODE(Enum):
"""
Clients error codes.
"""
NONE = 0
NETWORK = 100005
class SQLITECLOUD_ERRCODE(Enum):
"""
Error codes from Sqlite Cloud.
"""
MEM = 10000
NOTFOUND = 10001
COMMAND = 10002
INTERNAL = 10003
AUTH = 10004
GENERIC = 10005
RAFT = 10006
class SQLITECLOUD_PUBSUB_SUBJECT(Enum):
"""
Subjects that can be subscribed to by PubSub.
"""
TABLE = "TABLE"
CHANNEL = "CHANNEL"
class SQLiteCloudRowsetSignature:
"""
Represents the parsed signature for a rowset.
"""
def __init__(self) -> None:
self.start: int = -1
self.len: int = 0
self.idx: int = 0
self.version: int = 0
self.nrows: int = 0
self.ncols: int = 0
class SQLiteCloudAccount:
def __init__(
self,
username: Optional[str] = "",
password: Optional[str] = "",
hostname: str = "",
dbname: Optional[str] = "",
port: int = SQLITECLOUD_DEFAULT.PORT.value,
apikey: Optional[str] = "",
token: Optional[str] = "",
) -> None:
# User name is required unless connectionstring is provided
self.username = username
# Password is required unless connection string is provided
self.password = password
# Password is hashed
self.password_hashed = False
# API key
self.apikey = apikey
# Access Token
self.token = token
# Name of database to open
self.dbname = dbname
# Like mynode.sqlitecloud.io
self.hostname = hostname
self.port = port
class SQLiteCloudConnect:
"""
Represents the connection information.
"""
def __init__(self):
self.socket: any = None
self.config: SQLiteCloudConfig
self.pubsub_socket: any = None
self.pubsub_callback: Callable[
[SQLiteCloudConnect, Optional[SQLiteCloudResultSet], Optional[any]],
None,
] = None
self.pubsub_data: any = None
self.pubsub_thread: AbstractEventLoop = None
class SQLiteCloudConfig:
def __init__(self, connection_str: Optional[str] = None) -> None:
self.account: SQLiteCloudAccount = None
# Optional query timeout passed directly to TLS socket
self.timeout = 0
# Socket connection timeout
self.connect_timeout = SQLITECLOUD_DEFAULT.TIMEOUT.value
# Compression enabled by default
self.compression = True
# Tell the server to zero-terminate strings
self.zerotext = False
# Database will be created in memory
self.memory = False
# Create the database if it doesn't exist?
self.create = False
# Request for immediate responses from the server node without waiting for linerizability guarantees
self.non_linearizable = False
# Connect using plain TCP port, without TLS encryption, NOT RECOMMENDED
self.insecure = False
# Accept invalid TLS certificates
self.no_verify_certificate = False
# Filepath of certificates
self.root_certificate: str = None
self.certificate: str = None
self.certificate_key: str = None
# Server should send BLOB columns
self.noblob = False
# Do not send columns with more than max_data bytes
self.maxdata = 0
# Server should chunk responses with more than maxRows
self.maxrows = 0
# Server should limit total number of rows in a set to maxRowset
self.maxrowset = 0
if connection_str is not None:
self._parse_connection_string(connection_str)
def _parse_connection_string(self, connection_string) -> None:
# URL STRING FORMAT
# sqlitecloud://user:pass@host.com:port/dbname?timeout=10&key2=value2&key3=value3
# or sqlitecloud://host.sqlite.cloud:8860/dbname?apikey=zIiAARzKm9XBVllbAzkB1wqrgijJ3Gx0X5z1A4m4xBA
self.account = SQLiteCloudAccount()
try:
params = parse.urlparse(connection_string)
options = {}
query = params.query
options = parse.parse_qs(query)
for option, values in options.items():
opt = option.lower()
value = values.pop()
if value.lower() in ["true", "false"]:
value = bool(value)
elif value.isdigit():
value = int(value)
# alias
if opt == "nonlinearizable":
opt = "non_linearizable"
if hasattr(self, opt):
setattr(self, opt, value)
elif hasattr(self.account, opt):
setattr(self.account, opt, value)
if params.username and params.password:
self.account.username = parse.unquote(params.username)
self.account.password = parse.unquote(params.password)
# either you use an apikey, token or username and password
if (
bool(self.account.apikey)
+ bool(self.account.token)
+ bool(self.account.username or self.account.password)
> 1
):
raise SQLiteCloudException(
"Choose between apikey, token or username/password"
)
path = params.path
database = path.strip("/")
if database:
self.account.dbname = database
self.account.hostname = params.hostname
self.account.port = (
int(params.port) if params.port else SQLITECLOUD_DEFAULT.PORT.value
)
except Exception as e:
raise SQLiteCloudException("Invalid connection string") from e
class SQLiteCloudNumber:
"""
Represents the parsed number or the error code.
"""
def __init__(self) -> None:
self.value: Optional[int] = None
self.cstart: int = 0
self.extcode: int = None
self.offcode: int = None
class SQLiteCloudValue:
"""
Represents the parse value.
"""
def __init__(self) -> None:
self.value: Optional[SQLiteCloudDataTypes] = None
self.len: int = 0
self.cellsize: int = 0