Exemplo n.º 1
0
    def rollback_to_savepoint(self, name: str) -> TransactionState:
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        for sp in reversed(self._savepoints.values()):
            if sp.name == name:
                self._current = sp
                return sp

        raise errors.TransactionError(f'there is no {name!r} savepoint')
Exemplo n.º 2
0
    def release_savepoint(self, name: str):
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        for sp in reversed(self._savepoints.values()):
            if sp.name == name:
                sp_id = sp.id
                break
        else:
            raise errors.TransactionError(f'there is no {name!r} savepoint')

        self._savepoints.pop(sp_id)
Exemplo n.º 3
0
    async def try_compile_rollback(self, dbver: int, eql: bytes):
        statements = edgeql.parse_block(eql.decode())

        stmt = statements[0]
        unit = None
        if isinstance(stmt, qlast.RollbackTransaction):
            sql = b'ROLLBACK;'
            unit = dbstate.QueryUnit(dbver=dbver,
                                     status=b'ROLLBACK',
                                     sql=(sql, ),
                                     tx_rollback=True,
                                     cacheable=False)

        elif isinstance(stmt, qlast.RollbackToSavepoint):
            sql = f'ROLLBACK TO {pg_common.quote_ident(stmt.name)};'.encode()
            unit = dbstate.QueryUnit(dbver=dbver,
                                     status=b'ROLLBACK TO SAVEPOINT',
                                     sql=(sql, ),
                                     tx_savepoint_rollback=True,
                                     cacheable=False)

        if unit is not None:
            return unit, len(statements) - 1

        raise errors.TransactionError(
            'expected a ROLLBACK or ROLLBACK TO SAVEPOINT command'
        )  # pragma: no cover
Exemplo n.º 4
0
    def declare_savepoint(self, name: str):
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        sp_id = time.monotonic_ns()

        # Save the savepoint state so that we can rollback to it.
        self._stack.append(
            TransactionState(id=sp_id,
                             name=name,
                             schema=self.get_schema(),
                             modaliases=self.get_modaliases(),
                             config=self.get_session_config()))

        # The top of the stack is the "current" state.
        self._stack.append(
            TransactionState(id=sp_id,
                             name=None,
                             schema=self.get_schema(),
                             modaliases=self.get_modaliases(),
                             config=self.get_session_config()))

        copy = self.copy()
        self._constate._savepoints_log[sp_id] = copy

        return sp_id
Exemplo n.º 5
0
    def release_savepoint(self, name: str):
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        new_stack = []
        released = False
        for st in reversed(self._stack):
            if not released and st.name == name:
                released = True
                continue
            else:
                new_stack.append(st)
        if not released:
            raise errors.TransactionError(f'there is no {name!r} savepoint')
        else:
            self._stack = new_stack[::-1]
Exemplo n.º 6
0
    def rollback_to_savepoint(self, name: str):
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        new_stack = self._stack.copy()
        while new_stack:
            top_new_state = new_stack[-1]
            if top_new_state.name == name:
                self._stack = new_stack
                # Add a nameless copy of the savepoint's state -- new
                # "working" state.
                self._stack.append(self._stack[-1]._replace(name=None))
                return self._stack[-1]
            else:
                new_stack.pop()
        raise errors.TransactionError(f'there is no {name!r} savepoint')
Exemplo n.º 7
0
    def rollback_to_savepoint(self, name: str) -> TransactionState:
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        sp_ids_to_erase = []
        for sp in reversed(self._savepoints.values()):
            if sp.name == name:
                self._current = sp
                break

            sp_ids_to_erase.append(sp.id)
        else:
            raise errors.TransactionError(f'there is no {name!r} savepoint')

        for sp_id in sp_ids_to_erase:
            self._savepoints.pop(sp_id)

        return sp
Exemplo n.º 8
0
    def commit_tx(self):
        if self._current_tx.is_implicit():
            raise errors.TransactionError('cannot commit: not in transaction')

        latest_state = self._current_tx._stack[-1]

        self._init_current_tx(latest_state.schema, latest_state.modaliases,
                              latest_state.config)

        return latest_state
Exemplo n.º 9
0
    def declare_savepoint(self, name: str):
        if self.is_implicit():
            raise errors.TransactionError(
                'savepoints can only be used in transaction blocks')

        sp_id = self._constate._new_txid()
        sp_state = self._current._replace(id=sp_id, name=name)
        self._savepoints[sp_id] = sp_state
        self._constate._savepoints_log[sp_id] = sp_state
        return sp_id
