github.com/mad-app/mattermost-server@v5.11.1+incompatible/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  	"net/http"
     9  
    10  	"github.com/mattermost/mattermost-server/einterfaces"
    11  	"github.com/mattermost/mattermost-server/model"
    12  	"github.com/mattermost/mattermost-server/store"
    13  	"github.com/mattermost/mattermost-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) store.StoreChannel {
    65  	return store.Do(func(result *store.StoreResult) {
    66  		info.PreSave()
    67  		if result.Err = info.IsValid(); result.Err != nil {
    68  			return
    69  		}
    70  
    71  		if err := fs.GetMaster().Insert(info); err != nil {
    72  			result.Err = model.NewAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error(), http.StatusInternalServerError)
    73  		} else {
    74  			result.Data = info
    75  		}
    76  	})
    77  }
    78  
    79  func (fs SqlFileInfoStore) Get(id string) store.StoreChannel {
    80  	return store.Do(func(result *store.StoreResult) {
    81  		info := &model.FileInfo{}
    82  
    83  		if err := fs.GetReplica().SelectOne(info,
    84  			`SELECT
    85  				*
    86  			FROM
    87  				FileInfo
    88  			WHERE
    89  				Id = :Id
    90  				AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil {
    91  			if err == sql.ErrNoRows {
    92  				result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound)
    93  			} else {
    94  				result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError)
    95  			}
    96  		} else {
    97  			result.Data = info
    98  		}
    99  	})
   100  }
   101  
   102  func (fs SqlFileInfoStore) GetByPath(path string) store.StoreChannel {
   103  	return store.Do(func(result *store.StoreResult) {
   104  		info := &model.FileInfo{}
   105  
   106  		if err := fs.GetReplica().SelectOne(info,
   107  			`SELECT
   108  				*
   109  			FROM
   110  				FileInfo
   111  			WHERE
   112  				Path = :Path
   113  				AND DeleteAt = 0
   114  			LIMIT 1`, map[string]interface{}{"Path": path}); err != nil {
   115  			result.Err = model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError)
   116  		} else {
   117  			result.Data = info
   118  		}
   119  	})
   120  }
   121  
   122  func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string) {
   123  	fileInfoCache.Remove(postId)
   124  	if fs.metrics != nil {
   125  		fs.metrics.IncrementMemCacheInvalidationCounter("File Info Cache - Remove by PostId")
   126  	}
   127  }
   128  
   129  func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster bool, allowFromCache bool) store.StoreChannel {
   130  	return store.Do(func(result *store.StoreResult) {
   131  		if allowFromCache {
   132  			if cacheItem, ok := fileInfoCache.Get(postId); ok {
   133  				if fs.metrics != nil {
   134  					fs.metrics.IncrementMemCacheHitCounter("File Info Cache")
   135  				}
   136  
   137  				result.Data = cacheItem.([]*model.FileInfo)
   138  				return
   139  			} else {
   140  				if fs.metrics != nil {
   141  					fs.metrics.IncrementMemCacheMissCounter("File Info Cache")
   142  				}
   143  			}
   144  		} else {
   145  			if fs.metrics != nil {
   146  				fs.metrics.IncrementMemCacheMissCounter("File Info Cache")
   147  			}
   148  		}
   149  
   150  		var infos []*model.FileInfo
   151  
   152  		dbmap := fs.GetReplica()
   153  
   154  		if readFromMaster {
   155  			dbmap = fs.GetMaster()
   156  		}
   157  
   158  		if _, err := dbmap.Select(&infos,
   159  			`SELECT
   160  				*
   161  			FROM
   162  				FileInfo
   163  			WHERE
   164  				PostId = :PostId
   165  				AND DeleteAt = 0
   166  			ORDER BY
   167  				CreateAt`, map[string]interface{}{"PostId": postId}); err != nil {
   168  			result.Err = model.NewAppError("SqlFileInfoStore.GetForPost",
   169  				"store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError)
   170  		} else {
   171  			if len(infos) > 0 {
   172  				fileInfoCache.AddWithExpiresInSecs(postId, infos, FILE_INFO_CACHE_SEC)
   173  			}
   174  
   175  			result.Data = infos
   176  		}
   177  	})
   178  }
   179  
   180  func (fs SqlFileInfoStore) GetForUser(userId string) store.StoreChannel {
   181  	return store.Do(func(result *store.StoreResult) {
   182  		var infos []*model.FileInfo
   183  
   184  		dbmap := fs.GetReplica()
   185  
   186  		if _, err := dbmap.Select(&infos,
   187  			`SELECT
   188  				*
   189  			FROM
   190  				FileInfo
   191  			WHERE
   192  				CreatorId = :CreatorId
   193  				AND DeleteAt = 0
   194  			ORDER BY
   195  				CreateAt`, map[string]interface{}{"CreatorId": userId}); err != nil {
   196  			result.Err = model.NewAppError("SqlFileInfoStore.GetForPost",
   197  				"store.sql_file_info.get_for_user_id.app_error", nil, "creator_id="+userId+", "+err.Error(), http.StatusInternalServerError)
   198  		} else {
   199  			result.Data = infos
   200  		}
   201  	})
   202  }
   203  
   204  func (fs SqlFileInfoStore) AttachToPost(fileId, postId, creatorId string) store.StoreChannel {
   205  	return store.Do(func(result *store.StoreResult) {
   206  		sqlResult, err := fs.GetMaster().Exec(
   207  			`UPDATE
   208  					FileInfo
   209  				SET
   210  					PostId = :PostId
   211  				WHERE
   212  					Id = :Id
   213  					AND PostId = ''
   214  					AND CreatorId = :CreatorId`, map[string]interface{}{"PostId": postId, "Id": fileId, "CreatorId": creatorId})
   215  		if err != nil {
   216  			result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost",
   217  				"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   218  			return
   219  		}
   220  
   221  		count, err := sqlResult.RowsAffected()
   222  		if err != nil {
   223  			// RowsAffected should never fail with the MySQL or Postgres drivers
   224  			result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost",
   225  				"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   226  		} else if count == 0 {
   227  			// Could not attach the file to the post
   228  			result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost",
   229  				"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId, http.StatusBadRequest)
   230  		}
   231  	})
   232  }
   233  
   234  func (fs SqlFileInfoStore) DeleteForPost(postId string) store.StoreChannel {
   235  	return store.Do(func(result *store.StoreResult) {
   236  		if _, err := fs.GetMaster().Exec(
   237  			`UPDATE
   238  				FileInfo
   239  			SET
   240  				DeleteAt = :DeleteAt
   241  			WHERE
   242  				PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil {
   243  			result.Err = model.NewAppError("SqlFileInfoStore.DeleteForPost",
   244  				"store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError)
   245  		} else {
   246  			result.Data = postId
   247  		}
   248  	})
   249  }
   250  
   251  func (fs SqlFileInfoStore) PermanentDelete(fileId string) store.StoreChannel {
   252  	return store.Do(func(result *store.StoreResult) {
   253  		if _, err := fs.GetMaster().Exec(
   254  			`DELETE FROM
   255  				FileInfo
   256  			WHERE
   257  				Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil {
   258  			result.Err = model.NewAppError("SqlFileInfoStore.PermanentDelete",
   259  				"store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
   260  		}
   261  	})
   262  }
   263  
   264  func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) store.StoreChannel {
   265  	return store.Do(func(result *store.StoreResult) {
   266  		var query string
   267  		if s.DriverName() == "postgres" {
   268  			query = "DELETE from FileInfo WHERE Id = any (array (SELECT Id FROM FileInfo WHERE CreateAt < :EndTime LIMIT :Limit))"
   269  		} else {
   270  			query = "DELETE from FileInfo WHERE CreateAt < :EndTime LIMIT :Limit"
   271  		}
   272  
   273  		sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"EndTime": endTime, "Limit": limit})
   274  		if err != nil {
   275  			result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   276  		} else {
   277  			rowsAffected, err1 := sqlResult.RowsAffected()
   278  			if err1 != nil {
   279  				result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   280  				result.Data = int64(0)
   281  			} else {
   282  				result.Data = rowsAffected
   283  			}
   284  		}
   285  	})
   286  }
   287  
   288  func (s SqlFileInfoStore) PermanentDeleteByUser(userId string) store.StoreChannel {
   289  	return store.Do(func(result *store.StoreResult) {
   290  		query := "DELETE from FileInfo WHERE CreatorId = :CreatorId"
   291  
   292  		sqlResult, err := s.GetMaster().Exec(query, map[string]interface{}{"CreatorId": userId})
   293  		if err != nil {
   294  			result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   295  		} else {
   296  			rowsAffected, err1 := sqlResult.RowsAffected()
   297  			if err1 != nil {
   298  				result.Err = model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
   299  				result.Data = int64(0)
   300  			} else {
   301  				result.Data = rowsAffected
   302  			}
   303  		}
   304  	})
   305  }