LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1336 2210 60.5 %
Date: 2024-12-19 23:10:26 Functions: 94 139 67.6 %

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

Generated by: LCOV version 1.14