github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/store/sqlstore/file_info_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"database/sql"
     8  	"fmt"
     9  	"net/http"
    10  
    11  	sq "github.com/Masterminds/squirrel"
    12  
    13  	"github.com/vnforks/kid/v5/einterfaces"
    14  	"github.com/vnforks/kid/v5/model"
    15  	"github.com/vnforks/kid/v5/store"
    16  )
    17  
    18  type SqlFileInfoStore struct {
    19  	SqlStore
    20  	metrics einterfaces.MetricsInterface
    21  }
    22  
    23  func (fs SqlFileInfoStore) ClearCaches() {
    24  }
    25  
    26  func newSqlFileInfoStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore {
    27  	s := &SqlFileInfoStore{
    28  		SqlStore: sqlStore,
    29  		metrics:  metrics,
    30  	}
    31  
    32  	for _, db := range sqlStore.GetAllConns() {
    33  		table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id")
    34  		table.ColMap("Id").SetMaxSize(26)
    35  		table.ColMap("CreatorId").SetMaxSize(26)
    36  		table.ColMap("PostId").SetMaxSize(26)
    37  		table.ColMap("Path").SetMaxSize(512)
    38  		table.ColMap("ThumbnailPath").SetMaxSize(512)
    39  		table.ColMap("PreviewPath").SetMaxSize(512)
    40  		table.ColMap("Name").SetMaxSize(256)
    41  		table.ColMap("Extension").SetMaxSize(64)
    42  		table.ColMap("MimeType").SetMaxSize(256)
    43  	}
    44  
    45  	return s
    46  }
    47  
    48  func (fs SqlFileInfoStore) createIndexesIfNotExists() {
    49  	fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt")
    50  	fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt")
    51  	fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt")
    52  	fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId")
    53  }
    54  
    55  func (fs SqlFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, *model.AppError) {
    56  	info.PreSave()
    57  	if err := info.IsValid(); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	if err := fs.GetMaster().Insert(info); err != nil {
    62  		return nil, model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError)
    63  	}
    64  	return info, nil
    65  }
    66  
    67  func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, *model.AppError) {
    68  	info := &model.FileInfo{}
    69  
    70  	if err := fs.GetReplica().SelectOne(info,
    71  		`SELECT
    72  			*
    73  		FROM
    74  			FileInfo
    75  		WHERE
    76  			Id = :Id
    77  			AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil {
    78  		if err == sql.ErrNoRows {
    79  			return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound)
    80  		}
    81  		return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError)
    82  	}
    83  	return info, nil
    84  }
    85  
    86  func (fs SqlFileInfoStore) GetWithOptions(page, perPage int, opt *model.GetFileInfosOptions) ([]*model.FileInfo, *model.AppError) {
    87  	if perPage < 0 || page < 0 {
    88  		return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions",
    89  			"store.sql_file_info.get_with_options.app_error", nil, fmt.Sprintf("page=%d and perPage=%d must be non-negative", page, perPage), http.StatusBadRequest)
    90  	}
    91  	if perPage == 0 {
    92  		return nil, nil
    93  	}
    94  
    95  	if opt == nil {
    96  		opt = &model.GetFileInfosOptions{}
    97  	}
    98  
    99  	query := fs.getQueryBuilder().
   100  		Select("FileInfo.*").
   101  		From("FileInfo")
   102  
   103  	if len(opt.ClassIds) > 0 {
   104  		query = query.Join("Posts ON FileInfo.PostId = Posts.Id").
   105  			Where(sq.Eq{"Posts.ClassId": opt.ClassIds})
   106  	}
   107  
   108  	if len(opt.UserIds) > 0 {
   109  		query = query.Where(sq.Eq{"FileInfo.CreatorId": opt.UserIds})
   110  	}
   111  
   112  	if opt.Since > 0 {
   113  		query = query.Where(sq.GtOrEq{"FileInfo.CreateAt": opt.Since})
   114  	}
   115  
   116  	if !opt.IncludeDeleted {
   117  		query = query.Where("FileInfo.DeleteAt = 0")
   118  	}
   119  
   120  	if opt.SortBy == "" {
   121  		opt.SortBy = model.FILEINFO_SORT_BY_CREATED
   122  	}
   123  	sortDirection := "ASC"
   124  	if opt.SortDescending {
   125  		sortDirection = "DESC"
   126  	}
   127  
   128  	switch opt.SortBy {
   129  	case model.FILEINFO_SORT_BY_CREATED:
   130  		query = query.OrderBy("FileInfo.CreateAt " + sortDirection)
   131  	case model.FILEINFO_SORT_BY_SIZE:
   132  		query = query.OrderBy("FileInfo.Size " + sortDirection)
   133  	default:
   134  		return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions",
   135  			"store.sql_file_info.get_with_options.app_error", nil, "invalid sort option", http.StatusBadRequest)
   136  	}
   137  
   138  	query = query.OrderBy("FileInfo.Id ASC") // secondary sort for sort stability
   139  
   140  	query = query.Limit(uint64(perPage)).Offset(uint64(perPage * page))
   141  
   142  	queryString, args, err := query.ToSql()
   143  	if err != nil {
   144  		return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions",
   145  			"store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
   146  	}
   147  	var infos []*model.FileInfo
   148  	if _, err := fs.GetReplica().Select(&infos, queryString, args...); err != nil {
   149  		return nil, model.NewAppError("SqlFileInfoStore.GetWithOptions",
   150  			"store.sql_file_info.get_with_options.app_error", nil, err.Error(), http.StatusInternalServerError)
   151  	}
   152  	return infos, nil
   153  }
   154  
   155  func (fs SqlFileInfoStore) GetByPath(path string) (*model.FileInfo, *model.AppError) {
   156  	info := &model.FileInfo{}
   157  
   158  	if err := fs.GetReplica().SelectOne(info,
   159  		`SELECT
   160  				*
   161  			FROM
   162  				FileInfo
   163  			WHERE
   164  				Path = :Path
   165  				AND DeleteAt = 0
   166  			LIMIT 1`, map[string]interface{}{"Path": path}); err != nil {
   167  		return nil, model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError)
   168  	}
   169  	return info, nil
   170  }
   171  
   172  func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string, deleted bool) {
   173  }
   174  
   175  func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster, includeDeleted, allowFromCache bool) ([]*model.FileInfo, *model.AppError) {
   176  	var infos []*model.FileInfo
   177  
   178  	dbmap := fs.GetReplica()
   179  
   180  	if readFromMaster {
   181  		dbmap = fs.GetMaster()
   182  	}
   183  
   184  	query := fs.getQueryBuilder().
   185  		Select("*").
   186  		From("FileInfo").
   187  		Where(sq.Eq{"PostId": postId}).
   188  		OrderBy("CreateAt")
   189  
   190  	if !includeDeleted {
   191  		query = query.Where("DeleteAt = 0")
   192  	}
   193  
   194  	queryString, args, err := query.ToSql()
   195  	if err != nil {
   196  		return nil, model.NewAppError("SqlFileInfoStore.GetForPost", "store.sql_file_info.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError)
   197  	}
   198  
   199  	if _, err := dbmap.Select(&infos, queryString, args...); err != nil {
   200  		return nil, model.NewAppError("SqlFileInfoStore.GetForPost",
   201  			"store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError)
   202  	}
   203  	return infos, nil
   204  }
   205  
   206  func (fs SqlFileInfoStore) GetForUser(userId string) ([]*model.FileInfo, *model.AppError) {
   207  	var infos []*model.FileInfo
   208  
   209  	dbmap := fs.GetReplica()
   210  
   211  	if _, err := dbmap.Select(&infos,
   212  		`SELECT
   213  				*
   214  			FROM
   215  				FileInfo
   216  			WHERE
   217  				CreatorId = :CreatorId
   218  				AND DeleteAt = 0
   219  			ORDER BY
   220  				CreateAt`, map[string]interface{}{"CreatorId": userId}); err != nil {
   221  		return nil, model.NewAppError("SqlFileInfoStore.GetForUser",
   222  			"store.sql_file_info.get_for_user_id.app_error", nil, "creator_id="+userId+", "+err.Error(), http.StatusInternalServerError)
   223  	}
   224  	return infos, nil
   225  }
   226  
   227  func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) *model.AppError {
   228  	sqlResult, err := fs.GetMaster().Exec(`
   229  		UPDATE
   230  			FileInfo
   231  		SET
   232  			PostId = :PostId
   233  		WHERE
   234  			Id = :Id
   235  			AND PostId = ''
   236  			AND (CreatorId = :CreatorId OR CreatorId = 'nouser')
   237  	`, map[string]interface{}{
   238  		"PostId":    postId,
   239  		"Id":        fileId,
   240  		"CreatorId": creatorId,
   241  	})
   242  	if err != nil {
   243  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   244  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   245  	}
   246  
   247  	count, err := sqlResult.RowsAffected()
   248  	if err != nil {
   249  		// RowsAffected should never fail with the MySQL or Postgres drivers
   250  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   251  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   252  	} else if count == 0 {
   253  		// Could not attach the file to the post
   254  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   255  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId, http.StatusBadRequest)
   256  	}
   257  	return nil
   258  }
   259  
   260  func (fs SqlFileInfoStore) DeleteForPost(postId string) (string, *model.AppError) {
   261  	if _, err := fs.GetMaster().Exec(
   262  		`UPDATE
   263  				FileInfo
   264  			SET
   265  				DeleteAt = :DeleteAt
   266  			WHERE
   267  				PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil {
   268  		return "", model.NewAppError("SqlFileInfoStore.DeleteForPost",
   269  			"store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError)
   270  	}
   271  	return postId, nil
   272  }
   273  
   274  func (fs SqlFileInfoStore) PermanentDelete(fileId string) *model.AppError {
   275  	if _, err := fs.GetMaster().Exec(
   276  		`DELETE FROM
   277  				FileInfo
   278  			WHERE
   279  				Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil {
   280  		return model.NewAppError("SqlFileInfoStore.PermanentDelete",
   281  			"store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   282  	}
   283  	return nil
   284  }
   285  
   286  func (fs SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) {
   287  	var query string
   288  	if fs.DriverName() == "postgres" {
   289  		query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))"
   290  	} else {
   291  		query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit"
   292  	}
   293  
   294  	sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit})
   295  	if err != nil {
   296  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   297  	}
   298  
   299  	rowsAffected, err := sqlResult.RowsAffected()
   300  	if err != nil {
   301  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   302  	}
   303  
   304  	return rowsAffected, nil
   305  }
   306  
   307  func (fs SqlFileInfoStore) PermanentDeleteByUser(userId string) (int64, *model.AppError) {
   308  	query := "DELETE from FileInfo WHERE CreatorId = :CreatorId"
   309  
   310  	sqlResult, err := fs.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId})
   311  	if err != nil {
   312  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   313  	}
   314  
   315  	rowsAffected, err := sqlResult.RowsAffected()
   316  	if err != nil {
   317  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   318  	}
   319  
   320  	return rowsAffected, nil
   321  }