Project knowledge base for AI coding agents.
sqlalchemy-cubrid is a SQLAlchemy 2.0 dialect for the CUBRID relational database. It provides SQL compilation, type mapping, schema reflection, DDL/DML extensions, Alembic migration support, and PEP 561 typing.
- Language: Python 3.10+
- Framework: SQLAlchemy 2.0 – 2.1
- License: MIT
- Version: 1.4.3.dev0 (Production/Stable)
graph TD
root["sqlalchemy_cubrid/ (Main package, 12 Python modules + py.typed)"]
init["__init__.py - Public API exports types, insert(), merge(), replace(), trace_query(), __version__"]
compat["_compat.py - SQLAlchemy private API compatibility helpers"]
base["base.py - CubridExecutionContext, CubridIdentifierPreparer"]
compiler["compiler.py - CubridSQLCompiler, CubridDDLCompiler, CubridTypeCompiler"]
dialect["dialect.py - CubridDialect reflection, connection, isolation levels"]
pycubrid["pycubrid_dialect.py - PyCubridDialect pure Python driver variant"]
aio["aio_pycubrid_dialect.py - PyCubridAsyncDialect async driver variant"]
dml["dml.py - ON DUPLICATE KEY UPDATE (Insert), MERGE statement"]
trace["trace.py - Query tracing helper"]
types["types.py - CUBRID type system numeric, string, LOB, collection"]
req["requirements.py - SA 2.0 test requirement flags (40+ properties)"]
alembic["alembic_impl.py - CubridImpl for Alembic migrations"]
typed["py.typed - PEP 561 marker"]
root --> init
root --> compat
root --> base
root --> compiler
root --> dialect
root --> pycubrid
root --> aio
root --> dml
root --> trace
root --> types
root --> req
root --> alembic
root --> typed
| Module | Role |
|---|---|
dialect.py |
Main dialect class. Handles create_connect_args, reflection (get_columns, get_pk_constraint, get_foreign_keys, get_indexes, get_table_comment, etc.), isolation levels, import_dbapi(). |
pycubrid_dialect.py |
PyCubridDialect — subclasses CubridDialect for the pycubrid pure Python driver. Overrides import_dbapi(), create_connect_args(), on_connect(), do_ping(). |
aio_pycubrid_dialect.py |
PyCubridAsyncDialect — async pycubrid variant for create_async_engine() / AsyncSession, registered as cubrid.aiopycubrid. |
compiler.py |
SQL compilation. visit_cast, limit_clause, for_update_clause, update_limit_clause, DDL (get_column_specification, AUTO_INCREMENT, COMMENT), type compilation for all CUBRID types. |
dml.py |
Custom DML constructs: insert() with .on_duplicate_key_update(), merge() with .using(), .on(), .when_matched_then_update(), .when_not_matched_then_insert(). |
types.py |
Type classes: STRING, BIT, CLOB, SET, MULTISET, SEQUENCE, MONETARY, OBJECT, plus standard type overrides. |
base.py |
Execution context (get_lastrowid), identifier preparer (lowercase folding, 254-char max, reserved words). |
trace.py |
trace_query() helper that enables CUBRID tracing around a statement and returns trace output. |
requirements.py |
Test requirement flags — marks what CUBRID does/doesn't support for SA's test suite. |
alembic_impl.py |
CubridImpl(DefaultImpl) with transactional_ddl = False. Auto-discovered via alembic.ddl entry point. |
_compat.py |
Internal compatibility helpers that wrap SQLAlchemy private APIs used by the dialect/compiler. |
[project.entry-points."sqlalchemy.dialects"]
cubrid = "sqlalchemy_cubrid.dialect:CubridDialect"
"cubrid.cubrid" = "sqlalchemy_cubrid.dialect:CubridDialect"
"cubrid.pycubrid" = "sqlalchemy_cubrid.pycubrid_dialect:PyCubridDialect"
"cubrid.aiopycubrid" = "sqlalchemy_cubrid.aio_pycubrid_dialect:PyCubridAsyncDialect"
[project.entry-points."alembic.ddl"]
cubrid = "sqlalchemy_cubrid.alembic_impl:CubridImpl"git clone https://github.com/cubrid-lab/sqlalchemy-cubrid.git
cd sqlalchemy-cubrid
make install # pip install -e ".[dev]" + pytest-cov + pre-commit + toxmake test # Offline tests with 95% coverage threshold
make lint # ruff check + format
make format # Auto-fix lint/format
make integration # Docker → integration tests → cleanup
make test-all # tox across Python 3.10–3.14# Offline (no DB needed) — this is the primary test command
pytest test/ -v --ignore=test/test_integration.py --ignore=test/test_suite.py \
--cov=sqlalchemy_cubrid --cov-report=term-missing --cov-fail-under=95
# Integration (requires Docker)
docker compose up -d
export CUBRID_TEST_URL="cubrid://dba@localhost:33000/testdb"
pytest test/test_integration.py -vdocker compose up -d # Default CUBRID 11.2
CUBRID_VERSION=11.4 docker compose up -d # Specific version
docker compose down -v # Cleanup- Linter/Formatter: Ruff
- Line length: 100 characters
- Target Python: 3.10+
- Imports:
from __future__ import annotationsin every module - Type hints: Full typing; PEP 561 compliant (
py.typed) - super(): Always
super().__init__(), neversuper(ClassName, self)
- Classes:
CubridDialect,CubridSQLCompiler,CubridTypeCompiler,CubridDDLCompiler - Test classes:
TestCubridSQLCompiler,TestCubridTypes, etc. - Test files:
test/test_*.py
- All SA dialect methods use
@cache_anon_map/@reflection.cachewhere appropriate - Reflection methods accept
**kwand usetext()for parameterized queries (no f-string SQL) - Type compiler methods:
visit_TYPENAME(self, type_, **kw)returning SQL string supports_statement_cache = True— required for SA 2.0
- No
as any,@ts-ignoreequivalents — no type suppression - No f-string interpolation in SQL queries (SQL injection risk)
- No
super(ClassName, self)— usesuper()only - No Python 2 constructs (
basestring,getargspec, etc.) - No empty
exceptblocks
All non-trivial work across cubrid-lab repositories MUST follow this 4-phase cycle:
- Oracle Design Review — Consult Oracle before implementation to validate architecture, API surface, and approach. Raise concerns early.
- Implementation — Build the feature/fix with tests. Follow existing codebase patterns.
- Documentation Update — Update ALL affected docs (README, CHANGELOG, ROADMAP, API docs, SUPPORT_MATRIX, PRD, etc.) in the same PR or as an immediate follow-up. Code without doc updates is incomplete.
- Oracle Post-Implementation Review — Consult Oracle to review the completed work for correctness, edge cases, and consistency before merging.
Skipping any phase requires explicit justification. Trivial changes (typos, single-line fixes) may skip phases 1 and 4.
- All changes to
mainMUST go through a Pull Request with at least one review. No direct pushes.
test/
├── conftest.py # Fixtures: mock dialect, engine, connection
├── test_compiler.py # SQL compilation (SELECT, JOIN, CAST, LIMIT, etc.)
├── test_types.py # Type system (all type compilations, reflection)
├── test_dialect_offline.py # Dialect (reflection stubs, connection, isolation)
├── test_base.py # ExecutionContext, IdentifierPreparer
├── test_requirements.py # SA requirement flags (parametrized)
├── test_dml.py # ON DUPLICATE KEY UPDATE, MERGE compilation
├── test_alembic.py # Alembic CubridImpl import/registry
├── test_dialects.py # Edge cases
├── test_pycubrid_dialect.py # PyCubridDialect (pure Python driver variant)
├── test_integration.py # Live DB tests (skipped offline)
└── test_suite.py # SA test suite runner (skipped offline)
- 619 offline tests + 35 sync integration tests + 16 async integration tests, ~98.26% offline coverage
- Coverage threshold: 95% (CI-enforced)
- 6 unreachable lines (defensive fallbacks):
compiler.py:72,compiler.py:84,compiler.py:298-300,dml.py:310
Most tests are offline — they mock the CUBRID connection and test SQL compilation,
type mapping, and reflection logic without a database. Only test_integration.py and
test_suite.py need a live CUBRID instance.
- No RETURNING —
INSERT/UPDATE/DELETE ... RETURNINGnot supported - No native BOOLEAN — mapped to
SMALLINT(0/1) - JSON support (10.2+) — native JSON type is mapped in this dialect, including path/index helpers and reflection support
- No ARRAY — uses
SET,MULTISET,SEQUENCEcollection types - No Sequences — uses
AUTO_INCREMENT - No multi-schema — single-schema model
- No RELEASE SAVEPOINT —
do_release_savepoint()is a no-op - DDL auto-commits —
transactional_ddl = False - 6 isolation levels — dual-granularity (class-level + instance-level)
- Identifier folding — lowercase (not uppercase like SQL standard)
- Max identifier length — 254 characters
SQLAlchemy URL: cubrid://user:password@host:port/dbname
CUBRID native: CUBRID:host:port:dbname:::
The dialect translates automatically in create_connect_args().
10.2, 11.0, 11.2, 11.4 — via Docker images cubrid/cubrid:{version}.
| File | Trigger | Purpose |
|---|---|---|
.github/workflows/ci.yml |
Push to main, PRs | Lint + offline tests (Py 3.10–3.14) + regular integration matrix |
.github/workflows/integration-full.yml |
Nightly (03:00 UTC), tag push, manual dispatch | Full Python × CUBRID compatibility matrix |
.github/workflows/publish-pypi.yml |
GitHub Release | Build and publish to PyPI |
- Offline (every PR/push): Python 3.10, 3.11, 3.12, 3.13, 3.14
- Integration (every PR/push): Python {3.10, 3.14} × CUBRID {10.2, 11.0, 11.2, 11.4} — 8 jobs
- Integration full (nightly + tag push + dispatch): Python {3.10, 3.11, 3.12, 3.13, 3.14} × CUBRID {10.2, 11.0, 11.2, 11.4} — 20 jobs
| File | Content |
|---|---|
README.md |
Concise landing page (~80 lines) |
docs/CONNECTION.md |
Connection strings, URL format, driver setup |
docs/TYPES.md |
Full type mapping, CUBRID-specific types |
docs/ISOLATION_LEVELS.md |
All 6 CUBRID isolation levels |
docs/DML_EXTENSIONS.md |
ON DUPLICATE KEY UPDATE, MERGE, GROUP_CONCAT, TRUNCATE |
docs/ALEMBIC.md |
Alembic migration guide, limitations, workarounds |
docs/FEATURE_SUPPORT.md |
Feature comparison with MySQL, PostgreSQL, SQLite |
docs/DEVELOPMENT.md |
Dev setup, testing, Docker, coverage, CI/CD |
docs/ORM_COOKBOOK.md |
Practical ORM usage examples with CUBRID |
docs/PRD.md |
Product requirements document |
CHANGELOG.md |
Release history (Keep a Changelog format) |
CONTRIBUTING.md |
Contribution guidelines |
SECURITY.md |
Security vulnerability reporting |
docs/DRIVER_COMPAT.md |
CUBRID-Python driver versions and known issues |
docs/TROUBLESHOOTING.md |
Common issues, error solutions, debugging techniques |
<type>(<scope>): <imperative summary of WHAT changed>
- <bullet 1: specific change with file/function context>
- <bullet 2: ...>
Closes #<issue>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
| Type | When to use |
|---|---|
feat |
New user-facing capability (new DML construct, new type, new dialect flag) |
fix |
Bug fix — corrects wrong behavior |
refactor |
Internal restructuring with no behavior change |
test |
Add/update tests only |
docs |
Documentation only |
chore |
Tooling, deps, CI config, version bumps |
ci |
CI workflow changes |
perf |
Performance improvement with measurable impact |
- Subject line describes WHAT changed, not batch/phase labels.
- ❌
fix: batch 4 correctness fixes for 1.0 readiness - ✅
fix: preserve TZ reflection, STRING national kwarg, and UPDATE LIMIT 0
- ❌
- Be specific — name the function, type, or behavior.
- ❌
feat: gap fixes, stability messaging, code quality improvements - ✅
fix: FK actions regex, Alembic guardrails, FULL JOIN/LATERAL rejection
- ❌
- One logical change per commit. If subject needs "and" more than once, split.
- Never use internal jargon (batch N, phase N, readiness) — commit history is public.
- Type must match content:
feat= new capability that didn't exist beforefix= something was broken and is now correctrefactor= code moved/restructured but behavior unchanged- Don't label a fix as
feator a mixed bag asfeat
- Body bullets reference issue numbers (
#135,#136) for traceability. - Scope is optional but use it for module-specific changes:
fix(compiler):,feat(types):. - Version in commit message must match actual project version. Never reference "1.0" when project is v1.4.x.
- Update version in
pyproject.tomlandsqlalchemy_cubrid/__init__.py - Add changelog entry in
CHANGELOG.md - Commit, tag (
v{major}.{minor}.{patch}), push with tags - Create GitHub release via
gh release create - PyPI publish triggers automatically from the release
This repo provides ORM-level validation of the Performance Loop. Board: CUBRID Ecosystem Roadmap
sqlalchemy-cubrid proves that pycubrid driver optimizations propagate to the application layer. Tier 2 ORM benchmarks (in cubrid-benchmark) measure this repo's overhead vs raw pycubrid.
| Issue | Phase | Priority |
|---|---|---|
| #70 Optimize query compilation and result mapping | R3 | Must-Have |
| #68 Configure PyPI Trusted Publisher | R0 | Nice-to-Have |
- Query compile path optimization (compiler.py)
- Result mapping improvement (dialect.py)
- Bulk insert optimization (executemany/insertmanyvalues)
- Quantify ORM overhead vs raw driver (% of total time)