sqlsaber 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
Potentially problematic release.
This version of sqlsaber might be problematic. Click here for more details.
- sqlsaber/cli/database.py +87 -0
- sqlsaber/config/database.py +49 -4
- sqlsaber/database/connection.py +113 -14
- {sqlsaber-0.1.0.dist-info → sqlsaber-0.2.0.dist-info}/METADATA +1 -1
- {sqlsaber-0.1.0.dist-info → sqlsaber-0.2.0.dist-info}/RECORD +8 -8
- {sqlsaber-0.1.0.dist-info → sqlsaber-0.2.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.1.0.dist-info → sqlsaber-0.2.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.1.0.dist-info → sqlsaber-0.2.0.dist-info}/licenses/LICENSE +0 -0
sqlsaber/cli/database.py
CHANGED
|
@@ -40,6 +40,20 @@ def add_database(
|
|
|
40
40
|
None, "--database", "--db", help="Database name"
|
|
41
41
|
),
|
|
42
42
|
username: Optional[str] = typer.Option(None, "--username", "-u", help="Username"),
|
|
43
|
+
ssl_mode: Optional[str] = typer.Option(
|
|
44
|
+
None,
|
|
45
|
+
"--ssl-mode",
|
|
46
|
+
help="SSL mode (disable, allow, prefer, require, verify-ca, verify-full for PostgreSQL; DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY for MySQL)",
|
|
47
|
+
),
|
|
48
|
+
ssl_ca: Optional[str] = typer.Option(
|
|
49
|
+
None, "--ssl-ca", help="SSL CA certificate file path"
|
|
50
|
+
),
|
|
51
|
+
ssl_cert: Optional[str] = typer.Option(
|
|
52
|
+
None, "--ssl-cert", help="SSL client certificate file path"
|
|
53
|
+
),
|
|
54
|
+
ssl_key: Optional[str] = typer.Option(
|
|
55
|
+
None, "--ssl-key", help="SSL client private key file path"
|
|
56
|
+
),
|
|
43
57
|
interactive: bool = typer.Option(
|
|
44
58
|
True, "--interactive/--no-interactive", help="Use interactive mode"
|
|
45
59
|
),
|
|
@@ -80,6 +94,63 @@ def add_database(
|
|
|
80
94
|
|
|
81
95
|
# Ask for password
|
|
82
96
|
password = getpass.getpass("Password (stored in your OS keychain): ")
|
|
97
|
+
|
|
98
|
+
# Ask for SSL configuration
|
|
99
|
+
if questionary.confirm("Configure SSL/TLS settings?", default=False).ask():
|
|
100
|
+
if type == "postgresql":
|
|
101
|
+
ssl_mode = (
|
|
102
|
+
ssl_mode
|
|
103
|
+
or questionary.select(
|
|
104
|
+
"SSL mode for PostgreSQL:",
|
|
105
|
+
choices=[
|
|
106
|
+
"disable",
|
|
107
|
+
"allow",
|
|
108
|
+
"prefer",
|
|
109
|
+
"require",
|
|
110
|
+
"verify-ca",
|
|
111
|
+
"verify-full",
|
|
112
|
+
],
|
|
113
|
+
default="prefer",
|
|
114
|
+
).ask()
|
|
115
|
+
)
|
|
116
|
+
elif type == "mysql":
|
|
117
|
+
ssl_mode = (
|
|
118
|
+
ssl_mode
|
|
119
|
+
or questionary.select(
|
|
120
|
+
"SSL mode for MySQL:",
|
|
121
|
+
choices=[
|
|
122
|
+
"DISABLED",
|
|
123
|
+
"PREFERRED",
|
|
124
|
+
"REQUIRED",
|
|
125
|
+
"VERIFY_CA",
|
|
126
|
+
"VERIFY_IDENTITY",
|
|
127
|
+
],
|
|
128
|
+
default="PREFERRED",
|
|
129
|
+
).ask()
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if ssl_mode and ssl_mode not in ["disable", "DISABLED"]:
|
|
133
|
+
if questionary.confirm(
|
|
134
|
+
"Specify SSL certificate files?", default=False
|
|
135
|
+
).ask():
|
|
136
|
+
ssl_ca = (
|
|
137
|
+
ssl_ca or questionary.path("SSL CA certificate file:").ask()
|
|
138
|
+
)
|
|
139
|
+
if questionary.confirm(
|
|
140
|
+
"Specify client certificate?", default=False
|
|
141
|
+
).ask():
|
|
142
|
+
ssl_cert = (
|
|
143
|
+
ssl_cert
|
|
144
|
+
or questionary.path(
|
|
145
|
+
"SSL client certificate file:"
|
|
146
|
+
).ask()
|
|
147
|
+
)
|
|
148
|
+
ssl_key = (
|
|
149
|
+
ssl_key
|
|
150
|
+
or questionary.path(
|
|
151
|
+
"SSL client private key file:"
|
|
152
|
+
).ask()
|
|
153
|
+
)
|
|
83
154
|
else:
|
|
84
155
|
# Non-interactive mode - use provided values or defaults
|
|
85
156
|
if type == "sqlite":
|
|
@@ -123,6 +194,10 @@ def add_database(
|
|
|
123
194
|
port=port,
|
|
124
195
|
database=database,
|
|
125
196
|
username=username,
|
|
197
|
+
ssl_mode=ssl_mode,
|
|
198
|
+
ssl_ca=ssl_ca,
|
|
199
|
+
ssl_cert=ssl_cert,
|
|
200
|
+
ssl_key=ssl_key,
|
|
126
201
|
)
|
|
127
202
|
|
|
128
203
|
try:
|
|
@@ -157,10 +232,21 @@ def list_databases():
|
|
|
157
232
|
table.add_column("Port", style="yellow")
|
|
158
233
|
table.add_column("Database", style="blue")
|
|
159
234
|
table.add_column("Username", style="white")
|
|
235
|
+
table.add_column("SSL", style="bright_green")
|
|
160
236
|
table.add_column("Default", style="bold red")
|
|
161
237
|
|
|
162
238
|
for db in databases:
|
|
163
239
|
is_default = "✓" if db.name == default_name else ""
|
|
240
|
+
|
|
241
|
+
# Format SSL status
|
|
242
|
+
ssl_status = ""
|
|
243
|
+
if db.ssl_mode:
|
|
244
|
+
ssl_status = db.ssl_mode
|
|
245
|
+
if db.ssl_ca or db.ssl_cert:
|
|
246
|
+
ssl_status += " (certs)"
|
|
247
|
+
else:
|
|
248
|
+
ssl_status = "disabled" if db.type != "sqlite" else "N/A"
|
|
249
|
+
|
|
164
250
|
table.add_row(
|
|
165
251
|
db.name,
|
|
166
252
|
db.type,
|
|
@@ -168,6 +254,7 @@ def list_databases():
|
|
|
168
254
|
str(db.port) if db.port else "",
|
|
169
255
|
db.database,
|
|
170
256
|
db.username,
|
|
257
|
+
ssl_status,
|
|
171
258
|
is_default,
|
|
172
259
|
)
|
|
173
260
|
|
sqlsaber/config/database.py
CHANGED
|
@@ -25,6 +25,9 @@ class DatabaseConfig:
|
|
|
25
25
|
username: Optional[str]
|
|
26
26
|
password: Optional[str] = None
|
|
27
27
|
ssl_mode: Optional[str] = None
|
|
28
|
+
ssl_ca: Optional[str] = None
|
|
29
|
+
ssl_cert: Optional[str] = None
|
|
30
|
+
ssl_key: Optional[str] = None
|
|
28
31
|
schema: Optional[str] = None
|
|
29
32
|
|
|
30
33
|
def to_connection_string(self) -> str:
|
|
@@ -34,21 +37,57 @@ class DatabaseConfig:
|
|
|
34
37
|
if self.type == "postgresql":
|
|
35
38
|
if not all([self.host, self.port, self.username]):
|
|
36
39
|
raise ValueError("Host, port, and username are required for PostgreSQL")
|
|
40
|
+
|
|
41
|
+
# Build base connection string
|
|
37
42
|
if password:
|
|
38
43
|
encoded_password = quote_plus(password)
|
|
39
|
-
|
|
44
|
+
base_url = f"postgresql://{self.username}:{encoded_password}@{self.host}:{self.port}/{self.database}"
|
|
40
45
|
else:
|
|
41
|
-
|
|
46
|
+
base_url = f"postgresql://{self.username}@{self.host}:{self.port}/{self.database}"
|
|
47
|
+
|
|
48
|
+
# Add SSL parameters
|
|
49
|
+
ssl_params = []
|
|
50
|
+
if self.ssl_mode:
|
|
51
|
+
ssl_params.append(f"sslmode={self.ssl_mode}")
|
|
52
|
+
if self.ssl_ca:
|
|
53
|
+
ssl_params.append(f"sslrootcert={quote_plus(self.ssl_ca)}")
|
|
54
|
+
if self.ssl_cert:
|
|
55
|
+
ssl_params.append(f"sslcert={quote_plus(self.ssl_cert)}")
|
|
56
|
+
if self.ssl_key:
|
|
57
|
+
ssl_params.append(f"sslkey={quote_plus(self.ssl_key)}")
|
|
58
|
+
|
|
59
|
+
if ssl_params:
|
|
60
|
+
return f"{base_url}?{'&'.join(ssl_params)}"
|
|
61
|
+
return base_url
|
|
62
|
+
|
|
42
63
|
elif self.type == "mysql":
|
|
43
64
|
if not all([self.host, self.port, self.username]):
|
|
44
65
|
raise ValueError("Host, port, and username are required for MySQL")
|
|
66
|
+
|
|
67
|
+
# Build base connection string
|
|
45
68
|
if password:
|
|
46
69
|
encoded_password = quote_plus(password)
|
|
47
|
-
|
|
70
|
+
base_url = f"mysql://{self.username}:{encoded_password}@{self.host}:{self.port}/{self.database}"
|
|
48
71
|
else:
|
|
49
|
-
|
|
72
|
+
base_url = (
|
|
50
73
|
f"mysql://{self.username}@{self.host}:{self.port}/{self.database}"
|
|
51
74
|
)
|
|
75
|
+
|
|
76
|
+
# Add SSL parameters
|
|
77
|
+
ssl_params = []
|
|
78
|
+
if self.ssl_mode:
|
|
79
|
+
ssl_params.append(f"ssl_mode={self.ssl_mode}")
|
|
80
|
+
if self.ssl_ca:
|
|
81
|
+
ssl_params.append(f"ssl_ca={quote_plus(self.ssl_ca)}")
|
|
82
|
+
if self.ssl_cert:
|
|
83
|
+
ssl_params.append(f"ssl_cert={quote_plus(self.ssl_cert)}")
|
|
84
|
+
if self.ssl_key:
|
|
85
|
+
ssl_params.append(f"ssl_key={quote_plus(self.ssl_key)}")
|
|
86
|
+
|
|
87
|
+
if ssl_params:
|
|
88
|
+
return f"{base_url}?{'&'.join(ssl_params)}"
|
|
89
|
+
return base_url
|
|
90
|
+
|
|
52
91
|
elif self.type == "sqlite":
|
|
53
92
|
return f"sqlite:///{self.database}"
|
|
54
93
|
else:
|
|
@@ -82,6 +121,9 @@ class DatabaseConfig:
|
|
|
82
121
|
"database": self.database,
|
|
83
122
|
"username": self.username,
|
|
84
123
|
"ssl_mode": self.ssl_mode,
|
|
124
|
+
"ssl_ca": self.ssl_ca,
|
|
125
|
+
"ssl_cert": self.ssl_cert,
|
|
126
|
+
"ssl_key": self.ssl_key,
|
|
85
127
|
"schema": self.schema,
|
|
86
128
|
}
|
|
87
129
|
|
|
@@ -96,6 +138,9 @@ class DatabaseConfig:
|
|
|
96
138
|
database=data["database"],
|
|
97
139
|
username=data["username"],
|
|
98
140
|
ssl_mode=data.get("ssl_mode"),
|
|
141
|
+
ssl_ca=data.get("ssl_ca"),
|
|
142
|
+
ssl_cert=data.get("ssl_cert"),
|
|
143
|
+
ssl_key=data.get("ssl_key"),
|
|
99
144
|
schema=data.get("schema"),
|
|
100
145
|
)
|
|
101
146
|
|
sqlsaber/database/connection.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from typing import Any, Dict, List, Optional
|
|
5
|
-
from urllib.parse import urlparse
|
|
5
|
+
from urllib.parse import urlparse, parse_qs
|
|
6
|
+
import ssl
|
|
6
7
|
|
|
7
8
|
import aiomysql
|
|
8
9
|
import aiosqlite
|
|
@@ -42,13 +43,65 @@ class PostgreSQLConnection(BaseDatabaseConnection):
|
|
|
42
43
|
def __init__(self, connection_string: str):
|
|
43
44
|
super().__init__(connection_string)
|
|
44
45
|
self._pool: Optional[asyncpg.Pool] = None
|
|
46
|
+
self._ssl_context = self._create_ssl_context()
|
|
47
|
+
|
|
48
|
+
def _create_ssl_context(self) -> Optional[ssl.SSLContext]:
|
|
49
|
+
"""Create SSL context from connection string parameters."""
|
|
50
|
+
parsed = urlparse(self.connection_string)
|
|
51
|
+
if not parsed.query:
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
params = parse_qs(parsed.query)
|
|
55
|
+
ssl_mode = params.get("sslmode", [None])[0]
|
|
56
|
+
|
|
57
|
+
if not ssl_mode or ssl_mode == "disable":
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
# Create SSL context based on mode
|
|
61
|
+
if ssl_mode in ["require", "verify-ca", "verify-full"]:
|
|
62
|
+
ssl_context = ssl.create_default_context()
|
|
63
|
+
|
|
64
|
+
# Configure certificate verification
|
|
65
|
+
if ssl_mode == "require":
|
|
66
|
+
ssl_context.check_hostname = False
|
|
67
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
68
|
+
elif ssl_mode == "verify-ca":
|
|
69
|
+
ssl_context.check_hostname = False
|
|
70
|
+
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
|
71
|
+
elif ssl_mode == "verify-full":
|
|
72
|
+
ssl_context.check_hostname = True
|
|
73
|
+
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
|
74
|
+
|
|
75
|
+
# Load certificates if provided
|
|
76
|
+
ssl_ca = params.get("sslrootcert", [None])[0]
|
|
77
|
+
ssl_cert = params.get("sslcert", [None])[0]
|
|
78
|
+
ssl_key = params.get("sslkey", [None])[0]
|
|
79
|
+
|
|
80
|
+
if ssl_ca:
|
|
81
|
+
ssl_context.load_verify_locations(ssl_ca)
|
|
82
|
+
|
|
83
|
+
if ssl_cert and ssl_key:
|
|
84
|
+
ssl_context.load_cert_chain(ssl_cert, ssl_key)
|
|
85
|
+
|
|
86
|
+
return ssl_context
|
|
87
|
+
|
|
88
|
+
return None
|
|
45
89
|
|
|
46
90
|
async def get_pool(self) -> asyncpg.Pool:
|
|
47
91
|
"""Get or create connection pool."""
|
|
48
92
|
if self._pool is None:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
93
|
+
# Create pool with SSL context if configured
|
|
94
|
+
if self._ssl_context:
|
|
95
|
+
self._pool = await asyncpg.create_pool(
|
|
96
|
+
self.connection_string,
|
|
97
|
+
min_size=1,
|
|
98
|
+
max_size=10,
|
|
99
|
+
ssl=self._ssl_context,
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
self._pool = await asyncpg.create_pool(
|
|
103
|
+
self.connection_string, min_size=1, max_size=10
|
|
104
|
+
)
|
|
52
105
|
return self._pool
|
|
53
106
|
|
|
54
107
|
async def close(self):
|
|
@@ -94,19 +147,65 @@ class MySQLConnection(BaseDatabaseConnection):
|
|
|
94
147
|
self.user = parsed.username or ""
|
|
95
148
|
self.password = parsed.password or ""
|
|
96
149
|
|
|
150
|
+
# Parse SSL parameters
|
|
151
|
+
self.ssl_params = {}
|
|
152
|
+
if parsed.query:
|
|
153
|
+
params = parse_qs(parsed.query)
|
|
154
|
+
|
|
155
|
+
ssl_mode = params.get("ssl_mode", [None])[0]
|
|
156
|
+
if ssl_mode:
|
|
157
|
+
# Map SSL modes to aiomysql SSL parameters
|
|
158
|
+
if ssl_mode.upper() == "DISABLED":
|
|
159
|
+
self.ssl_params["ssl"] = None
|
|
160
|
+
elif ssl_mode.upper() in [
|
|
161
|
+
"PREFERRED",
|
|
162
|
+
"REQUIRED",
|
|
163
|
+
"VERIFY_CA",
|
|
164
|
+
"VERIFY_IDENTITY",
|
|
165
|
+
]:
|
|
166
|
+
ssl_context = ssl.create_default_context()
|
|
167
|
+
|
|
168
|
+
if ssl_mode.upper() == "REQUIRED":
|
|
169
|
+
ssl_context.check_hostname = False
|
|
170
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
171
|
+
elif ssl_mode.upper() == "VERIFY_CA":
|
|
172
|
+
ssl_context.check_hostname = False
|
|
173
|
+
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
|
174
|
+
elif ssl_mode.upper() == "VERIFY_IDENTITY":
|
|
175
|
+
ssl_context.check_hostname = True
|
|
176
|
+
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
|
177
|
+
|
|
178
|
+
# Load certificates if provided
|
|
179
|
+
ssl_ca = params.get("ssl_ca", [None])[0]
|
|
180
|
+
ssl_cert = params.get("ssl_cert", [None])[0]
|
|
181
|
+
ssl_key = params.get("ssl_key", [None])[0]
|
|
182
|
+
|
|
183
|
+
if ssl_ca:
|
|
184
|
+
ssl_context.load_verify_locations(ssl_ca)
|
|
185
|
+
|
|
186
|
+
if ssl_cert and ssl_key:
|
|
187
|
+
ssl_context.load_cert_chain(ssl_cert, ssl_key)
|
|
188
|
+
|
|
189
|
+
self.ssl_params["ssl"] = ssl_context
|
|
190
|
+
|
|
97
191
|
async def get_pool(self) -> aiomysql.Pool:
|
|
98
192
|
"""Get or create connection pool."""
|
|
99
193
|
if self._pool is None:
|
|
100
|
-
|
|
101
|
-
host
|
|
102
|
-
port
|
|
103
|
-
user
|
|
104
|
-
password
|
|
105
|
-
db
|
|
106
|
-
minsize
|
|
107
|
-
maxsize
|
|
108
|
-
autocommit
|
|
109
|
-
|
|
194
|
+
pool_kwargs = {
|
|
195
|
+
"host": self.host,
|
|
196
|
+
"port": self.port,
|
|
197
|
+
"user": self.user,
|
|
198
|
+
"password": self.password,
|
|
199
|
+
"db": self.database,
|
|
200
|
+
"minsize": 1,
|
|
201
|
+
"maxsize": 10,
|
|
202
|
+
"autocommit": False,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Add SSL parameters if configured
|
|
206
|
+
pool_kwargs.update(self.ssl_params)
|
|
207
|
+
|
|
208
|
+
self._pool = await aiomysql.create_pool(**pool_kwargs)
|
|
110
209
|
return self._pool
|
|
111
210
|
|
|
112
211
|
async def close(self):
|
|
@@ -6,7 +6,7 @@ sqlsaber/agents/base.py,sha256=UUSGhoJImATXrYS7yrLR2qjg1iFW4udOUdRaV3Ryk5s,2086
|
|
|
6
6
|
sqlsaber/agents/streaming.py,sha256=0bNzd_JhLlgQB40pf9FZFMvmU9Q7W6D9BmglA1rIGqw,850
|
|
7
7
|
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
8
8
|
sqlsaber/cli/commands.py,sha256=Adrt_0LRgykb2FZ4F0TQpuBM8Z0qgfbggn0FexcVALI,4094
|
|
9
|
-
sqlsaber/cli/database.py,sha256=
|
|
9
|
+
sqlsaber/cli/database.py,sha256=ZOaW94dCM8_RUT5OZzI6-lbVmSYxSjdmAdYa3Nf_d9g,12633
|
|
10
10
|
sqlsaber/cli/display.py,sha256=5J4AgJADmMwKi9Aq5u6_MKRO1TA6unS4F4RUfml_sfU,7651
|
|
11
11
|
sqlsaber/cli/interactive.py,sha256=y92rdoM49SOSwEctm9ZcrEN220fhJ_DMHPSd_7KsORg,3701
|
|
12
12
|
sqlsaber/cli/memory.py,sha256=LW4ZF2V6Gw6hviUFGZ4ym9ostFCwucgBTIMZ3EANO-I,7671
|
|
@@ -14,10 +14,10 @@ sqlsaber/cli/models.py,sha256=3IcXeeU15IQvemSv-V-RQzVytJ3wuQ4YmWk89nTDcSE,7813
|
|
|
14
14
|
sqlsaber/cli/streaming.py,sha256=5QGAYTAvg9mzQLxDEVtdDH-TIbGfYYzMOLoOYPrHPu0,3788
|
|
15
15
|
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
16
16
|
sqlsaber/config/api_keys.py,sha256=kLdoExF_My9ojmdhO5Ca7-ZeowsO0v1GVa_QT5jjUPo,3658
|
|
17
|
-
sqlsaber/config/database.py,sha256=
|
|
17
|
+
sqlsaber/config/database.py,sha256=hrIr5IIqhkOpjJ2A4oaeMvTqWqNDdlJiVWQTaBudVQM,10388
|
|
18
18
|
sqlsaber/config/settings.py,sha256=zjQ7nS3ybcCb88Ea0tmwJox5-q0ettChZw89ZqRVpX8,3975
|
|
19
19
|
sqlsaber/database/__init__.py,sha256=a_gtKRJnZVO8-fEZI7g3Z8YnGa6Nio-5Y50PgVp07ss,176
|
|
20
|
-
sqlsaber/database/connection.py,sha256=
|
|
20
|
+
sqlsaber/database/connection.py,sha256=1lMQ2LhxlUIkl_msGzuNXczI_tNp0WBgfsdqDC2nxsw,10479
|
|
21
21
|
sqlsaber/database/schema.py,sha256=gURfCFVE--UWIqD_0StqS2NMB9VIPpqczBEoS2GnKR4,27025
|
|
22
22
|
sqlsaber/memory/__init__.py,sha256=GiWkU6f6YYVV0EvvXDmFWe_CxarmDCql05t70MkTEWs,63
|
|
23
23
|
sqlsaber/memory/manager.py,sha256=ML2NEO5Z4Aw36sEI9eOvWVnjl-qT2VOTojViJAj7Seo,2777
|
|
@@ -25,8 +25,8 @@ sqlsaber/memory/storage.py,sha256=DvZBsSPaAfk_DqrNEn86uMD-TQsWUI6rQLfNw6PSCB8,57
|
|
|
25
25
|
sqlsaber/models/__init__.py,sha256=RJ7p3WtuSwwpFQ1Iw4_DHV2zzCtHqIzsjJzxv8kUjUE,287
|
|
26
26
|
sqlsaber/models/events.py,sha256=55m41tDwMsFxnKKA5_VLJz8iV-V4Sq3LDfta4VoutJI,737
|
|
27
27
|
sqlsaber/models/types.py,sha256=3U_30n91EB3IglBTHipwiW4MqmmaA2qfshfraMZyPps,896
|
|
28
|
-
sqlsaber-0.
|
|
29
|
-
sqlsaber-0.
|
|
30
|
-
sqlsaber-0.
|
|
31
|
-
sqlsaber-0.
|
|
32
|
-
sqlsaber-0.
|
|
28
|
+
sqlsaber-0.2.0.dist-info/METADATA,sha256=EUgK7o9feWxmsx_pr9DwER6_t6db7ZaYSghwUKKYPao,3953
|
|
29
|
+
sqlsaber-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
30
|
+
sqlsaber-0.2.0.dist-info/entry_points.txt,sha256=POwcsEskUp7xQQWabrAi6Eawz4qc5eBlB3KzAiBq-Y0,124
|
|
31
|
+
sqlsaber-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
32
|
+
sqlsaber-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|