<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Helper functions to make constructing templates and sets easier.

package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Functions and methods to parse a single template.

// MustParse parses the template definition string to construct an internal
// representation of the template for execution.
// It panics if the template cannot be parsed.
func (t *Template) MustParse(text string) *Template {
	if err := t.Parse(text); err != nil {
		panic(err)
	}
	return t
}

// ParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
func (t *Template) ParseFile(filename string) os.Error {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	return t.Parse(string(b))
}

// MustParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
// It panics if the file cannot be read or the template cannot be parsed.
func (t *Template) MustParseFile(filename string) *Template {
	if err := t.ParseFile(filename); err != nil {
		panic(err)
	}
	return t
}

// ParseFile creates a new Template and parses the template definition from
// the named file.  The template name is the base name of the file.
func ParseFile(filename string) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t, t.ParseFile(filename)
}

// MustParseFile creates a new Template and parses the template definition
// from the named file.  The template name is the base name of the file.
// It panics if the file cannot be read or the template cannot be parsed.
func MustParseFile(filename string) *Template {
	return New(filepath.Base(filename)).MustParseFile(filename)
}

// Functions and methods to parse a set.

// MustParse parses a string into a set of named templates.
// It panics if the set cannot be parsed.
func (s *Set) MustParse(text string) *Set {
	if err := s.Parse(text); err != nil {
		panic(err)
	}
	return s
}

// ParseFile parses the named files into a set of named templates.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseFile(filenames ...string) os.Error {
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}
		err = s.Parse(string(b))
		if err != nil {
			return err
		}
	}
	return nil
}

// MustParseFile parses the named file into a set of named templates.
// Each file must be parseable by itself.
// MustParseFile panics if any file cannot be read or parsed.
func (s *Set) MustParseFile(filenames ...string) *Set {
	err := s.ParseFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
func ParseSetFile(filenames ...string) (set *Set, err os.Error) {
	s := new(Set)
	var b []byte
	for _, filename := range filenames {
		b, err = ioutil.ReadFile(filename)
		if err != nil {
			return
		}
		err = s.Parse(string(b))
		if err != nil {
			return
		}
	}
	return s, nil
}

// MustParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
// MustParseSetFile panics if any file cannot be read or parsed.
func MustParseSetFile(filenames ...string) *Set {
	s, err := ParseSetFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseFiles parses the set definition from the files identified by the
// pattern.  The pattern is processed by filepath.Glob and must match at
// least one file.
func (s *Set) ParseFiles(pattern string) os.Error {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return err
	}
	if len(filenames) == 0 {
		return fmt.Errorf("pattern matches no files: %#q", pattern)
	}
	return s.ParseFile(filenames...)
}

// ParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob
// and must match at least one file.
func ParseSetFiles(pattern string) (*Set, os.Error) {
	set := new(Set)
	err := set.ParseFiles(pattern)
	if err != nil {
		return nil, err
	}
	return set, nil
}

// MustParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob.
// MustParseSetFiles panics if the pattern is invalid or a matched file cannot be
// read or parsed.
func MustParseSetFiles(pattern string) *Set {
	set, err := ParseSetFiles(pattern)
	if err != nil {
		panic(err)
	}
	return set
}

// Functions and methods to parse stand-alone template files into a set.

// ParseTemplateFile parses the named template files and adds
// them to the set. Each template will named the base name of
// its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFile(filenames ...string) os.Error {
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return err
		}
		if err := s.add(t); err != nil {
			return err
		}
	}
	return nil
}

// MustParseTemplateFile is like ParseTemplateFile but
// panics if there is an error.
func (s *Set) MustParseTemplateFile(filenames ...string) *Set {
	err := s.ParseTemplateFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseTemplateFiles parses the template files matched by the
// patern and adds them to the set. Each template will named
// the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFiles(pattern string) os.Error {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return err
	}
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return err
		}
		if err := s.add(t); err != nil {
			return err
		}
	}
	return nil
}

// MustParseTemplateFile is like ParseTemplateFiles but
// panics if there is an error.
func (s *Set) MustParseTemplateFiles(pattern string) *Set {
	err := s.ParseTemplateFiles(pattern)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseTemplateFile creates a set by parsing the named files,
// each of which defines a single template. Each template will
// named the base name of its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	set := new(Set)
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return nil, err
		}
		if err := set.add(t); err != nil {
			return nil, err
		}
	}
	return set, nil
}

// MustParseTemplateFile is like ParseTemplateFile but
// panics if there is an error.
func MustParseTemplateFile(filenames ...string) *Set {
	set, err := ParseTemplateFile(filenames...)
	if err != nil {
		panic(err)
	}
	return set
}

// ParseTemplateFiles creates a set by parsing the files matched
// by the pattern, each of which defines a single template. Each
// template will named the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}
	set := new(Set)
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return nil, err
		}
		if err := set.add(t); err != nil {
			return nil, err
		}
	}
	return set, nil
}

// MustParseTemplateFiles is like ParseTemplateFiles but
// panics if there is a parse error or other problem
// constructing the set.
func MustParseTemplateFiles(pattern string) *Set {
	set, err := ParseTemplateFiles(pattern)
	if err != nil {
		panic(err)
	}
	return set
}
<commit_msg>exp/template: make Set.ParseFile etc resolve functions in the Set Fixes #2114<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Helper functions to make constructing templates and sets easier.

package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
)

// Functions and methods to parse a single template.

// MustParse parses the template definition string to construct an internal
// representation of the template for execution.
// It panics if the template cannot be parsed.
func (t *Template) MustParse(text string) *Template {
	if err := t.Parse(text); err != nil {
		panic(err)
	}
	return t
}

// ParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
func (t *Template) ParseFile(filename string) os.Error {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	return t.Parse(string(b))
}

// ParseFileInSet is the same as ParseFile except that function bindings
// are checked against those in the set and the template is added
// to the set.
func (t *Template) ParseFileInSet(filename string, set *Set) os.Error {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	return t.ParseInSet(string(b), set)
}

// MustParseFile reads the template definition from a file and parses it to
// construct an internal representation of the template for execution.
// It panics if the file cannot be read or the template cannot be parsed.
func (t *Template) MustParseFile(filename string) *Template {
	if err := t.ParseFile(filename); err != nil {
		panic(err)
	}
	return t
}

// ParseFile creates a new Template and parses the template definition from
// the named file.  The template name is the base name of the file.
func ParseFile(filename string) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t, t.ParseFile(filename)
}

// ParseFileInSet creates a new Template and parses the template
// definition from the named file. The template name is the base name
// of the file. It also adds the template to the set. Function bindings are
//checked against those in the set.
func ParseFileInSet(filename string, set *Set) (*Template, os.Error) {
	t := New(filepath.Base(filename))
	return t, t.ParseFileInSet(filename, set)
}

// MustParseFile creates a new Template and parses the template definition
// from the named file.  The template name is the base name of the file.
// It panics if the file cannot be read or the template cannot be parsed.
func MustParseFile(filename string) *Template {
	return New(filepath.Base(filename)).MustParseFile(filename)
}

// Functions and methods to parse a set.

// MustParse parses a string into a set of named templates.
// It panics if the set cannot be parsed.
func (s *Set) MustParse(text string) *Set {
	if err := s.Parse(text); err != nil {
		panic(err)
	}
	return s
}

// ParseFile parses the named files into a set of named templates.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseFile(filenames ...string) os.Error {
	for _, filename := range filenames {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			return err
		}
		err = s.Parse(string(b))
		if err != nil {
			return err
		}
	}
	return nil
}

// MustParseFile parses the named file into a set of named templates.
// Each file must be parseable by itself.
// MustParseFile panics if any file cannot be read or parsed.
func (s *Set) MustParseFile(filenames ...string) *Set {
	err := s.ParseFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
func ParseSetFile(filenames ...string) (set *Set, err os.Error) {
	s := new(Set)
	var b []byte
	for _, filename := range filenames {
		b, err = ioutil.ReadFile(filename)
		if err != nil {
			return
		}
		err = s.Parse(string(b))
		if err != nil {
			return
		}
	}
	return s, nil
}

// MustParseSetFile creates a new Set and parses the set definition from the
// named files. Each file must be individually parseable.
// MustParseSetFile panics if any file cannot be read or parsed.
func MustParseSetFile(filenames ...string) *Set {
	s, err := ParseSetFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseFiles parses the set definition from the files identified by the
// pattern.  The pattern is processed by filepath.Glob and must match at
// least one file.
func (s *Set) ParseFiles(pattern string) os.Error {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return err
	}
	if len(filenames) == 0 {
		return fmt.Errorf("pattern matches no files: %#q", pattern)
	}
	return s.ParseFile(filenames...)
}

// ParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob
// and must match at least one file.
func ParseSetFiles(pattern string) (*Set, os.Error) {
	set := new(Set)
	err := set.ParseFiles(pattern)
	if err != nil {
		return nil, err
	}
	return set, nil
}

// MustParseSetFiles creates a new Set and parses the set definition from the
// files identified by the pattern. The pattern is processed by filepath.Glob.
// MustParseSetFiles panics if the pattern is invalid or a matched file cannot be
// read or parsed.
func MustParseSetFiles(pattern string) *Set {
	set, err := ParseSetFiles(pattern)
	if err != nil {
		panic(err)
	}
	return set
}

// Functions and methods to parse stand-alone template files into a set.

// ParseTemplateFile parses the named template files and adds
// them to the set. Each template will named the base name of
// its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFile(filenames ...string) os.Error {
	for _, filename := range filenames {
		_, err := ParseFileInSet(filename, s)
		if err != nil {
			return err
		}
	}
	return nil
}

// MustParseTemplateFile is like ParseTemplateFile but
// panics if there is an error.
func (s *Set) MustParseTemplateFile(filenames ...string) *Set {
	err := s.ParseTemplateFile(filenames...)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseTemplateFiles parses the template files matched by the
// patern and adds them to the set. Each template will named
// the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func (s *Set) ParseTemplateFiles(pattern string) os.Error {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return err
	}
	for _, filename := range filenames {
		_, err := ParseFileInSet(filename, s)
		if err != nil {
			return err
		}
	}
	return nil
}

// MustParseTemplateFile is like ParseTemplateFiles but
// panics if there is an error.
func (s *Set) MustParseTemplateFiles(pattern string) *Set {
	err := s.ParseTemplateFiles(pattern)
	if err != nil {
		panic(err)
	}
	return s
}

// ParseTemplateFile creates a set by parsing the named files,
// each of which defines a single template. Each template will
// named the base name of its file.
// Unlike with ParseFile, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFile is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFile(filenames ...string) (*Set, os.Error) {
	set := new(Set)
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return nil, err
		}
		if err := set.add(t); err != nil {
			return nil, err
		}
	}
	return set, nil
}

