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, 2025 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * @f mapi
15 : * @a M.L. Kersten, K.S. Mullender, Fabian Groffen
16 : * @v 2.0
17 : * @* The MonetDB Programming Interface
18 : * @+ The Mapi Library
19 : *
20 : * The easiest way to extend the functionality of MonetDB is to construct
21 : * an independent application, which communicates with a running server
22 : * using a database driver with a simple API and a textual protocol. The
23 : * effectiveness of such an approach has been demonstrated by the wide
24 : * use of database API implementations, such as Perl DBI, PHP, ODBC,...
25 : *
26 : * @menu
27 : * * An Example:: a C/C++ code example to get going.
28 : * * Command Summary:: the list of API functions.
29 : * * Library Synopsis:: an short explanation of how MAPI works.
30 : * * Mapi Function Reference:: per API function its parameters and expected results.
31 : * @end menu
32 : *
33 : * @ifclear XQRYmanual
34 : * @node An Example, Command Summary, The Mapi Library, The Mapi Library
35 : * @subsection Sample MAPI Application
36 : *
37 : * The database driver implementation given in this document focuses on
38 : * developing applications in C. The command collection has been
39 : * chosen to align with common practice, i.e. queries follow a prepare,
40 : * execute, and fetch_row paradigm. The output is considered a regular
41 : * table. An example of a mini application below illustrates the main
42 : * operations.
43 : *
44 : * @example
45 : * @verbatim
46 : * #include <mapi.h>
47 : * #include <stdio.h>
48 : * #include <stdlib.h>
49 : *
50 : * void die(Mapi dbh, MapiHdl hdl)
51 : * {
52 : * if (hdl != NULL) {
53 : * mapi_explain_query(hdl, stderr);
54 : * do {
55 : * if (mapi_result_error(hdl) != NULL)
56 : * mapi_explain_result(hdl, stderr);
57 : * } while (mapi_next_result(hdl) == 1);
58 : * mapi_close_handle(hdl);
59 : * mapi_destroy(dbh);
60 : * } else if (dbh != NULL) {
61 : * mapi_explain(dbh, stderr);
62 : * mapi_destroy(dbh);
63 : * } else {
64 : * fprintf(stderr, "command failed\n");
65 : * }
66 : * exit(-1);
67 : * }
68 : *
69 : * MapiHdl query(Mapi dbh, char *q)
70 : * {
71 : * MapiHdl ret = NULL;
72 : * if ((ret = mapi_query(dbh, q)) == NULL || mapi_error(dbh) != MOK)
73 : * die(dbh, ret);
74 : * return(ret);
75 : * }
76 : *
77 : * void update(Mapi dbh, char *q)
78 : * {
79 : * MapiHdl ret = query(dbh, q);
80 : * if (mapi_close_handle(ret) != MOK)
81 : * die(dbh, ret);
82 : * }
83 : *
84 : * int main(int argc, char *argv[])
85 : * {
86 : * Mapi dbh;
87 : * MapiHdl hdl = NULL;
88 : * char *name;
89 : * char *age;
90 : *
91 : * dbh = mapi_connect("localhost", 50000, "monetdb", "monetdb", "sql", "demo");
92 : * if (mapi_error(dbh))
93 : * die(dbh, hdl);
94 : *
95 : * update(dbh, "CREATE TABLE emp (name VARCHAR(20), age INT)");
96 : * update(dbh, "INSERT INTO emp VALUES ('John', 23)");
97 : * update(dbh, "INSERT INTO emp VALUES ('Mary', 22)");
98 : *
99 : * hdl = query(dbh, "SELECT * FROM emp");
100 : *
101 : * while (mapi_fetch_row(hdl)) {
102 : * name = mapi_fetch_field(hdl, 0);
103 : * age = mapi_fetch_field(hdl, 1);
104 : * printf("%s is %s\n", name, age);
105 : * }
106 : *
107 : * mapi_close_handle(hdl);
108 : * mapi_destroy(dbh);
109 : *
110 : * return(0);
111 : * }
112 : * @end verbatim
113 : * @end example
114 : *
115 : * The @code{mapi_connect()} operation establishes a communication channel with
116 : * a running server.
117 : * The query language interface is either "sql" or "mal".
118 : *
119 : * Errors on the interaction can be captured using @code{mapi_error()},
120 : * possibly followed by a request to dump a short error message
121 : * explanation on a standard file location. It has been abstracted away
122 : * in a macro.
123 : *
124 : * Provided we can establish a connection, the interaction proceeds as in
125 : * many similar application development packages. Queries are shipped for
126 : * execution using @code{mapi_query()} and an answer table can be consumed one
127 : * row at a time. In many cases these functions suffice.
128 : *
129 : * The Mapi interface provides caching of rows at the client side.
130 : * @code{mapi_query()} will load tuples into the cache, after which they can be
131 : * read repeatedly using @code{mapi_fetch_row()} or directly accessed
132 : * (@code{mapi_seek_row()}). This facility is particularly handy when small,
133 : * but stable query results are repeatedly used in the client program.
134 : *
135 : * To ease communication between application code and the cache entries,
136 : * the user can bind the C-variables both for input and output to the
137 : * query parameters, and output columns, respectively. The query
138 : * parameters are indicated by '?' and may appear anywhere in the query
139 : * template.
140 : *
141 : * The Mapi library expects complete lines from the server as answers to
142 : * query actions. Incomplete lines leads to Mapi waiting forever on the
143 : * server. Thus formatted printing is discouraged in favor of tabular
144 : * printing as offered by the @code{table.print()} commands.
145 : * @end ifclear
146 : *
147 : * @ifset XQRYmanual
148 : * @node An Example
149 : * @subsection An Example
150 : *
151 : * C and C++ programs can use the MAPI library to execute queries on MonetDB.
152 : *
153 : * We give a short example with a minimal Mapi program:
154 : * @itemize
155 : * @item @code{mapi_connect()} and @code{mapi_disconnect()}: make a connection to a database server (@code{Mapi mid;}).
156 : * @strong{note:} pass the value @code{"sql"} in the @code{language} parameter, when connecting.
157 : * @item @code{mapi_error()} and @code{mapi_error_str()}: check for and print connection errors (on @code{Mapi mid}).
158 : * @item @code{mapi_query()} and @code{mapi_close_handle()} do a query and get a handle to it (@code{MapiHdl hdl}).
159 : * @item @code{mapi_result_error()}: check for query evaluation errors (on @code{MapiHdl hdl}).
160 : * @item @code{mapi_fetch_line()}: get a line of (result or error) output from the server (on @code{MapiHdl hdl}).
161 : * @strong{note:} output lines are prefixed with a @code{'='} character that must be escaped.
162 : * @end itemize
163 : *
164 : * @example
165 : * @verbatim
166 : * #include <stdio.h>
167 : * #include <mapi.h>
168 : * #include <stdlib.h>
169 : *
170 : * int
171 : * main(int argc, char** argv) {
172 : * const char *prog = argv[0];
173 : * const char *host = argv[1]; // where Mserver is started, e.g. localhost
174 : * const char *db = argv[2]; // database name e.g. demo
175 : * int port = atoi(argv[3]); // mapi_port e.g. 50000
176 : * char *mode = argv[4]; // output format e.g. xml
177 : * const char *query = argv[5]; // single-line query e.g. '1+1' (use quotes)
178 : * FILE *fp = stderr;
179 : * char *line;
180 : *
181 : * if (argc != 6) {
182 : * fprintf(fp, "usage: %s <host> <db> <port> <mode> <query>\n", prog);
183 : * fprintf(fp, " e.g. %s localhost demo 50000 xml '1+1'\n", prog);
184 : * } else {
185 : * // CONNECT TO SERVER, default insecure user/password, language="sql"
186 : * Mapi mid = mapi_connect(host, port, "monetdb", "monetdb", "sql", db);
187 : * MapiHdl hdl;
188 : * if (mid == NULL) {
189 : * fprintf(fp, "%s: failed to connect.\n", prog);
190 : * } else {
191 : * hdl = mapi_query(mid, query); // FIRE OFF A QUERY
192 : *
193 : * if (hdl == NULL || mapi_error(mid) != MOK) // CHECK CONNECTION ERROR
194 : * fprintf(fp, "%s: connection error: %s\n", prog, mapi_error_str(mid)); // GET CONNECTION ERROR STRING
195 : * if (hdl) {
196 : * if (mapi_result_error(hdl) != MOK) // CHECK QUERY ERROR
197 : * fprintf(fp, "%s: query error\n", prog);
198 : * else
199 : * fp = stdout; // success: connection&query went ok
200 : *
201 : * // FETCH SERVER QUERY ANSWER LINE-BY-LINE
202 : * while((line = mapi_fetch_line(hdl)) != NULL) {
203 : * if (*line == '=') line++; // XML result lines start with '='
204 : * fprintf(fp, "%s\n", line);
205 : * }
206 : * }
207 : * mapi_close_handle(hdl); // CLOSE QUERY HANDLE
208 : * }
209 : * mapi_disconnect(mid); // CLOSE CONNECTION
210 : * }
211 : * return (fp == stdout)? 0 : -1;
212 : * }
213 : * @end verbatim
214 : * @end example
215 : * @end ifset
216 : *
217 : * The following action is needed to get a working program.
218 : * Compilation of the application relies on the @emph{monetdb-config}
219 : * program shipped with the distribution.
220 : * It localizes the include files and library directories.
221 : * Once properly installed, the application can be compiled and linked as
222 : * follows:
223 : * @example
224 : * @verbatim
225 : * cc sample.c `monetdb-clients-config --cflags --libs` -lmapi -o sample
226 : * ./sample
227 : * @end verbatim
228 : * @end example
229 : *
230 : * It assumes that the dynamic loadable libraries are in public places.
231 : * If, however, the system is installed in your private environment
232 : * then the following option can be used on most ELF platforms.
233 : *
234 : * @example
235 : * @verbatim
236 : * cc sample.c `monetdb-clients-config --cflags --libs` -lmapi -o sample \
237 : * `monetdb-clients-config --libs | sed -e's:-L:-R:g'`
238 : * ./sample
239 : * @end verbatim
240 : * @end example
241 : *
242 : * The compilation on Windows is slightly more complicated. It requires
243 : * more attention towards the location of the include files and libraries.
244 : *
245 : * @ifclear XQRYmanual
246 : * @node Command Summary, Library Synopsis, An Example, The Mapi Library
247 : * @subsection Command Summary
248 : * @end ifclear
249 : * @ifset XQRYmanual
250 : * @node Command Summary
251 : * @subsection Command Summary
252 : * @end ifset
253 : *
254 : * The quick reference guide to the Mapi library is given below. More
255 : * details on their constraints and defaults are given in the next
256 : * section.
257 : *
258 : *
259 : * @multitable @columnfractions 0.25 0.75
260 : * @item mapi_bind() @tab Bind string C-variable to a field
261 : * @item mapi_bind_numeric() @tab Bind numeric C-variable to field
262 : * @item mapi_bind_var() @tab Bind typed C-variable to a field
263 : * @item mapi_cache_freeup() @tab Forcefully shuffle fraction for cache refreshment
264 : * @item mapi_cache_limit() @tab Set the tuple cache limit
265 : * @item mapi_clear_bindings() @tab Clear all field bindings
266 : * @item mapi_clear_params() @tab Clear all parameter bindings
267 : * @item mapi_close_handle() @tab Close query handle and free resources
268 : * @item mapi_connect() @tab Connect to a Mserver
269 : * @item mapi_destroy() @tab Free handle resources
270 : * @item mapi_disconnect() @tab Disconnect from server
271 : * @item mapi_error() @tab Test for error occurrence
272 : * @item mapi_execute() @tab Execute a query
273 : * @item mapi_explain() @tab Display error message and context on stream
274 : * @item mapi_explain_query() @tab Display error message and context on stream
275 : * @item mapi_fetch_all_rows() @tab Fetch all answers from server into cache
276 : * @item mapi_fetch_field() @tab Fetch a field from the current row
277 : * @item mapi_fetch_field_len() @tab Fetch the length of a field from the current row
278 : * @item mapi_fetch_line() @tab Retrieve the next line
279 : * @item mapi_fetch_reset() @tab Set the cache reader to the beginning
280 : * @item mapi_fetch_row() @tab Fetch row of values
281 : * @item mapi_finish() @tab Terminate the current query
282 : * @item mapi_get_dbname() @tab Database being served
283 : * @item mapi_get_field_count() @tab Number of fields in current row
284 : * @item mapi_get_host() @tab Host name of server
285 : * @item mapi_get_query() @tab Query being executed
286 : * @item mapi_get_language() @tab Query language name
287 : * @item mapi_get_mapi_version() @tab Mapi version name
288 : * @item mapi_get_monet_version() @tab MonetDB version name
289 : * @item mapi_get_motd() @tab Get server welcome message
290 : * @item mapi_get_row_count() @tab Number of rows in cache or -1
291 : * @item mapi_get_last_id() @tab last inserted id of an auto_increment (or alike) column
292 : * @item mapi_get_from() @tab Get the stream 'from'
293 : * @item mapi_get_to() @tab Get the stream 'to'
294 : * @item mapi_get_trace() @tab Get trace flag
295 : * @item mapi_get_user() @tab Current user name
296 : * @item mapi_log() @tab Keep log of client/server interaction
297 : * @item mapi_next_result() @tab Go to next result set
298 : * @item mapi_needmore() @tab Return whether more data is needed
299 : * @item mapi_ping() @tab Test server for accessibility
300 : * @item mapi_prepare() @tab Prepare a query for execution
301 : * @item mapi_query() @tab Send a query for execution
302 : * @item mapi_query_handle() @tab Send a query for execution
303 : * @item mapi_quote() @tab Escape characters
304 : * @item mapi_reconnect() @tab Reconnect with a clean session context
305 : * @item mapi_rows_affected() @tab Obtain number of rows changed
306 : * @item mapi_seek_row() @tab Move row reader to specific location in cache
307 : * @item mapi_setAutocommit() @tab Set auto-commit flag
308 : * @item mapi_table() @tab Get current table name
309 : * @item mapi_timeout() @tab Set timeout for long-running queries[TODO]
310 : * @item mapi_trace() @tab Set trace flag
311 : * @item mapi_unquote() @tab remove escaped characters
312 : * @end multitable
313 : *
314 : * @ifclear XQRYmanual
315 : * @node Library Synopsis, Mapi Function Reference, Command Summary, The Mapi Library
316 : * @subsection Library Synopsis
317 : * @end ifclear
318 : * @ifset XQRYmanual
319 : * @node Library Synopsis
320 : * @subsection Library Synopsis
321 : * @end ifset
322 : *
323 : * The routines to build a MonetDB application are grouped in the library
324 : * MonetDB Programming Interface, or shorthand Mapi.
325 : *
326 : * The protocol information is stored in a Mapi interface descriptor
327 : * (mid). This descriptor can be used to ship queries, which return a
328 : * MapiHdl to represent the query answer. The application can set up
329 : * several channels with the same or a different @code{mserver}. It is the
330 : * programmer's responsibility not to mix the descriptors in retrieving
331 : * the results.
332 : *
333 : * The application may be multi-threaded as long as the user respects the
334 : * individual connections represented by the database handlers.
335 : *
336 : * The interface assumes a cautious user, who understands and has
337 : * experience with the query or programming language model. It should also be
338 : * clear that references returned by the API point directly into the
339 : * administrative structures of Mapi. This means that they are valid
340 : * only for a short period, mostly between successive @code{mapi_fetch_row()}
341 : * commands. It also means that it the values are to retained, they have
342 : * to be copied. A defensive programming style is advised.
343 : *
344 : * Upon an error, the routines @code{mapi_explain()} and @code{mapi_explain_query()}
345 : * give information about the context of the failed call, including the
346 : * expression shipped and any response received. The side-effect is
347 : * clearing the error status.
348 : *
349 : * @subsection Error Message
350 : * Almost every call can fail since the connection with the database
351 : * server can fail at any time. Functions that return a handle (either
352 : * @code{Mapi} or @code{MapiHdl}) may return NULL on failure, or they may return the
353 : * handle with the error flag set. If the function returns a non-NULL
354 : * handle, always check for errors with mapi_error.
355 : *
356 : *
357 : * Functions that return MapiMsg indicate success and failure with the
358 : * following codes.
359 : *
360 : * @multitable @columnfractions 0.15 0.7
361 : * @item MOK @tab No error
362 : * @item MERROR @tab Mapi internal error.
363 : * @item MTIMEOUT @tab Error communicating with the server.
364 : * @end multitable
365 : *
366 : * When these functions return MERROR or MTIMEOUT, an explanation of the
367 : * error can be had by calling one of the functions @code{mapi_error_str()},
368 : * @code{mapi_explain()}, or @code{mapi_explain_query()}.
369 : *
370 : * To check for error messages from the server, call @code{mapi_result_error()}.
371 : * This function returns NULL if there was no error, or the error message
372 : * if there was. A user-friendly message can be printed using
373 : * @code{map_explain_result()}. Typical usage is:
374 : * @verbatim
375 : * do {
376 : * if ((error = mapi_result_error(hdl)) != NULL)
377 : * mapi_explain_result(hdl, stderr);
378 : * while ((line = mapi_fetch_line(hdl)) != NULL)
379 : * ; // use output
380 : * } while (mapi_next_result(hdl) == 1);
381 : * @end verbatim
382 : *
383 : * @ifclear XQRYmanual
384 : * @node Mapi Function Reference, The Perl Library , Library Synopsis, The Mapi Library
385 : * @subsection Mapi Function Reference
386 : * @end ifclear
387 : * @ifset XQRYmanual
388 : * @node Mapi Function Reference
389 : * @subsection Mapi Function Reference
390 : * @end ifset
391 : *
392 : * @subsection Connecting and Disconnecting
393 : * @itemize
394 : * @item Mapi mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
395 : *
396 : * Setup a connection with a Mserver at a @emph{host}:@emph{port} and login
397 : * with @emph{username} and @emph{password}. If host == NULL, the local
398 : * host is accessed. If host starts with a '/' and the system supports it,
399 : * host is the directory where should be searched for UNIX domain
400 : * sockets. Port is not ignored, but used to identify which socket to
401 : * use. If port == 0, a default port is used.
402 : * The preferred query language is
403 : * @verb{ { }sql,mal @verb{ } }. On success, the function returns a
404 : * pointer to a structure with administration about the connection.
405 : *
406 : * @item MapiMsg mapi_disconnect(Mapi mid)
407 : *
408 : * Terminate the session described by @emph{mid}. The only possible uses
409 : * of the handle after this call is @emph{mapi_destroy()} and
410 : * @code{mapi_reconnect()}.
411 : * Other uses lead to failure.
412 : *
413 : * @item MapiMsg mapi_destroy(Mapi mid)
414 : *
415 : * Terminate the session described by @emph{ mid} if not already done so,
416 : * and free all resources. The handle cannot be used anymore.
417 : *
418 : * @item MapiMsg mapi_reconnect(Mapi mid)
419 : *
420 : * Close the current channel (if still open) and re-establish a fresh
421 : * connection. This will remove all global session variables.
422 : *
423 : * @item MapiMsg mapi_ping(Mapi mid)
424 : *
425 : * Test availability of the server. Returns zero upon success.
426 : * @end itemize
427 : *
428 : * @subsection Sending Queries
429 : * @itemize
430 : * @item MapiHdl mapi_query(Mapi mid, const char *Command)
431 : *
432 : * Send the Command to the database server represented by mid. This
433 : * function returns a query handle with which the results of the query
434 : * can be retrieved. The handle should be closed with
435 : * @code{mapi_close_handle()}. The command response is buffered for
436 : * consumption, c.f. mapi\_fetch\_row().
437 : *
438 : * @item MapiMsg mapi_query_handle(MapiHdl hdl, const char *Command)
439 : *
440 : * Send the Command to the database server represented by hdl, reusing
441 : * the handle from a previous query. If Command is zero it takes the
442 : * last query string kept around. The command response is buffered for
443 : * consumption, e.g. @code{mapi_fetch_row()}.
444 : *
445 : * @item MapiHdl mapi_prepare(Mapi mid, const char *Command)
446 : *
447 : * Move the query to a newly allocated query handle (which is returned).
448 : * Possibly interact with the back-end to prepare the query for
449 : * execution.
450 : *
451 : * @item MapiMsg mapi_execute(MapiHdl hdl)
452 : *
453 : * Ship a previously prepared command to the backend for execution. A
454 : * single answer is pre-fetched to detect any runtime error. MOK is
455 : * returned upon success.
456 : *
457 : * @item MapiMsg mapi_finish(MapiHdl hdl)
458 : *
459 : * Terminate a query. This routine is used in the rare cases that
460 : * consumption of the tuple stream produced should be prematurely
461 : * terminated. It is automatically called when a new query using the same
462 : * query handle is shipped to the database and when the query handle is
463 : * closed with @code{mapi_close_handle()}.
464 : *
465 : * @subsection Getting Results
466 : * @itemize
467 : * @item int mapi_get_field_count(MapiHdl mid)
468 : *
469 : * Return the number of fields in the current row.
470 : *
471 : * @item int64_t mapi_get_row_count(MapiHdl mid)
472 : *
473 : * If possible, return the number of rows in the last select call. A -1
474 : * is returned if this information is not available.
475 : *
476 : * @item int64_t mapi_get_last_id(MapiHdl mid)
477 : *
478 : * If possible, return the last inserted id of auto_increment (or alike) column.
479 : * A -1 is returned if this information is not available. We restrict this to
480 : * single row inserts and one auto_increment column per table. If the restrictions
481 : * do not hold, the result is unspecified.
482 : *
483 : * @item int64_t mapi_rows_affected(MapiHdl hdl)
484 : *
485 : * Return the number of rows affected by a database update command
486 : * such as SQL's INSERT/DELETE/UPDATE statements.
487 : *
488 : * @item int mapi_fetch_row(MapiHdl hdl)
489 : *
490 : * Retrieve a row from the server. The text retrieved is kept around in
491 : * a buffer linked with the query handle from which selective fields can
492 : * be extracted. It returns the number of fields recognized. A zero is
493 : * returned upon encountering end of sequence or error. This can be
494 : * analyzed in using @code{mapi_error()}.
495 : *
496 : * @item int64_t mapi_fetch_all_rows(MapiHdl hdl)
497 : *
498 : * All rows are cached at the client side first. Subsequent calls to
499 : * @code{mapi_fetch_row()} will take the row from the cache. The number or
500 : * rows cached is returned.
501 : *
502 : * @item MapiMsg mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
503 : *
504 : * Reset the row pointer to the requested row number. If whence is
505 : * @code{MAPI_SEEK_SET}, rownr is the absolute row number (0 being the
506 : * first row); if whence is @code{MAPI_SEEK_CUR}, rownr is relative to the
507 : * current row; if whence is @code{MAPI_SEEK_END}, rownr is relative to
508 : * the last row.
509 : *
510 : * @item MapiMsg mapi_fetch_reset(MapiHdl hdl)
511 : *
512 : * Reset the row pointer to the first line in the cache. This need not
513 : * be a tuple. This is mostly used in combination with fetching all
514 : * tuples at once.
515 : *
516 : * @item char *mapi_fetch_field(MapiHdl hdl, int fnr)
517 : *
518 : * Return a pointer a C-string representation of the value returned. A
519 : * zero is returned upon encountering an error or when the database value
520 : * is NULL; this can be analyzed in using @code{mapi\_error()}.
521 : *
522 : * @item size_t mapi_fetch_field_len(MapiHdl hdl, int fnr)
523 : *
524 : * Return the length of the C-string representation excluding trailing NULL
525 : * byte of the value. Zero is returned upon encountering an error, when the
526 : * database value is NULL, of when the string is the empty string. This can
527 : * be analyzed by using @code{mapi\_error()} and @code{mapi\_fetch\_field()}.
528 : *
529 : * @item MapiMsg mapi_next_result(MapiHdl hdl)
530 : *
531 : * Go to the next result set, discarding the rest of the output of the
532 : * current result set.
533 : * @end itemize
534 : *
535 : * @subsection Errors
536 : * @itemize
537 : * @item MapiMsg mapi_error(Mapi mid)
538 : *
539 : * Return the last error code or 0 if there is no error.
540 : *
541 : * @item char *mapi_error_str(Mapi mid)
542 : *
543 : * Return a pointer to the last error message.
544 : *
545 : * @item char *mapi_result_error(MapiHdl hdl)
546 : *
547 : * Return a pointer to the last error message from the server.
548 : *
549 : * @item void mapi_explain(Mapi mid, FILE *fd)
550 : *
551 : * Write the error message obtained from @code{mserver} to a file.
552 : *
553 : * @item void mapi_explain_query(MapiHdl hdl, FILE *fd)
554 : *
555 : * Write the error message obtained from @code{mserver} to a file.
556 : *
557 : * @item void mapi_explain_result(MapiHdl hdl, FILE *fd)
558 : *
559 : * Write the error message obtained from @code{mserver} to a file.
560 : * @end itemize
561 : *
562 : * @subsection Parameters
563 : *
564 : * @itemize
565 : * @item MapiMsg mapi_bind(MapiHdl hdl, int fldnr, char **val)
566 : *
567 : * Bind a string variable with a field in the return table. Upon a
568 : * successful subsequent @code{mapi\_fetch\_row()} the indicated field is stored
569 : * in the space pointed to by val. Returns an error if the field
570 : * identified does not exist.
571 : *
572 : * @item MapiMsg mapi_bind_var(MapiHdl hdl, int fldnr, int type, void *val)
573 : *
574 : * Bind a variable to a field in the return table. Upon a successful
575 : * subsequent @code{mapi\_fetch\_row()}, the indicated field is converted to the
576 : * given type and stored in the space pointed to by val. The types
577 : * recognized are @verb{ { } @code{MAPI\_TINY, MAPI\_UTINY, MAPI\_SHORT, MAPI\_USHORT,
578 : * MAPI_INT, MAPI_UINT, MAPI_LONG, MAPI_ULONG, MAPI_LONGLONG,
579 : * MAPI_ULONGLONG, MAPI_CHAR, MAPI_VARCHAR, MAPI_FLOAT, MAPI_DOUBLE,
580 : * MAPI_DATE, MAPI_TIME, MAPI_DATETIME} @verb{ } }. The binding operations
581 : * should be performed after the mapi_execute command. Subsequently all
582 : * rows being fetched also involve delivery of the field values in the
583 : * C-variables using proper conversion. For variable length strings a
584 : * pointer is set into the cache.
585 : *
586 : * @item MapiMsg mapi_bind_numeric(MapiHdl hdl, int fldnr, int scale, int precision, void *val)
587 : *
588 : * Bind to a numeric variable, internally represented by MAPI_INT
589 : * Describe the location of a numeric parameter in a query template.
590 : *
591 : * @item MapiMsg mapi_clear_bindings(MapiHdl hdl)
592 : *
593 : * Clear all field bindings.
594 : *
595 : * @item MapiMsg mapi_param(MapiHdl hdl, int fldnr, char **val)
596 : *
597 : * Bind a string variable with the n-th placeholder in the query
598 : * template. No conversion takes place.
599 : *
600 : * @item MapiMsg mapi_param_type(MapiHdl hdl, int fldnr, int ctype, int sqltype, void *val)
601 : *
602 : * Bind a variable whose type is described by ctype to a parameter whose
603 : * type is described by sqltype.
604 : *
605 : * @item MapiMsg mapi_param_numeric(MapiHdl hdl, int fldnr, int scale, int precision, void *val)
606 : *
607 : * Bind to a numeric variable, internally represented by MAPI_INT.
608 : *
609 : * @item MapiMsg mapi_param_string(MapiHdl hdl, int fldnr, int sqltype, char *val, int *sizeptr)
610 : *
611 : * Bind a string variable, internally represented by MAPI_VARCHAR, to a
612 : * parameter. The sizeptr parameter points to the length of the string
613 : * pointed to by val. If sizeptr == NULL or *sizeptr == -1, the string
614 : * is NULL-terminated.
615 : *
616 : * @item MapiMsg mapi_clear_params(MapiHdl hdl)
617 : *
618 : * Clear all parameter bindings.
619 : * @end itemize
620 : *
621 : * @subsection Miscellaneous
622 : * @itemize
623 : * @item MapiMsg mapi_setAutocommit(Mapi mid, bool autocommit)
624 : *
625 : * Set the autocommit flag (default is on). This only has an effect
626 : * when the language is SQL. In that case, the server commits after each
627 : * statement sent to the server.
628 : *
629 : * @item MapiMsg mapi_cache_limit(Mapi mid, int maxrows)
630 : *
631 : * A limited number of tuples are pre-fetched after each @code{execute()}. If
632 : * maxrows is negative, all rows will be fetched before the application
633 : * is permitted to continue. Once the cache is filled, a number of tuples
634 : * are shuffled to make room for new ones, but taking into account
635 : * non-read elements. Filling the cache quicker than reading leads to an
636 : * error.
637 : *
638 : * @item MapiMsg mapi_cache_freeup(MapiHdl hdl, int percentage)
639 : *
640 : * Forcefully shuffle the cache making room for new rows. It ignores the
641 : * read counter, so rows may be lost.
642 : *
643 : * @item char * mapi_quote(const char *str, int size)
644 : *
645 : * Escape special characters such as @code{\n}, @code{\t} in str with
646 : * backslashes. The returned value is a newly allocated string which
647 : * should be freed by the caller.
648 : *
649 : * @item char * mapi_unquote(const char *name)
650 : *
651 : * The reverse action of @code{mapi_quote()}, turning the database
652 : * representation into a C-representation. The storage space is
653 : * dynamically created and should be freed after use.
654 : *
655 : * @item MapiMsg mapi_trace(Mapi mid, bool flag)
656 : *
657 : * Set the trace flag to monitor interaction of the client
658 : * with the library. It is primarilly used for debugging
659 : * Mapi applications.
660 : *
661 : * @item int mapi_get_trace(Mapi mid)
662 : *
663 : * Return the current value of the trace flag.
664 : *
665 : * @item MapiMsg mapi\_log(Mapi mid, const char *fname)
666 : *
667 : * Log the interaction between the client and server for offline
668 : * inspection. Beware that the log file overwrites any previous log.
669 : * For detailed interaction trace with the Mapi library itself use mapi\_trace().
670 : * @end itemize
671 : * The remaining operations are wrappers around the data structures
672 : * maintained. Note that column properties are derived from the table
673 : * output returned from the server.
674 : * @itemize
675 : * @item char *mapi_get_name(MapiHdl hdl, int fnr)
676 : * @item char *mapi_get_type(MapiHdl hdl, int fnr)
677 : * @item char *mapi_get_table(MapiHdl hdl, int fnr)
678 : * @item int mapi_get_len(Mapi mid, int fnr)
679 : *
680 : * @item const char *mapi_get_dbname(Mapi mid)
681 : * @item const char *mapi_get_host(Mapi mid)
682 : * @item const char *mapi_get_user(Mapi mid)
683 : * @item const char *mapi_get_lang(Mapi mid)
684 : * @item const char *mapi_get_motd(Mapi mid)
685 : *
686 : * @end itemize
687 : * @- Implementation
688 : */
689 :
690 : #include "monetdb_config.h"
691 : #include "stream.h" /* include before mapi.h */
692 : #include "stream_socket.h"
693 : #include "mapi.h"
694 : #include "mapi_prompt.h"
695 : #include "mcrypt.h"
696 : #include "matomic.h"
697 : #include "mstring.h"
698 : #include "mutils.h"
699 :
700 : #include "mapi_intern.h"
701 :
702 : #ifndef INVALID_SOCKET
703 : #define INVALID_SOCKET (-1)
704 : #endif
705 :
706 : #define MAPIBLKSIZE 256 /* minimum buffer shipped */
707 :
708 : #define MAPI_AUTO 0 /* automatic type detection */
709 : #define MAPI_TINY 1
710 : #define MAPI_UTINY 2
711 : #define MAPI_SHORT 3
712 : #define MAPI_USHORT 4
713 : #define MAPI_INT 5
714 : #define MAPI_UINT 6
715 : #define MAPI_LONG 7
716 : #define MAPI_ULONG 8
717 : #define MAPI_LONGLONG 9
718 : #define MAPI_ULONGLONG 10
719 : #define MAPI_CHAR 11
720 : #define MAPI_VARCHAR 12
721 : #define MAPI_FLOAT 13
722 : #define MAPI_DOUBLE 14
723 : #define MAPI_DATE 15
724 : #define MAPI_TIME 16
725 : #define MAPI_DATETIME 17
726 : #define MAPI_NUMERIC 18
727 :
728 : #define PLACEHOLDER '?'
729 :
730 :
731 : #ifdef DEBUG
732 : #define debugprint(fmt,arg) printf(fmt,arg)
733 : #else
734 : #define debugprint(fmt,arg) ((void) 0)
735 : #endif
736 :
737 : /*
738 : * All external calls to the library should pass the mapi-check
739 : * routine. It assures a working connection and proper reset of
740 : * the error status of the Mapi structure.
741 : */
742 : #define mapi_check(X) \
743 : do { \
744 : debugprint("entering %s\n", __func__); \
745 : assert(X); \
746 : if (!(X)->connected) { \
747 : mapi_setError((X), "Connection lost", \
748 : __func__, MERROR); \
749 : return (X)->error; \
750 : } \
751 : mapi_clrError(X); \
752 : } while (0)
753 : #define mapi_check0(X) \
754 : do { \
755 : debugprint("entering %s\n", __func__); \
756 : assert(X); \
757 : if (!(X)->connected) { \
758 : mapi_setError((X), "Connection lost", \
759 : __func__, MERROR); \
760 : return 0; \
761 : } \
762 : mapi_clrError(X); \
763 : } while (0)
764 : #define mapi_hdl_check(X) \
765 : do { \
766 : debugprint("entering %s\n", __func__); \
767 : assert(X); \
768 : assert((X)->mid); \
769 : if (!(X)->mid->connected) { \
770 : mapi_setError((X)->mid, "Connection lost", \
771 : __func__, MERROR); \
772 : return (X)->mid->error; \
773 : } \
774 : mapi_clrError((X)->mid); \
775 : } while (0)
776 : #define mapi_hdl_check0(X) \
777 : do { \
778 : debugprint("entering %s\n", __func__); \
779 : assert(X); \
780 : assert((X)->mid); \
781 : if (!(X)->mid->connected) { \
782 : mapi_setError((X)->mid, "Connection lost", \
783 : __func__, MERROR); \
784 : return 0; \
785 : } \
786 : mapi_clrError((X)->mid); \
787 : } while (0)
788 :
789 : static int mapi_extend_bindings(MapiHdl hdl, int minbindings);
790 : static int mapi_extend_params(MapiHdl hdl, int minparams);
791 : static int unquote(const char *msg, char **start, const char **next, int endchar, size_t *lenp);
792 : static int mapi_slice_row(struct MapiResultSet *result, int cr);
793 : static void mapi_store_bind(struct MapiResultSet *result, int cr);
794 :
795 : static ATOMIC_FLAG mapi_initialized = ATOMIC_FLAG_INIT;
796 :
797 : /*
798 : * Blocking
799 : * --------
800 : *
801 : * The server side code works with a common/stream package, a fast
802 : * buffered IO scheme. Nowadays this should be the only protocol used,
803 : * while historical uses were line-based instead.
804 : *
805 : *
806 : * Error Handling
807 : * --------------
808 : *
809 : * All externally visible functions should first call mapi_clrError (usually
810 : * though a call to one of the check macros above) to clear the error flag.
811 : * When an error is detected, the library calls mapi_setError to set the error
812 : * flag. The application can call mapi_error or mapi_error_str to check for
813 : * errors, and mapi_explain or mapi_explain_query to print a formatted error
814 : * report.
815 : */
816 : char mapi_nomem[] = "Memory allocation failed";
817 :
818 : void
819 15185845 : mapi_clrError(Mapi mid)
820 : {
821 15185845 : assert(mid);
822 15185845 : if (mid->errorstr && mid->errorstr != mapi_nomem)
823 1031 : free(mid->errorstr);
824 15185845 : mid->action = 0; /* contains references to constants */
825 15185845 : mid->error = 0;
826 15185845 : mid->errorstr = 0;
827 15185845 : }
828 :
829 : MapiMsg
830 1023 : mapi_setError(Mapi mid, const char *msg, const char *action, MapiMsg error)
831 : {
832 1023 : assert(msg);
833 1023 : REALLOC(mid->errorstr, strlen(msg) + 1);
834 1023 : if (mid->errorstr == NULL)
835 0 : mid->errorstr = mapi_nomem;
836 : else
837 1023 : strcpy(mid->errorstr, msg);
838 1023 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
839 1023 : mid->error = error;
840 1023 : mid->action = action;
841 1023 : return mid->error;
842 : }
843 :
844 34 : MapiMsg mapi_printError(Mapi mid, const char *action, MapiMsg error, const char *fmt, ...)
845 : {
846 34 : size_t size = 81; // just a guess
847 :
848 62 : while (1) {
849 48 : REALLOC(mid->errorstr, size);
850 48 : if (mid->errorstr == NULL) {
851 0 : mid->errorstr = mapi_nomem;
852 0 : break;
853 : }
854 48 : va_list ap;
855 48 : va_start(ap, fmt);
856 48 : int n = vsnprintf(mid->errorstr, size, fmt, ap);
857 48 : va_end(ap);
858 48 : if (n < 0) {
859 : // is it even possible for vsnprintf to fail?
860 : break;
861 : }
862 48 : if ((size_t)n < size) {
863 : // it fit
864 : break;
865 : } else {
866 : // try again larger
867 14 : size = (size_t)n + 1;
868 : }
869 : }
870 34 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
871 34 : mid->error = error;
872 34 : mid->action = action;
873 34 : return mid->error;
874 : }
875 :
876 : MapiMsg
877 195878 : mapi_error(Mapi mid)
878 : {
879 195878 : assert(mid);
880 195878 : return mid->error;
881 : }
882 :
883 : const char *
884 28 : mapi_error_str(Mapi mid)
885 : {
886 28 : assert(mid);
887 28 : return mid->errorstr;
888 : }
889 :
890 : #ifdef _MSC_VER
891 : static const struct {
892 : int e;
893 : const char *m;
894 : } wsaerrlist[] = {
895 : { WSA_INVALID_HANDLE, "Specified event object handle is invalid" },
896 : { WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available" },
897 : { WSA_INVALID_PARAMETER, "One or more parameters are invalid" },
898 : { WSA_OPERATION_ABORTED, "Overlapped operation aborted" },
899 : { WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state" },
900 : { WSA_IO_PENDING, "Overlapped operations will complete later" },
901 : { WSAEINTR, "Interrupted function call" },
902 : { WSAEBADF, "File handle is not valid" },
903 : { WSAEACCES, "Permission denied" },
904 : { WSAEFAULT, "Bad address" },
905 : { WSAEINVAL, "Invalid argument" },
906 : { WSAEMFILE, "Too many open files" },
907 : { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
908 : { WSAEINPROGRESS, "Operation now in progress" },
909 : { WSAEALREADY, "Operation already in progress" },
910 : { WSAENOTSOCK, "Socket operation on nonsocket" },
911 : { WSAEDESTADDRREQ, "Destination address required" },
912 : { WSAEMSGSIZE, "Message too long" },
913 : { WSAEPROTOTYPE, "Protocol wrong type for socket" },
914 : { WSAENOPROTOOPT, "Bad protocol option" },
915 : { WSAEPROTONOSUPPORT, "Protocol not supported" },
916 : { WSAESOCKTNOSUPPORT, "Socket type not supported" },
917 : { WSAEOPNOTSUPP, "Operation not supported" },
918 : { WSAEPFNOSUPPORT, "Protocol family not supported" },
919 : { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
920 : { WSAEADDRINUSE, "Address already in use" },
921 : { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
922 : { WSAENETDOWN, "Network is down" },
923 : { WSAENETUNREACH, "Network is unreachable" },
924 : { WSAENETRESET, "Network dropped connection on reset" },
925 : { WSAECONNABORTED, "Software caused connection abort" },
926 : { WSAECONNRESET, "Connection reset by peer" },
927 : { WSAENOBUFS, "No buffer space available" },
928 : { WSAEISCONN, "Socket is already connected" },
929 : { WSAENOTCONN, "Socket is not connected" },
930 : { WSAESHUTDOWN, "Cannot send after socket shutdown" },
931 : { WSAETOOMANYREFS, "Too many references" },
932 : { WSAETIMEDOUT, "Connection timed out" },
933 : { WSAECONNREFUSED, "Connection refused" },
934 : { WSAELOOP, "Cannot translate name" },
935 : { WSAENAMETOOLONG, "Name too long" },
936 : { WSAEHOSTDOWN, "Host is down" },
937 : { WSAEHOSTUNREACH, "No route to host" },
938 : { WSAENOTEMPTY, "Directory not empty" },
939 : { WSAEPROCLIM, "Too many processes" },
940 : { WSAEUSERS, "User quota exceeded" },
941 : { WSAEDQUOT, "Disk quota exceeded" },
942 : { WSAESTALE, "Stale file handle reference" },
943 : { WSAEREMOTE, "Item is remote" },
944 : { WSASYSNOTREADY, "Network subsystem is unavailable" },
945 : { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" },
946 : { WSANOTINITIALISED, "Successful WSAStartup not yet performed" },
947 : { WSAEDISCON, "Graceful shutdown in progress" },
948 : { WSAENOMORE, "No more results" },
949 : { WSAECANCELLED, "Call has been canceled" },
950 : { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" },
951 : { WSAEINVALIDPROVIDER, "Service provider is invalid" },
952 : { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" },
953 : { WSASYSCALLFAILURE, "System call failure" },
954 : { WSASERVICE_NOT_FOUND, "Service not found" },
955 : { WSATYPE_NOT_FOUND, "Class type not found" },
956 : { WSA_E_NO_MORE, "No more results" },
957 : { WSA_E_CANCELLED, "Call was canceled" },
958 : { WSAEREFUSED, "Database query was refused" },
959 : { WSAHOST_NOT_FOUND, "Host not found" },
960 : { WSATRY_AGAIN, "Nonauthoritative host not found" },
961 : { WSANO_RECOVERY, "This is a nonrecoverable error" },
962 : { WSANO_DATA, "Valid name, no data record of requested type" },
963 : { WSA_QOS_RECEIVERS, "QOS receivers" },
964 : { WSA_QOS_SENDERS, "QOS senders" },
965 : { WSA_QOS_NO_SENDERS, "No QOS senders" },
966 : { WSA_QOS_NO_RECEIVERS, "QOS no receivers" },
967 : { WSA_QOS_REQUEST_CONFIRMED, "QOS request confirmed" },
968 : { WSA_QOS_ADMISSION_FAILURE, "QOS admission error" },
969 : { WSA_QOS_POLICY_FAILURE, "QOS policy failure" },
970 : { WSA_QOS_BAD_STYLE, "QOS bad style" },
971 : { WSA_QOS_BAD_OBJECT, "QOS bad object" },
972 : { WSA_QOS_TRAFFIC_CTRL_ERROR, "QOS traffic control error" },
973 : { WSA_QOS_GENERIC_ERROR, "QOS generic error" },
974 : { WSA_QOS_ESERVICETYPE, "QOS service type error" },
975 : { WSA_QOS_EFLOWSPEC, "QOS flowspec error" },
976 : { WSA_QOS_EPROVSPECBUF, "Invalid QOS provider buffer" },
977 : { WSA_QOS_EFILTERSTYLE, "Invalid QOS filter style" },
978 : { WSA_QOS_EFILTERTYPE, "Invalid QOS filter type" },
979 : { WSA_QOS_EFILTERCOUNT, "Incorrect QOS filter count" },
980 : { WSA_QOS_EOBJLENGTH, "Invalid QOS object length" },
981 : { WSA_QOS_EFLOWCOUNT, "Incorrect QOS flow count" },
982 : { WSA_QOS_EUNKOWNPSOBJ, "Unrecognized QOS object" },
983 : { WSA_QOS_EPOLICYOBJ, "Invalid QOS policy object" },
984 : { WSA_QOS_EFLOWDESC, "Invalid QOS flow descriptor" },
985 : { WSA_QOS_EPSFLOWSPEC, "Invalid QOS provider-specific flowspec" },
986 : { WSA_QOS_EPSFILTERSPEC, "Invalid QOS provider-specific filterspec" },
987 : { WSA_QOS_ESDMODEOBJ, "Invalid QOS shape discard mode object" },
988 : { WSA_QOS_ESHAPERATEOBJ, "Invalid QOS shaping rate object" },
989 : { WSA_QOS_RESERVED_PETYPE, "Reserved policy QOS element type" },
990 : };
991 : const char *
992 : wsaerror(int err)
993 : {
994 : int i;
995 :
996 : for (i = 0; i < NELEM(wsaerrlist); i++)
997 : if (wsaerrlist[i].e == err)
998 : return wsaerrlist[i].m;
999 : return "Unknown error";
1000 : }
1001 : #endif
1002 :
1003 : static void
1004 0 : clean_print(char *msg, const char *prefix, FILE *fd)
1005 : {
1006 0 : size_t len = strlen(prefix);
1007 :
1008 0 : while (msg && *msg) {
1009 : /* cut by line */
1010 0 : char *p = strchr(msg, '\n');
1011 :
1012 0 : if (p)
1013 0 : *p++ = 0;
1014 :
1015 : /* skip over prefix */
1016 0 : if (strncmp(msg, prefix, len) == 0)
1017 0 : msg += len;
1018 :
1019 : /* output line */
1020 0 : fputs(msg, fd);
1021 0 : fputc('\n', fd);
1022 0 : msg = p;
1023 : }
1024 0 : }
1025 :
1026 : static void
1027 60 : indented_print(const char *msg, const char *prefix, FILE *fd)
1028 : {
1029 : /* for multiline error messages, indent all subsequent
1030 : lines with the space it takes to print "ERROR = " */
1031 60 : const char *s = prefix, *p = msg, *q;
1032 60 : const int len = (int) strlen(s);
1033 60 : const char t = s[len - 1];
1034 :
1035 98 : while (p && *p) {
1036 65 : fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
1037 65 : s = "";
1038 :
1039 65 : q = strchr(p, '\n');
1040 65 : if (q) {
1041 38 : q++; /* also print the newline */
1042 38 : fprintf(fd, "%.*s", (int) (q - p), p);
1043 : } else {
1044 : /* print bit after last newline,
1045 : adding one ourselves */
1046 27 : fprintf(fd, "%s\n", p);
1047 27 : break; /* nothing more to do */
1048 : }
1049 38 : p = q;
1050 : }
1051 60 : }
1052 :
1053 : void
1054 20 : mapi_noexplain(Mapi mid, const char *errorprefix)
1055 : {
1056 20 : assert(mid);
1057 20 : if (mid->noexplain)
1058 0 : free(mid->noexplain);
1059 20 : mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
1060 20 : }
1061 :
1062 : void
1063 0 : mapi_explain(Mapi mid, FILE *fd)
1064 : {
1065 0 : assert(mid);
1066 0 : if (mid->noexplain == NULL) {
1067 0 : const char *host = msetting_string(mid->settings, MP_HOST);
1068 0 : const char *user = msetting_string(mid->settings, MP_USER);
1069 0 : int port = msetting_long(mid->settings, MP_PORT);
1070 0 : if (host[0] == '/')
1071 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1072 : else
1073 0 : fprintf(fd, "MAPI = %s@%s:%d\n",
1074 : user, host, port);
1075 0 : if (mid->action)
1076 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1077 0 : if (mid->errorstr)
1078 0 : indented_print(mid->errorstr, "ERROR = !", fd);
1079 0 : } else if (mid->errorstr) {
1080 0 : clean_print(mid->errorstr, mid->noexplain, fd);
1081 : }
1082 0 : fflush(fd);
1083 0 : mapi_clrError(mid);
1084 0 : }
1085 :
1086 : void
1087 0 : mapi_explain_query(MapiHdl hdl, FILE *fd)
1088 : {
1089 0 : Mapi mid;
1090 :
1091 0 : assert(hdl);
1092 0 : mid = hdl->mid;
1093 0 : assert(mid);
1094 0 : if (mid->noexplain == NULL) {
1095 0 : const char *host = msetting_string(mid->settings, MP_HOST);
1096 0 : const char *user = msetting_string(mid->settings, MP_USER);
1097 0 : int port = msetting_long(mid->settings, MP_PORT);
1098 0 : if (host[0] == '/')
1099 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1100 : else
1101 0 : fprintf(fd, "MAPI = %s@%s:%d\n",
1102 : user, host, port);
1103 0 : if (mid->action)
1104 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1105 0 : if (hdl->query)
1106 0 : indented_print(hdl->query, "QUERY = ", fd);
1107 0 : if (mid->errorstr)
1108 0 : indented_print(mid->errorstr, "ERROR = !", fd);
1109 0 : } else if (mid->errorstr) {
1110 0 : clean_print(mid->errorstr, mid->noexplain, fd);
1111 : }
1112 0 : fflush(fd);
1113 0 : mapi_clrError(mid);
1114 0 : }
1115 :
1116 : void
1117 20 : mapi_explain_result(MapiHdl hdl, FILE *fd)
1118 : {
1119 20 : Mapi mid;
1120 :
1121 20 : if (hdl == NULL ||
1122 20 : hdl->result == NULL ||
1123 20 : hdl->result->errorstr == NULL)
1124 : return;
1125 20 : assert(hdl);
1126 20 : assert(hdl->result);
1127 20 : assert(hdl->result->errorstr);
1128 20 : mid = hdl->mid;
1129 20 : assert(mid);
1130 20 : if (mid->noexplain == NULL) {
1131 20 : const char *host = msetting_string(mid->settings, MP_HOST);
1132 20 : const char *user = msetting_string(mid->settings, MP_USER);
1133 20 : int port = msetting_long(mid->settings, MP_PORT);
1134 20 : if (host[0] == '/')
1135 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1136 : else
1137 20 : fprintf(fd, "MAPI = %s@%s:%d\n",
1138 : user, host, port);
1139 20 : if (mid->action)
1140 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1141 20 : if (hdl->query)
1142 20 : indented_print(hdl->query, "QUERY = ", fd);
1143 20 : indented_print(hdl->result->errorstr, "ERROR = !", fd);
1144 20 : if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
1145 20 : indented_print(hdl->result->sqlstate, "CODE = ", fd);
1146 : } else {
1147 0 : clean_print(hdl->result->errorstr, mid->noexplain, fd);
1148 : }
1149 20 : fflush(fd);
1150 : }
1151 :
1152 : stream *
1153 2834 : mapi_get_to(Mapi mid)
1154 : {
1155 2834 : mapi_check0(mid);
1156 2832 : return mid->to;
1157 : }
1158 :
1159 : stream *
1160 1417 : mapi_get_from(Mapi mid)
1161 : {
1162 1417 : mapi_check0(mid);
1163 1418 : return mid->from;
1164 : }
1165 :
1166 : bool
1167 0 : mapi_get_trace(Mapi mid)
1168 : {
1169 0 : mapi_check0(mid);
1170 0 : return mid->trace;
1171 : }
1172 :
1173 : bool
1174 0 : mapi_get_autocommit(Mapi mid)
1175 : {
1176 0 : mapi_check0(mid);
1177 0 : return msetting_bool(mid->settings, MP_AUTOCOMMIT);
1178 : }
1179 :
1180 : bool
1181 0 : mapi_get_columnar_protocol(Mapi mid)
1182 : {
1183 0 : mapi_check0(mid);
1184 0 : return mid->columnar_protocol;
1185 : }
1186 :
1187 : int
1188 0 : mapi_get_time_zone(Mapi mid)
1189 : {
1190 0 : mapi_check0(mid);
1191 0 : return msetting_long(mid->settings, MP_TIMEZONE);
1192 : }
1193 :
1194 : static int64_t
1195 104 : usec(void)
1196 : {
1197 : #ifdef HAVE_GETTIMEOFDAY
1198 104 : struct timeval tp;
1199 :
1200 104 : gettimeofday(&tp, NULL);
1201 104 : return ((int64_t) tp.tv_sec) * 1000000 + (int64_t) tp.tv_usec;
1202 : #else
1203 : #ifdef HAVE_FTIME
1204 : struct timeb tb;
1205 :
1206 : ftime(&tb);
1207 : return ((int64_t) tb.time) * 1000000 + ((int64_t) tb.millitm) * 1000;
1208 : #endif
1209 : #endif
1210 : }
1211 :
1212 : static void
1213 104 : mapi_log_header(Mapi mid, const char *funcname, long line, const char *mark1, const char *mark2)
1214 : {
1215 104 : int64_t now = usec();
1216 104 : static int64_t firstcall = 0;
1217 104 : if (firstcall == 0)
1218 15 : firstcall = now;
1219 104 : double seconds = (double)(now - firstcall) / 1e6;
1220 104 : mnstr_printf(mid->tracelog, "\n** [%u] t=%.3fs %s%s %s(), line %ld\n", mid->index, seconds, mark1, mark2, funcname, line);
1221 104 : }
1222 :
1223 : void
1224 80 : mapi_impl_log_record(Mapi mid, const char *funcname, long line, const char *mark, const char *fmt, ...)
1225 : {
1226 80 : va_list ap;
1227 :
1228 80 : if (mid->tracelog == NULL)
1229 0 : return;
1230 :
1231 : size_t needed = 128;
1232 81 : size_t to_print;
1233 82 : while (1) {
1234 81 : if (mid->tracebuffersize < needed) {
1235 16 : free(mid->tracebuffer);
1236 16 : mid->tracebuffer = malloc(needed);
1237 16 : if (mid->tracebuffer) {
1238 16 : mid->tracebuffersize = needed;
1239 : } else {
1240 0 : mid->tracebuffersize = 0;
1241 0 : to_print = 0;
1242 0 : break;
1243 : }
1244 : }
1245 81 : va_start(ap, fmt);
1246 81 : int n = vsnprintf(mid->tracebuffer, mid->tracebuffersize, fmt, ap);
1247 81 : va_end(ap);
1248 81 : if (n < 0) {
1249 : to_print = 0;
1250 : break;
1251 : }
1252 81 : if ((size_t)n < mid->tracebuffersize) {
1253 : to_print = n;
1254 : break;
1255 : }
1256 : // need to reallocate
1257 1 : needed = n + 1;
1258 : }
1259 :
1260 80 : mapi_log_header(mid, funcname, line, mark, "");
1261 80 : if (to_print > 0) {
1262 80 : mnstr_write(mid->tracelog, mid->tracebuffer, to_print, 1);
1263 80 : mnstr_writeChr(mid->tracelog, '\n');
1264 : }
1265 80 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1266 : }
1267 :
1268 : #define MAPILOG_OPEN "\tb'"
1269 : #define MAPILOG_CLOSE "'"
1270 :
1271 : void
1272 24 : mapi_impl_log_data(Mapi mid, const char *filename, long line, const char *mark, const char *start, size_t len)
1273 : {
1274 24 : const char hexdigits[] = "0123456789abcdef";
1275 24 : if (mid->tracelog == NULL)
1276 0 : return;
1277 :
1278 24 : mapi_log_header(mid, filename, line, mark, " (DATA)");
1279 :
1280 24 : const size_t margin = strlen("\\xNN" MAPILOG_CLOSE "\n," MAPILOG_OPEN);
1281 24 : char buffer[128] = { 0 };
1282 24 : char *pos = buffer;
1283 24 : mnstr_writeStr(mid->tracelog, MAPILOG_OPEN);
1284 24 : bool inside = true;
1285 1338 : for (unsigned char *p = (unsigned char*)start; (char*)p < start + len; p++) {
1286 1314 : unsigned char c = *p;
1287 1314 : if (!inside) {
1288 0 : for (char *text = "\n" MAPILOG_OPEN; *text; text++)
1289 0 : *pos++ = *text;
1290 : inside = true;
1291 : }
1292 1314 : if (pos >= buffer + sizeof(buffer) - margin) {
1293 0 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1294 0 : pos = buffer;
1295 : }
1296 1314 : switch (c) {
1297 0 : case '\\':
1298 : case '\'':
1299 0 : *pos++ = '\\';
1300 0 : *pos++ = c;
1301 0 : break;
1302 0 : case '\t':
1303 0 : *pos++ = '\\';
1304 0 : *pos++ = 't';
1305 0 : break;
1306 : case '\n':
1307 24 : for (char *text = "\\n" MAPILOG_CLOSE; *text; text++)
1308 18 : *pos++ = *text;
1309 : inside = false;
1310 : break;
1311 1308 : default:
1312 1308 : if (c >= 32 && c < 127) {
1313 1308 : *pos++ = c;
1314 : } else {
1315 0 : *pos++ = '\\';
1316 0 : *pos++ = 'x';
1317 0 : *pos++ = hexdigits[c / 16];
1318 0 : *pos++ = hexdigits[c % 16];
1319 : }
1320 : break;
1321 : }
1322 : }
1323 24 : if (inside) {
1324 36 : for (char *text = MAPILOG_CLOSE; *text; text++)
1325 18 : *pos++ = *text;
1326 : }
1327 24 : *pos++ = ',';
1328 24 : *pos++ = '\n';
1329 24 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1330 :
1331 24 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1332 : }
1333 :
1334 : MapiMsg
1335 15 : mapi_log(Mapi mid, const char *nme)
1336 : {
1337 15 : mapi_clrError(mid);
1338 15 : if (mid->tracelog) {
1339 0 : close_stream(mid->tracelog);
1340 0 : mid->tracelog = NULL;
1341 : }
1342 15 : if (nme == NULL)
1343 : return MOK;
1344 15 : if (nme[0] == '-' && nme[1] == '\0')
1345 0 : mid->tracelog = stderr_wastream();
1346 : else
1347 15 : mid->tracelog = open_wastream(nme);
1348 15 : if (mid->tracelog == NULL || mnstr_errnr(mid->tracelog) != MNSTR_NO__ERROR) {
1349 0 : if (mid->tracelog)
1350 0 : close_stream(mid->tracelog);
1351 0 : mid->tracelog = NULL;
1352 0 : return mapi_setError(mid, "Could not create log file", __func__, MERROR);
1353 : }
1354 : return MOK;
1355 : }
1356 :
1357 : /* send a dummy request to the server to see whether the connection is
1358 : still alive */
1359 : MapiMsg
1360 0 : mapi_ping(Mapi mid)
1361 : {
1362 0 : mapi_check(mid);
1363 :
1364 0 : MapiHdl hdl;
1365 0 : if (msettings_lang_is_sql(mid->settings))
1366 0 : hdl = mapi_query(mid, "select true;");
1367 0 : else if (msettings_lang_is_mal(mid->settings))
1368 0 : hdl = mapi_query(mid, "io.print(1);");
1369 : else
1370 : hdl = NULL;
1371 :
1372 0 : if (hdl)
1373 0 : mapi_close_handle(hdl);
1374 0 : return mid->error;
1375 : }
1376 :
1377 : /* allocate a new structure to represent a result set */
1378 : static struct MapiResultSet *
1379 33537 : new_result(MapiHdl hdl)
1380 : {
1381 33537 : struct MapiResultSet *result;
1382 :
1383 33537 : assert((hdl->lastresult == NULL && hdl->result == NULL) ||
1384 : (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
1385 :
1386 33537 : if (hdl->mid->trace)
1387 0 : printf("allocating new result set\n");
1388 : /* append a newly allocated struct to the end of the linked list */
1389 33537 : result = malloc(sizeof(*result));
1390 33537 : if (result == NULL)
1391 : return NULL;
1392 67074 : *result = (struct MapiResultSet) {
1393 : .hdl = hdl,
1394 : .tableid = -1,
1395 : .querytype = -1,
1396 : .last_id = -1,
1397 33537 : .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
1398 : .cache.reader = -1,
1399 : .commentonly = true,
1400 : };
1401 33537 : if (hdl->lastresult == NULL)
1402 33482 : hdl->result = hdl->lastresult = result;
1403 : else {
1404 55 : hdl->lastresult->next = result;
1405 55 : hdl->lastresult = result;
1406 : }
1407 :
1408 : return result;
1409 : }
1410 :
1411 : /* close a result set, discarding any unread results */
1412 : static MapiMsg
1413 33537 : close_result(MapiHdl hdl)
1414 : {
1415 33537 : struct MapiResultSet *result;
1416 33537 : Mapi mid;
1417 33537 : int i;
1418 :
1419 33537 : result = hdl->result;
1420 33537 : if (result == NULL)
1421 : return MERROR;
1422 33537 : mid = hdl->mid;
1423 33537 : assert(mid != NULL);
1424 33537 : if (mid->trace)
1425 0 : printf("closing result set\n");
1426 33537 : if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
1427 30443 : if (mid->active &&
1428 2 : result->next == NULL &&
1429 2 : !mid->active->needmore &&
1430 1 : read_into_cache(mid->active, -1) != MOK)
1431 : return MERROR;
1432 30443 : assert(hdl->npending_close == 0 ||
1433 : (hdl->npending_close > 0 && hdl->pending_close != NULL));
1434 30443 : if (mid->active &&
1435 1 : (mid->active->active != result ||
1436 0 : result->cache.tuplecount < result->row_count)) {
1437 : /* results for which we got all tuples at the initial
1438 : * response, need not to be closed as the server already
1439 : * did that immediately */
1440 1 : if (result->row_count > result->tuple_count) {
1441 : /* can't write "X" commands now, so save for later */
1442 0 : REALLOC(hdl->pending_close, hdl->npending_close + 1);
1443 0 : hdl->pending_close[hdl->npending_close] = result->tableid;
1444 0 : hdl->npending_close++;
1445 : }
1446 30442 : } else if (mid->to != NULL) {
1447 : /* first close saved up to-be-closed tables */
1448 30442 : for (i = 0; i < hdl->npending_close; i++) {
1449 0 : char msg[256];
1450 :
1451 0 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1452 0 : mapi_log_record(mid, "CMD", "%s", msg);
1453 0 : mid->active = hdl;
1454 0 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1455 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1456 0 : close_connection(mid);
1457 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1458 0 : break;
1459 : }
1460 0 : read_into_cache(hdl, 0);
1461 : }
1462 30442 : hdl->npending_close = 0;
1463 30442 : if (hdl->pending_close)
1464 0 : free(hdl->pending_close);
1465 30442 : hdl->pending_close = NULL;
1466 30442 : if (mid->to != NULL && result->tuple_count < result->row_count) {
1467 19 : char msg[256];
1468 :
1469 19 : snprintf(msg, sizeof(msg), "Xclose %d\n", result->tableid);
1470 19 : mapi_log_record(mid, "CMD", "%s", msg);
1471 19 : mid->active = hdl;
1472 38 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1473 19 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1474 0 : close_connection(mid);
1475 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1476 : } else
1477 19 : read_into_cache(hdl, 0);
1478 : }
1479 : }
1480 30443 : result->tableid = -1;
1481 : }
1482 33537 : if (mid->active == hdl &&
1483 3 : hdl->active == result &&
1484 0 : read_into_cache(hdl, -1) != MOK)
1485 : return MERROR;
1486 33537 : if( hdl->active == result)
1487 : return MERROR;
1488 : //assert(hdl->active != result);
1489 33537 : if (result->fields) {
1490 109556 : for (i = 0; i < result->maxfields; i++) {
1491 78909 : if (result->fields[i].tablename)
1492 78704 : free(result->fields[i].tablename);
1493 78909 : if (result->fields[i].columnname)
1494 78704 : free(result->fields[i].columnname);
1495 78909 : if (result->fields[i].columntype)
1496 78712 : free(result->fields[i].columntype);
1497 : }
1498 30647 : free(result->fields);
1499 : }
1500 33537 : result->fields = NULL;
1501 33537 : result->maxfields = result->fieldcnt = 0;
1502 33537 : if (result->cache.line) {
1503 187502 : for (i = 0; i < result->cache.writer; i++) {
1504 156851 : if (result->cache.line[i].rows)
1505 156851 : free(result->cache.line[i].rows);
1506 156851 : if (result->cache.line[i].anchors) {
1507 : int j;
1508 :
1509 137528 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
1510 123044 : if (result->cache.line[i].anchors[j]) {
1511 81484 : free(result->cache.line[i].anchors[j]);
1512 81484 : result->cache.line[i].anchors[j] = NULL;
1513 : }
1514 14484 : free(result->cache.line[i].anchors);
1515 : }
1516 156851 : if (result->cache.line[i].lens)
1517 14484 : free(result->cache.line[i].lens);
1518 : }
1519 30651 : free(result->cache.line);
1520 30651 : result->cache.line = NULL;
1521 30651 : result->cache.tuplecount = 0;
1522 : }
1523 33537 : if (result->errorstr && result->errorstr != mapi_nomem)
1524 29 : free(result->errorstr);
1525 33537 : result->errorstr = NULL;
1526 33537 : memset(result->sqlstate, 0, sizeof(result->sqlstate));
1527 33537 : result->hdl = NULL;
1528 33537 : hdl->result = result->next;
1529 33537 : if (hdl->result == NULL)
1530 33482 : hdl->lastresult = NULL;
1531 33537 : result->next = NULL;
1532 33537 : free(result);
1533 33537 : return MOK;
1534 : }
1535 :
1536 : static void
1537 43 : add_error(struct MapiResultSet *result, char *error)
1538 : {
1539 : /* concatenate the error messages */
1540 43 : size_t size = result->errorstr ? strlen(result->errorstr) : 0;
1541 :
1542 43 : if (strlen(error) > 6 && error[5] == '!' &&
1543 25 : (isdigit((unsigned char) error[0]) ||
1544 0 : (error[0] >= 'A' && error[0] <= 'Z')) &&
1545 25 : (isdigit((unsigned char) error[1]) ||
1546 2 : (error[1] >= 'A' && error[1] <= 'Z')) &&
1547 25 : (isdigit((unsigned char) error[2]) ||
1548 3 : (error[2] >= 'A' && error[2] <= 'Z')) &&
1549 25 : (isdigit((unsigned char) error[3]) ||
1550 0 : (error[3] >= 'A' && error[3] <= 'Z')) &&
1551 25 : (isdigit((unsigned char) error[4]) ||
1552 0 : (error[4] >= 'A' && error[4] <= 'Z'))) {
1553 25 : if (result->errorstr == NULL) {
1554 : /* remember SQLSTATE for first error */
1555 23 : strcpy_len(result->sqlstate, error,
1556 : sizeof(result->sqlstate));
1557 : }
1558 : /* skip SQLSTATE */
1559 25 : error += 6;
1560 : }
1561 43 : REALLOC(result->errorstr, size + strlen(error) + 2);
1562 43 : if (result->errorstr == NULL)
1563 0 : result->errorstr = mapi_nomem;
1564 : else
1565 43 : stpcpy(stpcpy(result->errorstr + size, error), "\n");
1566 43 : }
1567 :
1568 : const char *
1569 11535 : mapi_result_error(MapiHdl hdl)
1570 : {
1571 11535 : return hdl && hdl->result ? hdl->result->errorstr : NULL;
1572 : }
1573 :
1574 : const char *
1575 3 : mapi_result_errorcode(MapiHdl hdl)
1576 : {
1577 3 : return hdl && hdl->result && hdl->result->sqlstate[0] ? hdl->result->sqlstate : NULL;
1578 : }
1579 :
1580 : /* Go to the next result set, if any, and close the current result
1581 : set. This function returns 1 if there are more result sets after
1582 : the one that was closed, otherwise, if more input is needed, return
1583 : MMORE, else, return MOK */
1584 : MapiMsg
1585 5003 : mapi_next_result(MapiHdl hdl)
1586 : {
1587 5003 : mapi_hdl_check(hdl);
1588 :
1589 9446 : while (hdl->result != NULL) {
1590 4498 : if (close_result(hdl) != MOK)
1591 : return MERROR;
1592 4498 : if (hdl->result &&
1593 55 : (hdl->result->querytype == -1 ||
1594 : /* basically exclude Q_PARSE and Q_BLOCK */
1595 : (hdl->result->querytype >= Q_TABLE &&
1596 0 : hdl->result->querytype <= Q_PREPARE) ||
1597 0 : hdl->result->errorstr != NULL))
1598 : return 1;
1599 : }
1600 4948 : return hdl->needmore ? MMORE : MOK;
1601 : }
1602 :
1603 : MapiMsg
1604 16 : mapi_needmore(MapiHdl hdl)
1605 : {
1606 16 : return hdl->needmore ? MMORE : MOK;
1607 : }
1608 :
1609 : bool
1610 2008 : mapi_more_results(MapiHdl hdl)
1611 : {
1612 2008 : struct MapiResultSet *result;
1613 :
1614 2008 : mapi_hdl_check(hdl);
1615 :
1616 2008 : if ((result = hdl->result) == 0) {
1617 : /* there are no results at all */
1618 : return false;
1619 : }
1620 2000 : if (result->querytype == Q_TABLE && hdl->mid->active == hdl) {
1621 : /* read until next result (if any) */
1622 0 : read_into_cache(hdl, -1);
1623 : }
1624 2000 : if (hdl->needmore) {
1625 : /* assume the application will provide more data and
1626 : that we will then have a result */
1627 : return true;
1628 : }
1629 2000 : while (result->next) {
1630 0 : result = result->next;
1631 0 : if (result->querytype == -1 ||
1632 : /* basically exclude Q_PARSE and Q_BLOCK */
1633 0 : (hdl->result->querytype >= Q_TABLE &&
1634 0 : hdl->result->querytype <= Q_PREPARE) ||
1635 0 : result->errorstr != NULL)
1636 : return true;
1637 : }
1638 : /* no more results */
1639 : return false;
1640 : }
1641 :
1642 : MapiHdl
1643 38704 : mapi_new_handle(Mapi mid)
1644 : {
1645 38704 : MapiHdl hdl;
1646 :
1647 38704 : mapi_check0(mid);
1648 :
1649 38709 : hdl = malloc(sizeof(*hdl));
1650 38709 : if (hdl == NULL) {
1651 0 : mapi_setError(mid, "Memory allocation failure", __func__, MERROR);
1652 0 : return NULL;
1653 : }
1654 : /* initialize and add to doubly-linked list */
1655 38709 : *hdl = (struct MapiStatement) {
1656 : .mid = mid,
1657 38709 : .next = mid->first,
1658 : };
1659 38709 : mid->first = hdl;
1660 38709 : if (hdl->next)
1661 4870 : hdl->next->prev = hdl;
1662 : return hdl;
1663 : }
1664 :
1665 : /* close all result sets on the handle but don't close the handle itself */
1666 : static MapiMsg
1667 74074 : finish_handle(MapiHdl hdl)
1668 : {
1669 74074 : Mapi mid;
1670 74074 : int i;
1671 :
1672 74074 : if (hdl == NULL)
1673 : return MERROR;
1674 74074 : mid = hdl->mid;
1675 74481 : if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
1676 407 : read_into_cache(hdl, 0) != MOK)
1677 : return MERROR;
1678 74074 : if (mid->to) {
1679 74073 : if (hdl->needmore) {
1680 0 : assert(mid->active == NULL || mid->active == hdl);
1681 0 : hdl->needmore = false;
1682 0 : mid->active = hdl;
1683 0 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1684 0 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
1685 0 : read_into_cache(hdl, 0);
1686 : }
1687 74073 : for (i = 0; i < hdl->npending_close; i++) {
1688 1 : char msg[256];
1689 :
1690 1 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1691 1 : mapi_log_record(mid, "CMD", "%s", msg);
1692 1 : mid->active = hdl;
1693 1 : if (mnstr_printf(mid->to, "%s", msg) < 0 ||
1694 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
1695 0 : close_connection(mid);
1696 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
1697 0 : break;
1698 : }
1699 0 : read_into_cache(hdl, 0);
1700 : }
1701 : }
1702 74073 : hdl->npending_close = 0;
1703 74073 : if (hdl->pending_close)
1704 0 : free(hdl->pending_close);
1705 74073 : hdl->pending_close = NULL;
1706 177185 : while (hdl->result) {
1707 29040 : if (close_result(hdl) != MOK)
1708 : return MERROR;
1709 29039 : if (hdl->needmore) {
1710 0 : assert(mid->active == NULL || mid->active == hdl);
1711 0 : hdl->needmore = false;
1712 0 : mid->active = hdl;
1713 0 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
1714 0 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
1715 0 : read_into_cache(hdl, 0);
1716 : }
1717 : }
1718 : return MOK;
1719 : }
1720 :
1721 : /* Close a statement handle, discarding any unread output. */
1722 : MapiMsg
1723 38704 : mapi_close_handle(MapiHdl hdl)
1724 : {
1725 38704 : if (hdl == NULL)
1726 : return MOK;
1727 38704 : debugprint("entering %s\n", "mapi_close_handle");
1728 :
1729 : /* don't use mapi_check_hdl: it's ok if we're not connected */
1730 38704 : mapi_clrError(hdl->mid);
1731 :
1732 38705 : (void) finish_handle(hdl);
1733 38710 : hdl->npending_close = 0;
1734 38710 : if (hdl->pending_close)
1735 0 : free(hdl->pending_close);
1736 38710 : hdl->pending_close = NULL;
1737 38710 : if (hdl->bindings)
1738 0 : free(hdl->bindings);
1739 38710 : hdl->bindings = NULL;
1740 38710 : hdl->maxbindings = 0;
1741 38710 : if (hdl->params)
1742 0 : free(hdl->params);
1743 38710 : hdl->params = NULL;
1744 38710 : hdl->maxparams = 0;
1745 38710 : if (hdl->query)
1746 37056 : free(hdl->query);
1747 38710 : hdl->query = NULL;
1748 38710 : if (hdl->template)
1749 0 : free(hdl->template);
1750 38710 : hdl->template = NULL;
1751 : /* remove from doubly-linked list */
1752 38710 : if (hdl->prev)
1753 1 : hdl->prev->next = hdl->next;
1754 38710 : if (hdl->next)
1755 4866 : hdl->next->prev = hdl->prev;
1756 38710 : if (hdl->mid->first == hdl)
1757 38714 : hdl->mid->first = hdl->next;
1758 38710 : hdl->prev = NULL;
1759 38710 : hdl->next = NULL;
1760 38710 : hdl->mid = NULL;
1761 38710 : free(hdl);
1762 38710 : return MOK;
1763 : }
1764 :
1765 : const struct MapiStruct MapiStructDefaults = {
1766 : .error = MOK,
1767 : .redirmax = 10,
1768 : .blk.eos = false,
1769 : .blk.lim = BLOCK,
1770 : };
1771 :
1772 : /* Allocate a new connection handle. */
1773 : Mapi
1774 477 : mapi_new(msettings *settings)
1775 : {
1776 477 : Mapi mid;
1777 477 : static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
1778 :
1779 477 : if (!ATOMIC_TAS(&mapi_initialized)) {
1780 232 : if (mnstr_init() < 0)
1781 : return NULL;
1782 : }
1783 :
1784 477 : mid = malloc(sizeof(*mid));
1785 477 : if (mid == NULL)
1786 : return NULL;
1787 :
1788 : /* then fill in some details */
1789 477 : *mid = MapiStructDefaults;
1790 477 : mid->index = (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
1791 477 : if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
1792 0 : mapi_destroy(mid);
1793 0 : return NULL;
1794 : }
1795 477 : if (settings == NULL) {
1796 367 : settings = msettings_create();
1797 110 : } else if (msettings_get_allocator(settings, NULL) != NULL) {
1798 : // it uses a custom allocator, reallocate using regular
1799 0 : msettings *old = settings;
1800 0 : settings = msettings_clone(old);
1801 0 : if (settings)
1802 0 : msettings_destroy(old);
1803 : }
1804 476 : if (settings == NULL) {
1805 0 : mapi_destroy(mid);
1806 0 : return NULL;
1807 : }
1808 476 : mid->settings = settings;
1809 476 : mid->blk.buf[0] = 0;
1810 476 : mid->blk.buf[mid->blk.lim] = 0;
1811 :
1812 : /* also the current timezone, seconds EAST of UTC */
1813 476 : long time_zone;
1814 : #if defined(_MSC_VER)
1815 : DYNAMIC_TIME_ZONE_INFORMATION tzinf;
1816 :
1817 : /* documentation says: UTC = localtime + Bias (in minutes),
1818 : * but experimentation during DST period says, UTC = localtime
1819 : * + Bias + DaylightBias, and presumably during non DST
1820 : * period, UTC = localtime + Bias */
1821 : switch (GetDynamicTimeZoneInformation(&tzinf)) {
1822 : case TIME_ZONE_ID_STANDARD: /* using standard time */
1823 : case TIME_ZONE_ID_UNKNOWN: /* no daylight saving time in this zone */
1824 : time_zone = -(int) tzinf.Bias * 60;
1825 : break;
1826 : case TIME_ZONE_ID_DAYLIGHT: /* using daylight saving time */
1827 : time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
1828 : break;
1829 : default: /* aka TIME_ZONE_ID_INVALID */
1830 : /* call failed, we don't know the time zone */
1831 : time_zone = 0;
1832 : break;
1833 : }
1834 : #else
1835 476 : time_t t = time(NULL);
1836 476 : struct tm *local_tm = localtime_r(&t, &(struct tm){0});
1837 : #ifdef HAVE_TM_GMTOFF
1838 477 : time_zone = local_tm->tm_gmtoff;
1839 : #else
1840 : struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
1841 : time_t gt = mktime(gm_tm);
1842 : local_tm->tm_isdst=0; /* We need the difference without dst */
1843 : time_t lt = mktime(local_tm);
1844 : assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
1845 : time_zone = (long) (lt - gt);
1846 : #endif
1847 : #endif
1848 477 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
1849 477 : if (err)
1850 0 : mapi_setError(mid, err, __func__, MERROR);
1851 :
1852 : return mid;
1853 : }
1854 :
1855 : /* construct the uri field of a Mapi struct */
1856 : void
1857 327 : set_uri(Mapi mid)
1858 : {
1859 327 : const char *host = msetting_string(mid->settings, MP_HOST);
1860 328 : const char *database = msetting_string(mid->settings, MP_DATABASE);
1861 329 : int port = msetting_long(mid->settings, MP_PORT);
1862 329 : size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
1863 329 : char *uri = malloc(urilen);
1864 :
1865 : /* uri looks as follows:
1866 : * mapi:monetdb://host:port/database
1867 : * or
1868 : * mapi:monetdb:///some/path/to?database=database
1869 : */
1870 :
1871 329 : if (database != NULL) {
1872 329 : if (host[0] == '/') {
1873 0 : snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
1874 : host, database);
1875 : } else {
1876 329 : snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
1877 : host, port, database);
1878 : }
1879 : } else {
1880 0 : if (host[0] == '/') {
1881 0 : snprintf(uri, urilen, "mapi:monetdb://%s",
1882 : host);
1883 : } else {
1884 0 : snprintf(uri, urilen, "mapi:monetdb://%s:%d",
1885 : host, port);
1886 : }
1887 : }
1888 :
1889 329 : if (mid->uri != NULL)
1890 0 : free(mid->uri);
1891 329 : mid->uri = uri;
1892 329 : }
1893 :
1894 : Mapi
1895 216 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
1896 : {
1897 216 : Mapi mid;
1898 :
1899 216 : mid = mapi_new(NULL);
1900 219 : if (mid == NULL)
1901 : return NULL;
1902 :
1903 219 : if (url == NULL) {
1904 0 : mapi_setError(mid, "url is null", __func__, MERROR);
1905 0 : return mid;
1906 : }
1907 219 : if (user == NULL) {
1908 0 : mapi_setError(mid, "user is null", __func__, MERROR);
1909 0 : return mid;
1910 : }
1911 219 : if (pass == NULL) {
1912 0 : mapi_setError(mid, "pass is null", __func__, MERROR);
1913 0 : return mid;
1914 : }
1915 219 : if (lang == NULL) {
1916 0 : mapi_setError(mid, "lang is null", __func__, MERROR);
1917 0 : return mid;
1918 : }
1919 :
1920 219 : msettings_error err = NULL;
1921 219 : if ( (err = msetting_set_string(mid->settings, MP_USER, user))
1922 219 : || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
1923 219 : || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
1924 : ) {
1925 : // in the old implementation we returned NULL but that doesn't
1926 : // make sense to me because above we already started returning
1927 : // mid with the error set.
1928 0 : mapi_setError(mid, err, __func__, MERROR);
1929 0 : return mid;
1930 : }
1931 :
1932 219 : const char *error_message = msettings_parse_url(mid->settings, url);
1933 218 : if (error_message) {
1934 0 : mapi_setError(mid, error_message, __func__, MERROR);
1935 0 : return mid;
1936 : }
1937 :
1938 218 : set_uri(mid);
1939 :
1940 218 : return mid;
1941 : }
1942 :
1943 : /* Allocate a new connection handle and fill in the information needed
1944 : to connect to a server, but don't connect yet. */
1945 : Mapi
1946 148 : mapi_mapi(const char *host, int port, const char *username,
1947 : const char *password, const char *lang, const char *dbname)
1948 : {
1949 148 : Mapi mid;
1950 :
1951 148 : mid = mapi_new(NULL);
1952 148 : if (mid == NULL)
1953 : return NULL;
1954 148 : msettings *settings = mid->settings;
1955 :
1956 148 : if (lang == NULL)
1957 0 : lang = "sql";
1958 :
1959 148 : const char *sockdir = NULL;
1960 148 : if (host && host[0] == '/') {
1961 : sockdir = host;
1962 : host = NULL;
1963 : }
1964 :
1965 81 : msettings_error err = NULL;
1966 229 : do {
1967 81 : if (host && (err = msetting_set_string(settings, MP_HOST, host)))
1968 : break;
1969 148 : if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
1970 : break;
1971 148 : if (username && (err = msetting_set_string(settings, MP_USER, username)))
1972 : break;
1973 148 : if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
1974 : break;
1975 148 : if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
1976 : break;
1977 148 : if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
1978 : break;
1979 148 : if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
1980 : break;
1981 : } while (0);
1982 148 : if (err) {
1983 0 : mapi_setError(mid, err, __func__, MERROR);
1984 0 : return mid;
1985 : }
1986 :
1987 : return mid;
1988 : }
1989 :
1990 : Mapi
1991 110 : mapi_settings(msettings *settings)
1992 : {
1993 110 : assert(settings);
1994 110 : Mapi mid = mapi_new(settings);
1995 110 : if (mid == NULL)
1996 : return mid;
1997 :
1998 110 : set_uri(mid);
1999 110 : return mid;
2000 : }
2001 :
2002 :
2003 : /* Close a connection and free all memory associated with the
2004 : connection handle. */
2005 : MapiMsg
2006 455 : mapi_destroy(Mapi mid)
2007 : {
2008 455 : char **r;
2009 :
2010 455 : mapi_clrError(mid);
2011 :
2012 456 : while (mid->first)
2013 1 : mapi_close_handle(mid->first);
2014 455 : if (mid->connected)
2015 145 : (void) mapi_disconnect(mid);
2016 455 : if (mid->tracelog)
2017 0 : close_stream(mid->tracelog);
2018 :
2019 455 : free(mid->blk.buf);
2020 455 : free(mid->motd);
2021 455 : free(mid->server);
2022 455 : free(mid->uri);
2023 455 : free(mid->tracebuffer);
2024 455 : free(mid->noexplain);
2025 455 : if (mid->errorstr && mid->errorstr != mapi_nomem)
2026 0 : free(mid->errorstr);
2027 455 : free(mid->clientprefix);
2028 :
2029 455 : msettings_destroy(mid->settings);
2030 :
2031 455 : r = mid->redirects;
2032 455 : while (*r) {
2033 0 : free(*r);
2034 0 : r++;
2035 : }
2036 :
2037 455 : free(mid);
2038 455 : return MOK;
2039 : }
2040 :
2041 : /* Create a connection handle and connect to the server using the
2042 : specified parameters. */
2043 : Mapi
2044 11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
2045 : {
2046 11 : Mapi mid;
2047 :
2048 11 : mid = mapi_mapi(host, port, username, password, lang, dbname);
2049 11 : if (mid && mid->error == MOK)
2050 11 : mapi_reconnect(mid); /* actually, initial connect */
2051 11 : return mid;
2052 : }
2053 :
2054 : /* Returns an malloced NULL-terminated array with redirects */
2055 : char **
2056 0 : mapi_resolve(const char *host, int port, const char *pattern)
2057 : {
2058 0 : int rmax;
2059 0 : Mapi mid;
2060 :
2061 : /* if it doesn't make sense, don't try to crash */
2062 0 : if (pattern == NULL)
2063 : return NULL;
2064 :
2065 0 : mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
2066 0 : if (mid) {
2067 0 : if (mid->error == MOK) {
2068 0 : rmax = mid->redirmax;
2069 0 : mid->redirmax = 0;
2070 0 : mapi_reconnect(mid); /* real connect, don't follow redirects */
2071 0 : mid->redirmax = rmax;
2072 0 : if (mid->error == MOK) {
2073 0 : close_connection(mid); /* we didn't expect a connection actually */
2074 : } else {
2075 0 : char **ret = malloc(sizeof(char *) * MAXREDIR);
2076 0 : memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
2077 0 : mid->redirects[0] = NULL; /* make sure the members aren't freed */
2078 0 : mapi_destroy(mid);
2079 0 : return ret;
2080 : }
2081 : }
2082 0 : mapi_destroy(mid);
2083 : }
2084 : return NULL;
2085 : }
2086 :
2087 : void
2088 1455 : close_connection(Mapi mid)
2089 : {
2090 1455 : MapiHdl hdl;
2091 1455 : struct MapiResultSet *result;
2092 :
2093 1455 : mid->connected = false;
2094 1455 : mid->active = NULL;
2095 1456 : for (hdl = mid->first; hdl; hdl = hdl->next) {
2096 1 : hdl->active = NULL;
2097 1 : for (result = hdl->result; result; result = result->next)
2098 0 : result->tableid = -1;
2099 : }
2100 : /* finish channels */
2101 : /* Make sure that the write- (to-) stream is closed first,
2102 : * as the related read- (from-) stream closes the shared
2103 : * socket; see also src/common/stream.c:socket_close .
2104 : */
2105 1455 : if (mid->to) {
2106 1455 : close_stream(mid->to);
2107 1455 : mid->to = 0;
2108 : }
2109 1455 : if (mid->from) {
2110 1455 : close_stream(mid->from);
2111 1455 : mid->from = 0;
2112 : }
2113 1455 : mid->redircnt = 0;
2114 1455 : mapi_log_record(mid, "C", "Connection closed");
2115 1455 : }
2116 :
2117 : MapiMsg
2118 1441 : mapi_disconnect(Mapi mid)
2119 : {
2120 1441 : mapi_check(mid);
2121 :
2122 1441 : close_connection(mid);
2123 1441 : return MOK;
2124 : }
2125 :
2126 : /* Set callback function to retrieve or send file content for COPY
2127 : * INTO queries.
2128 : *
2129 : * char *getfile(void *private, const char *filename, bool binary,
2130 : * uint64_6 offset, size_t *size);
2131 : * Retrieve data from a file.
2132 : *
2133 : * The arguments are:
2134 : * private - the value of the filecontentprivate argument to
2135 : * mapi_setfilecallback;
2136 : * filename - the file to read (the application is free to interpret
2137 : * this any way it wants, including getting data over the
2138 : * Internet);
2139 : * binary - if set, the file is expected to contain binary data and
2140 : * should therefore be opened in binary mode, otherwise the
2141 : * file is expected to contain data in the UTF-8 encoding (of
2142 : * course, the application is free to transparently convert
2143 : * from the actual encoding to UTF-8);
2144 : * offset - the line number of the first line to be retrieved (this is
2145 : * one-based, i.e. the start of the file has line number one;
2146 : * lines are terminated by '\n');
2147 : * size - pointer in which to return the size of the chunk that is
2148 : * being returned.
2149 : *
2150 : * The callback function is expected to return data in chunks until it
2151 : * indicates to the caller that there is no more data or an error has
2152 : * occurred. Chunks can be any size. The caller does not modify or
2153 : * free the data returned. The size of the chunk being returned is
2154 : * stored in the size argument. Errors are indicated by returning a
2155 : * string containing an error message and setting *size to zero. The
2156 : * error message should not contain any newlines. Any call to the
2157 : * callback function is allowed to return an error.
2158 : *
2159 : * The first call to the callback function contains values for
2160 : * filename, binary, and offset. These parameters are all 0 for all
2161 : * subsequent calls for continuation data from the same file.
2162 : *
2163 : * If the caller has retrieved enough data before the file is
2164 : * exhausted, it calls the callback function one more time with a NULL
2165 : * pointer for the size argument. This gives the callback function
2166 : * the opportunity to free its resources (e.g. close the file).
2167 : *
2168 : * If there is no more data to be returned, the callback function
2169 : * returns a NULL pointer and sets *size to zero. No more calls for
2170 : * the current file will be made.
2171 : *
2172 : * Note that if the file to be read is empty, or contains fewer lines
2173 : * than the requested offset, the first call to the callback function
2174 : * may return NULL.
2175 : *
2176 : * char *putfile(void *private, const char *filename, bool binary,
2177 : * const void *data, size_t size);
2178 : * Send data to a file.
2179 : *
2180 : * The arguments are:
2181 : * private - the value of the filecontentprivate argument to
2182 : * mapi_setfilecallback;
2183 : * filename - the file to be written;
2184 : * binary - if set, the data to be written is binary and the file
2185 : * should therefore be opened in binary mode, otherwise the
2186 : * data is UTF-8 encoded text;
2187 : * data - the data to be written;
2188 : * size - the size of the data to be written.
2189 : *
2190 : * The callback is called multiple time to write a single file. The
2191 : * first time, a filename is specified, all subsequent times, the
2192 : * filename argument is NULL. When all data has been written, the
2193 : * callback function is called one last time with NULL pointer for the
2194 : * data argument so that the callback function can free any resources.
2195 : *
2196 : * When an error occurs, the callback function returns a string
2197 : * containing an error message after which the callback will not be
2198 : * called again for the same file. Otherwise, the callback function
2199 : * returns NULL.
2200 : *
2201 : * Note also that multibyte sequences may be split over two calls.
2202 : */
2203 : void
2204 114 : mapi_setfilecallback2(Mapi mid,
2205 : char *(*getfilecontent)(void *,
2206 : const char *, bool,
2207 : uint64_t, size_t *),
2208 : char *(*putfilecontent)(void *,
2209 : const char *, bool,
2210 : const void *, size_t),
2211 : void *filecontentprivate)
2212 : {
2213 114 : mid->getfilecontent = getfilecontent;
2214 114 : mid->putfilecontent = putfilecontent;
2215 114 : mid->filecontentprivate = filecontentprivate;
2216 114 : mid->putfilecontent_old = NULL;
2217 114 : mid->filecontentprivate_old = NULL;
2218 114 : }
2219 :
2220 : static char *
2221 0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
2222 : {
2223 0 : Mapi mid = priv;
2224 0 : void *priv_old = mid->filecontentprivate_old;
2225 0 : if (filename && binary)
2226 : return "Client does not support writing binary files";
2227 0 : return mid->putfilecontent_old(priv_old, filename, data, size);
2228 : }
2229 :
2230 : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
2231 : * INTO queries.
2232 : *
2233 : * Deprecated because it does not support binary downloads.
2234 : * Use mapi_setfilecallback2 instead.
2235 : */
2236 : void
2237 0 : mapi_setfilecallback(Mapi mid,
2238 : char *(*getfilecontent)(void *,
2239 : const char *, bool,
2240 : uint64_t, size_t *),
2241 : char *(*putfilecontent)(void *,
2242 : const char *,
2243 : const void *, size_t),
2244 : void *filecontentprivate)
2245 : {
2246 0 : mid->getfilecontent = getfilecontent;
2247 0 : mid->putfilecontent = putfilecontent_wrap;
2248 0 : mid->filecontentprivate = mid;
2249 0 : mid->putfilecontent_old = putfilecontent;
2250 0 : mid->filecontentprivate_old = filecontentprivate;
2251 0 : }
2252 :
2253 : void
2254 110 : mapi_setclientprefix(Mapi mid, const char *prefix)
2255 : {
2256 110 : free(mid->clientprefix);
2257 110 : if (prefix == NULL)
2258 0 : mid->clientprefix = NULL;
2259 : else
2260 110 : mid->clientprefix = strdup(prefix);
2261 :
2262 110 : }
2263 :
2264 : #define testBinding(hdl,fnr) \
2265 : do { \
2266 : mapi_hdl_check(hdl); \
2267 : if (fnr < 0) { \
2268 : return mapi_setError(hdl->mid, \
2269 : "Illegal field number", \
2270 : __func__, MERROR); \
2271 : } \
2272 : /* make sure there is enough space */ \
2273 : if (fnr >= hdl->maxbindings) \
2274 : mapi_extend_bindings(hdl, fnr); \
2275 : } while (0)
2276 :
2277 : #define testParam(hdl, fnr) \
2278 : do { \
2279 : mapi_hdl_check(hdl); \
2280 : if (fnr < 0) { \
2281 : return mapi_setError(hdl->mid, \
2282 : "Illegal param number", \
2283 : __func__, MERROR); \
2284 : } \
2285 : if (fnr >= hdl->maxparams) \
2286 : mapi_extend_params(hdl, fnr); \
2287 : } while (0)
2288 :
2289 : MapiMsg
2290 0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
2291 : {
2292 0 : testBinding(hdl, fnr);
2293 0 : hdl->bindings[fnr].outparam = ptr;
2294 :
2295 0 : hdl->bindings[fnr].outtype = MAPI_AUTO;
2296 0 : return MOK;
2297 : }
2298 :
2299 : MapiMsg
2300 0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
2301 : {
2302 0 : testBinding(hdl, fnr);
2303 0 : hdl->bindings[fnr].outparam = ptr;
2304 :
2305 0 : if (type >= 0 && type < MAPI_NUMERIC)
2306 0 : hdl->bindings[fnr].outtype = type;
2307 : else
2308 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2309 0 : return MOK;
2310 : }
2311 :
2312 : MapiMsg
2313 0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2314 : {
2315 0 : if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
2316 0 : return hdl->mid->error;
2317 :
2318 0 : hdl->bindings[fnr].scale = scale;
2319 0 : hdl->bindings[fnr].precision = prec;
2320 0 : return MOK;
2321 : }
2322 :
2323 : MapiMsg
2324 0 : mapi_clear_bindings(MapiHdl hdl)
2325 : {
2326 0 : mapi_hdl_check(hdl);
2327 0 : if (hdl->bindings)
2328 0 : memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
2329 : return MOK;
2330 : }
2331 :
2332 : MapiMsg
2333 0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
2334 : {
2335 0 : testParam(hdl, fnr);
2336 0 : hdl->params[fnr].inparam = ptr;
2337 :
2338 0 : if (ctype >= 0 && ctype < MAPI_NUMERIC)
2339 0 : hdl->params[fnr].intype = ctype;
2340 : else
2341 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2342 0 : hdl->params[fnr].sizeptr = NULL;
2343 0 : hdl->params[fnr].outtype = sqltype;
2344 0 : hdl->params[fnr].scale = 0;
2345 0 : hdl->params[fnr].precision = 0;
2346 0 : return MOK;
2347 : }
2348 :
2349 : MapiMsg
2350 0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
2351 : {
2352 0 : testParam(hdl, fnr);
2353 0 : hdl->params[fnr].inparam = (void *) ptr;
2354 :
2355 0 : hdl->params[fnr].intype = MAPI_VARCHAR;
2356 0 : hdl->params[fnr].sizeptr = sizeptr;
2357 0 : hdl->params[fnr].outtype = sqltype;
2358 0 : hdl->params[fnr].scale = 0;
2359 0 : hdl->params[fnr].precision = 0;
2360 0 : return MOK;
2361 : }
2362 :
2363 : MapiMsg
2364 0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
2365 : {
2366 0 : return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
2367 : }
2368 :
2369 : MapiMsg
2370 0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2371 : {
2372 0 : if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
2373 0 : return hdl->mid->error;
2374 :
2375 0 : hdl->params[fnr].scale = scale;
2376 0 : hdl->params[fnr].precision = prec;
2377 0 : return MOK;
2378 : }
2379 :
2380 : MapiMsg
2381 2 : mapi_clear_params(MapiHdl hdl)
2382 : {
2383 2 : mapi_hdl_check(hdl);
2384 2 : if (hdl->params)
2385 0 : memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
2386 : return MOK;
2387 : }
2388 :
2389 : static MapiHdl
2390 34494 : prepareQuery(MapiHdl hdl, const char *cmd)
2391 : {
2392 34494 : if (hdl && cmd) {
2393 34503 : if (hdl->query)
2394 2169 : free(hdl->query);
2395 34503 : hdl->query = strdup(cmd);
2396 34503 : assert(hdl->query);
2397 34503 : if (hdl->template) {
2398 0 : free(hdl->template);
2399 0 : hdl->template = NULL;
2400 : }
2401 : }
2402 34494 : return hdl;
2403 : }
2404 :
2405 :
2406 : MapiMsg
2407 0 : mapi_set_rtimeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2408 : {
2409 0 : mapi_check(mid);
2410 0 : if (mid->trace)
2411 0 : printf("Set timeout to %u\n", timeout);
2412 0 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2413 0 : return MOK;
2414 : }
2415 :
2416 : MapiMsg
2417 0 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2418 : {
2419 0 : mapi_check(mid);
2420 0 : if (mid->trace)
2421 0 : printf("Set timeout to %u\n", timeout);
2422 0 : mnstr_settimeout(mid->to, timeout, callback, callback_data);
2423 0 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2424 0 : return MOK;
2425 : }
2426 :
2427 : MapiMsg
2428 0 : mapi_timeout(Mapi mid, unsigned int timeout)
2429 : {
2430 0 : return mapi_set_timeout(mid, timeout, NULL, NULL);
2431 : }
2432 :
2433 : MapiMsg
2434 1263 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
2435 : {
2436 1263 : MapiHdl hdl;
2437 :
2438 1263 : mapi_check(mid);
2439 1263 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
2440 : return MERROR;
2441 2526 : if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
2442 1263 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
2443 0 : close_connection(mid);
2444 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
2445 0 : return MERROR;
2446 : }
2447 1263 : mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
2448 1263 : hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
2449 1263 : if (hdl == NULL)
2450 : return MERROR;
2451 1263 : mid->active = hdl;
2452 1263 : read_into_cache(hdl, 0);
2453 1263 : mapi_close_handle(hdl); /* reads away any output */
2454 1263 : return MOK;
2455 : }
2456 :
2457 : MapiMsg
2458 0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
2459 : {
2460 0 : mapi_hdl_check(hdl);
2461 0 : if (finish_handle(hdl) != MOK)
2462 : return MERROR;
2463 0 : prepareQuery(hdl, cmd);
2464 0 : hdl->template = strdup(hdl->query);
2465 0 : assert(hdl->template);
2466 0 : return hdl->mid->error;
2467 : }
2468 :
2469 : MapiHdl
2470 0 : mapi_prepare(Mapi mid, const char *cmd)
2471 : {
2472 0 : MapiHdl hdl;
2473 :
2474 0 : mapi_check0(mid);
2475 0 : hdl = mapi_new_handle(mid);
2476 0 : if (hdl == NULL)
2477 : return NULL;
2478 0 : mapi_prepare_handle(hdl, cmd);
2479 0 : return hdl;
2480 : }
2481 :
2482 : /*
2483 : * Building the query string using replacement of values requires
2484 : * some care to not overflow the space allocated.
2485 : */
2486 : #define checkSpace(len) \
2487 : do { \
2488 : /* note: k==strlen(hdl->query) */ \
2489 : if (k+len >= lim) { \
2490 : lim = k + len + MAPIBLKSIZE; \
2491 : char *q = realloc(hdl->query, lim); \
2492 : if (q == NULL) { \
2493 : free(hdl->query); \
2494 : hdl->query = NULL; \
2495 : return; \
2496 : } \
2497 : hdl->query = q; \
2498 : } \
2499 : } while (0)
2500 :
2501 : static void
2502 33109 : mapi_param_store(MapiHdl hdl)
2503 : {
2504 33109 : char *val, buf[MAPIBLKSIZE];
2505 33109 : char *p = hdl->template, *q;
2506 33109 : int i;
2507 33109 : size_t k;
2508 33109 : size_t lim;
2509 :
2510 33109 : if (hdl->template == 0)
2511 : return;
2512 :
2513 0 : lim = strlen(hdl->template) + MAPIBLKSIZE;
2514 0 : REALLOC(hdl->query, lim);
2515 0 : if (hdl->query == NULL)
2516 : return;
2517 0 : hdl->query[0] = 0;
2518 0 : k = 0;
2519 :
2520 0 : q = strchr(hdl->template, PLACEHOLDER);
2521 0 : i = 0;
2522 : /* loop invariant: k == strlen(hdl->query) */
2523 0 : while (q && i < hdl->maxparams) {
2524 0 : if (q > p && *(q - 1) == '\\') {
2525 0 : q = strchr(q + 1, PLACEHOLDER);
2526 0 : continue;
2527 : }
2528 :
2529 0 : if (k + (q - p) >= lim) {
2530 0 : lim += MAPIBLKSIZE;
2531 0 : REALLOC(hdl->query, lim);
2532 0 : if (hdl->query == NULL)
2533 : return;
2534 : }
2535 0 : memcpy(hdl->query + k, p, q - p);
2536 0 : k += q - p;
2537 0 : hdl->query[k] = 0;
2538 :
2539 0 : if (hdl->params[i].inparam == 0) {
2540 0 : char *nullstr = "NULL";
2541 0 : checkSpace(5);
2542 0 : if (msettings_lang_is_mal(hdl->mid->settings))
2543 0 : nullstr = "nil";
2544 0 : strcpy(hdl->query + k, nullstr);
2545 : } else {
2546 0 : void *src = hdl->params[i].inparam; /* abbrev */
2547 :
2548 0 : switch (hdl->params[i].intype) {
2549 0 : case MAPI_TINY:
2550 0 : checkSpace(5);
2551 0 : snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
2552 0 : break;
2553 0 : case MAPI_UTINY:
2554 0 : checkSpace(5);
2555 0 : snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
2556 0 : break;
2557 0 : case MAPI_SHORT:
2558 0 : checkSpace(10);
2559 0 : snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
2560 0 : break;
2561 0 : case MAPI_USHORT:
2562 0 : checkSpace(10);
2563 0 : snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
2564 0 : break;
2565 0 : case MAPI_INT:
2566 0 : checkSpace(20);
2567 0 : snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
2568 0 : break;
2569 0 : case MAPI_UINT:
2570 0 : checkSpace(20);
2571 0 : snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
2572 0 : break;
2573 0 : case MAPI_LONG:
2574 0 : checkSpace(20);
2575 0 : snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
2576 0 : break;
2577 0 : case MAPI_ULONG:
2578 0 : checkSpace(20);
2579 0 : snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
2580 0 : break;
2581 0 : case MAPI_LONGLONG:
2582 0 : checkSpace(30);
2583 0 : snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
2584 0 : break;
2585 0 : case MAPI_ULONGLONG:
2586 0 : checkSpace(30);
2587 0 : snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
2588 0 : break;
2589 0 : case MAPI_FLOAT:
2590 0 : checkSpace(30);
2591 0 : snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
2592 0 : break;
2593 0 : case MAPI_DOUBLE:
2594 0 : checkSpace(30);
2595 0 : snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
2596 0 : break;
2597 0 : case MAPI_DATE:
2598 0 : checkSpace(50);
2599 0 : snprintf(hdl->query + k, lim - k,
2600 : "DATE '%04hd-%02hu-%02hu'",
2601 0 : ((MapiDate *) src)->year,
2602 0 : ((MapiDate *) src)->month,
2603 0 : ((MapiDate *) src)->day);
2604 0 : break;
2605 0 : case MAPI_TIME:
2606 0 : checkSpace(60);
2607 0 : snprintf(hdl->query + k, lim - k,
2608 : "TIME '%02hu:%02hu:%02hu'",
2609 0 : ((MapiTime *) src)->hour,
2610 0 : ((MapiTime *) src)->minute,
2611 0 : ((MapiTime *) src)->second);
2612 0 : break;
2613 0 : case MAPI_DATETIME:
2614 0 : checkSpace(110);
2615 0 : snprintf(hdl->query + k, lim - k,
2616 : "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
2617 0 : ((MapiDateTime *) src)->year,
2618 0 : ((MapiDateTime *) src)->month,
2619 0 : ((MapiDateTime *) src)->day,
2620 0 : ((MapiDateTime *) src)->hour,
2621 0 : ((MapiDateTime *) src)->minute,
2622 0 : ((MapiDateTime *) src)->second,
2623 : ((MapiDateTime *) src)->fraction);
2624 0 : break;
2625 0 : case MAPI_CHAR:
2626 0 : buf[0] = *(char *) src;
2627 0 : buf[1] = 0;
2628 0 : val = mapi_quote(buf, 1);
2629 : /* note: k==strlen(hdl->query) */
2630 0 : if (k + strlen(val) + 3 >= lim) {
2631 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2632 0 : char *q = realloc(hdl->query, lim);
2633 0 : if (q == NULL) {
2634 0 : free(hdl->query);
2635 0 : hdl->query = NULL;
2636 0 : free(val);
2637 0 : return;
2638 : }
2639 0 : hdl->query = q;
2640 : }
2641 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2642 0 : free(val);
2643 0 : break;
2644 0 : case MAPI_VARCHAR:
2645 0 : val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
2646 : /* note: k==strlen(hdl->query) */
2647 0 : if (k + strlen(val) + 3 >= lim) {
2648 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2649 0 : char *q = realloc(hdl->query, lim);
2650 0 : if (q == NULL) {
2651 0 : free(hdl->query);
2652 0 : hdl->query = NULL;
2653 0 : free(val);
2654 0 : return;
2655 : }
2656 0 : hdl->query = q;
2657 : }
2658 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2659 0 : free(val);
2660 0 : break;
2661 0 : default:
2662 0 : strcpy_len(hdl->query + k, src, lim - k);
2663 0 : break;
2664 : }
2665 : }
2666 0 : k += strlen(hdl->query + k);
2667 :
2668 0 : i++;
2669 0 : p = q + 1;
2670 0 : q = strchr(p, PLACEHOLDER);
2671 : }
2672 0 : checkSpace(strlen(p) + 1);
2673 0 : strcpy(hdl->query + k, p);
2674 0 : if (hdl->mid->trace)
2675 0 : printf("param_store: result=%s\n", hdl->query);
2676 : return;
2677 : }
2678 :
2679 : /* Read one more line from the input stream and return it. This
2680 : returns a pointer into the input buffer, so the data needs to be
2681 : copied if it is to be retained. */
2682 : static char *
2683 1796660 : read_line(Mapi mid)
2684 : {
2685 1796660 : char *reply;
2686 1796660 : char *nl;
2687 1796660 : char *s; /* from where to search for newline */
2688 :
2689 1796660 : if (mid->active == NULL)
2690 : return NULL;
2691 :
2692 : /* check if we need to read more blocks to get a new line */
2693 1796660 : mid->blk.eos = false;
2694 1796660 : s = mid->blk.buf + mid->blk.nxt;
2695 2110957 : while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
2696 314281 : ssize_t len;
2697 :
2698 314281 : if (mid->blk.lim - mid->blk.end < BLOCK) {
2699 16445 : int len;
2700 :
2701 16445 : len = mid->blk.lim;
2702 16445 : if (mid->blk.nxt <= BLOCK) {
2703 : /* extend space */
2704 3851 : len += BLOCK;
2705 : }
2706 16445 : REALLOC(mid->blk.buf, len + 1);
2707 16445 : if (mid->blk.nxt > 0) {
2708 13078 : memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
2709 13078 : mid->blk.end -= mid->blk.nxt;
2710 13078 : mid->blk.nxt = 0;
2711 : }
2712 16445 : mid->blk.lim = len;
2713 : }
2714 :
2715 314281 : s = mid->blk.buf + mid->blk.end;
2716 :
2717 : /* fetch one more block */
2718 314281 : if (mid->trace)
2719 0 : printf("fetch next block: start at:%d\n", mid->blk.end);
2720 314281 : for (;;) {
2721 314281 : len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
2722 314303 : if (len == -1 && mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
2723 0 : mnstr_clearerr(mid->from);
2724 0 : if (mid->oobintr && !mid->active->aborted) {
2725 0 : mid->active->aborted = true;
2726 0 : mnstr_putoob(mid->to, 1);
2727 : }
2728 : } else
2729 : break;
2730 : }
2731 314303 : check_stream(mid, mid->from, len, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
2732 314300 : mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
2733 314300 : mid->blk.buf[mid->blk.end + len] = 0;
2734 314300 : if (mid->trace) {
2735 0 : printf("got next block: length:%zd\n", len);
2736 0 : printf("text:%s\n", mid->blk.buf + mid->blk.end);
2737 : }
2738 314300 : if (len == 0) { /* add prompt */
2739 145635 : if (mnstr_eof(mid->from))
2740 : return NULL;
2741 145632 : if (mid->blk.end > mid->blk.nxt) {
2742 : /* add fake newline since newline was
2743 : * missing from server */
2744 6 : nl = mid->blk.buf + mid->blk.end;
2745 6 : *nl = '\n';
2746 6 : mid->blk.end++;
2747 : }
2748 145632 : len = 2;
2749 145632 : mid->blk.buf[mid->blk.end] = PROMPTBEG;
2750 145632 : mid->blk.buf[mid->blk.end + 1] = '\n';
2751 145632 : mid->blk.buf[mid->blk.end + 2] = 0;
2752 : }
2753 314297 : mid->blk.end += (int) len;
2754 : }
2755 1796676 : if (mid->trace) {
2756 0 : printf("got complete block: \n");
2757 0 : printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
2758 : }
2759 :
2760 : /* we have a complete line in the buffer */
2761 1796676 : assert(nl);
2762 1796676 : *nl++ = 0;
2763 1796676 : reply = mid->blk.buf + mid->blk.nxt;
2764 1796676 : mid->blk.nxt = (int) (nl - mid->blk.buf);
2765 :
2766 1796676 : if (mid->trace)
2767 0 : printf("read_line:%s\n", reply);
2768 : return reply;
2769 : }
2770 :
2771 : /* set or unset the autocommit flag in the server */
2772 : MapiMsg
2773 1143 : mapi_setAutocommit(Mapi mid, bool autocommit)
2774 : {
2775 1143 : if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
2776 : return MOK;
2777 8 : if (!msettings_lang_is_sql(mid->settings)) {
2778 0 : mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
2779 0 : return MERROR;
2780 : }
2781 8 : msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
2782 8 : if (err)
2783 0 : return mapi_setError(mid, err, __func__, MERROR);
2784 8 : if (!mid->connected)
2785 : return MOK;
2786 8 : if (autocommit)
2787 1 : return mapi_Xcommand(mid, "auto_commit", "1");
2788 : else
2789 7 : return mapi_Xcommand(mid, "auto_commit", "0");
2790 : }
2791 :
2792 : MapiMsg
2793 31 : mapi_set_time_zone(Mapi mid, int time_zone)
2794 : {
2795 31 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
2796 31 : if (err)
2797 0 : return mapi_setError(mid, err, __func__, MERROR);
2798 31 : if (!mid->connected)
2799 : return MOK;
2800 :
2801 0 : char buf[100];
2802 0 : if (time_zone < 0)
2803 0 : snprintf(buf, sizeof(buf),
2804 : "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
2805 0 : -time_zone / 3600, (-time_zone % 3600) / 60);
2806 : else
2807 0 : snprintf(buf, sizeof(buf),
2808 : "SET TIME ZONE INTERVAL '+%02d:%02d' HOUR TO MINUTE",
2809 0 : time_zone / 3600, (time_zone % 3600) / 60);
2810 :
2811 0 : MapiHdl hdl = mapi_query(mid, buf);
2812 0 : if (hdl == NULL)
2813 0 : return mid->error;
2814 0 : mapi_close_handle(hdl);
2815 :
2816 0 : return MOK;
2817 : }
2818 :
2819 : MapiMsg
2820 0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
2821 : {
2822 0 : if (mid->columnar_protocol == columnar_protocol)
2823 : return MOK;
2824 0 : mid->columnar_protocol = columnar_protocol;
2825 0 : if (!mid->connected)
2826 : return MOK;
2827 0 : if (columnar_protocol)
2828 0 : return mapi_Xcommand(mid, "columnar_protocol", "1");
2829 : else
2830 0 : return mapi_Xcommand(mid, "columnar_protocol", "0");
2831 : }
2832 :
2833 : MapiMsg
2834 247 : mapi_set_size_header(Mapi mid, bool value)
2835 : {
2836 247 : if (!msettings_lang_is_sql(mid->settings)) {
2837 0 : mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
2838 0 : return MERROR;
2839 : }
2840 247 : if (mid->sizeheader == value)
2841 : return MOK;
2842 119 : mid->sizeheader = value;
2843 119 : if (!mid->connected)
2844 : return MOK;
2845 0 : if (value)
2846 0 : return mapi_Xcommand(mid, "sizeheader", "1");
2847 : else
2848 0 : return mapi_Xcommand(mid, "sizeheader", "0");
2849 : }
2850 :
2851 : MapiMsg
2852 2 : mapi_release_id(Mapi mid, int id)
2853 : {
2854 2 : char buf[10];
2855 :
2856 2 : if (!msettings_lang_is_sql(mid->settings)) {
2857 0 : mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
2858 0 : return MERROR;
2859 : }
2860 2 : snprintf(buf, sizeof(buf), "%d", id);
2861 2 : return mapi_Xcommand(mid, "release", buf);
2862 : }
2863 :
2864 : void
2865 138 : mapi_trace(Mapi mid, bool flag)
2866 : {
2867 138 : mapi_clrError(mid);
2868 138 : mid->trace = flag;
2869 138 : }
2870 :
2871 :
2872 : static int
2873 1491450 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
2874 : {
2875 : /* This function does the actual work for splicing a real,
2876 : multi-column row into columns. It skips over the first
2877 : character and ends at the end of the string or at endchar,
2878 : whichever comes first. */
2879 1491450 : char *start;
2880 1491450 : char **anchors;
2881 1491450 : int i;
2882 1491450 : size_t len;
2883 1491450 : size_t *lens;
2884 :
2885 1491450 : reply++; /* skip over initial char (usually '[') */
2886 1491450 : i = 0;
2887 1491450 : anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
2888 1491254 : lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
2889 5632467 : for (;;) {
2890 5632467 : if (i >= length) {
2891 33756 : length = i + 1;
2892 33756 : REALLOC(anchors, length);
2893 33756 : REALLOC(lens, length);
2894 : }
2895 5632467 : if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
2896 : /* indicate NULL/nil with NULL pointer */
2897 796806 : free(start);
2898 796806 : start = NULL;
2899 796806 : len = 0;
2900 : }
2901 5632467 : lens[i] = len;
2902 5632467 : anchors[i++] = start;
2903 5632467 : if (reply == NULL)
2904 : break;
2905 5632467 : while (*reply && isspace((unsigned char) *reply))
2906 0 : reply++;
2907 5632467 : if (*reply == ',') {
2908 4141013 : reply++;
2909 8282026 : while (*reply && isspace((unsigned char) *reply))
2910 4141013 : reply++;
2911 1491454 : } else if (*reply == 0 || *reply == endchar)
2912 : break;
2913 : }
2914 1491450 : *anchorsp = anchors;
2915 1491450 : *lensp = lens;
2916 1491450 : return i;
2917 : }
2918 :
2919 : static MapiMsg
2920 15142 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
2921 : {
2922 15142 : int i; /* just a counter */
2923 15142 : int64_t n = 0; /* # of tuples being deleted from front */
2924 :
2925 15142 : result->cache.tuplecount = 0;
2926 15142 : for (i = 0; i < result->cache.writer - k; i++) {
2927 0 : if (result->cache.line[i].rows) {
2928 0 : if (result->cache.line[i].rows[0] == '[' ||
2929 : result->cache.line[i].rows[0] == '=')
2930 0 : n++;
2931 0 : free(result->cache.line[i].rows);
2932 : }
2933 0 : result->cache.line[i].rows = result->cache.line[i + k].rows;
2934 0 : result->cache.line[i + k].rows = 0;
2935 0 : if (result->cache.line[i].anchors) {
2936 : int j = 0;
2937 :
2938 0 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2939 0 : free(result->cache.line[i].anchors[j]);
2940 0 : free(result->cache.line[i].anchors);
2941 : }
2942 0 : if (result->cache.line[i].lens)
2943 0 : free(result->cache.line[i].lens);
2944 0 : result->cache.line[i].anchors = result->cache.line[i + k].anchors;
2945 0 : result->cache.line[i + k].anchors = 0;
2946 0 : result->cache.line[i].lens = result->cache.line[i + k].lens;
2947 0 : result->cache.line[i + k].lens = 0;
2948 0 : result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
2949 0 : if (result->cache.line[i].rows &&
2950 0 : (result->cache.line[i].rows[0] == '[' ||
2951 : result->cache.line[i].rows[0] == '=')) {
2952 0 : result->cache.line[i].tuplerev = result->cache.tuplecount;
2953 0 : result->cache.line[result->cache.tuplecount++].tupleindex = i;
2954 : }
2955 : }
2956 : /* after the previous loop, i == result->cache.writer - k, and
2957 : the last (result->cache.writer - k) cache entries have been
2958 : cleared already , so we don't need to go the Full Monty
2959 : here */
2960 1371070 : for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
2961 1355928 : if (result->cache.line[i].rows) {
2962 1355928 : if (result->cache.line[i].rows[0] == '[' ||
2963 : result->cache.line[i].rows[0] == '=')
2964 1354868 : n++;
2965 1355928 : free(result->cache.line[i].rows);
2966 : }
2967 1355928 : result->cache.line[i].rows = 0;
2968 1355928 : if (result->cache.line[i].anchors) {
2969 : int j = 0;
2970 :
2971 6546788 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2972 5191920 : free(result->cache.line[i].anchors[j]);
2973 1354868 : free(result->cache.line[i].anchors);
2974 : }
2975 1355928 : if (result->cache.line[i].lens)
2976 1354868 : free(result->cache.line[i].lens);
2977 1355928 : result->cache.line[i].anchors = 0;
2978 1355928 : result->cache.line[i].lens = 0;
2979 1355928 : result->cache.line[i].fldcnt = 0;
2980 : }
2981 15142 : result->cache.reader -= k;
2982 15142 : if (result->cache.reader < 0)
2983 15142 : result->cache.reader = -1;
2984 15142 : result->cache.writer -= k;
2985 15142 : if (result->cache.writer < 0) /* "cannot happen" */
2986 0 : result->cache.writer = 0;
2987 15142 : result->cache.first += n;
2988 :
2989 15142 : return MOK;
2990 : }
2991 :
2992 : static void
2993 44184 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
2994 : {
2995 44184 : int incr, newsize, oldsize = result->cache.limit, i;
2996 :
2997 : /* if there are read entries, delete them */
2998 44184 : if (result->cache.reader >= 0) {
2999 13527 : mapi_cache_freeup_internal(result, result->cache.reader + 1);
3000 : /* since we've made space, we can return */
3001 13527 : return;
3002 : }
3003 :
3004 : /* extend row cache */
3005 30657 : retry:;
3006 30659 : if (oldsize == 0)
3007 : incr = 100;
3008 : else
3009 8 : incr = oldsize * 2;
3010 8 : if (incr > 200000)
3011 0 : incr = 20000;
3012 30659 : newsize = oldsize + incr;
3013 30659 : if (result->cache.rowlimit > 0 &&
3014 4 : newsize > result->cache.rowlimit &&
3015 : !cacheall) {
3016 4 : newsize = result->cache.rowlimit;
3017 4 : incr = newsize - oldsize;
3018 4 : if (incr <= 0) {
3019 : /* not enough space, so increase limit and try again */
3020 2 : result->cache.rowlimit += 100;
3021 2 : goto retry;
3022 : }
3023 : }
3024 :
3025 30657 : REALLOC(result->cache.line, newsize + 1);
3026 30657 : assert(result->cache.line);
3027 3129026 : for (i = oldsize; i <= newsize; i++) {
3028 3098369 : result->cache.line[i].fldcnt = 0;
3029 3098369 : result->cache.line[i].rows = NULL;
3030 3098369 : result->cache.line[i].tupleindex = -1;
3031 3098369 : result->cache.line[i].tuplerev = -1;
3032 3098369 : result->cache.line[i].anchors = NULL;
3033 3098369 : result->cache.line[i].lens = NULL;
3034 : }
3035 30657 : result->cache.limit = newsize;
3036 : }
3037 :
3038 : /* store a line in the cache */
3039 : static void
3040 1512779 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
3041 : {
3042 : /* manage the row cache space first */
3043 1512779 : if (result->cache.writer >= result->cache.limit)
3044 44184 : mapi_extend_cache(result, cacheall);
3045 :
3046 1512779 : result->cache.line[result->cache.writer].rows = line;
3047 1512779 : result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
3048 1512779 : result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
3049 1512779 : if (*line == '[' || *line == '=') {
3050 1390628 : result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
3051 1390628 : if (result->row_count < result->cache.first + result->cache.tuplecount)
3052 208 : result->row_count = result->cache.first + result->cache.tuplecount;
3053 : }
3054 1512779 : result->cache.writer++;
3055 1512779 : }
3056 :
3057 : static struct MapiResultSet *
3058 155470 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
3059 : {
3060 155470 : char *tag, *etag;
3061 155470 : int i, n;
3062 155470 : char **anchors;
3063 155470 : size_t *lens;
3064 :
3065 155470 : if (line[0] == '&') {
3066 33322 : char *nline = line;
3067 33322 : int qt;
3068 33322 : uint64_t queryid;
3069 :
3070 : /* handle fields &qt */
3071 :
3072 33322 : nline++; /* query type */
3073 33322 : qt = (int) strtol(nline, &nline, 0);
3074 :
3075 33322 : if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
3076 33295 : result = new_result(hdl);
3077 33322 : result->querytype = qt;
3078 33322 : result->commentonly = false;
3079 33322 : result->querytime = 0;
3080 33322 : result->maloptimizertime = 0;
3081 33322 : result->sqloptimizertime = 0;
3082 :
3083 33322 : nline++; /* skip space */
3084 33322 : switch (qt) {
3085 500 : case Q_SCHEMA:
3086 500 : result->querytime = strtoll(nline, &nline, 10);
3087 500 : result->maloptimizertime = strtoll(nline, &nline, 10);
3088 500 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3089 500 : break;
3090 183 : case Q_TRANS:
3091 183 : msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
3092 183 : break;
3093 2165 : case Q_UPDATE:
3094 2165 : result->row_count = strtoll(nline, &nline, 10);
3095 2165 : result->last_id = strtoll(nline, &nline, 10);
3096 2165 : queryid = strtoll(nline, &nline, 10);
3097 2165 : result->querytime = strtoll(nline, &nline, 10);
3098 2165 : result->maloptimizertime = strtoll(nline, &nline, 10);
3099 2165 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3100 2165 : break;
3101 30443 : case Q_TABLE:
3102 30443 : if (sscanf(nline,
3103 : "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
3104 : " %" SCNd64 " %" SCNd64 " %" SCNd64,
3105 : &result->tableid, &result->row_count,
3106 : &result->fieldcnt, &result->tuple_count,
3107 : &queryid, &result->querytime,
3108 : &result->maloptimizertime,
3109 : &result->sqloptimizertime) < 8){
3110 2 : result->querytime = 0;
3111 2 : result->maloptimizertime = 0;
3112 2 : result->sqloptimizertime = 0;
3113 : }
3114 : (void) queryid; /* ignored for now */
3115 : break;
3116 4 : case Q_PREPARE:
3117 4 : sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
3118 : &result->tableid, &result->row_count,
3119 : &result->fieldcnt, &result->tuple_count);
3120 4 : break;
3121 27 : case Q_BLOCK:
3122 : /* Mapi ignores the Q_BLOCK header, so spoof
3123 : * the querytype back to a Q_TABLE to let it
3124 : * go unnoticed */
3125 27 : result->querytype = Q_TABLE;
3126 27 : break;
3127 : }
3128 :
3129 :
3130 33322 : if (result->fieldcnt > result->maxfields) {
3131 30447 : REALLOC(result->fields, result->fieldcnt);
3132 30447 : memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
3133 30447 : result->maxfields = result->fieldcnt;
3134 : }
3135 :
3136 : /* start of new SQL result */
3137 33322 : return result;
3138 : }
3139 122148 : if (result == NULL)
3140 4 : result = new_result(hdl);
3141 :
3142 122148 : if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
3143 : /* comment */
3144 : return result;
3145 : }
3146 :
3147 122148 : line = strdup(line); /* make copy we can play with */
3148 122148 : etag = strrchr(line, '#');
3149 122148 : if (etag == 0 || etag == line) {
3150 : /* not a useful header line */
3151 0 : free(line);
3152 0 : return result;
3153 : }
3154 :
3155 122148 : n = slice_row(line, NULL, &anchors, &lens, 10, '#');
3156 :
3157 122148 : result->commentonly = false;
3158 :
3159 122148 : tag = etag + 1;
3160 244288 : while (*tag && isspace((unsigned char) *tag))
3161 122140 : tag++;
3162 :
3163 122148 : if (n > result->fieldcnt) {
3164 8 : result->fieldcnt = n;
3165 8 : if (n > result->maxfields) {
3166 8 : REALLOC(result->fields, n);
3167 8 : memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
3168 8 : result->maxfields = n;
3169 : }
3170 : }
3171 :
3172 122148 : if (strcmp(tag, "name") == 0) {
3173 30447 : result->fieldcnt = n;
3174 109151 : for (i = 0; i < n; i++) {
3175 78704 : if (anchors[i]) {
3176 78704 : if (result->fields[i].columnname)
3177 0 : free(result->fields[i].columnname);
3178 78704 : result->fields[i].columnname = anchors[i];
3179 78704 : anchors[i] = NULL;
3180 : }
3181 : }
3182 91701 : } else if (strcmp(tag, "type") == 0) {
3183 30451 : result->fieldcnt = n;
3184 109163 : for (i = 0; i < n; i++) {
3185 78712 : if (anchors[i]) {
3186 78712 : if (result->fields[i].columntype)
3187 0 : free(result->fields[i].columntype);
3188 78712 : result->fields[i].columntype = anchors[i];
3189 78712 : anchors[i] = NULL;
3190 : }
3191 : }
3192 61250 : } else if (strcmp(tag, "length") == 0) {
3193 30447 : result->fieldcnt = n;
3194 109151 : for (i = 0; i < n; i++) {
3195 78704 : if (anchors[i])
3196 78704 : result->fields[i].columnlength = atoi(anchors[i]);
3197 : }
3198 30803 : } else if (strcmp(tag, "table_name") == 0) {
3199 30447 : result->fieldcnt = n;
3200 109151 : for (i = 0; i < n; i++) {
3201 78704 : if (anchors[i]) {
3202 78704 : if (result->fields[i].tablename)
3203 0 : free(result->fields[i].tablename);
3204 78704 : result->fields[i].tablename = anchors[i];
3205 78704 : anchors[i] = NULL;
3206 : }
3207 : }
3208 356 : } else if (strcmp(tag, "typesizes") == 0) {
3209 348 : result->fieldcnt = n;
3210 3069 : for (i = 0; i < n; i++) {
3211 2721 : if (anchors[i]) {
3212 2721 : char *p;
3213 2721 : result->fields[i].digits = atoi(anchors[i]);
3214 2721 : p = strchr(anchors[i], ' ');
3215 2721 : if (p)
3216 2721 : result->fields[i].scale = atoi(p + 1);
3217 : }
3218 : }
3219 : }
3220 :
3221 : /* clean up */
3222 122148 : free(line);
3223 439701 : for (i = 0; i < n; i++)
3224 317553 : if (anchors[i])
3225 81433 : free(anchors[i]);
3226 122148 : free(anchors);
3227 122148 : free(lens);
3228 :
3229 122148 : return result;
3230 : }
3231 :
3232 : static void
3233 75 : write_file(MapiHdl hdl, char *filename, bool binary)
3234 : {
3235 75 : Mapi mid = hdl->mid;
3236 75 : char *line;
3237 75 : char data[BLOCK];
3238 75 : ssize_t len;
3239 :
3240 75 : (void) read_line(mid); /* read flush marker */
3241 75 : if (filename == NULL) {
3242 : /* malloc failure */
3243 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3244 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3245 0 : return;
3246 : }
3247 75 : if (mid->putfilecontent == NULL) {
3248 0 : free(filename);
3249 0 : mnstr_printf(mid->to, "!HY000!cannot send files\n");
3250 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3251 0 : return;
3252 : }
3253 75 : line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
3254 75 : free(filename);
3255 75 : if (line != NULL) {
3256 0 : if (strchr(line, '\n'))
3257 0 : line = "incorrect response from application";
3258 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", line);
3259 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3260 0 : return;
3261 : }
3262 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3263 180811 : for (;;) {
3264 180811 : len = mnstr_read(mid->from, data, 1, sizeof(data));
3265 180811 : if (len == -1) {
3266 0 : if (mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
3267 0 : mnstr_clearerr(mid->from);
3268 0 : if (mid->oobintr && !hdl->aborted) {
3269 0 : hdl->aborted = true;
3270 0 : mnstr_putoob(mid->to, 1);
3271 : }
3272 : } else {
3273 : break;
3274 : }
3275 180811 : } else if (len == 0) {
3276 : break;
3277 180736 : } else if (line == NULL) {
3278 180736 : line = mid->putfilecontent(mid->filecontentprivate,
3279 : NULL, binary, data, len);
3280 : }
3281 : }
3282 75 : if (line == NULL)
3283 75 : line = mid->putfilecontent(mid->filecontentprivate,
3284 : NULL, binary, NULL, 0);
3285 75 : if (line && strchr(line, '\n'))
3286 : line = "incorrect response from application";
3287 75 : mnstr_printf(mid->to, "%s\n", line ? line : "");
3288 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3289 : }
3290 :
3291 : #define MiB (1 << 20) /* a megabyte */
3292 :
3293 : static void
3294 104 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
3295 : {
3296 104 : Mapi mid = hdl->mid;
3297 104 : size_t size = 0, flushsize = 0;
3298 104 : char *data, *line;
3299 :
3300 104 : (void) read_line(mid); /* read flush marker */
3301 104 : if (filename == NULL) {
3302 : /* malloc failure */
3303 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3304 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3305 20 : return;
3306 : }
3307 104 : if (mid->getfilecontent == NULL) {
3308 0 : free(filename);
3309 0 : mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
3310 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3311 0 : return;
3312 : }
3313 104 : data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
3314 : off, &size);
3315 104 : free(filename);
3316 104 : if (data != NULL && size == 0) {
3317 0 : if (strchr(data, '\n'))
3318 0 : data = "incorrect response from application";
3319 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", data);
3320 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3321 0 : return;
3322 : }
3323 104 : mnstr_printf(mid->to, "\n");
3324 23786 : while (data != NULL && size != 0) {
3325 23683 : if (flushsize >= MiB) {
3326 : /* after every MiB give the server the
3327 : * opportunity to stop reading more data */
3328 1414 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3329 : /* at this point we expect to get a PROMPT2 if
3330 : * the server wants more data, or a PROMPT3 if
3331 : * the server had enough; anything else is a
3332 : * protocol violation */
3333 1414 : line = read_line(mid);
3334 1414 : if (line == NULL) {
3335 : /* error */
3336 0 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3337 0 : return;
3338 : }
3339 1414 : assert(line[0] == PROMPTBEG);
3340 1414 : if (line[0] != PROMPTBEG) {
3341 : /* error in protocol */
3342 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3343 : return;
3344 : }
3345 1414 : if (line[1] == PROMPT3[1]) {
3346 : /* done reading: close file */
3347 1 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3348 1 : (void) read_line(mid);
3349 1 : return;
3350 : }
3351 1413 : assert(line[1] == PROMPT2[1]);
3352 1413 : if (line[1] != PROMPT2[1]) {
3353 : /* error in protocol */
3354 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3355 : return;
3356 : }
3357 : /* clear the flush marker */
3358 1413 : (void) read_line(mid);
3359 1413 : flushsize = 0;
3360 : }
3361 23682 : if (size > MiB) {
3362 0 : if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
3363 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3364 0 : return;
3365 : }
3366 0 : size -= MiB;
3367 0 : data += MiB;
3368 0 : flushsize += MiB;
3369 : } else {
3370 23682 : if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
3371 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3372 0 : return;
3373 : }
3374 23682 : flushsize += size;
3375 23682 : data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
3376 : }
3377 : }
3378 103 : if (data != NULL && size == 0) {
3379 : /* some error occurred */
3380 0 : mnstr_clearerr(mid->from);
3381 0 : if (mid->oobintr && !hdl->aborted) {
3382 0 : hdl->aborted = true;
3383 0 : mnstr_putoob(mid->to, 1);
3384 : }
3385 : }
3386 103 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3387 103 : line = read_line(mid);
3388 103 : if (line == NULL)
3389 : return;
3390 103 : assert(line[0] == PROMPTBEG);
3391 103 : if (line[0] != PROMPTBEG)
3392 : return;
3393 103 : if (line[1] == PROMPT3[1]) {
3394 19 : (void) read_line(mid);
3395 19 : return;
3396 : }
3397 84 : assert(line[1] == PROMPT2[1]);
3398 84 : if (line[1] != PROMPT2[1])
3399 : return;
3400 84 : (void) read_line(mid);
3401 84 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3402 84 : line = read_line(mid);
3403 84 : if (line == NULL)
3404 : return;
3405 84 : assert(line[0] == PROMPTBEG);
3406 84 : assert(line[1] == PROMPT3[1]);
3407 84 : (void) read_line(mid);
3408 : }
3409 :
3410 :
3411 : /* Read ahead and cache data read. Depending on the second argument,
3412 : reading may stop at the first non-header and non-error line, or at
3413 : a prompt.
3414 : This function is called either after a command has been sent to the
3415 : server (in which case the second argument is 1), when the
3416 : application asks for a result tuple that hadn't been cached yet (in
3417 : which case the second argument is also 1), or whenever all pending
3418 : data needs to be read in order to send a new command to the server
3419 : (in which case the second argument is 0).
3420 : Header lines result tuples are stored in the cache. Certain header
3421 : lines may cause a new result set to be created in which case all
3422 : subsequent lines are added to that result set.
3423 : */
3424 : MapiMsg
3425 1532672 : read_into_cache(MapiHdl hdl, int lookahead)
3426 : {
3427 1532672 : char *line;
3428 1532672 : Mapi mid;
3429 1532672 : struct MapiResultSet *result;
3430 :
3431 1532672 : mid = hdl->mid;
3432 1532672 : assert(mid->active == hdl);
3433 1532672 : if (hdl->needmore) {
3434 0 : hdl->needmore = false;
3435 0 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3436 0 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
3437 : }
3438 1532672 : if ((result = hdl->active) == NULL)
3439 143816 : result = hdl->result; /* may also be NULL */
3440 :
3441 1690169 : for (;;) {
3442 1690169 : line = read_line(mid);
3443 1690172 : if (line == NULL) {
3444 0 : if (mid->from && mnstr_eof(mid->from)) {
3445 0 : return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
3446 : }
3447 :
3448 0 : return mid->error;
3449 : }
3450 1690172 : switch (*line) {
3451 144028 : case PROMPTBEG: /* \001 */
3452 144028 : mid->active = NULL;
3453 144028 : hdl->active = NULL;
3454 : /* set needmore flag if line equals PROMPT2 up
3455 : to newline */
3456 144028 : if (line[1] == PROMPT2[1] && line[2] == '\0') {
3457 : /* skip end of block */
3458 102932 : mid->active = hdl;
3459 102932 : (void) read_line(mid);
3460 102932 : hdl->needmore = true;
3461 102932 : mid->active = hdl;
3462 41096 : } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
3463 179 : mid->active = hdl;
3464 179 : line = read_line(mid);
3465 179 : bool binary = false;
3466 : /* rb FILE
3467 : * r OFF FILE
3468 : * w FILE
3469 : * wb FILE
3470 : */
3471 179 : switch (*line++) {
3472 104 : case 'r': {
3473 104 : uint64_t off = 0;
3474 104 : if (*line == 'b') {
3475 85 : line++;
3476 85 : binary = true;
3477 : } else {
3478 19 : off = strtoul(line, &line, 10);
3479 : }
3480 104 : if (*line++ != ' ') {
3481 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3482 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3483 0 : break;
3484 : }
3485 104 : read_file(hdl, off, strdup(line), binary);
3486 104 : break;
3487 : }
3488 75 : case 'w':
3489 75 : if (*line == 'b') {
3490 75 : line++;
3491 75 : binary = true;
3492 : }
3493 75 : if (*line++ != ' ') {
3494 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3495 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3496 0 : break;
3497 : }
3498 75 : write_file(hdl, strdup(line), binary);
3499 75 : break;
3500 : }
3501 179 : continue;
3502 : }
3503 143849 : return mid->error;
3504 43 : case '!':
3505 : /* start a new result set if we don't have one
3506 : yet (duh!), or if we've already seen
3507 : normal output for the current one */
3508 43 : if (result == NULL ||
3509 2 : result->cache.writer > 0 ||
3510 2 : result->querytype > 0) {
3511 41 : result = new_result(hdl);
3512 41 : result->commentonly = false;
3513 41 : hdl->active = result;
3514 : }
3515 43 : add_error(result, line + 1 /* skip ! */ );
3516 43 : if (!mid->error)
3517 41 : mid->error = MSERVER;
3518 : break;
3519 155470 : case '%':
3520 : case '#':
3521 : case '&':
3522 155470 : if (lookahead < 0)
3523 0 : lookahead = 1;
3524 155470 : result = parse_header_line(hdl, line, result);
3525 155470 : hdl->active = result;
3526 155470 : if (result && *line != '&')
3527 122148 : add_cache(result, strdup(line), !lookahead);
3528 : break;
3529 1390631 : default:
3530 1390631 : if (result == NULL) {
3531 197 : result = new_result(hdl);
3532 197 : hdl->active = result;
3533 : }
3534 1390631 : add_cache(result, strdup(line), !lookahead);
3535 1390631 : if (lookahead > 0 &&
3536 1388853 : (result->querytype == -1 /* unknown (not SQL) */ ||
3537 27 : result->querytype == Q_TABLE ||
3538 : result->querytype == Q_UPDATE)) {
3539 1388826 : return mid->error;
3540 : }
3541 : break;
3542 : }
3543 : }
3544 : }
3545 :
3546 : static MapiMsg
3547 33111 : mapi_execute_internal(MapiHdl hdl)
3548 : {
3549 33111 : size_t size;
3550 33111 : char *cmd;
3551 33111 : Mapi mid;
3552 :
3553 33111 : mid = hdl->mid;
3554 33111 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3555 : return MERROR;
3556 33111 : assert(mid->active == NULL);
3557 33111 : finish_handle(hdl);
3558 33110 : mapi_param_store(hdl);
3559 33109 : cmd = hdl->query;
3560 33109 : if (cmd == NULL)
3561 : return MERROR;
3562 33109 : size = strlen(cmd);
3563 :
3564 33109 : bool is_sql = msettings_lang_is_sql(mid->settings);
3565 33114 : char *prefix = is_sql ? "s" : "";
3566 4258 : char *suffix = is_sql ? "\n;" : "";
3567 33114 : mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
3568 :
3569 33118 : if (is_sql) {
3570 : /* indicate to server this is a SQL command */
3571 28851 : ssize_t w = mnstr_write(mid->to, "s", 1, 1);
3572 28851 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3573 : }
3574 33118 : ssize_t w = mnstr_write(mid->to, cmd, 1, size);
3575 33108 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3576 : /* all SQL statements should end with a semicolon */
3577 : /* for the other languages it is assumed that the statements are correct */
3578 33108 : if (is_sql) {
3579 28851 : w = mnstr_write(mid->to, "\n;", 2, 1);
3580 28851 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3581 : }
3582 33108 : w = mnstr_write(mid->to, "\n", 1, 1);
3583 33110 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3584 33111 : w = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3585 33110 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3586 33111 : mid->active = hdl;
3587 :
3588 33111 : return MOK;
3589 : }
3590 :
3591 : MapiMsg
3592 0 : mapi_execute(MapiHdl hdl)
3593 : {
3594 0 : int ret;
3595 :
3596 0 : mapi_hdl_check(hdl);
3597 0 : if ((ret = mapi_execute_internal(hdl)) == MOK)
3598 0 : return read_into_cache(hdl, 1);
3599 :
3600 : return ret;
3601 : }
3602 :
3603 : /*
3604 : * The routine mapi_query is one of the most heavily used ones.
3605 : * It sends a complete statement for execution
3606 : * (i.e., ending in a newline; possibly including additional newlines).
3607 : * Interaction with the server is sped up using block based interaction.
3608 : * The query is retained in the Mapi structure to repeat shipping.
3609 : */
3610 : MapiHdl
3611 30856 : mapi_query(Mapi mid, const char *cmd)
3612 : {
3613 30856 : int ret;
3614 30856 : MapiHdl hdl;
3615 :
3616 30856 : mapi_check0(mid);
3617 30856 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3618 30871 : ret = mid->error;
3619 30871 : if (ret == MOK)
3620 30865 : ret = mapi_execute_internal(hdl);
3621 30853 : if (ret == MOK)
3622 30856 : ret = read_into_cache(hdl, 1);
3623 : return hdl;
3624 : }
3625 :
3626 : /* version of mapi_query that does not wait for a response */
3627 : MapiHdl
3628 0 : mapi_send(Mapi mid, const char *cmd)
3629 : {
3630 0 : int ret;
3631 0 : MapiHdl hdl;
3632 :
3633 0 : mapi_check0(mid);
3634 0 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3635 0 : ret = mid->error;
3636 0 : if (ret == MOK)
3637 0 : ret = mapi_execute_internal(hdl);
3638 : return hdl;
3639 : }
3640 :
3641 : MapiMsg
3642 0 : mapi_read_response(MapiHdl hdl)
3643 : {
3644 0 : return read_into_cache(hdl, 1);
3645 : }
3646 :
3647 : MapiMsg
3648 2257 : mapi_query_handle(MapiHdl hdl, const char *cmd)
3649 : {
3650 2257 : int ret;
3651 :
3652 2257 : mapi_hdl_check(hdl);
3653 2257 : if (finish_handle(hdl) != MOK)
3654 : return MERROR;
3655 2257 : prepareQuery(hdl, cmd);
3656 2257 : ret = hdl->mid->error;
3657 2257 : if (ret == MOK)
3658 2257 : ret = mapi_execute_internal(hdl);
3659 2257 : if (ret == MOK)
3660 2257 : ret = read_into_cache(hdl, 1);
3661 : return ret;
3662 : }
3663 :
3664 : MapiHdl
3665 4915 : mapi_query_prep(Mapi mid)
3666 : {
3667 4915 : mapi_check0(mid);
3668 4915 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3669 : return NULL;
3670 4915 : assert(mid->active == NULL);
3671 4915 : if (msettings_lang_is_sql(mid->settings)) {
3672 : /* indicate to server this is a SQL command */
3673 4915 : mnstr_write(mid->to, "S", 1, 1);
3674 4915 : mapi_log_data(mid, "SEND", "S", 1);
3675 : }
3676 4915 : return (mid->active = mapi_new_handle(mid));
3677 : }
3678 :
3679 : MapiMsg
3680 107651 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
3681 : {
3682 107651 : Mapi mid;
3683 :
3684 107651 : mapi_hdl_check(hdl);
3685 107651 : mid = hdl->mid;
3686 107651 : assert(mid->active == NULL || mid->active == hdl);
3687 107651 : mid->active = hdl;
3688 : /* remember the query just for the error messages */
3689 107651 : if (hdl->query == NULL) {
3690 4726 : hdl->query = malloc(size + 1);
3691 4726 : if (hdl->query) {
3692 4726 : strcpy_len(hdl->query, query, size + 1);
3693 : }
3694 : } else {
3695 102925 : size_t sz = strlen(hdl->query);
3696 102925 : char *q;
3697 :
3698 102925 : if (sz < 512 &&
3699 1437 : (q = realloc(hdl->query, sz + size + 1)) != NULL) {
3700 1437 : strcpy_len(q + sz, query, size + 1);
3701 1437 : hdl->query = q;
3702 : }
3703 : }
3704 :
3705 107651 : if (mid->trace) {
3706 0 : printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
3707 : }
3708 107651 : hdl->needmore = false;
3709 107651 : size = mnstr_write(mid->to, query, 1, size);
3710 107651 : if (mid->tracelog) {
3711 0 : mnstr_write(mid->tracelog, query, 1, size);
3712 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
3713 : }
3714 107651 : check_stream(mid, mid->to, size, "write error on stream", mid->error);
3715 107651 : return mid->error;
3716 : }
3717 :
3718 : MapiMsg
3719 107847 : mapi_query_done(MapiHdl hdl)
3720 : {
3721 107847 : int ret;
3722 107847 : Mapi mid;
3723 :
3724 107847 : mapi_hdl_check(hdl);
3725 107847 : mid = hdl->mid;
3726 107847 : assert(mid->active == NULL || mid->active == hdl);
3727 107847 : mid->active = hdl;
3728 107847 : hdl->needmore = false;
3729 107847 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3730 107847 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
3731 107847 : ret = mid->error;
3732 107847 : if (ret == MOK)
3733 107847 : ret = read_into_cache(hdl, 1);
3734 107847 : return ret == MOK && hdl->needmore ? MMORE : ret;
3735 : }
3736 :
3737 : MapiMsg
3738 0 : mapi_query_abort(MapiHdl hdl, int reason)
3739 : {
3740 0 : Mapi mid;
3741 :
3742 0 : assert(reason > 0 && reason <= 127);
3743 0 : mapi_hdl_check(hdl);
3744 0 : mid = hdl->mid;
3745 0 : assert(mid->active == NULL || mid->active == hdl);
3746 0 : if (mid->oobintr && !hdl->aborted && mnstr_putoob(mid->to, reason) == 0) {
3747 0 : hdl->aborted = true;
3748 0 : return MOK;
3749 : }
3750 : return MERROR;
3751 : }
3752 :
3753 : MapiMsg
3754 257 : mapi_cache_limit(Mapi mid, int limit)
3755 : {
3756 : /* clean out superfluous space TODO */
3757 257 : msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
3758 257 : if (err)
3759 0 : return mapi_setError(mid, err, __func__, MERROR);
3760 257 : if (!mid->connected)
3761 : return MOK;
3762 121 : mapi_check(mid);
3763 : /* if (hdl->cache.rowlimit < hdl->cache.limit) { */
3764 : /* TODO: decide what to do here */
3765 : /* hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
3766 : /* if (hdl->cache.writer > hdl->cache.limit) { */
3767 : /* hdl->cache.writer = hdl->cache.limit; */
3768 : /* if (hdl->cache.reader > hdl->cache.writer) */
3769 : /* hdl->cache.reader = hdl->cache.writer; */
3770 : /* } */
3771 : /* } */
3772 121 : if (msettings_lang_is_sql(mid->settings)) {
3773 121 : MapiHdl hdl;
3774 :
3775 121 : if (mid->active)
3776 1 : read_into_cache(mid->active, 0);
3777 :
3778 121 : mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
3779 242 : if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
3780 121 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
3781 0 : close_connection(mid);
3782 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
3783 0 : return MERROR;
3784 : }
3785 121 : hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
3786 121 : if (hdl == NULL)
3787 : return MERROR;
3788 121 : mid->active = hdl;
3789 121 : read_into_cache(hdl, 0);
3790 121 : mapi_close_handle(hdl); /* reads away any output */
3791 : }
3792 : return MOK;
3793 : }
3794 :
3795 : MapiMsg
3796 0 : mapi_fetch_reset(MapiHdl hdl)
3797 : {
3798 0 : mapi_hdl_check(hdl);
3799 0 : if (hdl->result)
3800 0 : hdl->result->cache.reader = -1;
3801 : return MOK;
3802 : }
3803 :
3804 : MapiMsg
3805 2743 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
3806 : {
3807 2743 : struct MapiResultSet *result;
3808 :
3809 2743 : mapi_hdl_check(hdl);
3810 2743 : result = hdl->result;
3811 2743 : switch (whence) {
3812 : case MAPI_SEEK_SET:
3813 : break;
3814 0 : case MAPI_SEEK_CUR:
3815 0 : rownr += result->cache.line[result->cache.reader + 1].tuplerev;
3816 0 : break;
3817 0 : case MAPI_SEEK_END:
3818 0 : if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3819 : return MERROR;
3820 0 : rownr += result->row_count;
3821 0 : break;
3822 0 : default:
3823 0 : return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
3824 : }
3825 2743 : if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3826 : return MERROR;
3827 2743 : if (rownr < 0 || rownr > result->row_count)
3828 0 : return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
3829 2743 : if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
3830 : /* we've got the requested tuple in the cache */
3831 1128 : result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
3832 : } else {
3833 : /* we don't have the requested tuple in the cache
3834 : reset the cache and at the next fetch we'll get the data */
3835 1615 : if (mapi_cache_freeup(hdl, 100) == MOK) {
3836 1615 : result->cache.first = rownr;
3837 : }
3838 : }
3839 2743 : return hdl->mid->error;
3840 : }
3841 :
3842 : /* Make space in the cache for new tuples, ignore the read pointer */
3843 : MapiMsg
3844 1615 : mapi_cache_freeup(MapiHdl hdl, int percentage)
3845 : {
3846 1615 : struct MapiResultSet *result;
3847 1615 : int k; /* # of cache lines to be deleted from front */
3848 :
3849 1615 : mapi_hdl_check(hdl);
3850 1615 : result = hdl->result;
3851 1615 : if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
3852 : return MOK;
3853 1615 : if (percentage < 0 || percentage > 100)
3854 0 : percentage = 100;
3855 1615 : k = (result->cache.writer * percentage) / 100;
3856 1615 : if (k < 1)
3857 0 : k = 1;
3858 1615 : return mapi_cache_freeup_internal(result, k);
3859 : }
3860 :
3861 : static char *
3862 1541040 : mapi_fetch_line_internal(MapiHdl hdl)
3863 : {
3864 1541040 : Mapi mid;
3865 1541040 : struct MapiResultSet *result;
3866 1541040 : char *reply;
3867 :
3868 : /* try to read a line from the cache */
3869 1541040 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
3870 1394339 : mid = hdl->mid;
3871 1394339 : if (mid->active != hdl || hdl->needmore)
3872 : return NULL;
3873 :
3874 1388303 : if (read_into_cache(hdl, 1) != MOK)
3875 : return NULL;
3876 1388303 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
3877 : return NULL;
3878 : }
3879 1510876 : reply = result->cache.line[++result->cache.reader].rows;
3880 1510876 : if (hdl->bindings && (*reply == '[' || *reply == '=')) {
3881 0 : mapi_slice_row(result, result->cache.reader);
3882 0 : mapi_store_bind(result, result->cache.reader);
3883 : }
3884 : return reply;
3885 : }
3886 :
3887 : /*
3888 : * The routine mapi_fetch_line forms the basic interaction with the server.
3889 : * It simply retrieves the next line and stores it in the row cache.
3890 : * The field anchor structure is prepared for subsequent use by
3891 : * mapi_fetch_row.
3892 : * The content received is analyzed further by mapi_getRow()
3893 : */
3894 : char *
3895 1541013 : mapi_fetch_line(MapiHdl hdl)
3896 : {
3897 1541013 : char *reply;
3898 1541013 : struct MapiResultSet *result;
3899 :
3900 1541013 : mapi_hdl_check0(hdl);
3901 1541013 : reply = mapi_fetch_line_internal(hdl);
3902 1541013 : if (reply == NULL &&
3903 60328 : (result = hdl->result) != NULL &&
3904 30164 : msettings_lang_is_sql(hdl->mid->settings) &&
3905 30153 : result->querytype == Q_TABLE &&
3906 30153 : result->row_count > 0 &&
3907 24258 : result->cache.first + result->cache.tuplecount < result->row_count) {
3908 27 : if (hdl->needmore) /* escalate */
3909 : return NULL;
3910 27 : if (hdl->mid->active != NULL)
3911 0 : read_into_cache(hdl->mid->active, 0);
3912 27 : hdl->mid->active = hdl;
3913 27 : hdl->active = result;
3914 27 : mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
3915 : result->tableid,
3916 : result->cache.first + result->cache.tuplecount);
3917 27 : int e;
3918 27 : if ((e = mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
3919 : result->tableid,
3920 27 : result->cache.first + result->cache.tuplecount)) < 0 ||
3921 27 : (e = mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA)) < 0)
3922 0 : check_stream(hdl->mid, hdl->mid->to, e, "sending export command", NULL);
3923 27 : reply = mapi_fetch_line_internal(hdl);
3924 : }
3925 : return reply;
3926 : }
3927 :
3928 : /*
3929 : * To synchronize on a prompt, the low level routine mapi_finish can be used.
3930 : * It discards all output received.
3931 : */
3932 : MapiMsg
3933 0 : mapi_finish(MapiHdl hdl)
3934 : {
3935 0 : mapi_hdl_check(hdl);
3936 0 : return finish_handle(hdl);
3937 : }
3938 :
3939 : /* msg is a string consisting comma-separated values. The list of
3940 : values is terminated by endchar or by the end-of-string NULL byte.
3941 : Values can be quoted strings or unquoted values. Upon return,
3942 : *start points to the start of the first value which is stripped of
3943 : leading and trailing white space, and if it was a quoted string,
3944 : also of the quotes. Also, backslash-escaped characters in the
3945 : quoted string are replaced by the values the escapes represent.
3946 : *next points to either the start of the next value (i.e. after the
3947 : separating comma, possibly to the leading white space of the next
3948 : value), or to the trailing ] or NULL byte if this was the last
3949 : value. *lenp is the number of bytes occupied by the (possibly
3950 : converted) value, excluding final NULL byte.
3951 : msg is *not* a const string: it is altered by this function.
3952 : The function returns true if the string was quoted.
3953 : */
3954 : static int
3955 5632467 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
3956 : {
3957 5632467 : const char *p = msg;
3958 5632467 : char quote;
3959 :
3960 : /* first skip over leading white space */
3961 7123909 : while (*p && isspace((unsigned char) *p))
3962 1491442 : p++;
3963 5632467 : quote = *p;
3964 5632467 : if (quote == '\'' || quote == '"') {
3965 4334224 : size_t len = 0;
3966 4334224 : char *s, *start;
3967 :
3968 : /* get quoted string and remove trailing bracket first */
3969 4334224 : p++;
3970 : /* first count how much space we need */
3971 4334224 : msg = p; /* save for later */
3972 176780787 : while (*p && *p != quote) {
3973 172446563 : if (*p == '\\') {
3974 29423 : p++;
3975 29423 : switch (*p) {
3976 0 : case '0':
3977 : case '1':
3978 : case '2':
3979 : case '3':
3980 : /* this could be the start of
3981 : an octal sequence, check it
3982 : out */
3983 0 : if (p[1] && p[2] &&
3984 0 : p[1] >= '0' && p[1] <= '7' &&
3985 0 : p[2] >= '0' && p[2] <= '7') {
3986 0 : p += 2;
3987 0 : break;
3988 : }
3989 : /* fall through */
3990 : default:
3991 : break;
3992 : }
3993 : }
3994 172446563 : p++;
3995 172446563 : len++;
3996 : }
3997 : /* now allocate space and copy string into new space */
3998 4334224 : p = msg; /* start over */
3999 4334224 : start = s = malloc(len + 1);
4000 176780787 : while (*p && *p != quote) {
4001 172446563 : if (*p == '\\') {
4002 29423 : p++;
4003 29423 : switch (*p) {
4004 : /* later
4005 : case '0': case '1': case '2': case '3': case '4':
4006 : case '5': case '6': case '7': case '8': case '9':
4007 : */
4008 1700 : case 'n':
4009 1700 : *s = '\n';
4010 1700 : break;
4011 15 : case 't':
4012 15 : *s = '\t';
4013 15 : break;
4014 0 : case 'r':
4015 0 : *s = '\r';
4016 0 : break;
4017 0 : case 'f':
4018 0 : *s = '\f';
4019 0 : break;
4020 0 : case '0':
4021 : case '1':
4022 : case '2':
4023 : case '3':
4024 : /* this could be the start of
4025 : an octal sequence, check it
4026 : out */
4027 0 : if (p[1] && p[2] &&
4028 0 : p[1] >= '0' && p[1] <= '7' &&
4029 0 : p[2] >= '0' && p[2] <= '7') {
4030 0 : *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
4031 0 : p += 2;
4032 0 : break;
4033 : }
4034 : /* fall through */
4035 : default:
4036 27708 : *s = *p;
4037 27708 : break;
4038 : }
4039 29423 : p++;
4040 : } else {
4041 172417140 : *s = *p++;
4042 : }
4043 172446563 : s++;
4044 : }
4045 4334224 : *s = 0; /* close string */
4046 4334224 : p++; /* skip over end-of-string quote */
4047 : /* skip over trailing junk (presumably white space) */
4048 5667582 : while (*p && *p != ',' && *p != endchar)
4049 1333358 : p++;
4050 4334224 : if (next)
4051 4334224 : *next = p;
4052 4334224 : *str = start;
4053 4334224 : if (lenp)
4054 4334224 : *lenp = len;
4055 :
4056 4334224 : return 1;
4057 : } else {
4058 : const char *s;
4059 : size_t len;
4060 :
4061 : /* p points at first non-white space character */
4062 57527699 : msg = p; /* record start of value */
4063 : /* find separator or terminator */
4064 57527699 : while (*p && *p != ',' && *p != '\t' && *p != endchar)
4065 56229456 : p++;
4066 : /* search back over trailing white space */
4067 1420594 : for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
4068 : ;
4069 1298243 : if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
4070 1298243 : s++;
4071 1298243 : if (*p == '\t') {
4072 35751 : p++;
4073 : }
4074 1298243 : len = s - msg;
4075 1298243 : *str = malloc(len + 1);
4076 1298243 : strcpy_len(*str, msg, len + 1);
4077 :
4078 1298243 : if (next)
4079 1298243 : *next = p;
4080 1298243 : if (lenp)
4081 1298243 : *lenp = len;
4082 1298243 : return 0;
4083 : }
4084 : }
4085 :
4086 : char *
4087 0 : mapi_unquote(char *msg)
4088 : {
4089 0 : char *start;
4090 :
4091 0 : unquote(msg, &start, NULL, ']', NULL);
4092 0 : return start;
4093 : }
4094 :
4095 : char *
4096 0 : mapi_quote(const char *msg, int size)
4097 : {
4098 : /* we absolutely don't need more than this (until we start
4099 : producing octal escapes */
4100 0 : char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
4101 0 : char *t = s;
4102 :
4103 : /* the condition is tricky: if initially size < 0, we must
4104 : continue until a NULL byte, else, size gives the number of
4105 : bytes to be copied */
4106 0 : while (size < 0 ? *msg : size > 0) {
4107 0 : if (size > 0)
4108 0 : size--;
4109 0 : switch (*msg) {
4110 0 : case '\n':
4111 0 : *t++ = '\\';
4112 0 : *t++ = 'n';
4113 0 : break;
4114 0 : case '\t':
4115 0 : *t++ = '\\';
4116 0 : *t++ = 't';
4117 0 : break;
4118 0 : case PLACEHOLDER:
4119 0 : *t++ = '\\';
4120 0 : *t++ = PLACEHOLDER;
4121 0 : break;
4122 0 : case '\\':
4123 0 : *t++ = '\\';
4124 0 : *t++ = '\\';
4125 0 : break;
4126 0 : case '\'':
4127 0 : *t++ = '\\';
4128 0 : *t++ = '\'';
4129 0 : break;
4130 0 : case '"':
4131 0 : *t++ = '\\';
4132 0 : *t++ = '"';
4133 0 : break;
4134 0 : case '\0':
4135 0 : *t++ = '\\';
4136 0 : *t++ = '0';
4137 0 : break;
4138 0 : default:
4139 0 : *t++ = *msg;
4140 0 : break;
4141 : }
4142 0 : msg++;
4143 : /* also deal with binaries */
4144 : }
4145 0 : *t = 0;
4146 0 : return s;
4147 : }
4148 :
4149 : static int
4150 0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
4151 : {
4152 : /* extend the bindings table */
4153 0 : int nm = hdl->maxbindings + 32;
4154 :
4155 0 : if (nm <= minbindings)
4156 0 : nm = minbindings + 32;
4157 0 : REALLOC(hdl->bindings, nm);
4158 0 : assert(hdl->bindings);
4159 : /* clear new entries */
4160 0 : memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
4161 0 : hdl->maxbindings = nm;
4162 0 : return MOK;
4163 : }
4164 :
4165 : static int
4166 0 : mapi_extend_params(MapiHdl hdl, int minparams)
4167 : {
4168 : /* extend the params table */
4169 0 : int nm = hdl->maxparams + 32;
4170 :
4171 0 : if (nm <= minparams)
4172 0 : nm = minparams + 32;
4173 0 : REALLOC(hdl->params, nm);
4174 0 : assert(hdl->params);
4175 : /* clear new entries */
4176 0 : memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
4177 0 : hdl->maxparams = nm;
4178 0 : return MOK;
4179 : }
4180 :
4181 : static MapiMsg
4182 0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
4183 : {
4184 0 : char *val;
4185 :
4186 0 : val = result->cache.line[cr].anchors[fnr];
4187 :
4188 0 : if (val == 0) {
4189 0 : return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
4190 : }
4191 :
4192 : /* auto convert to C-type */
4193 0 : switch (outtype) {
4194 0 : case MAPI_TINY:
4195 0 : *(signed char *) dst = (signed char) strtol(val, NULL, 0);
4196 0 : break;
4197 0 : case MAPI_UTINY:
4198 0 : *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
4199 0 : break;
4200 0 : case MAPI_SHORT:
4201 0 : *(short *) dst = (short) strtol(val, NULL, 0);
4202 0 : break;
4203 0 : case MAPI_USHORT:
4204 0 : *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
4205 0 : break;
4206 0 : case MAPI_NUMERIC:
4207 : case MAPI_INT:
4208 0 : *(int *) dst = (int) strtol(val, NULL, 0);
4209 0 : break;
4210 0 : case MAPI_UINT:
4211 0 : *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
4212 0 : break;
4213 0 : case MAPI_LONG:
4214 0 : *(long *) dst = strtol(val, NULL, 0);
4215 0 : break;
4216 0 : case MAPI_ULONG:
4217 0 : *(unsigned long *) dst = strtoul(val, NULL, 0);
4218 0 : break;
4219 0 : case MAPI_LONGLONG:
4220 0 : *(int64_t *) dst = strtoll(val, NULL, 0);
4221 0 : break;
4222 0 : case MAPI_ULONGLONG:
4223 0 : *(uint64_t *) dst = strtoull(val, NULL, 0);
4224 0 : break;
4225 0 : case MAPI_CHAR:
4226 0 : *(char *) dst = *val;
4227 0 : break;
4228 0 : case MAPI_FLOAT:
4229 0 : *(float *) dst = strtof(val, NULL);
4230 0 : break;
4231 0 : case MAPI_DOUBLE:
4232 0 : *(double *) dst = strtod(val, NULL);
4233 0 : break;
4234 0 : case MAPI_DATE:
4235 0 : sscanf(val, "%hd-%hu-%hu",
4236 : &((MapiDate *) dst)->year,
4237 : &((MapiDate *) dst)->month,
4238 : &((MapiDate *) dst)->day);
4239 0 : break;
4240 0 : case MAPI_TIME:
4241 0 : sscanf(val, "%hu:%hu:%hu",
4242 : &((MapiTime *) dst)->hour,
4243 : &((MapiTime *) dst)->minute,
4244 : &((MapiTime *) dst)->second);
4245 0 : break;
4246 0 : case MAPI_DATETIME:{
4247 0 : int n;
4248 :
4249 0 : ((MapiDateTime *) dst)->fraction = 0;
4250 0 : sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
4251 : &((MapiDateTime *) dst)->year,
4252 : &((MapiDateTime *) dst)->month,
4253 : &((MapiDateTime *) dst)->day,
4254 : &((MapiDateTime *) dst)->hour,
4255 : &((MapiDateTime *) dst)->minute,
4256 : &((MapiDateTime *) dst)->second,
4257 : &n);
4258 0 : if (val[n] == '.') {
4259 0 : unsigned int fac = 1000000000;
4260 0 : unsigned int nsec = 0;
4261 :
4262 0 : for (n++; isdigit((unsigned char) val[n]); n++) {
4263 0 : fac /= 10;
4264 0 : nsec += (val[n] - '0') * fac;
4265 : }
4266 0 : ((MapiDateTime *) dst)->fraction = nsec;
4267 : }
4268 0 : break;
4269 : }
4270 0 : case MAPI_AUTO:
4271 : case MAPI_VARCHAR:
4272 : default:
4273 0 : *(char **) dst = val;
4274 : }
4275 : return MOK;
4276 : }
4277 :
4278 : MapiMsg
4279 0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
4280 : {
4281 0 : struct MapiResultSet *result;
4282 :
4283 0 : mapi_hdl_check(hdl);
4284 :
4285 0 : if ((result = hdl->result) == NULL) {
4286 0 : return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
4287 : }
4288 :
4289 0 : if (fnr < 0 || fnr >= result->fieldcnt) {
4290 0 : return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4291 : }
4292 :
4293 0 : return store_field(result, result->cache.reader, fnr, outtype, dst);
4294 : }
4295 :
4296 : static void
4297 0 : mapi_store_bind(struct MapiResultSet *result, int cr)
4298 : {
4299 0 : int i;
4300 0 : MapiHdl hdl = result->hdl;
4301 :
4302 0 : for (i = 0; i < hdl->maxbindings; i++)
4303 0 : if (hdl->bindings[i].outparam)
4304 0 : store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
4305 0 : }
4306 :
4307 : /*
4308 : * The low level routine mapi_slice_row breaks the last row received
4309 : * into pieces and binds the field descriptors with their location. All
4310 : * escaped characters are immediately replaced, such that we end with a
4311 : * list of C-strings. It overwrites the contents of the row buffer,
4312 : * because de-escaping only reduces the size. It also silently extends
4313 : * the field descriptor table.
4314 : */
4315 : static int
4316 1369352 : mapi_slice_row(struct MapiResultSet *result, int cr)
4317 : {
4318 1369352 : char *p;
4319 1369352 : int i = 0;
4320 :
4321 1369352 : p = result->cache.line[cr].rows;
4322 1369352 : if (p == NULL)
4323 0 : return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
4324 1369352 : if (result->cache.line[cr].fldcnt)
4325 : return result->cache.line[cr].fldcnt; /* already sliced */
4326 :
4327 1369352 : if (*p != '[') {
4328 : /* nothing to slice */
4329 50 : i = 1;
4330 50 : REALLOC(result->cache.line[cr].anchors, 1);
4331 50 : REALLOC(result->cache.line[cr].lens, 1);
4332 : /* skip initial '=' if present */
4333 50 : if (*p == '=')
4334 50 : p++;
4335 50 : result->cache.line[cr].anchors[0] = strdup(p);
4336 50 : result->cache.line[cr].lens[0] = strlen(p);
4337 : } else {
4338 : /* work on a copy to preserve the original */
4339 1369302 : p = strdup(p);
4340 1369509 : i = slice_row(p,
4341 1369302 : msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
4342 : &result->cache.line[cr].anchors,
4343 : &result->cache.line[cr].lens,
4344 : result->fieldcnt, ']');
4345 1369302 : free(p);
4346 : }
4347 1369352 : if (i != result->fieldcnt) {
4348 : int j;
4349 196 : for (j = 0; j < result->fieldcnt; j++) {
4350 0 : if (result->fields[j].columnname)
4351 0 : free(result->fields[j].columnname);
4352 0 : result->fields[j].columnname = NULL;
4353 0 : if (result->fields[j].columntype)
4354 0 : free(result->fields[j].columntype);
4355 0 : result->fields[j].columntype = NULL;
4356 0 : if (result->fields[j].tablename)
4357 0 : free(result->fields[j].tablename);
4358 0 : result->fields[j].tablename = NULL;
4359 0 : result->fields[j].columnlength = 0;
4360 : }
4361 : }
4362 1369352 : if (i > result->fieldcnt) {
4363 196 : result->fieldcnt = i;
4364 196 : if (i > result->maxfields) {
4365 196 : REALLOC(result->fields, i);
4366 196 : memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
4367 196 : result->maxfields = i;
4368 : }
4369 : }
4370 1369352 : result->cache.line[cr].fldcnt = i;
4371 1369352 : return i;
4372 : }
4373 :
4374 : /*
4375 : * The rows presented are broken down into pieces to
4376 : * simplify access later on. However, mclient may
4377 : * first want to check the content of the line for
4378 : * useful information (e.g. #EOD)
4379 : */
4380 : int
4381 46294 : mapi_split_line(MapiHdl hdl)
4382 : {
4383 46294 : int n;
4384 46294 : struct MapiResultSet *result;
4385 :
4386 46294 : result = hdl->result;
4387 46294 : assert(result != NULL);
4388 46294 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4389 46294 : n = mapi_slice_row(result, result->cache.reader);
4390 : /* no need to call mapi_store_bind since
4391 : mapi_fetch_line would have done that if needed */
4392 : }
4393 46294 : return n;
4394 : }
4395 :
4396 : int
4397 1328500 : mapi_fetch_row(MapiHdl hdl)
4398 : {
4399 1328500 : char *reply;
4400 1328500 : int n;
4401 1328500 : struct MapiResultSet *result;
4402 :
4403 1328500 : mapi_hdl_check(hdl);
4404 1350235 : do {
4405 1350235 : if ((reply = mapi_fetch_line(hdl)) == NULL)
4406 : return 0;
4407 1344793 : } while (*reply != '[' && *reply != '=');
4408 1323058 : result = hdl->result;
4409 1323058 : assert(result != NULL);
4410 1323058 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4411 1323058 : n = mapi_slice_row(result, result->cache.reader);
4412 : /* no need to call mapi_store_bind since
4413 : mapi_fetch_line would have done that if needed */
4414 : }
4415 : return n;
4416 : }
4417 :
4418 : /*
4419 : * All rows can be cached first as well.
4420 : */
4421 : int64_t
4422 1 : mapi_fetch_all_rows(MapiHdl hdl)
4423 : {
4424 1 : Mapi mid;
4425 1 : struct MapiResultSet *result;
4426 :
4427 1 : mapi_hdl_check(hdl);
4428 :
4429 1 : mid = hdl->mid;
4430 3 : for (;;) {
4431 4 : if ((result = hdl->result) != NULL &&
4432 2 : msettings_lang_is_sql(mid->settings) &&
4433 2 : mid->active == NULL &&
4434 1 : result->row_count > 0 &&
4435 1 : result->cache.first + result->cache.tuplecount < result->row_count) {
4436 0 : mid->active = hdl;
4437 0 : hdl->active = result;
4438 0 : mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
4439 : result->tableid, result->cache.first + result->cache.tuplecount);
4440 0 : int e;
4441 0 : if ((e = mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
4442 0 : result->tableid, result->cache.first + result->cache.tuplecount)) < 0 ||
4443 0 : (e = mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) < 0)
4444 0 : check_stream(mid, mid->to, e, "sending export command", 0);
4445 : }
4446 2 : if (mid->active)
4447 1 : read_into_cache(mid->active, 0);
4448 : else
4449 : break;
4450 : }
4451 1 : return result ? result->cache.tuplecount : 0;
4452 : }
4453 :
4454 : char *
4455 5311254 : mapi_fetch_field(MapiHdl hdl, int fnr)
4456 : {
4457 5311254 : int cr;
4458 5311254 : struct MapiResultSet *result;
4459 :
4460 5311254 : mapi_hdl_check0(hdl);
4461 :
4462 5311254 : if ((result = hdl->result) == NULL ||
4463 5311254 : (cr = result->cache.reader) < 0 ||
4464 5311254 : (result->cache.line[cr].rows[0] != '[' &&
4465 : result->cache.line[cr].rows[0] != '=')) {
4466 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4467 0 : return 0;
4468 : }
4469 5311254 : assert(result->cache.line != NULL);
4470 5311254 : if (fnr >= 0) {
4471 : /* slice if needed */
4472 5311254 : if (result->cache.line[cr].fldcnt == 0)
4473 0 : mapi_slice_row(result, cr);
4474 5311254 : if (fnr < result->cache.line[cr].fldcnt)
4475 5311253 : return result->cache.line[cr].anchors[fnr];
4476 : }
4477 1 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4478 1 : return 0;
4479 : }
4480 :
4481 : size_t
4482 1339892 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
4483 : {
4484 1339892 : int cr;
4485 1339892 : struct MapiResultSet *result;
4486 :
4487 1339892 : mapi_hdl_check0(hdl);
4488 :
4489 1339892 : if ((result = hdl->result) == NULL ||
4490 1339892 : (cr = result->cache.reader) < 0 ||
4491 1339892 : (result->cache.line[cr].rows[0] != '[' &&
4492 : result->cache.line[cr].rows[0] != '=')) {
4493 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4494 0 : return 0;
4495 : }
4496 1339892 : assert(result->cache.line != NULL);
4497 1339892 : if (fnr >= 0) {
4498 : /* slice if needed */
4499 1339892 : if (result->cache.line[cr].fldcnt == 0)
4500 0 : mapi_slice_row(result, cr);
4501 1339892 : if (fnr < result->cache.line[cr].fldcnt)
4502 1339892 : return result->cache.line[cr].lens[fnr];
4503 : }
4504 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4505 0 : return 0;
4506 : }
4507 :
4508 : int
4509 2570 : mapi_get_field_count(MapiHdl hdl)
4510 : {
4511 2570 : mapi_hdl_check(hdl);
4512 2570 : if (hdl->result && hdl->result->fieldcnt == 0) {
4513 : /* no rows have been sliced yet, and there was no
4514 : header, so try to figure out how many columns there
4515 : are for ourselves */
4516 : int i;
4517 :
4518 2063 : for (i = 0; i < hdl->result->cache.writer; i++)
4519 0 : if (hdl->result->cache.line[i].rows[0] == '[' ||
4520 : hdl->result->cache.line[i].rows[0] == '=')
4521 0 : mapi_slice_row(hdl->result, i);
4522 : }
4523 2570 : return hdl->result ? hdl->result->fieldcnt : 0;
4524 : }
4525 :
4526 : int64_t
4527 454 : mapi_get_row_count(MapiHdl hdl)
4528 : {
4529 454 : mapi_hdl_check(hdl);
4530 454 : return hdl->result ? hdl->result->row_count : 0;
4531 : }
4532 :
4533 : int64_t
4534 0 : mapi_get_last_id(MapiHdl hdl)
4535 : {
4536 0 : mapi_hdl_check(hdl);
4537 0 : return hdl->result ? hdl->result->last_id : -1;
4538 : }
4539 :
4540 : char *
4541 2633 : mapi_get_name(MapiHdl hdl, int fnr)
4542 : {
4543 2633 : struct MapiResultSet *result;
4544 :
4545 2633 : mapi_hdl_check0(hdl);
4546 2633 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4547 2633 : return result->fields[fnr].columnname;
4548 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4549 0 : return 0;
4550 : }
4551 :
4552 : char *
4553 5272648 : mapi_get_type(MapiHdl hdl, int fnr)
4554 : {
4555 5272648 : struct MapiResultSet *result;
4556 :
4557 5272648 : mapi_hdl_check0(hdl);
4558 5272648 : if ((result = hdl->result) != 0 &&
4559 5272648 : fnr >= 0 && fnr < result->fieldcnt) {
4560 5272648 : if (result->fields[fnr].columntype == NULL)
4561 : return "unknown";
4562 5272648 : return result->fields[fnr].columntype;
4563 : }
4564 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4565 0 : return 0;
4566 : }
4567 :
4568 : char *
4569 2492 : mapi_get_table(MapiHdl hdl, int fnr)
4570 : {
4571 2492 : struct MapiResultSet *result;
4572 :
4573 2492 : mapi_hdl_check0(hdl);
4574 2492 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4575 2492 : return result->fields[fnr].tablename;
4576 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4577 0 : return 0;
4578 : }
4579 :
4580 : int
4581 1342 : mapi_get_len(MapiHdl hdl, int fnr)
4582 : {
4583 1342 : struct MapiResultSet *result;
4584 :
4585 1342 : mapi_hdl_check0(hdl);
4586 1342 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4587 1342 : return result->fields[fnr].columnlength;
4588 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4589 0 : return 0;
4590 : }
4591 :
4592 : int
4593 2513 : mapi_get_digits(MapiHdl hdl, int fnr)
4594 : {
4595 2513 : struct MapiResultSet *result;
4596 :
4597 2513 : mapi_hdl_check0(hdl);
4598 2513 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4599 2513 : return result->fields[fnr].digits;
4600 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4601 0 : return 0;
4602 : }
4603 :
4604 : int
4605 22 : mapi_get_scale(MapiHdl hdl, int fnr)
4606 : {
4607 22 : struct MapiResultSet *result;
4608 :
4609 22 : mapi_hdl_check0(hdl);
4610 22 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4611 22 : return result->fields[fnr].scale;
4612 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4613 0 : return 0;
4614 : }
4615 :
4616 : char *
4617 533 : mapi_get_query(MapiHdl hdl)
4618 : {
4619 533 : mapi_hdl_check0(hdl);
4620 533 : if (hdl->query != NULL) {
4621 533 : return strdup(hdl->query);
4622 : } else {
4623 : return NULL;
4624 : }
4625 : }
4626 :
4627 :
4628 : int
4629 12132 : mapi_get_querytype(MapiHdl hdl)
4630 : {
4631 12132 : struct MapiResultSet *result;
4632 :
4633 12132 : mapi_hdl_check0(hdl);
4634 12132 : if ((result = hdl->result) != 0)
4635 11124 : return result->querytype;
4636 1008 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4637 1008 : return 0; /* Q_PARSE! */
4638 : }
4639 :
4640 : int
4641 4 : mapi_get_tableid(MapiHdl hdl)
4642 : {
4643 4 : struct MapiResultSet *result;
4644 :
4645 4 : mapi_hdl_check0(hdl);
4646 4 : if ((result = hdl->result) != 0)
4647 4 : return result->tableid;
4648 0 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4649 0 : return 0;
4650 : }
4651 :
4652 : int64_t
4653 3064 : mapi_rows_affected(MapiHdl hdl)
4654 : {
4655 3064 : struct MapiResultSet *result;
4656 :
4657 3064 : mapi_hdl_check(hdl);
4658 3064 : if ((result = hdl->result) == NULL)
4659 : return 0;
4660 3064 : return result->row_count;
4661 : }
4662 :
4663 : int64_t
4664 4933 : mapi_get_querytime(MapiHdl hdl)
4665 : {
4666 4933 : struct MapiResultSet *result;
4667 :
4668 4933 : mapi_hdl_check(hdl);
4669 4933 : if ((result = hdl->result) == NULL)
4670 : return 0;
4671 4429 : return result->querytime;
4672 : }
4673 :
4674 : int64_t
4675 4933 : mapi_get_maloptimizertime(MapiHdl hdl)
4676 : {
4677 4933 : struct MapiResultSet *result;
4678 :
4679 4933 : mapi_hdl_check(hdl);
4680 4933 : if ((result = hdl->result) == NULL)
4681 : return 0;
4682 4429 : return result->maloptimizertime;
4683 : }
4684 :
4685 : int64_t
4686 4933 : mapi_get_sqloptimizertime(MapiHdl hdl)
4687 : {
4688 4933 : struct MapiResultSet *result;
4689 :
4690 4933 : mapi_hdl_check(hdl);
4691 4933 : if ((result = hdl->result) == NULL)
4692 : return 0;
4693 4429 : return result->sqloptimizertime;
4694 : }
4695 :
4696 : const char *
4697 199 : mapi_get_dbname(Mapi mid)
4698 : {
4699 199 : return msetting_string(mid->settings, MP_DATABASE);
4700 : }
4701 :
4702 : const char *
4703 6 : mapi_get_host(Mapi mid)
4704 : {
4705 6 : return msetting_string(mid->settings, MP_HOST);
4706 : }
4707 :
4708 : const char *
4709 6 : mapi_get_user(Mapi mid)
4710 : {
4711 6 : return msetting_string(mid->settings, MP_USER);;
4712 : }
4713 :
4714 : const char *
4715 0 : mapi_get_lang(Mapi mid)
4716 : {
4717 0 : return msetting_string(mid->settings, MP_LANGUAGE);;
4718 : }
4719 :
4720 : const char *
4721 0 : mapi_get_uri(Mapi mid)
4722 : {
4723 0 : return mid->uri;
4724 : }
4725 :
4726 : const char *
4727 1 : mapi_get_mapi_version(void)
4728 : {
4729 1 : return MAPI_VERSION;
4730 : }
4731 :
4732 : const char *
4733 0 : mapi_get_monet_version(Mapi mid)
4734 : {
4735 0 : mapi_check0(mid);
4736 0 : return mid->server ? mid->server : "";
4737 : }
4738 :
4739 : const char *
4740 0 : mapi_get_motd(Mapi mid)
4741 : {
4742 0 : mapi_check0(mid);
4743 0 : return mid->motd;
4744 : }
4745 :
4746 : bool
4747 0 : mapi_is_connected(Mapi mid)
4748 : {
4749 0 : return mid->connected;
4750 : }
4751 :
4752 : MapiHdl
4753 130 : mapi_get_active(Mapi mid)
4754 : {
4755 130 : return mid->active;
4756 : }
4757 :
4758 : msettings*
4759 0 : mapi_get_settings(Mapi mid)
4760 : {
4761 0 : return mid->settings;
4762 : }
4763 :
4764 :
4765 : MapiMsg
4766 1462 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
4767 : {
4768 : // do not use check_stream here yet because the socket is not yet in 'mid'
4769 1462 : const char *error_message;
4770 1462 : stream *error_stream;
4771 :
4772 1462 : assert(!isa_block_stream(rstream));
4773 1462 : assert(!isa_block_stream(wstream));
4774 :
4775 : // First send some NUL bytes. If we're accidentally connecting to the wrong
4776 : // port or protocol this may cause the remote server to close the
4777 : // connection. If we don't do this, the connection will often hang
4778 : // because the server expects us to speak first and we expect the server
4779 : // to speak first.
4780 : //
4781 : // Note that a pair of NUL bytes is a no-op message in MAPI.
4782 : //
4783 : // Surprisingly, it seems sending these NUL bytes makes non-TLS
4784 : // connection setup a little faster rather than slower!
4785 : static const char zeroes[8] = { 0 };
4786 : ssize_t to_write = sizeof(zeroes);
4787 2924 : while (to_write > 0) {
4788 1462 : ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
4789 1462 : if (n < 0) {
4790 0 : close_connection(mid);
4791 0 : return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
4792 : }
4793 1462 : to_write -= (size_t)n;
4794 : }
4795 1462 : if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
4796 0 : close_connection(mid);
4797 0 : return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
4798 : }
4799 :
4800 :
4801 1462 : stream *brstream = NULL;
4802 1462 : stream *bwstream = NULL;
4803 :
4804 1462 : bwstream = block_stream(wstream);
4805 1462 : if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
4806 0 : error_stream = bwstream;
4807 0 : error_message = "block_stream wstream";
4808 0 : goto bailout;
4809 : }
4810 1462 : brstream = block_stream(rstream);
4811 1462 : if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
4812 0 : error_stream = brstream;
4813 0 : error_message = "block_stream rstream";
4814 0 : goto bailout;
4815 : }
4816 :
4817 1462 : mid->to = bwstream;
4818 1462 : mid->from = brstream;
4819 1462 : return MOK;
4820 0 : bailout:
4821 : // adapted from the check_stream macro
4822 0 : if (brstream)
4823 0 : mnstr_destroy(brstream);
4824 0 : if (bwstream)
4825 0 : mnstr_destroy(bwstream);
4826 : // malloc failure is the only way these calls could have failed
4827 0 : return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
4828 : }
|