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