diff --git a/doc/src/api_manual/soda.rst b/doc/src/api_manual/soda.rst index 45ee6e5..1c26f72 100644 --- a/doc/src/api_manual/soda.rst +++ b/doc/src/api_manual/soda.rst @@ -218,6 +218,13 @@ SodaCollection Methods no documents in the collection, None is returned. +.. method:: SodaCollection.getIndexes() + + Returns a list of specifications for the indexes found on the collection. + + .. versionadded:: 1.4.0 + + .. method:: SodaCollection.insertMany(docs) Inserts a list of documents into the collection at one time. Each of the diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index 564f622..833d81b 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -53,6 +53,8 @@ Thin Mode Changes Thick Mode Changes ++++++++++++++++++ +#) Added function :meth:`SodaCollection.getIndexes()` for geting the indexes + on a SODA collection. #) Fixed bug when using external authentication with an Easy Connect connection string. #) Relaxed restriction for end-to-end string connection attributes. These diff --git a/src/oracledb/impl/thick/odpi.pxd b/src/oracledb/impl/thick/odpi.pxd index 9df360c..865058a 100644 --- a/src/oracledb/impl/thick/odpi.pxd +++ b/src/oracledb/impl/thick/odpi.pxd @@ -430,11 +430,6 @@ cdef extern from "impl/thick/odpi/embed/dpi.c": uint32_t nativeTypeNum dpiDataBuffer value - ctypedef struct dpiSodaCollNames: - uint32_t numNames - const char **names - uint32_t *nameLengths - ctypedef struct dpiSodaOperOptions: uint32_t numKeys const char **keys @@ -459,6 +454,11 @@ cdef extern from "impl/thick/odpi/embed/dpi.c": uint16_t statementType bint isReturning + ctypedef struct dpiStringList: + uint32_t numStrings + const char **strings + uint32_t *stringLengths + ctypedef struct dpiSubscrCreateParams: uint32_t subscrNamespace uint32_t protocol @@ -684,6 +684,9 @@ cdef extern from "impl/thick/odpi/embed/dpi.c": unsigned int minorVersion, dpiContextCreateParams *params, dpiContext **context, dpiErrorInfo *errorInfo) nogil + int dpiContext_freeStringList(dpiContext *context, + dpiStringList *stringList) nogil + int dpiContext_getClientVersion(const dpiContext *context, dpiVersionInfo *versionInfo) nogil @@ -1037,6 +1040,9 @@ cdef extern from "impl/thick/odpi/embed/dpi.c": const dpiSodaOperOptions *options, uint32_t flags, uint64_t *count) nogil + int dpiSodaColl_getIndexes(dpiSodaColl *coll, uint32_t flags, + dpiStringList *lst) nogil + int dpiSodaColl_getMetadata(dpiSodaColl *coll, const char **value, uint32_t *valueLength) nogil @@ -1076,12 +1082,9 @@ cdef extern from "impl/thick/odpi/embed/dpi.c": const char *mediaType, uint32_t mediaTypeLength, uint32_t flags, dpiSodaDoc **doc) nogil - int dpiSodaDb_freeCollectionNames(dpiSodaDb *db, - dpiSodaCollNames *names) nogil - int dpiSodaDb_getCollectionNames(dpiSodaDb *db, const char *startName, uint32_t startNameLength, uint32_t limit, - uint32_t flags, dpiSodaCollNames *names) nogil + uint32_t flags, dpiStringList *names) nogil int dpiSodaDb_openCollection(dpiSodaDb *db, const char *name, uint32_t nameLength, uint32_t flags, dpiSodaColl **coll) nogil diff --git a/src/oracledb/impl/thick/soda.pyx b/src/oracledb/impl/thick/soda.pyx index b2eab09..a744e4d 100644 --- a/src/oracledb/impl/thick/soda.pyx +++ b/src/oracledb/impl/thick/soda.pyx @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------ -# Copyright (c) 2020, 2022, Oracle and/or its affiliates. +# Copyright (c) 2020, 2023, Oracle and/or its affiliates. # # This software is dual-licensed to you under the Universal Permissive License # (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License @@ -99,30 +99,19 @@ cdef class ThickSodaDbImpl(BaseSodaDbImpl): """ cdef: StringBuffer start_name_buf = StringBuffer() - dpiSodaCollNames coll_names - uint32_t flags, i - list result + dpiStringList names + uint32_t flags int status - str temp start_name_buf.set_value(start_name) self._get_flags(&flags) with nogil: status = dpiSodaDb_getCollectionNames(self._handle, start_name_buf.ptr, start_name_buf.length, - limit, flags, &coll_names) + limit, flags, &names) if status < 0: _raise_from_odpi() - try: - result = cpython.PyList_New(coll_names.numNames) - for i in range(coll_names.numNames): - temp = coll_names.names[i][:coll_names.nameLengths[i]].decode() - cpython.Py_INCREF(temp) - cpython.PyList_SET_ITEM(result, i, temp) - return result - finally: - if dpiSodaDb_freeCollectionNames(self._handle, &coll_names) < 0: - _raise_from_odpi() + return _string_list_to_python(&names) def open_collection(self, str name): """ @@ -283,6 +272,21 @@ cdef class ThickSodaCollImpl(BaseSodaCollImpl): if doc_impl._handle != NULL: return doc_impl + def get_indexes(self): + """ + Internal method for getting the list of indexes on a collection. + """ + cdef: + dpiStringList indexes + uint32_t flags + int status + self._db_impl._get_flags(&flags) + with nogil: + status = dpiSodaColl_getIndexes(self._handle, flags, &indexes) + if status < 0: + _raise_from_odpi() + return _string_list_to_python(&indexes) + def get_metadata(self): """ Internal method for getting the metadata for a collection. diff --git a/src/oracledb/impl/thick/utils.pyx b/src/oracledb/impl/thick/utils.pyx index d538aea..c3b89fd 100644 --- a/src/oracledb/impl/thick/utils.pyx +++ b/src/oracledb/impl/thick/utils.pyx @@ -374,6 +374,24 @@ cdef uint32_t _get_native_type_num(DbType dbtype): return DPI_NATIVE_TYPE_JSON return DPI_NATIVE_TYPE_BYTES +cdef list _string_list_to_python(dpiStringList *str_list): + """ + Converts the contents of dpiStringList to a Python list of strings. + """ + cdef: + list result + uint32_t i + str temp + try: + result = cpython.PyList_New(str_list.numStrings) + for i in range(str_list.numStrings): + temp = str_list.strings[i][:str_list.stringLengths[i]].decode() + cpython.Py_INCREF(temp) + cpython.PyList_SET_ITEM(result, i, temp) + return result + finally: + if dpiContext_freeStringList(driver_context, str_list) < 0: + _raise_from_odpi() cdef object _create_new_from_info(dpiErrorInfo *error_info): """ diff --git a/src/oracledb/soda.py b/src/oracledb/soda.py index 06661de..e17eeb5 100644 --- a/src/oracledb/soda.py +++ b/src/oracledb/soda.py @@ -214,6 +214,12 @@ class SodaCollection: if doc_impl is not None: return SodaDocument._from_impl(doc_impl) + def getIndexes(self) -> list: + """ + Return a list of indexes associated with the collection. + """ + return [json.loads(s) for s in self._impl.get_indexes()] + def insertMany(self, docs: list) -> None: """ Inserts a list of documents into the collection at one time. Each of diff --git a/tests/test_3400_soda_collection.py b/tests/test_3400_soda_collection.py index 03f40b8..23e485d 100644 --- a/tests/test_3400_soda_collection.py +++ b/tests/test_3400_soda_collection.py @@ -611,5 +611,37 @@ class TestCase(test_env.BaseTestCase): self.assertRaises(TypeError, coll.find().fetchArraySize, -1) coll.drop() + def test_3429_getting_indexes(self): + "3429 - test getting indexes on a collection" + soda_db = self.get_soda_database() + coll = soda_db.createCollection("TestSodaGetIndexes") + index_1 = { + 'name': 'ix_3428-1', + 'fields': [ + { + 'path': 'address.city', + 'datatype': 'string', + 'order': 'asc' + } + ] + } + index_2 = { + 'name': 'ix_3428-2', + 'fields': [ + { + 'path': 'address.postal_code', + 'datatype': 'string', + 'order': 'asc' + } + ] + } + self.assertEqual(coll.getIndexes(), []) + coll.createIndex(index_1) + coll.createIndex(index_2) + indexes = coll.getIndexes() + indexes.sort(key=lambda x: x["name"]) + self.assertEqual(indexes[0]["fields"][0]["path"], "address.city") + self.assertEqual(indexes[1]["fields"][0]["path"], "address.postal_code") + if __name__ == "__main__": test_env.run_test_cases()