Add support (as preview) for SODA.
This commit is contained in:
parent
b611246a7b
commit
64f65050a2
@ -280,6 +280,26 @@ Connection Object
|
|||||||
This attribute is an extension to the DB API definition.
|
This attribute is an extension to the DB API definition.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: Connection.getSodaDatabase()
|
||||||
|
|
||||||
|
Return a :ref:`SodaDatabase <sodadb>` object for Simple Oracle Document
|
||||||
|
Access (SODA). All SODA operations are performed either on the returned
|
||||||
|
SodaDatabase object or from objects created by SodaDatabase. See
|
||||||
|
`here <http://www.oracle.com/pls/topic/lookup?
|
||||||
|
ctx=dblatest&id=GUID-BE42F8D3-B86B-43B4-B2A3-5760A4DF79FB>`__ for
|
||||||
|
additional information on SODA.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This method is an extension to the DB API definition.
|
||||||
|
|
||||||
|
|
||||||
.. method:: Connection.gettype(name)
|
.. method:: Connection.gettype(name)
|
||||||
|
|
||||||
Return a :ref:`type object <objecttype>` given its name. This can then be
|
Return a :ref:`type object <objecttype>` given its name. This can then be
|
||||||
|
|||||||
@ -24,6 +24,11 @@ Contents:
|
|||||||
lob.rst
|
lob.rst
|
||||||
objecttype.rst
|
objecttype.rst
|
||||||
aq.rst
|
aq.rst
|
||||||
|
sodadb.rst
|
||||||
|
sodacoll.rst
|
||||||
|
sodaop.rst
|
||||||
|
sodadoc.rst
|
||||||
|
sodadoccur.rst
|
||||||
whatsnew.rst
|
whatsnew.rst
|
||||||
releasenotes.rst
|
releasenotes.rst
|
||||||
license.rst
|
license.rst
|
||||||
|
|||||||
118
doc/src/sodacoll.rst
Normal file
118
doc/src/sodacoll.rst
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
.. _sodacoll:
|
||||||
|
|
||||||
|
**********************
|
||||||
|
SODA Collection Object
|
||||||
|
**********************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This object is an extension the DB API. It is used to represent SODA
|
||||||
|
collections and is created by methods
|
||||||
|
:meth:`SodaDatabase.createCollection()` and
|
||||||
|
:meth:`SodaDatabase.openCollection()`.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.createIndex(spec)
|
||||||
|
|
||||||
|
Creates an index on a SODA collection. The spec is expected to be a
|
||||||
|
dictionary or a JSON-encoded string. See this `overview
|
||||||
|
<https://www.oracle.com/pls/topic/
|
||||||
|
lookup?ctx=dblatest&id=GUID-4848E6A0-58A7-44FD-8D6D-A033D0CCF9CB>`__
|
||||||
|
for information on indexes in SODA.
|
||||||
|
|
||||||
|
Note that a commit should be performed before attempting to create an
|
||||||
|
index.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.drop()
|
||||||
|
|
||||||
|
Drops the collection from the database, if it exists. Note that if the
|
||||||
|
collection was created with mapMode set to True the underlying table will
|
||||||
|
not be dropped.
|
||||||
|
|
||||||
|
A boolean value is returned indicating if the collection was actually
|
||||||
|
dropped.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.dropIndex(name, force=False)
|
||||||
|
|
||||||
|
Drops the index with the specified name, if it exists.
|
||||||
|
|
||||||
|
The force parameter, if set to True, can be used to force the dropping of
|
||||||
|
an index that the underlying Oracle Database domain index doesn't normally
|
||||||
|
permit. This is only applicable to spatial and JSON search indexes.
|
||||||
|
See `here <https://www.oracle.com/pls/topic/
|
||||||
|
lookup?ctx=dblatest&id=GUID-F60F75DF-2866-4F93-BB7F-8FCE64BF67B6>`__
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
A boolean value is returned indicating if the index was actually dropped.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.find()
|
||||||
|
|
||||||
|
This method is used to begin an operation that will act upon documents in
|
||||||
|
the collection. It creates and returns a
|
||||||
|
:ref:`SodaOperation object <sodaop>` which is used to specify the criteria
|
||||||
|
and the operation that will be performed on the documents that match that
|
||||||
|
criteria.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.getDataGuide()
|
||||||
|
|
||||||
|
Returns a :ref:`SODA document object <sodadoc>` containing property names,
|
||||||
|
data types and lengths inferred from the JSON documents in the collection.
|
||||||
|
It can be useful for exploring the schema of a collection. Note that this
|
||||||
|
method is only supported for JSON-only collections where a JSON search
|
||||||
|
index has been created with the 'dataguide' option enabled. If there are
|
||||||
|
no documents in the collection, None is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.insertOne(doc)
|
||||||
|
|
||||||
|
Inserts a given document into the collection. The input document can be a
|
||||||
|
dictionary or list or an existing :ref:`SODA document object <sodadoc>`.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaCollection.insertOneAndGet(doc)
|
||||||
|
|
||||||
|
Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
|
||||||
|
given document into the collection. The only difference is that it
|
||||||
|
returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
|
||||||
|
reasons the returned document does not contain the content.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaCollection.metadata
|
||||||
|
|
||||||
|
This read-only attribute returns a dicationary containing the metadata that
|
||||||
|
was used to create the collection. See this `collection metadata reference
|
||||||
|
<https://www.oracle.com/pls/topic/
|
||||||
|
lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaCollection.name
|
||||||
|
|
||||||
|
This read-only attribute returns the name of the collection.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
90
doc/src/sodadb.rst
Normal file
90
doc/src/sodadb.rst
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
.. _sodadb:
|
||||||
|
|
||||||
|
********************
|
||||||
|
SODA Database Object
|
||||||
|
********************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This object is an extension the DB API. It is returned by the method
|
||||||
|
:meth:`Connection.getSodaDatabase()`.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDatabase.createCollection(name, metadata=None, mapMode=False)
|
||||||
|
|
||||||
|
Creates a SODA collection with the given name and returns a new
|
||||||
|
:ref:`SODA collection object <sodacoll>`. If you try to create a
|
||||||
|
collection, and a collection with the same name and metadata already
|
||||||
|
exists, then that existing collection is opened without error.
|
||||||
|
|
||||||
|
If metadata is specified, it is expected to be a string containing valid
|
||||||
|
JSON or a dictionary that will be transformed into a JSON string. This JSON
|
||||||
|
permits you to specify the configuration of the collection including
|
||||||
|
storage options; specifying the presence or absence of columns for creation
|
||||||
|
timestamp, last modified timestamp and version; whether the collection can
|
||||||
|
store only JSON documents; and methods of key and version generation. The
|
||||||
|
default metadata creates a collection that only supports JSON documents and
|
||||||
|
uses system generated keys. See this `collection metadata reference
|
||||||
|
<https://www.oracle.com/pls/topic/
|
||||||
|
lookup?ctx=dblatest&id=GUID-49EFF3D3-9FAB-4DA6-BDE2-2650383566A3>`__
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
If the mapMode parameter is set to True, the new collection is mapped to an
|
||||||
|
existing table instead of creating a table. If a collection is created in
|
||||||
|
this way, dropping the collection will not drop the existing table either.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDatabase.createDocument(content, key=None, mediaType="application/json")
|
||||||
|
|
||||||
|
Creates a :ref:`Soda document <sodadoc>` usable for SODA write operations.
|
||||||
|
You only need to use this method if your collection requires
|
||||||
|
client-assigned keys or has non-JSON content; otherwise, you can pass your
|
||||||
|
content directly to SODA write operations. SodaDocument attributes
|
||||||
|
'createdOn', 'lastModified' and 'version' will be None.
|
||||||
|
|
||||||
|
The content parameter can be a dictionary or list which will be transformed
|
||||||
|
into a JSON string and then UTF-8 encoded. It can also be a string which
|
||||||
|
will be UTF-8 encoded or it can be a bytes object which will be stored
|
||||||
|
unchanged. If a bytes object is provided and the content is expected to be
|
||||||
|
JSON, note that SODA only supports UTF-8, UTF-16LE and UTF-16BE encodings.
|
||||||
|
|
||||||
|
The key parameter should only be supplied if the collection in which the
|
||||||
|
document is to be placed requires client-assigned keys.
|
||||||
|
|
||||||
|
The mediaType parameter should only be supplied if the collection in which
|
||||||
|
the document is to be placed supports non-JSON documents and the content
|
||||||
|
for this document is non-JSON. Using a standard MIME type for this value is
|
||||||
|
recommended but any string will be accepted.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDatabase.getCollectionNames(startName=None, limit=0)
|
||||||
|
|
||||||
|
Returns a list of the names of collections in the database that match the
|
||||||
|
criteria, in alphabetical order.
|
||||||
|
|
||||||
|
If the startName parameter is specified, the list of names returned will
|
||||||
|
start with this value and also contain any names that fall after this value
|
||||||
|
in alphabetical order.
|
||||||
|
|
||||||
|
If the limit parameter is specified and is non-zero, the number of
|
||||||
|
collection names returned will be limited to this value.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDatabase.openCollection(name)
|
||||||
|
|
||||||
|
Opens an existing collection with the given name and returns a new
|
||||||
|
:ref:`SODA collection object <sodacoll>`. If a collection with that name
|
||||||
|
does not exist, None is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
97
doc/src/sodadoc.rst
Normal file
97
doc/src/sodadoc.rst
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
.. _sodadoc:
|
||||||
|
|
||||||
|
********************
|
||||||
|
SODA Document Object
|
||||||
|
********************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This object is an extension the DB API. It is returned by the methods
|
||||||
|
:meth:`SodaDatabase.createDocument()`,
|
||||||
|
:meth:`SodaOperation.getDocuments()` and
|
||||||
|
:meth:`SodaOperation.getOne()` as well as by iterating over
|
||||||
|
:ref:`SODA document cursors <sodadoccur>`.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaDoc.createdOn
|
||||||
|
|
||||||
|
This read-only attribute returns the creation time of the document in
|
||||||
|
`ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
|
||||||
|
format. Documents created by :meth:`SodaDatabase.createDocument()` or
|
||||||
|
fetched from collections where this attribute is not stored will return
|
||||||
|
None.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDoc.getContent()
|
||||||
|
|
||||||
|
Returns the content of the document as a dictionary or list. This method
|
||||||
|
assumes that the content is application/json and will raise an exception if
|
||||||
|
this is not the case. If there is no content, however, None will be
|
||||||
|
returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDoc.getContentAsBytes()
|
||||||
|
|
||||||
|
Returns the content of the document as a bytes object. If there is no
|
||||||
|
content, however, None will be returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDoc.getContentAsString()
|
||||||
|
|
||||||
|
Returns the content of the document as a string. This method assumes that
|
||||||
|
the content is application/json and will raise an exception if this is not
|
||||||
|
the case. If there is no content, however, None will be returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaDoc.key
|
||||||
|
|
||||||
|
This read-only attribute returns the unique key assigned to this document.
|
||||||
|
Documents created by :meth:`SodaDatabase.createDocument()` may not have a
|
||||||
|
value assigned to them and return None.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaDoc.lastModified
|
||||||
|
|
||||||
|
This read-only attribute returns the last modified time of the document in
|
||||||
|
`ISO 8601 <https://www.iso.org/iso-8601-date-and-time-format.html>`__
|
||||||
|
format. Documents created by :meth:`SodaDatabase.createDocument()` or
|
||||||
|
fetched from collections where this attribute is not stored will return
|
||||||
|
None.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaDoc.mediaType
|
||||||
|
|
||||||
|
This read-only attribute returns the media type assigned to the document.
|
||||||
|
By convention this is expected to be a MIME type but no checks are
|
||||||
|
performed on this value. If a value is not specified when calling
|
||||||
|
:meth:`SodaDatabase.createDocument()` or the document is fetched from a
|
||||||
|
collection where this component is not stored, the string
|
||||||
|
"application/json" is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. attribute:: SodaDoc.version
|
||||||
|
|
||||||
|
This read-only attribute returns the version assigned to this document.
|
||||||
|
Documents created by :meth:`SodaDatabase.createDocument()` or fetched
|
||||||
|
from collections where this attribute is not stored will return None.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
25
doc/src/sodadoccur.rst
Normal file
25
doc/src/sodadoccur.rst
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.. _sodadoccur:
|
||||||
|
|
||||||
|
***************************
|
||||||
|
SODA Document Cursor Object
|
||||||
|
***************************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This object is an extension the DB API. It is returned by the method
|
||||||
|
:meth:`SodaOperation.getCursor()` and implements the iterator protocol.
|
||||||
|
Each iteration will return a :ref:`SODA document object <sodadoc>`.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaDocCursor.close()
|
||||||
|
|
||||||
|
Close the cursor now, rather than whenever __del__ is called. The cursor
|
||||||
|
will be unusable from this point forward; an Error exception will be raised
|
||||||
|
if any operation is attempted with the cursor.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
165
doc/src/sodaop.rst
Normal file
165
doc/src/sodaop.rst
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
.. _sodaop:
|
||||||
|
|
||||||
|
*********************
|
||||||
|
SODA Operation Object
|
||||||
|
*********************
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This object is an extension the DB API. It represents an operation that
|
||||||
|
will be performed on all or some of the documents in a SODA collection. It
|
||||||
|
is created by the method :meth:`SodaCollection.find()`.
|
||||||
|
|
||||||
|
SODA support in cx_Oracle is in Preview status and should not be used in
|
||||||
|
production. It will be supported with a future version of Oracle Client
|
||||||
|
libraries.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.count()
|
||||||
|
|
||||||
|
Returns a count of the number of documents in the collection that match
|
||||||
|
the criteria. If :meth:`~SodaOperation.skip()` or
|
||||||
|
:meth:`~SodaOperation.limit()` were called on this object, an exception is
|
||||||
|
raised.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.filter(value)
|
||||||
|
|
||||||
|
Sets a filter specification for complex document queries and ordering of
|
||||||
|
JSON documents. Filter specifications must be provided as a dictionary or
|
||||||
|
JSON-encoded string and can include comparisons, regular expressions,
|
||||||
|
logical and spatial operators, among others. See the
|
||||||
|
`overview of SODA filter specifications
|
||||||
|
<https://www.oracle.com/pls/topic/
|
||||||
|
lookup?ctx=dblatest&id=GUID-CB09C4E3-BBB1-40DC-88A8-8417821B0FBE>`__
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.getCursor()
|
||||||
|
|
||||||
|
Returns a :ref:`SODA Document Cursor object <sodadoccur>` that can be used
|
||||||
|
to iterate over the documents that match the criteria.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.getDocuments()
|
||||||
|
|
||||||
|
Returns a list of :ref:`SODA Document objects <sodadoc>` that match the
|
||||||
|
criteria.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.getOne()
|
||||||
|
|
||||||
|
Returns a single :ref:`SODA Document object <sodadoc>` that matches the
|
||||||
|
criteria. Note that if multiple documents match the criteria only the first
|
||||||
|
one is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.key(value)
|
||||||
|
|
||||||
|
Specifies that the document with the specified key should be returned.
|
||||||
|
This causes any previous calls made to this method and
|
||||||
|
:meth:`~SodaOperation.keys()` to be ignored.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.keys(seq)
|
||||||
|
|
||||||
|
Specifies that documents that match the keys found in the supplied sequence
|
||||||
|
should be returned. This causes any previous calls made to this method and
|
||||||
|
:meth:`~SodaOperation.key()` to be ignored.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.limit(value)
|
||||||
|
|
||||||
|
Specifies that only the specified number of documents should be returned.
|
||||||
|
This method is only usable for read operations such as
|
||||||
|
:meth:`~SodaOperation.getCursor()` and
|
||||||
|
:meth:`~SodaOperation.getDocuments()`. For write operations, any value set
|
||||||
|
using this method is ignored.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.remove()
|
||||||
|
|
||||||
|
Removes all of the documents in the collection that match the criteria. The
|
||||||
|
number of documents that have been removed is returned.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.replaceOne(doc)
|
||||||
|
|
||||||
|
Replaces a single document in the collection with the specified document.
|
||||||
|
The input document can be a dictionary or list or an existing
|
||||||
|
:ref:`SODA document object <sodadoc>`. A boolean indicating if a document
|
||||||
|
was replaced or not is returned.
|
||||||
|
|
||||||
|
Currently the method :meth:`~SodaOperation.key()` must be called before
|
||||||
|
this method can be called.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.replaceOneAndGet(doc)
|
||||||
|
|
||||||
|
Similarly to :meth:`~SodaOperation.replaceOne()`, this method replaces a
|
||||||
|
single document in the collection with the specified document. The only
|
||||||
|
difference is that it returns a :ref:`SODA document object <sodadoc>`.
|
||||||
|
Note that for performance reasons the returned document does not contain
|
||||||
|
the content.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.skip(value)
|
||||||
|
|
||||||
|
Specifies the number of documents that match the other criteria that will
|
||||||
|
be skipped. This method is only usable for read operations such as
|
||||||
|
:meth:`~SodaOperation.getCursor()` and
|
||||||
|
:meth:`~SodaOperation.getDocuments()`. For write operations, any value set
|
||||||
|
using this method is ignored.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: SodaOperation.version(value)
|
||||||
|
|
||||||
|
Specifies that documents with the specified version should be returned.
|
||||||
|
Typically this is used with :meth:`~SodaOperation.key()` to implement
|
||||||
|
optimistic locking, so that the write operation called later does not
|
||||||
|
affect a document that someone else has modified.
|
||||||
|
|
||||||
|
As a convenience, the SodaOperation object is returned so that further
|
||||||
|
criteria can be specified by chaining methods together.
|
||||||
|
|
||||||
|
.. versionadded:: 7.0
|
||||||
|
|
||||||
88
samples/SodaBasic.py
Normal file
88
samples/SodaBasic.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# soda1.py
|
||||||
|
# A basic Simple Oracle Document Access (SODA) example.
|
||||||
|
#
|
||||||
|
# This script requires cx_Oracle 7.0 and higher.
|
||||||
|
# The user must have been granted the SODA_APP privilege
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import cx_Oracle
|
||||||
|
import SampleEnv
|
||||||
|
|
||||||
|
connection = cx_Oracle.Connection(SampleEnv.MAIN_CONNECT_STRING)
|
||||||
|
|
||||||
|
# The general recommendation for simple SODA usage is to enable autocommit
|
||||||
|
connection.autocommit = True
|
||||||
|
|
||||||
|
# Create the parent object for SODA
|
||||||
|
soda = connection.getSodaDatabase()
|
||||||
|
|
||||||
|
# Create a new SODA collection and index
|
||||||
|
# This will open an existing collection, if the name is already in use.
|
||||||
|
collection = soda.createCollection("mycollection")
|
||||||
|
|
||||||
|
indexSpec = { 'name': 'CITY_IDX',
|
||||||
|
'fields': [ {
|
||||||
|
'path': 'address.city',
|
||||||
|
'datatype': 'string',
|
||||||
|
'order': 'asc' } ] }
|
||||||
|
collection.createIndex(indexSpec)
|
||||||
|
|
||||||
|
# Insert a documents.
|
||||||
|
# A system generated key is created by default.
|
||||||
|
content = {'name': 'Matilda', 'address': {'city': 'Melbourne'}}
|
||||||
|
doc = collection.insertOneAndGet(content)
|
||||||
|
key = doc.key
|
||||||
|
print('The key of the new SODA document is: ', key)
|
||||||
|
|
||||||
|
# Fetch the document back
|
||||||
|
doc = collection.find().key(key).getOne() # A SodaDocument
|
||||||
|
content = doc.getContent() # A JavaScript object
|
||||||
|
print('Retrieved SODA document dictionary is:')
|
||||||
|
print(content)
|
||||||
|
content = doc.getContentAsString() # A JSON string
|
||||||
|
print('Retrieved SODA document string is:')
|
||||||
|
print(content)
|
||||||
|
|
||||||
|
# Replace document contents
|
||||||
|
content = {'name': 'Matilda', 'address': {'city': 'Sydney'}}
|
||||||
|
collection.find().key(key).replaceOne(content)
|
||||||
|
|
||||||
|
# Insert some more documents without caring about their keys
|
||||||
|
content = {'name': 'Venkat', 'address': {'city': 'Bengaluru'}}
|
||||||
|
collection.insertOne(content)
|
||||||
|
content = {'name': 'May', 'address': {'city': 'London'}}
|
||||||
|
collection.insertOne(content)
|
||||||
|
content = {'name': 'Sally-Ann', 'address': {'city': 'San Francisco'}}
|
||||||
|
collection.insertOne(content)
|
||||||
|
|
||||||
|
# Find all documents with names like 'Ma%'
|
||||||
|
print("Names matching 'Ma%'")
|
||||||
|
documents = collection.find().filter({'name': {'$like': 'Ma%'}}).getDocuments()
|
||||||
|
for d in documents:
|
||||||
|
content = d.getContent()
|
||||||
|
print(content["name"])
|
||||||
|
|
||||||
|
# Count all documents
|
||||||
|
c = collection.find().count()
|
||||||
|
print('Collection has', c, 'documents')
|
||||||
|
|
||||||
|
# Remove documents with cities containing 'o'
|
||||||
|
print('Removing documents')
|
||||||
|
c = collection.find().filter({'address.city': {'$regex': '.*o.*'}}).remove()
|
||||||
|
print('Dropped', c, 'documents')
|
||||||
|
|
||||||
|
# Count all documents
|
||||||
|
c = collection.find().count()
|
||||||
|
print('Collection has', c, 'documents')
|
||||||
|
|
||||||
|
# Drop the collection
|
||||||
|
if collection.drop():
|
||||||
|
print('Collection was dropped')
|
||||||
|
|
||||||
@ -39,6 +39,19 @@ to &main_user;
|
|||||||
grant execute on dbms_aqadm to &main_user;
|
grant execute on dbms_aqadm to &main_user;
|
||||||
grant execute on dbms_lock to &main_user;
|
grant execute on dbms_lock to &main_user;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
for r in
|
||||||
|
( select role
|
||||||
|
from dba_roles
|
||||||
|
where role in ('SODA_APP')
|
||||||
|
) loop
|
||||||
|
execute immediate 'grant ' || r.role || ' to &main_user';
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
end;
|
||||||
|
/
|
||||||
|
|
||||||
create user &edition_user identified by &edition_password;
|
create user &edition_user identified by &edition_password;
|
||||||
|
|
||||||
grant
|
grant
|
||||||
|
|||||||
@ -50,6 +50,7 @@ static PyObject *cxoConnection_startup(cxoConnection*, PyObject*, PyObject*);
|
|||||||
static PyObject *cxoConnection_subscribe(cxoConnection*, PyObject*, PyObject*);
|
static PyObject *cxoConnection_subscribe(cxoConnection*, PyObject*, PyObject*);
|
||||||
static PyObject *cxoConnection_unsubscribe(cxoConnection*, PyObject*,
|
static PyObject *cxoConnection_unsubscribe(cxoConnection*, PyObject*,
|
||||||
PyObject*);
|
PyObject*);
|
||||||
|
static PyObject *cxoConnection_getSodaDatabase(cxoConnection*, PyObject*);
|
||||||
static PyObject *cxoConnection_getLTXID(cxoConnection*, void*);
|
static PyObject *cxoConnection_getLTXID(cxoConnection*, void*);
|
||||||
static PyObject *cxoConnection_getHandle(cxoConnection*, void*);
|
static PyObject *cxoConnection_getHandle(cxoConnection*, void*);
|
||||||
static PyObject *cxoConnection_getCurrentSchema(cxoConnection*, void*);
|
static PyObject *cxoConnection_getCurrentSchema(cxoConnection*, void*);
|
||||||
@ -108,6 +109,8 @@ static PyMethodDef cxoConnectionMethods[] = {
|
|||||||
{ "enq", (PyCFunction) cxoConnection_enqueue,
|
{ "enq", (PyCFunction) cxoConnection_enqueue,
|
||||||
METH_VARARGS | METH_KEYWORDS },
|
METH_VARARGS | METH_KEYWORDS },
|
||||||
{ "createlob", (PyCFunction) cxoConnection_createLob, METH_O },
|
{ "createlob", (PyCFunction) cxoConnection_createLob, METH_O },
|
||||||
|
{ "getSodaDatabase", (PyCFunction) cxoConnection_getSodaDatabase,
|
||||||
|
METH_NOARGS },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -498,6 +501,22 @@ static int cxoConnectionParams_finalize(cxoConnectionParams *params)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoConnection_getSodaFlags()
|
||||||
|
// Get the flags to use for SODA. This checks the autocommit flag and enables
|
||||||
|
// atomic commit if set to a true value. It also checks to ensure that the
|
||||||
|
// connection is valid.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags)
|
||||||
|
{
|
||||||
|
if (cxoConnection_isConnected(conn) < 0)
|
||||||
|
return -1;
|
||||||
|
*flags = (conn->autocommit) ? DPI_SODA_FLAGS_ATOMIC_COMMIT :
|
||||||
|
DPI_SODA_FLAGS_DEFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cxoConnection_isConnected()
|
// cxoConnection_isConnected()
|
||||||
// Determines if the connection object is connected to the database. If not,
|
// Determines if the connection object is connected to the database. If not,
|
||||||
@ -1615,6 +1634,19 @@ static PyObject *cxoConnection_unsubscribe(cxoConnection *conn, PyObject* args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoConnection_commit()
|
||||||
|
// Commit the transaction on the connection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoConnection_getSodaDatabase(cxoConnection *conn,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
if (cxoConnection_isConnected(conn) < 0)
|
||||||
|
return NULL;
|
||||||
|
return (PyObject*) cxoSodaDatabase_new(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// cxoConnection_getCurrentSchema()
|
// cxoConnection_getCurrentSchema()
|
||||||
// Return the current schema associated with the connection.
|
// Return the current schema associated with the connection.
|
||||||
|
|||||||
@ -46,6 +46,8 @@ PyObject *cxoIntegrityErrorException = NULL;
|
|||||||
PyObject *cxoInternalErrorException = NULL;
|
PyObject *cxoInternalErrorException = NULL;
|
||||||
PyObject *cxoProgrammingErrorException = NULL;
|
PyObject *cxoProgrammingErrorException = NULL;
|
||||||
PyObject *cxoNotSupportedErrorException = NULL;
|
PyObject *cxoNotSupportedErrorException = NULL;
|
||||||
|
PyObject *cxoJsonDumpFunction = NULL;
|
||||||
|
PyObject *cxoJsonLoadFunction = NULL;
|
||||||
cxoFuture *cxoFutureObj = NULL;
|
cxoFuture *cxoFutureObj = NULL;
|
||||||
dpiContext *cxoDpiContext = NULL;
|
dpiContext *cxoDpiContext = NULL;
|
||||||
dpiVersionInfo cxoClientVersionInfo;
|
dpiVersionInfo cxoClientVersionInfo;
|
||||||
@ -277,6 +279,11 @@ static PyObject *cxoModule_initialize(void)
|
|||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeObjectVar);
|
||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeRowidVar);
|
||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSessionPool);
|
||||||
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaCollection);
|
||||||
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDatabase);
|
||||||
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDoc);
|
||||||
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaDocCursor);
|
||||||
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSodaOperation);
|
||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeStringVar);
|
||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeSubscr);
|
||||||
CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar);
|
CXO_MAKE_TYPE_READY(&cxoPyTypeTimestampVar);
|
||||||
@ -339,6 +346,11 @@ static PyObject *cxoModule_initialize(void)
|
|||||||
CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
|
CXO_ADD_TYPE_OBJECT("EnqOptions", &cxoPyTypeEnqOptions)
|
||||||
CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
|
CXO_ADD_TYPE_OBJECT("DeqOptions", &cxoPyTypeDeqOptions)
|
||||||
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
|
CXO_ADD_TYPE_OBJECT("MessageProperties", &cxoPyTypeMsgProps)
|
||||||
|
CXO_ADD_TYPE_OBJECT("SodaCollection", &cxoPyTypeSodaCollection)
|
||||||
|
CXO_ADD_TYPE_OBJECT("SodaDatabase", &cxoPyTypeSodaDatabase)
|
||||||
|
CXO_ADD_TYPE_OBJECT("SodaDoc", &cxoPyTypeSodaDoc)
|
||||||
|
CXO_ADD_TYPE_OBJECT("SodaDocCursor", &cxoPyTypeSodaDocCursor)
|
||||||
|
CXO_ADD_TYPE_OBJECT("SodaOperation", &cxoPyTypeSodaOperation)
|
||||||
|
|
||||||
// the name "connect" is required by the DB API
|
// the name "connect" is required by the DB API
|
||||||
CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
|
CXO_ADD_TYPE_OBJECT("connect", &cxoPyTypeConnection)
|
||||||
|
|||||||
@ -80,6 +80,11 @@ typedef struct cxoObject cxoObject;
|
|||||||
typedef struct cxoObjectAttr cxoObjectAttr;
|
typedef struct cxoObjectAttr cxoObjectAttr;
|
||||||
typedef struct cxoObjectType cxoObjectType;
|
typedef struct cxoObjectType cxoObjectType;
|
||||||
typedef struct cxoSessionPool cxoSessionPool;
|
typedef struct cxoSessionPool cxoSessionPool;
|
||||||
|
typedef struct cxoSodaCollection cxoSodaCollection;
|
||||||
|
typedef struct cxoSodaDatabase cxoSodaDatabase;
|
||||||
|
typedef struct cxoSodaDoc cxoSodaDoc;
|
||||||
|
typedef struct cxoSodaDocCursor cxoSodaDocCursor;
|
||||||
|
typedef struct cxoSodaOperation cxoSodaOperation;
|
||||||
typedef struct cxoSubscr cxoSubscr;
|
typedef struct cxoSubscr cxoSubscr;
|
||||||
typedef struct cxoVar cxoVar;
|
typedef struct cxoVar cxoVar;
|
||||||
typedef struct cxoVarType cxoVarType;
|
typedef struct cxoVarType cxoVarType;
|
||||||
@ -137,6 +142,11 @@ extern PyTypeObject cxoPyTypeObjectType;
|
|||||||
extern PyTypeObject cxoPyTypeObjectVar;
|
extern PyTypeObject cxoPyTypeObjectVar;
|
||||||
extern PyTypeObject cxoPyTypeRowidVar;
|
extern PyTypeObject cxoPyTypeRowidVar;
|
||||||
extern PyTypeObject cxoPyTypeSessionPool;
|
extern PyTypeObject cxoPyTypeSessionPool;
|
||||||
|
extern PyTypeObject cxoPyTypeSodaCollection;
|
||||||
|
extern PyTypeObject cxoPyTypeSodaDatabase;
|
||||||
|
extern PyTypeObject cxoPyTypeSodaDoc;
|
||||||
|
extern PyTypeObject cxoPyTypeSodaDocCursor;
|
||||||
|
extern PyTypeObject cxoPyTypeSodaOperation;
|
||||||
extern PyTypeObject cxoPyTypeStringVar;
|
extern PyTypeObject cxoPyTypeStringVar;
|
||||||
extern PyTypeObject cxoPyTypeSubscr;
|
extern PyTypeObject cxoPyTypeSubscr;
|
||||||
extern PyTypeObject cxoPyTypeTimestampVar;
|
extern PyTypeObject cxoPyTypeTimestampVar;
|
||||||
@ -145,6 +155,10 @@ extern PyTypeObject cxoPyTypeTimestampVar;
|
|||||||
extern PyTypeObject *cxoPyTypeDate;
|
extern PyTypeObject *cxoPyTypeDate;
|
||||||
extern PyTypeObject *cxoPyTypeDateTime;
|
extern PyTypeObject *cxoPyTypeDateTime;
|
||||||
|
|
||||||
|
// JSON dump and load functions for use with SODA
|
||||||
|
extern PyObject *cxoJsonDumpFunction;
|
||||||
|
extern PyObject *cxoJsonLoadFunction;
|
||||||
|
|
||||||
// ODPI-C context and version information
|
// ODPI-C context and version information
|
||||||
extern dpiContext *cxoDpiContext;
|
extern dpiContext *cxoDpiContext;
|
||||||
extern dpiVersionInfo cxoClientVersionInfo;
|
extern dpiVersionInfo cxoClientVersionInfo;
|
||||||
@ -353,6 +367,43 @@ struct cxoSessionPool {
|
|||||||
PyTypeObject *connectionType;
|
PyTypeObject *connectionType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cxoSodaCollection {
|
||||||
|
PyObject_HEAD
|
||||||
|
dpiSodaColl *handle;
|
||||||
|
cxoSodaDatabase *db;
|
||||||
|
PyObject *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cxoSodaDatabase {
|
||||||
|
PyObject_HEAD
|
||||||
|
dpiSodaDb *handle;
|
||||||
|
cxoConnection *connection;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cxoSodaDoc {
|
||||||
|
PyObject_HEAD
|
||||||
|
cxoSodaDatabase *db;
|
||||||
|
dpiSodaDoc *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cxoSodaDocCursor {
|
||||||
|
PyObject_HEAD
|
||||||
|
cxoSodaDatabase *db;
|
||||||
|
dpiSodaDocCursor *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cxoSodaOperation {
|
||||||
|
PyObject_HEAD
|
||||||
|
cxoSodaCollection *coll;
|
||||||
|
dpiSodaOperOptions options;
|
||||||
|
uint32_t numKeyBuffers;
|
||||||
|
cxoBuffer *keyBuffers;
|
||||||
|
cxoBuffer keyBuffer;
|
||||||
|
cxoBuffer versionBuffer;
|
||||||
|
cxoBuffer filterBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct cxoSubscr {
|
struct cxoSubscr {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
dpiSubscr *handle;
|
dpiSubscr *handle;
|
||||||
@ -403,6 +454,7 @@ struct cxoVarType {
|
|||||||
int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding);
|
int cxoBuffer_fromObject(cxoBuffer *buf, PyObject *obj, const char *encoding);
|
||||||
int cxoBuffer_init(cxoBuffer *buf);
|
int cxoBuffer_init(cxoBuffer *buf);
|
||||||
|
|
||||||
|
int cxoConnection_getSodaFlags(cxoConnection *conn, uint32_t *flags);
|
||||||
int cxoConnection_isConnected(cxoConnection *conn);
|
int cxoConnection_isConnected(cxoConnection *conn);
|
||||||
|
|
||||||
int cxoCursor_performBind(cxoCursor *cursor);
|
int cxoCursor_performBind(cxoCursor *cursor);
|
||||||
@ -436,6 +488,18 @@ cxoObjectType *cxoObjectType_new(cxoConnection *connection,
|
|||||||
cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
|
cxoObjectType *cxoObjectType_newByName(cxoConnection *connection,
|
||||||
PyObject *name);
|
PyObject *name);
|
||||||
|
|
||||||
|
cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
|
||||||
|
dpiSodaColl *handle);
|
||||||
|
|
||||||
|
cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection);
|
||||||
|
|
||||||
|
cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle);
|
||||||
|
|
||||||
|
cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db,
|
||||||
|
dpiSodaDocCursor *handle);
|
||||||
|
|
||||||
|
cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *collection);
|
||||||
|
|
||||||
void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message);
|
void cxoSubscr_callback(cxoSubscr *subscr, dpiSubscrMessage *message);
|
||||||
|
|
||||||
PyObject *cxoTransform_dateFromTicks(PyObject *args);
|
PyObject *cxoTransform_dateFromTicks(PyObject *args);
|
||||||
@ -459,6 +523,9 @@ int cxoUtils_getBooleanValue(PyObject *obj, int defaultValue, int *value);
|
|||||||
int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
|
int cxoUtils_getModuleAndName(PyTypeObject *type, PyObject **module,
|
||||||
PyObject **name);
|
PyObject **name);
|
||||||
int cxoUtils_initializeDPI(void);
|
int cxoUtils_initializeDPI(void);
|
||||||
|
int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer);
|
||||||
|
int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
|
||||||
|
cxoSodaDoc **doc);
|
||||||
|
|
||||||
cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info);
|
cxoVarType *cxoVarType_fromDataTypeInfo(dpiDataTypeInfo *info);
|
||||||
cxoVarType *cxoVarType_fromPythonType(PyTypeObject *type);
|
cxoVarType *cxoVarType_fromPythonType(PyTypeObject *type);
|
||||||
|
|||||||
406
src/cxoSodaCollection.c
Normal file
406
src/cxoSodaCollection.c
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection.c
|
||||||
|
// Defines the routines for handling the SODA collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "cxoModule.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of functions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaCollection_free(cxoSodaCollection*);
|
||||||
|
static PyObject *cxoSodaCollection_repr(cxoSodaCollection*);
|
||||||
|
static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection*, PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_drop(cxoSodaCollection*, PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection*, PyObject*,
|
||||||
|
PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_find(cxoSodaCollection*, PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection*, PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection*, PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection*,
|
||||||
|
PyObject*);
|
||||||
|
static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection*, PyObject*);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of methods
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMethodDef cxoMethods[] = {
|
||||||
|
{ "createIndex", (PyCFunction) cxoSodaCollection_createIndex, METH_O },
|
||||||
|
{ "drop", (PyCFunction) cxoSodaCollection_drop, METH_NOARGS },
|
||||||
|
{ "dropIndex", (PyCFunction) cxoSodaCollection_dropIndex,
|
||||||
|
METH_VARARGS | METH_KEYWORDS },
|
||||||
|
{ "find", (PyCFunction) cxoSodaCollection_find, METH_NOARGS },
|
||||||
|
{ "getDataGuide", (PyCFunction) cxoSodaCollection_getDataGuide,
|
||||||
|
METH_NOARGS },
|
||||||
|
{ "insertOne", (PyCFunction) cxoSodaCollection_insertOne, METH_O },
|
||||||
|
{ "insertOneAndGet", (PyCFunction) cxoSodaCollection_insertOneAndGet,
|
||||||
|
METH_O },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of members
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMemberDef cxoMembers[] = {
|
||||||
|
{ "name", T_OBJECT, offsetof(cxoSodaCollection, name), READONLY },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of calculated members
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyGetSetDef cxoCalcMembers[] = {
|
||||||
|
{ "metadata", (getter) cxoSodaCollection_getMetadata, 0, 0, 0 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Python type declarations
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PyTypeObject cxoPyTypeSodaCollection = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"cx_Oracle.SodaCollection", // tp_name
|
||||||
|
sizeof(cxoSodaCollection), // tp_basicsize
|
||||||
|
0, // tp_itemsize
|
||||||
|
(destructor) cxoSodaCollection_free,// tp_dealloc
|
||||||
|
0, // tp_print
|
||||||
|
0, // tp_getattr
|
||||||
|
0, // tp_setattr
|
||||||
|
0, // tp_compare
|
||||||
|
(reprfunc) cxoSodaCollection_repr, // tp_repr
|
||||||
|
0, // tp_as_number
|
||||||
|
0, // tp_as_sequence
|
||||||
|
0, // tp_as_mapping
|
||||||
|
0, // tp_hash
|
||||||
|
0, // tp_call
|
||||||
|
0, // tp_str
|
||||||
|
0, // tp_getattro
|
||||||
|
0, // tp_setattro
|
||||||
|
0, // tp_as_buffer
|
||||||
|
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||||
|
0, // tp_doc
|
||||||
|
0, // tp_traverse
|
||||||
|
0, // tp_clear
|
||||||
|
0, // tp_richcompare
|
||||||
|
0, // tp_weaklistoffset
|
||||||
|
0, // tp_iter
|
||||||
|
0, // tp_iternext
|
||||||
|
cxoMethods, // tp_methods
|
||||||
|
cxoMembers, // tp_members
|
||||||
|
cxoCalcMembers, // tp_getset
|
||||||
|
0, // tp_base
|
||||||
|
0, // tp_dict
|
||||||
|
0, // tp_descr_get
|
||||||
|
0, // tp_descr_set
|
||||||
|
0, // tp_dictoffset
|
||||||
|
0, // tp_init
|
||||||
|
0, // tp_alloc
|
||||||
|
0, // tp_new
|
||||||
|
0, // tp_free
|
||||||
|
0, // tp_is_gc
|
||||||
|
0 // tp_bases
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_initialize()
|
||||||
|
// Initialize a new collection with its attributes.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static int cxoSodaCollection_initialize(cxoSodaCollection *coll,
|
||||||
|
cxoSodaDatabase *db, const char *encoding, dpiSodaColl *handle)
|
||||||
|
{
|
||||||
|
uint32_t nameLength;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
// get name from ODPI-C
|
||||||
|
if (dpiSodaColl_getName(handle, &name, &nameLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnInt();
|
||||||
|
coll->name = cxoPyString_fromEncodedString(name, nameLength, encoding,
|
||||||
|
NULL);
|
||||||
|
if (!coll->name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// set base attributes (handle should not be added until there is no
|
||||||
|
// possibility of further failure)
|
||||||
|
coll->handle = handle;
|
||||||
|
Py_INCREF(db);
|
||||||
|
coll->db = db;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_new()
|
||||||
|
// Create a new SODA collection object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
cxoSodaCollection *cxoSodaCollection_new(cxoSodaDatabase *db,
|
||||||
|
dpiSodaColl *handle)
|
||||||
|
{
|
||||||
|
cxoSodaCollection *coll;
|
||||||
|
|
||||||
|
coll = (cxoSodaCollection*)
|
||||||
|
cxoPyTypeSodaCollection.tp_alloc(&cxoPyTypeSodaCollection, 0);
|
||||||
|
if (!coll)
|
||||||
|
return NULL;
|
||||||
|
if (cxoSodaCollection_initialize(coll, db,
|
||||||
|
db->connection->encodingInfo.encoding, handle) < 0) {
|
||||||
|
Py_DECREF(coll);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return coll;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_free()
|
||||||
|
// Free the memory associated with a SODA collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaCollection_free(cxoSodaCollection *coll)
|
||||||
|
{
|
||||||
|
if (coll->handle) {
|
||||||
|
dpiSodaColl_release(coll->handle);
|
||||||
|
coll->handle = NULL;
|
||||||
|
}
|
||||||
|
Py_CLEAR(coll->db);
|
||||||
|
Py_CLEAR(coll->name);
|
||||||
|
Py_TYPE(coll)->tp_free((PyObject*) coll);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_repr()
|
||||||
|
// Return a string representation of a SODA collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_repr(cxoSodaCollection *coll)
|
||||||
|
{
|
||||||
|
PyObject *module, *name, *result;
|
||||||
|
|
||||||
|
if (cxoUtils_getModuleAndName(Py_TYPE(coll), &module, &name) < 0)
|
||||||
|
return NULL;
|
||||||
|
result = cxoUtils_formatString("<%s.%s %s>",
|
||||||
|
PyTuple_Pack(3, module, name, coll->name));
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_createIndex()
|
||||||
|
// Create an index on a SODA collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_createIndex(cxoSodaCollection *coll,
|
||||||
|
PyObject *specObj)
|
||||||
|
{
|
||||||
|
cxoBuffer specBuffer;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoUtils_processJsonArg(specObj, &specBuffer) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_createIndex(coll->handle, specBuffer.ptr,
|
||||||
|
specBuffer.size, flags);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
cxoBuffer_clear(&specBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_drop()
|
||||||
|
// Create a SODA collection and return it.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_drop(cxoSodaCollection *coll,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
uint32_t flags;
|
||||||
|
int isDropped;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (dpiSodaColl_drop(coll->handle, flags, &isDropped) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (isDropped)
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_dropIndex()
|
||||||
|
// Drop an index on a SODA collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_dropIndex(cxoSodaCollection *coll,
|
||||||
|
PyObject *args, PyObject *keywordArgs)
|
||||||
|
{
|
||||||
|
static char *keywordList[] = { "name", "force", NULL };
|
||||||
|
int status, isDropped, force;
|
||||||
|
PyObject *nameObj, *forceObj;
|
||||||
|
cxoBuffer nameBuffer;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
forceObj = NULL;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
|
||||||
|
&nameObj, &forceObj))
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_getBooleanValue(forceObj, 0, &force) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// drop index
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (force)
|
||||||
|
flags |= DPI_SODA_FLAGS_INDEX_DROP_FORCE;
|
||||||
|
if (cxoBuffer_fromObject(&nameBuffer, nameObj,
|
||||||
|
coll->db->connection->encodingInfo.encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_dropIndex(coll->handle, nameBuffer.ptr,
|
||||||
|
nameBuffer.size, flags, &isDropped);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
cxoBuffer_clear(&nameBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (isDropped)
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_find()
|
||||||
|
// Creates an operation options object which can be used to perform a number
|
||||||
|
// of operations on the collection using the criteria set on the object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_find(cxoSodaCollection *coll,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
return (PyObject*) cxoSodaOperation_new(coll);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_getDataGuide()
|
||||||
|
// Return the data guide associated with the collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection *coll,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *handle;
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_getDataGuide(coll->handle, flags, &handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (handle) {
|
||||||
|
doc = cxoSodaDoc_new(coll->db, handle);
|
||||||
|
if (!doc)
|
||||||
|
return NULL;
|
||||||
|
return (PyObject*) doc;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_insertOne()
|
||||||
|
// Insert a single document into the collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection *coll,
|
||||||
|
PyObject *arg)
|
||||||
|
{
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoUtils_processSodaDocArg(coll->db, arg, &doc) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_insertOne(coll->handle, doc->handle, flags, NULL);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
Py_DECREF(doc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(doc);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_insertOneAndGet()
|
||||||
|
// Insert a single document into the collection and return a document
|
||||||
|
// containing all but the content itself.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection *coll,
|
||||||
|
PyObject *arg)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *returnedDoc;
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoUtils_processSodaDocArg(coll->db, arg, &doc) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_insertOne(coll->handle, doc->handle, flags,
|
||||||
|
&returnedDoc);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
Py_DECREF(doc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(doc);
|
||||||
|
return (PyObject*) cxoSodaDoc_new(coll->db, returnedDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaCollection_getMetadata()
|
||||||
|
// Retrieve the metadata for the collection.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaCollection_getMetadata(cxoSodaCollection *coll,
|
||||||
|
PyObject *unused)
|
||||||
|
{
|
||||||
|
PyObject *str, *result;
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaColl_getMetadata(coll->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
str = PyUnicode_Decode(value, valueLength,
|
||||||
|
coll->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
if (!str)
|
||||||
|
return NULL;
|
||||||
|
result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
|
||||||
|
Py_DECREF(str);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
381
src/cxoSodaDatabase.c
Normal file
381
src/cxoSodaDatabase.c
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase.c
|
||||||
|
// Defines the routines for handling the SODA database.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "cxoModule.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Declaration of functions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDatabase_free(cxoSodaDatabase*);
|
||||||
|
static PyObject *cxoSodaDatabase_repr(cxoSodaDatabase*);
|
||||||
|
static PyObject *cxoSodaDatabase_createCollection(cxoSodaDatabase*,
|
||||||
|
PyObject*, PyObject*);
|
||||||
|
static PyObject *cxoSodaDatabase_createDocument(cxoSodaDatabase*,
|
||||||
|
PyObject*, PyObject*);
|
||||||
|
static PyObject *cxoSodaDatabase_getCollectionNames(cxoSodaDatabase*,
|
||||||
|
PyObject*, PyObject*);
|
||||||
|
static PyObject *cxoSodaDatabase_openCollection(cxoSodaDatabase*, PyObject*);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of methods for Python type "SodaDatabase"
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMethodDef cxoMethods[] = {
|
||||||
|
{ "createCollection", (PyCFunction) cxoSodaDatabase_createCollection,
|
||||||
|
METH_VARARGS | METH_KEYWORDS },
|
||||||
|
{ "createDocument", (PyCFunction) cxoSodaDatabase_createDocument,
|
||||||
|
METH_VARARGS | METH_KEYWORDS },
|
||||||
|
{ "getCollectionNames", (PyCFunction) cxoSodaDatabase_getCollectionNames,
|
||||||
|
METH_VARARGS | METH_KEYWORDS },
|
||||||
|
{ "openCollection", (PyCFunction) cxoSodaDatabase_openCollection, METH_O },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Python type declarations
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PyTypeObject cxoPyTypeSodaDatabase = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"cx_Oracle.SodaDatabase", // tp_name
|
||||||
|
sizeof(cxoSodaDatabase), // tp_basicsize
|
||||||
|
0, // tp_itemsize
|
||||||
|
(destructor) cxoSodaDatabase_free, // tp_dealloc
|
||||||
|
0, // tp_print
|
||||||
|
0, // tp_getattr
|
||||||
|
0, // tp_setattr
|
||||||
|
0, // tp_compare
|
||||||
|
(reprfunc) cxoSodaDatabase_repr, // tp_repr
|
||||||
|
0, // tp_as_number
|
||||||
|
0, // tp_as_sequence
|
||||||
|
0, // tp_as_mapping
|
||||||
|
0, // tp_hash
|
||||||
|
0, // tp_call
|
||||||
|
0, // tp_str
|
||||||
|
0, // tp_getattro
|
||||||
|
0, // tp_setattro
|
||||||
|
0, // tp_as_buffer
|
||||||
|
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||||
|
0, // tp_doc
|
||||||
|
0, // tp_traverse
|
||||||
|
0, // tp_clear
|
||||||
|
0, // tp_richcompare
|
||||||
|
0, // tp_weaklistoffset
|
||||||
|
0, // tp_iter
|
||||||
|
0, // tp_iternext
|
||||||
|
cxoMethods, // tp_methods
|
||||||
|
0, // tp_members
|
||||||
|
0, // tp_getset
|
||||||
|
0, // tp_base
|
||||||
|
0, // tp_dict
|
||||||
|
0, // tp_descr_get
|
||||||
|
0, // tp_descr_set
|
||||||
|
0, // tp_dictoffset
|
||||||
|
0, // tp_init
|
||||||
|
0, // tp_alloc
|
||||||
|
0, // tp_new
|
||||||
|
0, // tp_free
|
||||||
|
0, // tp_is_gc
|
||||||
|
0 // tp_bases
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_new()
|
||||||
|
// Create a new SODA database object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
cxoSodaDatabase *cxoSodaDatabase_new(cxoConnection *connection)
|
||||||
|
{
|
||||||
|
cxoSodaDatabase *db;
|
||||||
|
PyObject *module;
|
||||||
|
|
||||||
|
// load JSON dump/load functions, if needed
|
||||||
|
if (!cxoJsonDumpFunction || !cxoJsonLoadFunction) {
|
||||||
|
module = PyImport_ImportModule("json");
|
||||||
|
if (!module)
|
||||||
|
return NULL;
|
||||||
|
if (!cxoJsonDumpFunction) {
|
||||||
|
cxoJsonDumpFunction = PyObject_GetAttrString(module, "dumps");
|
||||||
|
if (!cxoJsonDumpFunction)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!cxoJsonLoadFunction) {
|
||||||
|
cxoJsonLoadFunction = PyObject_GetAttrString(module, "loads");
|
||||||
|
if (!cxoJsonLoadFunction)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create SODA database object
|
||||||
|
db = (cxoSodaDatabase*)
|
||||||
|
cxoPyTypeSodaDatabase.tp_alloc(&cxoPyTypeSodaDatabase, 0);
|
||||||
|
if (!db)
|
||||||
|
return NULL;
|
||||||
|
if (dpiConn_getSodaDb(connection->handle, &db->handle) < 0) {
|
||||||
|
Py_DECREF(db);
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(connection);
|
||||||
|
db->connection = connection;
|
||||||
|
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_free()
|
||||||
|
// Free the memory associated with a SODA database.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDatabase_free(cxoSodaDatabase *db)
|
||||||
|
{
|
||||||
|
if (db->handle) {
|
||||||
|
dpiSodaDb_release(db->handle);
|
||||||
|
db->handle = NULL;
|
||||||
|
}
|
||||||
|
Py_CLEAR(db->connection);
|
||||||
|
Py_TYPE(db)->tp_free((PyObject*) db);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_repr()
|
||||||
|
// Return a string representation of a SODA database.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDatabase_repr(cxoSodaDatabase *db)
|
||||||
|
{
|
||||||
|
PyObject *connectionRepr, *module, *name, *result;
|
||||||
|
|
||||||
|
connectionRepr = PyObject_Repr((PyObject*) db->connection);
|
||||||
|
if (!connectionRepr)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_getModuleAndName(Py_TYPE(db), &module, &name) < 0) {
|
||||||
|
Py_DECREF(connectionRepr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = cxoUtils_formatString("<%s.%s on %s>",
|
||||||
|
PyTuple_Pack(3, module, name, connectionRepr));
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(name);
|
||||||
|
Py_DECREF(connectionRepr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_createCollection()
|
||||||
|
// Create a SODA collection and return it.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDatabase_createCollection(cxoSodaDatabase *db,
|
||||||
|
PyObject *args, PyObject *keywordArgs)
|
||||||
|
{
|
||||||
|
static char *keywordList[] = { "name", "metadata", "mapMode", NULL };
|
||||||
|
PyObject *nameObj, *metadataObj, *mapModeObj;
|
||||||
|
cxoBuffer nameBuffer, metadataBuffer;
|
||||||
|
cxoSodaCollection *coll;
|
||||||
|
const char *encoding;
|
||||||
|
dpiSodaColl *handle;
|
||||||
|
int status, mapMode;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
nameObj = metadataObj = mapModeObj = NULL;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
|
||||||
|
&nameObj, &metadataObj, &mapModeObj))
|
||||||
|
return NULL;
|
||||||
|
encoding = db->connection->encodingInfo.encoding;
|
||||||
|
if (cxoBuffer_fromObject(&nameBuffer, nameObj, encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_processJsonArg(metadataObj, &metadataBuffer) < 0) {
|
||||||
|
cxoBuffer_clear(&nameBuffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cxoUtils_getBooleanValue(mapModeObj, 0, &mapMode) < 0) {
|
||||||
|
cxoBuffer_clear(&nameBuffer);
|
||||||
|
cxoBuffer_clear(&metadataBuffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create collection
|
||||||
|
if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (mapMode)
|
||||||
|
flags |= DPI_SODA_FLAGS_CREATE_COLL_MAP;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaDb_createCollection(db->handle, nameBuffer.ptr,
|
||||||
|
nameBuffer.size, metadataBuffer.ptr, metadataBuffer.size, flags,
|
||||||
|
&handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
cxoBuffer_clear(&nameBuffer);
|
||||||
|
cxoBuffer_clear(&metadataBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
coll = cxoSodaCollection_new(db, handle);
|
||||||
|
if (!coll) {
|
||||||
|
dpiSodaColl_release(handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (PyObject*) coll;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_createDocument()
|
||||||
|
// Create a SODA document with the specified key, content and media type.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDatabase_createDocument(cxoSodaDatabase *db,
|
||||||
|
PyObject *args, PyObject *keywordArgs)
|
||||||
|
{
|
||||||
|
static char *keywordList[] = { "content", "key", "mediaType", NULL };
|
||||||
|
cxoBuffer contentBuffer, keyBuffer, mediaTypeBuffer;
|
||||||
|
PyObject *contentObj, *keyObj, *mediaTypeObj;
|
||||||
|
const char *encoding;
|
||||||
|
dpiSodaDoc *doc;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
keyObj = mediaTypeObj = NULL;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|OO", keywordList,
|
||||||
|
&contentObj, &keyObj, &mediaTypeObj))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// content must be converted to string if it is a dictionary
|
||||||
|
if (PyDict_Check(contentObj)) {
|
||||||
|
contentObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction,
|
||||||
|
contentObj, NULL);
|
||||||
|
if (!contentObj)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get buffers for each of the content, key and media type parameters
|
||||||
|
if (cxoUtils_processJsonArg(contentObj, &contentBuffer) < 0)
|
||||||
|
return NULL;
|
||||||
|
encoding = db->connection->encodingInfo.encoding;
|
||||||
|
if (cxoBuffer_fromObject(&keyBuffer, keyObj, encoding) < 0) {
|
||||||
|
cxoBuffer_clear(&contentBuffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cxoBuffer_fromObject(&mediaTypeBuffer, mediaTypeObj, encoding) < 0) {
|
||||||
|
cxoBuffer_clear(&contentBuffer);
|
||||||
|
cxoBuffer_clear(&keyBuffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create SODA document
|
||||||
|
status = dpiSodaDb_createDocument(db->handle, keyBuffer.ptr,
|
||||||
|
keyBuffer.size, contentBuffer.ptr, contentBuffer.size,
|
||||||
|
mediaTypeBuffer.ptr, mediaTypeBuffer.size, DPI_SODA_FLAGS_DEFAULT,
|
||||||
|
&doc);
|
||||||
|
cxoBuffer_clear(&contentBuffer);
|
||||||
|
cxoBuffer_clear(&keyBuffer);
|
||||||
|
cxoBuffer_clear(&mediaTypeBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
|
||||||
|
return (PyObject*) cxoSodaDoc_new(db, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_getCollectionNames()
|
||||||
|
// Return a list of the names of the collections found in the database.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDatabase_getCollectionNames(cxoSodaDatabase *db,
|
||||||
|
PyObject *args, PyObject *keywordArgs)
|
||||||
|
{
|
||||||
|
static char *keywordList[] = { "startName", "limit", NULL };
|
||||||
|
PyObject *startName, *result, *temp;
|
||||||
|
dpiSodaCollNames collNames;
|
||||||
|
cxoBuffer startNameBuffer;
|
||||||
|
uint32_t limit, i, flags;
|
||||||
|
const char *encoding;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
limit = 0;
|
||||||
|
startName = NULL;
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "|Oi", keywordList,
|
||||||
|
&startName, &limit))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// get collection names from the database
|
||||||
|
encoding = db->connection->encodingInfo.encoding;
|
||||||
|
if (cxoBuffer_fromObject(&startNameBuffer, startName, encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaDb_getCollectionNames(db->handle,
|
||||||
|
(const char*) startNameBuffer.ptr, startNameBuffer.size, limit,
|
||||||
|
flags, &collNames);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
cxoBuffer_clear(&startNameBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
|
||||||
|
// transform results into a Python list
|
||||||
|
result = PyList_New(collNames.numNames);
|
||||||
|
if (!result)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < collNames.numNames; i++) {
|
||||||
|
temp = cxoPyString_fromEncodedString(collNames.names[i],
|
||||||
|
collNames.nameLengths[i], encoding, NULL);
|
||||||
|
if (!temp) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(result, i, temp);
|
||||||
|
}
|
||||||
|
if (dpiSodaDb_freeCollectionNames(db->handle, &collNames) < 0) {
|
||||||
|
Py_DECREF(result);
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDatabase_openCollection()
|
||||||
|
// Open a SODA collection and return it.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDatabase_openCollection(cxoSodaDatabase *db,
|
||||||
|
PyObject *nameObj)
|
||||||
|
{
|
||||||
|
cxoSodaCollection *coll;
|
||||||
|
cxoBuffer nameBuffer;
|
||||||
|
dpiSodaColl *handle;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// open collection
|
||||||
|
if (cxoBuffer_fromObject(&nameBuffer, nameObj,
|
||||||
|
db->connection->encodingInfo.encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoConnection_getSodaFlags(db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaDb_openCollection(db->handle, nameBuffer.ptr,
|
||||||
|
nameBuffer.size, flags, &handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
cxoBuffer_clear(&nameBuffer);
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (!handle)
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
coll = cxoSodaCollection_new(db, handle);
|
||||||
|
if (!coll) {
|
||||||
|
dpiSodaColl_release(handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (PyObject*) coll;
|
||||||
|
}
|
||||||
|
|
||||||
309
src/cxoSodaDoc.c
Normal file
309
src/cxoSodaDoc.c
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc.c
|
||||||
|
// Defines the routines for handling SODA documents.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "cxoModule.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Declaration of functions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDoc_free(cxoSodaDoc*);
|
||||||
|
static PyObject *cxoSodaDoc_repr(cxoSodaDoc*);
|
||||||
|
static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc*, void*);
|
||||||
|
static PyObject *cxoSodaDoc_getKey(cxoSodaDoc*, void*);
|
||||||
|
static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc*, void*);
|
||||||
|
static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc*, void*);
|
||||||
|
static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc*, void*);
|
||||||
|
static PyObject *cxoSodaDoc_getContent(cxoSodaDoc*, PyObject*);
|
||||||
|
static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc*, PyObject*);
|
||||||
|
static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc*, PyObject*);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of methods
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMethodDef cxoMethods[] = {
|
||||||
|
{ "getContent", (PyCFunction) cxoSodaDoc_getContent, METH_NOARGS },
|
||||||
|
{ "getContentAsBytes", (PyCFunction) cxoSodaDoc_getContentAsBytes,
|
||||||
|
METH_NOARGS },
|
||||||
|
{ "getContentAsString", (PyCFunction) cxoSodaDoc_getContentAsString,
|
||||||
|
METH_NOARGS },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of calculated members
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyGetSetDef cxoCalcMembers[] = {
|
||||||
|
{ "createdOn", (getter) cxoSodaDoc_getCreatedOn, 0, 0, 0 },
|
||||||
|
{ "key", (getter) cxoSodaDoc_getKey, 0, 0, 0 },
|
||||||
|
{ "lastModified", (getter) cxoSodaDoc_getLastModified, 0, 0, 0 },
|
||||||
|
{ "mediaType", (getter) cxoSodaDoc_getMediaType, 0, 0, 0 },
|
||||||
|
{ "version", (getter) cxoSodaDoc_getVersion, 0, 0, 0 },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Python type declarations
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PyTypeObject cxoPyTypeSodaDoc = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"cx_Oracle.SodaDoc", // tp_name
|
||||||
|
sizeof(cxoSodaDoc), // tp_basicsize
|
||||||
|
0, // tp_itemsize
|
||||||
|
(destructor) cxoSodaDoc_free, // tp_dealloc
|
||||||
|
0, // tp_print
|
||||||
|
0, // tp_getattr
|
||||||
|
0, // tp_setattr
|
||||||
|
0, // tp_compare
|
||||||
|
(reprfunc) cxoSodaDoc_repr, // tp_repr
|
||||||
|
0, // tp_as_number
|
||||||
|
0, // tp_as_sequence
|
||||||
|
0, // tp_as_mapping
|
||||||
|
0, // tp_hash
|
||||||
|
0, // tp_call
|
||||||
|
0, // tp_str
|
||||||
|
0, // tp_getattro
|
||||||
|
0, // tp_setattro
|
||||||
|
0, // tp_as_buffer
|
||||||
|
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||||
|
0, // tp_doc
|
||||||
|
0, // tp_traverse
|
||||||
|
0, // tp_clear
|
||||||
|
0, // tp_richcompare
|
||||||
|
0, // tp_weaklistoffset
|
||||||
|
0, // tp_iter
|
||||||
|
0, // tp_iternext
|
||||||
|
cxoMethods, // tp_methods
|
||||||
|
0, // tp_members
|
||||||
|
cxoCalcMembers, // tp_getset
|
||||||
|
0, // tp_base
|
||||||
|
0, // tp_dict
|
||||||
|
0, // tp_descr_get
|
||||||
|
0, // tp_descr_set
|
||||||
|
0, // tp_dictoffset
|
||||||
|
0, // tp_init
|
||||||
|
0, // tp_alloc
|
||||||
|
0, // tp_new
|
||||||
|
0, // tp_free
|
||||||
|
0, // tp_is_gc
|
||||||
|
0 // tp_bases
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_new()
|
||||||
|
// Create a new SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
cxoSodaDoc *cxoSodaDoc_new(cxoSodaDatabase *db, dpiSodaDoc *handle)
|
||||||
|
{
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
|
||||||
|
doc = (cxoSodaDoc*) cxoPyTypeSodaDoc.tp_alloc(&cxoPyTypeSodaDoc, 0);
|
||||||
|
if (!doc) {
|
||||||
|
dpiSodaDoc_release(handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(db);
|
||||||
|
doc->db = db;
|
||||||
|
doc->handle = handle;
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_free()
|
||||||
|
// Free the memory associated with a SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDoc_free(cxoSodaDoc *doc)
|
||||||
|
{
|
||||||
|
if (doc->handle) {
|
||||||
|
dpiSodaDoc_release(doc->handle);
|
||||||
|
doc->handle = NULL;
|
||||||
|
}
|
||||||
|
Py_CLEAR(doc->db);
|
||||||
|
Py_TYPE(doc)->tp_free((PyObject*) doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_repr()
|
||||||
|
// Return a string representation of a SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_repr(cxoSodaDoc *doc)
|
||||||
|
{
|
||||||
|
PyObject *module, *name, *result, *keyObj;
|
||||||
|
uint32_t keyLength;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getKey(doc->handle, &key, &keyLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
keyObj = cxoPyString_fromEncodedString(key, keyLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
if (!keyObj)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_getModuleAndName(Py_TYPE(doc), &module, &name) < 0) {
|
||||||
|
Py_DECREF(keyObj);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = cxoUtils_formatString("<%s.%s with key %s>",
|
||||||
|
PyTuple_Pack(3, module, name, keyObj));
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getCreatedOn()
|
||||||
|
// Retrieve the time the SODA document was created, as a string in ISO 8601
|
||||||
|
// format.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getCreatedOn(cxoSodaDoc *doc, void *unused)
|
||||||
|
{
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getCreatedOn(doc->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (valueLength > 0)
|
||||||
|
return cxoPyString_fromEncodedString(value, valueLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getKey()
|
||||||
|
// Retrieve the key for the SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getKey(cxoSodaDoc *doc, void *unused)
|
||||||
|
{
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getKey(doc->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (valueLength > 0)
|
||||||
|
return cxoPyString_fromEncodedString(value, valueLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getLastModified()
|
||||||
|
// Retrieve the time the SODA document was last modified, as a string in ISO
|
||||||
|
// 8601 format.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getLastModified(cxoSodaDoc *doc, void *unused)
|
||||||
|
{
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getLastModified(doc->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (valueLength > 0)
|
||||||
|
return cxoPyString_fromEncodedString(value, valueLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getMediaType()
|
||||||
|
// Retrieve the media type of the SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getMediaType(cxoSodaDoc *doc, void *unused)
|
||||||
|
{
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getMediaType(doc->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (valueLength > 0)
|
||||||
|
return cxoPyString_fromEncodedString(value, valueLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getVersion()
|
||||||
|
// Retrieve the version for the SODA document.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getVersion(cxoSodaDoc *doc, void *unused)
|
||||||
|
{
|
||||||
|
uint32_t valueLength;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getVersion(doc->handle, &value, &valueLength) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (valueLength > 0)
|
||||||
|
return cxoPyString_fromEncodedString(value, valueLength,
|
||||||
|
doc->db->connection->encodingInfo.encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getContent()
|
||||||
|
// Get the content from the document and return a Python object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getContent(cxoSodaDoc *doc, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *str, *result;
|
||||||
|
|
||||||
|
str = cxoSodaDoc_getContentAsString(doc, args);
|
||||||
|
if (!str)
|
||||||
|
return NULL;
|
||||||
|
if (str == Py_None)
|
||||||
|
return str;
|
||||||
|
result = PyObject_CallFunctionObjArgs(cxoJsonLoadFunction, str, NULL);
|
||||||
|
Py_DECREF(str);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getContentAsBytes()
|
||||||
|
// Get the content from the document and return a bytes object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getContentAsBytes(cxoSodaDoc *doc, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *content, *encoding;
|
||||||
|
uint32_t contentLength;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
|
||||||
|
&encoding) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (contentLength > 0)
|
||||||
|
return PyBytes_FromStringAndSize(content, contentLength);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDoc_getContentAsString()
|
||||||
|
// Get the content from the document and return a string.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDoc_getContentAsString(cxoSodaDoc *doc, PyObject *args)
|
||||||
|
{
|
||||||
|
const char *content, *encoding;
|
||||||
|
uint32_t contentLength;
|
||||||
|
|
||||||
|
if (dpiSodaDoc_getContent(doc->handle, &content, &contentLength,
|
||||||
|
&encoding) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (contentLength > 0)
|
||||||
|
return PyUnicode_Decode(content, contentLength, encoding, NULL);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
184
src/cxoSodaDocCursor.c
Normal file
184
src/cxoSodaDocCursor.c
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor.c
|
||||||
|
// Defines the routines for handling SODA document cursors. These cursors
|
||||||
|
// permit iterating over the documents that match the criteria that was
|
||||||
|
// specified by the user.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "cxoModule.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Declaration of functions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDocCursor_free(cxoSodaDocCursor*);
|
||||||
|
static PyObject *cxoSodaDocCursor_repr(cxoSodaDocCursor*);
|
||||||
|
static PyObject *cxoSodaDocCursor_getIter(cxoSodaDocCursor*);
|
||||||
|
static PyObject *cxoSodaDocCursor_getNext(cxoSodaDocCursor*);
|
||||||
|
static PyObject *cxoSodaDocCursor_close(cxoSodaDocCursor*, PyObject*);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of methods
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMethodDef cxoMethods[] = {
|
||||||
|
{ "close", (PyCFunction) cxoSodaDocCursor_close, METH_NOARGS },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Python type declarations
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PyTypeObject cxoPyTypeSodaDocCursor = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"cx_Oracle.SodaDocCursor", // tp_name
|
||||||
|
sizeof(cxoSodaDocCursor), // tp_basicsize
|
||||||
|
0, // tp_itemsize
|
||||||
|
(destructor) cxoSodaDocCursor_free, // tp_dealloc
|
||||||
|
0, // tp_print
|
||||||
|
0, // tp_getattr
|
||||||
|
0, // tp_setattr
|
||||||
|
0, // tp_compare
|
||||||
|
(reprfunc) cxoSodaDocCursor_repr, // tp_repr
|
||||||
|
0, // tp_as_number
|
||||||
|
0, // tp_as_sequence
|
||||||
|
0, // tp_as_mapping
|
||||||
|
0, // tp_hash
|
||||||
|
0, // tp_call
|
||||||
|
0, // tp_str
|
||||||
|
0, // tp_getattro
|
||||||
|
0, // tp_setattro
|
||||||
|
0, // tp_as_buffer
|
||||||
|
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||||
|
0, // tp_doc
|
||||||
|
0, // tp_traverse
|
||||||
|
0, // tp_clear
|
||||||
|
0, // tp_richcompare
|
||||||
|
0, // tp_weaklistoffset
|
||||||
|
(getiterfunc) cxoSodaDocCursor_getIter, // tp_iter
|
||||||
|
(iternextfunc) cxoSodaDocCursor_getNext, // tp_iternext
|
||||||
|
cxoMethods, // tp_methods
|
||||||
|
0, // tp_members
|
||||||
|
0, // tp_getset
|
||||||
|
0, // tp_base
|
||||||
|
0, // tp_dict
|
||||||
|
0, // tp_descr_get
|
||||||
|
0, // tp_descr_set
|
||||||
|
0, // tp_dictoffset
|
||||||
|
0, // tp_init
|
||||||
|
0, // tp_alloc
|
||||||
|
0, // tp_new
|
||||||
|
0, // tp_free
|
||||||
|
0, // tp_is_gc
|
||||||
|
0 // tp_bases
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_new()
|
||||||
|
// Create a new SODA document cursor.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
cxoSodaDocCursor *cxoSodaDocCursor_new(cxoSodaDatabase *db,
|
||||||
|
dpiSodaDocCursor *handle)
|
||||||
|
{
|
||||||
|
cxoSodaDocCursor *cursor;
|
||||||
|
|
||||||
|
cursor = (cxoSodaDocCursor*)
|
||||||
|
cxoPyTypeSodaDocCursor.tp_alloc(&cxoPyTypeSodaDocCursor, 0);
|
||||||
|
if (!cursor) {
|
||||||
|
dpiSodaDocCursor_release(handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_INCREF(db);
|
||||||
|
cursor->db = db;
|
||||||
|
cursor->handle = handle;
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_free()
|
||||||
|
// Free the memory associated with a SODA document cursor.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaDocCursor_free(cxoSodaDocCursor *cursor)
|
||||||
|
{
|
||||||
|
if (cursor->handle) {
|
||||||
|
dpiSodaDocCursor_release(cursor->handle);
|
||||||
|
cursor->handle = NULL;
|
||||||
|
}
|
||||||
|
Py_CLEAR(cursor->db);
|
||||||
|
Py_TYPE(cursor)->tp_free((PyObject*) cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_repr()
|
||||||
|
// Return a string representation of a SODA document cursor.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDocCursor_repr(cxoSodaDocCursor *cursor)
|
||||||
|
{
|
||||||
|
PyObject *module, *name, *result;
|
||||||
|
|
||||||
|
if (cxoUtils_getModuleAndName(Py_TYPE(cursor), &module, &name) < 0)
|
||||||
|
return NULL;
|
||||||
|
result = cxoUtils_formatString("<%s.%s>", PyTuple_Pack(2, module, name));
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_close()
|
||||||
|
// Create a SODA collection and return it.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDocCursor_close(cxoSodaDocCursor *cursor,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
if (dpiSodaDocCursor_close(cursor->handle) < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_getIter()
|
||||||
|
// Return a reference to the cursor which supports the iterator protocol.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDocCursor_getIter(cxoSodaDocCursor *cursor)
|
||||||
|
{
|
||||||
|
Py_INCREF(cursor);
|
||||||
|
return (PyObject*) cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaDocCursor_getNext()
|
||||||
|
// Return the next document from the cursor.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaDocCursor_getNext(cxoSodaDocCursor *cursor)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *handle;
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(cursor->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaDocCursor_getNext(cursor->handle, flags, &handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (!handle)
|
||||||
|
return NULL;
|
||||||
|
doc = cxoSodaDoc_new(cursor->db, handle);
|
||||||
|
if (!doc)
|
||||||
|
return NULL;
|
||||||
|
return (PyObject*) doc;
|
||||||
|
}
|
||||||
|
|
||||||
564
src/cxoSodaOperation.c
Normal file
564
src/cxoSodaOperation.c
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation.c
|
||||||
|
// Defines the routines for the various operations performed on SODA
|
||||||
|
// collections.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "cxoModule.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Declaration of functions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaOperation_free(cxoSodaOperation*);
|
||||||
|
static PyObject *cxoSodaOperation_repr(cxoSodaOperation*);
|
||||||
|
static PyObject *cxoSodaOperation_filter(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_key(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_keys(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_limit(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_skip(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_version(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_count(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_getCursor(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_getDocuments(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_getOne(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_remove(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_replaceOne(cxoSodaOperation*, PyObject*);
|
||||||
|
static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation*,
|
||||||
|
PyObject*);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// declaration of methods for Python type "SodaOperation"
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyMethodDef cxoMethods[] = {
|
||||||
|
{ "filter", (PyCFunction) cxoSodaOperation_filter, METH_O },
|
||||||
|
{ "key", (PyCFunction) cxoSodaOperation_key, METH_O },
|
||||||
|
{ "keys", (PyCFunction) cxoSodaOperation_keys, METH_O },
|
||||||
|
{ "limit", (PyCFunction) cxoSodaOperation_limit, METH_O },
|
||||||
|
{ "skip", (PyCFunction) cxoSodaOperation_skip, METH_O },
|
||||||
|
{ "version", (PyCFunction) cxoSodaOperation_version, METH_O },
|
||||||
|
{ "count", (PyCFunction) cxoSodaOperation_count, METH_NOARGS },
|
||||||
|
{ "getCursor", (PyCFunction) cxoSodaOperation_getCursor, METH_NOARGS },
|
||||||
|
{ "getDocuments", (PyCFunction) cxoSodaOperation_getDocuments,
|
||||||
|
METH_NOARGS },
|
||||||
|
{ "getOne", (PyCFunction) cxoSodaOperation_getOne, METH_NOARGS },
|
||||||
|
{ "remove", (PyCFunction) cxoSodaOperation_remove, METH_NOARGS },
|
||||||
|
{ "replaceOne", (PyCFunction) cxoSodaOperation_replaceOne, METH_O },
|
||||||
|
{ "replaceOneAndGet", (PyCFunction) cxoSodaOperation_replaceOneAndGet,
|
||||||
|
METH_O },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Python type declarations
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
PyTypeObject cxoPyTypeSodaOperation = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"cx_Oracle.SodaOperation", // tp_name
|
||||||
|
sizeof(cxoSodaOperation), // tp_basicsize
|
||||||
|
0, // tp_itemsize
|
||||||
|
(destructor) cxoSodaOperation_free, // tp_dealloc
|
||||||
|
0, // tp_print
|
||||||
|
0, // tp_getattr
|
||||||
|
0, // tp_setattr
|
||||||
|
0, // tp_compare
|
||||||
|
(reprfunc) cxoSodaOperation_repr, // tp_repr
|
||||||
|
0, // tp_as_number
|
||||||
|
0, // tp_as_sequence
|
||||||
|
0, // tp_as_mapping
|
||||||
|
0, // tp_hash
|
||||||
|
0, // tp_call
|
||||||
|
0, // tp_str
|
||||||
|
0, // tp_getattro
|
||||||
|
0, // tp_setattro
|
||||||
|
0, // tp_as_buffer
|
||||||
|
Py_TPFLAGS_DEFAULT, // tp_flags
|
||||||
|
0, // tp_doc
|
||||||
|
0, // tp_traverse
|
||||||
|
0, // tp_clear
|
||||||
|
0, // tp_richcompare
|
||||||
|
0, // tp_weaklistoffset
|
||||||
|
0, // tp_iter
|
||||||
|
0, // tp_iternext
|
||||||
|
cxoMethods, // tp_methods
|
||||||
|
0, // tp_members
|
||||||
|
0, // tp_getset
|
||||||
|
0, // tp_base
|
||||||
|
0, // tp_dict
|
||||||
|
0, // tp_descr_get
|
||||||
|
0, // tp_descr_set
|
||||||
|
0, // tp_dictoffset
|
||||||
|
0, // tp_init
|
||||||
|
0, // tp_alloc
|
||||||
|
0, // tp_new
|
||||||
|
0, // tp_free
|
||||||
|
0, // tp_is_gc
|
||||||
|
0 // tp_bases
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_clearKeys()
|
||||||
|
// Clear the keys set on the operation object, if applicable.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void cxoSodaOperation_clearKeys(cxoSodaOperation *op)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
if (op->keyBuffers) {
|
||||||
|
for (i = 0; i < op->numKeyBuffers; i++)
|
||||||
|
cxoBuffer_clear(&op->keyBuffers[i]);
|
||||||
|
PyMem_Free(op->keyBuffers);
|
||||||
|
op->keyBuffers = NULL;
|
||||||
|
}
|
||||||
|
op->numKeyBuffers = 0;
|
||||||
|
op->options.numKeys = 0;
|
||||||
|
if (op->options.keys) {
|
||||||
|
PyMem_Free(op->options.keys);
|
||||||
|
op->options.keys = NULL;
|
||||||
|
}
|
||||||
|
if (op->options.keyLengths) {
|
||||||
|
PyMem_Free(op->options.keyLengths);
|
||||||
|
op->options.keyLengths = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_new()
|
||||||
|
// Create a new SODA operation object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
cxoSodaOperation *cxoSodaOperation_new(cxoSodaCollection *coll)
|
||||||
|
{
|
||||||
|
cxoSodaOperation *op;
|
||||||
|
|
||||||
|
op = (cxoSodaOperation*)
|
||||||
|
cxoPyTypeSodaOperation.tp_alloc(&cxoPyTypeSodaOperation, 0);
|
||||||
|
if (!op)
|
||||||
|
return NULL;
|
||||||
|
if (dpiContext_initSodaOperOptions(cxoDpiContext, &op->options) < 0) {
|
||||||
|
Py_DECREF(op);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cxoBuffer_init(&op->keyBuffer);
|
||||||
|
cxoBuffer_init(&op->versionBuffer);
|
||||||
|
cxoBuffer_init(&op->filterBuffer);
|
||||||
|
Py_INCREF(coll);
|
||||||
|
op->coll = coll;
|
||||||
|
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_free()
|
||||||
|
// Free the memory associated with a SODA operation object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static void cxoSodaOperation_free(cxoSodaOperation *op)
|
||||||
|
{
|
||||||
|
cxoSodaOperation_clearKeys(op);
|
||||||
|
cxoBuffer_clear(&op->keyBuffer);
|
||||||
|
cxoBuffer_clear(&op->versionBuffer);
|
||||||
|
cxoBuffer_clear(&op->filterBuffer);
|
||||||
|
Py_CLEAR(op->coll);
|
||||||
|
Py_TYPE(op)->tp_free((PyObject*) op);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_repr()
|
||||||
|
// Return a string representation of a SODA operation object.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_repr(cxoSodaOperation *op)
|
||||||
|
{
|
||||||
|
PyObject *collRepr, *module, *name, *result;
|
||||||
|
|
||||||
|
collRepr = PyObject_Repr((PyObject*) op->coll);
|
||||||
|
if (!collRepr)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_getModuleAndName(Py_TYPE(op), &module, &name) < 0) {
|
||||||
|
Py_DECREF(collRepr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
result = cxoUtils_formatString("<%s.%s on %s>",
|
||||||
|
PyTuple_Pack(3, module, name, collRepr));
|
||||||
|
Py_DECREF(module);
|
||||||
|
Py_DECREF(name);
|
||||||
|
Py_DECREF(collRepr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_filter()
|
||||||
|
// Set the filter to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_filter(cxoSodaOperation *op,
|
||||||
|
PyObject *filterObj)
|
||||||
|
{
|
||||||
|
cxoBuffer_clear(&op->filterBuffer);
|
||||||
|
if (PyDict_Check(filterObj)) {
|
||||||
|
filterObj = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction,
|
||||||
|
filterObj, NULL);
|
||||||
|
if (!filterObj)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cxoBuffer_fromObject(&op->filterBuffer, filterObj,
|
||||||
|
op->coll->db->connection->encodingInfo.encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
op->options.filter = op->filterBuffer.ptr;
|
||||||
|
op->options.filterLength = op->filterBuffer.size;
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_key()
|
||||||
|
// Set the key to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_key(cxoSodaOperation *op,
|
||||||
|
PyObject *keyObj)
|
||||||
|
{
|
||||||
|
cxoBuffer_clear(&op->keyBuffer);
|
||||||
|
if (cxoBuffer_fromObject(&op->keyBuffer, keyObj,
|
||||||
|
op->coll->db->connection->encodingInfo.encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
op->options.key = op->keyBuffer.ptr;
|
||||||
|
op->options.keyLength = op->keyBuffer.size;
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_keys()
|
||||||
|
// Set the keys to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_keys(cxoSodaOperation *op,
|
||||||
|
PyObject *keysObj)
|
||||||
|
{
|
||||||
|
Py_ssize_t size, i;
|
||||||
|
PyObject *element;
|
||||||
|
|
||||||
|
// determine size of sequence passed to method
|
||||||
|
size = PySequence_Size(keysObj);
|
||||||
|
if (PyErr_Occurred())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// clear original keys, if applicable
|
||||||
|
cxoSodaOperation_clearKeys(op);
|
||||||
|
|
||||||
|
// zero-length arrays don't need any further processing
|
||||||
|
if (size == 0) {
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize memory
|
||||||
|
op->keyBuffers = PyMem_Malloc(size * sizeof(cxoBuffer));
|
||||||
|
if (!op->keyBuffers)
|
||||||
|
return NULL;
|
||||||
|
op->numKeyBuffers = (uint32_t) size;
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
cxoBuffer_init(&op->keyBuffers[i]);
|
||||||
|
op->options.keys = PyMem_Malloc(size * sizeof(const char *));
|
||||||
|
op->options.keyLengths = PyMem_Malloc(size * sizeof(uint32_t));
|
||||||
|
if (!op->options.keys || !op->options.keyLengths) {
|
||||||
|
cxoSodaOperation_clearKeys(op);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
op->options.numKeys = op->numKeyBuffers;
|
||||||
|
|
||||||
|
// process each of the elements of the sequence
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
element = PySequence_GetItem(keysObj, i);
|
||||||
|
if (!element) {
|
||||||
|
cxoSodaOperation_clearKeys(op);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (cxoBuffer_fromObject(&op->keyBuffers[i], element,
|
||||||
|
op->coll->db->connection->encodingInfo.encoding) < 0) {
|
||||||
|
Py_DECREF(element);
|
||||||
|
cxoSodaOperation_clearKeys(op);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(element);
|
||||||
|
op->options.keys[i] = op->keyBuffers[i].ptr;
|
||||||
|
op->options.keyLengths[i] = op->keyBuffers[i].size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_limit()
|
||||||
|
// Set the limit value to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_limit(cxoSodaOperation *op,
|
||||||
|
PyObject *limitObj)
|
||||||
|
{
|
||||||
|
op->options.limit = PyLong_AsUnsignedLong(limitObj);
|
||||||
|
if (PyErr_Occurred())
|
||||||
|
return NULL;
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_skip()
|
||||||
|
// Set the skip value to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_skip(cxoSodaOperation *op,
|
||||||
|
PyObject *skipObj)
|
||||||
|
{
|
||||||
|
op->options.skip = PyLong_AsUnsignedLong(skipObj);
|
||||||
|
if (PyErr_Occurred())
|
||||||
|
return NULL;
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_version()
|
||||||
|
// Set the version to be used for the operation.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_version(cxoSodaOperation *op,
|
||||||
|
PyObject *versionObj)
|
||||||
|
{
|
||||||
|
cxoBuffer_clear(&op->versionBuffer);
|
||||||
|
if (cxoBuffer_fromObject(&op->versionBuffer, versionObj,
|
||||||
|
op->coll->db->connection->encodingInfo.encoding) < 0)
|
||||||
|
return NULL;
|
||||||
|
op->options.version = op->versionBuffer.ptr;
|
||||||
|
op->options.versionLength = op->versionBuffer.size;
|
||||||
|
Py_INCREF(op);
|
||||||
|
return (PyObject*) op;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_count()
|
||||||
|
// Returns the number of documents that match the criteria.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_count(cxoSodaOperation *op, PyObject *args)
|
||||||
|
{
|
||||||
|
uint64_t count;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_getDocCount(op->coll->handle, &op->options, flags,
|
||||||
|
&count);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
return PyLong_FromUnsignedLongLong(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_getCursor()
|
||||||
|
// Returns a document cursor which can be used to iterate over the documents
|
||||||
|
// that match the criteria.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_getCursor(cxoSodaOperation *op,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
dpiSodaDocCursor *handle;
|
||||||
|
cxoSodaDocCursor *cursor;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
cursor = cxoSodaDocCursor_new(op->coll->db, handle);
|
||||||
|
if (!cursor)
|
||||||
|
return NULL;
|
||||||
|
return (PyObject*) cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_getDocuments()
|
||||||
|
// Returns a list of documents that match the criteria.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_getDocuments(cxoSodaOperation *op,
|
||||||
|
PyObject *args)
|
||||||
|
{
|
||||||
|
dpiSodaDocCursor *cursor;
|
||||||
|
PyObject *docObj;
|
||||||
|
dpiSodaDoc *doc;
|
||||||
|
PyObject *list;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// acquire cursor
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_find(op->coll->handle, &op->options, flags, &cursor);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
|
||||||
|
// iterate cursor and create array of documents
|
||||||
|
list = PyList_New(0);
|
||||||
|
if (!list) {
|
||||||
|
dpiSodaDocCursor_release(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaDocCursor_getNext(cursor, flags, &doc);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
dpiSodaDocCursor_release(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!doc)
|
||||||
|
break;
|
||||||
|
docObj = (PyObject*) cxoSodaDoc_new(op->coll->db, doc);
|
||||||
|
if (!docObj) {
|
||||||
|
dpiSodaDocCursor_release(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PyList_Append(list, docObj) < 0) {
|
||||||
|
Py_DECREF(docObj);
|
||||||
|
dpiSodaDocCursor_release(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(docObj);
|
||||||
|
}
|
||||||
|
dpiSodaDocCursor_release(cursor);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_getOne()
|
||||||
|
// Returns a single document that matches the criteria or None if no
|
||||||
|
// documents match the criteria.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_getOne(cxoSodaOperation *op, PyObject *args)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *handle;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_findOne(op->coll->handle, &op->options, flags,
|
||||||
|
&handle);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
if (handle)
|
||||||
|
return (PyObject*) cxoSodaDoc_new(op->coll->db, handle);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_remove()
|
||||||
|
// Remove all of the documents that match the criteria.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_remove(cxoSodaOperation *op, PyObject *args)
|
||||||
|
{
|
||||||
|
uint64_t count;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_remove(op->coll->handle, &op->options, flags, &count);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0)
|
||||||
|
return cxoError_raiseAndReturnNull();
|
||||||
|
return PyLong_FromUnsignedLongLong(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_replaceOne()
|
||||||
|
// Replace a single document in the collection with the provided replacement.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_replaceOne(cxoSodaOperation *op,
|
||||||
|
PyObject *arg)
|
||||||
|
{
|
||||||
|
int status, replaced;
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_processSodaDocArg(op->coll->db, arg, &doc) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_replaceOne(op->coll->handle, &op->options,
|
||||||
|
doc->handle, flags, &replaced, NULL);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
Py_DECREF(doc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(doc);
|
||||||
|
if (replaced)
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoSodaOperation_replaceOneAndGet()
|
||||||
|
// Replace a single document in the collection with the provided replacement
|
||||||
|
// and return a document (without the content) to the caller.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
static PyObject *cxoSodaOperation_replaceOneAndGet(cxoSodaOperation *op,
|
||||||
|
PyObject *arg)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *replacedDoc;
|
||||||
|
cxoSodaDoc *doc;
|
||||||
|
uint32_t flags;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (cxoConnection_getSodaFlags(op->coll->db->connection, &flags) < 0)
|
||||||
|
return NULL;
|
||||||
|
if (cxoUtils_processSodaDocArg(op->coll->db, arg, &doc) < 0)
|
||||||
|
return NULL;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
status = dpiSodaColl_replaceOne(op->coll->handle, &op->options,
|
||||||
|
doc->handle, flags, NULL, &replacedDoc);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (status < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
Py_DECREF(doc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(doc);
|
||||||
|
if (replacedDoc)
|
||||||
|
return (PyObject*) cxoSodaDoc_new(op->coll->db, replacedDoc);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
@ -131,3 +131,73 @@ int cxoUtils_initializeDPI(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoUtils_processJsonArg()
|
||||||
|
// Process the argument which is expected to be either a string or bytes, or
|
||||||
|
// a dictionary or list which is converted to a string via the json.dumps()
|
||||||
|
// method. All strings are encoded to UTF-8 which is what SODA expects.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
int cxoUtils_processJsonArg(PyObject *arg, cxoBuffer *buffer)
|
||||||
|
{
|
||||||
|
int converted = 0;
|
||||||
|
|
||||||
|
if (arg && (PyDict_Check(arg) || PyList_Check(arg))) {
|
||||||
|
arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL);
|
||||||
|
if (!arg)
|
||||||
|
return -1;
|
||||||
|
converted = 1;
|
||||||
|
}
|
||||||
|
if (cxoBuffer_fromObject(buffer, arg, "UTF-8") < 0)
|
||||||
|
return -1;
|
||||||
|
if (converted)
|
||||||
|
Py_DECREF(arg);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// cxoUtils_processSodaDocArg()
|
||||||
|
// Process a SODA document argument. This is expectd to be an actual SODA
|
||||||
|
// document object or a dictionary. If the argument refers to a dictionary or
|
||||||
|
// list, a new SODA document will be created with the given content and without
|
||||||
|
// a key or media type specified.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
int cxoUtils_processSodaDocArg(cxoSodaDatabase *db, PyObject *arg,
|
||||||
|
cxoSodaDoc **doc)
|
||||||
|
{
|
||||||
|
dpiSodaDoc *handle;
|
||||||
|
cxoBuffer buffer;
|
||||||
|
|
||||||
|
if (PyObject_TypeCheck(arg, &cxoPyTypeSodaDoc)) {
|
||||||
|
Py_INCREF(arg);
|
||||||
|
*doc = (cxoSodaDoc*) arg;
|
||||||
|
} else if (PyDict_Check(arg) || PyList_Check(arg)) {
|
||||||
|
arg = PyObject_CallFunctionObjArgs(cxoJsonDumpFunction, arg, NULL);
|
||||||
|
if (!arg)
|
||||||
|
return -1;
|
||||||
|
if (cxoBuffer_fromObject(&buffer, arg, "UTF-8") < 0) {
|
||||||
|
Py_DECREF(arg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_DECREF(arg);
|
||||||
|
if (dpiSodaDb_createDocument(db->handle, NULL, 0, buffer.ptr,
|
||||||
|
buffer.size, NULL, 0, DPI_SODA_FLAGS_DEFAULT, &handle) < 0) {
|
||||||
|
cxoError_raiseAndReturnNull();
|
||||||
|
cxoBuffer_clear(&buffer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cxoBuffer_clear(&buffer);
|
||||||
|
*doc = cxoSodaDoc_new(db, handle);
|
||||||
|
if (!*doc)
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"value must be a SODA document or dictionary");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
194
test/SodaCollection.py
Normal file
194
test/SodaCollection.py
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
"""Module for testing Simple Oracle Document Access (SODA) Collections"""
|
||||||
|
|
||||||
|
class TestSodaDocuments(BaseTestCase):
|
||||||
|
|
||||||
|
def testInvalidJson(self):
|
||||||
|
"test inserting invalid JSON value into SODA collection"
|
||||||
|
invalidJson = "{testKey:testValue}"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoInvalidJSON")
|
||||||
|
doc = sodaDatabase.createDocument(invalidJson)
|
||||||
|
self.assertRaises(cx_Oracle.IntegrityError, coll.insertOne, doc)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testInsertDocuments(self):
|
||||||
|
"test inserting documents into a SODA collection"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoInsertDocs")
|
||||||
|
coll.find().remove()
|
||||||
|
valuesToInsert = [
|
||||||
|
{ "name" : "George", "age" : 47 },
|
||||||
|
{ "name" : "Susan", "age" : 39 },
|
||||||
|
{ "name" : "John", "age" : 50 },
|
||||||
|
{ "name" : "Jill", "age" : 54 }
|
||||||
|
]
|
||||||
|
insertedKeys = []
|
||||||
|
for value in valuesToInsert:
|
||||||
|
doc = coll.insertOneAndGet(value)
|
||||||
|
insertedKeys.append(doc.key)
|
||||||
|
self.connection.commit()
|
||||||
|
self.assertEqual(coll.find().count(), len(valuesToInsert))
|
||||||
|
for key, value in zip(insertedKeys, valuesToInsert):
|
||||||
|
doc = coll.find().key(key).getOne()
|
||||||
|
self.assertEqual(doc.getContent(), value)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testSkipDocuments(self):
|
||||||
|
"test skip documents from SODA collection"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoSkipDocs")
|
||||||
|
coll.find().remove()
|
||||||
|
valuesToInsert = [
|
||||||
|
{ "name" : "Matthew", "age" : 28 },
|
||||||
|
{ "name" : "Martha", "age" : 43 },
|
||||||
|
{ "name" : "Mark", "age" : 37 },
|
||||||
|
{ "name" : "Anna", "age" : 62 }
|
||||||
|
]
|
||||||
|
for value in valuesToInsert:
|
||||||
|
coll.insertOne(value)
|
||||||
|
self.connection.commit()
|
||||||
|
self.assertEqual(coll.find().skip(1).getOne().getContent(),
|
||||||
|
valuesToInsert[1])
|
||||||
|
self.assertEqual(coll.find().skip(3).getOne().getContent(),
|
||||||
|
valuesToInsert[3])
|
||||||
|
self.assertEqual(coll.find().skip(4).getOne(), None)
|
||||||
|
self.assertEqual(coll.find().skip(125).getOne(), None)
|
||||||
|
|
||||||
|
def testReplaceDocument(self):
|
||||||
|
"test replace documents in SODA collection"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoReplaceDoc")
|
||||||
|
coll.find().remove()
|
||||||
|
content = {'name': 'John', 'address': {'city': 'Sydney'}}
|
||||||
|
doc = coll.insertOneAndGet(content)
|
||||||
|
newContent = {'name': 'John', 'address': {'city':'Melbourne'}}
|
||||||
|
coll.find().key(doc.key).replaceOne(newContent)
|
||||||
|
self.connection.commit()
|
||||||
|
self.assertEqual(coll.find().key(doc.key).getOne().getContent(),
|
||||||
|
newContent)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testSearchDocumentsWithContent(self):
|
||||||
|
"test search documents with content using $like and $regex"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoSearchDocContent")
|
||||||
|
coll.find().remove()
|
||||||
|
data = [
|
||||||
|
{'name': 'John', 'address': {'city': 'Bangalore'}},
|
||||||
|
{'name': 'Johnson', 'address': {'city': 'Banaras'}},
|
||||||
|
{'name': 'Joseph', 'address': {'city': 'Bangalore'}},
|
||||||
|
{'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
|
||||||
|
{'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
|
||||||
|
{'name': 'Matthew', 'address': {'city': 'Mumbai'}}
|
||||||
|
]
|
||||||
|
for value in data:
|
||||||
|
coll.insertOne(value)
|
||||||
|
self.connection.commit()
|
||||||
|
filterSpecs = [
|
||||||
|
({'name': {'$like': 'And%'}}, 1),
|
||||||
|
({'name': {'$like': 'J%n'}}, 3),
|
||||||
|
({'name': {'$like': '%hn%'}}, 2),
|
||||||
|
({'address.city': {'$like': 'Ban%'}}, 3),
|
||||||
|
({'address.city': {'$like': '%bad'}}, 2),
|
||||||
|
({'address.city': {'$like': 'Hyderabad'}}, 1),
|
||||||
|
({'address.city': {'$like': 'China%'}}, 0),
|
||||||
|
({'name': {'$regex': 'Jo.*'}}, 3),
|
||||||
|
({'name': {'$regex': '.*[ho]n'}}, 2),
|
||||||
|
({'name': {'$regex': 'J.*h'}}, 1),
|
||||||
|
({'address.city': {'$regex': 'Ba.*'}}, 3),
|
||||||
|
({'address.city': {'$regex': '.*bad'}}, 2),
|
||||||
|
({'address.city': {'$regex': 'Hyderabad'}}, 1),
|
||||||
|
({'name': {'$regex': 'Js.*n'}}, 0)
|
||||||
|
]
|
||||||
|
for filterSpec, expectedCount in filterSpecs:
|
||||||
|
self.assertEqual(coll.find().filter(filterSpec).count(),
|
||||||
|
expectedCount, filterSpec)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testDocumentRemove(self):
|
||||||
|
"test removing documents"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoRemoveDocs")
|
||||||
|
coll.find().remove()
|
||||||
|
data = [
|
||||||
|
{'name': 'John', 'address': {'city': 'Bangalore'}},
|
||||||
|
{'name': 'Johnson', 'address': {'city': 'Banaras'}},
|
||||||
|
{'name': 'Joseph', 'address': {'city': 'Mangalore'}},
|
||||||
|
{'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
|
||||||
|
{'name': 'Andrew', 'address': {'city': 'Hyderabad'}},
|
||||||
|
{'name': 'Matthew', 'address': {'city': 'Mumbai'}}
|
||||||
|
]
|
||||||
|
docs = [coll.insertOneAndGet(v) for v in data]
|
||||||
|
coll.find().key(docs[3].key).remove()
|
||||||
|
self.assertEqual(coll.find().count(), len(data) - 1)
|
||||||
|
searchResults = coll.find().filter({'name': {'$like': 'Jibin'}})
|
||||||
|
self.assertEqual(searchResults.count(), 0)
|
||||||
|
coll.find().filter({'name': {'$like': 'John%'}}).remove()
|
||||||
|
self.assertEqual(coll.find().count(), len(data) - 3)
|
||||||
|
coll.find().filter({'name': {'$regex': 'J.*'}}).remove()
|
||||||
|
self.assertEqual(coll.find().count(), len(data) - 4)
|
||||||
|
self.connection.commit()
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testCreateAndDropIndex(self):
|
||||||
|
"test create and drop Index"
|
||||||
|
indexName = "cxoTestIndexes_ix_1"
|
||||||
|
indexSpec = {
|
||||||
|
'name': indexName,
|
||||||
|
'fields': [
|
||||||
|
{
|
||||||
|
'path': 'address.city',
|
||||||
|
'datatype': 'string',
|
||||||
|
'order': 'asc'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoTestIndexes")
|
||||||
|
coll.find().remove()
|
||||||
|
self.connection.commit()
|
||||||
|
coll.dropIndex(indexName)
|
||||||
|
coll.createIndex(indexSpec)
|
||||||
|
self.assertRaises(cx_Oracle.DatabaseError, coll.createIndex, indexSpec)
|
||||||
|
self.assertEqual(coll.dropIndex(indexName), True)
|
||||||
|
self.assertEqual(coll.dropIndex(indexName), False)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testGetDocuments(self):
|
||||||
|
"test getting documents from Collection"
|
||||||
|
self.connection.autocommit = True
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoTestGetDocs")
|
||||||
|
coll.find().remove()
|
||||||
|
data = [
|
||||||
|
{'name': 'John', 'address': {'city': 'Bangalore'}},
|
||||||
|
{'name': 'Johnson', 'address': {'city': 'Banaras'}},
|
||||||
|
{'name': 'Joseph', 'address': {'city': 'Mangalore'}},
|
||||||
|
{'name': 'Jibin', 'address': {'city': 'Secunderabad'}},
|
||||||
|
{'name': 'Andrew', 'address': {'city': 'Hyderabad'}}
|
||||||
|
]
|
||||||
|
insertedKeys = list(sorted(coll.insertOneAndGet(v).key for v in data))
|
||||||
|
fetchedKeys = list(sorted(d.key for d in coll.find().getDocuments()))
|
||||||
|
self.assertEqual(fetchedKeys, insertedKeys)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testCursor(self):
|
||||||
|
"test fetching documents from a cursor"
|
||||||
|
self.connection.autocommit = True
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
coll = sodaDatabase.createCollection("cxoFindViaCursor")
|
||||||
|
coll.find().remove()
|
||||||
|
data = [
|
||||||
|
{'name': 'John', 'address': {'city': 'Bangalore'}},
|
||||||
|
{'name': 'Johnson', 'address': {'city': 'Banaras'}},
|
||||||
|
{'name': 'Joseph', 'address': {'city': 'Mangalore'}},
|
||||||
|
]
|
||||||
|
insertedKeys = list(sorted(coll.insertOneAndGet(v).key for v in data))
|
||||||
|
fetchedKeys = list(sorted(d.key for d in coll.find().getCursor()))
|
||||||
|
self.assertEqual(fetchedKeys, insertedKeys)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
101
test/SodaDatabase.py
Normal file
101
test/SodaDatabase.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Copyright 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
"""Module for testing Simple Oracle Document Access (SODA) Database"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
class TestSodaCollection(BaseTestCase):
|
||||||
|
|
||||||
|
def __dropExistingCollections(self, sodaDatabase):
|
||||||
|
for name in sodaDatabase.getCollectionNames():
|
||||||
|
sodaDatabase.openCollection(name).drop()
|
||||||
|
|
||||||
|
def __verifyDocument(self, doc, rawContent, strContent=None, content=None,
|
||||||
|
key=None, mediaType='application/json'):
|
||||||
|
self.assertEqual(doc.getContentAsBytes(), rawContent)
|
||||||
|
if strContent is not None:
|
||||||
|
self.assertEqual(doc.getContentAsString(), strContent)
|
||||||
|
if content is not None:
|
||||||
|
self.assertEqual(doc.getContent(), content)
|
||||||
|
self.assertEqual(doc.key, key)
|
||||||
|
self.assertEqual(doc.mediaType, mediaType)
|
||||||
|
|
||||||
|
def testCreateDocumentWithJson(self):
|
||||||
|
"test creating documents with JSON data"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
val = {"testKey1" : "testValue1", "testKey2" : "testValue2" }
|
||||||
|
strVal = json.dumps(val)
|
||||||
|
bytesVal = strVal.encode("UTF-8")
|
||||||
|
key = "MyKey"
|
||||||
|
mediaType = "text/plain"
|
||||||
|
doc = sodaDatabase.createDocument(val)
|
||||||
|
self.__verifyDocument(doc, bytesVal, strVal, val)
|
||||||
|
doc = sodaDatabase.createDocument(strVal, key)
|
||||||
|
self.__verifyDocument(doc, bytesVal, strVal, val, key)
|
||||||
|
doc = sodaDatabase.createDocument(bytesVal, key, mediaType)
|
||||||
|
self.__verifyDocument(doc, bytesVal, strVal, val, key, mediaType)
|
||||||
|
|
||||||
|
def testCreateDocumentWithRaw(self):
|
||||||
|
"test creating documents with raw data"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
val = b"<html/>"
|
||||||
|
key = "MyRawKey"
|
||||||
|
mediaType = "text/html"
|
||||||
|
doc = sodaDatabase.createDocument(val)
|
||||||
|
self.__verifyDocument(doc, val)
|
||||||
|
doc = sodaDatabase.createDocument(val, key)
|
||||||
|
self.__verifyDocument(doc, val, key=key)
|
||||||
|
doc = sodaDatabase.createDocument(val, key, mediaType)
|
||||||
|
self.__verifyDocument(doc, val, key=key, mediaType=mediaType)
|
||||||
|
|
||||||
|
def testGetCollectionNames(self):
|
||||||
|
"test getting collection names from the database"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
self.__dropExistingCollections(sodaDatabase)
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames(), [])
|
||||||
|
names = ["zCol", "dCol", "sCol", "aCol", "gCol"]
|
||||||
|
sortedNames = list(sorted(names))
|
||||||
|
for name in names:
|
||||||
|
sodaDatabase.createCollection(name)
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames(), sortedNames)
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames(limit=2),
|
||||||
|
sortedNames[:2])
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames("a"), sortedNames)
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames("C"), sortedNames)
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames("b", limit=3),
|
||||||
|
sortedNames[1:4])
|
||||||
|
self.assertEqual(sodaDatabase.getCollectionNames("z"),
|
||||||
|
sortedNames[-1:])
|
||||||
|
|
||||||
|
def testOpenCollection(self):
|
||||||
|
"test opening a collection"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
self.__dropExistingCollections(sodaDatabase)
|
||||||
|
coll = sodaDatabase.openCollection("CollectionThatDoesNotExist")
|
||||||
|
self.assertEqual(coll, None)
|
||||||
|
createdColl = sodaDatabase.createCollection("cxoTestOpenCollection")
|
||||||
|
coll = sodaDatabase.openCollection(createdColl.name)
|
||||||
|
self.assertEqual(coll.name, createdColl.name)
|
||||||
|
coll.drop()
|
||||||
|
|
||||||
|
def testRepr(self):
|
||||||
|
"test SodaDatabase representation"
|
||||||
|
con1 = self.connection
|
||||||
|
con2 = self.getConnection()
|
||||||
|
sodaDatabase1 = con1.getSodaDatabase()
|
||||||
|
sodaDatabase2 = con1.getSodaDatabase()
|
||||||
|
sodaDatabase3 = con2.getSodaDatabase()
|
||||||
|
self.assertEqual(str(sodaDatabase1), str(sodaDatabase2))
|
||||||
|
self.assertEqual(str(sodaDatabase2), str(sodaDatabase3))
|
||||||
|
|
||||||
|
def testNegative(self):
|
||||||
|
"test negative cases for SODA database methods"
|
||||||
|
sodaDatabase = self.connection.getSodaDatabase()
|
||||||
|
self.assertRaises(TypeError, sodaDatabase.createCollection)
|
||||||
|
self.assertRaises(TypeError, sodaDatabase.createCollection, 1)
|
||||||
|
self.assertRaises(cx_Oracle.DatabaseError,
|
||||||
|
sodaDatabase.createCollection, None)
|
||||||
|
self.assertRaises(TypeError, sodaDatabase.getCollectionNames, 1)
|
||||||
|
|
||||||
@ -49,6 +49,19 @@ grant execute on dbms_aqadm to &main_user;
|
|||||||
|
|
||||||
grant execute on dbms_transform to &main_user;
|
grant execute on dbms_transform to &main_user;
|
||||||
|
|
||||||
|
begin
|
||||||
|
|
||||||
|
for r in
|
||||||
|
( select role
|
||||||
|
from dba_roles
|
||||||
|
where role in ('SODA_APP')
|
||||||
|
) loop
|
||||||
|
execute immediate 'grant ' || r.role || ' to &main_user';
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
end;
|
||||||
|
/
|
||||||
|
|
||||||
-- create types
|
-- create types
|
||||||
create type &main_user..udt_SubObject as object (
|
create type &main_user..udt_SubObject as object (
|
||||||
SubNumberValue number,
|
SubNumberValue number,
|
||||||
|
|||||||
@ -60,6 +60,9 @@ else:
|
|||||||
if clientVersion[:2] >= (12, 1):
|
if clientVersion[:2] >= (12, 1):
|
||||||
moduleNames.append("BooleanVar")
|
moduleNames.append("BooleanVar")
|
||||||
moduleNames.append("Features12_1")
|
moduleNames.append("Features12_1")
|
||||||
|
if clientVersion[:2] >= (18, 3):
|
||||||
|
moduleNames.append("SodaDatabase")
|
||||||
|
moduleNames.append("SodaCollection")
|
||||||
|
|
||||||
class BaseTestCase(unittest.TestCase):
|
class BaseTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user