LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1259 2170 58.0 %
Date: 2024-04-25 20:03:45 Functions: 89 136 65.4 %

          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 unsecure 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_fiels_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             :  * Blocking
     798             :  * --------
     799             :  *
     800             :  * The server side code works with a common/stream package, a fast
     801             :  * buffered IO scheme.  Nowadays this should be the only protocol used,
     802             :  * while historical uses were line-based instead.
     803             :  *
     804             :  *
     805             :  * Error Handling
     806             :  * --------------
     807             :  *
     808             :  * All externally visible functions should first call mapi_clrError (usually
     809             :  * though a call to one of the check macros above) to clear the error flag.
     810             :  * When an error is detected, the library calls mapi_setError to set the error
     811             :  * flag.  The application can call mapi_error or mapi_error_str to check for
     812             :  * errors, and mapi_explain or mapi_explain_query to print a formatted error
     813             :  * report.
     814             :  */
     815             : char mapi_nomem[] = "Memory allocation failed";
     816             : 
     817             : void
     818    11851436 : mapi_clrError(Mapi mid)
     819             : {
     820    11851436 :         assert(mid);
     821    11851436 :         if (mid->errorstr && mid->errorstr != mapi_nomem)
     822        1067 :                 free(mid->errorstr);
     823    11851436 :         mid->action = 0;     /* contains references to constants */
     824    11851436 :         mid->error = 0;
     825    11851436 :         mid->errorstr = 0;
     826    11851436 : }
     827             : 
     828             : MapiMsg
     829        1071 : mapi_setError(Mapi mid, const char *msg, const char *action, MapiMsg error)
     830             : {
     831        1071 :         assert(msg);
     832        1071 :         REALLOC(mid->errorstr, strlen(msg) + 1);
     833        1071 :         if (mid->errorstr == NULL)
     834           0 :                 mid->errorstr = mapi_nomem;
     835             :         else
     836        1071 :                 strcpy(mid->errorstr, msg);
     837        1071 :         mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
     838        1071 :         mid->error = error;
     839        1071 :         mid->action = action;
     840        1071 :         return mid->error;
     841             : }
     842             : 
     843          13 : MapiMsg mapi_printError(Mapi mid, const char *action, MapiMsg error, const char *fmt, ...)
     844             : {
     845          13 :         size_t size = 81; // just a guess
     846             : 
     847          19 :         while (1) {
     848          16 :                 REALLOC(mid->errorstr, size);
     849          16 :                 if (mid->errorstr == NULL) {
     850           0 :                         mid->errorstr = mapi_nomem;
     851           0 :                         break;
     852             :                 }
     853          16 :                 va_list ap;
     854          16 :                 va_start(ap, fmt);
     855          16 :                 int n = vsnprintf(mid->errorstr, size, fmt, ap);
     856          16 :                 va_end(ap);
     857          16 :                 if (n < 0) {
     858             :                         // is it even possible for vsnprintf to fail?
     859             :                         break;
     860             :                 }
     861          16 :                 if ((size_t)n < size) {
     862             :                         // it fit
     863             :                         break;
     864             :                 } else {
     865             :                         // try again larger
     866           3 :                         size = (size_t)n + 1;
     867             :                 }
     868             :         }
     869          13 :         mapi_log_record(mid, "ERROR", "%s: %s", action, mid->errorstr);
     870          13 :         mid->error = error;
     871          13 :         mid->action = action;
     872          13 :         return mid->error;
     873             : }
     874             : 
     875             : MapiMsg
     876      188124 : mapi_error(Mapi mid)
     877             : {
     878      188124 :         assert(mid);
     879      188124 :         return mid->error;
     880             : }
     881             : 
     882             : const char *
     883          19 : mapi_error_str(Mapi mid)
     884             : {
     885          19 :         assert(mid);
     886          19 :         return mid->errorstr;
     887             : }
     888             : 
     889             : #ifdef _MSC_VER
     890             : static const struct {
     891             :         int e;
     892             :         const char *m;
     893             : } wsaerrlist[] = {
     894             :         { WSA_INVALID_HANDLE, "Specified event object handle is invalid" },
     895             :         { WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available" },
     896             :         { WSA_INVALID_PARAMETER, "One or more parameters are invalid" },
     897             :         { WSA_OPERATION_ABORTED, "Overlapped operation aborted" },
     898             :         { WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state" },
     899             :         { WSA_IO_PENDING, "Overlapped operations will complete later" },
     900             :         { WSAEINTR, "Interrupted function call" },
     901             :         { WSAEBADF, "File handle is not valid" },
     902             :         { WSAEACCES, "Permission denied" },
     903             :         { WSAEFAULT, "Bad address" },
     904             :         { WSAEINVAL, "Invalid argument" },
     905             :         { WSAEMFILE, "Too many open files" },
     906             :         { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
     907             :         { WSAEINPROGRESS, "Operation now in progress" },
     908             :         { WSAEALREADY, "Operation already in progress" },
     909             :         { WSAENOTSOCK, "Socket operation on nonsocket" },
     910             :         { WSAEDESTADDRREQ, "Destination address required" },
     911             :         { WSAEMSGSIZE, "Message too long" },
     912             :         { WSAEPROTOTYPE, "Protocol wrong type for socket" },
     913             :         { WSAENOPROTOOPT, "Bad protocol option" },
     914             :         { WSAEPROTONOSUPPORT, "Protocol not supported" },
     915             :         { WSAESOCKTNOSUPPORT, "Socket type not supported" },
     916             :         { WSAEOPNOTSUPP, "Operation not supported" },
     917             :         { WSAEPFNOSUPPORT, "Protocol family not supported" },
     918             :         { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
     919             :         { WSAEADDRINUSE, "Address already in use" },
     920             :         { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
     921             :         { WSAENETDOWN, "Network is down" },
     922             :         { WSAENETUNREACH, "Network is unreachable" },
     923             :         { WSAENETRESET, "Network dropped connection on reset" },
     924             :         { WSAECONNABORTED, "Software caused connection abort" },
     925             :         { WSAECONNRESET, "Connection reset by peer" },
     926             :         { WSAENOBUFS, "No buffer space available" },
     927             :         { WSAEISCONN, "Socket is already connected" },
     928             :         { WSAENOTCONN, "Socket is not connected" },
     929             :         { WSAESHUTDOWN, "Cannot send after socket shutdown" },
     930             :         { WSAETOOMANYREFS, "Too many references" },
     931             :         { WSAETIMEDOUT, "Connection timed out" },
     932             :         { WSAECONNREFUSED, "Connection refused" },
     933             :         { WSAELOOP, "Cannot translate name" },
     934             :         { WSAENAMETOOLONG, "Name too long" },
     935             :         { WSAEHOSTDOWN, "Host is down" },
     936             :         { WSAEHOSTUNREACH, "No route to host" },
     937             :         { WSAENOTEMPTY, "Directory not empty" },
     938             :         { WSAEPROCLIM, "Too many processes" },
     939             :         { WSAEUSERS, "User quota exceeded" },
     940             :         { WSAEDQUOT, "Disk quota exceeded" },
     941             :         { WSAESTALE, "Stale file handle reference" },
     942             :         { WSAEREMOTE, "Item is remote" },
     943             :         { WSASYSNOTREADY, "Network subsystem is unavailable" },
     944             :         { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" },
     945             :         { WSANOTINITIALISED, "Successful WSAStartup not yet performed" },
     946             :         { WSAEDISCON, "Graceful shutdown in progress" },
     947             :         { WSAENOMORE, "No more results" },
     948             :         { WSAECANCELLED, "Call has been canceled" },
     949             :         { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" },
     950             :         { WSAEINVALIDPROVIDER, "Service provider is invalid" },
     951             :         { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" },
     952             :         { WSASYSCALLFAILURE, "System call failure" },
     953             :         { WSASERVICE_NOT_FOUND, "Service not found" },
     954             :         { WSATYPE_NOT_FOUND, "Class type not found" },
     955             :         { WSA_E_NO_MORE, "No more results" },
     956             :         { WSA_E_CANCELLED, "Call was canceled" },
     957             :         { WSAEREFUSED, "Database query was refused" },
     958             :         { WSAHOST_NOT_FOUND, "Host not found" },
     959             :         { WSATRY_AGAIN, "Nonauthoritative host not found" },
     960             :         { WSANO_RECOVERY, "This is a nonrecoverable error" },
     961             :         { WSANO_DATA, "Valid name, no data record of requested type" },
     962             :         { WSA_QOS_RECEIVERS, "QOS receivers" },
     963             :         { WSA_QOS_SENDERS, "QOS senders" },
     964             :         { WSA_QOS_NO_SENDERS, "No QOS senders" },
     965             :         { WSA_QOS_NO_RECEIVERS, "QOS no receivers" },
     966             :         { WSA_QOS_REQUEST_CONFIRMED, "QOS request confirmed" },
     967             :         { WSA_QOS_ADMISSION_FAILURE, "QOS admission error" },
     968             :         { WSA_QOS_POLICY_FAILURE, "QOS policy failure" },
     969             :         { WSA_QOS_BAD_STYLE, "QOS bad style" },
     970             :         { WSA_QOS_BAD_OBJECT, "QOS bad object" },
     971             :         { WSA_QOS_TRAFFIC_CTRL_ERROR, "QOS traffic control error" },
     972             :         { WSA_QOS_GENERIC_ERROR, "QOS generic error" },
     973             :         { WSA_QOS_ESERVICETYPE, "QOS service type error" },
     974             :         { WSA_QOS_EFLOWSPEC, "QOS flowspec error" },
     975             :         { WSA_QOS_EPROVSPECBUF, "Invalid QOS provider buffer" },
     976             :         { WSA_QOS_EFILTERSTYLE, "Invalid QOS filter style" },
     977             :         { WSA_QOS_EFILTERTYPE, "Invalid QOS filter type" },
     978             :         { WSA_QOS_EFILTERCOUNT, "Incorrect QOS filter count" },
     979             :         { WSA_QOS_EOBJLENGTH, "Invalid QOS object length" },
     980             :         { WSA_QOS_EFLOWCOUNT, "Incorrect QOS flow count" },
     981             :         { WSA_QOS_EUNKOWNPSOBJ, "Unrecognized QOS object" },
     982             :         { WSA_QOS_EPOLICYOBJ, "Invalid QOS policy object" },
     983             :         { WSA_QOS_EFLOWDESC, "Invalid QOS flow descriptor" },
     984             :         { WSA_QOS_EPSFLOWSPEC, "Invalid QOS provider-specific flowspec" },
     985             :         { WSA_QOS_EPSFILTERSPEC, "Invalid QOS provider-specific filterspec" },
     986             :         { WSA_QOS_ESDMODEOBJ, "Invalid QOS shape discard mode object" },
     987             :         { WSA_QOS_ESHAPERATEOBJ, "Invalid QOS shaping rate object" },
     988             :         { WSA_QOS_RESERVED_PETYPE, "Reserved policy QOS element type" },
     989             : };
     990             : const char *
     991             : wsaerror(int err)
     992             : {
     993             :         int i;
     994             : 
     995             :         for (i = 0; i < NELEM(wsaerrlist); i++)
     996             :                 if (wsaerrlist[i].e == err)
     997             :                         return wsaerrlist[i].m;
     998             :         return "Unknown error";
     999             : }
    1000             : #endif
    1001             : 
    1002             : static void
    1003           0 : clean_print(char *msg, const char *prefix, FILE *fd)
    1004             : {
    1005           0 :         size_t len = strlen(prefix);
    1006             : 
    1007           0 :         while (msg && *msg) {
    1008             :                 /* cut by line */
    1009           0 :                 char *p = strchr(msg, '\n');
    1010             : 
    1011           0 :                 if (p)
    1012           0 :                         *p++ = 0;
    1013             : 
    1014             :                 /* skip over prefix */
    1015           0 :                 if (strncmp(msg, prefix, len) == 0)
    1016           0 :                         msg += len;
    1017             : 
    1018             :                 /* output line */
    1019           0 :                 fputs(msg, fd);
    1020           0 :                 fputc('\n', fd);
    1021           0 :                 msg = p;
    1022             :         }
    1023           0 : }
    1024             : 
    1025             : static void
    1026         182 : indented_print(const char *msg, const char *prefix, FILE *fd)
    1027             : {
    1028             :         /* for multiline error messages, indent all subsequent
    1029             :            lines with the space it takes to print "ERROR = " */
    1030         182 :         const char *s = prefix, *p = msg, *q;
    1031         182 :         const int len = (int) strlen(s);
    1032         182 :         const char t = s[len - 1];
    1033             : 
    1034         308 :         while (p && *p) {
    1035         193 :                 fprintf(fd, "%*.*s%c", len - 1, len - 1, s, t);
    1036         193 :                 s = "";
    1037             : 
    1038         193 :                 q = strchr(p, '\n');
    1039         193 :                 if (q) {
    1040         126 :                         q++;    /* also print the newline */
    1041         126 :                         fprintf(fd, "%.*s", (int) (q - p), p);
    1042             :                 } else {
    1043             :                         /* print bit after last newline,
    1044             :                            adding one ourselves */
    1045          67 :                         fprintf(fd, "%s\n", p);
    1046          67 :                         break;  /* nothing more to do */
    1047             :                 }
    1048         126 :                 p = q;
    1049             :         }
    1050         182 : }
    1051             : 
    1052             : void
    1053          61 : mapi_noexplain(Mapi mid, const char *errorprefix)
    1054             : {
    1055          61 :         assert(mid);
    1056          61 :         if (mid->noexplain)
    1057           0 :                 free(mid->noexplain);
    1058          61 :         mid->noexplain = errorprefix ? strdup(errorprefix) : NULL;
    1059          61 : }
    1060             : 
    1061             : void
    1062           0 : mapi_explain(Mapi mid, FILE *fd)
    1063             : {
    1064           0 :         assert(mid);
    1065           0 :         if (mid->noexplain == NULL) {
    1066           0 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1067           0 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1068           0 :                 int port = msetting_long(mid->settings, MP_PORT);
    1069           0 :                 if (host[0] == '/')
    1070           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1071             :                 else
    1072           0 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1073             :                                 user, host, port);
    1074           0 :                 if (mid->action)
    1075           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1076           0 :                 if (mid->errorstr)
    1077           0 :                         indented_print(mid->errorstr, "ERROR = !", fd);
    1078           0 :         } else if (mid->errorstr) {
    1079           0 :                 clean_print(mid->errorstr, mid->noexplain, fd);
    1080             :         }
    1081           0 :         fflush(fd);
    1082           0 :         mapi_clrError(mid);
    1083           0 : }
    1084             : 
    1085             : void
    1086           0 : mapi_explain_query(MapiHdl hdl, FILE *fd)
    1087             : {
    1088           0 :         Mapi mid;
    1089             : 
    1090           0 :         assert(hdl);
    1091           0 :         mid = hdl->mid;
    1092           0 :         assert(mid);
    1093           0 :         if (mid->noexplain == NULL) {
    1094           0 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1095           0 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1096           0 :                 int port = msetting_long(mid->settings, MP_PORT);
    1097           0 :                 if (host[0] == '/')
    1098           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1099             :                 else
    1100           0 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1101             :                                 user, host, port);
    1102           0 :                 if (mid->action)
    1103           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1104           0 :                 if (hdl->query)
    1105           0 :                         indented_print(hdl->query, "QUERY = ", fd);
    1106           0 :                 if (mid->errorstr)
    1107           0 :                         indented_print(mid->errorstr, "ERROR = !", fd);
    1108           0 :         } else if (mid->errorstr) {
    1109           0 :                 clean_print(mid->errorstr, mid->noexplain, fd);
    1110             :         }
    1111           0 :         fflush(fd);
    1112           0 :         mapi_clrError(mid);
    1113           0 : }
    1114             : 
    1115             : void
    1116          61 : mapi_explain_result(MapiHdl hdl, FILE *fd)
    1117             : {
    1118          61 :         Mapi mid;
    1119             : 
    1120          61 :         if (hdl == NULL ||
    1121          61 :             hdl->result == NULL ||
    1122          61 :             hdl->result->errorstr == NULL)
    1123             :                 return;
    1124          61 :         assert(hdl);
    1125          61 :         assert(hdl->result);
    1126          61 :         assert(hdl->result->errorstr);
    1127          61 :         mid = hdl->mid;
    1128          61 :         assert(mid);
    1129          61 :         if (mid->noexplain == NULL) {
    1130          61 :                 const char *host = msetting_string(mid->settings, MP_HOST);
    1131          61 :                 const char *user = msetting_string(mid->settings, MP_USER);
    1132          61 :                 int port = msetting_long(mid->settings, MP_PORT);
    1133          61 :                 if (host[0] == '/')
    1134           0 :                         fprintf(fd, "MAPI  = (%s) %s\n", user, host);
    1135             :                 else
    1136          61 :                         fprintf(fd, "MAPI  = %s@%s:%d\n",
    1137             :                                 user, host, port);
    1138          61 :                 if (mid->action)
    1139           0 :                         fprintf(fd, "ACTION= %s\n", mid->action);
    1140          61 :                 if (hdl->query)
    1141          61 :                         indented_print(hdl->query, "QUERY = ", fd);
    1142          61 :                 indented_print(hdl->result->errorstr, "ERROR = !", fd);
    1143          61 :                 if (msettings_lang_is_sql(mid->settings) && hdl->result->sqlstate[0])
    1144          60 :                         indented_print(hdl->result->sqlstate, "CODE  = ", fd);
    1145             :         } else {
    1146           0 :                 clean_print(hdl->result->errorstr, mid->noexplain, fd);
    1147             :         }
    1148          61 :         fflush(fd);
    1149             : }
    1150             : 
    1151             : stream *
    1152        2792 : mapi_get_to(Mapi mid)
    1153             : {
    1154        2792 :         mapi_check0(mid);
    1155        2792 :         return mid->to;
    1156             : }
    1157             : 
    1158             : stream *
    1159        1396 : mapi_get_from(Mapi mid)
    1160             : {
    1161        1396 :         mapi_check0(mid);
    1162        1396 :         return mid->from;
    1163             : }
    1164             : 
    1165             : bool
    1166           0 : mapi_get_trace(Mapi mid)
    1167             : {
    1168           0 :         mapi_check0(mid);
    1169           0 :         return mid->trace;
    1170             : }
    1171             : 
    1172             : bool
    1173           0 : mapi_get_autocommit(Mapi mid)
    1174             : {
    1175           0 :         mapi_check0(mid);
    1176           0 :         return msetting_bool(mid->settings, MP_AUTOCOMMIT);
    1177             : }
    1178             : 
    1179             : bool
    1180           0 : mapi_get_columnar_protocol(Mapi mid)
    1181             : {
    1182           0 :         mapi_check0(mid);
    1183           0 :         return mid->columnar_protocol;
    1184             : }
    1185             : 
    1186             : int
    1187           0 : mapi_get_time_zone(Mapi mid)
    1188             : {
    1189           0 :         mapi_check0(mid);
    1190           0 :         return msetting_long(mid->settings, MP_TIMEZONE);
    1191             : }
    1192             : 
    1193             : static int64_t
    1194           0 : usec(void)
    1195             : {
    1196             : #ifdef HAVE_GETTIMEOFDAY
    1197           0 :         struct timeval tp;
    1198             : 
    1199           0 :         gettimeofday(&tp, NULL);
    1200           0 :         return ((int64_t) tp.tv_sec) * 1000000 + (int64_t) tp.tv_usec;
    1201             : #else
    1202             : #ifdef HAVE_FTIME
    1203             :         struct timeb tb;
    1204             : 
    1205             :         ftime(&tb);
    1206             :         return ((int64_t) tb.time) * 1000000 + ((int64_t) tb.millitm) * 1000;
    1207             : #endif
    1208             : #endif
    1209             : }
    1210             : 
    1211             : static void
    1212           0 : mapi_log_header(Mapi mid, const char *funcname, long line, const char *mark1, const char *mark2)
    1213             : {
    1214           0 :         int64_t now = usec();
    1215           0 :         static int64_t firstcall = 0;
    1216           0 :         if (firstcall == 0)
    1217           0 :                 firstcall = now;
    1218           0 :         double seconds = (double)(now - firstcall) / 1e6;
    1219           0 :         mnstr_printf(mid->tracelog, "\342\226\266 [%u] t=%.3fs %s%s %s(), line %ld\n", mid->index, seconds, mark1, mark2, funcname, line); /* U+25B6: right-pointing triangle */
    1220           0 : }
    1221             : 
    1222             : void
    1223           0 : mapi_impl_log_record(Mapi mid, const char *funcname, long line, const char *mark, const char *fmt, ...)
    1224             : {
    1225           0 :         va_list ap;
    1226             : 
    1227           0 :         if (mid->tracelog == NULL)
    1228           0 :                 return;
    1229             : 
    1230             :         size_t needed = 128;
    1231           0 :         size_t to_print;
    1232           0 :         while (1) {
    1233           0 :                 if (mid->tracebuffersize < needed) {
    1234           0 :                         free(mid->tracebuffer);
    1235           0 :                         mid->tracebuffer = malloc(needed);
    1236           0 :                         if (mid->tracebuffer) {
    1237           0 :                                 mid->tracebuffersize = needed;
    1238             :                         } else {
    1239           0 :                                 mid->tracebuffersize = 0;
    1240           0 :                                 to_print = 0;
    1241           0 :                                 break;
    1242             :                         }
    1243             :                 }
    1244           0 :                 va_start(ap, fmt);
    1245           0 :                 int n = vsnprintf(mid->tracebuffer, mid->tracebuffersize, fmt, ap);
    1246           0 :                 va_end(ap);
    1247           0 :                 if (n < 0) {
    1248             :                         to_print = 0;
    1249             :                         break;
    1250             :                 }
    1251           0 :                 if ((size_t)n < mid->tracebuffersize) {
    1252             :                         to_print = n;
    1253             :                         break;
    1254             :                 }
    1255             :                 // need to reallocate
    1256           0 :                 needed = n + 1;
    1257             :         }
    1258             : 
    1259           0 :         mapi_log_header(mid, funcname, line, mark, "");
    1260           0 :         if (to_print > 0) {
    1261           0 :                 mnstr_write(mid->tracelog, mid->tracebuffer, to_print, 1);
    1262           0 :                 mnstr_writeChr(mid->tracelog, '\n');
    1263             :         }
    1264           0 :         mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    1265             : }
    1266             : 
    1267             : #define MAPILOG_OPEN "\tb'"
    1268             : #define MAPILOG_CLOSE "'"
    1269             : 
    1270             : void
    1271           0 : mapi_impl_log_data(Mapi mid, const char *filename, long line, const char *mark, const char *start, size_t len)
    1272             : {
    1273           0 :         const char hexdigits[] = "0123456789abcdef";
    1274           0 :         if (mid->tracelog == NULL)
    1275           0 :                 return;
    1276             : 
    1277           0 :         mapi_log_header(mid, filename, line, mark, " (DATA)");
    1278             : 
    1279           0 :         const size_t margin = strlen("\\xNN" MAPILOG_CLOSE "\n," MAPILOG_OPEN);
    1280           0 :         char buffer[128] = { 0 };
    1281           0 :         char *pos = buffer;
    1282           0 :         mnstr_writeStr(mid->tracelog, MAPILOG_OPEN);
    1283           0 :         bool inside = true;
    1284           0 :         for (unsigned char *p = (unsigned char*)start; (char*)p < start + len; p++) {
    1285           0 :                 unsigned char c = *p;
    1286           0 :                 if (!inside) {
    1287           0 :                         for (char *text = "\n" MAPILOG_OPEN; *text; text++)
    1288           0 :                                 *pos++ = *text;
    1289             :                         inside = true;
    1290             :                 }
    1291           0 :                 if (pos >= buffer + sizeof(buffer) - margin) {
    1292           0 :                         mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
    1293           0 :                         pos = buffer;
    1294             :                 }
    1295           0 :                 switch (c) {
    1296           0 :                         case '\\':
    1297             :                         case '\'':
    1298           0 :                                 *pos++ = '\\';
    1299           0 :                                 *pos++ = c;
    1300           0 :                                 break;
    1301           0 :                         case '\t':
    1302           0 :                                 *pos++ = '\\';
    1303           0 :                                 *pos++ = 't';
    1304           0 :                                 break;
    1305             :                         case '\n':
    1306           0 :                                 for (char *text = "\\n" MAPILOG_CLOSE; *text; text++)
    1307           0 :                                         *pos++ = *text;
    1308             :                                 inside = false;
    1309             :                                 break;
    1310           0 :                         default:
    1311           0 :                                 if (c >= 32 && c < 127) {
    1312           0 :                                         *pos++ = c;
    1313             :                                 } else {
    1314           0 :                                         *pos++ = '\\';
    1315           0 :                                         *pos++ = 'x';
    1316           0 :                                         *pos++ = hexdigits[c / 16];
    1317           0 :                                         *pos++ = hexdigits[c % 16];
    1318             :                                 }
    1319             :                         break;
    1320             :                 }
    1321             :         }
    1322           0 :         if (inside) {
    1323           0 :                 for (char *text = MAPILOG_CLOSE; *text; text++)
    1324           0 :                         *pos++ = *text;
    1325             :         }
    1326           0 :         *pos++ = ',';
    1327           0 :         *pos++ = '\n';
    1328           0 :         mnstr_write(mid->tracelog, buffer, 1, pos - buffer);
    1329             : 
    1330           0 :         mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    1331             : }
    1332             : 
    1333             : MapiMsg
    1334           0 : mapi_log(Mapi mid, const char *nme)
    1335             : {
    1336           0 :         mapi_clrError(mid);
    1337           0 :         if (mid->tracelog) {
    1338           0 :                 close_stream(mid->tracelog);
    1339           0 :                 mid->tracelog = NULL;
    1340             :         }
    1341           0 :         if (nme == NULL)
    1342             :                 return MOK;
    1343           0 :         if (nme[0] == '-' && nme[1] == '\0')
    1344           0 :                 mid->tracelog = stderr_wastream();
    1345             :         else
    1346           0 :                 mid->tracelog = open_wastream(nme);
    1347           0 :         if (mid->tracelog == NULL || mnstr_errnr(mid->tracelog) != MNSTR_NO__ERROR) {
    1348           0 :                 if (mid->tracelog)
    1349           0 :                         close_stream(mid->tracelog);
    1350           0 :                 mid->tracelog = NULL;
    1351           0 :                 return mapi_setError(mid, "Could not create log file", __func__, MERROR);
    1352             :         }
    1353             :         return MOK;
    1354             : }
    1355             : 
    1356             : /* send a dummy request to the server to see whether the connection is
    1357             :    still alive */
    1358             : MapiMsg
    1359           0 : mapi_ping(Mapi mid)
    1360             : {
    1361           0 :         mapi_check(mid);
    1362             : 
    1363           0 :         MapiHdl hdl;
    1364           0 :         if (msettings_lang_is_sql(mid->settings))
    1365           0 :                 hdl = mapi_query(mid, "select true;");
    1366           0 :         else if (msettings_lang_is_mal(mid->settings))
    1367           0 :                 hdl = mapi_query(mid, "io.print(1);");
    1368             :         else
    1369             :                 hdl = NULL;
    1370             : 
    1371           0 :         if (hdl)
    1372           0 :                 mapi_close_handle(hdl);
    1373           0 :         return mid->error;
    1374             : }
    1375             : 
    1376             : /* allocate a new structure to represent a result set */
    1377             : static struct MapiResultSet *
    1378       30889 : new_result(MapiHdl hdl)
    1379             : {
    1380       30889 :         struct MapiResultSet *result;
    1381             : 
    1382       30889 :         assert((hdl->lastresult == NULL && hdl->result == NULL) ||
    1383             :                (hdl->result != NULL && hdl->lastresult != NULL && hdl->lastresult->next == NULL));
    1384             : 
    1385       30889 :         if (hdl->mid->trace)
    1386           0 :                 printf("allocating new result set\n");
    1387             :         /* append a newly allocated struct to the end of the linked list */
    1388       30889 :         result = malloc(sizeof(*result));
    1389       30889 :         if (result == NULL)
    1390             :                 return NULL;
    1391       61778 :         *result = (struct MapiResultSet) {
    1392             :                 .hdl = hdl,
    1393             :                 .tableid = -1,
    1394             :                 .querytype = -1,
    1395             :                 .last_id = -1,
    1396       30889 :                 .cache.rowlimit = msetting_long(hdl->mid->settings, MP_REPLYSIZE),
    1397             :                 .cache.reader = -1,
    1398             :                 .commentonly = true,
    1399             :         };
    1400       30889 :         if (hdl->lastresult == NULL)
    1401       30834 :                 hdl->result = hdl->lastresult = result;
    1402             :         else {
    1403          55 :                 hdl->lastresult->next = result;
    1404          55 :                 hdl->lastresult = result;
    1405             :         }
    1406             : 
    1407             :         return result;
    1408             : }
    1409             : 
    1410             : /* close a result set, discarding any unread results */
    1411             : static MapiMsg
    1412       30889 : close_result(MapiHdl hdl)
    1413             : {
    1414       30889 :         struct MapiResultSet *result;
    1415       30889 :         Mapi mid;
    1416       30889 :         int i;
    1417             : 
    1418       30889 :         result = hdl->result;
    1419       30889 :         if (result == NULL)
    1420             :                 return MERROR;
    1421       30889 :         mid = hdl->mid;
    1422       30889 :         assert(mid != NULL);
    1423       30889 :         if (mid->trace)
    1424           0 :                 printf("closing result set\n");
    1425       30889 :         if (result->tableid >= 0 && result->querytype != Q_PREPARE) {
    1426       27575 :                 if (mid->active &&
    1427           2 :                     result->next == NULL &&
    1428           2 :                     !mid->active->needmore &&
    1429           1 :                     read_into_cache(mid->active, -1) != MOK)
    1430             :                         return MERROR;
    1431       27575 :                 assert(hdl->npending_close == 0 ||
    1432             :                        (hdl->npending_close > 0 && hdl->pending_close != NULL));
    1433       27575 :                 if (mid->active &&
    1434           1 :                     (mid->active->active != result ||
    1435           0 :                      result->cache.tuplecount < result->row_count)) {
    1436             :                         /* results for which we got all tuples at the initial
    1437             :                          * response, need not to be closed as the server already
    1438             :                          * did that immediately */
    1439           1 :                         if (result->row_count > result->tuple_count) {
    1440             :                                 /* can't write "X" commands now, so save for later */
    1441           0 :                                 REALLOC(hdl->pending_close, hdl->npending_close + 1);
    1442           0 :                                 hdl->pending_close[hdl->npending_close] = result->tableid;
    1443           0 :                                 hdl->npending_close++;
    1444             :                         }
    1445       27574 :                 } else if (mid->to != NULL) {
    1446             :                         /* first close saved up to-be-closed tables */
    1447       27574 :                         for (i = 0; i < hdl->npending_close; i++) {
    1448           0 :                                 char msg[256];
    1449             : 
    1450           0 :                                 snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
    1451           0 :                                 mapi_log_record(mid, "CMD", "%s", msg);
    1452           0 :                                 mid->active = hdl;
    1453           0 :                                 if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1454           0 :                                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1455           0 :                                         close_connection(mid);
    1456           0 :                                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1457           0 :                                         break;
    1458             :                                 }
    1459           0 :                                 read_into_cache(hdl, 0);
    1460             :                         }
    1461       27574 :                         hdl->npending_close = 0;
    1462       27574 :                         if (hdl->pending_close)
    1463           0 :                                 free(hdl->pending_close);
    1464       27574 :                         hdl->pending_close = NULL;
    1465       27574 :                         if (mid->to != NULL && result->tuple_count < result->row_count) {
    1466          11 :                                 char msg[256];
    1467             : 
    1468          11 :                                 snprintf(msg, sizeof(msg), "Xclose %d\n", result->tableid);
    1469          11 :                                 mapi_log_record(mid, "CMD", "%s", msg);
    1470          11 :                                 mid->active = hdl;
    1471          22 :                                 if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1472          11 :                                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1473           0 :                                         close_connection(mid);
    1474           0 :                                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1475             :                                 } else
    1476          11 :                                         read_into_cache(hdl, 0);
    1477             :                         }
    1478             :                 }
    1479       27575 :                 result->tableid = -1;
    1480             :         }
    1481       30889 :         if (mid->active == hdl &&
    1482           3 :             hdl->active == result &&
    1483           0 :             read_into_cache(hdl, -1) != MOK)
    1484             :                 return MERROR;
    1485       30889 :         if( hdl->active == result)
    1486             :                 return MERROR;
    1487             :         //assert(hdl->active != result);
    1488       30889 :         if (result->fields) {
    1489       85648 :                 for (i = 0; i < result->maxfields; i++) {
    1490       57753 :                         if (result->fields[i].tablename)
    1491       57550 :                                 free(result->fields[i].tablename);
    1492       57753 :                         if (result->fields[i].columnname)
    1493       57550 :                                 free(result->fields[i].columnname);
    1494       57753 :                         if (result->fields[i].columntype)
    1495       57558 :                                 free(result->fields[i].columntype);
    1496             :                 }
    1497       27895 :                 free(result->fields);
    1498             :         }
    1499       30889 :         result->fields = NULL;
    1500       30889 :         result->maxfields = result->fieldcnt = 0;
    1501       30889 :         if (result->cache.line) {
    1502      169939 :                 for (i = 0; i < result->cache.writer; i++) {
    1503      142043 :                         if (result->cache.line[i].rows)
    1504      142043 :                                 free(result->cache.line[i].rows);
    1505      142043 :                         if (result->cache.line[i].anchors) {
    1506             :                                 int j;
    1507             : 
    1508       86299 :                                 for (j = 0; j < result->cache.line[i].fldcnt; j++)
    1509       75632 :                                         if (result->cache.line[i].anchors[j]) {
    1510       54520 :                                                 free(result->cache.line[i].anchors[j]);
    1511       54520 :                                                 result->cache.line[i].anchors[j] = NULL;
    1512             :                                         }
    1513       10667 :                                 free(result->cache.line[i].anchors);
    1514             :                         }
    1515      142043 :                         if (result->cache.line[i].lens)
    1516       10667 :                                 free(result->cache.line[i].lens);
    1517             :                 }
    1518       27896 :                 free(result->cache.line);
    1519       27896 :                 result->cache.line = NULL;
    1520       27896 :                 result->cache.tuplecount = 0;
    1521             :         }
    1522       30889 :         if (result->errorstr && result->errorstr != mapi_nomem)
    1523          70 :                 free(result->errorstr);
    1524       30889 :         result->errorstr = NULL;
    1525       30889 :         memset(result->sqlstate, 0, sizeof(result->sqlstate));
    1526       30889 :         result->hdl = NULL;
    1527       30889 :         hdl->result = result->next;
    1528       30889 :         if (hdl->result == NULL)
    1529       30834 :                 hdl->lastresult = NULL;
    1530       30889 :         result->next = NULL;
    1531       30889 :         free(result);
    1532       30889 :         return MOK;
    1533             : }
    1534             : 
    1535             : static void
    1536          80 : add_error(struct MapiResultSet *result, char *error)
    1537             : {
    1538             :         /* concatenate the error messages */
    1539          80 :         size_t size = result->errorstr ? strlen(result->errorstr) : 0;
    1540             : 
    1541          80 :         if (strlen(error) > 6 && error[5] == '!' &&
    1542          63 :             (isdigit((unsigned char) error[0]) ||
    1543           0 :              (error[0] >= 'A' && error[0] <= 'Z')) &&
    1544          63 :             (isdigit((unsigned char) error[1]) ||
    1545           1 :              (error[1] >= 'A' && error[1] <= 'Z')) &&
    1546          63 :             (isdigit((unsigned char) error[2]) ||
    1547           1 :              (error[2] >= 'A' && error[2] <= 'Z')) &&
    1548          63 :             (isdigit((unsigned char) error[3]) ||
    1549           0 :              (error[3] >= 'A' && error[3] <= 'Z')) &&
    1550          63 :             (isdigit((unsigned char) error[4]) ||
    1551           0 :              (error[4] >= 'A' && error[4] <= 'Z'))) {
    1552          63 :                 if (result->errorstr == NULL) {
    1553             :                         /* remeber SQLSTATE for first error */
    1554          61 :                         strcpy_len(result->sqlstate, error,
    1555             :                                    sizeof(result->sqlstate));
    1556             :                 }
    1557             :                 /* skip SQLSTATE */
    1558          63 :                 error += 6;
    1559             :         }
    1560          80 :         REALLOC(result->errorstr, size + strlen(error) + 2);
    1561          80 :         if (result->errorstr == NULL)
    1562           0 :                 result->errorstr = mapi_nomem;
    1563             :         else {
    1564          80 :                 strcpy(result->errorstr + size, error);
    1565          80 :                 strcat(result->errorstr + size, "\n");
    1566             :         }
    1567          80 : }
    1568             : 
    1569             : const char *
    1570       10061 : mapi_result_error(MapiHdl hdl)
    1571             : {
    1572       10061 :         return hdl && hdl->result ? hdl->result->errorstr : NULL;
    1573             : }
    1574             : 
    1575             : const char *
    1576           1 : mapi_result_errorcode(MapiHdl hdl)
    1577             : {
    1578           1 :         return hdl && hdl->result && hdl->result->sqlstate[0] ? hdl->result->sqlstate : NULL;
    1579             : }
    1580             : 
    1581             : /* Go to the next result set, if any, and close the current result
    1582             :    set.  This function returns 1 if there are more result sets after
    1583             :    the one that was closed, otherwise, if more input is needed, return
    1584             :    MMORE, else, return MOK */
    1585             : MapiMsg
    1586        3726 : mapi_next_result(MapiHdl hdl)
    1587             : {
    1588        3726 :         mapi_hdl_check(hdl);
    1589             : 
    1590        6867 :         while (hdl->result != NULL) {
    1591        3196 :                 if (close_result(hdl) != MOK)
    1592             :                         return MERROR;
    1593        3196 :                 if (hdl->result &&
    1594          55 :                     (hdl->result->querytype == -1 ||
    1595             :                      /* basically exclude Q_PARSE and Q_BLOCK */
    1596             :                      (hdl->result->querytype >= Q_TABLE &&
    1597           0 :                       hdl->result->querytype <= Q_PREPARE) ||
    1598           0 :                      hdl->result->errorstr != NULL))
    1599             :                         return 1;
    1600             :         }
    1601        3671 :         return hdl->needmore ? MMORE : MOK;
    1602             : }
    1603             : 
    1604             : MapiMsg
    1605          13 : mapi_needmore(MapiHdl hdl)
    1606             : {
    1607          13 :         return hdl->needmore ? MMORE : MOK;
    1608             : }
    1609             : 
    1610             : bool
    1611        2007 : mapi_more_results(MapiHdl hdl)
    1612             : {
    1613        2007 :         struct MapiResultSet *result;
    1614             : 
    1615        2007 :         mapi_hdl_check(hdl);
    1616             : 
    1617        2007 :         if ((result = hdl->result) == 0) {
    1618             :                 /* there are no results at all */
    1619             :                 return false;
    1620             :         }
    1621        1999 :         if (result->querytype == Q_TABLE && hdl->mid->active == hdl) {
    1622             :                 /* read until next result (if any) */
    1623           0 :                 read_into_cache(hdl, -1);
    1624             :         }
    1625        1999 :         if (hdl->needmore) {
    1626             :                 /* assume the application will provide more data and
    1627             :                    that we will then have a result */
    1628             :                 return true;
    1629             :         }
    1630        1999 :         while (result->next) {
    1631           0 :                 result = result->next;
    1632           0 :                 if (result->querytype == -1 ||
    1633             :                     /* basically exclude Q_PARSE and Q_BLOCK */
    1634           0 :                     (hdl->result->querytype >= Q_TABLE &&
    1635           0 :                      hdl->result->querytype <= Q_PREPARE) ||
    1636           0 :                     result->errorstr != NULL)
    1637             :                         return true;
    1638             :         }
    1639             :         /* no more results */
    1640             :         return false;
    1641             : }
    1642             : 
    1643             : MapiHdl
    1644       34681 : mapi_new_handle(Mapi mid)
    1645             : {
    1646       34681 :         MapiHdl hdl;
    1647             : 
    1648       34681 :         mapi_check0(mid);
    1649             : 
    1650       34681 :         hdl = malloc(sizeof(*hdl));
    1651       34681 :         if (hdl == NULL) {
    1652           0 :                 mapi_setError(mid, "Memory allocation failure", __func__, MERROR);
    1653           0 :                 return NULL;
    1654             :         }
    1655       34681 :         *hdl = (struct MapiStatement) {
    1656             :                 .mid = mid,
    1657             :                 .needmore = false,
    1658             :         };
    1659             :         /* add to doubly-linked list */
    1660       34681 :         hdl->next = mid->first;
    1661       34681 :         mid->first = hdl;
    1662       34681 :         if (hdl->next)
    1663        3704 :                 hdl->next->prev = hdl;
    1664             :         return hdl;
    1665             : }
    1666             : 
    1667             : /* close all result sets on the handle but don't close the handle itself */
    1668             : static MapiMsg
    1669       68489 : finish_handle(MapiHdl hdl)
    1670             : {
    1671       68489 :         Mapi mid;
    1672       68489 :         int i;
    1673             : 
    1674       68489 :         if (hdl == NULL)
    1675             :                 return MERROR;
    1676       68489 :         mid = hdl->mid;
    1677       68767 :         if (mid->active == hdl && !hdl->needmore && !mnstr_eof(mid->from) &&
    1678         278 :             read_into_cache(hdl, 0) != MOK)
    1679             :                 return MERROR;
    1680       68489 :         if (mid->to) {
    1681       68488 :                 if (hdl->needmore) {
    1682           0 :                         assert(mid->active == NULL || mid->active == hdl);
    1683           0 :                         hdl->needmore = false;
    1684           0 :                         mid->active = hdl;
    1685           0 :                         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    1686           0 :                         check_stream(mid, mid->to, "write error on stream", mid->error);
    1687           0 :                         read_into_cache(hdl, 0);
    1688             :                 }
    1689       68488 :                 for (i = 0; i < hdl->npending_close; i++) {
    1690           0 :                         char msg[256];
    1691             : 
    1692           0 :                         snprintf(msg, sizeof(msg), "Xclose %d\n", hdl->pending_close[i]);
    1693           0 :                         mapi_log_record(mid, "CMD", "%s", msg);
    1694           0 :                         mid->active = hdl;
    1695           0 :                         if (mnstr_printf(mid->to, "%s", msg) < 0 ||
    1696           0 :                             mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    1697           0 :                                 close_connection(mid);
    1698           0 :                                 mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    1699           0 :                                 break;
    1700             :                         }
    1701           0 :                         read_into_cache(hdl, 0);
    1702             :                 }
    1703             :         }
    1704       68489 :         hdl->npending_close = 0;
    1705       68489 :         if (hdl->pending_close)
    1706           0 :                 free(hdl->pending_close);
    1707       68489 :         hdl->pending_close = NULL;
    1708      164671 :         while (hdl->result) {
    1709       27693 :                 if (close_result(hdl) != MOK)
    1710             :                         return MERROR;
    1711       27693 :                 if (hdl->needmore) {
    1712           0 :                         assert(mid->active == NULL || mid->active == hdl);
    1713           0 :                         hdl->needmore = false;
    1714           0 :                         mid->active = hdl;
    1715           0 :                         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    1716           0 :                         check_stream(mid, mid->to, "write error on stream", mid->error);
    1717           0 :                         read_into_cache(hdl, 0);
    1718             :                 }
    1719             :         }
    1720             :         return MOK;
    1721             : }
    1722             : 
    1723             : /* Close a statement handle, discarding any unread output. */
    1724             : MapiMsg
    1725       34681 : mapi_close_handle(MapiHdl hdl)
    1726             : {
    1727       34681 :         if (hdl == NULL)
    1728             :                 return MOK;
    1729       34681 :         debugprint("entering %s\n", "mapi_close_handle");
    1730             : 
    1731             :         /* don't use mapi_check_hdl: it's ok if we're not connected */
    1732       34681 :         mapi_clrError(hdl->mid);
    1733             : 
    1734       34681 :         if (finish_handle(hdl) != MOK)
    1735             :                 return MERROR;
    1736       34681 :         hdl->npending_close = 0;
    1737       34681 :         if (hdl->pending_close)
    1738           0 :                 free(hdl->pending_close);
    1739       34681 :         hdl->pending_close = NULL;
    1740       34681 :         if (hdl->bindings)
    1741           0 :                 free(hdl->bindings);
    1742       34681 :         hdl->bindings = NULL;
    1743       34681 :         hdl->maxbindings = 0;
    1744       34681 :         if (hdl->params)
    1745           0 :                 free(hdl->params);
    1746       34681 :         hdl->params = NULL;
    1747       34681 :         hdl->maxparams = 0;
    1748       34681 :         if (hdl->query)
    1749       32965 :                 free(hdl->query);
    1750       34681 :         hdl->query = NULL;
    1751       34681 :         if (hdl->template)
    1752           0 :                 free(hdl->template);
    1753       34681 :         hdl->template = NULL;
    1754             :         /* remove from doubly-linked list */
    1755       34681 :         if (hdl->prev)
    1756           1 :                 hdl->prev->next = hdl->next;
    1757       34681 :         if (hdl->next)
    1758        3703 :                 hdl->next->prev = hdl->prev;
    1759       34681 :         if (hdl->mid->first == hdl)
    1760       34680 :                 hdl->mid->first = hdl->next;
    1761       34681 :         hdl->prev = NULL;
    1762       34681 :         hdl->next = NULL;
    1763       34681 :         hdl->mid = NULL;
    1764       34681 :         free(hdl);
    1765       34681 :         return MOK;
    1766             : }
    1767             : 
    1768             : const struct MapiStruct MapiStructDefaults = {
    1769             :         .error = MOK,
    1770             :         .redirmax = 10,
    1771             :         .blk.eos = false,
    1772             :         .blk.lim = BLOCK,
    1773             : };
    1774             : 
    1775             : /* Allocate a new connection handle. */
    1776             : Mapi
    1777         393 : mapi_new(msettings *settings)
    1778             : {
    1779         393 :         Mapi mid;
    1780         393 :         static ATOMIC_TYPE index = ATOMIC_VAR_INIT(0);
    1781             : 
    1782         393 :         mid = malloc(sizeof(*mid));
    1783         393 :         if (mid == NULL)
    1784             :                 return NULL;
    1785         393 :         if (settings == NULL)
    1786         394 :                 settings = msettings_create();
    1787         394 :         if (settings == NULL) {
    1788           0 :                 free(mid);
    1789           0 :                 return NULL;
    1790             :         }
    1791             : 
    1792             :         /* then fill in some details */
    1793         393 :         *mid = MapiStructDefaults;
    1794         393 :         mid->settings = settings;
    1795         393 :         mid->index =  (uint32_t) ATOMIC_ADD(&index, 1); /* for distinctions in log records */
    1796         393 :         if ((mid->blk.buf = malloc(mid->blk.lim + 1)) == NULL) {
    1797           0 :                 mapi_destroy(mid);
    1798           0 :                 return NULL;
    1799             :         }
    1800         393 :         mid->blk.buf[0] = 0;
    1801         393 :         mid->blk.buf[mid->blk.lim] = 0;
    1802             : 
    1803             :         /* also the current timezone, seconds EAST of UTC */
    1804         393 :         long time_zone;
    1805             : #if defined(_MSC_VER)
    1806             :         DYNAMIC_TIME_ZONE_INFORMATION tzinf;
    1807             : 
    1808             :         /* documentation says: UTC = localtime + Bias (in minutes),
    1809             :          * but experimentation during DST period says, UTC = localtime
    1810             :          * + Bias + DaylightBias, and presumably during non DST
    1811             :          * period, UTC = localtime + Bias */
    1812             :         switch (GetDynamicTimeZoneInformation(&tzinf)) {
    1813             :         case TIME_ZONE_ID_STANDARD:     /* using standard time */
    1814             :         case TIME_ZONE_ID_UNKNOWN:      /* no daylight saving time in this zone */
    1815             :                 time_zone = -(int) tzinf.Bias * 60;
    1816             :                 break;
    1817             :         case TIME_ZONE_ID_DAYLIGHT:     /* using daylight saving time */
    1818             :                 time_zone = -(int) (tzinf.Bias + tzinf.DaylightBias) * 60;
    1819             :                 break;
    1820             :         default:                                        /* aka TIME_ZONE_ID_INVALID */
    1821             :                 /* call failed, we don't know the time zone */
    1822             :                 time_zone = 0;
    1823             :                 break;
    1824             :         }
    1825             : #else
    1826         393 :         time_t t = time(NULL);
    1827         393 :         struct tm *local_tm = localtime_r(&t, &(struct tm){0});
    1828             : #ifdef HAVE_TM_GMTOFF
    1829         394 :         time_zone = local_tm->tm_gmtoff;
    1830             : #else
    1831             :         struct tm *gm_tm = gmtime_r(&t, &(struct tm){0});
    1832             :         time_t gt = mktime(gm_tm);
    1833             :         local_tm->tm_isdst=0; /* We need the difference without dst */
    1834             :         time_t lt = mktime(local_tm);
    1835             :         assert((int64_t) gt - (int64_t) lt >= (int64_t) INT_MIN && (int64_t) gt - (int64_t) lt <= (int64_t) INT_MAX);
    1836             :         time_zone = (long) (lt - gt);
    1837             : #endif
    1838             : #endif
    1839         394 :         msettings_error err =  msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
    1840         394 :         if (err)
    1841           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1842             : 
    1843             :         return mid;
    1844             : }
    1845             : 
    1846             : /* construct the uri field of a Mapi struct */
    1847             : void
    1848         206 : set_uri(Mapi mid)
    1849             : {
    1850         206 :         const char *host = msetting_string(mid->settings, MP_HOST);
    1851         205 :         const char *database = msetting_string(mid->settings, MP_DATABASE);
    1852         206 :         int port = msetting_long(mid->settings, MP_PORT);
    1853         206 :         size_t urilen = strlen(host) + (database ? strlen(database) : 0) + 32;
    1854         206 :         char *uri = malloc(urilen);
    1855             : 
    1856             :         /* uri looks as follows:
    1857             :          *  mapi:monetdb://host:port/database
    1858             :          * or
    1859             :          *  mapi:monetdb:///some/path/to?database=database
    1860             :          */
    1861             : 
    1862         206 :         if (database != NULL) {
    1863         206 :                 if (host[0] == '/') {
    1864           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s?database=%s",
    1865             :                                  host, database);
    1866             :                 } else {
    1867         206 :                         snprintf(uri, urilen, "mapi:monetdb://%s:%d/%s",
    1868             :                                  host, port, database);
    1869             :                 }
    1870             :         } else {
    1871           0 :                 if (host[0] == '/') {
    1872           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s",
    1873             :                                  host);
    1874             :                 } else {
    1875           0 :                         snprintf(uri, urilen, "mapi:monetdb://%s:%d",
    1876             :                                  host, port);
    1877             :                 }
    1878             :         }
    1879             : 
    1880         206 :         if (mid->uri != NULL)
    1881           0 :                 free(mid->uri);
    1882         206 :         mid->uri = uri;
    1883         206 : }
    1884             : 
    1885             : Mapi
    1886         205 : mapi_mapiuri(const char *url, const char *user, const char *pass, const char *lang)
    1887             : {
    1888         205 :         Mapi mid;
    1889             : 
    1890         205 :         if (!ATOMIC_TAS(&mapi_initialized)) {
    1891          42 :                 if (mnstr_init() < 0)
    1892             :                         return NULL;
    1893             :         }
    1894             : 
    1895         205 :         mid = mapi_new(NULL);
    1896         206 :         if (mid == NULL)
    1897             :                 return NULL;
    1898             : 
    1899         206 :         if (url == NULL) {
    1900           0 :                 mapi_setError(mid, "url is null", __func__, MERROR);
    1901           0 :                 return mid;
    1902             :         }
    1903         206 :         if (user == NULL) {
    1904           0 :                 mapi_setError(mid, "user is null", __func__, MERROR);
    1905           0 :                 return mid;
    1906             :         }
    1907         206 :         if (pass == NULL) {
    1908           0 :                 mapi_setError(mid, "pass is null", __func__, MERROR);
    1909           0 :                 return mid;
    1910             :         }
    1911         206 :         if (lang == NULL) {
    1912           0 :                 mapi_setError(mid, "lang is null", __func__, MERROR);
    1913           0 :                 return mid;
    1914             :         }
    1915             : 
    1916         206 :         msettings_error err = NULL;
    1917         206 :         if (    (err = msetting_set_string(mid->settings, MP_USER, user))
    1918         206 :                 || (err = msetting_set_string(mid->settings, MP_PASSWORD, pass))
    1919         206 :                 || (err = msetting_set_string(mid->settings, MP_LANGUAGE, lang))
    1920             :         ) {
    1921             :                 // in the old implementation we returned NULL but that doesn't
    1922             :                 // make sense to me because above we already started returning
    1923             :                 // mid with the error set.
    1924           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1925           0 :                 return mid;
    1926             :         }
    1927             : 
    1928         206 :         char *error_message = NULL;
    1929         206 :         if (!msettings_parse_url(mid->settings, url, &error_message)) {
    1930           0 :                 char *msg = error_message ? error_message : "malloc failed";
    1931           0 :                 mapi_setError(mid, msg, __func__, MERROR);
    1932           0 :                 free(error_message);
    1933           0 :                 return mid;
    1934             :         }
    1935             : 
    1936         206 :         set_uri(mid);
    1937             : 
    1938         206 :         return mid;
    1939             : }
    1940             : 
    1941             : /* Allocate a new connection handle and fill in the information needed
    1942             :    to connect to a server, but don't connect yet. */
    1943             : Mapi
    1944         188 : mapi_mapi(const char *host, int port, const char *username,
    1945             :           const char *password, const char *lang, const char *dbname)
    1946             : {
    1947         188 :         Mapi mid;
    1948             : 
    1949         188 :         if (!ATOMIC_TAS(&mapi_initialized)) {
    1950         183 :                 if (mnstr_init() < 0)
    1951             :                         return NULL;
    1952             :         }
    1953             : 
    1954         188 :         mid = mapi_new(NULL);
    1955         188 :         if (mid == NULL)
    1956             :                 return NULL;
    1957         188 :         msettings *settings = mid->settings;
    1958             : 
    1959         188 :         if (lang == NULL)
    1960           0 :                 lang = "sql";
    1961             : 
    1962         188 :         const char *sockdir = NULL;
    1963         188 :         if (host && host[0] == '/') {
    1964             :                 sockdir = host;
    1965             :                 host = NULL;
    1966             :         }
    1967             : 
    1968         126 :         msettings_error err = NULL;
    1969         314 :         do {
    1970         126 :                 if (host && (err = msetting_set_string(settings, MP_HOST, host)))
    1971             :                         break;
    1972         188 :                 if (sockdir && (err = msetting_set_string(settings, MP_SOCKDIR, sockdir)))
    1973             :                         break;
    1974         188 :                 if (username && (err = msetting_set_string(settings, MP_USER, username)))
    1975             :                         break;
    1976         188 :                 if (password && (err = msetting_set_string(settings, MP_PASSWORD, password)))
    1977             :                         break;
    1978         188 :                 if (lang && (err = msetting_set_string(settings, MP_LANGUAGE, lang)))
    1979             :                         break;
    1980         188 :                 if (dbname && (err = msetting_set_string(settings, MP_DATABASE, dbname)))
    1981             :                         break;
    1982         188 :                 if (port > 0 && (err = msetting_set_long(settings, MP_PORT, port)))
    1983             :                         break;
    1984             :         } while (0);
    1985         188 :         if (err) {
    1986           0 :                 mapi_setError(mid, err, __func__, MERROR);
    1987           0 :                 return mid;
    1988             :         }
    1989             : 
    1990             :         return mid;
    1991             : }
    1992             : 
    1993             : Mapi
    1994           0 : mapi_settings(msettings *settings)
    1995             : {
    1996           0 :         assert(settings);
    1997           0 :         Mapi mid = mapi_new(settings);
    1998           0 :         if (mid == NULL)
    1999             :                 return mid;
    2000             : 
    2001           0 :         set_uri(mid);
    2002           0 :         return mid;
    2003             : }
    2004             : 
    2005             : 
    2006             : /* Close a connection and free all memory associated with the
    2007             :    connection handle. */
    2008             : MapiMsg
    2009         373 : mapi_destroy(Mapi mid)
    2010             : {
    2011         373 :         char **r;
    2012             : 
    2013         373 :         mapi_clrError(mid);
    2014             : 
    2015         374 :         while (mid->first)
    2016           1 :                 mapi_close_handle(mid->first);
    2017         373 :         if (mid->connected)
    2018         170 :                 (void) mapi_disconnect(mid);
    2019         373 :         if (mid->tracelog)
    2020           0 :                 close_stream(mid->tracelog);
    2021             : 
    2022         373 :         free(mid->blk.buf);
    2023         373 :         free(mid->motd);
    2024         373 :         free(mid->server);
    2025         373 :         free(mid->uri);
    2026         373 :         free(mid->tracebuffer);
    2027         373 :         free(mid->noexplain);
    2028         373 :         if (mid->errorstr && mid->errorstr != mapi_nomem)
    2029           0 :                 free(mid->errorstr);
    2030             : 
    2031         373 :         msettings_destroy(mid->settings);
    2032             : 
    2033         373 :         r = mid->redirects;
    2034         373 :         while (*r) {
    2035           0 :                 free(*r);
    2036           0 :                 r++;
    2037             :         }
    2038             : 
    2039         373 :         free(mid);
    2040         373 :         return MOK;
    2041             : }
    2042             : 
    2043             : /* Create a connection handle and connect to the server using the
    2044             :    specified parameters. */
    2045             : Mapi
    2046          11 : mapi_connect(const char *host, int port, const char *username, const char *password, const char *lang, const char *dbname)
    2047             : {
    2048          11 :         Mapi mid;
    2049             : 
    2050          11 :         mid = mapi_mapi(host, port, username, password, lang, dbname);
    2051          11 :         if (mid && mid->error == MOK)
    2052          11 :                 mapi_reconnect(mid);    /* actually, initial connect */
    2053          11 :         return mid;
    2054             : }
    2055             : 
    2056             : /* Returns an malloced NULL-terminated array with redirects */
    2057             : char **
    2058           0 : mapi_resolve(const char *host, int port, const char *pattern)
    2059             : {
    2060           0 :         int rmax;
    2061           0 :         Mapi mid;
    2062             : 
    2063             :         /* if it doesn't make sense, don't try to crash */
    2064           0 :         if (pattern == NULL)
    2065             :                 return NULL;
    2066             : 
    2067           0 :         mid = mapi_mapi(host, port, "mero", "mero", "resolve", pattern);
    2068           0 :         if (mid) {
    2069           0 :                 if (mid->error == MOK) {
    2070           0 :                         rmax = mid->redirmax;
    2071           0 :                         mid->redirmax = 0;
    2072           0 :                         mapi_reconnect(mid);    /* real connect, don't follow redirects */
    2073           0 :                         mid->redirmax = rmax;
    2074           0 :                         if (mid->error == MOK) {
    2075           0 :                                 close_connection(mid);  /* we didn't expect a connection actually */
    2076             :                         } else {
    2077           0 :                                 char **ret = malloc(sizeof(char *) * MAXREDIR);
    2078           0 :                                 memcpy(ret, mid->redirects, sizeof(char *) * MAXREDIR);
    2079           0 :                                 mid->redirects[0] = NULL;    /* make sure the members aren't freed */
    2080           0 :                                 mapi_destroy(mid);
    2081           0 :                                 return ret;
    2082             :                         }
    2083             :                 }
    2084           0 :                 mapi_destroy(mid);
    2085             :         }
    2086             :         return NULL;
    2087             : }
    2088             : 
    2089             : void
    2090        1377 : close_connection(Mapi mid)
    2091             : {
    2092        1377 :         MapiHdl hdl;
    2093        1377 :         struct MapiResultSet *result;
    2094             : 
    2095        1377 :         mid->connected = false;
    2096        1377 :         mid->active = NULL;
    2097        1378 :         for (hdl = mid->first; hdl; hdl = hdl->next) {
    2098           1 :                 hdl->active = NULL;
    2099           1 :                 for (result = hdl->result; result; result = result->next)
    2100           0 :                         result->tableid = -1;
    2101             :         }
    2102             :         /* finish channels */
    2103             :         /* Make sure that the write- (to-) stream is closed first,
    2104             :          * as the related read- (from-) stream closes the shared
    2105             :          * socket; see also src/common/stream.c:socket_close .
    2106             :          */
    2107        1377 :         if (mid->to) {
    2108        1377 :                 close_stream(mid->to);
    2109        1377 :                 mid->to = 0;
    2110             :         }
    2111        1377 :         if (mid->from) {
    2112        1377 :                 close_stream(mid->from);
    2113        1377 :                 mid->from = 0;
    2114             :         }
    2115        1377 :         mid->redircnt = 0;
    2116        1377 :         mapi_log_record(mid, "C", "Connection closed");
    2117        1377 : }
    2118             : 
    2119             : MapiMsg
    2120        1367 : mapi_disconnect(Mapi mid)
    2121             : {
    2122        1367 :         mapi_check(mid);
    2123             : 
    2124        1367 :         close_connection(mid);
    2125        1367 :         return MOK;
    2126             : }
    2127             : 
    2128             : /* Set callback function to retrieve or send file content for COPY
    2129             :  * INTO queries.
    2130             :  *
    2131             :  * char *getfile(void *private, const char *filename, bool binary,
    2132             :  *               uint64_6 offset, size_t *size);
    2133             :  * Retrieve data from a file.
    2134             :  *
    2135             :  * The arguments are:
    2136             :  * private - the value of the filecontentprivate argument to
    2137             :  *           mapi_setfilecallback;
    2138             :  * filename - the file to read (the application is free to interpret
    2139             :  *            this any way it wants, including getting data over the
    2140             :  *            Internet);
    2141             :  * binary - if set, the file is expected to contain binary data and
    2142             :  *          should therefore be opened in binary mode, otherwise the
    2143             :  *          file is expected to contain data in the UTF-8 encoding (of
    2144             :  *          course, the application is free to transparently convert
    2145             :  *          from the actual encoding to UTF-8);
    2146             :  * offset - the line number of the first line to be retrieved (this is
    2147             :  *          one-based, i.e. the start of the file has line number one;
    2148             :  *          lines are terminated by '\n');
    2149             :  * size - pointer in which to return the size of the chunk that is
    2150             :  *        being returned.
    2151             :  *
    2152             :  * The callback function is expected to return data in chunks until it
    2153             :  * indicates to the caller that there is no more data or an error has
    2154             :  * occurred.  Chunks can be any size.  The caller does not modify or
    2155             :  * free the data returned.  The size of the chunk being returned is
    2156             :  * stored in the size argument.  Errors are indicated by returning a
    2157             :  * string containing an error message and setting *size to zero.  The
    2158             :  * error message should not contain any newlines.  Any call to the
    2159             :  * callback function is allowed to return an error.
    2160             :  *
    2161             :  * The first call to the callback function contains values for
    2162             :  * filename, binary, and offset.  These parameters are all 0 for all
    2163             :  * subsequent calls for continuation data from the same file.
    2164             :  *
    2165             :  * If the caller has retrieved enough data before the file is
    2166             :  * exhausted, it calls the callback function one more time with a NULL
    2167             :  * pointer for the size argument.  This gives the callback function
    2168             :  * the opportunity to free its resources (e.g. close the file).
    2169             :  *
    2170             :  * If there is no more data to be returned, the callback function
    2171             :  * returns a NULL pointer and sets *size to zero.  No more calls for
    2172             :  * the current file will be made.
    2173             :  *
    2174             :  * Note that if the file to be read is empty, or contains fewer lines
    2175             :  * than the requested offset, the first call to the callback function
    2176             :  * may return NULL.
    2177             :  *
    2178             :  * char *putfile(void *private, const char *filename, bool binary,
    2179             :  *               const void *data, size_t size);
    2180             :  * Send data to a file.
    2181             :  *
    2182             :  * The arguments are:
    2183             :  * private - the value of the filecontentprivate argument to
    2184             :  *           mapi_setfilecallback;
    2185             :  * filename - the file to be written, files are always written as text
    2186             :  *            files;
    2187             :  * binary - if set, the data to be written is binary and the file
    2188             :  *          should therefore be opened in binary mode, otherwise the
    2189             :  *          data is UTF-8 encoded text;
    2190             :  * data - the data to be written;
    2191             :  * size - the size of the data to be written.
    2192             :  *
    2193             :  * The callback is called multiple time to write a single file.  The
    2194             :  * first time, a filename is specified, all subsequent times, the
    2195             :  * filename argument is NULL.  When all data has been written, the
    2196             :  * callback function is called one last time with NULL pointer for the
    2197             :  * data argument so that the callback function can free any resources.
    2198             :  *
    2199             :  * When an error occurs, the callback function returns a string
    2200             :  * containing an error message after which the callback will not be
    2201             :  * called again for the same file.  Otherwise, the callback function
    2202             :  * returns NULL.
    2203             :  *
    2204             :  * Note also that multibyte sequences may be split over two calls.
    2205             :  */
    2206             : void
    2207         144 : mapi_setfilecallback2(Mapi mid,
    2208             :                      char *(*getfilecontent)(void *,
    2209             :                                              const char *, bool,
    2210             :                                              uint64_t, size_t *),
    2211             :                      char *(*putfilecontent)(void *,
    2212             :                                              const char *, bool,
    2213             :                                              const void *, size_t),
    2214             :                      void *filecontentprivate)
    2215             : {
    2216         144 :         mid->getfilecontent = getfilecontent;
    2217         144 :         mid->putfilecontent = putfilecontent;
    2218         144 :         mid->filecontentprivate = filecontentprivate;
    2219         144 :         mid->putfilecontent_old = NULL;
    2220         144 :         mid->filecontentprivate_old = NULL;
    2221         144 : }
    2222             : 
    2223             : static char *
    2224           0 : putfilecontent_wrap(void *priv, const char *filename, bool binary, const void *data, size_t size)
    2225             : {
    2226           0 :         Mapi mid = priv;
    2227           0 :         void *priv_old = mid->filecontentprivate_old;
    2228           0 :         if (filename && binary)
    2229             :                 return "Client does not support writing binary files";
    2230           0 :         return mid->putfilecontent_old(priv_old, filename, data, size);
    2231             : }
    2232             : 
    2233             : /* DEPRECATED. Set callback function to retrieve or send file content for COPY
    2234             :  * INTO queries.
    2235             :  *
    2236             :  * Deprecated because it does not support binary downloads.
    2237             :  * Use mapi_setfilecallback2 instead.
    2238             :  */
    2239             : void
    2240           0 : mapi_setfilecallback(Mapi mid,
    2241             :                      char *(*getfilecontent)(void *,
    2242             :                                              const char *, bool,
    2243             :                                              uint64_t, size_t *),
    2244             :                      char *(*putfilecontent)(void *,
    2245             :                                              const char *,
    2246             :                                              const void *, size_t),
    2247             :                      void *filecontentprivate)
    2248             : {
    2249           0 :         mid->getfilecontent = getfilecontent;
    2250           0 :         mid->putfilecontent = putfilecontent_wrap;
    2251           0 :         mid->filecontentprivate = mid;
    2252           0 :         mid->putfilecontent_old = putfilecontent;
    2253           0 :         mid->filecontentprivate_old = filecontentprivate;
    2254           0 : }
    2255             : 
    2256             : #define testBinding(hdl,fnr)                                            \
    2257             :         do {                                                            \
    2258             :                 mapi_hdl_check(hdl);                                    \
    2259             :                 if (fnr < 0) {                                               \
    2260             :                         return mapi_setError(hdl->mid,                       \
    2261             :                                              "Illegal field number",  \
    2262             :                                              __func__, MERROR);         \
    2263             :                 }                                                       \
    2264             :                 /* make sure there is enough space */                   \
    2265             :                 if (fnr >= hdl->maxbindings)                              \
    2266             :                         mapi_extend_bindings(hdl, fnr);                 \
    2267             :         } while (0)
    2268             : 
    2269             : #define testParam(hdl, fnr)                                             \
    2270             :         do {                                                            \
    2271             :                 mapi_hdl_check(hdl);                                    \
    2272             :                 if (fnr < 0) {                                               \
    2273             :                         return mapi_setError(hdl->mid,                       \
    2274             :                                              "Illegal param number",  \
    2275             :                                              __func__, MERROR);         \
    2276             :                 }                                                       \
    2277             :                 if (fnr >= hdl->maxparams)                                \
    2278             :                         mapi_extend_params(hdl, fnr);                   \
    2279             :         } while (0)
    2280             : 
    2281             : MapiMsg
    2282           0 : mapi_bind(MapiHdl hdl, int fnr, char **ptr)
    2283             : {
    2284           0 :         testBinding(hdl, fnr);
    2285           0 :         hdl->bindings[fnr].outparam = ptr;
    2286             : 
    2287           0 :         hdl->bindings[fnr].outtype = MAPI_AUTO;
    2288           0 :         return MOK;
    2289             : }
    2290             : 
    2291             : MapiMsg
    2292           0 : mapi_bind_var(MapiHdl hdl, int fnr, int type, void *ptr)
    2293             : {
    2294           0 :         testBinding(hdl, fnr);
    2295           0 :         hdl->bindings[fnr].outparam = ptr;
    2296             : 
    2297           0 :         if (type >= 0 && type < MAPI_NUMERIC)
    2298           0 :                 hdl->bindings[fnr].outtype = type;
    2299             :         else
    2300           0 :                 return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
    2301           0 :         return MOK;
    2302             : }
    2303             : 
    2304             : MapiMsg
    2305           0 : mapi_bind_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
    2306             : {
    2307           0 :         if (mapi_bind_var(hdl, fnr, MAPI_NUMERIC, ptr))
    2308           0 :                 return hdl->mid->error;
    2309             : 
    2310           0 :         hdl->bindings[fnr].scale = scale;
    2311           0 :         hdl->bindings[fnr].precision = prec;
    2312           0 :         return MOK;
    2313             : }
    2314             : 
    2315             : MapiMsg
    2316           0 : mapi_clear_bindings(MapiHdl hdl)
    2317             : {
    2318           0 :         mapi_hdl_check(hdl);
    2319           0 :         if (hdl->bindings)
    2320           0 :                 memset(hdl->bindings, 0, hdl->maxbindings * sizeof(*hdl->bindings));
    2321             :         return MOK;
    2322             : }
    2323             : 
    2324             : MapiMsg
    2325           0 : mapi_param_type(MapiHdl hdl, int fnr, int ctype, int sqltype, void *ptr)
    2326             : {
    2327           0 :         testParam(hdl, fnr);
    2328           0 :         hdl->params[fnr].inparam = ptr;
    2329             : 
    2330           0 :         if (ctype >= 0 && ctype < MAPI_NUMERIC)
    2331           0 :                 hdl->params[fnr].intype = ctype;
    2332             :         else
    2333           0 :                 return mapi_setError(hdl->mid, "Illegal SQL type identifier", __func__, MERROR);
    2334           0 :         hdl->params[fnr].sizeptr = NULL;
    2335           0 :         hdl->params[fnr].outtype = sqltype;
    2336           0 :         hdl->params[fnr].scale = 0;
    2337           0 :         hdl->params[fnr].precision = 0;
    2338           0 :         return MOK;
    2339             : }
    2340             : 
    2341             : MapiMsg
    2342           0 : mapi_param_string(MapiHdl hdl, int fnr, int sqltype, char *ptr, int *sizeptr)
    2343             : {
    2344           0 :         testParam(hdl, fnr);
    2345           0 :         hdl->params[fnr].inparam = (void *) ptr;
    2346             : 
    2347           0 :         hdl->params[fnr].intype = MAPI_VARCHAR;
    2348           0 :         hdl->params[fnr].sizeptr = sizeptr;
    2349           0 :         hdl->params[fnr].outtype = sqltype;
    2350           0 :         hdl->params[fnr].scale = 0;
    2351           0 :         hdl->params[fnr].precision = 0;
    2352           0 :         return MOK;
    2353             : }
    2354             : 
    2355             : MapiMsg
    2356           0 : mapi_param(MapiHdl hdl, int fnr, char **ptr)
    2357             : {
    2358           0 :         return mapi_param_type(hdl, fnr, MAPI_AUTO, MAPI_AUTO, ptr);
    2359             : }
    2360             : 
    2361             : MapiMsg
    2362           0 : mapi_param_numeric(MapiHdl hdl, int fnr, int scale, int prec, void *ptr)
    2363             : {
    2364           0 :         if (mapi_param_type(hdl, fnr, MAPI_NUMERIC, MAPI_NUMERIC, ptr))
    2365           0 :                 return hdl->mid->error;
    2366             : 
    2367           0 :         hdl->params[fnr].scale = scale;
    2368           0 :         hdl->params[fnr].precision = prec;
    2369           0 :         return MOK;
    2370             : }
    2371             : 
    2372             : MapiMsg
    2373           2 : mapi_clear_params(MapiHdl hdl)
    2374             : {
    2375           2 :         mapi_hdl_check(hdl);
    2376           2 :         if (hdl->params)
    2377           0 :                 memset(hdl->params, 0, hdl->maxparams * sizeof(*hdl->params));
    2378             :         return MOK;
    2379             : }
    2380             : 
    2381             : static MapiHdl
    2382       31747 : prepareQuery(MapiHdl hdl, const char *cmd)
    2383             : {
    2384       31747 :         if (hdl && cmd) {
    2385       31747 :                 if (hdl->query)
    2386        2098 :                         free(hdl->query);
    2387       31747 :                 hdl->query = strdup(cmd);
    2388       31747 :                 assert(hdl->query);
    2389       31747 :                 if (hdl->template) {
    2390           0 :                         free(hdl->template);
    2391           0 :                         hdl->template = NULL;
    2392             :                 }
    2393             :         }
    2394       31747 :         return hdl;
    2395             : }
    2396             : 
    2397             : 
    2398             : MapiMsg
    2399           6 : mapi_set_timeout(Mapi mid, unsigned int timeout, bool (*callback)(void *), void *callback_data)
    2400             : {
    2401           6 :         mapi_check(mid);
    2402           6 :         if (mid->trace)
    2403           0 :                 printf("Set timeout to %u\n", timeout);
    2404           6 :         mnstr_settimeout(mid->to, timeout, callback, callback_data);
    2405           6 :         mnstr_settimeout(mid->from, timeout, callback, callback_data);
    2406           6 :         return MOK;
    2407             : }
    2408             : 
    2409             : MapiMsg
    2410           6 : mapi_timeout(Mapi mid, unsigned int timeout)
    2411             : {
    2412           6 :         return mapi_set_timeout(mid, timeout, NULL, NULL);
    2413             : }
    2414             : 
    2415             : MapiMsg
    2416          10 : mapi_Xcommand(Mapi mid, const char *cmdname, const char *cmdvalue)
    2417             : {
    2418          10 :         MapiHdl hdl;
    2419             : 
    2420          10 :         mapi_check(mid);
    2421          10 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    2422             :                 return MERROR;
    2423          20 :         if (mnstr_printf(mid->to, "X" "%s %s\n", cmdname, cmdvalue) < 0 ||
    2424          10 :             mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    2425           0 :                 close_connection(mid);
    2426           0 :                 mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    2427           0 :                 return MERROR;
    2428             :         }
    2429          10 :         mapi_log_record(mid, "X", "X" "%s %s\n", cmdname, cmdvalue);
    2430          10 :         hdl = prepareQuery(mapi_new_handle(mid), "Xcommand");
    2431          10 :         if (hdl == NULL)
    2432             :                 return MERROR;
    2433          10 :         mid->active = hdl;
    2434          10 :         read_into_cache(hdl, 0);
    2435          10 :         mapi_close_handle(hdl); /* reads away any output */
    2436          10 :         return MOK;
    2437             : }
    2438             : 
    2439             : MapiMsg
    2440           0 : mapi_prepare_handle(MapiHdl hdl, const char *cmd)
    2441             : {
    2442           0 :         mapi_hdl_check(hdl);
    2443           0 :         if (finish_handle(hdl) != MOK)
    2444             :                 return MERROR;
    2445           0 :         prepareQuery(hdl, cmd);
    2446           0 :         hdl->template = strdup(hdl->query);
    2447           0 :         assert(hdl->template);
    2448           0 :         return hdl->mid->error;
    2449             : }
    2450             : 
    2451             : MapiHdl
    2452           0 : mapi_prepare(Mapi mid, const char *cmd)
    2453             : {
    2454           0 :         MapiHdl hdl;
    2455             : 
    2456           0 :         mapi_check0(mid);
    2457           0 :         hdl = mapi_new_handle(mid);
    2458           0 :         if (hdl == NULL)
    2459             :                 return NULL;
    2460           0 :         mapi_prepare_handle(hdl, cmd);
    2461           0 :         return hdl;
    2462             : }
    2463             : 
    2464             : /*
    2465             :  * Building the query string using replacement of values requires
    2466             :  * some care to not overflow the space allocated.
    2467             :  */
    2468             : #define checkSpace(len)                                         \
    2469             :         do {                                                    \
    2470             :                 /* note: k==strlen(hdl->query) */            \
    2471             :                 if (k+len >= lim) {                          \
    2472             :                         lim = k + len + MAPIBLKSIZE;            \
    2473             :                         char *q = realloc(hdl->query, lim);  \
    2474             :                         if (q == NULL) {                        \
    2475             :                                 free(hdl->query);            \
    2476             :                                 hdl->query = NULL;           \
    2477             :                                 return;                         \
    2478             :                         }                                       \
    2479             :                         hdl->query = q;                              \
    2480             :                 }                                               \
    2481             :         } while (0)
    2482             : 
    2483             : static void
    2484       31698 : mapi_param_store(MapiHdl hdl)
    2485             : {
    2486       31698 :         char *val, buf[MAPIBLKSIZE];
    2487       31698 :         char *p = hdl->template, *q;
    2488       31698 :         int i;
    2489       31698 :         size_t k;
    2490       31698 :         size_t lim;
    2491             : 
    2492       31698 :         if (hdl->template == 0)
    2493             :                 return;
    2494             : 
    2495           0 :         lim = strlen(hdl->template) + MAPIBLKSIZE;
    2496           0 :         REALLOC(hdl->query, lim);
    2497           0 :         if (hdl->query == NULL)
    2498             :                 return;
    2499           0 :         hdl->query[0] = 0;
    2500           0 :         k = 0;
    2501             : 
    2502           0 :         q = strchr(hdl->template, PLACEHOLDER);
    2503           0 :         i = 0;
    2504             :         /* loop invariant: k == strlen(hdl->query) */
    2505           0 :         while (q && i < hdl->maxparams) {
    2506           0 :                 if (q > p && *(q - 1) == '\\') {
    2507           0 :                         q = strchr(q + 1, PLACEHOLDER);
    2508           0 :                         continue;
    2509             :                 }
    2510             : 
    2511           0 :                 if (k + (q - p) >= lim) {
    2512           0 :                         lim += MAPIBLKSIZE;
    2513           0 :                         REALLOC(hdl->query, lim);
    2514           0 :                         if (hdl->query == NULL)
    2515             :                                 return;
    2516             :                 }
    2517           0 :                 memcpy(hdl->query + k, p, q - p);
    2518           0 :                 k += q - p;
    2519           0 :                 hdl->query[k] = 0;
    2520             : 
    2521           0 :                 if (hdl->params[i].inparam == 0) {
    2522           0 :                         char *nullstr = "NULL";
    2523           0 :                         checkSpace(5);
    2524           0 :                         if (msettings_lang_is_mal(hdl->mid->settings))
    2525           0 :                                 nullstr = "nil";
    2526           0 :                         strcpy(hdl->query + k, nullstr);
    2527             :                 } else {
    2528           0 :                         void *src = hdl->params[i].inparam;  /* abbrev */
    2529             : 
    2530           0 :                         switch (hdl->params[i].intype) {
    2531           0 :                         case MAPI_TINY:
    2532           0 :                                 checkSpace(5);
    2533           0 :                                 snprintf(hdl->query + k, lim - k, "%hhd", *(signed char *) src);
    2534           0 :                                 break;
    2535           0 :                         case MAPI_UTINY:
    2536           0 :                                 checkSpace(5);
    2537           0 :                                 snprintf(hdl->query + k, lim - k, "%hhu", *(unsigned char *) src);
    2538           0 :                                 break;
    2539           0 :                         case MAPI_SHORT:
    2540           0 :                                 checkSpace(10);
    2541           0 :                                 snprintf(hdl->query + k, lim - k, "%hd", *(short *) src);
    2542           0 :                                 break;
    2543           0 :                         case MAPI_USHORT:
    2544           0 :                                 checkSpace(10);
    2545           0 :                                 snprintf(hdl->query + k, lim - k, "%hu", *(unsigned short *) src);
    2546           0 :                                 break;
    2547           0 :                         case MAPI_INT:
    2548           0 :                                 checkSpace(20);
    2549           0 :                                 snprintf(hdl->query + k, lim - k, "%d", *(int *) src);
    2550           0 :                                 break;
    2551           0 :                         case MAPI_UINT:
    2552           0 :                                 checkSpace(20);
    2553           0 :                                 snprintf(hdl->query + k, lim - k, "%u", *(unsigned int *) src);
    2554           0 :                                 break;
    2555           0 :                         case MAPI_LONG:
    2556           0 :                                 checkSpace(20);
    2557           0 :                                 snprintf(hdl->query + k, lim - k, "%ld", *(long *) src);
    2558           0 :                                 break;
    2559           0 :                         case MAPI_ULONG:
    2560           0 :                                 checkSpace(20);
    2561           0 :                                 snprintf(hdl->query + k, lim - k, "%lu", *(unsigned long *) src);
    2562           0 :                                 break;
    2563           0 :                         case MAPI_LONGLONG:
    2564           0 :                                 checkSpace(30);
    2565           0 :                                 snprintf(hdl->query + k, lim - k, "%"PRId64, *(int64_t *) src);
    2566           0 :                                 break;
    2567           0 :                         case MAPI_ULONGLONG:
    2568           0 :                                 checkSpace(30);
    2569           0 :                                 snprintf(hdl->query + k, lim - k, "%"PRIu64, *(uint64_t *) src);
    2570           0 :                                 break;
    2571           0 :                         case MAPI_FLOAT:
    2572           0 :                                 checkSpace(30);
    2573           0 :                                 snprintf(hdl->query + k, lim - k, "%.9g", *(float *) src);
    2574           0 :                                 break;
    2575           0 :                         case MAPI_DOUBLE:
    2576           0 :                                 checkSpace(30);
    2577           0 :                                 snprintf(hdl->query + k, lim - k, "%.17g", *(double *) src);
    2578           0 :                                 break;
    2579           0 :                         case MAPI_DATE:
    2580           0 :                                 checkSpace(50);
    2581           0 :                                 snprintf(hdl->query + k, lim - k,
    2582             :                                          "DATE '%04hd-%02hu-%02hu'",
    2583           0 :                                          ((MapiDate *) src)->year,
    2584           0 :                                          ((MapiDate *) src)->month,
    2585           0 :                                          ((MapiDate *) src)->day);
    2586           0 :                                 break;
    2587           0 :                         case MAPI_TIME:
    2588           0 :                                 checkSpace(60);
    2589           0 :                                 snprintf(hdl->query + k, lim - k,
    2590             :                                          "TIME '%02hu:%02hu:%02hu'",
    2591           0 :                                          ((MapiTime *) src)->hour,
    2592           0 :                                          ((MapiTime *) src)->minute,
    2593           0 :                                          ((MapiTime *) src)->second);
    2594           0 :                                 break;
    2595           0 :                         case MAPI_DATETIME:
    2596           0 :                                 checkSpace(110);
    2597           0 :                                 snprintf(hdl->query + k, lim - k,
    2598             :                                          "TIMESTAMP '%04hd-%02hu-%02hu %02hu:%02hu:%02hu.%09u'",
    2599           0 :                                          ((MapiDateTime *) src)->year,
    2600           0 :                                          ((MapiDateTime *) src)->month,
    2601           0 :                                          ((MapiDateTime *) src)->day,
    2602           0 :                                          ((MapiDateTime *) src)->hour,
    2603           0 :                                          ((MapiDateTime *) src)->minute,
    2604           0 :                                          ((MapiDateTime *) src)->second,
    2605             :                                          ((MapiDateTime *) src)->fraction);
    2606           0 :                                 break;
    2607           0 :                         case MAPI_CHAR:
    2608           0 :                                 buf[0] = *(char *) src;
    2609           0 :                                 buf[1] = 0;
    2610           0 :                                 val = mapi_quote(buf, 1);
    2611             :                                 /* note: k==strlen(hdl->query) */
    2612           0 :                                 if (k + strlen(val) + 3 >= lim) {
    2613           0 :                                         lim = k + strlen(val) + 3 + MAPIBLKSIZE;
    2614           0 :                                         char *q = realloc(hdl->query, lim);
    2615           0 :                                         if (q == NULL) {
    2616           0 :                                                 free(hdl->query);
    2617           0 :                                                 hdl->query = NULL;
    2618           0 :                                                 free(val);
    2619           0 :                                                 return;
    2620             :                                         }
    2621           0 :                                         hdl->query = q;
    2622             :                                 }
    2623           0 :                                 snprintf(hdl->query + k, lim - k, "'%s'", val);
    2624           0 :                                 free(val);
    2625           0 :                                 break;
    2626           0 :                         case MAPI_VARCHAR:
    2627           0 :                                 val = mapi_quote((char *) src, hdl->params[i].sizeptr ? *hdl->params[i].sizeptr : -1);
    2628             :                                 /* note: k==strlen(hdl->query) */
    2629           0 :                                 if (k + strlen(val) + 3 >= lim) {
    2630           0 :                                         lim = k + strlen(val) + 3 + MAPIBLKSIZE;
    2631           0 :                                         char *q = realloc(hdl->query, lim);
    2632           0 :                                         if (q == NULL) {
    2633           0 :                                                 free(hdl->query);
    2634           0 :                                                 hdl->query = NULL;
    2635           0 :                                                 free(val);
    2636           0 :                                                 return;
    2637             :                                         }
    2638           0 :                                         hdl->query = q;
    2639             :                                 }
    2640           0 :                                 snprintf(hdl->query + k, lim - k, "'%s'", val);
    2641           0 :                                 free(val);
    2642           0 :                                 break;
    2643           0 :                         default:
    2644           0 :                                 strcpy_len(hdl->query + k, src, lim - k);
    2645           0 :                                 break;
    2646             :                         }
    2647             :                 }
    2648           0 :                 k += strlen(hdl->query + k);
    2649             : 
    2650           0 :                 i++;
    2651           0 :                 p = q + 1;
    2652           0 :                 q = strchr(p, PLACEHOLDER);
    2653             :         }
    2654           0 :         checkSpace(strlen(p) + 1);
    2655           0 :         strcpy(hdl->query + k, p);
    2656           0 :         if (hdl->mid->trace)
    2657           0 :                 printf("param_store: result=%s\n", hdl->query);
    2658             :         return;
    2659             : }
    2660             : 
    2661             : /* Read one more line from the input stream and return it.  This
    2662             :    returns a pointer into the input buffer, so the data needs to be
    2663             :    copied if it is to be retained. */
    2664             : static char *
    2665     1555421 : read_line(Mapi mid)
    2666             : {
    2667     1555421 :         char *reply;
    2668     1555421 :         char *nl;
    2669     1555421 :         char *s;                /* from where to search for newline */
    2670             : 
    2671     1555421 :         if (mid->active == NULL)
    2672             :                 return NULL;
    2673             : 
    2674             :         /* check if we need to read more blocks to get a new line */
    2675     1555421 :         mid->blk.eos = false;
    2676     1555421 :         s = mid->blk.buf + mid->blk.nxt;
    2677     1858114 :         while ((nl = strchr(s, '\n')) == NULL && !mid->blk.eos) {
    2678      302694 :                 ssize_t len;
    2679             : 
    2680      302694 :                 if (mid->blk.lim - mid->blk.end < BLOCK) {
    2681       13783 :                         int len;
    2682             : 
    2683       13783 :                         len = mid->blk.lim;
    2684       13783 :                         if (mid->blk.nxt <= BLOCK) {
    2685             :                                 /* extend space */
    2686        3247 :                                 len += BLOCK;
    2687             :                         }
    2688       13783 :                         REALLOC(mid->blk.buf, len + 1);
    2689       13783 :                         if (mid->blk.nxt > 0) {
    2690       10940 :                                 memmove(mid->blk.buf, mid->blk.buf + mid->blk.nxt, mid->blk.end - mid->blk.nxt + 1);
    2691       10940 :                                 mid->blk.end -= mid->blk.nxt;
    2692       10940 :                                 mid->blk.nxt = 0;
    2693             :                         }
    2694       13783 :                         mid->blk.lim = len;
    2695             :                 }
    2696             : 
    2697      302694 :                 s = mid->blk.buf + mid->blk.end;
    2698             : 
    2699             :                 /* fetch one more block */
    2700      302694 :                 if (mid->trace)
    2701           0 :                         printf("fetch next block: start at:%d\n", mid->blk.end);
    2702      302694 :                 len = mnstr_read(mid->from, mid->blk.buf + mid->blk.end, 1, BLOCK);
    2703      302693 :                 check_stream(mid, mid->from, "Connection terminated during read line", (mid->blk.eos = true, (char *) 0));
    2704      302693 :                 mapi_log_data(mid, "RECV", mid->blk.buf + mid->blk.end, len);
    2705      302693 :                 mid->blk.buf[mid->blk.end + len] = 0;
    2706      302693 :                 if (mid->trace) {
    2707           0 :                         printf("got next block: length:%zd\n", len);
    2708           0 :                         printf("text:%s\n", mid->blk.buf + mid->blk.end);
    2709             :                 }
    2710      302693 :                 if (len == 0) { /* add prompt */
    2711      141616 :                         if (mnstr_eof(mid->from))
    2712             :                                 return NULL;
    2713      141616 :                         if (mid->blk.end > mid->blk.nxt) {
    2714             :                                 /* add fake newline since newline was
    2715             :                                  * missing from server */
    2716           5 :                                 nl = mid->blk.buf + mid->blk.end;
    2717           5 :                                 *nl = '\n';
    2718           5 :                                 mid->blk.end++;
    2719             :                         }
    2720      141616 :                         len = 2;
    2721      141616 :                         mid->blk.buf[mid->blk.end] = PROMPTBEG;
    2722      141616 :                         mid->blk.buf[mid->blk.end + 1] = '\n';
    2723      141616 :                         mid->blk.buf[mid->blk.end + 2] = 0;
    2724             :                 }
    2725      302693 :                 mid->blk.end += (int) len;
    2726             :         }
    2727     1555420 :         if (mid->trace) {
    2728           0 :                 printf("got complete block: \n");
    2729           0 :                 printf("text:%s\n", mid->blk.buf + mid->blk.nxt);
    2730             :         }
    2731             : 
    2732             :         /* we have a complete line in the buffer */
    2733     1555420 :         assert(nl);
    2734     1555420 :         *nl++ = 0;
    2735     1555420 :         reply = mid->blk.buf + mid->blk.nxt;
    2736     1555420 :         mid->blk.nxt = (int) (nl - mid->blk.buf);
    2737             : 
    2738     1555420 :         if (mid->trace)
    2739           0 :                 printf("read_line:%s\n", reply);
    2740             :         return reply;
    2741             : }
    2742             : 
    2743             : /* set or unset the autocommit flag in the server */
    2744             : MapiMsg
    2745        1178 : mapi_setAutocommit(Mapi mid, bool autocommit)
    2746             : {
    2747        1178 :         if (msetting_bool(mid->settings, MP_AUTOCOMMIT) == autocommit)
    2748             :                 return MOK;
    2749           8 :         if (!msettings_lang_is_sql(mid->settings)) {
    2750           0 :                 mapi_setError(mid, "autocommit only supported in SQL", __func__, MERROR);
    2751           0 :                 return MERROR;
    2752             :         }
    2753           8 :         msettings_error err = msetting_set_bool(mid->settings, MP_AUTOCOMMIT, autocommit);
    2754           8 :         if (err)
    2755           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    2756           8 :         if (!mid->connected)
    2757             :                 return MOK;
    2758           8 :         if (autocommit)
    2759           1 :                 return mapi_Xcommand(mid, "auto_commit", "1");
    2760             :         else
    2761           7 :                 return mapi_Xcommand(mid, "auto_commit", "0");
    2762             : }
    2763             : 
    2764             : MapiMsg
    2765          22 : mapi_set_time_zone(Mapi mid, int time_zone)
    2766             : {
    2767          22 :         msettings_error err = msetting_set_long(mid->settings, MP_TIMEZONE, time_zone);
    2768          22 :         if (err)
    2769           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    2770          22 :         if (!mid->connected)
    2771             :                 return MOK;
    2772             : 
    2773           0 :         char buf[100];
    2774           0 :         if (time_zone < 0)
    2775           0 :                 snprintf(buf, sizeof(buf),
    2776             :                          "SET TIME ZONE INTERVAL '-%02d:%02d' HOUR TO MINUTE",
    2777           0 :                          -time_zone / 3600, (-time_zone % 3600) / 60);
    2778             :         else
    2779           0 :                 snprintf(buf, sizeof(buf),
    2780             :                          "SET TIME ZONE INTERVAL '+%02d:%02d' HOUR TO MINUTE",
    2781           0 :                          time_zone / 3600, (time_zone % 3600) / 60);
    2782             : 
    2783           0 :         MapiHdl hdl = mapi_query(mid, buf);
    2784           0 :         if (hdl == NULL)
    2785           0 :                 return mid->error;
    2786           0 :         mapi_close_handle(hdl);
    2787             : 
    2788           0 :         return MOK;
    2789             : }
    2790             : 
    2791             : MapiMsg
    2792           0 : mapi_set_columnar_protocol(Mapi mid, bool columnar_protocol)
    2793             : {
    2794           0 :         if (mid->columnar_protocol == columnar_protocol)
    2795             :                 return MOK;
    2796           0 :         mid->columnar_protocol = columnar_protocol;
    2797           0 :         if (!mid->connected)
    2798             :                 return MOK;
    2799           0 :         if (columnar_protocol)
    2800           0 :                 return mapi_Xcommand(mid, "columnar_protocol", "1");
    2801             :         else
    2802           0 :                 return mapi_Xcommand(mid, "columnar_protocol", "0");
    2803             : }
    2804             : 
    2805             : MapiMsg
    2806         171 : mapi_set_size_header(Mapi mid, bool value)
    2807             : {
    2808         171 :         if (!msettings_lang_is_sql(mid->settings)) {
    2809           0 :                 mapi_setError(mid, "size header only supported in SQL", __func__, MERROR);
    2810           0 :                 return MERROR;
    2811             :         }
    2812         171 :         if (mid->sizeheader == value)
    2813             :                 return MOK;
    2814          15 :         mid->sizeheader = value;
    2815          15 :         if (!mid->connected)
    2816             :                 return MOK;
    2817           0 :         if (value)
    2818           0 :                 return mapi_Xcommand(mid, "sizeheader", "1");
    2819             :         else
    2820           0 :                 return mapi_Xcommand(mid, "sizeheader", "0");
    2821             : }
    2822             : 
    2823             : MapiMsg
    2824           2 : mapi_release_id(Mapi mid, int id)
    2825             : {
    2826           2 :         char buf[10];
    2827             : 
    2828           2 :         if (!msettings_lang_is_sql(mid->settings)) {
    2829           0 :                 mapi_setError(mid, "release only supported in SQL", __func__, MERROR);
    2830           0 :                 return MERROR;
    2831             :         }
    2832           2 :         snprintf(buf, sizeof(buf), "%d", id);
    2833           2 :         return mapi_Xcommand(mid, "release", buf);
    2834             : }
    2835             : 
    2836             : void
    2837         166 : mapi_trace(Mapi mid, bool flag)
    2838             : {
    2839         166 :         mapi_clrError(mid);
    2840         166 :         mid->trace = flag;
    2841         166 : }
    2842             : 
    2843             : 
    2844             : static int
    2845     1256913 : slice_row(const char *reply, char *null, char ***anchorsp, size_t **lensp, int length, int endchar)
    2846             : {
    2847             :         /* This function does the actual work for splicing a real,
    2848             :            multi-column row into columns.  It skips over the first
    2849             :            character and ends at the end of the string or at endchar,
    2850             :            whichever comes first. */
    2851     1256913 :         char *start;
    2852     1256913 :         char **anchors;
    2853     1256913 :         int i;
    2854     1256913 :         size_t len;
    2855     1256913 :         size_t *lens;
    2856             : 
    2857     1256913 :         reply++;                /* skip over initial char (usually '[') */
    2858     1256913 :         i = 0;
    2859     1256913 :         anchors = length == 0 ? NULL : malloc(length * sizeof(*anchors));
    2860     1256719 :         lens = length == 0 ? NULL : malloc(length * sizeof(*lens));
    2861     4383872 :         for (;;) {
    2862     4383872 :                 if (i >= length) {
    2863       17508 :                         length = i + 1;
    2864       17508 :                         REALLOC(anchors, length);
    2865       17508 :                         REALLOC(lens, length);
    2866             :                 }
    2867     4383872 :                 if (!unquote(reply, &start, &reply, endchar, &len) && null && strcmp(start, null) == 0) {
    2868             :                         /* indicate NULL/nil with NULL pointer */
    2869      472981 :                         free(start);
    2870      472981 :                         start = NULL;
    2871      472981 :                         len = 0;
    2872             :                 }
    2873     4383872 :                 lens[i] = len;
    2874     4383872 :                 anchors[i++] = start;
    2875     4383872 :                 if (reply == NULL)
    2876             :                         break;
    2877     4383872 :                 while (*reply && isspace((unsigned char) *reply))
    2878           0 :                         reply++;
    2879     4383872 :                 if (*reply == ',') {
    2880     3126955 :                         reply++;
    2881     6253910 :                         while (*reply && isspace((unsigned char) *reply))
    2882     3126955 :                                 reply++;
    2883     1256917 :                 } else if (*reply == 0 || *reply == endchar)
    2884             :                         break;
    2885             :         }
    2886     1256913 :         *anchorsp = anchors;
    2887     1256913 :         *lensp = lens;
    2888     1256913 :         return i;
    2889             : }
    2890             : 
    2891             : static MapiMsg
    2892       12901 : mapi_cache_freeup_internal(struct MapiResultSet *result, int k)
    2893             : {
    2894       12901 :         int i;                  /* just a counter */
    2895       12901 :         int64_t n = 0;  /* # of tuples being deleted from front */
    2896             : 
    2897       12901 :         result->cache.tuplecount = 0;
    2898       12901 :         for (i = 0; i < result->cache.writer - k; i++) {
    2899           0 :                 if (result->cache.line[i].rows) {
    2900           0 :                         if (result->cache.line[i].rows[0] == '[' ||
    2901             :                             result->cache.line[i].rows[0] == '=')
    2902           0 :                                 n++;
    2903           0 :                         free(result->cache.line[i].rows);
    2904             :                 }
    2905           0 :                 result->cache.line[i].rows = result->cache.line[i + k].rows;
    2906           0 :                 result->cache.line[i + k].rows = 0;
    2907           0 :                 if (result->cache.line[i].anchors) {
    2908             :                         int j = 0;
    2909             : 
    2910           0 :                         for (j = 0; j < result->cache.line[i].fldcnt; j++)
    2911           0 :                                 free(result->cache.line[i].anchors[j]);
    2912           0 :                         free(result->cache.line[i].anchors);
    2913             :                 }
    2914           0 :                 if (result->cache.line[i].lens)
    2915           0 :                         free(result->cache.line[i].lens);
    2916           0 :                 result->cache.line[i].anchors = result->cache.line[i + k].anchors;
    2917           0 :                 result->cache.line[i + k].anchors = 0;
    2918           0 :                 result->cache.line[i].lens = result->cache.line[i + k].lens;
    2919           0 :                 result->cache.line[i + k].lens = 0;
    2920           0 :                 result->cache.line[i].fldcnt = result->cache.line[i + k].fldcnt;
    2921           0 :                 if (result->cache.line[i].rows &&
    2922           0 :                     (result->cache.line[i].rows[0] == '[' ||
    2923             :                      result->cache.line[i].rows[0] == '=')) {
    2924           0 :                         result->cache.line[i].tuplerev = result->cache.tuplecount;
    2925           0 :                         result->cache.line[result->cache.tuplecount++].tupleindex = i;
    2926             :                 }
    2927             :         }
    2928             :         /* after the previous loop, i == result->cache.writer - k, and
    2929             :            the last (result->cache.writer - k) cache entries have been
    2930             :            cleared already , so we don't need to go the Full Monty
    2931             :            here */
    2932     1148984 :         for ( /*i = result->cache.writer - k */ ; i < k /*result->cache.writer */ ; i++) {
    2933     1136083 :                 if (result->cache.line[i].rows) {
    2934     1136083 :                         if (result->cache.line[i].rows[0] == '[' ||
    2935             :                             result->cache.line[i].rows[0] == '=')
    2936     1135383 :                                 n++;
    2937     1136083 :                         free(result->cache.line[i].rows);
    2938             :                 }
    2939     1136083 :                 result->cache.line[i].rows = 0;
    2940     1136083 :                 if (result->cache.line[i].anchors) {
    2941             :                         int j = 0;
    2942             : 
    2943     5212495 :                         for (j = 0; j < result->cache.line[i].fldcnt; j++)
    2944     4077112 :                                 free(result->cache.line[i].anchors[j]);
    2945     1135383 :                         free(result->cache.line[i].anchors);
    2946             :                 }
    2947     1136083 :                 if (result->cache.line[i].lens)
    2948     1135383 :                         free(result->cache.line[i].lens);
    2949     1136083 :                 result->cache.line[i].anchors = 0;
    2950     1136083 :                 result->cache.line[i].lens = 0;
    2951     1136083 :                 result->cache.line[i].fldcnt = 0;
    2952             :         }
    2953       12901 :         result->cache.reader -= k;
    2954       12901 :         if (result->cache.reader < 0)
    2955       12901 :                 result->cache.reader = -1;
    2956       12901 :         result->cache.writer -= k;
    2957       12901 :         if (result->cache.writer < 0)     /* "cannot happen" */
    2958           0 :                 result->cache.writer = 0;
    2959       12901 :         result->cache.first += n;
    2960             : 
    2961       12901 :         return MOK;
    2962             : }
    2963             : 
    2964             : static void
    2965       39234 : mapi_extend_cache(struct MapiResultSet *result, int cacheall)
    2966             : {
    2967       39234 :         int incr, newsize, oldsize = result->cache.limit, i;
    2968             : 
    2969             :         /* if there are read entries, delete them */
    2970       39234 :         if (result->cache.reader >= 0) {
    2971       11331 :                 mapi_cache_freeup_internal(result, result->cache.reader + 1);
    2972             :                 /* since we've made space, we can return */
    2973       11331 :                 return;
    2974             :         }
    2975             : 
    2976             :         /* extend row cache */
    2977       27903 :   retry:;
    2978       27905 :         if (oldsize == 0)
    2979             :                 incr = 100;
    2980             :         else
    2981           9 :                 incr = oldsize * 2;
    2982           9 :         if (incr > 200000)
    2983           0 :                 incr = 20000;
    2984       27905 :         newsize = oldsize + incr;
    2985       27905 :         if (result->cache.rowlimit > 0 &&
    2986           4 :             newsize > result->cache.rowlimit &&
    2987             :             !cacheall) {
    2988           4 :                 newsize = result->cache.rowlimit;
    2989           4 :                 incr = newsize - oldsize;
    2990           4 :                 if (incr <= 0) {
    2991             :                         /* not enough space, so increase limit and try again */
    2992           2 :                         result->cache.rowlimit += 100;
    2993           2 :                         goto retry;
    2994             :                 }
    2995             :         }
    2996             : 
    2997       27903 :         REALLOC(result->cache.line, newsize + 1);
    2998       27903 :         assert(result->cache.line);
    2999     2848218 :         for (i = oldsize; i <= newsize; i++) {
    3000     2820315 :                 result->cache.line[i].fldcnt = 0;
    3001     2820315 :                 result->cache.line[i].rows = NULL;
    3002     2820315 :                 result->cache.line[i].tupleindex = -1;
    3003     2820315 :                 result->cache.line[i].tuplerev = -1;
    3004     2820315 :                 result->cache.line[i].anchors = NULL;
    3005     2820315 :                 result->cache.line[i].lens = NULL;
    3006             :         }
    3007       27903 :         result->cache.limit = newsize;
    3008             : }
    3009             : 
    3010             : /* store a line in the cache */
    3011             : static void
    3012     1278126 : add_cache(struct MapiResultSet *result, char *line, int cacheall)
    3013             : {
    3014             :         /* manage the row cache space first */
    3015     1278126 :         if (result->cache.writer >= result->cache.limit)
    3016       39234 :                 mapi_extend_cache(result, cacheall);
    3017             : 
    3018     1278126 :         result->cache.line[result->cache.writer].rows = line;
    3019     1278126 :         result->cache.line[result->cache.writer].tuplerev = result->cache.tuplecount;
    3020     1278126 :         result->cache.line[result->cache.writer + 1].tuplerev = result->cache.tuplecount + 1;
    3021     1278126 :         if (*line == '[' || *line == '=') {
    3022     1167214 :                 result->cache.line[result->cache.tuplecount++].tupleindex = result->cache.writer;
    3023     1167214 :                 if (result->row_count < result->cache.first + result->cache.tuplecount)
    3024         206 :                         result->row_count = result->cache.first + result->cache.tuplecount;
    3025             :         }
    3026     1278126 :         result->cache.writer++;
    3027     1278126 : }
    3028             : 
    3029             : static struct MapiResultSet *
    3030      141543 : parse_header_line(MapiHdl hdl, char *line, struct MapiResultSet *result)
    3031             : {
    3032      141543 :         char *tag, *etag;
    3033      141543 :         int i, n;
    3034      141543 :         char **anchors;
    3035      141543 :         size_t *lens;
    3036             : 
    3037      141543 :         if (line[0] == '&') {
    3038       30631 :                 char *nline = line;
    3039       30631 :                 int qt;
    3040       30631 :                 uint64_t queryid;
    3041             : 
    3042             :                 /* handle fields &qt */
    3043             : 
    3044       30631 :                 nline++;        /* query type */
    3045       30631 :                 qt = (int) strtol(nline, &nline, 0);
    3046             : 
    3047       30631 :                 if (result == NULL || (qt != Q_BLOCK && !result->commentonly))
    3048       30612 :                         result = new_result(hdl);
    3049       30631 :                 result->querytype = qt;
    3050       30631 :                 result->commentonly = false;
    3051       30631 :                 result->querytime = 0;
    3052       30631 :                 result->maloptimizertime = 0;
    3053       30631 :                 result->sqloptimizertime = 0;
    3054             : 
    3055       30631 :                 nline++;        /* skip space */
    3056       30631 :                 switch (qt) {
    3057         484 :                 case Q_SCHEMA:
    3058         484 :                         result->querytime = strtoll(nline, &nline, 10);
    3059         484 :                         result->maloptimizertime = strtoll(nline, &nline, 10);
    3060         484 :                         result->sqloptimizertime = strtoll(nline, &nline, 10);
    3061         484 :                         break;
    3062         222 :                 case Q_TRANS:
    3063         222 :                         msetting_set_bool(hdl->mid->settings, MP_AUTOCOMMIT, *nline != 'f');
    3064         222 :                         break;
    3065        2209 :                 case Q_UPDATE:
    3066        2209 :                         result->row_count = strtoll(nline, &nline, 10);
    3067        2209 :                         result->last_id = strtoll(nline, &nline, 10);
    3068        2209 :                         queryid = strtoll(nline, &nline, 10);
    3069        2209 :                         result->querytime = strtoll(nline, &nline, 10);
    3070        2209 :                         result->maloptimizertime = strtoll(nline, &nline, 10);
    3071        2209 :                         result->sqloptimizertime = strtoll(nline, &nline, 10);
    3072        2209 :                         break;
    3073       27575 :                 case Q_TABLE:
    3074       27575 :                         if (sscanf(nline,
    3075             :                                    "%d %" SCNd64 " %d %" SCNd64 " %" SCNu64
    3076             :                                    " %" SCNd64 " %" SCNd64 " %" SCNd64,
    3077             :                                    &result->tableid, &result->row_count,
    3078             :                                    &result->fieldcnt, &result->tuple_count,
    3079             :                                    &queryid, &result->querytime,
    3080             :                                    &result->maloptimizertime,
    3081             :                                    &result->sqloptimizertime) < 8){
    3082           2 :                                 result->querytime = 0;
    3083           2 :                                 result->maloptimizertime = 0;
    3084           2 :                                 result->sqloptimizertime = 0;
    3085             :                         }
    3086             :                         (void) queryid; /* ignored for now */
    3087             :                         break;
    3088         122 :                 case Q_PREPARE:
    3089         122 :                         sscanf(nline, "%d %" SCNd64 " %d %" SCNd64,
    3090             :                                &result->tableid, &result->row_count,
    3091             :                                &result->fieldcnt, &result->tuple_count);
    3092         122 :                         break;
    3093          19 :                 case Q_BLOCK:
    3094             :                         /* Mapi ignores the Q_BLOCK header, so spoof
    3095             :                          * the querytype back to a Q_TABLE to let it
    3096             :                          * go unnoticed */
    3097          19 :                         result->querytype = Q_TABLE;
    3098          19 :                         break;
    3099             :                 }
    3100             : 
    3101             : 
    3102       30631 :                 if (result->fieldcnt > result->maxfields) {
    3103       27697 :                         REALLOC(result->fields, result->fieldcnt);
    3104       27697 :                         memset(result->fields + result->maxfields, 0, (result->fieldcnt - result->maxfields) * sizeof(*result->fields));
    3105       27697 :                         result->maxfields = result->fieldcnt;
    3106             :                 }
    3107             : 
    3108             :                 /* start of new SQL result */
    3109       30631 :                 return result;
    3110             :         }
    3111      110912 :         if (result == NULL)
    3112           4 :                 result = new_result(hdl);
    3113             : 
    3114      110912 :         if (line[0] == '#' && !msettings_lang_is_mal(hdl->mid->settings)) {
    3115             :                 /* comment */
    3116             :                 return result;
    3117             :         }
    3118             : 
    3119      110912 :         line = strdup(line);    /* make copy we can play with */
    3120      110912 :         etag = strrchr(line, '#');
    3121      110912 :         if (etag == 0 || etag == line) {
    3122             :                 /* not a useful header line */
    3123           0 :                 free(line);
    3124           0 :                 return result;
    3125             :         }
    3126             : 
    3127      110912 :         n = slice_row(line, NULL, &anchors, &lens, 10, '#');
    3128             : 
    3129      110912 :         result->commentonly = false;
    3130             : 
    3131      110912 :         tag = etag + 1;
    3132      221816 :         while (*tag && isspace((unsigned char) *tag))
    3133      110904 :                 tag++;
    3134             : 
    3135      110912 :         if (n > result->fieldcnt) {
    3136           8 :                 result->fieldcnt = n;
    3137           8 :                 if (n > result->maxfields) {
    3138           8 :                         REALLOC(result->fields, n);
    3139           8 :                         memset(result->fields + result->maxfields, 0, (n - result->maxfields) * sizeof(*result->fields));
    3140           8 :                         result->maxfields = n;
    3141             :                 }
    3142             :         }
    3143             : 
    3144      110912 :         if (strcmp(tag, "name") == 0) {
    3145       27697 :                 result->fieldcnt = n;
    3146       85247 :                 for (i = 0; i < n; i++) {
    3147       57550 :                         if (anchors[i]) {
    3148       57550 :                                 if (result->fields[i].columnname)
    3149           0 :                                         free(result->fields[i].columnname);
    3150       57550 :                                 result->fields[i].columnname = anchors[i];
    3151       57550 :                                 anchors[i] = NULL;
    3152             :                         }
    3153             :                 }
    3154       83215 :         } else if (strcmp(tag, "type") == 0) {
    3155       27701 :                 result->fieldcnt = n;
    3156       85259 :                 for (i = 0; i < n; i++) {
    3157       57558 :                         if (anchors[i]) {
    3158       57558 :                                 if (result->fields[i].columntype)
    3159           0 :                                         free(result->fields[i].columntype);
    3160       57558 :                                 result->fields[i].columntype = anchors[i];
    3161       57558 :                                 anchors[i] = NULL;
    3162             :                         }
    3163             :                 }
    3164       55514 :         } else if (strcmp(tag, "length") == 0) {
    3165       27697 :                 result->fieldcnt = n;
    3166       85247 :                 for (i = 0; i < n; i++) {
    3167       57550 :                         if (anchors[i])
    3168       57550 :                                 result->fields[i].columnlength = atoi(anchors[i]);
    3169             :                 }
    3170       27817 :         } else if (strcmp(tag, "table_name") == 0) {
    3171       27697 :                 result->fieldcnt = n;
    3172       85247 :                 for (i = 0; i < n; i++) {
    3173       57550 :                         if (anchors[i]) {
    3174       57550 :                                 if (result->fields[i].tablename)
    3175           0 :                                         free(result->fields[i].tablename);
    3176       57550 :                                 result->fields[i].tablename = anchors[i];
    3177       57550 :                                 anchors[i] = NULL;
    3178             :                         }
    3179             :                 }
    3180         120 :         } else if (strcmp(tag, "typesizes") == 0) {
    3181         112 :                 result->fieldcnt = n;
    3182        1073 :                 for (i = 0; i < n; i++) {
    3183         961 :                         if (anchors[i]) {
    3184         961 :                                 char *p;
    3185         961 :                                 result->fields[i].digits = atoi(anchors[i]);
    3186         961 :                                 p = strchr(anchors[i], ' ');
    3187         961 :                                 if (p)
    3188         961 :                                         result->fields[i].scale = atoi(p + 1);
    3189             :                         }
    3190             :                 }
    3191             :         }
    3192             : 
    3193             :         /* clean up */
    3194      110912 :         free(line);
    3195      342089 :         for (i = 0; i < n; i++)
    3196      231177 :                 if (anchors[i])
    3197       58519 :                         free(anchors[i]);
    3198      110912 :         free(anchors);
    3199      110912 :         free(lens);
    3200             : 
    3201      110912 :         return result;
    3202             : }
    3203             : 
    3204             : static void
    3205          75 : write_file(MapiHdl hdl, char *filename, bool binary)
    3206             : {
    3207          75 :         Mapi mid = hdl->mid;
    3208          75 :         char *line;
    3209          75 :         char data[BLOCK];
    3210          75 :         ssize_t len;
    3211             : 
    3212          75 :         (void) read_line(mid);  /* read flush marker */
    3213          75 :         if (filename == NULL) {
    3214             :                 /* malloc failure */
    3215           0 :                 mnstr_printf(mid->to, "!HY001!allocation failure\n");
    3216           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3217           0 :                 return;
    3218             :         }
    3219          75 :         if (mid->putfilecontent == NULL) {
    3220           0 :                 free(filename);
    3221           0 :                 mnstr_printf(mid->to, "!HY000!cannot send files\n");
    3222           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3223           0 :                 return;
    3224             :         }
    3225          75 :         line = mid->putfilecontent(mid->filecontentprivate, filename, binary, NULL, 0);
    3226          75 :         free(filename);
    3227          75 :         if (line != NULL) {
    3228           0 :                 if (strchr(line, '\n'))
    3229           0 :                         line = "incorrect response from application";
    3230           0 :                 mnstr_printf(mid->to, "!HY000!%.64s\n", line);
    3231           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3232           0 :                 return;
    3233             :         }
    3234          75 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3235      180886 :         while ((len = mnstr_read(mid->from, data, 1, sizeof(data))) > 0) {
    3236      180736 :                 if (line == NULL)
    3237      180736 :                         line = mid->putfilecontent(mid->filecontentprivate,
    3238             :                                                    NULL, binary, data, len);
    3239             :         }
    3240          75 :         if (line == NULL)
    3241          75 :                 line = mid->putfilecontent(mid->filecontentprivate,
    3242             :                                            NULL, binary, NULL, 0);
    3243          75 :         if (line && strchr(line, '\n'))
    3244             :                 line = "incorrect response from application";
    3245          75 :         mnstr_printf(mid->to, "%s\n", line ? line : "");
    3246          75 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3247             : }
    3248             : 
    3249             : #define MiB     (1 << 20) /* a megabyte */
    3250             : 
    3251             : static void
    3252          85 : read_file(MapiHdl hdl, uint64_t off, char *filename, bool binary)
    3253             : {
    3254          85 :         Mapi mid = hdl->mid;
    3255          85 :         size_t size = 0, flushsize = 0;
    3256          85 :         char *data, *line;
    3257             : 
    3258          85 :         (void) read_line(mid);  /* read flush marker */
    3259          85 :         if (filename == NULL) {
    3260             :                 /* malloc failure */
    3261           0 :                 mnstr_printf(mid->to, "!HY001!allocation failure\n");
    3262           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3263           1 :                 return;
    3264             :         }
    3265          85 :         if (mid->getfilecontent == NULL) {
    3266           0 :                 free(filename);
    3267           0 :                 mnstr_printf(mid->to, "!HY000!cannot retrieve files\n");
    3268           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3269           0 :                 return;
    3270             :         }
    3271          85 :         data = mid->getfilecontent(mid->filecontentprivate, filename, binary,
    3272             :                                    off, &size);
    3273          85 :         free(filename);
    3274          85 :         if (data != NULL && size == 0) {
    3275           0 :                 if (strchr(data, '\n'))
    3276           0 :                         data = "incorrect response from application";
    3277           0 :                 mnstr_printf(mid->to, "!HY000!%.64s\n", data);
    3278           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3279           0 :                 return;
    3280             :         }
    3281          85 :         mnstr_printf(mid->to, "\n");
    3282       23479 :         while (data != NULL && size != 0) {
    3283       23395 :                 if (flushsize >= MiB) {
    3284             :                         /* after every MiB give the server the
    3285             :                          * opportunity to stop reading more data */
    3286        1398 :                         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3287             :                         /* at this point we expect to get a PROMPT2 if
    3288             :                          * the server wants more data, or a PROMPT3 if
    3289             :                          * the server had enough; anything else is a
    3290             :                          * protocol violation */
    3291        1398 :                         line = read_line(mid);
    3292        1398 :                         if (line == NULL) {
    3293             :                                 /* error */
    3294           0 :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3295           0 :                                 return;
    3296             :                         }
    3297        1398 :                         assert(line[0] == PROMPTBEG);
    3298        1398 :                         if (line[0] != PROMPTBEG) {
    3299             :                                 /* error in protocol */
    3300             :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3301             :                                 return;
    3302             :                         }
    3303        1398 :                         if (line[1] == PROMPT3[1]) {
    3304             :                                 /* done reading: close file */
    3305           1 :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3306           1 :                                 (void) read_line(mid);
    3307           1 :                                 return;
    3308             :                         }
    3309        1397 :                         assert(line[1] == PROMPT2[1]);
    3310        1397 :                         if (line[1] != PROMPT2[1]) {
    3311             :                                 /* error  in protocol */
    3312             :                                 (void) mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, NULL);
    3313             :                                 return;
    3314             :                         }
    3315             :                         /* clear the flush marker */
    3316        1397 :                         (void) read_line(mid);
    3317        1397 :                         flushsize = 0;
    3318             :                 }
    3319       23394 :                 if (size > MiB) {
    3320           0 :                         if (mnstr_write(mid->to, data, 1, MiB) != MiB) {
    3321           0 :                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3322           0 :                                 return;
    3323             :                         }
    3324           0 :                         size -= MiB;
    3325           0 :                         data += MiB;
    3326           0 :                         flushsize += MiB;
    3327             :                 } else {
    3328       23394 :                         if (mnstr_write(mid->to, data, 1, size) != (ssize_t) size) {
    3329           0 :                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3330           0 :                                 return;
    3331             :                         }
    3332       23394 :                         flushsize += size;
    3333       23394 :                         data = mid->getfilecontent(mid->filecontentprivate, NULL, false, 0, &size);
    3334             :                 }
    3335             :         }
    3336          84 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3337          84 :         line = read_line(mid);
    3338          84 :         if (line == NULL)
    3339             :                 return;
    3340          84 :         assert(line[0] == PROMPTBEG);
    3341          84 :         if (line[0] != PROMPTBEG)
    3342             :                 return;
    3343          84 :         if (line[1] == PROMPT3[1]) {
    3344           0 :                 (void) read_line(mid);
    3345           0 :                 return;
    3346             :         }
    3347          84 :         assert(line[1] == PROMPT2[1]);
    3348          84 :         if (line[1] != PROMPT2[1])
    3349             :                 return;
    3350          84 :         (void) read_line(mid);
    3351          84 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3352          84 :         line = read_line(mid);
    3353          84 :         if (line == NULL)
    3354             :                 return;
    3355          84 :         assert(line[0] == PROMPTBEG);
    3356          84 :         assert(line[1] == PROMPT3[1]);
    3357          84 :         (void) read_line(mid);
    3358             : }
    3359             : 
    3360             : /* Read ahead and cache data read.  Depending on the second argument,
    3361             :    reading may stop at the first non-header and non-error line, or at
    3362             :    a prompt.
    3363             :    This function is called either after a command has been sent to the
    3364             :    server (in which case the second argument is 1), when the
    3365             :    application asks for a result tuple that hadn't been cached yet (in
    3366             :    which case the second argument is also 1), or whenever all pending
    3367             :    data needs to be read in order to send a new command to the server
    3368             :    (in which case the second argument is 0).
    3369             :    Header lines result tuples are stored in the cache.  Certain header
    3370             :    lines may cause a new result set to be created in which case all
    3371             :    subsequent lines are added to that result set.
    3372             : */
    3373             : MapiMsg
    3374     1304876 : read_into_cache(MapiHdl hdl, int lookahead)
    3375             : {
    3376     1304876 :         char *line;
    3377     1304876 :         Mapi mid;
    3378     1304876 :         struct MapiResultSet *result;
    3379             : 
    3380     1304876 :         mid = hdl->mid;
    3381     1304876 :         assert(mid->active == hdl);
    3382     1304876 :         if (hdl->needmore) {
    3383           0 :                 hdl->needmore = false;
    3384           0 :                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3385           0 :                 check_stream(mid, mid->to, "write error on stream", mid->error);
    3386             :         }
    3387     1304876 :         if ((result = hdl->active) == NULL)
    3388      139871 :                 result = hdl->result;        /* may also be NULL */
    3389     1448887 :         for (;;) {
    3390     1448887 :                 line = read_line(mid);
    3391     1448887 :                 if (line == NULL) {
    3392           0 :                         if (mid->from && mnstr_eof(mid->from)) {
    3393           0 :                                 return mapi_setError(mid, "unexpected end of file", __func__, MTIMEOUT);
    3394             :                         }
    3395           0 :                         return mid->error;
    3396             :                 }
    3397     1448887 :                 switch (*line) {
    3398      140050 :                 case PROMPTBEG: /* \001 */
    3399      140050 :                         mid->active = NULL;
    3400      140050 :                         hdl->active = NULL;
    3401             :                         /* set needmore flag if line equals PROMPT2 up
    3402             :                            to newline */
    3403      140050 :                         if (line[1] == PROMPT2[1] && line[2] == '\0') {
    3404             :                                 /* skip end of block */
    3405      103082 :                                 mid->active = hdl;
    3406      103082 :                                 (void) read_line(mid);
    3407      103082 :                                 hdl->needmore = true;
    3408      103082 :                                 mid->active = hdl;
    3409       36968 :                         } else if (line[1] == PROMPT3[1] && line[2] == '\0') {
    3410         160 :                                 mid->active = hdl;
    3411         160 :                                 line = read_line(mid);
    3412         160 :                                 bool binary = false;
    3413             :                                 /* rb FILE
    3414             :                                  * r OFF FILE
    3415             :                                  * w FILE
    3416             :                                  * wb FILE
    3417             :                                  */
    3418         160 :                                 switch (*line++) {
    3419          85 :                                 case 'r': {
    3420          85 :                                         uint64_t off = 0;
    3421          85 :                                         if (*line == 'b') {
    3422          85 :                                                 line++;
    3423          85 :                                                 binary = true;
    3424             :                                         } else {
    3425           0 :                                                 off = strtoul(line, &line, 10);
    3426             :                                         }
    3427          85 :                                         if (*line++ != ' ') {
    3428           0 :                                                 mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
    3429           0 :                                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3430           0 :                                                 break;
    3431             :                                         }
    3432          85 :                                         read_file(hdl, off, strdup(line), binary);
    3433          85 :                                         break;
    3434             :                                 }
    3435          75 :                                 case 'w':
    3436          75 :                                         if (*line == 'b') {
    3437          75 :                                                 line++;
    3438          75 :                                                 binary = true;
    3439             :                                         }
    3440          75 :                                         if (*line++ != ' ') {
    3441           0 :                                                 mnstr_printf(mid->to, "!HY000!unrecognized command from server\n");
    3442           0 :                                                 mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3443           0 :                                                 break;
    3444             :                                         }
    3445          75 :                                         write_file(hdl, strdup(line), binary);
    3446          75 :                                         break;
    3447             :                                 }
    3448         160 :                                 continue;
    3449             :                         }
    3450      139890 :                         return mid->error;
    3451          80 :                 case '!':
    3452             :                         /* start a new result set if we don't have one
    3453             :                            yet (duh!), or if we've already seen
    3454             :                            normal output for the current one */
    3455          80 :                         if (result == NULL ||
    3456           2 :                             result->cache.writer > 0 ||
    3457           2 :                             result->querytype > 0) {
    3458          78 :                                 result = new_result(hdl);
    3459          78 :                                 result->commentonly = false;
    3460          78 :                                 hdl->active = result;
    3461             :                         }
    3462          80 :                         add_error(result, line + 1 /* skip ! */ );
    3463          80 :                         if (!mid->error)
    3464          78 :                                 mid->error = MSERVER;
    3465             :                         break;
    3466      141543 :                 case '%':
    3467             :                 case '#':
    3468             :                 case '&':
    3469      141543 :                         if (lookahead < 0)
    3470           0 :                                 lookahead = 1;
    3471      141543 :                         result = parse_header_line(hdl, line, result);
    3472      141543 :                         hdl->active = result;
    3473      141543 :                         if (result && *line != '&')
    3474      110912 :                                 add_cache(result, strdup(line), !lookahead);
    3475             :                         break;
    3476     1167214 :                 default:
    3477     1167214 :                         if (result == NULL) {
    3478         195 :                                 result = new_result(hdl);
    3479         195 :                                 hdl->active = result;
    3480             :                         }
    3481     1167214 :                         add_cache(result, strdup(line), !lookahead);
    3482     1167214 :                         if (lookahead > 0 &&
    3483     1165592 :                             (result->querytype == -1 /* unknown (not SQL) */ ||
    3484         606 :                              result->querytype == Q_TABLE ||
    3485             :                              result->querytype == Q_UPDATE))
    3486     1164986 :                                 return mid->error;
    3487             :                         break;
    3488             :                 }
    3489             :         }
    3490             : }
    3491             : 
    3492             : static MapiMsg
    3493       31698 : mapi_execute_internal(MapiHdl hdl)
    3494             : {
    3495       31698 :         size_t size;
    3496       31698 :         char *cmd;
    3497       31698 :         Mapi mid;
    3498             : 
    3499       31698 :         mid = hdl->mid;
    3500       31698 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    3501             :                 return MERROR;
    3502       31698 :         assert(mid->active == NULL);
    3503       31698 :         finish_handle(hdl);
    3504       31698 :         mapi_param_store(hdl);
    3505       31698 :         cmd = hdl->query;
    3506       31698 :         if (cmd == NULL)
    3507             :                 return MERROR;
    3508       31698 :         size = strlen(cmd);
    3509             : 
    3510       31698 :         bool is_sql = msettings_lang_is_sql(mid->settings);
    3511       31698 :         char *prefix = is_sql ? "s" : "";
    3512        4198 :         char *suffix = is_sql ? "\n;" : "";
    3513       31698 :         mapi_log_record(mid, "SEND", "%s%s%s", prefix, cmd, suffix);
    3514             : 
    3515       31698 :         if (is_sql) {
    3516             :                 /* indicate to server this is a SQL command */
    3517       27500 :                 mnstr_write(mid->to, "s", 1, 1);
    3518       27500 :                 check_stream(mid, mid->to, "write error on stream", mid->error);
    3519             :         }
    3520       31698 :         mnstr_write(mid->to, cmd, 1, size);
    3521       31698 :         check_stream(mid, mid->to, "write error on stream", mid->error);
    3522             :         /* all SQL statements should end with a semicolon */
    3523             :         /* for the other languages it is assumed that the statements are correct */
    3524       31698 :         if (is_sql) {
    3525       27500 :                 mnstr_write(mid->to, "\n;", 2, 1);
    3526       27500 :                 check_stream(mid, mid->to, "write error on stream", mid->error);
    3527             :         }
    3528       31698 :         mnstr_write(mid->to, "\n", 1, 1);
    3529       31698 :         check_stream(mid, mid->to, "write error on stream", mid->error);
    3530       31698 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3531       31698 :         check_stream(mid, mid->to, "write error on stream", mid->error);
    3532       31698 :         mid->active = hdl;
    3533       31698 :         return MOK;
    3534             : }
    3535             : 
    3536             : MapiMsg
    3537           0 : mapi_execute(MapiHdl hdl)
    3538             : {
    3539           0 :         int ret;
    3540             : 
    3541           0 :         mapi_hdl_check(hdl);
    3542           0 :         if ((ret = mapi_execute_internal(hdl)) == MOK)
    3543           0 :                 return read_into_cache(hdl, 1);
    3544             : 
    3545             :         return ret;
    3546             : }
    3547             : 
    3548             : /*
    3549             :  * The routine mapi_query is one of the most heavily used ones.
    3550             :  * It sends a complete statement for execution
    3551             :  * (i.e., ending in a newline; possibly including additional newlines).
    3552             :  * Interaction with the server is sped up using block based interaction.
    3553             :  * The query is retained in the Mapi structure to repeat shipping.
    3554             :  */
    3555             : MapiHdl
    3556       29588 : mapi_query(Mapi mid, const char *cmd)
    3557             : {
    3558       29588 :         int ret;
    3559       29588 :         MapiHdl hdl;
    3560             : 
    3561       29588 :         mapi_check0(mid);
    3562       29588 :         hdl = prepareQuery(mapi_new_handle(mid), cmd);
    3563       29588 :         ret = mid->error;
    3564       29588 :         if (ret == MOK)
    3565       29588 :                 ret = mapi_execute_internal(hdl);
    3566       29588 :         if (ret == MOK)
    3567       29588 :                 ret = read_into_cache(hdl, 1);
    3568             :         return hdl;
    3569             : }
    3570             : 
    3571             : /* version of mapi_query that does not wait for a response */
    3572             : MapiHdl
    3573           0 : mapi_send(Mapi mid, const char *cmd)
    3574             : {
    3575           0 :         int ret;
    3576           0 :         MapiHdl hdl;
    3577             : 
    3578           0 :         mapi_check0(mid);
    3579           0 :         hdl = prepareQuery(mapi_new_handle(mid), cmd);
    3580           0 :         ret = mid->error;
    3581           0 :         if (ret == MOK)
    3582           0 :                 ret = mapi_execute_internal(hdl);
    3583             :         return hdl;
    3584             : }
    3585             : 
    3586             : MapiMsg
    3587           0 : mapi_read_response(MapiHdl hdl)
    3588             : {
    3589           0 :         return read_into_cache(hdl, 1);
    3590             : }
    3591             : 
    3592             : MapiMsg
    3593        2110 : mapi_query_handle(MapiHdl hdl, const char *cmd)
    3594             : {
    3595        2110 :         int ret;
    3596             : 
    3597        2110 :         mapi_hdl_check(hdl);
    3598        2110 :         if (finish_handle(hdl) != MOK)
    3599             :                 return MERROR;
    3600        2110 :         prepareQuery(hdl, cmd);
    3601        2110 :         ret = hdl->mid->error;
    3602        2110 :         if (ret == MOK)
    3603        2110 :                 ret = mapi_execute_internal(hdl);
    3604        2110 :         if (ret == MOK)
    3605        2110 :                 ret = read_into_cache(hdl, 1);
    3606             :         return ret;
    3607             : }
    3608             : 
    3609             : MapiHdl
    3610        3648 : mapi_query_prep(Mapi mid)
    3611             : {
    3612        3648 :         mapi_check0(mid);
    3613        3648 :         if (mid->active && read_into_cache(mid->active, 0) != MOK)
    3614             :                 return NULL;
    3615        3648 :         assert(mid->active == NULL);
    3616        3648 :         if (msettings_lang_is_sql(mid->settings)) {
    3617             :                 /* indicate to server this is a SQL command */
    3618        3648 :                 mnstr_write(mid->to, "S", 1, 1);
    3619        3648 :                 mapi_log_data(mid, "SEND", "S", 1);
    3620             :         }
    3621        3648 :         return (mid->active = mapi_new_handle(mid));
    3622             : }
    3623             : 
    3624             : MapiMsg
    3625      106391 : mapi_query_part(MapiHdl hdl, const char *query, size_t size)
    3626             : {
    3627      106391 :         Mapi mid;
    3628             : 
    3629      106391 :         mapi_hdl_check(hdl);
    3630      106391 :         mid = hdl->mid;
    3631      106391 :         assert(mid->active == NULL || mid->active == hdl);
    3632      106391 :         mid->active = hdl;
    3633             :         /* remember the query just for the error messages */
    3634      106391 :         if (hdl->query == NULL) {
    3635        3316 :                 hdl->query = malloc(size + 1);
    3636        3316 :                 if (hdl->query) {
    3637        3316 :                         strcpy_len(hdl->query, query, size + 1);
    3638             :                 }
    3639             :         } else {
    3640      103075 :                 size_t sz = strlen(hdl->query);
    3641      103075 :                 char *q;
    3642             : 
    3643      103075 :                 if (sz < 512 &&
    3644        1506 :                     (q = realloc(hdl->query, sz + size + 1)) != NULL) {
    3645        1506 :                         strcpy_len(q + sz, query, size + 1);
    3646        1506 :                         hdl->query = q;
    3647             :                 }
    3648             :         }
    3649             : 
    3650      106391 :         if (mid->trace) {
    3651           0 :                 printf("mapi_query_part:%zu:%.*s\n", size, (int) size, query);
    3652             :         }
    3653      106391 :         hdl->needmore = false;
    3654      106391 :         mnstr_write(mid->to, query, 1, size);
    3655      106391 :         if (mid->tracelog) {
    3656           0 :                 mnstr_write(mid->tracelog, query, 1, size);
    3657           0 :                 mnstr_flush(mid->tracelog, MNSTR_FLUSH_DATA);
    3658             :         }
    3659      106391 :         check_stream(mid, mid->to, "write error on stream", mid->error);
    3660      106391 :         return mid->error;
    3661             : }
    3662             : 
    3663             : MapiMsg
    3664      106730 : mapi_query_done(MapiHdl hdl)
    3665             : {
    3666      106730 :         int ret;
    3667      106730 :         Mapi mid;
    3668             : 
    3669      106730 :         mapi_hdl_check(hdl);
    3670      106730 :         mid = hdl->mid;
    3671      106730 :         assert(mid->active == NULL || mid->active == hdl);
    3672      106730 :         mid->active = hdl;
    3673      106730 :         hdl->needmore = false;
    3674      106730 :         mnstr_flush(mid->to, MNSTR_FLUSH_DATA);
    3675      106730 :         check_stream(mid, mid->to, "write error on stream", mid->error);
    3676      106730 :         ret = mid->error;
    3677      106730 :         if (ret == MOK)
    3678      106730 :                 ret = read_into_cache(hdl, 1);
    3679      106730 :         return ret == MOK && hdl->needmore ? MMORE : ret;
    3680             : }
    3681             : 
    3682             : MapiMsg
    3683         204 : mapi_cache_limit(Mapi mid, int limit)
    3684             : {
    3685             :         /* clean out superflous space TODO */
    3686         204 :         msettings_error err = msetting_set_long(mid->settings, MP_REPLYSIZE, limit);
    3687         204 :         if (err)
    3688           0 :                 return mapi_setError(mid, err, __func__, MERROR);
    3689         204 :         if (!mid->connected)
    3690             :                 return MOK;
    3691          39 :         mapi_check(mid);
    3692             : /*      if (hdl->cache.rowlimit < hdl->cache.limit) { */
    3693             :         /* TODO: decide what to do here */
    3694             :         /*              hdl->cache.limit = hdl->cache.rowlimit; *//* arbitrarily throw away cache lines */
    3695             : /*              if (hdl->cache.writer > hdl->cache.limit) { */
    3696             : /*                      hdl->cache.writer = hdl->cache.limit; */
    3697             : /*                      if (hdl->cache.reader > hdl->cache.writer) */
    3698             : /*                              hdl->cache.reader = hdl->cache.writer; */
    3699             : /*              } */
    3700             : /*      } */
    3701          39 :         if (msettings_lang_is_sql(mid->settings)) {
    3702          39 :                 MapiHdl hdl;
    3703             : 
    3704          39 :                 if (mid->active)
    3705           1 :                         read_into_cache(mid->active, 0);
    3706             : 
    3707          39 :                 mapi_log_record(mid, "X", "X" "reply_size %d\n", limit);
    3708          78 :                 if (mnstr_printf(mid->to, "X" "reply_size %d\n", limit) < 0 ||
    3709          39 :                     mnstr_flush(mid->to, MNSTR_FLUSH_DATA)) {
    3710           0 :                         close_connection(mid);
    3711           0 :                         mapi_setError(mid, mnstr_peek_error(mid->to), __func__, MTIMEOUT);
    3712           0 :                         return MERROR;
    3713             :                 }
    3714          39 :                 hdl = prepareQuery(mapi_new_handle(mid), "reply_size");
    3715          39 :                 if (hdl == NULL)
    3716             :                         return MERROR;
    3717          39 :                 mid->active = hdl;
    3718          39 :                 read_into_cache(hdl, 0);
    3719          39 :                 mapi_close_handle(hdl); /* reads away any output */
    3720             :         }
    3721             :         return MOK;
    3722             : }
    3723             : 
    3724             : MapiMsg
    3725           0 : mapi_fetch_reset(MapiHdl hdl)
    3726             : {
    3727           0 :         mapi_hdl_check(hdl);
    3728           0 :         if (hdl->result)
    3729           0 :                 hdl->result->cache.reader = -1;
    3730             :         return MOK;
    3731             : }
    3732             : 
    3733             : MapiMsg
    3734        2655 : mapi_seek_row(MapiHdl hdl, int64_t rownr, int whence)
    3735             : {
    3736        2655 :         struct MapiResultSet *result;
    3737             : 
    3738        2655 :         mapi_hdl_check(hdl);
    3739        2655 :         result = hdl->result;
    3740        2655 :         switch (whence) {
    3741             :         case MAPI_SEEK_SET:
    3742             :                 break;
    3743           0 :         case MAPI_SEEK_CUR:
    3744           0 :                 rownr += result->cache.line[result->cache.reader + 1].tuplerev;
    3745           0 :                 break;
    3746           0 :         case MAPI_SEEK_END:
    3747           0 :                 if (hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
    3748             :                         return MERROR;
    3749           0 :                 rownr += result->row_count;
    3750           0 :                 break;
    3751           0 :         default:
    3752           0 :                 return mapi_setError(hdl->mid, "Illegal whence value", __func__, MERROR);
    3753             :         }
    3754        2655 :         if (rownr > result->row_count && hdl->mid->active && read_into_cache(hdl->mid->active, 0) != MOK)
    3755             :                 return MERROR;
    3756        2655 :         if (rownr < 0 || rownr > result->row_count)
    3757           0 :                 return mapi_setError(hdl->mid, "Illegal row number", __func__, MERROR);
    3758        2655 :         if (result->cache.first <= rownr && rownr < result->cache.first + result->cache.tuplecount) {
    3759             :                 /* we've got the requested tuple in the cache */
    3760        1085 :                 result->cache.reader = result->cache.line[rownr - result->cache.first].tupleindex - 1;
    3761             :         } else {
    3762             :                 /* we don't have the requested tuple in the cache
    3763             :                    reset the cache and at the next fetch we'll get the data */
    3764        1570 :                 if (mapi_cache_freeup(hdl, 100) == MOK) {
    3765        1570 :                         result->cache.first = rownr;
    3766             :                 }
    3767             :         }
    3768        2655 :         return hdl->mid->error;
    3769             : }
    3770             : 
    3771             : /* Make space in the cache for new tuples, ignore the read pointer */
    3772             : MapiMsg
    3773        1570 : mapi_cache_freeup(MapiHdl hdl, int percentage)
    3774             : {
    3775        1570 :         struct MapiResultSet *result;
    3776        1570 :         int k;                  /* # of cache lines to be deleted from front */
    3777             : 
    3778        1570 :         mapi_hdl_check(hdl);
    3779        1570 :         result = hdl->result;
    3780        1570 :         if (result == NULL || (result->cache.writer == 0 && result->cache.reader == -1))
    3781             :                 return MOK;
    3782        1570 :         if (percentage < 0 || percentage > 100)
    3783           0 :                 percentage = 100;
    3784        1570 :         k = (result->cache.writer * percentage) / 100;
    3785        1570 :         if (k < 1)
    3786           0 :                 k = 1;
    3787        1570 :         return mapi_cache_freeup_internal(result, k);
    3788             : }
    3789             : 
    3790             : static char *
    3791     1304734 : mapi_fetch_line_internal(MapiHdl hdl)
    3792             : {
    3793     1304734 :         Mapi mid;
    3794     1304734 :         struct MapiResultSet *result;
    3795     1304734 :         char *reply;
    3796             : 
    3797             :         /* try to read a line from the cache */
    3798     1304734 :         if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer) {
    3799     1168792 :                 mid = hdl->mid;
    3800     1168792 :                 if (mid->active != hdl || hdl->needmore)
    3801             :                         return NULL;
    3802             : 
    3803     1164608 :                 if (read_into_cache(hdl, 1) != MOK)
    3804             :                         return NULL;
    3805     1164608 :                 if ((result = hdl->result) == NULL || result->cache.writer <= 0 || result->cache.reader + 1 >= result->cache.writer)
    3806             :                         return NULL;
    3807             :         }
    3808     1277176 :         reply = result->cache.line[++result->cache.reader].rows;
    3809     1277176 :         if (hdl->bindings && (*reply == '[' || *reply == '=')) {
    3810           0 :                 mapi_slice_row(result, result->cache.reader);
    3811           0 :                 mapi_store_bind(result, result->cache.reader);
    3812             :         }
    3813             :         return reply;
    3814             : }
    3815             : 
    3816             : /*
    3817             :  * The routine mapi_fetch_line forms the basic interaction with the server.
    3818             :  * It simply retrieves the next line and stores it in the row cache.
    3819             :  * The field anchor structure is prepared for subsequent use by
    3820             :  * mapi_fetch_row.
    3821             :  * The content received is analyzed further by mapi_getRow()
    3822             :  */
    3823             : char *
    3824     1304715 : mapi_fetch_line(MapiHdl hdl)
    3825             : {
    3826     1304715 :         char *reply;
    3827     1304715 :         struct MapiResultSet *result;
    3828             : 
    3829     1304715 :         mapi_hdl_check0(hdl);
    3830     1304715 :         reply = mapi_fetch_line_internal(hdl);
    3831     1304715 :         if (reply == NULL &&
    3832       55116 :             (result = hdl->result) != NULL &&
    3833       27558 :             msettings_lang_is_sql(hdl->mid->settings) &&
    3834       27546 :             result->querytype == Q_TABLE &&
    3835       27428 :             result->row_count > 0 &&
    3836       23479 :             result->cache.first + result->cache.tuplecount < result->row_count) {
    3837          19 :                 if (hdl->needmore)   /* escalate */
    3838             :                         return NULL;
    3839          19 :                 if (hdl->mid->active != NULL)
    3840           0 :                         read_into_cache(hdl->mid->active, 0);
    3841          19 :                 hdl->mid->active = hdl;
    3842          19 :                 hdl->active = result;
    3843          19 :                 mapi_log_record(hdl->mid, "W", "X" "export %d %" PRId64 "\n",
    3844             :                                      result->tableid,
    3845             :                                      result->cache.first + result->cache.tuplecount);
    3846          19 :                 if (mnstr_printf(hdl->mid->to, "X" "export %d %" PRId64 "\n",
    3847             :                                  result->tableid,
    3848          38 :                                  result->cache.first + result->cache.tuplecount) < 0 ||
    3849          19 :                     mnstr_flush(hdl->mid->to, MNSTR_FLUSH_DATA))
    3850           0 :                         check_stream(hdl->mid, hdl->mid->to, "sending export command", NULL);
    3851          19 :                 reply = mapi_fetch_line_internal(hdl);
    3852             :         }
    3853             :         return reply;
    3854             : }
    3855             : 
    3856             : /*
    3857             :  * To synchronize on a prompt, the low level routine mapi_finish can be used.
    3858             :  * It discards all output received.
    3859             :  */
    3860             : MapiMsg
    3861           0 : mapi_finish(MapiHdl hdl)
    3862             : {
    3863           0 :         mapi_hdl_check(hdl);
    3864           0 :         return finish_handle(hdl);
    3865             : }
    3866             : 
    3867             : /* msg is a string consisting comma-separated values.  The list of
    3868             :    values is terminated by endchar or by the end-of-string NULL byte.
    3869             :    Values can be quoted strings or unquoted values.  Upon return,
    3870             :    *start points to the start of the first value which is stripped of
    3871             :    leading and trailing white space, and if it was a quoted string,
    3872             :    also of the quotes.  Also, backslash-escaped characters in the
    3873             :    quoted string are replaced by the values the escapes represent.
    3874             :    *next points to either the start of the next value (i.e. after the
    3875             :    separating comma, possibly to the leading white space of the next
    3876             :    value), or to the trailing ] or NULL byte if this was the last
    3877             :    value.  *lenp is the number of bytes occupied by the (possibly
    3878             :    converted) value, excluding final NULL byte.
    3879             :    msg is *not* a const string: it is altered by this function.
    3880             :    The function returns true if the string was quoted.
    3881             : */
    3882             : static int
    3883     4383872 : unquote(const char *msg, char **str, const char **next, int endchar, size_t *lenp)
    3884             : {
    3885     4383872 :         const char *p = msg;
    3886     4383872 :         char quote;
    3887             : 
    3888             :         /* first skip over leading white space */
    3889     5640777 :         while (*p && isspace((unsigned char) *p))
    3890     1256905 :                 p++;
    3891     4383872 :         quote = *p;
    3892     4383872 :         if (quote == '\'' || quote == '"') {
    3893     3564516 :                 size_t len = 0;
    3894     3564516 :                 char *s, *start;
    3895             : 
    3896             :                 /* get quoted string and remove trailing bracket first */
    3897     3564516 :                 p++;
    3898             :                 /* first count how much space we need */
    3899     3564516 :                 msg = p;        /* save for later */
    3900   148317244 :                 while (*p && *p != quote) {
    3901   144752728 :                         if (*p == '\\') {
    3902       16844 :                                 p++;
    3903       16844 :                                 switch (*p) {
    3904           0 :                                 case '0':
    3905             :                                 case '1':
    3906             :                                 case '2':
    3907             :                                 case '3':
    3908             :                                         /* this could be the start of
    3909             :                                            an octal sequence, check it
    3910             :                                            out */
    3911           0 :                                         if (p[1] && p[2] &&
    3912           0 :                                             p[1] >= '0' && p[1] <= '7' &&
    3913           0 :                                             p[2] >= '0' && p[2] <= '7') {
    3914           0 :                                                 p += 2;
    3915           0 :                                                 break;
    3916             :                                         }
    3917             :                                         /* fall through */
    3918             :                                 default:
    3919             :                                         break;
    3920             :                                 }
    3921             :                         }
    3922   144752728 :                         p++;
    3923   144752728 :                         len++;
    3924             :                 }
    3925             :                 /* now allocate space and copy string into new space */
    3926     3564516 :                 p = msg;        /* start over */
    3927     3564516 :                 start = s = malloc(len + 1);
    3928   148317244 :                 while (*p && *p != quote) {
    3929   144752728 :                         if (*p == '\\') {
    3930       16844 :                                 p++;
    3931       16844 :                                 switch (*p) {
    3932             :                                 /* later
    3933             :                                    case '0': case '1': case '2': case '3': case '4':
    3934             :                                    case '5': case '6': case '7': case '8': case '9':
    3935             :                                 */
    3936        1607 :                                 case 'n':
    3937        1607 :                                         *s = '\n';
    3938        1607 :                                         break;
    3939          15 :                                 case 't':
    3940          15 :                                         *s = '\t';
    3941          15 :                                         break;
    3942           0 :                                 case 'r':
    3943           0 :                                         *s = '\r';
    3944           0 :                                         break;
    3945           0 :                                 case 'f':
    3946           0 :                                         *s = '\f';
    3947           0 :                                         break;
    3948           0 :                                 case '0':
    3949             :                                 case '1':
    3950             :                                 case '2':
    3951             :                                 case '3':
    3952             :                                         /* this could be the start of
    3953             :                                            an octal sequence, check it
    3954             :                                            out */
    3955           0 :                                         if (p[1] && p[2] &&
    3956           0 :                                             p[1] >= '0' && p[1] <= '7' &&
    3957           0 :                                             p[2] >= '0' && p[2] <= '7') {
    3958           0 :                                                 *s = ((p[0] - '0') << 6) | ((p[1] - '0') << 3) | (p[2] - '0');
    3959           0 :                                                 p += 2;
    3960           0 :                                                 break;
    3961             :                                         }
    3962             :                                         /* fall through */
    3963             :                                 default:
    3964       15222 :                                         *s = *p;
    3965       15222 :                                         break;
    3966             :                                 }
    3967       16844 :                                 p++;
    3968             :                         } else {
    3969   144735884 :                                 *s = *p++;
    3970             :                         }
    3971   144752728 :                         s++;
    3972             :                 }
    3973     3564516 :                 *s = 0;         /* close string */
    3974     3564516 :                 p++;            /* skip over end-of-string quote */
    3975             :                 /* skip over trailing junk (presumably white space) */
    3976     4687462 :                 while (*p && *p != ',' && *p != endchar)
    3977     1122946 :                         p++;
    3978     3564516 :                 if (next)
    3979     3564516 :                         *next = p;
    3980     3564516 :                 *str = start;
    3981     3564516 :                 if (lenp)
    3982     3564516 :                         *lenp = len;
    3983             : 
    3984     3564516 :                 return 1;
    3985             :         } else {
    3986             :                 const char *s;
    3987             :                 size_t len;
    3988             : 
    3989             :                 /* p points at first non-white space character */
    3990    47257864 :                 msg = p;        /* record start of value */
    3991             :                 /* find separator or terminator */
    3992    47257864 :                 while (*p && *p != ',' && *p != '\t' && *p != endchar)
    3993    46438508 :                         p++;
    3994             :                 /* search back over trailing white space */
    3995      930467 :                 for (s = p - 1; s > msg && isspace((unsigned char) *s); s--)
    3996             :                         ;
    3997      819356 :                 if (s < msg || !isspace((unsigned char) *s)) /* gone one too far */
    3998      819356 :                         s++;
    3999      819356 :                 if (*p == '\t') {
    4000       22866 :                         p++;
    4001             :                 }
    4002      819356 :                 len = s - msg;
    4003      819356 :                 *str = malloc(len + 1);
    4004      819356 :                 strcpy_len(*str, msg, len + 1);
    4005             : 
    4006      819356 :                 if (next)
    4007      819356 :                         *next = p;
    4008      819356 :                 if (lenp)
    4009      819356 :                         *lenp = len;
    4010      819356 :                 return 0;
    4011             :         }
    4012             : }
    4013             : 
    4014             : char *
    4015           0 : mapi_unquote(char *msg)
    4016             : {
    4017           0 :         char *start;
    4018             : 
    4019           0 :         unquote(msg, &start, NULL, ']', NULL);
    4020           0 :         return start;
    4021             : }
    4022             : 
    4023             : char *
    4024           0 : mapi_quote(const char *msg, int size)
    4025             : {
    4026             :         /* we absolutely don't need more than this (until we start
    4027             :            producing octal escapes */
    4028           0 :         char *s = malloc((size < 0 ? strlen(msg) : (size_t) size) * 2 + 1);
    4029           0 :         char *t = s;
    4030             : 
    4031             :         /* the condition is tricky: if initially size < 0, we must
    4032             :            continue until a NULL byte, else, size gives the number of
    4033             :            bytes to be copied */
    4034           0 :         while (size < 0 ? *msg : size > 0) {
    4035           0 :                 if (size > 0)
    4036           0 :                         size--;
    4037           0 :                 switch (*msg) {
    4038           0 :                 case '\n':
    4039           0 :                         *t++ = '\\';
    4040           0 :                         *t++ = 'n';
    4041           0 :                         break;
    4042           0 :                 case '\t':
    4043           0 :                         *t++ = '\\';
    4044           0 :                         *t++ = 't';
    4045           0 :                         break;
    4046           0 :                 case PLACEHOLDER:
    4047           0 :                         *t++ = '\\';
    4048           0 :                         *t++ = PLACEHOLDER;
    4049           0 :                         break;
    4050           0 :                 case '\\':
    4051           0 :                         *t++ = '\\';
    4052           0 :                         *t++ = '\\';
    4053           0 :                         break;
    4054           0 :                 case '\'':
    4055           0 :                         *t++ = '\\';
    4056           0 :                         *t++ = '\'';
    4057           0 :                         break;
    4058           0 :                 case '"':
    4059           0 :                         *t++ = '\\';
    4060           0 :                         *t++ = '"';
    4061           0 :                         break;
    4062           0 :                 case '\0':
    4063           0 :                         *t++ = '\\';
    4064           0 :                         *t++ = '0';
    4065           0 :                         break;
    4066           0 :                 default:
    4067           0 :                         *t++ = *msg;
    4068           0 :                         break;
    4069             :                 }
    4070           0 :                 msg++;
    4071             :                 /* also deal with binaries */
    4072             :         }
    4073           0 :         *t = 0;
    4074           0 :         return s;
    4075             : }
    4076             : 
    4077             : static int
    4078           0 : mapi_extend_bindings(MapiHdl hdl, int minbindings)
    4079             : {
    4080             :         /* extend the bindings table */
    4081           0 :         int nm = hdl->maxbindings + 32;
    4082             : 
    4083           0 :         if (nm <= minbindings)
    4084           0 :                 nm = minbindings + 32;
    4085           0 :         REALLOC(hdl->bindings, nm);
    4086           0 :         assert(hdl->bindings);
    4087             :         /* clear new entries */
    4088           0 :         memset(hdl->bindings + hdl->maxbindings, 0, (nm - hdl->maxbindings) * sizeof(*hdl->bindings));
    4089           0 :         hdl->maxbindings = nm;
    4090           0 :         return MOK;
    4091             : }
    4092             : 
    4093             : static int
    4094           0 : mapi_extend_params(MapiHdl hdl, int minparams)
    4095             : {
    4096             :         /* extend the params table */
    4097           0 :         int nm = hdl->maxparams + 32;
    4098             : 
    4099           0 :         if (nm <= minparams)
    4100           0 :                 nm = minparams + 32;
    4101           0 :         REALLOC(hdl->params, nm);
    4102           0 :         assert(hdl->params);
    4103             :         /* clear new entries */
    4104           0 :         memset(hdl->params + hdl->maxparams, 0, (nm - hdl->maxparams) * sizeof(*hdl->params));
    4105           0 :         hdl->maxparams = nm;
    4106           0 :         return MOK;
    4107             : }
    4108             : 
    4109             : static MapiMsg
    4110           0 : store_field(struct MapiResultSet *result, int cr, int fnr, int outtype, void *dst)
    4111             : {
    4112           0 :         char *val;
    4113             : 
    4114           0 :         val = result->cache.line[cr].anchors[fnr];
    4115             : 
    4116           0 :         if (val == 0) {
    4117           0 :                 return mapi_setError(result->hdl->mid, "Field value undefined or nil", __func__, MERROR);
    4118             :         }
    4119             : 
    4120             :         /* auto convert to C-type */
    4121           0 :         switch (outtype) {
    4122           0 :         case MAPI_TINY:
    4123           0 :                 *(signed char *) dst = (signed char) strtol(val, NULL, 0);
    4124           0 :                 break;
    4125           0 :         case MAPI_UTINY:
    4126           0 :                 *(unsigned char *) dst = (unsigned char) strtoul(val, NULL, 0);
    4127           0 :                 break;
    4128           0 :         case MAPI_SHORT:
    4129           0 :                 *(short *) dst = (short) strtol(val, NULL, 0);
    4130           0 :                 break;
    4131           0 :         case MAPI_USHORT:
    4132           0 :                 *(unsigned short *) dst = (unsigned short) strtoul(val, NULL, 0);
    4133           0 :                 break;
    4134           0 :         case MAPI_NUMERIC:
    4135             :         case MAPI_INT:
    4136           0 :                 *(int *) dst = (int) strtol(val, NULL, 0);
    4137           0 :                 break;
    4138           0 :         case MAPI_UINT:
    4139           0 :                 *(unsigned int *) dst = (unsigned int) strtoul(val, NULL, 0);
    4140           0 :                 break;
    4141           0 :         case MAPI_LONG:
    4142           0 :                 *(long *) dst = strtol(val, NULL, 0);
    4143           0 :                 break;
    4144           0 :         case MAPI_ULONG:
    4145           0 :                 *(unsigned long *) dst = strtoul(val, NULL, 0);
    4146           0 :                 break;
    4147           0 :         case MAPI_LONGLONG:
    4148           0 :                 *(int64_t *) dst = strtoll(val, NULL, 0);
    4149           0 :                 break;
    4150           0 :         case MAPI_ULONGLONG:
    4151           0 :                 *(uint64_t *) dst = strtoull(val, NULL, 0);
    4152           0 :                 break;
    4153           0 :         case MAPI_CHAR:
    4154           0 :                 *(char *) dst = *val;
    4155           0 :                 break;
    4156           0 :         case MAPI_FLOAT:
    4157           0 :                 *(float *) dst = strtof(val, NULL);
    4158           0 :                 break;
    4159           0 :         case MAPI_DOUBLE:
    4160           0 :                 *(double *) dst = strtod(val, NULL);
    4161           0 :                 break;
    4162           0 :         case MAPI_DATE:
    4163           0 :                 sscanf(val, "%hd-%hu-%hu",
    4164             :                        &((MapiDate *) dst)->year,
    4165             :                        &((MapiDate *) dst)->month,
    4166             :                        &((MapiDate *) dst)->day);
    4167           0 :                 break;
    4168           0 :         case MAPI_TIME:
    4169           0 :                 sscanf(val, "%hu:%hu:%hu",
    4170             :                        &((MapiTime *) dst)->hour,
    4171             :                        &((MapiTime *) dst)->minute,
    4172             :                        &((MapiTime *) dst)->second);
    4173           0 :                 break;
    4174           0 :         case MAPI_DATETIME:{
    4175           0 :                 int n;
    4176             : 
    4177           0 :                 ((MapiDateTime *) dst)->fraction = 0;
    4178           0 :                 sscanf(val, "%hd-%hu-%hu %hu:%hu:%hu%n",
    4179             :                        &((MapiDateTime *) dst)->year,
    4180             :                        &((MapiDateTime *) dst)->month,
    4181             :                        &((MapiDateTime *) dst)->day,
    4182             :                        &((MapiDateTime *) dst)->hour,
    4183             :                        &((MapiDateTime *) dst)->minute,
    4184             :                        &((MapiDateTime *) dst)->second,
    4185             :                        &n);
    4186           0 :                 if (val[n] == '.') {
    4187           0 :                         unsigned int fac = 1000000000;
    4188           0 :                         unsigned int nsec = 0;
    4189             : 
    4190           0 :                         for (n++; isdigit((unsigned char) val[n]); n++) {
    4191           0 :                                 fac /= 10;
    4192           0 :                                 nsec += (val[n] - '0') * fac;
    4193             :                         }
    4194           0 :                         ((MapiDateTime *) dst)->fraction = nsec;
    4195             :                 }
    4196           0 :                 break;
    4197             :         }
    4198           0 :         case MAPI_AUTO:
    4199             :         case MAPI_VARCHAR:
    4200             :         default:
    4201           0 :                 *(char **) dst = val;
    4202             :         }
    4203             :         return MOK;
    4204             : }
    4205             : 
    4206             : MapiMsg
    4207           0 : mapi_store_field(MapiHdl hdl, int fnr, int outtype, void *dst)
    4208             : {
    4209           0 :         struct MapiResultSet *result;
    4210             : 
    4211           0 :         mapi_hdl_check(hdl);
    4212             : 
    4213           0 :         if ((result = hdl->result) == NULL) {
    4214           0 :                 return mapi_setError(hdl->mid, "No data read", __func__, MERROR);
    4215             :         }
    4216             : 
    4217           0 :         if (fnr < 0 || fnr >= result->fieldcnt) {
    4218           0 :                 return mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4219             :         }
    4220             : 
    4221           0 :         return store_field(result, result->cache.reader, fnr, outtype, dst);
    4222             : }
    4223             : 
    4224             : static void
    4225           0 : mapi_store_bind(struct MapiResultSet *result, int cr)
    4226             : {
    4227           0 :         int i;
    4228           0 :         MapiHdl hdl = result->hdl;
    4229             : 
    4230           0 :         for (i = 0; i < hdl->maxbindings; i++)
    4231           0 :                 if (hdl->bindings[i].outparam)
    4232           0 :                         store_field(result, cr, i, hdl->bindings[i].outtype, hdl->bindings[i].outparam);
    4233           0 : }
    4234             : 
    4235             : /*
    4236             :  * The low level routine mapi_slice_row breaks the last row received
    4237             :  * into pieces and binds the field descriptors with their location. All
    4238             :  * escaped characters are immediately replaced, such that we end with a
    4239             :  * list of C-strings.  It overwrites the contents of the row buffer,
    4240             :  * because de-escaping only reduces the size.  It also silently extends
    4241             :  * the field descriptor table.
    4242             :  */
    4243             : static int
    4244     1146050 : mapi_slice_row(struct MapiResultSet *result, int cr)
    4245             : {
    4246     1146050 :         char *p;
    4247     1146050 :         int i = 0;
    4248             : 
    4249     1146050 :         p = result->cache.line[cr].rows;
    4250     1146050 :         if (p == NULL)
    4251           0 :                 return mapi_setError(result->hdl->mid, "Current row missing", __func__, MERROR);
    4252     1146050 :         if (result->cache.line[cr].fldcnt)
    4253             :                 return result->cache.line[cr].fldcnt;        /* already sliced */
    4254             : 
    4255     1146050 :         if (*p != '[') {
    4256             :                 /* nothing to slice */
    4257          49 :                 i = 1;
    4258          49 :                 REALLOC(result->cache.line[cr].anchors, 1);
    4259          49 :                 REALLOC(result->cache.line[cr].lens, 1);
    4260             :                 /* skip initial '=' if present */
    4261          49 :                 if (*p == '=')
    4262          49 :                         p++;
    4263          49 :                 result->cache.line[cr].anchors[0] = strdup(p);
    4264          49 :                 result->cache.line[cr].lens[0] = strlen(p);
    4265             :         } else {
    4266             :                 /* work on a copy to preserve the original */
    4267     1146001 :                 p = strdup(p);
    4268     1146206 :                 i = slice_row(p,
    4269     1146001 :                               msettings_lang_is_sql(result->hdl->mid->settings) ? "NULL" : "nil",
    4270             :                               &result->cache.line[cr].anchors,
    4271             :                               &result->cache.line[cr].lens,
    4272             :                               result->fieldcnt, ']');
    4273     1146001 :                 free(p);
    4274             :         }
    4275     1146050 :         if (i != result->fieldcnt) {
    4276             :                 int j;
    4277         194 :                 for (j = 0; j < result->fieldcnt; j++) {
    4278           0 :                         if (result->fields[j].columnname)
    4279           0 :                                 free(result->fields[j].columnname);
    4280           0 :                         result->fields[j].columnname = NULL;
    4281           0 :                         if (result->fields[j].columntype)
    4282           0 :                                 free(result->fields[j].columntype);
    4283           0 :                         result->fields[j].columntype = NULL;
    4284           0 :                         if (result->fields[j].tablename)
    4285           0 :                                 free(result->fields[j].tablename);
    4286           0 :                         result->fields[j].tablename = NULL;
    4287           0 :                         result->fields[j].columnlength = 0;
    4288             :                 }
    4289             :         }
    4290     1146050 :         if (i > result->fieldcnt) {
    4291         194 :                 result->fieldcnt = i;
    4292         194 :                 if (i > result->maxfields) {
    4293         194 :                         REALLOC(result->fields, i);
    4294         194 :                         memset(result->fields + result->maxfields, 0, (i - result->maxfields) * sizeof(*result->fields));
    4295         194 :                         result->maxfields = i;
    4296             :                 }
    4297             :         }
    4298     1146050 :         result->cache.line[cr].fldcnt = i;
    4299     1146050 :         return i;
    4300             : }
    4301             : 
    4302             : /*
    4303             :  * The rows presented are broken down into pieces to
    4304             :  * simplify access later on. However, mclient may
    4305             :  * first want to check the content of the line for
    4306             :  * useful information (e.g. #EOD)
    4307             :  */
    4308             : int
    4309       26672 : mapi_split_line(MapiHdl hdl)
    4310             : {
    4311       26672 :         int n;
    4312       26672 :         struct MapiResultSet *result;
    4313             : 
    4314       26672 :         result = hdl->result;
    4315       26672 :         assert(result != NULL);
    4316       26672 :         if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
    4317       26672 :                 n = mapi_slice_row(result, result->cache.reader);
    4318             :                 /* no need to call mapi_store_bind since
    4319             :                    mapi_fetch_line would have done that if needed */
    4320             :         }
    4321       26672 :         return n;
    4322             : }
    4323             : 
    4324             : int
    4325     1123629 : mapi_fetch_row(MapiHdl hdl)
    4326             : {
    4327     1123629 :         char *reply;
    4328     1123629 :         int n;
    4329     1123629 :         struct MapiResultSet *result;
    4330             : 
    4331     1123629 :         mapi_hdl_check(hdl);
    4332     1140598 :         do {
    4333     1140598 :                 if ((reply = mapi_fetch_line(hdl)) == NULL)
    4334             :                         return 0;
    4335     1136347 :         } while (*reply != '[' && *reply != '=');
    4336     1119378 :         result = hdl->result;
    4337     1119378 :         assert(result != NULL);
    4338     1119378 :         if ((n = result->cache.line[result->cache.reader].fldcnt) == 0) {
    4339     1119378 :                 n = mapi_slice_row(result, result->cache.reader);
    4340             :                 /* no need to call mapi_store_bind since
    4341             :                    mapi_fetch_line would have done that if needed */
    4342             :         }
    4343             :         return n;
    4344             : }
    4345             : 
    4346             : /*
    4347             :  * All rows can be cached first as well.
    4348             :  */
    4349             : int64_t
    4350           1 : mapi_fetch_all_rows(MapiHdl hdl)
    4351             : {
    4352           1 :         Mapi mid;
    4353           1 :         struct MapiResultSet *result;
    4354             : 
    4355           1 :         mapi_hdl_check(hdl);
    4356             : 
    4357           1 :         mid = hdl->mid;
    4358           3 :         for (;;) {
    4359           4 :                 if ((result = hdl->result) != NULL &&
    4360           2 :                     msettings_lang_is_sql(mid->settings) &&
    4361           2 :                     mid->active == NULL &&
    4362           1 :                     result->row_count > 0 &&
    4363           1 :                     result->cache.first + result->cache.tuplecount < result->row_count) {
    4364           0 :                         mid->active = hdl;
    4365           0 :                         hdl->active = result;
    4366           0 :                         mapi_log_record(mid, "SEND", "X" "export %d %" PRId64 "\n",
    4367             :                                              result->tableid, result->cache.first + result->cache.tuplecount);
    4368           0 :                         if (mnstr_printf(mid->to, "X" "export %d %" PRId64 "\n",
    4369           0 :                                          result->tableid, result->cache.first + result->cache.tuplecount) < 0 ||
    4370           0 :                             mnstr_flush(mid->to, MNSTR_FLUSH_DATA))
    4371           0 :                                 check_stream(mid, mid->to, "sending export command", 0);
    4372             :                 }
    4373           2 :                 if (mid->active)
    4374           1 :                         read_into_cache(mid->active, 0);
    4375             :                 else
    4376             :                         break;
    4377             :         }
    4378           1 :         return result ? result->cache.tuplecount : 0;
    4379             : }
    4380             : 
    4381             : char *
    4382     4149569 : mapi_fetch_field(MapiHdl hdl, int fnr)
    4383             : {
    4384     4149569 :         int cr;
    4385     4149569 :         struct MapiResultSet *result;
    4386             : 
    4387     4149569 :         mapi_hdl_check0(hdl);
    4388             : 
    4389     4149569 :         if ((result = hdl->result) == NULL ||
    4390     4149569 :             (cr = result->cache.reader) < 0 ||
    4391     4149569 :             (result->cache.line[cr].rows[0] != '[' &&
    4392             :              result->cache.line[cr].rows[0] != '=')) {
    4393           0 :                 mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
    4394           0 :                 return 0;
    4395             :         }
    4396     4149569 :         assert(result->cache.line != NULL);
    4397     4149569 :         if (fnr >= 0) {
    4398             :                 /* slice if needed */
    4399     4149569 :                 if (result->cache.line[cr].fldcnt == 0)
    4400           0 :                         mapi_slice_row(result, cr);
    4401     4149569 :                 if (fnr < result->cache.line[cr].fldcnt)
    4402     4149568 :                         return result->cache.line[cr].anchors[fnr];
    4403             :         }
    4404           1 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4405           1 :         return 0;
    4406             : }
    4407             : 
    4408             : size_t
    4409      792264 : mapi_fetch_field_len(MapiHdl hdl, int fnr)
    4410             : {
    4411      792264 :         int cr;
    4412      792264 :         struct MapiResultSet *result;
    4413             : 
    4414      792264 :         mapi_hdl_check0(hdl);
    4415             : 
    4416      792264 :         if ((result = hdl->result) == NULL ||
    4417      792264 :             (cr = result->cache.reader) < 0 ||
    4418      792264 :             (result->cache.line[cr].rows[0] != '[' &&
    4419             :              result->cache.line[cr].rows[0] != '=')) {
    4420           0 :                 mapi_setError(hdl->mid, "Must do a successful mapi_fetch_row first", __func__, MERROR);
    4421           0 :                 return 0;
    4422             :         }
    4423      792264 :         assert(result->cache.line != NULL);
    4424      792264 :         if (fnr >= 0) {
    4425             :                 /* slice if needed */
    4426      792264 :                 if (result->cache.line[cr].fldcnt == 0)
    4427           0 :                         mapi_slice_row(result, cr);
    4428      792264 :                 if (fnr < result->cache.line[cr].fldcnt)
    4429      792264 :                         return result->cache.line[cr].lens[fnr];
    4430             :         }
    4431           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4432           0 :         return 0;
    4433             : }
    4434             : 
    4435             : int
    4436        2373 : mapi_get_field_count(MapiHdl hdl)
    4437             : {
    4438        2373 :         mapi_hdl_check(hdl);
    4439        2373 :         if (hdl->result && hdl->result->fieldcnt == 0) {
    4440             :                 /* no rows have been sliced yet, and there was no
    4441             :                    header, so try to figure out how many columns there
    4442             :                    are for ourselves */
    4443             :                 int i;
    4444             : 
    4445        2064 :                 for (i = 0; i < hdl->result->cache.writer; i++)
    4446           0 :                         if (hdl->result->cache.line[i].rows[0] == '[' ||
    4447             :                             hdl->result->cache.line[i].rows[0] == '=')
    4448           0 :                                 mapi_slice_row(hdl->result, i);
    4449             :         }
    4450        2373 :         return hdl->result ? hdl->result->fieldcnt : 0;
    4451             : }
    4452             : 
    4453             : int64_t
    4454         382 : mapi_get_row_count(MapiHdl hdl)
    4455             : {
    4456         382 :         mapi_hdl_check(hdl);
    4457         382 :         return hdl->result ? hdl->result->row_count : 0;
    4458             : }
    4459             : 
    4460             : int64_t
    4461           0 : mapi_get_last_id(MapiHdl hdl)
    4462             : {
    4463           0 :         mapi_hdl_check(hdl);
    4464           0 :         return hdl->result ? hdl->result->last_id : -1;
    4465             : }
    4466             : 
    4467             : char *
    4468         928 : mapi_get_name(MapiHdl hdl, int fnr)
    4469             : {
    4470         928 :         struct MapiResultSet *result;
    4471             : 
    4472         928 :         mapi_hdl_check0(hdl);
    4473         928 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4474         928 :                 return result->fields[fnr].columnname;
    4475           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4476           0 :         return 0;
    4477             : }
    4478             : 
    4479             : char *
    4480     4116531 : mapi_get_type(MapiHdl hdl, int fnr)
    4481             : {
    4482     4116531 :         struct MapiResultSet *result;
    4483             : 
    4484     4116531 :         mapi_hdl_check0(hdl);
    4485     4116531 :         if ((result = hdl->result) != 0 &&
    4486     4116531 :             fnr >= 0 && fnr < result->fieldcnt) {
    4487     4116531 :                 if (result->fields[fnr].columntype == NULL)
    4488             :                         return "unknown";
    4489     4116530 :                 return result->fields[fnr].columntype;
    4490             :         }
    4491           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4492           0 :         return 0;
    4493             : }
    4494             : 
    4495             : char *
    4496         918 : mapi_get_table(MapiHdl hdl, int fnr)
    4497             : {
    4498         918 :         struct MapiResultSet *result;
    4499             : 
    4500         918 :         mapi_hdl_check0(hdl);
    4501         918 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4502         918 :                 return result->fields[fnr].tablename;
    4503           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4504           0 :         return 0;
    4505             : }
    4506             : 
    4507             : int
    4508         864 : mapi_get_len(MapiHdl hdl, int fnr)
    4509             : {
    4510         864 :         struct MapiResultSet *result;
    4511             : 
    4512         864 :         mapi_hdl_check0(hdl);
    4513         864 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4514         864 :                 return result->fields[fnr].columnlength;
    4515           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4516           0 :         return 0;
    4517             : }
    4518             : 
    4519             : int
    4520         921 : mapi_get_digits(MapiHdl hdl, int fnr)
    4521             : {
    4522         921 :         struct MapiResultSet *result;
    4523             : 
    4524         921 :         mapi_hdl_check0(hdl);
    4525         921 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4526         921 :                 return result->fields[fnr].digits;
    4527           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4528           0 :         return 0;
    4529             : }
    4530             : 
    4531             : int
    4532           4 : mapi_get_scale(MapiHdl hdl, int fnr)
    4533             : {
    4534           4 :         struct MapiResultSet *result;
    4535             : 
    4536           4 :         mapi_hdl_check0(hdl);
    4537           4 :         if ((result = hdl->result) != 0 && fnr >= 0 && fnr < result->fieldcnt)
    4538           4 :                 return result->fields[fnr].scale;
    4539           0 :         mapi_setError(hdl->mid, "Illegal field number", __func__, MERROR);
    4540           0 :         return 0;
    4541             : }
    4542             : 
    4543             : char *
    4544        1053 : mapi_get_query(MapiHdl hdl)
    4545             : {
    4546        1053 :         mapi_hdl_check0(hdl);
    4547        1053 :         if (hdl->query != NULL) {
    4548        1053 :                 return strdup(hdl->query);
    4549             :         } else {
    4550             :                 return NULL;
    4551             :         }
    4552             : }
    4553             : 
    4554             : 
    4555             : int
    4556        9409 : mapi_get_querytype(MapiHdl hdl)
    4557             : {
    4558        9409 :         struct MapiResultSet *result;
    4559             : 
    4560        9409 :         mapi_hdl_check0(hdl);
    4561        9409 :         if ((result = hdl->result) != 0)
    4562        8349 :                 return result->querytype;
    4563        1060 :         mapi_setError(hdl->mid, "No query result", __func__, MERROR);
    4564        1060 :         return 0; /* Q_PARSE! */
    4565             : }
    4566             : 
    4567             : int
    4568         122 : mapi_get_tableid(MapiHdl hdl)
    4569             : {
    4570         122 :         struct MapiResultSet *result;
    4571             : 
    4572         122 :         mapi_hdl_check0(hdl);
    4573         122 :         if ((result = hdl->result) != 0)
    4574         122 :                 return result->tableid;
    4575           0 :         mapi_setError(hdl->mid, "No query result", __func__, MERROR);
    4576           0 :         return 0;
    4577             : }
    4578             : 
    4579             : int64_t
    4580        2826 : mapi_rows_affected(MapiHdl hdl)
    4581             : {
    4582        2826 :         struct MapiResultSet *result;
    4583             : 
    4584        2826 :         mapi_hdl_check(hdl);
    4585        2826 :         if ((result = hdl->result) == NULL)
    4586             :                 return 0;
    4587        2826 :         return result->row_count;
    4588             : }
    4589             : 
    4590             : int64_t
    4591        3663 : mapi_get_querytime(MapiHdl hdl)
    4592             : {
    4593        3663 :         struct MapiResultSet *result;
    4594             : 
    4595        3663 :         mapi_hdl_check(hdl);
    4596        3663 :         if ((result = hdl->result) == NULL)
    4597             :                 return 0;
    4598        3133 :         return result->querytime;
    4599             : }
    4600             : 
    4601             : int64_t
    4602        3663 : mapi_get_maloptimizertime(MapiHdl hdl)
    4603             : {
    4604        3663 :         struct MapiResultSet *result;
    4605             : 
    4606        3663 :         mapi_hdl_check(hdl);
    4607        3663 :         if ((result = hdl->result) == NULL)
    4608             :                 return 0;
    4609        3133 :         return result->maloptimizertime;
    4610             : }
    4611             : 
    4612             : int64_t
    4613        3663 : mapi_get_sqloptimizertime(MapiHdl hdl)
    4614             : {
    4615        3663 :         struct MapiResultSet *result;
    4616             : 
    4617        3663 :         mapi_hdl_check(hdl);
    4618        3663 :         if ((result = hdl->result) == NULL)
    4619             :                 return 0;
    4620        3133 :         return result->sqloptimizertime;
    4621             : }
    4622             : 
    4623             : const char *
    4624         198 : mapi_get_dbname(Mapi mid)
    4625             : {
    4626         198 :         return msetting_string(mid->settings, MP_DATABASE);
    4627             : }
    4628             : 
    4629             : const char *
    4630           8 : mapi_get_host(Mapi mid)
    4631             : {
    4632           8 :         return msetting_string(mid->settings, MP_HOST);
    4633             : }
    4634             : 
    4635             : const char *
    4636           8 : mapi_get_user(Mapi mid)
    4637             : {
    4638           8 :         return msetting_string(mid->settings, MP_USER);;
    4639             : }
    4640             : 
    4641             : const char *
    4642           0 : mapi_get_lang(Mapi mid)
    4643             : {
    4644           0 :         return msetting_string(mid->settings, MP_LANGUAGE);;
    4645             : }
    4646             : 
    4647             : const char *
    4648           0 : mapi_get_uri(Mapi mid)
    4649             : {
    4650           0 :         return mid->uri;
    4651             : }
    4652             : 
    4653             : const char *
    4654           1 : mapi_get_mapi_version(void)
    4655             : {
    4656           1 :         return MAPI_VERSION;
    4657             : }
    4658             : 
    4659             : const char *
    4660           0 : mapi_get_monet_version(Mapi mid)
    4661             : {
    4662           0 :         mapi_check0(mid);
    4663           0 :         return mid->server ? mid->server : "";
    4664             : }
    4665             : 
    4666             : const char *
    4667           0 : mapi_get_motd(Mapi mid)
    4668             : {
    4669           0 :         mapi_check0(mid);
    4670           0 :         return mid->motd;
    4671             : }
    4672             : 
    4673             : bool
    4674           0 : mapi_is_connected(Mapi mid)
    4675             : {
    4676           0 :         return mid->connected;
    4677             : }
    4678             : 
    4679             : MapiHdl
    4680         157 : mapi_get_active(Mapi mid)
    4681             : {
    4682         157 :         return mid->active;
    4683             : }
    4684             : 
    4685             : msettings*
    4686           0 : mapi_get_settings(Mapi mid)
    4687             : {
    4688           0 :         return mid->settings;
    4689             : }
    4690             : 
    4691             : 
    4692             : MapiMsg
    4693        1384 : mapi_wrap_streams(Mapi mid, stream *rstream, stream *wstream)
    4694             : {
    4695             :         // do not use check_stream here yet because the socket is not yet in 'mid'
    4696        1384 :         const char *error_message;
    4697        1384 :         stream *error_stream;
    4698             : 
    4699        1384 :         assert(!isa_block_stream(rstream));
    4700        1384 :         assert(!isa_block_stream(wstream));
    4701             : 
    4702             :         // First send some NUL bytes. If we're accidentally connecting to the wrong
    4703             :         // port or protocol this may cause the remote server to close the
    4704             :         // connection. If we don't do this, the connection will often hang
    4705             :         // because the server expects us to speak first and we expect the server
    4706             :         // to speak first.
    4707             :         //
    4708             :         // Note that a pair of NUL bytes is a no-op message in MAPI.
    4709             :         //
    4710             :         // Surprisingly, it seems sending these NUL bytes makes non-TLS
    4711             :         // connection setup a little faster rather than slower!
    4712             :         static const char zeroes[8] = { 0 };
    4713             :         ssize_t to_write = sizeof(zeroes);
    4714        2768 :         while (to_write > 0) {
    4715        1384 :                 ssize_t n = mnstr_write(wstream, zeroes, 1, to_write);
    4716        1384 :                 if (n < 0) {
    4717           0 :                         close_connection(mid);
    4718           0 :                         return mapi_printError(mid, __func__, MERROR, "could not send leader block: %s", mnstr_peek_error(wstream));
    4719             :                 }
    4720        1384 :                 to_write -= (size_t)n;
    4721             :         }
    4722        1384 :         if (mnstr_flush(wstream, MNSTR_FLUSH_DATA) != 0) {
    4723           0 :                 close_connection(mid);
    4724           0 :                 return mapi_printError(mid, __func__, MERROR, "could not flush leader block: %s", mnstr_peek_error(wstream));
    4725             :         }
    4726             : 
    4727             : 
    4728        1384 :         stream *brstream = NULL;
    4729        1384 :         stream *bwstream = NULL;
    4730             : 
    4731        1384 :         bwstream = block_stream(wstream);
    4732        1384 :         if (bwstream == NULL || mnstr_errnr(bwstream) != MNSTR_NO__ERROR) {
    4733           0 :                 error_stream = bwstream;
    4734           0 :                 error_message = "block_stream wstream";
    4735           0 :                 goto bailout;
    4736             :         }
    4737        1384 :         brstream = block_stream(rstream);
    4738        1384 :         if (brstream == NULL || mnstr_errnr(brstream) != MNSTR_NO__ERROR) {
    4739           0 :                 error_stream = brstream;
    4740           0 :                 error_message = "block_stream rstream";
    4741           0 :                 goto bailout;
    4742             :         }
    4743             : 
    4744        1384 :         mid->to = bwstream;
    4745        1384 :         mid->from = brstream;
    4746        1384 :         return MOK;
    4747           0 : bailout:
    4748             :         // adapted from the check_stream macro
    4749           0 :         if (brstream)
    4750           0 :                 mnstr_destroy(brstream);
    4751           0 :         if (bwstream)
    4752           0 :                 mnstr_destroy(bwstream);
    4753             :         // malloc failure is the only way these calls could have failed
    4754           0 :         return mapi_printError(mid, __func__, MERROR, "%s: %s", error_message, mnstr_peek_error(error_stream));
    4755             : }

Generated by: LCOV version 1.14