Beispiel #1
0
    def _extract_rpc_error(self, exec_method, error):
        """Raises a VitessError for a proto3 vtrpc.RPCError structure, if set.

    Args:
      exec_method: name of the method to use in VitessError.
      error: vtrpc.RPCError structure.

    Raises:
      vtgate_utils.VitessError: if an error was set.
    """
        if error.code:
            raise vtgate_utils.VitessError(exec_method, error.code,
                                           error.message)
        elif error.legacy_code:
            raise vtgate_utils.VitessError(
                exec_method, legacy_code_to_code_map[error.legacy_code],
                error.message)
Beispiel #2
0
    def _stream_execute(self,
                        sql,
                        bind_variables,
                        keyspace_name,
                        tablet_type,
                        keyspace_ids=None,
                        keyranges=None,
                        not_in_transaction=False,
                        effective_caller_id=None):
        """Return a generator and the fields for the response.

    This method takes ownership of self.client, since multiple
    stream_executes can be active at once.

    Args:
      sql: Str sql.
      bind_variables: A (str: value) dict.
      keyspace_name: Str keyspace name.
      tablet_type: Str tablet_type.
      keyspace_ids: List of uint64 or bytes keyspace_ids.
      keyranges: KeyRange objects.
      not_in_transaction: bool.
      effective_caller_id: CallerID.

    Returns:
      Generator, fields pair.

    Raises:
      dbexceptions.ProgrammingError: On bad input.
    """
        exec_method = None
        req = None
        if keyspace_ids is not None:
            req = _create_req_with_keyspace_ids(sql, bind_variables,
                                                keyspace_name, tablet_type,
                                                keyspace_ids,
                                                not_in_transaction)
            exec_method = 'VTGate.StreamExecuteKeyspaceIds2'
        elif keyranges is not None:
            req = _create_req_with_keyranges(sql, bind_variables,
                                             keyspace_name, tablet_type,
                                             keyranges, not_in_transaction)
            exec_method = 'VTGate.StreamExecuteKeyRanges2'
        else:
            raise dbexceptions.ProgrammingError(
                '_stream_execute called without specifying keyspace_ids or keyranges'
            )

        self._add_caller_id(req, effective_caller_id)

        stream_fields = []
        stream_conversions = []
        rpc_client = self._create_client()
        try:
            rpc_client.dial()
        except gorpc.GoRpcError as e:
            raise self._convert_exception(e)

        def drain_conn_after_streaming_app_error():
            """Drain connection of all incoming streaming packets (ignoring them).

      This is necessary for streaming calls which return application
      errors inside the RPC response (instead of through the usual GoRPC
      error return).  This is because GoRPC always expects the last
      packet to be an error; either the usual GoRPC application error
      return, or a special "end-of-stream" error.

      If an application error is returned with the RPC response, there
      will still be at least one more packet coming, as GoRPC has not
      seen anything that it considers to be an error. If the connection
      is not drained of this last packet, future reads from the wire
      will be off by one and will return errors.
      """
            next_result = rpc_client.stream_next()
            if next_result is not None:
                rpc_client.close()
                raise gorpc.GoRpcError(
                    'Connection should only have one packet remaining'
                    ' after streaming app error in RPC response.')

        try:
            rpc_client.stream_call(exec_method, req)
            first_response = rpc_client.stream_next()
            if first_response:
                if first_response.reply.get('Err'):
                    drain_conn_after_streaming_app_error()
                    raise vtgate_utils.VitessError(exec_method,
                                                   first_response.reply['Err'])
                reply = first_response.reply['Result']
                for field in reply['Fields']:
                    stream_fields.append((field['Name'], field['Type']))
                    stream_conversions.append(
                        field_types.conversions.get(field['Type']))
        except (gorpc.GoRpcError, vtgate_utils.VitessError) as e:
            self.logger_object.log_private_data(bind_variables)
            raise self._convert_exception(e,
                                          sql,
                                          keyspace_ids,
                                          keyranges,
                                          keyspace=keyspace_name,
                                          tablet_type=tablet_type)
        except Exception:
            logging.exception('gorpc low-level error')
            raise

        def row_generator():
            try:
                while True:
                    stream_result = rpc_client.stream_next()
                    if stream_result is None:
                        break
                    if stream_result.reply.get('Result'):
                        for result_item in stream_result.reply['Result'][
                                'Rows']:
                            yield tuple(
                                _make_row(result_item, stream_conversions))
            except gorpc.GoRpcError as e:
                raise self._convert_exception(e)
            except Exception:
                logging.exception('gorpc low-level error')
                raise
            finally:
                rpc_client.close()

        return row_generator(), stream_fields
