sqlalchemy-cubrid 1.2.2__tar.gz → 1.2.3__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.
Files changed (46) hide show
  1. sqlalchemy_cubrid-1.2.3/PKG-INFO +282 -0
  2. sqlalchemy_cubrid-1.2.3/README.md +232 -0
  3. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/pyproject.toml +51 -8
  4. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/__init__.py +15 -2
  5. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid/aio_pycubrid_dialect.py +130 -0
  6. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid/alembic_impl.py +185 -0
  7. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/base.py +16 -14
  8. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/compiler.py +140 -58
  9. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/dialect.py +373 -124
  10. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/dml.py +57 -6
  11. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid/pycubrid_dialect.py +129 -0
  12. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid/requirements.py +276 -0
  13. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid/trace.py +92 -0
  14. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/types.py +156 -30
  15. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid.egg-info/PKG-INFO +282 -0
  16. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid.egg-info/SOURCES.txt +8 -0
  17. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid.egg-info/entry_points.txt +2 -0
  18. sqlalchemy_cubrid-1.2.3/sqlalchemy_cubrid.egg-info/requires.txt +21 -0
  19. sqlalchemy_cubrid-1.2.3/test/test_aio_integration.py +278 -0
  20. sqlalchemy_cubrid-1.2.3/test/test_aio_pycubrid_dialect.py +206 -0
  21. sqlalchemy_cubrid-1.2.3/test/test_alembic.py +301 -0
  22. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_compiler.py +175 -1
  23. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_dialect_offline.py +379 -32
  24. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_dialects.py +30 -0
  25. sqlalchemy_cubrid-1.2.3/test/test_integration.py +613 -0
  26. sqlalchemy_cubrid-1.2.3/test/test_json.py +312 -0
  27. sqlalchemy_cubrid-1.2.3/test/test_pycubrid_dialect.py +306 -0
  28. sqlalchemy_cubrid-1.2.3/test/test_trace.py +197 -0
  29. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_types.py +16 -0
  30. sqlalchemy_cubrid-1.2.2/PKG-INFO +0 -113
  31. sqlalchemy_cubrid-1.2.2/README.md +0 -79
  32. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid/alembic_impl.py +0 -69
  33. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid/requirements.py +0 -270
  34. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid.egg-info/PKG-INFO +0 -113
  35. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid.egg-info/requires.txt +0 -11
  36. sqlalchemy_cubrid-1.2.2/test/test_alembic.py +0 -120
  37. sqlalchemy_cubrid-1.2.2/test/test_integration.py +0 -326
  38. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/AUTHORS +0 -0
  39. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/LICENSE +0 -0
  40. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/setup.cfg +0 -0
  41. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid/py.typed +0 -0
  42. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid.egg-info/dependency_links.txt +0 -0
  43. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/sqlalchemy_cubrid.egg-info/top_level.txt +0 -0
  44. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_base.py +0 -0
  45. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_requirements.py +0 -0
  46. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.2.3}/test/test_suite.py +0 -0
