Metadata-Version: 2.4
Name: pxa-db-connector
Version: 0.1.0
Summary: PXA DB 커넥터 (ODBC 기본/커넥션풀/트랜잭션/ORM/SQL 혼용) — 단독 사용 가능
Author: Platform Team
License-Expression: Apache-2.0
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: SQLAlchemy<2.1,>=2.0
Provides-Extra: odbc
Requires-Dist: pyodbc>=5.0; extra == "odbc"
Provides-Extra: mariadb
Requires-Dist: PyMySQL>=1.1; extra == "mariadb"
Provides-Extra: postgresql
Requires-Dist: psycopg[binary]>=3.1; extra == "postgresql"
Provides-Extra: common
Requires-Dist: pxa-common>=0.1.0; extra == "common"
Requires-Dist: fastapi==0.128.*; extra == "common"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pyodbc>=5.0; extra == "dev"
Requires-Dist: PyMySQL>=1.1; extra == "dev"
Requires-Dist: pxa-common>=0.1.0; extra == "dev"
Requires-Dist: fastapi==0.128.*; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Dynamic: license-file

# pxa-db-connector

PXA **DB 관련 일체** 패키지. **ODBC 연결** · 커넥션 풀 · 트랜잭션 · ORM(Base/Repository) · SQL 혼용을 제공합니다.

> **단독 사용 가능** — 다른 pxa 패키지에 의존하지 않습니다. 의존성은 `SQLAlchemy` 뿐입니다.
> 설정 관리는 **개발자의 몫**이고, 이 패키지는 `DbSettings` 만 받아서 동작합니다.

개발자는 커넥션·풀·트랜잭션을 **몰라도 됩니다.**

---

## 설치

```bash
pip install "pxa-db-connector[odbc]"          # ODBC (기본 연결 방식)
pip install "pxa-db-connector[mariadb]"       # connector="native" 로 쓸 때
pip install "pxa-db-connector[postgresql]"    # PostgreSQL (native 만 지원)
pip install "pxa-db-connector[common]"        # pxa-common 통합 어댑터
```

ODBC 는 OS 에 드라이버가 설치되어 있어야 합니다(예: *MariaDB ODBC 3.2 Driver*, *ODBC Driver 17 for SQL Server*).

## 1. 설정 (`DbSettings`) — ODBC 기본, dialect 만 바꾸면 DB 교체

설정 파일을 읽지 않습니다. 개발자가 자신의 설정 체계에서 값을 꺼내 넣어 주면 됩니다.

```python
from pxa_db_connector import DbSettings, init_engine

cfg = my_yaml_loader("config.yaml")            # 개발자의 config 구현

init_engine(DbSettings(
    dialect="mariadb",                         # mariadb | mysql | mssql | postgresql | sqlite
    connector="odbc",                          # odbc(기본) | native
    host=cfg["db"]["host"],
    user=cfg["db"]["user"],
    password=cfg["db"]["password"],            # @ : / 등 특수문자 자동 URL 인코딩
    database=cfg["db"]["name"],
    pool_size=10,                              # 커넥션 풀 (개발자는 몰라도 됨)
    sql_dirs=["sql"],                          # .sql 디렉토리 자동 등록
))
```

| dialect | `connector="odbc"` (기본) | `connector="native"` |
|---|---|---|
| `mariadb` / `mysql` | `+pyodbc` | `+pymysql` |
| `mssql` | `+pyodbc` | – |
| `postgresql` | – | `+psycopg` |
| `sqlite` | (해당 없음 — 파일 경로로 접속) | |

ODBC 드라이버명은 dialect 별 기본값이 들어가며 `odbc_driver` 로 바꿉니다. 추가 옵션은 `odbc_params={"charset": "utf8mb4"}` 처럼 넘깁니다. 지원하지 않는 조합(예: `postgresql`+`odbc`)은 대안을 알려주는 `ValueError` 로 즉시 걸러집니다.

비밀번호는 `repr()` 과 로그(`masked_url`)에 남지 않습니다.

