Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [#540] Remove SQL in show_command.go #825

Merged
merged 4 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions contracts/database/config.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
package database

// TODO Remove this
const (
DriverMysql Driver = "mysql"
DriverPostgres Driver = "postgres"
DriverSqlite Driver = "sqlite"
DriverSqlserver Driver = "sqlserver"
)

type Driver string

func (d Driver) String() string {
return string(d)
}

type Config struct {
Connection string
Database string
Driver string
// TODO Check if it can be removed
Prefix string
// TODO Check if it can be removed
Schema string
Host string
Password string
Port int
Prefix string
Schema string
Username string
Version string
}
6 changes: 6 additions & 0 deletions contracts/database/orm/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package orm
import (
"context"
"database/sql"

"github.com/goravel/framework/contracts/database"
)

type Orm interface {
// Config gets the database config.
Config() database.Config
// Connection gets an Orm instance from the connection pool.
Connection(name string) Orm
// DB gets the underlying database connection.
Expand All @@ -26,6 +30,8 @@ type Orm interface {
SetQuery(query Query)
// Transaction runs a callback wrapped in a database transaction.
Transaction(txFunc func(tx Query) error) error
// Version gets the current database version.
Version() string
// WithContext sets the context to be used by the Orm.
WithContext(ctx context.Context) Orm
}
Expand Down
88 changes: 27 additions & 61 deletions database/console/show_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package console

import (
"fmt"
"strings"

"github.com/spf13/cast"

"github.com/goravel/framework/contracts/config"
"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/contracts/database"
"github.com/goravel/framework/contracts/database/schema"
"github.com/goravel/framework/support/str"
)
Expand All @@ -22,15 +22,13 @@ type databaseInfo struct {
Version string
Database string
Host string
Port string
Port int
Username string
OpenConnections string
OpenConnections int
Tables []schema.Table `gorm:"-"`
Views []schema.View `gorm:"-"`
}

type queryResult struct{ Value string }

func NewShowCommand(config config.Config, schema schema.Schema) *ShowCommand {
return &ShowCommand{
config: config,
Expand Down Expand Up @@ -73,82 +71,50 @@ func (r *ShowCommand) Handle(ctx console.Context) error {
ctx.Error(fmt.Sprintf("No arguments expected for '%s' command, got '%s'.", r.Signature(), got))
return nil
}

r.schema = r.schema.Connection(ctx.Option("database"))
connection := r.schema.GetConnection()
getConfigValue := func(k string) string {
return r.config.GetString("database.connections." + connection + "." + k)
}
dbConfig := r.schema.Orm().Config()
info := databaseInfo{
Database: getConfigValue("database"),
Host: getConfigValue("host"),
Port: getConfigValue("port"),
Username: getConfigValue("username"),
Database: dbConfig.Database,
Host: dbConfig.Host,
Port: dbConfig.Port,
Username: dbConfig.Username,
}

var err error
info.Name, info.Version, info.OpenConnections, err = r.getDataBaseInfo()
if err != nil {
ctx.Error(err.Error())
return nil
}

if info.Tables, err = r.schema.GetTables(); err != nil {
ctx.Error(err.Error())
return nil
}

if ctx.OptionBool("views") {
if info.Views, err = r.schema.GetViews(); err != nil {
ctx.Error(err.Error())
return nil
}
}
r.display(ctx, info)

return nil
}

func (r *ShowCommand) getDataBaseInfo() (name, version, openConnections string, err error) {
var (
drivers = map[string]struct {
name string
versionQuery string
openConnectionsQuery string
}{
database.DriverSqlite.String(): {
name: "SQLite",
versionQuery: "SELECT sqlite_version() AS value;",
},
database.DriverMysql.String(): {
name: "MySQL",
versionQuery: "SELECT VERSION() AS value;",
openConnectionsQuery: "SHOW status WHERE variable_name = 'threads_connected';",
},
database.DriverPostgres.String(): {
name: "PostgresSQL",
versionQuery: "SELECT current_setting('server_version') AS value;",
openConnectionsQuery: "SELECT COUNT(*) AS value FROM pg_stat_activity;",
},
database.DriverSqlserver.String(): {
name: "SQL Server",
versionQuery: "SELECT SERVERPROPERTY('productversion') AS value;",
openConnectionsQuery: "SELECT COUNT(*) Value FROM sys.dm_exec_sessions WHERE status = 'running';",
},
}
)
name = r.schema.Orm().Query().Driver()
if driver, ok := drivers[name]; ok {
var versionResult queryResult
if err = r.schema.Orm().Query().Raw(driver.versionQuery).Scan(&versionResult); err == nil {
version = versionResult.Value
if strings.Contains(version, "MariaDB") {
name = "MariaDB"
version = strings.Trim(version[:strings.Index(version, "MariaDB")], "-")
}
if len(driver.openConnectionsQuery) > 0 {
var openConnectionsResult queryResult
if err = r.schema.Orm().Query().Raw(driver.openConnectionsQuery).Scan(&openConnectionsResult); err == nil {
openConnections = openConnectionsResult.Value
}
}
}
func (r *ShowCommand) getDataBaseInfo() (name, version string, openConnections int, err error) {
name = r.schema.Orm().Name()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database names should be PostgreSQL, MySQL, MariaDB, SQL Server, and SQLite, not lowercase driver name like postgres, mysql, sqlite, or sqlserver

version = r.schema.Orm().Version()

db, err := r.schema.Orm().DB()
if err != nil {
return
}

openConnections = db.Stats().OpenConnections

return
}

Expand All @@ -157,10 +123,10 @@ func (r *ShowCommand) display(ctx console.Context, info databaseInfo) {
ctx.TwoColumnDetail(fmt.Sprintf("<fg=green;op=bold>%s</>", info.Name), info.Version)
ctx.TwoColumnDetail("Database", info.Database)
ctx.TwoColumnDetail("Host", info.Host)
ctx.TwoColumnDetail("Port", info.Port)
ctx.TwoColumnDetail("Port", cast.ToString(info.Port))
ctx.TwoColumnDetail("Username", info.Username)
ctx.TwoColumnDetail("Open Connections", info.OpenConnections)
ctx.TwoColumnDetail("Tables", fmt.Sprintf("%d", len(info.Tables)))
ctx.TwoColumnDetail("Open Connections", cast.ToString(info.OpenConnections))
ctx.TwoColumnDetail("Tables", cast.ToString(len(info.Tables)))
if size := func() (size int) {
for i := range info.Tables {
size += info.Tables[i].Size
Expand Down
101 changes: 55 additions & 46 deletions database/console/show_command_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package console

import (
"database/sql"
"testing"

"github.com/stretchr/testify/assert"

"github.com/goravel/framework/contracts/database"
"github.com/goravel/framework/contracts/database/schema"
mocksconfig "github.com/goravel/framework/mocks/config"
mocksconsole "github.com/goravel/framework/mocks/console"
Expand All @@ -29,16 +31,16 @@ func TestShowCommand(t *testing.T) {
mockQuery = mocksorm.NewQuery(t)
}
successCaseExpected := [][2]string{
{"<fg=green;op=bold>MariaDB</>", "test-version"},
{"<fg=green;op=bold>test</>", "version"},
{"Database", "db"},
{"Host", "host"},
{"Port", "port"},
{"Port", "1234"},
{"Username", "username"},
{"Open Connections", "2"},
{"Open Connections", "0"},
{"Tables", "1"},
{"Total Size", "0.000 MB"},
{"Total Size", "0.001 MB"},
{"<fg=green;op=bold>Tables</>", "<fg=yellow;op=bold>Size (MB)</>"},
{"test", "0.000"},
{"test", "0.001"},
{"<fg=green;op=bold>Views</>", "<fg=yellow;op=bold>Rows</>"},
{"test", "0"},
}
Expand All @@ -56,20 +58,25 @@ func TestShowCommand(t *testing.T) {
{
name: "get tables failed",
setup: func() {
// Handle
mockContext.EXPECT().Argument(0).Return("").Once()
mockContext.EXPECT().Option("database").Return("test").Once()
mockSchema.EXPECT().Connection("test").Return(mockSchema).Once()
mockSchema.EXPECT().GetConnection().Return("test").Once()
mockConfig.EXPECT().GetString("database.connections.test.database").Return("db").Once()
mockConfig.EXPECT().GetString("database.connections.test.host").Return("host").Once()
mockConfig.EXPECT().GetString("database.connections.test.port").Return("port").Once()
mockConfig.EXPECT().GetString("database.connections.test.username").Return("username").Once()
mockOrm.EXPECT().Query().Return(mockQuery).Times(3)
mockSchema.EXPECT().Orm().Return(mockOrm).Once()
mockOrm.EXPECT().Config().Return(database.Config{
Database: "db",
Host: "host",
Port: 1234,
Username: "username",
}).Once()

// getDataBaseInfo
mockSchema.EXPECT().Orm().Return(mockOrm).Times(3)
mockQuery.EXPECT().Driver().Return("mysql").Once()
mockQuery.EXPECT().Raw("SELECT VERSION() AS value;").Return(mockQuery).Once()
mockQuery.EXPECT().Raw("SHOW status WHERE variable_name = 'threads_connected';").Return(mockQuery).Once()
mockQuery.EXPECT().Scan(&queryResult{}).Return(nil).Twice()
mockOrm.EXPECT().Name().Return("test").Once()
mockOrm.EXPECT().Version().Return("version").Once()
mockOrm.EXPECT().DB().Return(&sql.DB{}, nil).Once()

// Handle
mockSchema.EXPECT().GetTables().Return(nil, assert.AnError).Once()
mockContext.EXPECT().Error(assert.AnError.Error()).Once()
},
Expand All @@ -80,17 +87,21 @@ func TestShowCommand(t *testing.T) {
mockContext.EXPECT().Argument(0).Return("").Once()
mockContext.EXPECT().Option("database").Return("test").Once()
mockSchema.EXPECT().Connection("test").Return(mockSchema).Once()
mockSchema.EXPECT().GetConnection().Return("test").Once()
mockConfig.EXPECT().GetString("database.connections.test.database").Return("db").Once()
mockConfig.EXPECT().GetString("database.connections.test.host").Return("host").Once()
mockConfig.EXPECT().GetString("database.connections.test.port").Return("port").Once()
mockConfig.EXPECT().GetString("database.connections.test.username").Return("username").Once()
mockOrm.EXPECT().Query().Return(mockQuery).Times(3)
mockSchema.EXPECT().Orm().Return(mockOrm).Once()
mockOrm.EXPECT().Config().Return(database.Config{
Database: "db",
Host: "host",
Port: 1234,
Username: "username",
}).Once()

// getDataBaseInfo
mockSchema.EXPECT().Orm().Return(mockOrm).Times(3)
mockQuery.EXPECT().Driver().Return("mysql").Once()
mockQuery.EXPECT().Raw("SELECT VERSION() AS value;").Return(mockQuery).Once()
mockQuery.EXPECT().Raw("SHOW status WHERE variable_name = 'threads_connected';").Return(mockQuery).Once()
mockQuery.EXPECT().Scan(&queryResult{}).Return(nil).Twice()
mockOrm.EXPECT().Name().Return("test").Once()
mockOrm.EXPECT().Version().Return("version").Once()
mockOrm.EXPECT().DB().Return(&sql.DB{}, nil).Once()

// Handle
mockSchema.EXPECT().GetTables().Return(nil, nil).Once()
mockContext.EXPECT().OptionBool("views").Return(true).Once()
mockSchema.EXPECT().GetViews().Return(nil, assert.AnError).Once()
Expand All @@ -103,34 +114,32 @@ func TestShowCommand(t *testing.T) {
mockContext.EXPECT().Argument(0).Return("").Once()
mockContext.EXPECT().Option("database").Return("test").Once()
mockSchema.EXPECT().Connection("test").Return(mockSchema).Once()
mockSchema.EXPECT().GetConnection().Return("test").Once()
mockConfig.EXPECT().GetString("database.connections.test.database").Return("db").Once()
mockConfig.EXPECT().GetString("database.connections.test.host").Return("host").Once()
mockConfig.EXPECT().GetString("database.connections.test.port").Return("port").Once()
mockConfig.EXPECT().GetString("database.connections.test.username").Return("username").Once()
mockOrm.EXPECT().Query().Return(mockQuery).Times(4)
mockSchema.EXPECT().Orm().Return(mockOrm).Times(4)
mockQuery.EXPECT().Driver().Return("mysql").Once()
mockQuery.EXPECT().Raw("SELECT VERSION() AS value;").Return(mockQuery).Once()
mockQuery.EXPECT().Raw("SHOW status WHERE variable_name = 'threads_connected';").Return(mockQuery).Once()
mockQuery.EXPECT().Scan(&queryResult{}).Run(func(dest interface{}) {
if d, ok := dest.(*queryResult); ok {
d.Value = "test-version-MariaDB"
}
}).Return(nil).Once()
mockQuery.EXPECT().Scan(&queryResult{}).Run(func(dest interface{}) {
if d, ok := dest.(*queryResult); ok {
d.Value = "2"
}
}).Return(nil).Once()
mockSchema.EXPECT().Orm().Return(mockOrm).Once()
mockOrm.EXPECT().Config().Return(database.Config{
Database: "db",
Host: "host",
Port: 1234,
Username: "username",
}).Once()

// getDataBaseInfo
mockSchema.EXPECT().Orm().Return(mockOrm).Times(3)
mockOrm.EXPECT().Name().Return("test").Once()
mockOrm.EXPECT().Version().Return("version").Once()
mockOrm.EXPECT().DB().Return(&sql.DB{}, nil).Once()

// Handle
mockSchema.EXPECT().GetTables().Return([]schema.Table{
{Name: "test", Size: 100},
{Name: "test", Size: 1024},
}, nil).Once()
mockContext.EXPECT().OptionBool("views").Return(true).Once()
mockSchema.EXPECT().GetViews().Return([]schema.View{
{Name: "test"},
}, nil).Once()
mockSchema.EXPECT().Orm().Return(mockOrm).Once()
mockOrm.EXPECT().Query().Return(mockQuery).Once()
mockQuery.EXPECT().Table("test").Return(mockQuery).Once()

var rows int64
mockQuery.EXPECT().Count(&rows).Return(nil).Once()
mockContext.EXPECT().NewLine().Times(4)
Expand Down
Loading
Loading