django-gbase8s 4.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 zhaojinlong
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,65 @@
1
+ Metadata-Version: 2.1
2
+ Name: django-gbase8s
3
+ Version: 4.0.0
4
+ Summary: Django backend for GBase 8s database
5
+ Author: GBase 8s team
6
+ Project-URL: Source, https://gitee.com/GBase8s/django-gbase8s
7
+ Classifier: Framework :: Django
8
+ Classifier: Framework :: Django :: 4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: gbase8sdb
18
+ Requires-Dist: django>=4.0.0
19
+
20
+ # django-gbase8s
21
+
22
+ #### 介绍
23
+ GBase 8s数据库的Django方言,支持GBase 8s V8.8_3.6.2版本及以上。
24
+
25
+ #### 依赖
26
+ - gbase8sdb
27
+ - django>=4.0.0
28
+
29
+
30
+ #### 安装教程
31
+
32
+ ```python
33
+ pip install django-gbase8s
34
+ ```
35
+
36
+ #### 使用说明
37
+
38
+ 1. 方言使用python-gbase8sdb驱动连接数据库,安装django-gbase8s时会作为依赖进行安装,您也可以手动安装:
39
+ ```python
40
+ pip install gbase8sdb
41
+ ```
42
+ 2. python-gbase8sdb驱动连接数据库依赖GSDK 1.1版本,所以您需要联系GBase 8s技术支持或通过官方渠道获取相应版本的GSDK,并安装到您的机器上, 并设置如下环境变量:
43
+ ```bash
44
+ GSDK_PATH=/path/to/gsdk
45
+ export LD_LIBRARY_PATH=${GSDK_PATH}/lib:$LD_LIBRARY_PATH
46
+ export GBASEDBTDIR=${GSDK_PATH}/lib
47
+ ```
48
+
49
+
50
+ 3. 在Django项目的`settings.py`文件中进行如下配置:
51
+ ```python
52
+ DATABASES = {
53
+ "default": {
54
+ "ENGINE": "django_gbase8s",
55
+ 'SERVER_NAME': 'ol_gbasedbt1210_1', # 实例的名称
56
+ 'NAME': 'testdb', # 数据库的名称
57
+ 'HOST': '192.168.xxx.xxx', # 数据库IP
58
+ 'PORT': 9088, # 实例端口号
59
+ 'DB_LOCALE': 'zh_CN.utf8', # 数据库字符集
60
+ 'USER': 'gbasedbt', # 用户名
61
+ 'PASSWORD': 'xxxxxxx' # 密码
62
+ }
63
+ }
64
+ ```
65
+
@@ -0,0 +1,46 @@
1
+ # django-gbase8s
2
+
3
+ #### 介绍
4
+ GBase 8s数据库的Django方言,支持GBase 8s V8.8_3.6.2版本及以上。
5
+
6
+ #### 依赖
7
+ - gbase8sdb
8
+ - django>=4.0.0
9
+
10
+
11
+ #### 安装教程
12
+
13
+ ```python
14
+ pip install django-gbase8s
15
+ ```
16
+
17
+ #### 使用说明
18
+
19
+ 1. 方言使用python-gbase8sdb驱动连接数据库,安装django-gbase8s时会作为依赖进行安装,您也可以手动安装:
20
+ ```python
21
+ pip install gbase8sdb
22
+ ```
23
+ 2. python-gbase8sdb驱动连接数据库依赖GSDK 1.1版本,所以您需要联系GBase 8s技术支持或通过官方渠道获取相应版本的GSDK,并安装到您的机器上, 并设置如下环境变量:
24
+ ```bash
25
+ GSDK_PATH=/path/to/gsdk
26
+ export LD_LIBRARY_PATH=${GSDK_PATH}/lib:$LD_LIBRARY_PATH
27
+ export GBASEDBTDIR=${GSDK_PATH}/lib
28
+ ```
29
+
30
+
31
+ 3. 在Django项目的`settings.py`文件中进行如下配置:
32
+ ```python
33
+ DATABASES = {
34
+ "default": {
35
+ "ENGINE": "django_gbase8s",
36
+ 'SERVER_NAME': 'ol_gbasedbt1210_1', # 实例的名称
37
+ 'NAME': 'testdb', # 数据库的名称
38
+ 'HOST': '192.168.xxx.xxx', # 数据库IP
39
+ 'PORT': 9088, # 实例端口号
40
+ 'DB_LOCALE': 'zh_CN.utf8', # 数据库字符集
41
+ 'USER': 'gbasedbt', # 用户名
42
+ 'PASSWORD': 'xxxxxxx' # 密码
43
+ }
44
+ }
45
+ ```
46
+
@@ -0,0 +1 @@
1
+ __version__ = "4.0.0"
@@ -0,0 +1,297 @@
1
+ import decimal
2
+ from contextlib import contextmanager
3
+ from collections.abc import Mapping
4
+
5
+ from django.core.exceptions import ImproperlyConfigured
6
+ from django.db.backends.base.base import BaseDatabaseWrapper
7
+ from django.db.backends.base.base import NO_DB_ALIAS
8
+ from django.utils.asyncio import async_unsafe
9
+ from django.utils.regex_helper import _lazy_re_compile
10
+
11
+ try:
12
+ import gbase8sdb as Database
13
+ Database.defaults.fetch_decimals = True
14
+ except ImportError as e:
15
+ raise ImproperlyConfigured("Error loading gbase8sdb module: %s" % e)
16
+
17
+ from .client import DatabaseClient
18
+ from .creation import DatabaseCreation
19
+ from .features import DatabaseFeatures
20
+ from .introspection import DatabaseIntrospection
21
+ from .operations import DatabaseOperations
22
+ from .schema import DatabaseSchemaEditor
23
+ from .validation import DatabaseValidation
24
+ from .utils import dsn
25
+ from .utils import GBase8sParam
26
+
27
+
28
+ FORMAT_QMARK_REGEX = _lazy_re_compile(r"(?<!%)%s")
29
+
30
+
31
+ class CursorWrapper(object):
32
+ def __init__(self, cursor):
33
+ self.cursor = cursor
34
+
35
+ def _format_params(self, params):
36
+ if isinstance(params, (list, tuple)):
37
+ return tuple(GBase8sParam(p) for p in params)
38
+ elif isinstance(params, dict):
39
+ return {k: GBase8sParam(v) for k, v in params.items()}
40
+ else:
41
+ raise TypeError("params must be tuple or dict")
42
+
43
+
44
+ def _param_generator(self, gbase8sparams):
45
+ if isinstance(gbase8sparams, (list, tuple)):
46
+ return tuple(p.param for p in gbase8sparams)
47
+ elif isinstance(gbase8sparams, dict):
48
+ return {k: v.param for k, v in gbase8sparams.items()}
49
+ else:
50
+ raise TypeError("params must be tuple or dict")
51
+
52
+ def _param_inputsize(self, gbase8sparams_list):
53
+ if hasattr(gbase8sparams_list[0], 'keys'): # [{k: gbase8sparams}, {}...]
54
+ sizes = {}
55
+ for params in gbase8sparams_list:
56
+ for k, value in params.items():
57
+ if value.input_size:
58
+ sizes[k] = value.input_size
59
+ else:
60
+ sizes = [None] * len(gbase8sparams_list[0])
61
+ for params in gbase8sparams_list:
62
+ for i, value in enumerate(params):
63
+ if value.input_size:
64
+ sizes[i] = value.input_size
65
+ return sizes
66
+
67
+ def _format_sql(self, sql, params):
68
+ if not params:
69
+ return FORMAT_QMARK_REGEX.sub("?", sql).replace("%%", "%")
70
+ elif isinstance(params, (tuple, list)):
71
+ sql = sql % tuple('?' * len(params))
72
+ elif isinstance(params, Mapping):
73
+ sql = sql % {name: ":{name}".format(name=name) for name in params}
74
+ else:
75
+ raise TypeError("params must be tuple or dict")
76
+ return sql
77
+
78
+ def execute(self, sql, params=None):
79
+ if params is None:
80
+ return self.cursor.execute(sql)
81
+ sql = self._format_sql(sql, params)
82
+ gbase8sparams = self._format_params(params)
83
+ params = self._param_generator(gbase8sparams)
84
+ input_sizes = self._param_inputsize([gbase8sparams])
85
+ if isinstance(input_sizes, (list, tuple)):
86
+ self.cursor.setinputsizes(*input_sizes)
87
+ elif isinstance(input_sizes, dict):
88
+ self.cursor.setinputsizes(**input_sizes)
89
+ cursor = self.cursor.execute(sql, params)
90
+ return cursor
91
+
92
+ def executemany(self, sql, params_list=()):
93
+ if not params_list:
94
+ return None
95
+ params_list = list(params_list)
96
+ sql = self._format_sql(sql, params_list[0])
97
+
98
+ new_params_list = []
99
+ gbase8sparams_list = []
100
+ for params in params_list:
101
+ gbase8sparams = self._format_params(params)
102
+ gbase8sparams_list.append(gbase8sparams)
103
+ params = self._param_generator(gbase8sparams)
104
+ new_params_list.append(params)
105
+ input_sizes = self._param_inputsize(gbase8sparams_list)
106
+ if isinstance(input_sizes, (list, tuple)):
107
+ self.cursor.setinputsizes(*input_sizes)
108
+ elif isinstance(input_sizes, dict):
109
+ self.cursor.setinputsizes(**input_sizes)
110
+ return self.cursor.executemany(sql, new_params_list)
111
+
112
+ @property
113
+ def lastrowid(self):
114
+ return self.cursor.lastrowid
115
+
116
+ def __getattr__(self, attr):
117
+ return getattr(self.cursor, attr)
118
+
119
+ def __iter__(self):
120
+ return iter(self.cursor)
121
+
122
+
123
+ class DatabaseWrapper(BaseDatabaseWrapper):
124
+ vendor = 'gbase8s'
125
+ display_name = "GBase 8s"
126
+ Database = Database
127
+ isolation_levels = [
128
+ "DIRTY READ",
129
+ "COMMITTED READ LAST COMMITTED",
130
+ "COMMITTED READ",
131
+ "CURSOR STABILITY",
132
+ "REPEATABLE READ",
133
+ ]
134
+ _limited_data_types = ("clob", "nclob", "blob")
135
+ data_types = {
136
+ 'AutoField': 'SERIAL',
137
+ 'SmallAutoField': 'SERIAL',
138
+ 'BigAutoField': 'BIGSERIAL',
139
+ 'BinaryField': 'BLOB',
140
+ 'BooleanField': 'NUMBER(1)',
141
+ 'CharField': 'VARCHAR(%(max_length)s)',
142
+ 'DateField': 'TIMESTAMP(0)',
143
+ 'DateTimeField': 'TIMESTAMP',
144
+ 'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
145
+ 'DurationField': 'INTERVAL DAY(9) TO SECOND(5)',
146
+ 'FileField': 'VARCHAR(%(max_length)s)',
147
+ 'FilePathField': 'VARCHAR(%(max_length)s)',
148
+ 'FloatField': 'FLOAT',
149
+ 'IntegerField': 'INTEGER',
150
+ "JSONField": 'VARCHAR(32765)',
151
+ 'BigIntegerField': 'BIGINT',
152
+ 'IPAddressField': 'VARCHAR(15)',
153
+ 'GenericIPAddressField': 'VARCHAR(39)',
154
+ 'OneToOneField': 'INTEGER',
155
+ 'PositiveIntegerField': 'INTEGER',
156
+ 'PositiveSmallIntegerField': 'INTEGER',
157
+ "PositiveBigIntegerField": "BIGINT",
158
+ 'SlugField': 'VARCHAR(%(max_length)s)',
159
+ 'SmallIntegerField': 'INTEGER',
160
+ 'TextField': 'NCLOB',
161
+ 'TimeField': 'TIMESTAMP',
162
+ "URLField": "VARCHAR(%(max_length)s)",
163
+ 'UUIDField': 'VARCHAR(32)',
164
+ }
165
+ data_type_check_constraints = {
166
+ "BooleanField": "%(qn_column)s IN (0,1)",
167
+ "JSONField": "%(qn_column)s IS JSON",
168
+ "PositiveBigIntegerField": "%(qn_column)s >= 0",
169
+ "PositiveIntegerField": "%(qn_column)s >= 0",
170
+ "PositiveSmallIntegerField": "%(qn_column)s >= 0",
171
+ }
172
+ operators = {
173
+ "exact": "= %s",
174
+ "iexact": "= UPPER(%s)",
175
+ "contains": "LIKE %s ESCAPE '\\'",
176
+ "icontains": "LIKE UPPER(%s) ESCAPE '\\'",
177
+ "gt": "> %s",
178
+ "gte": ">= %s",
179
+ "lt": "< %s",
180
+ "lte": "<= %s",
181
+ "startswith": "LIKE %s ESCAPE '\\'",
182
+ "endswith": "LIKE %s ESCAPE '\\'",
183
+ "istartswith": "LIKE UPPER(%s) ESCAPE '\\'",
184
+ "iendswith": "LIKE UPPER(%s) ESCAPE '\\'",
185
+ }
186
+ pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '\\'), '%%', '\%%'), '_', '\_')"
187
+ pattern_ops = {
188
+ "contains": r"LIKE '%%' || {} || '%%' ESCAPE '\'",
189
+ "icontains": r"LIKE '%%' || UPPER({}) || '%%' ESCAPE '\'",
190
+ "startswith": r"LIKE {} || '%%' ESCAPE '\'",
191
+ "istartswith": r"LIKE UPPER({}) || '%%' ESCAPE '\'",
192
+ "endswith": r"LIKE '%%' || {} ESCAPE '\'",
193
+ "iendswith": r"LIKE '%%' || UPPER({}) ESCAPE '\'",
194
+ }
195
+ client_class = DatabaseClient
196
+ creation_class = DatabaseCreation
197
+ features_class = DatabaseFeatures
198
+ introspection_class = DatabaseIntrospection
199
+ ops_class = DatabaseOperations
200
+ SchemaEditorClass = DatabaseSchemaEditor
201
+ validation_class = DatabaseValidation
202
+
203
+ def __init__(self, *args, **kwargs):
204
+ super().__init__(*args, **kwargs)
205
+ self.isolation_level = 'COMMITTED READ LAST COMMITTED'
206
+
207
+ def get_database_version(self):
208
+ return (8, 8)
209
+
210
+ def get_connection_params(self):
211
+ conn_params = {
212
+ "user": self.settings_dict["USER"],
213
+ "password": self.settings_dict["PASSWORD"],
214
+ "dsn": dsn(self.settings_dict)
215
+ }
216
+ if 'ISOLATION_LEVEL' in self.settings_dict['OPTIONS']:
217
+ if self.settings_dict['OPTIONS']['ISOLATION_LEVEL'].upper() in self.isolation_levels:
218
+ self.isolation_level = self.settings_dict['OPTIONS']['ISOLATION_LEVEL']
219
+ else:
220
+ raise ImproperlyConfigured(f"Invalid isolation level, need {self.isolation_levels}")
221
+
222
+ return conn_params
223
+
224
+ @async_unsafe
225
+ def get_new_connection(self, conn_params):
226
+ conn = Database.connect(**conn_params)
227
+ cursor = conn.cursor()
228
+ cursor.execute("select 1 from sysprocedures where procname='django_isoyear_extract'")
229
+ if not cursor.fetchone():
230
+ cursor.execute("""
231
+ create or replace function django_isoyear_extract(d in timestamp)
232
+ return int as
233
+ v_year int;
234
+ begin
235
+ SELECT
236
+ CASE
237
+ WHEN WEEKDAY(d) = 0 THEN YEAR(d -3 UNITS DAY)
238
+ ELSE YEAR(d + (4 - WEEKDAY(d)) UNITS DAY)
239
+ END AS iso_year
240
+ INTO v_year
241
+ FROM DUAL;
242
+ return v_year;
243
+ end django_isoyear_extract;
244
+ """)
245
+ cursor.close()
246
+
247
+ return conn
248
+
249
+ def init_connection_state(self):
250
+ # super().init_connection_state()
251
+ if self.isolation_level:
252
+ with self.cursor() as cursor:
253
+ cursor.execute(f"SET ISOLATION TO {self.isolation_level}")
254
+
255
+ def create_cursor(self, name=None):
256
+ return CursorWrapper(self.connection.cursor())
257
+
258
+ def _set_autocommit(self, autocommit):
259
+ with self.wrap_database_errors:
260
+ self.connection.autocommit = autocommit
261
+
262
+ def disable_constraint_checking(self):
263
+ with self.cursor() as cursor:
264
+ cursor.execute("SET CONSTRAINTS ALL DEFERRED")
265
+ return True
266
+
267
+ def enable_constraint_checking(self):
268
+ pass
269
+
270
+ def check_constraints(self, table_names=None):
271
+ """
272
+ To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they
273
+ are returned to deferred.
274
+ """
275
+ with self.cursor() as cursor:
276
+ cursor.execute('SET CONSTRAINTS ALL IMMEDIATE')
277
+ cursor.execute('SET CONSTRAINTS ALL DEFERRED')
278
+
279
+ def is_usable(self):
280
+ try:
281
+ self.connection.ping()
282
+ except Database.Error:
283
+ return False
284
+ else:
285
+ return True
286
+
287
+ @contextmanager
288
+ def _nodb_cursor(self):
289
+ conn = self.__class__({**self.settings_dict, "NAME": "sysmaster"}, alias=NO_DB_ALIAS)
290
+ try:
291
+ with conn.cursor() as cursor:
292
+ cursor.execute("CLOSE DATABASE")
293
+ yield cursor
294
+ finally:
295
+ conn.close()
296
+
297
+
@@ -0,0 +1,17 @@
1
+ import shutil
2
+ from django.db.backends.base.client import BaseDatabaseClient
3
+
4
+ class DatabaseClient(BaseDatabaseClient):
5
+ executable_name = "dbaccess"
6
+ wrapper_name = "rlwrap"
7
+
8
+ @classmethod
9
+ def settings_to_cmd_args_env(cls, settings_dict, parameters):
10
+ server_name = settings_dict.get("SERVER_NAME")
11
+ db_name = settings_dict.get("NAME")
12
+ args = [cls.executable_name, db_name, '-']
13
+ wrapper_path = shutil.which(cls.wrapper_name)
14
+ if wrapper_path:
15
+ args = [wrapper_path, *args]
16
+ args.extend(parameters)
17
+ return args, {'GBASEDBTSERVER': server_name}