mattermost-community-enterp.../channels/store/sqlstore/property_value_store.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

385 lines
10 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"fmt"
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
type SqlPropertyValueStore struct {
*SqlStore
tableSelectQuery sq.SelectBuilder
}
var propertyValueColumns = []string{"ID", "TargetID", "TargetType", "GroupID", "FieldID", "Value", "CreateAt", "UpdateAt", "DeleteAt"}
func newPropertyValueStore(sqlStore *SqlStore) store.PropertyValueStore {
s := SqlPropertyValueStore{SqlStore: sqlStore}
s.tableSelectQuery = s.getQueryBuilder().
Select(propertyValueColumns...).
From("PropertyValues")
return &s
}
func (s *SqlPropertyValueStore) Create(value *model.PropertyValue) (*model.PropertyValue, error) {
if value.ID != "" {
return nil, store.NewErrInvalidInput("PropertyValue", "id", value.ID)
}
value.PreSave()
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_create_isvalid")
}
valueJSON := value.Value
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
builder := s.getQueryBuilder().
Insert("PropertyValues").
Columns(propertyValueColumns...).
Values(value.ID, value.TargetID, value.TargetType, value.GroupID, value.FieldID, valueJSON, value.CreateAt, value.UpdateAt, value.DeleteAt)
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_value_create_insert")
}
return value, nil
}
func (s *SqlPropertyValueStore) CreateMany(values []*model.PropertyValue) ([]*model.PropertyValue, error) {
if len(values) == 0 {
return nil, nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "property_value_create_many_begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
for _, value := range values {
value.PreSave()
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_create_many_isvalid")
}
valueJSON := value.Value
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
builder := s.getQueryBuilder().
Insert("PropertyValues").
Columns(propertyValueColumns...).
Values(value.ID, value.TargetID, value.TargetType, value.GroupID, value.FieldID, valueJSON, value.CreateAt, value.UpdateAt, value.DeleteAt)
if _, err := transaction.ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_value_create_many_exec")
}
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_value_create_many_commit_transaction")
}
return values, nil
}
func (s *SqlPropertyValueStore) Get(groupID, id string) (*model.PropertyValue, error) {
builder := s.tableSelectQuery.Where(sq.Eq{"id": id})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
var value model.PropertyValue
if err := s.GetReplica().GetBuilder(&value, builder); err != nil {
return nil, errors.Wrap(err, "property_value_get_select")
}
return &value, nil
}
func (s *SqlPropertyValueStore) GetMany(groupID string, ids []string) ([]*model.PropertyValue, error) {
builder := s.tableSelectQuery.Where(sq.Eq{"id": ids})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
var values []*model.PropertyValue
if err := s.GetReplica().SelectBuilder(&values, builder); err != nil {
return nil, errors.Wrap(err, "property_value_get_many_query")
}
if len(values) < len(ids) {
return nil, fmt.Errorf("missmatch results: got %d results of the %d ids passed", len(values), len(ids))
}
return values, nil
}
func (s *SqlPropertyValueStore) SearchPropertyValues(opts model.PropertyValueSearchOpts) ([]*model.PropertyValue, error) {
if err := opts.Cursor.IsValid(); err != nil {
return nil, fmt.Errorf("cursor is invalid: %w", err)
}
if opts.PerPage < 1 {
return nil, errors.New("per page must be positive integer greater than zero")
}
builder := s.tableSelectQuery.
OrderBy("CreateAt ASC, Id ASC").
Limit(uint64(opts.PerPage))
if !opts.Cursor.IsEmpty() {
builder = builder.Where(sq.Or{
sq.Gt{"CreateAt": opts.Cursor.CreateAt},
sq.And{
sq.Eq{"CreateAt": opts.Cursor.CreateAt},
sq.Gt{"Id": opts.Cursor.PropertyValueID},
},
})
}
if !opts.IncludeDeleted {
builder = builder.Where(sq.Eq{"DeleteAt": 0})
}
if opts.GroupID != "" {
builder = builder.Where(sq.Eq{"GroupID": opts.GroupID})
}
if opts.TargetType != "" {
builder = builder.Where(sq.Eq{"TargetType": opts.TargetType})
}
if len(opts.TargetIDs) > 0 {
builder = builder.Where(sq.Eq{"TargetID": opts.TargetIDs})
}
if opts.FieldID != "" {
builder = builder.Where(sq.Eq{"FieldID": opts.FieldID})
}
if opts.SinceUpdateAt > 0 {
builder = builder.Where(sq.Gt{"UpdateAt": opts.SinceUpdateAt})
}
if opts.Value != nil {
builder = builder.Where(sq.Eq{"Value": string(opts.Value)})
}
var values []*model.PropertyValue
if err := s.GetReplica().SelectBuilder(&values, builder); err != nil {
return nil, errors.Wrap(err, "property_value_search_query")
}
return values, nil
}
func (s *SqlPropertyValueStore) Update(groupID string, values []*model.PropertyValue) (_ []*model.PropertyValue, err error) {
if len(values) == 0 {
return nil, nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
updateTime := model.GetMillis()
isPostgres := s.DriverName() == model.DatabaseDriverPostgres
valueCase := sq.Case("id")
deleteAtCase := sq.Case("id")
ids := make([]string, len(values))
for i, value := range values {
value.UpdateAt = updateTime
if vErr := value.IsValid(); vErr != nil {
return nil, errors.Wrap(vErr, "property_value_update_isvalid")
}
ids[i] = value.ID
valueJSON := value.Value
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
if isPostgres {
valueCase = valueCase.When(sq.Expr("?", value.ID), sq.Expr("?::jsonb", valueJSON))
deleteAtCase = deleteAtCase.When(sq.Expr("?", value.ID), sq.Expr("?::bigint", value.DeleteAt))
} else {
valueCase = valueCase.When(sq.Expr("?", value.ID), sq.Expr("?", valueJSON))
deleteAtCase = deleteAtCase.When(sq.Expr("?", value.ID), sq.Expr("?", value.DeleteAt))
}
}
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("Value", valueCase).
Set("DeleteAt", deleteAtCase).
Set("UpdateAt", updateTime).
Where(sq.Eq{"id": ids})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
result, err := transaction.ExecBuilder(builder)
if err != nil {
return nil, errors.Wrap(err, "property_value_update_exec")
}
count, err := result.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_rowsaffected")
}
if count != int64(len(values)) {
return nil, errors.Errorf("failed to update, some property values were not found, got %d of %d", count, len(values))
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_value_update_commit_transaction")
}
return values, nil
}
func (s *SqlPropertyValueStore) Upsert(values []*model.PropertyValue) (_ []*model.PropertyValue, err error) {
if len(values) == 0 {
return nil, nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "property_value_upsert_begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
updatedValues := make([]*model.PropertyValue, len(values))
updateTime := model.GetMillis()
for i, value := range values {
value.PreSave()
value.UpdateAt = updateTime
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_upsert_isvalid")
}
valueJSON := value.Value
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
builder := s.getQueryBuilder().
Insert("PropertyValues").
Columns(propertyValueColumns...).
Values(value.ID, value.TargetID, value.TargetType, value.GroupID, value.FieldID, valueJSON, value.CreateAt, value.UpdateAt, value.DeleteAt)
builder = builder.SuffixExpr(sq.Expr(
"ON CONFLICT (GroupID, TargetID, FieldID) WHERE DeleteAt = 0 DO UPDATE SET Value = ?, UpdateAt = ?, DeleteAt = ? RETURNING *",
valueJSON,
value.UpdateAt,
0,
))
var values []*model.PropertyValue
if err := transaction.SelectBuilder(&values, builder); err != nil {
return nil, errors.Wrapf(err, "failed to upsert property value with id: %s", value.ID)
}
if len(values) != 1 {
return nil, errors.New("property_value_upsert_select_length")
}
updatedValues[i] = values[0]
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_value_upsert_commit")
}
return updatedValues, nil
}
func (s *SqlPropertyValueStore) Delete(groupID string, id string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"id": id})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
result, err := s.GetMaster().ExecBuilder(builder)
if err != nil {
return errors.Wrapf(err, "failed to delete property value with id: %s", id)
}
count, err := result.RowsAffected()
if err != nil {
return errors.Wrap(err, "property_value_delete_rowsaffected")
}
if count == 0 {
return store.NewErrNotFound("PropertyValue", id)
}
return nil
}
func (s *SqlPropertyValueStore) DeleteForField(groupID, fieldID string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"FieldID": fieldID})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return errors.Wrap(err, "property_value_delete_for_field_exec")
}
return nil
}
func (s *SqlPropertyValueStore) DeleteForTarget(groupID string, targetType string, targetID string) error {
if targetType == "" || targetID == "" {
return store.NewErrInvalidInput("PropertyValue", "target", "type or id empty")
}
builder := s.getQueryBuilder().
Delete("PropertyValues").
Where(sq.Eq{
"TargetType": targetType,
"TargetID": targetID,
})
if groupID != "" {
builder = builder.Where(sq.Eq{"GroupID": groupID})
}
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return errors.Wrap(err, "property_value_delete_for_target_exec")
}
return nil
}