@@ -0,0 +1,282 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlalchemy-cubrid
3
+ Version: 1.2.3
4
+ Summary: CUBRID dialect for SQLAlchemy
5
+ Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>, Gyeongjun Paik <paikend@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/cubrid-labs/sqlalchemy-cubrid
8
+ Project-URL: Repository, https://github.com/cubrid-labs/sqlalchemy-cubrid
9
+ Project-URL: Documentation, https://github.com/cubrid-labs/sqlalchemy-cubrid/tree/main/docs
10
+ Project-URL: Changelog, https://github.com/cubrid-labs/sqlalchemy-cubrid/blob/main/CHANGELOG.md
11
+ Project-URL: Issues, https://github.com/cubrid-labs/sqlalchemy-cubrid/issues
12
+ Keywords: SQLAlchemy,CUBRID,dialect
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Programming Language :: SQL
26
+ Classifier: Topic :: Database
27
+ Classifier: Topic :: Database :: Front-Ends
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ License-File: AUTHORS
32
+ Requires-Dist: sqlalchemy<2.2,>=2.0
33
+ Provides-Extra: cubrid
34
+ Requires-Dist: CUBRID-Python; extra == "cubrid"
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=7.0; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Requires-Dist: pytest-asyncio; extra == "dev"
39
+ Requires-Dist: ruff==0.15.8; extra == "dev"
40
+ Requires-Dist: mypy==1.19.1; extra == "dev"
41
+ Requires-Dist: bandit[toml]==1.9.4; extra == "dev"
42
+ Requires-Dist: pre-commit; extra == "dev"
43
+ Requires-Dist: tox; extra == "dev"
44
+ Requires-Dist: alembic>=1.7; extra == "dev"
45
+ Provides-Extra: alembic
46
+ Requires-Dist: alembic>=1.7; extra == "alembic"
47
+ Provides-Extra: pycubrid
48
+ Requires-Dist: pycubrid<2.0,>=1.0; extra == "pycubrid"
49
+ Dynamic: license-file
50
+
51
+ # sqlalchemy-cubrid
52
+
53
+ **SQLAlchemy 2.0–2.1 dialect for the CUBRID database** — Python ORM, schema reflection, Alembic migrations, and full type system support.
54
+
55
+ [🇰🇷 한국어](docs/README.ko.md) · [🇺🇸 English](README.md) · [🇨🇳 中文](docs/README.zh.md) · [🇮🇳 हिन्दी](docs/README.hi.md) · [🇩🇪 Deutsch](docs/README.de.md) · [🇷🇺 Русский](docs/README.ru.md)
56
+
57
+ <!-- BADGES:START -->
58
+ [![PyPI version](https://img.shields.io/pypi/v/sqlalchemy-cubrid)](https://pypi.org/project/sqlalchemy-cubrid)
59
+ [![python version](https://img.shields.io/pypi/pyversions/sqlalchemy-cubrid)](https://www.python.org)
60
+ [![ci workflow](https://github.com/cubrid-labs/sqlalchemy-cubrid/actions/workflows/ci.yml/badge.svg)](https://github.com/cubrid-labs/sqlalchemy-cubrid/actions/workflows/ci.yml)
61
+ [![coverage](https://codecov.io/gh/cubrid-labs/sqlalchemy-cubrid/branch/main/graph/badge.svg)](https://codecov.io/gh/cubrid-labs/sqlalchemy-cubrid)
62
+ [![license](https://img.shields.io/github/license/cubrid-labs/sqlalchemy-cubrid)](https://github.com/cubrid-labs/sqlalchemy-cubrid/blob/main/LICENSE)
63
+ [![GitHub stars](https://img.shields.io/github/stars/cubrid-labs/sqlalchemy-cubrid)](https://github.com/cubrid-labs/sqlalchemy-cubrid)
64
+ [![docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://cubrid-labs.github.io/sqlalchemy-cubrid/)
65
+ <!-- BADGES:END -->
66
+
67
+ ---
68
+
69
+ ## Why sqlalchemy-cubrid?
70
+
71
+ CUBRID is a high-performance open-source relational database, widely adopted in
72
+ Korean public-sector and enterprise applications. Until now, there was no
73
+ production-ready SQLAlchemy dialect that supports the modern 2.0–2.1 API.
74
+
75
+ **sqlalchemy-cubrid** bridges that gap:
76
+
77
+ - Full SQLAlchemy 2.0–2.1 dialect with **statement caching** and **PEP 561 typing**
78
+ - **426 offline tests** with **99%+ code coverage** — no database required to run them
79
+ - Tested against **4 CUBRID versions** (10.2, 11.0, 11.2, 11.4) across **Python 3.10 -- 3.14**
80
+ - CUBRID-specific DML constructs: `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`
81
+ - Alembic migration support out of the box
82
+ - **Three driver options** — C-extension (`cubrid://`), pure Python (`cubrid+pycubrid://`), or async pure Python (`cubrid+aiopycubrid://`)
83
+
84
+ ## Architecture
85
+
86
+ ```mermaid
87
+ flowchart TD
88
+ app["Application"] --> sa["SQLAlchemy Core/ORM"]
89
+ sa --> dialect["CubridDialect"]
90
+ dialect --> pycubrid["pycubrid driver"]
91
+ dialect --> cext["CUBRIDdb driver"]
92
+ dialect --> aio["pycubrid.aio async driver"]
93
+ pycubrid --> server["CUBRID Server"]
94
+ cext --> server
95
+ aio --> server
96
+ ```
97
+
98
+ ```mermaid
99
+ flowchart TD
100
+ expr["SQL Expression"] --> compiler["CubridSQLCompiler"] --> sql["SQL String"]
101
+ ```
102
+
103
+ ## Requirements
104
+
105
+ - Python 3.10+
106
+ - SQLAlchemy 2.0 – 2.1
107
+ - [CUBRID-Python](https://github.com/CUBRID/cubrid-python) (C-extension) **or** [pycubrid](https://github.com/sqlalchemy-cubrid/pycubrid) (pure Python)
108
+
109
+ ## Installation
110
+
111
+ ```bash
112
+ pip install sqlalchemy-cubrid
113
+ ```
114
+
115
+ With the pure Python driver (no C build needed):
116
+
117
+ ```bash
118
+ pip install "sqlalchemy-cubrid[pycubrid]"
119
+ ```
120
+
121
+ With Alembic support:
122
+
123
+ ```bash
124
+ pip install "sqlalchemy-cubrid[alembic]"
125
+ ```
126
+
127
+ ## Quick Start
128
+
129
+ ### Core (Connection-Level)
130
+
131
+ ```python
132
+ from sqlalchemy import create_engine, text
133
+
134
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
135
+
136
+ with engine.connect() as conn:
137
+ result = conn.execute(text("SELECT 1"))
138
+ print(result.scalar())
139
+ ```
140
+
141
+ ### ORM (Session-Level)
142
+
143
+ ```python
144
+ from sqlalchemy import create_engine, String
145
+ from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
146
+
147
+
148
+ class Base(DeclarativeBase):
149
+ pass
150
+
151
+
152
+ class User(Base):
153
+ __tablename__ = "users"
154
+
155
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
156
+ name: Mapped[str] = mapped_column(String(100))
157
+ email: Mapped[str] = mapped_column(String(200), unique=True)
158
+
159
+
160
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
161
+ Base.metadata.create_all(engine)
162
+
163
+ with Session(engine) as session:
164
+ user = User(name="Alice", email="alice@example.com")
165
+ session.add(user)
166
+ session.commit()
167
+ ```
168
+
169
+ ### Async
170
+
171
+ ```python
172
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
173
+ from sqlalchemy import text
174
+
175
+ engine = create_async_engine("cubrid+aiopycubrid://dba:password@localhost:33000/demodb")
176
+
177
+ async with AsyncSession(engine) as session:
178
+ result = await session.execute(text("SELECT 1"))
179
+ print(result.scalar())
180
+ ```
181
+
182
+ ## Features
183
+
184
+ - Complete type system -- numeric, string, date/time, bit, LOB, collection, and JSON types
185
+ - SQL compilation -- SELECT, JOIN, CAST, LIMIT/OFFSET, subqueries, CTEs, window functions
186
+ - DML extensions -- `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`, `FOR UPDATE`, `TRUNCATE`
187
+ - DDL support -- `COMMENT`, `IF NOT EXISTS` / `IF EXISTS`, `AUTO_INCREMENT`
188
+ - Schema reflection -- tables, views, columns, PKs, FKs, indexes, unique constraints, comments
189
+ - Alembic migrations via `CubridImpl` (auto-discovered entry point)
190
+ - All 6 CUBRID isolation levels (dual-granularity: class-level + instance-level)
191
+ - Async support — `create_async_engine("cubrid+aiopycubrid://...")` via pycubrid.aio
192
+
193
+ ## Documentation
194
+
195
+ | Guide | Description |
196
+ |---|---|
197
+ | [Connection](docs/CONNECTION.md) | Connection strings, URL format, driver setup, pool tuning |
198
+ | [Type Mapping](docs/TYPES.md) | Full type mapping, CUBRID-specific types, collection types |
199
+ | [DML Extensions](docs/DML_EXTENSIONS.md) | ON DUPLICATE KEY UPDATE, MERGE, REPLACE INTO, query trace |
200
+ | [Isolation Levels](docs/ISOLATION_LEVELS.md) | All 6 CUBRID isolation levels, configuration |
201
+ | [Alembic Migrations](docs/ALEMBIC.md) | Setup, configuration, limitations, batch workarounds |
202
+ | [Feature Support](docs/FEATURE_SUPPORT.md) | Comparison with MySQL, PostgreSQL, SQLite |
203
+ | [ORM Cookbook](docs/ORM_COOKBOOK.md) | Practical ORM examples, relationships, queries |
204
+ | [Development](docs/DEVELOPMENT.md) | Dev setup, testing, Docker, coverage, CI/CD |
205
+ | [Driver Compatibility](docs/DRIVER_COMPAT.md) | CUBRID-Python driver versions and known issues |
206
+ | [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues, error solutions, debugging techniques |
207
+ | [Async Connection](docs/CONNECTION.md#async-connection) | Async engine setup with `cubrid+aiopycubrid://` |
208
+
209
+ ## Compatibility Matrix
210
+
211
+ | Component | Supported versions |
212
+ |---|---|
213
+ | Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
214
+ | CUBRID | 10.2, 11.0, 11.2, 11.4 |
215
+ | SQLAlchemy | 2.0–2.1 |
216
+ | Alembic | >=1.7 |
217
+
218
+ ## FAQ
219
+
220
+ ### How do I connect to CUBRID with SQLAlchemy?
221
+
222
+ ```python
223
+ from sqlalchemy import create_engine
224
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
225
+ ```
226
+
227
+ For the pure Python driver (no C build needed): `create_engine("cubrid+pycubrid://dba@localhost:33000/demodb")`
228
+
229
+ ### Does sqlalchemy-cubrid support SQLAlchemy 2.0–2.1?
230
+
231
+ Yes. sqlalchemy-cubrid is built for SQLAlchemy 2.0–2.1 and supports the 2.0-style API including `Session.execute()`, typed `Mapped[]` columns, and statement caching.
232
+
233
+ ### Does sqlalchemy-cubrid support Alembic migrations?
234
+
235
+ Yes. Install with `pip install "sqlalchemy-cubrid[alembic]"`. The dialect auto-registers via entry point. Note that CUBRID auto-commits DDL, so migrations are not transactional.
236
+
237
+ ### What Python versions are supported?
238
+
239
+ Python 3.10, 3.11, 3.12, 3.13, and 3.14.
240
+
241
+ ### Does CUBRID support RETURNING clauses?
242
+
243
+ No. CUBRID does not support `INSERT ... RETURNING` or `UPDATE ... RETURNING`. Use `cursor.lastrowid` or `SELECT LAST_INSERT_ID()` instead.
244
+
245
+ ### How do I use ON DUPLICATE KEY UPDATE with CUBRID?
246
+
247
+ ```python
248
+ from sqlalchemy_cubrid import insert
249
+ stmt = insert(users).values(name="Alice").on_duplicate_key_update(name="Alice Updated")
250
+ ```
251
+
252
+ ### What's the difference between `cubrid://` and `cubrid+pycubrid://`?
253
+
254
+ `cubrid://` uses the C-extension driver (CUBRIDdb) which requires compilation. `cubrid+pycubrid://` uses the pure Python driver which installs with pip alone — no build tools needed. `cubrid+aiopycubrid://` uses the async variant of the pure Python driver for use with `create_async_engine` and `AsyncSession`.
255
+
256
+ ### Does sqlalchemy-cubrid support async?
257
+
258
+ Yes. Use `create_async_engine("cubrid+aiopycubrid://...")` with the pycubrid async driver. Requires `pycubrid>=1.1.0`. All Core and ORM features work with `AsyncSession`.
259
+
260
+
261
+ ## Related Projects
262
+
263
+ - [pycubrid](https://github.com/cubrid-labs/pycubrid) — Pure Python DB-API 2.0 driver for CUBRID
264
+ - [cubrid-cookbook-python](https://github.com/cubrid-labs/cubrid-cookbook-python) — Production-ready Python examples for CUBRID
265
+
266
+ ## Roadmap
267
+
268
+ See [`ROADMAP.md`](ROADMAP.md) for this project's direction and next milestones.
269
+
270
+ For the ecosystem-wide view, see the [CUBRID Labs Ecosystem Roadmap](https://github.com/cubrid-labs/.github/blob/main/ROADMAP.md) and [Project Board](https://github.com/orgs/cubrid-labs/projects/2).
271
+
272
+ ## Contributing
273
+
274
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for development setup.
275
+
276
+ ## Security
277
+
278
+ Report vulnerabilities via email -- see [SECURITY.md](SECURITY.md). Do not open public issues for security concerns.
279
+
280
+ ## License
281
+
282
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,232 @@
1
+ # sqlalchemy-cubrid
2
+
3
+ **SQLAlchemy 2.0–2.1 dialect for the CUBRID database** — Python ORM, schema reflection, Alembic migrations, and full type system support.
4
+
5
+ [🇰🇷 한국어](docs/README.ko.md) · [🇺🇸 English](README.md) · [🇨🇳 中文](docs/README.zh.md) · [🇮🇳 हिन्दी](docs/README.hi.md) · [🇩🇪 Deutsch](docs/README.de.md) · [🇷🇺 Русский](docs/README.ru.md)
6
+
7
+ <!-- BADGES:START -->
8
+ [![PyPI version](https://img.shields.io/pypi/v/sqlalchemy-cubrid)](https://pypi.org/project/sqlalchemy-cubrid)
9
+ [![python version](https://img.shields.io/pypi/pyversions/sqlalchemy-cubrid)](https://www.python.org)
10
+ [![ci workflow](https://github.com/cubrid-labs/sqlalchemy-cubrid/actions/workflows/ci.yml/badge.svg)](https://github.com/cubrid-labs/sqlalchemy-cubrid/actions/workflows/ci.yml)
11
+ [![coverage](https://codecov.io/gh/cubrid-labs/sqlalchemy-cubrid/branch/main/graph/badge.svg)](https://codecov.io/gh/cubrid-labs/sqlalchemy-cubrid)
12
+ [![license](https://img.shields.io/github/license/cubrid-labs/sqlalchemy-cubrid)](https://github.com/cubrid-labs/sqlalchemy-cubrid/blob/main/LICENSE)
13
+ [![GitHub stars](https://img.shields.io/github/stars/cubrid-labs/sqlalchemy-cubrid)](https://github.com/cubrid-labs/sqlalchemy-cubrid)
14
+ [![docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://cubrid-labs.github.io/sqlalchemy-cubrid/)
15
+ <!-- BADGES:END -->
16
+
17
+ ---
18
+
19
+ ## Why sqlalchemy-cubrid?
20
+
21
+ CUBRID is a high-performance open-source relational database, widely adopted in
22
+ Korean public-sector and enterprise applications. Until now, there was no
23
+ production-ready SQLAlchemy dialect that supports the modern 2.0–2.1 API.
24
+
25
+ **sqlalchemy-cubrid** bridges that gap:
26
+
27
+ - Full SQLAlchemy 2.0–2.1 dialect with **statement caching** and **PEP 561 typing**
28
+ - **426 offline tests** with **99%+ code coverage** — no database required to run them
29
+ - Tested against **4 CUBRID versions** (10.2, 11.0, 11.2, 11.4) across **Python 3.10 -- 3.14**
30
+ - CUBRID-specific DML constructs: `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`
31
+ - Alembic migration support out of the box
32
+ - **Three driver options** — C-extension (`cubrid://`), pure Python (`cubrid+pycubrid://`), or async pure Python (`cubrid+aiopycubrid://`)
33
+
34
+ ## Architecture
35
+
36
+ ```mermaid
37
+ flowchart TD
38
+ app["Application"] --> sa["SQLAlchemy Core/ORM"]
39
+ sa --> dialect["CubridDialect"]
40
+ dialect --> pycubrid["pycubrid driver"]
41
+ dialect --> cext["CUBRIDdb driver"]
42
+ dialect --> aio["pycubrid.aio async driver"]
43
+ pycubrid --> server["CUBRID Server"]
44
+ cext --> server
45
+ aio --> server
46
+ ```
47
+
48
+ ```mermaid
49
+ flowchart TD
50
+ expr["SQL Expression"] --> compiler["CubridSQLCompiler"] --> sql["SQL String"]
51
+ ```
52
+
53
+ ## Requirements
54
+
55
+ - Python 3.10+
56
+ - SQLAlchemy 2.0 – 2.1
57
+ - [CUBRID-Python](https://github.com/CUBRID/cubrid-python) (C-extension) **or** [pycubrid](https://github.com/sqlalchemy-cubrid/pycubrid) (pure Python)
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install sqlalchemy-cubrid
63
+ ```
64
+
65
+ With the pure Python driver (no C build needed):
66
+
67
+ ```bash
68
+ pip install "sqlalchemy-cubrid[pycubrid]"
69
+ ```
70
+
71
+ With Alembic support:
72
+
73
+ ```bash
74
+ pip install "sqlalchemy-cubrid[alembic]"
75
+ ```
76
+
77
+ ## Quick Start
78
+
79
+ ### Core (Connection-Level)
80
+
81
+ ```python
82
+ from sqlalchemy import create_engine, text
83
+
84
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
85
+
86
+ with engine.connect() as conn:
87
+ result = conn.execute(text("SELECT 1"))
88
+ print(result.scalar())
89
+ ```
90
+
91
+ ### ORM (Session-Level)
92
+
93
+ ```python
94
+ from sqlalchemy import create_engine, String
95
+ from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
96
+
97
+
98
+ class Base(DeclarativeBase):
99
+ pass
100
+
101
+
102
+ class User(Base):
103
+ __tablename__ = "users"
104
+
105
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
106
+ name: Mapped[str] = mapped_column(String(100))
107
+ email: Mapped[str] = mapped_column(String(200), unique=True)
108
+
109
+
110
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
111
+ Base.metadata.create_all(engine)
112
+
113
+ with Session(engine) as session:
114
+ user = User(name="Alice", email="alice@example.com")
115
+ session.add(user)
116
+ session.commit()
117
+ ```
118
+
119
+ ### Async
120
+
121
+ ```python
122
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
123
+ from sqlalchemy import text
124
+
125
+ engine = create_async_engine("cubrid+aiopycubrid://dba:password@localhost:33000/demodb")
126
+
127
+ async with AsyncSession(engine) as session:
128
+ result = await session.execute(text("SELECT 1"))
129
+ print(result.scalar())
130
+ ```
131
+
132
+ ## Features
133
+
134
+ - Complete type system -- numeric, string, date/time, bit, LOB, collection, and JSON types
135
+ - SQL compilation -- SELECT, JOIN, CAST, LIMIT/OFFSET, subqueries, CTEs, window functions
136
+ - DML extensions -- `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`, `FOR UPDATE`, `TRUNCATE`
137
+ - DDL support -- `COMMENT`, `IF NOT EXISTS` / `IF EXISTS`, `AUTO_INCREMENT`
138
+ - Schema reflection -- tables, views, columns, PKs, FKs, indexes, unique constraints, comments
139
+ - Alembic migrations via `CubridImpl` (auto-discovered entry point)
140
+ - All 6 CUBRID isolation levels (dual-granularity: class-level + instance-level)
141
+ - Async support — `create_async_engine("cubrid+aiopycubrid://...")` via pycubrid.aio
142
+
143
+ ## Documentation
144
+
145
+ | Guide | Description |
146
+ |---|---|
147
+ | [Connection](docs/CONNECTION.md) | Connection strings, URL format, driver setup, pool tuning |
148
+ | [Type Mapping](docs/TYPES.md) | Full type mapping, CUBRID-specific types, collection types |
149
+ | [DML Extensions](docs/DML_EXTENSIONS.md) | ON DUPLICATE KEY UPDATE, MERGE, REPLACE INTO, query trace |
150
+ | [Isolation Levels](docs/ISOLATION_LEVELS.md) | All 6 CUBRID isolation levels, configuration |
151
+ | [Alembic Migrations](docs/ALEMBIC.md) | Setup, configuration, limitations, batch workarounds |
152
+ | [Feature Support](docs/FEATURE_SUPPORT.md) | Comparison with MySQL, PostgreSQL, SQLite |
153
+ | [ORM Cookbook](docs/ORM_COOKBOOK.md) | Practical ORM examples, relationships, queries |
154
+ | [Development](docs/DEVELOPMENT.md) | Dev setup, testing, Docker, coverage, CI/CD |
155
+ | [Driver Compatibility](docs/DRIVER_COMPAT.md) | CUBRID-Python driver versions and known issues |
156
+ | [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues, error solutions, debugging techniques |
157
+ | [Async Connection](docs/CONNECTION.md#async-connection) | Async engine setup with `cubrid+aiopycubrid://` |
158
+
159
+ ## Compatibility Matrix
160
+
161
+ | Component | Supported versions |
162
+ |---|---|
163
+ | Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
164
+ | CUBRID | 10.2, 11.0, 11.2, 11.4 |
165
+ | SQLAlchemy | 2.0–2.1 |
166
+ | Alembic | >=1.7 |
167
+
168
+ ## FAQ
169
+
170
+ ### How do I connect to CUBRID with SQLAlchemy?
171
+
172
+ ```python
173
+ from sqlalchemy import create_engine
174
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
175
+ ```
176
+
177
+ For the pure Python driver (no C build needed): `create_engine("cubrid+pycubrid://dba@localhost:33000/demodb")`
178
+
179
+ ### Does sqlalchemy-cubrid support SQLAlchemy 2.0–2.1?
180
+
181
+ Yes. sqlalchemy-cubrid is built for SQLAlchemy 2.0–2.1 and supports the 2.0-style API including `Session.execute()`, typed `Mapped[]` columns, and statement caching.
182
+
183
+ ### Does sqlalchemy-cubrid support Alembic migrations?
184
+
185
+ Yes. Install with `pip install "sqlalchemy-cubrid[alembic]"`. The dialect auto-registers via entry point. Note that CUBRID auto-commits DDL, so migrations are not transactional.
186
+
187
+ ### What Python versions are supported?
188
+
189
+ Python 3.10, 3.11, 3.12, 3.13, and 3.14.
190
+
191
+ ### Does CUBRID support RETURNING clauses?
192
+
193
+ No. CUBRID does not support `INSERT ... RETURNING` or `UPDATE ... RETURNING`. Use `cursor.lastrowid` or `SELECT LAST_INSERT_ID()` instead.
194
+
195
+ ### How do I use ON DUPLICATE KEY UPDATE with CUBRID?
196
+
197
+ ```python
198
+ from sqlalchemy_cubrid import insert
199
+ stmt = insert(users).values(name="Alice").on_duplicate_key_update(name="Alice Updated")
200
+ ```
201
+
202
+ ### What's the difference between `cubrid://` and `cubrid+pycubrid://`?
203
+
204
+ `cubrid://` uses the C-extension driver (CUBRIDdb) which requires compilation. `cubrid+pycubrid://` uses the pure Python driver which installs with pip alone — no build tools needed. `cubrid+aiopycubrid://` uses the async variant of the pure Python driver for use with `create_async_engine` and `AsyncSession`.
205
+
206
+ ### Does sqlalchemy-cubrid support async?
207
+
208
+ Yes. Use `create_async_engine("cubrid+aiopycubrid://...")` with the pycubrid async driver. Requires `pycubrid>=1.1.0`. All Core and ORM features work with `AsyncSession`.
209
+
210
+
211
+ ## Related Projects
212
+
213
+ - [pycubrid](https://github.com/cubrid-labs/pycubrid) — Pure Python DB-API 2.0 driver for CUBRID
214
+ - [cubrid-cookbook-python](https://github.com/cubrid-labs/cubrid-cookbook-python) — Production-ready Python examples for CUBRID
215
+
216
+ ## Roadmap
217
+
218
+ See [`ROADMAP.md`](ROADMAP.md) for this project's direction and next milestones.
219
+
220
+ For the ecosystem-wide view, see the [CUBRID Labs Ecosystem Roadmap](https://github.com/cubrid-labs/.github/blob/main/ROADMAP.md) and [Project Board](https://github.com/orgs/cubrid-labs/projects/2).
221
+
222
+ ## Contributing
223
+
224
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for development setup.
225
+
226
+ ## Security
227
+
228
+ Report vulnerabilities via email -- see [SECURITY.md](SECURITY.md). Do not open public issues for security concerns.
229
+
230
+ ## License
231
+
232
+ MIT -- see [LICENSE](LICENSE).
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sqlalchemy-cubrid"
7
- version = "1.2.2"
7
+ version = "1.2.3"
8
8
  description = "CUBRID dialect for SQLAlchemy"
9
9
  readme = "README.md"
10
- license = {text = "MIT"}
10
+ license = "MIT"
11
11
  requires-python = ">=3.10"
12
12
  authors = [
13
13
  {name = "Yeongseon Choe", email = "yeongseon.choe@gmail.com"},
@@ -15,16 +15,21 @@ authors = [
15
15
  ]
16
16
  keywords = ["SQLAlchemy", "CUBRID", "dialect"]
17
17
  classifiers = [
18
- "Development Status :: 3 - Alpha",
18
+ "Development Status :: 5 - Production/Stable",
19
19
  "Environment :: Console",
20
20
  "Intended Audience :: Developers",
21
- "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
22
  "Programming Language :: Python",
23
23
  "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3 :: Only",
24
25
  "Programming Language :: Python :: 3.10",
25
26
  "Programming Language :: Python :: 3.11",
26
27
  "Programming Language :: Python :: 3.12",
27
28
  "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
30
+ "Programming Language :: SQL",
31
+ "Topic :: Database",
32
+ "Topic :: Database :: Front-Ends",
28
33
  ]
29
34
  dependencies = [
30
35
  "sqlalchemy>=2.0,<2.2",
@@ -36,20 +41,34 @@ cubrid = [
36
41
  ]
37
42
  dev = [
38
43
  "pytest>=7.0",
39
- "ruff>=0.4",
44
+ "pytest-cov",
45
+ "pytest-asyncio",
46
+ "ruff==0.15.8",
47
+ "mypy==1.19.1",
48
+ "bandit[toml]==1.9.4",
49
+ "pre-commit",
50
+ "tox",
51
+ "alembic>=1.7",
40
52
  ]
41
53
  alembic = [
42
54
  "alembic>=1.7",
43
55
  ]
56
+ pycubrid = [
57
+ "pycubrid>=1.0,<2.0",
58
+ ]
44
59
 
45
60
  [project.urls]
46
- Homepage = "https://github.com/sqlalchemy-cubrid/sqlalchemy-cubrid"
47
- Repository = "https://github.com/sqlalchemy-cubrid/sqlalchemy-cubrid"
48
- Documentation = "https://github.com/sqlalchemy-cubrid/sqlalchemy-cubrid"
61
+ Homepage = "https://github.com/cubrid-labs/sqlalchemy-cubrid"
62
+ Repository = "https://github.com/cubrid-labs/sqlalchemy-cubrid"
63
+ Documentation = "https://github.com/cubrid-labs/sqlalchemy-cubrid/tree/main/docs"
64
+ Changelog = "https://github.com/cubrid-labs/sqlalchemy-cubrid/blob/main/CHANGELOG.md"
65
+ Issues = "https://github.com/cubrid-labs/sqlalchemy-cubrid/issues"
49
66
 
50
67
  [project.entry-points."sqlalchemy.dialects"]
51
68
  cubrid = "sqlalchemy_cubrid.dialect:CubridDialect"
52
69
  "cubrid.cubrid" = "sqlalchemy_cubrid.dialect:CubridDialect"
70
+ "cubrid.pycubrid" = "sqlalchemy_cubrid.pycubrid_dialect:PyCubridDialect"
71
+ "cubrid.aiopycubrid" = "sqlalchemy_cubrid.aio_pycubrid_dialect:PyCubridAsyncDialect"
53
72
 
54
73
  [project.entry-points."alembic.ddl"]
55
74
  cubrid = "sqlalchemy_cubrid.alembic_impl:CubridImpl"
@@ -62,6 +81,30 @@ include-package-data = true
62
81
  line-length = 100
63
82
  target-version = "py310"
64
83
 
84
+ [tool.ruff.lint.isort]
85
+ known-first-party = ["sqlalchemy_cubrid"]
86
+
87
+ [tool.mypy]
88
+ python_version = "3.10"
89
+ strict = true
90
+ warn_return_any = true
91
+ warn_unused_configs = true
92
+
93
+ [[tool.mypy.overrides]]
94
+ module = "test.*"
95
+ ignore_errors = true
96
+
97
+ [tool.bandit]
98
+ exclude_dirs = ["test"]
99
+ skips = ["B101"]
100
+
101
+ [tool.coverage.run]
102
+ source = ["sqlalchemy_cubrid"]
103
+
104
+ [tool.coverage.report]
105
+ show_missing = true
106
+ fail_under = 95
107
+
65
108
  [tool.pytest.ini_options]
66
109
  testpaths = ["test"]
67
110
  addopts = "--tb native -v -r fxX --maxfail=25 -p no:warnings"
@@ -25,10 +25,15 @@ from .types import (
25
25
  DOUBLE,
26
26
  DOUBLE_PRECISION,
27
27
  FLOAT,
28
+ JSON,
29
+ JSONIndexType,
30
+ JSONPathType,
31
+ MONETARY,
28
32
  MULTISET,
29
33
  NCHAR,
30
34
  NUMERIC,
31
35
  NVARCHAR,
36
+ OBJECT,
32
37
  REAL,
33
38
  SEQUENCE,
34
39
  SET,
@@ -36,7 +41,8 @@ from .types import (
36
41
  STRING,
37
42
  VARCHAR,
38
43
  )
39
- from .dml import insert, merge
44
+ from .dml import insert, merge, replace
45
+ from .trace import trace_query
40
46
 
41
47
  from sqlalchemy.sql.sqltypes import (
42
48
  DATE,
@@ -46,11 +52,13 @@ from sqlalchemy.sql.sqltypes import (
46
52
  TIMESTAMP,
47
53
  )
48
54
 
49
- __version__ = "1.2.2"
55
+ __version__ = "1.2.3"
50
56
 
51
57
  __all__ = (
52
58
  "insert",
53
59
  "merge",
60
+ "replace",
61
+ "trace_query",
54
62
  "SMALLINT",
55
63
  "INTEGER",
56
64
  "BIGINT",
@@ -60,6 +68,7 @@ __all__ = (
60
68
  "REAL",
61
69
  "DOUBLE",
62
70
  "DOUBLE_PRECISION",
71
+ "MONETARY",
63
72
  "DATE",
64
73
  "TIME",
65
74
  "TIMESTAMP",
@@ -75,4 +84,8 @@ __all__ = (
75
84
  "SET",
76
85
  "MULTISET",
77
86
  "SEQUENCE",
87
+ "OBJECT",
88
+ "JSON",
89
+ "JSONIndexType",
90
+ "JSONPathType",
78
91
  )