Adds the main Property System Architecture components (#29644)

* Adds the main Property System Architecture components

This change adds the necessary migrations for the Property Groups,
Fields and Values tables to be created, the store layer and a Property
Service that can be used from the app layer.

* Update property field type to use user instead of person

* Update PropertyFields to allow for unique nondeleted fields and remove redundant indexes

* Update PropertyValues to allow for unique nondeleted fields and remove redundant indexes

* Use StringMap instead of the map[string]any on property fields

* Add i18n strings

* Revert "Use StringMap instead of the map[string]any on property fields"

This reverts commit e2735ab0f8589d2524d636419ca0cb144575c4d6.

* Cast JSON binary data to string and add todo note for StringMap use

* Add mocks to the retrylayer tests

* Cast JSON binary data to string in property value store

* Check for binary parameter instead of casting to string for JSON data

* Check property field type is one of the allowed ones

* Avoid reusing err variable to be explicit about the returned value

* Merge Property System Migrations into one file

* Adds NOT NULL to timestamps at the DB level

* Update stores to use tableSelectQuery instead of a slice var

* Update PropertyField model translations to be more explicit and avoid repetition

* Update PropertyValue model translations to be more explicit and avoid repetition

* Use ExecBuilder instead of ToSql&Exec

* Update property field errors to add context

* Ensure PerPage is greater than zero

* Update store errors to give more context

* Use ExecBuilder in the property stores where possible

* Add an on conflict suffix to the group register to avoid race conditions

* Remove badly used translation string

* Remove unused get in register group method

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Miguel de la Cruz
2025-01-13 12:41:44 +01:00
коммит произвёл GitHub
родитель d2b334e605
Коммит ecdce71fc4
35 изменённых файлов: 3793 добавлений и 0 удалений

Просмотреть файл

@@ -0,0 +1,321 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"database/sql"
"encoding/json"
"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"
)
func (s *SqlPropertyFieldStore) propertyFieldToInsertMap(field *model.PropertyField) (map[string]any, error) {
attrsJSON, err := json.Marshal(field.Attrs)
if err != nil {
return nil, errors.Wrap(err, "property_field_to_insert_map_marshal_attrs")
}
if s.IsBinaryParamEnabled() {
attrsJSON = AppendBinaryFlag(attrsJSON)
}
return map[string]any{
"ID": field.ID,
"GroupID": field.GroupID,
"Name": field.Name,
"Type": field.Type,
"Attrs": attrsJSON,
"TargetID": field.TargetID,
"TargetType": field.TargetType,
"CreateAt": field.CreateAt,
"UpdateAt": field.UpdateAt,
"DeleteAt": field.DeleteAt,
}, nil
}
func (s *SqlPropertyFieldStore) propertyFieldToUpdateMap(field *model.PropertyField) (map[string]any, error) {
attrsJSON, err := json.Marshal(field.Attrs)
if err != nil {
return nil, errors.Wrap(err, "property_field_to_update_map_marshal_attrs")
}
if s.IsBinaryParamEnabled() {
attrsJSON = AppendBinaryFlag(attrsJSON)
}
return map[string]any{
"Name": field.Name,
"Type": field.Type,
"Attrs": attrsJSON,
"TargetID": field.TargetID,
"TargetType": field.TargetType,
"UpdateAt": field.UpdateAt,
"DeleteAt": field.DeleteAt,
}, nil
}
func propertyFieldsFromRows(rows *sql.Rows) ([]*model.PropertyField, error) {
results := []*model.PropertyField{}
for rows.Next() {
var field model.PropertyField
var attrsJSON string
err := rows.Scan(
&field.ID,
&field.GroupID,
&field.Name,
&field.Type,
&attrsJSON,
&field.TargetID,
&field.TargetType,
&field.CreateAt,
&field.UpdateAt,
&field.DeleteAt,
)
if err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(attrsJSON), &field.Attrs); err != nil {
return nil, errors.Wrap(err, "property_fields_from_rows_unmarshal_attrs")
}
results = append(results, &field)
}
return results, nil
}
func propertyFieldFromRows(rows *sql.Rows) (*model.PropertyField, error) {
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, err
}
if len(fields) > 0 {
return fields[0], nil
}
return nil, sql.ErrNoRows
}
type SqlPropertyFieldStore struct {
*SqlStore
tableSelectQuery sq.SelectBuilder
}
func newPropertyFieldStore(sqlStore *SqlStore) store.PropertyFieldStore {
s := SqlPropertyFieldStore{SqlStore: sqlStore}
s.tableSelectQuery = s.getQueryBuilder().
Select("ID", "GroupID", "Name", "Type", "Attrs", "TargetID", "TargetType", "CreateAt", "UpdateAt", "DeleteAt").
From("PropertyFields")
return &s
}
func (s *SqlPropertyFieldStore) Create(field *model.PropertyField) (*model.PropertyField, error) {
if field.ID != "" {
return nil, store.NewErrInvalidInput("PropertyField", "id", field.ID)
}
field.PreSave()
if err := field.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_field_create_isvalid")
}
insertMap, err := s.propertyFieldToInsertMap(field)
if err != nil {
return nil, err
}
builder := s.getQueryBuilder().
Insert("PropertyFields").
SetMap(insertMap)
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_field_create_insert")
}
return field, nil
}
func (s *SqlPropertyFieldStore) Get(id string) (*model.PropertyField, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": id}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_get_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_select")
}
defer rows.Close()
field, err := propertyFieldFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_propertyfieldfromrows")
}
return field, nil
}
func (s *SqlPropertyFieldStore) GetMany(ids []string) ([]*model.PropertyField, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": ids}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_query")
}
defer rows.Close()
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_get_many_propertyfieldfromrows")
}
if len(fields) < len(ids) {
return nil, fmt.Errorf("missmatch results: got %d results of the %d ids passed", len(fields), len(ids))
}
return fields, nil
}
func (s *SqlPropertyFieldStore) SearchPropertyFields(opts model.PropertyFieldSearchOpts) ([]*model.PropertyField, error) {
if opts.Page < 0 {
return nil, errors.New("page must be positive integer")
}
if opts.PerPage < 1 {
return nil, errors.New("per page must be positive integer greater than zero")
}
query := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
Limit(uint64(opts.PerPage))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"DeleteAt": 0})
}
if opts.GroupID != "" {
query = query.Where(sq.Eq{"GroupID": opts.GroupID})
}
if opts.TargetType != "" {
query = query.Where(sq.Eq{"TargetType": opts.TargetType})
}
if opts.TargetID != "" {
query = query.Where(sq.Eq{"TargetID": opts.TargetID})
}
queryString, args, err := query.ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_search_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_field_search_query")
}
defer rows.Close()
fields, err := propertyFieldsFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_field_search_propertyfieldfromrows")
}
return fields, nil
}
func (s *SqlPropertyFieldStore) Update(fields []*model.PropertyField) (_ []*model.PropertyField, err error) {
if len(fields) == 0 {
return nil, nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "property_field_update_begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
updateTime := model.GetMillis()
for _, field := range fields {
field.UpdateAt = updateTime
if vErr := field.IsValid(); vErr != nil {
return nil, errors.Wrap(vErr, "property_field_update_isvalid")
}
updateMap, err := s.propertyFieldToUpdateMap(field)
if err != nil {
return nil, err
}
queryString, args, err := s.getQueryBuilder().
Update("PropertyFields").
SetMap(updateMap).
Where(sq.Eq{"id": field.ID}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_field_update_tosql")
}
result, err := transaction.Exec(queryString, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to update property field with id: %s", field.ID)
}
count, err := result.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_field_update_rowsaffected")
}
if count == 0 {
return nil, store.NewErrNotFound("PropertyField", field.ID)
}
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_field_update_commit")
}
return fields, nil
}
func (s *SqlPropertyFieldStore) Delete(id string) error {
builder := s.getQueryBuilder().
Update("PropertyFields").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"id": id})
result, err := s.GetMaster().ExecBuilder(builder)
if err != nil {
return errors.Wrapf(err, "failed to delete property field with id: %s", id)
}
count, err := result.RowsAffected()
if err != nil {
return errors.Wrap(err, "property_field_delete_rowsaffected")
}
if count == 0 {
return store.NewErrNotFound("PropertyField", id)
}
return nil
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyFieldStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyFieldStore)
}

