misakadb 0.1.1__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.
- misakadb/__init__.py +13 -0
- misakadb/__main__.py +4 -0
- misakadb/apis/__init__.py +1 -0
- misakadb/apis/api.py +394 -0
- misakadb/cli.py +124 -0
- misakadb/interface/__init__.py +1 -0
- misakadb/interface/status.py +7 -0
- misakadb/mql/MQ.py +191 -0
- misakadb/mql/__init__.py +1 -0
- misakadb/mql/literals.py +22 -0
- misakadb/network/__init__.py +1 -0
- misakadb/network/command_send.py +208 -0
- misakadb/network/session.py +16 -0
- misakadb/network/sock.py +111 -0
- misakadb/sdk.py +251 -0
- misakadb-0.1.1.dist-info/METADATA +441 -0
- misakadb-0.1.1.dist-info/RECORD +21 -0
- misakadb-0.1.1.dist-info/WHEEL +5 -0
- misakadb-0.1.1.dist-info/entry_points.txt +3 -0
- misakadb-0.1.1.dist-info/licenses/LICENSE +132 -0
- misakadb-0.1.1.dist-info/top_level.txt +1 -0
misakadb/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Python client and MiQL query builder for MisakaDB."""
|
|
2
|
+
|
|
3
|
+
from .apis.api import MisakaDBClient
|
|
4
|
+
from .mql.MQ import MiQL
|
|
5
|
+
from .network.command_send import commandSend
|
|
6
|
+
from .network.sock import clientCore
|
|
7
|
+
from .sdk import Connection, MisakaError, conn, getCurrent
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.1"
|
|
10
|
+
__all__ = [
|
|
11
|
+
"conn", "getCurrent", "Connection", "MisakaError",
|
|
12
|
+
"MisakaDBClient", "MiQL", "clientCore", "commandSend", "__version__",
|
|
13
|
+
]
|
misakadb/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MisakaDB apis implementation."""
|
misakadb/apis/api.py
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
from typing import Optional, Dict, Any, List, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from ..network import sock
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HeartbeatController:
|
|
12
|
+
def __init__(self, owner: "MisakaDBClient", interval: float) -> None:
|
|
13
|
+
self._owner = owner
|
|
14
|
+
self.interval = interval
|
|
15
|
+
self._enabled = threading.Event()
|
|
16
|
+
self._shutdown = threading.Event()
|
|
17
|
+
self._stats_lock = threading.Lock()
|
|
18
|
+
self._thread: Optional[threading.Thread] = None
|
|
19
|
+
self._attempt_count = 0
|
|
20
|
+
self._success_count = 0
|
|
21
|
+
self._failure_count = 0
|
|
22
|
+
self._last_error: Optional[str] = None
|
|
23
|
+
self._last_sent_at: Optional[float] = None
|
|
24
|
+
|
|
25
|
+
def _ensure_thread(self) -> None:
|
|
26
|
+
if self._thread is not None and self._thread.is_alive():
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
self._shutdown.clear()
|
|
30
|
+
self._thread = threading.Thread(
|
|
31
|
+
target=self._loop,
|
|
32
|
+
name=f"MisakaDBHeartbeat-{self._owner.host}:{self._owner.port}",
|
|
33
|
+
daemon=True,
|
|
34
|
+
)
|
|
35
|
+
self._thread.start()
|
|
36
|
+
|
|
37
|
+
def _loop(self) -> None:
|
|
38
|
+
while not self._shutdown.is_set():
|
|
39
|
+
if not self._enabled.is_set():
|
|
40
|
+
if self._shutdown.wait(0.1):
|
|
41
|
+
return
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if self._owner.connected and self._owner.client is not None:
|
|
45
|
+
try:
|
|
46
|
+
with self._owner._socket_lock:
|
|
47
|
+
if self._owner.connected and self._owner.client is not None:
|
|
48
|
+
self._owner.client.send_heartbeat()
|
|
49
|
+
with self._stats_lock:
|
|
50
|
+
self._attempt_count += 1
|
|
51
|
+
self._success_count += 1
|
|
52
|
+
self._last_error = None
|
|
53
|
+
self._last_sent_at = time.time()
|
|
54
|
+
except Exception as e:
|
|
55
|
+
with self._stats_lock:
|
|
56
|
+
self._attempt_count += 1
|
|
57
|
+
self._failure_count += 1
|
|
58
|
+
self._last_error = str(e)
|
|
59
|
+
print(f"心跳发送失败: {e}", file=sys.stderr)
|
|
60
|
+
self._owner.connected = False
|
|
61
|
+
|
|
62
|
+
if self._shutdown.wait(self.interval):
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def running(self) -> bool:
|
|
67
|
+
return self._enabled.is_set()
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def count(self) -> int:
|
|
71
|
+
with self._stats_lock:
|
|
72
|
+
return self._attempt_count
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def success_count(self) -> int:
|
|
76
|
+
with self._stats_lock:
|
|
77
|
+
return self._success_count
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def failure_count(self) -> int:
|
|
81
|
+
with self._stats_lock:
|
|
82
|
+
return self._failure_count
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def loss_rate(self) -> float:
|
|
86
|
+
with self._stats_lock:
|
|
87
|
+
if self._attempt_count == 0:
|
|
88
|
+
return 0.0
|
|
89
|
+
return self._failure_count / self._attempt_count * 100.0
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def last_error(self) -> Optional[str]:
|
|
93
|
+
with self._stats_lock:
|
|
94
|
+
return self._last_error
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def last_sent_at(self) -> Optional[float]:
|
|
98
|
+
with self._stats_lock:
|
|
99
|
+
return self._last_sent_at
|
|
100
|
+
|
|
101
|
+
def stats(self) -> Dict[str, Any]:
|
|
102
|
+
with self._stats_lock:
|
|
103
|
+
loss_rate = (
|
|
104
|
+
self._failure_count / self._attempt_count * 100.0
|
|
105
|
+
if self._attempt_count > 0 else 0.0
|
|
106
|
+
)
|
|
107
|
+
return {
|
|
108
|
+
"running": self._enabled.is_set(),
|
|
109
|
+
"interval": self.interval,
|
|
110
|
+
"count": self._attempt_count,
|
|
111
|
+
"success_count": self._success_count,
|
|
112
|
+
"failure_count": self._failure_count,
|
|
113
|
+
# 当前协议没有心跳回包确认,这里按发送失败率统计。
|
|
114
|
+
"loss_rate": loss_rate,
|
|
115
|
+
"last_error": self._last_error,
|
|
116
|
+
"last_sent_at": self._last_sent_at,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
def start(self) -> None:
|
|
120
|
+
self._ensure_thread()
|
|
121
|
+
self._enabled.set()
|
|
122
|
+
|
|
123
|
+
def stop(self) -> None:
|
|
124
|
+
self._enabled.clear()
|
|
125
|
+
|
|
126
|
+
def shutdown(self) -> None:
|
|
127
|
+
self._enabled.clear()
|
|
128
|
+
self._shutdown.set()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class MisakaDBClient:
|
|
132
|
+
"""MisakaDB客户端API"""
|
|
133
|
+
|
|
134
|
+
def __init__(
|
|
135
|
+
self,
|
|
136
|
+
host: str = "127.0.0.1",
|
|
137
|
+
port: int = 10032,
|
|
138
|
+
heartbeat_interval: float = 1
|
|
139
|
+
):
|
|
140
|
+
self.host = host
|
|
141
|
+
self.port = port
|
|
142
|
+
self.client: Optional[sock.clientCore] = None
|
|
143
|
+
self.service_info: Optional[Dict[str, Any]] = None
|
|
144
|
+
self.connected = False
|
|
145
|
+
self.heartbeat_interval = heartbeat_interval
|
|
146
|
+
self._socket_lock = threading.Lock()
|
|
147
|
+
self.heart = HeartbeatController(self, heartbeat_interval)
|
|
148
|
+
self.heart.start()
|
|
149
|
+
|
|
150
|
+
def connect(self, retries: int = 3, retry_delay: float = 1.0) -> bool:
|
|
151
|
+
"""连接到MisakaDB服务器
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
retries: 重试次数
|
|
155
|
+
retry_delay: 重试延迟(秒)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
bool: 连接是否成功
|
|
159
|
+
"""
|
|
160
|
+
self.heart.start()
|
|
161
|
+
|
|
162
|
+
for attempt in range(retries):
|
|
163
|
+
try:
|
|
164
|
+
self.client = sock.clientCore(self.host, self.port)
|
|
165
|
+
connect_err = self.client.connect()
|
|
166
|
+
|
|
167
|
+
if connect_err is not None:
|
|
168
|
+
if attempt < retries - 1:
|
|
169
|
+
print(f"连接失败,第{attempt + 1}次重试... ({connect_err})", file=sys.stderr)
|
|
170
|
+
time.sleep(retry_delay)
|
|
171
|
+
continue
|
|
172
|
+
else:
|
|
173
|
+
print(f"连接失败: {connect_err}", file=sys.stderr)
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
self.connected = True
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
180
|
+
if attempt < retries - 1:
|
|
181
|
+
print(f"连接过程中发生错误,第{attempt + 1}次重试... ({e})", file=sys.stderr)
|
|
182
|
+
time.sleep(retry_delay)
|
|
183
|
+
else:
|
|
184
|
+
print(f"连接过程中发生错误: {e}", file=sys.stderr)
|
|
185
|
+
return False
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
def get_service_info(self) -> Optional[Dict[str, Any]]:
|
|
189
|
+
"""获取服务器信息
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Optional[Dict[str, Any]]: 服务信息字典,如果失败返回None
|
|
193
|
+
"""
|
|
194
|
+
if not self.connected or self.client is None:
|
|
195
|
+
print("未连接到服务器,请先调用connect()", file=sys.stderr)
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
with self._socket_lock:
|
|
200
|
+
if not self.connected or self.client is None:
|
|
201
|
+
return None
|
|
202
|
+
response = self.client.send_command("get-service-info")
|
|
203
|
+
self.service_info = json.loads(response)
|
|
204
|
+
return self.service_info
|
|
205
|
+
except json.JSONDecodeError as e:
|
|
206
|
+
print(f"解析服务信息失败: {e}", file=sys.stderr)
|
|
207
|
+
return None
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print(f"获取服务信息失败: {e}", file=sys.stderr)
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
def execute_command(self, command: str) -> Optional[Union[str, Dict, List]]:
|
|
213
|
+
"""执行自定义命令
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
command: 要执行的命令字符串
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Optional[Union[str, Dict, List]]: 命令执行结果
|
|
220
|
+
"""
|
|
221
|
+
if not self.connected or self.client is None:
|
|
222
|
+
print("未连接到服务器,请先调用connect()", file=sys.stderr)
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
with self._socket_lock:
|
|
227
|
+
if not self.connected or self.client is None:
|
|
228
|
+
return None
|
|
229
|
+
response = self.client.send_command(command)
|
|
230
|
+
|
|
231
|
+
# 尝试解析为JSON
|
|
232
|
+
try:
|
|
233
|
+
return json.loads(response)
|
|
234
|
+
except json.JSONDecodeError:
|
|
235
|
+
# 如果不是JSON,返回原始字符串
|
|
236
|
+
return response.decode('utf-8', errors='replace')
|
|
237
|
+
|
|
238
|
+
except Exception as e:
|
|
239
|
+
print(f"执行命令失败: {e}", file=sys.stderr)
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
def ping(self) -> bool:
|
|
243
|
+
"""检查服务器是否可达
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
bool: 服务器是否可达
|
|
247
|
+
"""
|
|
248
|
+
if not self.connected or self.client is None:
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
with self._socket_lock:
|
|
253
|
+
if not self.connected or self.client is None:
|
|
254
|
+
return False
|
|
255
|
+
response = self.client.send_command("ping")
|
|
256
|
+
return response is not None
|
|
257
|
+
except:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
def get_server_version(self) -> Optional[str]:
|
|
261
|
+
"""获取服务器版本
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Optional[str]: 服务器版本号
|
|
265
|
+
"""
|
|
266
|
+
service_info = self.get_service_info()
|
|
267
|
+
if service_info:
|
|
268
|
+
return service_info.get('service', {}).get('version')
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def is_command_allowed(self, command: str) -> bool:
|
|
274
|
+
"""检查命令是否被服务器允许
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
command: 要检查的命令
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
bool: 命令是否被允许
|
|
281
|
+
"""
|
|
282
|
+
allowed_commands = self.get_allowed_commands()
|
|
283
|
+
if allowed_commands:
|
|
284
|
+
return command in allowed_commands
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
def get_network_config(self) -> Optional[Dict[str, Any]]:
|
|
288
|
+
"""获取网络配置
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Optional[Dict[str, Any]]: 网络配置信息
|
|
292
|
+
"""
|
|
293
|
+
service_info = self.get_service_info()
|
|
294
|
+
if service_info:
|
|
295
|
+
return service_info.get('network', {})
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
def close(self) -> None:
|
|
299
|
+
"""关闭连接"""
|
|
300
|
+
self.heart.shutdown()
|
|
301
|
+
with self._socket_lock:
|
|
302
|
+
if self.client is not None and self.client.s:
|
|
303
|
+
self.client.close()
|
|
304
|
+
self.client = None
|
|
305
|
+
self.service_info = None
|
|
306
|
+
self.connected = False
|
|
307
|
+
self.heart = HeartbeatController(self, self.heartbeat_interval)
|
|
308
|
+
|
|
309
|
+
def __enter__(self):
|
|
310
|
+
"""上下文管理器入口"""
|
|
311
|
+
if not self.connect():
|
|
312
|
+
raise ConnectionError(f"无法连接到服务器 {self.host}:{self.port}")
|
|
313
|
+
return self
|
|
314
|
+
|
|
315
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
316
|
+
"""上下文管理器退出"""
|
|
317
|
+
self.close()
|
|
318
|
+
|
|
319
|
+
def __repr__(self) -> str:
|
|
320
|
+
"""对象表示"""
|
|
321
|
+
status = "已连接" if self.connected else "未连接"
|
|
322
|
+
return f"<MisakaDBClient {self.host}:{self.port} [{status}]>"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# 兼容旧版本的函数接口
|
|
326
|
+
def connect(host: str, port: int) -> Optional[MisakaDBClient]:
|
|
327
|
+
"""连接到MisakaDB服务器(兼容旧版本接口)
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
host: 服务器地址
|
|
331
|
+
port: 服务器端口
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Optional[MisakaDBClient]: 客户端实例,如果连接失败返回None
|
|
335
|
+
"""
|
|
336
|
+
client = MisakaDBClient(host, port)
|
|
337
|
+
if client.connect():
|
|
338
|
+
return client
|
|
339
|
+
return None
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# 示例使用
|
|
343
|
+
if __name__ == "__main__":
|
|
344
|
+
# 使用上下文管理器(推荐)
|
|
345
|
+
print("=== 使用上下文管理器 ===")
|
|
346
|
+
try:
|
|
347
|
+
with MisakaDBClient("127.0.0.1", 10032) as client:
|
|
348
|
+
# 获取服务信息
|
|
349
|
+
service_info = client.get_service_info()
|
|
350
|
+
if service_info:
|
|
351
|
+
print("服务信息获取成功:")
|
|
352
|
+
print(json.dumps(service_info, indent=2, ensure_ascii=False))
|
|
353
|
+
|
|
354
|
+
# 获取特定信息
|
|
355
|
+
version = client.get_server_version()
|
|
356
|
+
allowed_commands = client.get_allowed_commands()
|
|
357
|
+
network_config = client.get_network_config()
|
|
358
|
+
|
|
359
|
+
print(f"\n服务器版本: {version}")
|
|
360
|
+
print(f"允许的命令: {allowed_commands}")
|
|
361
|
+
print(f"网络配置: {network_config}")
|
|
362
|
+
|
|
363
|
+
# 检查ping
|
|
364
|
+
if client.ping():
|
|
365
|
+
print("服务器可达")
|
|
366
|
+
else:
|
|
367
|
+
print("服务器不可达")
|
|
368
|
+
|
|
369
|
+
# 检查命令是否允许
|
|
370
|
+
test_command = "get-service-info"
|
|
371
|
+
if client.is_command_allowed(test_command):
|
|
372
|
+
print(f"命令 '{test_command}' 被允许")
|
|
373
|
+
else:
|
|
374
|
+
print(f"命令 '{test_command}' 不被允许")
|
|
375
|
+
|
|
376
|
+
# 执行自定义命令
|
|
377
|
+
result = client.execute_command("get-service-info")
|
|
378
|
+
print(f"\n执行命令结果: {result}")
|
|
379
|
+
except ConnectionError as e:
|
|
380
|
+
print(f"连接失败: {e}")
|
|
381
|
+
|
|
382
|
+
print("\n=== 使用传统方式 ===")
|
|
383
|
+
client = MisakaDBClient("127.0.0.1", 10032)
|
|
384
|
+
if client.connect():
|
|
385
|
+
try:
|
|
386
|
+
service_info = client.get_service_info()
|
|
387
|
+
if service_info:
|
|
388
|
+
print(f"服务版本: {service_info.get('service', {}).get('version', '未知')}")
|
|
389
|
+
print(f"监听地址: {service_info.get('network', {}).get('address', '未知')}")
|
|
390
|
+
print(f"监听端口: {service_info.get('network', {}).get('port', '未知')}")
|
|
391
|
+
finally:
|
|
392
|
+
client.close()
|
|
393
|
+
else:
|
|
394
|
+
print("连接失败")
|
misakadb/cli.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import getpass
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
# 在类 Unix 终端中启用 input() 的行编辑和历史能力。
|
|
8
|
+
try:
|
|
9
|
+
import readline
|
|
10
|
+
readline.parse_and_bind("tab: complete")
|
|
11
|
+
except ImportError:
|
|
12
|
+
readline = None
|
|
13
|
+
|
|
14
|
+
from .interface.status import StatusSocket
|
|
15
|
+
from . import __version__
|
|
16
|
+
from .mql.MQ import MiQL
|
|
17
|
+
from .network.command_send import commandSend
|
|
18
|
+
from .network import sock
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def main(argv=None):
|
|
22
|
+
parser = argparse.ArgumentParser(description=f"MisakaDB CLI {__version__}")
|
|
23
|
+
parser.add_argument("--version", action="version", version=f"misakadb {__version__}")
|
|
24
|
+
parser.add_argument("--address", type=str, default="127.0.0.1", help="服务地址, 默认本地")
|
|
25
|
+
parser.add_argument("--port", type=int, default=10032, help="服务端口, 默认10032")
|
|
26
|
+
parser.add_argument("--mode", type=str, default="shell", help="运行模式: shell[cli调试模式]、onlyConn[仅连接]")
|
|
27
|
+
parser.add_argument("--username", type=str, default="", help="[可选]用户名")
|
|
28
|
+
parser.add_argument("--password", type=str, default="", help="[可选]密码")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
args = parser.parse_args(argv)
|
|
32
|
+
|
|
33
|
+
address = args.address
|
|
34
|
+
port = args.port
|
|
35
|
+
mode = args.mode
|
|
36
|
+
username = args.username
|
|
37
|
+
password = args.password
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
cli = sock.clientCore(address, port)
|
|
41
|
+
connect_err = cli.connect()
|
|
42
|
+
if connect_err is not None:
|
|
43
|
+
print(f"连接失败: {connect_err}", file=sys.stderr)
|
|
44
|
+
cli.close()
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
command_send = commandSend(cli)
|
|
49
|
+
server_recv = command_send.init_command()
|
|
50
|
+
|
|
51
|
+
if mode in ["shell", "sh", "s"]:
|
|
52
|
+
retry = False
|
|
53
|
+
while username == "" or password == "":
|
|
54
|
+
if retry:
|
|
55
|
+
print("\n登录失败, 请重新输入用户名和密码:\n")
|
|
56
|
+
else:
|
|
57
|
+
retry = True
|
|
58
|
+
username = input("请输入用户名: ")
|
|
59
|
+
password = getpass.getpass("请输入密码: ")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
while not command_send.login(username, password):
|
|
64
|
+
print("登录失败, 请重新输入用户名和密码:\n")
|
|
65
|
+
username = input("请输入用户名: ")
|
|
66
|
+
password = getpass.getpass("请输入密码: ")
|
|
67
|
+
|
|
68
|
+
cli.status = StatusSocket.Connected
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
mq = MiQL(cli)
|
|
72
|
+
while True:
|
|
73
|
+
commands = ""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
user_input = "\\"
|
|
77
|
+
row = 0
|
|
78
|
+
while user_input.endswith("\\"):
|
|
79
|
+
selection = ""
|
|
80
|
+
if cli.session.database:
|
|
81
|
+
selection = f" db={cli.session.database}"
|
|
82
|
+
if cli.session.table:
|
|
83
|
+
selection += f" table={cli.session.table}"
|
|
84
|
+
user_input = input(f"[{address}:{port} {cli.status.value}{selection}]{row if row > 0 else ''} >")
|
|
85
|
+
commands += (user_input[:-1] if user_input.endswith("\\") else user_input) + "\n"
|
|
86
|
+
row += 1
|
|
87
|
+
|
|
88
|
+
commands = commands.strip()
|
|
89
|
+
if commands.startswith("mq."):
|
|
90
|
+
try:
|
|
91
|
+
start_time = time.time()
|
|
92
|
+
res = mq.shot(commands)
|
|
93
|
+
end_time = time.time()
|
|
94
|
+
elapsed_ms = (end_time - start_time) * 1000.0
|
|
95
|
+
|
|
96
|
+
if isinstance(res, bytes):
|
|
97
|
+
res_str = res.decode("utf-8", errors="replace")
|
|
98
|
+
command_send.handle_response(res_str, elapsed_ms, is_init=False)
|
|
99
|
+
elif res is not None:
|
|
100
|
+
print(res)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"MiQL 执行失败: {e}", file=sys.stderr)
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
elif commands in ["exit", 'e', 'q']:
|
|
106
|
+
cli.close()
|
|
107
|
+
print("bye~")
|
|
108
|
+
sys.exit(0)
|
|
109
|
+
elif commands:
|
|
110
|
+
start_time = time.time()
|
|
111
|
+
res = cli.send_command(commands)
|
|
112
|
+
end_time = time.time()
|
|
113
|
+
elapsed_ms = (end_time - start_time) * 1000.0
|
|
114
|
+
|
|
115
|
+
res_str = res.decode('utf-8', errors='replace')
|
|
116
|
+
command_send.handle_response(res_str, elapsed_ms, is_init=False)
|
|
117
|
+
except (EOFError, KeyboardInterrupt):
|
|
118
|
+
print("\nbye~")
|
|
119
|
+
finally:
|
|
120
|
+
cli.close()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
main()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MisakaDB interface implementation."""
|