-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Добавлен модуль работы с подключением к SQL базе данных и миграциями.
- Loading branch information
Showing
12 changed files
with
866 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Package migration_sql | ||
package migration_sql | ||
|
||
import ( | ||
kitModuleCfg "github.com/webnice/kit/v3/module/cfg" | ||
kitModuleCfgReg "github.com/webnice/kit/v3/module/cfg/reg" | ||
kitModuleDbSql "github.com/webnice/kit/v3/module/db/sql" | ||
kitTypes "github.com/webnice/kit/v3/types" | ||
kitTypesDb "github.com/webnice/kit/v3/types/db" | ||
) | ||
|
||
// Структура объекта компоненты. | ||
type impl struct { | ||
cfg kitModuleCfg.Interface | ||
databaseSql *kitTypesDb.DatabaseSqlConfiguration | ||
} | ||
|
||
// Регистрация компоненты в приложении. | ||
func init() { kitModuleCfgReg.Registration(newComponent()) } | ||
|
||
// Конструктор объекта компоненты. | ||
func newComponent() kitTypes.Component { | ||
var m8s = &impl{ | ||
cfg: kitModuleCfg.Get(), | ||
databaseSql: new(kitTypesDb.DatabaseSqlConfiguration), | ||
} | ||
|
||
m8s.registrationConfigurationError(m8s.cfg.Gist().ConfigurationRegistration(m8s.databaseSql)) | ||
|
||
return m8s | ||
} | ||
|
||
// Ссылка на менеджер логирования, для удобного использования внутри компоненты или модуля. | ||
func (m8s *impl) log() kitTypes.Logger { return m8s.cfg.Log() } | ||
|
||
// Обработка ошибки регистрации конфигурации. | ||
func (m8s *impl) registrationConfigurationError(err error) { | ||
if err == nil { | ||
return | ||
} | ||
switch eto := err.(type) { | ||
case kitModuleCfg.Err: | ||
m8s.cfg.Gist().ErrorAppend(eto) | ||
default: | ||
m8s.cfg.Gist().ErrorAppend(m8s.cfg.Errors().ConfigurationApplicationObject(0, eto)) | ||
} | ||
} | ||
|
||
// Preferences Функция возвращает настройки компоненты. | ||
func (m8s *impl) Preferences() kitTypes.ComponentPreferences { | ||
const ( | ||
cEnvironment = `(?mi)application/component/environment$` | ||
cInterrupt = `(?mi)application/component/interrupt$` | ||
cConfiguration = `(?mi)application/component/configuration$` | ||
cLogging = `(?mi)application/component/logg.*` | ||
cLoggerConsole = `(?mi)application/component/logger_console$` | ||
cPidfile = `(?mi)application/component/pidfile$` | ||
cBootstrap = `(?mi)application/component/bootstrap$` | ||
) | ||
return kitTypes.ComponentPreferences{ | ||
After: []string{cConfiguration, cLoggerConsole, cLogging, cPidfile, cInterrupt, cEnvironment}, | ||
Require: []string{cPidfile}, | ||
Before: []string{cBootstrap}, | ||
} | ||
} | ||
|
||
// Initiate Функция инициализации компонента и подготовки компонента к запуску. | ||
func (m8s *impl) Initiate() (err error) { | ||
var ( | ||
elm interface{} | ||
ok bool | ||
c *kitTypesDb.DatabaseSqlConfiguration | ||
) | ||
|
||
// Загрузка конфигурации базы данных, сохранённой в конфигурации приложения. | ||
if elm, err = m8s.cfg.ConfigurationByObject(m8s.databaseSql); err != nil { | ||
return | ||
} | ||
// Приведение пустого интерфейса к типу данных. | ||
if c, ok = elm.(*kitTypesDb.DatabaseSqlConfiguration); ok { | ||
// Исправление пути к миграции на абсолютный путь, исправление по адресу, поэтому все кто запросят | ||
// конфигурацию базы данных, получат исправленный вариант. | ||
m8s.cfg.Gist().AbsolutePathAndUpdate(&c.SqlDB.Migration) | ||
// Обновление локальной копии конфигурации, так как после работы yaml библиотеки может слетать адрес. | ||
m8s.databaseSql = c | ||
} | ||
|
||
return | ||
} | ||
|
||
// Do Выполнение компонента приложения. | ||
func (m8s *impl) Do() (levelDone bool, levelExit bool, err error) { | ||
if err = kitModuleDbSql.Get().MigrationUp(); err != nil { | ||
levelDone, levelExit = true, true | ||
} | ||
|
||
return | ||
} | ||
|
||
// Finalize Функция вызывается перед завершением компонента и приложения в целом. | ||
func (m8s *impl) Finalize() (err error) { return } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Package sql | ||
package sql | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// Создание DSN для подключения к базе данных. | ||
func (mys *impl) makeDsn() (err error) { | ||
const keyTcp, keySocket = `tcp`, `socket` | ||
var ( | ||
n int | ||
found bool | ||
) | ||
|
||
// Проверка конфигурации. | ||
if mys.cfg == nil { | ||
err = mys.Errors().ConfigurationIsEmpty(0) | ||
return | ||
} | ||
// Проверка драйвера базы данных. | ||
for n = range supportDrivers { | ||
if strings.EqualFold(mys.cfg.Driver, supportDrivers[n]) { | ||
mys.cfg.Driver, found = supportDrivers[n], true | ||
break | ||
} | ||
} | ||
if !found { | ||
err = mys.Errors().UnknownDatabaseDriver(0, mys.cfg.Driver) | ||
return | ||
} | ||
// Самая простая конфигурация: sqlite | ||
if mys.cfg.Driver == driverSqlite { | ||
mys.dsn = fmt.Sprintf("%s?%s", mys.cfg.Name, dsnTimeSettings) | ||
return | ||
} | ||
// Имя пользователя. | ||
if mys.cfg.Login == "" { | ||
err = mys.Errors().UsernameIsEmpty(0) | ||
return | ||
} | ||
// Имя пользователя и пароль можно добавлять в DSN. | ||
mys.dsn = fmt.Sprintf("%s:%s", mys.cfg.Login, mys.cfg.Password) | ||
// Тип подключения. | ||
switch strings.ToLower(mys.cfg.Type) { | ||
case keyTcp: | ||
mys.dsn += fmt.Sprintf("@%s(%s:%d)", keyTcp, mys.cfg.Host, mys.cfg.Port) | ||
case keySocket: | ||
mys.dsn += fmt.Sprintf(dsnUnixTpl, mys.cfg.Socket) | ||
default: | ||
err = mys.Errors().WrongConnectionType(0, mys.cfg.Type) | ||
return | ||
} | ||
mys.cfg.Type = strings.ToLower(mys.cfg.Type) | ||
// Название базы данных. | ||
mys.dsn += fmt.Sprintf("/%s", mys.cfg.Name) | ||
// Парсинг времени. | ||
mys.dsn += fmt.Sprintf(dsnTimeSettings, mys.cfg.ParseTime) | ||
// Зона времени. | ||
if mys.cfg.TimezoneLocation != "" { | ||
mys.dsn += fmt.Sprintf(dsnLocationSettings, mys.cfg.TimezoneLocation) | ||
} | ||
// Кодировка соединения с базой данных. | ||
if mys.cfg.Charset != "" { | ||
mys.dsn += fmt.Sprintf(dsnCharsetTpl, mys.cfg.Charset) | ||
} | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Package sql | ||
package sql | ||
|
||
// Обычные ошибки | ||
const ( | ||
eConfigurationIsEmpty uint = iota + 1 // 001 | ||
eUnknownDatabaseDriver // 002 | ||
eUsernameIsEmpty // 003 | ||
eWrongConnectionType // 004 | ||
eConnectError // 005 | ||
eDriverUnImplemented // 006 | ||
eApplyMigration // 007 | ||
eUnknownDialect // 008 | ||
) | ||
|
||
// Текстовые значения кодов ошибок на основном языке приложения. | ||
const ( | ||
cConfigurationIsEmpty = `Конфигурация подключения к базе данных пустая.` | ||
cUnknownDatabaseDriver = `Указан неизвестный или не поддерживаемый драйвер базы данных: ` + "%q." | ||
cUsernameIsEmpty = `Не указано имя пользователя, для подключения к базе данных.` | ||
cWrongConnectionType = `Указан неизвестный или не поддерживаемый способ подключения к базе данных: ` + "%q." | ||
cConnectError = `Подключение к базе данных завершилось ошибкой: ` + "%s." | ||
cDriverUnImplemented = `Подключение к базе данных с помощью драйвера %q не создано.` | ||
cApplyMigration = `Применение новых миграций базы данных прервано ошибкой: ` + "%s." | ||
cUnknownDialect = `Применение миграций базы данных, настройка диалекта %q прервано ошибкой: ` + "%s." | ||
) | ||
|
||
// Константы указаны в объектах, адрес которых фиксирован всё время работы приложения. | ||
// Это позволяет сравнивать ошибки между собой используя обычное сравнение "==", но сравнивать необходимо только | ||
// якорь "Anchor()" объекта ошибки. | ||
var ( | ||
errSingleton = &Error{} | ||
errConfigurationIsEmpty = err{tpl: cConfigurationIsEmpty, code: eConfigurationIsEmpty} | ||
errUnknownDatabaseDriver = err{tpl: cUnknownDatabaseDriver, code: eUnknownDatabaseDriver} | ||
errUsernameIsEmpty = err{tpl: cUsernameIsEmpty, code: eUsernameIsEmpty} | ||
errWrongConnectionType = err{tpl: cWrongConnectionType, code: eWrongConnectionType} | ||
errConnectError = err{tpl: cConnectError, code: eConnectError} | ||
errDriverUnImplemented = err{tpl: cDriverUnImplemented, code: eDriverUnImplemented} | ||
errApplyMigration = err{tpl: cApplyMigration, code: eApplyMigration} | ||
errUnknownDialect = err{tpl: cUnknownDialect, code: eUnknownDialect} | ||
) | ||
|
||
// ERRORS: Реализация ошибок с возможностью сравнения ошибок между собой. | ||
|
||
// ConfigurationIsEmpty Конфигурация подключения к базе данных пустая. | ||
func (e *Error) ConfigurationIsEmpty(code uint) Err { return newErr(&errConfigurationIsEmpty, code) } | ||
|
||
// UnknownDatabaseDriver Указан неизвестный или не поддерживаемый драйвер базы данных: ... | ||
func (e *Error) UnknownDatabaseDriver(code uint, driver string) Err { | ||
return newErr(&errUnknownDatabaseDriver, code, driver) | ||
} | ||
|
||
// UsernameIsEmpty Не указано имя пользователя, для подключения к базе данных. | ||
func (e *Error) UsernameIsEmpty(code uint) Err { return newErr(&errUsernameIsEmpty, code) } | ||
|
||
// WrongConnectionType Указан неизвестный или не поддерживаемый способ подключения к базе данных: ... | ||
func (e *Error) WrongConnectionType(code uint, connType string) Err { | ||
return newErr(&errWrongConnectionType, code, connType) | ||
} | ||
|
||
// ConnectError Подключение к базе данных завершилось ошибкой: ... | ||
func (e *Error) ConnectError(code uint, err error) Err { return newErr(&errConnectError, code, err) } | ||
|
||
// DriverUnImplemented Подключение к базе данных с помощью драйвера ... не создано. | ||
func (e *Error) DriverUnImplemented(code uint, driver string) Err { | ||
return newErr(&errDriverUnImplemented, code, driver) | ||
} | ||
|
||
// ApplyMigration Применение новых миграций базы данных прервано ошибкой: ... | ||
func (e *Error) ApplyMigration(code uint, err error) Err { | ||
return newErr(&errApplyMigration, code, err) | ||
} | ||
|
||
// UnknownDialect Применение миграций базы данных, настройка диалекта ... прервано ошибкой: ... | ||
func (e *Error) UnknownDialect(code uint, dialect string, err error) Err { | ||
return newErr(&errUnknownDialect, code, dialect, err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Package sql | ||
package sql | ||
|
||
import ( | ||
"github.com/jmoiron/sqlx" | ||
"gorm.io/gorm" | ||
) | ||
|
||
// Gist Возвращается настроенный и готовый к работе интерфейс подключения к базе данных. | ||
func (db *Implementation) Gist() Interface { return db.getParent() } | ||
|
||
// Gorm Возвращается настроенный и готовый к работе объект ORM gorm.io/gorm. | ||
func (db *Implementation) Gorm() *gorm.DB { return db.getParent().GormDB() } | ||
|
||
// Sqlx Настроенный и готовый к работе объект обёртки над соединением с БД github.com/jmoiron/sqlx. | ||
func (db *Implementation) Sqlx() *sqlx.DB { return db.getParent().SqlxDB() } | ||
|
||
// Возвращает объект родителя, с запоминанием объекта. | ||
func (db *Implementation) getParent() Interface { | ||
if db.parent != nil { | ||
return db.parent | ||
} | ||
db.parent = Get() | ||
|
||
return db.parent | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Package sql | ||
package sql | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
kitModuleDye "github.com/webnice/kit/v3/module/dye" | ||
kitTypes "github.com/webnice/kit/v3/types" | ||
|
||
gormLogger "gorm.io/gorm/logger" | ||
) | ||
|
||
func (mys *impl) LogMode(l gormLogger.LogLevel) gormLogger.Interface { | ||
mys.log().Noticef("gorm уровень логирования: %d", int(l)) | ||
return mys | ||
} | ||
|
||
func (mys *impl) Info(_ context.Context, s string, i ...interface{}) { mys.log().Infof(s, i...) } | ||
|
||
func (mys *impl) Warn(_ context.Context, s string, i ...interface{}) { mys.log().Warningf(s, i...) } | ||
|
||
func (mys *impl) Error(_ context.Context, s string, i ...interface{}) { mys.log().Errorf(s, i...) } | ||
|
||
func (mys *impl) Trace(_ context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||
const ( | ||
keyQuery, keySql = `query`, `sql` | ||
keyDriver, keyElapsed, keyRows = `driver`, `elapsed`, `rows` | ||
tplTracef, tplErrorf = `sql:"%s"`, `sql:"%s", ошибка: %s` | ||
) | ||
var ( | ||
elapsed time.Duration | ||
sql string | ||
rows int64 | ||
keys kitTypes.LoggerKey | ||
) | ||
|
||
elapsed = time.Since(begin) | ||
sql, rows = fc() | ||
keys = kitTypes.LoggerKey{ | ||
keyQuery: keySql, | ||
keyDriver: mys.cfg.Driver, | ||
keyElapsed: elapsed, | ||
keyRows: rows, | ||
} | ||
switch err { | ||
case nil: | ||
mys.log().Key(keys).Tracef( | ||
tplTracef, | ||
kitModuleDye.New().Yellow().Done().String()+sql+kitModuleDye.New().Normal().Done().String(), | ||
) | ||
default: | ||
mys.log().Key(keys).Errorf( | ||
tplErrorf, | ||
kitModuleDye.New().Yellow().Done().String()+sql+kitModuleDye.New().Reset().Done().String(), | ||
kitModuleDye.New().Red().Done().String()+err.Error()+kitModuleDye.New().Reset().Done().String(), | ||
) | ||
} | ||
} |
Oops, something went wrong.