Exemplo n.º 10
0
    def commit_tx(self):
        if self._current_tx.is_implicit():
            raise errors.TransactionError('cannot commit: not in transaction')

        latest_state = self._current_tx._current

        self._init_current_tx(
            user_schema=latest_state.user_schema,
            global_schema=latest_state.global_schema,
            modaliases=latest_state.modaliases,
            session_config=latest_state.session_config,
            database_config=latest_state.database_config,
            system_config=latest_state.system_config,
            cached_reflection=latest_state.cached_reflection,
        )

        return latest_state
Exemplo n.º 11
0
 def start_tx(self):
     if self._current_tx.is_implicit():
         self._current_tx.make_explicit()
     else:
         raise errors.TransactionError('already in transaction')
Exemplo n.º 12
0
def static_interpret_backend_error(fields):
    err_details = get_error_details(fields)
    # handle some generic errors if possible
    err = get_generic_exception_from_err_details(err_details)
    if err is not None:
        return err

    if err_details.code == PGErrorCode.NotNullViolationError:
        if err_details.table_name or err_details.column_name:
            return SchemaRequired

        else:
            return errors.InternalServerError(err_details.message)

    elif err_details.code in constraint_errors:
        source = pointer = None

        for errtype, ere in constraint_res.items():
            m = ere.match(err_details.message)
            if m:
                error_type = errtype
                break
        else:
            return errors.InternalServerError(err_details.message)

        if error_type == 'cardinality':
            return errors.CardinalityViolationError('cardinality violation',
                                                    source=source,
                                                    pointer=pointer)

        elif error_type == 'link_target':
            if err_details.detail_json:
                srcname = err_details.detail_json.get('source')
                ptrname = err_details.detail_json.get('pointer')
                target = err_details.detail_json.get('target')
                expected = err_details.detail_json.get('expected')

                if srcname and ptrname:
                    srcname = sn.QualName.from_string(srcname)
                    ptrname = sn.QualName.from_string(ptrname)
                    lname = '{}.{}'.format(srcname, ptrname.name)
                else:
                    lname = ''

                msg = (f'invalid target for link {lname!r}: {target!r} '
                       f'(expecting {expected!r})')

            else:
                msg = 'invalid target for link'

            return errors.UnknownLinkError(msg)

        elif error_type == 'link_target_del':
            return errors.ConstraintViolationError(err_details.message,
                                                   details=err_details.detail)

        elif error_type == 'constraint':
            if err_details.constraint_name is None:
                return errors.InternalServerError(err_details.message)

            constraint_id, _, _ = err_details.constraint_name.rpartition(';')

            try:
                constraint_id = uuidgen.UUID(constraint_id)
            except ValueError:
                return errors.InternalServerError(err_details.message)

            return SchemaRequired

        elif error_type == 'newconstraint':
            # We can reconstruct what went wrong from the schema_name,
            # table_name, and column_name. But we don't expect
            # constraint_name to be present (because the constraint is
            # not yet present in the schema?).
            if (err_details.schema_name and err_details.table_name
                    and err_details.column_name):
                return SchemaRequired

            else:
                return errors.InternalServerError(err_details.message)

        elif error_type == 'scalar':
            return SchemaRequired

        elif error_type == 'id':
            return errors.ConstraintViolationError(
                'unique link constraint violation')

    elif err_details.code in SCHEMA_CODES:
        if err_details.code == PGErrorCode.InvalidDatetimeFormatError:
            hint = None
            if err_details.detail_json:
                hint = err_details.detail_json.get('hint')

            if err_details.message.startswith('missing required time zone'):
                return errors.InvalidValueError(err_details.message, hint=hint)
            elif err_details.message.startswith('unexpected time zone'):
                return errors.InvalidValueError(err_details.message, hint=hint)

        return SchemaRequired

    elif err_details.code == PGErrorCode.InvalidParameterValue:
        return errors.InvalidValueError(
            err_details.message,
            details=err_details.detail if err_details.detail else None)

    elif err_details.code == PGErrorCode.WrongObjectType:
        return errors.InvalidValueError(
            err_details.message,
            details=err_details.detail if err_details.detail else None)

    elif err_details.code == PGErrorCode.DivisionByZeroError:
        return errors.DivisionByZeroError(err_details.message)

    elif err_details.code == PGErrorCode.ReadOnlySQLTransactionError:
        return errors.TransactionError(
            'cannot execute query in a read-only transaction')

    elif err_details.code == PGErrorCode.TransactionSerializationFailure:
        return errors.TransactionSerializationError(err_details.message)

    elif err_details.code == PGErrorCode.TransactionDeadlockDetected:
        return errors.TransactionDeadlockError(err_details.message)

    elif err_details.code == PGErrorCode.InvalidCatalogNameError:
        return errors.AuthenticationError(err_details.message)

    elif err_details.code == PGErrorCode.ObjectInUse:
        return errors.ExecutionError(err_details.message)

    return errors.InternalServerError(err_details.message)