// MustParseTemplateFile is like ParseTemplateFile but
// panics if there is an error.
func MustParseTemplateFile(filenames ...string) *Set {
	set, err := ParseTemplateFile(filenames...)
	if err != nil {
		panic(err)
	}
	return set
}

// ParseTemplateFiles creates a set by parsing the files matched
// by the pattern, each of which defines a single template. Each
// template will named the base name of its file.
// Unlike with ParseFiles, each file should be a stand-alone template
// definition suitable for Template.Parse (not Set.Parse); that is, the
// file does not contain {{define}} clauses. ParseTemplateFiles is
// therefore equivalent to calling the ParseFile function to create
// individual templates, which are then added to the set.
// Each file must be parseable by itself. Parsing stops if an error is
// encountered.
func ParseTemplateFiles(pattern string) (*Set, os.Error) {
	filenames, err := filepath.Glob(pattern)
	if err != nil {
		return nil, err
	}
	set := new(Set)
	for _, filename := range filenames {
		t, err := ParseFile(filename)
		if err != nil {
			return nil, err
		}
		if err := set.add(t); err != nil {
			return nil, err
		}
	}
	return set, nil
}

// MustParseTemplateFiles is like ParseTemplateFiles but
// panics if there is a parse error or other problem
// constructing the set.
func MustParseTemplateFiles(pattern string) *Set {
	set, err := ParseTemplateFiles(pattern)
	if err != nil {
		panic(err)
	}
	return set
}
<|endoftext|>


<commit_before>package influxdb

import (
	"errors"
	"sync"
	"time"

	"github.com/influxdb/influxdb/data"
	"github.com/influxdb/influxdb/meta"
)

const defaultWriteTimeout = 5 * time.Second

// ConsistencyLevel represent a required replication criteria before a write can
// be returned as successful
type ConsistencyLevel int

const (
	// ConsistencyLevelAny allows for hinted hand off, potentially no write happened yet
	ConsistencyLevelAny ConsistencyLevel = iota

	// ConsistencyLevelOne requires at least one data node acknowledged a write
	ConsistencyLevelOne

	// ConsistencyLevelOne requires a quorum of data nodes to acknowledge a write
	ConsistencyLevelQuorum

	// ConsistencyLevelAll requires all data nodes to acknowledge a write
	ConsistencyLevelAll
)

var (
	// ErrTimeout is returned when a write times out
	ErrTimeout = errors.New("timeout")

	// ErrPartialWrite is returned when a write partially succeeds but does
	// not meet the requested consistency level
	ErrPartialWrite = errors.New("partial write")

	// ErrWriteFailed is returned when no writes succeeded
	ErrWriteFailed = errors.New("write failed")
)

// Coordinator handle queries and writes across multiple local and remote
// data nodes.
type Coordinator struct {
	mu           sync.RWMutex
	MetaStore    meta.Store
	shardWriters []ShardWriter
}

// ShardMapping contiains a mapping of a shards to a points.
type ShardMapping struct {
	Points map[uint64][]data.Point   // The points associated with a shard ID
	Shards map[uint64]meta.ShardInfo // The shards that have been mapped, keyed by shard ID
}

// NewShardMapping creates an empty ShardMapping
func NewShardMapping() *ShardMapping {
	return &ShardMapping{
		Points: map[uint64][]data.Point{},
		Shards: map[uint64]meta.ShardInfo{},
	}
}

// MapPoint maps a point to shard
func (s *ShardMapping) MapPoint(shardInfo meta.ShardInfo, p data.Point) {
	points, ok := s.Points[shardInfo.ID]
	if !ok {
		s.Points[shardInfo.ID] = []data.Point{p}
	} else {
		s.Points[shardInfo.ID] = append(points, p)
	}
	s.Shards[shardInfo.ID] = shardInfo
}

// ShardWriter provides the ability to write a slice of points to s given shard ID.
// It should return the number of times the set of points was written or an error
// if the write failed.
type ShardWriter interface {
	WriteShard(shardID uint64, points []data.Point) (int, error)
}

func (c *Coordinator) AddShardWriter(s ShardWriter) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.shardWriters = append(c.shardWriters, s)
}

// MapShards maps the points contained in wp to a ShardMapping.  If a point
// maps to a shard group or shard that does not currently exist, it will be
// created before returning the mapping.
func (c *Coordinator) MapShards(wp *WritePointsRequest) (*ShardMapping, error) {

	// holds the start time ranges for required shard groups
	timeRanges := map[time.Time]*meta.ShardGroupInfo{}

	rp, err := c.MetaStore.RetentionPolicy(wp.Database, wp.RetentionPolicy)
	if err != nil {
		return nil, err
	}

	for _, p := range wp.Points {
		timeRanges[p.Time.Truncate(rp.ShardGroupDuration)] = nil
	}

	// holds all the shard groups and shards that are required for writes
	for t := range timeRanges {
		g, err := c.MetaStore.CreateShardGroupIfNotExists(wp.Database, wp.RetentionPolicy, t)
		if err != nil {
			return nil, err
		}
		timeRanges[t] = g
	}

	shardMapping := NewShardMapping()
	for _, p := range wp.Points {
		g := timeRanges[p.Time.Truncate(rp.ShardGroupDuration)]
		sid := p.SeriesID()
		shardInfo := g.Shards[sid%uint64(len(g.Shards))]
		shardMapping.MapPoint(shardInfo, p)
	}
	return shardMapping, nil
}

// Write is coordinates multiple writes across local and remote data nodes
// according the request consistency level
func (c *Coordinator) Write(p *WritePointsRequest) error {
	shardMappings, err := c.MapShards(p)
	if err != nil {
		return err
	}

	ch := make(chan error, len(shardMappings.Points))
	for shardID, points := range shardMappings.Points {
		go func(shard meta.ShardInfo, points []data.Point) {
			ch <- c.writeToShards(shard, p.ConsistencyLevel, points)
		}(shardMappings.Shards[shardID], points)
	}

	for range shardMappings.Points {
		select {
		case err := <-ch:
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// writeToShards writes points to a shard and ensures a write consistency level has been met.  If the write
// partially succceds, ErrPartialWrite is returned.
func (c *Coordinator) writeToShards(shard meta.ShardInfo, consistency ConsistencyLevel, points []data.Point) error {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if len(c.shardWriters) == 0 {
		return ErrWriteFailed
	}

	requiredResponses := len(shard.OwnerIDs)
	switch consistency {
	case ConsistencyLevelAny, ConsistencyLevelOne:
		requiredResponses = 1
	case ConsistencyLevelQuorum:
		requiredResponses = requiredResponses/2 + 1
	}

	// holds the response to the ShardWriter.Write calls
	type result struct {
		wrote int
		err   error
	}

	// response channel for each shard writer go routine
	ch := make(chan result, len(c.shardWriters))

	for _, w := range c.shardWriters {
		// write to each ShardWriter (local and remote), in parallel
		go func(w ShardWriter, shardID uint64, points []data.Point) {
			wrote, err := w.WriteShard(shardID, points)
			ch <- result{wrote, err}
		}(w, shard.ID, points)
	}

	var wrote int
	timeout := time.After(defaultWriteTimeout)
	for range c.shardWriters {
		select {
		case <-timeout:
			// return timeout error to caller
			return ErrTimeout
		case res := <-ch:
			wrote += res.wrote

			// ErrShardNotLocal might be returned from a local writer. Ignore it.
			if res.err != nil && res.err != ErrShardNotLocal {
				return res.err
			}

			// We wrote the required consistency level
			if wrote >= requiredResponses {
				return nil
			}
		}
	}

	if wrote > 0 {
		return ErrPartialWrite
	}

	return ErrWriteFailed
}

func (c *Coordinator) Execute(q *QueryRequest) (chan *Result, error) {
	return nil, nil
}
<commit_msg>Make coordinator a service<commit_after>package influxdb

import (
	"errors"
	"sync"
	"time"

	"github.com/influxdb/influxdb/data"
	"github.com/influxdb/influxdb/meta"
)

const defaultWriteTimeout = 5 * time.Second

// ConsistencyLevel represent a required replication criteria before a write can
// be returned as successful
type ConsistencyLevel int

const (
	// ConsistencyLevelAny allows for hinted hand off, potentially no write happened yet
	ConsistencyLevelAny ConsistencyLevel = iota

	// ConsistencyLevelOne requires at least one data node acknowledged a write
	ConsistencyLevelOne

	// ConsistencyLevelOne requires a quorum of data nodes to acknowledge a write
	ConsistencyLevelQuorum

	// ConsistencyLevelAll requires all data nodes to acknowledge a write
	ConsistencyLevelAll
)

var (
	// ErrTimeout is returned when a write times out
	ErrTimeout = errors.New("timeout")

	// ErrPartialWrite is returned when a write partially succeeds but does
	// not meet the requested consistency level
	ErrPartialWrite = errors.New("partial write")

	// ErrWriteFailed is returned when no writes succeeded
	ErrWriteFailed = errors.New("write failed")
)

// Coordinator handle queries and writes across multiple local and remote
// data nodes.
type Coordinator struct {
	mu      sync.RWMutex
	closing chan struct{}

	MetaStore    meta.Store
	shardWriters []ShardWriter
}

// ShardMapping contiains a mapping of a shards to a points.
type ShardMapping struct {
	Points map[uint64][]data.Point   // The points associated with a shard ID
	Shards map[uint64]meta.ShardInfo // The shards that have been mapped, keyed by shard ID
}

func NewCoordinator() *Coordinator {
	return &Coordinator{
		closing: make(chan struct{}),
	}
}

// NewShardMapping creates an empty ShardMapping
func NewShardMapping() *ShardMapping {
	return &ShardMapping{
		Points: map[uint64][]data.Point{},
		Shards: map[uint64]meta.ShardInfo{},
	}
}

// MapPoint maps a point to shard
func (s *ShardMapping) MapPoint(shardInfo meta.ShardInfo, p data.Point) {
	points, ok := s.Points[shardInfo.ID]
	if !ok {
		s.Points[shardInfo.ID] = []data.Point{p}
	} else {
		s.Points[shardInfo.ID] = append(points, p)
	}
	s.Shards[shardInfo.ID] = shardInfo
}

// ShardWriter provides the ability to write a slice of points to s given shard ID.
// It should return the number of times the set of points was written or an error
// if the write failed.
type ShardWriter interface {
	WriteShard(shardID uint64, points []data.Point) (int, error)
}

func (c *Coordinator) Open() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.closing == nil {
		c.closing = make(chan struct{})
	}
	return nil
}

func (c *Coordinator) Close() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.closing != nil {
		close(c.closing)
		c.closing = nil
	}
	return nil
}

func (c *Coordinator) AddShardWriter(s ShardWriter) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.shardWriters = append(c.shardWriters, s)
}

