From 9926fc5d0d7b67ce18bc7cc091fdd284c48fad0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net> Date: Thu, 28 May 2015 05:20:06 -0500 Subject: [PATCH] Adapting MySQL adapter to newer sqlgen. --- mysql/collection.go | 228 ++++-------------- mysql/database.go | 396 +++++++++++++++---------------- mysql/mysql.go | 69 ++++++ mysql/result.go | 308 ------------------------ mysql/{layout.go => template.go} | 62 +++-- mysql/tx.go | 44 ---- 6 files changed, 330 insertions(+), 777 deletions(-) create mode 100644 mysql/mysql.go delete mode 100644 mysql/result.go rename mysql/{layout.go => template.go} (64%) delete mode 100644 mysql/tx.go diff --git a/mysql/collection.go b/mysql/collection.go index fb1ee860..8e8cf484 100644 --- a/mysql/collection.go +++ b/mysql/collection.go @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam +// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -23,177 +23,26 @@ package mysql import ( "database/sql" - "fmt" - "reflect" "strings" "upper.io/db" "upper.io/db/util/sqlgen" "upper.io/db/util/sqlutil" + "upper.io/db/util/sqlutil/result" ) -const defaultOperator = `=` - type table struct { sqlutil.T - source *source - names []string -} - -func whereValues(term interface{}) (where sqlgen.Where, args []interface{}) { - - args = []interface{}{} - - switch t := term.(type) { - case []interface{}: - l := len(t) - where = make(sqlgen.Where, 0, l) - for _, cond := range t { - w, v := whereValues(cond) - args = append(args, v...) - where = append(where, w...) - } - case db.And: - and := make(sqlgen.And, 0, len(t)) - for _, cond := range t { - k, v := whereValues(cond) - args = append(args, v...) - and = append(and, k...) - } - where = append(where, and) - case db.Or: - or := make(sqlgen.Or, 0, len(t)) - for _, cond := range t { - k, v := whereValues(cond) - args = append(args, v...) - or = append(or, k...) - } - where = append(where, or) - case db.Raw: - if s, ok := t.Value.(string); ok == true { - where = append(where, sqlgen.Raw{s}) - } - case db.Cond: - k, v := conditionValues(t) - args = append(args, v...) - for _, kk := range k { - where = append(where, kk) - } - case db.Constrainer: - k, v := conditionValues(t.Constraint()) - args = append(args, v...) - for _, kk := range k { - where = append(where, kk) - } - default: - panic(fmt.Sprintf(db.ErrUnknownConditionType.Error(), reflect.TypeOf(t))) - } - - return where, args -} - -func interfaceArgs(value interface{}) (args []interface{}) { - if value == nil { - return nil - } - - v := reflect.ValueOf(value) - - switch v.Type().Kind() { - case reflect.Slice: - var i, total int - - total = v.Len() - if total > 0 { - args = make([]interface{}, total) - - for i = 0; i < total; i++ { - args[i] = v.Index(i).Interface() - } - - return args - } - return nil - default: - args = []interface{}{value} - } - - return args -} - -func conditionValues(cond db.Cond) (columnValues sqlgen.ColumnValues, args []interface{}) { - args = []interface{}{} - - for column, value := range cond { - var columnValue sqlgen.ColumnValue - - // Guessing operator from input, or using a default one. - column := strings.TrimSpace(column) - chunks := strings.SplitN(column, ` `, 2) - - columnValue.Column = sqlgen.Column{chunks[0]} - - if len(chunks) > 1 { - columnValue.Operator = chunks[1] - } else { - columnValue.Operator = defaultOperator - } - - switch value := value.(type) { - case db.Func: - // Catches functions. - v := interfaceArgs(value.Args) - columnValue.Operator = value.Name - - if v == nil { - // A function with no arguments. - columnValue.Value = sqlgen.Value{sqlgen.Raw{`()`}} - } else { - // A function with one or more arguments. - columnValue.Value = sqlgen.Value{sqlgen.Raw{fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))}} - } - - args = append(args, v...) - default: - // Catches everything else. - v := interfaceArgs(value) - l := len(v) - if v == nil || l == 0 { - // Nil value given. - columnValue.Value = sqlgen.Value{sqlgen.Raw{`NULL`}} - } else { - if l > 1 { - // Array value given. - columnValue.Value = sqlgen.Value{sqlgen.Raw{fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))}} - } else { - // Single value given. - columnValue.Value = sqlPlaceholder - } - args = append(args, v...) - } - } - - columnValues = append(columnValues, columnValue) - } - - return columnValues, args + *source + names []string } -func (c *table) Find(terms ...interface{}) db.Result { - where, arguments := whereValues(terms) - - result := &result{ - table: c, - where: where, - arguments: arguments, - } +var _ = db.Collection(&table{}) - return result -} - -func (c *table) tableN(i int) string { - if len(c.names) > i { - chunks := strings.SplitN(c.names[i], " ", 2) +// tableN returns the nth name provided to the table. +func (t *table) tableN(i int) string { + if len(t.names) > i { + chunks := strings.SplitN(t.names[i], " ", 2) if len(chunks) > 0 { return chunks[0] } @@ -201,55 +50,64 @@ func (c *table) tableN(i int) string { return "" } -// Deletes all the rows within the collection. -func (c *table) Truncate() error { +// Find creates a result set with the given conditions. +func (t *table) Find(terms ...interface{}) db.Result { + where, arguments := sqlutil.ToWhereWithArguments(terms) + return result.NewResult(t, where, arguments) +} - _, err := c.source.doExec(sqlgen.Statement{ - Type: sqlgen.SqlTruncate, - Table: sqlgen.Table{c.tableN(0)}, +// Truncate deletes all rows from the table. +func (t *table) Truncate() error { + _, err := t.source.Exec(sqlgen.Statement{ + Type: sqlgen.Truncate, + Table: sqlgen.TableWithName(t.tableN(0)), }) if err != nil { return err } - return nil } // Appends an item (map or struct) into the collection. -func (c *table) Append(item interface{}) (interface{}, error) { - +func (t *table) Append(item interface{}) (interface{}, error) { var pKey []string - var columns sqlgen.Columns - var values sqlgen.Values - var arguments []interface{} - cols, vals, err := c.FieldValues(item) + cols, vals, err := t.FieldValues(item) if err != nil { return nil, err } - columns = make(sqlgen.Columns, 0, len(cols)) + columns := new(sqlgen.Columns) + + columns.Columns = make([]sqlgen.Fragment, 0, len(cols)) for i := range cols { - columns = append(columns, sqlgen.Column{cols[i]}) + columns.Columns = append(columns.Columns, sqlgen.ColumnWithName(cols[i])) } + values := new(sqlgen.Values) + var arguments []interface{} + arguments = make([]interface{}, 0, len(vals)) - values = make(sqlgen.Values, 0, len(vals)) + values.Values = make([]sqlgen.Fragment, 0, len(vals)) + for i := range vals { switch v := vals[i].(type) { + case *sqlgen.Value: + // Adding value. + values.Values = append(values.Values, v) case sqlgen.Value: // Adding value. - values = append(values, v) + values.Values = append(values.Values, &v) default: // Adding both value and placeholder. - values = append(values, sqlPlaceholder) + values.Values = append(values.Values, sqlPlaceholder) arguments = append(arguments, v) } } - if pKey, err = c.source.getPrimaryKey(c.tableN(0)); err != nil { + if pKey, err = t.source.getPrimaryKey(t.tableN(0)); err != nil { if err != sql.ErrNoRows { // Can't tell primary key. return nil, err @@ -257,14 +115,14 @@ func (c *table) Append(item interface{}) (interface{}, error) { } stmt := sqlgen.Statement{ - Type: sqlgen.SqlInsert, - Table: sqlgen.Table{c.tableN(0)}, + Type: sqlgen.Insert, + Table: sqlgen.TableWithName(t.tableN(0)), Columns: columns, Values: values, } var res sql.Result - if res, err = c.source.doExec(stmt, arguments...); err != nil { + if res, err = t.source.Exec(stmt, arguments...); err != nil { return nil, err } @@ -318,13 +176,13 @@ func (c *table) Append(item interface{}) (interface{}, error) { } // Returns true if the collection exists. -func (c *table) Exists() bool { - if err := c.source.tableExists(c.names...); err != nil { +func (t *table) Exists() bool { + if err := t.source.tableExists(t.names...); err != nil { return false } return true } -func (c *table) Name() string { - return strings.Join(c.names, `, `) +func (t *table) Name() string { + return strings.Join(t.names, `, `) } diff --git a/mysql/database.go b/mysql/database.go index 97bf5038..84bd8533 100644 --- a/mysql/database.go +++ b/mysql/database.go @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam +// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -23,89 +23,49 @@ package mysql import ( "database/sql" - "os" "strings" "time" - // Importing MySQL driver. - _ "github.com/go-sql-driver/mysql" + _ "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" "upper.io/db/util/sqlutil" + "upper.io/db/util/sqlutil/tx" ) -const ( - // Adapter is the public name of the adapter. - Adapter = `mysql` -) - -var template *sqlgen.Template - var ( - sqlPlaceholder = sqlgen.Value{sqlgen.Raw{`?`}} + sqlPlaceholder = sqlgen.RawValue(`?`) ) type source struct { connURL db.ConnectionURL session *sqlx.DB - tx *tx + tx *sqltx.Tx schema *schema.DatabaseSchema } -type columnSchemaT struct { - Name string `db:"column_name"` -} - -// Returns the string name of the database. -func (s *source) Name() string { - return s.schema.Name -} - -// Ping verifies a connection to the database is still alive, -// establishing a connection if necessary. -func (s *source) Ping() error { - return s.session.Ping() +type tx struct { + *sqltx.Tx + *source } -func (s *source) Clone() (db.Database, error) { - return s.clone() -} - -func (s *source) Transaction() (db.Tx, error) { - var err error - var clone *source - var sqlTx *sqlx.Tx - - if sqlTx, err = s.session.Beginx(); err != nil { - return nil, err - } - - if clone, err = s.clone(); err != nil { - return nil, err - } - - tx := &tx{source: clone, sqlTx: sqlTx} - - clone.tx = tx - - return tx, nil -} +var ( + _ = db.Database(&source{}) + _ = db.Tx(&tx{}) +) -// Stores database settings. -func (s *source) Setup(connURL db.ConnectionURL) error { - s.connURL = connURL - return s.Open() +type columnSchemaT struct { + Name string `db:"column_name"` } -// Returns the underlying *sqlx.DB instance. +// Driver returns the underlying *sqlx.DB instance. func (s *source) Driver() interface{} { return s.session } -// Attempts to connect to a database using the stored settings. +// Open attempts to connect to the database server using already stored settings. func (s *source) Open() error { var err error @@ -158,30 +118,38 @@ func (s *source) Open() error { return nil } -// Closes the current database session. -func (s *source) Close() error { - if s.session != nil { - return s.session.Close() - } - return nil +// Clone returns a cloned db.Database session, this is typically used for +// transactions. +func (s *source) Clone() (db.Database, error) { + return s.clone() } -// Changes the active database. -func (s *source) Use(database string) (err error) { - var conn ConnectionURL +func (s *source) clone() (*source, error) { + src := &source{} + src.Setup(s.connURL) - if conn, err = ParseURL(s.connURL.String()); err != nil { - return err + if err := src.Open(); err != nil { + return nil, err } - conn.Database = database + return src, nil +} - s.connURL = conn +// Ping checks whether a connection to the database is still alive by pinging +// it, establishing a connection if necessary. +func (s *source) Ping() error { + return s.session.Ping() +} - return s.Open() +// Close terminates the current database session. +func (s *source) Close() error { + if s.session != nil { + return s.session.Close() + } + return nil } -// Returns a collection instance by name. +// Collection returns a table by name. func (s *source) Collection(names ...string) (db.Collection, error) { var err error @@ -190,7 +158,7 @@ func (s *source) Collection(names ...string) (db.Collection, error) { } if s.tx != nil { - if s.tx.done { + if s.tx.Done() { return nil, sql.ErrTxDone } } @@ -221,19 +189,7 @@ func (s *source) Collection(names ...string) (db.Collection, error) { return col, nil } -// Drops the currently active database. -func (s *source) Drop() error { - - _, err := s.doQuery(sqlgen.Statement{ - Type: sqlgen.SqlDropDatabase, - Database: sqlgen.Database{s.schema.Name}, - }) - - return err -} - -// Collections() Returns a list of non-system tables/collections contained -// within the currently active database. +// Collections returns a list of non-system tables from the database. func (s *source) Collections() (collections []string, err error) { tablesInSchema := len(s.schema.Tables) @@ -245,25 +201,23 @@ func (s *source) Collections() (collections []string, err error) { } stmt := sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Columns: sqlgen.Columns{ - {`table_name`}, - }, - Table: sqlgen.Table{ - `information_schema.tables`, - }, - Where: sqlgen.Where{ - sqlgen.ColumnValue{ - sqlgen.Column{`table_schema`}, - `=`, - sqlPlaceholder, + Type: sqlgen.Select, + Columns: sqlgen.JoinColumns( + sqlgen.ColumnWithName(`table_name`), + ), + Table: sqlgen.TableWithName(`information_schema.tables`), + Where: sqlgen.WhereConditions( + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`table_schema`), + Operator: `=`, + Value: sqlPlaceholder, }, - }, + ), } // Executing statement. var rows *sqlx.Rows - if rows, err = s.doQuery(stmt, s.schema.Name); err != nil { + if rows, err = s.Query(stmt, s.schema.Name); err != nil { return nil, err } @@ -289,25 +243,65 @@ func (s *source) Collections() (collections []string, err error) { return collections, nil } -func (s *source) clone() (*source, error) { - src := &source{} - src.Setup(s.connURL) +// Use changes the active database. +func (s *source) Use(database string) (err error) { + var conn ConnectionURL - if err := src.Open(); err != nil { - return nil, err + if conn, err = ParseURL(s.connURL.String()); err != nil { + return err } - return src, nil + conn.Database = database + + s.connURL = conn + + return s.Open() +} + +// Drop removes all tables from the current database. +func (s *source) Drop() error { + + _, err := s.Query(sqlgen.Statement{ + Type: sqlgen.DropDatabase, + Database: sqlgen.DatabaseWithName(s.schema.Name), + }) + + return err +} + +// Setup stores database settings. +func (s *source) Setup(connURL db.ConnectionURL) error { + s.connURL = connURL + return s.Open() +} + +// Name returns the name of the database. +func (s *source) Name() string { + return s.schema.Name } -func debugLog(query string, args []interface{}, err error, start int64, end int64) { - if debugEnabled() == true { - d := sqlutil.Debug{query, args, err, start, end} - d.Print() +// Transaction starts a transaction block and returns a db.Tx struct that can +// be used to issue transactional queries. +func (s *source) Transaction() (db.Tx, error) { + var err error + var clone *source + var sqlTx *sqlx.Tx + + if sqlTx, err = s.session.Beginx(); err != nil { + return nil, err + } + + if clone, err = s.clone(); err != nil { + return nil, err } + + clone.tx = sqltx.New(sqlTx) + + return tx{Tx: clone.tx, source: clone}, nil } -func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) { +// Exec compiles and executes a statement that does not return any rows. +func (s *source) Exec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) { var query string var res sql.Result var err error @@ -317,7 +311,7 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, defer func() { end = time.Now().UnixNano() - debugLog(query, args, err, start, end) + sqlutil.Log(query, args, err, start, end) }() if s.session == nil { @@ -327,7 +321,7 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, query = stmt.Compile(template) if s.tx != nil { - res, err = s.tx.sqlTx.Exec(query, args...) + res, err = s.tx.Exec(query, args...) } else { res, err = s.session.Exec(query, args...) } @@ -335,7 +329,8 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, return res, err } -func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) { +// Query compiles and executes a statement that returns rows. +func (s *source) Query(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) { var rows *sqlx.Rows var query string var err error @@ -345,7 +340,7 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows defer func() { end = time.Now().UnixNano() - debugLog(query, args, err, start, end) + sqlutil.Log(query, args, err, start, end) }() if s.session == nil { @@ -355,7 +350,7 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows query = stmt.Compile(template) if s.tx != nil { - rows, err = s.tx.sqlTx.Queryx(query, args...) + rows, err = s.tx.Queryx(query, args...) } else { rows, err = s.session.Queryx(query, args...) } @@ -363,7 +358,8 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows return rows, err } -func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) { +// QueryRow compiles and executes a statement that returns at most one row. +func (s *source) QueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) { var query string var row *sqlx.Row var err error @@ -373,7 +369,7 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R defer func() { end = time.Now().UnixNano() - debugLog(query, args, err, start, end) + sqlutil.Log(query, args, err, start, end) }() if s.session == nil { @@ -383,7 +379,7 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R query = stmt.Compile(template) if s.tx != nil { - row = s.tx.sqlTx.QueryRowx(query, args...) + row = s.tx.QueryRowx(query, args...) } else { row = s.session.QueryRowx(query, args...) } @@ -391,13 +387,8 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R return row, err } -func debugEnabled() bool { - if os.Getenv(db.EnvEnableDebug) != "" { - return true - } - return false -} - +// populateSchema looks up for the table info in the database and populates its +// schema for internal use. func (s *source) populateSchema() (err error) { var collections []string @@ -405,15 +396,15 @@ func (s *source) populateSchema() (err error) { // Get database name. stmt := sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Columns: sqlgen.Columns{ - {sqlgen.Raw{`DATABASE()`}}, - }, + Type: sqlgen.Select, + Columns: sqlgen.JoinColumns( + sqlgen.RawValue(`DATABASE()`), + ), } var row *sqlx.Row - if row, err = s.doQueryRow(stmt); err != nil { + if row, err = s.QueryRow(stmt); err != nil { return err } @@ -449,18 +440,26 @@ func (s *source) tableExists(names ...string) error { } stmt = sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Table: sqlgen.Table{`information_schema.tables`}, - Columns: sqlgen.Columns{ - {`table_name`}, - }, - Where: sqlgen.Where{ - sqlgen.ColumnValue{sqlgen.Column{`table_schema`}, `=`, sqlPlaceholder}, - sqlgen.ColumnValue{sqlgen.Column{`table_name`}, `=`, sqlPlaceholder}, - }, + Type: sqlgen.Select, + Table: sqlgen.TableWithName(`information_schema.tables`), + Columns: sqlgen.JoinColumns( + sqlgen.ColumnWithName(`table_name`), + ), + Where: sqlgen.WhereConditions( + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`table_schema`), + Operator: `=`, + Value: sqlPlaceholder, + }, + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`table_name`), + Operator: `=`, + Value: sqlPlaceholder, + }, + ), } - if rows, err = s.doQuery(stmt, s.schema.Name, names[i]); err != nil { + if rows, err = s.Query(stmt, s.schema.Name, names[i]); err != nil { return db.ErrCollectionDoesNotExist } @@ -484,22 +483,30 @@ func (s *source) tableColumns(tableName string) ([]string, error) { } stmt := sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Table: sqlgen.Table{`information_schema.columns`}, - Columns: sqlgen.Columns{ - {`column_name`}, - {`data_type`}, - }, - Where: sqlgen.Where{ - sqlgen.ColumnValue{sqlgen.Column{`table_schema`}, `=`, sqlPlaceholder}, - sqlgen.ColumnValue{sqlgen.Column{`table_name`}, `=`, sqlPlaceholder}, - }, + Type: sqlgen.Select, + Table: sqlgen.TableWithName(`information_schema.columns`), + Columns: sqlgen.JoinColumns( + sqlgen.ColumnWithName(`column_name`), + sqlgen.ColumnWithName(`data_type`), + ), + Where: sqlgen.WhereConditions( + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`table_schema`), + Operator: `=`, + Value: sqlPlaceholder, + }, + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`table_name`), + Operator: `=`, + Value: sqlPlaceholder, + }, + ), } var rows *sqlx.Rows var err error - if rows, err = s.doQuery(stmt, s.schema.Name, tableName); err != nil { + if rows, err = s.Query(stmt, s.schema.Name, tableName); err != nil { return nil, err } @@ -527,36 +534,46 @@ func (s *source) getPrimaryKey(tableName string) ([]string, error) { } stmt := sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Table: sqlgen.Table{ - sqlgen.Raw{` + Type: sqlgen.Select, + Table: sqlgen.RawValue(` information_schema.table_constraints AS t JOIN information_schema.key_column_usage k USING(constraint_name, table_schema, table_name) - `}, - }, - Columns: sqlgen.Columns{ - {`k.column_name`}, - }, - Where: sqlgen.Where{ - sqlgen.ColumnValue{sqlgen.Column{`t.constraint_type`}, `=`, sqlgen.Value{`primary key`}}, - sqlgen.ColumnValue{sqlgen.Column{`t.table_schema`}, `=`, sqlPlaceholder}, - sqlgen.ColumnValue{sqlgen.Column{`t.table_name`}, `=`, sqlPlaceholder}, - }, - OrderBy: sqlgen.OrderBy{ - sqlgen.SortColumns{ - { - sqlgen.Column{`k.ordinal_position`}, - sqlgen.SqlSortAsc, - }, + `), + Columns: sqlgen.JoinColumns( + sqlgen.ColumnWithName(`k.column_name`), + ), + Where: sqlgen.WhereConditions( + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`t.constraint_type`), + Operator: `=`, + Value: sqlgen.NewValue(`primary key`), + }, + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`t.table_schema`), + Operator: `=`, + Value: sqlPlaceholder, }, + &sqlgen.ColumnValue{ + Column: sqlgen.ColumnWithName(`t.table_name`), + Operator: `=`, + Value: sqlPlaceholder, + }, + ), + OrderBy: &sqlgen.OrderBy{ + SortColumns: sqlgen.JoinSortColumns( + &sqlgen.SortColumn{ + Column: sqlgen.ColumnWithName(`k.ordinal_position`), + Order: sqlgen.Ascendent, + }, + ), }, } var rows *sqlx.Rows var err error - if rows, err = s.doQuery(stmt, s.schema.Name, tableName); err != nil { + if rows, err = s.Query(stmt, s.schema.Name, tableName); err != nil { return nil, err } @@ -572,40 +589,3 @@ func (s *source) getPrimaryKey(tableName string) ([]string, error) { return tableSchema.PrimaryKey, nil } - -func init() { - - template = &sqlgen.Template{ - mysqlColumnSeparator, - mysqlIdentifierSeparator, - mysqlIdentifierQuote, - mysqlValueSeparator, - mysqlValueQuote, - mysqlAndKeyword, - mysqlOrKeyword, - mysqlNotKeyword, - mysqlDescKeyword, - mysqlAscKeyword, - mysqlDefaultOperator, - mysqlClauseGroup, - mysqlClauseOperator, - mysqlColumnValue, - mysqlTableAliasLayout, - mysqlColumnAliasLayout, - mysqlSortByColumnLayout, - mysqlWhereLayout, - mysqlOrderByLayout, - mysqlInsertLayout, - mysqlSelectLayout, - mysqlUpdateLayout, - mysqlDeleteLayout, - mysqlTruncateLayout, - mysqlDropDatabaseLayout, - mysqlDropTableLayout, - mysqlSelectCountLayout, - mysqlGroupByLayout, - cache.NewCache(), - } - - db.Register(Adapter, &source{}) -} diff --git a/mysql/mysql.go b/mysql/mysql.go new file mode 100644 index 00000000..58e2a9ee --- /dev/null +++ b/mysql/mysql.go @@ -0,0 +1,69 @@ +// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package mysql + +import ( + "upper.io/cache" + "upper.io/db" + "upper.io/db/util/sqlgen" +) + +// Adapter is the public name of the adapter. +const Adapter = `mysql` + +var template *sqlgen.Template + +func init() { + template = &sqlgen.Template{ + ColumnSeparator: adapterColumnSeparator, + IdentifierSeparator: adapterIdentifierSeparator, + IdentifierQuote: adapterIdentifierQuote, + ValueSeparator: adapterValueSeparator, + ValueQuote: adapterValueQuote, + AndKeyword: adapterAndKeyword, + OrKeyword: adapterOrKeyword, + NotKeyword: adapterNotKeyword, + DescKeyword: adapterDescKeyword, + AscKeyword: adapterAscKeyword, + DefaultOperator: adapterDefaultOperator, + ClauseGroup: adapterClauseGroup, + ClauseOperator: adapterClauseOperator, + ColumnValue: adapterColumnValue, + TableAliasLayout: adapterTableAliasLayout, + ColumnAliasLayout: adapterColumnAliasLayout, + SortByColumnLayout: adapterSortByColumnLayout, + WhereLayout: adapterWhereLayout, + OrderByLayout: adapterOrderByLayout, + InsertLayout: adapterInsertLayout, + SelectLayout: adapterSelectLayout, + UpdateLayout: adapterUpdateLayout, + DeleteLayout: adapterDeleteLayout, + TruncateLayout: adapterTruncateLayout, + DropDatabaseLayout: adapterDropDatabaseLayout, + DropTableLayout: adapterDropTableLayout, + CountLayout: adapterSelectCountLayout, + GroupByLayout: adapterGroupByLayout, + Cache: cache.NewCache(), + } + + db.Register(Adapter, &source{}) +} diff --git a/mysql/result.go b/mysql/result.go deleted file mode 100644 index b4fe80c8..00000000 --- a/mysql/result.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql - -import ( - "fmt" - "strings" - - "github.com/jmoiron/sqlx" - "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" -) - -type counter struct { - Total uint64 `db:"_t"` -} - -type result struct { - table *table - cursor *sqlx.Rows // This is the main query cursor. It starts as a nil value. - limit sqlgen.Limit - offset sqlgen.Offset - columns sqlgen.Columns - where sqlgen.Where - orderBy sqlgen.OrderBy - groupBy sqlgen.GroupBy - arguments []interface{} -} - -// Executes a SELECT statement that can feed Next(), All() or One(). -func (r *result) setCursor() error { - var err error - // We need a cursor, if the cursor does not exists yet then we create one. - if r.cursor == nil { - r.cursor, err = r.table.source.doQuery(sqlgen.Statement{ - Type: sqlgen.SqlSelect, - Table: sqlgen.Table{r.table.Name()}, - Columns: r.columns, - Limit: r.limit, - Offset: r.offset, - Where: r.where, - OrderBy: r.orderBy, - GroupBy: r.groupBy, - }, r.arguments...) - } - return err -} - -// Sets conditions for reducing the working set. -func (r *result) Where(terms ...interface{}) db.Result { - r.where, r.arguments = whereValues(terms) - return r -} - -// Determines the maximum limit of results to be returned. -func (r *result) Limit(n uint) db.Result { - r.limit = sqlgen.Limit(n) - return r -} - -// Determines how many documents will be skipped before starting to grab -// results. -func (r *result) Skip(n uint) db.Result { - r.offset = sqlgen.Offset(n) - return r -} - -// Used to group results that have the same value in the same column or -// columns. -func (r *result) Group(fields ...interface{}) db.Result { - - groupByColumns := make(sqlgen.GroupBy, 0, len(fields)) - - l := len(fields) - - for i := 0; i < l; i++ { - switch value := fields[i].(type) { - // Maybe other types? - default: - groupByColumns = append(groupByColumns, sqlgen.Column{value}) - } - } - - r.groupBy = groupByColumns - - return r -} - -// Determines sorting of results according to the provided names. Fields may be -// prefixed by - (minus) which means descending order, ascending order would be -// used otherwise. -func (r *result) Sort(fields ...interface{}) db.Result { - - sortColumns := make(sqlgen.SortColumns, 0, len(fields)) - - l := len(fields) - for i := 0; i < l; i++ { - var sort sqlgen.SortColumn - - switch value := fields[i].(type) { - case db.Raw: - sort = sqlgen.SortColumn{ - sqlgen.Column{sqlgen.Raw{fmt.Sprintf(`%v`, value.Value)}}, - sqlgen.SqlSortAsc, - } - case string: - if strings.HasPrefix(value, `-`) { - // Explicit descending order. - sort = sqlgen.SortColumn{ - sqlgen.Column{value[1:]}, - sqlgen.SqlSortDesc, - } - } else { - // Ascending order. - sort = sqlgen.SortColumn{ - sqlgen.Column{value}, - sqlgen.SqlSortAsc, - } - } - } - sortColumns = append(sortColumns, sort) - } - - r.orderBy.SortColumns = sortColumns - - return r -} - -// Retrieves only the given fields. -func (r *result) Select(fields ...interface{}) db.Result { - - r.columns = make(sqlgen.Columns, 0, len(fields)) - - l := len(fields) - for i := 0; i < l; i++ { - var col sqlgen.Column - switch value := fields[i].(type) { - case db.Func: - v := interfaceArgs(value.Args) - var s string - if len(v) == 0 { - s = fmt.Sprintf(`%s()`, value.Name) - } else { - ss := make([]string, 0, len(v)) - for j := range v { - ss = append(ss, fmt.Sprintf(`%v`, v[j])) - } - s = fmt.Sprintf(`%s(%s)`, value.Name, strings.Join(ss, `, `)) - } - col = sqlgen.Column{sqlgen.Raw{s}} - case db.Raw: - col = sqlgen.Column{sqlgen.Raw{fmt.Sprintf(`%v`, value.Value)}} - default: - col = sqlgen.Column{value} - } - r.columns = append(r.columns, col) - } - - return r -} - -// Dumps all results into a pointer to an slice of structs or maps. -func (r *result) All(dst interface{}) error { - var err error - - if r.cursor != nil { - return db.ErrQueryIsPending - } - - // Current cursor. - err = r.setCursor() - - if err != nil { - return err - } - - defer r.Close() - - // Fetching all results within the cursor. - err = sqlutil.FetchRows(r.cursor, dst) - - return err -} - -// Fetches only one result from the resultset. -func (r *result) One(dst interface{}) error { - var err error - - if r.cursor != nil { - return db.ErrQueryIsPending - } - - defer r.Close() - - err = r.Next(dst) - - return err -} - -// Fetches the next result from the resultset. -func (r *result) Next(dst interface{}) (err error) { - - if err = r.setCursor(); err != nil { - r.Close() - return err - } - - if err = sqlutil.FetchRow(r.cursor, dst); err != nil { - r.Close() - return err - } - - return nil -} - -// Removes the matching items from the collection. -func (r *result) Remove() error { - var err error - - _, err = r.table.source.doExec(sqlgen.Statement{ - Type: sqlgen.SqlDelete, - Table: sqlgen.Table{r.table.Name()}, - Where: r.where, - }, r.arguments...) - - return err - -} - -// Updates matching items from the collection with values of the given map or -// struct. -func (r *result) Update(values interface{}) error { - - ff, vv, err := r.table.FieldValues(values) - if err != nil { - return err - } - - total := len(ff) - - cvs := make(sqlgen.ColumnValues, 0, total) - - for i := 0; i < total; i++ { - cvs = append(cvs, sqlgen.ColumnValue{sqlgen.Column{ff[i]}, "=", sqlPlaceholder}) - } - - vv = append(vv, r.arguments...) - - _, err = r.table.source.doExec(sqlgen.Statement{ - Type: sqlgen.SqlUpdate, - Table: sqlgen.Table{r.table.Name()}, - ColumnValues: cvs, - Where: r.where, - }, vv...) - - return err -} - -// Closes the result set. -func (r *result) Close() (err error) { - if r.cursor != nil { - err = r.cursor.Close() - r.cursor = nil - } - return err -} - -// Counts the elements within the main conditions of the set. -func (r *result) Count() (uint64, error) { - var count counter - - row, err := r.table.source.doQueryRow(sqlgen.Statement{ - Type: sqlgen.SqlSelectCount, - Table: sqlgen.Table{r.table.Name()}, - Where: r.where, - }, r.arguments...) - - if err != nil { - return 0, err - } - - err = row.Scan(&count.Total) - if err != nil { - return 0, err - } - - return count.Total, nil -} diff --git a/mysql/layout.go b/mysql/template.go similarity index 64% rename from mysql/layout.go rename to mysql/template.go index c0ef3406..97af9cd2 100644 --- a/mysql/layout.go +++ b/mysql/template.go @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam +// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -22,37 +22,37 @@ package mysql const ( - mysqlColumnSeparator = `.` - mysqlIdentifierSeparator = `, ` - mysqlIdentifierQuote = "`{{.Raw}}`" - mysqlValueSeparator = `, ` - mysqlValueQuote = `'{{.}}'` - mysqlAndKeyword = `AND` - mysqlOrKeyword = `OR` - mysqlNotKeyword = `NOT` - mysqlDescKeyword = `DESC` - mysqlAscKeyword = `ASC` - mysqlDefaultOperator = `=` - mysqlClauseGroup = `({{.}})` - mysqlClauseOperator = ` {{.}} ` - mysqlColumnValue = `{{.Column}} {{.Operator}} {{.Value}}` - mysqlTableAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - mysqlColumnAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - mysqlSortByColumnLayout = `{{.Column}} {{.Sort}}` - - mysqlOrderByLayout = ` + adapterColumnSeparator = `.` + adapterIdentifierSeparator = `, ` + adapterIdentifierQuote = "`{{.Value}}`" + adapterValueSeparator = `, ` + adapterValueQuote = `'{{.}}'` + adapterAndKeyword = `AND` + adapterOrKeyword = `OR` + adapterNotKeyword = `NOT` + adapterDescKeyword = `DESC` + adapterAscKeyword = `ASC` + adapterDefaultOperator = `=` + adapterClauseGroup = `({{.}})` + adapterClauseOperator = ` {{.}} ` + adapterColumnValue = `{{.Column}} {{.Operator}} {{.Value}}` + adapterTableAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` + adapterColumnAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` + adapterSortByColumnLayout = `{{.Column}} {{.Order}}` + + adapterOrderByLayout = ` {{if .SortColumns}} ORDER BY {{.SortColumns}} {{end}} ` - mysqlWhereLayout = ` + adapterWhereLayout = ` {{if .Conds}} WHERE {{.Conds}} {{end}} ` - mysqlSelectLayout = ` + adapterSelectLayout = ` SELECT {{if .Columns}} @@ -79,19 +79,19 @@ const ( OFFSET {{.Offset}} {{end}} ` - mysqlDeleteLayout = ` + adapterDeleteLayout = ` DELETE FROM {{.Table}} {{.Where}} ` - mysqlUpdateLayout = ` + adapterUpdateLayout = ` UPDATE {{.Table}} SET {{.ColumnValues}} {{ .Where }} ` - mysqlSelectCountLayout = ` + adapterSelectCountLayout = ` SELECT COUNT(1) AS _t FROM {{.Table}} @@ -106,7 +106,7 @@ const ( {{end}} ` - mysqlInsertLayout = ` + adapterInsertLayout = ` INSERT INTO {{.Table}} ({{.Columns}}) VALUES @@ -114,23 +114,21 @@ const ( {{.Extra}} ` - mysqlTruncateLayout = ` + adapterTruncateLayout = ` TRUNCATE TABLE {{.Table}} ` - mysqlDropDatabaseLayout = ` + adapterDropDatabaseLayout = ` DROP DATABASE {{.Database}} ` - mysqlDropTableLayout = ` + adapterDropTableLayout = ` DROP TABLE {{.Table}} ` - mysqlGroupByLayout = ` + adapterGroupByLayout = ` {{if .GroupColumns}} GROUP BY {{.GroupColumns}} {{end}} ` - - mysqlNull = `NULL` ) diff --git a/mysql/tx.go b/mysql/tx.go deleted file mode 100644 index 844f11d0..00000000 --- a/mysql/tx.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -package mysql - -import ( - "github.com/jmoiron/sqlx" -) - -type tx struct { - *source - sqlTx *sqlx.Tx - done bool -} - -func (t *tx) Commit() (err error) { - err = t.sqlTx.Commit() - if err == nil { - t.done = true - } - return err -} - -func (t *tx) Rollback() error { - return t.sqlTx.Rollback() -} -- GitLab