Skip to content

Commit 517868e

Browse files
Added support for supplying hints to various SODA operations.
1 parent 74b2e9a commit 517868e

File tree

5 files changed

+179
-23
lines changed

5 files changed

+179
-23
lines changed

doc/src/api_manual/soda.rst

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,29 @@ SODA Collection Object
247247
.. versionadded:: 7.2
248248

249249

250-
.. method:: SodaCollection.insertManyAndGet(docs)
250+
.. method:: SodaCollection.insertManyAndGet(docs, hint=None)
251251

252252
Similarly to :meth:`~SodaCollection.insertMany()` this method inserts a
253253
list of documents into the collection at one time. The only difference is
254254
that it returns a list of :ref:`SODA Document objects <sodadoc>`. Note that
255255
for performance reasons the returned documents do not contain the content.
256256

257+
The hint parameter, if specified, supplies a hint to the database when
258+
processing the SODA operation. This is expected to be a string in the same
259+
format as SQL hints but without the enclosing comment characters. Use of
260+
this parameter requires Oracle Client 21.3 or higher (or Oracle Client 19
261+
from 19.11).
262+
257263
.. note::
258264

259265
This method requires Oracle Client 18.5 and higher.
260266

261267
.. versionadded:: 7.2
262268

269+
.. versionchanged:: 8.2
270+
271+
The parameter `hint` was added.
272+
263273

264274
.. method:: SodaCollection.insertOne(doc)
265275

@@ -269,15 +279,25 @@ SODA Collection Object
269279
.. versionadded:: 7.0
270280

271281

272-
.. method:: SodaCollection.insertOneAndGet(doc)
282+
.. method:: SodaCollection.insertOneAndGet(doc, hint=None)
273283

274284
Similarly to :meth:`~SodaCollection.insertOne()` this method inserts a
275285
given document into the collection. The only difference is that it
276286
returns a :ref:`SODA Document object <sodadoc>`. Note that for performance
277287
reasons the returned document does not contain the content.
278288

289+
The hint parameter, if specified, supplies a hint to the database when
290+
processing the SODA operation. This is expected to be a string in the same
291+
format as SQL hints but without the enclosing comment characters. Use of
292+
this parameter requires Oracle Client 21.3 or higher (or Oracle Client 19
293+
from 19.11).
294+
279295
.. versionadded:: 7.0
280296

297+
.. versionchanged:: 8.2
298+
299+
The parameter `hint` was added.
300+
281301

282302
.. attribute:: SodaCollection.metadata
283303

@@ -310,18 +330,28 @@ SODA Collection Object
310330
.. versionadded:: 8.0
311331

312332

313-
.. method:: SodaCollection.saveAndGet(doc)
333+
.. method:: SodaCollection.saveAndGet(doc, hint=None)
314334

315335
Saves a document into the collection. This method is equivalent to
316336
:meth:`~SodaCollection.insertOneAndGet()` except that if client-assigned
317337
keys are used, and the document with the specified key already exists in
318338
the collection, it will be replaced with the input document.
319339

340+
The hint parameter, if specified, supplies a hint to the database when
341+
processing the SODA operation. This is expected to be a string in the same
342+
format as SQL hints but without the enclosing comment characters. Use of
343+
this parameter requires Oracle Client 21.3 or higher (or Oracle Client 19
344+
from 19.11).
345+
320346
This method requires Oracle Client 19.9 or higher in addition to the usual
321347
SODA requirements.
322348

323349
.. versionadded:: 8.0
324350

351+
.. versionchanged:: 8.2
352+
353+
The parameter `hint` was added.
354+
325355

326356
.. method:: SodaCollection.truncate()
327357

@@ -526,6 +556,19 @@ SODA Operation Object
526556
.. versionadded:: 7.0
527557

528558

559+
.. method:: SodaOperation.hint(value)
560+
561+
Specifies a hint that will be provided to the SODA operation when it is
562+
performed. This is expected to be a string in the same format as SQL hints
563+
but without the enclosing comment characters. Use of this method
564+
requires Oracle Client 21.3 or higher (or Oracle Client 19 from 19.11).
565+
566+
As a convenience, the SodaOperation object is returned so that further
567+
criteria can be specified by chaining methods together.
568+
569+
.. versionadded:: 8.2
570+
571+
529572
.. method:: SodaOperation.key(value)
530573

531574
Specifies that the document with the specified key should be returned.