## 2. 커넥션풀 · 트랜잭션

```python
from pxa_db_connector import init_engine, session_scope, dispose_engine

init_engine(settings)              # 앱 시작 시 1회 (풀 생성)

with session_scope() as db:        # 트랜잭션 시작
    ...                            # 예외 발생 시 자동 rollback
                                   # 정상 종료 시 commit, 커넥션은 풀로 반환

dispose_engine()                   # 앱 종료 시
```

## 3. ORM — 단일 테이블 CRUD + 저장 전 검증

```python
from pxa_db_connector import Base, Repository, ValidationError, session_scope

class User(Base):
    __tablename__ = "users"
    __required__ = ("username", "email")      # 필수값 선언

    def validate(self) -> None:                # 추가 정합성 규칙
        if "@" not in self.email:
            raise ValidationError("이메일 형식 오류")

with session_scope() as db:
    repo = Repository(User, db)
    user = repo.get_or_404(1)                             # 없으면 pxa-20004
    repo.create(User(username="bob", email="b@x.com"))    # 저장 전 is_valid() 자동 검증
```

| 상황 | 예외 | code |
|---|---|---|
| 필수값 누락 | `RequiredFieldError` | `pxa-20002` |
| 정합성 위반 | `ValidationError` | `pxa-20001` |
| 없는 데이터 | `NotFoundError` | `pxa-20004` |
| 엔진/트랜잭션 오류 | `DbError` | `pxa-20006` |

모두 `DbConnectorError` 를 상속하며 `.code` / `.message` 를 가집니다.

## 4. SQL 혼용 — 복잡한 쿼리는 .sql 파일로 분리

### 4-1. 파사드 — 세션/트랜잭션 없이 (기본 사용법)

```python
from pxa_db_connector import register_sql_dirs, query, query_one, execute

register_sql_dirs("sql")                    # 또는 DbSettings(sql_dirs=["sql"])

rows = query("user_search", keyword="kim", limit=10, offset=0)
user = query_one("find_user", user_id=7)
n    = execute("deactivate_user", user_id=7)   # 영향받은 행 수
```

검색 순서는 **인스턴스 지정 → 등록된 앱 디렉토리 → 패키지 동봉** 이라, 같은 이름이면 앱 SQL 이 기본 SQL 을 덮어씁니다.

> `query("...", name="alice")` 처럼 **`name` 바인드 파라미터도 그대로 쓸 수 있습니다** (첫 인자가 positional-only).

### 4-2. 여러 SQL 을 한 트랜잭션으로 묶기

```python
from pxa_db_connector import session_scope, SqlRepository

with session_scope() as db:
    sql = SqlRepository(db)
    sql.execute("withdraw", account_id=1, amount=100)
    sql.execute("deposit",  account_id=2, amount=100)
    # 중간에 예외가 나면 둘 다 rollback
```

## 5. pxa-common 과 함께 쓰기 (선택)

어댑터를 **명시적으로 한 줄** 호출하면 DB 예외가 표준 envelope(`ApiResponse`)로 변환됩니다.

```python
from pxa_db_connector.integrations.pxa_common import use_pxa_common

use_pxa_common(app)      # DbConnectorError -> {success, code, message, result}, HTTP 200
```

## 6. 테스트 & 배포

pytest 는 **외부 DB 없이** sqlite 로, 다른 pxa 패키지 없이 통과합니다.

```bash
pip install -e ".[dev]"
pytest
```

실 DB(MariaDB) ODBC 연동은 pytest 와 분리된 검증 스크립트로 확인합니다. 커넥션풀 → 트랜잭션 → ORM CRUD → 저장 전 검증 → SQL 파일 실행까지 한 번에 점검합니다.

```bash
python scripts/verify_mariadb_odbc.py
# 접속정보 변경: PXA_VERIFY_HOST / _PORT / _USER / _PASSWORD / _DATABASE
```

```bash
python -m build && twine upload -r nexus dist/*
```
