datus-postgresql 0.1.0__tar.gz → 0.1.2__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.
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/.gitignore +3 -0
- datus_postgresql-0.1.2/PKG-INFO +232 -0
- datus_postgresql-0.1.2/README.md +210 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/datus_postgresql/__init__.py +11 -2
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/datus_postgresql/connector.py +3 -7
- datus_postgresql-0.1.2/datus_postgresql/handlers.py +80 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/pyproject.toml +3 -2
- datus_postgresql-0.1.2/scripts/init_tpch_data.py +419 -0
- datus_postgresql-0.1.2/tests/integration/conftest.py +399 -0
- datus_postgresql-0.1.2/tests/integration/test_tpch.py +152 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/unit/test_connector_unit.py +2 -2
- datus_postgresql-0.1.0/PKG-INFO +0 -81
- datus_postgresql-0.1.0/README.md +0 -59
- datus_postgresql-0.1.0/tests/integration/conftest.py +0 -41
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/datus_postgresql/config.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/docker-compose.yml +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/__init__.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/conftest.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/integration/__init__.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/integration/test_integration.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/unit/__init__.py +0 -0
- {datus_postgresql-0.1.0 → datus_postgresql-0.1.2}/tests/unit/test_config.py +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datus-postgresql
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: PostgreSQL database adapter for Datus
|
|
5
|
+
Project-URL: Homepage, https://github.com/Datus-ai/datus-db-adapters
|
|
6
|
+
Project-URL: Repository, https://github.com/Datus-ai/datus-db-adapters
|
|
7
|
+
Project-URL: Issues, https://github.com/Datus-ai/datus-db-adapters/issues
|
|
8
|
+
Author-email: DatusAI <support@datus.ai>
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
Keywords: adapter,database,datus,postgresql
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: datus-db-core>=0.1.0
|
|
18
|
+
Requires-Dist: datus-sqlalchemy>=0.1.2
|
|
19
|
+
Requires-Dist: psycopg2-binary>=2.9.11
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# datus-postgresql
|
|
24
|
+
|
|
25
|
+
PostgreSQL database adapter for [Datus](https://github.com/Datus-ai/datus-agent).
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
This adapter provides PostgreSQL connectivity for Datus, supporting full SQL operations, metadata discovery, schema management, and materialized views via the standard PostgreSQL protocol.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install datus-postgresql
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This will automatically install the required dependencies:
|
|
38
|
+
- `datus-agent`
|
|
39
|
+
- `datus-sqlalchemy` (which includes SQLAlchemy and psycopg2)
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
The adapter is automatically registered with Datus when installed. Configure your database connection:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
namespace:
|
|
47
|
+
postgresql_prod:
|
|
48
|
+
type: postgresql
|
|
49
|
+
host: localhost
|
|
50
|
+
port: 5432
|
|
51
|
+
username: postgres
|
|
52
|
+
password: your_password
|
|
53
|
+
database: mydb
|
|
54
|
+
schema: public
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or use programmatically:
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from datus_postgresql import PostgreSQLConnector, PostgreSQLConfig
|
|
61
|
+
|
|
62
|
+
# Using config object
|
|
63
|
+
config = PostgreSQLConfig(
|
|
64
|
+
host="localhost",
|
|
65
|
+
port=5432,
|
|
66
|
+
username="postgres",
|
|
67
|
+
password="password",
|
|
68
|
+
database="mydb",
|
|
69
|
+
schema_name="public",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
connector = PostgreSQLConnector(config)
|
|
73
|
+
|
|
74
|
+
# Or using dict
|
|
75
|
+
connector = PostgreSQLConnector({
|
|
76
|
+
"host": "localhost",
|
|
77
|
+
"port": 5432,
|
|
78
|
+
"username": "postgres",
|
|
79
|
+
"password": "password",
|
|
80
|
+
"database": "mydb",
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
# Use context manager for automatic cleanup
|
|
84
|
+
with connector:
|
|
85
|
+
# Test connection
|
|
86
|
+
connector.test_connection()
|
|
87
|
+
|
|
88
|
+
# Execute queries
|
|
89
|
+
result = connector.execute({"sql_query": "SELECT * FROM users LIMIT 10"})
|
|
90
|
+
print(result.sql_return)
|
|
91
|
+
|
|
92
|
+
# Get tables
|
|
93
|
+
tables = connector.get_tables(schema_name="public")
|
|
94
|
+
print(f"Tables: {tables}")
|
|
95
|
+
|
|
96
|
+
# Get table schema
|
|
97
|
+
columns = connector.get_schema(schema_name="public", table_name="users")
|
|
98
|
+
for col in columns:
|
|
99
|
+
print(f" {col['name']}: {col['type']}")
|
|
100
|
+
|
|
101
|
+
# Get materialized views
|
|
102
|
+
mvs = connector.get_materialized_views(schema_name="public")
|
|
103
|
+
print(f"Materialized Views: {mvs}")
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Configuration Options
|
|
107
|
+
|
|
108
|
+
| Option | Type | Default | Description |
|
|
109
|
+
|--------|------|---------|-------------|
|
|
110
|
+
| `host` | str | `"127.0.0.1"` | PostgreSQL server host |
|
|
111
|
+
| `port` | int | `5432` | PostgreSQL server port |
|
|
112
|
+
| `username` | str | required | PostgreSQL username |
|
|
113
|
+
| `password` | str | `""` | PostgreSQL password |
|
|
114
|
+
| `database` | str | `None` | Default database name |
|
|
115
|
+
| `schema_name` | str | `"public"` | Default schema name |
|
|
116
|
+
| `sslmode` | str | `"prefer"` | SSL mode (`disable`, `allow`, `prefer`, `require`, `verify-ca`, `verify-full`) |
|
|
117
|
+
| `timeout_seconds` | int | `30` | Connection timeout in seconds |
|
|
118
|
+
|
|
119
|
+
## Features
|
|
120
|
+
|
|
121
|
+
- **Full SQL Support**: Execute queries, DDL, DML operations
|
|
122
|
+
- **Metadata Discovery**: Automatic discovery of databases, schemas, tables, views, and materialized views
|
|
123
|
+
- **DDL Generation**: Reconstruct CREATE TABLE / VIEW / MATERIALIZED VIEW statements
|
|
124
|
+
- **Schema Management**: Switch between schemas seamlessly
|
|
125
|
+
- **Sample Data**: Extract sample rows for data profiling
|
|
126
|
+
- **Connection Management**: SQLAlchemy-based connection pooling with SSL support
|
|
127
|
+
- **Multiple Result Formats**: pandas, arrow, csv, list
|
|
128
|
+
|
|
129
|
+
## Code Structure
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
datus-postgresql/
|
|
133
|
+
├── datus_postgresql/
|
|
134
|
+
│ ├── __init__.py # Package exports
|
|
135
|
+
│ ├── config.py # PostgreSQLConfig (Pydantic model)
|
|
136
|
+
│ └── connector.py # PostgreSQLConnector implementation
|
|
137
|
+
├── tests/
|
|
138
|
+
│ ├── unit/ # Unit tests with mocks (no database needed)
|
|
139
|
+
│ └── integration/ # Integration tests (requires PostgreSQL)
|
|
140
|
+
│ ├── conftest.py # Test fixtures and TPC-H data setup
|
|
141
|
+
│ ├── test_integration.py # General integration tests
|
|
142
|
+
│ └── test_tpch.py # TPC-H benchmark data tests
|
|
143
|
+
├── scripts/
|
|
144
|
+
│ └── init_tpch_data.py # TPC-H data initialization script
|
|
145
|
+
├── docker-compose.yml # PostgreSQL test container
|
|
146
|
+
├── pyproject.toml
|
|
147
|
+
└── README.md
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Testing
|
|
151
|
+
|
|
152
|
+
### Quick Start
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 1. Start PostgreSQL test container
|
|
156
|
+
docker-compose up -d
|
|
157
|
+
|
|
158
|
+
# 2. Run tests
|
|
159
|
+
pytest tests/unit/ -v # Unit tests (no database needed)
|
|
160
|
+
pytest tests/integration/ -v # Integration tests (requires PostgreSQL)
|
|
161
|
+
pytest tests/ -v # All tests
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Test Types
|
|
165
|
+
|
|
166
|
+
- **Unit tests**: Configuration and connector logic with mocks (no database needed)
|
|
167
|
+
- **Integration tests**: Real database operations (SQL, metadata, schema)
|
|
168
|
+
- **TPC-H tests**: Analytical query tests using TPC-H benchmark data
|
|
169
|
+
|
|
170
|
+
### TPC-H Integration Tests
|
|
171
|
+
|
|
172
|
+
The adapter includes TPC-H benchmark data tests for validating analytical query capabilities.
|
|
173
|
+
|
|
174
|
+
#### TPC-H Tables
|
|
175
|
+
|
|
176
|
+
| Table | Rows | Description |
|
|
177
|
+
|-------|------|-------------|
|
|
178
|
+
| `tpch_region` | 5 | World regions |
|
|
179
|
+
| `tpch_nation` | 25 | Countries with region references |
|
|
180
|
+
| `tpch_supplier` | 5 | Suppliers with nation references |
|
|
181
|
+
| `tpch_customer` | 10 | Customers with nation references |
|
|
182
|
+
| `tpch_orders` | 15 | Orders with customer references |
|
|
183
|
+
|
|
184
|
+
#### Running TPC-H Tests
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# Start PostgreSQL container
|
|
188
|
+
docker-compose up -d
|
|
189
|
+
|
|
190
|
+
# Run TPC-H integration tests
|
|
191
|
+
pytest tests/integration/test_tpch.py -v
|
|
192
|
+
|
|
193
|
+
# Initialize TPC-H data manually (for ad-hoc testing)
|
|
194
|
+
python scripts/init_tpch_data.py \
|
|
195
|
+
--host localhost --port 5432 \
|
|
196
|
+
--username test_user --password test_password \
|
|
197
|
+
--database test --schema public
|
|
198
|
+
|
|
199
|
+
# Drop and recreate TPC-H tables
|
|
200
|
+
python scripts/init_tpch_data.py --drop
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
#### Environment Variables
|
|
204
|
+
|
|
205
|
+
Tests use these default values (matching docker-compose.yml):
|
|
206
|
+
|
|
207
|
+
| Variable | Default | Description |
|
|
208
|
+
|----------|---------|-------------|
|
|
209
|
+
| `POSTGRESQL_HOST` | `localhost` | PostgreSQL server host |
|
|
210
|
+
| `POSTGRESQL_PORT` | `5432` | PostgreSQL server port |
|
|
211
|
+
| `POSTGRESQL_USER` | `test_user` | PostgreSQL username |
|
|
212
|
+
| `POSTGRESQL_PASSWORD` | `test_password` | PostgreSQL password |
|
|
213
|
+
| `POSTGRESQL_DATABASE` | `test` | Database name |
|
|
214
|
+
| `POSTGRESQL_SCHEMA` | `public` | Schema name |
|
|
215
|
+
|
|
216
|
+
## Requirements
|
|
217
|
+
|
|
218
|
+
- Python >= 3.12
|
|
219
|
+
- PostgreSQL >= 12
|
|
220
|
+
- datus-agent >= 0.2.1
|
|
221
|
+
- datus-sqlalchemy >= 0.1.0
|
|
222
|
+
|
|
223
|
+
## License
|
|
224
|
+
|
|
225
|
+
Apache License 2.0
|
|
226
|
+
|
|
227
|
+
## Related Packages
|
|
228
|
+
|
|
229
|
+
- `datus-sqlalchemy` - SQLAlchemy base connector
|
|
230
|
+
- `datus-mysql` - MySQL adapter
|
|
231
|
+
- `datus-redshift` - Amazon Redshift adapter
|
|
232
|
+
- `datus-snowflake` - Snowflake adapter
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# datus-postgresql
|
|
2
|
+
|
|
3
|
+
PostgreSQL database adapter for [Datus](https://github.com/Datus-ai/datus-agent).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This adapter provides PostgreSQL connectivity for Datus, supporting full SQL operations, metadata discovery, schema management, and materialized views via the standard PostgreSQL protocol.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install datus-postgresql
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This will automatically install the required dependencies:
|
|
16
|
+
- `datus-agent`
|
|
17
|
+
- `datus-sqlalchemy` (which includes SQLAlchemy and psycopg2)
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
The adapter is automatically registered with Datus when installed. Configure your database connection:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
namespace:
|
|
25
|
+
postgresql_prod:
|
|
26
|
+
type: postgresql
|
|
27
|
+
host: localhost
|
|
28
|
+
port: 5432
|
|
29
|
+
username: postgres
|
|
30
|
+
password: your_password
|
|
31
|
+
database: mydb
|
|
32
|
+
schema: public
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or use programmatically:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from datus_postgresql import PostgreSQLConnector, PostgreSQLConfig
|
|
39
|
+
|
|
40
|
+
# Using config object
|
|
41
|
+
config = PostgreSQLConfig(
|
|
42
|
+
host="localhost",
|
|
43
|
+
port=5432,
|
|
44
|
+
username="postgres",
|
|
45
|
+
password="password",
|
|
46
|
+
database="mydb",
|
|
47
|
+
schema_name="public",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
connector = PostgreSQLConnector(config)
|
|
51
|
+
|
|
52
|
+
# Or using dict
|
|
53
|
+
connector = PostgreSQLConnector({
|
|
54
|
+
"host": "localhost",
|
|
55
|
+
"port": 5432,
|
|
56
|
+
"username": "postgres",
|
|
57
|
+
"password": "password",
|
|
58
|
+
"database": "mydb",
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
# Use context manager for automatic cleanup
|
|
62
|
+
with connector:
|
|
63
|
+
# Test connection
|
|
64
|
+
connector.test_connection()
|
|
65
|
+
|
|
66
|
+
# Execute queries
|
|
67
|
+
result = connector.execute({"sql_query": "SELECT * FROM users LIMIT 10"})
|
|
68
|
+
print(result.sql_return)
|
|
69
|
+
|
|
70
|
+
# Get tables
|
|
71
|
+
tables = connector.get_tables(schema_name="public")
|
|
72
|
+
print(f"Tables: {tables}")
|
|
73
|
+
|
|
74
|
+
# Get table schema
|
|
75
|
+
columns = connector.get_schema(schema_name="public", table_name="users")
|
|
76
|
+
for col in columns:
|
|
77
|
+
print(f" {col['name']}: {col['type']}")
|
|
78
|
+
|
|
79
|
+
# Get materialized views
|
|
80
|
+
mvs = connector.get_materialized_views(schema_name="public")
|
|
81
|
+
print(f"Materialized Views: {mvs}")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration Options
|
|
85
|
+
|
|
86
|
+
| Option | Type | Default | Description |
|
|
87
|
+
|--------|------|---------|-------------|
|
|
88
|
+
| `host` | str | `"127.0.0.1"` | PostgreSQL server host |
|
|
89
|
+
| `port` | int | `5432` | PostgreSQL server port |
|
|
90
|
+
| `username` | str | required | PostgreSQL username |
|
|
91
|
+
| `password` | str | `""` | PostgreSQL password |
|
|
92
|
+
| `database` | str | `None` | Default database name |
|
|
93
|
+
| `schema_name` | str | `"public"` | Default schema name |
|
|
94
|
+
| `sslmode` | str | `"prefer"` | SSL mode (`disable`, `allow`, `prefer`, `require`, `verify-ca`, `verify-full`) |
|
|
95
|
+
| `timeout_seconds` | int | `30` | Connection timeout in seconds |
|
|
96
|
+
|
|
97
|
+
## Features
|
|
98
|
+
|
|
99
|
+
- **Full SQL Support**: Execute queries, DDL, DML operations
|
|
100
|
+
- **Metadata Discovery**: Automatic discovery of databases, schemas, tables, views, and materialized views
|
|
101
|
+
- **DDL Generation**: Reconstruct CREATE TABLE / VIEW / MATERIALIZED VIEW statements
|
|
102
|
+
- **Schema Management**: Switch between schemas seamlessly
|
|
103
|
+
- **Sample Data**: Extract sample rows for data profiling
|
|
104
|
+
- **Connection Management**: SQLAlchemy-based connection pooling with SSL support
|
|
105
|
+
- **Multiple Result Formats**: pandas, arrow, csv, list
|
|
106
|
+
|
|
107
|
+
## Code Structure
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
datus-postgresql/
|
|
111
|
+
├── datus_postgresql/
|
|
112
|
+
│ ├── __init__.py # Package exports
|
|
113
|
+
│ ├── config.py # PostgreSQLConfig (Pydantic model)
|
|
114
|
+
│ └── connector.py # PostgreSQLConnector implementation
|
|
115
|
+
├── tests/
|
|
116
|
+
│ ├── unit/ # Unit tests with mocks (no database needed)
|
|
117
|
+
│ └── integration/ # Integration tests (requires PostgreSQL)
|
|
118
|
+
│ ├── conftest.py # Test fixtures and TPC-H data setup
|
|
119
|
+
│ ├── test_integration.py # General integration tests
|
|
120
|
+
│ └── test_tpch.py # TPC-H benchmark data tests
|
|
121
|
+
├── scripts/
|
|
122
|
+
│ └── init_tpch_data.py # TPC-H data initialization script
|
|
123
|
+
├── docker-compose.yml # PostgreSQL test container
|
|
124
|
+
├── pyproject.toml
|
|
125
|
+
└── README.md
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Testing
|
|
129
|
+
|
|
130
|
+
### Quick Start
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# 1. Start PostgreSQL test container
|
|
134
|
+
docker-compose up -d
|
|
135
|
+
|
|
136
|
+
# 2. Run tests
|
|
137
|
+
pytest tests/unit/ -v # Unit tests (no database needed)
|
|
138
|
+
pytest tests/integration/ -v # Integration tests (requires PostgreSQL)
|
|
139
|
+
pytest tests/ -v # All tests
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Test Types
|
|
143
|
+
|
|
144
|
+
- **Unit tests**: Configuration and connector logic with mocks (no database needed)
|
|
145
|
+
- **Integration tests**: Real database operations (SQL, metadata, schema)
|
|
146
|
+
- **TPC-H tests**: Analytical query tests using TPC-H benchmark data
|
|
147
|
+
|
|
148
|
+
### TPC-H Integration Tests
|
|
149
|
+
|
|
150
|
+
The adapter includes TPC-H benchmark data tests for validating analytical query capabilities.
|
|
151
|
+
|
|
152
|
+
#### TPC-H Tables
|
|
153
|
+
|
|
154
|
+
| Table | Rows | Description |
|
|
155
|
+
|-------|------|-------------|
|
|
156
|
+
| `tpch_region` | 5 | World regions |
|
|
157
|
+
| `tpch_nation` | 25 | Countries with region references |
|
|
158
|
+
| `tpch_supplier` | 5 | Suppliers with nation references |
|
|
159
|
+
| `tpch_customer` | 10 | Customers with nation references |
|
|
160
|
+
| `tpch_orders` | 15 | Orders with customer references |
|
|
161
|
+
|
|
162
|
+
#### Running TPC-H Tests
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Start PostgreSQL container
|
|
166
|
+
docker-compose up -d
|
|
167
|
+
|
|
168
|
+
# Run TPC-H integration tests
|
|
169
|
+
pytest tests/integration/test_tpch.py -v
|
|
170
|
+
|
|
171
|
+
# Initialize TPC-H data manually (for ad-hoc testing)
|
|
172
|
+
python scripts/init_tpch_data.py \
|
|
173
|
+
--host localhost --port 5432 \
|
|
174
|
+
--username test_user --password test_password \
|
|
175
|
+
--database test --schema public
|
|
176
|
+
|
|
177
|
+
# Drop and recreate TPC-H tables
|
|
178
|
+
python scripts/init_tpch_data.py --drop
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Environment Variables
|
|
182
|
+
|
|
183
|
+
Tests use these default values (matching docker-compose.yml):
|
|
184
|
+
|
|
185
|
+
| Variable | Default | Description |
|
|
186
|
+
|----------|---------|-------------|
|
|
187
|
+
| `POSTGRESQL_HOST` | `localhost` | PostgreSQL server host |
|
|
188
|
+
| `POSTGRESQL_PORT` | `5432` | PostgreSQL server port |
|
|
189
|
+
| `POSTGRESQL_USER` | `test_user` | PostgreSQL username |
|
|
190
|
+
| `POSTGRESQL_PASSWORD` | `test_password` | PostgreSQL password |
|
|
191
|
+
| `POSTGRESQL_DATABASE` | `test` | Database name |
|
|
192
|
+
| `POSTGRESQL_SCHEMA` | `public` | Schema name |
|
|
193
|
+
|
|
194
|
+
## Requirements
|
|
195
|
+
|
|
196
|
+
- Python >= 3.12
|
|
197
|
+
- PostgreSQL >= 12
|
|
198
|
+
- datus-agent >= 0.2.1
|
|
199
|
+
- datus-sqlalchemy >= 0.1.0
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
Apache License 2.0
|
|
204
|
+
|
|
205
|
+
## Related Packages
|
|
206
|
+
|
|
207
|
+
- `datus-sqlalchemy` - SQLAlchemy base connector
|
|
208
|
+
- `datus-mysql` - MySQL adapter
|
|
209
|
+
- `datus-redshift` - Amazon Redshift adapter
|
|
210
|
+
- `datus-snowflake` - Snowflake adapter
|
|
@@ -11,6 +11,15 @@ __all__ = ["PostgreSQLConnector", "PostgreSQLConfig", "register"]
|
|
|
11
11
|
|
|
12
12
|
def register():
|
|
13
13
|
"""Register PostgreSQL connector with Datus registry."""
|
|
14
|
-
from
|
|
14
|
+
from datus_db_core import connector_registry
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
from .handlers import build_postgresql_uri, resolve_postgresql_context
|
|
17
|
+
|
|
18
|
+
connector_registry.register(
|
|
19
|
+
"postgresql",
|
|
20
|
+
PostgreSQLConnector,
|
|
21
|
+
config_class=PostgreSQLConfig,
|
|
22
|
+
capabilities={"database", "schema"},
|
|
23
|
+
uri_builder=build_postgresql_uri,
|
|
24
|
+
context_resolver=resolve_postgresql_context,
|
|
25
|
+
)
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
from typing import Any, Dict, List, Optional, Set, Union, override
|
|
6
6
|
from urllib.parse import quote_plus
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from datus.tools.db_tools.base import list_to_in_str
|
|
10
|
-
from datus.utils.constants import DBType
|
|
11
|
-
from datus.utils.exceptions import DatusException, ErrorCode
|
|
12
|
-
from datus.utils.loggings import get_logger
|
|
8
|
+
from datus_db_core import TABLE_TYPE, DatusDbException, ErrorCode, get_logger, list_to_in_str
|
|
13
9
|
from datus_sqlalchemy import SQLAlchemyConnector
|
|
14
10
|
from pydantic import BaseModel, Field
|
|
15
11
|
|
|
@@ -43,7 +39,7 @@ METADATA_DICT: Dict[TABLE_TYPE, TableMetadataNames] = {
|
|
|
43
39
|
def _get_metadata_config(table_type: TABLE_TYPE) -> TableMetadataNames:
|
|
44
40
|
"""Get metadata configuration for given table type."""
|
|
45
41
|
if table_type not in METADATA_DICT:
|
|
46
|
-
raise
|
|
42
|
+
raise DatusDbException(ErrorCode.COMMON_FIELD_INVALID, f"Invalid table type '{table_type}'")
|
|
47
43
|
return METADATA_DICT[table_type]
|
|
48
44
|
|
|
49
45
|
|
|
@@ -80,7 +76,7 @@ class PostgreSQLConnector(SQLAlchemyConnector):
|
|
|
80
76
|
f"{database}?sslmode={config.sslmode}"
|
|
81
77
|
)
|
|
82
78
|
|
|
83
|
-
super().__init__(connection_string, dialect=
|
|
79
|
+
super().__init__(connection_string, dialect="postgresql", timeout_seconds=config.timeout_seconds)
|
|
84
80
|
self.database_name = database
|
|
85
81
|
self.schema_name = config.schema_name or "public"
|
|
86
82
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Copyright 2025-present DatusAI, Inc.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
# See http://www.apache.org/licenses/LICENSE-2.0 for details.
|
|
4
|
+
|
|
5
|
+
"""URI builder and context resolver for PostgreSQL."""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import Dict, Optional, Tuple, Union
|
|
9
|
+
from urllib.parse import unquote
|
|
10
|
+
|
|
11
|
+
from sqlalchemy.engine.url import URL, make_url
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _clean_str(value: Optional[Union[str, int]]) -> str:
|
|
15
|
+
if value is None:
|
|
16
|
+
return ""
|
|
17
|
+
if isinstance(value, (list, tuple, set)):
|
|
18
|
+
for item in value:
|
|
19
|
+
if item:
|
|
20
|
+
return str(item).strip()
|
|
21
|
+
return ""
|
|
22
|
+
return str(value).strip()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _value_or_none(value: Optional[Union[str, int]]) -> Optional[str]:
|
|
26
|
+
cleaned = _clean_str(value)
|
|
27
|
+
return cleaned or None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _port_or_none(port_value: Optional[Union[str, int]]) -> Optional[int]:
|
|
31
|
+
cleaned = _clean_str(port_value)
|
|
32
|
+
if not cleaned:
|
|
33
|
+
return None
|
|
34
|
+
try:
|
|
35
|
+
return int(cleaned)
|
|
36
|
+
except ValueError:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_schema_from_pg_options(options: str) -> str:
|
|
41
|
+
if not options:
|
|
42
|
+
return ""
|
|
43
|
+
decoded = unquote(options)
|
|
44
|
+
match = re.search(r"search_path\s*=\s*([^ ,]+)", decoded, flags=re.IGNORECASE)
|
|
45
|
+
if not match:
|
|
46
|
+
return ""
|
|
47
|
+
value = match.group(1)
|
|
48
|
+
if "," in value:
|
|
49
|
+
value = value.split(",", 1)[0]
|
|
50
|
+
return value.strip()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def build_postgresql_uri(db_config) -> str:
|
|
54
|
+
sslmode = _value_or_none(getattr(db_config, "sslmode", None))
|
|
55
|
+
return str(
|
|
56
|
+
URL.create(
|
|
57
|
+
drivername="postgresql+psycopg2",
|
|
58
|
+
username=_value_or_none(db_config.username),
|
|
59
|
+
password=_value_or_none(db_config.password),
|
|
60
|
+
host=_value_or_none(db_config.host),
|
|
61
|
+
port=_port_or_none(db_config.port),
|
|
62
|
+
database=_value_or_none(db_config.database),
|
|
63
|
+
query={"sslmode": sslmode} if sslmode else {},
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def resolve_postgresql_context(db_config, uri: str) -> Tuple[str, str, str, str]:
|
|
69
|
+
url = make_url(uri)
|
|
70
|
+
query_params: Dict[str, str] = {k: _clean_str(v) for k, v in url.query.items()}
|
|
71
|
+
database = _clean_str(url.database) or _clean_str(db_config.database) or "postgres"
|
|
72
|
+
config_schema = _clean_str(getattr(db_config, "schema_name", None) or getattr(db_config, "schema", None))
|
|
73
|
+
schema = (
|
|
74
|
+
query_params.get("currentSchema")
|
|
75
|
+
or query_params.get("schema")
|
|
76
|
+
or _extract_schema_from_pg_options(query_params.get("options", ""))
|
|
77
|
+
or config_schema
|
|
78
|
+
or "public"
|
|
79
|
+
)
|
|
80
|
+
return "postgresql", "", database, schema
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "datus-postgresql"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "PostgreSQL database adapter for Datus"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
dependencies = [
|
|
21
|
-
"datus-
|
|
21
|
+
"datus-db-core>=0.1.0",
|
|
22
22
|
"datus-sqlalchemy>=0.1.2",
|
|
23
23
|
"psycopg2-binary>=2.9.11",
|
|
24
24
|
"pydantic>=2.0.0",
|
|
@@ -33,6 +33,7 @@ Issues = "https://github.com/Datus-ai/datus-db-adapters/issues"
|
|
|
33
33
|
postgresql = "datus_postgresql:register"
|
|
34
34
|
|
|
35
35
|
[tool.uv.sources]
|
|
36
|
+
datus-db-core = { workspace = true }
|
|
36
37
|
datus-sqlalchemy = { workspace = true }
|
|
37
38
|
|
|
38
39
|
[build-system]
|