Просмотреть файл

@@ -0,0 +1,78 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/store"
)
var propertyGroupColumns = []string{"ID", "Name"}
type SqlPropertyGroupStore struct {
*SqlStore
}
func newPropertyGroupStore(sqlStore *SqlStore) store.PropertyGroupStore {
return &SqlPropertyGroupStore{sqlStore}
}
func (s *SqlPropertyGroupStore) Register(name string) (*model.PropertyGroup, error) {
if name == "" {
return nil, store.NewErrInvalidInput("PropertyGroup", "name", name)
}
group := &model.PropertyGroup{Name: name}
group.PreSave()
builder := s.getQueryBuilder().
Insert("PropertyGroups").
Columns("ID", "Name").
Values(group.ID, group.Name)
if s.DriverName() == model.DatabaseDriverMysql {
builder = builder.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE Name=Name"))
} else {
builder = builder.SuffixExpr(sq.Expr("ON CONFLICT (Name) DO NOTHING"))
}
r, err := s.GetMaster().ExecBuilder(builder)
if err != nil {
return nil, errors.Wrap(err, "property_group_register_insert")
}
rowsAffected, err := r.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_group_register_rows_affected")
}
// there was a conflict during the insert, so we need to fetch the
// group to get its data
if rowsAffected == 0 {
return s.Get(name)
}
return group, nil
}
func (s *SqlPropertyGroupStore) Get(name string) (*model.PropertyGroup, error) {
queryString, args, err := s.getQueryBuilder().
Select(propertyGroupColumns...).
From("PropertyGroups").
Where(sq.Eq{"Name": name}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_group_get_tosql")
}
var propertyGroup model.PropertyGroup
if err := s.GetReplica().Get(&propertyGroup, queryString, args...); err != nil {
return nil, store.NewErrNotFound("PropertyGroup", name)
}
return &propertyGroup, nil
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyGroupStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyGroupStore)
}

