good morning!!!!

Skip to content
Snippets Groups Projects
Commit 49d6a341 authored by José Carlos Nieto's avatar José Carlos Nieto
Browse files

MySQL: Applying speed changes from PostgreSQL adapter.

parent 33675c1e
No related branches found
No related tags found
No related merge requests found
TEST_HOST ?= 127.0.0.1
build:
go build && go install
reset-db:
$(MAKE) -C _dumps
test: reset-db
go test -v
$(MAKE) -C _example
bench: reset-db
go test -v -test.bench=. -test.benchtime=10s -benchmem
TEST_HOST ?= 127.0.0.1
TEST_PORT ?= 3306
DB_USERNAME ?= upperio_tests
DB_PASSWORD ?= upperio_secret
DB_NAME ?= upperio_tests
test:
cat example.sql | mysql -u"$(DB_USERNAME)" -p"$(DB_PASSWORD)" -h"$(TEST_HOST)" -P$(TEST_PORT) "$(DB_NAME)"
go run -v main.go
......@@ -3,6 +3,7 @@ package main
import (
"fmt"
"log"
"os"
"time"
"upper.io/db" // Imports the main db package.
......@@ -10,10 +11,10 @@ import (
)
var settings = db.Settings{
Database: `upperio_tests`, // Database name
Host: `testserver.local`,
User: `upperio`, // Database username.
Password: `upperio`, // Database password.
Database: `upperio_tests`, // Database name.
Host: `127.0.0.1`,
User: `upperio_tests`, // Database username.
Password: `upperio_secret`, // Database password.
}
// Birthday struct example.
......@@ -26,6 +27,10 @@ type Birthday struct {
func main() {
if os.Getenv("TEST_HOST") != "" {
settings.Host = os.Getenv("TEST_HOST")
}
// Attemping to establish a connection to the database.
sess, err := db.Open("mysql", settings)
......
......@@ -46,7 +46,7 @@ func (t *table) Find(terms ...interface{}) db.Result {
// Truncate deletes all rows from the table.
func (t *table) Truncate() error {
_, err := t.database.Exec(sqlgen.Statement{
_, err := t.database.Exec(&sqlgen.Statement{
Type: sqlgen.Truncate,
Table: sqlgen.TableWithName(t.MainTableName()),
})
......@@ -80,7 +80,7 @@ func (t *table) Append(item interface{}) (interface{}, error) {
}
}
stmt := sqlgen.Statement{
stmt := &sqlgen.Statement{
Type: sqlgen.Insert,
Table: sqlgen.TableWithName(t.MainTableName()),
Columns: sqlgenCols,
......
......@@ -28,6 +28,7 @@ import (
_ "github.com/go-sql-driver/mysql" // MySQL driver.
"github.com/jmoiron/sqlx"
"upper.io/cache"
"upper.io/db"
"upper.io/db/util/schema"
"upper.io/db/util/sqlgen"
......@@ -40,10 +41,11 @@ var (
)
type database struct {
connURL db.ConnectionURL
session *sqlx.DB
tx *sqltx.Tx
schema *schema.DatabaseSchema
connURL db.ConnectionURL
session *sqlx.DB
tx *sqltx.Tx
schema *schema.DatabaseSchema
cachedStatements *cache.Cache
}
type tx struct {
......@@ -51,6 +53,11 @@ type tx struct {
*database
}
type cachedStatement struct {
*sqlx.Stmt
query string
}
var (
_ = db.Database(&database{})
_ = db.Tx(&tx{})
......@@ -60,6 +67,40 @@ type columnSchemaT struct {
Name string `db:"column_name"`
}
func (d *database) prepareStatement(stmt *sqlgen.Statement) (p *sqlx.Stmt, query string, err error) {
if d.session == nil {
return nil, "", db.ErrNotConnected
}
pc, ok := d.cachedStatements.ReadRaw(stmt)
if ok {
ps := pc.(*cachedStatement)
p = ps.Stmt
query = ps.query
} else {
query = compileAndReplacePlaceholders(stmt)
if d.tx != nil {
p, err = d.tx.Preparex(query)
} else {
p, err = d.session.Preparex(query)
}
if err != nil {
return nil, "", err
}
d.cachedStatements.Write(stmt, &cachedStatement{p, query})
}
return p, query, nil
}
func compileAndReplacePlaceholders(stmt *sqlgen.Statement) string {
return stmt.Compile(template.Template)
}
// Driver returns the underlying *sqlx.DB instance.
func (d *database) Driver() interface{} {
return d.session
......@@ -89,6 +130,11 @@ func (d *database) Open() error {
conn.Options["charset"] = "utf8"
}
// Connection charset, UTF-8 by default.
if conn.Options["parseTime"] == "" {
conn.Options["parseTime"] = "true"
}
if settings.Socket != "" {
conn.Address = db.Socket(settings.Socket)
} else {
......@@ -111,8 +157,12 @@ func (d *database) Open() error {
d.session.Mapper = sqlutil.NewMapper()
if err = d.populateSchema(); err != nil {
return err
d.cachedStatements = cache.NewCache()
if d.schema == nil {
if err = d.populateSchema(); err != nil {
return err
}
}
return nil
......@@ -125,14 +175,13 @@ func (d *database) Clone() (db.Database, error) {
}
func (d *database) clone() (*database, error) {
src := &database{}
src.Setup(d.connURL)
if err := src.Open(); err != nil {
clone := &database{
schema: d.schema,
}
if err := clone.Setup(d.connURL); err != nil {
return nil, err
}
return src, nil
return clone, nil
}
// Ping checks whether a connection to the database is still alive by pinging
......@@ -144,6 +193,7 @@ func (d *database) Ping() error {
// Close terminates the current database session.
func (d *database) Close() error {
if d.session != nil {
d.cachedStatements.Clear()
return d.session.Close()
}
return nil
......@@ -199,7 +249,7 @@ func (d *database) Collections() (collections []string, err error) {
return d.schema.Tables, nil
}
stmt := sqlgen.Statement{
stmt := &sqlgen.Statement{
Type: sqlgen.Select,
Columns: sqlgen.JoinColumns(
sqlgen.ColumnWithName(`table_name`),
......@@ -220,8 +270,6 @@ func (d *database) Collections() (collections []string, err error) {
return nil, err
}
defer rows.Close()
collections = []string{}
var name string
......@@ -229,6 +277,7 @@ func (d *database) Collections() (collections []string, err error) {
for rows.Next() {
// Getting table name.
if err = rows.Scan(&name); err != nil {
rows.Close()
return nil, err
}
......@@ -243,24 +292,26 @@ func (d *database) Collections() (collections []string, err error) {
}
// Use changes the active database.
func (d *database) Use(database string) (err error) {
func (d *database) Use(name string) (err error) {
var conn ConnectionURL
if conn, err = ParseURL(d.connURL.String()); err != nil {
return err
}
conn.Database = database
conn.Database = name
d.connURL = conn
d.schema = nil
return d.Open()
}
// Drop removes all tables from the current database.
func (d *database) Drop() error {
_, err := d.Query(sqlgen.Statement{
_, err := d.Query(&sqlgen.Statement{
Type: sqlgen.DropDatabase,
Database: sqlgen.DatabaseWithName(d.schema.Name),
})
......@@ -283,8 +334,8 @@ func (d *database) Name() string {
// be used to issue transactional queries.
func (d *database) Transaction() (db.Tx, error) {
var err error
var clone *database
var sqlTx *sqlx.Tx
var clone *database
if clone, err = d.clone(); err != nil {
return nil, err
......@@ -300,90 +351,72 @@ func (d *database) Transaction() (db.Tx, error) {
}
// Exec compiles and executes a statement that does not return any rows.
func (d *database) Exec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) {
func (d *database) Exec(stmt *sqlgen.Statement, args ...interface{}) (sql.Result, error) {
var query string
var res sql.Result
var p *sqlx.Stmt
var err error
var start, end int64
start = time.Now().UnixNano()
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
if db.Debug {
var start, end int64
start = time.Now().UnixNano()
if d.session == nil {
return nil, db.ErrNotConnected
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
}
query = stmt.Compile(template.Template)
if d.tx != nil {
res, err = d.tx.Exec(query, args...)
} else {
res, err = d.session.Exec(query, args...)
if p, query, err = d.prepareStatement(stmt); err != nil {
return nil, err
}
return res, err
return p.Exec(args...)
}
// Query compiles and executes a statement that returns rows.
func (d *database) Query(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
var rows *sqlx.Rows
func (d *database) Query(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
var query string
var p *sqlx.Stmt
var err error
var start, end int64
start = time.Now().UnixNano()
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
if db.Debug {
var start, end int64
start = time.Now().UnixNano()
if d.session == nil {
return nil, db.ErrNotConnected
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
}
query = stmt.Compile(template.Template)
if d.tx != nil {
rows, err = d.tx.Queryx(query, args...)
} else {
rows, err = d.session.Queryx(query, args...)
if p, query, err = d.prepareStatement(stmt); err != nil {
return nil, err
}
return rows, err
return p.Queryx(args...)
}
// QueryRow compiles and executes a statement that returns at most one row.
func (d *database) QueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
func (d *database) QueryRow(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
var query string
var row *sqlx.Row
var p *sqlx.Stmt
var err error
var start, end int64
start = time.Now().UnixNano()
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
if db.Debug {
var start, end int64
start = time.Now().UnixNano()
if d.session == nil {
return nil, db.ErrNotConnected
defer func() {
end = time.Now().UnixNano()
sqlutil.Log(query, args, err, start, end)
}()
}
query = stmt.Compile(template.Template)
if d.tx != nil {
row = d.tx.QueryRowx(query, args...)
} else {
row = d.session.QueryRowx(query, args...)
if p, query, err = d.prepareStatement(stmt); err != nil {
return nil, err
}
return row, err
return p.QueryRowx(args...), nil
}
// populateSchema looks up for the table info in the database and populates its
......@@ -394,7 +427,7 @@ func (d *database) populateSchema() (err error) {
d.schema = schema.NewDatabaseSchema()
// Get database name.
stmt := sqlgen.Statement{
stmt := &sqlgen.Statement{
Type: sqlgen.Select,
Columns: sqlgen.JoinColumns(
sqlgen.RawValue(`DATABASE()`),
......@@ -427,7 +460,7 @@ func (d *database) populateSchema() (err error) {
}
func (d *database) tableExists(names ...string) error {
var stmt sqlgen.Statement
var stmt *sqlgen.Statement
var err error
var rows *sqlx.Rows
......@@ -438,7 +471,7 @@ func (d *database) tableExists(names ...string) error {
continue
}
stmt = sqlgen.Statement{
stmt = &sqlgen.Statement{
Type: sqlgen.Select,
Table: sqlgen.TableWithName(`information_schema.tables`),
Columns: sqlgen.JoinColumns(
......@@ -462,9 +495,8 @@ func (d *database) tableExists(names ...string) error {
return db.ErrCollectionDoesNotExist
}
defer rows.Close()
if rows.Next() == false {
if !rows.Next() {
rows.Close()
return db.ErrCollectionDoesNotExist
}
}
......@@ -481,7 +513,7 @@ func (d *database) tableColumns(tableName string) ([]string, error) {
return tableSchema.Columns, nil
}
stmt := sqlgen.Statement{
stmt := &sqlgen.Statement{
Type: sqlgen.Select,
Table: sqlgen.TableWithName(`information_schema.columns`),
Columns: sqlgen.JoinColumns(
......@@ -532,7 +564,7 @@ func (d *database) getPrimaryKey(tableName string) ([]string, error) {
return tableSchema.PrimaryKey, nil
}
stmt := sqlgen.Statement{
stmt := &sqlgen.Statement{
Type: sqlgen.Select,
Table: sqlgen.RawValue(`
information_schema.table_constraints AS t
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment