modules/platforms/python/cpp_module/utils.cpp (416 lines of code) (raw):
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "module.h"
#include "utils.h"
#include <ignite/odbc/diagnostic/diagnosable.h>
#include <ignite/common/detail/defer.h>
#include <Python.h>
#define LAZY_INIT_MODULE_CLASS(class_name) \
static PyObject* instance{nullptr}; \
if (!instance) \
instance = py_get_module_class(class_name); \
return instance
bool check_errors(ignite::sql_result ret, ignite::diagnosable& diag) {
auto &records = diag.get_diagnostic_records();
if ((ret == ignite::sql_result::AI_SUCCESS || ret == ignite::sql_result::AI_SUCCESS_WITH_INFO)
&& diag.get_diagnostic_records().is_successful()) {
return true;
}
auto error_class = py_get_module_interface_error_class();
std::string err_msg;
switch (records.get_return_code()) {
case SQL_INVALID_HANDLE:
err_msg = "Invalid object handle";
break;
case SQL_NO_DATA:
err_msg = "No data available";
break;
case SQL_ERROR:
default:
if (records.get_status_records_number() == 0) {
err_msg = "Unknown error";
break;
}
auto record = records.get_status_record(1);
err_msg = record.get_message_text();
using ignite::sql_state;
switch (record.get_sql_state_internal()) {
case sql_state::SHY000_GENERAL_ERROR: {
error_class = py_get_module_database_error_class();
break;
}
case sql_state::S01S02_OPTION_VALUE_CHANGED:
case sql_state::S01004_DATA_TRUNCATED: {
error_class = py_get_module_warning_class();
break;
}
case sql_state::SHY003_INVALID_APPLICATION_BUFFER_TYPE:
case sql_state::SHY009_INVALID_USE_OF_NULL_POINTER:
case sql_state::SHY010_SEQUENCE_ERROR:
case sql_state::SHY092_OPTION_TYPE_OUT_OF_RANGE:
case sql_state::SHY097_COLUMN_TYPE_OUT_OF_RANGE:
case sql_state::SHY105_INVALID_PARAMETER_TYPE:
case sql_state::SHY106_FETCH_TYPE_OUT_OF_RANGE:
case sql_state::S07009_INVALID_DESCRIPTOR_INDEX:
case sql_state::S40001_SERIALIZATION_FAILURE:
case sql_state::SHY090_INVALID_STRING_OR_BUFFER_LENGTH:
case sql_state::S22026_DATA_LENGTH_MISMATCH:
case sql_state::S22002_INDICATOR_NEEDED:
case sql_state::S01S00_INVALID_CONNECTION_STRING_ATTRIBUTE: {
error_class = py_get_module_interface_error_class();
break;
}
case sql_state::S01S01_ERROR_IN_ROW:
case sql_state::S01S07_FRACTIONAL_TRUNCATION: {
error_class = py_get_module_data_error_class();
break;
}
case sql_state::S07006_RESTRICTION_VIOLATION:
case sql_state::S23000_INTEGRITY_CONSTRAINT_VIOLATION: {
error_class = py_get_module_integrity_error_class();
break;
}
case sql_state::S24000_INVALID_CURSOR_STATE:
case sql_state::S25000_INVALID_TRANSACTION_STATE: {
error_class = py_get_module_internal_error_class();
break;
}
case sql_state::S08001_CANNOT_CONNECT:
case sql_state::S08002_ALREADY_CONNECTED:
case sql_state::S08003_NOT_CONNECTED:
case sql_state::S08004_CONNECTION_REJECTED:
case sql_state::S08S01_LINK_FAILURE:
case sql_state::SHYT00_TIMEOUT_EXPIRED:
case sql_state::SHYT01_CONNECTION_TIMEOUT: {
error_class = py_get_module_operational_error_class();
break;
}
case sql_state::S42000_SYNTAX_ERROR_OR_ACCESS_VIOLATION:
case sql_state::S42S01_TABLE_OR_VIEW_ALREADY_EXISTS:
case sql_state::S42S02_TABLE_OR_VIEW_NOT_FOUND:
case sql_state::S42S11_INDEX_ALREADY_EXISTS:
case sql_state::S42S12_INDEX_NOT_FOUND:
case sql_state::S42S21_COLUMN_ALREADY_EXISTS:
case sql_state::S42S22_COLUMN_NOT_FOUND:
case sql_state::SHY001_MEMORY_ALLOCATION:
case sql_state::S3F000_INVALID_SCHEMA_NAME: {
error_class = py_get_module_programming_error_class();
break;
}
case sql_state::SHYC00_OPTIONAL_FEATURE_NOT_IMPLEMENTED:
case sql_state::SIM001_FUNCTION_NOT_SUPPORTED:{
error_class = py_get_module_not_supported_error_class();
break;
}
default:
break;
}
break;
}
PyErr_SetString(error_class, err_msg.c_str());
return false;
}
std::string get_current_exception_as_string() {
auto err_obj = PyErr_Occurred();
if (!err_obj)
return {};
auto err_str_obj = PyObject_Str(err_obj);
if (!err_str_obj)
return "<Can not cast exception to a string>";
auto *data = PyBytes_AsString(err_str_obj);
auto len = PyBytes_Size(err_str_obj);
return {data, std::size_t(len)};
}
const char* py_object_get_typename(PyObject* obj) {
if (!obj || !obj->ob_type || !obj->ob_type->tp_name) {
return "Unknown";
}
return obj->ob_type->tp_name;
}
PyObject* py_get_module() {
static PyObject* instance{nullptr};
// No need for sync here - Python is single-threaded
if (!instance) {
instance = PyImport_ImportModule(MODULE_NAME);
}
return instance;
}
PyObject* py_get_class(const char* module_name, const char* class_name) {
auto module_obj = PyImport_ImportModule(module_name);
if (!module_obj)
return nullptr;
auto class_obj = PyObject_GetAttrString(module_obj, class_name);
Py_DECREF(module_obj);
return class_obj;
}
PyObject* py_get_module_class(const char* class_name) {
auto pyignite_dbapi_mod = py_get_module();
if (!pyignite_dbapi_mod)
return nullptr;
return PyObject_GetAttrString(pyignite_dbapi_mod, class_name);
}
PyObject* py_call_method_no_arg(PyObject* obj, const char* method_name) {
PyObject* py_method_name = PyUnicode_FromString(method_name);
if (!py_method_name)
return nullptr;
auto py_method_name_guard = ignite::detail::defer([&]{ Py_DECREF(py_method_name); });
return PyObject_CallMethodObjArgs(obj, py_method_name, nullptr);
}
std::int64_t py_get_attr_int(PyObject* obj, const char* attr_name) {
auto attr_obj = PyObject_GetAttrString(obj, attr_name);
if (!attr_obj) {
throw ignite::ignite_error(get_current_exception_as_string());
}
auto attr_obj_guard = ignite::detail::defer([&] { Py_DECREF(attr_obj); });
if (PyErr_Occurred()) {
throw ignite::ignite_error(get_current_exception_as_string());
}
auto res = PyLong_AsLongLong(attr_obj);
if (PyErr_Occurred()) {
throw ignite::ignite_error(get_current_exception_as_string());
}
return res;
}
PyObject* py_get_module_uuid_class() {
LAZY_INIT_MODULE_CLASS("UUID");
}
PyObject* py_get_module_date_class() {
LAZY_INIT_MODULE_CLASS("DATE");
}
PyObject* py_get_module_time_class() {
LAZY_INIT_MODULE_CLASS("TIME");
}
PyObject* py_get_module_datetime_class() {
LAZY_INIT_MODULE_CLASS("DATETIME");
}
PyObject* py_get_module_timestamp_class() {
LAZY_INIT_MODULE_CLASS("TIMESTAMP");
}
PyObject* py_get_module_number_class() {
LAZY_INIT_MODULE_CLASS("NUMBER");
}
PyObject* py_get_module_duration_class() {
LAZY_INIT_MODULE_CLASS("DURATION");
}
PyObject* py_get_module_warning_class() {
LAZY_INIT_MODULE_CLASS("Warning");
}
PyObject* py_get_module_interface_error_class() {
LAZY_INIT_MODULE_CLASS("InterfaceError");
}
PyObject* py_get_module_database_error_class() {
LAZY_INIT_MODULE_CLASS("DatabaseError");
}
PyObject* py_get_module_data_error_class() {
LAZY_INIT_MODULE_CLASS("DataError");
}
PyObject* py_get_module_operational_error_class() {
LAZY_INIT_MODULE_CLASS("OperationalError");
}
PyObject* py_get_module_integrity_error_class() {
LAZY_INIT_MODULE_CLASS("IntegrityError");
}
PyObject* py_get_module_internal_error_class() {
LAZY_INIT_MODULE_CLASS("InternalError");
}
PyObject* py_get_module_programming_error_class() {
LAZY_INIT_MODULE_CLASS("ProgrammingError");
}
PyObject* py_get_module_not_supported_error_class() {
LAZY_INIT_MODULE_CLASS("NotSupportedError");
}
PyObject* py_create_uuid(ignite::bytes_view bytes) {
auto uuid_class = py_get_module_uuid_class();
if (!uuid_class)
return nullptr;
auto args = PyTuple_New(0);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
PyObject* py_bytes = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(bytes.data()), bytes.size());
if (!py_bytes)
return nullptr;
if (PyDict_SetItemString(kwargs, "bytes", py_bytes) < 0) {
Py_DECREF(py_bytes);
return nullptr;
}
return PyObject_Call(uuid_class, args, kwargs);
}
PyObject* py_create_date(const ignite::ignite_date &value) {
auto date_class = py_get_module_date_class();
if (!date_class)
return nullptr;
PyObject* year = PyLong_FromLong(value.get_year());
if (!year)
return nullptr;
auto year_guard = ignite::detail::defer([&]{ Py_DECREF(year); });
PyObject* month = PyLong_FromLong(value.get_month());
if (!month)
return nullptr;
auto month_guard = ignite::detail::defer([&]{ Py_DECREF(month); });
PyObject* day = PyLong_FromLong(value.get_day_of_month());
if (!day)
return nullptr;
auto day_guard = ignite::detail::defer([&]{ Py_DECREF(day); });
auto args = PyTuple_Pack(3, year, month, day);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
year_guard.release();
month_guard.release();
day_guard.release();
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
return PyObject_Call(date_class, args, kwargs);
}
PyObject* py_create_time(const ignite::ignite_time &value) {
auto time_class = py_get_module_time_class();
if (!time_class)
return nullptr;
PyObject* hour = PyLong_FromLong(value.get_hour());
if (!hour)
return nullptr;
auto hour_guard = ignite::detail::defer([&]{ Py_DECREF(hour); });
PyObject* minute = PyLong_FromLong(value.get_minute());
if (!minute)
return nullptr;
auto minute_guard = ignite::detail::defer([&]{ Py_DECREF(minute); });
PyObject* second = PyLong_FromLong(value.get_second());
if (!second)
return nullptr;
auto second_guard = ignite::detail::defer([&]{ Py_DECREF(second); });
PyObject* u_second = PyLong_FromLong(value.get_nano() / 1000);
if (!u_second)
return nullptr;
auto u_second_guard = ignite::detail::defer([&]{ Py_DECREF(u_second); });
auto args = PyTuple_Pack(4, hour, minute, second, u_second);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
hour_guard.release();
minute_guard.release();
second_guard.release();
u_second_guard.release();
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
return PyObject_Call(time_class, args, kwargs);
}
PyObject* py_create_datetime(const ignite::ignite_date_time &value) {
auto datetime_class = py_get_module_datetime_class();
if (!datetime_class)
return nullptr;
PyObject* year = PyLong_FromLong(value.get_year());
if (!year)
return nullptr;
auto year_guard = ignite::detail::defer([&]{ Py_DECREF(year); });
PyObject* month = PyLong_FromLong(value.get_month());
if (!month)
return nullptr;
auto month_guard = ignite::detail::defer([&]{ Py_DECREF(month); });
PyObject* day = PyLong_FromLong(value.get_day_of_month());
if (!day)
return nullptr;
auto day_guard = ignite::detail::defer([&]{ Py_DECREF(day); });
PyObject* hour = PyLong_FromLong(value.get_hour());
if (!hour)
return nullptr;
auto hour_guard = ignite::detail::defer([&]{ Py_DECREF(hour); });
PyObject* minute = PyLong_FromLong(value.get_minute());
if (!minute)
return nullptr;
auto minute_guard = ignite::detail::defer([&]{ Py_DECREF(minute); });
PyObject* second = PyLong_FromLong(value.get_second());
if (!second)
return nullptr;
auto second_guard = ignite::detail::defer([&]{ Py_DECREF(second); });
PyObject* u_second = PyLong_FromLong(value.get_nano() / 1000);
if (!u_second)
return nullptr;
auto u_second_guard = ignite::detail::defer([&]{ Py_DECREF(u_second); });
auto args = PyTuple_Pack(7, year, month, day, hour, minute, second, u_second);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
year_guard.release();
month_guard.release();
day_guard.release();
hour_guard.release();
minute_guard.release();
second_guard.release();
u_second_guard.release();
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
return PyObject_Call(datetime_class, args, kwargs);
}
PyObject* py_create_datetime(const ignite::ignite_timestamp &value) {
auto datetime_class = py_get_module_datetime_class();
if (!datetime_class)
return nullptr;
PyObject* second = PyLong_FromLongLong(value.get_epoch_second());
if (!second)
return nullptr;
auto second_guard = ignite::detail::defer([&]{ Py_DECREF(second); });
PyObject* u_second = PyLong_FromLongLong(value.get_nano() / 1000);
if (!u_second)
return nullptr;
auto u_second_guard = ignite::detail::defer([&]{ Py_DECREF(u_second); });
PyObject* constructor_name = PyUnicode_FromString("fromtimestamp");
if (!constructor_name)
return nullptr;
auto constructor_name_guard = ignite::detail::defer([&]{ Py_DECREF(constructor_name); });
return PyObject_CallMethodObjArgs(datetime_class, constructor_name, second, nullptr);
}
PyObject* py_create_number(std::string_view value) {
auto number_class = py_get_module_number_class();
if (!number_class)
return nullptr;
PyObject* str_obj = PyUnicode_FromStringAndSize(value.data(), value.size());
if (!str_obj)
return nullptr;
auto str_obj_guard = ignite::detail::defer([&]{ Py_DECREF(str_obj); });
auto args = PyTuple_Pack(1, str_obj);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
str_obj_guard.release();
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
return PyObject_Call(number_class, args, kwargs);
}
PyObject* py_create_timedelta(const ignite::ignite_duration &value) {
auto duration_class = py_get_module_duration_class();
if (!duration_class)
return nullptr;
auto args = PyTuple_New(0);
if (!args)
return nullptr;
auto args_guard = ignite::detail::defer([&]{ Py_DECREF(args); });
PyObject* second = PyLong_FromLong(value.get_seconds());
if (!second)
return nullptr;
auto second_guard = ignite::detail::defer([&]{ Py_DECREF(second); });
PyObject* u_second = PyLong_FromLong(value.get_nano() / 1000);
if (!u_second)
return nullptr;
auto u_second_guard = ignite::detail::defer([&]{ Py_DECREF(u_second); });
auto kwargs = PyDict_New();
if (!kwargs)
return nullptr;
auto kwargs_guard = ignite::detail::defer([&]{ Py_DECREF(kwargs); });
if (PyDict_SetItemString(kwargs, "seconds", second) < 0)
return nullptr;
second_guard.release();
if (PyDict_SetItemString(kwargs, "microseconds", u_second) < 0)
return nullptr;
u_second_guard.release();
return PyObject_Call(duration_class, args, kwargs);
}