Skip to content

Commit 5aa7dac

Browse files
Added support for internal use of JSON in SODA with Oracle Client 23.
1 parent fee318e commit 5aa7dac

File tree

14 files changed

+200
-83
lines changed

14 files changed

+200
-83
lines changed

doc/src/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Thin Mode Changes
4848
Thick Mode Changes
4949
++++++++++++++++++
5050

51+
#) Added support for internal use of JSON in SODA with Oracle Client 23. This
52+
allows for seamless transfer of extended data types.
5153
#) Fixed bug when calling :meth:`SodaDoc.getContent()` for SODA documents
5254
that do not contain JSON.
5355
#) Errors ``DPY-4011: the database or network closed the connection`` and

src/oracledb/base_impl.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,7 @@ cdef class BaseDbObjectImpl:
657657

658658
cdef class BaseSodaDbImpl:
659659
cdef:
660+
public bint supports_json
660661
object _conn
661662

662663

src/oracledb/impl/base/soda.pyx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2022, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2024, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -35,10 +35,14 @@ cdef class BaseSodaDbImpl:
3535
def create_collection(self, str name, str metadata, bint map_mode):
3636
pass
3737

38-
@utils.CheckImpls("creating a SODA document")
38+
@utils.CheckImpls("creating a SODA binary/text document")
3939
def create_document(self, bytes content, str key, str media_type):
4040
pass
4141

42+
@utils.CheckImpls("creating a SODA JSON document")
43+
def create_json_document(self, object content, str key):
44+
pass
45+
4246
@utils.CheckImpls("getting a list of SODA collection names")
4347
def get_collection_names(self, str start_name, uint32_t limit):
4448
pass

src/oracledb/impl/thick/connection.pyx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ cdef class ThickConnImpl(BaseConnImpl):
344344
params.private_key_len = <uint32_t> len(params.private_key)
345345

346346
# set up common creation parameters
347-
if dpiContext_initCommonCreateParams(driver_context,
347+
if dpiContext_initCommonCreateParams(driver_info.context,
348348
&common_params) < 0:
349349
_raise_from_odpi()
350350
common_params.createMode |= DPI_MODE_CREATE_THREADED
@@ -361,7 +361,8 @@ cdef class ThickConnImpl(BaseConnImpl):
361361
common_params.accessToken = &access_token
362362

363363
# set up connection specific creation parameters
364-
if dpiContext_initConnCreateParams(driver_context, &conn_params) < 0:
364+
if dpiContext_initConnCreateParams(driver_info.context,
365+
&conn_params) < 0:
365366
_raise_from_odpi()
366367
if params.username_len == 0 and params.password_len == 0:
367368
conn_params.externalAuth = 1
@@ -394,12 +395,12 @@ cdef class ThickConnImpl(BaseConnImpl):
394395

395396
# perform connection
396397
with nogil:
397-
status = dpiConn_create(driver_context, params.username_ptr,
398+
status = dpiConn_create(driver_info.context, params.username_ptr,
398399
params.username_len, params.password_ptr,
399400
params.password_len, params.dsn_ptr,
400401
params.dsn_len, &common_params,
401402
&conn_params, &self._handle)
402-
dpiContext_getError(driver_context, &error_info)
403+
dpiContext_getError(driver_info.context, &error_info)
403404
if status < 0:
404405
_raise_from_info(&error_info)
405406
elif error_info.isWarning:
@@ -416,7 +417,7 @@ cdef class ThickConnImpl(BaseConnImpl):
416417
version_info.portReleaseNum,
417418
version_info.portUpdateNum
418419
)
419-
if client_version_info.versionNum >= 23 \
420+
if driver_info.client_version_info.versionNum >= 23 \
420421
and version_info.versionNum >= 23:
421422
self.supports_bool = True
422423
self._oson_max_fname_size = 65535
@@ -448,6 +449,7 @@ cdef class ThickConnImpl(BaseConnImpl):
448449

449450
def create_soda_database_impl(self, conn):
450451
cdef ThickSodaDbImpl impl = ThickSodaDbImpl.__new__(ThickSodaDbImpl)
452+
impl.supports_json = driver_info.soda_use_json_desc
451453
impl._conn = conn
452454
if dpiConn_getSodaDb(self._handle, &impl._handle) < 0:
453455
_raise_from_odpi()

src/oracledb/impl/thick/cursor.pyx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ cdef class ThickCursorImpl(BaseCursorImpl):
302302
with nogil:
303303
status = dpiStmt_execute(self._handle, mode, &num_query_cols)
304304
if status == DPI_SUCCESS:
305-
dpiContext_getError(driver_context, &error_info)
305+
dpiContext_getError(driver_info.context, &error_info)
306306
if not self._stmt_info.isPLSQL:
307307
status = dpiStmt_getRowCount(self._handle, &rowcount)
308308
if status < 0:
@@ -340,7 +340,7 @@ cdef class ThickCursorImpl(BaseCursorImpl):
340340
if num_execs_int > 0:
341341
with nogil:
342342
status = dpiStmt_executeMany(self._handle, mode, num_execs_int)
343-
dpiContext_getError(driver_context, &error_info)
343+
dpiContext_getError(driver_info.context, &error_info)
344344
dpiStmt_getRowCount(self._handle, &rowcount)
345345
if not self._stmt_info.isPLSQL:
346346
self.rowcount = rowcount

src/oracledb/impl/thick/odpi.pxd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ cdef extern from "impl/thick/odpi/embed/dpi.c":
341341
const char *loadErrorUrl
342342
const char *oracleClientLibDir
343343
const char *oracleClientConfigDir
344+
bint sodaUseJsonDesc
344345

345346
ctypedef union dpiDataBuffer:
346347
bint asBoolean
@@ -843,6 +844,8 @@ cdef extern from "impl/thick/odpi/embed/dpi.c":
843844
int dpiJson_getValue(dpiJson *json, uint32_t options,
844845
dpiJsonNode **topNode) nogil
845846

847+
int dpiJson_release(dpiJson *json) nogil
848+
846849
int dpiJson_setValue(dpiJson *json, dpiJsonNode *topNode)
847850

848851
int dpiLob_addRef(dpiLob *lob) nogil
@@ -1142,6 +1145,10 @@ cdef extern from "impl/thick/odpi/embed/dpi.c":
11421145
const char *mediaType, uint32_t mediaTypeLength, uint32_t flags,
11431146
dpiSodaDoc **doc) nogil
11441147