// MapShards maps the points contained in wp to a ShardMapping.  If a point
// maps to a shard group or shard that does not currently exist, it will be
// created before returning the mapping.
func (c *Coordinator) MapShards(wp *WritePointsRequest) (*ShardMapping, error) {

	// holds the start time ranges for required shard groups
	timeRanges := map[time.Time]*meta.ShardGroupInfo{}

	rp, err := c.MetaStore.RetentionPolicy(wp.Database, wp.RetentionPolicy)
	if err != nil {
		return nil, err
	}

	for _, p := range wp.Points {
		timeRanges[p.Time.Truncate(rp.ShardGroupDuration)] = nil
	}

	// holds all the shard groups and shards that are required for writes
	for t := range timeRanges {
		g, err := c.MetaStore.CreateShardGroupIfNotExists(wp.Database, wp.RetentionPolicy, t)
		if err != nil {
			return nil, err
		}
		timeRanges[t] = g
	}

	shardMapping := NewShardMapping()
	for _, p := range wp.Points {
		g := timeRanges[p.Time.Truncate(rp.ShardGroupDuration)]
		sid := p.SeriesID()
		shardInfo := g.Shards[sid%uint64(len(g.Shards))]
		shardMapping.MapPoint(shardInfo, p)
	}
	return shardMapping, nil
}

// Write is coordinates multiple writes across local and remote data nodes
// according the request consistency level
func (c *Coordinator) Write(p *WritePointsRequest) error {
	shardMappings, err := c.MapShards(p)
	if err != nil {
		return err
	}

	ch := make(chan error, len(shardMappings.Points))
	for shardID, points := range shardMappings.Points {
		go func(shard meta.ShardInfo, points []data.Point) {
			ch <- c.writeToShards(shard, p.ConsistencyLevel, points)
		}(shardMappings.Shards[shardID], points)
	}

	for range shardMappings.Points {
		select {
		case <-c.closing:
			return ErrWriteFailed
		case err := <-ch:
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// writeToShards writes points to a shard and ensures a write consistency level has been met.  If the write
// partially succceds, ErrPartialWrite is returned.
func (c *Coordinator) writeToShards(shard meta.ShardInfo, consistency ConsistencyLevel, points []data.Point) error {
	c.mu.RLock()
	defer c.mu.RUnlock()

	if len(c.shardWriters) == 0 {
		return ErrWriteFailed
	}

	requiredResponses := len(shard.OwnerIDs)
	switch consistency {
	case ConsistencyLevelAny, ConsistencyLevelOne:
		requiredResponses = 1
	case ConsistencyLevelQuorum:
		requiredResponses = requiredResponses/2 + 1
	}

	// holds the response to the ShardWriter.Write calls
	type result struct {
		wrote int
		err   error
	}

	// response channel for each shard writer go routine
	ch := make(chan result, len(c.shardWriters))

	for _, w := range c.shardWriters {
		// write to each ShardWriter (local and remote), in parallel
		go func(w ShardWriter, shardID uint64, points []data.Point) {
			wrote, err := w.WriteShard(shardID, points)
			ch <- result{wrote, err}
		}(w, shard.ID, points)
	}

	var wrote int
	timeout := time.After(defaultWriteTimeout)
	for range c.shardWriters {
		select {
		case <-c.closing:
			return ErrWriteFailed
		case <-timeout:
			// return timeout error to caller
			return ErrTimeout
		case res := <-ch:
			wrote += res.wrote

			// ErrShardNotLocal might be returned from a local writer. Ignore it.
			if res.err != nil && res.err != ErrShardNotLocal {
				return res.err
			}

			// We wrote the required consistency level
			if wrote >= requiredResponses {
				return nil
			}
		}
	}

	if wrote > 0 {
		return ErrPartialWrite
	}

	return ErrWriteFailed
}

func (c *Coordinator) Execute(q *QueryRequest) (chan *Result, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>//  Copyright 2017 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

// gce_inventory_agent gathers and writes instance inventory to guest attributes.
package main

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/compute-image-tools/go/osinfo"
	"github.com/GoogleCloudPlatform/compute-image-tools/go/packages"
	"github.com/GoogleCloudPlatform/compute-image-tools/go/service"
	"github.com/google/logger"
)

const (
	reportURL = "http://metadata.google.internal/computeMetadata/v1/instance/guest-attributes/guestInventory"
)

type instanceInventory struct {
	Hostname          string
	LongName          string
	ShortName         string
	Version           string
	Architecture      string
	KernelVersion     string
	InstalledPackages map[string][]packages.PkgInfo
	PackageUpdates    map[string][]packages.PkgInfo
	Errors            []string
}

func postAttribute(url string, value io.Reader) error {
	req, err := http.NewRequest("PUT", url, value)
	if err != nil {
		return err
	}
	req.Header.Add("Metadata-Flavor", "Google")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(`received status code %q for request "%s %s"`, resp.Status, req.Method, req.URL.String())
	}
	return nil
}

func postAttributeCompressed(url string, body interface{}) error {
	msg, err := json.Marshal(body)
	if err != nil {
		return err
	}

	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)

	if _, err := zw.Write(msg); err != nil {
		return err
	}

	if err := zw.Close(); err != nil {
		return err
	}

	return postAttribute(url, strings.NewReader(base64.StdEncoding.EncodeToString(buf.Bytes())))
}

func writeInventory(state *instanceInventory, url string) {
	logger.Info("Writing instance inventory.")

	if err := postAttribute(url+"/Timestamp", strings.NewReader(time.Now().UTC().Format(time.RFC3339))); err != nil {
		state.Errors = append(state.Errors, err.Error())
		logger.Error(err)
	}

	e := reflect.ValueOf(state).Elem()
	t := e.Type()
	for i := 0; i < e.NumField(); i++ {
		f := e.Field(i)
		u := fmt.Sprintf("%s/%s", url, t.Field(i).Name)
		switch f.Kind() {
		case reflect.String:
			if err := postAttribute(u, strings.NewReader(f.String())); err != nil {
				state.Errors = append(state.Errors, err.Error())
				logger.Error(err)
			}
		case reflect.Map:
			if err := postAttributeCompressed(u, f.Interface()); err != nil {
				state.Errors = append(state.Errors, err.Error())
				logger.Error(err)
			}
		}
	}
	if err := postAttribute(url+"/Errors", strings.NewReader(fmt.Sprintf("%q", state.Errors))); err != nil {
		logger.Error(err)
	}
}

// disabled checks if the inventory agent is disabled in either instance or
// project metadata.
// Instance metadata takes precedence.
func disabled(md *metadataJSON) bool {
	disabled, err := strconv.ParseBool(md.Instance.Attributes.DisableInventoryAgent)
	if err == nil {
		return disabled
	}
	disabled, err = strconv.ParseBool(md.Project.Attributes.DisableInventoryAgent)
	if err == nil {
		return disabled
	}
	return false
}

func getInventory() *instanceInventory {
	logger.Info("Gathering instance inventory.")

	hs := &instanceInventory{}

	hn, err := os.Hostname()
	if err != nil {
		hs.Errors = append(hs.Errors, err.Error())
	}

	hs.Hostname = hn

	di, err := osinfo.GetDistributionInfo()
	if err != nil {
		hs.Errors = append(hs.Errors, err.Error())
	}

	hs.LongName = di.LongName
	hs.ShortName = di.ShortName
	hs.Version = di.Version
	hs.KernelVersion = di.Kernel
	hs.Architecture = di.Architecture

	var errs []string
	hs.InstalledPackages, errs = packages.GetInstalledPackages()
	if len(errs) != 0 {
		hs.Errors = append(hs.Errors, errs...)
	}

	hs.PackageUpdates, errs = packages.GetPackageUpdates()
	if len(errs) != 0 {
		hs.Errors = append(hs.Errors, errs...)
	}

	return hs
}

func run(ctx context.Context) {
	agentDisabled := false

	ticker := time.NewTicker(30 * time.Minute)
	for {
		md, err := getMetadata(ctx)
		if err != nil {
			logger.Error(err)
			continue
		}

		if disabled(md) {
			if !agentDisabled {
				logger.Info("GCE inventory agent disabled by metadata")
			}
			agentDisabled = true
			continue
		}

		agentDisabled = false

		writeInventory(getInventory(), reportURL)

		select {
		case <-ticker.C:
			continue
		case <-ctx.Done():
			return
		}
	}
}

func main() {
	logger.Init("gce_inventory_agent", true, false, ioutil.Discard)
	ctx := context.Background()

	var action string
	if len(os.Args) > 1 {
		action = os.Args[1]
	}
	if action == "noservice" {
		writeInventory(getInventory(), reportURL)
		os.Exit(0)
	}
	if err := service.Register(ctx, "gce_inventory_agent", "GCE Inventory Agent", "", run, action); err != nil {
		logger.Fatal(err)
	}
}
<commit_msg>gce_inventory_agent: Rewrite compressed encoding to use stacked writers (#561)<commit_after>//  Copyright 2017 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

// gce_inventory_agent gathers and writes instance inventory to guest attributes.
package main

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/GoogleCloudPlatform/compute-image-tools/go/osinfo"
	"github.com/GoogleCloudPlatform/compute-image-tools/go/packages"
	"github.com/GoogleCloudPlatform/compute-image-tools/go/service"
	"github.com/google/logger"
)

const (
	reportURL = "http://metadata.google.internal/computeMetadata/v1/instance/guest-attributes/guestInventory"
)

type instanceInventory struct {
	Hostname          string
	LongName          string
	ShortName         string
	Version           string
	Architecture      string
	KernelVersion     string
	InstalledPackages map[string][]packages.PkgInfo
	PackageUpdates    map[string][]packages.PkgInfo
	Errors            []string
}

func postAttribute(url string, value io.Reader) error {
	req, err := http.NewRequest("PUT", url, value)
	if err != nil {
		return err
	}
	req.Header.Add("Metadata-Flavor", "Google")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf(`received status code %q for request "%s %s"`, resp.Status, req.Method, req.URL.String())
	}
	return nil
}

func postAttributeCompressed(url string, body interface{}) error {

	buf := &bytes.Buffer{}
	b := base64.NewEncoder(base64.StdEncoding, buf)
	zw := gzip.NewWriter(b)
	w := json.NewEncoder(zw)
	if err := w.Encode(body); err != nil {
		return err
	}

	if err := zw.Close(); err != nil {
		return err
	}
	if err := b.Close(); err != nil {
		return err
	}

	return postAttribute(url, buf)
}

func writeInventory(state *instanceInventory, url string) {
	logger.Info("Writing instance inventory.")

	if err := postAttribute(url+"/Timestamp", strings.NewReader(time.Now().UTC().Format(time.RFC3339))); err != nil {
		state.Errors = append(state.Errors, err.Error())
		logger.Error(err)
	}

	e := reflect.ValueOf(state).Elem()
	t := e.Type()
	for i := 0; i < e.NumField(); i++ {
		f := e.Field(i)
		u := fmt.Sprintf("%s/%s", url, t.Field(i).Name)
		switch f.Kind() {
		case reflect.String:
			if err := postAttribute(u, strings.NewReader(f.String())); err != nil {
				state.Errors = append(state.Errors, err.Error())
				logger.Error(err)
			}
		case reflect.Map:
			if err := postAttributeCompressed(u, f.Interface()); err != nil {
				state.Errors = append(state.Errors, err.Error())
				logger.Error(err)
			}
		}
	}
	if err := postAttribute(url+"/Errors", strings.NewReader(fmt.Sprintf("%q", state.Errors))); err != nil {
		logger.Error(err)
	}
}

