<commit_before>package main

import (
	"fmt"
	"log"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)

// DaoBuilds serializes to/from AWS DynamoDB
type DaoBuilds struct {
	svc *dynamodb.DynamoDB
}

type BuildRecordKey struct {
	Build string
}

type BuildRecord struct {
	BuildRecordKey
	Name        string
	Namespace   string
	Number      int
	LastUpdated string
}

type BuildCounterRecord struct {
	BuildRecordKey
	Count int
}

var svc = dynamodb.New(session.New(&aws.Config{Region: awsRegion}))

// NewDaoBuilds are used to perform CRUD operations on Builds
func NewDaoBuilds() (*DaoBuilds, error) {
	if svc != nil {
		return &DaoBuilds{svc: svc}, nil
	}
	return nil, fmt.Errorf("unable to construct DaoBuilds object with a <nil> DynamoDB attribute")
}

func formatBuildKey(namespace string, build string, number int) string {
	return namespace + "/" + build + "/" + strconv.Itoa(number)
}

func (dao *DaoBuilds) incrementBuildCounter(build string) (int, error) {
	o := &BuildCounterRecord{
		BuildRecordKey: BuildRecordKey{
			Build: build,
		},
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return 0, err
	}

	atomic_update := &struct {
		IncrementValue int
	}{IncrementValue: 1}
	av, err := dynamodbattribute.MarshalMap(atomic_update)
	if err != nil {
		log.Fatal(err)
		return 0, err
	}
	params := &dynamodb.UpdateItemInput{
		TableName:                 aws.String("jeeves.dev.buildCounters"),
		Key:                       item,
		UpdateExpression:          aws.String("SET Count = Count + :incrementValue"),
		ExpressionAttributeValues: av,
	}

	resp, err := dao.svc.UpdateItem(params)
	if err != nil {
		log.Fatal(err)
		return 0, err
	}

	out := &BuildCounterRecord{}
	err = dynamodbattribute.UnmarshalMap(resp.Attributes, out)

	return out.Count, err
}

func (dao *DaoBuilds) Persist(obj *Build) error {

	number, err := dao.incrementBuildCounter(obj.Namespace + "/" + obj.Build)
	if err != nil {
		log.Fatal(err)
		return err
	}
	log.Printf("Build Counter for %s/%s: %d\n", obj.Namespace, obj.Build, number)

	o := &BuildRecord{
		BuildRecordKey: BuildRecordKey{
			Build: formatBuildKey(obj.Namespace, obj.Build, obj.Number),
		},
		Name:        obj.Build,
		Namespace:   obj.Namespace,
		Number:      obj.Number,
		LastUpdated: time.Now().Format(time.RFC3339Nano),
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return err
	}

	params := &dynamodb.PutItemInput{
		TableName: aws.String("jeeves.dev.builds"),
		Item:      item,
	}

	_, err = dao.svc.PutItem(params)
	if err != nil {
		log.Fatal(err)
		return err
	}

	return nil
}

func (dao *DaoBuilds) Fetch(namespace string, buildName string, number int) (*Build, error) {

	o := &BuildRecordKey{
		Build: formatBuildKey(namespace, buildName, number),
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	params := &dynamodb.GetItemInput{
		TableName: aws.String("jeeves.dev.builds"),
		Key:       item,
	}

	resp, err := dao.svc.GetItem(params)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	out := &BuildRecord{}
	err = dynamodbattribute.UnmarshalMap(resp.Item, out)

	updated, err := time.Parse(time.RFC3339Nano, out.LastUpdated)
	if err != nil {
		updated = time.Time{}
	}

	result := &Build{
		Build:       out.Name,
		Namespace:   out.Namespace,
		Number:      out.Number,
		LastUpdated: updated,
	}
	return result, nil
}
<commit_msg>build counters now incrementing via DynamoDB's 'atomic counter' feature<commit_after>package main

import (
	"fmt"
	"log"
	"strconv"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)

// DaoBuilds serializes to/from AWS DynamoDB
type DaoBuilds struct {
	svc *dynamodb.DynamoDB
}

type BuildRecordKey struct {
	Build string
}

type BuildRecord struct {
	BuildRecordKey
	Name        string
	Namespace   string
	Number      int
	LastUpdated string
}

type BuildCounterRecord struct {
	BuildRecordKey
	CurrentCount int
}

var svc = dynamodb.New(session.New(&aws.Config{Region: awsRegion}))

// NewDaoBuilds are used to perform CRUD operations on Builds
func NewDaoBuilds() (*DaoBuilds, error) {
	if svc != nil {
		return &DaoBuilds{svc: svc}, nil
	}
	return nil, fmt.Errorf("unable to construct DaoBuilds object with a <nil> DynamoDB attribute")
}

func formatBuildKey(namespace string, build string, number int) string {
	return namespace + "/" + build + "/" + strconv.Itoa(number)
}

// use DynamoDB's "atomic counter" "feature" to get the next build number
// for each namespace/buildName pair
func (dao *DaoBuilds) incrementBuildCounter(build string) (int, error) {
	o := &BuildRecordKey{
		Build: build,
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return 0, err
	}

	if err != nil {
		log.Fatal(err)
		return 0, err
	}

	params := &dynamodb.UpdateItemInput{
		TableName:        aws.String("jeeves.dev.buildCounters"),
		Key:              item,
		UpdateExpression: aws.String("ADD CurrentCount :incrementValue"),
		ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
			":incrementValue": {
				N: aws.String("1"),
			},
		},
		ReturnValues: aws.String("ALL_NEW"),
	}

	resp, err := dao.svc.UpdateItem(params)
	if err != nil {
		log.Fatalf("updating %v encountered: %v", *params, err)
		return 0, err
	}

	out := &BuildCounterRecord{}
	err = dynamodbattribute.UnmarshalMap(resp.Attributes, out)

	return out.CurrentCount, err
}

func (dao *DaoBuilds) Persist(obj *Build) error {

	number, err := dao.incrementBuildCounter(obj.Namespace + "/" + obj.Build)
	if err != nil {
		log.Fatal(err)
		return err
	}
	log.Printf("Build Counter for %s/%s: %d\n", obj.Namespace, obj.Build, number)

	o := &BuildRecord{
		BuildRecordKey: BuildRecordKey{
			Build: formatBuildKey(obj.Namespace, obj.Build, obj.Number),
		},
		Name:        obj.Build,
		Namespace:   obj.Namespace,
		Number:      obj.Number,
		LastUpdated: time.Now().Format(time.RFC3339Nano),
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return err
	}

	params := &dynamodb.PutItemInput{
		TableName: aws.String("jeeves.dev.builds"),
		Item:      item,
	}

	_, err = dao.svc.PutItem(params)
	if err != nil {
		log.Fatal(err)
		return err
	}

	return nil
}

