2015-10-08 12:27:09 -04:00
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
2015-06-14 23:53:32 -08:00
// See License.txt for license information.
package store
import (
"crypto/aes"
"crypto/cipher"
2015-06-24 16:29:45 +02:00
"crypto/hmac"
2015-07-06 00:50:42 -08:00
crand "crypto/rand"
2015-06-24 16:29:45 +02:00
"crypto/sha256"
"crypto/sha512"
2015-06-14 23:53:32 -08:00
dbsql "database/sql"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
sqltrace "log"
"os"
2015-07-12 18:19:03 -08:00
"strings"
2016-10-27 13:50:24 -04:00
"sync/atomic"
2015-06-14 23:53:32 -08:00
"time"
2015-10-15 11:16:26 -07:00
2016-10-27 13:50:24 -04:00
l4g "github.com/alecthomas/log4go"
2015-10-15 11:16:26 -07:00
"github.com/go-gorp/gorp"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
2015-06-14 23:53:32 -08:00
)
2015-10-01 14:07:20 -04:00
const (
INDEX_TYPE_FULL_TEXT = "full_text"
INDEX_TYPE_DEFAULT = "default"
2016-10-19 14:49:25 -04:00
MAX_DB_CONN_LIFETIME = 15
2015-10-01 14:07:20 -04:00
)
2016-08-24 16:17:40 -08:00
const (
EXIT_CREATE_TABLE = 100
EXIT_DB_OPEN = 101
EXIT_PING = 102
EXIT_NO_DRIVER = 103
EXIT_TABLE_EXISTS = 104
EXIT_TABLE_EXISTS_MYSQL = 105
EXIT_COLUMN_EXISTS = 106
EXIT_DOES_COLUMN_EXISTS_POSTGRES = 107
EXIT_DOES_COLUMN_EXISTS_MYSQL = 108
EXIT_DOES_COLUMN_EXISTS_MISSING = 109
EXIT_CREATE_COLUMN_POSTGRES = 110
EXIT_CREATE_COLUMN_MYSQL = 111
EXIT_CREATE_COLUMN_MISSING = 112
EXIT_REMOVE_COLUMN = 113
EXIT_RENAME_COLUMN = 114
EXIT_MAX_COLUMN = 115
EXIT_ALTER_COLUMN = 116
EXIT_CREATE_INDEX_POSTGRES = 117
EXIT_CREATE_INDEX_MYSQL = 118
EXIT_CREATE_INDEX_FULL_MYSQL = 119
EXIT_CREATE_INDEX_MISSING = 120
EXIT_REMOVE_INDEX_POSTGRES = 121
EXIT_REMOVE_INDEX_MYSQL = 122
EXIT_REMOVE_INDEX_MISSING = 123
)
2015-06-14 23:53:32 -08:00
type SqlStore struct {
2016-04-21 22:37:01 -07:00
master * gorp . DbMap
replicas [ ] * gorp . DbMap
team TeamStore
channel ChannelStore
post PostStore
user UserStore
audit AuditStore
compliance ComplianceStore
session SessionStore
oauth OAuthStore
system SystemStore
webhook WebhookStore
command CommandStore
preference PreferenceStore
license LicenseStore
recovery PasswordRecoveryStore
2016-06-14 09:38:19 -04:00
emoji EmojiStore
2016-07-18 11:10:03 -04:00
status StatusStore
2016-09-30 11:06:30 -04:00
fileInfo FileInfoStore
2016-11-30 13:55:49 -05:00
reaction ReactionStore
2016-04-21 22:37:01 -07:00
SchemaVersion string
2016-10-27 13:50:24 -04:00
rrCounter int64
2016-04-21 22:37:01 -07:00
}
func initConnection ( ) * SqlStore {
2016-10-27 13:50:24 -04:00
sqlStore := & SqlStore {
rrCounter : 0 ,
}
2015-06-14 23:53:32 -08:00
sqlStore . master = setupConnection ( "master" , utils . Cfg . SqlSettings . DriverName ,
utils . Cfg . SqlSettings . DataSource , utils . Cfg . SqlSettings . MaxIdleConns ,
utils . Cfg . SqlSettings . MaxOpenConns , utils . Cfg . SqlSettings . Trace )
2015-09-22 00:00:19 -07:00
if len ( utils . Cfg . SqlSettings . DataSourceReplicas ) == 0 {
sqlStore . replicas = make ( [ ] * gorp . DbMap , 1 )
2016-10-19 14:49:25 -04:00
sqlStore . replicas [ 0 ] = sqlStore . master
2015-09-22 00:00:19 -07:00
} else {
sqlStore . replicas = make ( [ ] * gorp . DbMap , len ( utils . Cfg . SqlSettings . DataSourceReplicas ) )
for i , replica := range utils . Cfg . SqlSettings . DataSourceReplicas {
sqlStore . replicas [ i ] = setupConnection ( fmt . Sprintf ( "replica-%v" , i ) , utils . Cfg . SqlSettings . DriverName , replica ,
utils . Cfg . SqlSettings . MaxIdleConns , utils . Cfg . SqlSettings . MaxOpenConns ,
utils . Cfg . SqlSettings . Trace )
}
2015-06-14 23:53:32 -08:00
}
2016-04-21 22:37:01 -07:00
sqlStore . SchemaVersion = sqlStore . GetCurrentSchemaVersion ( )
return sqlStore
}
func NewSqlStore ( ) Store {
sqlStore := initConnection ( )
2015-09-16 19:59:57 -07:00
2015-06-14 23:53:32 -08:00
sqlStore . team = NewSqlTeamStore ( sqlStore )
sqlStore . channel = NewSqlChannelStore ( sqlStore )
sqlStore . post = NewSqlPostStore ( sqlStore )
sqlStore . user = NewSqlUserStore ( sqlStore )
sqlStore . audit = NewSqlAuditStore ( sqlStore )
2016-03-14 16:07:58 -07:00
sqlStore . compliance = NewSqlComplianceStore ( sqlStore )
2015-06-14 23:53:32 -08:00
sqlStore . session = NewSqlSessionStore ( sqlStore )
2015-09-16 15:49:12 -04:00
sqlStore . oauth = NewSqlOAuthStore ( sqlStore )
2015-09-16 19:59:57 -07:00
sqlStore . system = NewSqlSystemStore ( sqlStore )
2015-09-21 14:22:23 -04:00
sqlStore . webhook = NewSqlWebhookStore ( sqlStore )
2016-01-06 21:09:05 -06:00
sqlStore . command = NewSqlCommandStore ( sqlStore )
2015-10-01 10:56:07 -04:00
sqlStore . preference = NewSqlPreferenceStore ( sqlStore )
2016-02-04 13:00:03 -05:00
sqlStore . license = NewSqlLicenseStore ( sqlStore )
2016-04-21 22:37:01 -07:00
sqlStore . recovery = NewSqlPasswordRecoveryStore ( sqlStore )
2016-06-14 09:38:19 -04:00
sqlStore . emoji = NewSqlEmojiStore ( sqlStore )
2016-07-18 11:10:03 -04:00
sqlStore . status = NewSqlStatusStore ( sqlStore )
2016-09-30 11:06:30 -04:00
sqlStore . fileInfo = NewSqlFileInfoStore ( sqlStore )
2016-11-30 13:55:49 -05:00
sqlStore . reaction = NewSqlReactionStore ( sqlStore )
2015-06-14 23:53:32 -08:00
2015-10-30 17:44:00 +02:00
err := sqlStore . master . CreateTablesIfNotExists ( )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.creating_tables.critical" ) , err )
2016-04-21 22:37:01 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_TABLE )
2015-10-30 17:44:00 +02:00
}
2015-06-14 23:53:32 -08:00
2016-08-24 16:17:40 -08:00
UpgradeDatabase ( sqlStore )
2015-09-16 15:49:12 -04:00
sqlStore . team . ( * SqlTeamStore ) . CreateIndexesIfNotExists ( )
sqlStore . channel . ( * SqlChannelStore ) . CreateIndexesIfNotExists ( )
sqlStore . post . ( * SqlPostStore ) . CreateIndexesIfNotExists ( )
sqlStore . user . ( * SqlUserStore ) . CreateIndexesIfNotExists ( )
sqlStore . audit . ( * SqlAuditStore ) . CreateIndexesIfNotExists ( )
2016-03-14 16:07:58 -07:00
sqlStore . compliance . ( * SqlComplianceStore ) . CreateIndexesIfNotExists ( )
2015-09-16 15:49:12 -04:00
sqlStore . session . ( * SqlSessionStore ) . CreateIndexesIfNotExists ( )
sqlStore . oauth . ( * SqlOAuthStore ) . CreateIndexesIfNotExists ( )
2015-09-16 19:59:57 -07:00
sqlStore . system . ( * SqlSystemStore ) . CreateIndexesIfNotExists ( )
2015-09-21 14:22:23 -04:00
sqlStore . webhook . ( * SqlWebhookStore ) . CreateIndexesIfNotExists ( )
2016-01-06 21:09:05 -06:00
sqlStore . command . ( * SqlCommandStore ) . CreateIndexesIfNotExists ( )
2015-10-01 10:56:07 -04:00
sqlStore . preference . ( * SqlPreferenceStore ) . CreateIndexesIfNotExists ( )
2016-02-04 13:00:03 -05:00
sqlStore . license . ( * SqlLicenseStore ) . CreateIndexesIfNotExists ( )
2016-04-21 22:37:01 -07:00
sqlStore . recovery . ( * SqlPasswordRecoveryStore ) . CreateIndexesIfNotExists ( )
2016-06-14 09:38:19 -04:00
sqlStore . emoji . ( * SqlEmojiStore ) . CreateIndexesIfNotExists ( )
2016-07-18 11:10:03 -04:00
sqlStore . status . ( * SqlStatusStore ) . CreateIndexesIfNotExists ( )
2016-09-30 11:06:30 -04:00
sqlStore . fileInfo . ( * SqlFileInfoStore ) . CreateIndexesIfNotExists ( )
2016-11-30 13:55:49 -05:00
sqlStore . reaction . ( * SqlReactionStore ) . CreateIndexesIfNotExists ( )
2015-09-16 19:59:57 -07:00
2015-11-13 22:56:41 +01:00
sqlStore . preference . ( * SqlPreferenceStore ) . DeleteUnusedFeatures ( )
2016-04-21 22:37:01 -07:00
return sqlStore
}
2015-06-14 23:53:32 -08:00
func setupConnection ( con_type string , driver string , dataSource string , maxIdle int , maxOpen int , trace bool ) * gorp . DbMap {
2015-07-23 10:15:53 -08:00
db , err := dbsql . Open ( driver , dataSource )
2015-06-14 23:53:32 -08:00
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.open_conn.critical" ) , err )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_DB_OPEN )
2015-06-14 23:53:32 -08:00
}
2016-01-25 16:08:06 -03:00
l4g . Info ( utils . T ( "store.sql.pinging.info" ) , con_type )
2015-06-14 23:53:32 -08:00
err = db . Ping ( )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.ping.critical" ) , err )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_PING )
2015-06-14 23:53:32 -08:00
}
db . SetMaxIdleConns ( maxIdle )
db . SetMaxOpenConns ( maxOpen )
2016-10-19 14:49:25 -04:00
db . SetConnMaxLifetime ( time . Duration ( MAX_DB_CONN_LIFETIME ) * time . Minute )
2015-06-14 23:53:32 -08:00
var dbmap * gorp . DbMap
if driver == "sqlite3" {
dbmap = & gorp . DbMap { Db : db , TypeConverter : mattermConverter { } , Dialect : gorp . SqliteDialect { } }
2015-09-29 14:17:16 -07:00
} else if driver == model . DATABASE_DRIVER_MYSQL {
2015-07-09 08:34:36 -07:00
dbmap = & gorp . DbMap { Db : db , TypeConverter : mattermConverter { } , Dialect : gorp . MySQLDialect { Engine : "InnoDB" , Encoding : "UTF8MB4" } }
2015-09-29 14:17:16 -07:00
} else if driver == model . DATABASE_DRIVER_POSTGRES {
2015-07-12 14:56:44 -08:00
dbmap = & gorp . DbMap { Db : db , TypeConverter : mattermConverter { } , Dialect : gorp . PostgresDialect { } }
2015-06-14 23:53:32 -08:00
} else {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.dialect_driver.critical" ) )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_NO_DRIVER )
2015-06-14 23:53:32 -08:00
}
if trace {
dbmap . TraceOn ( "" , sqltrace . New ( os . Stdout , "sql-trace:" , sqltrace . Lmicroseconds ) )
}
return dbmap
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) TotalMasterDbConnections ( ) int {
2016-10-19 14:49:25 -04:00
return ss . GetMaster ( ) . Db . Stats ( ) . OpenConnections
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) TotalReadDbConnections ( ) int {
2016-10-19 14:49:25 -04:00
if len ( utils . Cfg . SqlSettings . DataSourceReplicas ) == 0 {
return 0
} else {
count := 0
for _ , db := range ss . replicas {
count = count + db . Db . Stats ( ) . OpenConnections
}
return count
}
return 0
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) GetCurrentSchemaVersion ( ) string {
2015-09-16 19:59:57 -07:00
version , _ := ss . GetMaster ( ) . SelectStr ( "SELECT Value FROM Systems WHERE Name='Version'" )
2015-09-16 17:37:11 -07:00
return version
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) MarkSystemRanUnitTests ( ) {
2015-10-15 11:16:26 -07:00
if result := <- ss . System ( ) . Get ( ) ; result . Err == nil {
props := result . Data . ( model . StringMap )
unitTests := props [ model . SYSTEM_RAN_UNIT_TESTS ]
if len ( unitTests ) == 0 {
systemTests := & model . System { Name : model . SYSTEM_RAN_UNIT_TESTS , Value : "1" }
<- ss . System ( ) . Save ( systemTests )
}
}
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) DoesTableExist ( tableName string ) bool {
2015-09-29 14:17:16 -07:00
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2015-09-16 21:16:07 -07:00
count , err := ss . GetMaster ( ) . SelectInt (
` SELECT count(relname) FROM pg_class WHERE relname=$1 ` ,
strings . ToLower ( tableName ) ,
)
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.table_exists.critical" ) , err )
2015-09-16 21:16:07 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_TABLE_EXISTS )
2015-09-16 21:16:07 -07:00
}
return count > 0
2015-09-29 14:17:16 -07:00
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
2015-09-16 21:16:07 -07:00
count , err := ss . GetMaster ( ) . SelectInt (
` SELECT
COUNT ( 0 ) AS table_exists
FROM
information_schema . TABLES
WHERE
TABLE_SCHEMA = DATABASE ( )
AND TABLE_NAME = ?
` ,
tableName ,
)
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.table_exists.critical" ) , err )
2015-09-16 21:16:07 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_TABLE_EXISTS_MYSQL )
2015-09-16 21:16:07 -07:00
}
return count > 0
} else {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.column_exists_missing_driver.critical" ) )
2015-09-16 21:16:07 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_COLUMN_EXISTS )
return false
2015-09-16 21:16:07 -07:00
}
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) DoesColumnExist ( tableName string , columnName string ) bool {
2015-09-29 14:17:16 -07:00
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2015-08-27 16:01:17 -07:00
count , err := ss . GetMaster ( ) . SelectInt (
` SELECT COUNT ( 0 )
FROM pg_attribute
WHERE attrelid = $ 1 : : regclass
AND attname = $ 2
AND NOT attisdropped ` ,
strings . ToLower ( tableName ) ,
strings . ToLower ( columnName ) ,
)
if err != nil {
2015-09-18 08:07:31 -04:00
if err . Error ( ) == "pq: relation \"" + strings . ToLower ( tableName ) + "\" does not exist" {
return false
}
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.column_exists.critical" ) , err )
2015-08-27 16:01:17 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_DOES_COLUMN_EXISTS_POSTGRES )
2015-08-27 16:01:17 -07:00
}
return count > 0
2015-09-29 14:17:16 -07:00
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
2015-08-27 16:01:17 -07:00
count , err := ss . GetMaster ( ) . SelectInt (
` SELECT
2015-06-14 23:53:32 -08:00
COUNT ( 0 ) AS column_exists
FROM
information_schema . COLUMNS
WHERE
TABLE_SCHEMA = DATABASE ( )
AND TABLE_NAME = ?
AND COLUMN_NAME = ? ` ,
2015-08-27 16:01:17 -07:00
tableName ,
columnName ,
)
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.column_exists.critical" ) , err )
2015-08-27 16:01:17 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_DOES_COLUMN_EXISTS_MYSQL )
2015-08-27 16:01:17 -07:00
}
return count > 0
} else {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.column_exists_missing_driver.critical" ) )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_DOES_COLUMN_EXISTS_MISSING )
return false
2015-06-14 23:53:32 -08:00
}
2015-07-09 13:59:19 -04:00
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) CreateColumnIfNotExists ( tableName string , columnName string , mySqlColType string , postgresColType string , defaultValue string ) bool {
2015-07-23 10:15:53 -08:00
2015-07-09 13:59:19 -04:00
if ss . DoesColumnExist ( tableName , columnName ) {
2015-06-14 23:53:32 -08:00
return false
}
2015-09-29 14:17:16 -07:00
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2015-08-27 16:01:17 -07:00
_ , err := ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " ADD " + columnName + " " + postgresColType + " DEFAULT '" + defaultValue + "'" )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_column.critical" ) , err )
2015-08-27 16:01:17 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_COLUMN_POSTGRES )
2015-08-27 16:01:17 -07:00
}
return true
2015-09-29 14:17:16 -07:00
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
2015-08-27 16:01:17 -07:00
_ , err := ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " ADD " + columnName + " " + mySqlColType + " DEFAULT '" + defaultValue + "'" )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_column.critical" ) , err )
2015-08-27 16:01:17 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_COLUMN_MYSQL )
2015-08-27 16:01:17 -07:00
}
return true
} else {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_column_missing_driver.critical" ) )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_COLUMN_MISSING )
return false
2015-06-14 23:53:32 -08:00
}
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) RemoveColumnIfExists ( tableName string , columnName string ) bool {
2015-07-23 10:15:53 -08:00
2015-09-22 01:15:41 -07:00
if ! ss . DoesColumnExist ( tableName , columnName ) {
return false
}
2015-07-09 13:59:19 -04:00
2015-09-22 01:15:41 -07:00
_ , err := ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " DROP COLUMN " + columnName )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.drop_column.critical" ) , err )
2015-09-22 01:15:41 -07:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_REMOVE_COLUMN )
2015-09-22 01:15:41 -07:00
}
2015-06-14 23:53:32 -08:00
2015-09-22 01:15:41 -07:00
return true
}
2015-07-09 13:59:19 -04:00
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) RenameColumnIfExists ( tableName string , oldColumnName string , newColumnName string , colType string ) bool {
2015-10-26 16:18:28 -04:00
if ! ss . DoesColumnExist ( tableName , oldColumnName ) {
return false
}
2015-06-14 23:53:32 -08:00
2015-10-26 16:18:28 -04:00
var err error
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
_ , err = ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " CHANGE " + oldColumnName + " " + newColumnName + " " + colType )
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
_ , err = ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " RENAME COLUMN " + oldColumnName + " TO " + newColumnName )
}
2015-07-09 13:59:19 -04:00
2015-10-26 16:18:28 -04:00
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.rename_column.critical" ) , err )
2015-10-26 16:18:28 -04:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_RENAME_COLUMN )
2015-10-26 16:18:28 -04:00
}
2015-06-14 23:53:32 -08:00
2015-10-26 16:18:28 -04:00
return true
}
2015-06-14 23:53:32 -08:00
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) GetMaxLengthOfColumnIfExists ( tableName string , columnName string ) string {
2016-02-22 16:08:40 -08:00
if ! ss . DoesColumnExist ( tableName , columnName ) {
return ""
}
var result string
var err error
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
result , err = ss . GetMaster ( ) . SelectStr ( "SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.columns WHERE table_name = '" + tableName + "' AND COLUMN_NAME = '" + columnName + "'" )
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2016-02-22 16:31:19 -08:00
result , err = ss . GetMaster ( ) . SelectStr ( "SELECT character_maximum_length FROM information_schema.columns WHERE table_name = '" + strings . ToLower ( tableName ) + "' AND column_name = '" + strings . ToLower ( columnName ) + "'" )
2016-02-22 16:08:40 -08:00
}
if err != nil {
l4g . Critical ( utils . T ( "store.sql.maxlength_column.critical" ) , err )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_MAX_COLUMN )
2016-02-22 16:08:40 -08:00
}
return result
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) AlterColumnTypeIfExists ( tableName string , columnName string , mySqlColType string , postgresColType string ) bool {
2016-02-22 16:08:40 -08:00
if ! ss . DoesColumnExist ( tableName , columnName ) {
return false
}
var err error
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
_ , err = ss . GetMaster ( ) . Exec ( "ALTER TABLE " + tableName + " MODIFY " + columnName + " " + mySqlColType )
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2016-02-22 16:31:19 -08:00
_ , err = ss . GetMaster ( ) . Exec ( "ALTER TABLE " + strings . ToLower ( tableName ) + " ALTER COLUMN " + strings . ToLower ( columnName ) + " TYPE " + postgresColType )
2016-02-22 16:08:40 -08:00
}
if err != nil {
l4g . Critical ( utils . T ( "store.sql.alter_column_type.critical" ) , err )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_ALTER_COLUMN )
2016-02-22 16:08:40 -08:00
}
return true
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) CreateUniqueIndexIfNotExists ( indexName string , tableName string , columnName string ) {
2016-04-21 22:37:01 -07:00
ss . createIndexIfNotExists ( indexName , tableName , columnName , INDEX_TYPE_DEFAULT , true )
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) CreateIndexIfNotExists ( indexName string , tableName string , columnName string ) {
2016-04-21 22:37:01 -07:00
ss . createIndexIfNotExists ( indexName , tableName , columnName , INDEX_TYPE_DEFAULT , false )
2015-06-14 23:53:32 -08:00
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) CreateFullTextIndexIfNotExists ( indexName string , tableName string , columnName string ) {
2016-04-21 22:37:01 -07:00
ss . createIndexIfNotExists ( indexName , tableName , columnName , INDEX_TYPE_FULL_TEXT , false )
2015-10-01 14:07:20 -04:00
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) createIndexIfNotExists ( indexName string , tableName string , columnName string , indexType string , unique bool ) {
2016-04-21 22:37:01 -07:00
uniqueStr := ""
if unique {
uniqueStr = "UNIQUE "
}
2015-06-14 23:53:32 -08:00
2015-09-29 14:17:16 -07:00
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2015-07-28 12:42:17 -04:00
_ , err := ss . GetMaster ( ) . SelectStr ( "SELECT $1::regclass" , indexName )
2015-07-23 10:15:53 -08:00
// It should fail if the index does not exist
if err == nil {
return
}
query := ""
2015-10-01 14:07:20 -04:00
if indexType == INDEX_TYPE_FULL_TEXT {
2016-10-20 11:30:44 -04:00
postgresColumnNames := convertMySQLFullTextColumnsToPostgres ( columnName )
query = "CREATE INDEX " + indexName + " ON " + tableName + " USING gin(to_tsvector('english', " + postgresColumnNames + "))"
2015-07-23 10:15:53 -08:00
} else {
2016-04-21 22:37:01 -07:00
query = "CREATE " + uniqueStr + "INDEX " + indexName + " ON " + tableName + " (" + columnName + ")"
2015-07-23 10:15:53 -08:00
}
_ , err = ss . GetMaster ( ) . Exec ( query )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_index.critical" ) , err )
2015-07-23 10:15:53 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_INDEX_POSTGRES )
2015-07-23 10:15:53 -08:00
}
2015-09-29 14:17:16 -07:00
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
2015-07-23 10:15:53 -08:00
count , err := ss . GetMaster ( ) . SelectInt ( "SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?" , tableName , indexName )
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.check_index.critical" ) , err )
2015-07-23 10:15:53 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_INDEX_MYSQL )
2015-07-23 10:15:53 -08:00
}
if count > 0 {
return
}
fullTextIndex := ""
2015-10-16 11:17:24 -04:00
if indexType == INDEX_TYPE_FULL_TEXT {
2015-07-23 10:15:53 -08:00
fullTextIndex = " FULLTEXT "
}
2016-04-21 22:37:01 -07:00
_ , err = ss . GetMaster ( ) . Exec ( "CREATE " + uniqueStr + fullTextIndex + " INDEX " + indexName + " ON " + tableName + " (" + columnName + ")" )
2015-07-23 10:15:53 -08:00
if err != nil {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_index.critical" ) , err )
2015-07-23 10:15:53 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_INDEX_FULL_MYSQL )
2015-07-23 10:15:53 -08:00
}
} else {
2016-01-25 16:08:06 -03:00
l4g . Critical ( utils . T ( "store.sql.create_index_missing_driver.critical" ) )
2015-06-14 23:53:32 -08:00
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_CREATE_INDEX_MISSING )
2015-06-14 23:53:32 -08:00
}
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) RemoveIndexIfExists ( indexName string , tableName string ) {
2016-02-22 16:08:40 -08:00
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
_ , err := ss . GetMaster ( ) . SelectStr ( "SELECT $1::regclass" , indexName )
// It should fail if the index does not exist
if err == nil {
return
}
_ , err = ss . GetMaster ( ) . Exec ( "DROP INDEX " + indexName )
if err != nil {
l4g . Critical ( utils . T ( "store.sql.remove_index.critical" ) , err )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_REMOVE_INDEX_POSTGRES )
2016-02-22 16:08:40 -08:00
}
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
count , err := ss . GetMaster ( ) . SelectInt ( "SELECT COUNT(0) AS index_exists FROM information_schema.statistics WHERE TABLE_SCHEMA = DATABASE() and table_name = ? AND index_name = ?" , tableName , indexName )
if err != nil {
l4g . Critical ( utils . T ( "store.sql.check_index.critical" ) , err )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_REMOVE_INDEX_MYSQL )
2016-02-22 16:08:40 -08:00
}
if count > 0 {
return
}
_ , err = ss . GetMaster ( ) . Exec ( "DROP INDEX " + indexName + " ON " + tableName )
if err != nil {
l4g . Critical ( utils . T ( "store.sql.remove_index.critical" ) , err )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_REMOVE_INDEX_MYSQL )
2016-02-22 16:08:40 -08:00
}
} else {
l4g . Critical ( utils . T ( "store.sql.create_index_missing_driver.critical" ) )
time . Sleep ( time . Second )
2016-08-24 16:17:40 -08:00
os . Exit ( EXIT_REMOVE_INDEX_MISSING )
2016-02-22 16:08:40 -08:00
}
}
2016-05-17 12:52:10 -07:00
func IsUniqueConstraintError ( err string , indexName [ ] string ) bool {
2015-07-12 18:19:03 -08:00
unique := strings . Contains ( err , "unique constraint" ) || strings . Contains ( err , "Duplicate entry" )
2016-05-17 12:52:10 -07:00
field := false
for _ , contain := range indexName {
if strings . Contains ( err , contain ) {
field = true
break
}
}
2015-07-12 18:19:03 -08:00
return unique && field
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) GetMaster ( ) * gorp . DbMap {
2015-06-14 23:53:32 -08:00
return ss . master
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) GetReplica ( ) * gorp . DbMap {
rrNum := atomic . AddInt64 ( & ss . rrCounter , 1 ) % int64 ( len ( ss . replicas ) )
return ss . replicas [ rrNum ]
2015-06-14 23:53:32 -08:00
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) GetAllConns ( ) [ ] * gorp . DbMap {
2015-06-14 23:53:32 -08:00
all := make ( [ ] * gorp . DbMap , len ( ss . replicas ) + 1 )
copy ( all , ss . replicas )
all [ len ( ss . replicas ) ] = ss . master
return all
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Close ( ) {
2016-01-25 16:08:06 -03:00
l4g . Info ( utils . T ( "store.sql.closing.info" ) )
2015-06-14 23:53:32 -08:00
ss . master . Db . Close ( )
for _ , replica := range ss . replicas {
replica . Db . Close ( )
}
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Team ( ) TeamStore {
2015-06-14 23:53:32 -08:00
return ss . team
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Channel ( ) ChannelStore {
2015-06-14 23:53:32 -08:00
return ss . channel
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Post ( ) PostStore {
2015-06-14 23:53:32 -08:00
return ss . post
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) User ( ) UserStore {
2015-06-14 23:53:32 -08:00
return ss . user
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Session ( ) SessionStore {
2015-06-14 23:53:32 -08:00
return ss . session
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Audit ( ) AuditStore {
2015-06-14 23:53:32 -08:00
return ss . audit
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Compliance ( ) ComplianceStore {
2016-03-14 16:07:58 -07:00
return ss . compliance
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) OAuth ( ) OAuthStore {
2015-09-16 15:49:12 -04:00
return ss . oauth
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) System ( ) SystemStore {
2015-09-16 19:59:57 -07:00
return ss . system
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Webhook ( ) WebhookStore {
2015-09-21 14:22:23 -04:00
return ss . webhook
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Command ( ) CommandStore {
2016-01-06 21:09:05 -06:00
return ss . command
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Preference ( ) PreferenceStore {
2015-10-01 10:56:07 -04:00
return ss . preference
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) License ( ) LicenseStore {
2016-02-04 13:00:03 -05:00
return ss . license
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) PasswordRecovery ( ) PasswordRecoveryStore {
2016-04-21 22:37:01 -07:00
return ss . recovery
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Emoji ( ) EmojiStore {
2016-06-14 09:38:19 -04:00
return ss . emoji
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) Status ( ) StatusStore {
2016-07-18 11:10:03 -04:00
return ss . status
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) FileInfo ( ) FileInfoStore {
2016-09-30 11:06:30 -04:00
return ss . fileInfo
}
2016-11-30 13:55:49 -05:00
func ( ss * SqlStore ) Reaction ( ) ReactionStore {
return ss . reaction
}
2016-10-27 13:50:24 -04:00
func ( ss * SqlStore ) DropAllTables ( ) {
2016-04-21 22:37:01 -07:00
ss . master . TruncateTables ( )
}
2015-06-14 23:53:32 -08:00
type mattermConverter struct { }
func ( me mattermConverter ) ToDb ( val interface { } ) ( interface { } , error ) {
switch t := val . ( type ) {
case model . StringMap :
return model . MapToJson ( t ) , nil
case model . StringArray :
return model . ArrayToJson ( t ) , nil
case model . EncryptStringMap :
return encrypt ( [ ] byte ( utils . Cfg . SqlSettings . AtRestEncryptKey ) , model . MapToJson ( t ) )
2015-11-05 23:32:44 +01:00
case model . StringInterface :
return model . StringInterfaceToJson ( t ) , nil
2015-06-14 23:53:32 -08:00
}
return val , nil
}
func ( me mattermConverter ) FromDb ( target interface { } ) ( gorp . CustomScanner , bool ) {
switch target . ( type ) {
case * model . StringMap :
binder := func ( holder , target interface { } ) error {
s , ok := holder . ( * string )
if ! ok {
2016-01-25 16:08:06 -03:00
return errors . New ( utils . T ( "store.sql.convert_string_map" ) )
2015-06-14 23:53:32 -08:00
}
b := [ ] byte ( * s )
return json . Unmarshal ( b , target )
}
return gorp . CustomScanner { new ( string ) , target , binder } , true
case * model . StringArray :
binder := func ( holder , target interface { } ) error {
s , ok := holder . ( * string )
if ! ok {
2016-01-25 16:08:06 -03:00
return errors . New ( utils . T ( "store.sql.convert_string_array" ) )
2015-06-14 23:53:32 -08:00
}
b := [ ] byte ( * s )
return json . Unmarshal ( b , target )
}
return gorp . CustomScanner { new ( string ) , target , binder } , true
case * model . EncryptStringMap :
binder := func ( holder , target interface { } ) error {
s , ok := holder . ( * string )
if ! ok {
2016-01-25 16:08:06 -03:00
return errors . New ( utils . T ( "store.sql.convert_encrypt_string_map" ) )
2015-06-14 23:53:32 -08:00
}
ue , err := decrypt ( [ ] byte ( utils . Cfg . SqlSettings . AtRestEncryptKey ) , * s )
if err != nil {
return err
}
b := [ ] byte ( ue )
return json . Unmarshal ( b , target )
2015-11-05 23:32:44 +01:00
}
return gorp . CustomScanner { new ( string ) , target , binder } , true
case * model . StringInterface :
binder := func ( holder , target interface { } ) error {
s , ok := holder . ( * string )
if ! ok {
2016-01-25 16:08:06 -03:00
return errors . New ( utils . T ( "store.sql.convert_string_interface" ) )
2015-11-05 23:32:44 +01:00
}
b := [ ] byte ( * s )
return json . Unmarshal ( b , target )
2015-06-14 23:53:32 -08:00
}
return gorp . CustomScanner { new ( string ) , target , binder } , true
}
return gorp . CustomScanner { } , false
}
2016-10-20 11:30:44 -04:00
func convertMySQLFullTextColumnsToPostgres ( columnNames string ) string {
columns := strings . Split ( columnNames , ", " )
concatenatedColumnNames := ""
for i , c := range columns {
concatenatedColumnNames += c
if i < len ( columns ) - 1 {
concatenatedColumnNames += " || ' ' || "
}
}
return concatenatedColumnNames
}
2015-06-14 23:53:32 -08:00
func encrypt ( key [ ] byte , text string ) ( string , error ) {
if text == "" || text == "{}" {
return "" , nil
}
plaintext := [ ] byte ( text )
2015-06-24 16:29:45 +02:00
skey := sha512 . Sum512 ( key )
ekey , akey := skey [ : 32 ] , skey [ 32 : ]
2015-06-14 23:53:32 -08:00
2015-06-24 16:29:45 +02:00
block , err := aes . NewCipher ( ekey )
2015-06-14 23:53:32 -08:00
if err != nil {
return "" , err
}
2015-06-24 16:29:45 +02:00
macfn := hmac . New ( sha256 . New , akey )
ciphertext := make ( [ ] byte , aes . BlockSize + macfn . Size ( ) + len ( plaintext ) )
2015-06-14 23:53:32 -08:00
iv := ciphertext [ : aes . BlockSize ]
if _ , err := io . ReadFull ( crand . Reader , iv ) ; err != nil {
return "" , err
}
stream := cipher . NewCFBEncrypter ( block , iv )
2015-06-24 16:29:45 +02:00
stream . XORKeyStream ( ciphertext [ aes . BlockSize + macfn . Size ( ) : ] , plaintext )
macfn . Write ( ciphertext [ aes . BlockSize + macfn . Size ( ) : ] )
mac := macfn . Sum ( nil )
copy ( ciphertext [ aes . BlockSize : aes . BlockSize + macfn . Size ( ) ] , mac )
2015-06-14 23:53:32 -08:00
return base64 . URLEncoding . EncodeToString ( ciphertext ) , nil
}
func decrypt ( key [ ] byte , cryptoText string ) ( string , error ) {
if cryptoText == "" || cryptoText == "{}" {
return "{}" , nil
}
2015-06-24 16:29:45 +02:00
ciphertext , err := base64 . URLEncoding . DecodeString ( cryptoText )
if err != nil {
2015-07-06 00:50:42 -08:00
return "" , err
2015-06-24 16:29:45 +02:00
}
skey := sha512 . Sum512 ( key )
ekey , akey := skey [ : 32 ] , skey [ 32 : ]
macfn := hmac . New ( sha256 . New , akey )
if len ( ciphertext ) < aes . BlockSize + macfn . Size ( ) {
2016-01-25 16:08:06 -03:00
return "" , errors . New ( utils . T ( "store.sql.short_ciphertext" ) )
2015-06-24 16:29:45 +02:00
}
macfn . Write ( ciphertext [ aes . BlockSize + macfn . Size ( ) : ] )
expectedMac := macfn . Sum ( nil )
2015-07-06 00:50:42 -08:00
mac := ciphertext [ aes . BlockSize : aes . BlockSize + macfn . Size ( ) ]
2015-06-24 16:29:45 +02:00
if hmac . Equal ( expectedMac , mac ) != true {
2016-01-25 16:08:06 -03:00
return "" , errors . New ( utils . T ( "store.sql.incorrect_mac" ) )
2015-06-24 16:29:45 +02:00
}
2015-06-14 23:53:32 -08:00
2015-06-24 16:29:45 +02:00
block , err := aes . NewCipher ( ekey )
2015-06-14 23:53:32 -08:00
if err != nil {
return "" , err
}
if len ( ciphertext ) < aes . BlockSize {
2016-01-25 16:08:06 -03:00
return "" , errors . New ( utils . T ( "store.sql.too_short_ciphertext" ) )
2015-06-14 23:53:32 -08:00
}
iv := ciphertext [ : aes . BlockSize ]
2015-06-24 16:29:45 +02:00
ciphertext = ciphertext [ aes . BlockSize + macfn . Size ( ) : ]
2015-06-14 23:53:32 -08:00
stream := cipher . NewCFBDecrypter ( block , iv )
stream . XORKeyStream ( ciphertext , ciphertext )
return fmt . Sprintf ( "%s" , ciphertext ) , nil
}