// disabled checks if the inventory agent is disabled in either instance or
// project metadata.
// Instance metadata takes precedence.
func disabled(md *metadataJSON) bool {
	disabled, err := strconv.ParseBool(md.Instance.Attributes.DisableInventoryAgent)
	if err == nil {
		return disabled
	}
	disabled, err = strconv.ParseBool(md.Project.Attributes.DisableInventoryAgent)
	if err == nil {
		return disabled
	}
	return false
}

func getInventory() *instanceInventory {
	logger.Info("Gathering instance inventory.")

	hs := &instanceInventory{}

	hn, err := os.Hostname()
	if err != nil {
		hs.Errors = append(hs.Errors, err.Error())
	}

	hs.Hostname = hn

	di, err := osinfo.GetDistributionInfo()
	if err != nil {
		hs.Errors = append(hs.Errors, err.Error())
	}

	hs.LongName = di.LongName
	hs.ShortName = di.ShortName
	hs.Version = di.Version
	hs.KernelVersion = di.Kernel
	hs.Architecture = di.Architecture

	var errs []string
	hs.InstalledPackages, errs = packages.GetInstalledPackages()
	if len(errs) != 0 {
		hs.Errors = append(hs.Errors, errs...)
	}

	hs.PackageUpdates, errs = packages.GetPackageUpdates()
	if len(errs) != 0 {
		hs.Errors = append(hs.Errors, errs...)
	}

	return hs
}

func run(ctx context.Context) {
	agentDisabled := false

	ticker := time.NewTicker(30 * time.Minute)
	for {
		md, err := getMetadata(ctx)
		if err != nil {
			logger.Error(err)
			continue
		}

		if disabled(md) {
			if !agentDisabled {
				logger.Info("GCE inventory agent disabled by metadata")
			}
			agentDisabled = true
			continue
		}

		agentDisabled = false

		writeInventory(getInventory(), reportURL)

		select {
		case <-ticker.C:
			continue
		case <-ctx.Done():
			return
		}
	}
}

func main() {
	logger.Init("gce_inventory_agent", true, false, ioutil.Discard)
	ctx := context.Background()

	var action string
	if len(os.Args) > 1 {
		action = os.Args[1]
	}
	if action == "noservice" {
		writeInventory(getInventory(), reportURL)
		os.Exit(0)
	}
	if err := service.Register(ctx, "gce_inventory_agent", "GCE Inventory Agent", "", run, action); err != nil {
		logger.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package core

import "github.com/ethereum/go-ethereum/common"

var badHashes = []common.Hash{
	common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"),
}
<commit_msg>core: ban hash 38f5bb...a714bc<commit_after>package core

import "github.com/ethereum/go-ethereum/common"

var badHashes = []common.Hash{
	common.HexToHash("f269c503aed286caaa0d114d6a5320e70abbc2febe37953207e76a2873f2ba79"),
	common.HexToHash("38f5bbbffd74804820ffa4bab0cd540e9de229725afb98c1a7e57936f4a714bc"),
}
<|endoftext|>


<commit_before>package game

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

type Server struct {
	name         string
	players      map[string]Player
	levels       map[string]Level
	workingdir   string
	defaultLevel Level
	Config       ServerConfig
}

type ServerConfig struct {
	Name      string `xml:"name"`
	Interface string `xml:"interface"`
	Motd      string `xml:"motd"`
}

func (s *Server) HasDefaultLevel() bool {
	return s.defaultLevel.Key != ""
}

func NewServer(serverdir string) *Server {
	server := &Server{
		players:    make(map[string]Player),
		levels:     make(map[string]Level),
		workingdir: serverdir,
	}

	server.LoadConfig()

	return server
}

func (s *Server) LoadConfig() error {
	log.Println("Loading config ...")
	configFileName := s.workingdir + "/static/server.xml"
	fileContent, fileIoErr := ioutil.ReadFile(configFileName)
	if fileIoErr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be loaded\n", configFileName)
		log.Printf("%v", fileIoErr)
		return fileIoErr
	}
	config := ServerConfig{}
	if xmlerr := xml.Unmarshal(fileContent, &config); xmlerr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be Unmarshaled\n", configFileName, xmlerr)
		log.Printf("%v", xmlerr)
		return xmlerr
	}
	s.Config = config
	log.Println(" config loaded")
	return nil
}

func (s *Server) LoadLevels() error {
	log.Println("Loading levels ...")
	levelWalker := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		fileContent, fileIoErr := ioutil.ReadFile(path)
		if fileIoErr != nil {
			log.Printf("\n")
			log.Printf("File %s could not be loaded\n", path)
			log.Printf("%v", fileIoErr)
			return fileIoErr
		}
		level := Level{}
		if xmlerr := xml.Unmarshal(fileContent, &level); xmlerr != nil {
			log.Printf("\n")
			log.Printf("File %s could not be Unmarshaled\n", path, xmlerr)
			log.Printf("%v", xmlerr)
			return xmlerr
		}
		log.Printf(" loaded: %s\n", info.Name())
		s.addLevel(level)
		return nil
	}

	return filepath.Walk(s.workingdir+"/static/levels/", levelWalker)
}

func (s *Server) getPlayerFileName(playerName string) string {
	return s.workingdir + "/static/player/" + playerName + ".player"
}

func (s *Server) LoadPlayer(playerName string) bool {
	playerFileName := s.getPlayerFileName(playerName)

	log.Println("Loading player %s", playerFileName)

	fileContent, fileIoErr := ioutil.ReadFile(playerFileName)
	if fileIoErr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be loaded\n", playerFileName)
		log.Printf("%v", fileIoErr)
		//return fileIoErr
		return false
	}

	player := Player{}
	if xmlerr := xml.Unmarshal(fileContent, &player); xmlerr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be Unmarshaled\n", playerFileName, xmlerr)
		log.Printf("%v", xmlerr)
		//return xmlerr
		return false
	}
	log.Printf(" loaded: %s", player.Gamename)
	s.addPlayer(player)

	return true
}

func (s *Server) addLevel(level Level) error {
	if level.Tag == "default" {
		log.Printf("default level loaded: %s\n", level.Key)
		s.defaultLevel = level
	}
	s.levels[level.Key] = level
	return nil
}

func (s *Server) addPlayer(player Player) error {
	s.players[player.Nickname] = player
	return nil
}

func (s *Server) GetPlayerByNick(nickname string) (Player, bool) {
	player, ok := s.players[nickname]
	return player, ok
}

func (s *Server) GetRoom(key string) (Level, bool) {
	level, ok := s.levels[key]
	return level, ok
}

func (s *Server) GetName() string {
	return s.Config.Name
}

func (s *Server) CreatePlayer(nick string, name string, playerType string) {
	playerFileName := s.getPlayerFileName(nick)
	if _, err := os.Stat(playerFileName); err == nil {
		s.LoadPlayer(nick)
		fmt.Printf("Player %s does already exists", nick)
		return
	}
	player := Player{
		Gamename:   name,
		Nickname:   nick,
		PlayerType: playerType,
		Position:   s.defaultLevel.Key,
	}
	s.addPlayer(player)
}

func (s *Server) SavePlayer(player Player) bool {
	data, err := xml.MarshalIndent(player, "", "    ")
	if err == nil {
		playerFileName := s.getPlayerFileName(player.Nickname)
		if ioerror := ioutil.WriteFile(playerFileName, data, 0666); ioerror != nil {
			log.Println(ioerror)
			return true
		}
	} else {
		log.Println(err)
	}
	return false
}

func (s *Server) OnExit(client Client) {
	s.SavePlayer(client.Player)
	client.WriteLineToUser(fmt.Sprintf("Good bye %s", client.Player.Gamename))
}
<commit_msg>added username validation<commit_after>package game

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
)

type Server struct {
	players      map[string]Player
	levels       map[string]Level
	workingdir   string
	DefaultLevel Level
	Config       ServerConfig
}

type ServerConfig struct {
	Name      string `xml:"name"`
	Interface string `xml:"interface"`
	Motd      string `xml:"motd"`
}

func (s *Server) HasDefaultLevel() bool {
	return s.DefaultLevel.Key != ""
}

func NewServer(serverdir string) *Server {
	server := &Server{
		players:    make(map[string]Player),
		levels:     make(map[string]Level),
		workingdir: serverdir,
	}

	server.LoadConfig()

	return server
}

func (s *Server) LoadConfig() error {
	log.Println("Loading config ...")
	configFileName := s.workingdir + "/static/server.xml"
	fileContent, fileIoErr := ioutil.ReadFile(configFileName)
	if fileIoErr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be loaded\n", configFileName)
		log.Printf("%v", fileIoErr)
		return fileIoErr
	}
	config := ServerConfig{}
	if xmlerr := xml.Unmarshal(fileContent, &config); xmlerr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be Unmarshaled\n", configFileName, xmlerr)
		log.Printf("%v", xmlerr)
		return xmlerr
	}
	s.Config = config
	log.Println(" config loaded")
	return nil
}

func (s *Server) LoadLevels() error {
	log.Println("Loading levels ...")
	levelWalker := func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		fileContent, fileIoErr := ioutil.ReadFile(path)
		if fileIoErr != nil {
			log.Printf("\n")
			log.Printf("File %s could not be loaded\n", path)
			log.Printf("%v", fileIoErr)
			return fileIoErr
		}
		level := Level{}
		if xmlerr := xml.Unmarshal(fileContent, &level); xmlerr != nil {
			log.Printf("\n")
			log.Printf("File %s could not be Unmarshaled\n", path, xmlerr)
			log.Printf("%v", xmlerr)
			return xmlerr
		}
		log.Printf(" loaded: %s\n", info.Name())
		s.addLevel(level)
		return nil
	}

	return filepath.Walk(s.workingdir+"/static/levels/", levelWalker)
}

func (s *Server) getPlayerFileName(playerName string) (bool, string) {
	if !s.IsValidUsername(playerName) {
		return false, ""
	}
	return true, s.workingdir + "/static/player/" + playerName + ".player"
}

func (s *Server) IsValidUsername(playerName string) bool {
	r, err := regexp.Compile(`^[a-zA-Z0-9_-]{1,40}$`)
	if err != nil {
		return false
	}
	if !r.MatchString(playerName) {
		return false
	}
	return true
}