func (dao *DaoBuilds) Fetch(namespace string, buildName string, number int) (*Build, error) {

	o := &BuildRecordKey{
		Build: formatBuildKey(namespace, buildName, number),
	}

	item, err := dynamodbattribute.MarshalMap(o)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	params := &dynamodb.GetItemInput{
		TableName: aws.String("jeeves.dev.builds"),
		Key:       item,
	}

	resp, err := dao.svc.GetItem(params)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	out := &BuildRecord{}
	err = dynamodbattribute.UnmarshalMap(resp.Item, out)

	updated, err := time.Parse(time.RFC3339Nano, out.LastUpdated)
	if err != nil {
		updated = time.Time{}
	}

	result := &Build{
		Build:       out.Name,
		Namespace:   out.Namespace,
		Number:      out.Number,
		LastUpdated: updated,
	}
	return result, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Davin Hills. All rights reserved.
// MIT license. License details can be found in the LICENSE file.

package data

import (
	"errors"
	"strconv"
)

// QResult is a query results
type QResult interface {
	Clean()
}

// QStatus represents a query status
type QStatus struct {
	Status     string `json:"status"`
	Usage      string `json:"usage"`
	TT         string `json:"TotalTransactions"`
	Language   string `json:"language"`
	StatusInfo string `json:"statusInfo"`

	Transactions int
}

// Error returns the error from the query or nil
func (q *QStatus) Error() error {
	if q.Status != "OK" {
		return errors.New(q.StatusInfo)
	}
	return nil
}

// Clean casts query string results to more convenient data types
func (q *QStatus) Clean() {
	q.Transactions, _ = strconv.Atoi(q.TT)
}

// Sentiment represents a Sentiment result
type Sentiment struct {
	Type  string `json:"type"`
	S     string `json:"score"`
	Mixed string `json:"mixed"`

	Score float32
}

// Clean casts query string results to more convenient data types
func (s *Sentiment) Clean() {
	s64, _ := strconv.ParseFloat(s.S, 32)
	s.Score = float32(s64)
}

// Entity represents a entity result
type Entity struct {
	Type           string         `json:"type"`
	R              string         `json:"relevance"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`
	Count          string         `json:"count"`
	Text           string         `json:"text"`
	Sentiment      Sentiment      `json:"sentiment"`
	Disambiguated  Concept        `json:"disambiguated"`
	Quotations     []Quotation    `json:"quotations"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (e *Entity) Clean() {
	s64, _ := strconv.ParseFloat(e.R, 32)
	e.Relevance = float32(s64)
	e.TypeHierarchy = e.KnowledgeGraph.TypeHierarchy
	e.Sentiment.Clean()
	for i := 0; i < len(e.Quotations); i++ {
		e.Quotations[i].Clean()
	}
	e.Disambiguated.Clean()
	e.KnowledgeGraph.Clean()
}

// Concept represents a concept result
type Concept struct {
	Text           string         `json:"text"`
	R              string         `json:"relevance"`
	Name           string         `json:"name"`
	SubType        []string       `json:"subType"`
	Website        string         `json:"website"`
	Geo            string         `json:"geo"`
	Dbpedia        string         `json:"dbpedia"`
	Yago           string         `json:"yago"`
	Opencyc        string         `json:"opencyc"`
	Umbel          string         `json:"umbel"`
	Freebase       string         `json:"freebase"`
	CiaFactbook    string         `json:"ciaFactbook"`
	Census         string         `json:"census"`
	Geonames       string         `json:"geonames"`
	MusicBrainz    string         `json:"musicBrainz"`
	Crunchbase     string         `json:"crunchbase"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (c *Concept) Clean() {
	s64, _ := strconv.ParseFloat(c.R, 32)
	c.Relevance = float32(s64)
	if c.Name == "" {
		c.Name = c.Text
	}
	if c.Text == "" {
		c.Text = c.Name
	}
	c.TypeHierarchy = c.KnowledgeGraph.TypeHierarchy
	c.KnowledgeGraph.Clean()
}

// Quotation represents a quotation result
type Quotation struct {
	Quotation string    `json:"quotation"`
	Sentiment Sentiment `json:"sentiment"`
}

// Clean casts query string results to more convenient data types
func (q *Quotation) Clean() {
	q.Sentiment.Clean()
}

// KnowledgeGraph represents a knowledge graph result
type KnowledgeGraph struct {
	TypeHierarchy string `json:"TypeHierarchy"`
}

// Clean casts query string results to more convenient data types
func (k *KnowledgeGraph) Clean() {
}

// Keyword represents a keyword result
type Keyword struct {
	Sentiment      Sentiment      `json:"sentiment"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`
	Text           string         `json:"text"`
	R              string         `json:"relevance"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (k *Keyword) Clean() {
	s64, _ := strconv.ParseFloat(k.R, 32)
	k.Relevance = float32(s64)
	k.TypeHierarchy = k.KnowledgeGraph.TypeHierarchy
	k.Sentiment.Clean()
}

// Micro represents a microformats query result
type Micro struct {
	FieldName string `json:"fieldName"`
	FieldData string `json:"fieldData"`
}

// Clean casts query string results to more convenient data types
func (m *Micro) Clean() {
}

// Taxonomy represents a scoring for a category
type Taxonomy struct {
	Category string `json:"label"`
	C        string `json:"confident"`
	S        string `json:"score"`

	Confident bool
	Score     float32
}

// Clean casts query string results to more convenient data types
func (t *Taxonomy) Clean() {
	s64, _ := strconv.ParseFloat(t.S, 32)
	t.Score = float32(s64)
	if t.C == "" {
		t.Confident = true
	}
}

// Relation represents relation query
type Relation struct {
	Sentence string   `json:"sentence"`
	Subject  Subject  `json:"subject"`
	Action   Action   `json:"action"`
	Object   Object   `json:"object"`
	Location Location `json:"location"`
}

// Clean casts query string results to more convenient data types
func (r *Relation) Clean() {
	r.Subject.Clean()
	r.Action.Clean()
	r.Object.Clean()
	r.Location.Clean()
}

// Subject represents the subject of a relation
type Subject struct {
	Text      string    `json:"text"`
	Keywords  []Keyword `json:"keywords"`
	Sentiment Sentiment `json:"sentiment"`
}

// Clean casts query string results to more convenient data types
func (s *Subject) Clean() {
	s.Sentiment.Clean()
	for i := 0; i < len(s.Keywords); i++ {
		s.Keywords[i].Clean()
	}
}

// Action is the action of a relation
type Action struct {
	Text       string `json:"text"`
	Lemmatized string `json:"lemmatized"`
	Verb       Verb   `json:"verb"`
}

// Clean casts query string results to more convenient data types
func (a *Action) Clean() {
	a.Verb.Clean()
}

// Object is the object of a relation
type Object struct {
	Text                 string    `json:"text"`
	Sentiment            Sentiment `json:"sentiment"`
	SentimentFromSubject Sentiment `json:"sentimentFromSubject"`
	Entity               Entity    `json:"entity"`
}

// Clean casts query string results to more convenient data types
func (o *Object) Clean() {
	o.Sentiment.Clean()
	o.SentimentFromSubject.Clean()
	o.Entity.Clean()
}

// Location is the location of a relation
type Location struct {
	Text      string    `json:"text"`
	Sentiment Sentiment `json:"sentiment"`
	Entities  []Entity  `json:"entities"`
}

// Clean casts query string results to more convenient data types
func (l *Location) Clean() {
	l.Sentiment.Clean()
	for i := 0; i < len(l.Entities); i++ {
		l.Entities[i].Clean()
	}
}

// Verb is the verb of an action
type Verb struct {
	Text    string `json:"text"`
	Tense   string `json:"tense"`
	Negated string `json:"negated"`
}

// Clean casts query string results to more convenient data types
func (v *Verb) Clean() {
}
<commit_msg>Updated package documentation<commit_after>// Copyright 2015 Davin Hills. All rights reserved.
// MIT license. License details can be found in the LICENSE file.

// Package data contains common data structures shared between the AlchemyAPIer packages
package data

import (
	"errors"
	"strconv"
)

// QResult is a query results
type QResult interface {
	Clean()
}

// QStatus represents a query status
type QStatus struct {
	Status     string `json:"status"`
	Usage      string `json:"usage"`
	TT         string `json:"TotalTransactions"`
	Language   string `json:"language"`
	StatusInfo string `json:"statusInfo"`

	Transactions int
}

// Error returns the error from the query or nil
func (q *QStatus) Error() error {
	if q.Status != "OK" {
		return errors.New(q.StatusInfo)
	}
	return nil
}

// Clean casts query string results to more convenient data types
func (q *QStatus) Clean() {
	q.Transactions, _ = strconv.Atoi(q.TT)
}

// Sentiment represents a Sentiment result
type Sentiment struct {
	Type  string `json:"type"`
	S     string `json:"score"`
	Mixed string `json:"mixed"`

	Score float32
}

// Clean casts query string results to more convenient data types
func (s *Sentiment) Clean() {
	s64, _ := strconv.ParseFloat(s.S, 32)
	s.Score = float32(s64)
}

// Entity represents a entity result
type Entity struct {
	Type           string         `json:"type"`
	R              string         `json:"relevance"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`
	Count          string         `json:"count"`
	Text           string         `json:"text"`
	Sentiment      Sentiment      `json:"sentiment"`
	Disambiguated  Concept        `json:"disambiguated"`
	Quotations     []Quotation    `json:"quotations"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (e *Entity) Clean() {
	s64, _ := strconv.ParseFloat(e.R, 32)
	e.Relevance = float32(s64)
	e.TypeHierarchy = e.KnowledgeGraph.TypeHierarchy
	e.Sentiment.Clean()
	for i := 0; i < len(e.Quotations); i++ {
		e.Quotations[i].Clean()
	}
	e.Disambiguated.Clean()
	e.KnowledgeGraph.Clean()
}

// Concept represents a concept result
type Concept struct {
	Text           string         `json:"text"`
	R              string         `json:"relevance"`
	Name           string         `json:"name"`
	SubType        []string       `json:"subType"`
	Website        string         `json:"website"`
	Geo            string         `json:"geo"`
	Dbpedia        string         `json:"dbpedia"`
	Yago           string         `json:"yago"`
	Opencyc        string         `json:"opencyc"`
	Umbel          string         `json:"umbel"`
	Freebase       string         `json:"freebase"`
	CiaFactbook    string         `json:"ciaFactbook"`
	Census         string         `json:"census"`
	Geonames       string         `json:"geonames"`
	MusicBrainz    string         `json:"musicBrainz"`
	Crunchbase     string         `json:"crunchbase"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (c *Concept) Clean() {
	s64, _ := strconv.ParseFloat(c.R, 32)
	c.Relevance = float32(s64)
	if c.Name == "" {
		c.Name = c.Text
	}
	if c.Text == "" {
		c.Text = c.Name
	}
	c.TypeHierarchy = c.KnowledgeGraph.TypeHierarchy
	c.KnowledgeGraph.Clean()
}

// Quotation represents a quotation result
type Quotation struct {
	Quotation string    `json:"quotation"`
	Sentiment Sentiment `json:"sentiment"`
}

// Clean casts query string results to more convenient data types
func (q *Quotation) Clean() {
	q.Sentiment.Clean()
}

// KnowledgeGraph represents a knowledge graph result
type KnowledgeGraph struct {
	TypeHierarchy string `json:"TypeHierarchy"`
}

// Clean casts query string results to more convenient data types
func (k *KnowledgeGraph) Clean() {
}

// Keyword represents a keyword result
type Keyword struct {
	Sentiment      Sentiment      `json:"sentiment"`
	KnowledgeGraph KnowledgeGraph `json:"knowledgeGraph"`
	Text           string         `json:"text"`
	R              string         `json:"relevance"`

	Relevance     float32
	TypeHierarchy string
}

// Clean casts query string results to more convenient data types
func (k *Keyword) Clean() {
	s64, _ := strconv.ParseFloat(k.R, 32)
	k.Relevance = float32(s64)
	k.TypeHierarchy = k.KnowledgeGraph.TypeHierarchy
	k.Sentiment.Clean()
}

// Micro represents a microformats query result
type Micro struct {
	FieldName string `json:"fieldName"`
	FieldData string `json:"fieldData"`
}

// Clean casts query string results to more convenient data types
func (m *Micro) Clean() {
}

// Taxonomy represents a scoring for a category
type Taxonomy struct {
	Category string `json:"label"`
	C        string `json:"confident"`
	S        string `json:"score"`

	Confident bool
	Score     float32
}

// Clean casts query string results to more convenient data types
func (t *Taxonomy) Clean() {
	s64, _ := strconv.ParseFloat(t.S, 32)
	t.Score = float32(s64)
	if t.C == "" {
		t.Confident = true
	}
}

// Relation represents relation query
type Relation struct {
	Sentence string   `json:"sentence"`
	Subject  Subject  `json:"subject"`
	Action   Action   `json:"action"`
	Object   Object   `json:"object"`
	Location Location `json:"location"`
}

// Clean casts query string results to more convenient data types
func (r *Relation) Clean() {
	r.Subject.Clean()
	r.Action.Clean()
	r.Object.Clean()
	r.Location.Clean()
}

// Subject represents the subject of a relation
type Subject struct {
	Text      string    `json:"text"`
	Keywords  []Keyword `json:"keywords"`
	Sentiment Sentiment `json:"sentiment"`
}

// Clean casts query string results to more convenient data types
func (s *Subject) Clean() {
	s.Sentiment.Clean()
	for i := 0; i < len(s.Keywords); i++ {
		s.Keywords[i].Clean()
	}
}

// Action is the action of a relation
type Action struct {
	Text       string `json:"text"`
	Lemmatized string `json:"lemmatized"`
	Verb       Verb   `json:"verb"`
}

// Clean casts query string results to more convenient data types
func (a *Action) Clean() {
	a.Verb.Clean()
}

// Object is the object of a relation
type Object struct {
	Text                 string    `json:"text"`
	Sentiment            Sentiment `json:"sentiment"`
	SentimentFromSubject Sentiment `json:"sentimentFromSubject"`
	Entity               Entity    `json:"entity"`
}

// Clean casts query string results to more convenient data types
func (o *Object) Clean() {
	o.Sentiment.Clean()
	o.SentimentFromSubject.Clean()
	o.Entity.Clean()
}

// Location is the location of a relation
type Location struct {
	Text      string    `json:"text"`
	Sentiment Sentiment `json:"sentiment"`
	Entities  []Entity  `json:"entities"`
}

// Clean casts query string results to more convenient data types
func (l *Location) Clean() {
	l.Sentiment.Clean()
	for i := 0; i < len(l.Entities); i++ {
		l.Entities[i].Clean()
	}
}

// Verb is the verb of an action
type Verb struct {
	Text    string `json:"text"`
	Tense   string `json:"tense"`
	Negated string `json:"negated"`
}

// Clean casts query string results to more convenient data types
func (v *Verb) Clean() {
}
<|endoftext|>


<commit_before>package datastore

import (
	"errors"
	"time"

	query "github.com/ipfs/go-datastore/query"
)

/*
Datastore represents storage for any key-value pair.

Datastores are general enough to be backed by all kinds of different storage:
in-memory caches, databases, a remote datastore, flat files on disk, etc.

The general idea is to wrap a more complicated storage facility in a simple,
uniform interface, keeping the freedom of using the right tools for the job.
In particular, a Datastore can aggregate other datastores in interesting ways,
like sharded (to distribute load) or tiered access (caches before databases).

While Datastores should be written general enough to accept all sorts of
values, some implementations will undoubtedly have to be specific (e.g. SQL
databases where fields should be decomposed into columns), particularly to
support queries efficiently. Moreover, certain datastores may enforce certain
types of values (e.g. requiring an io.Reader, a specific struct, etc) or
serialization formats (JSON, Protobufs, etc).

IMPORTANT: No Datastore should ever Panic! This is a cross-module interface,
and thus it should behave predictably and handle exceptional conditions with
proper error reporting. Thus, all Datastore calls may return errors, which
should be checked by callers.
*/
type Datastore interface {
	// Put stores the object `value` named by `key`.
	//
	// The generalized Datastore interface does not impose a value type,
	// allowing various datastore middleware implementations (which do not
	// handle the values directly) to be composed together.
	//
	// Ultimately, the lowest-level datastore will need to do some value checking
	// or risk getting incorrect values. It may also be useful to expose a more
	// type-safe interface to your application, and do the checking up-front.
	Put(key Key, value []byte) error

	// Get retrieves the object `value` named by `key`.
	// Get will return ErrNotFound if the key is not mapped to a value.
	Get(key Key) (value []byte, err error)

	// Has returns whether the `key` is mapped to a `value`.
	// In some contexts, it may be much cheaper only to check for existence of
	// a value, rather than retrieving the value itself. (e.g. HTTP HEAD).
	// The default implementation is found in `GetBackedHas`.
	Has(key Key) (exists bool, err error)

	// Delete removes the value for given `key`.
	Delete(key Key) error

	// Query searches the datastore and returns a query result. This function
	// may return before the query actually runs. To wait for the query:
	//
	//   result, _ := ds.Query(q)
	//
	//   // use the channel interface; result may come in at different times
	//   for entry := range result.Next() { ... }
	//
	//   // or wait for the query to be completely done
	//   entries, _ := result.Rest()
	//   for entry := range entries { ... }
	//
	Query(q query.Query) (query.Results, error)
}

type Batching interface {
	Datastore

	Batch() (Batch, error)
}

var ErrBatchUnsupported = errors.New("this datastore does not support batching")

// ThreadSafeDatastore is an interface that all threadsafe datastore should
// implement to leverage type safety checks.
type ThreadSafeDatastore interface {
	Datastore

	IsThreadSafe()
}

// CheckedDatastore is an interface that should be implemented by datastores
// which may need checking on-disk data integrity.
type CheckedDatastore interface {
	Datastore

	Check() error
}

// CheckedDatastore is an interface that should be implemented by datastores
// which want to provide a mechanism to check data integrity and/or
// error correction.
type ScrubbedDatastore interface {
	Datastore

	Scrub() error
}

// GCDatastore is an interface that should be implemented by datastores which
// don't free disk space by just removing data from them.
type GCDatastore interface {
	Datastore

	CollectGarbage() error
}

// PersistentDatastore is an interface that should be implemented by datastores
// which can report disk usage.
type PersistentDatastore interface {
	Datastore

	// DiskUsage returns the space used by a datastore, in bytes.
	DiskUsage() (uint64, error)
}

// DiskUsage checks if a Datastore is a
// PersistentDatastore and returns its DiskUsage(),
// otherwise returns 0.
func DiskUsage(d Datastore) (uint64, error) {
	persDs, ok := d.(PersistentDatastore)
	if !ok {
		return 0, nil
	}
	return persDs.DiskUsage()
}

// TTLDatastore is an interface that should be implemented by datastores that
// support expiring entries.
type TTLDatastore interface {
	Datastore

	PutWithTTL(key Key, value interface{}, ttl time.Duration) error
	SetTTL(key Key, ttl time.Duration) error
}

// Txn extends the Datastore type. Txns allow users to batch queries and
// mutations to the Datastore into atomic groups, or transactions. Actions
// performed on a transaction will not take hold until a successful call to
// Commit has been made. Likewise, transactions can be aborted by calling
// Discard before a successful Commit has been made.
type Txn interface {
	Datastore

	// Commit finalizes a transaction, attempting to commit it to the Datastore.
	// May return an error if the transaction has gone stale. The presence of an
	// error is an indication that the data was not committed to the Datastore.
	Commit() error
	// Discard throws away changes recorded in a transaction without committing
	// them to the underlying Datastore. Any calls made to Discard after Commit
	// has been successfully called will have no effect on the transaction and
	// state of the Datastore, making it safe to defer.
	Discard()
}

// TxnDatastore is an interface that should be implemented by datastores that
// support transactions.
type TxnDatastore interface {
	Datastore

	NewTransaction(readOnly bool) Txn
}

// Errors

// ErrNotFound is returned by Get, Has, and Delete when a datastore does not
// map the given key to a value.
var ErrNotFound = errors.New("datastore: key not found")

// ErrInvalidType is returned by Put when a given value is incopatible with
// the type the datastore supports. This means a conversion (or serialization)
// is needed beforehand.
var ErrInvalidType = errors.New("datastore: invalid type error")

// GetBackedHas provides a default Datastore.Has implementation.
// It exists so Datastore.Has implementations can use it, like so:
//
// func (*d SomeDatastore) Has(key Key) (exists bool, err error) {
//   return GetBackedHas(d, key)
// }
func GetBackedHas(ds Datastore, key Key) (bool, error) {
	_, err := ds.Get(key)
	switch err {
	case nil:
		return true, nil
	case ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

type Batch interface {
	Put(key Key, val []byte) error

	Delete(key Key) error

	Commit() error
}
<commit_msg>Update TTL interface to follow new []byte interfaces<commit_after>package datastore

import (
	"errors"
	"time"

	query "github.com/ipfs/go-datastore/query"
)

/*
Datastore represents storage for any key-value pair.

Datastores are general enough to be backed by all kinds of different storage:
in-memory caches, databases, a remote datastore, flat files on disk, etc.

The general idea is to wrap a more complicated storage facility in a simple,
uniform interface, keeping the freedom of using the right tools for the job.
In particular, a Datastore can aggregate other datastores in interesting ways,
like sharded (to distribute load) or tiered access (caches before databases).

While Datastores should be written general enough to accept all sorts of
values, some implementations will undoubtedly have to be specific (e.g. SQL
databases where fields should be decomposed into columns), particularly to
support queries efficiently. Moreover, certain datastores may enforce certain
types of values (e.g. requiring an io.Reader, a specific struct, etc) or
serialization formats (JSON, Protobufs, etc).

IMPORTANT: No Datastore should ever Panic! This is a cross-module interface,
and thus it should behave predictably and handle exceptional conditions with
proper error reporting. Thus, all Datastore calls may return errors, which
should be checked by callers.
*/
type Datastore interface {
	// Put stores the object `value` named by `key`.
	//
	// The generalized Datastore interface does not impose a value type,
	// allowing various datastore middleware implementations (which do not
	// handle the values directly) to be composed together.
	//
	// Ultimately, the lowest-level datastore will need to do some value checking
	// or risk getting incorrect values. It may also be useful to expose a more
	// type-safe interface to your application, and do the checking up-front.
	Put(key Key, value []byte) error

	// Get retrieves the object `value` named by `key`.
	// Get will return ErrNotFound if the key is not mapped to a value.
	Get(key Key) (value []byte, err error)

	// Has returns whether the `key` is mapped to a `value`.
	// In some contexts, it may be much cheaper only to check for existence of
	// a value, rather than retrieving the value itself. (e.g. HTTP HEAD).
	// The default implementation is found in `GetBackedHas`.
	Has(key Key) (exists bool, err error)

	// Delete removes the value for given `key`.
	Delete(key Key) error

	// Query searches the datastore and returns a query result. This function
	// may return before the query actually runs. To wait for the query:
	//
	//   result, _ := ds.Query(q)
	//
	//   // use the channel interface; result may come in at different times
	//   for entry := range result.Next() { ... }
	//
	//   // or wait for the query to be completely done
	//   entries, _ := result.Rest()
	//   for entry := range entries { ... }
	//
	Query(q query.Query) (query.Results, error)
}

type Batching interface {
	Datastore

	Batch() (Batch, error)
}

var ErrBatchUnsupported = errors.New("this datastore does not support batching")

// ThreadSafeDatastore is an interface that all threadsafe datastore should
// implement to leverage type safety checks.
type ThreadSafeDatastore interface {
	Datastore

	IsThreadSafe()
}

// CheckedDatastore is an interface that should be implemented by datastores
// which may need checking on-disk data integrity.
type CheckedDatastore interface {
	Datastore

	Check() error
}

// CheckedDatastore is an interface that should be implemented by datastores
// which want to provide a mechanism to check data integrity and/or
// error correction.
type ScrubbedDatastore interface {
	Datastore

	Scrub() error
}

// GCDatastore is an interface that should be implemented by datastores which
// don't free disk space by just removing data from them.
type GCDatastore interface {
	Datastore

	CollectGarbage() error
}

// PersistentDatastore is an interface that should be implemented by datastores
// which can report disk usage.
type PersistentDatastore interface {
	Datastore

	// DiskUsage returns the space used by a datastore, in bytes.
	DiskUsage() (uint64, error)
}

// DiskUsage checks if a Datastore is a
// PersistentDatastore and returns its DiskUsage(),
// otherwise returns 0.
func DiskUsage(d Datastore) (uint64, error) {
	persDs, ok := d.(PersistentDatastore)
	if !ok {
		return 0, nil
	}
	return persDs.DiskUsage()
}

// TTLDatastore is an interface that should be implemented by datastores that
// support expiring entries.
type TTLDatastore interface {
	Datastore

	PutWithTTL(key Key, value []byte, ttl time.Duration) error
	SetTTL(key Key, ttl time.Duration) error
}

// Txn extends the Datastore type. Txns allow users to batch queries and
// mutations to the Datastore into atomic groups, or transactions. Actions
// performed on a transaction will not take hold until a successful call to
// Commit has been made. Likewise, transactions can be aborted by calling
// Discard before a successful Commit has been made.
type Txn interface {
	Datastore

	// Commit finalizes a transaction, attempting to commit it to the Datastore.
	// May return an error if the transaction has gone stale. The presence of an
	// error is an indication that the data was not committed to the Datastore.
	Commit() error
	// Discard throws away changes recorded in a transaction without committing
	// them to the underlying Datastore. Any calls made to Discard after Commit
	// has been successfully called will have no effect on the transaction and
	// state of the Datastore, making it safe to defer.
	Discard()
}

// TxnDatastore is an interface that should be implemented by datastores that
// support transactions.
type TxnDatastore interface {
	Datastore

	NewTransaction(readOnly bool) Txn
}

// Errors

// ErrNotFound is returned by Get, Has, and Delete when a datastore does not
// map the given key to a value.
var ErrNotFound = errors.New("datastore: key not found")

// ErrInvalidType is returned by Put when a given value is incopatible with
// the type the datastore supports. This means a conversion (or serialization)
// is needed beforehand.
var ErrInvalidType = errors.New("datastore: invalid type error")

// GetBackedHas provides a default Datastore.Has implementation.
// It exists so Datastore.Has implementations can use it, like so:
//
// func (*d SomeDatastore) Has(key Key) (exists bool, err error) {
//   return GetBackedHas(d, key)
// }
func GetBackedHas(ds Datastore, key Key) (bool, error) {
	_, err := ds.Get(key)
	switch err {
	case nil:
		return true, nil
	case ErrNotFound:
		return false, nil
	default:
		return false, err
	}
}

type Batch interface {
	Put(key Key, val []byte) error

	Delete(key Key) error

	Commit() error
}
<|endoftext|>


<commit_before>package datatable

import (
	"encoding/json"
	"errors"
)

var (
	ColIndexError  = errors.New("Column index out of range")
	RowIndexError  = errors.New("Row index out of range")
	ValueError     = errors.New("Incorrect argument value")
	NumColError    = errors.New("Incorrect number of columns")
	SingleColError = errors.New("Refuse to remove the last column")
)

// DataTable is an in-memory relational table.
// The data values are immutable.
type DataTable struct {
	rows [][]string
	nrow int
	ncol int
}

// NewDataTable creates a new data table with a given number of columns.
func NewDataTable(ncol int) *DataTable {
	rows := make([][]string, 0)
	return &DataTable{
		rows: rows,
		nrow: 0,
		ncol: ncol,
	}
}

func (dt *DataTable) NumRow() int {
	return dt.nrow
}

func (dt *DataTable) NumCol() int {
	return dt.ncol
}

// CheckRow returns an error if row x does not exist.
func (dt *DataTable) CheckRow(x int) error {
	if x < 0 || x >= dt.nrow {
		return RowIndexError
	}
	return nil
}

// CheckCol returns an error if column y does not exist.
func (dt *DataTable) CheckCol(y int) error {
	if y < 0 || y >= dt.ncol {
		return ColIndexError
	}
	return nil
}

// AppendRow appends a new row at the bottom of the table.
func (dt *DataTable) AppendRow(row []string) error {
	if len(row) != dt.ncol {
		return NumColError
	}
	dt.rows = append(dt.rows, row)
	dt.nrow++
	return nil
}

// GetRow returns the row at index x.
func (dt *DataTable) GetRow(x int) ([]string, error) {
	if err := dt.CheckRow(x); err != nil {
		return nil, err
	}
	row := make([]string, dt.ncol)
	copy(row, dt.rows[x])
	return row, nil
}

// ApplyColumn calls the function fn using all values in column y
// from top to bottom.
// fn takes two argument: the first is the row index and the second
// is the corresponding value.
// Error is returned immediately if encountered.
func (dt *DataTable) ApplyColumn(y int, fn func(int, string) error) error {
	if err := dt.CheckCol(y); err != nil {
		return err
	}
	for x, row := range dt.rows {
		if err := fn(x, row[y]); err != nil {
			return err
		}
	}
	return nil
}

// RemoveColumn deletes the column at index y
func (dt *DataTable) RemoveColumn(y int) error {
	if err := dt.CheckCol(y); err != nil {
		return err
	}
	if dt.NumCol() == 1 {
		return SingleColError
	}
	for x, row := range dt.rows {
		dt.rows[x] = append(row[:y], row[y+1:]...)
	}
	dt.ncol--
	return nil
}

// RemoveRow deletes the row at index x
func (dt *DataTable) RemoveRow(x int) error {
	if err := dt.CheckRow(x); err != nil {
		return err
	}
	dt.rows = append(dt.rows[:x], dt.rows[x+1:]...)
	dt.nrow--
	return nil
}

// Slice take a contiguous subset of at most n rows, starting at index x,
// and make a new data table from them.
func (dt *DataTable) Slice(x, n int) (*DataTable, error) {
	if x < 0 || x >= dt.nrow {
		return nil, RowIndexError
	}
	if n < 0 {
		return nil, ValueError
	}
	end := x + n
	if end >= dt.nrow {
		end = dt.nrow
	}
	rows := dt.rows[x:end]
	return &DataTable{
		rows: rows,
		nrow: len(rows),
		ncol: dt.ncol,
	}, nil
}

// Marshal data table into JSON.
func (dt *DataTable) MarshalJSON() ([]byte, error) {
	return json.Marshal(dt.rows)
}

// Unmarshal data table from JSON.
func (dt *DataTable) UnmarshalJSON(data []byte) error {
	var rows [][]string
	if err := json.Unmarshal(data, &rows); err != nil {
		return err
	}
	var nrow, ncol int
	nrow = len(rows)
	if nrow > 0 {
		ncol = len(rows[0])
		for x := range rows {
			if len(rows[x]) != ncol {
				return NumColError
			}
		}
	}
	dt = &DataTable{
		rows: rows,
		nrow: nrow,
		ncol: ncol,
	}
	return nil
}

// Join performs relational join between the left and right tables.
// The join condition is defined by the function fn, which takes two rows,
// l and r, from the left and right tables respectively, and returns
// whether the two rows should be joined.
// The join result is returned as a new data table.
// Each joined rows contains all the fields from the input tables,
// in the order of [left table fields ... right table fields ...].
func Join(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
				}
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}

// LeftJoin is similar to Join, execpt that every row from the left table
// will be part of the join result even it doesn't join with any row from
// the right table.
// e.g., [left table fields ... empty fields]
// where the empty fields have the same number of columns as the right table.
func LeftJoin(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			var rowsJoined int
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
					rowsJoined++
				}
			}
			if rowsJoined == 0 {
				r := make([]string, right.NumCol())
				out <- append(l, r...)
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}

func HashJoin(left, right *DataTable, fnLeft, fnRight func([]string) string) *DataTable {
	out := make(chan []string)
	go func() {
		defer close(out)
		// Find relative sizes
		var smaller, larger *DataTable
		var fnSmall, fnLarge func([]string) string
		var fnJoin func([]string, []string) []string
		if left.NumRow() > right.NumRow() {
			smaller, larger = right, left
			fnSmall, fnLarge = fnRight, fnLeft
			fnJoin = func(s, l []string) []string {
				return append(l, s...)
			}
		} else {
			smaller, larger = left, right
			fnSmall, fnLarge = fnLeft, fnRight
			fnJoin = func(s, l []string) []string {
				return append(s, l...)
			}
		}
		// Build map for the larger
		ht := make(map[string][][]string)
		for i := 0; i < larger.NumRow(); i++ {
			row, _ := larger.GetRow(i)
			key := fnLarge(row)
			if _, exists := ht[key]; !exists {
				ht[key] = make([][]string, 0)
			}
			ht[key] = append(ht[key], row)
		}
		// Perform join
		for i := 0; i < smaller.NumRow(); i++ {
			rowSmall, _ := smaller.GetRow(i)
			key := fnSmall(rowSmall)
			if bucket, exists := ht[key]; exists {
				for _, rowLarge := range bucket {
					out <- fnJoin(rowSmall, rowLarge)
				}
			}
		}
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}
<commit_msg>add to csv<commit_after>package datatable

import (
	"encoding/csv"
	"encoding/json"
	"errors"
	"io"
)

var (
	ColIndexError  = errors.New("Column index out of range")
	RowIndexError  = errors.New("Row index out of range")
	ValueError     = errors.New("Incorrect argument value")
	NumColError    = errors.New("Incorrect number of columns")
	SingleColError = errors.New("Refuse to remove the last column")
)

// DataTable is an in-memory relational table.
// The data values are immutable.
type DataTable struct {
	rows [][]string
	nrow int
	ncol int
}

// NewDataTable creates a new data table with a given number of columns.
func NewDataTable(ncol int) *DataTable {
	rows := make([][]string, 0)
	return &DataTable{
		rows: rows,
		nrow: 0,
		ncol: ncol,
	}
}

func (dt *DataTable) NumRow() int {
	return dt.nrow
}

func (dt *DataTable) NumCol() int {
	return dt.ncol
}

// CheckRow returns an error if row x does not exist.
func (dt *DataTable) CheckRow(x int) error {
	if x < 0 || x >= dt.nrow {
		return RowIndexError
	}
	return nil
}

// CheckCol returns an error if column y does not exist.
func (dt *DataTable) CheckCol(y int) error {
	if y < 0 || y >= dt.ncol {
		return ColIndexError
	}
	return nil
}

// AppendRow appends a new row at the bottom of the table.
func (dt *DataTable) AppendRow(row []string) error {
	if len(row) != dt.ncol {
		return NumColError
	}
	dt.rows = append(dt.rows, row)
	dt.nrow++
	return nil
}

// GetRow returns the row at index x.
func (dt *DataTable) GetRow(x int) ([]string, error) {
	if err := dt.CheckRow(x); err != nil {
		return nil, err
	}
	row := make([]string, dt.ncol)
	copy(row, dt.rows[x])
	return row, nil
}

// ApplyColumn calls the function fn using all values in column y
// from top to bottom.
// fn takes two argument: the first is the row index and the second
// is the corresponding value.
// Error is returned immediately if encountered.
func (dt *DataTable) ApplyColumn(y int, fn func(int, string) error) error {
	if err := dt.CheckCol(y); err != nil {
		return err
	}
	for x, row := range dt.rows {
		if err := fn(x, row[y]); err != nil {
			return err
		}
	}
	return nil
}

// RemoveColumn deletes the column at index y
func (dt *DataTable) RemoveColumn(y int) error {
	if err := dt.CheckCol(y); err != nil {
		return err
	}
	if dt.NumCol() == 1 {
		return SingleColError
	}
	for x, row := range dt.rows {
		dt.rows[x] = append(row[:y], row[y+1:]...)
	}
	dt.ncol--
	return nil
}

// RemoveRow deletes the row at index x
func (dt *DataTable) RemoveRow(x int) error {
	if err := dt.CheckRow(x); err != nil {
		return err
	}
	dt.rows = append(dt.rows[:x], dt.rows[x+1:]...)
	dt.nrow--
	return nil
}

// Slice take a contiguous subset of at most n rows, starting at index x,
// and make a new data table from them.
func (dt *DataTable) Slice(x, n int) (*DataTable, error) {
	if x < 0 || x >= dt.nrow {
		return nil, RowIndexError
	}
	if n < 0 {
		return nil, ValueError
	}
	end := x + n
	if end >= dt.nrow {
		end = dt.nrow
	}
	rows := dt.rows[x:end]
	return &DataTable{
		rows: rows,
		nrow: len(rows),
		ncol: dt.ncol,
	}, nil
}

// Marshal data table into JSON.
func (dt *DataTable) MarshalJSON() ([]byte, error) {
	return json.Marshal(dt.rows)
}

// Unmarshal data table from JSON.
func (dt *DataTable) UnmarshalJSON(data []byte) error {
	var rows [][]string
	if err := json.Unmarshal(data, &rows); err != nil {
		return err
	}
	var nrow, ncol int
	nrow = len(rows)
	if nrow > 0 {
		ncol = len(rows[0])
		for x := range rows {
			if len(rows[x]) != ncol {
				return NumColError
			}
		}
	}
	dt = &DataTable{
		rows: rows,
		nrow: nrow,
		ncol: ncol,
	}
	return nil
}

// ToCsv writes the table in standard CSV format to a file
func (dt *DataTable) ToCsv(file io.Writer) error {
	writer := csv.NewWriter(file)
	for i := 0; i < dt.NumRow(); i++ {
		row, _ := dt.GetRow(i)
		if err := writer.Write(row); err != nil {
			return err
		}
	}
	writer.Flush()
	if err := writer.Error(); err != nil {
		return err
	}
	return nil
}

// Join performs relational join between the left and right tables.
// The join condition is defined by the function fn, which takes two rows,
// l and r, from the left and right tables respectively, and returns
// whether the two rows should be joined.
// The join result is returned as a new data table.
// Each joined rows contains all the fields from the input tables,
// in the order of [left table fields ... right table fields ...].
func Join(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
				}
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}

// LeftJoin is similar to Join, execpt that every row from the left table
// will be part of the join result even it doesn't join with any row from
// the right table.
// e.g., [left table fields ... empty fields]
// where the empty fields have the same number of columns as the right table.
func LeftJoin(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			var rowsJoined int
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
					rowsJoined++
				}
			}
			if rowsJoined == 0 {
				r := make([]string, right.NumCol())
				out <- append(l, r...)
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}

func HashJoin(left, right *DataTable, fnLeft, fnRight func([]string) string) *DataTable {
	out := make(chan []string)
	go func() {
		defer close(out)
		// Find relative sizes
		var smaller, larger *DataTable
		var fnSmall, fnLarge func([]string) string
		var fnJoin func([]string, []string) []string
		if left.NumRow() > right.NumRow() {
			smaller, larger = right, left
			fnSmall, fnLarge = fnRight, fnLeft
			fnJoin = func(s, l []string) []string {
				return append(l, s...)
			}
		} else {
			smaller, larger = left, right
			fnSmall, fnLarge = fnLeft, fnRight
			fnJoin = func(s, l []string) []string {
				return append(s, l...)
			}
		}
		// Build map for the larger
		ht := make(map[string][][]string)
		for i := 0; i < larger.NumRow(); i++ {
			row, _ := larger.GetRow(i)
			key := fnLarge(row)
			if _, exists := ht[key]; !exists {
				ht[key] = make([][]string, 0)
			}
			ht[key] = append(ht[key], row)
		}
		// Perform join
		for i := 0; i < smaller.NumRow(); i++ {
			rowSmall, _ := smaller.GetRow(i)
			key := fnSmall(rowSmall)
			if bucket, exists := ht[key]; exists {
				for _, rowLarge := range bucket {
					out <- fnJoin(rowSmall, rowLarge)
				}
			}
		}
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
)

var CurrentSchema uint = 1

func (db *DB) Setup() error {
	v, err := db.SchemaVersion()
	if err != nil {
		return err
	}

	if v == 0 {
		err = db.v1schema()
	} else if v > 1 {
		err = fmt.Errorf("Schema version %d is newer than this version of SHIELD", v)
	}

	if err != nil {
		return err
	}
	return nil
}

func (db *DB) SchemaVersion() (uint, error) {
	r, err := db.Query(`SELECT version FROM schema_info LIMIT 1`)
	if err != nil {
		if err.Error() == "no such table: schema_info" {
			return 0, nil
		}
		if err.Error() == `pq: relation "schema_info" does not exist` {
			return 0, nil
		}
		return 0, err
	}
	defer r.Close()

	// no records = no schema
	if !r.Next() {
		return 0, nil
	}

	var v int
	err = r.Scan(&v)
	// failed unmarshall is an actual error
	if err != nil {
		return 0, err
	}

	// invalid (negative) schema version is an actual error
	if v < 0 {
		return 0, fmt.Errorf("Invalid schema version %d found", v)
	}

	return uint(v), nil
}

func (db *DB) CheckCurrentSchema() error {
	v, err := db.SchemaVersion()
	if err != nil {
		return err
	}
	if v != CurrentSchema {
		return fmt.Errorf("wrong schema version (%d, but want to be at %d)", v, CurrentSchema)
	}
	return nil
}

func (db *DB) v1schema() error {
	db.Exec(`CREATE TABLE schema_info (
               version INTEGER
             )`)
	db.Exec(`INSERT INTO schema_info VALUES (?)`, CurrentSchema)

	db.Exec(`CREATE TABLE targets (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               plugin    TEXT,
               endpoint  TEXT,
               agent     TEXT
             )`)

	db.Exec(`CREATE TABLE stores (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               plugin    TEXT,
               endpoint  TEXT
             )`)

	db.Exec(`CREATE TABLE schedules (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               timespec  TEXT
             )`)

	db.Exec(`CREATE TABLE retention (
               uuid     UUID PRIMARY KEY,
               name     TEXT,
               summary  TEXT,
               expiry   INTEGER
             )`)

	db.Exec(`CREATE TABLE jobs (
               uuid            UUID PRIMARY KEY,
               target_uuid     UUID,
               store_uuid      UUID,
               schedule_uuid   UUID,
               retention_uuid  UUID,
               paused          BOOLEAN,
               name            TEXT,
               summary         TEXT
             )`)

	db.Exec(`CREATE TABLE archives (
               uuid         UUID PRIMARY KEY,
               target_uuid  UUID,
               store_uuid   UUID,
               store_key    TEXT,

               taken_at     timestamp without time zone,
               expires_at   timestamp without time zone,
               notes        TEXT DEFAULT ''
             )`)

	db.Exec(`CREATE TABLE tasks (
               uuid      UUID PRIMARY KEY,
               owner     TEXT,
               op        TEXT,

               job_uuid      UUID,
               archive_uuid  UUID,
               target_uuid   UUID,

               status      TEXT,
               started_at  timestamp without time zone,
               stopped_at  timestamp without time zone,

               log       TEXT,
               debug     TEXT
             )`)

	return nil
}
<commit_msg>Hard-code the schema version<commit_after>package db

import (
	"fmt"
)

var CurrentSchema uint = 1

func (db *DB) Setup() error {
	v, err := db.SchemaVersion()
	if err != nil {
		return err
	}

	if v == 0 {
		err = db.v1schema()
	} else if v > 1 {
		err = fmt.Errorf("Schema version %d is newer than this version of SHIELD", v)
	}

	if err != nil {
		return err
	}
	return nil
}

func (db *DB) SchemaVersion() (uint, error) {
	r, err := db.Query(`SELECT version FROM schema_info LIMIT 1`)
	if err != nil {
		if err.Error() == "no such table: schema_info" {
			return 0, nil
		}
		if err.Error() == `pq: relation "schema_info" does not exist` {
			return 0, nil
		}
		return 0, err
	}
	defer r.Close()

	// no records = no schema
	if !r.Next() {
		return 0, nil
	}

	var v int
	err = r.Scan(&v)
	// failed unmarshall is an actual error
	if err != nil {
		return 0, err
	}

	// invalid (negative) schema version is an actual error
	if v < 0 {
		return 0, fmt.Errorf("Invalid schema version %d found", v)
	}

	return uint(v), nil
}

func (db *DB) CheckCurrentSchema() error {
	v, err := db.SchemaVersion()
	if err != nil {
		return err
	}
	if v != CurrentSchema {
		return fmt.Errorf("wrong schema version (%d, but want to be at %d)", v, CurrentSchema)
	}
	return nil
}

func (db *DB) v1schema() error {
	db.Exec(`CREATE TABLE schema_info (
               version INTEGER
             )`)
	db.Exec(`INSERT INTO schema_info VALUES (1)`)

	db.Exec(`CREATE TABLE targets (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               plugin    TEXT,
               endpoint  TEXT,
               agent     TEXT
             )`)

	db.Exec(`CREATE TABLE stores (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               plugin    TEXT,
               endpoint  TEXT
             )`)

	db.Exec(`CREATE TABLE schedules (
               uuid      UUID PRIMARY KEY,
               name      TEXT,
               summary   TEXT,
               timespec  TEXT
             )`)

	db.Exec(`CREATE TABLE retention (
               uuid     UUID PRIMARY KEY,
               name     TEXT,
               summary  TEXT,
               expiry   INTEGER
             )`)

	db.Exec(`CREATE TABLE jobs (
               uuid            UUID PRIMARY KEY,
               target_uuid     UUID,
               store_uuid      UUID,
               schedule_uuid   UUID,
               retention_uuid  UUID,
               paused          BOOLEAN,
               name            TEXT,
               summary         TEXT
             )`)

	db.Exec(`CREATE TABLE archives (
               uuid         UUID PRIMARY KEY,
               target_uuid  UUID,
               store_uuid   UUID,
               store_key    TEXT,

               taken_at     timestamp without time zone,
               expires_at   timestamp without time zone,
               notes        TEXT DEFAULT ''
             )`)

	db.Exec(`CREATE TABLE tasks (
               uuid      UUID PRIMARY KEY,
               owner     TEXT,
               op        TEXT,

               job_uuid      UUID,
               archive_uuid  UUID,
               target_uuid   UUID,

               status      TEXT,
               started_at  timestamp without time zone,
               stopped_at  timestamp without time zone,

               log       TEXT,
               debug     TEXT
             )`)

	return nil
}
<|endoftext|>


<commit_before>package dbng

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"strings"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/Masterminds/squirrel"
	"github.com/concourse/atc/db/migrations"
	"github.com/concourse/atc/dbng/migration"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/lib/pq"
)

//go:generate counterfeiter . Conn

type Conn interface {
	Bus() NotificationsBus
	EncryptionStrategy() EncryptionStrategy

	Begin() (Tx, error)
	Driver() driver.Driver
	Exec(query string, args ...interface{}) (sql.Result, error)
	Ping() error
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) squirrel.RowScanner
	SetMaxIdleConns(n int)
	SetMaxOpenConns(n int)
	Stats() sql.DBStats

	Close() error
}

//go:generate counterfeiter . Tx

type Tx interface {
	Commit() error
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) squirrel.RowScanner
	Rollback() error
	Stmt(stmt *sql.Stmt) *sql.Stmt
}

func Open(logger lager.Logger, sqlDriver string, sqlDataSource string, newKey *EncryptionKey, oldKey *EncryptionKey) (Conn, error) {
	for {
		sqlDb, err := migration.Open(sqlDriver, sqlDataSource, migrations.Migrations)
		if err != nil {
			if strings.Contains(err.Error(), " dial ") {
				logger.Error("failed-to-open-db-retrying", err)
				time.Sleep(5 * time.Second)
				continue
			}

			return nil, err
		}

		var strategy EncryptionStrategy
		if newKey != nil {
			strategy = newKey
		} else {
			strategy = NewNoEncryption()
		}

		switch {
		case oldKey != nil && newKey == nil:
			err = decryptToPlaintext(logger.Session("decrypt"), sqlDb, oldKey)
		case oldKey != nil && newKey != nil:
			err = encryptWithNewKey(logger.Session("rotate"), sqlDb, newKey, oldKey)
		}
		if err != nil {
			return nil, err
		}

		if newKey != nil {
			err = encryptPlaintext(logger.Session("encrypt"), sqlDb, newKey)
			if err != nil {
				return nil, err
			}
		}

		listener := pq.NewListener(sqlDataSource, time.Second, time.Minute, nil)

		return &db{
			DB: sqlDb,

			bus:        NewNotificationsBus(listener, sqlDb),
			encryption: strategy,
		}, nil
	}
}

var encryptedColumns = map[string]string{
	"teams":          "auth",
	"resources":      "config",
	"jobs":           "config",
	"resource_types": "config",
	"pipelines":      "config",
}

func encryptPlaintext(logger lager.Logger, sqlDB *sql.DB, key *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		encryptedRows := 0

		for rows.Next() {
			var (
				id  int
				val sql.NullString
			)

			err := rows.Scan(&id, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			if !val.Valid {
				continue
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			encrypted, nonce, err := key.Encrypt([]byte(val.String))
			if err != nil {
				rLog.Error("failed-to-encrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = $2
				WHERE id = $3
			`, encrypted, nonce, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			encryptedRows++
		}

		if encryptedRows > 0 {
			tLog.Info("encrypted-existing-plaintext-data", lager.Data{
				"rows": encryptedRows,
			})
		}
	}

	return nil
}

func decryptToPlaintext(logger lager.Logger, sqlDB *sql.DB, oldKey *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, nonce, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NOT NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		decryptedRows := 0

		for rows.Next() {
			var (
				id         int
				val, nonce string
			)

			err := rows.Scan(&id, &nonce, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			decrypted, err := oldKey.Decrypt(val, &nonce)
			if err != nil {
				rLog.Error("failed-to-decrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = NULL
				WHERE id = $2
			`, decrypted, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			decryptedRows++
		}

		if decryptedRows > 0 {
			tLog.Info("decrypted-existing-encrypted-data", lager.Data{
				"rows": decryptedRows,
			})
		}
	}

	return nil
}

var ErrEncryptedWithUnknownKey = errors.New("row encrypted with neither old nor new key")

func encryptWithNewKey(logger lager.Logger, sqlDB *sql.DB, newKey *EncryptionKey, oldKey *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, nonce, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NOT NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		encryptedRows := 0

		for rows.Next() {
			var (
				id         int
				val, nonce string
			)

			err := rows.Scan(&id, &nonce, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			decrypted, err := oldKey.Decrypt(val, &nonce)
			if err != nil {
				_, err = newKey.Decrypt(val, &nonce)
				if err == nil {
					rLog.Debug("already-encrypted-with-new-key")
					continue
				}

				logger.Error("failed-to-decrypt-with-either-key", err)
				return ErrEncryptedWithUnknownKey
			}

			encrypted, newNonce, err := newKey.Encrypt(decrypted)
			if err != nil {
				rLog.Error("failed-to-encrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = $2
				WHERE id = $3
			`, encrypted, newNonce, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			encryptedRows++
		}

		if encryptedRows > 0 {
			tLog.Info("re-encrypted-existing-encrypted-data", lager.Data{
				"rows": encryptedRows,
			})
		}
	}

	return nil
}

type db struct {
	*sql.DB

	bus        NotificationsBus
	encryption EncryptionStrategy
}

func (db *db) Bus() NotificationsBus {
	return db.bus
}

func (db *db) EncryptionStrategy() EncryptionStrategy {
	return db.encryption
}

func (db *db) Close() error {
	var errs error
	dbErr := db.DB.Close()
	if dbErr != nil {
		errs = multierror.Append(errs, dbErr)
	}

	busErr := db.bus.Close()
	if busErr != nil {
		errs = multierror.Append(errs, busErr)
	}

	return errs
}

func (db *db) Begin() (Tx, error) {
	tx, err := db.DB.Begin()
	if err != nil {
		return nil, err
	}

	return &dbTx{tx}, nil
}

// to conform to squirrel.Runner interface
func (db *db) QueryRow(query string, args ...interface{}) squirrel.RowScanner {
	return db.DB.QueryRow(query, args...)
}

type dbTx struct {
	*sql.Tx
}

// to conform to squirrel.Runner interface
func (tx *dbTx) QueryRow(query string, args ...interface{}) squirrel.RowScanner {
	return tx.Tx.QueryRow(query, args...)
}

type nonOneRowAffectedError struct {
	RowsAffected int64
}

func (err nonOneRowAffectedError) Error() string {
	return fmt.Sprintf("expected 1 row to be updated; got %d", err.RowsAffected)
}
<commit_msg>too much space<commit_after>package dbng

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"strings"
	"time"

	"code.cloudfoundry.org/lager"

	"github.com/Masterminds/squirrel"
	"github.com/concourse/atc/db/migrations"
	"github.com/concourse/atc/dbng/migration"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/lib/pq"
)

//go:generate counterfeiter . Conn

type Conn interface {
	Bus() NotificationsBus
	EncryptionStrategy() EncryptionStrategy

	Begin() (Tx, error)
	Driver() driver.Driver
	Exec(query string, args ...interface{}) (sql.Result, error)
	Ping() error
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) squirrel.RowScanner
	SetMaxIdleConns(n int)
	SetMaxOpenConns(n int)
	Stats() sql.DBStats

	Close() error
}

//go:generate counterfeiter . Tx

type Tx interface {
	Commit() error
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) squirrel.RowScanner
	Rollback() error
	Stmt(stmt *sql.Stmt) *sql.Stmt
}

