LCOV - code coverage report
Current view: top level - clients/mapilib - mapi.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1224 2305 53.1 %
Date: 2021-10-13 02:24:04 Functions: 87 131 66.4 %

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

Generated by: LCOV version 1.14