mydb/adapter/mongo/database.go

246 lines
5.1 KiB
Go
Raw Normal View History

2023-09-18 15:15:42 +08:00
// Package mongo wraps the gopkg.in/mgo.v2 MongoDB driver. See
// https://github.com/upper/db/adapter/mongo for documentation, particularities and usage
// examples.
package mongo
import (
"context"
"database/sql"
"strings"
"sync"
"time"
"git.hexq.cn/tiglog/mydb"
mgo "gopkg.in/mgo.v2"
)
// Adapter holds the name of the mongodb adapter.
const Adapter = `mongo`
var connTimeout = time.Second * 5
// Source represents a MongoDB database.
type Source struct {
mydb.Settings
ctx context.Context
name string
connURL mydb.ConnectionURL
session *mgo.Session
database *mgo.Database
version []int
collections map[string]*Collection
collectionsMu sync.Mutex
}
type mongoAdapter struct {
}
func (mongoAdapter) Open(dsn mydb.ConnectionURL) (mydb.Session, error) {
return Open(dsn)
}
func init() {
mydb.RegisterAdapter(Adapter, mydb.Adapter(&mongoAdapter{}))
}
// Open stablishes a new connection to a SQL server.
func Open(settings mydb.ConnectionURL) (mydb.Session, error) {
d := &Source{Settings: mydb.NewSettings(), ctx: context.Background()}
if err := d.Open(settings); err != nil {
return nil, err
}
return d, nil
}
func (s *Source) TxContext(context.Context, func(tx mydb.Session) error, *sql.TxOptions) error {
return mydb.ErrNotSupportedByAdapter
}
func (s *Source) Tx(func(mydb.Session) error) error {
return mydb.ErrNotSupportedByAdapter
}
func (s *Source) SQL() mydb.SQL {
// Not supported
panic("sql builder is not supported by mongodb")
}
func (s *Source) ConnectionURL() mydb.ConnectionURL {
return s.connURL
}
// SetConnMaxLifetime is not supported.
func (s *Source) SetConnMaxLifetime(time.Duration) {
s.Settings.SetConnMaxLifetime(time.Duration(0))
}
// SetMaxIdleConns is not supported.
func (s *Source) SetMaxIdleConns(int) {
s.Settings.SetMaxIdleConns(0)
}
// SetMaxOpenConns is not supported.
func (s *Source) SetMaxOpenConns(int) {
s.Settings.SetMaxOpenConns(0)
}
// Name returns the name of the database.
func (s *Source) Name() string {
return s.name
}
// Open attempts to connect to the database.
func (s *Source) Open(connURL mydb.ConnectionURL) error {
s.connURL = connURL
return s.open()
}
// Clone returns a cloned mydb.Session session.
func (s *Source) Clone() (mydb.Session, error) {
newSession := s.session.Copy()
clone := &Source{
Settings: mydb.NewSettings(),
name: s.name,
connURL: s.connURL,
session: newSession,
database: newSession.DB(s.database.Name),
version: s.version,
collections: map[string]*Collection{},
}
return clone, nil
}
// 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()
}
func (s *Source) Reset() {
s.collectionsMu.Lock()
defer s.collectionsMu.Unlock()
s.collections = make(map[string]*Collection)
}
// Driver returns the underlying *mgo.Session instance.
func (s *Source) Driver() interface{} {
return s.session
}
func (s *Source) open() error {
var err error
if s.session, err = mgo.DialWithTimeout(s.connURL.String(), connTimeout); err != nil {
return err
}
s.collections = map[string]*Collection{}
s.database = s.session.DB("")
return nil
}
// Close terminates the current database session.
func (s *Source) Close() error {
if s.session != nil {
s.session.Close()
}
return nil
}
// Collections returns a list of non-system tables from the database.
func (s *Source) Collections() (cols []mydb.Collection, err error) {
var rawcols []string
var col string
if rawcols, err = s.database.CollectionNames(); err != nil {
return nil, err
}
cols = make([]mydb.Collection, 0, len(rawcols))
for _, col = range rawcols {
if !strings.HasPrefix(col, "system.") {
cols = append(cols, s.Collection(col))
}
}
return cols, nil
}
func (s *Source) Delete(mydb.Record) error {
return mydb.ErrNotImplemented
}
func (s *Source) Get(mydb.Record, interface{}) error {
return mydb.ErrNotImplemented
}
func (s *Source) Save(mydb.Record) error {
return mydb.ErrNotImplemented
}
func (s *Source) Context() context.Context {
return s.ctx
}
func (s *Source) WithContext(ctx context.Context) mydb.Session {
return &Source{
ctx: ctx,
Settings: s.Settings,
name: s.name,
connURL: s.connURL,
session: s.session,
database: s.database,
version: s.version,
}
}
// Collection returns a collection by name.
func (s *Source) Collection(name string) mydb.Collection {
s.collectionsMu.Lock()
defer s.collectionsMu.Unlock()
var col *Collection
var ok bool
if col, ok = s.collections[name]; !ok {
col = &Collection{
parent: s,
collection: s.database.C(name),
}
s.collections[name] = col
}
return col
}
func (s *Source) versionAtLeast(version ...int) bool {
// only fetch this once - it makes a db call
if len(s.version) == 0 {
buildInfo, err := s.database.Session.BuildInfo()
if err != nil {
return false
}
s.version = buildInfo.VersionArray
}
// Check major version first
if s.version[0] > version[0] {
return true
}
for i := range version {
if i == len(s.version) {
return false
}
if s.version[i] < version[i] {
return false
}
}
return true
}