MM-12342: merge the experimental channel store (#9681)
* MM-12342: merge the experimental channel store * gofmt after upgrading to go 1.11
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
1074a1c7ab
Коммит
937b6480d5
@@ -219,14 +219,6 @@ func New(options ...Option) (outApp *App, outErr error) {
|
|||||||
|
|
||||||
app.Srv.Store = app.newStore()
|
app.Srv.Store = app.newStore()
|
||||||
|
|
||||||
app.AddConfigListener(func(_, current *model.Config) {
|
|
||||||
if current.SqlSettings.EnablePublicChannelsMaterialization != nil && !*current.SqlSettings.EnablePublicChannelsMaterialization {
|
|
||||||
app.Srv.Store.Channel().DisableExperimentalPublicChannelsMaterialization()
|
|
||||||
} else {
|
|
||||||
app.Srv.Store.Channel().EnableExperimentalPublicChannelsMaterialization()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := app.ensureAsymmetricSigningKey(); err != nil {
|
if err := app.ensureAsymmetricSigningKey(); err != nil {
|
||||||
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,15 +306,14 @@ func (a *App) trackConfig() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
a.SendDiagnostic(TRACK_CONFIG_SQL, map[string]interface{}{
|
a.SendDiagnostic(TRACK_CONFIG_SQL, map[string]interface{}{
|
||||||
"driver_name": *cfg.SqlSettings.DriverName,
|
"driver_name": *cfg.SqlSettings.DriverName,
|
||||||
"trace": cfg.SqlSettings.Trace,
|
"trace": cfg.SqlSettings.Trace,
|
||||||
"max_idle_conns": *cfg.SqlSettings.MaxIdleConns,
|
"max_idle_conns": *cfg.SqlSettings.MaxIdleConns,
|
||||||
"conn_max_lifetime_milliseconds": *cfg.SqlSettings.ConnMaxLifetimeMilliseconds,
|
"conn_max_lifetime_milliseconds": *cfg.SqlSettings.ConnMaxLifetimeMilliseconds,
|
||||||
"max_open_conns": *cfg.SqlSettings.MaxOpenConns,
|
"max_open_conns": *cfg.SqlSettings.MaxOpenConns,
|
||||||
"data_source_replicas": len(cfg.SqlSettings.DataSourceReplicas),
|
"data_source_replicas": len(cfg.SqlSettings.DataSourceReplicas),
|
||||||
"data_source_search_replicas": len(cfg.SqlSettings.DataSourceSearchReplicas),
|
"data_source_search_replicas": len(cfg.SqlSettings.DataSourceSearchReplicas),
|
||||||
"query_timeout": *cfg.SqlSettings.QueryTimeout,
|
"query_timeout": *cfg.SqlSettings.QueryTimeout,
|
||||||
"enable_public_channels_materialization": *cfg.SqlSettings.EnablePublicChannelsMaterialization,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
a.SendDiagnostic(TRACK_CONFIG_LOG, map[string]interface{}{
|
a.SendDiagnostic(TRACK_CONFIG_LOG, map[string]interface{}{
|
||||||
|
|||||||
@@ -133,8 +133,7 @@
|
|||||||
"MaxOpenConns": 300,
|
"MaxOpenConns": 300,
|
||||||
"Trace": false,
|
"Trace": false,
|
||||||
"AtRestEncryptKey": "",
|
"AtRestEncryptKey": "",
|
||||||
"QueryTimeout": 30,
|
"QueryTimeout": 30
|
||||||
"EnablePublicChannelsMaterialization": true
|
|
||||||
},
|
},
|
||||||
"LogSettings": {
|
"LogSettings": {
|
||||||
"EnableConsole": true,
|
"EnableConsole": true,
|
||||||
|
|||||||
@@ -683,17 +683,16 @@ type SSOSettings struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SqlSettings struct {
|
type SqlSettings struct {
|
||||||
DriverName *string
|
DriverName *string
|
||||||
DataSource *string
|
DataSource *string
|
||||||
DataSourceReplicas []string
|
DataSourceReplicas []string
|
||||||
DataSourceSearchReplicas []string
|
DataSourceSearchReplicas []string
|
||||||
MaxIdleConns *int
|
MaxIdleConns *int
|
||||||
ConnMaxLifetimeMilliseconds *int
|
ConnMaxLifetimeMilliseconds *int
|
||||||
MaxOpenConns *int
|
MaxOpenConns *int
|
||||||
Trace bool
|
Trace bool
|
||||||
AtRestEncryptKey string
|
AtRestEncryptKey string
|
||||||
QueryTimeout *int
|
QueryTimeout *int
|
||||||
EnablePublicChannelsMaterialization *bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlSettings) SetDefaults() {
|
func (s *SqlSettings) SetDefaults() {
|
||||||
@@ -724,10 +723,6 @@ func (s *SqlSettings) SetDefaults() {
|
|||||||
if s.QueryTimeout == nil {
|
if s.QueryTimeout == nil {
|
||||||
s.QueryTimeout = NewInt(30)
|
s.QueryTimeout = NewInt(30)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.EnablePublicChannelsMaterialization == nil {
|
|
||||||
s.EnablePublicChannelsMaterialization = NewBool(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogSettings struct {
|
type LogSettings struct {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mattermost/gorp"
|
"github.com/mattermost/gorp"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/einterfaces"
|
"github.com/mattermost/mattermost-server/einterfaces"
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
@@ -232,6 +233,17 @@ func (db allChannelMembers) ToMapStringString() map[string]string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// publicChannel is a subset of the metadata corresponding to public channels only.
|
||||||
|
type publicChannel struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
DeleteAt int64 `json:"delete_at"`
|
||||||
|
TeamId string `json:"team_id"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Header string `json:"header"`
|
||||||
|
Purpose string `json:"purpose"`
|
||||||
|
}
|
||||||
|
|
||||||
var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
|
var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
|
||||||
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
|
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
|
||||||
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
|
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
|
||||||
@@ -278,6 +290,15 @@ func NewSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface)
|
|||||||
tablem.ColMap("UserId").SetMaxSize(26)
|
tablem.ColMap("UserId").SetMaxSize(26)
|
||||||
tablem.ColMap("Roles").SetMaxSize(64)
|
tablem.ColMap("Roles").SetMaxSize(64)
|
||||||
tablem.ColMap("NotifyProps").SetMaxSize(2000)
|
tablem.ColMap("NotifyProps").SetMaxSize(2000)
|
||||||
|
|
||||||
|
tablePublicChannels := db.AddTableWithName(publicChannel{}, "PublicChannels").SetKeys(false, "Id")
|
||||||
|
tablePublicChannels.ColMap("Id").SetMaxSize(26)
|
||||||
|
tablePublicChannels.ColMap("TeamId").SetMaxSize(26)
|
||||||
|
tablePublicChannels.ColMap("DisplayName").SetMaxSize(64)
|
||||||
|
tablePublicChannels.ColMap("Name").SetMaxSize(64)
|
||||||
|
tablePublicChannels.SetUniqueTogether("Name", "TeamId")
|
||||||
|
tablePublicChannels.ColMap("Header").SetMaxSize(1024)
|
||||||
|
tablePublicChannels.ColMap("Purpose").SetMaxSize(250)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
@@ -299,21 +320,112 @@ func (s SqlChannelStore) CreateIndexesIfNotExists() {
|
|||||||
s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId")
|
s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId")
|
||||||
|
|
||||||
s.CreateFullTextIndexIfNotExists("idx_channel_search_txt", "Channels", "Name, DisplayName, Purpose")
|
s.CreateFullTextIndexIfNotExists("idx_channel_search_txt", "Channels", "Name, DisplayName, Purpose")
|
||||||
|
|
||||||
|
s.CreateIndexIfNotExists("idx_publicchannels_team_id", "PublicChannels", "TeamId")
|
||||||
|
s.CreateIndexIfNotExists("idx_publicchannels_name", "PublicChannels", "Name")
|
||||||
|
s.CreateIndexIfNotExists("idx_publicchannels_delete_at", "PublicChannels", "DeleteAt")
|
||||||
|
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
||||||
|
s.CreateIndexIfNotExists("idx_publicchannels_name_lower", "PublicChannels", "lower(Name)")
|
||||||
|
s.CreateIndexIfNotExists("idx_publicchannels_displayname_lower", "PublicChannels", "lower(DisplayName)")
|
||||||
|
}
|
||||||
|
s.CreateFullTextIndexIfNotExists("idx_publicchannels_search_txt", "PublicChannels", "Name, DisplayName, Purpose")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MigratePublicChannels initializes the PublicChannels table with data created before this version
|
||||||
|
// of the Mattermost server kept it up-to-date.
|
||||||
func (s SqlChannelStore) MigratePublicChannels() error {
|
func (s SqlChannelStore) MigratePublicChannels() error {
|
||||||
// See SqlChannelStoreExperimental
|
transaction, err := s.GetMaster().Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := transaction.Exec(`
|
||||||
|
INSERT INTO PublicChannels
|
||||||
|
(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
|
||||||
|
SELECT
|
||||||
|
c.Id, c.DeleteAt, c.TeamId, c.DisplayName, c.Name, c.Header, c.Purpose
|
||||||
|
FROM
|
||||||
|
Channels c
|
||||||
|
LEFT JOIN
|
||||||
|
PublicChannels pc ON (pc.Id = c.Id)
|
||||||
|
WHERE
|
||||||
|
c.Type = 'O'
|
||||||
|
AND pc.Id IS NULL
|
||||||
|
`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := transaction.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) DropPublicChannels() error {
|
func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, channel *model.Channel) error {
|
||||||
// See SqlChannelStoreExperimental
|
publicChannel := &publicChannel{
|
||||||
|
Id: channel.Id,
|
||||||
|
DeleteAt: channel.DeleteAt,
|
||||||
|
TeamId: channel.TeamId,
|
||||||
|
DisplayName: channel.DisplayName,
|
||||||
|
Name: channel.Name,
|
||||||
|
Header: channel.Header,
|
||||||
|
Purpose: channel.Purpose,
|
||||||
|
}
|
||||||
|
|
||||||
|
if channel.Type != model.CHANNEL_OPEN {
|
||||||
|
if _, err := transaction.Delete(publicChannel); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to delete public channel")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||||
|
// Leverage native upsert for MySQL, since RowsAffected returns 0 if the row exists
|
||||||
|
// but no changes were made, breaking the update-then-insert paradigm below when
|
||||||
|
// the row already exists. (Postgres 9.4 doesn't support native upsert.)
|
||||||
|
if _, err := transaction.Exec(`
|
||||||
|
INSERT INTO
|
||||||
|
PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
|
||||||
|
VALUES
|
||||||
|
(:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
DeleteAt = :DeleteAt,
|
||||||
|
TeamId = :TeamId,
|
||||||
|
DisplayName = :DisplayName,
|
||||||
|
Name = :Name,
|
||||||
|
Header = :Header,
|
||||||
|
Purpose = :Purpose;
|
||||||
|
`, map[string]interface{}{
|
||||||
|
"Id": publicChannel.Id,
|
||||||
|
"DeleteAt": publicChannel.DeleteAt,
|
||||||
|
"TeamId": publicChannel.TeamId,
|
||||||
|
"DisplayName": publicChannel.DisplayName,
|
||||||
|
"Name": publicChannel.Name,
|
||||||
|
"Header": publicChannel.Header,
|
||||||
|
"Purpose": publicChannel.Purpose,
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to insert public channel")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count, err := transaction.Update(publicChannel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to update public channel")
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := transaction.Insert(publicChannel); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to insert public channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save writes the (non-direct) channel channel to the database.
|
// Save writes the (non-direct) channel channel to the database.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel {
|
func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
if channel.DeleteAt != 0 {
|
if channel.DeleteAt != 0 {
|
||||||
@@ -338,6 +450,13 @@ func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additionally propagate the write to the PublicChannels table.
|
||||||
|
if err := s.upsertPublicChannelT(transaction, result.Data.(*model.Channel)); err != nil {
|
||||||
|
transaction.Rollback()
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.upsert_public_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
if err := transaction.Commit(); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -473,8 +592,6 @@ func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update writes the updated channel to the database.
|
// Update writes the updated channel to the database.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) Update(channel *model.Channel) store.StoreChannel {
|
func (s SqlChannelStore) Update(channel *model.Channel) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
transaction, err := s.GetMaster().Begin()
|
transaction, err := s.GetMaster().Begin()
|
||||||
@@ -489,11 +606,19 @@ func (s SqlChannelStore) Update(channel *model.Channel) store.StoreChannel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additionally propagate the write to the PublicChannels table.
|
||||||
|
if err := s.upsertPublicChannelT(transaction, result.Data.(*model.Channel)); err != nil {
|
||||||
|
transaction.Rollback()
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.upsert_public_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
if err := transaction.Commit(); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.Update", "store.sql_channel.update.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel *model.Channel) store.StoreResult {
|
func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel *model.Channel) store.StoreResult {
|
||||||
@@ -642,22 +767,16 @@ func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) store.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete records the given deleted timestamp to the channel in question.
|
// Delete records the given deleted timestamp to the channel in question.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) Delete(channelId string, time int64) store.StoreChannel {
|
func (s SqlChannelStore) Delete(channelId string, time int64) store.StoreChannel {
|
||||||
return s.SetDeleteAt(channelId, time, time)
|
return s.SetDeleteAt(channelId, time, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore reverts a previous deleted timestamp from the channel in question.
|
// Restore reverts a previous deleted timestamp from the channel in question.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) Restore(channelId string, time int64) store.StoreChannel {
|
func (s SqlChannelStore) Restore(channelId string, time int64) store.StoreChannel {
|
||||||
return s.SetDeleteAt(channelId, 0, time)
|
return s.SetDeleteAt(channelId, 0, time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDeleteAt records the given deleted and updated timestamp to the channel in question.
|
// SetDeleteAt records the given deleted and updated timestamp to the channel in question.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) store.StoreChannel {
|
func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
defer s.InvalidateChannel(channelId)
|
defer s.InvalidateChannel(channelId)
|
||||||
@@ -674,6 +793,23 @@ func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additionally propagate the write to the PublicChannels table.
|
||||||
|
if _, err := transaction.Exec(`
|
||||||
|
UPDATE
|
||||||
|
PublicChannels
|
||||||
|
SET
|
||||||
|
DeleteAt = :DeleteAt
|
||||||
|
WHERE
|
||||||
|
Id = :ChannelId
|
||||||
|
`, map[string]interface{}{
|
||||||
|
"DeleteAt": deleteAt,
|
||||||
|
"ChannelId": channelId,
|
||||||
|
}); err != nil {
|
||||||
|
transaction.Rollback()
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.SetDeleteAt", "store.sql_channel.set_delete_at.update_public_channel.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
if err := transaction.Commit(); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.SetDeleteAt", "store.sql_channel.set_delete_at.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.SetDeleteAt", "store.sql_channel.set_delete_at.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -694,8 +830,6 @@ func (s SqlChannelStore) setDeleteAtT(transaction *gorp.Transaction, channelId s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PermanentDeleteByTeam removes all channels for the given team from the database.
|
// PermanentDeleteByTeam removes all channels for the given team from the database.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) store.StoreChannel {
|
func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
transaction, err := s.GetMaster().Begin()
|
transaction, err := s.GetMaster().Begin()
|
||||||
@@ -710,6 +844,20 @@ func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) store.StoreChannel
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additionally propagate the deletions to the PublicChannels table.
|
||||||
|
if _, err := transaction.Exec(`
|
||||||
|
DELETE FROM
|
||||||
|
PublicChannels
|
||||||
|
WHERE
|
||||||
|
TeamId = :TeamId
|
||||||
|
`, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
}); err != nil {
|
||||||
|
transaction.Rollback()
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.PermanentDeleteByTeamt", "store.sql_channel.permanent_delete_by_team.delete_public_channels.app_error", nil, "team_id="+teamId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
if err := transaction.Commit(); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.PermanentDeleteByTeam", "store.sql_channel.permanent_delete_by_team.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.PermanentDeleteByTeam", "store.sql_channel.permanent_delete_by_team.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -729,8 +877,6 @@ func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *gorp.Transaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PermanentDelete removes the given channel from the database.
|
// PermanentDelete removes the given channel from the database.
|
||||||
//
|
|
||||||
// @see ChannelStoreExperimental for how this update propagates to the PublicChannels table.
|
|
||||||
func (s SqlChannelStore) PermanentDelete(channelId string) store.StoreChannel {
|
func (s SqlChannelStore) PermanentDelete(channelId string) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
transaction, err := s.GetMaster().Begin()
|
transaction, err := s.GetMaster().Begin()
|
||||||
@@ -745,6 +891,20 @@ func (s SqlChannelStore) PermanentDelete(channelId string) store.StoreChannel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Additionally propagate the deletion to the PublicChannels table.
|
||||||
|
if _, err := transaction.Exec(`
|
||||||
|
DELETE FROM
|
||||||
|
PublicChannels
|
||||||
|
WHERE
|
||||||
|
Id = :ChannelId
|
||||||
|
`, map[string]interface{}{
|
||||||
|
"ChannelId": channelId,
|
||||||
|
}); err != nil {
|
||||||
|
transaction.Rollback()
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.PermanentDelete", "store.sql_channel.permanent_delete.delete_public_channel.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
if err := transaction.Commit(); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.PermanentDelete", "store.sql_channel.permanent_delete.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.PermanentDelete", "store.sql_channel.permanent_delete.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
@@ -798,29 +958,38 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDelete
|
|||||||
func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
|
func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
data := &model.ChannelList{}
|
data := &model.ChannelList{}
|
||||||
_, err := s.GetReplica().Select(data,
|
_, err := s.GetReplica().Select(data, `
|
||||||
`SELECT
|
SELECT
|
||||||
*
|
Channels.*
|
||||||
FROM
|
FROM
|
||||||
Channels
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels c ON (c.Id = Channels.Id)
|
||||||
WHERE
|
WHERE
|
||||||
TeamId = :TeamId1
|
c.TeamId = :TeamId
|
||||||
AND Type IN ('O')
|
AND c.DeleteAt = 0
|
||||||
AND DeleteAt = 0
|
AND c.Id NOT IN (
|
||||||
AND Id NOT IN (SELECT
|
SELECT
|
||||||
Channels.Id
|
c.Id
|
||||||
FROM
|
FROM
|
||||||
Channels,
|
PublicChannels c
|
||||||
ChannelMembers
|
JOIN
|
||||||
WHERE
|
ChannelMembers cm ON (cm.ChannelId = c.Id)
|
||||||
Id = ChannelId
|
WHERE
|
||||||
AND TeamId = :TeamId2
|
c.TeamId = :TeamId
|
||||||
AND UserId = :UserId
|
AND cm.UserId = :UserId
|
||||||
AND DeleteAt = 0)
|
AND c.DeleteAt = 0
|
||||||
ORDER BY DisplayName
|
)
|
||||||
|
ORDER BY
|
||||||
|
c.DisplayName
|
||||||
LIMIT :Limit
|
LIMIT :Limit
|
||||||
OFFSET :Offset`,
|
OFFSET :Offset
|
||||||
map[string]interface{}{"TeamId1": teamId, "TeamId2": teamId, "UserId": userId, "Limit": limit, "Offset": offset})
|
`, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
"UserId": userId,
|
||||||
|
"Limit": limit,
|
||||||
|
"Offset": offset,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
|
||||||
@@ -834,19 +1003,24 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset in
|
|||||||
func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) store.StoreChannel {
|
func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
data := &model.ChannelList{}
|
data := &model.ChannelList{}
|
||||||
_, err := s.GetReplica().Select(data,
|
_, err := s.GetReplica().Select(data, `
|
||||||
`SELECT
|
SELECT
|
||||||
*
|
Channels.*
|
||||||
FROM
|
FROM
|
||||||
Channels
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels pc ON (pc.Id = Channels.Id)
|
||||||
WHERE
|
WHERE
|
||||||
TeamId = :TeamId
|
pc.TeamId = :TeamId
|
||||||
AND Type = 'O'
|
AND pc.DeleteAt = 0
|
||||||
AND DeleteAt = 0
|
ORDER BY pc.DisplayName
|
||||||
ORDER BY DisplayName
|
|
||||||
LIMIT :Limit
|
LIMIT :Limit
|
||||||
OFFSET :Offset`,
|
OFFSET :Offset
|
||||||
map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset})
|
`, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
"Limit": limit,
|
||||||
|
"Offset": offset,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
|
||||||
@@ -874,18 +1048,19 @@ func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := &model.ChannelList{}
|
data := &model.ChannelList{}
|
||||||
_, err := s.GetReplica().Select(data,
|
_, err := s.GetReplica().Select(data, `
|
||||||
`SELECT
|
SELECT
|
||||||
*
|
Channels.*
|
||||||
FROM
|
FROM
|
||||||
Channels
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels pc ON (pc.Id = Channels.Id)
|
||||||
WHERE
|
WHERE
|
||||||
TeamId = :teamId
|
pc.TeamId = :teamId
|
||||||
AND Type = 'O'
|
AND pc.DeleteAt = 0
|
||||||
AND DeleteAt = 0
|
AND pc.Id IN (`+idQuery+`)
|
||||||
AND Id IN (`+idQuery+`)
|
ORDER BY pc.DisplayName
|
||||||
ORDER BY DisplayName`,
|
`, props)
|
||||||
props)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -1721,33 +1896,35 @@ func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.S
|
|||||||
|
|
||||||
func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
deleteFilter := "AND DeleteAt = 0"
|
deleteFilter := "AND c.DeleteAt = 0"
|
||||||
if includeDeleted {
|
if includeDeleted {
|
||||||
deleteFilter = ""
|
deleteFilter = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
queryFormat := `
|
queryFormat := `
|
||||||
SELECT
|
SELECT
|
||||||
*
|
Channels.*
|
||||||
FROM
|
FROM
|
||||||
Channels
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels c ON (c.Id = Channels.Id)
|
||||||
WHERE
|
WHERE
|
||||||
TeamId = :TeamId
|
c.TeamId = :TeamId
|
||||||
AND Type = 'O'
|
` + deleteFilter + `
|
||||||
` + deleteFilter + `
|
%v
|
||||||
%v
|
LIMIT 50
|
||||||
LIMIT 50`
|
`
|
||||||
|
|
||||||
var channels model.ChannelList
|
var channels model.ChannelList
|
||||||
|
|
||||||
if likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose"); likeClause == "" {
|
if likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose"); likeClause == "" {
|
||||||
if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil {
|
if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Using a UNION results in index_merge and fulltext queries and is much faster than the ref
|
// Using a UNION results in index_merge and fulltext queries and is much faster than the ref
|
||||||
// query you would get using an OR of the LIKE and full-text clauses.
|
// query you would get using an OR of the LIKE and full-text clauses.
|
||||||
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose")
|
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause)
|
likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause)
|
||||||
fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause)
|
fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause)
|
||||||
query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery)
|
query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery)
|
||||||
@@ -1863,53 +2040,61 @@ func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string
|
|||||||
|
|
||||||
func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
deleteFilter := "AND DeleteAt = 0"
|
deleteFilter := "AND c.DeleteAt = 0"
|
||||||
if includeDeleted {
|
if includeDeleted {
|
||||||
deleteFilter = ""
|
deleteFilter = ""
|
||||||
}
|
}
|
||||||
searchQuery := `
|
|
||||||
SELECT
|
|
||||||
*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
WHERE
|
|
||||||
TeamId = :TeamId
|
|
||||||
AND Type = 'O'
|
|
||||||
` + deleteFilter + `
|
|
||||||
SEARCH_CLAUSE
|
|
||||||
ORDER BY DisplayName
|
|
||||||
LIMIT 100`
|
|
||||||
|
|
||||||
*result = s.performSearch(searchQuery, term, map[string]interface{}{"TeamId": teamId})
|
*result = s.performSearch(`
|
||||||
|
SELECT
|
||||||
|
Channels.*
|
||||||
|
FROM
|
||||||
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels c ON (c.Id = Channels.Id)
|
||||||
|
WHERE
|
||||||
|
c.TeamId = :TeamId
|
||||||
|
`+deleteFilter+`
|
||||||
|
SEARCH_CLAUSE
|
||||||
|
ORDER BY c.DisplayName
|
||||||
|
LIMIT 100
|
||||||
|
`, term, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) store.StoreChannel {
|
func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := `
|
*result = s.performSearch(`
|
||||||
SELECT
|
SELECT
|
||||||
*
|
Channels.*
|
||||||
FROM
|
FROM
|
||||||
Channels
|
Channels
|
||||||
|
JOIN
|
||||||
|
PublicChannels c ON (c.Id = Channels.Id)
|
||||||
WHERE
|
WHERE
|
||||||
TeamId = :TeamId
|
c.TeamId = :TeamId
|
||||||
AND Type = 'O'
|
AND c.DeleteAt = 0
|
||||||
AND DeleteAt = 0
|
AND c.Id NOT IN (
|
||||||
AND Id NOT IN (SELECT
|
SELECT
|
||||||
Channels.Id
|
c.Id
|
||||||
FROM
|
FROM
|
||||||
Channels,
|
PublicChannels c
|
||||||
ChannelMembers
|
JOIN
|
||||||
|
ChannelMembers cm ON (cm.ChannelId = c.Id)
|
||||||
WHERE
|
WHERE
|
||||||
Id = ChannelId
|
c.TeamId = :TeamId
|
||||||
AND TeamId = :TeamId
|
AND cm.UserId = :UserId
|
||||||
AND UserId = :UserId
|
AND c.DeleteAt = 0
|
||||||
AND DeleteAt = 0)
|
)
|
||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
ORDER BY DisplayName
|
ORDER BY c.DisplayName
|
||||||
LIMIT 100`
|
LIMIT 100
|
||||||
|
`, term, map[string]interface{}{
|
||||||
*result = s.performSearch(searchQuery, term, map[string]interface{}{"TeamId": teamId, "UserId": userId})
|
"TeamId": teamId,
|
||||||
|
"UserId": userId,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1987,13 +2172,13 @@ func (s SqlChannelStore) buildFulltextClause(term string, searchColumns string)
|
|||||||
func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) store.StoreResult {
|
func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) store.StoreResult {
|
||||||
result := store.StoreResult{}
|
result := store.StoreResult{}
|
||||||
|
|
||||||
likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose")
|
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
if likeTerm == "" {
|
if likeTerm == "" {
|
||||||
// If the likeTerm is empty after preparing, then don't bother searching.
|
// If the likeTerm is empty after preparing, then don't bother searching.
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
||||||
} else {
|
} else {
|
||||||
parameters["LikeTerm"] = likeTerm
|
parameters["LikeTerm"] = likeTerm
|
||||||
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose")
|
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
parameters["FulltextTerm"] = fulltextTerm
|
parameters["FulltextTerm"] = fulltextTerm
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
|
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
|
||||||
}
|
}
|
||||||
@@ -2259,19 +2444,6 @@ func (s SqlChannelStore) resetLastPostAtT(transaction *gorp.Transaction) store.S
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) EnableExperimentalPublicChannelsMaterialization() {
|
|
||||||
// See SqlChannelStoreExperimental
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStore) DisableExperimentalPublicChannelsMaterialization() {
|
|
||||||
// See SqlChannelStoreExperimental
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStore) IsExperimentalPublicChannelsMaterializationEnabled() bool {
|
|
||||||
// See SqlChannelStoreExperimental
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) store.StoreChannel {
|
func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
var data []*model.ChannelForExport
|
var data []*model.ChannelForExport
|
||||||
|
|||||||
@@ -1,778 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See License.txt for license information.
|
|
||||||
|
|
||||||
package sqlstore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/mattermost/gorp"
|
|
||||||
"github.com/mattermost/mattermost-server/einterfaces"
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
|
||||||
"github.com/mattermost/mattermost-server/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
// publicChannel is a subset of the metadata corresponding to public channels only.
|
|
||||||
type publicChannel struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
DeleteAt int64 `json:"delete_at"`
|
|
||||||
TeamId string `json:"team_id"`
|
|
||||||
DisplayName string `json:"display_name"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Header string `json:"header"`
|
|
||||||
Purpose string `json:"purpose"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SqlChannelStoreExperimental struct {
|
|
||||||
SqlChannelStore
|
|
||||||
experimentalPublicChannelsMaterializationDisabled *uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSqlChannelStoreExperimental(sqlStore SqlStore, metrics einterfaces.MetricsInterface, enabled bool) store.ChannelStore {
|
|
||||||
s := &SqlChannelStoreExperimental{
|
|
||||||
SqlChannelStore: *NewSqlChannelStore(sqlStore, metrics).(*SqlChannelStore),
|
|
||||||
experimentalPublicChannelsMaterializationDisabled: new(uint32),
|
|
||||||
}
|
|
||||||
|
|
||||||
if enabled {
|
|
||||||
// Forcibly log, since the default state is enabled and we want this on startup.
|
|
||||||
mlog.Info("Enabling experimental public channels materialization")
|
|
||||||
s.EnableExperimentalPublicChannelsMaterialization()
|
|
||||||
} else {
|
|
||||||
s.DisableExperimentalPublicChannelsMaterialization()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
for _, db := range sqlStore.GetAllConns() {
|
|
||||||
tablePublicChannels := db.AddTableWithName(publicChannel{}, "PublicChannels").SetKeys(false, "Id")
|
|
||||||
tablePublicChannels.ColMap("Id").SetMaxSize(26)
|
|
||||||
tablePublicChannels.ColMap("TeamId").SetMaxSize(26)
|
|
||||||
tablePublicChannels.ColMap("DisplayName").SetMaxSize(64)
|
|
||||||
tablePublicChannels.ColMap("Name").SetMaxSize(64)
|
|
||||||
tablePublicChannels.SetUniqueTogether("Name", "TeamId")
|
|
||||||
tablePublicChannels.ColMap("Header").SetMaxSize(1024)
|
|
||||||
tablePublicChannels.ColMap("Purpose").SetMaxSize(250)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// migratePublicChannels initializes the PublicChannels table with data created before this version
|
|
||||||
// of the Mattermost server kept it up-to-date.
|
|
||||||
func (s SqlChannelStoreExperimental) MigratePublicChannels() error {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.MigratePublicChannels()
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := transaction.Exec(`
|
|
||||||
INSERT INTO PublicChannels
|
|
||||||
(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
|
|
||||||
SELECT
|
|
||||||
c.Id, c.DeleteAt, c.TeamId, c.DisplayName, c.Name, c.Header, c.Purpose
|
|
||||||
FROM
|
|
||||||
Channels c
|
|
||||||
LEFT JOIN
|
|
||||||
PublicChannels pc ON (pc.Id = c.Id)
|
|
||||||
WHERE
|
|
||||||
c.Type = 'O'
|
|
||||||
AND pc.Id IS NULL
|
|
||||||
`); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropPublicChannels removes the public channels table.
|
|
||||||
func (s SqlChannelStoreExperimental) DropPublicChannels() error {
|
|
||||||
_, err := s.GetMaster().Exec(`
|
|
||||||
DROP TABLE IF EXISTS PublicChannels
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to drop public channels table")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) CreateIndexesIfNotExists() {
|
|
||||||
s.SqlChannelStore.CreateIndexesIfNotExists()
|
|
||||||
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s.CreateIndexIfNotExists("idx_publicchannels_team_id", "PublicChannels", "TeamId")
|
|
||||||
s.CreateIndexIfNotExists("idx_publicchannels_name", "PublicChannels", "Name")
|
|
||||||
s.CreateIndexIfNotExists("idx_publicchannels_delete_at", "PublicChannels", "DeleteAt")
|
|
||||||
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
|
||||||
s.CreateIndexIfNotExists("idx_publicchannels_name_lower", "PublicChannels", "lower(Name)")
|
|
||||||
s.CreateIndexIfNotExists("idx_publicchannels_displayname_lower", "PublicChannels", "lower(DisplayName)")
|
|
||||||
}
|
|
||||||
s.CreateFullTextIndexIfNotExists("idx_publicchannels_search_txt", "PublicChannels", "Name, DisplayName, Purpose")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) upsertPublicChannelT(transaction *gorp.Transaction, channel *model.Channel) error {
|
|
||||||
publicChannel := &publicChannel{
|
|
||||||
Id: channel.Id,
|
|
||||||
DeleteAt: channel.DeleteAt,
|
|
||||||
TeamId: channel.TeamId,
|
|
||||||
DisplayName: channel.DisplayName,
|
|
||||||
Name: channel.Name,
|
|
||||||
Header: channel.Header,
|
|
||||||
Purpose: channel.Purpose,
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.Type != model.CHANNEL_OPEN {
|
|
||||||
if _, err := transaction.Delete(publicChannel); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to delete public channel")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
||||||
// Leverage native upsert for MySQL, since RowsAffected returns 0 if the row exists
|
|
||||||
// but no changes were made, breaking the update-then-insert paradigm below when
|
|
||||||
// the row already exists. (Postgres 9.4 doesn't support native upsert.)
|
|
||||||
if _, err := transaction.Exec(`
|
|
||||||
INSERT INTO
|
|
||||||
PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose)
|
|
||||||
VALUES
|
|
||||||
(:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
DeleteAt = :DeleteAt,
|
|
||||||
TeamId = :TeamId,
|
|
||||||
DisplayName = :DisplayName,
|
|
||||||
Name = :Name,
|
|
||||||
Header = :Header,
|
|
||||||
Purpose = :Purpose;
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"Id": publicChannel.Id,
|
|
||||||
"DeleteAt": publicChannel.DeleteAt,
|
|
||||||
"TeamId": publicChannel.TeamId,
|
|
||||||
"DisplayName": publicChannel.DisplayName,
|
|
||||||
"Name": publicChannel.Name,
|
|
||||||
"Header": publicChannel.Header,
|
|
||||||
"Purpose": publicChannel.Purpose,
|
|
||||||
}); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to insert public channel")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
count, err := transaction.Update(publicChannel)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to update public channel")
|
|
||||||
}
|
|
||||||
if count > 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Insert(publicChannel); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to insert public channel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.Save(channel, maxChannelsPerTeam)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
if channel.DeleteAt != 0 {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Save", "store.sql_channel.save.archived_channel.app_error", nil, "", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if channel.Type == model.CHANNEL_DIRECT {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Save", "store.sql_channel.save.direct_channel.app_error", nil, "", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Save", "store.sql_channel.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.saveChannelT(transaction, channel, maxChannelsPerTeam)
|
|
||||||
if result.Err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additionally propagate the write to the PublicChannels table.
|
|
||||||
if err := s.upsertPublicChannelT(transaction, result.Data.(*model.Channel)); err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Save", "store.sql_channel.save.upsert_public_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Save", "store.sql_channel.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) Update(channel *model.Channel) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.Update(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Update", "store.sql_channel.update.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.updateChannelT(transaction, channel)
|
|
||||||
if result.Err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additionally propagate the write to the PublicChannels table.
|
|
||||||
if err := s.upsertPublicChannelT(transaction, result.Data.(*model.Channel)); err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Update", "store.sql_channel.update.upsert_public_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.Update", "store.sql_channel.update.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) Delete(channelId string, time int64) store.StoreChannel {
|
|
||||||
// Call the experimental version first.
|
|
||||||
return s.SetDeleteAt(channelId, time, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) Restore(channelId string, time int64) store.StoreChannel {
|
|
||||||
// Call the experimental version first.
|
|
||||||
return s.SetDeleteAt(channelId, 0, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) SetDeleteAt(channelId string, deleteAt, updateAt int64) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.SetDeleteAt(channelId, deleteAt, updateAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
defer s.InvalidateChannel(channelId)
|
|
||||||
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.SetDeleteAt", "store.sql_channel.set_delete_at.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.setDeleteAtT(transaction, channelId, deleteAt, updateAt)
|
|
||||||
if result.Err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additionally propagate the write to the PublicChannels table.
|
|
||||||
if _, err := transaction.Exec(`
|
|
||||||
UPDATE
|
|
||||||
PublicChannels
|
|
||||||
SET
|
|
||||||
DeleteAt = :DeleteAt
|
|
||||||
WHERE
|
|
||||||
Id = :ChannelId
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"DeleteAt": deleteAt,
|
|
||||||
"ChannelId": channelId,
|
|
||||||
}); err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.SetDeleteAt", "store.sql_channel.set_delete_at.update_public_channel.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.SetDeleteAt", "store.sql_channel.set_delete_at.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) PermanentDeleteByTeam(teamId string) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.PermanentDeleteByTeam(teamId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDeleteByTeam", "store.sql_channel.permanent_delete_by_team.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.permanentDeleteByTeamtT(transaction, teamId)
|
|
||||||
if result.Err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additionally propagate the deletions to the PublicChannels table.
|
|
||||||
if _, err := transaction.Exec(`
|
|
||||||
DELETE FROM
|
|
||||||
PublicChannels
|
|
||||||
WHERE
|
|
||||||
TeamId = :TeamId
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"TeamId": teamId,
|
|
||||||
}); err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDeleteByTeamt", "store.sql_channel.permanent_delete_by_team.delete_public_channels.app_error", nil, "team_id="+teamId+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDeleteByTeam", "store.sql_channel.permanent_delete_by_team.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) PermanentDelete(channelId string) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.PermanentDelete(channelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
transaction, err := s.GetMaster().Begin()
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDelete", "store.sql_channel.permanent_delete.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.permanentDeleteT(transaction, channelId)
|
|
||||||
if result.Err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additionally propagate the deletion to the PublicChannels table.
|
|
||||||
if _, err := transaction.Exec(`
|
|
||||||
DELETE FROM
|
|
||||||
PublicChannels
|
|
||||||
WHERE
|
|
||||||
Id = :ChannelId
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"ChannelId": channelId,
|
|
||||||
}); err != nil {
|
|
||||||
transaction.Rollback()
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDelete", "store.sql_channel.permanent_delete.delete_public_channel.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := transaction.Commit(); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStoreExperimental.PermanentDelete", "store.sql_channel.permanent_delete.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) GetMoreChannels(teamId string, userId string, offset int, limit int) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.GetMoreChannels(teamId, userId, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
data := &model.ChannelList{}
|
|
||||||
_, err := s.GetReplica().Select(data, `
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels c ON (c.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
AND c.DeleteAt = 0
|
|
||||||
AND c.Id NOT IN (
|
|
||||||
SELECT
|
|
||||||
c.Id
|
|
||||||
FROM
|
|
||||||
PublicChannels c
|
|
||||||
JOIN
|
|
||||||
ChannelMembers cm ON (cm.ChannelId = c.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
AND cm.UserId = :UserId
|
|
||||||
AND c.DeleteAt = 0
|
|
||||||
)
|
|
||||||
ORDER BY
|
|
||||||
c.DisplayName
|
|
||||||
LIMIT :Limit
|
|
||||||
OFFSET :Offset
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"TeamId": teamId,
|
|
||||||
"UserId": userId,
|
|
||||||
"Limit": limit,
|
|
||||||
"Offset": offset,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetMoreChannels", "store.sql_channel.get_more_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Data = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) GetPublicChannelsForTeam(teamId string, offset int, limit int) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.GetPublicChannelsForTeam(teamId, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
data := &model.ChannelList{}
|
|
||||||
_, err := s.GetReplica().Select(data, `
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels pc ON (pc.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
pc.TeamId = :TeamId
|
|
||||||
AND pc.DeleteAt = 0
|
|
||||||
ORDER BY pc.DisplayName
|
|
||||||
LIMIT :Limit
|
|
||||||
OFFSET :Offset
|
|
||||||
`, map[string]interface{}{
|
|
||||||
"TeamId": teamId,
|
|
||||||
"Limit": limit,
|
|
||||||
"Offset": offset,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Data = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.GetPublicChannelsByIdsForTeam(teamId, channelIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
props := make(map[string]interface{})
|
|
||||||
props["teamId"] = teamId
|
|
||||||
|
|
||||||
idQuery := ""
|
|
||||||
|
|
||||||
for index, channelId := range channelIds {
|
|
||||||
if len(idQuery) > 0 {
|
|
||||||
idQuery += ", "
|
|
||||||
}
|
|
||||||
|
|
||||||
props["channelId"+strconv.Itoa(index)] = channelId
|
|
||||||
idQuery += ":channelId" + strconv.Itoa(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := &model.ChannelList{}
|
|
||||||
_, err := s.GetReplica().Select(data, `
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels pc ON (pc.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
pc.TeamId = :teamId
|
|
||||||
AND pc.DeleteAt = 0
|
|
||||||
AND pc.Id IN (`+idQuery+`)
|
|
||||||
ORDER BY pc.DisplayName
|
|
||||||
`, props)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(*data) == 0 {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.not_found.app_error", nil, "", http.StatusNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Data = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.AutocompleteInTeam(teamId, term, includeDeleted)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
deleteFilter := "AND c.DeleteAt = 0"
|
|
||||||
if includeDeleted {
|
|
||||||
deleteFilter = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
queryFormat := `
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels c ON (c.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
` + deleteFilter + `
|
|
||||||
%v
|
|
||||||
LIMIT 50
|
|
||||||
`
|
|
||||||
|
|
||||||
var channels model.ChannelList
|
|
||||||
|
|
||||||
if likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose"); likeClause == "" {
|
|
||||||
if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Using a UNION results in index_merge and fulltext queries and is much faster than the ref
|
|
||||||
// query you would get using an OR of the LIKE and full-text clauses.
|
|
||||||
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
|
||||||
likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause)
|
|
||||||
fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause)
|
|
||||||
query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery)
|
|
||||||
|
|
||||||
if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(channels, func(a, b int) bool {
|
|
||||||
return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName)
|
|
||||||
})
|
|
||||||
result.Data = &channels
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.SearchInTeam(teamId, term, includeDeleted)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
deleteFilter := "AND c.DeleteAt = 0"
|
|
||||||
if includeDeleted {
|
|
||||||
deleteFilter = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = s.performSearch(`
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels c ON (c.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
`+deleteFilter+`
|
|
||||||
SEARCH_CLAUSE
|
|
||||||
ORDER BY c.DisplayName
|
|
||||||
LIMIT 100
|
|
||||||
`, term, map[string]interface{}{
|
|
||||||
"TeamId": teamId,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) SearchMore(userId string, teamId string, term string) store.StoreChannel {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.SearchMore(userId, teamId, term)
|
|
||||||
}
|
|
||||||
|
|
||||||
return store.Do(func(result *store.StoreResult) {
|
|
||||||
*result = s.performSearch(`
|
|
||||||
SELECT
|
|
||||||
Channels.*
|
|
||||||
FROM
|
|
||||||
Channels
|
|
||||||
JOIN
|
|
||||||
PublicChannels c ON (c.Id = Channels.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
AND c.DeleteAt = 0
|
|
||||||
AND c.Id NOT IN (
|
|
||||||
SELECT
|
|
||||||
c.Id
|
|
||||||
FROM
|
|
||||||
PublicChannels c
|
|
||||||
JOIN
|
|
||||||
ChannelMembers cm ON (cm.ChannelId = c.Id)
|
|
||||||
WHERE
|
|
||||||
c.TeamId = :TeamId
|
|
||||||
AND cm.UserId = :UserId
|
|
||||||
AND c.DeleteAt = 0
|
|
||||||
)
|
|
||||||
SEARCH_CLAUSE
|
|
||||||
ORDER BY c.DisplayName
|
|
||||||
LIMIT 100
|
|
||||||
`, term, map[string]interface{}{
|
|
||||||
"TeamId": teamId,
|
|
||||||
"UserId": userId,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) buildLIKEClause(term string, searchColumns string) (likeClause, likeTerm string) {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.buildLIKEClause(term, searchColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
likeTerm = term
|
|
||||||
|
|
||||||
// These chars must be removed from the like query.
|
|
||||||
for _, c := range ignoreLikeSearchChar {
|
|
||||||
likeTerm = strings.Replace(likeTerm, c, "", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// These chars must be escaped in the like query.
|
|
||||||
for _, c := range escapeLikeSearchChar {
|
|
||||||
likeTerm = strings.Replace(likeTerm, c, "*"+c, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if likeTerm == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the LIKE portion of the query.
|
|
||||||
var searchFields []string
|
|
||||||
for _, field := range strings.Split(searchColumns, ", ") {
|
|
||||||
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
|
||||||
searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*'", field, ":LikeTerm"))
|
|
||||||
} else {
|
|
||||||
searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*'", field, ":LikeTerm"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
likeClause = fmt.Sprintf("(%s)", strings.Join(searchFields, " OR "))
|
|
||||||
likeTerm += "%"
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) buildFulltextClause(term string, searchColumns string) (fulltextClause, fulltextTerm string) {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.buildFulltextClause(term, searchColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the terms as we will need to prepare them differently for each search type.
|
|
||||||
fulltextTerm = term
|
|
||||||
|
|
||||||
// These chars must be treated as spaces in the fulltext query.
|
|
||||||
for _, c := range spaceFulltextSearchChar {
|
|
||||||
fulltextTerm = strings.Replace(fulltextTerm, c, " ", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare the FULLTEXT portion of the query.
|
|
||||||
if s.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
|
||||||
fulltextTerm = strings.Replace(fulltextTerm, "|", "", -1)
|
|
||||||
|
|
||||||
splitTerm := strings.Fields(fulltextTerm)
|
|
||||||
for i, t := range strings.Fields(fulltextTerm) {
|
|
||||||
if i == len(splitTerm)-1 {
|
|
||||||
splitTerm[i] = t + ":*"
|
|
||||||
} else {
|
|
||||||
splitTerm[i] = t + ":* &"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fulltextTerm = strings.Join(splitTerm, " ")
|
|
||||||
|
|
||||||
fulltextClause = fmt.Sprintf("((%s) @@ to_tsquery(:FulltextTerm))", convertMySQLFullTextColumnsToPostgres(searchColumns))
|
|
||||||
} else if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
|
||||||
splitTerm := strings.Fields(fulltextTerm)
|
|
||||||
for i, t := range strings.Fields(fulltextTerm) {
|
|
||||||
splitTerm[i] = "+" + t + "*"
|
|
||||||
}
|
|
||||||
|
|
||||||
fulltextTerm = strings.Join(splitTerm, " ")
|
|
||||||
|
|
||||||
fulltextClause = fmt.Sprintf("MATCH(%s) AGAINST (:FulltextTerm IN BOOLEAN MODE)", searchColumns)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) performSearch(searchQuery string, term string, parameters map[string]interface{}) store.StoreResult {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return s.SqlChannelStore.performSearch(searchQuery, term, parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := store.StoreResult{}
|
|
||||||
|
|
||||||
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
|
|
||||||
if likeTerm == "" {
|
|
||||||
// If the likeTerm is empty after preparing, then don't bother searching.
|
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
|
||||||
} else {
|
|
||||||
parameters["LikeTerm"] = likeTerm
|
|
||||||
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
|
||||||
parameters["FulltextTerm"] = fulltextTerm
|
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
var channels model.ChannelList
|
|
||||||
|
|
||||||
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
|
|
||||||
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Data = &channels
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) EnableExperimentalPublicChannelsMaterialization() {
|
|
||||||
if !s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
mlog.Info("Enabling experimental public channels materialization")
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StoreUint32(s.experimentalPublicChannelsMaterializationDisabled, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) DisableExperimentalPublicChannelsMaterialization() {
|
|
||||||
if s.IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
mlog.Info("Disabling experimental public channels materialization")
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.StoreUint32(s.experimentalPublicChannelsMaterializationDisabled, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SqlChannelStoreExperimental) IsExperimentalPublicChannelsMaterializationEnabled() bool {
|
|
||||||
return atomic.LoadUint32(s.experimentalPublicChannelsMaterializationDisabled) == 0
|
|
||||||
}
|
|
||||||
@@ -118,13 +118,8 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
|||||||
|
|
||||||
supplier.initConnection()
|
supplier.initConnection()
|
||||||
|
|
||||||
enableExperimentalPublicChannelsMaterialization := true
|
|
||||||
if settings.EnablePublicChannelsMaterialization != nil && !*settings.EnablePublicChannelsMaterialization {
|
|
||||||
enableExperimentalPublicChannelsMaterialization = false
|
|
||||||
}
|
|
||||||
|
|
||||||
supplier.oldStores.team = NewSqlTeamStore(supplier)
|
supplier.oldStores.team = NewSqlTeamStore(supplier)
|
||||||
supplier.oldStores.channel = NewSqlChannelStoreExperimental(supplier, metrics, enableExperimentalPublicChannelsMaterialization)
|
supplier.oldStores.channel = NewSqlChannelStore(supplier, metrics)
|
||||||
supplier.oldStores.post = NewSqlPostStore(supplier, metrics)
|
supplier.oldStores.post = NewSqlPostStore(supplier, metrics)
|
||||||
supplier.oldStores.user = NewSqlUserStore(supplier, metrics)
|
supplier.oldStores.user = NewSqlUserStore(supplier, metrics)
|
||||||
supplier.oldStores.audit = NewSqlAuditStore(supplier)
|
supplier.oldStores.audit = NewSqlAuditStore(supplier)
|
||||||
@@ -162,7 +157,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
|
|||||||
UpgradeDatabase(supplier)
|
UpgradeDatabase(supplier)
|
||||||
|
|
||||||
supplier.oldStores.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
supplier.oldStores.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
||||||
supplier.oldStores.channel.(*SqlChannelStoreExperimental).CreateIndexesIfNotExists()
|
supplier.oldStores.channel.(*SqlChannelStore).CreateIndexesIfNotExists()
|
||||||
supplier.oldStores.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
supplier.oldStores.post.(*SqlPostStore).CreateIndexesIfNotExists()
|
||||||
supplier.oldStores.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
supplier.oldStores.user.(*SqlUserStore).CreateIndexesIfNotExists()
|
||||||
supplier.oldStores.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
supplier.oldStores.audit.(*SqlAuditStore).CreateIndexesIfNotExists()
|
||||||
|
|||||||
@@ -179,10 +179,6 @@ type ChannelStore interface {
|
|||||||
ClearAllCustomRoleAssignments() StoreChannel
|
ClearAllCustomRoleAssignments() StoreChannel
|
||||||
ResetLastPostAt() StoreChannel
|
ResetLastPostAt() StoreChannel
|
||||||
MigratePublicChannels() error
|
MigratePublicChannels() error
|
||||||
DropPublicChannels() error
|
|
||||||
EnableExperimentalPublicChannelsMaterialization()
|
|
||||||
DisableExperimentalPublicChannelsMaterialization()
|
|
||||||
IsExperimentalPublicChannelsMaterializationEnabled() bool
|
|
||||||
GetAllChannelsForExportAfter(limit int, afterId string) StoreChannel
|
GetAllChannelsForExportAfter(limit int, afterId string) StoreChannel
|
||||||
GetChannelMembersForExport(userId string, teamId string) StoreChannel
|
GetChannelMembersForExport(userId string, teamId string) StoreChannel
|
||||||
RemoveAllDeactivatedMembers(channelId string) StoreChannel
|
RemoveAllDeactivatedMembers(channelId string) StoreChannel
|
||||||
|
|||||||
@@ -24,65 +24,48 @@ type SqlSupplier interface {
|
|||||||
func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
func TestChannelStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||||
createDefaultRoles(t, ss)
|
createDefaultRoles(t, ss)
|
||||||
|
|
||||||
for _, enabled := range []bool{true, false} {
|
t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) })
|
||||||
description := "experimental materialization"
|
t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) })
|
||||||
if enabled {
|
t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) })
|
||||||
description += " enabled"
|
t.Run("Update", func(t *testing.T) { testChannelStoreUpdate(t, ss) })
|
||||||
ss.Channel().EnableExperimentalPublicChannelsMaterialization()
|
t.Run("GetChannelUnread", func(t *testing.T) { testGetChannelUnread(t, ss) })
|
||||||
} else {
|
t.Run("Get", func(t *testing.T) { testChannelStoreGet(t, ss) })
|
||||||
description += " disabled"
|
t.Run("GetForPost", func(t *testing.T) { testChannelStoreGetForPost(t, ss) })
|
||||||
ss.Channel().DisableExperimentalPublicChannelsMaterialization()
|
t.Run("Restore", func(t *testing.T) { testChannelStoreRestore(t, ss) })
|
||||||
|
t.Run("Delete", func(t *testing.T) { testChannelStoreDelete(t, ss) })
|
||||||
// Additionally drop the public channels table and all associated triggers
|
t.Run("GetByName", func(t *testing.T) { testChannelStoreGetByName(t, ss) })
|
||||||
// to prove that the experimental store is fully disabled.
|
t.Run("GetByNames", func(t *testing.T) { testChannelStoreGetByNames(t, ss) })
|
||||||
ss.Channel().DropPublicChannels()
|
t.Run("GetDeletedByName", func(t *testing.T) { testChannelStoreGetDeletedByName(t, ss) })
|
||||||
}
|
t.Run("GetDeleted", func(t *testing.T) { testChannelStoreGetDeleted(t, ss) })
|
||||||
|
t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) })
|
||||||
t.Run(description, func(t *testing.T) {
|
t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) })
|
||||||
t.Run("Save", func(t *testing.T) { testChannelStoreSave(t, ss) })
|
t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) })
|
||||||
t.Run("SaveDirectChannel", func(t *testing.T) { testChannelStoreSaveDirectChannel(t, ss) })
|
t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) })
|
||||||
t.Run("CreateDirectChannel", func(t *testing.T) { testChannelStoreCreateDirectChannel(t, ss) })
|
t.Run("GetPublicChannelsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsForTeam(t, ss) })
|
||||||
t.Run("Update", func(t *testing.T) { testChannelStoreUpdate(t, ss) })
|
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
|
||||||
t.Run("GetChannelUnread", func(t *testing.T) { testGetChannelUnread(t, ss) })
|
t.Run("GetChannelCounts", func(t *testing.T) { testChannelStoreGetChannelCounts(t, ss) })
|
||||||
t.Run("Get", func(t *testing.T) { testChannelStoreGet(t, ss) })
|
t.Run("GetMembersForUser", func(t *testing.T) { testChannelStoreGetMembersForUser(t, ss) })
|
||||||
t.Run("GetForPost", func(t *testing.T) { testChannelStoreGetForPost(t, ss) })
|
t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) })
|
||||||
t.Run("Restore", func(t *testing.T) { testChannelStoreRestore(t, ss) })
|
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) })
|
||||||
t.Run("Delete", func(t *testing.T) { testChannelStoreDelete(t, ss) })
|
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) })
|
||||||
t.Run("GetByName", func(t *testing.T) { testChannelStoreGetByName(t, ss) })
|
t.Run("GetMember", func(t *testing.T) { testGetMember(t, ss) })
|
||||||
t.Run("GetByNames", func(t *testing.T) { testChannelStoreGetByNames(t, ss) })
|
t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, ss) })
|
||||||
t.Run("GetDeletedByName", func(t *testing.T) { testChannelStoreGetDeletedByName(t, ss) })
|
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) })
|
||||||
t.Run("GetDeleted", func(t *testing.T) { testChannelStoreGetDeleted(t, ss) })
|
t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) })
|
||||||
t.Run("ChannelMemberStore", func(t *testing.T) { testChannelMemberStore(t, ss) })
|
t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) })
|
||||||
t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) })
|
t.Run("AutocompleteInTeamForSearch", func(t *testing.T) { testChannelStoreAutocompleteInTeamForSearch(t, ss) })
|
||||||
t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) })
|
t.Run("GetMembersByIds", func(t *testing.T) { testChannelStoreGetMembersByIds(t, ss) })
|
||||||
t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) })
|
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
|
||||||
t.Run("GetPublicChannelsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsForTeam(t, ss) })
|
t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) })
|
||||||
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
|
t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) })
|
||||||
t.Run("GetChannelCounts", func(t *testing.T) { testChannelStoreGetChannelCounts(t, ss) })
|
t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) })
|
||||||
t.Run("GetMembersForUser", func(t *testing.T) { testChannelStoreGetMembersForUser(t, ss) })
|
t.Run("MigrateChannelMembers", func(t *testing.T) { testChannelStoreMigrateChannelMembers(t, ss) })
|
||||||
t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) })
|
t.Run("ResetAllChannelSchemes", func(t *testing.T) { testResetAllChannelSchemes(t, ss) })
|
||||||
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) })
|
t.Run("ClearAllCustomRoleAssignments", func(t *testing.T) { testChannelStoreClearAllCustomRoleAssignments(t, ss) })
|
||||||
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) })
|
t.Run("MaterializedPublicChannels", func(t *testing.T) { testMaterializedPublicChannels(t, ss, s) })
|
||||||
t.Run("GetMember", func(t *testing.T) { testGetMember(t, ss) })
|
t.Run("GetAllChannelsForExportAfter", func(t *testing.T) { testChannelStoreGetAllChannelsForExportAfter(t, ss) })
|
||||||
t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, ss) })
|
t.Run("GetChannelMembersForExport", func(t *testing.T) { testChannelStoreGetChannelMembersForExport(t, ss) })
|
||||||
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, ss) })
|
t.Run("RemoveAllDeactivatedMembers", func(t *testing.T) { testChannelStoreRemoveAllDeactivatedMembers(t, ss) })
|
||||||
t.Run("SearchMore", func(t *testing.T) { testChannelStoreSearchMore(t, ss) })
|
|
||||||
t.Run("SearchInTeam", func(t *testing.T) { testChannelStoreSearchInTeam(t, ss) })
|
|
||||||
t.Run("AutocompleteInTeamForSearch", func(t *testing.T) { testChannelStoreAutocompleteInTeamForSearch(t, ss) })
|
|
||||||
t.Run("GetMembersByIds", func(t *testing.T) { testChannelStoreGetMembersByIds(t, ss) })
|
|
||||||
t.Run("AnalyticsDeletedTypeCount", func(t *testing.T) { testChannelStoreAnalyticsDeletedTypeCount(t, ss) })
|
|
||||||
t.Run("GetPinnedPosts", func(t *testing.T) { testChannelStoreGetPinnedPosts(t, ss) })
|
|
||||||
t.Run("MaxChannelsPerTeam", func(t *testing.T) { testChannelStoreMaxChannelsPerTeam(t, ss) })
|
|
||||||
t.Run("GetChannelsByScheme", func(t *testing.T) { testChannelStoreGetChannelsByScheme(t, ss) })
|
|
||||||
t.Run("MigrateChannelMembers", func(t *testing.T) { testChannelStoreMigrateChannelMembers(t, ss) })
|
|
||||||
t.Run("ResetAllChannelSchemes", func(t *testing.T) { testResetAllChannelSchemes(t, ss) })
|
|
||||||
t.Run("ClearAllCustomRoleAssignments", func(t *testing.T) { testChannelStoreClearAllCustomRoleAssignments(t, ss) })
|
|
||||||
t.Run("MaterializedPublicChannels", func(t *testing.T) { testMaterializedPublicChannels(t, ss, s) })
|
|
||||||
t.Run("GetAllChannelsForExportAfter", func(t *testing.T) { testChannelStoreGetAllChannelsForExportAfter(t, ss) })
|
|
||||||
t.Run("GetChannelMembersForExport", func(t *testing.T) { testChannelStoreGetChannelMembersForExport(t, ss) })
|
|
||||||
t.Run("RemoveAllDeactivatedMembers", func(t *testing.T) { testChannelStoreRemoveAllDeactivatedMembers(t, ss) })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testChannelStoreSave(t *testing.T, ss store.Store) {
|
func testChannelStoreSave(t *testing.T, ss store.Store) {
|
||||||
@@ -2574,10 +2557,6 @@ func testChannelStoreClearAllCustomRoleAssignments(t *testing.T, ss store.Store)
|
|||||||
// testMaterializedPublicChannels tests edge cases involving the triggers and stored procedures
|
// testMaterializedPublicChannels tests edge cases involving the triggers and stored procedures
|
||||||
// that materialize the PublicChannels table.
|
// that materialize the PublicChannels table.
|
||||||
func testMaterializedPublicChannels(t *testing.T, ss store.Store, s SqlSupplier) {
|
func testMaterializedPublicChannels(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||||
if !ss.Channel().IsExperimentalPublicChannelsMaterializationEnabled() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
teamId := model.NewId()
|
teamId := model.NewId()
|
||||||
|
|
||||||
// o1 is a public channel on the team
|
// o1 is a public channel on the team
|
||||||
|
|||||||
@@ -130,30 +130,6 @@ func (_m *ChannelStore) Delete(channelId string, time int64) store.StoreChannel
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableExperimentalPublicChannelsMaterialization provides a mock function with given fields:
|
|
||||||
func (_m *ChannelStore) DisableExperimentalPublicChannelsMaterialization() {
|
|
||||||
_m.Called()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropPublicChannels provides a mock function with given fields:
|
|
||||||
func (_m *ChannelStore) DropPublicChannels() error {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 error
|
|
||||||
if rf, ok := ret.Get(0).(func() error); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableExperimentalPublicChannelsMaterialization provides a mock function with given fields:
|
|
||||||
func (_m *ChannelStore) EnableExperimentalPublicChannelsMaterialization() {
|
|
||||||
_m.Called()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get provides a mock function with given fields: id, allowFromCache
|
// Get provides a mock function with given fields: id, allowFromCache
|
||||||
func (_m *ChannelStore) Get(id string, allowFromCache bool) store.StoreChannel {
|
func (_m *ChannelStore) Get(id string, allowFromCache bool) store.StoreChannel {
|
||||||
ret := _m.Called(id, allowFromCache)
|
ret := _m.Called(id, allowFromCache)
|
||||||
@@ -673,20 +649,6 @@ func (_m *ChannelStore) InvalidateMemberCount(channelId string) {
|
|||||||
_m.Called(channelId)
|
_m.Called(channelId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsExperimentalPublicChannelsMaterializationEnabled provides a mock function with given fields:
|
|
||||||
func (_m *ChannelStore) IsExperimentalPublicChannelsMaterializationEnabled() bool {
|
|
||||||
ret := _m.Called()
|
|
||||||
|
|
||||||
var r0 bool
|
|
||||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
|
||||||
r0 = rf()
|
|
||||||
} else {
|
|
||||||
r0 = ret.Get(0).(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserInChannelUseCache provides a mock function with given fields: userId, channelId
|
// IsUserInChannelUseCache provides a mock function with given fields: userId, channelId
|
||||||
func (_m *ChannelStore) IsUserInChannelUseCache(userId string, channelId string) bool {
|
func (_m *ChannelStore) IsUserInChannelUseCache(userId string, channelId string) bool {
|
||||||
ret := _m.Called(userId, channelId)
|
ret := _m.Called(userId, channelId)
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user