func (s *Server) LoadPlayer(playerName string) bool {
	ok, playerFileName := s.getPlayerFileName(playerName)
	if !ok {
		return false
	}
	log.Println("Loading player %s", playerFileName)

	fileContent, fileIoErr := ioutil.ReadFile(playerFileName)
	if fileIoErr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be loaded\n", playerFileName)
		log.Printf("%v", fileIoErr)
		//return fileIoErr
		return false
	}

	player := Player{}
	if xmlerr := xml.Unmarshal(fileContent, &player); xmlerr != nil {
		log.Printf("\n")
		log.Printf("File %s could not be Unmarshaled\n", playerFileName, xmlerr)
		log.Printf("%v", xmlerr)
		//return xmlerr
		return false
	}
	log.Printf(" loaded: %s", player.Gamename)
	s.addPlayer(player)

	return true
}

func (s *Server) addLevel(level Level) error {
	if level.Tag == "default" {
		log.Printf("default level loaded: %s\n", level.Key)
		s.defaultLevel = level
	}
	s.levels[level.Key] = level
	return nil
}

func (s *Server) addPlayer(player Player) error {
	s.players[player.Nickname] = player
	return nil
}

func (s *Server) GetPlayerByNick(nickname string) (Player, bool) {
	player, ok := s.players[nickname]
	return player, ok
}

func (s *Server) GetRoom(key string) (Level, bool) {
	level, ok := s.levels[key]
	return level, ok
}

func (s *Server) GetName() string {
	return s.Config.Name
}

func (s *Server) CreatePlayer(nick string, name string, playerType string) {
	ok, playerFileName := s.getPlayerFileName(nick)
	if !ok {
		return
	}
	if _, err := os.Stat(playerFileName); err == nil {
		s.LoadPlayer(nick)
		fmt.Printf("Player %s does already exists", nick)
		return
	}
	player := Player{
		Gamename:   name,
		Nickname:   nick,
		PlayerType: playerType,
		Position:   s.defaultLevel.Key,
	}
	s.addPlayer(player)
}

func (s *Server) SavePlayer(player Player) bool {
	data, err := xml.MarshalIndent(player, "", "    ")
	if err == nil {
		ok, playerFileName := s.getPlayerFileName(player.Nickname)
		if !ok {
			return false
		}

		if ioerror := ioutil.WriteFile(playerFileName, data, 0666); ioerror != nil {
			log.Println(ioerror)
			return true
		}
	} else {
		log.Println(err)
	}
	return false
}

func (s *Server) OnExit(client Client) {
	s.SavePlayer(client.Player)
	client.WriteLineToUser(fmt.Sprintf("Good bye %s", client.Player.Gamename))
}
<|endoftext|>


<commit_before>// Copyright 2016 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration,!quick

package service

import (
	"time"

	"github.com/control-center/serviced/zzk"
	. "gopkg.in/check.v1"
)

