sqlalchemy-cubrid 1.2.2__tar.gz → 1.3.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.
Files changed (49) hide show
  1. sqlalchemy_cubrid-1.3.0/PKG-INFO +295 -0
  2. sqlalchemy_cubrid-1.3.0/README.md +245 -0
  3. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/pyproject.toml +51 -8
  4. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/__init__.py +15 -2
  5. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/aio_pycubrid_dialect.py +130 -0
  6. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/alembic_impl.py +219 -0
  7. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/base.py +19 -16
  8. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/compiler.py +146 -58
  9. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/dialect.py +878 -0
  10. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/dml.py +57 -6
  11. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/pycubrid_dialect.py +129 -0
  12. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/requirements.py +276 -0
  13. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid/trace.py +92 -0
  14. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/types.py +156 -30
  15. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid.egg-info/PKG-INFO +295 -0
  16. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid.egg-info/SOURCES.txt +10 -0
  17. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid.egg-info/entry_points.txt +2 -0
  18. sqlalchemy_cubrid-1.3.0/sqlalchemy_cubrid.egg-info/requires.txt +21 -0
  19. sqlalchemy_cubrid-1.3.0/test/test_aio_integration.py +278 -0
  20. sqlalchemy_cubrid-1.3.0/test/test_aio_pycubrid_dialect.py +206 -0
  21. sqlalchemy_cubrid-1.3.0/test/test_alembic.py +675 -0
  22. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_compiler.py +204 -1
  23. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_dialect_offline.py +402 -33
  24. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_dialects.py +30 -0
  25. sqlalchemy_cubrid-1.3.0/test/test_integration.py +613 -0
  26. sqlalchemy_cubrid-1.3.0/test/test_json.py +312 -0
  27. sqlalchemy_cubrid-1.3.0/test/test_packaging.py +169 -0
  28. sqlalchemy_cubrid-1.3.0/test/test_pycubrid_dialect.py +306 -0
  29. sqlalchemy_cubrid-1.3.0/test/test_show_create_table.py +608 -0
  30. sqlalchemy_cubrid-1.3.0/test/test_trace.py +197 -0
  31. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_types.py +16 -0
  32. sqlalchemy_cubrid-1.2.2/PKG-INFO +0 -113
  33. sqlalchemy_cubrid-1.2.2/README.md +0 -79
  34. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid/alembic_impl.py +0 -69
  35. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid/dialect.py +0 -605
  36. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid/requirements.py +0 -270
  37. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid.egg-info/PKG-INFO +0 -113
  38. sqlalchemy_cubrid-1.2.2/sqlalchemy_cubrid.egg-info/requires.txt +0 -11
  39. sqlalchemy_cubrid-1.2.2/test/test_alembic.py +0 -120
  40. sqlalchemy_cubrid-1.2.2/test/test_integration.py +0 -326
  41. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/AUTHORS +0 -0
  42. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/LICENSE +0 -0
  43. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/setup.cfg +0 -0
  44. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid/py.typed +0 -0
  45. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid.egg-info/dependency_links.txt +0 -0
  46. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/sqlalchemy_cubrid.egg-info/top_level.txt +0 -0
  47. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_base.py +0 -0
  48. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_requirements.py +0 -0
  49. {sqlalchemy_cubrid-1.2.2 → sqlalchemy_cubrid-1.3.0}/test/test_suite.py +0 -0