doc/src/release_notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Version 8.2 (TBD)
1111
#) Updated embedded ODPI-C to `version 4.2.0
1212
<https://oracle.github.io/odpi/doc/releasenotes.html#
1313
version-4-2-tbd>`__.
14+
#) Added support for supplying hints to SODA operations. A new non-terminal
15+
method :meth:`~SodaOperation.hint()` was added and a `hint` parameter was
16+
added to the methods :meth:`SodaCollection.insertOneAndGet()`,
17+
:meth:`SodaCollection.insertManyAndGet()` and
18+
:meth:`SodaCollection.saveAndGet()`. All of these require Oracle Client
19+
21.3 or higher (or Oracle Client 19 from 19.11).
1420
#) Eliminated memory leak when calling :meth:`SodaOperation.filter()` with a
1521
dictionary.
1622
#) The distributed transaction handle assosciated with the connection is now

src/cxoModule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ struct cxoSodaOperation {
436436
cxoBuffer keyBuffer;
437437
cxoBuffer versionBuffer;
438438
cxoBuffer filterBuffer;
439+
cxoBuffer hintBuffer;
439440
};
440441

441442

src/cxoSodaCollection.c

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@
99

1010
#include "cxoModule.h"
1111

12+
//-----------------------------------------------------------------------------
13+
// cxoSodaCollection_processOptions()
14+
// Populate the SODA operations structure with the information provided by
15+
// the user.
16+
//-----------------------------------------------------------------------------
17+
static int cxoSodaCollection_processOptions(cxoSodaCollection *coll,
18+
dpiSodaOperOptions *options, PyObject *hintObj, cxoBuffer *hintBuffer)
19+
{
20+
if (dpiContext_initSodaOperOptions(cxoDpiContext, options) < 0)
21+
return cxoError_raiseAndReturnInt();
22+
if (cxoBuffer_fromObject(hintBuffer, hintObj,
23+
coll->db->connection->encodingInfo.encoding) < 0)
24+
return -1;
25+
options->hint = hintBuffer->ptr;
26+
options->hintLength = hintBuffer->size;
27+
28+
return 0;
29+
}
30+
31+
1232
//-----------------------------------------------------------------------------
1333
// cxoSodaCollection_initialize()
1434
// Initialize a new collection with its attributes.
@@ -228,7 +248,7 @@ static PyObject *cxoSodaCollection_getDataGuide(cxoSodaCollection *coll,
228248
//-----------------------------------------------------------------------------
229249
static PyObject *cxoSodaCollection_insertManyHelper(cxoSodaCollection *coll,
230250
PyObject *docs, Py_ssize_t numDocs, dpiSodaDoc **handles,
231-
dpiSodaDoc **returnHandles)
251+
dpiSodaDoc **returnHandles, dpiSodaOperOptions *options)
232252
{
233253
PyObject *element, *returnDocs;
234254
Py_ssize_t i, j;
@@ -252,8 +272,8 @@ static PyObject *cxoSodaCollection_insertManyHelper(cxoSodaCollection *coll,
252272

253273
// perform bulk insert
254274
Py_BEGIN_ALLOW_THREADS
255-
status = dpiSodaColl_insertMany(coll->handle, (uint32_t) numDocs, handles,
256-
flags, returnHandles);
275+
status = dpiSodaColl_insertManyWithOptions(coll->handle,
276+
(uint32_t) numDocs, handles, options, flags, returnHandles);
257277
Py_END_ALLOW_THREADS
258278
if (status < 0)
259279
cxoError_raiseAndReturnNull();
@@ -309,7 +329,7 @@ static PyObject *cxoSodaCollection_insertMany(cxoSodaCollection *coll,
309329
return NULL;
310330
}
311331
result = cxoSodaCollection_insertManyHelper(coll, arg, numDocs, handles,
312-
NULL);
332+
NULL, NULL);
313333
PyMem_Free(handles);
314334
return result;
315335
}
@@ -321,32 +341,52 @@ static PyObject *cxoSodaCollection_insertMany(cxoSodaCollection *coll,
321341
// list of documents containing all but the content itself.
322342
//-----------------------------------------------------------------------------
323343
static PyObject *cxoSodaCollection_insertManyAndGet(cxoSodaCollection *coll,
324-
PyObject *arg)
344+
PyObject *args, PyObject *keywordArgs)
325345
{
346+
static char *keywordList[] = { "docs", "hint", NULL };
347+
dpiSodaOperOptions options, *optionsPtr = NULL;
326348
dpiSodaDoc **handles, **returnHandles;
349+
PyObject *docsObj, *hintObj, *result;
350+
cxoBuffer hintBuffer;
327351
Py_ssize_t numDocs;
328-
PyObject *result;
329352

330-
if (!PyList_Check(arg)) {
353+
// parse arguments
354+
docsObj = hintObj = NULL;
355+
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
356+
&docsObj, &hintObj))
357+
return NULL;
358+
if (!PyList_Check(docsObj)) {
331359
PyErr_SetString(PyExc_TypeError, "expecting list");
332360
return NULL;
333361
}
334-
numDocs = PyList_GET_SIZE(arg);
362+
363+
// setup for actual work
364+
cxoBuffer_init(&hintBuffer);
365+
if (hintObj && hintObj != Py_None) {
366+
optionsPtr = &options;
367+
if (cxoSodaCollection_processOptions(coll, &options, hintObj,
368+
&hintBuffer) < 0)
369+
return NULL;
370+
}
371+
numDocs = PyList_GET_SIZE(docsObj);
335372
handles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*));
336373
if (!handles) {
337374
PyErr_NoMemory();
375+
cxoBuffer_clear(&hintBuffer);
338376
return NULL;
339377
}
340378
returnHandles = PyMem_Malloc(numDocs * sizeof(dpiSodaDoc*));
341379
if (!returnHandles) {
342380
PyErr_NoMemory();
343381
PyMem_Free(handles);
382+
cxoBuffer_clear(&hintBuffer);
344383
return NULL;
345384
}
346-
result = cxoSodaCollection_insertManyHelper(coll, arg, numDocs, handles,
347-
returnHandles);
385+
result = cxoSodaCollection_insertManyHelper(coll, docsObj, numDocs,
386+
handles, returnHandles, optionsPtr);
348387
PyMem_Free(handles);
349388
PyMem_Free(returnHandles);
389+
cxoBuffer_clear(&hintBuffer);
350390
return result;
351391
}
352392

@@ -384,23 +424,46 @@ static PyObject *cxoSodaCollection_insertOne(cxoSodaCollection *coll,
384424
// containing all but the content itself.
385425
//-----------------------------------------------------------------------------
386426
static PyObject *cxoSodaCollection_insertOneAndGet(cxoSodaCollection *coll,
387-
PyObject *arg)
427+
PyObject *args, PyObject *keywordArgs)
388428
{
429+
static char *keywordList[] = { "doc", "hint", NULL };
430+
dpiSodaOperOptions options, *optionsPtr = NULL;
389431
dpiSodaDoc *handle, *returnedHandle;
432+
PyObject *docObj, *hintObj;
433+
cxoBuffer hintBuffer;
390434
uint32_t flags;
391435
int status;
392436

393-
if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
437+
// parse arguments
438+
docObj = hintObj = NULL;
439+
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
440+
&docObj, &hintObj))
394441
return NULL;
442+
443+
// setup for actual work
395444
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
396445
return NULL;
446+
if (cxoUtils_processSodaDocArg(coll->db, docObj, &handle) < 0)
447+
return NULL;
448+
cxoBuffer_init(&hintBuffer);
449+
if (hintObj && hintObj != Py_None) {
450+
optionsPtr = &options;
451+
if (cxoSodaCollection_processOptions(coll, &options, hintObj,
452+
&hintBuffer) < 0) {
453+
dpiSodaDoc_release(handle);
454+
return NULL;
455+
}
456+
}
457+
458+
// perform actual work
397459
Py_BEGIN_ALLOW_THREADS
398-
status = dpiSodaColl_insertOne(coll->handle, handle, flags,
399-
&returnedHandle);
460+
status = dpiSodaColl_insertOneWithOptions(coll->handle, handle, optionsPtr,
461+
flags, &returnedHandle);
400462
Py_END_ALLOW_THREADS
401463
if (status < 0)
402464
cxoError_raiseAndReturnNull();
403465
dpiSodaDoc_release(handle);
466+
cxoBuffer_clear(&hintBuffer);
404467
if (status < 0)
405468
return NULL;
406469
return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle);
@@ -463,22 +526,46 @@ static PyObject *cxoSodaCollection_save(cxoSodaCollection *coll,
463526
// containing all but the content itself.
464527
//-----------------------------------------------------------------------------
465528
static PyObject *cxoSodaCollection_saveAndGet(cxoSodaCollection *coll,
466-
PyObject *arg)
529+
PyObject *args, PyObject *keywordArgs)
467530
{
531+
static char *keywordList[] = { "doc", "hint", NULL };
532+
dpiSodaOperOptions options, *optionsPtr = NULL;
468533
dpiSodaDoc *handle, *returnedHandle;
534+
PyObject *docObj, *hintObj;
535+
cxoBuffer hintBuffer;
469536
uint32_t flags;
470537
int status;
471538

472-
if (cxoUtils_processSodaDocArg(coll->db, arg, &handle) < 0)
539+
// parse arguments
540+
docObj = hintObj = NULL;
541+
if (!PyArg_ParseTupleAndKeywords(args, keywordArgs, "O|O", keywordList,
542+
&docObj, &hintObj))
473543
return NULL;
544+
545+
// setup for actual work
474546
if (cxoConnection_getSodaFlags(coll->db->connection, &flags) < 0)
475547
return NULL;
548+
if (cxoUtils_processSodaDocArg(coll->db, docObj, &handle) < 0)
549+
return NULL;
550+
cxoBuffer_init(&hintBuffer);
551+
if (hintObj && hintObj != Py_None) {
552+
optionsPtr = &options;
553+
if (cxoSodaCollection_processOptions(coll, &options, hintObj,
554+
&hintBuffer) < 0) {
555+
dpiSodaDoc_release(handle);
556+
return NULL;
557+
}
558+
}
559+
560+
// perform actual work
476561
Py_BEGIN_ALLOW_THREADS
477-
status = dpiSodaColl_save(coll->handle, handle, flags, &returnedHandle);
562+
status = dpiSodaColl_saveWithOptions(coll->handle, handle, optionsPtr,
563+
flags, &returnedHandle);
478564
Py_END_ALLOW_THREADS
479565
if (status < 0)
480566
cxoError_raiseAndReturnNull();
481567
dpiSodaDoc_release(handle);
568+
cxoBuffer_clear(&hintBuffer);
482569
if (status < 0)
483570
return NULL;
484571
return (PyObject*) cxoSodaDoc_new(coll->db, returnedHandle);
@@ -516,12 +603,13 @@ static PyMethodDef cxoMethods[] = {
516603
METH_NOARGS },
517604
{ "insertOne", (PyCFunction) cxoSodaCollection_insertOne, METH_O },
518605
{ "insertOneAndGet", (PyCFunction) cxoSodaCollection_insertOneAndGet,
519-
METH_O },
606+
METH_VARARGS | METH_KEYWORDS },
520607
{ "insertMany", (PyCFunction) cxoSodaCollection_insertMany, METH_O },
521608
{ "insertManyAndGet", (PyCFunction) cxoSodaCollection_insertManyAndGet,
522-
METH_O },
609+
METH_VARARGS | METH_KEYWORDS },
523610
{ "save", (PyCFunction) cxoSodaCollection_save, METH_O },
524-
{ "saveAndGet", (PyCFunction) cxoSodaCollection_saveAndGet, METH_O },
611+
{ "saveAndGet", (PyCFunction) cxoSodaCollection_saveAndGet,
612+
METH_VARARGS | METH_KEYWORDS },
525613
{ "truncate", (PyCFunction) cxoSodaCollection_truncate, METH_NOARGS },
526614
{ NULL }
527615
};

src/cxoSodaOperation.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,23 @@ static PyObject *cxoSodaOperation_filter(cxoSodaOperation *op,
130130
}
131131

132132

133+
//-----------------------------------------------------------------------------
134+
// cxoSodaOperation_hint()
135+
// Set the hint to be used for the operation.
136+
//-----------------------------------------------------------------------------
137+
static PyObject *cxoSodaOperation_hint(cxoSodaOperation *op, PyObject *hintObj)
138+
{
139+
cxoBuffer_clear(&op->hintBuffer);
140+
if (cxoBuffer_fromObject(&op->hintBuffer, hintObj,
141+
op->coll->db->connection->encodingInfo.encoding) < 0)
142+
return NULL;
143+
op->options.hint = op->hintBuffer.ptr;
144+
op->options.hintLength = op->hintBuffer.size;
145+
Py_INCREF(op);
146+
return (PyObject*) op;
147+
}
148+
149+
133150
//-----------------------------------------------------------------------------
134151
// cxoSodaOperation_key()
135152
// Set the key to be used for the operation.
@@ -502,6 +519,7 @@ static PyMethodDef cxoMethods[] = {
502519
{ "getDocuments", (PyCFunction) cxoSodaOperation_getDocuments,
503520
METH_NOARGS },
504521
{ "getOne", (PyCFunction) cxoSodaOperation_getOne, METH_NOARGS },
522+
{ "hint", (PyCFunction) cxoSodaOperation_hint, METH_O },
505523
{ "remove", (PyCFunction) cxoSodaOperation_remove, METH_NOARGS },
506524
{ "replaceOne", (PyCFunction) cxoSodaOperation_replaceOne, METH_O },
507525
{ "replaceOneAndGet", (PyCFunction) cxoSodaOperation_replaceOneAndGet,

0 commit comments

Comments
 (0)