func (t *ZZKTest) TestRegisterExport(c *C) {

	// pre-requisites
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// watch the application path
	done := make(chan struct{})
	ok, ev, err := conn.ExistsW("/net/tenantid/app", done)
	c.Assert(err, IsNil)
	c.Assert(ok, Equals, false)

	// start
	shutdown := make(chan struct{})
	go func() {
		RegisterExport(shutdown, conn, "tenantid", ExportDetails{
			ExportBinding: ExportBinding{Application: "app"},
			InstanceID:    1,
		})
		close(done)
	}()

	var ch []string

	timer := time.NewTimer(time.Second)
	select {
	case <-ev:
		ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
		c.Assert(err, IsNil)
		if len(ch) == 0 {
			timer.Reset(time.Second)
			select {
			case <-ev:
				ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
				c.Assert(err, IsNil)
			case <-done:
				c.Fatalf("Listener exited unexpectedly")
			case <-timer.C:
				close(shutdown)
				c.Fatalf("Listener timed out")
			}
		}
	case <-done:
		c.Fatalf("Listener exited unexpectedly")
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Listener timed out")
	}
	c.Assert(ch, HasLen, 1)
	node := ch[0]

	// delete
	err = conn.Delete("/net/tenantid/app/" + node)
	c.Assert(err, IsNil)

	ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
	c.Assert(err, IsNil)
	if len(ch) == 0 {
		timer.Reset(time.Second)
		select {
		case <-ev:
			ch, err = conn.Children("/net/tenantid/app")
			c.Assert(err, IsNil)
		case <-done:
			c.Fatalf("Listener exited unexpectedly")
		case <-timer.C:
			close(shutdown)
			c.Fatalf("Listener timed out")
		}
	}
	c.Assert(ch, HasLen, 1)
	c.Assert(ch[0], Not(Equals), node)

	// shutdown
	close(shutdown)
	timer.Reset(time.Second)
	select {
	case <-done:
	case <-timer.C:
		c.Fatalf("Listener timed out")
	}
	ch, err = conn.Children("/net/tenantid/app")
	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (t *ZZKTest) TestTrackExports(c *C) {
	// pre-requisites
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// start the listener
	shutdown := make(chan struct{})
	ev := TrackExports(shutdown, conn, "tenantid", "app")

	// get empty list
	timer := time.NewTimer(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 0)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// add an export
	export := &ExportDetails{
		ExportBinding: ExportBinding{Application: "app"},
		InstanceID:    0,
	}
	err = conn.Create("/net/tenantid/app/0", export)
	c.Assert(err, IsNil)

	timer.Reset(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 1)
		c.Check(exports[0].InstanceID, Equals, export.InstanceID)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// add an export and delete the other export
	export = &ExportDetails{
		ExportBinding: ExportBinding{Application: "app"},
		InstanceID:    1,
	}
	err = conn.Create("/net/tenantid/app/1", export)
	c.Assert(err, IsNil)
	timer.Stop() // timer won't reset once it has triggered
	time.Sleep(time.Second)
	err = conn.Delete("/net/tenantid/app/0")
	c.Assert(err, IsNil)

	timer.Reset(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 1)
		c.Check(exports[0].InstanceID, Equals, export.InstanceID)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// shutdown
	close(shutdown)

	timer.Reset(time.Second)
	select {
	case _, ok := <-ev:
		c.Check(ok, Equals, false)
	case <-timer.C:
		c.Fatalf("Timed out waiting for exports")
	}
}
<commit_msg>added sleep timer to fix test<commit_after>// Copyright 2016 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration,!quick

package service

import (
	"time"

	"github.com/control-center/serviced/zzk"
	. "gopkg.in/check.v1"
)

func (t *ZZKTest) TestRegisterExport(c *C) {

	// pre-requisites
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// watch the application path
	done := make(chan struct{})
	ok, ev, err := conn.ExistsW("/net/tenantid/app", done)
	c.Assert(err, IsNil)
	c.Assert(ok, Equals, false)

	// start
	shutdown := make(chan struct{})
	go func() {
		RegisterExport(shutdown, conn, "tenantid", ExportDetails{
			ExportBinding: ExportBinding{Application: "app"},
			InstanceID:    1,
		})
		close(done)
	}()

	var ch []string

	timer := time.NewTimer(time.Second)
	select {
	case <-ev:
		ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
		c.Assert(err, IsNil)
		if len(ch) == 0 {
			timer.Reset(time.Second)
			select {
			case <-ev:
				ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
				c.Assert(err, IsNil)
			case <-done:
				c.Fatalf("Listener exited unexpectedly")
			case <-timer.C:
				close(shutdown)
				c.Fatalf("Listener timed out")
			}
		}
	case <-done:
		c.Fatalf("Listener exited unexpectedly")
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Listener timed out")
	}
	c.Assert(ch, HasLen, 1)
	node := ch[0]

	// delete
	err = conn.Delete("/net/tenantid/app/" + node)
	c.Assert(err, IsNil)

	ch, ev, err = conn.ChildrenW("/net/tenantid/app", done)
	c.Assert(err, IsNil)
	if len(ch) == 0 {
		timer.Reset(time.Second)
		select {
		case <-ev:
			ch, err = conn.Children("/net/tenantid/app")
			c.Assert(err, IsNil)
		case <-done:
			c.Fatalf("Listener exited unexpectedly")
		case <-timer.C:
			close(shutdown)
			c.Fatalf("Listener timed out")
		}
	}
	c.Assert(ch, HasLen, 1)
	c.Assert(ch[0], Not(Equals), node)

	// shutdown
	close(shutdown)
	timer.Reset(time.Second)
	select {
	case <-done:
	case <-timer.C:
		c.Fatalf("Listener timed out")
	}
	ch, err = conn.Children("/net/tenantid/app")
	c.Assert(err, IsNil)
	c.Assert(ch, HasLen, 0)
}

func (t *ZZKTest) TestTrackExports(c *C) {
	// pre-requisites
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// start the listener
	shutdown := make(chan struct{})
	ev := TrackExports(shutdown, conn, "tenantid", "app")

	// get empty list
	timer := time.NewTimer(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 0)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// add an export
	export := &ExportDetails{
		ExportBinding: ExportBinding{Application: "app"},
		InstanceID:    0,
	}
	err = conn.Create("/net/tenantid/app/0", export)
	c.Assert(err, IsNil)

	timer.Reset(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 1)
		c.Check(exports[0].InstanceID, Equals, export.InstanceID)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// add an export and delete the other export
	export = &ExportDetails{
		ExportBinding: ExportBinding{Application: "app"},
		InstanceID:    1,
	}
	err = conn.Create("/net/tenantid/app/1", export)
	c.Assert(err, IsNil)
	timer.Stop() // timer won't reset once it has triggered
	time.Sleep(time.Second)
	err = conn.Delete("/net/tenantid/app/0")
	c.Assert(err, IsNil)
	time.Sleep(time.Second)

	timer.Reset(time.Second)
	select {
	case exports := <-ev:
		c.Check(exports, HasLen, 1)
		c.Check(exports[0].InstanceID, Equals, export.InstanceID)
	case <-timer.C:
		close(shutdown)
		c.Fatalf("Timed out waiting for exports")
	}

	// shutdown
	close(shutdown)

	timer.Reset(time.Second)
	select {
	case _, ok := <-ev:
		c.Check(ok, Equals, false)
	case <-timer.C:
		c.Fatalf("Timed out waiting for exports")
	}
}
<|endoftext|>


<commit_before><commit_msg>Change log level for function ControllerRequestPassesFilter to debug<commit_after><|endoftext|>


<commit_before>package eval

//go:generate stringer -type=Type

import (
	"bytes"
	"errors"
	"fmt"
	"math/big"
	"reflect"
	"strconv"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/strutil"
)

// Value is the runtime representation of an elvish value.
type Value interface {
	Type() Type
	Reprer
}

type Reprer interface {
	Repr() string
}

// Booler represents a Value with a custom semantics of truthness. If a Value
// does not satisfy this interface, it is automatically true.
type Booler interface {
	Bool() bool
}

// Stringer represents a Value with a custom string representation. If a Value
// does not satisfy this interface, its Repr method is used.
type Stringer interface {
	String() string
}

// Indexer represents a Value that may be indexed.
type Indexer interface {
	Index(idx string) (Value, error)
}

// Caller represents a Value that may be called.
type Caller interface {
	Call(ec *evalCtx, args []Value)
}

// Type is the type of a value.
type Type int

const (
	TInvalid Type = iota
	TString
	TError
	TBool
	TList
	TMap
	TFn
	TRat
)

// Error definitions.
var (
	needIntIndex    = errors.New("need integer index")
	indexOutOfRange = errors.New("index out of range")
	errOnlyStrOrRat = errors.New("only str or rat may be converted to rat")
)

// String is just a string.
type String string

func (s String) Type() Type {
	return TString
}

func (s String) Repr() string {
	return parse.Quote(string(s))
}

func (s String) String() string {
	return string(s)
}

func (s String) Index(idx string) (Value, error) {
	i, err := strconv.Atoi(idx)
	if err != nil {
		return nil, err
	}

	r, err := strutil.NthRune(string(s), i)
	if err != nil {
		return nil, err
	}
	return String(string(r)), nil
}

// Bool represents truthness.
type Bool bool

func (b Bool) Type() Type {
	return TBool
}

func (b Bool) Repr() string {
	if b {
		return "$true"
	}
	return "$false"
}

func (b Bool) String() string {
	if b {
		return "true"
	}
	return "false"
}

func (b Bool) Bool() bool {
	return bool(b)
}

// Error represents runtime errors in elvish constructs.
type Error struct {
	inner error
}

func (e Error) Type() Type {
	return TError
}

func (e Error) Repr() string {
	if e.inner == nil {
		return "$ok"
	}
	if r, ok := e.inner.(Reprer); ok {
		return r.Repr()
	}
	return "?(error " + parse.Quote(e.inner.Error()) + ")"
}

func (e Error) String() string {
	if e.inner == nil {
		return "ok"
	}
	return e.inner.Error()
}

func (e Error) Bool() bool {
	return e.inner == nil
}

// Common Error values.
var (
	OK             = Error{nil}
	GenericFailure = Error{errors.New("generic failure")}
)

func NewFailure(text string) Error {
	return Error{errors.New(text)}
}

// multiError is multiple errors packed into one. It is used for reporting
// errors of pipelines, in which multiple forms may error.
type multiError struct {
	errors []Error
}

func (me multiError) Repr() string {
	b := new(bytes.Buffer)
	b.WriteString("(multi-error")
	for _, e := range me.errors {
		b.WriteString(" ")
		b.WriteString(e.Repr())
	}
	b.WriteString(")")
	return b.String()
}

func (me multiError) Error() string {
	b := new(bytes.Buffer)
	b.WriteString("(")
	for i, e := range me.errors {
		if i > 0 {
			b.WriteString(" | ")
		}
		b.WriteString(e.inner.Error())
	}
	b.WriteString(")")
	return b.String()
}

func newMultiError(es ...Error) Error {
	return Error{multiError{es}}
}

// Flow is a special type of Error used for control flows.
type flow uint

const (
	Return flow = iota
	Break
	Continue
)

var flowNames = [...]string{
	"return", "break", "continue",
}

func (f flow) Repr() string {
	return "?(" + f.Error() + ")"
}

func (f flow) Error() string {
	if f >= flow(len(flowNames)) {
		return fmt.Sprintf("!(BAD FLOW: %v)", f)
	}
	return flowNames[f]
}

func allok(es []Error) bool {
	for _, e := range es {
		if e.inner != nil {
			return false
		}
	}
	return true
}

// List is a list of Value's.
type List struct {
	inner *[]Value
}

func NewList(vs ...Value) List {
	return List{&vs}
}

func (l List) Type() Type {
	return TList
}

func (l List) appendStrings(ss []string) {
	for _, s := range ss {
		*l.inner = append(*l.inner, String(s))
	}
}

func (l List) Repr() string {
	buf := new(bytes.Buffer)
	buf.WriteRune('[')
	for i, v := range *l.inner {
		if i > 0 {
			buf.WriteByte(' ')
		}
		buf.WriteString(v.Repr())
	}
	buf.WriteRune(']')
	return buf.String()
}

func (l List) Index(idx string) (Value, error) {
	i, err := strconv.Atoi(idx)
	if err != nil {
		return nil, err
	}
	if i < 0 {
		i += len(*l.inner)
	}
	if i < 0 || i >= len(*l.inner) {
		return nil, indexOutOfRange
	}
	return (*l.inner)[i], nil
}

// Map is a map from string to Value.
// TODO(xiaq): support Value keys.
type Map map[string]Value

func NewMap() Map {
	return Map(make(map[string]Value))
}

func (m Map) Type() Type {
	return TMap
}

func (m Map) Repr() string {
	buf := new(bytes.Buffer)
	buf.WriteRune('[')
	for k, v := range m {
		if buf.Len() > 1 {
			buf.WriteByte(' ')
		}
		buf.WriteByte('&')
		buf.WriteString(parse.Quote(k))
		buf.WriteByte(' ')
		buf.WriteString(v.Repr())
	}
	buf.WriteRune(']')
	return buf.String()
}

func (m Map) Index(idx string) (Value, error) {
	v, ok := m[idx]
	if !ok {
		return nil, errors.New("no such key: " + idx)
	}
	return v, nil
}

// Closure is a closure.
type Closure struct {
	ArgNames []string
	Op       op
	Captured map[string]Variable
}

func (c *Closure) Type() Type {
	return TFn
}

func newClosure(a []string, op op, e map[string]Variable) *Closure {
	return &Closure{a, op, e}
}

func (c *Closure) Repr() string {
	return fmt.Sprintf("<Closure%v>", *c)
}

// BuiltinFn is a builtin function.
type BuiltinFn struct {
	Name string
	Impl func(*evalCtx, []Value)
}

func (b *BuiltinFn) Type() Type {
	return TFn
}

func (b *BuiltinFn) Repr() string {
	return "$" + FnPrefix + b.Name
}

// ExternalCmd is an external command.
type ExternalCmd struct {
	Name string
}

func (e ExternalCmd) Type() Type {
	return TFn
}

func (e ExternalCmd) Repr() string {
	return "<external " + e.Name + " >"
}

// Rat is a rational number
type Rat struct {
	b *big.Rat
}

func (r Rat) Type() Type {
	return TRat
}

func (r Rat) Repr() string {
	return "(rat " + r.String() + ")"
}

func (r Rat) String() string {
	if r.b.IsInt() {
		return r.b.Num().String()
	}
	return r.b.String()
}

func evalIndex(ec *evalCtx, l, r Value, lp, rp int) Value {
	left, ok := l.(Indexer)
	if !ok {
		ec.errorf(lp, "%s value cannot be indexed", l.Type())
	}

	right, ok := r.(String)
	if !ok {
		ec.errorf(rp, "%s invalid cannot be used as index", r.Type())
	}

	v, err := left.Index(string(right))
	if err != nil {
		ec.errorf(lp, "%v", err)
	}
	return v
}

// FromJSONInterface converts a interface{} that results from json.Unmarshal to
// a Value.
func FromJSONInterface(v interface{}) Value {
	if v == nil {
		// TODO Use a more appropriate type
		return String("")
	}
	switch v.(type) {
	case bool:
		return Bool(v.(bool))
	case float64, string:
		// TODO Use a numeric type for float64
		return String(fmt.Sprint(v))
	case []interface{}:
		a := v.([]interface{})
		vs := make([]Value, len(a))
		for i, v := range a {
			vs[i] = FromJSONInterface(v)
		}
		return List{&vs}
	case map[string]interface{}:
		m := v.(map[string]interface{})
		m_ := NewMap()
		for k, v := range m {
			m_[k] = FromJSONInterface(v)
		}
		return m_
	default:
		throw(fmt.Errorf("unexpected json type: %T", v))
		return nil // no reached
	}
}

// Eq compares two Value's.
func Eq(a, b Value) bool {
	// BUG(xiaq): valueEq uses reflect.DeepEqual to check the equality of two
	// values, may can become wrong when values get more complex.
	return reflect.DeepEqual(a, b)
}

// ToString converts a Value to String. When the Value type implements
// String(), it is used. Otherwise Repr() is used.
func ToString(v Value) string {
	if s, ok := v.(Stringer); ok {
		return s.String()
	}
	return v.Repr()
}

// ToBool converts a Value to bool. When the Value type implements Bool(), it
// is used. Otherwise it is considered true.
func ToBool(v Value) bool {
	if b, ok := v.(Booler); ok {
		return b.Bool()
	}
	return true
}

func allTrue(vs []Value) bool {
	for _, v := range vs {
		if !ToBool(v) {
			return false
		}
	}
	return true
}

// ToRat converts a Value to rat. A str can be converted to a rat if it can be
// parsed. A rat is returned as-is. Other types of values cannot be converted.
func ToRat(v Value) (Rat, error) {
	switch v := v.(type) {
	case Rat:
		return v, nil
	case String:
		r := big.Rat{}
		_, err := fmt.Sscanln(string(v), &r)
		if err != nil {
			return Rat{}, fmt.Errorf("%s cannot be parsed as rat", v.Repr())
		}
		return Rat{&r}, nil
	default:
		return Rat{}, errOnlyStrOrRat
	}
}
<commit_msg>typo fix<commit_after>package eval

//go:generate stringer -type=Type

import (
	"bytes"
	"errors"
	"fmt"
	"math/big"
	"reflect"
	"strconv"

	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/strutil"
)

// Value is the runtime representation of an elvish value.
type Value interface {
	Type() Type
	Reprer
}

type Reprer interface {
	Repr() string
}

// Booler represents a Value with a custom semantics of truthness. If a Value
// does not satisfy this interface, it is automatically true.
type Booler interface {
	Bool() bool
}

// Stringer represents a Value with a custom string representation. If a Value
// does not satisfy this interface, its Repr method is used.
type Stringer interface {
	String() string
}

// Indexer represents a Value that may be indexed.
type Indexer interface {
	Index(idx string) (Value, error)
}

// Caller represents a Value that may be called.
type Caller interface {
	Call(ec *evalCtx, args []Value)
}

// Type is the type of a value.
type Type int

const (
	TInvalid Type = iota
	TString
	TError
	TBool
	TList
	TMap
	TFn
	TRat
)

// Error definitions.
var (
	needIntIndex    = errors.New("need integer index")
	indexOutOfRange = errors.New("index out of range")
	errOnlyStrOrRat = errors.New("only str or rat may be converted to rat")
)

// String is just a string.
type String string

func (s String) Type() Type {
	return TString
}

func (s String) Repr() string {
	return parse.Quote(string(s))
}

func (s String) String() string {
	return string(s)
}

func (s String) Index(idx string) (Value, error) {
	i, err := strconv.Atoi(idx)
	if err != nil {
		return nil, err
	}

	r, err := strutil.NthRune(string(s), i)
	if err != nil {
		return nil, err
	}
	return String(string(r)), nil
}

// Bool represents truthness.
type Bool bool

func (b Bool) Type() Type {
	return TBool
}

func (b Bool) Repr() string {
	if b {
		return "$true"
	}
	return "$false"
}

func (b Bool) String() string {
	if b {
		return "true"
	}
	return "false"
}

func (b Bool) Bool() bool {
	return bool(b)
}

// Error represents runtime errors in elvish constructs.
type Error struct {
	inner error
}

func (e Error) Type() Type {
	return TError
}

func (e Error) Repr() string {
	if e.inner == nil {
		return "$ok"
	}
	if r, ok := e.inner.(Reprer); ok {
		return r.Repr()
	}
	return "?(error " + parse.Quote(e.inner.Error()) + ")"
}

func (e Error) String() string {
	if e.inner == nil {
		return "ok"
	}
	return e.inner.Error()
}

func (e Error) Bool() bool {
	return e.inner == nil
}

// Common Error values.
var (
	OK             = Error{nil}
	GenericFailure = Error{errors.New("generic failure")}
)

func NewFailure(text string) Error {
	return Error{errors.New(text)}
}

// multiError is multiple errors packed into one. It is used for reporting
// errors of pipelines, in which multiple forms may error.
type multiError struct {
	errors []Error
}

func (me multiError) Repr() string {
	b := new(bytes.Buffer)
	b.WriteString("(multi-error")
	for _, e := range me.errors {
		b.WriteString(" ")
		b.WriteString(e.Repr())
	}
	b.WriteString(")")
	return b.String()
}

func (me multiError) Error() string {
	b := new(bytes.Buffer)
	b.WriteString("(")
	for i, e := range me.errors {
		if i > 0 {
			b.WriteString(" | ")
		}
		b.WriteString(e.inner.Error())
	}
	b.WriteString(")")
	return b.String()
}

func newMultiError(es ...Error) Error {
	return Error{multiError{es}}
}

// Flow is a special type of Error used for control flows.
type flow uint

const (
	Return flow = iota
	Break
	Continue
)

var flowNames = [...]string{
	"return", "break", "continue",
}

func (f flow) Repr() string {
	return "?(" + f.Error() + ")"
}

func (f flow) Error() string {
	if f >= flow(len(flowNames)) {
		return fmt.Sprintf("!(BAD FLOW: %v)", f)
	}
	return flowNames[f]
}

func allok(es []Error) bool {
	for _, e := range es {
		if e.inner != nil {
			return false
		}
	}
	return true
}

// List is a list of Value's.
type List struct {
	inner *[]Value
}

func NewList(vs ...Value) List {
	return List{&vs}
}

func (l List) Type() Type {
	return TList
}

func (l List) appendStrings(ss []string) {
	for _, s := range ss {
		*l.inner = append(*l.inner, String(s))
	}
}

func (l List) Repr() string {
	buf := new(bytes.Buffer)
	buf.WriteRune('[')
	for i, v := range *l.inner {
		if i > 0 {
			buf.WriteByte(' ')
		}
		buf.WriteString(v.Repr())
	}
	buf.WriteRune(']')
	return buf.String()
}

func (l List) Index(idx string) (Value, error) {
	i, err := strconv.Atoi(idx)
	if err != nil {
		return nil, err
	}
	if i < 0 {
		i += len(*l.inner)
	}
	if i < 0 || i >= len(*l.inner) {
		return nil, indexOutOfRange
	}
	return (*l.inner)[i], nil
}

// Map is a map from string to Value.
// TODO(xiaq): support Value keys.
type Map map[string]Value

func NewMap() Map {
	return Map(make(map[string]Value))
}

func (m Map) Type() Type {
	return TMap
}

func (m Map) Repr() string {
	buf := new(bytes.Buffer)
	buf.WriteRune('[')
	for k, v := range m {
		if buf.Len() > 1 {
			buf.WriteByte(' ')
		}
		buf.WriteByte('&')
		buf.WriteString(parse.Quote(k))
		buf.WriteByte(' ')
		buf.WriteString(v.Repr())
	}
	buf.WriteRune(']')
	return buf.String()
}

func (m Map) Index(idx string) (Value, error) {
	v, ok := m[idx]
	if !ok {
		return nil, errors.New("no such key: " + idx)
	}
	return v, nil
}

// Closure is a closure.
type Closure struct {
	ArgNames []string
	Op       op
	Captured map[string]Variable
}

func (c *Closure) Type() Type {
	return TFn
}

func newClosure(a []string, op op, e map[string]Variable) *Closure {
	return &Closure{a, op, e}
}

func (c *Closure) Repr() string {
	return fmt.Sprintf("<Closure%v>", *c)
}

// BuiltinFn is a builtin function.
type BuiltinFn struct {
	Name string
	Impl func(*evalCtx, []Value)
}

func (b *BuiltinFn) Type() Type {
	return TFn
}

func (b *BuiltinFn) Repr() string {
	return "$" + FnPrefix + b.Name
}

// ExternalCmd is an external command.
type ExternalCmd struct {
	Name string
}

func (e ExternalCmd) Type() Type {
	return TFn
}

func (e ExternalCmd) Repr() string {
	return "<external " + e.Name + " >"
}

// Rat is a rational number.
type Rat struct {
	b *big.Rat
}

func (r Rat) Type() Type {
	return TRat
}

func (r Rat) Repr() string {
	return "(rat " + r.String() + ")"
}

func (r Rat) String() string {
	if r.b.IsInt() {
		return r.b.Num().String()
	}
	return r.b.String()
}

func evalIndex(ec *evalCtx, l, r Value, lp, rp int) Value {
	left, ok := l.(Indexer)
	if !ok {
		ec.errorf(lp, "%s value cannot be indexed", l.Type())
	}

	right, ok := r.(String)
	if !ok {
		ec.errorf(rp, "%s invalid cannot be used as index", r.Type())
	}

	v, err := left.Index(string(right))
	if err != nil {
		ec.errorf(lp, "%v", err)
	}
	return v
}

// FromJSONInterface converts a interface{} that results from json.Unmarshal to
// a Value.
func FromJSONInterface(v interface{}) Value {
	if v == nil {
		// TODO Use a more appropriate type
		return String("")
	}
	switch v.(type) {
	case bool:
		return Bool(v.(bool))
	case float64, string:
		// TODO Use a numeric type for float64
		return String(fmt.Sprint(v))
	case []interface{}:
		a := v.([]interface{})
		vs := make([]Value, len(a))
		for i, v := range a {
			vs[i] = FromJSONInterface(v)
		}
		return List{&vs}
	case map[string]interface{}:
		m := v.(map[string]interface{})
		m_ := NewMap()
		for k, v := range m {
			m_[k] = FromJSONInterface(v)
		}
		return m_
	default:
		throw(fmt.Errorf("unexpected json type: %T", v))
		return nil // not reached
	}
}

// Eq compares two Value's.
func Eq(a, b Value) bool {
	// BUG(xiaq): valueEq uses reflect.DeepEqual to check the equality of two
	// values, may can become wrong when values get more complex.
	return reflect.DeepEqual(a, b)
}

// ToString converts a Value to String. When the Value type implements
// String(), it is used. Otherwise Repr() is used.
func ToString(v Value) string {
	if s, ok := v.(Stringer); ok {
		return s.String()
	}
	return v.Repr()
}

// ToBool converts a Value to bool. When the Value type implements Bool(), it
// is used. Otherwise it is considered true.
func ToBool(v Value) bool {
	if b, ok := v.(Booler); ok {
		return b.Bool()
	}
	return true
}

func allTrue(vs []Value) bool {
	for _, v := range vs {
		if !ToBool(v) {
			return false
		}
	}
	return true
}

// ToRat converts a Value to rat. A str can be converted to a rat if it can be
// parsed. A rat is returned as-is. Other types of values cannot be converted.
func ToRat(v Value) (Rat, error) {
	switch v := v.(type) {
	case Rat:
		return v, nil
	case String:
		r := big.Rat{}
		_, err := fmt.Sscanln(string(v), &r)
		if err != nil {
			return Rat{}, fmt.Errorf("%s cannot be parsed as rat", v.Repr())
		}
		return Rat{&r}, nil
	default:
		return Rat{}, errOnlyStrOrRat
	}
}
<|endoftext|>


<commit_before>package core

import (
	"fmt"
	"strings"
	"time"
)

type DbType string

type Uri struct {
	DbType  DbType
	Proto   string
	Host    string
	Port    string
	DbName  string
	User    string
	Passwd  string
	Charset string
	Laddr   string
	Raddr   string
	Timeout time.Duration
	Schema  string
}

// a dialect is a driver's wrapper
type Dialect interface {
	SetLogger(logger ILogger)
	Init(*DB, *Uri, string, string) error
	URI() *Uri
	DB() *DB
	DBType() DbType
	SqlType(*Column) string
	FormatBytes(b []byte) string

	DriverName() string
	DataSourceName() string

	QuoteStr() string
	IsReserved(string) bool
	Quote(string) string
	AndStr() string
	OrStr() string
	EqStr() string
	RollBackStr() string
	AutoIncrStr() string

	SupportInsertMany() bool
	SupportEngine() bool
	SupportCharset() bool
	SupportDropIfExists() bool
	IndexOnTable() bool
	ShowCreateNull() bool

	IndexCheckSql(tableName, idxName string) (string, []interface{})
	TableCheckSql(tableName string) (string, []interface{})

	IsColumnExist(tableName string, colName string) (bool, error)

	CreateTableSql(table *Table, tableName, storeEngine, charset string) string
	DropTableSql(tableName string) string
	CreateIndexSql(tableName string, index *Index) string
	DropIndexSql(tableName string, index *Index) string

	ModifyColumnSql(tableName string, col *Column) string

	ForUpdateSql(query string) string

	//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
	//MustDropTable(tableName string) error

	GetColumns(tableName string) ([]string, map[string]*Column, error)
	GetTables() ([]*Table, error)
	GetIndexes(tableName string) (map[string]*Index, error)

	Filters() []Filter
}

func OpenDialect(dialect Dialect) (*DB, error) {
	return Open(dialect.DriverName(), dialect.DataSourceName())
}

type Base struct {
	db             *DB
	dialect        Dialect
	driverName     string
	dataSourceName string
	logger         ILogger
	*Uri
}

func (b *Base) DB() *DB {
	return b.db
}

func (b *Base) SetLogger(logger ILogger) {
	b.logger = logger
}

func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
	b.db, b.dialect, b.Uri = db, dialect, uri
	b.driverName, b.dataSourceName = drivername, dataSourceName
	return nil
}