func Open(logger lager.Logger, sqlDriver string, sqlDataSource string, newKey *EncryptionKey, oldKey *EncryptionKey) (Conn, error) {
	for {
		sqlDb, err := migration.Open(sqlDriver, sqlDataSource, migrations.Migrations)
		if err != nil {
			if strings.Contains(err.Error(), "dial ") {
				logger.Error("failed-to-open-db-retrying", err)
				time.Sleep(5 * time.Second)
				continue
			}

			return nil, err
		}

		var strategy EncryptionStrategy
		if newKey != nil {
			strategy = newKey
		} else {
			strategy = NewNoEncryption()
		}

		switch {
		case oldKey != nil && newKey == nil:
			err = decryptToPlaintext(logger.Session("decrypt"), sqlDb, oldKey)
		case oldKey != nil && newKey != nil:
			err = encryptWithNewKey(logger.Session("rotate"), sqlDb, newKey, oldKey)
		}
		if err != nil {
			return nil, err
		}

		if newKey != nil {
			err = encryptPlaintext(logger.Session("encrypt"), sqlDb, newKey)
			if err != nil {
				return nil, err
			}
		}

		listener := pq.NewListener(sqlDataSource, time.Second, time.Minute, nil)

		return &db{
			DB: sqlDb,

			bus:        NewNotificationsBus(listener, sqlDb),
			encryption: strategy,
		}, nil
	}
}