Exemplo n.º 13
0
 async def commit_transaction(self):
     if not self.transactions:
         raise errors.TransactionError(
             'there is no transaction in progress')
     transaction, _ = self.transactions.pop()
     await transaction.commit()
Exemplo n.º 14
0
 def make_explicit(self):
     if self._implicit:
         self._implicit = False
     else:
         raise errors.TransactionError('already in explicit transaction')
Exemplo n.º 15
0
 async def rollback_transaction(self):
     if not self.transactions:
         raise errors.TransactionError(
             'there is no transaction in progress')
     transaction, self.schema = self.transactions.pop()
     await transaction.rollback()
Exemplo n.º 16
0
def static_interpret_backend_error(fields):
    err_details = get_error_details(fields)
    # handle some generic errors if possible
    err = get_generic_exception_from_err_details(err_details)
    if err is not None:
        return err

    if err_details.code == pgerrors.ERROR_NOT_NULL_VIOLATION:
        if err_details.table_name or err_details.column_name:
            return SchemaRequired

        else:
            return errors.InternalServerError(err_details.message)

    elif err_details.code in constraint_errors:
        source = pointer = None

        for errtype, ere in constraint_res.items():
            m = ere.match(err_details.message)
            if m:
                error_type = errtype
                break
        else:
            return errors.InternalServerError(err_details.message)

        if error_type == 'cardinality':
            return errors.CardinalityViolationError(
                'cardinality violation',
                source=source, pointer=pointer)

        elif error_type == 'link_target':
            if err_details.detail_json:
                srcname = err_details.detail_json.get('source')
                ptrname = err_details.detail_json.get('pointer')
                target = err_details.detail_json.get('target')
                expected = err_details.detail_json.get('expected')

                if srcname and ptrname:
                    srcname = sn.QualName.from_string(srcname)
                    ptrname = sn.QualName.from_string(ptrname)
                    lname = '{}.{}'.format(srcname, ptrname.name)
                else:
                    lname = ''

                msg = (
                    f'invalid target for link {lname!r}: {target!r} '
                    f'(expecting {expected!r})'
                )

            else:
                msg = 'invalid target for link'

            return errors.UnknownLinkError(msg)

        elif error_type == 'link_target_del':
            return errors.ConstraintViolationError(
                err_details.message, details=err_details.detail)

        elif error_type == 'constraint':
            if err_details.constraint_name is None:
                return errors.InternalServerError(err_details.message)

            constraint_id, _, _ = err_details.constraint_name.rpartition(';')

            try:
                constraint_id = uuidgen.UUID(constraint_id)
            except ValueError:
                return errors.InternalServerError(err_details.message)

            return SchemaRequired

        elif error_type == 'newconstraint':
            # We can reconstruct what went wrong from the schema_name,
            # table_name, and column_name. But we don't expect
            # constraint_name to be present (because the constraint is
            # not yet present in the schema?).
            if (err_details.schema_name and err_details.table_name and
                    err_details.column_name):
                return SchemaRequired

            else:
                return errors.InternalServerError(err_details.message)

        elif error_type == 'scalar':
            return SchemaRequired

        elif error_type == 'id':
            return errors.ConstraintViolationError(
                'unique link constraint violation')

    elif err_details.code in SCHEMA_CODES:
        if err_details.code == pgerrors.ERROR_INVALID_DATETIME_FORMAT:
            hint = None
            if err_details.detail_json:
                hint = err_details.detail_json.get('hint')

            if err_details.message.startswith('missing required time zone'):
                return errors.InvalidValueError(err_details.message, hint=hint)
            elif err_details.message.startswith('unexpected time zone'):
                return errors.InvalidValueError(err_details.message, hint=hint)

        return SchemaRequired

    elif err_details.code == pgerrors.ERROR_INVALID_PARAMETER_VALUE:
        return errors.InvalidValueError(
            err_details.message,
            details=err_details.detail if err_details.detail else None
        )

    elif err_details.code == pgerrors.ERROR_WRONG_OBJECT_TYPE:
        if err_details.column_name:
            return SchemaRequired

        return errors.InvalidValueError(
            err_details.message,
            details=err_details.detail if err_details.detail else None
        )

    elif err_details.code == pgerrors.ERROR_DIVISION_BY_ZERO:
        return errors.DivisionByZeroError(err_details.message)

    elif err_details.code == pgerrors.ERROR_INTERVAL_FIELD_OVERFLOW:
        return errors.NumericOutOfRangeError(err_details.message)

    elif err_details.code == pgerrors.ERROR_READ_ONLY_SQL_TRANSACTION:
        return errors.TransactionError(
            'cannot execute query in a read-only transaction')

    elif err_details.code == pgerrors.ERROR_SERIALIZATION_FAILURE:
        return errors.TransactionSerializationError(err_details.message)

    elif err_details.code == pgerrors.ERROR_DEADLOCK_DETECTED:
        return errors.TransactionDeadlockError(err_details.message)

    elif err_details.code == pgerrors.ERROR_INVALID_CATALOG_NAME:
        return errors.UnknownDatabaseError(err_details.message)

    elif err_details.code == pgerrors.ERROR_OBJECT_IN_USE:
        return errors.ExecutionError(err_details.message)

    elif err_details.code == pgerrors.ERROR_DUPLICATE_DATABASE:
        return errors.DuplicateDatabaseDefinitionError(err_details.message)

    elif (
        err_details.code == pgerrors.ERROR_CARDINALITY_VIOLATION
        and err_details.constraint_name == 'std::assert_single'
    ):
        return errors.CardinalityViolationError(err_details.message)

    return errors.InternalServerError(err_details.message)