func (b *Base) URI() *Uri {
	return b.Uri
}

func (b *Base) DBType() DbType {
	return b.Uri.DbType
}

func (b *Base) FormatBytes(bs []byte) string {
	return fmt.Sprintf("0x%x", bs)
}

func (b *Base) DriverName() string {
	return b.driverName
}

func (b *Base) ShowCreateNull() bool {
	return true
}

func (b *Base) DataSourceName() string {
	return b.dataSourceName
}

func (b *Base) AndStr() string {
	return "AND"
}

func (b *Base) OrStr() string {
	return "OR"
}

func (b *Base) EqStr() string {
	return "="
}

func (db *Base) RollBackStr() string {
	return "ROLL BACK"
}

func (db *Base) SupportDropIfExists() bool {
	return true
}

func (db *Base) DropTableSql(tableName string) string {
	return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName)
}

func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
	db.LogSQL(query, args)
	rows, err := db.DB().Query(query, args...)
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
	query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
	query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1)
	return db.HasRecords(query, db.DbName, tableName, colName)
}

/*
func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error {
	sql, args := db.dialect.TableCheckSql(tableName)
	rows, err := db.DB().Query(sql, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql, args)
	}
	if err != nil {
		return err
	}
	defer rows.Close()

	if rows.Next() {
		return nil
	}

	sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset)
	_, err = db.DB().Exec(sql)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql)
	}
	return err
}*/

