-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_yottadb.h
237 lines (206 loc) · 12 KB
/
_yottadb.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/****************************************************************
* *
* Copyright (c) 2020-2021 Peter Goss All rights reserved. *
* *
* Copyright (c) 2020-2022 YottaDB LLC and/or its subsidiaries. *
* All rights reserved. *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include <libyottadb.h>
#include <Python.h>
#define YDBPY_DEFAULT_VALUE_LEN 32
#define YDBPY_DEFAULT_SUBSCRIPT_LEN 16
#define YDBPY_DEFAULT_SUBSCRIPT_COUNT 2
#define CANONICAL_NUMBER_TO_STRING_MAX 48
#define YDB_LOCK_MIN_ARGS 2
#define YDB_LOCK_ARGS_PER_KEY 3
#define YDB_CALL_VARIADIC_MAX_ARGUMENTS 36
#define YDB_LOCK_MAX_KEYS (YDB_CALL_VARIADIC_MAX_ARGUMENTS - YDB_LOCK_MIN_ARGS) / YDB_LOCK_ARGS_PER_KEY
/* Large enough to fit any YDB error message, per
* https://docs.yottadb.com/ProgrammersGuide/extrout.html#ydb-zstatus
*/
#define YDBPY_MAX_ERRORMSG 2048
// Default size to allocate for ci() output parameters
#define YDBPY_DEFAULT_OUTBUF 2048
#define YDBPY_CHECK_TYPE 2
/* Set of acceptable Python error types. Each type is named by prefixing a Python error name with `YDBPython`,
* with the exception of YDBPython_NoError. This item doesn't represent a Python error, but is included at enum value 0
* to prevent conflicts with YDB_OK which signals no error with a value of 0.
*/
typedef enum YDBPythonErrorType {
YDBPython_TypeError = 1,
YDBPython_ValueError,
YDBPython_OSError,
} YDBPythonErrorType;
/* Set of acceptable Python Sequence types. Used to enforce correct limits and
* emit relevant errors when processing Python Sequences passed down from
* yottadb.py.
*/
typedef enum YDBPythonSequenceType {
YDBPython_VarnameSequence,
YDBPython_SubsarraySequence,
YDBPython_KeySequence,
} YDBPythonSequenceType;
// TypeError messages
#define YDBPY_ERR_IMMUTABLE_OUTPUT_ARGS \
"YottaDB call-in argument list is immutable, but routine has output argument(s). Pass argument list as a Python List to " \
"allow output argument updates."
#define YDBPY_ERR_CALLIN_ARGS_NOT_SEQ "YottaDB call-in arguments must be passed as a Sequence"
#define YDBPY_ERR_INVALID_ARGS "YottaDB call-in routine '%s' has incorrect number of parameters: %u expected, got %u"
#define YDBPY_ERR_INVALID_CI_ARG_TYPE \
"YottaDB call-in routine '%s' parameter %d has invalid type: must be str, bytes, int, or float"
#define YDBPY_ERR_CI_PARM_UNDEFINED "YottaDB call-in routine %s parameter %d not defined in call-in table"
#define YDBPY_ERR_NOT_LIST_OR_TUPLE "key must be list or tuple."
#define YDBPY_ERR_VARNAME_NOT_BYTES_LIKE "varname argument is not a bytes-like object (bytes or str)"
#define YDBPY_ERR_ARG_NOT_BYTES_LIKE "argument is not a bytes-like object (bytes or str)"
#define YDBPY_ERR_ITEM_NOT_BYTES_LIKE "item %ld is not a bytes-like object (bytes or str)"
#define YDBPY_ERR_KEY_IN_SEQUENCE_NOT_LIST_OR_TUPLE "item %ld is not a list or tuple."
#define YDBPY_ERR_KEY_IN_SEQUENCE_VARNAME_NOT_BYTES "item %ld in key sequence invalid: first element must be of type 'bytes'"
// ValueError messages
#define YDBPY_ERR_EMPTY_FILENAME "YottaDB filenames must be one character or longer"
#define YDBPY_ERR_VARNAME_TOO_LONG "invalid varname length %ld: max %d"
#define YDBPY_ERR_SEQUENCE_TOO_LONG "invalid sequence length %ld: max %d"
#define YDBPY_ERR_BYTES_TOO_LONG "invalid bytes length %ld: max %d"
#define YDBPY_ERR_KEY_IN_SEQUENCE_INCORRECT_LENGTH "item %lu must be length 1 or 2."
#define YDBPY_ERR_KEY_IN_SEQUENCE_VARNAME_TOO_LONG "item %ld in key sequence has invalid varname length %ld: max %d."
#define YDBPY_ERR_KEY_IN_SEQUENCE_SUBSARRAY_INVALID "item %ld in key sequence has invalid subsarray: %s"
#define YDBPY_ERR_VARNAME_INVALID "'varnames' argument invalid: %s"
#define YDBPY_ERR_SUBSARRAY_INVALID "'subsarray' argument invalid: %s"
#define YDBPY_ERR_KEYS_INVALID "'keys' argument invalid: %s"
#define YDBPY_ERR_ROUTINE_UNSPECIFIED "No call-in routine specified. Routine name required for M call-in."
#define YDBPY_ERR_SYSCALL "System call failed: %s, return %d (%s)"
#define YDBPY_ERR_FAILED_NUMERIC_CONVERSION "Failed to convert Python numeric value to internal representation"
// Prevents compiler warnings for variables used only in asserts
#define UNUSED(x) (void)(x)
// Redefine __assert_fail from libc (used by assert.h) to enable custom assert message.
void __assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) {
fprintf(stderr,
"Assertion '%s' failed in function '%s' in %s at line %d.\nPlease file a bug report at "
"https://gitlab.com/YottaDB/Lang/YDBPython/-/issues\n",
assertion, function, file, line);
abort();
}
/* A structure that represents a key using YDB C types. used internally for
* converting between Python and YDB C types.
*/
typedef struct {
ydb_buffer_t *varname;
int subs_used;
ydb_buffer_t *subsarray;
} YDBKey;
#define YDB_COPY_BYTES_TO_BUFFER(BYTES, BYTES_LEN, BUFFERP, COPY_DONE) \
{ \
if (BYTES_LEN <= (BUFFERP)->len_alloc) { \
memcpy((BUFFERP)->buf_addr, BYTES, BYTES_LEN); \
(BUFFERP)->len_used = BYTES_LEN; \
COPY_DONE = TRUE; \
} else { \
COPY_DONE = FALSE; \
} \
}
#define POPULATE_SUBS_USED_AND_SUBSARRAY(SUBSARRAY_PY, SUBSUSED, SUBSARRAY_YDB, RETURN_NULL) \
{ \
bool success = true; \
\
SUBSUSED = 0; \
SUBSARRAY_YDB = NULL; \
if (Py_None != SUBSARRAY_PY) { \
SUBSUSED = PySequence_Length(SUBSARRAY_PY); \
SUBSARRAY_YDB = malloc(SUBSUSED * sizeof(ydb_buffer_t)); \
success = convert_py_sequence_to_ydb_buffer_array(SUBSARRAY_PY, SUBSUSED, SUBSARRAY_YDB); \
if (!success) { \
FREE_BUFFER_ARRAY(SUBSARRAY_YDB, SUBSUSED); \
RETURN_NULL = true; \
} \
} \
}
#define FREE_BUFFER_ARRAY(ARRAY, LEN) \
{ \
if (NULL != ARRAY) { \
for (int i = 0; i < (LEN); i++) { \
YDB_FREE_BUFFER(&((ydb_buffer_t *)ARRAY)[i]); \
} \
free(ARRAY); \
} \
}
#define FREE_STRING_ARRAY(ARRAY, LEN) \
{ \
if (NULL != ARRAY) { \
for (unsigned int i = 0; i < (LEN); i++) { \
free(ARRAY[i].address); \
} \
free(ARRAY); \
} \
}
#define RETURN_IF_INVALID_SEQUENCE(SEQUENCE, SEQUENCE_TYPE) \
{ \
if (!is_valid_sequence(SEQUENCE, SEQUENCE_TYPE, NULL)) { \
return NULL; \
} \
}
#define FIX_BUFFER_LENGTH(BUFFER) \
{ \
int correct_length = BUFFER.len_used; \
\
YDB_FREE_BUFFER(&BUFFER); \
YDB_MALLOC_BUFFER(&BUFFER, correct_length); \
}
#define RAISE_SPECIFIC_ERROR(ERROR_TYPE, MESSAGE) \
{ \
assert(NULL != MESSAGE); \
PyErr_SetObject(ERROR_TYPE, MESSAGE); \
}
/* Allocate and populate a ydb_buffer_t struct from a Python AnyStr (`str` or `bytes`)
* object and return on failure.
*/
#define INVOKE_ANYSTR_TO_BUFFER(ANYSTR, BUFFER, IS_VARNAME) \
{ \
int status; \
\
status = anystr_to_buffer(ANYSTR, &(BUFFER), IS_VARNAME); \
if (YDB_OK != status) { \
return NULL; \
} \
}
/* Allocate and populate a ydb_buffer_t array representing a set of subscripts.
* In case of failure, free the specified CLEANUP_BUF and return.
*/
#define INVOKE_POPULATE_SUBS_USED_AND_SUBSARRAY_AND_CLEANUP_VARNAME(SUBSARRAY_PY, SUBS_USED, SUBSARRAY_YDB, CLEANUP_BUF) \
{ \
int status; \
\
status = populate_subs_used_and_subsarray(SUBSARRAY_PY, &(SUBS_USED), &(SUBSARRAY_YDB)); \
if (YDB_OK != status) { \
YDB_FREE_BUFFER(&(CLEANUP_BUF)); \
return NULL; \
} \
}
/* PYTHON EXCEPTION DECLARATIONS */
/* YottaDBError represents an error return status from any of the libyottadb
* functions being wrapped. Since YottaDB returns a status that is a number and
* has a way to create a message from that number the choice was to preserve
* both in the python exception. This means we need to extend the exception to
* accept both. Use raise_YottaDBError function to raise
*/
static PyObject *YDBException;
static PyObject *YDBError;
static PyObject *YDBTPException;
static PyObject *YDBTPRestart;
static PyObject *YDBTPRollback;
static PyObject *YDBNotOk;
static PyObject *YDBDeferHandler;
static PyObject *YDBNodeEnd;
/* YDBLockTimeoutError is a simple exception to indicate that a lock failed due
* to timeout. */
static PyObject *YDBLockTimeoutError;
/* YDBTPTimeoutError is a simple exception to indicate that a transaction callback
* function failed due to timeout. */
static PyObject *YDBTPTimeoutError;
/* YDBPythonError is to be raised when there is a possibility for an error to
occur but that we believe that it should never happen. */
static PyObject *YDBPythonError;