jiaofu-dbview 0.2.0__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.
- jiaofu_dbview-0.2.0/PKG-INFO +42 -0
- jiaofu_dbview-0.2.0/README.md +34 -0
- jiaofu_dbview-0.2.0/__init__.py +5 -0
- jiaofu_dbview-0.2.0/config.py +80 -0
- jiaofu_dbview-0.2.0/init_db_example.py +50 -0
- jiaofu_dbview-0.2.0/jiaofu_dbview.egg-info/PKG-INFO +42 -0
- jiaofu_dbview-0.2.0/jiaofu_dbview.egg-info/SOURCES.txt +87 -0
- jiaofu_dbview-0.2.0/jiaofu_dbview.egg-info/dependency_links.txt +1 -0
- jiaofu_dbview-0.2.0/jiaofu_dbview.egg-info/requires.txt +1 -0
- jiaofu_dbview-0.2.0/jiaofu_dbview.egg-info/top_level.txt +1 -0
- jiaofu_dbview-0.2.0/oceanbase/__init__.py +0 -0
- jiaofu_dbview-0.2.0/oceanbase/init_db.py +104 -0
- jiaofu_dbview-0.2.0/oceanbase/mysql_cli.py +58 -0
- jiaofu_dbview-0.2.0/pyproject.toml +30 -0
- jiaofu_dbview-0.2.0/pys/__init__.py +0 -0
- jiaofu_dbview-0.2.0/pys/agent_messages_dao.py +70 -0
- jiaofu_dbview-0.2.0/pys/agent_sessions_dao.py +28 -0
- jiaofu_dbview-0.2.0/pys/backend.py +13 -0
- jiaofu_dbview-0.2.0/pys/dbview_dataset.py +405 -0
- jiaofu_dbview-0.2.0/pys/loader.py +45 -0
- jiaofu_dbview-0.2.0/pys/profile_task_queue_dao.py +43 -0
- jiaofu_dbview-0.2.0/pys/request_traces_dao.py +21 -0
- jiaofu_dbview-0.2.0/pys/sql_executor.py +82 -0
- jiaofu_dbview-0.2.0/pys/user_books_dao.py +12 -0
- jiaofu_dbview-0.2.0/pys/user_feedback_dao.py +44 -0
- jiaofu_dbview-0.2.0/pys/user_profile_dao.py +16 -0
- jiaofu_dbview-0.2.0/pys/users_dao.py +12 -0
- jiaofu_dbview-0.2.0/setup.cfg +4 -0
- jiaofu_dbview-0.2.0/sqlite/__init__.py +0 -0
- jiaofu_dbview-0.2.0/sqlite/init_db.py +63 -0
- jiaofu_dbview-0.2.0/sqlite/main.py +405 -0
- jiaofu_dbview-0.2.0/sqls/ddl/oceanbase.sql +119 -0
- jiaofu_dbview-0.2.0/sqls/ddl/sqlite.sql +136 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/agent_messages.sql +116 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/agent_sessions.sql +53 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/profile_task_queue.sql +37 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/request_traces.sql +8 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/user_books.sql +31 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/user_feedback.sql +16 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/user_profile.sql +38 -0
- jiaofu_dbview-0.2.0/sqls/dml/oceanbase/users.sql +44 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/agent_messages.sql +116 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/agent_sessions.sql +45 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/profile_task_queue.sql +38 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/request_traces.sql +8 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/user_books.sql +32 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/user_feedback.sql +16 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/user_profile.sql +37 -0
- jiaofu_dbview-0.2.0/sqls/dml/sqlite/users.sql +43 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jiaofu-dbview
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared database access layer for Jiaofu AgentOS services
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: pymysql>=1.1.1
|
|
8
|
+
|
|
9
|
+
# jiaofu-dbview
|
|
10
|
+
|
|
11
|
+
Shared database access package for Jiaofu AgentOS services.
|
|
12
|
+
|
|
13
|
+
The distribution name is `jiaofu-dbview`; the Python import name remains
|
|
14
|
+
`dbview`, so existing application imports do not need to change:
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Both the OceanBase and SQLite SQL definitions are included in the wheel.
|
|
21
|
+
Runtime SQLite database files are deliberately not packaged.
|
|
22
|
+
|
|
23
|
+
Configuration is supplied explicitly by the caller; this package does not read
|
|
24
|
+
service config files or environment variables:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from dbview import OceanBaseConfig, SQLiteConfig
|
|
28
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
29
|
+
|
|
30
|
+
oceanbase = DbViewDataset(
|
|
31
|
+
"oceanbase",
|
|
32
|
+
OceanBaseConfig(
|
|
33
|
+
host="db.example.internal",
|
|
34
|
+
port=2880,
|
|
35
|
+
user="user",
|
|
36
|
+
password="secret",
|
|
37
|
+
database="tikumis",
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
sqlite = DbViewDataset("sqlite", SQLiteConfig(path="/data/sessions.db"))
|
|
42
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# jiaofu-dbview
|
|
2
|
+
|
|
3
|
+
Shared database access package for Jiaofu AgentOS services.
|
|
4
|
+
|
|
5
|
+
The distribution name is `jiaofu-dbview`; the Python import name remains
|
|
6
|
+
`dbview`, so existing application imports do not need to change:
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Both the OceanBase and SQLite SQL definitions are included in the wheel.
|
|
13
|
+
Runtime SQLite database files are deliberately not packaged.
|
|
14
|
+
|
|
15
|
+
Configuration is supplied explicitly by the caller; this package does not read
|
|
16
|
+
service config files or environment variables:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from dbview import OceanBaseConfig, SQLiteConfig
|
|
20
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
21
|
+
|
|
22
|
+
oceanbase = DbViewDataset(
|
|
23
|
+
"oceanbase",
|
|
24
|
+
OceanBaseConfig(
|
|
25
|
+
host="db.example.internal",
|
|
26
|
+
port=2880,
|
|
27
|
+
user="user",
|
|
28
|
+
password="secret",
|
|
29
|
+
database="tikumis",
|
|
30
|
+
),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
sqlite = DbViewDataset("sqlite", SQLiteConfig(path="/data/sessions.db"))
|
|
34
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Explicit database configuration objects used by dbview callers."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Mapping, TypeAlias
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class OceanBaseConfig:
|
|
11
|
+
host: str
|
|
12
|
+
user: str
|
|
13
|
+
password: str
|
|
14
|
+
database: str
|
|
15
|
+
port: int = 2880
|
|
16
|
+
charset: str = "utf8mb4"
|
|
17
|
+
|
|
18
|
+
def connection_params(self) -> dict:
|
|
19
|
+
return {
|
|
20
|
+
"host": self.host,
|
|
21
|
+
"port": self.port,
|
|
22
|
+
"user": self.user,
|
|
23
|
+
"password": self.password,
|
|
24
|
+
"database": self.database,
|
|
25
|
+
"charset": self.charset,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class SQLiteConfig:
|
|
31
|
+
path: str
|
|
32
|
+
|
|
33
|
+
def resolved_path(self) -> str:
|
|
34
|
+
return str(Path(self.path).expanduser().resolve())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
DatabaseConfig: TypeAlias = OceanBaseConfig | SQLiteConfig
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def normalize_config(
|
|
41
|
+
backend: str,
|
|
42
|
+
config: DatabaseConfig | Mapping[str, object],
|
|
43
|
+
) -> DatabaseConfig:
|
|
44
|
+
"""Validate and normalize a caller-provided backend configuration."""
|
|
45
|
+
if backend == "oceanbase":
|
|
46
|
+
if isinstance(config, OceanBaseConfig):
|
|
47
|
+
result = config
|
|
48
|
+
elif isinstance(config, Mapping):
|
|
49
|
+
required = ("host", "user", "password", "database")
|
|
50
|
+
missing = [key for key in required if key not in config]
|
|
51
|
+
if missing:
|
|
52
|
+
raise ValueError(f"missing OceanBase config fields: {', '.join(missing)}")
|
|
53
|
+
result = OceanBaseConfig(
|
|
54
|
+
host=str(config["host"]),
|
|
55
|
+
port=int(config.get("port", 2880)),
|
|
56
|
+
user=str(config["user"]),
|
|
57
|
+
password=str(config["password"]),
|
|
58
|
+
database=str(config["database"]),
|
|
59
|
+
charset=str(config.get("charset", "utf8mb4")),
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
raise TypeError("oceanbase config must be OceanBaseConfig or a mapping")
|
|
63
|
+
if not result.host or not result.user or not result.database:
|
|
64
|
+
raise ValueError("OceanBase host, user and database must not be empty")
|
|
65
|
+
return result
|
|
66
|
+
|
|
67
|
+
if backend == "sqlite":
|
|
68
|
+
if isinstance(config, SQLiteConfig):
|
|
69
|
+
result = config
|
|
70
|
+
elif isinstance(config, Mapping):
|
|
71
|
+
if "path" not in config:
|
|
72
|
+
raise ValueError("missing SQLite config field: path")
|
|
73
|
+
result = SQLiteConfig(path=str(config["path"]))
|
|
74
|
+
else:
|
|
75
|
+
raise TypeError("sqlite config must be SQLiteConfig or a mapping")
|
|
76
|
+
if not result.path:
|
|
77
|
+
raise ValueError("SQLite path must not be empty")
|
|
78
|
+
return result
|
|
79
|
+
|
|
80
|
+
raise ValueError(f"unsupported dbview backend: {backend!r}")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Unified database schema initializer."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
|
|
6
|
+
from dbview.config import OceanBaseConfig, SQLiteConfig
|
|
7
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
parser = argparse.ArgumentParser(description="Initialize dbview schema")
|
|
12
|
+
parser.add_argument(
|
|
13
|
+
"--backend",
|
|
14
|
+
choices=("sqlite", "oceanbase"),
|
|
15
|
+
required=True,
|
|
16
|
+
help="database backend to initialize",
|
|
17
|
+
)
|
|
18
|
+
parser.add_argument("--sqlite-path")
|
|
19
|
+
parser.add_argument("--host")
|
|
20
|
+
parser.add_argument("--port", type=int, default=2880)
|
|
21
|
+
parser.add_argument("--user")
|
|
22
|
+
parser.add_argument("--password")
|
|
23
|
+
parser.add_argument("--database")
|
|
24
|
+
parser.add_argument("--charset", default="utf8mb4")
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
if args.backend == "sqlite":
|
|
28
|
+
if not args.sqlite_path:
|
|
29
|
+
parser.error("--sqlite-path is required for sqlite")
|
|
30
|
+
config = SQLiteConfig(path=args.sqlite_path)
|
|
31
|
+
else:
|
|
32
|
+
missing = [name for name in ("host", "user", "password", "database") if not getattr(args, name)]
|
|
33
|
+
if missing:
|
|
34
|
+
parser.error("OceanBase requires: " + ", ".join(f"--{name}" for name in missing))
|
|
35
|
+
config = OceanBaseConfig(
|
|
36
|
+
host=args.host,
|
|
37
|
+
port=args.port,
|
|
38
|
+
user=args.user,
|
|
39
|
+
password=args.password,
|
|
40
|
+
database=args.database,
|
|
41
|
+
charset=args.charset,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
dataset = DbViewDataset(args.backend, config)
|
|
45
|
+
dataset.init_schema()
|
|
46
|
+
print(f"initialized {dataset.backend} schema")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
main()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jiaofu-dbview
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Shared database access layer for Jiaofu AgentOS services
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: pymysql>=1.1.1
|
|
8
|
+
|
|
9
|
+
# jiaofu-dbview
|
|
10
|
+
|
|
11
|
+
Shared database access package for Jiaofu AgentOS services.
|
|
12
|
+
|
|
13
|
+
The distribution name is `jiaofu-dbview`; the Python import name remains
|
|
14
|
+
`dbview`, so existing application imports do not need to change:
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Both the OceanBase and SQLite SQL definitions are included in the wheel.
|
|
21
|
+
Runtime SQLite database files are deliberately not packaged.
|
|
22
|
+
|
|
23
|
+
Configuration is supplied explicitly by the caller; this package does not read
|
|
24
|
+
service config files or environment variables:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from dbview import OceanBaseConfig, SQLiteConfig
|
|
28
|
+
from dbview.pys.dbview_dataset import DbViewDataset
|
|
29
|
+
|
|
30
|
+
oceanbase = DbViewDataset(
|
|
31
|
+
"oceanbase",
|
|
32
|
+
OceanBaseConfig(
|
|
33
|
+
host="db.example.internal",
|
|
34
|
+
port=2880,
|
|
35
|
+
user="user",
|
|
36
|
+
password="secret",
|
|
37
|
+
database="tikumis",
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
sqlite = DbViewDataset("sqlite", SQLiteConfig(path="/data/sessions.db"))
|
|
42
|
+
```
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
__init__.py
|
|
3
|
+
config.py
|
|
4
|
+
init_db_example.py
|
|
5
|
+
pyproject.toml
|
|
6
|
+
./__init__.py
|
|
7
|
+
./config.py
|
|
8
|
+
./init_db_example.py
|
|
9
|
+
./oceanbase/__init__.py
|
|
10
|
+
./oceanbase/init_db.py
|
|
11
|
+
./oceanbase/mysql_cli.py
|
|
12
|
+
./pys/__init__.py
|
|
13
|
+
./pys/agent_messages_dao.py
|
|
14
|
+
./pys/agent_sessions_dao.py
|
|
15
|
+
./pys/backend.py
|
|
16
|
+
./pys/dbview_dataset.py
|
|
17
|
+
./pys/loader.py
|
|
18
|
+
./pys/profile_task_queue_dao.py
|
|
19
|
+
./pys/request_traces_dao.py
|
|
20
|
+
./pys/sql_executor.py
|
|
21
|
+
./pys/user_books_dao.py
|
|
22
|
+
./pys/user_feedback_dao.py
|
|
23
|
+
./pys/user_profile_dao.py
|
|
24
|
+
./pys/users_dao.py
|
|
25
|
+
./sqlite/__init__.py
|
|
26
|
+
./sqlite/init_db.py
|
|
27
|
+
./sqlite/main.py
|
|
28
|
+
./sqls/ddl/oceanbase.sql
|
|
29
|
+
./sqls/ddl/sqlite.sql
|
|
30
|
+
./sqls/dml/oceanbase/agent_messages.sql
|
|
31
|
+
./sqls/dml/oceanbase/agent_sessions.sql
|
|
32
|
+
./sqls/dml/oceanbase/profile_task_queue.sql
|
|
33
|
+
./sqls/dml/oceanbase/request_traces.sql
|
|
34
|
+
./sqls/dml/oceanbase/user_books.sql
|
|
35
|
+
./sqls/dml/oceanbase/user_feedback.sql
|
|
36
|
+
./sqls/dml/oceanbase/user_profile.sql
|
|
37
|
+
./sqls/dml/oceanbase/users.sql
|
|
38
|
+
./sqls/dml/sqlite/agent_messages.sql
|
|
39
|
+
./sqls/dml/sqlite/agent_sessions.sql
|
|
40
|
+
./sqls/dml/sqlite/profile_task_queue.sql
|
|
41
|
+
./sqls/dml/sqlite/request_traces.sql
|
|
42
|
+
./sqls/dml/sqlite/user_books.sql
|
|
43
|
+
./sqls/dml/sqlite/user_feedback.sql
|
|
44
|
+
./sqls/dml/sqlite/user_profile.sql
|
|
45
|
+
./sqls/dml/sqlite/users.sql
|
|
46
|
+
jiaofu_dbview.egg-info/PKG-INFO
|
|
47
|
+
jiaofu_dbview.egg-info/SOURCES.txt
|
|
48
|
+
jiaofu_dbview.egg-info/dependency_links.txt
|
|
49
|
+
jiaofu_dbview.egg-info/requires.txt
|
|
50
|
+
jiaofu_dbview.egg-info/top_level.txt
|
|
51
|
+
oceanbase/__init__.py
|
|
52
|
+
oceanbase/init_db.py
|
|
53
|
+
oceanbase/mysql_cli.py
|
|
54
|
+
pys/__init__.py
|
|
55
|
+
pys/agent_messages_dao.py
|
|
56
|
+
pys/agent_sessions_dao.py
|
|
57
|
+
pys/backend.py
|
|
58
|
+
pys/dbview_dataset.py
|
|
59
|
+
pys/loader.py
|
|
60
|
+
pys/profile_task_queue_dao.py
|
|
61
|
+
pys/request_traces_dao.py
|
|
62
|
+
pys/sql_executor.py
|
|
63
|
+
pys/user_books_dao.py
|
|
64
|
+
pys/user_feedback_dao.py
|
|
65
|
+
pys/user_profile_dao.py
|
|
66
|
+
pys/users_dao.py
|
|
67
|
+
sqlite/__init__.py
|
|
68
|
+
sqlite/init_db.py
|
|
69
|
+
sqlite/main.py
|
|
70
|
+
sqls/ddl/oceanbase.sql
|
|
71
|
+
sqls/ddl/sqlite.sql
|
|
72
|
+
sqls/dml/oceanbase/agent_messages.sql
|
|
73
|
+
sqls/dml/oceanbase/agent_sessions.sql
|
|
74
|
+
sqls/dml/oceanbase/profile_task_queue.sql
|
|
75
|
+
sqls/dml/oceanbase/request_traces.sql
|
|
76
|
+
sqls/dml/oceanbase/user_books.sql
|
|
77
|
+
sqls/dml/oceanbase/user_feedback.sql
|
|
78
|
+
sqls/dml/oceanbase/user_profile.sql
|
|
79
|
+
sqls/dml/oceanbase/users.sql
|
|
80
|
+
sqls/dml/sqlite/agent_messages.sql
|
|
81
|
+
sqls/dml/sqlite/agent_sessions.sql
|
|
82
|
+
sqls/dml/sqlite/profile_task_queue.sql
|
|
83
|
+
sqls/dml/sqlite/request_traces.sql
|
|
84
|
+
sqls/dml/sqlite/user_books.sql
|
|
85
|
+
sqls/dml/sqlite/user_feedback.sql
|
|
86
|
+
sqls/dml/sqlite/user_profile.sql
|
|
87
|
+
sqls/dml/sqlite/users.sql
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pymysql>=1.1.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dbview
|
|
File without changes
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""OceanBase (MySQL protocol) backend keyed by explicit connection config."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
from dbview.config import OceanBaseConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class OceanBaseDB:
|
|
10
|
+
_instances: dict[OceanBaseConfig, "OceanBaseDB"] = {}
|
|
11
|
+
_instances_lock = threading.Lock()
|
|
12
|
+
|
|
13
|
+
def __init__(self, config: OceanBaseConfig) -> None:
|
|
14
|
+
import pymysql # lazy: only needed on the mysql path
|
|
15
|
+
from pymysql.cursors import DictCursor
|
|
16
|
+
|
|
17
|
+
self._pymysql = pymysql
|
|
18
|
+
self._DictCursor = DictCursor
|
|
19
|
+
self._lock = threading.Lock()
|
|
20
|
+
self.config = config
|
|
21
|
+
self.conn = self._connect()
|
|
22
|
+
|
|
23
|
+
def _connect(self):
|
|
24
|
+
params = self.config.connection_params()
|
|
25
|
+
params["cursorclass"] = self._DictCursor
|
|
26
|
+
params["autocommit"] = False
|
|
27
|
+
return self._pymysql.connect(**params)
|
|
28
|
+
|
|
29
|
+
def _reconnect(self) -> None:
|
|
30
|
+
try:
|
|
31
|
+
self.conn.close()
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
self.conn = self._connect()
|
|
35
|
+
|
|
36
|
+
def _ensure_connection(self) -> None:
|
|
37
|
+
try:
|
|
38
|
+
self.conn.ping(reconnect=True)
|
|
39
|
+
except Exception:
|
|
40
|
+
self._reconnect()
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def instance(cls, config: OceanBaseConfig) -> "OceanBaseDB":
|
|
44
|
+
with cls._instances_lock:
|
|
45
|
+
if config not in cls._instances:
|
|
46
|
+
cls._instances[config] = cls(config)
|
|
47
|
+
return cls._instances[config]
|
|
48
|
+
|
|
49
|
+
def init_schema(self, statements: list[str]) -> None:
|
|
50
|
+
"""Execute schema statements from the unified initializer."""
|
|
51
|
+
with self._lock:
|
|
52
|
+
self._ensure_connection()
|
|
53
|
+
with self.conn.cursor() as cur:
|
|
54
|
+
for stmt in statements:
|
|
55
|
+
try:
|
|
56
|
+
cur.execute(stmt)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
code = getattr(e, "args", [None])[0]
|
|
59
|
+
if code not in (1050, 1061):
|
|
60
|
+
raise
|
|
61
|
+
self.conn.commit()
|
|
62
|
+
|
|
63
|
+
def execute(self, sql: str, params: dict | None = None, fetch: str = "none"):
|
|
64
|
+
"""Run one statement; commit on writes."""
|
|
65
|
+
last_exc = None
|
|
66
|
+
for attempt in (1, 2):
|
|
67
|
+
with self._lock:
|
|
68
|
+
try:
|
|
69
|
+
self._ensure_connection()
|
|
70
|
+
with self.conn.cursor() as cur:
|
|
71
|
+
cur.execute(sql, params or {})
|
|
72
|
+
if fetch == "one":
|
|
73
|
+
row = cur.fetchone()
|
|
74
|
+
return row if row is not None else None
|
|
75
|
+
if fetch == "all":
|
|
76
|
+
return list(cur.fetchall())
|
|
77
|
+
if cur.description is None:
|
|
78
|
+
self.conn.commit()
|
|
79
|
+
return cur.rowcount
|
|
80
|
+
return None
|
|
81
|
+
except self._pymysql.err.InterfaceError as exc:
|
|
82
|
+
last_exc = exc
|
|
83
|
+
self._reconnect()
|
|
84
|
+
if attempt == 2:
|
|
85
|
+
raise
|
|
86
|
+
except self._pymysql.err.OperationalError as exc:
|
|
87
|
+
last_exc = exc
|
|
88
|
+
try:
|
|
89
|
+
self.conn.rollback()
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
code = getattr(exc, "args", [None])[0]
|
|
93
|
+
if code in (2006, 2013, 0):
|
|
94
|
+
self._reconnect()
|
|
95
|
+
if attempt == 1:
|
|
96
|
+
continue
|
|
97
|
+
raise
|
|
98
|
+
except Exception:
|
|
99
|
+
try:
|
|
100
|
+
self.conn.rollback()
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
raise
|
|
104
|
+
raise last_exc
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Small OceanBase SQL client with explicit connection arguments."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
import pymysql
|
|
8
|
+
|
|
9
|
+
from dbview.config import OceanBaseConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> None:
|
|
13
|
+
parser = argparse.ArgumentParser(description="Connect to OceanBase")
|
|
14
|
+
parser.add_argument("--host", required=True)
|
|
15
|
+
parser.add_argument("--port", type=int, default=2880)
|
|
16
|
+
parser.add_argument("--user", required=True)
|
|
17
|
+
parser.add_argument("--password", required=True)
|
|
18
|
+
parser.add_argument("--database", required=True)
|
|
19
|
+
parser.add_argument("--charset", default="utf8mb4")
|
|
20
|
+
parser.add_argument("sql", nargs="*")
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
config = OceanBaseConfig(
|
|
24
|
+
host=args.host,
|
|
25
|
+
port=args.port,
|
|
26
|
+
user=args.user,
|
|
27
|
+
password=args.password,
|
|
28
|
+
database=args.database,
|
|
29
|
+
charset=args.charset,
|
|
30
|
+
)
|
|
31
|
+
conn = pymysql.connect(**config.connection_params())
|
|
32
|
+
cursor = conn.cursor()
|
|
33
|
+
try:
|
|
34
|
+
if args.sql:
|
|
35
|
+
statements = [" ".join(args.sql)]
|
|
36
|
+
else:
|
|
37
|
+
print("Connected to OceanBase. Type SQL (Ctrl+D to exit):")
|
|
38
|
+
statements = iter(lambda: input("mysql> ").strip(), "")
|
|
39
|
+
|
|
40
|
+
for sql in statements:
|
|
41
|
+
if not sql:
|
|
42
|
+
continue
|
|
43
|
+
cursor.execute(sql)
|
|
44
|
+
if cursor.description:
|
|
45
|
+
print("\t".join(item[0] for item in cursor.description))
|
|
46
|
+
for row in cursor.fetchall():
|
|
47
|
+
print("\t".join(str(value) for value in row))
|
|
48
|
+
print(f"Rows: {cursor.rowcount}")
|
|
49
|
+
conn.commit()
|
|
50
|
+
except EOFError:
|
|
51
|
+
pass
|
|
52
|
+
finally:
|
|
53
|
+
cursor.close()
|
|
54
|
+
conn.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jiaofu-dbview"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Shared database access layer for Jiaofu AgentOS services"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pymysql>=1.1.1",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[tool.setuptools]
|
|
16
|
+
packages = [
|
|
17
|
+
"dbview",
|
|
18
|
+
"dbview.oceanbase",
|
|
19
|
+
"dbview.pys",
|
|
20
|
+
"dbview.sqlite",
|
|
21
|
+
]
|
|
22
|
+
package-dir = {dbview = "."}
|
|
23
|
+
include-package-data = true
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.package-data]
|
|
26
|
+
dbview = [
|
|
27
|
+
"sqls/ddl/*.sql",
|
|
28
|
+
"sqls/dml/oceanbase/*.sql",
|
|
29
|
+
"sqls/dml/sqlite/*.sql",
|
|
30
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""DAO for the agent_messages table (history reads + profile-worker reads)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .backend import run
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def insert_message(session_id: str, message_data: str, turn_number: int | None = None, book_id: str | None = None) -> None:
|
|
8
|
+
run("insert_message", {
|
|
9
|
+
"session_id": session_id,
|
|
10
|
+
"book_id": book_id or "",
|
|
11
|
+
"message_data": message_data,
|
|
12
|
+
"turn_number": turn_number,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def max_msg_id(session_id: str) -> dict | None:
|
|
17
|
+
return run("max_msg_id", {"session_id": session_id}, fetch="one")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def count_messages(cuid: str, uid: str | None = None) -> dict | None:
|
|
21
|
+
return run("count_messages", {"cuid": cuid, "uid": uid}, fetch="one")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def list_history_turns(cuid: str, before_msg_id: int, limit: int, uid: str | None = None) -> list[dict]:
|
|
25
|
+
return run("list_history_turns", {
|
|
26
|
+
"cuid": cuid,
|
|
27
|
+
"uid": uid,
|
|
28
|
+
"before_msg_id": before_msg_id,
|
|
29
|
+
"limit": limit,
|
|
30
|
+
}, fetch="all")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def list_turn_items(session_id: str, turn_number: int) -> list[dict]:
|
|
34
|
+
return run("list_turn_items", {
|
|
35
|
+
"session_id": session_id,
|
|
36
|
+
"turn_number": turn_number,
|
|
37
|
+
}, fetch="all")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def has_more_history(cuid: str, first_msg_id: int, uid: str | None = None) -> dict | None:
|
|
41
|
+
return run("has_more_history", {
|
|
42
|
+
"cuid": cuid,
|
|
43
|
+
"uid": uid,
|
|
44
|
+
"first_msg_id": first_msg_id,
|
|
45
|
+
}, fetch="one")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def list_items_by_msg_range(cuid: str, from_msg_id: int, max_turns: int, uid: str | None = None) -> list[dict]:
|
|
49
|
+
return run("list_items_by_msg_range", {
|
|
50
|
+
"cuid": cuid,
|
|
51
|
+
"uid": uid,
|
|
52
|
+
"from_msg_id": from_msg_id,
|
|
53
|
+
"max_turns": max_turns,
|
|
54
|
+
}, fetch="all")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def list_session_turn_items(session_id: str, from_turn: int | None = None, to_turn: int | None = None, book_id: str | None = None) -> list[dict]:
|
|
58
|
+
return run("list_session_turn_items", {
|
|
59
|
+
"session_id": session_id,
|
|
60
|
+
"book_id": book_id or "",
|
|
61
|
+
"from_turn": from_turn,
|
|
62
|
+
"to_turn": to_turn,
|
|
63
|
+
}, fetch="all")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def list_session_books(session_id: str) -> list[dict]:
|
|
67
|
+
return run("list_session_books", {"session_id": session_id}, fetch="all")
|
|
68
|
+
|
|
69
|
+
def max_turn_number(session_id: str) -> dict | None:
|
|
70
|
+
return run("max_turn_number", {"session_id": session_id}, fetch="one")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""DAO for the agent_sessions table."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .backend import run
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def insert_session(session_id: str, cuid: str | None, last_book_id: str | None, uid: str | None = None) -> None:
|
|
8
|
+
updated = run("touch_session", {"session_id": session_id})
|
|
9
|
+
if updated:
|
|
10
|
+
return
|
|
11
|
+
run("insert_session", {
|
|
12
|
+
"session_id": session_id,
|
|
13
|
+
"cuid": cuid,
|
|
14
|
+
"uid": uid,
|
|
15
|
+
"book_id": last_book_id,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def touch_session(session_id: str) -> None:
|
|
20
|
+
run("touch_session", {"session_id": session_id})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def set_last_book(session_id: str, last_book_id: str | None) -> None:
|
|
24
|
+
run("set_last_book", {"session_id": session_id, "last_book_id": last_book_id})
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_latest_session(cuid: str, uid: str | None = None) -> dict | None:
|
|
28
|
+
return run("get_latest_session", {"cuid": cuid, "uid": uid}, fetch="one")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Deprecated function-style DAO dispatcher.
|
|
2
|
+
|
|
3
|
+
Use ``dbview.pys.dbview_dataset.DbViewDataset`` instead. Keeping this module only makes
|
|
4
|
+
old imports fail loudly instead of silently using an unintended database.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(*args, **kwargs):
|
|
10
|
+
raise RuntimeError(
|
|
11
|
+
"dbview.pys.backend.run is deprecated; create "
|
|
12
|
+
"DbViewDataset(backend, config) and call methods on that instance"
|
|
13
|
+
)
|