Exemplo n.º 17
0
def interpret_backend_error(schema, fields):
    # See https://www.postgresql.org/docs/current/protocol-error-fields.html
    # for the full list of PostgreSQL error message fields.
    message = fields.get('M')

    detail = fields.get('D')
    detail_json = None
    if detail and detail.startswith('{'):
        detail_json = json.loads(detail)
        detail = None

    if detail_json:
        errcode = detail_json.get('code')
        if errcode:
            try:
                errcls = type(
                    errors.EdgeDBError).get_error_class_from_code(errcode)
            except LookupError:
                pass
            else:
                err = errcls(message)
                err.set_linecol(detail_json.get('line', -1),
                                detail_json.get('column', -1))
                return err

    try:
        code = PGError(fields['C'])
    except ValueError:
        return errors.InternalServerError(message)

    schema_name = fields.get('s')
    table_name = fields.get('t')
    column_name = fields.get('c')
    constraint_name = fields.get('n')

    if code == PGError.NotNullViolationError:
        source_name = pointer_name = None

        if schema_name and table_name:
            tabname = (schema_name, table_name)

            source = common.get_object_from_backend_name(
                schema, s_objtypes.ObjectType, tabname)
            source_name = source.get_displayname(schema)

            if column_name:
                pointer_name = column_name

        if pointer_name is not None:
            pname = f'{source_name}.{pointer_name}'

            return errors.MissingRequiredError(
                f'missing value for required property {pname}')

        else:
            return errors.InternalServerError(message)

    elif code in constraint_errors:
        source = pointer = None

        for errtype, ere in constraint_res.items():
            m = ere.match(message)
            if m:
                error_type = errtype
                break
        else:
            return errors.InternalServerError(message)

        if error_type == 'cardinality':
            return errors.CardinalityViolationError('cardinality violation',
                                                    source=source,
                                                    pointer=pointer)

        elif error_type == 'link_target':
            if detail_json:
                srcname = detail_json.get('source')
                ptrname = detail_json.get('pointer')
                target = detail_json.get('target')
                expected = detail_json.get('expected')

                if srcname and ptrname:
                    srcname = sn.Name(srcname)
                    ptrname = sn.Name(ptrname)
                    lname = '{}.{}'.format(srcname, ptrname.name)
                else:
                    lname = ''

                msg = (f'invalid target for link {lname!r}: {target!r} '
                       f'(expecting {expected!r})')

            else:
                msg = 'invalid target for link'

            return errors.UnknownLinkError(msg)

        elif error_type == 'link_target_del':
            return errors.ConstraintViolationError(message, details=detail)

        elif error_type == 'constraint':
            if constraint_name is None:
                return errors.InternalServerError(message)

            constraint_id, _, _ = constraint_name.rpartition(';')

            try:
                constraint_id = uuid.UUID(constraint_id)
            except ValueError:
                return errors.InternalServerError(message)

            constraint = schema.get_by_id(constraint_id)

            return errors.ConstraintViolationError(
                constraint.format_error_message(schema))

        elif error_type == 'id':
            return errors.ConstraintViolationError(
                'unique link constraint violation')

    elif code == PGError.InvalidParameterValue:
        return errors.InvalidValueError(message,
                                        details=detail if detail else None)

    elif code == PGError.InvalidTextRepresentation:
        return errors.InvalidValueError(translate_pgtype(schema, message))

    elif code == PGError.NumericValueOutOfRange:
        return errors.NumericOutOfRangeError(translate_pgtype(schema, message))

    elif code == PGError.DivisionByZeroError:
        return errors.DivisionByZeroError(message)

    elif code == PGError.ReadOnlySQLTransactionError:
        return errors.TransactionError(
            'cannot execute query in a read-only transaction')

    elif code in {PGError.InvalidDatetimeFormatError, PGError.DatetimeError}:
        return errors.InvalidValueError(translate_pgtype(schema, message))

    elif code == PGError.TransactionSerializationFailure:
        return errors.TransactionSerializationError(message)

    elif code == PGError.TransactionDeadlockDetected:
        return errors.TransactionDeadlockError(message)

    return errors.InternalServerError(message)