Beispiel #3
0
    def _stream_execute(self,
                        sql,
                        bind_variables,
                        tablet_type,
                        not_in_transaction=False):
        """Start a streaming query via VTGate.StreamExecute2."""
        req = _create_req(sql, bind_variables, tablet_type, not_in_transaction)
        self._add_session(req)

        rpc_client = self._get_client()

        stream_fields = []
        stream_conversions = []

        def drain_conn_after_streaming_app_error():
            """Drain connection of all incoming streaming packets (ignoring them).

      This is necessary for streaming calls which return application
      errors inside the RPC response (instead of through the usual GoRPC
      error return).  This is because GoRPC always expects the last
      packet to be an error; either the usual GoRPC application error
      return, or a special "end-of-stream" error.

      If an application error is returned with the RPC response, there
      will still be at least one more packet coming, as GoRPC has not
      seen anything that it considers to be an error. If the connection
      is not drained of this last packet, future reads from the wire
      will be off by one and will return errors.
      """
            next_result = rpc_client.stream_next()
            if next_result is not None:
                rpc_client.close()
                raise gorpc.GoRpcError(
                    'Connection should only have one packet remaining'
                    ' after streaming app error in RPC response.')

        try:
            method_name = 'VTGate.StreamExecute2'
            rpc_client.stream_call(method_name, req)
            first_response = rpc_client.stream_next()
            if first_response.reply.get('Err'):
                drain_conn_after_streaming_app_error()
                raise vtgate_utils.VitessError(method_name,
                                               first_response.reply['Err'])
            reply = first_response.reply['Result']

            for field in reply['Fields']:
                stream_fields.append((field['Name'], field['Type']))
                stream_conversions.append(
                    field_types.conversions.get(field['Type']))
        except (gorpc.GoRpcError, vtgate_utils.VitessError) as e:
            self.logger_object.log_private_data(bind_variables)
            raise convert_exception(e, str(self), sql)
        except Exception:
            logging.exception('gorpc low-level error')
            raise

        # Take the BsonRpcClient from VTGateConnection. The row_generator
        # will manage the BsonRpcClient. This VTGateConnection will connect
        # to a new client if needed.
        self.client = None

        def row_generator():
            try:
                while True:
                    try:
                        stream_result = rpc_client.stream_next()
                        if stream_result is None:
                            break
                        # A session message, if any comes separately with no rows.
                        # I am not sure if we can ignore this.
                        if stream_result.reply.get('Session'):
                            self.session = stream_result.reply['Session']
                        else:
                            for result_item in stream_result.reply['Result'][
                                    'Rows']:
                                yield tuple(
                                    _make_row(result_item, stream_conversions))
                    except gorpc.GoRpcError as e:
                        raise convert_exception(e, str(self))
                    except Exception:
                        logging.exception('gorpc low-level error')
                        raise
            finally:
                rpc_client.close()

        return row_generator(), stream_fields
Beispiel #4
0
def _extract_rpc_error(exec_method, error):
    if error.code:
        raise vtgate_utils.VitessError(exec_method, {
            'Code': error.code,
            'Message': error.message,
        })