sqlalchemy-pyaltibase 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.
- sqlalchemy_pyaltibase-0.1.0/LICENSE +21 -0
- sqlalchemy_pyaltibase-0.1.0/PKG-INFO +88 -0
- sqlalchemy_pyaltibase-0.1.0/README.md +52 -0
- sqlalchemy_pyaltibase-0.1.0/pyproject.toml +81 -0
- sqlalchemy_pyaltibase-0.1.0/setup.cfg +4 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_altibase/__init__.py +54 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_altibase/base.py +154 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_altibase/compiler.py +226 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_altibase/dialect.py +572 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_altibase/types.py +180 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/PKG-INFO +88 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/SOURCES.txt +18 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/dependency_links.txt +1 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/entry_points.txt +3 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/requires.txt +9 -0
- sqlalchemy_pyaltibase-0.1.0/sqlalchemy_pyaltibase.egg-info/top_level.txt +1 -0
- sqlalchemy_pyaltibase-0.1.0/test/test_base.py +69 -0
- sqlalchemy_pyaltibase-0.1.0/test/test_compiler.py +227 -0
- sqlalchemy_pyaltibase-0.1.0/test/test_dialect_offline.py +353 -0
- sqlalchemy_pyaltibase-0.1.0/test/test_types.py +100 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Yeongseon Choe
|
|
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.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlalchemy-pyaltibase
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Altibase dialect for SQLAlchemy
|
|
5
|
+
Author-email: Yeongseon Choe <yeongseon.choe@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yeongseon/sqlalchemy-pyaltibase
|
|
8
|
+
Project-URL: Repository, https://github.com/yeongseon/sqlalchemy-pyaltibase
|
|
9
|
+
Project-URL: Issues, https://github.com/yeongseon/sqlalchemy-pyaltibase/issues
|
|
10
|
+
Keywords: SQLAlchemy,Altibase,dialect
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: SQL
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Classifier: Topic :: Database :: Front-Ends
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: sqlalchemy<2.2,>=2.0
|
|
29
|
+
Provides-Extra: pyaltibase
|
|
30
|
+
Requires-Dist: pyaltibase; extra == "pyaltibase"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff==0.15.6; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# sqlalchemy-pyaltibase
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/sqlalchemy-pyaltibase)
|
|
40
|
+
[](https://github.com/yeongseon/sqlalchemy-pyaltibase/actions/workflows/ci.yml)
|
|
41
|
+
[](https://github.com/yeongseon/sqlalchemy-pyaltibase/blob/main/LICENSE)
|
|
42
|
+
|
|
43
|
+
SQLAlchemy 2.0 dialect for the Altibase database, backed by `pyaltibase`.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install sqlalchemy-pyaltibase
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With DB-API dependency:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install "sqlalchemy-pyaltibase[pyaltibase]"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from sqlalchemy import create_engine, text
|
|
61
|
+
|
|
62
|
+
engine = create_engine("altibase://user:password@localhost:20300/mydb")
|
|
63
|
+
|
|
64
|
+
with engine.connect() as conn:
|
|
65
|
+
value = conn.execute(text("SELECT 1 FROM DUAL")).scalar()
|
|
66
|
+
print(value)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Architecture
|
|
70
|
+
|
|
71
|
+
```mermaid
|
|
72
|
+
flowchart TD
|
|
73
|
+
app["Application"] --> sa["SQLAlchemy Core/ORM"]
|
|
74
|
+
sa --> dialect["AltibaseDialect"]
|
|
75
|
+
dialect --> dbapi["pyaltibase"]
|
|
76
|
+
dbapi --> server["Altibase Server"]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Development
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
make lint
|
|
83
|
+
make test
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# sqlalchemy-pyaltibase
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/sqlalchemy-pyaltibase)
|
|
4
|
+
[](https://github.com/yeongseon/sqlalchemy-pyaltibase/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/yeongseon/sqlalchemy-pyaltibase/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
SQLAlchemy 2.0 dialect for the Altibase database, backed by `pyaltibase`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install sqlalchemy-pyaltibase
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
With DB-API dependency:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install "sqlalchemy-pyaltibase[pyaltibase]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from sqlalchemy import create_engine, text
|
|
25
|
+
|
|
26
|
+
engine = create_engine("altibase://user:password@localhost:20300/mydb")
|
|
27
|
+
|
|
28
|
+
with engine.connect() as conn:
|
|
29
|
+
value = conn.execute(text("SELECT 1 FROM DUAL")).scalar()
|
|
30
|
+
print(value)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
flowchart TD
|
|
37
|
+
app["Application"] --> sa["SQLAlchemy Core/ORM"]
|
|
38
|
+
sa --> dialect["AltibaseDialect"]
|
|
39
|
+
dialect --> dbapi["pyaltibase"]
|
|
40
|
+
dbapi --> server["Altibase Server"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
make lint
|
|
47
|
+
make test
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sqlalchemy-pyaltibase"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Altibase dialect for SQLAlchemy"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Yeongseon Choe", email = "yeongseon.choe@gmail.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["SQLAlchemy", "Altibase", "dialect"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: SQL",
|
|
29
|
+
"Topic :: Database",
|
|
30
|
+
"Topic :: Database :: Front-Ends",
|
|
31
|
+
]
|
|
32
|
+
dependencies = [
|
|
33
|
+
"sqlalchemy>=2.0,<2.2",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
pyaltibase = ["pyaltibase"]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.0",
|
|
40
|
+
"pytest-cov",
|
|
41
|
+
"ruff==0.15.6",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/yeongseon/sqlalchemy-pyaltibase"
|
|
46
|
+
Repository = "https://github.com/yeongseon/sqlalchemy-pyaltibase"
|
|
47
|
+
Issues = "https://github.com/yeongseon/sqlalchemy-pyaltibase/issues"
|
|
48
|
+
|
|
49
|
+
[project.entry-points."sqlalchemy.dialects"]
|
|
50
|
+
altibase = "sqlalchemy_altibase.dialect:AltibaseDialect"
|
|
51
|
+
"altibase.pyaltibase" = "sqlalchemy_altibase.dialect:AltibaseDialect"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools]
|
|
54
|
+
packages = ["sqlalchemy_altibase"]
|
|
55
|
+
include-package-data = true
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
line-length = 100
|
|
59
|
+
target-version = "py310"
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint.isort]
|
|
62
|
+
known-first-party = ["sqlalchemy_altibase"]
|
|
63
|
+
|
|
64
|
+
[tool.coverage.run]
|
|
65
|
+
source = ["sqlalchemy_altibase"]
|
|
66
|
+
|
|
67
|
+
[tool.coverage.report]
|
|
68
|
+
show_missing = true
|
|
69
|
+
fail_under = 95
|
|
70
|
+
|
|
71
|
+
[tool.pytest.ini_options]
|
|
72
|
+
testpaths = ["test"]
|
|
73
|
+
addopts = "--tb native -v -r fxX --maxfail=25 -p no:warnings"
|
|
74
|
+
python_files = "test/*test_*.py"
|
|
75
|
+
|
|
76
|
+
[tool.basedpyright]
|
|
77
|
+
typeCheckingMode = "off"
|
|
78
|
+
reportUnsafeMultipleInheritance = "none"
|
|
79
|
+
reportMissingTypeArgument = "none"
|
|
80
|
+
reportIncompatibleMethodOverride = "none"
|
|
81
|
+
reportAssignmentType = "none"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from .dialect import AltibaseDialect
|
|
2
|
+
from .types import (
|
|
3
|
+
BIGINT,
|
|
4
|
+
BIT,
|
|
5
|
+
BLOB,
|
|
6
|
+
BYTE,
|
|
7
|
+
CHAR,
|
|
8
|
+
CLOB,
|
|
9
|
+
DATE,
|
|
10
|
+
DECIMAL,
|
|
11
|
+
DOUBLE,
|
|
12
|
+
FLOAT,
|
|
13
|
+
GEOMETRY,
|
|
14
|
+
INTEGER,
|
|
15
|
+
NCHAR,
|
|
16
|
+
NIBBLE,
|
|
17
|
+
NUMERIC,
|
|
18
|
+
NVARCHAR,
|
|
19
|
+
REAL,
|
|
20
|
+
SERIAL,
|
|
21
|
+
SMALLINT,
|
|
22
|
+
VARBIT,
|
|
23
|
+
VARBYTE,
|
|
24
|
+
VARCHAR,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"__version__",
|
|
31
|
+
"AltibaseDialect",
|
|
32
|
+
"NUMERIC",
|
|
33
|
+
"DECIMAL",
|
|
34
|
+
"FLOAT",
|
|
35
|
+
"REAL",
|
|
36
|
+
"DOUBLE",
|
|
37
|
+
"SMALLINT",
|
|
38
|
+
"INTEGER",
|
|
39
|
+
"BIGINT",
|
|
40
|
+
"SERIAL",
|
|
41
|
+
"VARCHAR",
|
|
42
|
+
"CHAR",
|
|
43
|
+
"NCHAR",
|
|
44
|
+
"NVARCHAR",
|
|
45
|
+
"CLOB",
|
|
46
|
+
"BLOB",
|
|
47
|
+
"DATE",
|
|
48
|
+
"BIT",
|
|
49
|
+
"VARBIT",
|
|
50
|
+
"BYTE",
|
|
51
|
+
"VARBYTE",
|
|
52
|
+
"NIBBLE",
|
|
53
|
+
"GEOMETRY",
|
|
54
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# pyright: reportIncompatibleMethodOverride=false
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
from sqlalchemy.engine import default
|
|
7
|
+
from sqlalchemy.sql import compiler
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
AUTOCOMMIT_REGEXP = re.compile(
|
|
11
|
+
r"\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER|MERGE|TRUNCATE)", re.I | re.UNICODE
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
RESERVED_WORDS = frozenset(
|
|
15
|
+
{
|
|
16
|
+
"access",
|
|
17
|
+
"add",
|
|
18
|
+
"all",
|
|
19
|
+
"alter",
|
|
20
|
+
"and",
|
|
21
|
+
"any",
|
|
22
|
+
"as",
|
|
23
|
+
"at",
|
|
24
|
+
"between",
|
|
25
|
+
"by",
|
|
26
|
+
"cascade",
|
|
27
|
+
"case",
|
|
28
|
+
"check",
|
|
29
|
+
"column",
|
|
30
|
+
"connect",
|
|
31
|
+
"constraint",
|
|
32
|
+
"create",
|
|
33
|
+
"cross",
|
|
34
|
+
"current",
|
|
35
|
+
"cursor",
|
|
36
|
+
"database",
|
|
37
|
+
"date",
|
|
38
|
+
"decimal",
|
|
39
|
+
"default",
|
|
40
|
+
"delete",
|
|
41
|
+
"desc",
|
|
42
|
+
"distinct",
|
|
43
|
+
"drop",
|
|
44
|
+
"each",
|
|
45
|
+
"else",
|
|
46
|
+
"end",
|
|
47
|
+
"escape",
|
|
48
|
+
"exception",
|
|
49
|
+
"exec",
|
|
50
|
+
"exists",
|
|
51
|
+
"float",
|
|
52
|
+
"for",
|
|
53
|
+
"foreign",
|
|
54
|
+
"from",
|
|
55
|
+
"full",
|
|
56
|
+
"grant",
|
|
57
|
+
"group",
|
|
58
|
+
"having",
|
|
59
|
+
"if",
|
|
60
|
+
"in",
|
|
61
|
+
"index",
|
|
62
|
+
"inner",
|
|
63
|
+
"insert",
|
|
64
|
+
"integer",
|
|
65
|
+
"intersect",
|
|
66
|
+
"into",
|
|
67
|
+
"is",
|
|
68
|
+
"join",
|
|
69
|
+
"key",
|
|
70
|
+
"left",
|
|
71
|
+
"level",
|
|
72
|
+
"like",
|
|
73
|
+
"limit",
|
|
74
|
+
"lock",
|
|
75
|
+
"merge",
|
|
76
|
+
"minus",
|
|
77
|
+
"modify",
|
|
78
|
+
"not",
|
|
79
|
+
"null",
|
|
80
|
+
"number",
|
|
81
|
+
"of",
|
|
82
|
+
"on",
|
|
83
|
+
"open",
|
|
84
|
+
"or",
|
|
85
|
+
"order",
|
|
86
|
+
"outer",
|
|
87
|
+
"primary",
|
|
88
|
+
"prior",
|
|
89
|
+
"privileges",
|
|
90
|
+
"public",
|
|
91
|
+
"raw",
|
|
92
|
+
"references",
|
|
93
|
+
"rename",
|
|
94
|
+
"replace",
|
|
95
|
+
"return",
|
|
96
|
+
"revoke",
|
|
97
|
+
"right",
|
|
98
|
+
"row",
|
|
99
|
+
"rowcount",
|
|
100
|
+
"rownum",
|
|
101
|
+
"rows",
|
|
102
|
+
"select",
|
|
103
|
+
"sequence",
|
|
104
|
+
"session",
|
|
105
|
+
"set",
|
|
106
|
+
"some",
|
|
107
|
+
"start",
|
|
108
|
+
"step",
|
|
109
|
+
"table",
|
|
110
|
+
"then",
|
|
111
|
+
"to",
|
|
112
|
+
"trigger",
|
|
113
|
+
"truncate",
|
|
114
|
+
"union",
|
|
115
|
+
"unique",
|
|
116
|
+
"update",
|
|
117
|
+
"user",
|
|
118
|
+
"using",
|
|
119
|
+
"values",
|
|
120
|
+
"varchar",
|
|
121
|
+
"view",
|
|
122
|
+
"when",
|
|
123
|
+
"where",
|
|
124
|
+
"with",
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AltibaseIdentifierPreparer(compiler.IdentifierPreparer):
|
|
130
|
+
reserved_words = RESERVED_WORDS
|
|
131
|
+
|
|
132
|
+
def __init__(
|
|
133
|
+
self,
|
|
134
|
+
dialect,
|
|
135
|
+
initial_quote='"',
|
|
136
|
+
final_quote=None,
|
|
137
|
+
escape_quote='"',
|
|
138
|
+
omit_schema=False,
|
|
139
|
+
):
|
|
140
|
+
super().__init__(dialect, initial_quote, final_quote, escape_quote, omit_schema)
|
|
141
|
+
|
|
142
|
+
def _quote_free_identifiers(self, *ids):
|
|
143
|
+
return tuple(self.quote_identifier(i) for i in ids if i is not None)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class AltibaseExecutionContext(default.DefaultExecutionContext):
|
|
147
|
+
def should_autocommit_text(self, statement):
|
|
148
|
+
return AUTOCOMMIT_REGEXP.match(statement)
|
|
149
|
+
|
|
150
|
+
def get_lastrowid(self):
|
|
151
|
+
try:
|
|
152
|
+
return self.cursor.lastrowid
|
|
153
|
+
except Exception:
|
|
154
|
+
return None
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# pyright: reportIncompatibleMethodOverride=false, reportArgumentType=false, reportUnusedParameter=false
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from sqlalchemy.sql import compiler
|
|
5
|
+
from sqlalchemy.sql import sqltypes
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AltibaseCompiler(compiler.SQLCompiler):
|
|
9
|
+
def visit_sysdate_func(self, fn, **kw):
|
|
10
|
+
return "SYSDATE"
|
|
11
|
+
|
|
12
|
+
def visit_dual_func(self, fn, **kw):
|
|
13
|
+
return "DUAL"
|
|
14
|
+
|
|
15
|
+
def default_from(self):
|
|
16
|
+
return " FROM DUAL"
|
|
17
|
+
|
|
18
|
+
def visit_cast(self, cast, **kw):
|
|
19
|
+
type_ = self.process(cast.typeclause)
|
|
20
|
+
if type_ is None:
|
|
21
|
+
return self.process(cast.clause.self_group())
|
|
22
|
+
return f"CAST({self.process(cast.clause)} AS {type_})"
|
|
23
|
+
|
|
24
|
+
def render_literal_value(self, value, type_):
|
|
25
|
+
value = super().render_literal_value(value, type_)
|
|
26
|
+
return value.replace("\\", "\\\\")
|
|
27
|
+
|
|
28
|
+
def get_select_precolumns(self, select, **kw):
|
|
29
|
+
if bool(select._distinct):
|
|
30
|
+
return "DISTINCT "
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
def visit_join(self, join, asfrom=False, **kwargs):
|
|
34
|
+
return "".join(
|
|
35
|
+
(
|
|
36
|
+
self.process(join.left, asfrom=True, **kwargs),
|
|
37
|
+
(join.isouter and " LEFT OUTER JOIN " or " INNER JOIN "),
|
|
38
|
+
self.process(join.right, asfrom=True, **kwargs),
|
|
39
|
+
" ON ",
|
|
40
|
+
self.process(join.onclause, **kwargs),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def for_update_clause(self, select, **kw):
|
|
45
|
+
if select._for_update_arg is None:
|
|
46
|
+
return ""
|
|
47
|
+
text = " FOR UPDATE"
|
|
48
|
+
if select._for_update_arg.of:
|
|
49
|
+
text += " OF " + ", ".join(self.process(col, **kw) for col in select._for_update_arg.of)
|
|
50
|
+
wait = getattr(select._for_update_arg, "wait", None)
|
|
51
|
+
if wait is not None:
|
|
52
|
+
text += f" WAIT {int(wait)}"
|
|
53
|
+
elif select._for_update_arg.nowait:
|
|
54
|
+
text += " NOWAIT"
|
|
55
|
+
return text
|
|
56
|
+
|
|
57
|
+
def limit_clause(self, select, **kw):
|
|
58
|
+
limit_clause = select._limit_clause
|
|
59
|
+
offset_clause = select._offset_clause
|
|
60
|
+
if limit_clause is None and offset_clause is None:
|
|
61
|
+
return ""
|
|
62
|
+
if limit_clause is None:
|
|
63
|
+
return "\n LIMIT 9223372036854775807 OFFSET %s" % self.process(offset_clause, **kw)
|
|
64
|
+
if offset_clause is None:
|
|
65
|
+
return "\n LIMIT %s" % self.process(limit_clause, **kw)
|
|
66
|
+
return "\n LIMIT %s OFFSET %s" % (
|
|
67
|
+
self.process(limit_clause, **kw),
|
|
68
|
+
self.process(offset_clause, **kw),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AltibaseDDLCompiler(compiler.DDLCompiler):
|
|
73
|
+
def get_column_specification(self, column, **kw):
|
|
74
|
+
colspec = [self.preparer.format_column(column)]
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
column.table is not None
|
|
78
|
+
and column is column.table._autoincrement_column
|
|
79
|
+
and column.server_default is None
|
|
80
|
+
):
|
|
81
|
+
colspec.append("SERIAL")
|
|
82
|
+
else:
|
|
83
|
+
colspec.append(
|
|
84
|
+
self.dialect.type_compiler_instance.process(column.type, type_expression=column)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if not column.nullable:
|
|
88
|
+
colspec.append("NOT NULL")
|
|
89
|
+
|
|
90
|
+
default = self.get_column_default_string(column)
|
|
91
|
+
if default is not None:
|
|
92
|
+
colspec.append("DEFAULT " + default)
|
|
93
|
+
|
|
94
|
+
return " ".join(colspec)
|
|
95
|
+
|
|
96
|
+
def post_create_table(self, table):
|
|
97
|
+
if table.comment is None:
|
|
98
|
+
return ""
|
|
99
|
+
literal = self.sql_compiler.render_literal_value(table.comment, sqltypes.String())
|
|
100
|
+
return f"\nCOMMENT ON TABLE {self.preparer.format_table(table)} IS {literal}"
|
|
101
|
+
|
|
102
|
+
def visit_set_table_comment(self, create, **kw):
|
|
103
|
+
return "COMMENT ON TABLE %s IS %s" % (
|
|
104
|
+
self.preparer.format_table(create.element),
|
|
105
|
+
self.sql_compiler.render_literal_value(create.element.comment, sqltypes.String()),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def visit_drop_table_comment(self, drop, **kw):
|
|
109
|
+
return "COMMENT ON TABLE %s IS ''" % (self.preparer.format_table(drop.element),)
|
|
110
|
+
|
|
111
|
+
def visit_set_column_comment(self, create, **kw):
|
|
112
|
+
return "COMMENT ON COLUMN %s.%s IS %s" % (
|
|
113
|
+
self.preparer.format_table(create.element.table),
|
|
114
|
+
self.preparer.format_column(create.element),
|
|
115
|
+
self.sql_compiler.render_literal_value(create.element.comment, sqltypes.String()),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class AltibaseTypeCompiler(compiler.GenericTypeCompiler):
|
|
120
|
+
def visit_BOOLEAN(self, type_, **kw):
|
|
121
|
+
return "SMALLINT"
|
|
122
|
+
|
|
123
|
+
def visit_NUMERIC(self, type_, **kw):
|
|
124
|
+
if type_.precision is None:
|
|
125
|
+
return "NUMERIC"
|
|
126
|
+
if type_.scale is None:
|
|
127
|
+
return f"NUMERIC({type_.precision})"
|
|
128
|
+
return f"NUMERIC({type_.precision}, {type_.scale})"
|
|
129
|
+
|
|
130
|
+
def visit_DECIMAL(self, type_, **kw):
|
|
131
|
+
if type_.precision is None:
|
|
132
|
+
return "DECIMAL"
|
|
133
|
+
if type_.scale is None:
|
|
134
|
+
return f"DECIMAL({type_.precision})"
|
|
135
|
+
return f"DECIMAL({type_.precision}, {type_.scale})"
|
|
136
|
+
|
|
137
|
+
def visit_FLOAT(self, type_, **kw):
|
|
138
|
+
if type_.precision is None:
|
|
139
|
+
return "FLOAT"
|
|
140
|
+
return f"FLOAT({type_.precision})"
|
|
141
|
+
|
|
142
|
+
def visit_REAL(self, type_, **kw):
|
|
143
|
+
return "REAL"
|
|
144
|
+
|
|
145
|
+
def visit_DOUBLE(self, type_, **kw):
|
|
146
|
+
return "DOUBLE"
|
|
147
|
+
|
|
148
|
+
def visit_SMALLINT(self, type_, **kw):
|
|
149
|
+
return "SMALLINT"
|
|
150
|
+
|
|
151
|
+
def visit_INTEGER(self, type_, **kw):
|
|
152
|
+
return "INTEGER"
|
|
153
|
+
|
|
154
|
+
def visit_BIGINT(self, type_, **kw):
|
|
155
|
+
return "BIGINT"
|
|
156
|
+
|
|
157
|
+
def visit_SERIAL(self, type_, **kw):
|
|
158
|
+
return "SERIAL"
|
|
159
|
+
|
|
160
|
+
def visit_VARCHAR(self, type_, **kw):
|
|
161
|
+
if type_.length:
|
|
162
|
+
return f"VARCHAR({type_.length})"
|
|
163
|
+
return "VARCHAR"
|
|
164
|
+
|
|
165
|
+
def visit_CHAR(self, type_, **kw):
|
|
166
|
+
if getattr(type_, "national", False):
|
|
167
|
+
return self.visit_NCHAR(type_, **kw)
|
|
168
|
+
if type_.length:
|
|
169
|
+
return f"CHAR({type_.length})"
|
|
170
|
+
return "CHAR"
|
|
171
|
+
|
|
172
|
+
def visit_NCHAR(self, type_, **kw):
|
|
173
|
+
if type_.length:
|
|
174
|
+
return f"NCHAR({type_.length})"
|
|
175
|
+
return "NCHAR"
|
|
176
|
+
|
|
177
|
+
def visit_NVARCHAR(self, type_, **kw):
|
|
178
|
+
if type_.length:
|
|
179
|
+
return f"NVARCHAR({type_.length})"
|
|
180
|
+
return "NVARCHAR"
|
|
181
|
+
|
|
182
|
+
def visit_CLOB(self, type_, **kw):
|
|
183
|
+
return "CLOB"
|
|
184
|
+
|
|
185
|
+
def visit_BLOB(self, type_, **kw):
|
|
186
|
+
return "BLOB"
|
|
187
|
+
|
|
188
|
+
def visit_BIT(self, type_, **kw):
|
|
189
|
+
if getattr(type_, "length", None):
|
|
190
|
+
return f"BIT({type_.length})"
|
|
191
|
+
return "BIT"
|
|
192
|
+
|
|
193
|
+
def visit_VARBIT(self, type_, **kw):
|
|
194
|
+
if getattr(type_, "length", None):
|
|
195
|
+
return f"VARBIT({type_.length})"
|
|
196
|
+
return "VARBIT"
|
|
197
|
+
|
|
198
|
+
def visit_BYTE(self, type_, **kw):
|
|
199
|
+
if getattr(type_, "length", None):
|
|
200
|
+
return f"BYTE({type_.length})"
|
|
201
|
+
return "BYTE"
|
|
202
|
+
|
|
203
|
+
def visit_VARBYTE(self, type_, **kw):
|
|
204
|
+
if getattr(type_, "length", None):
|
|
205
|
+
return f"VARBYTE({type_.length})"
|
|
206
|
+
return "VARBYTE"
|
|
207
|
+
|
|
208
|
+
def visit_NIBBLE(self, type_, **kw):
|
|
209
|
+
if getattr(type_, "length", None):
|
|
210
|
+
return f"NIBBLE({type_.length})"
|
|
211
|
+
return "NIBBLE"
|
|
212
|
+
|
|
213
|
+
def visit_GEOMETRY(self, type_, **kw):
|
|
214
|
+
return "GEOMETRY"
|
|
215
|
+
|
|
216
|
+
def visit_DATE(self, type_, **kw):
|
|
217
|
+
return "DATE"
|
|
218
|
+
|
|
219
|
+
def visit_large_binary(self, type_, **kw):
|
|
220
|
+
return "BLOB"
|
|
221
|
+
|
|
222
|
+
def visit_text(self, type_, **kw):
|
|
223
|
+
return "CLOB"
|
|
224
|
+
|
|
225
|
+
def visit_datetime(self, type_, **kw):
|
|
226
|
+
return "DATE"
|