var encryptedColumns = map[string]string{
	"teams":          "auth",
	"resources":      "config",
	"jobs":           "config",
	"resource_types": "config",
	"pipelines":      "config",
}

func encryptPlaintext(logger lager.Logger, sqlDB *sql.DB, key *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		encryptedRows := 0

		for rows.Next() {
			var (
				id  int
				val sql.NullString
			)

			err := rows.Scan(&id, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			if !val.Valid {
				continue
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			encrypted, nonce, err := key.Encrypt([]byte(val.String))
			if err != nil {
				rLog.Error("failed-to-encrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = $2
				WHERE id = $3
			`, encrypted, nonce, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			encryptedRows++
		}

		if encryptedRows > 0 {
			tLog.Info("encrypted-existing-plaintext-data", lager.Data{
				"rows": encryptedRows,
			})
		}
	}

	return nil
}

func decryptToPlaintext(logger lager.Logger, sqlDB *sql.DB, oldKey *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, nonce, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NOT NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		decryptedRows := 0

		for rows.Next() {
			var (
				id         int
				val, nonce string
			)

			err := rows.Scan(&id, &nonce, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			decrypted, err := oldKey.Decrypt(val, &nonce)
			if err != nil {
				rLog.Error("failed-to-decrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = NULL
				WHERE id = $2
			`, decrypted, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			decryptedRows++
		}

		if decryptedRows > 0 {
			tLog.Info("decrypted-existing-encrypted-data", lager.Data{
				"rows": decryptedRows,
			})
		}
	}

	return nil
}

var ErrEncryptedWithUnknownKey = errors.New("row encrypted with neither old nor new key")

func encryptWithNewKey(logger lager.Logger, sqlDB *sql.DB, newKey *EncryptionKey, oldKey *EncryptionKey) error {
	for table, col := range encryptedColumns {
		rows, err := sqlDB.Query(`
			SELECT id, nonce, ` + col + `
			FROM ` + table + `
			WHERE nonce IS NOT NULL
		`)
		if err != nil {
			return err
		}

		tLog := logger.Session("table", lager.Data{
			"table": table,
		})

		encryptedRows := 0

		for rows.Next() {
			var (
				id         int
				val, nonce string
			)

			err := rows.Scan(&id, &nonce, &val)
			if err != nil {
				tLog.Error("failed-to-scan", err)
				return err
			}

			rLog := tLog.Session("row", lager.Data{
				"id": id,
			})

			decrypted, err := oldKey.Decrypt(val, &nonce)
			if err != nil {
				_, err = newKey.Decrypt(val, &nonce)
				if err == nil {
					rLog.Debug("already-encrypted-with-new-key")
					continue
				}

				logger.Error("failed-to-decrypt-with-either-key", err)
				return ErrEncryptedWithUnknownKey
			}

			encrypted, newNonce, err := newKey.Encrypt(decrypted)
			if err != nil {
				rLog.Error("failed-to-encrypt", err)
				return err
			}

			_, err = sqlDB.Exec(`
				UPDATE `+table+`
				SET `+col+` = $1, nonce = $2
				WHERE id = $3
			`, encrypted, newNonce, id)
			if err != nil {
				rLog.Error("failed-to-update", err)
				return err
			}

			encryptedRows++
		}

		if encryptedRows > 0 {
			tLog.Info("re-encrypted-existing-encrypted-data", lager.Data{
				"rows": encryptedRows,
			})
		}
	}

	return nil
}

type db struct {
	*sql.DB

	bus        NotificationsBus
	encryption EncryptionStrategy
}

func (db *db) Bus() NotificationsBus {
	return db.bus
}

func (db *db) EncryptionStrategy() EncryptionStrategy {
	return db.encryption
}

func (db *db) Close() error {
	var errs error
	dbErr := db.DB.Close()
	if dbErr != nil {
		errs = multierror.Append(errs, dbErr)
	}

	busErr := db.bus.Close()
	if busErr != nil {
		errs = multierror.Append(errs, busErr)
	}

	return errs
}

func (db *db) Begin() (Tx, error) {
	tx, err := db.DB.Begin()
	if err != nil {
		return nil, err
	}

	return &dbTx{tx}, nil
}

// to conform to squirrel.Runner interface
func (db *db) QueryRow(query string, args ...interface{}) squirrel.RowScanner {
	return db.DB.QueryRow(query, args...)
}

type dbTx struct {
	*sql.Tx
}

// to conform to squirrel.Runner interface
func (tx *dbTx) QueryRow(query string, args ...interface{}) squirrel.RowScanner {
	return tx.Tx.QueryRow(query, args...)
}

type nonOneRowAffectedError struct {
	RowsAffected int64
}

func (err nonOneRowAffectedError) Error() string {
	return fmt.Sprintf("expected 1 row to be updated; got %d", err.RowsAffected)
}
<|endoftext|>


<commit_before>package deb

// PackageQuery is interface of predicate on Package
type PackageQuery interface {
	// Matches calculates match of condition against package
	Matches(pkg *Package) bool
	// Fast returns if search strategy is possible for this query
	Fast() bool
	// Query performs search on package list
	Query(list *PackageList) *PackageList
}

// OrQuery is L | R
type OrQuery struct {
	L, R PackageQuery
}

// AndQuery is L , R
type AndQuery struct {
	L, R PackageQuery
}

// NotQuery is ! Q
type NotQuery struct {
	Q PackageQuery
}

// FieldQuery is generic request against field
type FieldQuery struct {
	Field    string
	Relation int
	Value    string
}

// PkgQuery is search request against specific package
type PkgQuery struct {
	Pkg     string
	Version string
	Arch    string
}

// DependencyQuery is generic Debian-dependency like query
type DependencyQuery struct {
	Dep Dependency
}

// Matches if any of L, R matches
func (q *OrQuery) Matches(pkg *Package) bool {
	return q.L.Matches(pkg) || q.R.Matches(pkg)
}

// Fast is true only if both parts are fast
func (q *OrQuery) Fast() bool {
	return q.L.Fast() && q.R.Fast()
}

// Query strategy depends on nodes
func (q *OrQuery) Query(list *PackageList) (result *PackageList) {
	if q.Fast() {
		result = q.L.Query(list)
		result.Append(q.R.Query(list))
	} else {
		result = list.Scan(q)
	}
	return
}

// Matches if both of L, R matches
func (q *AndQuery) Matches(pkg *Package) bool {
	return q.L.Matches(pkg) && q.R.Matches(pkg)
}

// Fast is true if any of the parts are fast
func (q *AndQuery) Fast() bool {
	return q.L.Fast() || q.R.Fast()
}

// Query strategy depends on nodes
func (q *AndQuery) Query(list *PackageList) (result *PackageList) {
	if !q.Fast() {
		result = list.Scan(q)
	} else {
		if q.L.Fast() {
			result = q.L.Query(list)
			result = result.Scan(q.R)
		} else {
			result = q.R.Query(list)
			result = result.Scan(q.L)
		}
	}
	return
}

// Matches if not matches
func (q *NotQuery) Matches(pkg *Package) bool {
	return !q.Q.Matches(pkg)
}

// Fast is false
func (q *NotQuery) Fast() bool {
	return false
}

// Query strategy is scan always
func (q *NotQuery) Query(list *PackageList) (result *PackageList) {
	result = list.Scan(q)
	return
}

// Matches on generic field
func (q *FieldQuery) Matches(pkg *Package) bool {
	panic("not implemented yet")
}

// Query runs iteration through list
func (q *FieldQuery) Query(list *PackageList) (result *PackageList) {
	panic("not implemented yet")
}

// Fast depends on the query
func (q *FieldQuery) Fast() bool {
	return false
}

// Matches on dependency condition
func (q *DependencyQuery) Matches(pkg *Package) bool {
	return pkg.MatchesDependency(q.Dep)
}

// Fast is always true for dependency query
func (q *DependencyQuery) Fast() bool {
	return true
}

// Query runs PackageList.Search
func (q *DependencyQuery) Query(list *PackageList) (result *PackageList) {
	result = NewPackageList()
	for _, pkg := range list.Search(q.Dep, true) {
		result.Add(pkg)
	}

	return
}

// Matches on specific properties
func (q *PkgQuery) Matches(pkg *Package) bool {
	return pkg.Name == q.Pkg && pkg.Version == q.Version && pkg.Architecture == q.Arch
}

// Fast is always true for package query
func (q *PkgQuery) Fast() bool {
	return true
}

// Query looks up specific package
func (q *PkgQuery) Query(list *PackageList) (result *PackageList) {
	result = NewPackageList()

	pkg := list.packages["P"+q.Arch+" "+q.Pkg+" "+q.Version]
	if pkg != nil {
		result.Add(pkg)
	}

	return
}
<commit_msg>Implementation for FieldQuery.<commit_after>package deb

import (
	"path/filepath"
)

// PackageQuery is interface of predicate on Package
type PackageQuery interface {
	// Matches calculates match of condition against package
	Matches(pkg *Package) bool
	// Fast returns if search strategy is possible for this query
	Fast() bool
	// Query performs search on package list
	Query(list *PackageList) *PackageList
}

// OrQuery is L | R
type OrQuery struct {
	L, R PackageQuery
}

// AndQuery is L , R
type AndQuery struct {
	L, R PackageQuery
}

// NotQuery is ! Q
type NotQuery struct {
	Q PackageQuery
}

// FieldQuery is generic request against field
type FieldQuery struct {
	Field    string
	Relation int
	Value    string
}

// PkgQuery is search request against specific package
type PkgQuery struct {
	Pkg     string
	Version string
	Arch    string
}

// DependencyQuery is generic Debian-dependency like query
type DependencyQuery struct {
	Dep Dependency
}

// Matches if any of L, R matches
func (q *OrQuery) Matches(pkg *Package) bool {
	return q.L.Matches(pkg) || q.R.Matches(pkg)
}

// Fast is true only if both parts are fast
func (q *OrQuery) Fast() bool {
	return q.L.Fast() && q.R.Fast()
}

// Query strategy depends on nodes
func (q *OrQuery) Query(list *PackageList) (result *PackageList) {
	if q.Fast() {
		result = q.L.Query(list)
		result.Append(q.R.Query(list))
	} else {
		result = list.Scan(q)
	}
	return
}

// Matches if both of L, R matches
func (q *AndQuery) Matches(pkg *Package) bool {
	return q.L.Matches(pkg) && q.R.Matches(pkg)
}

// Fast is true if any of the parts are fast
func (q *AndQuery) Fast() bool {
	return q.L.Fast() || q.R.Fast()
}

// Query strategy depends on nodes
func (q *AndQuery) Query(list *PackageList) (result *PackageList) {
	if !q.Fast() {
		result = list.Scan(q)
	} else {
		if q.L.Fast() {
			result = q.L.Query(list)
			result = result.Scan(q.R)
		} else {
			result = q.R.Query(list)
			result = result.Scan(q.L)
		}
	}
	return
}

// Matches if not matches
func (q *NotQuery) Matches(pkg *Package) bool {
	return !q.Q.Matches(pkg)
}

// Fast is false
func (q *NotQuery) Fast() bool {
	return false
}

// Query strategy is scan always
func (q *NotQuery) Query(list *PackageList) (result *PackageList) {
	result = list.Scan(q)
	return
}

// Matches on generic field
func (q *FieldQuery) Matches(pkg *Package) bool {
	if q.Field == "$Version" {
		return pkg.MatchesDependency(Dependency{Pkg: pkg.Name, Relation: q.Relation, Version: q.Value})
	}

	field := pkg.GetField(q.Field)

	switch q.Relation {
	case VersionDontCare:
		return field != ""
	case VersionEqual:
		return field == q.Value
	case VersionGreater:
		return field > q.Value
	case VersionGreaterOrEqual:
		return field >= q.Value
	case VersionLess:
		return field < q.Value
	case VersionLessOrEqual:
		return field <= q.Value
	case VersionPatternMatch:
		matched, err := filepath.Match(q.Value, field)
		return err == nil && matched
	case VersionRegexp:
		panic("regexp matching not implemented yet")

	}
	panic("unknown relation")
}

// Query runs iteration through list
func (q *FieldQuery) Query(list *PackageList) (result *PackageList) {
	result = list.Scan(q)
	return
}

// Fast depends on the query
func (q *FieldQuery) Fast() bool {
	return false
}

// Matches on dependency condition
func (q *DependencyQuery) Matches(pkg *Package) bool {
	return pkg.MatchesDependency(q.Dep)
}

// Fast is always true for dependency query
func (q *DependencyQuery) Fast() bool {
	return true
}

// Query runs PackageList.Search
func (q *DependencyQuery) Query(list *PackageList) (result *PackageList) {
	result = NewPackageList()
	for _, pkg := range list.Search(q.Dep, true) {
		result.Add(pkg)
	}

	return
}

// Matches on specific properties
func (q *PkgQuery) Matches(pkg *Package) bool {
	return pkg.Name == q.Pkg && pkg.Version == q.Version && pkg.Architecture == q.Arch
}

// Fast is always true for package query
func (q *PkgQuery) Fast() bool {
	return true
}

// Query looks up specific package
func (q *PkgQuery) Query(list *PackageList) (result *PackageList) {
	result = NewPackageList()

	pkg := list.packages["P"+q.Arch+" "+q.Pkg+" "+q.Version]
	if pkg != nil {
		result.Add(pkg)
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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.

package workflow

import (
	"encoding/json"
	"fmt"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type EventDB struct {
	*ResourceDB
}

func NewEventDB(service *WorkflowService, esi elasticsearch.IIndex) (*EventDB, error) {
	rdb, err := NewResourceDB(service, esi, EventIndexSettings)
	if err != nil {
		return nil, err
	}
	erdb := EventDB{ResourceDB: rdb}
	return &erdb, nil
}

func (db *EventDB) PostData(mapping string, obj interface{}, id piazza.Ident) (piazza.Ident, error) {
	indexResult, err := db.Esi.PostData(mapping, id.String(), obj)
	if err != nil {
		return piazza.NoIdent, LoggedError("EventDB.PostData failed: %s", err)
	}
	if !indexResult.Created {
		return piazza.NoIdent, LoggedError("EventDB.PostData failed: not created")
	}

	return id, nil
}

func (db *EventDB) GetAll(mapping string, format *piazza.JsonPagination) ([]Event, int64, error) {
	events := []Event{}
	var err error

	exists := true
	if mapping != "" {
		exists, err = db.Esi.TypeExists(mapping)
		if err != nil {
			return events, 0, err
		}
	}
	if !exists {
		return nil, 0, fmt.Errorf("Type %s does not exist", mapping)
	}

	searchResult, err := db.Esi.FilterByMatchAll(mapping, format)
	if err != nil {
		return nil, 0, LoggedError("EventDB.GetAll failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("EventDB.GetAll failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {
		for _, hit := range *searchResult.GetHits() {
			var event Event
			err := json.Unmarshal(*hit.Source, &event)
			if err != nil {
				return nil, 0, err
			}
			events = append(events, event)
		}
	}

	return events, searchResult.TotalHits(), nil
}

func (db *EventDB) lookupEventTypeNameByEventID(id piazza.Ident) (string, error) {
	var mapping string = ""

	types, err := db.Esi.GetTypes()
	if err != nil {
		return "", err
	}
	for _, typ := range types {
		ok, err := db.Esi.ItemExists(typ, id.String())
		if err != nil {
			return "", err
		}
		if ok {
			mapping = typ
			break
		}
	}

	return mapping, nil
}

// NameExists checks if an EventType name exists.
// This is easier to check in EventDB, as the mappings use the EventType.Name.
func (db *EventDB) NameExists(name string) (bool, error) {
	return db.Esi.TypeExists(name)
}

func (db *EventDB) GetOne(mapping string, id piazza.Ident) (*Event, bool, error) {
	getResult, err := db.Esi.GetByID(mapping, id.String())
	if err != nil {
		return nil, getResult.Found, LoggedError("EventDB.GetOne failed: %s", err)
	}
	if getResult == nil {
		return nil, true, LoggedError("EventDB.GetOne failed: no getResult")
	}

	src := getResult.Source
	var event Event
	err = json.Unmarshal(*src, &event)
	if err != nil {
		return nil, getResult.Found, err
	}

	return &event, getResult.Found, nil
}

func (db *EventDB) DeleteByID(mapping string, id piazza.Ident) (bool, error) {
	deleteResult, err := db.Esi.DeleteByID(mapping, string(id))
	if err != nil {
		return deleteResult.Found, LoggedError("EventDB.DeleteById failed: %s", err)
	}
	if deleteResult == nil {
		return false, LoggedError("EventDB.DeleteById failed: no deleteResult")
	}

	return deleteResult.Found, nil
}

func (db *EventDB) AddMapping(name string, mapping map[string]elasticsearch.MappingElementTypeName) error {
	jsn, err := elasticsearch.ConstructMappingSchema(name, mapping)
	if err != nil {
		return LoggedError("EventDB.AddMapping failed: %s", err)
	}

	err = db.Esi.SetMapping(name, jsn)
	if err != nil {
		return LoggedError("EventDB.AddMapping SetMapping failed: %s", err)
	}

	return nil
}

func (db *EventDB) PercolateEventData(eventType string, data map[string]interface{}, id piazza.Ident) (*[]piazza.Ident, error) {
	percolateResponse, err := db.Esi.AddPercolationDocument(eventType, data)

	if err != nil {
		return nil, LoggedError("EventDB.PercolateEventData failed: %s", err)
	}
	if percolateResponse == nil {
		return nil, LoggedError("EventDB.PercolateEventData failed: no percolateResult")
	}

	// add the triggers to the alert queue
	ids := make([]piazza.Ident, len(percolateResponse.Matches))
	for i, v := range percolateResponse.Matches {
		ids[i] = piazza.Ident(v.Id)
	}

	return &ids, nil
}
<commit_msg>Created ConstructEventMappingSchema function<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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.

package workflow

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type EventDB struct {
	*ResourceDB
}

func NewEventDB(service *WorkflowService, esi elasticsearch.IIndex) (*EventDB, error) {
	rdb, err := NewResourceDB(service, esi, EventIndexSettings)
	if err != nil {
		return nil, err
	}
	erdb := EventDB{ResourceDB: rdb}
	return &erdb, nil
}

func (db *EventDB) PostData(mapping string, obj interface{}, id piazza.Ident) (piazza.Ident, error) {
	indexResult, err := db.Esi.PostData(mapping, id.String(), obj)
	if err != nil {
		return piazza.NoIdent, LoggedError("EventDB.PostData failed: %s", err)
	}
	if !indexResult.Created {
		return piazza.NoIdent, LoggedError("EventDB.PostData failed: not created")
	}

	return id, nil
}

func (db *EventDB) GetAll(mapping string, format *piazza.JsonPagination) ([]Event, int64, error) {
	events := []Event{}
	var err error

	exists := true
	if mapping != "" {
		exists, err = db.Esi.TypeExists(mapping)
		if err != nil {
			return events, 0, err
		}
	}
	if !exists {
		return nil, 0, fmt.Errorf("Type %s does not exist", mapping)
	}

	searchResult, err := db.Esi.FilterByMatchAll(mapping, format)
	if err != nil {
		return nil, 0, LoggedError("EventDB.GetAll failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("EventDB.GetAll failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {
		for _, hit := range *searchResult.GetHits() {
			var event Event
			err := json.Unmarshal(*hit.Source, &event)
			if err != nil {
				return nil, 0, err
			}
			events = append(events, event)
		}
	}

	return events, searchResult.TotalHits(), nil
}

func (db *EventDB) lookupEventTypeNameByEventID(id piazza.Ident) (string, error) {
	var mapping string = ""

	types, err := db.Esi.GetTypes()
	if err != nil {
		return "", err
	}
	for _, typ := range types {
		ok, err := db.Esi.ItemExists(typ, id.String())
		if err != nil {
			return "", err
		}
		if ok {
			mapping = typ
			break
		}
	}

	return mapping, nil
}

// NameExists checks if an EventType name exists.
// This is easier to check in EventDB, as the mappings use the EventType.Name.
func (db *EventDB) NameExists(name string) (bool, error) {
	return db.Esi.TypeExists(name)
}

func (db *EventDB) GetOne(mapping string, id piazza.Ident) (*Event, bool, error) {
	getResult, err := db.Esi.GetByID(mapping, id.String())
	if err != nil {
		return nil, getResult.Found, LoggedError("EventDB.GetOne failed: %s", err)
	}
	if getResult == nil {
		return nil, true, LoggedError("EventDB.GetOne failed: no getResult")
	}

	src := getResult.Source
	var event Event
	err = json.Unmarshal(*src, &event)
	if err != nil {
		return nil, getResult.Found, err
	}

	return &event, getResult.Found, nil
}

func (db *EventDB) DeleteByID(mapping string, id piazza.Ident) (bool, error) {
	deleteResult, err := db.Esi.DeleteByID(mapping, string(id))
	if err != nil {
		return deleteResult.Found, LoggedError("EventDB.DeleteById failed: %s", err)
	}
	if deleteResult == nil {
		return false, LoggedError("EventDB.DeleteById failed: no deleteResult")
	}

	return deleteResult.Found, nil
}

func (db *EventDB) AddMapping(name string, mapping map[string]elasticsearch.MappingElementTypeName) error {
	jsn, err := ConstructEventMappingSchema(name, mapping)
	if err != nil {
		return LoggedError("EventDB.AddMapping failed: %s", err)
	}

	err = db.Esi.SetMapping(name, jsn)
	if err != nil {
		return LoggedError("EventDB.AddMapping SetMapping failed: %s", err)
	}

	return nil
}

func ConstructEventMappingSchema(name string, mapping map[string]elasticsearch.MappingElementTypeName) (piazza.JsonString, error) {
	const template string = `{
		"%s":{
			"properties":{
				"data":{
					"properties": {
						%s
					}
				}
			}
		}
	}`
	stuff := make([]string, len(mapping))
	i := 0
	for k, v := range mapping {
		stuff[i] = fmt.Sprintf(`"%s": {"type":"%s"}`, k, v)
		i++
	}
	json := fmt.Sprintf(template, name, strings.Join(stuff, ","))
	return piazza.JsonString(json), nil
}

func (db *EventDB) PercolateEventData(eventType string, data map[string]interface{}, id piazza.Ident) (*[]piazza.Ident, error) {
	percolateResponse, err := db.Esi.AddPercolationDocument(eventType, data)

	if err != nil {
		return nil, LoggedError("EventDB.PercolateEventData failed: %s", err)
	}
	if percolateResponse == nil {
		return nil, LoggedError("EventDB.PercolateEventData failed: no percolateResult")
	}

	// add the triggers to the alert queue
	ids := make([]piazza.Ident, len(percolateResponse.Matches))
	for i, v := range percolateResponse.Matches {
		ids[i] = piazza.Ident(v.Id)
	}

	return &ids, nil
}
<|endoftext|>


<commit_before>//
// Written by Michael Mattioli
//

package main

import (
    "fmt"
    "sort"
    "sync"
    "time"
    "log"
)

func ThreeSum(nums ...int) {

    // Calculate and log the total execution time.
    defer func(t time.Time) {
        log.Printf("Execution time: %s", time.Since(t))
    }(time.Now())

    // Use a slice to store the triplets.
    var trpl [][3]int

    // Use a channel to send data across goroutines and a WaitGroup to ensure all goroutines finish
    // before closing the channel.
    c := make(chan [3]int)
    var wg sync.WaitGroup
    wg.Add(len(nums))

    // Keep track of all triplets found and ensure no duplicates.
    go func(it <-chan [3]int) {
        for i := range it {
            var found bool
            for t := range trpl {
                if trpl[t][0] == i[0] && trpl[t][1] == i[1] && trpl[t][2] == i[2] {
                    found = true
                }
            }
            if !found {
                trpl = append(trpl, i)
            }
        }
    }(c)

    // Find all triplets whose sum equals zero (0).
    sort.Ints(nums)
    for i := range nums {
        go func(i int, ot chan<- [3]int) {
            defer wg.Done()
            start, end := i + 1, len(nums) - 1
            for start < end {
                val := nums[i] + nums[start] + nums[end]
                switch {
                case val == 0:
                    ot <- [3]int{nums[i], nums[start], nums[end]}
                    end--
                case val > 0:
                    end--
                default:
                    start++
                }
            }
        }(i, c)
    }

    wg.Wait()
    close(c)

    // Display the triplets.
    for t := range trpl {
        fmt.Println(trpl[t])
    }

}

func main() {
    ThreeSum(4, 5, -1, -2, -7, 2, -5, -3, -7, -3, 1)
    ThreeSum(-1, -6, -3, -7, 5, -8, 2, -8, 1)
    ThreeSum(-5, -1, -4, 2, 9, -9, -6, -1, -7)
}
<commit_msg>Add comments and return value to challenge 323<commit_after>//
// Written by Michael Mattioli
//

package main

import (
    "fmt"
    "sort"
    "sync"
    "time"
    "log"
)

// ThreeSum returns a slice containing slices composed of three (3) integers whose sum equals
// zero (0) given a set of real numbers.
func ThreeSum(nums ...int) [][3]int {

    // Calculate and log the total execution time.
    defer func(t time.Time) {
        log.Printf("Execution time: %s", time.Since(t))
    }(time.Now())

    // Use a slice to store the triplets.
    var trpl [][3]int

    // Use a channel to send data across goroutines and a WaitGroup to ensure all goroutines finish
    // before closing the channel.
    c := make(chan [3]int)
    var wg sync.WaitGroup
    wg.Add(len(nums))

    // Keep track of all triplets found and ensure no duplicates.
    go func(it <-chan [3]int) {
        for i := range it {
            var found bool
            for t := range trpl {
                if trpl[t][0] == i[0] && trpl[t][1] == i[1] && trpl[t][2] == i[2] {
                    found = true
                }
            }
            if !found {
                trpl = append(trpl, i)
            }
        }
    }(c)

    // Find all triplets whose sum equals zero (0).
    sort.Ints(nums)
    for i := range nums {
        go func(i int, ot chan<- [3]int) {
            defer wg.Done()
            start, end := i + 1, len(nums) - 1
            for start < end {
                val := nums[i] + nums[start] + nums[end]
                switch {
                case val == 0:
                    ot <- [3]int{nums[i], nums[start], nums[end]}
                    end--
                case val > 0:
                    end--
                default:
                    start++
                }
            }
        }(i, c)
    }

    wg.Wait()
    close(c)
    return trpl

}

func main() {
    fmt.Println(ThreeSum(4, 5, -1, -2, -7, 2, -5, -3, -7, -3, 1))
    fmt.Println(ThreeSum(-1, -6, -3, -7, 5, -8, 2, -8, 1))
    fmt.Println(ThreeSum(-5, -1, -4, 2, 9, -9, -6, -1, -7))
}
<|endoftext|>