github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/store/sqlstore/file_info_store.go (about)

     1  // Copyright (c) 2015-present Xenia, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"database/sql"
     8  	"net/http"
     9  
    10  	"github.com/xzl8028/xenia-server/einterfaces"
    11  	"github.com/xzl8028/xenia-server/model"
    12  	"github.com/xzl8028/xenia-server/store"
    13  	"github.com/xzl8028/xenia-server/utils"
    14  )
    15  
    16  type SqlFileInfoStore struct {
    17  	SqlStore
    18  	metrics einterfaces.MetricsInterface
    19  }
    20  
    21  const (
    22  	FILE_INFO_CACHE_SIZE = 25000
    23  	FILE_INFO_CACHE_SEC  = 1800 // 30 minutes
    24  )
    25  
    26  var fileInfoCache *utils.Cache = utils.NewLru(FILE_INFO_CACHE_SIZE)
    27  
    28  func (fs SqlFileInfoStore) ClearCaches() {
    29  	fileInfoCache.Purge()
    30  	if fs.metrics != nil {
    31  		fs.metrics.IncrementMemCacheInvalidationCounter("File Info Cache - Purge")
    32  	}
    33  }
    34  
    35  func NewSqlFileInfoStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.FileInfoStore {
    36  	s := &SqlFileInfoStore{
    37  		SqlStore: sqlStore,
    38  		metrics:  metrics,
    39  	}
    40  
    41  	for _, db := range sqlStore.GetAllConns() {
    42  		table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id")
    43  		table.ColMap("Id").SetMaxSize(26)
    44  		table.ColMap("CreatorId").SetMaxSize(26)
    45  		table.ColMap("PostId").SetMaxSize(26)
    46  		table.ColMap("Path").SetMaxSize(512)
    47  		table.ColMap("ThumbnailPath").SetMaxSize(512)
    48  		table.ColMap("PreviewPath").SetMaxSize(512)
    49  		table.ColMap("Name").SetMaxSize(256)
    50  		table.ColMap("Extension").SetMaxSize(64)
    51  		table.ColMap("MimeType").SetMaxSize(256)
    52  	}
    53  
    54  	return s
    55  }
    56  
    57  func (fs SqlFileInfoStore) CreateIndexesIfNotExists() {
    58  	fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt")
    59  	fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt")
    60  	fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt")
    61  	fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId")
    62  }
    63  
    64  func (fs SqlFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, *model.AppError) {
    65  	info.PreSave()
    66  	if err := info.IsValid(); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	if err := fs.GetMaster().Insert(info); err != nil {
    71  		return nil, model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError)
    72  	}
    73  	return info, nil
    74  }
    75  
    76  func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, *model.AppError) {
    77  	info := &model.FileInfo{}
    78  
    79  	if err := fs.GetReplica().SelectOne(info,
    80  		`SELECT
    81  			*
    82  		FROM
    83  			FileInfo
    84  		WHERE
    85  			Id = :Id
    86  			AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil {
    87  		if err == sql.ErrNoRows {
    88  			return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound)
    89  		}
    90  		return nil, model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError)
    91  	}
    92  	return info, nil
    93  }
    94  
    95  func (fs SqlFileInfoStore) GetByPath(path string) (*model.FileInfo, *model.AppError) {
    96  	info := &model.FileInfo{}
    97  
    98  	if err := fs.GetReplica().SelectOne(info,
    99  		`SELECT
   100  				*
   101  			FROM
   102  				FileInfo
   103  			WHERE
   104  				Path = :Path
   105  				AND DeleteAt = 0
   106  			LIMIT 1`, map[string]interface{}{"Path": path}); err != nil {
   107  		return nil, model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError)
   108  	}
   109  	return info, nil
   110  }
   111  
   112  func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string) {
   113  	fileInfoCache.Remove(postId)
   114  	if fs.metrics != nil {
   115  		fs.metrics.IncrementMemCacheInvalidationCounter("File Info Cache - Remove by PostId")
   116  	}
   117  }
   118  
   119  func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster bool, allowFromCache bool) ([]*model.FileInfo, *model.AppError) {
   120  	if allowFromCache {
   121  		if cacheItem, ok := fileInfoCache.Get(postId); ok {
   122  			if fs.metrics != nil {
   123  				fs.metrics.IncrementMemCacheHitCounter("File Info Cache")
   124  			}
   125  
   126  			return cacheItem.([]*model.FileInfo), nil
   127  		}
   128  		if fs.metrics != nil {
   129  			fs.metrics.IncrementMemCacheMissCounter("File Info Cache")
   130  		}
   131  	} else {
   132  		if fs.metrics != nil {
   133  			fs.metrics.IncrementMemCacheMissCounter("File Info Cache")
   134  		}
   135  	}
   136  
   137  	var infos []*model.FileInfo
   138  
   139  	dbmap := fs.GetReplica()
   140  
   141  	if readFromMaster {
   142  		dbmap = fs.GetMaster()
   143  	}
   144  
   145  	if _, err := dbmap.Select(&infos,
   146  		`SELECT
   147  				*
   148  			FROM
   149  				FileInfo
   150  			WHERE
   151  				PostId = :PostId
   152  				AND DeleteAt = 0
   153  			ORDER BY
   154  				CreateAt`, map[string]interface{}{"PostId": postId}); err != nil {
   155  		return nil, model.NewAppError("SqlFileInfoStore.GetForPost",
   156  			"store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError)
   157  	}
   158  	if len(infos) > 0 {
   159  		fileInfoCache.AddWithExpiresInSecs(postId, infos, FILE_INFO_CACHE_SEC)
   160  	}
   161  
   162  	return infos, nil
   163  }
   164  
   165  func (fs SqlFileInfoStore) GetForUser(userId string) ([]*model.FileInfo, *model.AppError) {
   166  	var infos []*model.FileInfo
   167  
   168  	dbmap := fs.GetReplica()
   169  
   170  	if _, err := dbmap.Select(&infos,
   171  		`SELECT
   172  				*
   173  			FROM
   174  				FileInfo
   175  			WHERE
   176  				CreatorId = :CreatorId
   177  				AND DeleteAt = 0
   178  			ORDER BY
   179  				CreateAt`, map[string]interface{}{"CreatorId": userId}); err != nil {
   180  		return nil, model.NewAppError("SqlFileInfoStore.GetForPost",
   181  			"store.sql_file_info.get_for_user_id.app_error", nil, "creator_id="+userId+", "+err.Error(), http.StatusInternalServerError)
   182  	}
   183  	return infos, nil
   184  }
   185  
   186  func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) *model.AppError {
   187  	sqlResult, err := fs.GetMaster().Exec(
   188  		`UPDATE
   189  					FileInfo
   190  				SET
   191  					PostId = :PostId
   192  				WHERE
   193  					Id = :Id
   194  					AND PostId = ''
   195  					AND CreatorId = :CreatorId`, map[string]interface{}{"PostId": postId, "Id": fileId, "CreatorId": creatorId})
   196  	if err != nil {
   197  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   198  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   199  	}
   200  
   201  	count, err := sqlResult.RowsAffected()
   202  	if err != nil {
   203  		// RowsAffected should never fail with the MySQL or Postgres drivers
   204  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   205  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   206  	} else if count == 0 {
   207  		// Could not attach the file to the post
   208  		return model.NewAppError("SqlFileInfoStore.AttachToPost",
   209  			"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId, http.StatusBadRequest)
   210  	}
   211  	return nil
   212  }
   213  
   214  func (fs SqlFileInfoStore) DeleteForPost(postId string) (string, *model.AppError) {
   215  	if _, err := fs.GetMaster().Exec(
   216  		`UPDATE
   217  				FileInfo
   218  			SET
   219  				DeleteAt = :DeleteAt
   220  			WHERE
   221  				PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil {
   222  		return "", model.NewAppError("SqlFileInfoStore.DeleteForPost",
   223  			"store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError)
   224  	}
   225  	return postId, nil
   226  }
   227  
   228  func (fs SqlFileInfoStore) PermanentDelete(fileId string) *model.AppError {
   229  	if _, err := fs.GetMaster().Exec(
   230  		`DELETE FROM
   231  				FileInfo
   232  			WHERE
   233  				Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil {
   234  		return model.NewAppError("SqlFileInfoStore.PermanentDelete",
   235  			"store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   236  	}
   237  	return nil
   238  }
   239  
   240  func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int64, *model.AppError) {
   241  	var query string
   242  	if s.DriverName() == "postgres" {
   243  		query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))"
   244  	} else {
   245  		query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit"
   246  	}
   247  
   248  	sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit})
   249  	if err != nil {
   250  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   251  	}
   252  	rowsAffected, err1 := sqlResult.RowsAffected()
   253  	if err1 != nil {
   254  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   255  	}
   256  	return rowsAffected, nil
   257  }
   258  
   259  func (s SqlFileInfoStore) PermanentDeleteByUser(userId string) (int64, *model.AppError) {
   260  	query := "DELETE from FileInfo WHERE CreatorId = :CreatorId"
   261  
   262  	sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId})
   263  	if err != nil {
   264  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   265  	}
   266  
   267  	rowsAffected, err1 := sqlResult.RowsAffected()
   268  	if err1 != nil {
   269  		return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   270  	}
   271  	return rowsAffected, nil
   272  }