sqlym 0.1.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.
- sqlym-0.1.0/.claude/settings.json +5 -0
- sqlym-0.1.0/.devcontainer/devcontainer.json +63 -0
- sqlym-0.1.0/.github/workflows/ci.yml +124 -0
- sqlym-0.1.0/.gitignore +134 -0
- sqlym-0.1.0/.pre-commit-config.yaml +11 -0
- sqlym-0.1.0/.python-version +1 -0
- sqlym-0.1.0/.vscode/settings.json +3 -0
- sqlym-0.1.0/CLAUDE.md +80 -0
- sqlym-0.1.0/LICENSE +21 -0
- sqlym-0.1.0/Makefile +48 -0
- sqlym-0.1.0/PKG-INFO +260 -0
- sqlym-0.1.0/README.ja.md +232 -0
- sqlym-0.1.0/README.md +233 -0
- sqlym-0.1.0/SQL_SYNTAX.ja.md +461 -0
- sqlym-0.1.0/SQL_SYNTAX.md +467 -0
- sqlym-0.1.0/docker-compose.yml +43 -0
- sqlym-0.1.0/docs/DESIGN.md +747 -0
- sqlym-0.1.0/docs/SPEC.md +350 -0
- sqlym-0.1.0/docs/TASK.M1.md +304 -0
- sqlym-0.1.0/pyproject.toml +113 -0
- sqlym-0.1.0/src/sqlym/__init__.py +28 -0
- sqlym-0.1.0/src/sqlym/_parse.py +36 -0
- sqlym-0.1.0/src/sqlym/config.py +7 -0
- sqlym-0.1.0/src/sqlym/dialect.py +81 -0
- sqlym-0.1.0/src/sqlym/escape_utils.py +48 -0
- sqlym-0.1.0/src/sqlym/exceptions.py +17 -0
- sqlym-0.1.0/src/sqlym/loader.py +72 -0
- sqlym-0.1.0/src/sqlym/mapper/__init__.py +7 -0
- sqlym-0.1.0/src/sqlym/mapper/column.py +40 -0
- sqlym-0.1.0/src/sqlym/mapper/dataclass.py +97 -0
- sqlym-0.1.0/src/sqlym/mapper/factory.py +50 -0
- sqlym-0.1.0/src/sqlym/mapper/manual.py +21 -0
- sqlym-0.1.0/src/sqlym/mapper/protocol.py +20 -0
- sqlym-0.1.0/src/sqlym/mapper/pydantic.py +23 -0
- sqlym-0.1.0/src/sqlym/parser/__init__.py +0 -0
- sqlym-0.1.0/src/sqlym/parser/line_unit.py +36 -0
- sqlym-0.1.0/src/sqlym/parser/tokenizer.py +117 -0
- sqlym-0.1.0/src/sqlym/parser/twoway.py +516 -0
- sqlym-0.1.0/tests/__init__.py +0 -0
- sqlym-0.1.0/tests/conftest.py +159 -0
- sqlym-0.1.0/tests/integration/test_mysql.py +422 -0
- sqlym-0.1.0/tests/integration/test_oracle.py +534 -0
- sqlym-0.1.0/tests/integration/test_postgresql.py +424 -0
- sqlym-0.1.0/tests/integration/test_sqlite.py +438 -0
- sqlym-0.1.0/tests/test_column_entity.py +133 -0
- sqlym-0.1.0/tests/test_create_mapper.py +158 -0
- sqlym-0.1.0/tests/test_dataclass_mapper.py +295 -0
- sqlym-0.1.0/tests/test_dialect.py +167 -0
- sqlym-0.1.0/tests/test_exceptions.py +49 -0
- sqlym-0.1.0/tests/test_line_unit.py +95 -0
- sqlym-0.1.0/tests/test_mapper_protocol.py +153 -0
- sqlym-0.1.0/tests/test_public_api.py +175 -0
- sqlym-0.1.0/tests/test_pydantic_mapper.py +128 -0
- sqlym-0.1.0/tests/test_sql_loader.py +217 -0
- sqlym-0.1.0/tests/test_tokenizer.py +172 -0
- sqlym-0.1.0/tests/test_twoway_clean_sql.py +149 -0
- sqlym-0.1.0/tests/test_twoway_in_clause.py +252 -0
- sqlym-0.1.0/tests/test_twoway_param_substitution.py +125 -0
- sqlym-0.1.0/tests/test_twoway_parse_lines.py +173 -0
- sqlym-0.1.0/tests/test_twoway_placeholder.py +282 -0
- sqlym-0.1.0/tests/test_twoway_removal.py +201 -0
- sqlym-0.1.0/uv.lock +923 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sqly",
|
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
|
|
4
|
+
"containerEnv": {
|
|
5
|
+
"PYTHONUNBUFFERED": "1",
|
|
6
|
+
"PYTHONDONTWRITEBYTECODE": "1",
|
|
7
|
+
"UV_CACHE_DIR": "/home/vscode/.cache/uv",
|
|
8
|
+
"UV_LINK_MODE": "copy",
|
|
9
|
+
"UV_PROJECT_ENVIRONMENT": "/home/vscode/.venv",
|
|
10
|
+
"UV_COMPILE_BYTECODE": "1",
|
|
11
|
+
"CLAUDE_CONFIG_DIR": "/home/vscode/.claude",
|
|
12
|
+
"SQLY_TEST_POSTGRESQL_URL": "host=localhost port=5432 dbname=sqly_test user=sqly password=sqly_test_pass",
|
|
13
|
+
"SQLY_TEST_MYSQL_URL": "mysql://sqly:sqly_test_pass@localhost:3306/sqly_test",
|
|
14
|
+
"SQLY_TEST_ORACLE_DSN": "localhost:1521/XEPDB1"
|
|
15
|
+
},
|
|
16
|
+
"features": {
|
|
17
|
+
"ghcr.io/devcontainers/features/github-cli:1": {},
|
|
18
|
+
"ghcr.io/devcontainers/features/common-utils:2": {
|
|
19
|
+
"configureZshAsDefaultShell": true
|
|
20
|
+
},
|
|
21
|
+
"ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {
|
|
22
|
+
"packages": "curl,wget,git,jq,ca-certificates,build-essential,ripgrep,fd-find"
|
|
23
|
+
},
|
|
24
|
+
"ghcr.io/va-h/devcontainers-features/uv:1": {
|
|
25
|
+
"shellAutocompletion": true
|
|
26
|
+
},
|
|
27
|
+
"ghcr.io/devcontainers/features/node:1": {},
|
|
28
|
+
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
|
|
29
|
+
},
|
|
30
|
+
"runArgs": [
|
|
31
|
+
"--init",
|
|
32
|
+
"--rm"
|
|
33
|
+
],
|
|
34
|
+
"hostRequirements": {
|
|
35
|
+
"gpu": "optional"
|
|
36
|
+
},
|
|
37
|
+
"customizations": {
|
|
38
|
+
"vscode": {
|
|
39
|
+
"settings": {
|
|
40
|
+
"python.defaultInterpreterPath": "/home/vscode/.venv/bin/python",
|
|
41
|
+
"python.testing.pytestEnabled": true
|
|
42
|
+
},
|
|
43
|
+
"extensions": [
|
|
44
|
+
"ms-python.python",
|
|
45
|
+
"charliermarsh.ruff",
|
|
46
|
+
"eamodio.gitlens",
|
|
47
|
+
"tamasfe.even-better-toml",
|
|
48
|
+
"yzhang.markdown-all-in-one"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"remoteUser": "vscode",
|
|
53
|
+
"containerUser": "vscode",
|
|
54
|
+
"forwardPorts": [5432, 3306, 1521],
|
|
55
|
+
"initializeCommand": "mkdir -p ${localEnv:HOME}/.claude && touch ${localEnv:HOME}/.claude/CLAUDE.md && docker compose up -d --wait postgresql mysql oracle",
|
|
56
|
+
"mounts": [
|
|
57
|
+
"source=shell_history-${devcontainerId},target=/shell_history,type=volume",
|
|
58
|
+
"source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind",
|
|
59
|
+
"source=${localEnv:HOME}/.claude/CLAUDE.md,target=/home/vscode/.claude/CLAUDE.md,type=bind"
|
|
60
|
+
],
|
|
61
|
+
"postCreateCommand": "uv sync --dev",
|
|
62
|
+
"postStartCommand": "uv run pre-commit install"
|
|
63
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.13"
|
|
16
|
+
- uses: astral-sh/setup-uv@v5
|
|
17
|
+
- run: uv sync --dev
|
|
18
|
+
- run: uv run ruff check src/ tests/
|
|
19
|
+
- run: uv run ruff format --check src/ tests/
|
|
20
|
+
|
|
21
|
+
test-sqlite:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
strategy:
|
|
24
|
+
matrix:
|
|
25
|
+
python-version: ["3.10", "3.13"]
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
- uses: astral-sh/setup-uv@v5
|
|
32
|
+
- run: uv sync --dev
|
|
33
|
+
- run: uv run pytest -m "not postgresql and not mysql and not oracle" -v
|
|
34
|
+
|
|
35
|
+
test-postgresql:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
matrix:
|
|
39
|
+
python-version: ["3.10", "3.13"]
|
|
40
|
+
services:
|
|
41
|
+
postgres:
|
|
42
|
+
image: postgres:16-alpine
|
|
43
|
+
env:
|
|
44
|
+
POSTGRES_USER: sqly
|
|
45
|
+
POSTGRES_PASSWORD: sqly_test_pass
|
|
46
|
+
POSTGRES_DB: sqly_test
|
|
47
|
+
ports:
|
|
48
|
+
- 5432:5432
|
|
49
|
+
options: >-
|
|
50
|
+
--health-cmd "pg_isready -U sqly -d sqly_test"
|
|
51
|
+
--health-interval 5s
|
|
52
|
+
--health-timeout 5s
|
|
53
|
+
--health-retries 5
|
|
54
|
+
env:
|
|
55
|
+
SQLY_TEST_POSTGRESQL_URL: postgresql://sqly:sqly_test_pass@localhost:5432/sqly_test
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/setup-python@v5
|
|
59
|
+
with:
|
|
60
|
+
python-version: ${{ matrix.python-version }}
|
|
61
|
+
- uses: astral-sh/setup-uv@v5
|
|
62
|
+
- run: uv sync --dev
|
|
63
|
+
- run: uv run pytest -m postgresql -v
|
|
64
|
+
|
|
65
|
+
test-mysql:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
strategy:
|
|
68
|
+
matrix:
|
|
69
|
+
python-version: ["3.10", "3.13"]
|
|
70
|
+
services:
|
|
71
|
+
mysql:
|
|
72
|
+
image: mysql:8.0
|
|
73
|
+
env:
|
|
74
|
+
MYSQL_ROOT_PASSWORD: sqly_test_pass
|
|
75
|
+
MYSQL_USER: sqly
|
|
76
|
+
MYSQL_PASSWORD: sqly_test_pass
|
|
77
|
+
MYSQL_DATABASE: sqly_test
|
|
78
|
+
ports:
|
|
79
|
+
- 3306:3306
|
|
80
|
+
options: >-
|
|
81
|
+
--health-cmd "mysqladmin ping -h localhost -u sqly -psqly_test_pass"
|
|
82
|
+
--health-interval 5s
|
|
83
|
+
--health-timeout 5s
|
|
84
|
+
--health-retries 5
|
|
85
|
+
env:
|
|
86
|
+
SQLY_TEST_MYSQL_URL: mysql+pymysql://sqly:sqly_test_pass@localhost:3306/sqly_test
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v4
|
|
89
|
+
- uses: actions/setup-python@v5
|
|
90
|
+
with:
|
|
91
|
+
python-version: ${{ matrix.python-version }}
|
|
92
|
+
- uses: astral-sh/setup-uv@v5
|
|
93
|
+
- run: uv sync --dev
|
|
94
|
+
- run: uv run pytest -m mysql -v
|
|
95
|
+
|
|
96
|
+
test-oracle:
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
strategy:
|
|
99
|
+
matrix:
|
|
100
|
+
python-version: ["3.10", "3.13"]
|
|
101
|
+
services:
|
|
102
|
+
oracle:
|
|
103
|
+
image: gvenzl/oracle-xe:21-slim
|
|
104
|
+
env:
|
|
105
|
+
ORACLE_PASSWORD: sqly_test_pass
|
|
106
|
+
APP_USER: sqly
|
|
107
|
+
APP_USER_PASSWORD: sqly_test_pass
|
|
108
|
+
ports:
|
|
109
|
+
- 1521:1521
|
|
110
|
+
options: >-
|
|
111
|
+
--health-cmd "healthcheck.sh"
|
|
112
|
+
--health-interval 10s
|
|
113
|
+
--health-timeout 5s
|
|
114
|
+
--health-retries 10
|
|
115
|
+
env:
|
|
116
|
+
SQLY_TEST_ORACLE_DSN: localhost:1521/XEPDB1
|
|
117
|
+
steps:
|
|
118
|
+
- uses: actions/checkout@v4
|
|
119
|
+
- uses: actions/setup-python@v5
|
|
120
|
+
with:
|
|
121
|
+
python-version: ${{ matrix.python-version }}
|
|
122
|
+
- uses: astral-sh/setup-uv@v5
|
|
123
|
+
- run: uv sync --dev
|
|
124
|
+
- run: uv run pytest -m oracle -v
|
sqlym-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Claude Code
|
|
2
|
+
.claude/settings.local.json
|
|
3
|
+
|
|
4
|
+
# Created by https://www.toptal.com/developers/gitignore/api/linux,macos,visualstudiocode,vim,python,jupyternotebooks,venv,virtualenv
|
|
5
|
+
|
|
6
|
+
### JupyterNotebooks ###
|
|
7
|
+
.ipynb_checkpoints
|
|
8
|
+
*/.ipynb_checkpoints/*
|
|
9
|
+
profile_default/
|
|
10
|
+
ipython_config.py
|
|
11
|
+
|
|
12
|
+
### Linux ###
|
|
13
|
+
*~
|
|
14
|
+
.fuse_hidden*
|
|
15
|
+
.directory
|
|
16
|
+
.Trash-*
|
|
17
|
+
.nfs*
|
|
18
|
+
|
|
19
|
+
### macOS ###
|
|
20
|
+
.DS_Store
|
|
21
|
+
.AppleDouble
|
|
22
|
+
.LSOverride
|
|
23
|
+
Icon
|
|
24
|
+
._*
|
|
25
|
+
.DocumentRevisions-V100
|
|
26
|
+
.fseventsd
|
|
27
|
+
.Spotlight-V100
|
|
28
|
+
.TemporaryItems
|
|
29
|
+
.Trashes
|
|
30
|
+
.VolumeIcon.icns
|
|
31
|
+
.com.apple.timemachine.donotpresent
|
|
32
|
+
.AppleDB
|
|
33
|
+
.AppleDesktop
|
|
34
|
+
Network Trash Folder
|
|
35
|
+
Temporary Items
|
|
36
|
+
.apdisk
|
|
37
|
+
*.icloud
|
|
38
|
+
|
|
39
|
+
### Python ###
|
|
40
|
+
__pycache__/
|
|
41
|
+
*.py[cod]
|
|
42
|
+
*$py.class
|
|
43
|
+
*.so
|
|
44
|
+
.Python
|
|
45
|
+
build/
|
|
46
|
+
develop-eggs/
|
|
47
|
+
dist/
|
|
48
|
+
downloads/
|
|
49
|
+
eggs/
|
|
50
|
+
.eggs/
|
|
51
|
+
lib/
|
|
52
|
+
lib64/
|
|
53
|
+
parts/
|
|
54
|
+
sdist/
|
|
55
|
+
var/
|
|
56
|
+
wheels/
|
|
57
|
+
share/python-wheels/
|
|
58
|
+
*.egg-info/
|
|
59
|
+
.installed.cfg
|
|
60
|
+
*.egg
|
|
61
|
+
MANIFEST
|
|
62
|
+
*.manifest
|
|
63
|
+
*.spec
|
|
64
|
+
pip-log.txt
|
|
65
|
+
pip-delete-this-directory.txt
|
|
66
|
+
|
|
67
|
+
# Unit test / coverage reports
|
|
68
|
+
htmlcov/
|
|
69
|
+
.tox/
|
|
70
|
+
.nox/
|
|
71
|
+
.coverage
|
|
72
|
+
.coverage.*
|
|
73
|
+
.cache
|
|
74
|
+
nosetests.xml
|
|
75
|
+
coverage.xml
|
|
76
|
+
*.cover
|
|
77
|
+
*.py,cover
|
|
78
|
+
.hypothesis/
|
|
79
|
+
.pytest_cache/
|
|
80
|
+
cover/
|
|
81
|
+
|
|
82
|
+
# Translations
|
|
83
|
+
*.mo
|
|
84
|
+
*.pot
|
|
85
|
+
|
|
86
|
+
# Environments
|
|
87
|
+
.env
|
|
88
|
+
.venv
|
|
89
|
+
env/
|
|
90
|
+
venv/
|
|
91
|
+
ENV/
|
|
92
|
+
env.bak/
|
|
93
|
+
venv.bak/
|
|
94
|
+
|
|
95
|
+
# Packaging
|
|
96
|
+
__pypackages__/
|
|
97
|
+
.pdm.toml
|
|
98
|
+
poetry.toml
|
|
99
|
+
|
|
100
|
+
# Type checkers
|
|
101
|
+
.mypy_cache/
|
|
102
|
+
.dmypy.json
|
|
103
|
+
dmypy.json
|
|
104
|
+
.pyre/
|
|
105
|
+
.pytype/
|
|
106
|
+
cython_debug/
|
|
107
|
+
pyrightconfig.json
|
|
108
|
+
|
|
109
|
+
# Ruff
|
|
110
|
+
.ruff_cache/
|
|
111
|
+
|
|
112
|
+
### Vim ###
|
|
113
|
+
[._]*.s[a-v][a-z]
|
|
114
|
+
!*.svg
|
|
115
|
+
[._]*.sw[a-p]
|
|
116
|
+
[._]s[a-rt-v][a-z]
|
|
117
|
+
[._]ss[a-gi-z]
|
|
118
|
+
[._]sw[a-p]
|
|
119
|
+
Session.vim
|
|
120
|
+
Sessionx.vim
|
|
121
|
+
.netrwhist
|
|
122
|
+
tags
|
|
123
|
+
[._]*.un~
|
|
124
|
+
|
|
125
|
+
### VisualStudioCode ###
|
|
126
|
+
.vscode/*
|
|
127
|
+
!.vscode/settings.json
|
|
128
|
+
!.vscode/tasks.json
|
|
129
|
+
!.vscode/launch.json
|
|
130
|
+
!.vscode/extensions.json
|
|
131
|
+
!.vscode/*.code-snippets
|
|
132
|
+
.history/
|
|
133
|
+
*.vsix
|
|
134
|
+
.ionide
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
sqlym-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
sqly is a SQL-first database access library for Python, inspired by Java's Clione-SQL and Doma2. It provides 2way SQL parsing (SQL files remain directly executable by DB tools) and flexible row-to-object mapping.
|
|
8
|
+
|
|
9
|
+
- **Language**: Python 3.10+
|
|
10
|
+
- **Dependencies**: None (stdlib only); Pydantic is optional
|
|
11
|
+
- **License**: MIT
|
|
12
|
+
- **Status**: v1.0 implementation complete
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
src/sqlym/
|
|
18
|
+
├── __init__.py # Public API (parse_sql, create_mapper, Column, entity, etc.)
|
|
19
|
+
├── _parse.py # parse_sql convenience function
|
|
20
|
+
├── config.py # Error message settings
|
|
21
|
+
├── dialect.py # Dialect enum (SQLITE, POSTGRESQL, MYSQL, ORACLE)
|
|
22
|
+
├── escape_utils.py # escape_like utility for LIKE clause escaping
|
|
23
|
+
├── exceptions.py # SqlyError, SqlParseError, MappingError, SqlFileNotFoundError
|
|
24
|
+
├── loader.py # SqlLoader: file-based SQL template loading (with dialect support)
|
|
25
|
+
├── parser/
|
|
26
|
+
│ ├── tokenizer.py # SQL tokenizer
|
|
27
|
+
│ ├── line_unit.py # LineUnit: line-level processing unit
|
|
28
|
+
│ └── twoway.py # TwoWaySQLParser: Clione-SQL 2way SQL engine
|
|
29
|
+
└── mapper/
|
|
30
|
+
├── protocol.py # RowMapper Protocol (runtime_checkable)
|
|
31
|
+
├── dataclass.py # DataclassMapper (auto-mapping with caching)
|
|
32
|
+
├── pydantic.py # PydanticMapper (optional)
|
|
33
|
+
├── column.py # Column annotation & @entity decorator
|
|
34
|
+
├── manual.py # ManualMapper
|
|
35
|
+
└── factory.py # create_mapper factory function
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Core Concepts
|
|
39
|
+
|
|
40
|
+
**Clione-SQL 4 Rules** govern the parser:
|
|
41
|
+
1. SQL is processed line-by-line (LineUnit)
|
|
42
|
+
2. Indentation defines parent/child relationships
|
|
43
|
+
3. If all children are removed, parent is also removed (bottom-up propagation)
|
|
44
|
+
4. `$`-prefixed parameters that are None cause line removal
|
|
45
|
+
|
|
46
|
+
**Parameter syntax in SQL comments**:
|
|
47
|
+
- `/* $name */default` — removable (None removes the line)
|
|
48
|
+
- `/* name */default` — non-removable (None binds as NULL)
|
|
49
|
+
- `IN /* $ids */(1,2,3)` — auto-expands list parameters
|
|
50
|
+
|
|
51
|
+
**Parser pipeline**: parse lines → build tree from indentation → evaluate params & mark removal → propagate removal upward → rebuild SQL → clean dangling WHERE/AND/OR/empty parens
|
|
52
|
+
|
|
53
|
+
**Mapper hierarchy** (via `create_mapper()` factory):
|
|
54
|
+
- `DataclassMapper` — auto-maps using field introspection with caching
|
|
55
|
+
- `PydanticMapper` — auto-detected via `model_validate()`
|
|
56
|
+
- `ManualMapper` — wraps user-provided function/lambda
|
|
57
|
+
|
|
58
|
+
**Column mapping priority**: `Annotated[T, Column("X")]` > `@entity(column_map={})` > `@entity(naming="...")` > field name as-is
|
|
59
|
+
|
|
60
|
+
### Placeholder formats
|
|
61
|
+
Supports `?` (SQLite/JDBC), `%s` (psycopg2), `:name` (Oracle) — configurable per parser instance.
|
|
62
|
+
|
|
63
|
+
## Implementation Phases (from DESIGN.md)
|
|
64
|
+
|
|
65
|
+
1. Parser foundation (LineUnit, line parsing, basic parameter substitution)
|
|
66
|
+
2. Parser completion (line removal, IN clause expansion, SQL cleanup)
|
|
67
|
+
3. Mappers (RowMapper, DataclassMapper, Column/entity)
|
|
68
|
+
4. Integration (SqlLoader, public API)
|
|
69
|
+
|
|
70
|
+
## Key Design Decisions
|
|
71
|
+
|
|
72
|
+
- Zero external dependencies for core — Pydantic support is optional and lazy-imported
|
|
73
|
+
- Protocol-based mapper interface with `@runtime_checkable` for duck typing
|
|
74
|
+
- Class-level mapping cache in DataclassMapper for performance
|
|
75
|
+
- Indentation carries semantic meaning (parent/child relationships for cascading removal)
|
|
76
|
+
|
|
77
|
+
## Specifications
|
|
78
|
+
|
|
79
|
+
- `docs/SPEC.md` — Functional specification (Japanese)
|
|
80
|
+
- `docs/DESIGN.md` — Implementation design with class-level details (Japanese)
|
sqlym-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 izuno4t
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sqlym-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.PHONY: install test lint format lint-fix pre-commit clean db-up db-down test-postgresql test-mysql test-oracle test-db test-all
|
|
2
|
+
|
|
3
|
+
install:
|
|
4
|
+
uv sync --dev
|
|
5
|
+
|
|
6
|
+
test:
|
|
7
|
+
uv run pytest
|
|
8
|
+
|
|
9
|
+
test-cov:
|
|
10
|
+
uv run pytest --cov=sqly --cov-report=term-missing
|
|
11
|
+
|
|
12
|
+
lint:
|
|
13
|
+
uv run ruff check .
|
|
14
|
+
|
|
15
|
+
format:
|
|
16
|
+
uv run ruff format .
|
|
17
|
+
|
|
18
|
+
lint-fix:
|
|
19
|
+
uv run ruff check . --fix
|
|
20
|
+
|
|
21
|
+
pre-commit:
|
|
22
|
+
uv run pre-commit run --all-files
|
|
23
|
+
|
|
24
|
+
db-up:
|
|
25
|
+
docker compose up -d --wait
|
|
26
|
+
|
|
27
|
+
db-down:
|
|
28
|
+
docker compose down
|
|
29
|
+
|
|
30
|
+
test-postgresql:
|
|
31
|
+
uv run pytest -m postgresql -v
|
|
32
|
+
|
|
33
|
+
test-mysql:
|
|
34
|
+
uv run pytest -m mysql -v
|
|
35
|
+
|
|
36
|
+
test-oracle:
|
|
37
|
+
uv run pytest -m oracle -v
|
|
38
|
+
|
|
39
|
+
test-db:
|
|
40
|
+
uv run pytest -m "postgresql or mysql or oracle" -v
|
|
41
|
+
|
|
42
|
+
test-all:
|
|
43
|
+
uv run pytest -v
|
|
44
|
+
|
|
45
|
+
clean:
|
|
46
|
+
rm -rf .venv .ruff_cache .pytest_cache .coverage htmlcov dist build
|
|
47
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
48
|
+
find . -type d -name "*.egg-info" -exec rm -rf {} +
|