2020-10-28 15:28:06 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"errors"
|
2021-06-11 15:34:46 +01:00
|
|
|
"time"
|
2020-10-28 15:28:06 +00:00
|
|
|
|
2020-12-15 12:49:58 +00:00
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
2020-11-02 10:51:36 +00:00
|
|
|
"storj.io/common/storj"
|
2020-10-28 15:28:06 +00:00
|
|
|
"storj.io/common/uuid"
|
|
|
|
)
|
|
|
|
|
2020-12-15 12:49:58 +00:00
|
|
|
// ErrSegmentNotFound is an error class for non-existing segment.
|
|
|
|
var ErrSegmentNotFound = errs.Class("segment not found")
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
// Object object metadata.
|
|
|
|
// TODO define separated struct.
|
|
|
|
type Object RawObject
|
|
|
|
|
2021-04-09 09:24:18 +01:00
|
|
|
// IsMigrated returns whether the object comes from PointerDB.
|
|
|
|
// Pointer objects are special that they are missing some information.
|
|
|
|
//
|
|
|
|
// * TotalPlainSize = 0 and FixedSegmentSize = 0.
|
|
|
|
// * Segment.PlainOffset = 0, Segment.PlainSize = 0
|
|
|
|
func (obj *Object) IsMigrated() bool {
|
|
|
|
return obj.TotalPlainSize <= 0
|
2021-03-31 12:08:22 +01:00
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
// Segment segment metadata.
|
|
|
|
// TODO define separated struct.
|
|
|
|
type Segment RawSegment
|
|
|
|
|
2020-12-10 15:09:44 +00:00
|
|
|
// Inline returns true if segment is inline.
|
|
|
|
func (s Segment) Inline() bool {
|
2021-02-24 21:14:18 +00:00
|
|
|
return s.Redundancy.IsZero() && len(s.Pieces) == 0
|
2020-12-10 15:09:44 +00:00
|
|
|
}
|
|
|
|
|
2021-06-11 15:34:46 +01:00
|
|
|
// Expired checks if segment is expired relative to now.
|
|
|
|
func (s Segment) Expired(now time.Time) bool {
|
|
|
|
return s.ExpiresAt != nil && s.ExpiresAt.Before(now)
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
// GetObjectExactVersion contains arguments necessary for fetching an information
|
|
|
|
// about exact object version.
|
|
|
|
type GetObjectExactVersion struct {
|
|
|
|
Version Version
|
|
|
|
ObjectLocation
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify verifies get object reqest fields.
|
|
|
|
func (obj *GetObjectExactVersion) Verify() error {
|
|
|
|
if err := obj.ObjectLocation.Verify(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if obj.Version <= 0 {
|
|
|
|
return ErrInvalidRequest.New("Version invalid: %v", obj.Version)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectExactVersion returns object information for exact version.
|
|
|
|
func (db *DB) GetObjectExactVersion(ctx context.Context, opts GetObjectExactVersion) (_ Object, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Object{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
object := Object{}
|
|
|
|
err = db.db.QueryRow(ctx, `
|
2020-11-16 13:58:22 +00:00
|
|
|
SELECT
|
2020-10-28 15:28:06 +00:00
|
|
|
stream_id,
|
|
|
|
created_at, expires_at,
|
|
|
|
segment_count,
|
2020-11-16 16:46:47 +00:00
|
|
|
encrypted_metadata_nonce, encrypted_metadata, encrypted_metadata_encrypted_key,
|
2020-11-24 12:29:16 +00:00
|
|
|
total_plain_size, total_encrypted_size, fixed_segment_size,
|
2020-10-28 15:28:06 +00:00
|
|
|
encryption
|
|
|
|
FROM objects
|
|
|
|
WHERE
|
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2 AND
|
|
|
|
object_key = $3 AND
|
|
|
|
version = $4 AND
|
2020-11-16 13:58:22 +00:00
|
|
|
status = `+committedStatus,
|
2021-03-02 11:22:49 +00:00
|
|
|
opts.ProjectID, []byte(opts.BucketName), []byte(opts.ObjectKey), opts.Version).
|
2020-10-28 15:28:06 +00:00
|
|
|
Scan(
|
|
|
|
&object.StreamID,
|
|
|
|
&object.CreatedAt, &object.ExpiresAt,
|
|
|
|
&object.SegmentCount,
|
2020-11-16 16:46:47 +00:00
|
|
|
&object.EncryptedMetadataNonce, &object.EncryptedMetadata, &object.EncryptedMetadataEncryptedKey,
|
2020-11-24 12:29:16 +00:00
|
|
|
&object.TotalPlainSize, &object.TotalEncryptedSize, &object.FixedSegmentSize,
|
2020-10-28 15:28:06 +00:00
|
|
|
encryptionParameters{&object.Encryption},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
2020-11-02 10:51:36 +00:00
|
|
|
return Object{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
return Object{}, Error.New("unable to query object status: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
object.ProjectID = opts.ProjectID
|
|
|
|
object.BucketName = opts.BucketName
|
|
|
|
object.ObjectKey = opts.ObjectKey
|
|
|
|
object.Version = opts.Version
|
|
|
|
|
|
|
|
object.Status = Committed
|
|
|
|
|
|
|
|
return object, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectLatestVersion contains arguments necessary for fetching
|
|
|
|
// an object information for latest version.
|
|
|
|
type GetObjectLatestVersion struct {
|
|
|
|
ObjectLocation
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectLatestVersion returns object information for latest version.
|
|
|
|
func (db *DB) GetObjectLatestVersion(ctx context.Context, opts GetObjectLatestVersion) (_ Object, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Object{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
object := Object{}
|
|
|
|
err = db.db.QueryRow(ctx, `
|
2020-11-16 13:58:22 +00:00
|
|
|
SELECT
|
2020-10-28 15:28:06 +00:00
|
|
|
stream_id, version,
|
|
|
|
created_at, expires_at,
|
|
|
|
segment_count,
|
2020-11-16 16:46:47 +00:00
|
|
|
encrypted_metadata_nonce, encrypted_metadata, encrypted_metadata_encrypted_key,
|
2020-11-24 12:29:16 +00:00
|
|
|
total_plain_size, total_encrypted_size, fixed_segment_size,
|
2020-10-28 15:28:06 +00:00
|
|
|
encryption
|
|
|
|
FROM objects
|
|
|
|
WHERE
|
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2 AND
|
|
|
|
object_key = $3 AND
|
2020-11-16 13:58:22 +00:00
|
|
|
status = `+committedStatus+`
|
2020-10-28 15:28:06 +00:00
|
|
|
ORDER BY version desc
|
|
|
|
LIMIT 1
|
2021-03-02 11:22:49 +00:00
|
|
|
`, opts.ProjectID, []byte(opts.BucketName), []byte(opts.ObjectKey)).
|
2020-10-28 15:28:06 +00:00
|
|
|
Scan(
|
|
|
|
&object.StreamID, &object.Version,
|
|
|
|
&object.CreatedAt, &object.ExpiresAt,
|
|
|
|
&object.SegmentCount,
|
2020-11-16 16:46:47 +00:00
|
|
|
&object.EncryptedMetadataNonce, &object.EncryptedMetadata, &object.EncryptedMetadataEncryptedKey,
|
2020-11-24 12:29:16 +00:00
|
|
|
&object.TotalPlainSize, &object.TotalEncryptedSize, &object.FixedSegmentSize,
|
2020-10-28 15:28:06 +00:00
|
|
|
encryptionParameters{&object.Encryption},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
2020-11-02 10:51:36 +00:00
|
|
|
return Object{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
return Object{}, Error.New("unable to query object status: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
object.ProjectID = opts.ProjectID
|
|
|
|
object.BucketName = opts.BucketName
|
|
|
|
object.ObjectKey = opts.ObjectKey
|
|
|
|
|
|
|
|
object.Status = Committed
|
|
|
|
|
|
|
|
return object, nil
|
|
|
|
}
|
|
|
|
|
2021-02-01 18:02:15 +00:00
|
|
|
// GetSegmentByLocation contains arguments necessary for fetching a segment on specific segment location.
|
|
|
|
type GetSegmentByLocation struct {
|
|
|
|
SegmentLocation
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSegmentByLocation returns information about segment on the specified location.
|
|
|
|
func (db *DB) GetSegmentByLocation(ctx context.Context, opts GetSegmentByLocation) (segment Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Segment{}, err
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
var aliasPieces AliasPieces
|
2021-02-01 18:02:15 +00:00
|
|
|
err = db.db.QueryRow(ctx, `
|
|
|
|
SELECT
|
|
|
|
stream_id,
|
2021-06-11 16:47:42 +01:00
|
|
|
created_at, expires_at, repaired_at,
|
2021-02-01 18:02:15 +00:00
|
|
|
root_piece_id, encrypted_key_nonce, encrypted_key,
|
|
|
|
encrypted_size, plain_offset, plain_size,
|
2021-03-25 07:53:10 +00:00
|
|
|
encrypted_etag,
|
2021-02-01 18:02:15 +00:00
|
|
|
redundancy,
|
2021-02-08 09:33:45 +00:00
|
|
|
inline_data, remote_alias_pieces
|
2021-02-01 18:02:15 +00:00
|
|
|
FROM segments
|
|
|
|
WHERE
|
|
|
|
stream_id IN (SELECT stream_id FROM objects WHERE
|
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2 AND
|
|
|
|
object_key = $3
|
|
|
|
ORDER BY version DESC
|
|
|
|
LIMIT 1
|
|
|
|
) AND
|
|
|
|
position = $4
|
2021-03-02 11:22:49 +00:00
|
|
|
`, opts.ProjectID, []byte(opts.BucketName), []byte(opts.ObjectKey), opts.Position.Encode()).
|
2021-02-01 18:02:15 +00:00
|
|
|
Scan(
|
|
|
|
&segment.StreamID,
|
2021-06-11 16:47:42 +01:00
|
|
|
&segment.CreatedAt, &segment.ExpiresAt, &segment.RepairedAt,
|
2021-02-01 18:02:15 +00:00
|
|
|
&segment.RootPieceID, &segment.EncryptedKeyNonce, &segment.EncryptedKey,
|
|
|
|
&segment.EncryptedSize, &segment.PlainOffset, &segment.PlainSize,
|
2021-03-25 07:53:10 +00:00
|
|
|
&segment.EncryptedETag,
|
2021-02-01 18:02:15 +00:00
|
|
|
redundancyScheme{&segment.Redundancy},
|
2021-02-08 09:33:45 +00:00
|
|
|
&segment.InlineData, &aliasPieces,
|
2021-02-01 18:02:15 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return Segment{}, storj.ErrObjectNotFound.Wrap(Error.New("object or segment missing"))
|
|
|
|
}
|
|
|
|
return Segment{}, Error.New("unable to query segment: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
segment.Pieces, err = db.aliasCache.ConvertAliasesToPieces(ctx, aliasPieces)
|
|
|
|
if err != nil {
|
|
|
|
return Segment{}, Error.New("unable to convert aliases to pieces: %w", err)
|
|
|
|
}
|
2021-02-01 18:02:15 +00:00
|
|
|
segment.Position = opts.Position
|
|
|
|
|
|
|
|
return segment, nil
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
// GetSegmentByPosition contains arguments necessary for fetching a segment on specific position.
|
|
|
|
type GetSegmentByPosition struct {
|
|
|
|
StreamID uuid.UUID
|
|
|
|
Position SegmentPosition
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify verifies get segment request fields.
|
|
|
|
func (seg *GetSegmentByPosition) Verify() error {
|
|
|
|
if seg.StreamID.IsZero() {
|
|
|
|
return ErrInvalidRequest.New("StreamID missing")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-01 18:02:15 +00:00
|
|
|
// GetSegmentByPosition returns information about segment on the specified position.
|
2020-10-28 15:28:06 +00:00
|
|
|
func (db *DB) GetSegmentByPosition(ctx context.Context, opts GetSegmentByPosition) (segment Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Segment{}, err
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
var aliasPieces AliasPieces
|
2020-10-28 15:28:06 +00:00
|
|
|
err = db.db.QueryRow(ctx, `
|
2020-11-16 13:58:22 +00:00
|
|
|
SELECT
|
2021-06-11 16:47:42 +01:00
|
|
|
created_at, expires_at, repaired_at,
|
2020-10-28 15:28:06 +00:00
|
|
|
root_piece_id, encrypted_key_nonce, encrypted_key,
|
2020-11-16 13:58:22 +00:00
|
|
|
encrypted_size, plain_offset, plain_size,
|
2021-03-25 07:53:10 +00:00
|
|
|
encrypted_etag,
|
2020-10-28 15:28:06 +00:00
|
|
|
redundancy,
|
2021-02-08 09:33:45 +00:00
|
|
|
inline_data, remote_alias_pieces
|
2021-01-19 13:33:33 +00:00
|
|
|
FROM segments
|
2020-10-28 15:28:06 +00:00
|
|
|
WHERE
|
2021-01-19 13:33:33 +00:00
|
|
|
stream_id = $1 AND
|
|
|
|
position = $2
|
2020-10-28 15:28:06 +00:00
|
|
|
`, opts.StreamID, opts.Position.Encode()).
|
|
|
|
Scan(
|
2021-06-11 16:47:42 +01:00
|
|
|
&segment.CreatedAt, &segment.ExpiresAt, &segment.RepairedAt,
|
2020-10-28 15:28:06 +00:00
|
|
|
&segment.RootPieceID, &segment.EncryptedKeyNonce, &segment.EncryptedKey,
|
|
|
|
&segment.EncryptedSize, &segment.PlainOffset, &segment.PlainSize,
|
2021-03-25 07:53:10 +00:00
|
|
|
&segment.EncryptedETag,
|
2020-10-28 15:28:06 +00:00
|
|
|
redundancyScheme{&segment.Redundancy},
|
2021-02-08 09:33:45 +00:00
|
|
|
&segment.InlineData, &aliasPieces,
|
2020-10-28 15:28:06 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
2020-12-15 12:49:58 +00:00
|
|
|
return Segment{}, ErrSegmentNotFound.New("segment missing")
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
return Segment{}, Error.New("unable to query segment: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
segment.Pieces, err = db.aliasCache.ConvertAliasesToPieces(ctx, aliasPieces)
|
|
|
|
if err != nil {
|
|
|
|
return Segment{}, Error.New("unable to convert aliases to pieces: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-10-28 15:28:06 +00:00
|
|
|
segment.StreamID = opts.StreamID
|
|
|
|
segment.Position = opts.Position
|
|
|
|
|
|
|
|
return segment, nil
|
|
|
|
}
|
2020-11-03 15:03:20 +00:00
|
|
|
|
|
|
|
// GetLatestObjectLastSegment contains arguments necessary for fetching a last segment information.
|
|
|
|
type GetLatestObjectLastSegment struct {
|
|
|
|
ObjectLocation
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLatestObjectLastSegment returns an object last segment information.
|
|
|
|
func (db *DB) GetLatestObjectLastSegment(ctx context.Context, opts GetLatestObjectLastSegment) (segment Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Segment{}, err
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
var aliasPieces AliasPieces
|
2020-11-03 15:03:20 +00:00
|
|
|
err = db.db.QueryRow(ctx, `
|
|
|
|
SELECT
|
|
|
|
stream_id, position,
|
2021-03-17 10:31:07 +00:00
|
|
|
created_at, repaired_at,
|
2020-11-03 15:03:20 +00:00
|
|
|
root_piece_id, encrypted_key_nonce, encrypted_key,
|
|
|
|
encrypted_size, plain_offset, plain_size,
|
2021-03-25 07:53:10 +00:00
|
|
|
encrypted_etag,
|
2020-11-03 15:03:20 +00:00
|
|
|
redundancy,
|
2021-02-08 09:33:45 +00:00
|
|
|
inline_data, remote_alias_pieces
|
2020-11-03 15:03:20 +00:00
|
|
|
FROM segments
|
|
|
|
WHERE
|
2021-01-27 15:45:27 +00:00
|
|
|
stream_id IN (SELECT stream_id FROM objects WHERE
|
2020-11-03 15:03:20 +00:00
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2 AND
|
|
|
|
object_key = $3 AND
|
2020-11-16 13:58:22 +00:00
|
|
|
status = `+committedStatus+`
|
2020-11-03 15:03:20 +00:00
|
|
|
ORDER BY version DESC
|
|
|
|
LIMIT 1
|
|
|
|
)
|
|
|
|
ORDER BY position DESC
|
|
|
|
LIMIT 1
|
2021-03-02 11:22:49 +00:00
|
|
|
`, opts.ProjectID, []byte(opts.BucketName), []byte(opts.ObjectKey)).
|
2020-11-03 15:03:20 +00:00
|
|
|
Scan(
|
|
|
|
&segment.StreamID, &segment.Position,
|
2021-03-17 10:31:07 +00:00
|
|
|
&segment.CreatedAt, &segment.RepairedAt,
|
2020-11-03 15:03:20 +00:00
|
|
|
&segment.RootPieceID, &segment.EncryptedKeyNonce, &segment.EncryptedKey,
|
|
|
|
&segment.EncryptedSize, &segment.PlainOffset, &segment.PlainSize,
|
2021-03-25 07:53:10 +00:00
|
|
|
&segment.EncryptedETag,
|
2020-11-03 15:03:20 +00:00
|
|
|
redundancyScheme{&segment.Redundancy},
|
2021-02-08 09:33:45 +00:00
|
|
|
&segment.InlineData, &aliasPieces,
|
2020-11-03 15:03:20 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return Segment{}, storj.ErrObjectNotFound.Wrap(Error.New("object or segment missing"))
|
|
|
|
}
|
|
|
|
return Segment{}, Error.New("unable to query segment: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
segment.Pieces, err = db.aliasCache.ConvertAliasesToPieces(ctx, aliasPieces)
|
|
|
|
if err != nil {
|
|
|
|
return Segment{}, Error.New("unable to convert aliases to pieces: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-11-03 15:03:20 +00:00
|
|
|
return segment, nil
|
|
|
|
}
|
2020-11-10 09:17:25 +00:00
|
|
|
|
|
|
|
// GetSegmentByOffset contains arguments necessary for fetching a segment information.
|
|
|
|
type GetSegmentByOffset struct {
|
|
|
|
ObjectLocation
|
|
|
|
PlainOffset int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSegmentByOffset returns an object segment information.
|
|
|
|
func (db *DB) GetSegmentByOffset(ctx context.Context, opts GetSegmentByOffset) (segment Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Verify(); err != nil {
|
|
|
|
return Segment{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if opts.PlainOffset < 0 {
|
|
|
|
return Segment{}, ErrInvalidRequest.New("Invalid PlainOffset: %d", opts.PlainOffset)
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
var aliasPieces AliasPieces
|
2020-11-10 09:17:25 +00:00
|
|
|
err = db.db.QueryRow(ctx, `
|
|
|
|
SELECT
|
|
|
|
stream_id, position,
|
2021-06-11 16:47:42 +01:00
|
|
|
created_at, expires_at, repaired_at,
|
2020-11-10 09:17:25 +00:00
|
|
|
root_piece_id, encrypted_key_nonce, encrypted_key,
|
|
|
|
encrypted_size, plain_offset, plain_size,
|
2021-03-25 07:53:10 +00:00
|
|
|
encrypted_etag,
|
2020-11-10 09:17:25 +00:00
|
|
|
redundancy,
|
2021-02-08 09:33:45 +00:00
|
|
|
inline_data, remote_alias_pieces
|
2020-11-10 09:17:25 +00:00
|
|
|
FROM segments
|
|
|
|
WHERE
|
2021-01-27 15:45:27 +00:00
|
|
|
stream_id IN (SELECT stream_id FROM objects WHERE
|
2020-11-10 09:17:25 +00:00
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2 AND
|
|
|
|
object_key = $3 AND
|
2020-11-16 13:58:22 +00:00
|
|
|
status = `+committedStatus+`
|
2020-11-10 09:17:25 +00:00
|
|
|
ORDER BY version DESC
|
|
|
|
LIMIT 1
|
|
|
|
) AND
|
|
|
|
plain_offset <= $4 AND
|
|
|
|
(plain_size + plain_offset) > $4
|
|
|
|
ORDER BY plain_offset ASC
|
|
|
|
LIMIT 1
|
2021-03-02 11:22:49 +00:00
|
|
|
`, opts.ProjectID, []byte(opts.BucketName), []byte(opts.ObjectKey), opts.PlainOffset).
|
2020-11-10 09:17:25 +00:00
|
|
|
Scan(
|
|
|
|
&segment.StreamID, &segment.Position,
|
2021-06-11 16:47:42 +01:00
|
|
|
&segment.CreatedAt, &segment.ExpiresAt, &segment.RepairedAt,
|
2020-11-10 09:17:25 +00:00
|
|
|
&segment.RootPieceID, &segment.EncryptedKeyNonce, &segment.EncryptedKey,
|
|
|
|
&segment.EncryptedSize, &segment.PlainOffset, &segment.PlainSize,
|
2021-03-25 07:53:10 +00:00
|
|
|
&segment.EncryptedETag,
|
2020-11-10 09:17:25 +00:00
|
|
|
redundancyScheme{&segment.Redundancy},
|
2021-02-08 09:33:45 +00:00
|
|
|
&segment.InlineData, &aliasPieces,
|
2020-11-10 09:17:25 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return Segment{}, storj.ErrObjectNotFound.Wrap(Error.New("object or segment missing"))
|
|
|
|
}
|
|
|
|
return Segment{}, Error.New("unable to query segment: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-02-08 09:33:45 +00:00
|
|
|
segment.Pieces, err = db.aliasCache.ConvertAliasesToPieces(ctx, aliasPieces)
|
|
|
|
if err != nil {
|
|
|
|
return Segment{}, Error.New("unable to convert aliases to pieces: %w", err)
|
|
|
|
}
|
|
|
|
|
2020-11-10 09:17:25 +00:00
|
|
|
return segment, nil
|
|
|
|
}
|
2020-11-17 17:37:58 +00:00
|
|
|
|
|
|
|
// BucketEmpty contains arguments necessary for checking if bucket is empty.
|
|
|
|
type BucketEmpty struct {
|
|
|
|
ProjectID uuid.UUID
|
|
|
|
BucketName string
|
|
|
|
}
|
|
|
|
|
|
|
|
// BucketEmpty returns true if bucket does not contain objects (pending or committed).
|
|
|
|
// This method doesn't check bucket existence.
|
|
|
|
func (db *DB) BucketEmpty(ctx context.Context, opts BucketEmpty) (empty bool, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case opts.ProjectID.IsZero():
|
|
|
|
return false, ErrInvalidRequest.New("ProjectID missing")
|
|
|
|
case opts.BucketName == "":
|
|
|
|
return false, ErrInvalidRequest.New("BucketName missing")
|
|
|
|
}
|
|
|
|
|
|
|
|
var value int
|
|
|
|
err = db.db.QueryRow(ctx, `
|
|
|
|
SELECT
|
|
|
|
1
|
|
|
|
FROM objects
|
|
|
|
WHERE
|
|
|
|
project_id = $1 AND
|
|
|
|
bucket_name = $2
|
|
|
|
LIMIT 1
|
2021-03-02 11:22:49 +00:00
|
|
|
`, opts.ProjectID, []byte(opts.BucketName)).Scan(&value)
|
2020-11-17 17:37:58 +00:00
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
return false, Error.New("unable to query objects: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
2020-11-30 08:39:51 +00:00
|
|
|
|
|
|
|
// TestingAllCommittedObjects gets all objects from bucket.
|
|
|
|
// Use only for testing purposes.
|
|
|
|
func (db *DB) TestingAllCommittedObjects(ctx context.Context, projectID uuid.UUID, bucketName string) (objects []ObjectEntry, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2020-12-03 18:04:01 +00:00
|
|
|
return db.testingAllObjectsByStatus(ctx, projectID, bucketName, Committed)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestingAllPendingObjects gets all objects from bucket.
|
|
|
|
// Use only for testing purposes.
|
|
|
|
func (db *DB) TestingAllPendingObjects(ctx context.Context, projectID uuid.UUID, bucketName string) (objects []ObjectEntry, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
return db.testingAllObjectsByStatus(ctx, projectID, bucketName, Pending)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *DB) testingAllObjectsByStatus(ctx context.Context, projectID uuid.UUID, bucketName string, status ObjectStatus) (objects []ObjectEntry, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2020-12-21 15:07:00 +00:00
|
|
|
err = db.IterateObjectsAllVersionsWithStatus(ctx,
|
|
|
|
IterateObjectsWithStatus{
|
2020-11-30 08:39:51 +00:00
|
|
|
ProjectID: projectID,
|
|
|
|
BucketName: bucketName,
|
|
|
|
Recursive: true,
|
2020-12-03 18:04:01 +00:00
|
|
|
Status: status,
|
2020-11-30 08:39:51 +00:00
|
|
|
}, func(ctx context.Context, it ObjectsIterator) error {
|
|
|
|
entry := ObjectEntry{}
|
|
|
|
for it.Next(ctx, &entry) {
|
|
|
|
objects = append(objects, entry)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestingAllObjectSegments gets all segments for given object.
|
|
|
|
// Use only for testing purposes.
|
|
|
|
func (db *DB) TestingAllObjectSegments(ctx context.Context, objectLocation ObjectLocation) (segments []Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
object, err := db.GetObjectLatestVersion(ctx, GetObjectLatestVersion{
|
|
|
|
ObjectLocation: objectLocation,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
response, err := db.ListSegments(ctx, ListSegments{
|
|
|
|
StreamID: object.StreamID,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.Segments, nil
|
|
|
|
}
|
2020-12-15 12:58:57 +00:00
|
|
|
|
|
|
|
// TestingAllObjects gets all objects.
|
|
|
|
// Use only for testing purposes.
|
|
|
|
func (db *DB) TestingAllObjects(ctx context.Context) (objects []Object, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
rawObjects, err := db.testingGetAllObjects(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, o := range rawObjects {
|
|
|
|
objects = append(objects, Object(o))
|
|
|
|
}
|
|
|
|
|
|
|
|
return objects, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestingAllSegments gets all segments.
|
|
|
|
// Use only for testing purposes.
|
|
|
|
func (db *DB) TestingAllSegments(ctx context.Context) (segments []Segment, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
rawSegments, err := db.testingGetAllSegments(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range rawSegments {
|
|
|
|
segments = append(segments, Segment(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
return segments, nil
|
|
|
|
}
|