Skip to content
Snippets Groups Projects
  1. Apr 20, 2020
    • Vladislav Shpilevoy's avatar
      error: export error_unref() function · ed217292
      Vladislav Shpilevoy authored
      C struct error objects can be created directly only in C.
      C-side increments their reference counter when pushes to the Lua
      stack.
      
      It is not going to be so convenient soon. error_unpack() function
      will be used in netbox to decode error object via Lua FFI.
      
      Such error object will have 0 refs and no Lua GC callback
      established. Because it won't be pushed on Lua stack natually,
      from Lua C. To make such errors alive their reference counter
      will be incremented and error_unref() will be set as GC callback.
      
      Follow up for #4398
      ed217292
    • Leonid Vasiliev's avatar
      error: add error MsgPack encoding · 345877df
      Leonid Vasiliev authored
      
      Co-authored-by: default avatarVladislav <Shpilevoy&lt;v.shpilevoy@tarantool.org>
      
      Closes #4398
      
      @TarantoolBot document
      Title: Error objects encoding in MessagePack
      
      Until now an error sent over IProto, or serialized into
      MessagePack was turned into a string consisting of the error
      message. As a result, all other error object attributes were lost,
      including type of the object. On client side seeing a string it
      was not possible to tell whether the string is a real string, or
      it is a serialized error.
      
      To deal with that the error objects encoding is reworked from the
      scratch. Now, when session setting `error_marshaling_enabled` is
      true, all fibers of that session will encode error objects as a
      new MP_EXT type - MP_ERROR (0x03).
      
      ```
          +--------+----------+========+
          | MP_EXT | MP_ERROR | MP_MAP |
          +--------+----------+========+
      
          MP_ERROR: <MP_MAP> {
              MP_ERROR_STACK: <MP_ARRAY> [
                  <MP_MAP> {
                      MP_ERROR_TYPE: <MP_STR>,
                      MP_ERROR_FILE: <MP_STR>,
                      MP_ERROR_LINE: <MP_UINT>,
                      MP_ERROR_MESSAGE: <MP_STR>,
                      MP_ERROR_ERRNO: <MP_UINT>,
                      MP_ERROR_CODE: <MP_UINT>,
                      MP_ERROR_FIELDS: <MP_MAP> {
                          <MP_STR>: ...,
                          <MP_STR>: ...,
                          ...
                      },
                      ...
                  },
                  ...
              ]
          }
      ```
      
      On the top level there is a single key: `MP_ERROR_STACK = 0x00`.
      More keys can be added in future, and a client should ignore all
      unknown keys to keep compatibility with new versions.
      
      Every error in the stack is a map with the following keys:
      * `MP_ERROR_TYPE = 0x00` - error type. This is what is visible in
        `<error_object>.base_type` field;
      * `MP_ERROR_FILE = 0x01` - file name from `<error_object>.trace`;
      * `MP_ERROR_LINE = 0x02` - line from `<error_object>.trace`;
      * `MP_ERROR_MESSAGE = 0x03` - error message from
        `<error_object>.message`;
      * `MP_ERROR_ERRNO = 0x04` - errno saved at the moment of the error
        creation. Visible in `<error_object>.errno`;
      * `MP_ERROR_CODE = 0x05` - error code. Visible in
        `<error_object>.code` and in C function `box_error_code()`.
      * `MP_ERROR_FIELDS = 0x06` - additional fields depending on error
        type. For example, AccessDenied error type stores here fields
        `access_type`, `object_type`, `object_name`. Connector's code
        should ignore unknown keys met here, and be ready, that for some
        existing errors new fields can be added, old can be dropped.
      345877df
    • Vladislav Shpilevoy's avatar
      box: move Lua MP_EXT decoder from tuple.c · d3a4dc68
      Vladislav Shpilevoy authored
      Lua C module 'msgpack' supports registration of custom extension
      decoders for MP_EXT values. That is needed to make 'msgpack' not
      depending on any modules which use it.
      
      So far the only box-related extension were tuples - struct tuple
      cdata needed to be encoded as an array.
      
      That is going to change in next commits, where struct error cdata
      appears, also depending on box. So the decoder can't be located
      in src/box/lua/tuple.c. It is moved to a more common place -
      src/box/lua/init.c.
      
      Needed for #4398
      d3a4dc68
    • Leonid Vasiliev's avatar
      error: update constructors of some errors · 6d0078d0
      Leonid Vasiliev authored
      We want to have a transparent marshalling through net.box
      for errors. To do this, we need to recreate the error
      on the client side with the same parameters as on the server.
      For convenience, we update AccessDeniedError constructor
      which has pointers to static strings and add the XlogGapError
      constructor that does not require vclock.
      
      Needed for #4398
      6d0078d0
    • Leonid Vasiliev's avatar
      error: add session setting for error type marshaling · c7a7f1cb
      Leonid Vasiliev authored
      
      Errors are encoded as a string when serialized to MessagePack to
      be sent over IProto or when just saved into a buffer via Lua
      modules msgpackffi and msgpack.
      
      That is not very useful on client-side, because most of the error
      metadata is lost: code, type, trace - everything except the
      message.
      
      Next commits are going to dedicate a new MP_EXT type to error
      objects so as everything could be encoded, and on client side it
      would be possible to restore types.
      
      But this is a breaking change in case some users use old
      connectors when work with newer Tarantool instances. So to smooth
      the upgrade there is a new session setting -
      'error_marshaling_enabled'.
      
      By default it is false. When it is true, all fibers of the given
      session will serialize error objects as MP_EXT.
      
      Co-authored-by: default avatarVladislav <Shpilevoy&lt;v.shpilevoy@tarantool.org>
      
      Needed for #4398
      c7a7f1cb
    • Vladislav Shpilevoy's avatar
      session: add offset to SQL session settings array · b6c6c536
      Vladislav Shpilevoy authored
      Session settings are stored in a monolithic array. Submodules
      can define a range of settings in there. For example, SQL. It
      occupies settings from 0 to 8. There is a second array of only
      SQL settings in build.c, of the same size, and it uses the same
      indexes.
      
      But if something will be added before SQL settings, so as they
      won't start from 0, it will break the equal indexes assumption.
      SQL should normalize all setting identifiers by
      SESSION_SETTING_SQL_BEGIN.
      b6c6c536
    • Leonid Vasiliev's avatar
      error: add custom error type · b728e7af
      Leonid Vasiliev authored
      
      Co-authored-by: default avatarVladislav <Shpilevoy&lt;v.shpilevoy@tarantool.org>
      
      Part of #4398
      
      @TarantoolBot document
      Title: Custom error types for Lua errors
      
      Errors can be created in 2 ways: `box.error.new()` and `box.error()`.
      
      Both used to take either `code, reason, <reason string args>` or
      `{code = code, reason = reason, ...}` arguments.
      
      Now in the first option instead of code a user can specify a
      string as its own error type. In the second option a user can
      specify both code and type. For example:
      
      ```Lua
      box.error('MyErrorType', 'Message')
      box.error({type = 'MyErrorType', code = 1024, reason = 'Message'})
      ```
      Or no-throw version:
      ```Lua
      box.error.new('MyErrorType', 'Message')
      box.error.new({type = 'MyErrorType', code = 1024, reason = 'Message'})
      ```
      When a custom type is specified, it is shown in `err.type`
      attribute. When it is not specified, `err.type` shows one of
      built-in errors such as 'ClientError', 'OurOfMemory', etc.
      
      Name length limit on the custom type is 63 bytes. All what is
      longer is truncated.
      
      Original error type can be checked using `err.base_type` member,
      although normally it should not be used. For user-defined types
      base type is 'CustomError'.
      
      For example:
      ```
      tarantool> e = box.error.new({type = 'MyErrorType', code = 1024, reason = 'Message'})
      ---
      ...
      
      tarantool> e:unpack()
      ---
      - code: 1024
        trace:
        - file: '[string "e = box.error.new({type = ''MyErrorType'', code..."]'
          line: 1
        type: MyErrorType
        custom_type: MyErrorType
        message: Message
        base_type: CustomError
      ...
      ```
      b728e7af
    • Alexander Turenko's avatar
      libev: backport select()'s limit workaround · 8acc011a
      Alexander Turenko authored
      As stated in the 'OS/X AND DARWIN BUGS' section of the libev
      documentation [1], kqueue() and poll() have known problems on Mac OS, so
      the library uses select() on Mac OS (it is the build time default). The
      library however uses the trick to overcome 1024 fds limit: libev sets
      the undocumented macro _DARWIN_UNLIMITED_SELECT, which enables linking
      against select() implementation without the limit.
      
      The magic macro stops working at some point around Mac OS 10.10 (see
      [2]), because it was defined after <sys/time.h> inclusion.  For recent
      Mac OS versions the macro has effect only when it is defined before
      <sys/time.h> inclusion.
      
      The macro definition was [moved][3] in libev 4.25. Excerpt from the
      changelog [4]:
      
       | 4.25 Fri Dec 21 07:49:20 CET 2018
       | <...>
       | - move the darwin select workaround higher in ev.c, as newer versions of
       |   darwin managed to break their broken select even more.
      
      More proper fix would be updating of libev to a newer version, however I
      would postpone it until a moment when we'll have a time to properly test
      everything with a new version of the library.
      
      [1]: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#OS_X_AND_DARWIN_BUGS
      [2]: http://lists.schmorp.de/pipermail/libev/2018q2/002788.html
      [3]: http://cvs.schmorp.de/libev/ev.c?r1=1.482&r2=1.483
      [4]: http://cvs.schmorp.de/libev/Changes?view=markup
      
      
      
      Fixes #3867
      Fixes #4673
      
      Investigated-by: default avatarMaria Khaydich <maria.khaydich@tarantool.org>
      Co-authored-by: default avatarMaria Khaydich <maria.khaydich@tarantool.org>
      8acc011a
    • Alexander Turenko's avatar
      popen: add popen Lua module · 6d1c5ff5
      Alexander Turenko authored
      
      Fixes #4031
      
      Co-developed-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      Co-developed-by: default avatarIgor Munkin <imun@tarantool.org>
      Acked-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      Reviewed-by: default avatarIgor Munkin <imun@tarantool.org>
      
      @TarantoolBot document
      Title: popen module
      
      ```
      Overview
      ========
      
      Tarantool supports execution of external programs similarly to well
      known Python's `subprocess` or Ruby's `Open3`. Note though the `popen`
      module does not match one to one to the helpers these languages provide
      and provides only basic functions. The popen object creation is
      implemented via `vfork()` system call which means the caller thread is
      blocked until execution of a child process begins.
      
      Module functions
      ================
      
      The `popen` module provides two functions to create that named popen
      object: `popen.shell` which is the similar to libc `popen` syscall and
      `popen.new` to create popen object with more specific options.
      
      `popen.shell(command[, mode]) -> handle, err`
      ---------------------------------------------
      
      Execute a shell command.
      
      @param command  a command to run, mandatory
      @param mode     communication mode, optional
                      'w'    to use ph:write()
                      'r'    to use ph:read()
                      'R'    to use ph:read({stderr = true})
                      nil    inherit parent's std* file descriptors
      
      Several mode characters can be set together: 'rw', 'rRw', etc.
      
      This function is just shortcut for popen.new({command}, opts)
      with opts.{shell,setsid,group_signal} set to `true` and
      and opts.{stdin,stdout,stderr} set based on `mode` parameter.
      
      All std* streams are inherited from parent by default if it is
      not changed using mode: 'r' for stdout, 'R' for stderr, 'w' for
      stdin.
      
      Raise an error on incorrect parameters:
      
      - IllegalParams: incorrect type or value of a parameter.
      
      Return a popen handle on success.
      
      Return `nil, err` on a failure.
      @see popen.new() for possible reasons.
      
      Example:
      
       | local popen = require('popen')
       |
       | -- Run the program and save its handle.
       | local ph = popen.shell('date', 'r')
       |
       | -- Read program's output, strip trailing newline.
       | local date = ph:read():rstrip()
       |
       | -- Free resources. The process is killed (but 'date'
       | -- exits itself anyway).
       | ph:close()
       |
       | print(date)
      
      Execute 'sh -c date' command, read the output and close the
      popen object.
      
      Unix defines a text file as a sequence of lines, each ends
      with the newline symbol. The same convention is usually
      applied for a text output of a command (so when it is
      redirected to a file, the file will be correct).
      
      However internally an application usually operates on
      strings, which are NOT newline terminated (e.g. literals
      for error messages). The newline is usually added right
      before a string is written to the outside world (stdout,
      console or log). :rstrip() in the example above is shown
      for this sake.
      
      `popen.new(argv[, opts]) -> handle, err`
      ----------------------------------------
      
      Execute a child program in a new process.
      
      @param argv  an array of a program to run with
                   command line options, mandatory;
                   absolute path to the program is required
                   when @a opts.shell is false (default)
      
      @param opts  table of options
      
      @param opts.stdin   action on STDIN_FILENO
      @param opts.stdout  action on STDOUT_FILENO
      @param opts.stderr  action on STDERR_FILENO
      
      File descriptor actions:
      
          popen.opts.INHERIT  (== 'inherit') [default]
                              inherit the fd from the parent
          popen.opts.DEVNULL  (== 'devnull')
                              open /dev/null on the fd
          popen.opts.CLOSE    (== 'close')
                              close the fd
          popen.opts.PIPE     (== 'pipe')
                              feed data from/to the fd to parent
                              using a pipe
      
      @param opts.env  a table of environment variables to
                       be used inside a process; key is a
                       variable name, value is a variable
                       value.
                       - when is not set then the current
                         environment is inherited;
                       - if set to an empty table then the
                         environment will be dropped
                       - if set then the environment will be
                         replaced
      
      @param opts.shell            (boolean, default: false)
             true                  run a child process via
                                   'sh -c "${opts.argv}"'
             false                 call the executable directly
      
      @param opts.setsid           (boolean, default: false)
             true                  run the program in a new
                                   session
             false                 run the program in the
                                   tarantool instance's
                                   session and process group
      
      @param opts.close_fds        (boolean, default: true)
             true                  close all inherited fds from a
                                   parent
             false                 don't do that
      
      @param opts.restore_signals  (boolean, default: true)
             true                  reset all signal actions
                                   modified in parent's process
             false                 inherit changed actions
      
      @param opts.group_signal     (boolean, default: false)
             true                  send signal to a child process
                                   group (only when opts.setsid is
                                   enabled)
             false                 send signal to a child process
                                   only
      
      @param opts.keep_child       (boolean, default: false)
             true                  don't send SIGKILL to a child
                                   process at freeing (by :close()
                                   or Lua GC)
             false                 send SIGKILL to a child process
                                   (or a process group if
                                   opts.group_signal is enabled) at
                                   :close() or collecting of the
                                   handle by Lua GC
      
      The returned handle provides :close() method to explicitly
      release all occupied resources (including the child process
      itself if @a opts.keep_child is not set). However if the
      method is not called for a handle during its lifetime, the
      same freeing actions will be triggered by Lua GC.
      
      It is recommended to use opts.setsid + opts.group_signal
      if a child process may spawn its own childs and they all
      should be killed together.
      
      Note: A signal will not be sent if the child process is
      already dead: otherwise we might kill another process that
      occupies the same PID later. This means that if the child
      process dies before its own childs, the function will not
      send a signal to the process group even when opts.setsid and
      opts.group_signal are set.
      
      Use os.environ() to pass copy of current environment with
      several replacements (see example 2 below).
      
      Raise an error on incorrect parameters:
      
      - IllegalParams: incorrect type or value of a parameter.
      - IllegalParams: group signal is set, while setsid is not.
      
      Return a popen handle on success.
      
      Return `nil, err` on a failure. Possible reasons:
      
      - SystemError: dup(), fcntl(), pipe(), vfork() or close()
                     fails in the parent process.
      - SystemError: (temporary restriction) the parent process
                     has closed stdin, stdout or stderr.
      - OutOfMemory: unable to allocate the handle or a temporary
                     buffer.
      
      Example 1:
      
       | local popen = require('popen')
       |
       | local ph = popen.new({'/bin/date'}, {
       |     stdout = popen.opts.PIPE,
       | })
       | local date = ph:read():rstrip()
       | ph:close()
       | print(date) -- Thu 16 Apr 2020 01:40:56 AM MSK
      
      Execute 'date' command, read the result and close the
      popen object.
      
      Example 2:
      
       | local popen = require('popen')
       |
       | local env = os.environ()
       | env['FOO'] = 'bar'
       |
       | local ph = popen.new({'echo "${FOO}"'}, {
       |     stdout = popen.opts.PIPE,
       |     shell = true,
       |     env = env,
       | })
       | local res = ph:read():rstrip()
       | ph:close()
       | print(res) -- bar
      
      It is quite similar to the previous one, but sets the
      environment variable and uses shell builtin 'echo' to
      show it.
      
      Example 3:
      
       | local popen = require('popen')
       |
       | local ph = popen.new({'echo hello >&2'}, { -- !!
       |     stderr = popen.opts.PIPE,              -- !!
       |     shell = true,
       | })
       | local res = ph:read({stderr = true}):rstrip()
       | ph:close()
       | print(res) -- hello
      
      This example demonstrates how to capture child's stderr.
      
      Example 4:
      
       | local function call_jq(input, filter)
       |     -- Start jq process, connect to stdin, stdout and stderr.
       |     local jq_argv = {'/usr/bin/jq', '-M', '--unbuffered', filter}
       |     local ph, err = popen.new(jq_argv, {
       |         stdin = popen.opts.PIPE,
       |         stdout = popen.opts.PIPE,
       |         stderr = popen.opts.PIPE,
       |     })
       |     if ph == nil then return nil, err end
       |
       |     -- Write input data to child's stdin and send EOF.
       |     local ok, err = ph:write(input)
       |     if not ok then return nil, err end
       |     ph:shutdown({stdin = true})
       |
       |     -- Read everything until EOF.
       |     local chunks = {}
       |     while true do
       |         local chunk, err = ph:read()
       |         if chunk == nil then
       |             ph:close()
       |             return nil, err
       |         end
       |         if chunk == '' then break end -- EOF
       |         table.insert(chunks, chunk)
       |     end
       |
       |     -- Read diagnostics from stderr if any.
       |     local err = ph:read({stderr = true})
       |     if err ~= '' then
       |         ph:close()
       |         return nil, err
       |     end
       |
       |     -- Glue all chunks, strip trailing newline.
       |     return table.concat(chunks):rstrip()
       | end
      
      Demonstrates how to run a stream program (like `grep`, `sed`
      and so), write to its stdin and read from its stdout.
      
      The example assumes that input data are small enough to fit
      a pipe buffer (typically 64 KiB, but depends on a platform
      and its configuration). It will stuck in :write() for large
      data. How to handle this case: call :read() in a loop in
      another fiber (start it before a first :write()).
      
      If a process writes large text to stderr, it may fill out
      stderr pipe buffer and stuck in write(2, ...). So we need
      to read stderr in a separate fiber to handle this case.
      
      Handle methods
      ==============
      
      `popen_handle:read([opts]) -> str, err`
      ---------------------------------------
      
      Read data from a child peer.
      
      @param handle        handle of a child process
      @param opts          an options table
      @param opts.stdout   whether to read from stdout, boolean
                           (default: true)
      @param opts.stderr   whether to read from stderr, boolean
                           (default: false)
      @param opts.timeout  time quota in seconds
                           (default: 100 years)
      
      Read data from stdout or stderr streams with @a timeout.
      By default it reads from stdout. Set @a opts.stderr to
      `true` to read from stderr.
      
      It is not possible to read from stdout and stderr both in
      one call. Set either @a opts.stdout or @a opts.stderr.
      
      Raise an error on incorrect parameters or when the fiber is
      cancelled:
      
      - IllegalParams:    incorrect type or value of a parameter.
      - IllegalParams:    called on a closed handle.
      - IllegalParams:    opts.stdout and opts.stderr are set both
      - IllegalParams:    a requested IO operation is not supported
                          by the handle (stdout / stderr is not
                          piped).
      - IllegalParams:    attempt to operate on a closed file
                          descriptor.
      - FiberIsCancelled: cancelled by an outside code.
      
      Return a string on success, an empty string at EOF.
      
      Return `nil, err` on a failure. Possible reasons:
      
      - SocketError: an IO error occurs at read().
      - TimedOut:    @a timeout quota is exceeded.
      - OutOfMemory: no memory space for a buffer to read into.
      - LuajitError: ("not enough memory"): no memory space for
                     the Lua string.
      
      `popen_handle:write(str[, opts]) -> str, err`
      ---------------------------------------------
      
      Write data to a child peer.
      
      @param handle        a handle of a child process
      @param str           a string to write
      @param opts          table of options
      @param opts.timeout  time quota in seconds
                           (default: 100 years)
      
      Write string @a str to stdin stream of a child process.
      
      The function may yield forever if a child process does
      not read data from stdin and a pipe buffer becomes full.
      Size of this buffer depends on a platform. Use
      @a opts.timeout when unsure.
      
      When @a opts.timeout is not set, the function blocks
      (yields the fiber) until all data is written or an error
      happened.
      
      Raise an error on incorrect parameters or when the fiber is
      cancelled:
      
      - IllegalParams:    incorrect type or value of a parameter.
      - IllegalParams:    called on a closed handle.
      - IllegalParams:    string length is greater then SSIZE_MAX.
      - IllegalParams:    a requested IO operation is not supported
                          by the handle (stdin is not piped).
      - IllegalParams:    attempt to operate on a closed file
                          descriptor.
      - FiberIsCancelled: cancelled by an outside code.
      
      Return `true` on success.
      
      Return `nil, err` on a failure. Possible reasons:
      
      - SocketError: an IO error occurs at write().
      - TimedOut:    @a timeout quota is exceeded.
      
      `popen_handle:shutdown(opts) -> true`
      ------------------------------------------
      
      Close parent's ends of std* fds.
      
      @param handle        handle of a child process
      @param opts          an options table
      @param opts.stdin    close parent's end of stdin, boolean
      @param opts.stdout   close parent's end of stdout, boolean
      @param opts.stderr   close parent's end of stderr, boolean
      
      The main reason to use this function is to send EOF to
      child's stdin. However parent's end of stdout / stderr
      may be closed too.
      
      The function does not fail on already closed fds (idempotence).
      However it fails on attempt to close the end of a pipe that was
      never exist. In other words, only those std* options that
      were set to popen.opts.PIPE at a handle creation may be used
      here (for popen.shell: 'r' corresponds to stdout, 'R' to stderr
      and 'w' to stdin).
      
      The function does not close any fds on a failure: either all
      requested fds are closed or neither of them.
      
      Example:
      
       | local popen = require('popen')
       |
       | local ph = popen.shell('sed s/foo/bar/', 'rw')
       | ph:write('lorem foo ipsum')
       | ph:shutdown({stdin = true})
       | local res = ph:read()
       | ph:close()
       | print(res) -- lorem bar ipsum
      
      Raise an error on incorrect parameters:
      
      - IllegalParams:  an incorrect handle parameter.
      - IllegalParams:  called on a closed handle.
      - IllegalParams:  neither stdin, stdout nor stderr is choosen.
      - IllegalParams:  a requested IO operation is not supported
                        by the handle (one of std* is not piped).
      
      Return `true` on success.
      
      `popen_handle:terminate() -> ok, err`
      -------------------------------------
      
      Send SIGTERM signal to a child process.
      
      @param handle  a handle carries child process to terminate
      
      The function only sends SIGTERM signal and does NOT
      free any resources (popen handle memory and file
      descriptors).
      
      @see popen_handle:signal() for errors and return values.
      
      `popen_handle:kill() -> ok, err`
      --------------------------------
      
      Send SIGKILL signal to a child process.
      
      @param handle  a handle carries child process to kill
      
      The function only sends SIGKILL signal and does NOT
      free any resources (popen handle memory and file
      descriptors).
      
      @see popen_handle:signal() for errors and return values.
      
      `popen_handle:signal(signo) -> ok, err`
      ---------------------------------------
      
      Send signal to a child process.
      
      @param handle  a handle carries child process to be signaled
      @param signo   signal number to send
      
      When opts.setsid and opts.group_signal are set on the handle
      the signal is sent to the process group rather than to the
      process. @see popen.new() for details about group
      signaling.
      
      Note: The module offers popen.signal.SIG* constants, because
      some signals have different numbers on different platforms.
      
      Raise an error on incorrect parameters:
      
      - IllegalParams:    an incorrect handle parameter.
      - IllegalParams:    called on a closed handle.
      
      Return `true` if signal is sent.
      
      Return `nil, err` on a failure. Possible reasons:
      
      - SystemError: a process does not exists anymore
      
                     Aside of a non-exist process it is also
                     returned for a zombie process or when all
                     processes in a group are zombies (but
                     see note re Mac OS below).
      
      - SystemError: invalid signal number
      
      - SystemError: no permission to send a signal to
                     a process or a process group
      
                     It is returned on Mac OS when a signal is
                     sent to a process group, where a group leader
                     is zombie (or when all processes in it
                     are zombies, don't sure).
      
                     Whether it may appear due to other
                     reasons is unclear.
      
      `popen_handle:info() -> res`
      ----------------------------
      
      Return information about popen handle.
      
      @param handle  a handle of a child process
      
      Raise an error on incorrect parameters:
      
      - IllegalParams: an incorrect handle parameter.
      - IllegalParams: called on a closed handle.
      
      Return information about the handle in the following
      format:
      
          {
              pid = <number> or <nil>,
              command = <string>,
              opts = <table>,
              status = <table>,
              stdin = one-of(
                  popen.stream.OPEN   (== 'open'),
                  popen.stream.CLOSED (== 'closed'),
                  nil,
              ),
              stdout = one-of(
                  popen.stream.OPEN   (== 'open'),
                  popen.stream.CLOSED (== 'closed'),
                  nil,
              ),
              stderr = one-of(
                  popen.stream.OPEN   (== 'open'),
                  popen.stream.CLOSED (== 'closed'),
                  nil,
              ),
          }
      
      `pid` is a process id of the process when it is alive,
      otherwise `pid` is nil.
      
      `command` is a concatenation of space separated arguments
      that were passed to execve(). Multiword arguments are quoted.
      Quotes inside arguments are not escaped.
      
      `opts` is a table of handle options in the format of
      popen.new() `opts` parameter. `opts.env` is not shown here,
      because the environment variables map is not stored in a
      handle.
      
      `status` is a table that represents a process status in the
      following format:
      
          {
              state = one-of(
                  popen.state.ALIVE    (== 'alive'),
                  popen.state.EXITED   (== 'exited'),
                  popen.state.SIGNALED (== 'signaled'),
              )
      
              -- Present when `state` is 'exited'.
              exit_code = <number>,
      
              -- Present when `state` is 'signaled'.
              signo = <number>,
              signame = <string>,
          }
      
      `stdin`, `stdout`, `stderr` reflect status of parent's end
      of a piped stream. When a stream is not piped the field is
      not present (`nil`). When it is piped, the status may be
      one of the following:
      
      - popen.stream.OPEN    (== 'open')
      - popen.stream.CLOSED  (== 'closed')
      
      The status may be changed from 'open' to 'closed'
      by :shutdown({std... = true}) call.
      
      Example 1 (tarantool console):
      
       | tarantool> require('popen').new({'/usr/bin/touch', '/tmp/foo'})
       | ---
       | - command: /usr/bin/touch /tmp/foo
       |   status:
       |     state: alive
       |   opts:
       |     stdout: inherit
       |     stdin: inherit
       |     group_signal: false
       |     keep_child: false
       |     close_fds: true
       |     restore_signals: true
       |     shell: false
       |     setsid: false
       |     stderr: inherit
       |   pid: 9499
       | ...
      
      Example 2 (tarantool console):
      
       | tarantool> require('popen').shell('grep foo', 'wrR')
       | ---
       | - stdout: open
       |   command: sh -c 'grep foo'
       |   stderr: open
       |   status:
       |     state: alive
       |   stdin: open
       |   opts:
       |     stdout: pipe
       |     stdin: pipe
       |     group_signal: true
       |     keep_child: false
       |     close_fds: true
       |     restore_signals: true
       |     shell: true
       |     setsid: true
       |     stderr: pipe
       |   pid: 10497
       | ...
      
      `popen_handle:wait() -> res`
      ----------------------------
      
      Wait until a child process get exited or signaled.
      
      @param handle  a handle of process to wait
      
      Raise an error on incorrect parameters or when the fiber is
      cancelled:
      
      - IllegalParams:    an incorrect handle parameter.
      - IllegalParams:    called on a closed handle.
      - FiberIsCancelled: cancelled by an outside code.
      
      Return a process status table (the same as ph.status and
      ph.info().status). @see popen_handle:info() for the format
      of the table.
      
      `popen_handle:close() -> ok, err`
      ---------------------------------
      
      Close a popen handle.
      
      @param handle  a handle to close
      
      Basically it kills a process using SIGKILL and releases all
      resources assosiated with the popen handle.
      
      Details about signaling:
      
      - The signal is sent only when opts.keep_child is not set.
      - The signal is sent only when a process is alive according
        to the information available on current even loop iteration.
        (There is a gap here: a zombie may be signaled; it is
        harmless.)
      - The signal is sent to a process or a grocess group depending
        of opts.group_signal. (@see lbox_popen_new() for details of
        group signaling).
      
      Resources are released disregarding of whether a signal
      sending succeeds: fds are closed, memory is released,
      the handle is marked as closed.
      
      No operation is possible on a closed handle except
      :close(), which always successful on closed handle
      (idempotence).
      
      Raise an error on incorrect parameters:
      
      - IllegalParams: an incorrect handle parameter.
      
      The function may return `true` or `nil, err`, but it always
      frees the handle resources. So any return value usually
      means success for a caller. The return values are purely
      informational: it is for logging or same kind of reporting.
      
      Possible diagnostics (don't consider them as errors):
      
      - SystemError: no permission to send a signal to
                     a process or a process group
      
                     This diagnostics may appear due to
                     Mac OS behaviour on zombies when
                     opts.group_signal is set,
                     @see lbox_popen_signal().
      
                     Whether it may appear due to other
                     reasons is unclear.
      
      Always return `true` when a process is known as dead (say,
      after ph:wait()): no signal will be send, so no 'failure'
      may appear.
      
      Handle fields
      =============
      
      - popen_handle.pid
      - popen_handle.command
      - popen_handle.opts
      - popen_handle.status
      - popen_handle.stdin
      - popen_handle.stdout
      - popen_handle.stderr
      
      See popen_handle:info() for description of those fields.
      
      Module constants
      ================
      
      - popen.opts
        - INHERIT (== 'inherit')
        - DEVNULL (== 'devnull')
        - CLOSE   (== 'close')
        - PIPE    (== 'pipe')
      
      - popen.signal
        - SIGTERM (== 9)
        - SIGKILL (== 15)
        - ...
      
      - popen.state
        - ALIVE    (== 'alive')
        - EXITED   (== 'exited')
        - SIGNALED (== 'signaled')
      
      - popen.stream
        - OPEN    (== 'open')
        - CLOSED  (== 'closed')
      ```
      
      (cherry picked from commit 34c2789b48eadd36da742f7f761a998889e70544)
      6d1c5ff5
    • Alexander Turenko's avatar
      popen: always free resources in popen_delete() · 01657bfb
      Alexander Turenko authored
      The function still set a diagnostics when a signal sending fails and
      returns -1, but it is purely informational result: for logging or so. It
      reflects notes about dealing with failures in Linux's `man 2 close`:
      
       | Note, however, that a failure return should be used only for
       | diagnostic purposes <...> or remedial purposes <...>.
       |
       | <...> Linux  kernel always releases the file descriptor early in the
       | close operation, freeing it for reuse; the steps that may return an
       | error <...> occur only later in the close operation.
       |
       | Many other implementations similarly always close the file descriptor
       | <...> even if they subsequently report an error on return from
       | close(). POSIX.1 is currently silent on this point, but there are
       | plans to mandate this behavior in the next major release of the
       | standard.
      
      When kill or killpg returns EPERM a caller usually unable to overcome it
      somehow: retrying is not sufficient here. So there are no needs to keep
      the handle: a caller refuses the handle and don't want to perform any
      other operation on it.  The open engine do its best to kill a child
      process or a process group, but when it is not possible, just set the a
      diagnostic and free handle resources anyway.
      
      Left comments about observed Mac OS behaviour regarding killing a
      process group, where all processes are zombies (or just when a process
      group leader is zombie, don't sure): it gives EPERM instead of ESRCH
      from killpg(). This result should not surprise a user, so it should be
      documented. See [1] for another description of the problem (I don't find
      any official information about this).
      
      [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1329528
      
      
      
      Part of #4031
      
      Acked-by: default avatarCyrill Gorcunov <gorcunov@gmail.com>
      (cherry picked from commit 56a8c346ecb0581300a63c6e677d8a4672ff1f95)
      01657bfb
    • Alexander Turenko's avatar
      net.box: fix fetching of schema of an old version · 06edcbe1
      Alexander Turenko authored
      After 2.2.0-633-gaa0964ae1 ('net.box: fix schema fetching from 1.10/2.1
      servers') net.box expects that _vcollation system view exists on a
      tarantool server of 2.2.1+ version. This is however not always so: a
      server may be run on a new version of tarantool, but work on a schema of
      an old version.
      
      The situation with non last schema is usual for replication cluster in
      process of upgrading: all instances run on the new version of tarantool
      first (no auto-upgrade is performed by tarantools in a cluster). Then
      box.schema.upgrade() should be called, but the instances should be
      operable even before the call.
      
      Before the commit net.box was unable to connect a server if it is run on
      a schema without _vcollation system view (say, 2.1.3), but the server
      executable is of 2.2.1 version or newer.
      
      Note: I trim tests from the commit to polish them a bit more, but
      include the fix itself to 2.4.1 release.
      
      Follows up #4307
      Fixes #4691
      06edcbe1
    • Igor Munkin's avatar
      test: adjust luajit-tap testing machinery · 335f80a0
      Igor Munkin authored
      
      This changeset makes possible to run luajit-tap tests requiring
      libraries implemented in C:
      * symlink to luajit test is created on configuration phase instead of
        build one.
      * introduced a CMake function for building shared libraries required for
        luajit tests.
      
      Furthermore this commit enables CMake build for the following luajit-tap
      tests:
      * gh-4427-ffi-sandwich
      * lj-flush-on-trace
      
      Reviewed-by: default avatarVladislav Shpilevoy <v.shpilevoy@tarantool.org>
      Reviewed-by: default avatarSergey Ostanevich <sergos@tarantool.org>
      Signed-off-by: default avatarIgor Munkin <imun@tarantool.org>
      335f80a0
    • Kirill Yukhin's avatar
      luajit: bump new version · a1594091
      Kirill Yukhin authored
      
      - jit: abort trace execution on JIT mode change
      - jit: abort trace recording and execution for C API
      
      Reviewed-by: default avatarVladislav Shpilevoy <v.shpilevoy@tarantool.org>
      Signed-off-by: default avatarIgor Munkin <imun@tarantool.org>
      a1594091
  2. Apr 18, 2020
    • Alexander V. Tikhonov's avatar
      build: fix build with CMake 2 · 930c7463
      Alexander V. Tikhonov authored
      Found that some package builds failed on the mistake in CMakeLists.txt
      file, the failed packages and test builds were:
      - CentOS 6
      - CentOS 7
      - Ubuntu 14.04
      and static build based on Dockerfile.
      
      The core of the issue is that CMake 2 does not support line continuation
      with backslash.
      
      The commit fixes the regression from
      7b443650 ('feedback: add cmake option to
      disable the daemon').
      
      Follow up #3308
      930c7463
  3. Apr 17, 2020
  4. Apr 16, 2020
    • Mergen Imeev's avatar
      sql: do not change order of inserted values · 2cc7e608
      Mergen Imeev authored
      Before this patch, if an ephemeral space was used during INSERT or
      REPLACE, the inserted values were sorted by the first column,
      since this was the first part of the index. This can lead to an
      error when using the AUTOINCREMENT feature, since changing the
      order of the inserted value can change the value inserted instead
      of NULL. To avoid this, the patch makes the rowid of the inserted
      row in the ephemeral space the only part of the ephemeral space
      index.
      
      Closes #4256
      2cc7e608
    • Mergen Imeev's avatar
      sql: specify field types in ephemeral space format · 2103f587
      Mergen Imeev authored
      This patch specifies field types in ephemeral space format in SQL.
      Prior to this patch, all fields had a SCALAR field type.
      
      This patch allows us to not use the primary index to obtain field
      types, since now the ephemeral space has field types in the
      format. This allows us to change the structure of the primary
      index, which helps to solve the issue #4256. In addition, since we
      can now set the field types of the ephemeral space, we can use
      this feature to set the field types according to the left value of
      the IN operator. This will fix issue #4692.
      
      Needed for #4256
      Needed for #4692
      Closes #3841
      2103f587
    • Mergen Imeev's avatar
      box: extend ephemeral space format · 032de39f
      Mergen Imeev authored
      This patch allows to set field types and names in ephemeral space
      formats.
      
      Needed for #4256
      Needed for #4692
      Part of #3841
      032de39f
    • Serge Petrenko's avatar
      relay: move relay_schedule_pending_gc before status update · e7ffddce
      Serge Petrenko authored
      relay_schedule_pending_gc() is executed after relay status update,
      which made perfect sense before we've introduced local spaces rework, making
      local space operations use a special instance id: 0.
      Relay status update is performed only when the remote instance has
      reported a bigger vclock, than its previous one. However, we may have an
      entire WAL file filled with local space changes, in which case the
      changes won't be transmitted to replica, and it will report the same
      vclock as before, postponing the scheduled gc until a non-local row is
      created on master.
      
      Fix this by reordering relay_schedule_pending_gc() and relay status
      update. In case nothing new is added to pending_gc queue and replica
      clock is not updated, relay_schedule_pending_gc() will exit on the first
      loop iteration, so it doesn't add an overhead.
      
      Also make relay_schedule_pending_gc() use vclock_compare_ignore0() instead
      of plain vclock_compare().
      
      Follow-up #4114
      e7ffddce
  5. Apr 15, 2020
    • Mergen Imeev's avatar
      sql: add '\0' to the BLOB when it is cast to INTEGER · a39e6a01
      Mergen Imeev authored
      Prior to this patch, due to the absence of the '\0' character at
      the end of the BLOB, it was possible to get an error or incorrect
      result when using CAST() from BLOB to INTEGER or UNSIGNED. This
      has now been fixed, but the maximum length of a BLOB that could be
      cast to INTEGER or UNSIGNED was limited to 12287 bytes.
      
      Examples of wrong CAST() from BLOB to INTEGER:
      
      CREATE TABLE t (i INT PRIMARY KEY, a VARBINARY, b INT, c INT);
      INSERT INTO t VALUES (1, X'33', 0x33, 0x00), (2, X'34', 0x41, 0);
      
      Example of wrong result:
      
      SELECT CAST(a AS INTEGER) FROM t WHERE i = 1;
      
      Result: 33
      
      Example of error during CAST():
      
      SELECT CAST(a AS INTEGER) FROM t WHERE i = 2;
      
      Result: 'Type mismatch: can not convert varbinary to integer'
      
      Closes #4766
      a39e6a01
    • Mergen Imeev's avatar
      sql: fix implicit cast from STRING to INTEGER · 6e6de43c
      Mergen Imeev authored
      Prior to this patch, STRING, which contains the DOUBLE value,
      could be implicitly cast to INTEGER. This was done by converting
      STRING to DOUBLE and then converting this DOUBLE value to INTEGER.
      This may affect the accuracy of CAST(), so it was forbidden. It
      is worth noting that these changes will not affect the comparison,
      since the implicit cast in this case has different mechanics.
      
      Example:
      box.execute("CREATE TABLE t(i INT PRIMARY KEY);")
      
      Before patch:
      box.execute("INSERT INTO t VALUES ('111.1');")
      box.execute("SELECT * FROM t;")
      Result: 111
      
      After patch:
      box.execute("INSERT INTO t VALUES ('1.1');")
      Result: 'Type mismatch: can not convert 1.1 to integer'
      
      box.execute("INSERT INTO t VALUES ('1.0');")
      Result: 'Type mismatch: can not convert 1.0 to integer'
      
      box.execute("INSERT INTO t VALUES ('1.');")
      Result: 'Type mismatch: can not convert 1. to integer'
      
      @TarantoolBot document
      Title: disallow cast from STRING contains DOUBLE to INTEGER
      
      After the last two patches, explicit and implicit casting from the
      string containing DOUBLE to INTEGER directly will be prohibited.
      The user must use the explicit cast to DOUBLE before the explicit
      or implicit cast to INTEGER. The reason for this is that before
      these patches, such STRINGs were implicitly cast to DOUBLE, and
      then this DOUBLE was implicitly or explicitly cast to INTEGER.
      Because of this, the result of such a cast may differ from what
      the user expects, and the user may not know why.
      
      It is worth noting that these changes will not affect the
      comparison, since the implicit cast in this case has different
      mechanics.
      
      Example for implicit cast:
      
      box.execute("CREATE TABLE t(i INT PRIMARY KEY);")
      -- Does not work anymore:
      box.execute("INSERT INTO t VALUES ('1.1');")
      -- Right way:
      box.execute("INSERT INTO t VALUES (CAST('1.1' AS DOUBLE));")
      
      Example for explicit cast:
      
      -- Does not work anymore:
      box.execute("SELECT CAST('1.1' AS INTEGER);")
      -- Right way:
      box.execute("SELECT CAST(CAST('1.1' AS DOUBLE) AS INTEGER);")
      6e6de43c
    • Mergen Imeev's avatar
      sql: fix CAST() from STRING to INTEGER · 11352a32
      Mergen Imeev authored
      Prior to this patch, STRING, which contains the DOUBLE value,
      could be cast to INTEGER. This was done by converting STRING to
      DOUBLE and then converting this DOUBLE value to INTEGER. This may
      affect the accuracy of CAST(), so it was forbidden.
      
      Before patch:
      box.execute("SELECT CAST('111.1' as INTEGER);")
      Result: 111
      
      After patch:
      box.execute("SELECT CAST('1.1' as INTEGER);")
      Result: 'Type mismatch: can not convert 1.1 to integer'
      
      box.execute("SELECT CAST('1.0' as INTEGER);")
      Result: 'Type mismatch: can not convert 1.0 to integer'
      
      box.execute("SELECT CAST('1.' as INTEGER);")
      Result: 'Type mismatch: can not convert 1. to integer'
      11352a32
    • Alexander V. Tikhonov's avatar
      gitlab-ci: move sources tarball creation to gitlab · 34f87bc6
      Alexander V. Tikhonov authored
      Moved sources tarball creation from travis-ci to gitlab-ci,
      moved its jobs for sources packing and sources deploying.
      
      Close #4895
      34f87bc6
Loading