1148+
int dpiSodaDb_createJsonDocument(dpiSodaDb *db, const char *key,
1149+
uint32_t keyLength, const dpiJsonNode *content, uint32_t flags,
1150+
dpiSodaDoc **doc) nogil
1151+
11451152
int dpiSodaDb_getCollectionNames(dpiSodaDb *db,
11461153
const char *startName, uint32_t startNameLength, uint32_t limit,
11471154
uint32_t flags, dpiStringList *names) nogil
@@ -1157,6 +1164,10 @@ cdef extern from "impl/thick/odpi/embed/dpi.c":
11571164
int dpiSodaDoc_getCreatedOn(dpiSodaDoc *doc, const char **value,
11581165
uint32_t *valueLength) nogil
11591166

1167+
int dpiSodaDoc_getIsJson(dpiSodaDoc *doc, bint *isJson) nogil
1168+
1169+
int dpiSodaDoc_getJsonContent(dpiSodaDoc *doc, dpiJson **value) nogil
1170+
11601171
int dpiSodaDoc_getKey(dpiSodaDoc *doc, const char **value,
11611172
uint32_t *valueLength) nogil
11621173

src/oracledb/impl/thick/pool.pyx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2024, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -83,7 +83,7 @@ cdef class ThickPoolImpl(BasePoolImpl):
8383
private_key_len = <uint32_t> len(private_key_bytes)
8484

