@kiyeonjeon21/datacontext 0.3.1 → 0.3.3
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.
- package/.cursorrules +22 -0
- package/dist/adapters/sqlite.d.ts.map +1 -1
- package/dist/adapters/sqlite.js +13 -0
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/cli/index.js +58 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +10 -1
- package/dist/knowledge/store.js.map +1 -1
- package/docs/KNOWLEDGE_TYPES.md +261 -0
- package/package.json +1 -1
- package/scripts/create-sqlite-testdb.sh +75 -0
- package/scripts/test-databases.sh +324 -0
- package/sqlite:./test-sqlite.db +0 -0
- package/src/adapters/sqlite.ts +16 -0
- package/src/cli/index.ts +57 -16
- package/src/knowledge/store.ts +13 -1
- package/test-sqlite.db +0 -0
- package/cursor-mcp-config.json.example +0 -29
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Knowledge Types Guide
|
|
2
|
+
|
|
3
|
+
> DataContext의 지식 유형 정리 및 사용 가이드
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
DataContext는 4가지 유형의 지식을 관리합니다:
|
|
8
|
+
|
|
9
|
+
| 유형 | 목적 | 자동 적용 | AI 참조 |
|
|
10
|
+
|------|------|:--------:|:-------:|
|
|
11
|
+
| **Table Description** | 테이블/컬럼 설명 | ❌ | ✅ |
|
|
12
|
+
| **Business Glossary** | 용어 → SQL 번역 | ❌ | ✅ |
|
|
13
|
+
| **Business Rule** | 자동 적용 필터 | ✅ | ✅ |
|
|
14
|
+
| **Query Example** | 검증된 SQL 패턴 | ❌ | ✅ |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Table Description (테이블 설명)
|
|
19
|
+
|
|
20
|
+
**목적:** 테이블과 컬럼의 비즈니스 의미 설명
|
|
21
|
+
|
|
22
|
+
**사용 시점:** AI가 스키마를 이해할 때
|
|
23
|
+
|
|
24
|
+
**예시:**
|
|
25
|
+
```yaml
|
|
26
|
+
table: users
|
|
27
|
+
description: "고객 계정 정보를 저장하는 테이블"
|
|
28
|
+
columns:
|
|
29
|
+
- name: status
|
|
30
|
+
description: "계정 상태 (0=비활성, 1=활성, 2=정지)"
|
|
31
|
+
- name: created_at
|
|
32
|
+
description: "계정 생성 일시"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**관리 방법:**
|
|
36
|
+
- `datacontext harvest` - DB 코멘트에서 자동 수집
|
|
37
|
+
- `datacontext describe` - 수동 추가/수정
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Business Glossary (용어 사전) ⭐
|
|
42
|
+
|
|
43
|
+
**목적:** 도메인 용어를 SQL 조건으로 번역
|
|
44
|
+
|
|
45
|
+
**사용 시점:** AI가 자연어 쿼리를 SQL로 변환할 때
|
|
46
|
+
|
|
47
|
+
**핵심 특징:**
|
|
48
|
+
- **선택적 적용** - AI가 컨텍스트에 맞게 판단
|
|
49
|
+
- **번역 도우미** - "활성 사용자"라고 하면 AI가 `status = 1` 사용
|
|
50
|
+
- **동의어 지원** - "active user", "활성화된 사용자" 모두 같은 의미
|
|
51
|
+
|
|
52
|
+
**예시:**
|
|
53
|
+
```yaml
|
|
54
|
+
term: "활성 사용자"
|
|
55
|
+
synonyms: ["active user", "활성화된 사용자"]
|
|
56
|
+
definition: "status 컬럼이 1인 사용자"
|
|
57
|
+
sqlExpression: "status = 1"
|
|
58
|
+
appliesTo:
|
|
59
|
+
tables: ["users"]
|
|
60
|
+
category: status
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**사용 시나리오:**
|
|
64
|
+
```
|
|
65
|
+
사용자: "활성 사용자 목록 보여줘"
|
|
66
|
+
AI: SELECT * FROM users WHERE status = 1; -- Glossary 참조
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**관리 방법:**
|
|
70
|
+
- `datacontext glossary add "활성 사용자" "status가 1인 사용자" --sql "status = 1"`
|
|
71
|
+
- `datacontext glossary generate "활성=status 1, VIP=주문 10건 이상"`
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. Business Rule (비즈니스 룰) ⚠️
|
|
76
|
+
|
|
77
|
+
**목적:** 모든 쿼리에 자동으로 적용되는 필터/제약
|
|
78
|
+
|
|
79
|
+
**사용 시점:** 쿼리 실행 전 시스템이 자동 적용
|
|
80
|
+
|
|
81
|
+
**핵심 특징:**
|
|
82
|
+
- **강제 적용** - 사용자가 명시하지 않아도 항상 추가
|
|
83
|
+
- **보안/무결성** - Soft delete, Tenant isolation 등
|
|
84
|
+
- **우선순위** - 여러 룰이 있으면 priority 순으로 적용
|
|
85
|
+
|
|
86
|
+
**예시:**
|
|
87
|
+
```yaml
|
|
88
|
+
name: "soft_delete_filter"
|
|
89
|
+
description: "삭제된 레코드 제외 (deleted_at IS NULL)"
|
|
90
|
+
condition: "deleted_at IS NULL"
|
|
91
|
+
appliesTo:
|
|
92
|
+
tables: ["users", "orders", "products"]
|
|
93
|
+
isActive: true
|
|
94
|
+
priority: 100
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**사용 시나리오:**
|
|
98
|
+
```
|
|
99
|
+
사용자: "SELECT * FROM users"
|
|
100
|
+
시스템 적용 후: "SELECT * FROM users WHERE deleted_at IS NULL"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**관리 방법:**
|
|
104
|
+
- VS Code: `DataContext: Add Business Rule`
|
|
105
|
+
- CLI: 현재 미구현 (향후 추가)
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 4. Query Example (쿼리 예시)
|
|
110
|
+
|
|
111
|
+
**목적:** 검증된 SQL 패턴 저장
|
|
112
|
+
|
|
113
|
+
**사용 시점:** AI가 유사한 쿼리 생성 시 참조
|
|
114
|
+
|
|
115
|
+
**예시:**
|
|
116
|
+
```yaml
|
|
117
|
+
intent: "최근 30일 주문 총액 조회"
|
|
118
|
+
sql: |
|
|
119
|
+
SELECT
|
|
120
|
+
user_id,
|
|
121
|
+
SUM(total_amount) as total_spent
|
|
122
|
+
FROM orders
|
|
123
|
+
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
|
|
124
|
+
GROUP BY user_id
|
|
125
|
+
tables: ["orders"]
|
|
126
|
+
isVerified: true
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**관리 방법:**
|
|
130
|
+
- VS Code: `DataContext: Add Query Example`
|
|
131
|
+
- 피드백에서 자동 생성 (성공한 쿼리)
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Glossary vs Rule: 언제 무엇을 사용?
|
|
136
|
+
|
|
137
|
+
### Glossary 사용 (용어 번역)
|
|
138
|
+
|
|
139
|
+
✅ 도메인 특화 용어가 있을 때
|
|
140
|
+
✅ 사용자가 자연어로 요청할 때
|
|
141
|
+
✅ 같은 개념에 여러 표현이 있을 때
|
|
142
|
+
|
|
143
|
+
**예시:**
|
|
144
|
+
- "활성 사용자", "VIP 고객", "최근 주문"
|
|
145
|
+
- 마케팅 용어, 비즈니스 지표
|
|
146
|
+
|
|
147
|
+
### Rule 사용 (자동 적용 필터)
|
|
148
|
+
|
|
149
|
+
✅ 항상 적용해야 하는 조건
|
|
150
|
+
✅ 보안/무결성 관련
|
|
151
|
+
✅ 사용자가 잊어버려도 적용되어야 함
|
|
152
|
+
|
|
153
|
+
**예시:**
|
|
154
|
+
- Soft delete 필터 (`deleted_at IS NULL`)
|
|
155
|
+
- Tenant isolation (`tenant_id = ?`)
|
|
156
|
+
- 권한 기반 필터 (`is_public = true OR owner_id = ?`)
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 스케일 대응 전략
|
|
161
|
+
|
|
162
|
+
### 테이블이 많아질 때
|
|
163
|
+
|
|
164
|
+
1. **태그로 그룹화**
|
|
165
|
+
```yaml
|
|
166
|
+
table: user_preferences
|
|
167
|
+
tags: ["user", "settings"]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
2. **스키마별 분리**
|
|
171
|
+
```
|
|
172
|
+
knowledge/
|
|
173
|
+
├── public/ # public schema
|
|
174
|
+
├── analytics/ # analytics schema
|
|
175
|
+
└── admin/ # admin schema
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### DB가 여러 개일 때
|
|
179
|
+
|
|
180
|
+
1. **DB별 Knowledge 분리**
|
|
181
|
+
```
|
|
182
|
+
.datacontext/
|
|
183
|
+
├── users-db/
|
|
184
|
+
│ └── knowledge.json
|
|
185
|
+
├── orders-db/
|
|
186
|
+
│ └── knowledge.json
|
|
187
|
+
└── analytics-db/
|
|
188
|
+
└── knowledge.json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
2. **Cross-DB Glossary**
|
|
192
|
+
```yaml
|
|
193
|
+
term: "고객"
|
|
194
|
+
appliesTo:
|
|
195
|
+
databases:
|
|
196
|
+
- name: users-db
|
|
197
|
+
tables: ["users"]
|
|
198
|
+
- name: orders-db
|
|
199
|
+
tables: ["customers"]
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 용어가 많아질 때
|
|
203
|
+
|
|
204
|
+
1. **카테고리 활용**
|
|
205
|
+
- `status`: 상태 관련 (활성, 비활성, 정지)
|
|
206
|
+
- `time`: 시간 관련 (최근, 이번달, 지난주)
|
|
207
|
+
- `money`: 금액 관련 (매출, 비용, 이익)
|
|
208
|
+
- `entity`: 엔티티 타입 (고객, 상품, 주문)
|
|
209
|
+
- `metric`: 지표 (전환율, 이탈률)
|
|
210
|
+
|
|
211
|
+
2. **검색 및 자동완성**
|
|
212
|
+
```bash
|
|
213
|
+
datacontext glossary search "사용자"
|
|
214
|
+
# → 활성 사용자, 신규 사용자, VIP 사용자, ...
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 권장 워크플로우
|
|
220
|
+
|
|
221
|
+
### 1단계: 기본 설정 (처음 연결 시)
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# DB 메타데이터 수집
|
|
225
|
+
datacontext harvest
|
|
226
|
+
|
|
227
|
+
# 기본 Soft Delete 룰 추가 (해당되는 경우)
|
|
228
|
+
# VS Code: DataContext: Add Business Rule
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 2단계: 도메인 용어 추가 (점진적)
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# 자주 사용하는 용어부터
|
|
235
|
+
datacontext glossary add "활성 사용자" "status가 1인 사용자" --sql "status = 1"
|
|
236
|
+
datacontext glossary add "최근 주문" "30일 이내 주문" --sql "created_at > now() - interval '30 days'"
|
|
237
|
+
|
|
238
|
+
# 또는 AI로 일괄 생성
|
|
239
|
+
datacontext glossary generate "활성=status 1, VIP=order 10건 이상, 신규=created_at 7일 이내"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 3단계: 사용하면서 개선
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# 쿼리 피드백 기록 (자동)
|
|
246
|
+
# 자주 수정되는 패턴 → Rule 또는 Glossary 추가
|
|
247
|
+
|
|
248
|
+
# 성공한 쿼리 → Query Example로 저장
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 요약
|
|
254
|
+
|
|
255
|
+
| 질문 | 답 |
|
|
256
|
+
|------|-----|
|
|
257
|
+
| "AI가 용어를 이해하게 하고 싶다" | → **Glossary** |
|
|
258
|
+
| "모든 쿼리에 조건을 자동 추가하고 싶다" | → **Rule** |
|
|
259
|
+
| "테이블 의미를 설명하고 싶다" | → **Table Description** |
|
|
260
|
+
| "좋은 쿼리 패턴을 저장하고 싶다" | → **Query Example** |
|
|
261
|
+
|
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Create SQLite test database with sample data
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
DB_PATH="${1:-./test-sqlite.db}"
|
|
7
|
+
|
|
8
|
+
echo "Creating SQLite test database at: $DB_PATH"
|
|
9
|
+
|
|
10
|
+
# Remove existing
|
|
11
|
+
rm -f "$DB_PATH"
|
|
12
|
+
|
|
13
|
+
# Create database and tables
|
|
14
|
+
sqlite3 "$DB_PATH" << 'EOF'
|
|
15
|
+
-- Users table
|
|
16
|
+
CREATE TABLE users (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
name TEXT NOT NULL,
|
|
19
|
+
email TEXT UNIQUE NOT NULL,
|
|
20
|
+
status INTEGER DEFAULT 1,
|
|
21
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
22
|
+
deleted_at DATETIME
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
-- Products table
|
|
26
|
+
CREATE TABLE products (
|
|
27
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
28
|
+
name TEXT NOT NULL,
|
|
29
|
+
price REAL NOT NULL,
|
|
30
|
+
inventory INTEGER DEFAULT 0,
|
|
31
|
+
category TEXT,
|
|
32
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- Orders table
|
|
36
|
+
CREATE TABLE orders (
|
|
37
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
38
|
+
user_id INTEGER REFERENCES users(id),
|
|
39
|
+
product_id INTEGER REFERENCES products(id),
|
|
40
|
+
quantity INTEGER NOT NULL,
|
|
41
|
+
total_amount REAL NOT NULL,
|
|
42
|
+
status TEXT DEFAULT 'pending',
|
|
43
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- Insert sample data
|
|
47
|
+
INSERT INTO users (name, email, status) VALUES
|
|
48
|
+
('Alice', 'alice@example.com', 1),
|
|
49
|
+
('Bob', 'bob@example.com', 1),
|
|
50
|
+
('Charlie', 'charlie@example.com', 0),
|
|
51
|
+
('Diana', 'diana@example.com', 2);
|
|
52
|
+
|
|
53
|
+
INSERT INTO products (name, price, inventory, category) VALUES
|
|
54
|
+
('Laptop', 999.99, 50, 'electronics'),
|
|
55
|
+
('Mouse', 29.99, 200, 'electronics'),
|
|
56
|
+
('Keyboard', 79.99, 100, 'electronics'),
|
|
57
|
+
('Desk Chair', 299.99, 30, 'furniture');
|
|
58
|
+
|
|
59
|
+
INSERT INTO orders (user_id, product_id, quantity, total_amount, status) VALUES
|
|
60
|
+
(1, 1, 1, 999.99, 'delivered'),
|
|
61
|
+
(1, 2, 2, 59.98, 'delivered'),
|
|
62
|
+
(2, 3, 1, 79.99, 'shipped'),
|
|
63
|
+
(2, 4, 1, 299.99, 'pending');
|
|
64
|
+
|
|
65
|
+
SELECT 'SQLite test database created!' as result;
|
|
66
|
+
.tables
|
|
67
|
+
EOF
|
|
68
|
+
|
|
69
|
+
echo ""
|
|
70
|
+
echo "Database created: $DB_PATH"
|
|
71
|
+
echo ""
|
|
72
|
+
echo "To start DataContext server:"
|
|
73
|
+
echo " npx @kiyeonjeon21/datacontext serve sqlite:$DB_PATH --port 3000"
|
|
74
|
+
echo ""
|
|
75
|
+
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Test Databases Setup Script
|
|
3
|
+
# Uses Podman to run PostgreSQL, MySQL, and MariaDB for testing
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# Colors
|
|
8
|
+
RED='\033[0;31m'
|
|
9
|
+
GREEN='\033[0;32m'
|
|
10
|
+
YELLOW='\033[1;33m'
|
|
11
|
+
NC='\033[0m' # No Color
|
|
12
|
+
|
|
13
|
+
echo -e "${GREEN}=== DataContext Database Test Environment ===${NC}"
|
|
14
|
+
|
|
15
|
+
# Check if podman is available
|
|
16
|
+
if ! command -v podman &> /dev/null; then
|
|
17
|
+
echo -e "${RED}Error: podman is not installed${NC}"
|
|
18
|
+
echo "Install with: brew install podman"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Configuration
|
|
23
|
+
POSTGRES_PORT=5433
|
|
24
|
+
MYSQL_PORT=3307
|
|
25
|
+
MARIADB_PORT=3308
|
|
26
|
+
NETWORK_NAME="datacontext-test"
|
|
27
|
+
|
|
28
|
+
# Create network if not exists
|
|
29
|
+
podman network exists $NETWORK_NAME 2>/dev/null || podman network create $NETWORK_NAME
|
|
30
|
+
|
|
31
|
+
# Function to start PostgreSQL
|
|
32
|
+
start_postgres() {
|
|
33
|
+
echo -e "\n${YELLOW}Starting PostgreSQL...${NC}"
|
|
34
|
+
|
|
35
|
+
# Stop if running
|
|
36
|
+
podman rm -f datacontext-postgres 2>/dev/null || true
|
|
37
|
+
|
|
38
|
+
podman run -d \
|
|
39
|
+
--name datacontext-postgres \
|
|
40
|
+
--network $NETWORK_NAME \
|
|
41
|
+
-e POSTGRES_USER=postgres \
|
|
42
|
+
-e POSTGRES_PASSWORD=postgres \
|
|
43
|
+
-e POSTGRES_DB=datacontext_test \
|
|
44
|
+
-p $POSTGRES_PORT:5432 \
|
|
45
|
+
postgres:15-alpine
|
|
46
|
+
|
|
47
|
+
echo -e "${GREEN}PostgreSQL started on port $POSTGRES_PORT${NC}"
|
|
48
|
+
echo "Connection: postgres://postgres:postgres@localhost:$POSTGRES_PORT/datacontext_test"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Function to start MySQL
|
|
52
|
+
start_mysql() {
|
|
53
|
+
echo -e "\n${YELLOW}Starting MySQL...${NC}"
|
|
54
|
+
|
|
55
|
+
# Stop if running
|
|
56
|
+
podman rm -f datacontext-mysql 2>/dev/null || true
|
|
57
|
+
|
|
58
|
+
podman run -d \
|
|
59
|
+
--name datacontext-mysql \
|
|
60
|
+
--network $NETWORK_NAME \
|
|
61
|
+
-e MYSQL_ROOT_PASSWORD=mysql \
|
|
62
|
+
-e MYSQL_DATABASE=datacontext_test \
|
|
63
|
+
-e MYSQL_USER=mysql \
|
|
64
|
+
-e MYSQL_PASSWORD=mysql \
|
|
65
|
+
-p $MYSQL_PORT:3306 \
|
|
66
|
+
mysql:8.0
|
|
67
|
+
|
|
68
|
+
echo -e "${GREEN}MySQL started on port $MYSQL_PORT${NC}"
|
|
69
|
+
echo "Connection: mysql://mysql:mysql@localhost:$MYSQL_PORT/datacontext_test"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Function to start MariaDB
|
|
73
|
+
start_mariadb() {
|
|
74
|
+
echo -e "\n${YELLOW}Starting MariaDB...${NC}"
|
|
75
|
+
|
|
76
|
+
# Stop if running
|
|
77
|
+
podman rm -f datacontext-mariadb 2>/dev/null || true
|
|
78
|
+
|
|
79
|
+
podman run -d \
|
|
80
|
+
--name datacontext-mariadb \
|
|
81
|
+
--network $NETWORK_NAME \
|
|
82
|
+
-e MARIADB_ROOT_PASSWORD=mariadb \
|
|
83
|
+
-e MARIADB_DATABASE=datacontext_test \
|
|
84
|
+
-e MARIADB_USER=mariadb \
|
|
85
|
+
-e MARIADB_PASSWORD=mariadb \
|
|
86
|
+
-p $MARIADB_PORT:3306 \
|
|
87
|
+
mariadb:10.11
|
|
88
|
+
|
|
89
|
+
echo -e "${GREEN}MariaDB started on port $MARIADB_PORT${NC}"
|
|
90
|
+
echo "Connection: mysql://mariadb:mariadb@localhost:$MARIADB_PORT/datacontext_test"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Function to wait for DB
|
|
94
|
+
wait_for_db() {
|
|
95
|
+
local name=$1
|
|
96
|
+
local port=$2
|
|
97
|
+
local max_attempts=30
|
|
98
|
+
local attempt=0
|
|
99
|
+
|
|
100
|
+
echo -e "${YELLOW}Waiting for $name to be ready...${NC}"
|
|
101
|
+
|
|
102
|
+
while [ $attempt -lt $max_attempts ]; do
|
|
103
|
+
if podman exec $name echo "SELECT 1" 2>/dev/null; then
|
|
104
|
+
echo -e "${GREEN}$name is ready!${NC}"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
attempt=$((attempt + 1))
|
|
108
|
+
sleep 1
|
|
109
|
+
done
|
|
110
|
+
|
|
111
|
+
echo -e "${RED}$name failed to start${NC}"
|
|
112
|
+
return 1
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Function to create test data
|
|
116
|
+
create_test_data() {
|
|
117
|
+
local db_type=$1
|
|
118
|
+
local container=$2
|
|
119
|
+
|
|
120
|
+
echo -e "\n${YELLOW}Creating test data for $db_type...${NC}"
|
|
121
|
+
|
|
122
|
+
case $db_type in
|
|
123
|
+
postgres)
|
|
124
|
+
podman exec -i $container psql -U postgres -d datacontext_test << 'EOF'
|
|
125
|
+
-- Users table
|
|
126
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
127
|
+
id SERIAL PRIMARY KEY,
|
|
128
|
+
name VARCHAR(100) NOT NULL,
|
|
129
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
130
|
+
status INTEGER DEFAULT 1,
|
|
131
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
132
|
+
deleted_at TIMESTAMP
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
COMMENT ON TABLE users IS 'User accounts table';
|
|
136
|
+
COMMENT ON COLUMN users.status IS 'Account status: 0=inactive, 1=active, 2=suspended';
|
|
137
|
+
|
|
138
|
+
-- Products table
|
|
139
|
+
CREATE TABLE IF NOT EXISTS products (
|
|
140
|
+
id SERIAL PRIMARY KEY,
|
|
141
|
+
name VARCHAR(200) NOT NULL,
|
|
142
|
+
price DECIMAL(10,2) NOT NULL,
|
|
143
|
+
inventory INTEGER DEFAULT 0,
|
|
144
|
+
category VARCHAR(50),
|
|
145
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
COMMENT ON TABLE products IS 'Product catalog';
|
|
149
|
+
|
|
150
|
+
-- Orders table
|
|
151
|
+
CREATE TABLE IF NOT EXISTS orders (
|
|
152
|
+
id SERIAL PRIMARY KEY,
|
|
153
|
+
user_id INTEGER REFERENCES users(id),
|
|
154
|
+
product_id INTEGER REFERENCES products(id),
|
|
155
|
+
quantity INTEGER NOT NULL,
|
|
156
|
+
total_amount DECIMAL(10,2) NOT NULL,
|
|
157
|
+
status VARCHAR(20) DEFAULT 'pending',
|
|
158
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
COMMENT ON TABLE orders IS 'Customer orders';
|
|
162
|
+
COMMENT ON COLUMN orders.status IS 'Order status: pending, paid, shipped, delivered, cancelled';
|
|
163
|
+
|
|
164
|
+
-- Insert sample data
|
|
165
|
+
INSERT INTO users (name, email, status) VALUES
|
|
166
|
+
('Alice', 'alice@example.com', 1),
|
|
167
|
+
('Bob', 'bob@example.com', 1),
|
|
168
|
+
('Charlie', 'charlie@example.com', 0),
|
|
169
|
+
('Diana', 'diana@example.com', 2)
|
|
170
|
+
ON CONFLICT DO NOTHING;
|
|
171
|
+
|
|
172
|
+
INSERT INTO products (name, price, inventory, category) VALUES
|
|
173
|
+
('Laptop', 999.99, 50, 'electronics'),
|
|
174
|
+
('Mouse', 29.99, 200, 'electronics'),
|
|
175
|
+
('Keyboard', 79.99, 100, 'electronics'),
|
|
176
|
+
('Desk Chair', 299.99, 30, 'furniture')
|
|
177
|
+
ON CONFLICT DO NOTHING;
|
|
178
|
+
|
|
179
|
+
INSERT INTO orders (user_id, product_id, quantity, total_amount, status) VALUES
|
|
180
|
+
(1, 1, 1, 999.99, 'delivered'),
|
|
181
|
+
(1, 2, 2, 59.98, 'delivered'),
|
|
182
|
+
(2, 3, 1, 79.99, 'shipped'),
|
|
183
|
+
(2, 4, 1, 299.99, 'pending')
|
|
184
|
+
ON CONFLICT DO NOTHING;
|
|
185
|
+
|
|
186
|
+
SELECT 'PostgreSQL test data created!' as result;
|
|
187
|
+
EOF
|
|
188
|
+
;;
|
|
189
|
+
|
|
190
|
+
mysql)
|
|
191
|
+
sleep 10 # MySQL takes longer to start
|
|
192
|
+
podman exec -i $container mysql -umysql -pmysql datacontext_test << 'EOF'
|
|
193
|
+
-- Users table
|
|
194
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
195
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
196
|
+
name VARCHAR(100) NOT NULL,
|
|
197
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
198
|
+
status INT DEFAULT 1,
|
|
199
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
200
|
+
deleted_at TIMESTAMP NULL
|
|
201
|
+
) COMMENT='User accounts table';
|
|
202
|
+
|
|
203
|
+
-- Products table
|
|
204
|
+
CREATE TABLE IF NOT EXISTS products (
|
|
205
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
206
|
+
name VARCHAR(200) NOT NULL,
|
|
207
|
+
price DECIMAL(10,2) NOT NULL,
|
|
208
|
+
inventory INT DEFAULT 0,
|
|
209
|
+
category VARCHAR(50),
|
|
210
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
211
|
+
) COMMENT='Product catalog';
|
|
212
|
+
|
|
213
|
+
-- Orders table
|
|
214
|
+
CREATE TABLE IF NOT EXISTS orders (
|
|
215
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
216
|
+
user_id INT,
|
|
217
|
+
product_id INT,
|
|
218
|
+
quantity INT NOT NULL,
|
|
219
|
+
total_amount DECIMAL(10,2) NOT NULL,
|
|
220
|
+
status VARCHAR(20) DEFAULT 'pending',
|
|
221
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
222
|
+
FOREIGN KEY (user_id) REFERENCES users(id),
|
|
223
|
+
FOREIGN KEY (product_id) REFERENCES products(id)
|
|
224
|
+
) COMMENT='Customer orders';
|
|
225
|
+
|
|
226
|
+
-- Insert sample data
|
|
227
|
+
INSERT IGNORE INTO users (name, email, status) VALUES
|
|
228
|
+
('Alice', 'alice@example.com', 1),
|
|
229
|
+
('Bob', 'bob@example.com', 1),
|
|
230
|
+
('Charlie', 'charlie@example.com', 0),
|
|
231
|
+
('Diana', 'diana@example.com', 2);
|
|
232
|
+
|
|
233
|
+
INSERT IGNORE INTO products (name, price, inventory, category) VALUES
|
|
234
|
+
('Laptop', 999.99, 50, 'electronics'),
|
|
235
|
+
('Mouse', 29.99, 200, 'electronics'),
|
|
236
|
+
('Keyboard', 79.99, 100, 'electronics'),
|
|
237
|
+
('Desk Chair', 299.99, 30, 'furniture');
|
|
238
|
+
|
|
239
|
+
INSERT IGNORE INTO orders (user_id, product_id, quantity, total_amount, status) VALUES
|
|
240
|
+
(1, 1, 1, 999.99, 'delivered'),
|
|
241
|
+
(1, 2, 2, 59.98, 'delivered'),
|
|
242
|
+
(2, 3, 1, 79.99, 'shipped'),
|
|
243
|
+
(2, 4, 1, 299.99, 'pending');
|
|
244
|
+
|
|
245
|
+
SELECT 'MySQL test data created!' as result;
|
|
246
|
+
EOF
|
|
247
|
+
;;
|
|
248
|
+
esac
|
|
249
|
+
|
|
250
|
+
echo -e "${GREEN}Test data created for $db_type${NC}"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Function to stop all
|
|
254
|
+
stop_all() {
|
|
255
|
+
echo -e "\n${YELLOW}Stopping all test databases...${NC}"
|
|
256
|
+
podman rm -f datacontext-postgres datacontext-mysql datacontext-mariadb 2>/dev/null || true
|
|
257
|
+
echo -e "${GREEN}All databases stopped${NC}"
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
# Function to show status
|
|
261
|
+
show_status() {
|
|
262
|
+
echo -e "\n${GREEN}=== Database Status ===${NC}"
|
|
263
|
+
podman ps --filter "name=datacontext-" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
# Function to show connection strings
|
|
267
|
+
show_connections() {
|
|
268
|
+
echo -e "\n${GREEN}=== Connection Strings ===${NC}"
|
|
269
|
+
echo ""
|
|
270
|
+
echo -e "${YELLOW}PostgreSQL:${NC}"
|
|
271
|
+
echo " postgres://postgres:postgres@localhost:$POSTGRES_PORT/datacontext_test"
|
|
272
|
+
echo ""
|
|
273
|
+
echo -e "${YELLOW}MySQL:${NC}"
|
|
274
|
+
echo " mysql://mysql:mysql@localhost:$MYSQL_PORT/datacontext_test"
|
|
275
|
+
echo ""
|
|
276
|
+
echo -e "${YELLOW}MariaDB:${NC}"
|
|
277
|
+
echo " mysql://mariadb:mariadb@localhost:$MARIADB_PORT/datacontext_test"
|
|
278
|
+
echo ""
|
|
279
|
+
echo -e "${GREEN}=== DataContext Commands ===${NC}"
|
|
280
|
+
echo ""
|
|
281
|
+
echo "# Start servers"
|
|
282
|
+
echo "npx @kiyeonjeon21/datacontext serve postgres://postgres:postgres@localhost:$POSTGRES_PORT/datacontext_test --port 3000"
|
|
283
|
+
echo "npx @kiyeonjeon21/datacontext serve mysql://mysql:mysql@localhost:$MYSQL_PORT/datacontext_test --port 3001"
|
|
284
|
+
echo ""
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# Parse arguments
|
|
288
|
+
case "${1:-start}" in
|
|
289
|
+
start)
|
|
290
|
+
start_postgres
|
|
291
|
+
start_mysql
|
|
292
|
+
sleep 15 # Wait for DBs to be ready
|
|
293
|
+
create_test_data postgres datacontext-postgres
|
|
294
|
+
create_test_data mysql datacontext-mysql
|
|
295
|
+
show_status
|
|
296
|
+
show_connections
|
|
297
|
+
;;
|
|
298
|
+
postgres)
|
|
299
|
+
start_postgres
|
|
300
|
+
sleep 5
|
|
301
|
+
create_test_data postgres datacontext-postgres
|
|
302
|
+
show_connections
|
|
303
|
+
;;
|
|
304
|
+
mysql)
|
|
305
|
+
start_mysql
|
|
306
|
+
sleep 15
|
|
307
|
+
create_test_data mysql datacontext-mysql
|
|
308
|
+
show_connections
|
|
309
|
+
;;
|
|
310
|
+
stop)
|
|
311
|
+
stop_all
|
|
312
|
+
;;
|
|
313
|
+
status)
|
|
314
|
+
show_status
|
|
315
|
+
show_connections
|
|
316
|
+
;;
|
|
317
|
+
*)
|
|
318
|
+
echo "Usage: $0 {start|postgres|mysql|stop|status}"
|
|
319
|
+
exit 1
|
|
320
|
+
;;
|
|
321
|
+
esac
|
|
322
|
+
|
|
323
|
+
echo -e "\n${GREEN}Done!${NC}"
|
|
324
|
+
|
|
File without changes
|
package/src/adapters/sqlite.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import Database from 'better-sqlite3';
|
|
14
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
15
|
+
import { dirname, resolve } from 'path';
|
|
14
16
|
import type { DatabaseAdapter, QueryResult, ConnectionConfig } from './base.js';
|
|
15
17
|
import type { SchemaInfo, TableInfo, ColumnInfo, IndexInfo, ForeignKeyInfo } from '../schema/types.js';
|
|
16
18
|
import { hashSchema } from '../knowledge/schema-hash.js';
|
|
@@ -41,6 +43,20 @@ export class SQLiteAdapter implements DatabaseAdapter {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
try {
|
|
46
|
+
// Resolve relative paths to absolute
|
|
47
|
+
let resolvedPath = this.filePath;
|
|
48
|
+
if (this.filePath !== ':memory:') {
|
|
49
|
+
resolvedPath = resolve(process.cwd(), this.filePath);
|
|
50
|
+
|
|
51
|
+
// Ensure parent directory exists
|
|
52
|
+
const dir = dirname(resolvedPath);
|
|
53
|
+
if (!existsSync(dir)) {
|
|
54
|
+
mkdirSync(dir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.filePath = resolvedPath;
|
|
58
|
+
}
|
|
59
|
+
|
|
44
60
|
this.db = new Database(this.filePath, { readonly: false });
|
|
45
61
|
// Enable foreign keys
|
|
46
62
|
this.db.pragma('foreign_keys = ON');
|