Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : #include "monetdb_config.h"
14 : #include "pyapi.h"
15 : #include "conversion.h"
16 : #include "connection.h"
17 : #include "type_conversion.h"
18 :
19 : static PyObject *
20 21 : _connection_execute(Py_ConnectionObject *self, PyObject *args)
21 : {
22 21 : char *query = NULL;
23 21 : if (PyUnicode_CheckExact(args)) {
24 21 : query = GDKstrdup(PyUnicode_AsUTF8(args));
25 : } else {
26 0 : PyErr_Format(PyExc_TypeError,
27 : "expected a query string, but got an object of type %s",
28 0 : Py_TYPE(args)->tp_name);
29 0 : return NULL;
30 : }
31 21 : if (!query) {
32 0 : PyErr_Format(PyExc_Exception, "%s", SQLSTATE(HY013) MAL_MALLOC_FAIL);
33 0 : return NULL;
34 : }
35 21 : PyObject *result;
36 21 : res_table *output = NULL;
37 21 : char *res = NULL;
38 : //Py_BEGIN_ALLOW_THREADS;
39 21 : res = _connection_query(self->cntxt, query, &output);
40 : //Py_END_ALLOW_THREADS;
41 21 : GDKfree(query);
42 21 : if (res != MAL_SUCCEED) {
43 3 : PyErr_Format(PyExc_Exception, "SQL Query Failed: %s",
44 3 : (res ? getExceptionMessage(res) : "<no error>"));
45 3 : freeException(res);
46 3 : return NULL;
47 : }
48 :
49 18 : result = PyDict_New();
50 18 : if (output && output->nr_cols > 0) {
51 : PyInput input;
52 : PyObject *numpy_array;
53 : int i;
54 31 : for (i = 0; i < output->nr_cols; i++) {
55 19 : res_col col = output->cols[i];
56 19 : BAT *b = BATdescriptor(col.b);
57 :
58 19 : if (b == NULL) {
59 0 : PyErr_Format(PyExc_Exception, "Internal error: could not retrieve bat");
60 0 : return NULL;
61 : }
62 :
63 19 : input.bat = b;
64 19 : input.conv_bat = NULL;
65 19 : input.count = BATcount(b);
66 19 : input.bat_type = getBatType(b->ttype);
67 19 : input.scalar = false;
68 19 : input.sql_subtype = &col.type;
69 :
70 19 : numpy_array = PyMaskedArray_FromBAT(&input, 0, input.count, &res, true);
71 19 : if (!numpy_array) {
72 0 : _connection_cleanup_result(output);
73 0 : BBPunfix(b->batCacheid);
74 0 : PyErr_Format(PyExc_Exception, "SQL Query Failed: %s",
75 0 : (res ? getExceptionMessage(res) : "<no error>"));
76 0 : return NULL;
77 : }
78 19 : PyObject *nme = PyUnicode_FromString(output->cols[i].name);
79 19 : PyDict_SetItem(result, nme, numpy_array);
80 19 : Py_DECREF(nme);
81 19 : Py_DECREF(numpy_array);
82 19 : BBPunfix(input.bat->batCacheid);
83 20 : BBPreclaim(input.conv_bat);
84 : }
85 12 : _connection_cleanup_result(output);
86 12 : return result;
87 : } else {
88 6 : Py_RETURN_NONE;
89 : }
90 : }
91 :
92 : static PyMethodDef _connectionObject_methods[] = {
93 : {"execute", (PyCFunction)_connection_execute, METH_O,
94 : "execute(query) -> executes a SQL query on the database in the current "
95 : "client context"},
96 : {NULL, NULL, 0, NULL} /* Sentinel */
97 : };
98 :
99 : PyTypeObject Py_ConnectionType = {
100 : .ob_base.ob_base.ob_refcnt = 1,
101 : .tp_name = "monetdb._connection",
102 : .tp_basicsize = sizeof(Py_ConnectionObject),
103 : .tp_hash = (hashfunc)PyObject_HashNotImplemented,
104 : .tp_flags = Py_TPFLAGS_DEFAULT,
105 : .tp_doc = "Connection to MonetDB",
106 : .tp_methods = _connectionObject_methods,
107 : .tp_alloc = PyType_GenericAlloc,
108 : .tp_new = PyType_GenericNew,
109 : .tp_free = PyObject_Del,
110 : };
111 :
112 12 : void _connection_cleanup_result(void *output)
113 : {
114 12 : SQLdestroyResult((res_table *)output);
115 12 : }
116 :
117 21 : str _connection_query(Client cntxt, const char *query, res_table **result)
118 : {
119 21 : str res = MAL_SUCCEED;
120 21 : res = SQLstatementIntern(cntxt, query, "name", 1, 0, result);
121 21 : return res;
122 : }
123 :
124 1 : str _connection_create_table(Client cntxt, char *sname, char *tname,
125 : sql_emit_col *columns, size_t ncols)
126 : {
127 1 : return create_table_from_emit(cntxt, sname, tname, columns, ncols);
128 : }
129 :
130 12 : str _connection_append_to_table(Client cntxt, char *sname, char *tname,
131 : sql_emit_col *columns, size_t ncols)
132 : {
133 12 : return append_to_table_from_emit(cntxt, sname, tname, columns, ncols);
134 : }
135 :
136 184 : PyObject *Py_Connection_Create(Client cntxt, QueryStruct *query_ptr, int query_sem)
137 : {
138 184 : register Py_ConnectionObject *op;
139 :
140 184 : op = (Py_ConnectionObject *)PyObject_MALLOC(sizeof(Py_ConnectionObject));
141 184 : if (op == NULL)
142 0 : return PyErr_NoMemory();
143 184 : PyObject_Init((PyObject *)op, &Py_ConnectionType);
144 :
145 184 : op->cntxt = cntxt;
146 184 : op->query_ptr = query_ptr;
147 184 : op->query_sem = query_sem;
148 :
149 184 : return (PyObject *)op;
150 : }
151 :
152 10 : static void _connection_import_array(void) { _import_array(); }
153 :
154 10 : str _connection_init(void)
155 : {
156 10 : str msg = MAL_SUCCEED;
157 10 : _connection_import_array();
158 :
159 10 : if (msg != MAL_SUCCEED) {
160 : return msg;
161 : }
162 :
163 10 : if (PyType_Ready(&Py_ConnectionType) < 0)
164 0 : return createException(MAL, "pyapi3.eval",
165 : SQLSTATE(PY000) "Failed to initialize connection type.");
166 : return msg;
167 : }
|