Просмотреть файл

@@ -0,0 +1,332 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"database/sql"
"encoding/json"
"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"
)
func (s *SqlPropertyValueStore) propertyValueToInsertMap(value *model.PropertyValue) (map[string]any, error) {
valueJSON, err := json.Marshal(value.Value)
if err != nil {
return nil, errors.Wrap(err, "property_value_to_insert_map_marshal_value")
}
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
return map[string]any{
"ID": value.ID,
"TargetID": value.TargetID,
"TargetType": value.TargetType,
"GroupID": value.GroupID,
"FieldID": value.FieldID,
"Value": valueJSON,
"CreateAt": value.CreateAt,
"UpdateAt": value.UpdateAt,
"DeleteAt": value.DeleteAt,
}, nil
}
func (s *SqlPropertyValueStore) propertyValueToUpdateMap(value *model.PropertyValue) (map[string]any, error) {
valueJSON, err := json.Marshal(value.Value)
if err != nil {
return nil, errors.Wrap(err, "property_value_to_udpate_map_marshal_value")
}
if s.IsBinaryParamEnabled() {
valueJSON = AppendBinaryFlag(valueJSON)
}
return map[string]any{
"Value": valueJSON,
"UpdateAt": value.UpdateAt,
"DeleteAt": value.DeleteAt,
}, nil
}
func propertyValuesFromRows(rows *sql.Rows) ([]*model.PropertyValue, error) {
results := []*model.PropertyValue{}
for rows.Next() {
var value model.PropertyValue
var valueJSON string
err := rows.Scan(
&value.ID,
&value.TargetID,
&value.TargetType,
&value.GroupID,
&value.FieldID,
&valueJSON,
&value.CreateAt,
&value.UpdateAt,
&value.DeleteAt,
)
if err != nil {
return nil, err
}
if err := json.Unmarshal([]byte(valueJSON), &value.Value); err != nil {
return nil, errors.Wrap(err, "property_values_from_rows_unmarshal_value")
}
results = append(results, &value)
}
return results, nil
}
func propertyValueFromRows(rows *sql.Rows) (*model.PropertyValue, error) {
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, err
}
if len(values) > 0 {
return values[0], nil
}
return nil, sql.ErrNoRows
}
type SqlPropertyValueStore struct {
*SqlStore
tableSelectQuery sq.SelectBuilder
}
func newPropertyValueStore(sqlStore *SqlStore) store.PropertyValueStore {
s := SqlPropertyValueStore{SqlStore: sqlStore}
s.tableSelectQuery = s.getQueryBuilder().
Select("ID", "TargetID", "TargetType", "GroupID", "FieldID", "Value", "CreateAt", "UpdateAt", "DeleteAt").
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")
}
insertMap, err := s.propertyValueToInsertMap(value)
if err != nil {
return nil, err
}
builder := s.getQueryBuilder().
Insert("PropertyValues").
SetMap(insertMap)
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return nil, errors.Wrap(err, "property_value_create_insert")
}
return value, nil
}
func (s *SqlPropertyValueStore) Get(id string) (*model.PropertyValue, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": id}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_get_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_select")
}
defer rows.Close()
value, err := propertyValueFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_propertyvaluefromrows")
}
return value, nil
}
func (s *SqlPropertyValueStore) GetMany(ids []string) ([]*model.PropertyValue, error) {
queryString, args, err := s.tableSelectQuery.
Where(sq.Eq{"id": ids}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_query")
}
defer rows.Close()
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_get_many_propertyvaluesfromrows")
}
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 opts.Page < 0 {
return nil, errors.New("page must be positive integer")
}
if opts.PerPage < 1 {
return nil, errors.New("per page must be positive integer greater than zero")
}
query := s.tableSelectQuery.
OrderBy("CreateAt ASC").
Offset(uint64(opts.Page * opts.PerPage)).
Limit(uint64(opts.PerPage))
if !opts.IncludeDeleted {
query = query.Where(sq.Eq{"DeleteAt": 0})
}
if opts.GroupID != "" {
query = query.Where(sq.Eq{"GroupID": opts.GroupID})
}
if opts.TargetType != "" {
query = query.Where(sq.Eq{"TargetType": opts.TargetType})
}
if opts.TargetID != "" {
query = query.Where(sq.Eq{"TargetID": opts.TargetID})
}
if opts.FieldID != "" {
query = query.Where(sq.Eq{"FieldID": opts.FieldID})
}
queryString, args, err := query.ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_search_tosql")
}
rows, err := s.GetReplica().Query(queryString, args...)
if err != nil {
return nil, errors.Wrap(err, "property_value_search_query")
}
defer rows.Close()
values, err := propertyValuesFromRows(rows)
if err != nil {
return nil, errors.Wrap(err, "property_value_search_propertyvaluesfromrows")
}
return values, nil
}
func (s *SqlPropertyValueStore) Update(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()
for _, value := range values {
value.UpdateAt = updateTime
if err := value.IsValid(); err != nil {
return nil, errors.Wrap(err, "property_value_update_isvalid")
}
updateMap, err := s.propertyValueToUpdateMap(value)
if err != nil {
return nil, err
}
queryString, args, err := s.getQueryBuilder().
Update("PropertyValues").
SetMap(updateMap).
Where(sq.Eq{"id": value.ID}).
ToSql()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_tosql")
}
result, err := transaction.Exec(queryString, args...)
if err != nil {
return nil, errors.Wrapf(err, "failed to update property value with id: %s", value.ID)
}
count, err := result.RowsAffected()
if err != nil {
return nil, errors.Wrap(err, "property_value_update_rowsaffected")
}
if count == 0 {
return nil, store.NewErrNotFound("PropertyValue", value.ID)
}
}
if err := transaction.Commit(); err != nil {
return nil, errors.Wrap(err, "property_value_update_commit")
}
return values, nil
}
func (s *SqlPropertyValueStore) Delete(id string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"id": id})
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(fieldID string) error {
builder := s.getQueryBuilder().
Update("PropertyValues").
Set("DeleteAt", model.GetMillis()).
Where(sq.Eq{"FieldID": fieldID})
if _, err := s.GetMaster().ExecBuilder(builder); err != nil {
return errors.Wrap(err, "property_value_delete_for_field_exec")
}
return nil
}

Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
)
func TestPropertyValueStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPropertyValueStore)
}

