Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
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 15179390 : mapi_clrError(Mapi mid)
820 : {
821 15179390 : assert(mid);
822 15179390 : if (mid->errorstr && mid->errorstr != mapi_nomem)
823 1356 : free(mid->errorstr);
824 15179390 : mid->action = 0; /* contains references to constants */
825 15179390 : mid->error = 0;
826 15179390 : mid->errorstr = 0;
827 15179390 : }
828 :
829 : MapiMsg
830 1348 : mapi_setError(Mapi mid, const char *msg, const char *action, MapiMsg error)
831 : {
832 1348 : assert(msg);
833 1348 : REALLOC(mid->errorstr, strlen(msg) + 1);
834 1348 : if (mid->errorstr == NULL)
835 0 : mid->errorstr = mapi_nomem;
836 : else
837 1348 : strcpy(mid->errorstr, msg);
838 1348 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
839 1348 : mid->error = error;
840 1348 : mid->action = action;
841 1348 : return mid->error;
842 : }
843 :
844 33 : MapiMsg mapi_printError(Mapi mid, const char *action, MapiMsg error, const char *fmt, ...)
845 : {
846 33 : size_t size = 81; // just a guess
847 :
848 59 : while (1) {
849 46 : REALLOC(mid->errorstr, size);
850 46 : if (mid->errorstr == NULL) {
851 0 : mid->errorstr = mapi_nomem;
852 0 : break;
853 : }
854 46 : va_list ap;
855 46 : va_start(ap, fmt);
856 46 : int n = vsnprintf(mid->errorstr, size, fmt, ap);
857 46 : va_end(ap);
858 46 : if (n < 0) {
859 : // is it even possible for vsnprintf to fail?
860 : break;
861 : }
862 46 : if ((size_t)n < size) {
863 : // it fit
864 : break;
865 : } else {
866 : // try again larger
867 13 : size = (size_t)n + 1;
868 : }
869 : }
870 33 : mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
871 33 : mid->error = error;
872 33 : mid->action = action;
873 33 : return mid->error;
874 : }
875 :
876 : MapiMsg
877 198355 : mapi_error(Mapi mid)
878 : {
879 198355 : assert(mid);
880 198355 : return mid->error;
881 : }
882 :
883 : const char *
884 27 : mapi_error_str(Mapi mid)
885 : {
886 27 : assert(mid);
887 27 : 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 176 : 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 176 : const char *s = prefix, *p = msg, *q;
1032 176 : const int len = (int) strlen(s);
1033 176 : const char t = s[len - 1];
1034 :
1035 298 : while (p && *p) {
1036 187 : fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
1037 187 : s = "";
1038 :
1039 187 : q = strchr(p, '\n');
1040 187 : if (q) {
1041 122 : q++; /* also print the newline */
1042 122 : fprintf(fd, "%.*s", (int) (q - p), p);
1043 : } else {
1044 : /* print bit after last newline,
1045 : adding one ourselves */
1046 65 : fprintf(fd, "%s\n", p);
1047 65 : break; /* nothing more to do */
1048 : }
1049 122 : p = q;
1050 : }
1051 176 : }
1052 :
1053 : void
1054 59 : mapi_noexplain(Mapi mid, const char *errorprefix)
1055 : {
1056 59 : assert(mid);
1057 59 : if (mid->noexplain)
1058 0 : free(mid->noexplain);
1059 59 : mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
1060 59 : }
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 59 : mapi_explain_result(MapiHdl hdl, FILE *fd)
1118 : {
1119 59 : Mapi mid;
1120 :
1121 59 : if (hdl == NULL ||
1122 59 : hdl->result == NULL ||
1123 59 : hdl->result->errorstr == NULL)
1124 : return;
1125 59 : assert(hdl);
1126 59 : assert(hdl->result);
1127 59 : assert(hdl->result->errorstr);
1128 59 : mid = hdl->mid;
1129 59 : assert(mid);
1130 59 : if (mid->noexplain == NULL) {
1131 59 : const char *host = msetting_string(mid->settings, MP_HOST);
1132 59 : const char *user = msetting_string(mid->settings, MP_USER);
1133 59 : int port = msetting_long(mid->settings, MP_PORT);
1134 59 : if (host[0] == '/')
1135 0 : fprintf(fd, "MAPI = (%s) %s\n", user, host);
1136 : else
1137 59 : fprintf(fd, "MAPI = %s@%s:%d\n",
1138 : user, host, port);
1139 59 : if (mid->action)
1140 0 : fprintf(fd, "ACTION= %s\n", mid->action);
1141 59 : if (hdl->query)
1142 59 : indented_print(hdl->query, "QUERY = ", fd);
1143 59 : indented_print(hdl->result->errorstr, "ERROR = !", fd);
1144 59 : if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
1145 58 : indented_print(hdl->result->sqlstate, "CODE = ", fd);
1146 : } else {
1147 0 : clean_print(hdl->result->errorstr, mid->noexplain, fd);
1148 : }
1149 59 : fflush(fd);
1150 : }
1151 :
1152 : stream *
1153 2801 : mapi_get_to(Mapi mid)
1154 : {
1155 2801 : mapi_check0(mid);
1156 2801 : return mid->to;
1157 : }
1158 :
1159 : stream *
1160 1401 : mapi_get_from(Mapi mid)
1161 : {
1162 1401 : mapi_check0(mid);
1163 1401 : 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 94 : usec(void)
1196 : {
1197 : #ifdef HAVE_GETTIMEOFDAY
1198 94 : struct timeval tp;
1199 :
1200 94 : gettimeofday(&tp, NULL);
1201 94 : 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 94 : mapi_log_header(Mapi mid, const char *funcname, long line, const char *mark1, const char *mark2)
1214 : {
1215 94 : int64_t now = usec();
1216 94 : static int64_t firstcall = 0;
1217 94 : if (firstcall == 0)
1218 14 : firstcall = now;
1219 94 : double seconds = (double)(now - firstcall) / 1e6;
1220 94 : mnstr_printf(mid->tracelog, "\n** [%u] t=%.3fs %s%s %s(), line %ld\n", mid->index, seconds, mark1, mark2, funcname, line);
1221 94 : }
1222 :
1223 : void
1224 74 : mapi_impl_log_record(Mapi mid, const char *funcname, long line, const char *mark, const char *fmt, ...)
1225 : {
1226 74 : va_list ap;
1227 :
1228 74 : if (mid->tracelog == NULL)
1229 0 : return;
1230 :
1231 : size_t needed = 128;
1232 75 : size_t to_print;
1233 76 : while (1) {
1234 75 : if (mid->tracebuffersize < needed) {
1235 15 : free(mid->tracebuffer);
1236 15 : mid->tracebuffer = malloc(needed);
1237 15 : if (mid->tracebuffer) {
1238 15 : mid->tracebuffersize = needed;
1239 : } else {
1240 0 : mid->tracebuffersize = 0;
1241 0 : to_print = 0;
1242 0 : break;
1243 : }
1244 : }
1245 75 : va_start(ap, fmt);
1246 75 : int n = vsnprintf(mid->tracebuffer, mid->tracebuffersize, fmt, ap);
1247 75 : va_end(ap);
1248 75 : if (n < 0) {
1249 : to_print = 0;
1250 : break;
1251 : }
1252 75 : 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 74 : mapi_log_header(mid, funcname, line, mark, "");
1261 74 : if (to_print > 0) {
1262 74 : mnstr_write(mid->tracelog, mid->tracebuffer, to_print, 1);
1263 74 : mnstr_writeChr(mid->tracelog, '\n');
1264 : }
1265 74 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1266 : }
1267 :
1268 : #define MAPILOG_OPEN "\tb'"
1269 : #define MAPILOG_CLOSE "'"
1270 :
1271 : void
1272 20 : mapi_impl_log_data(Mapi mid, const char *filename, long line, const char *mark, const char *start, size_t len)
1273 : {
1274 20 : const char hexdigits[] = "0123456789abcdef";
1275 20 : if (mid->tracelog == NULL)
1276 0 : return;
1277 :
1278 20 : mapi_log_header(mid, filename, line, mark, " (DATA)");
1279 :
1280 20 : const size_t margin = strlen("\\xNN" MAPILOG_CLOSE "\n," MAPILOG_OPEN);
1281 20 : char buffer[128] = { 0 };
1282 20 : char *pos = buffer;
1283 20 : mnstr_writeStr(mid->tracelog, MAPILOG_OPEN);
1284 20 : bool inside = true;
1285 1112 : for (unsigned char *p = (unsigned char*)start; (char*)p < start + len; p++) {
1286 1092 : unsigned char c = *p;
1287 1092 : if (!inside) {
1288 0 : for (char *text = "\n" MAPILOG_OPEN; *text; text++)
1289 0 : *pos++ = *text;
1290 : inside = true;
1291 : }
1292 1092 : if (pos >= buffer + sizeof(buffer) - margin) {
1293 0 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1294 0 : pos = buffer;
1295 : }
1296 1092 : 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 20 : for (char *text = "\\n" MAPILOG_CLOSE; *text; text++)
1308 15 : *pos++ = *text;
1309 : inside = false;
1310 : break;
1311 1087 : default:
1312 1087 : if (c >= 32 && c < 127) {
1313 1087 : *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 20 : if (inside) {
1324 30 : for (char *text = MAPILOG_CLOSE; *text; text++)
1325 15 : *pos++ = *text;
1326 : }
1327 20 : *pos++ = ',';
1328 20 : *pos++ = '\n';
1329 20 : mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
1330 :
1331 20 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
1332 : }
1333 :
1334 : MapiMsg
1335 14 : mapi_log(Mapi mid, const char *nme)
1336 : {
1337 14 : mapi_clrError(mid);
1338 14 : if (mid->tracelog) {
1339 0 : close_stream(mid->tracelog);
1340 0 : mid->tracelog = NULL;
1341 : }
1342 14 : if (nme == NULL)
1343 : return MOK;
1344 14 : if (nme[0] == '-' && nme[1] == '\0')
1345 0 : mid->tracelog = stderr_wastream();
1346 : else
1347 14 : mid->tracelog = open_wastream(nme);
1348 14 : 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 33824 : new_result(MapiHdl hdl)
1380 : {
1381 33824 : struct MapiResultSet *result;
1382 :
1383 33824 : assert((hdl->lastresult == NULL && hdl->result == NULL) ||
1384 : (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
1385 :
1386 33824 : 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 33824 : result = malloc(sizeof(*result));
1390 33824 : if (result == NULL)
1391 : return NULL;
1392 67648 : *result = (struct MapiResultSet) {
1393 : .hdl = hdl,
1394 : .tableid = -1,
1395 : .querytype = -1,
1396 : .last_id = -1,
1397 33824 : .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
1398 : .cache.reader = -1,
1399 : .commentonly = true,
1400 : };
1401 33824 : if (hdl->lastresult == NULL)
1402 33769 : 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 33824 : close_result(MapiHdl hdl)
1414 : {
1415 33824 : struct MapiResultSet *result;
1416 33824 : Mapi mid;
1417 33824 : int i;
1418 :
1419 33824 : result = hdl->result;
1420 33824 : if (result == NULL)
1421 : return MERROR;
1422 33824 : mid = hdl->mid;
1423 33824 : assert(mid != NULL);
1424 33824 : if (mid->trace)
1425 0 : printf("closing result set\n");
1426 33824 : if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
1427 30369 : 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 30369 : assert(hdl->npending_close == 0 ||
1433 : (hdl->npending_close > 0 && hdl->pending_close != NULL));
1434 30369 : 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 30368 : } else if (mid->to != NULL) {
1447 : /* first close saved up to-be-closed tables */
1448 30368 : 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 30368 : hdl->npending_close = 0;
1463 30368 : if (hdl->pending_close)
1464 0 : free(hdl->pending_close);
1465 30368 : hdl->pending_close = NULL;
1466 30368 : 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 30369 : result->tableid = -1;
1481 : }
1482 33824 : if (mid->active == hdl &&
1483 3 : hdl->active == result &&
1484 0 : read_into_cache(hdl, -1) != MOK)
1485 : return MERROR;
1486 33824 : if( hdl->active == result)
1487 : return MERROR;
1488 : //assert(hdl->active != result);
1489 33824 : if (result->fields) {
1490 107864 : for (i = 0; i < result->maxfields; i++) {
1491 77174 : if (result->fields[i].tablename)
1492 76971 : free(result->fields[i].tablename);
1493 77174 : if (result->fields[i].columnname)
1494 76971 : free(result->fields[i].columnname);
1495 77174 : if (result->fields[i].columntype)
1496 76979 : free(result->fields[i].columntype);
1497 : }
1498 30690 : free(result->fields);
1499 : }
1500 33824 : result->fields = NULL;
1501 33824 : result->maxfields = result->fieldcnt = 0;
1502 33824 : if (result->cache.line) {
1503 187954 : for (i = 0; i < result->cache.writer; i++) {
1504 157260 : if (result->cache.line[i].rows)
1505 157260 : free(result->cache.line[i].rows);
1506 157260 : if (result->cache.line[i].anchors) {
1507 : int j;
1508 :
1509 133706 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
1510 118882 : if (result->cache.line[i].anchors[j]) {
1511 79253 : free(result->cache.line[i].anchors[j]);
1512 79253 : result->cache.line[i].anchors[j] = NULL;
1513 : }
1514 14824 : free(result->cache.line[i].anchors);
1515 : }
1516 157260 : if (result->cache.line[i].lens)
1517 14824 : free(result->cache.line[i].lens);
1518 : }
1519 30694 : free(result->cache.line);
1520 30694 : result->cache.line = NULL;
1521 30694 : result->cache.tuplecount = 0;
1522 : }
1523 33824 : if (result->errorstr && result->errorstr != mapi_nomem)
1524 68 : free(result->errorstr);
1525 33824 : result->errorstr = NULL;
1526 33824 : memset(result->sqlstate, 0, sizeof(result->sqlstate));
1527 33824 : result->hdl = NULL;
1528 33824 : hdl->result = result->next;
1529 33824 : if (hdl->result == NULL)
1530 33769 : hdl->lastresult = NULL;
1531 33824 : result->next = NULL;
1532 33824 : free(result);
1533 33824 : return MOK;
1534 : }
1535 :
1536 : static void
1537 81 : add_error(struct MapiResultSet *result, char *error)
1538 : {
1539 : /* concatenate the error messages */
1540 81 : size_t size = result->errorstr ? strlen(result->errorstr) : 0;
1541 :
1542 81 : if (strlen(error) > 6 && error[5] == '!' &&
1543 63 : (isdigit((unsigned char) error[0]) ||
1544 0 : (error[0] >= 'A' && error[0] <= 'Z')) &&
1545 63 : (isdigit((unsigned char) error[1]) ||
1546 3 : (error[1] >= 'A' && error[1] <= 'Z')) &&
1547 63 : (isdigit((unsigned char) error[2]) ||
1548 3 : (error[2] >= 'A' && error[2] <= 'Z')) &&
1549 63 : (isdigit((unsigned char) error[3]) ||
1550 0 : (error[3] >= 'A' && error[3] <= 'Z')) &&
1551 63 : (isdigit((unsigned char) error[4]) ||
1552 0 : (error[4] >= 'A' && error[4] <= 'Z'))) {
1553 63 : if (result->errorstr == NULL) {
1554 : /* remember SQLSTATE for first error */
1555 61 : strcpy_len(result->sqlstate, error,
1556 : sizeof(result->sqlstate));
1557 : }
1558 : /* skip SQLSTATE */
1559 63 : error += 6;
1560 : }
1561 81 : REALLOC(result->errorstr, size + strlen(error) + 2);
1562 81 : if (result->errorstr == NULL)
1563 0 : result->errorstr = mapi_nomem;
1564 : else
1565 81 : stpcpy(stpcpy(result->errorstr + size, error), "\n");
1566 81 : }
1567 :
1568 : const char *
1569 11961 : mapi_result_error(MapiHdl hdl)
1570 : {
1571 11961 : 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 5607 : mapi_next_result(MapiHdl hdl)
1586 : {
1587 5607 : mapi_hdl_check(hdl);
1588 :
1589 10491 : while (hdl->result != NULL) {
1590 4939 : if (close_result(hdl) != MOK)
1591 : return MERROR;
1592 4939 : 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 5552 : 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 39041 : mapi_new_handle(Mapi mid)
1644 : {
1645 39041 : MapiHdl hdl;
1646 :
1647 39041 : mapi_check0(mid);
1648 :
1649 39045 : hdl = malloc(sizeof(*hdl));
1650 39045 : 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 39045 : *hdl = (struct MapiStatement) {
1656 : .mid = mid,
1657 39045 : .next = mid->first,
1658 : };
1659 39045 : mid->first = hdl;
1660 39045 : if (hdl->next)
1661 4800 : 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 74072 : finish_handle(MapiHdl hdl)
1668 : {
1669 74072 : Mapi mid;
1670 74072 : int i;
1671 :
1672 74072 : if (hdl == NULL)
1673 : return MERROR;
1674 74072 : mid = hdl->mid;
1675 74371 : if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
1676 299 : read_into_cache(hdl, 0) != MOK)
1677 : return MERROR;
1678 74072 : if (mid->to) {
1679 74071 : 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 74071 : for (i = 0; i < hdl->npending_close; i++) {
1688 0 : char msg[256];
1689 :
1690 0 : snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
1691 0 : mapi_log_record(mid, "CMD", "%s", msg);
1692 0 : mid->active = hdl;
1693 0 : 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 74072 : hdl->npending_close = 0;
1703 74072 : if (hdl->pending_close)
1704 0 : free(hdl->pending_close);
1705 74072 : hdl->pending_close = NULL;
1706 177029 : while (hdl->result) {
1707 28883 : if (close_result(hdl) != MOK)
1708 : return MERROR;
1709 28885 : 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 39044 : mapi_close_handle(MapiHdl hdl)
1724 : {
1725 39044 : if (hdl == NULL)
1726 : return MOK;
1727 39044 : debugprint("entering %s\n", "mapi_close_handle");
1728 :
1729 : /* don't use mapi_check_hdl: it's ok if we're not connected */
1730 39044 : mapi_clrError(hdl->mid);
1731 :
1732 39040 : (void) finish_handle(hdl);
1733 39044 : hdl->npending_close = 0;
1734 39044 : if (hdl->pending_close)
1735 0 : free(hdl->pending_close);
1736 39044 : hdl->pending_close = NULL;
1737 39044 : if (hdl->bindings)
1738 0 : free(hdl->bindings);
1739 39044 : hdl->bindings = NULL;
1740 39044 : hdl->maxbindings = 0;
1741 39044 : if (hdl->params)
1742 0 : free(hdl->params);
1743 39044 : hdl->params = NULL;
1744 39044 : hdl->maxparams = 0;
1745 39044 : if (hdl->query)
1746 37279 : free(hdl->query);
1747 39044 : hdl->query = NULL;
1748 39044 : if (hdl->template)
1749 0 : free(hdl->template);
1750 39044 : hdl->template = NULL;
1751 : /* remove from doubly-linked list */
1752 39044 : if (hdl->prev)
1753 1 : hdl->prev->next = hdl->next;
1754 39044 : if (hdl->next)
1755 4798 : hdl->next->prev = hdl->prev;
1756 39044 : if (hdl->mid->first == hdl)
1757 39044 : hdl->mid->first = hdl->next;
1758 39044 : hdl->prev = NULL;
1759 39044 : hdl->next = NULL;
1760 39044 : hdl->mid = NULL;
1761 39044 : free(hdl);
1762 39044 : 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 440 : mapi_new(msettings *settings)
1775 : {
1776 440 : Mapi mid;
1777 440 : static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
1778 :
1779 440 : if (!ATOMIC_TAS(&mapi_initialized)) {
1780 268 : if (mnstr_init() < 0)
1781 : return NULL;
1782 : }
1783 :
1784 440 : mid = malloc(sizeof(*mid));
1785 440 : if (mid == NULL)
1786 : return NULL;
1787 :
1788 : /* then fill in some details */
1789 440 : *mid = MapiStructDefaults;
1790 440 : mid->index = (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
1791 440 : if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
1792 0 : mapi_destroy(mid);
1793 0 : return NULL;
1794 : }
1795 440 : if (settings == NULL) {
1796 400 : settings = msettings_create();
1797 396 : if (settings == NULL) {
1798 0 : mapi_destroy(mid);
1799 0 : return NULL;
1800 : }
1801 : }
1802 436 : mid->settings = settings;
1803 436 : mid->blk.buf[0] = 0;
1804 436 : mid->blk.buf[mid->blk.lim] = 0;
1805 :
1806 : /* also the current timezone, seconds EAST of UTC */
1807 436 : long time_zone;
1808 : #if defined(_MSC_VER)
1809 : DYNAMIC_TIME_ZONE_INFORMATION tzinf;
1810 :
1811 : /* documentation says: UTC = localtime + Bias (in minutes),
1812 : * but experimentation during DST period says, UTC = localtime
1813 : * + Bias + DaylightBias, and presumably during non DST
1814 : * period, UTC = localtime + Bias */
1815 : switch (GetDynamicTimeZoneInformation(&tzinf)) {
1816 : case TIME_ZONE_ID_STANDARD: /* using standard time */
1817 : case TIME_ZONE_ID_UNKNOWN: /* no daylight saving time in this zone */
1818 : time_zone = -(int) tzinf.Bias * 60;
1819 : break;
1820 : case TIME_ZONE_ID_DAYLIGHT: /* using daylight saving time */
1821 : time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
1822 : break;
1823 : default: /* aka TIME_ZONE_ID_INVALID */
1824 : /* call failed, we don't know the time zone */
1825 : time_zone = 0;
1826 : break;
1827 : }
1828 : #else
1829 436 : time_t t = time(NULL);
1830 437 : struct tm *local_tm = localtime_r(&t, &(struct tm){0});
1831 : #ifdef HAVE_TM_GMTOFF
1832 440 : time_zone = local_tm->tm_gmtoff;
1833 : #else
1834 : struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
1835 : time_t gt = mktime(gm_tm);
1836 : local_tm->tm_isdst=0; /* We need the difference without dst */
1837 : time_t lt = mktime(local_tm);
1838 : assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
1839 : time_zone = (long) (lt - gt);
1840 : #endif
1841 : #endif
1842 440 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
1843 440 : if (err)
1844 0 : mapi_setError(mid, err, __func__, MERROR);
1845 :
1846 : return mid;
1847 : }
1848 :
1849 : /* construct the uri field of a Mapi struct */
1850 : void
1851 250 : set_uri(Mapi mid)
1852 : {
1853 250 : const char *host = msetting_string(mid->settings, MP_HOST);
1854 249 : const char *database = msetting_string(mid->settings, MP_DATABASE);
1855 251 : int port = msetting_long(mid->settings, MP_PORT);
1856 251 : size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
1857 251 : char *uri = malloc(urilen);
1858 :
1859 : /* uri looks as follows:
1860 : * mapi:monetdb://host:port/database
1861 : * or
1862 : * mapi:monetdb:///some/path/to?database=database
1863 : */
1864 :
1865 251 : if (database != NULL) {
1866 251 : if (host[0] == '/') {
1867 0 : snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
1868 : host, database);
1869 : } else {
1870 251 : snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
1871 : host, port, database);
1872 : }
1873 : } else {
1874 0 : if (host[0] == '/') {
1875 0 : snprintf(uri, urilen, "mapi:monetdb://%s",
1876 : host);
1877 : } else {
1878 0 : snprintf(uri, urilen, "mapi:monetdb://%s:%d",
1879 : host, port);
1880 : }
1881 : }
1882 :
1883 251 : if (mid->uri != NULL)
1884 0 : free(mid->uri);
1885 251 : mid->uri = uri;
1886 251 : }
1887 :
1888 : Mapi
1889 207 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
1890 : {
1891 207 : Mapi mid;
1892 :
1893 207 : mid = mapi_new(NULL);
1894 211 : if (mid == NULL)
1895 : return NULL;
1896 :
1897 211 : if (url == NULL) {
1898 0 : mapi_setError(mid, "url is null", __func__, MERROR);
1899 0 : return mid;
1900 : }
1901 211 : if (user == NULL) {
1902 0 : mapi_setError(mid, "user is null", __func__, MERROR);
1903 0 : return mid;
1904 : }
1905 211 : if (pass == NULL) {
1906 0 : mapi_setError(mid, "pass is null", __func__, MERROR);
1907 0 : return mid;
1908 : }
1909 211 : if (lang == NULL) {
1910 0 : mapi_setError(mid, "lang is null", __func__, MERROR);
1911 0 : return mid;
1912 : }
1913 :
1914 211 : msettings_error err = NULL;
1915 211 : if ( (err = msetting_set_string(mid->settings, MP_USER, user))
1916 210 : || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
1917 211 : || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
1918 : ) {
1919 : // in the old implementation we returned NULL but that doesn't
1920 : // make sense to me because above we already started returning
1921 : // mid with the error set.
1922 0 : mapi_setError(mid, err, __func__, MERROR);
1923 0 : return mid;
1924 : }
1925 :
1926 211 : char *error_message = NULL;
1927 211 : if (!msettings_parse_url(mid->settings, url, &error_message)) {
1928 0 : char *msg = error_message ? error_message : "malloc failed";
1929 0 : mapi_setError(mid, msg, __func__, MERROR);
1930 0 : free(error_message);
1931 0 : return mid;
1932 : }
1933 :
1934 211 : set_uri(mid);
1935 :
1936 211 : return mid;
1937 : }
1938 :
1939 : /* Allocate a new connection handle and fill in the information needed
1940 : to connect to a server, but don't connect yet. */
1941 : Mapi
1942 189 : mapi_mapi(const char *host, int port, const char *username,
1943 : const char *password, const char *lang, const char *dbname)
1944 : {
1945 189 : Mapi mid;
1946 :
1947 189 : mid = mapi_new(NULL);
1948 189 : if (mid == NULL)
1949 : return NULL;
1950 189 : msettings *settings = mid->settings;
1951 :
1952 189 : if (lang == NULL)
1953 0 : lang = "sql";
1954 :
1955 189 : const char *sockdir = NULL;
1956 189 : if (host && host[0] == '/') {
1957 : sockdir = host;
1958 : host = NULL;
1959 : }
1960 :
1961 120 : msettings_error err = NULL;
1962 309 : do {
1963 120 : if (host && (err = msetting_set_string(settings, MP_HOST, host)))
1964 : break;
1965 189 : if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
1966 : break;
1967 189 : if (username && (err = msetting_set_string(settings, MP_USER, username)))
1968 : break;
1969 189 : if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
1970 : break;
1971 189 : if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
1972 : break;
1973 189 : if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
1974 : break;
1975 189 : if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
1976 : break;
1977 : } while (0);
1978 189 : if (err) {
1979 0 : mapi_setError(mid, err, __func__, MERROR);
1980 0 : return mid;
1981 : }
1982 :
1983 : return mid;
1984 : }
1985 :
1986 : Mapi
1987 40 : mapi_settings(msettings *settings)
1988 : {
1989 40 : assert(settings);
1990 40 : Mapi mid = mapi_new(settings);
1991 40 : if (mid == NULL)
1992 : return mid;
1993 :
1994 40 : set_uri(mid);
1995 40 : return mid;
1996 : }
1997 :
1998 :
1999 : /* Close a connection and free all memory associated with the
2000 : connection handle. */
2001 : MapiMsg
2002 419 : mapi_destroy(Mapi mid)
2003 : {
2004 419 : char **r;
2005 :
2006 419 : mapi_clrError(mid);
2007 :
2008 420 : while (mid->first)
2009 1 : mapi_close_handle(mid->first);
2010 419 : if (mid->connected)
2011 181 : (void) mapi_disconnect(mid);
2012 419 : if (mid->tracelog)
2013 0 : close_stream(mid->tracelog);
2014 :
2015 419 : free(mid->blk.buf);
2016 419 : free(mid->motd);
2017 419 : free(mid->server);
2018 419 : free(mid->uri);
2019 419 : free(mid->tracebuffer);
2020 419 : free(mid->noexplain);
2021 419 : if (mid->errorstr && mid->errorstr != mapi_nomem)
2022 0 : free(mid->errorstr);
2023 419 : free(mid->clientprefix);
2024 :
2025 419 : msettings_destroy(mid->settings);
2026 :
2027 419 : r = mid->redirects;
2028 419 : while (*r) {
2029 0 : free(*r);
2030 0 : r++;
2031 : }
2032 :
2033 419 : free(mid);
2034 419 : return MOK;
2035 : }
2036 :
2037 : /* Create a connection handle and connect to the server using the
2038 : specified parameters. */
2039 : Mapi
2040 11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
2041 : {
2042 11 : Mapi mid;
2043 :
2044 11 : mid = mapi_mapi(host, port, username, password, lang, dbname);
2045 11 : if (mid && mid->error == MOK)
2046 11 : mapi_reconnect(mid); /* actually, initial connect */
2047 11 : return mid;
2048 : }
2049 :
2050 : /* Returns an malloced NULL-terminated array with redirects */
2051 : char **
2052 0 : mapi_resolve(const char *host, int port, const char *pattern)
2053 : {
2054 0 : int rmax;
2055 0 : Mapi mid;
2056 :
2057 : /* if it doesn't make sense, don't try to crash */
2058 0 : if (pattern == NULL)
2059 : return NULL;
2060 :
2061 0 : mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
2062 0 : if (mid) {
2063 0 : if (mid->error == MOK) {
2064 0 : rmax = mid->redirmax;
2065 0 : mid->redirmax = 0;
2066 0 : mapi_reconnect(mid); /* real connect, don't follow redirects */
2067 0 : mid->redirmax = rmax;
2068 0 : if (mid->error == MOK) {
2069 0 : close_connection(mid); /* we didn't expect a connection actually */
2070 : } else {
2071 0 : char **ret = malloc(sizeof(char *) * MAXREDIR);
2072 0 : memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
2073 0 : mid->redirects[0] = NULL; /* make sure the members aren't freed */
2074 0 : mapi_destroy(mid);
2075 0 : return ret;
2076 : }
2077 : }
2078 0 : mapi_destroy(mid);
2079 : }
2080 : return NULL;
2081 : }
2082 :
2083 : void
2084 1418 : close_connection(Mapi mid)
2085 : {
2086 1418 : MapiHdl hdl;
2087 1418 : struct MapiResultSet *result;
2088 :
2089 1418 : mid->connected = false;
2090 1418 : mid->active = NULL;
2091 1419 : for (hdl = mid->first; hdl; hdl = hdl->next) {
2092 1 : hdl->active = NULL;
2093 1 : for (result = hdl->result; result; result = result->next)
2094 0 : result->tableid = -1;
2095 : }
2096 : /* finish channels */
2097 : /* Make sure that the write- (to-) stream is closed first,
2098 : * as the related read- (from-) stream closes the shared
2099 : * socket; see also src/common/stream.c:socket_close .
2100 : */
2101 1418 : if (mid->to) {
2102 1418 : close_stream(mid->to);
2103 1418 : mid->to = 0;
2104 : }
2105 1418 : if (mid->from) {
2106 1418 : close_stream(mid->from);
2107 1418 : mid->from = 0;
2108 : }
2109 1418 : mid->redircnt = 0;
2110 1418 : mapi_log_record(mid, "C", "Connection closed");
2111 1418 : }
2112 :
2113 : MapiMsg
2114 1405 : mapi_disconnect(Mapi mid)
2115 : {
2116 1405 : mapi_check(mid);
2117 :
2118 1405 : close_connection(mid);
2119 1405 : return MOK;
2120 : }
2121 :
2122 : /* Set callback function to retrieve or send file content for COPY
2123 : * INTO queries.
2124 : *
2125 : * char *getfile(void *private, const char *filename, bool binary,
2126 : * uint64_6 offset, size_t *size);
2127 : * Retrieve data from a file.
2128 : *
2129 : * The arguments are:
2130 : * private - the value of the filecontentprivate argument to
2131 : * mapi_setfilecallback;
2132 : * filename - the file to read (the application is free to interpret
2133 : * this any way it wants, including getting data over the
2134 : * Internet);
2135 : * binary - if set, the file is expected to contain binary data and
2136 : * should therefore be opened in binary mode, otherwise the
2137 : * file is expected to contain data in the UTF-8 encoding (of
2138 : * course, the application is free to transparently convert
2139 : * from the actual encoding to UTF-8);
2140 : * offset - the line number of the first line to be retrieved (this is
2141 : * one-based, i.e. the start of the file has line number one;
2142 : * lines are terminated by '\n');
2143 : * size - pointer in which to return the size of the chunk that is
2144 : * being returned.
2145 : *
2146 : * The callback function is expected to return data in chunks until it
2147 : * indicates to the caller that there is no more data or an error has
2148 : * occurred. Chunks can be any size. The caller does not modify or
2149 : * free the data returned. The size of the chunk being returned is
2150 : * stored in the size argument. Errors are indicated by returning a
2151 : * string containing an error message and setting *size to zero. The
2152 : * error message should not contain any newlines. Any call to the
2153 : * callback function is allowed to return an error.
2154 : *
2155 : * The first call to the callback function contains values for
2156 : * filename, binary, and offset. These parameters are all 0 for all
2157 : * subsequent calls for continuation data from the same file.
2158 : *
2159 : * If the caller has retrieved enough data before the file is
2160 : * exhausted, it calls the callback function one more time with a NULL
2161 : * pointer for the size argument. This gives the callback function
2162 : * the opportunity to free its resources (e.g. close the file).
2163 : *
2164 : * If there is no more data to be returned, the callback function
2165 : * returns a NULL pointer and sets *size to zero. No more calls for
2166 : * the current file will be made.
2167 : *
2168 : * Note that if the file to be read is empty, or contains fewer lines
2169 : * than the requested offset, the first call to the callback function
2170 : * may return NULL.
2171 : *
2172 : * char *putfile(void *private, const char *filename, bool binary,
2173 : * const void *data, size_t size);
2174 : * Send data to a file.
2175 : *
2176 : * The arguments are:
2177 : * private - the value of the filecontentprivate argument to
2178 : * mapi_setfilecallback;
2179 : * filename - the file to be written;
2180 : * binary - if set, the data to be written is binary and the file
2181 : * should therefore be opened in binary mode, otherwise the
2182 : * data is UTF-8 encoded text;
2183 : * data - the data to be written;
2184 : * size - the size of the data to be written.
2185 : *
2186 : * The callback is called multiple time to write a single file. The
2187 : * first time, a filename is specified, all subsequent times, the
2188 : * filename argument is NULL. When all data has been written, the
2189 : * callback function is called one last time with NULL pointer for the
2190 : * data argument so that the callback function can free any resources.
2191 : *
2192 : * When an error occurs, the callback function returns a string
2193 : * containing an error message after which the callback will not be
2194 : * called again for the same file. Otherwise, the callback function
2195 : * returns NULL.
2196 : *
2197 : * Note also that multibyte sequences may be split over two calls.
2198 : */
2199 : void
2200 153 : mapi_setfilecallback2(Mapi mid,
2201 : char *(*getfilecontent)(void *,
2202 : const char *, bool,
2203 : uint64_t, size_t *),
2204 : char *(*putfilecontent)(void *,
2205 : const char *, bool,
2206 : const void *, size_t),
2207 : void *filecontentprivate)
2208 : {
2209 153 : mid->getfilecontent = getfilecontent;
2210 153 : mid->putfilecontent = putfilecontent;
2211 153 : mid->filecontentprivate = filecontentprivate;
2212 153 : mid->putfilecontent_old = NULL;
2213 153 : mid->filecontentprivate_old = NULL;
2214 153 : }
2215 :
2216 : static char *
2217 0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
2218 : {
2219 0 : Mapi mid = priv;
2220 0 : void *priv_old = mid->filecontentprivate_old;
2221 0 : if (filename && binary)
2222 : return "Client does not support writing binary files";
2223 0 : return mid->putfilecontent_old(priv_old, filename, data, size);
2224 : }
2225 :
2226 : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
2227 : * INTO queries.
2228 : *
2229 : * Deprecated because it does not support binary downloads.
2230 : * Use mapi_setfilecallback2 instead.
2231 : */
2232 : void
2233 0 : mapi_setfilecallback(Mapi mid,
2234 : char *(*getfilecontent)(void *,
2235 : const char *, bool,
2236 : uint64_t, size_t *),
2237 : char *(*putfilecontent)(void *,
2238 : const char *,
2239 : const void *, size_t),
2240 : void *filecontentprivate)
2241 : {
2242 0 : mid->getfilecontent = getfilecontent;
2243 0 : mid->putfilecontent = putfilecontent_wrap;
2244 0 : mid->filecontentprivate = mid;
2245 0 : mid->putfilecontent_old = putfilecontent;
2246 0 : mid->filecontentprivate_old = filecontentprivate;
2247 0 : }
2248 :
2249 : void
2250 40 : mapi_setclientprefix(Mapi mid, const char *prefix)
2251 : {
2252 40 : free(mid->clientprefix);
2253 40 : if (prefix == NULL)
2254 0 : mid->clientprefix = NULL;
2255 : else
2256 40 : mid->clientprefix = strdup(prefix);
2257 :
2258 40 : }
2259 :
2260 : #define testBinding(hdl,fnr) \
2261 : do { \
2262 : mapi_hdl_check(hdl); \
2263 : if (fnr < 0) { \
2264 : return mapi_setError(hdl->mid, \
2265 : "Illegal field number", \
2266 : __func__, MERROR); \
2267 : } \
2268 : /* make sure there is enough space */ \
2269 : if (fnr >= hdl->maxbindings) \
2270 : mapi_extend_bindings(hdl, fnr); \
2271 : } while (0)
2272 :
2273 : #define testParam(hdl, fnr) \
2274 : do { \
2275 : mapi_hdl_check(hdl); \
2276 : if (fnr < 0) { \
2277 : return mapi_setError(hdl->mid, \
2278 : "Illegal param number", \
2279 : __func__, MERROR); \
2280 : } \
2281 : if (fnr >= hdl->maxparams) \
2282 : mapi_extend_params(hdl, fnr); \
2283 : } while (0)
2284 :
2285 : MapiMsg
2286 0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
2287 : {
2288 0 : testBinding(hdl, fnr);
2289 0 : hdl->bindings[fnr].outparam = ptr;
2290 :
2291 0 : hdl->bindings[fnr].outtype = MAPI_AUTO;
2292 0 : return MOK;
2293 : }
2294 :
2295 : MapiMsg
2296 0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
2297 : {
2298 0 : testBinding(hdl, fnr);
2299 0 : hdl->bindings[fnr].outparam = ptr;
2300 :
2301 0 : if (type >= 0 && type < MAPI_NUMERIC)
2302 0 : hdl->bindings[fnr].outtype = type;
2303 : else
2304 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2305 0 : return MOK;
2306 : }
2307 :
2308 : MapiMsg
2309 0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2310 : {
2311 0 : if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
2312 0 : return hdl->mid->error;
2313 :
2314 0 : hdl->bindings[fnr].scale = scale;
2315 0 : hdl->bindings[fnr].precision = prec;
2316 0 : return MOK;
2317 : }
2318 :
2319 : MapiMsg
2320 0 : mapi_clear_bindings(MapiHdl hdl)
2321 : {
2322 0 : mapi_hdl_check(hdl);
2323 0 : if (hdl->bindings)
2324 0 : memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
2325 : return MOK;
2326 : }
2327 :
2328 : MapiMsg
2329 0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
2330 : {
2331 0 : testParam(hdl, fnr);
2332 0 : hdl->params[fnr].inparam = ptr;
2333 :
2334 0 : if (ctype >= 0 && ctype < MAPI_NUMERIC)
2335 0 : hdl->params[fnr].intype = ctype;
2336 : else
2337 0 : return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
2338 0 : hdl->params[fnr].sizeptr = NULL;
2339 0 : hdl->params[fnr].outtype = sqltype;
2340 0 : hdl->params[fnr].scale = 0;
2341 0 : hdl->params[fnr].precision = 0;
2342 0 : return MOK;
2343 : }
2344 :
2345 : MapiMsg
2346 0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
2347 : {
2348 0 : testParam(hdl, fnr);
2349 0 : hdl->params[fnr].inparam = (void *) ptr;
2350 :
2351 0 : hdl->params[fnr].intype = MAPI_VARCHAR;
2352 0 : hdl->params[fnr].sizeptr = sizeptr;
2353 0 : hdl->params[fnr].outtype = sqltype;
2354 0 : hdl->params[fnr].scale = 0;
2355 0 : hdl->params[fnr].precision = 0;
2356 0 : return MOK;
2357 : }
2358 :
2359 : MapiMsg
2360 0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
2361 : {
2362 0 : return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
2363 : }
2364 :
2365 : MapiMsg
2366 0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
2367 : {
2368 0 : if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
2369 0 : return hdl->mid->error;
2370 :
2371 0 : hdl->params[fnr].scale = scale;
2372 0 : hdl->params[fnr].precision = prec;
2373 0 : return MOK;
2374 : }
2375 :
2376 : MapiMsg
2377 2 : mapi_clear_params(MapiHdl hdl)
2378 : {
2379 2 : mapi_hdl_check(hdl);
2380 2 : if (hdl->params)
2381 0 : memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
2382 : return MOK;
2383 : }
2384 :
2385 : static MapiHdl
2386 34196 : prepareQuery(MapiHdl hdl, const char *cmd)
2387 : {
2388 34196 : if (hdl && cmd) {
2389 34198 : if (hdl->query)
2390 2098 : free(hdl->query);
2391 34198 : hdl->query = strdup(cmd);
2392 34198 : assert(hdl->query);
2393 34198 : if (hdl->template) {
2394 0 : free(hdl->template);
2395 0 : hdl->template = NULL;
2396 : }
2397 : }
2398 34196 : return hdl;
2399 : }
2400 :
2401 :
2402 : MapiMsg
2403 0 : mapi_set_rtimeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2404 : {
2405 0 : mapi_check(mid);
2406 0 : if (mid->trace)
2407 0 : printf("Set timeout to %u\n", timeout);
2408 0 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2409 0 : return MOK;
2410 : }
2411 :
2412 : MapiMsg
2413 0 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
2414 : {
2415 0 : mapi_check(mid);
2416 0 : if (mid->trace)
2417 0 : printf("Set timeout to %u\n", timeout);
2418 0 : mnstr_settimeout(mid->to, timeout, callback, callback_data);
2419 0 : mnstr_settimeout(mid->from, timeout, callback, callback_data);
2420 0 : return MOK;
2421 : }
2422 :
2423 : MapiMsg
2424 0 : mapi_timeout(Mapi mid, unsigned int timeout)
2425 : {
2426 0 : return mapi_set_timeout(mid, timeout, NULL, NULL);
2427 : }
2428 :
2429 : MapiMsg
2430 1229 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
2431 : {
2432 1229 : MapiHdl hdl;
2433 :
2434 1229 : mapi_check(mid);
2435 1229 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
2436 : return MERROR;
2437 2458 : if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
2438 1229 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
2439 0 : close_connection(mid);
2440 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
2441 0 : return MERROR;
2442 : }
2443 1229 : mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
2444 1229 : hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
2445 1229 : if (hdl == NULL)
2446 : return MERROR;
2447 1229 : mid->active = hdl;
2448 1229 : read_into_cache(hdl, 0);
2449 1229 : mapi_close_handle(hdl); /* reads away any output */
2450 1229 : return MOK;
2451 : }
2452 :
2453 : MapiMsg
2454 0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
2455 : {
2456 0 : mapi_hdl_check(hdl);
2457 0 : if (finish_handle(hdl) != MOK)
2458 : return MERROR;
2459 0 : prepareQuery(hdl, cmd);
2460 0 : hdl->template = strdup(hdl->query);
2461 0 : assert(hdl->template);
2462 0 : return hdl->mid->error;
2463 : }
2464 :
2465 : MapiHdl
2466 0 : mapi_prepare(Mapi mid, const char *cmd)
2467 : {
2468 0 : MapiHdl hdl;
2469 :
2470 0 : mapi_check0(mid);
2471 0 : hdl = mapi_new_handle(mid);
2472 0 : if (hdl == NULL)
2473 : return NULL;
2474 0 : mapi_prepare_handle(hdl, cmd);
2475 0 : return hdl;
2476 : }
2477 :
2478 : /*
2479 : * Building the query string using replacement of values requires
2480 : * some care to not overflow the space allocated.
2481 : */
2482 : #define checkSpace(len) \
2483 : do { \
2484 : /* note: k==strlen(hdl->query) */ \
2485 : if (k+len >= lim) { \
2486 : lim = k + len + MAPIBLKSIZE; \
2487 : char *q = realloc(hdl->query, lim); \
2488 : if (q == NULL) { \
2489 : free(hdl->query); \
2490 : hdl->query = NULL; \
2491 : return; \
2492 : } \
2493 : hdl->query = q; \
2494 : } \
2495 : } while (0)
2496 :
2497 : static void
2498 32918 : mapi_param_store(MapiHdl hdl)
2499 : {
2500 32918 : char *val, buf[MAPIBLKSIZE];
2501 32918 : char *p = hdl->template, *q;
2502 32918 : int i;
2503 32918 : size_t k;
2504 32918 : size_t lim;
2505 :
2506 32918 : if (hdl->template == 0)
2507 : return;
2508 :
2509 0 : lim = strlen(hdl->template) + MAPIBLKSIZE;
2510 0 : REALLOC(hdl->query, lim);
2511 0 : if (hdl->query == NULL)
2512 : return;
2513 0 : hdl->query[0] = 0;
2514 0 : k = 0;
2515 :
2516 0 : q = strchr(hdl->template, PLACEHOLDER);
2517 0 : i = 0;
2518 : /* loop invariant: k == strlen(hdl->query) */
2519 0 : while (q && i < hdl->maxparams) {
2520 0 : if (q > p && *(q - 1) == '\\') {
2521 0 : q = strchr(q + 1, PLACEHOLDER);
2522 0 : continue;
2523 : }
2524 :
2525 0 : if (k + (q - p) >= lim) {
2526 0 : lim += MAPIBLKSIZE;
2527 0 : REALLOC(hdl->query, lim);
2528 0 : if (hdl->query == NULL)
2529 : return;
2530 : }
2531 0 : memcpy(hdl->query + k, p, q - p);
2532 0 : k += q - p;
2533 0 : hdl->query[k] = 0;
2534 :
2535 0 : if (hdl->params[i].inparam == 0) {
2536 0 : char *nullstr = "NULL";
2537 0 : checkSpace(5);
2538 0 : if (msettings_lang_is_mal(hdl->mid->settings))
2539 0 : nullstr = "nil";
2540 0 : strcpy(hdl->query + k, nullstr);
2541 : } else {
2542 0 : void *src = hdl->params[i].inparam; /* abbrev */
2543 :
2544 0 : switch (hdl->params[i].intype) {
2545 0 : case MAPI_TINY:
2546 0 : checkSpace(5);
2547 0 : snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
2548 0 : break;
2549 0 : case MAPI_UTINY:
2550 0 : checkSpace(5);
2551 0 : snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
2552 0 : break;
2553 0 : case MAPI_SHORT:
2554 0 : checkSpace(10);
2555 0 : snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
2556 0 : break;
2557 0 : case MAPI_USHORT:
2558 0 : checkSpace(10);
2559 0 : snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
2560 0 : break;
2561 0 : case MAPI_INT:
2562 0 : checkSpace(20);
2563 0 : snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
2564 0 : break;
2565 0 : case MAPI_UINT:
2566 0 : checkSpace(20);
2567 0 : snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
2568 0 : break;
2569 0 : case MAPI_LONG:
2570 0 : checkSpace(20);
2571 0 : snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
2572 0 : break;
2573 0 : case MAPI_ULONG:
2574 0 : checkSpace(20);
2575 0 : snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
2576 0 : break;
2577 0 : case MAPI_LONGLONG:
2578 0 : checkSpace(30);
2579 0 : snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
2580 0 : break;
2581 0 : case MAPI_ULONGLONG:
2582 0 : checkSpace(30);
2583 0 : snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
2584 0 : break;
2585 0 : case MAPI_FLOAT:
2586 0 : checkSpace(30);
2587 0 : snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
2588 0 : break;
2589 0 : case MAPI_DOUBLE:
2590 0 : checkSpace(30);
2591 0 : snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
2592 0 : break;
2593 0 : case MAPI_DATE:
2594 0 : checkSpace(50);
2595 0 : snprintf(hdl->query + k, lim - k,
2596 : "DATE '%04hd-%02hu-%02hu'",
2597 0 : ((MapiDate *) src)->year,
2598 0 : ((MapiDate *) src)->month,
2599 0 : ((MapiDate *) src)->day);
2600 0 : break;
2601 0 : case MAPI_TIME:
2602 0 : checkSpace(60);
2603 0 : snprintf(hdl->query + k, lim - k,
2604 : "TIME '%02hu:%02hu:%02hu'",
2605 0 : ((MapiTime *) src)->hour,
2606 0 : ((MapiTime *) src)->minute,
2607 0 : ((MapiTime *) src)->second);
2608 0 : break;
2609 0 : case MAPI_DATETIME:
2610 0 : checkSpace(110);
2611 0 : snprintf(hdl->query + k, lim - k,
2612 : "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
2613 0 : ((MapiDateTime *) src)->year,
2614 0 : ((MapiDateTime *) src)->month,
2615 0 : ((MapiDateTime *) src)->day,
2616 0 : ((MapiDateTime *) src)->hour,
2617 0 : ((MapiDateTime *) src)->minute,
2618 0 : ((MapiDateTime *) src)->second,
2619 : ((MapiDateTime *) src)->fraction);
2620 0 : break;
2621 0 : case MAPI_CHAR:
2622 0 : buf[0] = *(char *) src;
2623 0 : buf[1] = 0;
2624 0 : val = mapi_quote(buf, 1);
2625 : /* note: k==strlen(hdl->query) */
2626 0 : if (k + strlen(val) + 3 >= lim) {
2627 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2628 0 : char *q = realloc(hdl->query, lim);
2629 0 : if (q == NULL) {
2630 0 : free(hdl->query);
2631 0 : hdl->query = NULL;
2632 0 : free(val);
2633 0 : return;
2634 : }
2635 0 : hdl->query = q;
2636 : }
2637 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2638 0 : free(val);
2639 0 : break;
2640 0 : case MAPI_VARCHAR:
2641 0 : val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
2642 : /* note: k==strlen(hdl->query) */
2643 0 : if (k + strlen(val) + 3 >= lim) {
2644 0 : lim = k + strlen(val) + 3 + MAPIBLKSIZE;
2645 0 : char *q = realloc(hdl->query, lim);
2646 0 : if (q == NULL) {
2647 0 : free(hdl->query);
2648 0 : hdl->query = NULL;
2649 0 : free(val);
2650 0 : return;
2651 : }
2652 0 : hdl->query = q;
2653 : }
2654 0 : snprintf(hdl->query + k, lim - k, "'%s'", val);
2655 0 : free(val);
2656 0 : break;
2657 0 : default:
2658 0 : strcpy_len(hdl->query + k, src, lim - k);
2659 0 : break;
2660 : }
2661 : }
2662 0 : k += strlen(hdl->query + k);
2663 :
2664 0 : i++;
2665 0 : p = q + 1;
2666 0 : q = strchr(p, PLACEHOLDER);
2667 : }
2668 0 : checkSpace(strlen(p) + 1);
2669 0 : strcpy(hdl->query + k, p);
2670 0 : if (hdl->mid->trace)
2671 0 : printf("param_store: result=%s\n", hdl->query);
2672 : return;
2673 : }
2674 :
2675 : /* Read one more line from the input stream and return it. This
2676 : returns a pointer into the input buffer, so the data needs to be
2677 : copied if it is to be retained. */
2678 : static char *
2679 1798200 : read_line(Mapi mid)
2680 : {
2681 1798200 : char *reply;
2682 1798200 : char *nl;
2683 1798200 : char *s; /* from where to search for newline */
2684 :
2685 1798200 : if (mid->active == NULL)
2686 : return NULL;
2687 :
2688 : /* check if we need to read more blocks to get a new line */
2689 1798200 : mid->blk.eos = false;
2690 1798200 : s = mid->blk.buf + mid->blk.nxt;
2691 2113842 : while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
2692 315632 : ssize_t len;
2693 :
2694 315632 : if (mid->blk.lim - mid->blk.end < BLOCK) {
2695 16390 : int len;
2696 :
2697 16390 : len = mid->blk.lim;
2698 16390 : if (mid->blk.nxt <= BLOCK) {
2699 : /* extend space */
2700 3814 : len += BLOCK;
2701 : }
2702 16390 : REALLOC(mid->blk.buf, len + 1);
2703 16390 : if (mid->blk.nxt > 0) {
2704 13024 : memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
2705 13024 : mid->blk.end -= mid->blk.nxt;
2706 13024 : mid->blk.nxt = 0;
2707 : }
2708 16390 : mid->blk.lim = len;
2709 : }
2710 :
2711 315632 : s = mid->blk.buf + mid->blk.end;
2712 :
2713 : /* fetch one more block */
2714 315632 : if (mid->trace)
2715 0 : printf("fetch next block: start at:%d\n", mid->blk.end);
2716 315632 : for (;;) {
2717 315632 : len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
2718 315643 : if (len == -1 && mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
2719 0 : mnstr_clearerr(mid->from);
2720 0 : if (mid->oobintr && !mid->active->aborted) {
2721 0 : mid->active->aborted = true;
2722 0 : mnstr_putoob(mid->to, 1);
2723 : }
2724 : } else
2725 : break;
2726 : }
2727 315643 : check_stream(mid, mid->from, len, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
2728 315644 : mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
2729 315644 : mid->blk.buf[mid->blk.end + len] = 0;
2730 315644 : if (mid->trace) {
2731 0 : printf("got next block: length:%zd\n", len);
2732 0 : printf("text:%s\n", mid->blk.buf + mid->blk.end);
2733 : }
2734 315644 : if (len == 0) { /* add prompt */
2735 146297 : if (mnstr_eof(mid->from))
2736 : return NULL;
2737 146295 : if (mid->blk.end > mid->blk.nxt) {
2738 : /* add fake newline since newline was
2739 : * missing from server */
2740 5 : nl = mid->blk.buf + mid->blk.end;
2741 5 : *nl = '\n';
2742 5 : mid->blk.end++;
2743 : }
2744 146295 : len = 2;
2745 146295 : mid->blk.buf[mid->blk.end] = PROMPTBEG;
2746 146295 : mid->blk.buf[mid->blk.end + 1] = '\n';
2747 146295 : mid->blk.buf[mid->blk.end + 2] = 0;
2748 : }
2749 315642 : mid->blk.end += (int) len;
2750 : }
2751 1798210 : if (mid->trace) {
2752 0 : printf("got complete block: \n");
2753 0 : printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
2754 : }
2755 :
2756 : /* we have a complete line in the buffer */
2757 1798210 : assert(nl);
2758 1798210 : *nl++ = 0;
2759 1798210 : reply = mid->blk.buf + mid->blk.nxt;
2760 1798210 : mid->blk.nxt = (int) (nl - mid->blk.buf);
2761 :
2762 1798210 : if (mid->trace)
2763 0 : printf("read_line:%s\n", reply);
2764 : return reply;
2765 : }
2766 :
2767 : /* set or unset the autocommit flag in the server */
2768 : MapiMsg
2769 1181 : mapi_setAutocommit(Mapi mid, bool autocommit)
2770 : {
2771 1181 : if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
2772 : return MOK;
2773 8 : if (!msettings_lang_is_sql(mid->settings)) {
2774 0 : mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
2775 0 : return MERROR;
2776 : }
2777 8 : msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
2778 8 : if (err)
2779 0 : return mapi_setError(mid, err, __func__, MERROR);
2780 8 : if (!mid->connected)
2781 : return MOK;
2782 8 : if (autocommit)
2783 1 : return mapi_Xcommand(mid, "auto_commit", "1");
2784 : else
2785 7 : return mapi_Xcommand(mid, "auto_commit", "0");
2786 : }
2787 :
2788 : MapiMsg
2789 31 : mapi_set_time_zone(Mapi mid, int time_zone)
2790 : {
2791 31 : msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
2792 31 : if (err)
2793 0 : return mapi_setError(mid, err, __func__, MERROR);
2794 31 : if (!mid->connected)
2795 : return MOK;
2796 :
2797 0 : char buf[100];
2798 0 : if (time_zone < 0)
2799 0 : snprintf(buf, sizeof(buf),
2800 : "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
2801 0 : -time_zone / 3600, (-time_zone % 3600) / 60);
2802 : else
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 :
2807 0 : MapiHdl hdl = mapi_query(mid, buf);
2808 0 : if (hdl == NULL)
2809 0 : return mid->error;
2810 0 : mapi_close_handle(hdl);
2811 :
2812 0 : return MOK;
2813 : }
2814 :
2815 : MapiMsg
2816 0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
2817 : {
2818 0 : if (mid->columnar_protocol == columnar_protocol)
2819 : return MOK;
2820 0 : mid->columnar_protocol = columnar_protocol;
2821 0 : if (!mid->connected)
2822 : return MOK;
2823 0 : if (columnar_protocol)
2824 0 : return mapi_Xcommand(mid, "columnar_protocol", "1");
2825 : else
2826 0 : return mapi_Xcommand(mid, "columnar_protocol", "0");
2827 : }
2828 :
2829 : MapiMsg
2830 215 : mapi_set_size_header(Mapi mid, bool value)
2831 : {
2832 215 : if (!msettings_lang_is_sql(mid->settings)) {
2833 0 : mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
2834 0 : return MERROR;
2835 : }
2836 215 : if (mid->sizeheader == value)
2837 : return MOK;
2838 49 : mid->sizeheader = value;
2839 49 : if (!mid->connected)
2840 : return MOK;
2841 0 : if (value)
2842 0 : return mapi_Xcommand(mid, "sizeheader", "1");
2843 : else
2844 0 : return mapi_Xcommand(mid, "sizeheader", "0");
2845 : }
2846 :
2847 : MapiMsg
2848 2 : mapi_release_id(Mapi mid, int id)
2849 : {
2850 2 : char buf[10];
2851 :
2852 2 : if (!msettings_lang_is_sql(mid->settings)) {
2853 0 : mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
2854 0 : return MERROR;
2855 : }
2856 2 : snprintf(buf, sizeof(buf), "%d", id);
2857 2 : return mapi_Xcommand(mid, "release", buf);
2858 : }
2859 :
2860 : void
2861 177 : mapi_trace(Mapi mid, bool flag)
2862 : {
2863 177 : mapi_clrError(mid);
2864 177 : mid->trace = flag;
2865 177 : }
2866 :
2867 :
2868 : static int
2869 1491753 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
2870 : {
2871 : /* This function does the actual work for splicing a real,
2872 : multi-column row into columns. It skips over the first
2873 : character and ends at the end of the string or at endchar,
2874 : whichever comes first. */
2875 1491753 : char *start;
2876 1491753 : char **anchors;
2877 1491753 : int i;
2878 1491753 : size_t len;
2879 1491753 : size_t *lens;
2880 :
2881 1491753 : reply++; /* skip over initial char (usually '[') */
2882 1491753 : i = 0;
2883 1491753 : anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
2884 1491559 : lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
2885 5619576 : for (;;) {
2886 5619576 : if (i >= length) {
2887 29256 : length = i + 1;
2888 29256 : REALLOC(anchors, length);
2889 29256 : REALLOC(lens, length);
2890 : }
2891 5619576 : if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
2892 : /* indicate NULL/nil with NULL pointer */
2893 794799 : free(start);
2894 794799 : start = NULL;
2895 794799 : len = 0;
2896 : }
2897 5619576 : lens[i] = len;
2898 5619576 : anchors[i++] = start;
2899 5619576 : if (reply == NULL)
2900 : break;
2901 5619576 : while (*reply && isspace((unsigned char) *reply))
2902 0 : reply++;
2903 5619576 : if (*reply == ',') {
2904 4127819 : reply++;
2905 8255638 : while (*reply && isspace((unsigned char) *reply))
2906 4127819 : reply++;
2907 1491757 : } else if (*reply == 0 || *reply == endchar)
2908 : break;
2909 : }
2910 1491753 : *anchorsp = anchors;
2911 1491753 : *lensp = lens;
2912 1491753 : return i;
2913 : }
2914 :
2915 : static MapiMsg
2916 15103 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
2917 : {
2918 15103 : int i; /* just a counter */
2919 15103 : int64_t n = 0; /* # of tuples being deleted from front */
2920 :
2921 15103 : result->cache.tuplecount = 0;
2922 15103 : for (i = 0; i < result->cache.writer - k; i++) {
2923 0 : if (result->cache.line[i].rows) {
2924 0 : if (result->cache.line[i].rows[0] == '[' ||
2925 : result->cache.line[i].rows[0] == '=')
2926 0 : n++;
2927 0 : free(result->cache.line[i].rows);
2928 : }
2929 0 : result->cache.line[i].rows = result->cache.line[i + k].rows;
2930 0 : result->cache.line[i + k].rows = 0;
2931 0 : if (result->cache.line[i].anchors) {
2932 : int j = 0;
2933 :
2934 0 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2935 0 : free(result->cache.line[i].anchors[j]);
2936 0 : free(result->cache.line[i].anchors);
2937 : }
2938 0 : if (result->cache.line[i].lens)
2939 0 : free(result->cache.line[i].lens);
2940 0 : result->cache.line[i].anchors = result->cache.line[i + k].anchors;
2941 0 : result->cache.line[i + k].anchors = 0;
2942 0 : result->cache.line[i].lens = result->cache.line[i + k].lens;
2943 0 : result->cache.line[i + k].lens = 0;
2944 0 : result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
2945 0 : if (result->cache.line[i].rows &&
2946 0 : (result->cache.line[i].rows[0] == '[' ||
2947 : result->cache.line[i].rows[0] == '=')) {
2948 0 : result->cache.line[i].tuplerev = result->cache.tuplecount;
2949 0 : result->cache.line[result->cache.tuplecount++].tupleindex = i;
2950 : }
2951 : }
2952 : /* after the previous loop, i == result->cache.writer - k, and
2953 : the last (result->cache.writer - k) cache entries have been
2954 : cleared already , so we don't need to go the Full Monty
2955 : here */
2956 1370817 : for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
2957 1355714 : if (result->cache.line[i].rows) {
2958 1355714 : if (result->cache.line[i].rows[0] == '[' ||
2959 : result->cache.line[i].rows[0] == '=')
2960 1354829 : n++;
2961 1355714 : free(result->cache.line[i].rows);
2962 : }
2963 1355714 : result->cache.line[i].rows = 0;
2964 1355714 : if (result->cache.line[i].anchors) {
2965 : int j = 0;
2966 :
2967 6546626 : for (j = 0; j < result->cache.line[i].fldcnt; j++)
2968 5191797 : free(result->cache.line[i].anchors[j]);
2969 1354829 : free(result->cache.line[i].anchors);
2970 : }
2971 1355714 : if (result->cache.line[i].lens)
2972 1354829 : free(result->cache.line[i].lens);
2973 1355714 : result->cache.line[i].anchors = 0;
2974 1355714 : result->cache.line[i].lens = 0;
2975 1355714 : result->cache.line[i].fldcnt = 0;
2976 : }
2977 15103 : result->cache.reader -= k;
2978 15103 : if (result->cache.reader < 0)
2979 15103 : result->cache.reader = -1;
2980 15103 : result->cache.writer -= k;
2981 15103 : if (result->cache.writer < 0) /* "cannot happen" */
2982 0 : result->cache.writer = 0;
2983 15103 : result->cache.first += n;
2984 :
2985 15103 : return MOK;
2986 : }
2987 :
2988 : static void
2989 44228 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
2990 : {
2991 44228 : int incr, newsize, oldsize = result->cache.limit, i;
2992 :
2993 : /* if there are read entries, delete them */
2994 44228 : if (result->cache.reader >= 0) {
2995 13527 : mapi_cache_freeup_internal(result, result->cache.reader + 1);
2996 : /* since we've made space, we can return */
2997 13527 : return;
2998 : }
2999 :
3000 : /* extend row cache */
3001 30701 : retry:;
3002 30703 : if (oldsize == 0)
3003 : incr = 100;
3004 : else
3005 9 : incr = oldsize * 2;
3006 9 : if (incr > 200000)
3007 0 : incr = 20000;
3008 30703 : newsize = oldsize + incr;
3009 30703 : if (result->cache.rowlimit > 0 &&
3010 4 : newsize > result->cache.rowlimit &&
3011 : !cacheall) {
3012 4 : newsize = result->cache.rowlimit;
3013 4 : incr = newsize - oldsize;
3014 4 : if (incr <= 0) {
3015 : /* not enough space, so increase limit and try again */
3016 2 : result->cache.rowlimit += 100;
3017 2 : goto retry;
3018 : }
3019 : }
3020 :
3021 30701 : REALLOC(result->cache.line, newsize + 1);
3022 30701 : assert(result->cache.line);
3023 3133614 : for (i = oldsize; i <= newsize; i++) {
3024 3102913 : result->cache.line[i].fldcnt = 0;
3025 3102913 : result->cache.line[i].rows = NULL;
3026 3102913 : result->cache.line[i].tupleindex = -1;
3027 3102913 : result->cache.line[i].tuplerev = -1;
3028 3102913 : result->cache.line[i].anchors = NULL;
3029 3102913 : result->cache.line[i].lens = NULL;
3030 : }
3031 30701 : result->cache.limit = newsize;
3032 : }
3033 :
3034 : /* store a line in the cache */
3035 : static void
3036 1512974 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
3037 : {
3038 : /* manage the row cache space first */
3039 1512974 : if (result->cache.writer >= result->cache.limit)
3040 44228 : mapi_extend_cache(result, cacheall);
3041 :
3042 1512974 : result->cache.line[result->cache.writer].rows = line;
3043 1512974 : result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
3044 1512974 : result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
3045 1512974 : if (*line == '[' || *line == '=') {
3046 1390822 : result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
3047 1390822 : if (result->row_count < result->cache.first + result->cache.tuplecount)
3048 206 : result->row_count = result->cache.first + result->cache.tuplecount;
3049 : }
3050 1512974 : result->cache.writer++;
3051 1512974 : }
3052 :
3053 : static struct MapiResultSet *
3054 155722 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
3055 : {
3056 155722 : char *tag, *etag;
3057 155722 : int i, n;
3058 155722 : char **anchors;
3059 155722 : size_t *lens;
3060 :
3061 155722 : if (line[0] == '&') {
3062 33573 : char *nline = line;
3063 33573 : int qt;
3064 33573 : uint64_t queryid;
3065 :
3066 : /* handle fields &qt */
3067 :
3068 33573 : nline++; /* query type */
3069 33573 : qt = (int) strtol(nline, &nline, 0);
3070 :
3071 33573 : if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
3072 33546 : result = new_result(hdl);
3073 33573 : result->querytype = qt;
3074 33573 : result->commentonly = false;
3075 33573 : result->querytime = 0;
3076 33573 : result->maloptimizertime = 0;
3077 33573 : result->sqloptimizertime = 0;
3078 :
3079 33573 : nline++; /* skip space */
3080 33573 : switch (qt) {
3081 598 : case Q_SCHEMA:
3082 598 : result->querytime = strtoll(nline, &nline, 10);
3083 598 : result->maloptimizertime = strtoll(nline, &nline, 10);
3084 598 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3085 598 : break;
3086 227 : case Q_TRANS:
3087 227 : msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
3088 227 : break;
3089 2229 : case Q_UPDATE:
3090 2229 : result->row_count = strtoll(nline, &nline, 10);
3091 2229 : result->last_id = strtoll(nline, &nline, 10);
3092 2229 : queryid = strtoll(nline, &nline, 10);
3093 2229 : result->querytime = strtoll(nline, &nline, 10);
3094 2229 : result->maloptimizertime = strtoll(nline, &nline, 10);
3095 2229 : result->sqloptimizertime = strtoll(nline, &nline, 10);
3096 2229 : break;
3097 30369 : case Q_TABLE:
3098 30369 : if (sscanf(nline,
3099 : "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
3100 : " %" SCNd64 " %" SCNd64 " %" SCNd64,
3101 : &result->tableid, &result->row_count,
3102 : &result->fieldcnt, &result->tuple_count,
3103 : &queryid, &result->querytime,
3104 : &result->maloptimizertime,
3105 : &result->sqloptimizertime) < 8){
3106 2 : result->querytime = 0;
3107 2 : result->maloptimizertime = 0;
3108 2 : result->sqloptimizertime = 0;
3109 : }
3110 : (void) queryid; /* ignored for now */
3111 : break;
3112 123 : case Q_PREPARE:
3113 123 : sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
3114 : &result->tableid, &result->row_count,
3115 : &result->fieldcnt, &result->tuple_count);
3116 123 : break;
3117 27 : case Q_BLOCK:
3118 : /* Mapi ignores the Q_BLOCK header, so spoof
3119 : * the querytype back to a Q_TABLE to let it
3120 : * go unnoticed */
3121 27 : result->querytype = Q_TABLE;
3122 27 : break;
3123 : }
3124 :
3125 :
3126 33573 : if (result->fieldcnt > result->maxfields) {
3127 30492 : REALLOC(result->fields, result->fieldcnt);
3128 30492 : memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
3129 30492 : result->maxfields = result->fieldcnt;
3130 : }
3131 :
3132 : /* start of new SQL result */
3133 33573 : return result;
3134 : }
3135 122149 : if (result == NULL)
3136 4 : result = new_result(hdl);
3137 :
3138 122149 : if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
3139 : /* comment */
3140 : return result;
3141 : }
3142 :
3143 122149 : line = strdup(line); /* make copy we can play with */
3144 122149 : etag = strrchr(line, '#');
3145 122149 : if (etag == 0 || etag == line) {
3146 : /* not a useful header line */
3147 0 : free(line);
3148 0 : return result;
3149 : }
3150 :
3151 122149 : n = slice_row(line, NULL, &anchors, &lens, 10, '#');
3152 :
3153 122149 : result->commentonly = false;
3154 :
3155 122149 : tag = etag + 1;
3156 244290 : while (*tag && isspace((unsigned char) *tag))
3157 122141 : tag++;
3158 :
3159 122149 : if (n > result->fieldcnt) {
3160 8 : result->fieldcnt = n;
3161 8 : if (n > result->maxfields) {
3162 8 : REALLOC(result->fields, n);
3163 8 : memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
3164 8 : result->maxfields = n;
3165 : }
3166 : }
3167 :
3168 122149 : if (strcmp(tag, "name") == 0) {
3169 30492 : result->fieldcnt = n;
3170 107463 : for (i = 0; i < n; i++) {
3171 76971 : if (anchors[i]) {
3172 76971 : if (result->fields[i].columnname)
3173 0 : free(result->fields[i].columnname);
3174 76971 : result->fields[i].columnname = anchors[i];
3175 76971 : anchors[i] = NULL;
3176 : }
3177 : }
3178 91657 : } else if (strcmp(tag, "type") == 0) {
3179 30496 : result->fieldcnt = n;
3180 107475 : for (i = 0; i < n; i++) {
3181 76979 : if (anchors[i]) {
3182 76979 : if (result->fields[i].columntype)
3183 0 : free(result->fields[i].columntype);
3184 76979 : result->fields[i].columntype = anchors[i];
3185 76979 : anchors[i] = NULL;
3186 : }
3187 : }
3188 61161 : } else if (strcmp(tag, "length") == 0) {
3189 30492 : result->fieldcnt = n;
3190 107463 : for (i = 0; i < n; i++) {
3191 76971 : if (anchors[i])
3192 76971 : result->fields[i].columnlength = atoi(anchors[i]);
3193 : }
3194 30669 : } else if (strcmp(tag, "table_name") == 0) {
3195 30492 : result->fieldcnt = n;
3196 107463 : for (i = 0; i < n; i++) {
3197 76971 : if (anchors[i]) {
3198 76971 : if (result->fields[i].tablename)
3199 0 : free(result->fields[i].tablename);
3200 76971 : result->fields[i].tablename = anchors[i];
3201 76971 : anchors[i] = NULL;
3202 : }
3203 : }
3204 177 : } else if (strcmp(tag, "typesizes") == 0) {
3205 169 : result->fieldcnt = n;
3206 1215 : for (i = 0; i < n; i++) {
3207 1046 : if (anchors[i]) {
3208 1046 : char *p;
3209 1046 : result->fields[i].digits = atoi(anchors[i]);
3210 1046 : p = strchr(anchors[i], ' ');
3211 1046 : if (p)
3212 1046 : result->fields[i].scale = atoi(p + 1);
3213 : }
3214 : }
3215 : }
3216 :
3217 : /* clean up */
3218 122149 : free(line);
3219 431095 : for (i = 0; i < n; i++)
3220 308946 : if (anchors[i])
3221 78025 : free(anchors[i]);
3222 122149 : free(anchors);
3223 122149 : free(lens);
3224 :
3225 122149 : return result;
3226 : }
3227 :
3228 : static void
3229 75 : write_file(MapiHdl hdl, char *filename, bool binary)
3230 : {
3231 75 : Mapi mid = hdl->mid;
3232 75 : char *line;
3233 75 : char data[BLOCK];
3234 75 : ssize_t len;
3235 :
3236 75 : (void) read_line(mid); /* read flush marker */
3237 75 : if (filename == NULL) {
3238 : /* malloc failure */
3239 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3240 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3241 0 : return;
3242 : }
3243 75 : if (mid->putfilecontent == NULL) {
3244 0 : free(filename);
3245 0 : mnstr_printf(mid->to, "!HY000!cannot send files\n");
3246 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3247 0 : return;
3248 : }
3249 75 : line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
3250 75 : free(filename);
3251 75 : if (line != NULL) {
3252 0 : if (strchr(line, '\n'))
3253 0 : line = "incorrect response from application";
3254 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", line);
3255 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3256 0 : return;
3257 : }
3258 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3259 180811 : for (;;) {
3260 180811 : len = mnstr_read(mid->from, data, 1, sizeof(data));
3261 180811 : if (len == -1) {
3262 0 : if (mnstr_errnr(mid->from) == MNSTR_INTERRUPT) {
3263 0 : mnstr_clearerr(mid->from);
3264 0 : if (mid->oobintr && !hdl->aborted) {
3265 0 : hdl->aborted = true;
3266 0 : mnstr_putoob(mid->to, 1);
3267 : }
3268 : } else {
3269 : break;
3270 : }
3271 180811 : } else if (len == 0) {
3272 : break;
3273 180736 : } else if (line == NULL) {
3274 180736 : line = mid->putfilecontent(mid->filecontentprivate,
3275 : NULL, binary, data, len);
3276 : }
3277 : }
3278 75 : if (line == NULL)
3279 75 : line = mid->putfilecontent(mid->filecontentprivate,
3280 : NULL, binary, NULL, 0);
3281 75 : if (line && strchr(line, '\n'))
3282 : line = "incorrect response from application";
3283 75 : mnstr_printf(mid->to, "%s\n", line ? line : "");
3284 75 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3285 : }
3286 :
3287 : #define MiB (1 << 20) /* a megabyte */
3288 :
3289 : static void
3290 104 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
3291 : {
3292 104 : Mapi mid = hdl->mid;
3293 104 : size_t size = 0, flushsize = 0;
3294 104 : char *data, *line;
3295 :
3296 104 : (void) read_line(mid); /* read flush marker */
3297 104 : if (filename == NULL) {
3298 : /* malloc failure */
3299 0 : mnstr_printf(mid->to, "!HY001!allocation failure\n");
3300 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3301 20 : return;
3302 : }
3303 104 : if (mid->getfilecontent == NULL) {
3304 0 : free(filename);
3305 0 : mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
3306 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3307 0 : return;
3308 : }
3309 104 : data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
3310 : off, &size);
3311 104 : free(filename);
3312 104 : if (data != NULL && size == 0) {
3313 0 : if (strchr(data, '\n'))
3314 0 : data = "incorrect response from application";
3315 0 : mnstr_printf(mid->to, "!HY000!%.64s\n", data);
3316 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3317 0 : return;
3318 : }
3319 104 : mnstr_printf(mid->to, "\n");
3320 23786 : while (data != NULL && size != 0) {
3321 23683 : if (flushsize >= MiB) {
3322 : /* after every MiB give the server the
3323 : * opportunity to stop reading more data */
3324 1414 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3325 : /* at this point we expect to get a PROMPT2 if
3326 : * the server wants more data, or a PROMPT3 if
3327 : * the server had enough; anything else is a
3328 : * protocol violation */
3329 1414 : line = read_line(mid);
3330 1414 : if (line == NULL) {
3331 : /* error */
3332 0 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3333 0 : return;
3334 : }
3335 1414 : assert(line[0] == PROMPTBEG);
3336 1414 : if (line[0] != PROMPTBEG) {
3337 : /* error in protocol */
3338 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3339 : return;
3340 : }
3341 1414 : if (line[1] == PROMPT3[1]) {
3342 : /* done reading: close file */
3343 1 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3344 1 : (void) read_line(mid);
3345 1 : return;
3346 : }
3347 1413 : assert(line[1] == PROMPT2[1]);
3348 1413 : if (line[1] != PROMPT2[1]) {
3349 : /* error in protocol */
3350 : (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
3351 : return;
3352 : }
3353 : /* clear the flush marker */
3354 1413 : (void) read_line(mid);
3355 1413 : flushsize = 0;
3356 : }
3357 23682 : if (size > MiB) {
3358 0 : if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
3359 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3360 0 : return;
3361 : }
3362 0 : size -= MiB;
3363 0 : data += MiB;
3364 0 : flushsize += MiB;
3365 : } else {
3366 23682 : if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
3367 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3368 0 : return;
3369 : }
3370 23682 : flushsize += size;
3371 23682 : data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
3372 : }
3373 : }
3374 103 : if (data != NULL && size == 0) {
3375 : /* some error occurred */
3376 0 : mnstr_clearerr(mid->from);
3377 0 : if (mid->oobintr && !hdl->aborted) {
3378 0 : hdl->aborted = true;
3379 0 : mnstr_putoob(mid->to, 1);
3380 : }
3381 : }
3382 103 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3383 103 : line = read_line(mid);
3384 103 : if (line == NULL)
3385 : return;
3386 103 : assert(line[0] == PROMPTBEG);
3387 103 : if (line[0] != PROMPTBEG)
3388 : return;
3389 103 : if (line[1] == PROMPT3[1]) {
3390 19 : (void) read_line(mid);
3391 19 : return;
3392 : }
3393 84 : assert(line[1] == PROMPT2[1]);
3394 84 : if (line[1] != PROMPT2[1])
3395 : return;
3396 84 : (void) read_line(mid);
3397 84 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3398 84 : line = read_line(mid);
3399 84 : if (line == NULL)
3400 : return;
3401 84 : assert(line[0] == PROMPTBEG);
3402 84 : assert(line[1] == PROMPT3[1]);
3403 84 : (void) read_line(mid);
3404 : }
3405 :
3406 :
3407 : /* Read ahead and cache data read. Depending on the second argument,
3408 : reading may stop at the first non-header and non-error line, or at
3409 : a prompt.
3410 : This function is called either after a command has been sent to the
3411 : server (in which case the second argument is 1), when the
3412 : application asks for a result tuple that hadn't been cached yet (in
3413 : which case the second argument is also 1), or whenever all pending
3414 : data needs to be read in order to send a new command to the server
3415 : (in which case the second argument is 0).
3416 : Header lines result tuples are stored in the cache. Certain header
3417 : lines may cause a new result set to be created in which case all
3418 : subsequent lines are added to that result set.
3419 : */
3420 : MapiMsg
3421 1532948 : read_into_cache(MapiHdl hdl, int lookahead)
3422 : {
3423 1532948 : char *line;
3424 1532948 : Mapi mid;
3425 1532948 : struct MapiResultSet *result;
3426 :
3427 1532948 : mid = hdl->mid;
3428 1532948 : assert(mid->active == hdl);
3429 1532948 : if (hdl->needmore) {
3430 0 : hdl->needmore = false;
3431 0 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3432 0 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
3433 : }
3434 1532948 : if ((result = hdl->active) == NULL)
3435 144478 : result = hdl->result; /* may also be NULL */
3436 :
3437 1691314 : for (;;) {
3438 1691314 : line = read_line(mid);
3439 1691318 : if (line == NULL) {
3440 0 : if (mid->from && mnstr_eof(mid->from)) {
3441 0 : return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
3442 : }
3443 :
3444 0 : return mid->error;
3445 : }
3446 1691318 : switch (*line) {
3447 144690 : case PROMPTBEG: /* \001 */
3448 144690 : mid->active = NULL;
3449 144690 : hdl->active = NULL;
3450 : /* set needmore flag if line equals PROMPT2 up
3451 : to newline */
3452 144690 : if (line[1] == PROMPT2[1] && line[2] == '\0') {
3453 : /* skip end of block */
3454 103327 : mid->active = hdl;
3455 103327 : (void) read_line(mid);
3456 103327 : hdl->needmore = true;
3457 103327 : mid->active = hdl;
3458 41363 : } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
3459 179 : mid->active = hdl;
3460 179 : line = read_line(mid);
3461 179 : bool binary = false;
3462 : /* rb FILE
3463 : * r OFF FILE
3464 : * w FILE
3465 : * wb FILE
3466 : */
3467 179 : switch (*line++) {
3468 104 : case 'r': {
3469 104 : uint64_t off = 0;
3470 104 : if (*line == 'b') {
3471 85 : line++;
3472 85 : binary = true;
3473 : } else {
3474 19 : off = strtoul(line, &line, 10);
3475 : }
3476 104 : if (*line++ != ' ') {
3477 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3478 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3479 0 : break;
3480 : }
3481 104 : read_file(hdl, off, strdup(line), binary);
3482 104 : break;
3483 : }
3484 75 : case 'w':
3485 75 : if (*line == 'b') {
3486 75 : line++;
3487 75 : binary = true;
3488 : }
3489 75 : if (*line++ != ' ') {
3490 0 : mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
3491 0 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3492 0 : break;
3493 : }
3494 75 : write_file(hdl, strdup(line), binary);
3495 75 : break;
3496 : }
3497 179 : continue;
3498 : }
3499 144511 : return mid->error;
3500 81 : case '!':
3501 : /* start a new result set if we don't have one
3502 : yet (duh!), or if we've already seen
3503 : normal output for the current one */
3504 81 : if (result == NULL ||
3505 2 : result->cache.writer > 0 ||
3506 2 : result->querytype > 0) {
3507 79 : result = new_result(hdl);
3508 79 : result->commentonly = false;
3509 79 : hdl->active = result;
3510 : }
3511 81 : add_error(result, line + 1 /* skip ! */ );
3512 81 : if (!mid->error)
3513 79 : mid->error = MSERVER;
3514 : break;
3515 155722 : case '%':
3516 : case '#':
3517 : case '&':
3518 155722 : if (lookahead < 0)
3519 0 : lookahead = 1;
3520 155722 : result = parse_header_line(hdl, line, result);
3521 155722 : hdl->active = result;
3522 155722 : if (result && *line != '&')
3523 122149 : add_cache(result, strdup(line), !lookahead);
3524 : break;
3525 1390825 : default:
3526 1390825 : if (result == NULL) {
3527 195 : result = new_result(hdl);
3528 195 : hdl->active = result;
3529 : }
3530 1390825 : add_cache(result, strdup(line), !lookahead);
3531 1390825 : if (lookahead > 0 &&
3532 1389049 : (result->querytype == -1 /* unknown (not SQL) */ ||
3533 608 : result->querytype == Q_TABLE ||
3534 : result->querytype == Q_UPDATE)) {
3535 1388441 : return mid->error;
3536 : }
3537 : break;
3538 : }
3539 : }
3540 : }
3541 :
3542 : static MapiMsg
3543 32920 : mapi_execute_internal(MapiHdl hdl)
3544 : {
3545 32920 : size_t size;
3546 32920 : char *cmd;
3547 32920 : Mapi mid;
3548 :
3549 32920 : mid = hdl->mid;
3550 32920 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3551 : return MERROR;
3552 32920 : assert(mid->active == NULL);
3553 32920 : finish_handle(hdl);
3554 32918 : mapi_param_store(hdl);
3555 32918 : cmd = hdl->query;
3556 32918 : if (cmd == NULL)
3557 : return MERROR;
3558 32918 : size = strlen(cmd);
3559 :
3560 32918 : bool is_sql = msettings_lang_is_sql(mid->settings);
3561 32916 : char *prefix = is_sql ? "s" : "";
3562 4216 : char *suffix = is_sql ? "\n;" : "";
3563 32916 : mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
3564 :
3565 32916 : if (is_sql) {
3566 : /* indicate to server this is a SQL command */
3567 28700 : ssize_t w = mnstr_write(mid->to, "s", 1, 1);
3568 28700 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3569 : }
3570 32916 : ssize_t w = mnstr_write(mid->to, cmd, 1, size);
3571 32916 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3572 : /* all SQL statements should end with a semicolon */
3573 : /* for the other languages it is assumed that the statements are correct */
3574 32916 : if (is_sql) {
3575 28700 : w = mnstr_write(mid->to, "\n;", 2, 1);
3576 28700 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3577 : }
3578 32916 : w = mnstr_write(mid->to, "\n", 1, 1);
3579 32918 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3580 32919 : w = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3581 32913 : check_stream(mid, mid->to, w, "write error on stream", mid->error);
3582 32915 : mid->active = hdl;
3583 :
3584 32915 : return MOK;
3585 : }
3586 :
3587 : MapiMsg
3588 0 : mapi_execute(MapiHdl hdl)
3589 : {
3590 0 : int ret;
3591 :
3592 0 : mapi_hdl_check(hdl);
3593 0 : if ((ret = mapi_execute_internal(hdl)) == MOK)
3594 0 : return read_into_cache(hdl, 1);
3595 :
3596 : return ret;
3597 : }
3598 :
3599 : /*
3600 : * The routine mapi_query is one of the most heavily used ones.
3601 : * It sends a complete statement for execution
3602 : * (i.e., ending in a newline; possibly including additional newlines).
3603 : * Interaction with the server is sped up using block based interaction.
3604 : * The query is retained in the Mapi structure to repeat shipping.
3605 : */
3606 : MapiHdl
3607 30804 : mapi_query(Mapi mid, const char *cmd)
3608 : {
3609 30804 : int ret;
3610 30804 : MapiHdl hdl;
3611 :
3612 30804 : mapi_check0(mid);
3613 30805 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3614 30817 : ret = mid->error;
3615 30817 : if (ret == MOK)
3616 30808 : ret = mapi_execute_internal(hdl);
3617 30798 : if (ret == MOK)
3618 30801 : ret = read_into_cache(hdl, 1);
3619 : return hdl;
3620 : }
3621 :
3622 : /* version of mapi_query that does not wait for a response */
3623 : MapiHdl
3624 0 : mapi_send(Mapi mid, const char *cmd)
3625 : {
3626 0 : int ret;
3627 0 : MapiHdl hdl;
3628 :
3629 0 : mapi_check0(mid);
3630 0 : hdl = prepareQuery(mapi_new_handle(mid), cmd);
3631 0 : ret = mid->error;
3632 0 : if (ret == MOK)
3633 0 : ret = mapi_execute_internal(hdl);
3634 : return hdl;
3635 : }
3636 :
3637 : MapiMsg
3638 0 : mapi_read_response(MapiHdl hdl)
3639 : {
3640 0 : return read_into_cache(hdl, 1);
3641 : }
3642 :
3643 : MapiMsg
3644 2116 : mapi_query_handle(MapiHdl hdl, const char *cmd)
3645 : {
3646 2116 : int ret;
3647 :
3648 2116 : mapi_hdl_check(hdl);
3649 2116 : if (finish_handle(hdl) != MOK)
3650 : return MERROR;
3651 2116 : prepareQuery(hdl, cmd);
3652 2116 : ret = hdl->mid->error;
3653 2116 : if (ret == MOK)
3654 2116 : ret = mapi_execute_internal(hdl);
3655 2116 : if (ret == MOK)
3656 2116 : ret = read_into_cache(hdl, 1);
3657 : return ret;
3658 : }
3659 :
3660 : MapiHdl
3661 5519 : mapi_query_prep(Mapi mid)
3662 : {
3663 5519 : mapi_check0(mid);
3664 5519 : if (mid->active && read_into_cache(mid->active, 0) != MOK)
3665 : return NULL;
3666 5519 : assert(mid->active == NULL);
3667 5519 : if (msettings_lang_is_sql(mid->settings)) {
3668 : /* indicate to server this is a SQL command */
3669 5519 : mnstr_write(mid->to, "S", 1, 1);
3670 5519 : mapi_log_data(mid, "SEND", "S", 1);
3671 : }
3672 5519 : return (mid->active = mapi_new_handle(mid));
3673 : }
3674 :
3675 : MapiMsg
3676 108502 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
3677 : {
3678 108502 : Mapi mid;
3679 :
3680 108502 : mapi_hdl_check(hdl);
3681 108502 : mid = hdl->mid;
3682 108502 : assert(mid->active == NULL || mid->active == hdl);
3683 108502 : mid->active = hdl;
3684 : /* remember the query just for the error messages */
3685 108502 : if (hdl->query == NULL) {
3686 5182 : hdl->query = malloc(size + 1);
3687 5182 : if (hdl->query) {
3688 5182 : strcpy_len(hdl->query, query, size + 1);
3689 : }
3690 : } else {
3691 103320 : size_t sz = strlen(hdl->query);
3692 103320 : char *q;
3693 :
3694 103320 : if (sz < 512 &&
3695 1722 : (q = realloc(hdl->query, sz + size + 1)) != NULL) {
3696 1722 : strcpy_len(q + sz, query, size + 1);
3697 1722 : hdl->query = q;
3698 : }
3699 : }
3700 :
3701 108502 : if (mid->trace) {
3702 0 : printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
3703 : }
3704 108502 : hdl->needmore = false;
3705 108502 : size = mnstr_write(mid->to, query, 1, size);
3706 108502 : if (mid->tracelog) {
3707 0 : mnstr_write(mid->tracelog, query, 1, size);
3708 0 : mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
3709 : }
3710 108502 : check_stream(mid, mid->to, size, "write error on stream", mid->error);
3711 108502 : return mid->error;
3712 : }
3713 :
3714 : MapiMsg
3715 108846 : mapi_query_done(MapiHdl hdl)
3716 : {
3717 108846 : int ret;
3718 108846 : Mapi mid;
3719 :
3720 108846 : mapi_hdl_check(hdl);
3721 108846 : mid = hdl->mid;
3722 108846 : assert(mid->active == NULL || mid->active == hdl);
3723 108846 : mid->active = hdl;
3724 108846 : hdl->needmore = false;
3725 108846 : int f = mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
3726 108846 : check_stream(mid, mid->to, f, "write error on stream", mid->error);
3727 108846 : ret = mid->error;
3728 108846 : if (ret == MOK)
3729 108846 : ret = read_into_cache(hdl, 1);
3730 108846 : return ret == MOK && hdl->needmore ? MMORE : ret;
3731 : }
3732 :
3733 : MapiMsg
3734 0 : mapi_query_abort(MapiHdl hdl, int reason)
3735 : {
3736 0 : Mapi mid;
3737 :
3738 0 : assert(reason > 0 && reason <= 127);
3739 0 : mapi_hdl_check(hdl);
3740 0 : mid = hdl->mid;
3741 0 : assert(mid->active == NULL || mid->active == hdl);
3742 0 : if (mid->oobintr && !hdl->aborted && mnstr_putoob(mid->to, reason) == 0) {
3743 0 : hdl->aborted = true;
3744 0 : return MOK;
3745 : }
3746 : return MERROR;
3747 : }
3748 :
3749 : MapiMsg
3750 222 : mapi_cache_limit(Mapi mid, int limit)
3751 : {
3752 : /* clean out superfluous space TODO */
3753 222 : msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
3754 222 : if (err)
3755 0 : return mapi_setError(mid, err, __func__, MERROR);
3756 222 : if (!mid->connected)
3757 : return MOK;
3758 48 : mapi_check(mid);
3759 : /* if (hdl->cache.rowlimit < hdl->cache.limit) { */
3760 : /* TODO: decide what to do here */
3761 : /* hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
3762 : /* if (hdl->cache.writer > hdl->cache.limit) { */
3763 : /* hdl->cache.writer = hdl->cache.limit; */
3764 : /* if (hdl->cache.reader > hdl->cache.writer) */
3765 : /* hdl->cache.reader = hdl->cache.writer; */
3766 : /* } */
3767 : /* } */
3768 48 : if (msettings_lang_is_sql(mid->settings)) {
3769 48 : MapiHdl hdl;
3770 :
3771 48 : if (mid->active)
3772 1 : read_into_cache(mid->active, 0);
3773 :
3774 48 : mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
3775 96 : if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
3776 48 : mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
3777 0 : close_connection(mid);
3778 0 : mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
3779 0 : return MERROR;
3780 : }
3781 48 : hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
3782 48 : if (hdl == NULL)
3783 : return MERROR;
3784 48 : mid->active = hdl;
3785 48 : read_into_cache(hdl, 0);
3786 48 : mapi_close_handle(hdl); /* reads away any output */
3787 : }
3788 : return MOK;
3789 : }
3790 :
3791 : MapiMsg
3792 0 : mapi_fetch_reset(MapiHdl hdl)
3793 : {
3794 0 : mapi_hdl_check(hdl);
3795 0 : if (hdl->result)
3796 0 : hdl->result->cache.reader = -1;
3797 : return MOK;
3798 : }
3799 :
3800 : MapiMsg
3801 2668 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
3802 : {
3803 2668 : struct MapiResultSet *result;
3804 :
3805 2668 : mapi_hdl_check(hdl);
3806 2668 : result = hdl->result;
3807 2668 : switch (whence) {
3808 : case MAPI_SEEK_SET:
3809 : break;
3810 0 : case MAPI_SEEK_CUR:
3811 0 : rownr += result->cache.line[result->cache.reader + 1].tuplerev;
3812 0 : break;
3813 0 : case MAPI_SEEK_END:
3814 0 : if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3815 : return MERROR;
3816 0 : rownr += result->row_count;
3817 0 : break;
3818 0 : default:
3819 0 : return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
3820 : }
3821 2668 : if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
3822 : return MERROR;
3823 2668 : if (rownr < 0 || rownr > result->row_count)
3824 0 : return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
3825 2668 : if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
3826 : /* we've got the requested tuple in the cache */
3827 1092 : result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
3828 : } else {
3829 : /* we don't have the requested tuple in the cache
3830 : reset the cache and at the next fetch we'll get the data */
3831 1576 : if (mapi_cache_freeup(hdl, 100) == MOK) {
3832 1576 : result->cache.first = rownr;
3833 : }
3834 : }
3835 2668 : return hdl->mid->error;
3836 : }
3837 :
3838 : /* Make space in the cache for new tuples, ignore the read pointer */
3839 : MapiMsg
3840 1576 : mapi_cache_freeup(MapiHdl hdl, int percentage)
3841 : {
3842 1576 : struct MapiResultSet *result;
3843 1576 : int k; /* # of cache lines to be deleted from front */
3844 :
3845 1576 : mapi_hdl_check(hdl);
3846 1576 : result = hdl->result;
3847 1576 : if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
3848 : return MOK;
3849 1576 : if (percentage < 0 || percentage > 100)
3850 0 : percentage = 100;
3851 1576 : k = (result->cache.writer * percentage) / 100;
3852 1576 : if (k < 1)
3853 0 : k = 1;
3854 1576 : return mapi_cache_freeup_internal(result, k);
3855 : }
3856 :
3857 : static char *
3858 1542203 : mapi_fetch_line_internal(MapiHdl hdl)
3859 : {
3860 1542203 : Mapi mid;
3861 1542203 : struct MapiResultSet *result;
3862 1542203 : char *reply;
3863 :
3864 : /* try to read a line from the cache */
3865 1542203 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
3866 1394210 : mid = hdl->mid;
3867 1394210 : if (mid->active != hdl || hdl->needmore)
3868 : return NULL;
3869 :
3870 1388026 : if (read_into_cache(hdl, 1) != MOK)
3871 : return NULL;
3872 1388026 : if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
3873 : return NULL;
3874 : }
3875 1511887 : reply = result->cache.line[++result->cache.reader].rows;
3876 1511887 : if (hdl->bindings && (*reply == '[' || *reply == '=')) {
3877 0 : mapi_slice_row(result, result->cache.reader);
3878 0 : mapi_store_bind(result, result->cache.reader);
3879 : }
3880 : return reply;
3881 : }
3882 :
3883 : /*
3884 : * The routine mapi_fetch_line forms the basic interaction with the server.
3885 : * It simply retrieves the next line and stores it in the row cache.
3886 : * The field anchor structure is prepared for subsequent use by
3887 : * mapi_fetch_row.
3888 : * The content received is analyzed further by mapi_getRow()
3889 : */
3890 : char *
3891 1542176 : mapi_fetch_line(MapiHdl hdl)
3892 : {
3893 1542176 : char *reply;
3894 1542176 : struct MapiResultSet *result;
3895 :
3896 1542176 : mapi_hdl_check0(hdl);
3897 1542176 : reply = mapi_fetch_line_internal(hdl);
3898 1542176 : if (reply == NULL &&
3899 60632 : (result = hdl->result) != NULL &&
3900 30316 : msettings_lang_is_sql(hdl->mid->settings) &&
3901 30305 : result->querytype == Q_TABLE &&
3902 30186 : result->row_count > 0 &&
3903 24262 : result->cache.first + result->cache.tuplecount < result->row_count) {
3904 27 : if (hdl->needmore) /* escalate */
3905 : return NULL;
3906 27 : if (hdl->mid->active != NULL)
3907 0 : read_into_cache(hdl->mid->active, 0);
3908 27 : hdl->mid->active = hdl;
3909 27 : hdl->active = result;
3910 27 : mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
3911 : result->tableid,
3912 : result->cache.first + result->cache.tuplecount);
3913 27 : int e;
3914 27 : if ((e = mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
3915 : result->tableid,
3916 27 : result->cache.first + result->cache.tuplecount)) < 0 ||
3917 27 : (e = mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA)) < 0)
3918 0 : check_stream(hdl->mid, hdl->mid->to, e, "sending export command", NULL);
3919 27 : reply = mapi_fetch_line_internal(hdl);
3920 : }
3921 : return reply;
3922 : }
3923 :
3924 : /*
3925 : * To synchronize on a prompt, the low level routine mapi_finish can be used.
3926 : * It discards all output received.
3927 : */
3928 : MapiMsg
3929 0 : mapi_finish(MapiHdl hdl)
3930 : {
3931 0 : mapi_hdl_check(hdl);
3932 0 : return finish_handle(hdl);
3933 : }
3934 :
3935 : /* msg is a string consisting comma-separated values. The list of
3936 : values is terminated by endchar or by the end-of-string NULL byte.
3937 : Values can be quoted strings or unquoted values. Upon return,
3938 : *start points to the start of the first value which is stripped of
3939 : leading and trailing white space, and if it was a quoted string,
3940 : also of the quotes. Also, backslash-escaped characters in the
3941 : quoted string are replaced by the values the escapes represent.
3942 : *next points to either the start of the next value (i.e. after the
3943 : separating comma, possibly to the leading white space of the next
3944 : value), or to the trailing ] or NULL byte if this was the last
3945 : value. *lenp is the number of bytes occupied by the (possibly
3946 : converted) value, excluding final NULL byte.
3947 : msg is *not* a const string: it is altered by this function.
3948 : The function returns true if the string was quoted.
3949 : */
3950 : static int
3951 5619576 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
3952 : {
3953 5619576 : const char *p = msg;
3954 5619576 : char quote;
3955 :
3956 : /* first skip over leading white space */
3957 7111321 : while (*p && isspace((unsigned char) *p))
3958 1491745 : p++;
3959 5619576 : quote = *p;
3960 5619576 : if (quote == '\'' || quote == '"') {
3961 4333389 : size_t len = 0;
3962 4333389 : char *s, *start;
3963 :
3964 : /* get quoted string and remove trailing bracket first */
3965 4333389 : p++;
3966 : /* first count how much space we need */
3967 4333389 : msg = p; /* save for later */
3968 176766601 : while (*p && *p != quote) {
3969 172433212 : if (*p == '\\') {
3970 29467 : p++;
3971 29467 : switch (*p) {
3972 0 : case '0':
3973 : case '1':
3974 : case '2':
3975 : case '3':
3976 : /* this could be the start of
3977 : an octal sequence, check it
3978 : out */
3979 0 : if (p[1] && p[2] &&
3980 0 : p[1] >= '0' && p[1] <= '7' &&
3981 0 : p[2] >= '0' && p[2] <= '7') {
3982 0 : p += 2;
3983 0 : break;
3984 : }
3985 : /* fall through */
3986 : default:
3987 : break;
3988 : }
3989 : }
3990 172433212 : p++;
3991 172433212 : len++;
3992 : }
3993 : /* now allocate space and copy string into new space */
3994 4333389 : p = msg; /* start over */
3995 4333389 : start = s = malloc(len + 1);
3996 176766601 : while (*p && *p != quote) {
3997 172433212 : if (*p == '\\') {
3998 29467 : p++;
3999 29467 : switch (*p) {
4000 : /* later
4001 : case '0': case '1': case '2': case '3': case '4':
4002 : case '5': case '6': case '7': case '8': case '9':
4003 : */
4004 1700 : case 'n':
4005 1700 : *s = '\n';
4006 1700 : break;
4007 15 : case 't':
4008 15 : *s = '\t';
4009 15 : break;
4010 0 : case 'r':
4011 0 : *s = '\r';
4012 0 : break;
4013 0 : case 'f':
4014 0 : *s = '\f';
4015 0 : break;
4016 0 : case '0':
4017 : case '1':
4018 : case '2':
4019 : case '3':
4020 : /* this could be the start of
4021 : an octal sequence, check it
4022 : out */
4023 0 : if (p[1] && p[2] &&
4024 0 : p[1] >= '0' && p[1] <= '7' &&
4025 0 : p[2] >= '0' && p[2] <= '7') {
4026 0 : *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
4027 0 : p += 2;
4028 0 : break;
4029 : }
4030 : /* fall through */
4031 : default:
4032 27752 : *s = *p;
4033 27752 : break;
4034 : }
4035 29467 : p++;
4036 : } else {
4037 172403745 : *s = *p++;
4038 : }
4039 172433212 : s++;
4040 : }
4041 4333389 : *s = 0; /* close string */
4042 4333389 : p++; /* skip over end-of-string quote */
4043 : /* skip over trailing junk (presumably white space) */
4044 5666657 : while (*p && *p != ',' && *p != endchar)
4045 1333268 : p++;
4046 4333389 : if (next)
4047 4333389 : *next = p;
4048 4333389 : *str = start;
4049 4333389 : if (lenp)
4050 4333389 : *lenp = len;
4051 :
4052 4333389 : return 1;
4053 : } else {
4054 : const char *s;
4055 : size_t len;
4056 :
4057 : /* p points at first non-white space character */
4058 57432106 : msg = p; /* record start of value */
4059 : /* find separator or terminator */
4060 57432106 : while (*p && *p != ',' && *p != '\t' && *p != endchar)
4061 56145919 : p++;
4062 : /* search back over trailing white space */
4063 1408532 : for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
4064 : ;
4065 1286187 : if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
4066 1286187 : s++;
4067 1286187 : if (*p == '\t') {
4068 36150 : p++;
4069 : }
4070 1286187 : len = s - msg;
4071 1286187 : *str = malloc(len + 1);
4072 1286187 : strcpy_len(*str, msg, len + 1);
4073 :
4074 1286187 : if (next)
4075 1286187 : *next = p;
4076 1286187 : if (lenp)
4077 1286187 : *lenp = len;
4078 1286187 : return 0;
4079 : }
4080 : }
4081 :
4082 : char *
4083 0 : mapi_unquote(char *msg)
4084 : {
4085 0 : char *start;
4086 :
4087 0 : unquote(msg, &start, NULL, ']', NULL);
4088 0 : return start;
4089 : }
4090 :
4091 : char *
4092 0 : mapi_quote(const char *msg, int size)
4093 : {
4094 : /* we absolutely don't need more than this (until we start
4095 : producing octal escapes */
4096 0 : char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
4097 0 : char *t = s;
4098 :
4099 : /* the condition is tricky: if initially size < 0, we must
4100 : continue until a NULL byte, else, size gives the number of
4101 : bytes to be copied */
4102 0 : while (size < 0 ? *msg : size > 0) {
4103 0 : if (size > 0)
4104 0 : size--;
4105 0 : switch (*msg) {
4106 0 : case '\n':
4107 0 : *t++ = '\\';
4108 0 : *t++ = 'n';
4109 0 : break;
4110 0 : case '\t':
4111 0 : *t++ = '\\';
4112 0 : *t++ = 't';
4113 0 : break;
4114 0 : case PLACEHOLDER:
4115 0 : *t++ = '\\';
4116 0 : *t++ = PLACEHOLDER;
4117 0 : break;
4118 0 : case '\\':
4119 0 : *t++ = '\\';
4120 0 : *t++ = '\\';
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 '\0':
4131 0 : *t++ = '\\';
4132 0 : *t++ = '0';
4133 0 : break;
4134 0 : default:
4135 0 : *t++ = *msg;
4136 0 : break;
4137 : }
4138 0 : msg++;
4139 : /* also deal with binaries */
4140 : }
4141 0 : *t = 0;
4142 0 : return s;
4143 : }
4144 :
4145 : static int
4146 0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
4147 : {
4148 : /* extend the bindings table */
4149 0 : int nm = hdl->maxbindings + 32;
4150 :
4151 0 : if (nm <= minbindings)
4152 0 : nm = minbindings + 32;
4153 0 : REALLOC(hdl->bindings, nm);
4154 0 : assert(hdl->bindings);
4155 : /* clear new entries */
4156 0 : memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
4157 0 : hdl->maxbindings = nm;
4158 0 : return MOK;
4159 : }
4160 :
4161 : static int
4162 0 : mapi_extend_params(MapiHdl hdl, int minparams)
4163 : {
4164 : /* extend the params table */
4165 0 : int nm = hdl->maxparams + 32;
4166 :
4167 0 : if (nm <= minparams)
4168 0 : nm = minparams + 32;
4169 0 : REALLOC(hdl->params, nm);
4170 0 : assert(hdl->params);
4171 : /* clear new entries */
4172 0 : memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
4173 0 : hdl->maxparams = nm;
4174 0 : return MOK;
4175 : }
4176 :
4177 : static MapiMsg
4178 0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
4179 : {
4180 0 : char *val;
4181 :
4182 0 : val = result->cache.line[cr].anchors[fnr];
4183 :
4184 0 : if (val == 0) {
4185 0 : return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
4186 : }
4187 :
4188 : /* auto convert to C-type */
4189 0 : switch (outtype) {
4190 0 : case MAPI_TINY:
4191 0 : *(signed char *) dst = (signed char) strtol(val, NULL, 0);
4192 0 : break;
4193 0 : case MAPI_UTINY:
4194 0 : *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
4195 0 : break;
4196 0 : case MAPI_SHORT:
4197 0 : *(short *) dst = (short) strtol(val, NULL, 0);
4198 0 : break;
4199 0 : case MAPI_USHORT:
4200 0 : *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
4201 0 : break;
4202 0 : case MAPI_NUMERIC:
4203 : case MAPI_INT:
4204 0 : *(int *) dst = (int) strtol(val, NULL, 0);
4205 0 : break;
4206 0 : case MAPI_UINT:
4207 0 : *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
4208 0 : break;
4209 0 : case MAPI_LONG:
4210 0 : *(long *) dst = strtol(val, NULL, 0);
4211 0 : break;
4212 0 : case MAPI_ULONG:
4213 0 : *(unsigned long *) dst = strtoul(val, NULL, 0);
4214 0 : break;
4215 0 : case MAPI_LONGLONG:
4216 0 : *(int64_t *) dst = strtoll(val, NULL, 0);
4217 0 : break;
4218 0 : case MAPI_ULONGLONG:
4219 0 : *(uint64_t *) dst = strtoull(val, NULL, 0);
4220 0 : break;
4221 0 : case MAPI_CHAR:
4222 0 : *(char *) dst = *val;
4223 0 : break;
4224 0 : case MAPI_FLOAT:
4225 0 : *(float *) dst = strtof(val, NULL);
4226 0 : break;
4227 0 : case MAPI_DOUBLE:
4228 0 : *(double *) dst = strtod(val, NULL);
4229 0 : break;
4230 0 : case MAPI_DATE:
4231 0 : sscanf(val, "%hd-%hu-%hu",
4232 : &((MapiDate *) dst)->year,
4233 : &((MapiDate *) dst)->month,
4234 : &((MapiDate *) dst)->day);
4235 0 : break;
4236 0 : case MAPI_TIME:
4237 0 : sscanf(val, "%hu:%hu:%hu",
4238 : &((MapiTime *) dst)->hour,
4239 : &((MapiTime *) dst)->minute,
4240 : &((MapiTime *) dst)->second);
4241 0 : break;
4242 0 : case MAPI_DATETIME:{
4243 0 : int n;
4244 :
4245 0 : ((MapiDateTime *) dst)->fraction = 0;
4246 0 : sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
4247 : &((MapiDateTime *) dst)->year,
4248 : &((MapiDateTime *) dst)->month,
4249 : &((MapiDateTime *) dst)->day,
4250 : &((MapiDateTime *) dst)->hour,
4251 : &((MapiDateTime *) dst)->minute,
4252 : &((MapiDateTime *) dst)->second,
4253 : &n);
4254 0 : if (val[n] == '.') {
4255 0 : unsigned int fac = 1000000000;
4256 0 : unsigned int nsec = 0;
4257 :
4258 0 : for (n++; isdigit((unsigned char) val[n]); n++) {
4259 0 : fac /= 10;
4260 0 : nsec += (val[n] - '0') * fac;
4261 : }
4262 0 : ((MapiDateTime *) dst)->fraction = nsec;
4263 : }
4264 0 : break;
4265 : }
4266 0 : case MAPI_AUTO:
4267 : case MAPI_VARCHAR:
4268 : default:
4269 0 : *(char **) dst = val;
4270 : }
4271 : return MOK;
4272 : }
4273 :
4274 : MapiMsg
4275 0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
4276 : {
4277 0 : struct MapiResultSet *result;
4278 :
4279 0 : mapi_hdl_check(hdl);
4280 :
4281 0 : if ((result = hdl->result) == NULL) {
4282 0 : return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
4283 : }
4284 :
4285 0 : if (fnr < 0 || fnr >= result->fieldcnt) {
4286 0 : return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4287 : }
4288 :
4289 0 : return store_field(result, result->cache.reader, fnr, outtype, dst);
4290 : }
4291 :
4292 : static void
4293 0 : mapi_store_bind(struct MapiResultSet *result, int cr)
4294 : {
4295 0 : int i;
4296 0 : MapiHdl hdl = result->hdl;
4297 :
4298 0 : for (i = 0; i < hdl->maxbindings; i++)
4299 0 : if (hdl->bindings[i].outparam)
4300 0 : store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
4301 0 : }
4302 :
4303 : /*
4304 : * The low level routine mapi_slice_row breaks the last row received
4305 : * into pieces and binds the field descriptors with their location. All
4306 : * escaped characters are immediately replaced, such that we end with a
4307 : * list of C-strings. It overwrites the contents of the row buffer,
4308 : * because de-escaping only reduces the size. It also silently extends
4309 : * the field descriptor table.
4310 : */
4311 : static int
4312 1369653 : mapi_slice_row(struct MapiResultSet *result, int cr)
4313 : {
4314 1369653 : char *p;
4315 1369653 : int i = 0;
4316 :
4317 1369653 : p = result->cache.line[cr].rows;
4318 1369653 : if (p == NULL)
4319 0 : return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
4320 1369653 : if (result->cache.line[cr].fldcnt)
4321 : return result->cache.line[cr].fldcnt; /* already sliced */
4322 :
4323 1369653 : if (*p != '[') {
4324 : /* nothing to slice */
4325 49 : i = 1;
4326 49 : REALLOC(result->cache.line[cr].anchors, 1);
4327 49 : REALLOC(result->cache.line[cr].lens, 1);
4328 : /* skip initial '=' if present */
4329 49 : if (*p == '=')
4330 49 : p++;
4331 49 : result->cache.line[cr].anchors[0] = strdup(p);
4332 49 : result->cache.line[cr].lens[0] = strlen(p);
4333 : } else {
4334 : /* work on a copy to preserve the original */
4335 1369604 : p = strdup(p);
4336 1369809 : i = slice_row(p,
4337 1369604 : msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
4338 : &result->cache.line[cr].anchors,
4339 : &result->cache.line[cr].lens,
4340 : result->fieldcnt, ']');
4341 1369604 : free(p);
4342 : }
4343 1369653 : if (i != result->fieldcnt) {
4344 : int j;
4345 194 : for (j = 0; j < result->fieldcnt; j++) {
4346 0 : if (result->fields[j].columnname)
4347 0 : free(result->fields[j].columnname);
4348 0 : result->fields[j].columnname = NULL;
4349 0 : if (result->fields[j].columntype)
4350 0 : free(result->fields[j].columntype);
4351 0 : result->fields[j].columntype = NULL;
4352 0 : if (result->fields[j].tablename)
4353 0 : free(result->fields[j].tablename);
4354 0 : result->fields[j].tablename = NULL;
4355 0 : result->fields[j].columnlength = 0;
4356 : }
4357 : }
4358 1369653 : if (i > result->fieldcnt) {
4359 194 : result->fieldcnt = i;
4360 194 : if (i > result->maxfields) {
4361 194 : REALLOC(result->fields, i);
4362 194 : memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
4363 194 : result->maxfields = i;
4364 : }
4365 : }
4366 1369653 : result->cache.line[cr].fldcnt = i;
4367 1369653 : return i;
4368 : }
4369 :
4370 : /*
4371 : * The rows presented are broken down into pieces to
4372 : * simplify access later on. However, mclient may
4373 : * first want to check the content of the line for
4374 : * useful information (e.g. #EOD)
4375 : */
4376 : int
4377 46881 : mapi_split_line(MapiHdl hdl)
4378 : {
4379 46881 : int n;
4380 46881 : struct MapiResultSet *result;
4381 :
4382 46881 : result = hdl->result;
4383 46881 : assert(result != NULL);
4384 46881 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4385 46881 : n = mapi_slice_row(result, result->cache.reader);
4386 : /* no need to call mapi_store_bind since
4387 : mapi_fetch_line would have done that if needed */
4388 : }
4389 46881 : return n;
4390 : }
4391 :
4392 : int
4393 1328170 : mapi_fetch_row(MapiHdl hdl)
4394 : {
4395 1328170 : char *reply;
4396 1328170 : int n;
4397 1328170 : struct MapiResultSet *result;
4398 :
4399 1328170 : mapi_hdl_check(hdl);
4400 1349831 : do {
4401 1349831 : if ((reply = mapi_fetch_line(hdl)) == NULL)
4402 : return 0;
4403 1344433 : } while (*reply != '[' && *reply != '=');
4404 1322772 : result = hdl->result;
4405 1322772 : assert(result != NULL);
4406 1322772 : if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
4407 1322772 : n = mapi_slice_row(result, result->cache.reader);
4408 : /* no need to call mapi_store_bind since
4409 : mapi_fetch_line would have done that if needed */
4410 : }
4411 : return n;
4412 : }
4413 :
4414 : /*
4415 : * All rows can be cached first as well.
4416 : */
4417 : int64_t
4418 1 : mapi_fetch_all_rows(MapiHdl hdl)
4419 : {
4420 1 : Mapi mid;
4421 1 : struct MapiResultSet *result;
4422 :
4423 1 : mapi_hdl_check(hdl);
4424 :
4425 1 : mid = hdl->mid;
4426 3 : for (;;) {
4427 4 : if ((result = hdl->result) != NULL &&
4428 2 : msettings_lang_is_sql(mid->settings) &&
4429 2 : mid->active == NULL &&
4430 1 : result->row_count > 0 &&
4431 1 : result->cache.first + result->cache.tuplecount < result->row_count) {
4432 0 : mid->active = hdl;
4433 0 : hdl->active = result;
4434 0 : mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
4435 : result->tableid, result->cache.first + result->cache.tuplecount);
4436 0 : int e;
4437 0 : if ((e = mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
4438 0 : result->tableid, result->cache.first + result->cache.tuplecount)) < 0 ||
4439 0 : (e = mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) < 0)
4440 0 : check_stream(mid, mid->to, e, "sending export command", 0);
4441 : }
4442 2 : if (mid->active)
4443 1 : read_into_cache(mid->active, 0);
4444 : else
4445 : break;
4446 : }
4447 1 : return result ? result->cache.tuplecount : 0;
4448 : }
4449 :
4450 : char *
4451 5306892 : mapi_fetch_field(MapiHdl hdl, int fnr)
4452 : {
4453 5306892 : int cr;
4454 5306892 : struct MapiResultSet *result;
4455 :
4456 5306892 : mapi_hdl_check0(hdl);
4457 :
4458 5306892 : if ((result = hdl->result) == NULL ||
4459 5306892 : (cr = result->cache.reader) < 0 ||
4460 5306892 : (result->cache.line[cr].rows[0] != '[' &&
4461 : result->cache.line[cr].rows[0] != '=')) {
4462 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4463 0 : return 0;
4464 : }
4465 5306892 : assert(result->cache.line != NULL);
4466 5306892 : if (fnr >= 0) {
4467 : /* slice if needed */
4468 5306892 : if (result->cache.line[cr].fldcnt == 0)
4469 0 : mapi_slice_row(result, cr);
4470 5306892 : if (fnr < result->cache.line[cr].fldcnt)
4471 5306891 : return result->cache.line[cr].anchors[fnr];
4472 : }
4473 1 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4474 1 : return 0;
4475 : }
4476 :
4477 : size_t
4478 1338453 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
4479 : {
4480 1338453 : int cr;
4481 1338453 : struct MapiResultSet *result;
4482 :
4483 1338453 : mapi_hdl_check0(hdl);
4484 :
4485 1338453 : if ((result = hdl->result) == NULL ||
4486 1338453 : (cr = result->cache.reader) < 0 ||
4487 1338453 : (result->cache.line[cr].rows[0] != '[' &&
4488 : result->cache.line[cr].rows[0] != '=')) {
4489 0 : mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
4490 0 : return 0;
4491 : }
4492 1338453 : assert(result->cache.line != NULL);
4493 1338453 : if (fnr >= 0) {
4494 : /* slice if needed */
4495 1338453 : if (result->cache.line[cr].fldcnt == 0)
4496 0 : mapi_slice_row(result, cr);
4497 1338453 : if (fnr < result->cache.line[cr].fldcnt)
4498 1338453 : return result->cache.line[cr].lens[fnr];
4499 : }
4500 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4501 0 : return 0;
4502 : }
4503 :
4504 : int
4505 2429 : mapi_get_field_count(MapiHdl hdl)
4506 : {
4507 2429 : mapi_hdl_check(hdl);
4508 2429 : if (hdl->result && hdl->result->fieldcnt == 0) {
4509 : /* no rows have been sliced yet, and there was no
4510 : header, so try to figure out how many columns there
4511 : are for ourselves */
4512 : int i;
4513 :
4514 2063 : for (i = 0; i < hdl->result->cache.writer; i++)
4515 0 : if (hdl->result->cache.line[i].rows[0] == '[' ||
4516 : hdl->result->cache.line[i].rows[0] == '=')
4517 0 : mapi_slice_row(hdl->result, i);
4518 : }
4519 2429 : return hdl->result ? hdl->result->fieldcnt : 0;
4520 : }
4521 :
4522 : int64_t
4523 482 : mapi_get_row_count(MapiHdl hdl)
4524 : {
4525 482 : mapi_hdl_check(hdl);
4526 482 : return hdl->result ? hdl->result->row_count : 0;
4527 : }
4528 :
4529 : int64_t
4530 0 : mapi_get_last_id(MapiHdl hdl)
4531 : {
4532 0 : mapi_hdl_check(hdl);
4533 0 : return hdl->result ? hdl->result->last_id : -1;
4534 : }
4535 :
4536 : char *
4537 1066 : mapi_get_name(MapiHdl hdl, int fnr)
4538 : {
4539 1066 : struct MapiResultSet *result;
4540 :
4541 1066 : mapi_hdl_check0(hdl);
4542 1066 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4543 1066 : return result->fields[fnr].columnname;
4544 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4545 0 : return 0;
4546 : }
4547 :
4548 : char *
4549 5269743 : mapi_get_type(MapiHdl hdl, int fnr)
4550 : {
4551 5269743 : struct MapiResultSet *result;
4552 :
4553 5269743 : mapi_hdl_check0(hdl);
4554 5269743 : if ((result = hdl->result) != 0 &&
4555 5269743 : fnr >= 0 && fnr < result->fieldcnt) {
4556 5269743 : if (result->fields[fnr].columntype == NULL)
4557 : return "unknown";
4558 5269743 : return result->fields[fnr].columntype;
4559 : }
4560 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4561 0 : return 0;
4562 : }
4563 :
4564 : char *
4565 925 : mapi_get_table(MapiHdl hdl, int fnr)
4566 : {
4567 925 : struct MapiResultSet *result;
4568 :
4569 925 : mapi_hdl_check0(hdl);
4570 925 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4571 925 : return result->fields[fnr].tablename;
4572 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4573 0 : return 0;
4574 : }
4575 :
4576 : int
4577 965 : mapi_get_len(MapiHdl hdl, int fnr)
4578 : {
4579 965 : struct MapiResultSet *result;
4580 :
4581 965 : mapi_hdl_check0(hdl);
4582 965 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4583 965 : return result->fields[fnr].columnlength;
4584 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4585 0 : return 0;
4586 : }
4587 :
4588 : int
4589 928 : mapi_get_digits(MapiHdl hdl, int fnr)
4590 : {
4591 928 : struct MapiResultSet *result;
4592 :
4593 928 : mapi_hdl_check0(hdl);
4594 928 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4595 928 : return result->fields[fnr].digits;
4596 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4597 0 : return 0;
4598 : }
4599 :
4600 : int
4601 4 : mapi_get_scale(MapiHdl hdl, int fnr)
4602 : {
4603 4 : struct MapiResultSet *result;
4604 :
4605 4 : mapi_hdl_check0(hdl);
4606 4 : if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
4607 4 : return result->fields[fnr].scale;
4608 0 : mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
4609 0 : return 0;
4610 : }
4611 :
4612 : char *
4613 1056 : mapi_get_query(MapiHdl hdl)
4614 : {
4615 1056 : mapi_hdl_check0(hdl);
4616 1056 : if (hdl->query != NULL) {
4617 1056 : return strdup(hdl->query);
4618 : } else {
4619 : return NULL;
4620 : }
4621 : }
4622 :
4623 :
4624 : int
4625 13160 : mapi_get_querytype(MapiHdl hdl)
4626 : {
4627 13160 : struct MapiResultSet *result;
4628 :
4629 13160 : mapi_hdl_check0(hdl);
4630 13160 : if ((result = hdl->result) != 0)
4631 11826 : return result->querytype;
4632 1334 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4633 1334 : return 0; /* Q_PARSE! */
4634 : }
4635 :
4636 : int
4637 123 : mapi_get_tableid(MapiHdl hdl)
4638 : {
4639 123 : struct MapiResultSet *result;
4640 :
4641 123 : mapi_hdl_check0(hdl);
4642 123 : if ((result = hdl->result) != 0)
4643 123 : return result->tableid;
4644 0 : mapi_setError(hdl->mid, "No query result", __func__, MERROR);
4645 0 : return 0;
4646 : }
4647 :
4648 : int64_t
4649 2987 : mapi_rows_affected(MapiHdl hdl)
4650 : {
4651 2987 : struct MapiResultSet *result;
4652 :
4653 2987 : mapi_hdl_check(hdl);
4654 2987 : if ((result = hdl->result) == NULL)
4655 : return 0;
4656 2987 : return result->row_count;
4657 : }
4658 :
4659 : int64_t
4660 5537 : mapi_get_querytime(MapiHdl hdl)
4661 : {
4662 5537 : struct MapiResultSet *result;
4663 :
4664 5537 : mapi_hdl_check(hdl);
4665 5537 : if ((result = hdl->result) == NULL)
4666 : return 0;
4667 4870 : return result->querytime;
4668 : }
4669 :
4670 : int64_t
4671 5537 : mapi_get_maloptimizertime(MapiHdl hdl)
4672 : {
4673 5537 : struct MapiResultSet *result;
4674 :
4675 5537 : mapi_hdl_check(hdl);
4676 5537 : if ((result = hdl->result) == NULL)
4677 : return 0;
4678 4870 : return result->maloptimizertime;
4679 : }
4680 :
4681 : int64_t
4682 5537 : mapi_get_sqloptimizertime(MapiHdl hdl)
4683 : {
4684 5537 : struct MapiResultSet *result;
4685 :
4686 5537 : mapi_hdl_check(hdl);
4687 5537 : if ((result = hdl->result) == NULL)
4688 : return 0;
4689 4870 : return result->sqloptimizertime;
4690 : }
4691 :
4692 : const char *
4693 197 : mapi_get_dbname(Mapi mid)
4694 : {
4695 197 : return msetting_string(mid->settings, MP_DATABASE);
4696 : }
4697 :
4698 : const char *
4699 6 : mapi_get_host(Mapi mid)
4700 : {
4701 6 : return msetting_string(mid->settings, MP_HOST);
4702 : }
4703 :
4704 : const char *
4705 6 : mapi_get_user(Mapi mid)
4706 : {
4707 6 : return msetting_string(mid->settings, MP_USER);;
4708 : }
4709 :
4710 : const char *
4711 0 : mapi_get_lang(Mapi mid)
4712 : {
4713 0 : return msetting_string(mid->settings, MP_LANGUAGE);;
4714 : }
4715 :
4716 : const char *
4717 0 : mapi_get_uri(Mapi mid)
4718 : {
4719 0 : return mid->uri;
4720 : }
4721 :
4722 : const char *
4723 1 : mapi_get_mapi_version(void)
4724 : {
4725 1 : return MAPI_VERSION;
4726 : }
4727 :
4728 : const char *
4729 0 : mapi_get_monet_version(Mapi mid)
4730 : {
4731 0 : mapi_check0(mid);
4732 0 : return mid->server ? mid->server : "";
4733 : }
4734 :
4735 : const char *
4736 0 : mapi_get_motd(Mapi mid)
4737 : {
4738 0 : mapi_check0(mid);
4739 0 : return mid->motd;
4740 : }
4741 :
4742 : bool
4743 0 : mapi_is_connected(Mapi mid)
4744 : {
4745 0 : return mid->connected;
4746 : }
4747 :
4748 : MapiHdl
4749 169 : mapi_get_active(Mapi mid)
4750 : {
4751 169 : return mid->active;
4752 : }
4753 :
4754 : msettings*
4755 0 : mapi_get_settings(Mapi mid)
4756 : {
4757 0 : return mid->settings;
4758 : }
4759 :
4760 :
4761 : MapiMsg
4762 1425 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
4763 : {
4764 : // do not use check_stream here yet because the socket is not yet in 'mid'
4765 1425 : const char *error_message;
4766 1425 : stream *error_stream;
4767 :
4768 1425 : assert(!isa_block_stream(rstream));
4769 1425 : assert(!isa_block_stream(wstream));
4770 :
4771 : // First send some NUL bytes. If we're accidentally connecting to the wrong
4772 : // port or protocol this may cause the remote server to close the
4773 : // connection. If we don't do this, the connection will often hang
4774 : // because the server expects us to speak first and we expect the server
4775 : // to speak first.
4776 : //
4777 : // Note that a pair of NUL bytes is a no-op message in MAPI.
4778 : //
4779 : // Surprisingly, it seems sending these NUL bytes makes non-TLS
4780 : // connection setup a little faster rather than slower!
4781 : static const char zeroes[8] = { 0 };
4782 : ssize_t to_write = sizeof(zeroes);
4783 2850 : while (to_write > 0) {
4784 1425 : ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
4785 1425 : if (n < 0) {
4786 0 : close_connection(mid);
4787 0 : return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
4788 : }
4789 1425 : to_write -= (size_t)n;
4790 : }
4791 1425 : if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
4792 0 : close_connection(mid);
4793 0 : return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
4794 : }
4795 :
4796 :
4797 1425 : stream *brstream = NULL;
4798 1425 : stream *bwstream = NULL;
4799 :
4800 1425 : bwstream = block_stream(wstream);
4801 1425 : if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
4802 0 : error_stream = bwstream;
4803 0 : error_message = "block_stream wstream";
4804 0 : goto bailout;
4805 : }
4806 1425 : brstream = block_stream(rstream);
4807 1425 : if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
4808 0 : error_stream = brstream;
4809 0 : error_message = "block_stream rstream";
4810 0 : goto bailout;
4811 : }
4812 :
4813 1425 : mid->to = bwstream;
4814 1425 : mid->from = brstream;
4815 1425 : return MOK;
4816 0 : bailout:
4817 : // adapted from the check_stream macro
4818 0 : if (brstream)
4819 0 : mnstr_destroy(brstream);
4820 0 : if (bwstream)
4821 0 : mnstr_destroy(bwstream);
4822 : // malloc failure is the only way these calls could have failed
4823 0 : return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
4824 : }
|