Exemplo n.º 18
0
def static_interpret_backend_error(fields):
    err_details = get_error_details(fields)
    # handle some generic errors if possible
    err = get_generic_exception_from_err_details(err_details)
    if err is not None:
        return err

    if err_details.code == PGErrorCode.NotNullViolationError:
        if err_details.schema_name and err_details.table_name:
            return SchemaRequired

        else:
            return errors.InternalServerError(err_details.message)

    elif err_details.code in constraint_errors:
        source = pointer = None

        for errtype, ere in constraint_res.items():
            m = ere.match(err_details.message)
            if m:
                error_type = errtype
                break
        else:
            return errors.InternalServerError(err_details.message)

        if error_type == 'cardinality':
            return errors.CardinalityViolationError('cardinality violation',
                                                    source=source,
                                                    pointer=pointer)

        elif error_type == 'link_target':
            if err_details.detail_json:
                srcname = err_details.detail_json.get('source')
                ptrname = err_details.detail_json.get('pointer')
                target = err_details.detail_json.get('target')
                expected = err_details.detail_json.get('expected')

                if srcname and ptrname:
                    srcname = sn.Name(srcname)
                    ptrname = sn.Name(ptrname)
                    lname = '{}.{}'.format(srcname, ptrname.name)
                else:
                    lname = ''

                msg = (f'invalid target for link {lname!r}: {target!r} '
                       f'(expecting {expected!r})')

            else:
                msg = 'invalid target for link'

            return errors.UnknownLinkError(msg)

        elif error_type == 'link_target_del':
            return errors.ConstraintViolationError(err_details.message,
                                                   details=err_details.detail)

        elif error_type == 'constraint':
            if err_details.constraint_name is None:
                return errors.InternalServerError(err_details.message)

            constraint_id, _, _ = err_details.constraint_name.rpartition(';')

            try:
                constraint_id = uuid.UUID(constraint_id)
            except ValueError:
                return errors.InternalServerError(err_details.message)

            return SchemaRequired

        elif error_type == 'id':
            return errors.ConstraintViolationError(
                'unique link constraint violation')

    elif err_details.code in SCHEMA_CODES:
        return SchemaRequired

    elif err_details.code == PGErrorCode.InvalidParameterValue:
        return errors.InvalidValueError(
            err_details.message,
            details=err_details.detail if err_details.detail else None)

    elif err_details.code == PGErrorCode.DivisionByZeroError:
        return errors.DivisionByZeroError(err_details.message)

    elif err_details.code == PGErrorCode.ReadOnlySQLTransactionError:
        return errors.TransactionError(
            'cannot execute query in a read-only transaction')

    elif err_details.code == PGErrorCode.TransactionSerializationFailure:
        return errors.TransactionSerializationError(err_details.message)

    elif err_details.code == PGErrorCode.TransactionDeadlockDetected:
        return errors.TransactionDeadlockError(err_details.message)

    return errors.InternalServerError(err_details.message)