@@ -0,0 +1,295 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlalchemy-cubrid
3
+ Version: 1.3.0
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 :: 4 - Beta
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 type mapping for SQLAlchemy and CUBRID-specific types.
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
+ > **Status: Beta.** The core public API follows semantic versioning; minor releases may add features and bug fixes while the project remains under active development.
70
+
71
+ ## Why sqlalchemy-cubrid?
72
+
73
+ CUBRID is a high-performance open-source relational database, widely adopted in
74
+ Korean public-sector and enterprise applications. Until now, there was no
75
+ production-ready SQLAlchemy dialect that supports the modern 2.0–2.1 API.
76
+
77
+ **sqlalchemy-cubrid** bridges that gap:
78
+
79
+ - Full SQLAlchemy 2.0–2.1 dialect with **statement caching** and **PEP 561 typing**
80
+ - **577 offline tests** with **99%+ code coverage** — no database required to run them
81
+ - Tested against **4 CUBRID versions** (10.2, 11.0, 11.2, 11.4) across **Python 3.10 -- 3.14**
82
+ - CUBRID-specific DML constructs: `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`
83
+ - Alembic migration support out of the box
84
+ - **Three driver options** — C-extension (`cubrid://`), pure Python (`cubrid+pycubrid://`), or async pure Python (`cubrid+aiopycubrid://`)
85
+
86
+ ## Architecture
87
+
88
+ ```mermaid
89
+ flowchart TD
90
+ app["Application"] --> sa["SQLAlchemy Core/ORM"]
91
+ sa --> dialect["CubridDialect"]
92
+ dialect --> pycubrid["pycubrid driver"]
93
+ dialect --> cext["CUBRIDdb driver"]
94
+ dialect --> aio["pycubrid.aio async driver"]
95
+ pycubrid --> server["CUBRID Server"]
96
+ cext --> server
97
+ aio --> server
98
+ ```
99
+
100
+ ```mermaid
101
+ flowchart TD
102
+ expr["SQL Expression"] --> compiler["CubridSQLCompiler"] --> sql["SQL String"]
103
+ ```
104
+
105
+ ## Requirements
106
+
107
+ - Python 3.10+
108
+ - SQLAlchemy 2.0 – 2.1
109
+ - [CUBRID-Python](https://github.com/CUBRID/cubrid-python) (C-extension) **or** [pycubrid](https://github.com/sqlalchemy-cubrid/pycubrid) (pure Python)
110
+
111
+ ## Installation
112
+
113
+ ```bash
114
+ pip install sqlalchemy-cubrid
115
+ ```
116
+
117
+ With the pure Python driver (no C build needed):
118
+
119
+ ```bash
120
+ pip install "sqlalchemy-cubrid[pycubrid]"
121
+ ```
122
+
123
+ With Alembic support:
124
+
125
+ ```bash
126
+ pip install "sqlalchemy-cubrid[alembic]"
127
+ ```
128
+
129
+ ## Quick Start
130
+
131
+ ### Core (Connection-Level)
132
+
133
+ ```python
134
+ from sqlalchemy import create_engine, text
135
+
136
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
137
+
138
+ with engine.connect() as conn:
139
+ result = conn.execute(text("SELECT 1"))
140
+ print(result.scalar())
141
+ ```
142
+
143
+ ### ORM (Session-Level)
144
+
145
+ ```python
146
+ from sqlalchemy import create_engine, String
147
+ from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
148
+
149
+
150
+ class Base(DeclarativeBase):
151
+ pass
152
+
153
+
154
+ class User(Base):
155
+ __tablename__ = "users"
156
+
157
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
158
+ name: Mapped[str] = mapped_column(String(100))
159
+ email: Mapped[str] = mapped_column(String(200), unique=True)
160
+
161
+
162
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
163
+ Base.metadata.create_all(engine)
164
+
165
+ with Session(engine) as session:
166
+ user = User(name="Alice", email="alice@example.com")
167
+ session.add(user)
168
+ session.commit()
169
+ ```
170
+
171
+ ### Async
172
+
173
+ ```python
174
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
175
+ from sqlalchemy import text
176
+
177
+ engine = create_async_engine("cubrid+aiopycubrid://dba:password@localhost:33000/demodb")
178
+
179
+ async with AsyncSession(engine) as session:
180
+ result = await session.execute(text("SELECT 1"))
181
+ print(result.scalar())
182
+ ```
183
+
184
+ ## Features
185
+
186
+ - Type mapping for SQLAlchemy standard and CUBRID-specific types — numeric, string, date/time, bit, LOB, collection, and JSON types
187
+ - SQL compilation -- SELECT, JOIN, CAST, LIMIT/OFFSET, subqueries, CTEs, window functions
188
+ - DML extensions -- `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`, `FOR UPDATE`, `TRUNCATE`
189
+ - DDL support -- `COMMENT`, `IF NOT EXISTS` / `IF EXISTS`, `AUTO_INCREMENT`
190
+ - Schema reflection -- tables, views, columns, PKs, FKs, indexes, unique constraints, comments
191
+ - Alembic migrations via `CubridImpl` (auto-discovered entry point)
192
+ - All 6 CUBRID isolation levels (dual-granularity: class-level + instance-level)
193
+ - Async support (available since v1.1.0) — `create_async_engine("cubrid+aiopycubrid://...")` via pycubrid.aio
194
+
195
+ ## Known Limitations
196
+
197
+ - **No `RETURNING`** — `INSERT/UPDATE/DELETE ... RETURNING` not supported; use `cursor.lastrowid` or `LAST_INSERT_ID()`
198
+ - **No sequences** — CUBRID uses `AUTO_INCREMENT` only
199
+ - **No multi-schema** — single schema per database
200
+ - **DDL auto-commits** — migrations are not transactional (`transactional_ddl = False`)
201
+ - **SQLAlchemy 2.0–2.1 only** — pinned to `<2.2` due to internal API dependencies ([details](docs/ARCHITECTURE.md))
202
+ - **Async requires pycubrid ≥ 1.1.0** — the `cubrid+aiopycubrid://` driver needs the async-capable pycubrid
203
+
204
+ ## Documentation
205
+
206
+ | Guide | Description |
207
+ |---|---|
208
+ | [Connection](docs/CONNECTION.md) | Connection strings, URL format, driver setup, pool tuning |
209
+ | [Type Mapping](docs/TYPES.md) | Full type mapping, CUBRID-specific types, collection types |
210
+ | [DML Extensions](docs/DML_EXTENSIONS.md) | ON DUPLICATE KEY UPDATE, MERGE, REPLACE INTO, query trace |
211
+ | [Isolation Levels](docs/ISOLATION_LEVELS.md) | All 6 CUBRID isolation levels, configuration |
212
+ | [Alembic Migrations](docs/ALEMBIC.md) | Setup, configuration, limitations, batch workarounds |
213
+ | [Feature Support](docs/FEATURE_SUPPORT.md) | Comparison with MySQL, PostgreSQL, SQLite |
214
+ | [ORM Cookbook](docs/ORM_COOKBOOK.md) | Practical ORM examples, relationships, queries |
215
+ | [Development](docs/DEVELOPMENT.md) | Dev setup, testing, Docker, coverage, CI/CD |
216
+ | [Driver Compatibility](docs/DRIVER_COMPAT.md) | CUBRID-Python driver versions and known issues |
217
+ | [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues, error solutions, debugging techniques |
218
+ | [Async Connection](docs/CONNECTION.md#async-connection) | Async engine setup with `cubrid+aiopycubrid://` |
219
+
220
+ ## Compatibility Matrix
221
+
222
+ | Component | Supported versions |
223
+ |---|---|
224
+ | Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
225
+ | CUBRID | 10.2, 11.0, 11.2, 11.4 |
226
+ | SQLAlchemy | 2.0–2.1 |
227
+ | Alembic | >=1.7 |
228
+ | pycubrid (sync) | >=1.2.0 |
229
+ | pycubrid (async) | >=1.2.0 |
230
+
231
+ ## FAQ
232
+
233
+ ### How do I connect to CUBRID with SQLAlchemy?
234
+
235
+ ```python
236
+ from sqlalchemy import create_engine
237
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
238
+ ```
239
+
240
+ For the pure Python driver (no C build needed): `create_engine("cubrid+pycubrid://dba@localhost:33000/demodb")`
241
+
242
+ ### Does sqlalchemy-cubrid support SQLAlchemy 2.0–2.1?
243
+
244
+ 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.
245
+
246
+ ### Does sqlalchemy-cubrid support Alembic migrations?
247
+
248
+ 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.
249
+
250
+ ### What Python versions are supported?
251
+
252
+ Python 3.10, 3.11, 3.12, 3.13, and 3.14.
253
+
254
+ ### Does CUBRID support RETURNING clauses?
255
+
256
+ No. CUBRID does not support `INSERT ... RETURNING` or `UPDATE ... RETURNING`. Use `cursor.lastrowid` or `SELECT LAST_INSERT_ID()` instead.
257
+
258
+ ### How do I use ON DUPLICATE KEY UPDATE with CUBRID?
259
+
260
+ ```python
261
+ from sqlalchemy_cubrid import insert
262
+ stmt = insert(users).values(name="Alice").on_duplicate_key_update(name="Alice Updated")
263
+ ```
264
+
265
+ ### What's the difference between `cubrid://` and `cubrid+pycubrid://`?
266
+
267
+ `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`.
268
+
269
+ ### Does sqlalchemy-cubrid support async?
270
+
271
+ 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`.
272
+
273
+
274
+ ## Related Projects
275
+
276
+ - [pycubrid](https://github.com/cubrid-labs/pycubrid) — Pure Python DB-API 2.0 driver for CUBRID
277
+ - [cubrid-cookbook-python](https://github.com/cubrid-labs/cubrid-cookbook-python) — Production-ready Python examples for CUBRID
278
+
279
+ ## Roadmap
280
+
281
+ See [`ROADMAP.md`](ROADMAP.md) for this project's direction and next milestones.
282
+
283
+ 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).
284
+
285
+ ## Contributing
286
+
287
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for development setup.
288
+
289
+ ## Security
290
+
291
+ Report vulnerabilities via email -- see [SECURITY.md](SECURITY.md). Do not open public issues for security concerns.
292
+
293
+ ## License
294
+
295
+ MIT -- see [LICENSE](LICENSE).
@@ -0,0 +1,245 @@
1
+ # sqlalchemy-cubrid
2
+
3
+ **SQLAlchemy 2.0–2.1 dialect for the CUBRID database** — Python ORM, schema reflection, Alembic migrations, and type mapping for SQLAlchemy and CUBRID-specific types.
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
+ > **Status: Beta.** The core public API follows semantic versioning; minor releases may add features and bug fixes while the project remains under active development.
20
+
21
+ ## Why sqlalchemy-cubrid?
22
+
23
+ CUBRID is a high-performance open-source relational database, widely adopted in
24
+ Korean public-sector and enterprise applications. Until now, there was no
25
+ production-ready SQLAlchemy dialect that supports the modern 2.0–2.1 API.
26
+
27
+ **sqlalchemy-cubrid** bridges that gap:
28
+
29
+ - Full SQLAlchemy 2.0–2.1 dialect with **statement caching** and **PEP 561 typing**
30
+ - **577 offline tests** with **99%+ code coverage** — no database required to run them
31
+ - Tested against **4 CUBRID versions** (10.2, 11.0, 11.2, 11.4) across **Python 3.10 -- 3.14**
32
+ - CUBRID-specific DML constructs: `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`
33
+ - Alembic migration support out of the box
34
+ - **Three driver options** — C-extension (`cubrid://`), pure Python (`cubrid+pycubrid://`), or async pure Python (`cubrid+aiopycubrid://`)
35
+
36
+ ## Architecture
37
+
38
+ ```mermaid
39
+ flowchart TD
40
+ app["Application"] --> sa["SQLAlchemy Core/ORM"]
41
+ sa --> dialect["CubridDialect"]
42
+ dialect --> pycubrid["pycubrid driver"]
43
+ dialect --> cext["CUBRIDdb driver"]
44
+ dialect --> aio["pycubrid.aio async driver"]
45
+ pycubrid --> server["CUBRID Server"]
46
+ cext --> server
47
+ aio --> server
48
+ ```
49
+
50
+ ```mermaid
51
+ flowchart TD
52
+ expr["SQL Expression"] --> compiler["CubridSQLCompiler"] --> sql["SQL String"]
53
+ ```
54
+
55
+ ## Requirements
56
+
57
+ - Python 3.10+
58
+ - SQLAlchemy 2.0 – 2.1
59
+ - [CUBRID-Python](https://github.com/CUBRID/cubrid-python) (C-extension) **or** [pycubrid](https://github.com/sqlalchemy-cubrid/pycubrid) (pure Python)
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install sqlalchemy-cubrid
65
+ ```
66
+
67
+ With the pure Python driver (no C build needed):
68
+
69
+ ```bash
70
+ pip install "sqlalchemy-cubrid[pycubrid]"
71
+ ```
72
+
73
+ With Alembic support:
74
+
75
+ ```bash
76
+ pip install "sqlalchemy-cubrid[alembic]"
77
+ ```
78
+
79
+ ## Quick Start
80
+
81
+ ### Core (Connection-Level)
82
+
83
+ ```python
84
+ from sqlalchemy import create_engine, text
85
+
86
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
87
+
88
+ with engine.connect() as conn:
89
+ result = conn.execute(text("SELECT 1"))
90
+ print(result.scalar())
91
+ ```
92
+
93
+ ### ORM (Session-Level)
94
+
95
+ ```python
96
+ from sqlalchemy import create_engine, String
97
+ from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
98
+
99
+
100
+ class Base(DeclarativeBase):
101
+ pass
102
+
103
+
104
+ class User(Base):
105
+ __tablename__ = "users"
106
+
107
+ id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
108
+ name: Mapped[str] = mapped_column(String(100))
109
+ email: Mapped[str] = mapped_column(String(200), unique=True)
110
+
111
+
112
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
113
+ Base.metadata.create_all(engine)
114
+
115
+ with Session(engine) as session:
116
+ user = User(name="Alice", email="alice@example.com")
117
+ session.add(user)
118
+ session.commit()
119
+ ```
120
+
121
+ ### Async
122
+
123
+ ```python
124
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
125
+ from sqlalchemy import text
126
+
127
+ engine = create_async_engine("cubrid+aiopycubrid://dba:password@localhost:33000/demodb")
128
+
129
+ async with AsyncSession(engine) as session:
130
+ result = await session.execute(text("SELECT 1"))
131
+ print(result.scalar())
132
+ ```
133
+
134
+ ## Features
135
+
136
+ - Type mapping for SQLAlchemy standard and CUBRID-specific types — numeric, string, date/time, bit, LOB, collection, and JSON types
137
+ - SQL compilation -- SELECT, JOIN, CAST, LIMIT/OFFSET, subqueries, CTEs, window functions
138
+ - DML extensions -- `ON DUPLICATE KEY UPDATE`, `MERGE`, `REPLACE INTO`, `FOR UPDATE`, `TRUNCATE`
139
+ - DDL support -- `COMMENT`, `IF NOT EXISTS` / `IF EXISTS`, `AUTO_INCREMENT`
140
+ - Schema reflection -- tables, views, columns, PKs, FKs, indexes, unique constraints, comments
141
+ - Alembic migrations via `CubridImpl` (auto-discovered entry point)
142
+ - All 6 CUBRID isolation levels (dual-granularity: class-level + instance-level)
143
+ - Async support (available since v1.1.0) — `create_async_engine("cubrid+aiopycubrid://...")` via pycubrid.aio
144
+
145
+ ## Known Limitations
146
+
147
+ - **No `RETURNING`** — `INSERT/UPDATE/DELETE ... RETURNING` not supported; use `cursor.lastrowid` or `LAST_INSERT_ID()`
148
+ - **No sequences** — CUBRID uses `AUTO_INCREMENT` only
149
+ - **No multi-schema** — single schema per database
150
+ - **DDL auto-commits** — migrations are not transactional (`transactional_ddl = False`)
151
+ - **SQLAlchemy 2.0–2.1 only** — pinned to `<2.2` due to internal API dependencies ([details](docs/ARCHITECTURE.md))
152
+ - **Async requires pycubrid ≥ 1.1.0** — the `cubrid+aiopycubrid://` driver needs the async-capable pycubrid
153
+
154
+ ## Documentation
155
+
156
+ | Guide | Description |
157
+ |---|---|
158
+ | [Connection](docs/CONNECTION.md) | Connection strings, URL format, driver setup, pool tuning |
159
+ | [Type Mapping](docs/TYPES.md) | Full type mapping, CUBRID-specific types, collection types |
160
+ | [DML Extensions](docs/DML_EXTENSIONS.md) | ON DUPLICATE KEY UPDATE, MERGE, REPLACE INTO, query trace |
161
+ | [Isolation Levels](docs/ISOLATION_LEVELS.md) | All 6 CUBRID isolation levels, configuration |
162
+ | [Alembic Migrations](docs/ALEMBIC.md) | Setup, configuration, limitations, batch workarounds |
163
+ | [Feature Support](docs/FEATURE_SUPPORT.md) | Comparison with MySQL, PostgreSQL, SQLite |
164
+ | [ORM Cookbook](docs/ORM_COOKBOOK.md) | Practical ORM examples, relationships, queries |
165
+ | [Development](docs/DEVELOPMENT.md) | Dev setup, testing, Docker, coverage, CI/CD |
166
+ | [Driver Compatibility](docs/DRIVER_COMPAT.md) | CUBRID-Python driver versions and known issues |
167
+ | [Troubleshooting](docs/TROUBLESHOOTING.md) | Common issues, error solutions, debugging techniques |
168
+ | [Async Connection](docs/CONNECTION.md#async-connection) | Async engine setup with `cubrid+aiopycubrid://` |
169
+
170
+ ## Compatibility Matrix
171
+
172
+ | Component | Supported versions |
173
+ |---|---|
174
+ | Python | 3.10, 3.11, 3.12, 3.13, 3.14 |
175
+ | CUBRID | 10.2, 11.0, 11.2, 11.4 |
176
+ | SQLAlchemy | 2.0–2.1 |
177
+ | Alembic | >=1.7 |
178
+ | pycubrid (sync) | >=1.2.0 |
179
+ | pycubrid (async) | >=1.2.0 |
180
+
181
+ ## FAQ
182
+
183
+ ### How do I connect to CUBRID with SQLAlchemy?
184
+
185
+ ```python
186
+ from sqlalchemy import create_engine
187
+ engine = create_engine("cubrid://dba:password@localhost:33000/demodb")
188
+ ```
189
+
190
+ For the pure Python driver (no C build needed): `create_engine("cubrid+pycubrid://dba@localhost:33000/demodb")`
191
+
192
+ ### Does sqlalchemy-cubrid support SQLAlchemy 2.0–2.1?
193
+
194
+ 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.
195
+
196
+ ### Does sqlalchemy-cubrid support Alembic migrations?
197
+
198
+ 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.
199
+
200
+ ### What Python versions are supported?
201
+
202
+ Python 3.10, 3.11, 3.12, 3.13, and 3.14.
203
+
204
+ ### Does CUBRID support RETURNING clauses?
205
+
206
+ No. CUBRID does not support `INSERT ... RETURNING` or `UPDATE ... RETURNING`. Use `cursor.lastrowid` or `SELECT LAST_INSERT_ID()` instead.
207
+
208
+ ### How do I use ON DUPLICATE KEY UPDATE with CUBRID?
209
+
210
+ ```python
211
+ from sqlalchemy_cubrid import insert
212
+ stmt = insert(users).values(name="Alice").on_duplicate_key_update(name="Alice Updated")
213
+ ```
214
+
215
+ ### What's the difference between `cubrid://` and `cubrid+pycubrid://`?
216
+
217
+ `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`.
218
+
219
+ ### Does sqlalchemy-cubrid support async?
220
+
221
+ 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`.
222
+
223
+
224
+ ## Related Projects
225
+
226
+ - [pycubrid](https://github.com/cubrid-labs/pycubrid) — Pure Python DB-API 2.0 driver for CUBRID
227
+ - [cubrid-cookbook-python](https://github.com/cubrid-labs/cubrid-cookbook-python) — Production-ready Python examples for CUBRID
228
+
229
+ ## Roadmap
230
+
231
+ See [`ROADMAP.md`](ROADMAP.md) for this project's direction and next milestones.
232
+
233
+ 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).
234
+
235
+ ## Contributing
236
+
237
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines and [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for development setup.
238
+
239
+ ## Security
240
+
241
+ Report vulnerabilities via email -- see [SECURITY.md](SECURITY.md). Do not open public issues for security concerns.
242
+
243
+ ## License
244
+
245
+ 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.3.0"
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 :: 4 - Beta",
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"