8585
# set up common creation parameters
86-
if dpiContext_initCommonCreateParams(driver_context,
86+
if dpiContext_initCommonCreateParams(driver_info.context,
8787
&common_params) < 0:
8888
_raise_from_odpi()
8989
common_params.createMode |= DPI_MODE_CREATE_THREADED
@@ -101,7 +101,8 @@ cdef class ThickPoolImpl(BasePoolImpl):
101101
common_params.accessToken = &access_token
102102

103103
# set up pool creation parameters
104-
if dpiContext_initPoolCreateParams(driver_context, &create_params) < 0:
104+
if dpiContext_initPoolCreateParams(driver_info.context,
105+
&create_params) < 0:
105106
_raise_from_odpi()
106107
create_params.minSessions = self.min
107108
create_params.maxSessions = self.max
@@ -142,11 +143,11 @@ cdef class ThickPoolImpl(BasePoolImpl):
142143

143144
# create pool
144145
with nogil:
145-
status = dpiPool_create(driver_context, user_ptr, user_len,
146+
status = dpiPool_create(driver_info.context, user_ptr, user_len,
146147
password_ptr, password_len, dsn_ptr,
147148
dsn_len, &common_params, &create_params,
148149
&self._handle)
149-
dpiContext_getError(driver_context, &error_info)
150+
dpiContext_getError(driver_info.context, &error_info)
150151
if status < 0:
151152
_raise_from_info(&error_info)
152153
elif error_info.isWarning:

src/oracledb/impl/thick/soda.pyx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#------------------------------------------------------------------------------
2-
# Copyright (c) 2020, 2023, Oracle and/or its affiliates.
2+
# Copyright (c) 2020, 2024, Oracle and/or its affiliates.
33
#
44
# This software is dual-licensed to you under the Universal Permissive License
55
# (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
@@ -74,7 +74,8 @@ cdef class ThickSodaDbImpl(BaseSodaDbImpl):
7474

7575
def create_document(self, bytes content, str key, str media_type):
7676
"""
77-
Internal method for creating a document.
77+
Internal method for creating a document containing binary or encoded
78+
text data.
7879
"""
7980
cdef:
8081
StringBuffer media_type_buf = StringBuffer()
@@ -93,6 +94,24 @@ cdef class ThickSodaDbImpl(BaseSodaDbImpl):
9394
_raise_from_odpi()
9495
return doc_impl
9596

97+
def create_json_document(self, object content, str key):
98+
"""
99+
Internal method for creating a document containing JSON.
100+
"""
101+
cdef:
102+
StringBuffer key_buf = StringBuffer()
103+
JsonBuffer json_buf = JsonBuffer()
104+
ThickSodaDocImpl doc_impl
105+
key_buf.set_value(key)
106+
json_buf.from_object(content)
107+
doc_impl = ThickSodaDocImpl.__new__(ThickSodaDocImpl)
108+
if dpiSodaDb_createJsonDocument(self._handle, key_buf.ptr,
109+
key_buf.length, &json_buf._top_node,
110+
DPI_SODA_FLAGS_DEFAULT,
111+
&doc_impl._handle) < 0:
112+
_raise_from_odpi()
113+
return doc_impl
114+
96115
def get_collection_names(self, str start_name, uint32_t limit):
97116
"""
98117
Internal method for getting the list of collection names.
@@ -163,7 +182,7 @@ cdef class ThickSodaCollImpl(BaseSodaCollImpl):
163182
Internal method for populating the SODA operations structure with the
164183
information provided by the user.
165184
"""
166-
if dpiContext_initSodaOperOptions(driver_context, options) < 0:
185+
if dpiContext_initSodaOperOptions(driver_info.context, options) < 0:
167186
_raise_from_odpi()
168187
options.hint = ptr
169188
options.hintLength = length
@@ -489,20 +508,29 @@ cdef class ThickSodaDocImpl(BaseSodaDocImpl):
489508
Internal method for returning the content of the document.
490509
"""
491510
cdef:
492-
bytes out_content = None
511+
object out_content = None
493512
str out_encoding = None
494513
const char *encoding
495514
uint32_t content_len
496515
const char *content
497-
if dpiSodaDoc_getContent(self._handle, &content, &content_len,
498-
&encoding) < 0:
516+
dpiJson *json
517+
bint is_json
518+
if dpiSodaDoc_getIsJson(self._handle, &is_json) < 0:
499519
_raise_from_odpi()
500-
if content != NULL:
501-
out_content = content[:content_len]
502-
if encoding != NULL:
503-
out_encoding = encoding.decode()
520+
if is_json:
521+
if dpiSodaDoc_getJsonContent(self._handle, &json) < 0:
522+
_raise_from_odpi()
523+
out_content = _convert_json_to_python(json)
504524
else:
505-
out_encoding = "UTF-8"
525+
if dpiSodaDoc_getContent(self._handle, &content, &content_len,
526+
&encoding) < 0:
527+
_raise_from_odpi()
528+
if content != NULL:
529+
out_content = content[:content_len]
530+
if encoding != NULL:
531+
out_encoding = encoding.decode()
532+
else:
533+
out_encoding = "UTF-8"
506534
return (out_content, out_encoding)
507535

508536
def get_created_on(self):
@@ -637,7 +665,7 @@ cdef class ThickSodaOpImpl:
637665
uint32_t i
638666
impl._buffers = []
639667
options = &impl._options
640-
if dpiContext_initSodaOperOptions(driver_context, options) < 0:
668+
if dpiContext_initSodaOperOptions(driver_info.context, options) < 0:
641669
_raise_from_odpi()
642670
if op._keys:
643671
options.numKeys = <uint32_t> len(op._keys)

src/oracledb/impl/thick/subscr.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ cdef class ThickSubscrImpl(BaseSubscrImpl):
146146
int status
147147
name_buf.set_value(self.name)
148148
ip_address_buf.set_value(self.ip_address)
149-
if dpiContext_initSubscrCreateParams(driver_context, &params) < 0:
149+
if dpiContext_initSubscrCreateParams(driver_info.context, &params) < 0:
150150
_raise_from_odpi()
151151
params.subscrNamespace = self.namespace
152152
params.protocol = self.protocol

0 commit comments

Comments
 (0)