565 lines
20 KiB
C
565 lines
20 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 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;
|
|
}
|
|
|