Просмотреть файл

@@ -115,6 +115,9 @@ type SqlStoreStores struct {
desktopTokens store.DesktopTokensStore
channelBookmarks store.ChannelBookmarkStore
scheduledPost store.ScheduledPostStore
propertyGroup store.PropertyGroupStore
propertyField store.PropertyFieldStore
propertyValue store.PropertyValueStore
}
type SqlStore struct {
@@ -257,6 +260,9 @@ func New(settings model.SqlSettings, logger mlog.LoggerIFace, metrics einterface
store.stores.desktopTokens = newSqlDesktopTokensStore(store, metrics)
store.stores.channelBookmarks = newSqlChannelBookmarkStore(store)
store.stores.scheduledPost = newScheduledPostStore(store)
store.stores.propertyGroup = newPropertyGroupStore(store)
store.stores.propertyField = newPropertyFieldStore(store)
store.stores.propertyValue = newPropertyValueStore(store)
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
@@ -1060,6 +1066,18 @@ func (ss *SqlStore) ChannelBookmark() store.ChannelBookmarkStore {
return ss.stores.channelBookmarks
}
func (ss *SqlStore) PropertyGroup() store.PropertyGroupStore {
return ss.stores.propertyGroup
}
func (ss *SqlStore) PropertyField() store.PropertyFieldStore {
return ss.stores.propertyField
}
func (ss *SqlStore) PropertyValue() store.PropertyValueStore {
return ss.stores.propertyValue
}
func (ss *SqlStore) DropAllTables() {
if ss.DriverName() == model.DatabaseDriverPostgres {
ss.masterX.Exec(`DO