func (db *Base) CreateIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var unique string
	var idxName string
	if index.Type == UniqueType {
		unique = " UNIQUE"
	}
	idxName = index.XName(tableName)
	return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
		quote(idxName), quote(tableName),
		quote(strings.Join(index.Cols, quote(","))))
}

func (db *Base) DropIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var name string
	if index.IsRegular {
		name = index.XName(tableName)
	} else {
		name = index.Name
	}
	return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
}

func (db *Base) ModifyColumnSql(tableName string, col *Column) string {
	return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, col.StringNoPk(db.dialect))
}

func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
	var sql string
	sql = "CREATE TABLE IF NOT EXISTS "
	if tableName == "" {
		tableName = table.Name
	}

	sql += b.dialect.Quote(tableName)
	sql += " ("

	if len(table.ColumnsSeq()) > 0 {
		pkList := table.PrimaryKeys

		for _, colName := range table.ColumnsSeq() {
			col := table.GetColumn(colName)
			if col.IsPrimaryKey && len(pkList) == 1 {
				sql += col.String(b.dialect)
			} else {
				sql += col.StringNoPk(b.dialect)
			}
			sql = strings.TrimSpace(sql)
			sql += ", "
		}

		if len(pkList) > 1 {
			sql += "PRIMARY KEY ( "
			sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
			sql += " ), "
		}

		sql = sql[:len(sql)-2]
	}
	sql += ")"

	if b.dialect.SupportEngine() && storeEngine != "" {
		sql += " ENGINE=" + storeEngine
	}
	if b.dialect.SupportCharset() {
		if len(charset) == 0 {
			charset = b.dialect.URI().Charset
		}
		if len(charset) > 0 {
			sql += " DEFAULT CHARSET " + charset
		}
	}

	return sql
}

func (b *Base) ForUpdateSql(query string) string {
	return query + " FOR UPDATE"
}

func (b *Base) LogSQL(sql string, args []interface{}) {
	if b.logger != nil && b.logger.IsShowSQL() {
		if len(args) > 0 {
			b.logger.Info("[sql]", sql, args)
		} else {
			b.logger.Info("[sql]", sql)
		}
	}
}

var (
	dialects = map[DbType]func() Dialect{}
)

// RegisterDialect register database dialect
func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
	if dialectFunc == nil {
		panic("core: Register dialect is nil")
	}
	dialects[dbName] = dialectFunc // !nashtsai! allow override dialect
}

// QueryDialect query if registed database dialect
func QueryDialect(dbName DbType) Dialect {
	if d, ok := dialects[dbName]; ok {
		return d()
	}
	return nil
}
<commit_msg>query dialect ignore case sensitive<commit_after>package core

import (
	"fmt"
	"strings"
	"time"
)

type DbType string

type Uri struct {
	DbType  DbType
	Proto   string
	Host    string
	Port    string
	DbName  string
	User    string
	Passwd  string
	Charset string
	Laddr   string
	Raddr   string
	Timeout time.Duration
	Schema  string
}

// a dialect is a driver's wrapper
type Dialect interface {
	SetLogger(logger ILogger)
	Init(*DB, *Uri, string, string) error
	URI() *Uri
	DB() *DB
	DBType() DbType
	SqlType(*Column) string
	FormatBytes(b []byte) string

	DriverName() string
	DataSourceName() string

	QuoteStr() string
	IsReserved(string) bool
	Quote(string) string
	AndStr() string
	OrStr() string
	EqStr() string
	RollBackStr() string
	AutoIncrStr() string

	SupportInsertMany() bool
	SupportEngine() bool
	SupportCharset() bool
	SupportDropIfExists() bool
	IndexOnTable() bool
	ShowCreateNull() bool

	IndexCheckSql(tableName, idxName string) (string, []interface{})
	TableCheckSql(tableName string) (string, []interface{})

	IsColumnExist(tableName string, colName string) (bool, error)

	CreateTableSql(table *Table, tableName, storeEngine, charset string) string
	DropTableSql(tableName string) string
	CreateIndexSql(tableName string, index *Index) string
	DropIndexSql(tableName string, index *Index) string

	ModifyColumnSql(tableName string, col *Column) string

	ForUpdateSql(query string) string

	//CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error
	//MustDropTable(tableName string) error

	GetColumns(tableName string) ([]string, map[string]*Column, error)
	GetTables() ([]*Table, error)
	GetIndexes(tableName string) (map[string]*Index, error)

	Filters() []Filter
}

func OpenDialect(dialect Dialect) (*DB, error) {
	return Open(dialect.DriverName(), dialect.DataSourceName())
}

type Base struct {
	db             *DB
	dialect        Dialect
	driverName     string
	dataSourceName string
	logger         ILogger
	*Uri
}

func (b *Base) DB() *DB {
	return b.db
}

func (b *Base) SetLogger(logger ILogger) {
	b.logger = logger
}

func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error {
	b.db, b.dialect, b.Uri = db, dialect, uri
	b.driverName, b.dataSourceName = drivername, dataSourceName
	return nil
}

func (b *Base) URI() *Uri {
	return b.Uri
}

func (b *Base) DBType() DbType {
	return b.Uri.DbType
}

func (b *Base) FormatBytes(bs []byte) string {
	return fmt.Sprintf("0x%x", bs)
}

func (b *Base) DriverName() string {
	return b.driverName
}

func (b *Base) ShowCreateNull() bool {
	return true
}

func (b *Base) DataSourceName() string {
	return b.dataSourceName
}

func (b *Base) AndStr() string {
	return "AND"
}

func (b *Base) OrStr() string {
	return "OR"
}

func (b *Base) EqStr() string {
	return "="
}

func (db *Base) RollBackStr() string {
	return "ROLL BACK"
}

func (db *Base) SupportDropIfExists() bool {
	return true
}

func (db *Base) DropTableSql(tableName string) string {
	return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName)
}

func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) {
	db.LogSQL(query, args)
	rows, err := db.DB().Query(query, args...)
	if err != nil {
		return false, err
	}
	defer rows.Close()

	if rows.Next() {
		return true, nil
	}
	return false, nil
}

func (db *Base) IsColumnExist(tableName, colName string) (bool, error) {
	query := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?"
	query = strings.Replace(query, "`", db.dialect.QuoteStr(), -1)
	return db.HasRecords(query, db.DbName, tableName, colName)
}

/*
func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error {
	sql, args := db.dialect.TableCheckSql(tableName)
	rows, err := db.DB().Query(sql, args...)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql, args)
	}
	if err != nil {
		return err
	}
	defer rows.Close()

	if rows.Next() {
		return nil
	}

	sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset)
	_, err = db.DB().Exec(sql)
	if db.Logger != nil {
		db.Logger.Info("[sql]", sql)
	}
	return err
}*/

func (db *Base) CreateIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var unique string
	var idxName string
	if index.Type == UniqueType {
		unique = " UNIQUE"
	}
	idxName = index.XName(tableName)
	return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique,
		quote(idxName), quote(tableName),
		quote(strings.Join(index.Cols, quote(","))))
}

func (db *Base) DropIndexSql(tableName string, index *Index) string {
	quote := db.dialect.Quote
	var name string
	if index.IsRegular {
		name = index.XName(tableName)
	} else {
		name = index.Name
	}
	return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName))
}

func (db *Base) ModifyColumnSql(tableName string, col *Column) string {
	return fmt.Sprintf("alter table %s MODIFY COLUMN %s", tableName, col.StringNoPk(db.dialect))
}

func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string {
	var sql string
	sql = "CREATE TABLE IF NOT EXISTS "
	if tableName == "" {
		tableName = table.Name
	}

	sql += b.dialect.Quote(tableName)
	sql += " ("

	if len(table.ColumnsSeq()) > 0 {
		pkList := table.PrimaryKeys

		for _, colName := range table.ColumnsSeq() {
			col := table.GetColumn(colName)
			if col.IsPrimaryKey && len(pkList) == 1 {
				sql += col.String(b.dialect)
			} else {
				sql += col.StringNoPk(b.dialect)
			}
			sql = strings.TrimSpace(sql)
			sql += ", "
		}

		if len(pkList) > 1 {
			sql += "PRIMARY KEY ( "
			sql += b.dialect.Quote(strings.Join(pkList, b.dialect.Quote(",")))
			sql += " ), "
		}

		sql = sql[:len(sql)-2]
	}
	sql += ")"

	if b.dialect.SupportEngine() && storeEngine != "" {
		sql += " ENGINE=" + storeEngine
	}
	if b.dialect.SupportCharset() {
		if len(charset) == 0 {
			charset = b.dialect.URI().Charset
		}
		if len(charset) > 0 {
			sql += " DEFAULT CHARSET " + charset
		}
	}

	return sql
}

func (b *Base) ForUpdateSql(query string) string {
	return query + " FOR UPDATE"
}

func (b *Base) LogSQL(sql string, args []interface{}) {
	if b.logger != nil && b.logger.IsShowSQL() {
		if len(args) > 0 {
			b.logger.Info("[sql]", sql, args)
		} else {
			b.logger.Info("[sql]", sql)
		}
	}
}

var (
	dialects = map[string]func() Dialect{}
)

// RegisterDialect register database dialect
func RegisterDialect(dbName DbType, dialectFunc func() Dialect) {
	if dialectFunc == nil {
		panic("core: Register dialect is nil")
	}
	dialects[strings.ToLower(string(dbName))] = dialectFunc // !nashtsai! allow override dialect
}

// QueryDialect query if registed database dialect
func QueryDialect(dbName DbType) Dialect {
	if d, ok := dialects[strings.ToLower(string(dbName))]; ok {
		return d()
	}
	return nil
}
<|endoftext|>