<commit_before>/* Data functions */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 main

import (
	"net/http"
	"encoding/json"
	"fmt"
	"github.com/ctdk/goiardi/data_bag"
	"github.com/ctdk/goiardi/util"
	"github.com/ctdk/goiardi/actor"
)

func data_handler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type", "application/json")

	path_array := SplitPath(r.URL.Path)

	db_response := make(map[string]interface{})
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		JsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}

	if len(path_array) == 1 {
		/* Either a list of data bags, or a POST to create a new one */
		switch r.Method {
			case "GET":
				if opUser.IsValidator() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				/* The list */
				db_list := data_bag.GetList()
				for _, k := range db_list {
					item_url := fmt.Sprintf("/data/%s", k)
					db_response[k] = util.CustomURL(item_url)
				}
			case "POST":
				if !opUser.IsAdmin() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				db_data, jerr := ParseObjJson(r.Body)
				if jerr != nil {
					JsonErrorReport(w, r, jerr.Error(), http.StatusBadRequest)
					return
				}
				/* check that the name exists */
				switch t := db_data["name"].(type) {
					case string:
						if t == "" {
							JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
							return
						}
					default:
						JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
						return
				}
				chef_dbag, _ := data_bag.Get(db_data["name"].(string))
				if chef_dbag != nil {
					httperr := fmt.Errorf("Data bag %s already exists.", db_data["name"].(string))
					JsonErrorReport(w, r, httperr.Error(), http.StatusConflict)
					return
				}
				chef_dbag, nerr := data_bag.New(db_data["name"].(string))
				if nerr != nil {
					JsonErrorReport(w, r, nerr.Error(), nerr.Status())
					return
				}
				serr := chef_dbag.Save()
				if serr != nil {
					JsonErrorReport(w, r, serr.Error(), http.StatusInternalServerError)
					return
				}
				db_response["uri"] = util.ObjURL(chef_dbag)
				w.WriteHeader(http.StatusCreated)
			default:
				/* The chef-pedant spec wants this response for
				 * some reason. Mix it up, I guess. */
				JsonErrorReport(w, r, "GET, PUT", http.StatusMethodNotAllowed)
				return
		}
	} else {
		db_name := path_array[1]

		/* chef-pedant is unhappy about not reporting the HTTP status
		 * as 404 by fetching the data bag before we see if the method
		 * is allowed, so do a quick check for that here. */
		if (len(path_array) == 2  && r.Method == "PUT") || (len(path_array) == 3 && r.Method == "POST"){
			JsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}
		if opUser.IsValidator() || (!opUser.IsAdmin() && r.Method != "GET") {
			JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
			return
		}
		chef_dbag, err := data_bag.Get(db_name)
		if err != nil {
			var err_msg string
			status := err.Status()
			if r.Method == "POST" {
				/* Posts get a special snowflake message */
				err_msg = fmt.Sprintf("No data bag '%s' could be found. Please create this data bag before adding items to it.", db_name)
			} else {
				if len(path_array) == 3 {
					/* This is nuts. */
					if r.Method == "DELETE" {
						err_msg = fmt.Sprintf("Cannot load data bag %s item %s", db_name, path_array[2])
					} else {
						err_msg = fmt.Sprintf("Cannot load data bag item %s for data bag %s", path_array[2], db_name)
					}
				} else {
					err_msg = err.Error()
				}
			}
			JsonErrorReport(w, r, err_msg, status)
			return
		}
		if len(path_array) == 2 {
			/* getting list of data bag items and creating data bag
			 * items. */
			switch r.Method {
				case "GET":

					for _, k := range chef_dbag.ListDBItems() {
						db_response[k] = util.CustomObjURL(chef_dbag, k)
					}
				case "DELETE":
					/* The chef API docs don't say anything
					 * about this existing, but it does,
					 * and without it you can't delete data
					 * bags at all. */
					db_response["chef_type"] = "data_bag"
					db_response["json_class"] = "Chef::DataBag"
					db_response["name"] = chef_dbag.Name
					err := chef_dbag.Delete()
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
				case "POST":
					raw_data := data_bag.RawDataBagJson(r.Body)
					dbitem, nerr := chef_dbag.NewDBItem(raw_data)
					if nerr != nil {
						JsonErrorReport(w, r, nerr.Error(), nerr.Status())
						return
					}

					/* The data bag return values are all
					 * kinds of weird. Sometimes it sends
					 * just the raw data, sometimes it sends
					 * the whole object, sometimes a special
					 * snowflake version. Ugh. Have to loop
					 * through to avoid updating the pointer
					 * in the cache by just assigning
					 * dbitem.RawData to db_response. Urk.
					 */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					w.WriteHeader(http.StatusCreated)
				default:
					JsonErrorReport(w, r, "GET, DELETE, POST", http.StatusMethodNotAllowed)
					return
			}
		} else {
			/* getting, editing, and deleting existing data bag items. */
			db_item_name := path_array[2]
			if _, err := chef_dbag.GetDBItem(db_item_name); err != nil {
				var httperr string
				if r.Method != "DELETE" {
					httperr = fmt.Sprintf("Cannot load data bag item %s for data bag %s", db_item_name, chef_dbag.Name)
				} else {
					httperr = fmt.Sprintf("Cannot load data bag %s item %s", chef_dbag.Name, db_item_name)
				}
				JsonErrorReport(w, r, httperr, http.StatusNotFound)
				return
			}
			switch r.Method {
				case "GET":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					db_response = dbi.RawData
				case "DELETE":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					/* Gotta short circuit this */
					enc := json.NewEncoder(w)
					if err := enc.Encode(&dbi); err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					err = chef_dbag.DeleteDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					return
				case "PUT":
					raw_data := data_bag.RawDataBagJson(r.Body)
					if raw_id, ok := raw_data["id"]; ok {
						switch raw_id := raw_id.(type) {
							case string:
								if raw_id != db_item_name {
									JsonErrorReport(w, r, "DataBagItem name mismatch.", http.StatusBadRequest)
									return
								}
							default:
								JsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
								return
						}
					}
					dbitem, err := chef_dbag.UpdateDBItem(db_item_name, raw_data)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					/* Another weird data bag item response
					 * which isn't at all unusual. */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					db_response["id"] = db_item_name
				default:
					JsonErrorReport(w, r, "GET, DELETE, PUT", http.StatusMethodNotAllowed)
					return
			}
		}
	}

	enc := json.NewEncoder(w)
	if err := enc.Encode(&db_response); err != nil {
		JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
	}
}

<commit_msg>Log data bags<commit_after>/* Data functions */

/*
 * Copyright (c) 2013-2014, Jeremy Bingham (<jbingham@gmail.com>)
 *
 * 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 main

import (
	"net/http"
	"encoding/json"
	"fmt"
	"github.com/ctdk/goiardi/data_bag"
	"github.com/ctdk/goiardi/util"
	"github.com/ctdk/goiardi/actor"
	"github.com/ctdk/goiardi/log_info"
)

func data_handler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type", "application/json")

	path_array := SplitPath(r.URL.Path)

	db_response := make(map[string]interface{})
	opUser, oerr := actor.GetReqUser(r.Header.Get("X-OPS-USERID"))
	if oerr != nil {
		JsonErrorReport(w, r, oerr.Error(), oerr.Status())
		return
	}

	if len(path_array) == 1 {
		/* Either a list of data bags, or a POST to create a new one */
		switch r.Method {
			case "GET":
				if opUser.IsValidator() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				/* The list */
				db_list := data_bag.GetList()
				for _, k := range db_list {
					item_url := fmt.Sprintf("/data/%s", k)
					db_response[k] = util.CustomURL(item_url)
				}
			case "POST":
				if !opUser.IsAdmin() {
					JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
					return
				}
				db_data, jerr := ParseObjJson(r.Body)
				if jerr != nil {
					JsonErrorReport(w, r, jerr.Error(), http.StatusBadRequest)
					return
				}
				/* check that the name exists */
				switch t := db_data["name"].(type) {
					case string:
						if t == "" {
							JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
							return
						}
					default:
						JsonErrorReport(w, r, "Field 'name' missing", http.StatusBadRequest)
						return
				}
				chef_dbag, _ := data_bag.Get(db_data["name"].(string))
				if chef_dbag != nil {
					httperr := fmt.Errorf("Data bag %s already exists.", db_data["name"].(string))
					JsonErrorReport(w, r, httperr.Error(), http.StatusConflict)
					return
				}
				chef_dbag, nerr := data_bag.New(db_data["name"].(string))
				if nerr != nil {
					JsonErrorReport(w, r, nerr.Error(), nerr.Status())
					return
				}
				serr := chef_dbag.Save()
				if serr != nil {
					JsonErrorReport(w, r, serr.Error(), http.StatusInternalServerError)
					return
				}
				if lerr := log_info.LogEvent(opUser, chef_dbag, "create"); lerr != nil {
					JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
					return
				}
				db_response["uri"] = util.ObjURL(chef_dbag)
				w.WriteHeader(http.StatusCreated)
			default:
				/* The chef-pedant spec wants this response for
				 * some reason. Mix it up, I guess. */
				JsonErrorReport(w, r, "GET, PUT", http.StatusMethodNotAllowed)
				return
		}
	} else {
		db_name := path_array[1]

		/* chef-pedant is unhappy about not reporting the HTTP status
		 * as 404 by fetching the data bag before we see if the method
		 * is allowed, so do a quick check for that here. */
		if (len(path_array) == 2  && r.Method == "PUT") || (len(path_array) == 3 && r.Method == "POST"){
			JsonErrorReport(w, r, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}
		if opUser.IsValidator() || (!opUser.IsAdmin() && r.Method != "GET") {
			JsonErrorReport(w, r, "You are not allowed to perform this action", http.StatusForbidden)
			return
		}
		chef_dbag, err := data_bag.Get(db_name)
		if err != nil {
			var err_msg string
			status := err.Status()
			if r.Method == "POST" {
				/* Posts get a special snowflake message */
				err_msg = fmt.Sprintf("No data bag '%s' could be found. Please create this data bag before adding items to it.", db_name)
			} else {
				if len(path_array) == 3 {
					/* This is nuts. */
					if r.Method == "DELETE" {
						err_msg = fmt.Sprintf("Cannot load data bag %s item %s", db_name, path_array[2])
					} else {
						err_msg = fmt.Sprintf("Cannot load data bag item %s for data bag %s", path_array[2], db_name)
					}
				} else {
					err_msg = err.Error()
				}
			}
			JsonErrorReport(w, r, err_msg, status)
			return
		}
		if len(path_array) == 2 {
			/* getting list of data bag items and creating data bag
			 * items. */
			switch r.Method {
				case "GET":

					for _, k := range chef_dbag.ListDBItems() {
						db_response[k] = util.CustomObjURL(chef_dbag, k)
					}
				case "DELETE":
					/* The chef API docs don't say anything
					 * about this existing, but it does,
					 * and without it you can't delete data
					 * bags at all. */
					db_response["chef_type"] = "data_bag"
					db_response["json_class"] = "Chef::DataBag"
					db_response["name"] = chef_dbag.Name
					err := chef_dbag.Delete()
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, chef_dbag, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
				case "POST":
					raw_data := data_bag.RawDataBagJson(r.Body)
					dbitem, nerr := chef_dbag.NewDBItem(raw_data)
					if nerr != nil {
						JsonErrorReport(w, r, nerr.Error(), nerr.Status())
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "create"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}

					/* The data bag return values are all
					 * kinds of weird. Sometimes it sends
					 * just the raw data, sometimes it sends
					 * the whole object, sometimes a special
					 * snowflake version. Ugh. Have to loop
					 * through to avoid updating the pointer
					 * in the cache by just assigning
					 * dbitem.RawData to db_response. Urk.
					 */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					w.WriteHeader(http.StatusCreated)
				default:
					JsonErrorReport(w, r, "GET, DELETE, POST", http.StatusMethodNotAllowed)
					return
			}
		} else {
			/* getting, editing, and deleting existing data bag items. */
			db_item_name := path_array[2]
			if _, err := chef_dbag.GetDBItem(db_item_name); err != nil {
				var httperr string
				if r.Method != "DELETE" {
					httperr = fmt.Sprintf("Cannot load data bag item %s for data bag %s", db_item_name, chef_dbag.Name)
				} else {
					httperr = fmt.Sprintf("Cannot load data bag %s item %s", chef_dbag.Name, db_item_name)
				}
				JsonErrorReport(w, r, httperr, http.StatusNotFound)
				return
			}
			switch r.Method {
				case "GET":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					db_response = dbi.RawData
				case "DELETE":
					dbi, err := chef_dbag.GetDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					/* Gotta short circuit this */
					enc := json.NewEncoder(w)
					if err := enc.Encode(&dbi); err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					err = chef_dbag.DeleteDBItem(db_item_name)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbi, "delete"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					return
				case "PUT":
					raw_data := data_bag.RawDataBagJson(r.Body)
					if raw_id, ok := raw_data["id"]; ok {
						switch raw_id := raw_id.(type) {
							case string:
								if raw_id != db_item_name {
									JsonErrorReport(w, r, "DataBagItem name mismatch.", http.StatusBadRequest)
									return
								}
							default:
								JsonErrorReport(w, r, "Bad request", http.StatusBadRequest)
								return
						}
					}
					dbitem, err := chef_dbag.UpdateDBItem(db_item_name, raw_data)
					if err != nil {
						JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
						return
					}
					if lerr := log_info.LogEvent(opUser, dbitem, "modify"); lerr != nil {
						JsonErrorReport(w, r, lerr.Error(), http.StatusInternalServerError)
						return
					}
					/* Another weird data bag item response
					 * which isn't at all unusual. */
					for k, v := range dbitem.RawData {
						db_response[k] = v
					}
					db_response["data_bag"] = dbitem.DataBagName
					db_response["chef_type"] = dbitem.ChefType
					db_response["id"] = db_item_name
				default:
					JsonErrorReport(w, r, "GET, DELETE, PUT", http.StatusMethodNotAllowed)
					return
			}
		}
	}

	enc := json.NewEncoder(w)
	if err := enc.Encode(&db_response); err != nil {
		JsonErrorReport(w, r, err.Error(), http.StatusInternalServerError)
	}
}

<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"time"

	"strings"

	"github.com/beevik/etree"
	zglob "github.com/mattn/go-zglob"
	"github.com/nfnt/resize"
	"golang.org/x/tools/godoc/vfs/zipfs"
)

// Series represents a book series
type Series struct {
	Name  string  `json:"name,omitempty"`
	ID    string  `json:"id,omitempty"`
	Index float64 `json:"index,omitempty"`
}

// Book represents a book
type Book struct {
	ID          string    `json:"id"`
	Title       string    `json:"title"`
	Author      string    `json:"author,omitempty"`
	AuthorID    string    `json:"authorid"`
	Publisher   string    `json:"publisher,omitempty"`
	Description string    `json:"description,omitempty"`
	Series      Series    `json:"series,omitempty"`
	Filepath    string    `json:"filepath"`
	HasCover    bool      `json:"hascover"`
	ModTime     time.Time `json:"modtime,omitempty"`
	FileType    string    `json:"filetype,omitempty"`
}

// NewBookFromFile creates a book object from a file
func NewBookFromFile(path, coverpath string) (bk *Book, err error) {
	defer func() {
		if r := recover(); r != nil {
			bk = nil
			err = fmt.Errorf("Unknown error parsing book. Skipping. Error: %s", r)
		}
	}()

	book := new(Book)
	book.Title = filepath.Base(path)
	book.Filepath = path
	book.FileType = strings.ToLower(strings.Replace(filepath.Ext(path), ".", "", -1))

	if file, err := os.Stat(path); err == nil {
		book.ModTime = file.ModTime()
	}

	switch ft := book.FileType; ft {
	case "pdf":
		book.Title = filepath.Base(path)

		m, err := GetPDFMeta(path)
		if err == nil {
			book.Title = m.Title
			book.Author = m.Author
		}

		id := sha1.New()
		io.WriteString(id, book.Author)
		book.AuthorID = hex.EncodeToString(id.Sum(nil))[:10]
		io.WriteString(id, book.Series.Name)
		io.WriteString(id, book.Title)
		book.ID = hex.EncodeToString(id.Sum(nil))[:10]
	case "epub":
		zr, err := zip.OpenReader(path)
		if err != nil {
			return nil, err
		}

		zfs := zipfs.New(zr, "epub")

		rsk, err := zfs.Open("/META-INF/container.xml")
		if err != nil {
			return nil, err
		}
		defer rsk.Close()
		container := etree.NewDocument()
		_, err = container.ReadFrom(rsk)
		if err != nil {
			return nil, err
		}
		rootfile := ""
		for _, e := range container.FindElements("//rootfiles/rootfile[@full-path]") {
			rootfile = e.SelectAttrValue("full-path", "")
		}
		if rootfile == "" {
			return nil, errors.New("Cannot parse container")
		}

		rrsk, err := zfs.Open("/" + rootfile)
		if err != nil {
			return nil, err
		}
		defer rrsk.Close()
		opfdir := filepath.Dir(rootfile)
		opf := etree.NewDocument()
		_, err = opf.ReadFrom(rrsk)
		if err != nil {
			return nil, err
		}
		book.Title = filepath.Base(path)
		for _, e := range opf.FindElements("//title") {
			book.Title = e.Text()
			break
		}
		for _, e := range opf.FindElements("//creator") {
			book.Author = e.Text()
			break
		}
		for _, e := range opf.FindElements("//publisher") {
			book.Publisher = e.Text()
			break
		}
		for _, e := range opf.FindElements("//description") {
			book.Description = e.Text()
			break
		}
		for _, e := range opf.FindElements("//meta[@name='calibre:series']") {
			book.Series.Name = e.SelectAttrValue("content", "")
			seriesid := sha1.New()
			io.WriteString(seriesid, book.Series.Name)
			book.Series.ID = hex.EncodeToString(seriesid.Sum(nil))[:10]
			break
		}
		for _, e := range opf.FindElements("//meta[@name='calibre:series_index']") {
			i, err := strconv.ParseFloat(e.SelectAttrValue("content", "0"), 64)
			if err == nil {
				book.Series.Index = i
				break
			}
		}

		id := sha1.New()
		io.WriteString(id, book.Author)
		book.AuthorID = hex.EncodeToString(id.Sum(nil))[:10]
		io.WriteString(id, book.Series.Name)
		io.WriteString(id, book.Title)
		book.ID = hex.EncodeToString(id.Sum(nil))[:10]

		for _, e := range opf.FindElements("//meta[@name='cover']") {
			coverid := e.SelectAttrValue("content", "")
			if coverid != "" {
				for _, f := range opf.FindElements("//[@id='" + coverid + "']") {
					cover := f.SelectAttrValue("href", "")
					if cover != "" {
						cr, err := zfs.Open("/" + opfdir + "/" + cover)
						if err != nil {
							continue
						}
						defer cr.Close()

						ext := filepath.Ext(cover)
						if ext == ".jpeg" {
							ext = ".jpg"
						}
						cpath := filepath.Join(coverpath, book.ID+".jpg")
						thumbpath := filepath.Join(coverpath, book.ID+"_thumb"+".jpg")

						var img image.Image

						switch ext {
						case ".jpg":
							img, err = jpeg.Decode(cr)
							if err != nil {
								continue
							}
						case ".gif":
							img, err = gif.Decode(cr)
							if err != nil {
								continue
							}
						case ".png":
							img, err = png.Decode(cr)
							if err != nil {
								continue
							}
						}

						coverfile, err := os.Create(cpath)
						if err != nil {
							continue
						}
						defer coverfile.Close()
						err = jpeg.Encode(coverfile, img, nil)
						if err != nil {
							continue
						}

						// Better quality: thumb := resize.Resize(200, 0, img, resize.Lanczos2)
						thumb := resize.Resize(200, 0, img, resize.Bicubic)
						thumbfile, err := os.Create(thumbpath)
						if err != nil {
							continue
						}
						defer thumbfile.Close()
						err = jpeg.Encode(thumbfile, thumb, nil)
						if err != nil {
							continue
						}
						book.HasCover = true
						break
					}
				}
				break
			}
		}
	default:
		return nil, fmt.Errorf("Unknown filetype: %s", book.FileType)
	}

	return book, nil
}

// BookList is a slice of books
type BookList []Book

// NewBookListFromDir creates a BookList from the books in a dir. It will still return a nil error if there are errors indexing some of the books. It will only return an error if there is a problem getting the file list.
func NewBookListFromDir(path, coverdir string, verbose bool) (*BookList, error) {
	matches, err := zglob.Glob(filepath.Join(path, "/**/*.epub"))
	if err != nil {
		return nil, err
	}

	pdfmatches, err := zglob.Glob(filepath.Join(path, "/**/*.pdf"))
	if err != nil {
		return nil, err
	}
	matches = append(matches, pdfmatches...)

	var books BookList
	for i, filename := range matches {
		if verbose {
			log.Printf("%.f%% Indexing %s\n", float64(i)/float64(len(matches))*100, filename)
		}
		book, err := NewBookFromFile(filename, coverdir)
		if err != nil {
			if verbose {
				log.Printf("Error indexing %s: %s\n", filename, err)
			}
			continue
		}
		books = append(books, *book)
	}
	debug.FreeOSMemory()
	return &books, nil
}
<commit_msg>Fix indexing progress percentage<commit_after>package main

import (
	"archive/zip"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime/debug"
	"strconv"
	"time"

	"strings"

	"github.com/beevik/etree"
	zglob "github.com/mattn/go-zglob"
	"github.com/nfnt/resize"
	"golang.org/x/tools/godoc/vfs/zipfs"
)

// Series represents a book series
type Series struct {
	Name  string  `json:"name,omitempty"`
	ID    string  `json:"id,omitempty"`
	Index float64 `json:"index,omitempty"`
}

// Book represents a book
type Book struct {
	ID          string    `json:"id"`
	Title       string    `json:"title"`
	Author      string    `json:"author,omitempty"`
	AuthorID    string    `json:"authorid"`
	Publisher   string    `json:"publisher,omitempty"`
	Description string    `json:"description,omitempty"`
	Series      Series    `json:"series,omitempty"`
	Filepath    string    `json:"filepath"`
	HasCover    bool      `json:"hascover"`
	ModTime     time.Time `json:"modtime,omitempty"`
	FileType    string    `json:"filetype,omitempty"`
}

// NewBookFromFile creates a book object from a file
func NewBookFromFile(path, coverpath string) (bk *Book, err error) {
	defer func() {
		if r := recover(); r != nil {
			bk = nil
			err = fmt.Errorf("Unknown error parsing book. Skipping. Error: %s", r)
		}
	}()

	book := new(Book)
	book.Title = filepath.Base(path)
	book.Filepath = path
	book.FileType = strings.ToLower(strings.Replace(filepath.Ext(path), ".", "", -1))

	if file, err := os.Stat(path); err == nil {
		book.ModTime = file.ModTime()
	}

	switch ft := book.FileType; ft {
	case "pdf":
		book.Title = filepath.Base(path)

		m, err := GetPDFMeta(path)
		if err == nil {
			book.Title = m.Title
			book.Author = m.Author
		}

		id := sha1.New()
		io.WriteString(id, book.Author)
		book.AuthorID = hex.EncodeToString(id.Sum(nil))[:10]
		io.WriteString(id, book.Series.Name)
		io.WriteString(id, book.Title)
		book.ID = hex.EncodeToString(id.Sum(nil))[:10]
	case "epub":
		zr, err := zip.OpenReader(path)
		if err != nil {
			return nil, err
		}

		zfs := zipfs.New(zr, "epub")

		rsk, err := zfs.Open("/META-INF/container.xml")
		if err != nil {
			return nil, err
		}
		defer rsk.Close()
		container := etree.NewDocument()
		_, err = container.ReadFrom(rsk)
		if err != nil {
			return nil, err
		}
		rootfile := ""
		for _, e := range container.FindElements("//rootfiles/rootfile[@full-path]") {
			rootfile = e.SelectAttrValue("full-path", "")
		}
		if rootfile == "" {
			return nil, errors.New("Cannot parse container")
		}

		rrsk, err := zfs.Open("/" + rootfile)
		if err != nil {
			return nil, err
		}
		defer rrsk.Close()
		opfdir := filepath.Dir(rootfile)
		opf := etree.NewDocument()
		_, err = opf.ReadFrom(rrsk)
		if err != nil {
			return nil, err
		}
		book.Title = filepath.Base(path)
		for _, e := range opf.FindElements("//title") {
			book.Title = e.Text()
			break
		}
		for _, e := range opf.FindElements("//creator") {
			book.Author = e.Text()
			break
		}
		for _, e := range opf.FindElements("//publisher") {
			book.Publisher = e.Text()
			break
		}
		for _, e := range opf.FindElements("//description") {
			book.Description = e.Text()
			break
		}
		for _, e := range opf.FindElements("//meta[@name='calibre:series']") {
			book.Series.Name = e.SelectAttrValue("content", "")
			seriesid := sha1.New()
			io.WriteString(seriesid, book.Series.Name)
			book.Series.ID = hex.EncodeToString(seriesid.Sum(nil))[:10]
			break
		}
		for _, e := range opf.FindElements("//meta[@name='calibre:series_index']") {
			i, err := strconv.ParseFloat(e.SelectAttrValue("content", "0"), 64)
			if err == nil {
				book.Series.Index = i
				break
			}
		}

		id := sha1.New()
		io.WriteString(id, book.Author)
		book.AuthorID = hex.EncodeToString(id.Sum(nil))[:10]
		io.WriteString(id, book.Series.Name)
		io.WriteString(id, book.Title)
		book.ID = hex.EncodeToString(id.Sum(nil))[:10]

		for _, e := range opf.FindElements("//meta[@name='cover']") {
			coverid := e.SelectAttrValue("content", "")
			if coverid != "" {
				for _, f := range opf.FindElements("//[@id='" + coverid + "']") {
					cover := f.SelectAttrValue("href", "")
					if cover != "" {
						cr, err := zfs.Open("/" + opfdir + "/" + cover)
						if err != nil {
							continue
						}
						defer cr.Close()

						ext := filepath.Ext(cover)
						if ext == ".jpeg" {
							ext = ".jpg"
						}
						cpath := filepath.Join(coverpath, book.ID+".jpg")
						thumbpath := filepath.Join(coverpath, book.ID+"_thumb"+".jpg")

						var img image.Image

						switch ext {
						case ".jpg":
							img, err = jpeg.Decode(cr)
							if err != nil {
								continue
							}
						case ".gif":
							img, err = gif.Decode(cr)
							if err != nil {
								continue
							}
						case ".png":
							img, err = png.Decode(cr)
							if err != nil {
								continue
							}
						}

						coverfile, err := os.Create(cpath)
						if err != nil {
							continue
						}
						defer coverfile.Close()
						err = jpeg.Encode(coverfile, img, nil)
						if err != nil {
							continue
						}

						// Better quality: thumb := resize.Resize(200, 0, img, resize.Lanczos2)
						thumb := resize.Resize(200, 0, img, resize.Bicubic)
						thumbfile, err := os.Create(thumbpath)
						if err != nil {
							continue
						}
						defer thumbfile.Close()
						err = jpeg.Encode(thumbfile, thumb, nil)
						if err != nil {
							continue
						}
						book.HasCover = true
						break
					}
				}
				break
			}
		}
	default:
		return nil, fmt.Errorf("Unknown filetype: %s", book.FileType)
	}

	return book, nil
}

// BookList is a slice of books
type BookList []Book

// NewBookListFromDir creates a BookList from the books in a dir. It will still return a nil error if there are errors indexing some of the books. It will only return an error if there is a problem getting the file list.
func NewBookListFromDir(path, coverdir string, verbose bool) (*BookList, error) {
	matches, err := zglob.Glob(filepath.Join(path, "/**/*.epub"))
	if err != nil {
		return nil, err
	}

	pdfmatches, err := zglob.Glob(filepath.Join(path, "/**/*.pdf"))
	if err != nil {
		return nil, err
	}
	matches = append(matches, pdfmatches...)

	var books BookList
	for i, filename := range matches {
		if verbose {
			log.Printf("%.f%% Indexing %s\n", float64(i+1)/float64(len(matches))*100, filename)
		}
		book, err := NewBookFromFile(filename, coverdir)
		if err != nil {
			if verbose {
				log.Printf("Error indexing %s: %s\n", filename, err)
			}
			continue
		}
		books = append(books, *book)
	}
	debug.FreeOSMemory()
	return &books, nil
}
<|endoftext|>


<commit_before>package sockaddr

var rfc1918Networks []IPv4Addr

func init() {
	RFC1918Nets := []string{
		"10.0.0.0/8",
		"192.168.0.0/16",
		"172.16.0.0/12",
	}
	rfc1918Networks = make([]IPv4Addr, 0, len(RFC1918Nets))
	for _, network := range RFC1918Nets {
		sa, err := NewIPv4Addr(network)
		if err != nil {
			panic("Invalid RFC1918 network")
		}
		rfc1918Networks = append(rfc1918Networks, sa)
	}
}
<commit_msg>Add support for RFC 6598<commit_after>package sockaddr

var rfc1918Networks []IPv4Addr
var rfc6598Networks []IPv4Addr

func init() {
	{ // initialize rfc1918
		RFC1918Nets := []string{
			"10.0.0.0/8",
			"192.168.0.0/16",
			"172.16.0.0/12",
		}
		rfc1918Networks = make([]IPv4Addr, 0, len(RFC1918Nets))
		for _, network := range RFC1918Nets {
			sa, err := NewIPv4Addr(network)
			if err != nil {
				panic("Invalid RFC1918 network")
			}
			rfc1918Networks = append(rfc1918Networks, sa)
		}
	}

	{ // initialize rfc6598
		RFC6598Nets := []string{
			"100.64.0.0/10",
		}
		rfc6598Networks = make([]IPv4Addr, 0, len(RFC6598Nets))
		for _, network := range RFC6598Nets {
			sa, err := NewIPv4Addr(network)
			if err != nil {
				panic("Invalid RFC6598 network")
			}
			rfc6598Networks = append(rfc6598Networks, sa)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "net"
//    "golang.org/x/net/ipv4"
    "fmt"
)

type IP_Conn struct {
    pc        *net.IPConn
    version   uint8
    dst, src  string
    headerLen uint16
    //len uint16
    //id uint16
    ttl      uint8
    protocol uint8
    //checksum int
}

func NewIP_Conn(dst string) (*IP_Conn, error) {
    pc, err := net.ListenIP("ip4:17", &net.IPAddr{IP: net.ParseIP(dst)})
    if err != nil {
        fmt.Println("Failed to ListenIP")
        return nil, err
    }

    return &IP_Conn{
        pc:        pc,
        version:   4,
        headerLen: 20,
        dst:       dst,
        src:       "127.0.0.1",
        ttl:       8,
        protocol:  17,
    }, nil
}

func calcChecksum(head []byte, excludeChecksum bool) uint16 {
    totalSum := uint64(0)
    for ind, elem := range head {
        if (ind == 10 || ind == 11) && excludeChecksum { // Ignore the checksum in some situations
            continue
        }

        if ind%2 == 0 {
            totalSum += (uint64)(uint16(elem) << 8)
        } else {
            totalSum += (uint64)(uint16(elem))
        }
    }

    for prefix := (totalSum >> 16); prefix != 0; {
        totalSum = uint64(uint16(totalSum)) + prefix
    }
    carried := uint16(totalSum)

    return ^carried
}

func slicePacket(b []byte) (payload []byte) {
    hdrLen := int(b[0] & 0x0f) * 4
    return payload[hdrLen:]
}

func (ipc *IP_Conn) ReadFrom(b []byte) (payload []byte, e error) {
    _, _, err := ipc.pc.ReadFrom(b)
    p := slicePacket(b)

    return p, err
}

func (ipc *IP_Conn) WriteTo(p []byte) error {
    totalLen := uint16(ipc.headerLen) + uint16(len(p))
    packet := make([]byte, totalLen)
    packet[0] = (byte)(ipc.version << 4) // Version
    packet[1] = 0
    packet[2] = (byte)(totalLen >> 8) // Total Len
    packet[3] = (byte)(totalLen)
    packet[4] = 0 // Identification (for now)
    packet[5] = 0
    packet[6] = byte(1 << 6)         // Flags: Don't fragment
    packet[7] = 0                    // Fragment Offset
    packet[8] = (byte)(ipc.ttl)      // Time to Live
    packet[9] = (byte)(ipc.protocol) // Protocol

    // Src and Dst IPs
    srcIP := net.ParseIP(ipc.src)
    dstIP := net.ParseIP(ipc.dst)
    packet[12] = srcIP[0]
    packet[13] = srcIP[1]
    packet[14] = srcIP[2]
    packet[15] = srcIP[3]
    packet[16] = dstIP[0]
    packet[17] = dstIP[1]
    packet[18] = dstIP[2]
    packet[19] = dstIP[3]

    // Checksum
    checksum := calcChecksum(packet[:20], true)
    packet[10] = byte(checksum >> 8)
    packet[11] = byte(checksum)

    // Payload
    for ind := range p {
        packet[20+ind] = p[ind]
    }
    fmt.Println(packet)

    dstIPAddr, err := net.ResolveIPAddr("ip", ipc.dst)
    if err != nil {
//        fmt.Println(err)
        return err
    }
    fmt.Println(dstIPAddr)

    ipc.pc.WriteToIP(packet, dstIPAddr)
    return err
}

func (ipc *IP_Conn) Close() error {
    return ipc.pc.Close()
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<commit_msg>Debug message<commit_after>package main

import (
    "net"
//    "golang.org/x/net/ipv4"
    "fmt"
)

type IP_Conn struct {
    pc        *net.IPConn
    version   uint8
    dst, src  string
    headerLen uint16
    //len uint16
    //id uint16
    ttl      uint8
    protocol uint8
    //checksum int
}

func NewIP_Conn(dst string) (*IP_Conn, error) {
    pc, err := net.ListenIP("ip4:17", &net.IPAddr{IP: net.ParseIP(dst)})
    if err != nil {
        fmt.Println("Failed to ListenIP")
        return nil, err
    }

    return &IP_Conn{
        pc:        pc,
        version:   4,
        headerLen: 20,
        dst:       dst,
        src:       "127.0.0.1",
        ttl:       8,
        protocol:  17,
    }, nil
}

func calcChecksum(head []byte, excludeChecksum bool) uint16 {
    totalSum := uint64(0)
    for ind, elem := range head {
        if (ind == 10 || ind == 11) && excludeChecksum { // Ignore the checksum in some situations
            continue
        }

        if ind%2 == 0 {
            totalSum += (uint64)(uint16(elem) << 8)
        } else {
            totalSum += (uint64)(uint16(elem))
        }
    }

    for prefix := (totalSum >> 16); prefix != 0; {
        totalSum = uint64(uint16(totalSum)) + prefix
    }
    carried := uint16(totalSum)

    return ^carried
}

func slicePacket(b []byte) (payload []byte) {
    hdrLen := int(b[0] & 0x0f) * 4
    return payload[hdrLen:]
}

func (ipc *IP_Conn) ReadFrom(b []byte) (payload []byte, e error) {
    _, _, err := ipc.pc.ReadFrom(b)
    p := slicePacket(b)

    return p, err
}

func (ipc *IP_Conn) WriteTo(p []byte) error {
    totalLen := uint16(ipc.headerLen) + uint16(len(p))
    fmt.Println(totalLen)
    packet := make([]byte, totalLen)
    packet[0] = (byte)(ipc.version << 4) // Version
    packet[1] = 0
    packet[2] = (byte)(totalLen >> 8) // Total Len
    packet[3] = (byte)(totalLen)
    packet[4] = 0 // Identification (for now)
    packet[5] = 0
    packet[6] = byte(1 << 6)         // Flags: Don't fragment
    packet[7] = 0                    // Fragment Offset
    packet[8] = (byte)(ipc.ttl)      // Time to Live
    packet[9] = (byte)(ipc.protocol) // Protocol

    // Src and Dst IPs
    srcIP := net.ParseIP(ipc.src)
    dstIP := net.ParseIP(ipc.dst)
    packet[12] = srcIP[0]
    packet[13] = srcIP[1]
    packet[14] = srcIP[2]
    packet[15] = srcIP[3]
    packet[16] = dstIP[0]
    packet[17] = dstIP[1]
    packet[18] = dstIP[2]
    packet[19] = dstIP[3]

    // Checksum
    checksum := calcChecksum(packet[:20], true)
    packet[10] = byte(checksum >> 8)
    packet[11] = byte(checksum)

    // Payload
    for ind := range p {
        packet[20+ind] = p[ind]
    }
    fmt.Println(packet)

    dstIPAddr, err := net.ResolveIPAddr("ip", ipc.dst)
    if err != nil {
//        fmt.Println(err)
        return err
    }
    fmt.Println(dstIPAddr)

    ipc.pc.WriteToIP(packet, dstIPAddr)
    return err
}

func (ipc *IP_Conn) Close() error {
    return ipc.pc.Close()
}

/* h := &ipv4.Header{
	Version:  ipv4.Version,      // protocol version
	Len:      20,                // header length
	TOS:      0,                 // type-of-service (0 is everything normal)
	TotalLen: len(x) + 20,       // packet total length (octets)
	ID:       0,                 // identification
	Flags:    ipv4.DontFragment, // flags
	FragOff:  0,                 // fragment offset
	TTL:      8,                 // time-to-live (maximum lifespan in seconds)
	Protocol: 17,                // next protocol (17 is UDP)
	Checksum: 0,                 // checksum (apparently autocomputed)
	//Src:    net.IPv4(127, 0, 0, 1), // source address, apparently done automatically
	Dst: net.ParseIP(c.manager.ipAddress), // destination address
	//Options                         // options, extension headers
}
*/
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAwsSESIdentityNotification_basic(t *testing.T) {
	domain := fmt.Sprintf(
		"%s.terraformtesting.com",
		acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsSESIdentityNotificationDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(testAccAwsSESIdentityNotificationConfig, domain),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsSESIdentityNotificationExists("aws_ses_identity_notification.test"),
				),
			},
		},
	})
}

func testAccCheckAwsSESIdentityNotificationDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).sesConn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_ses_identity_notification" {
			continue
		}

		identity := rs.Primary.ID
		params := &ses.GetIdentityVerificationAttributesInput{
			Identities: []*string{aws.String(identity)},
		}

		response, err := conn.GetIdentityNotificationAttributes(params)
		if err != nil {
			return err
		}

		if response.VerificationAttributes[identity] != nil {
			return fmt.Errorf("SES Identity Notification %s still exists. Failing!", domain)
		}
	}

	return nil
}

func testAccCheckAwsSESIdentityNotificationExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("SES Identity Notification not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("SES Identity Notification name not set")
		}

		identity := rs.Primary.ID
		conn := testAccProvider.Meta().(*AWSClient).sesConn

		params := &ses.GetIdentityNotificationAttributesInput{
			Identities: []*string{aws.String(identity)},
		}

		response, err := conn.GetIdentityNotificationAttributes(params)
		if err != nil {
			return err
		}

		if response.VerificationAttributes[identity] == nil {
			return fmt.Errorf("SES Identity Notification %s not found in AWS", identity)
		}

		return nil
	}
}

const testAccAwsSESIdentityNotificationConfig = `
resource "aws_ses_identity_notification" "test" {
	identity = "%s"
	notification_type = "Complaint"
}
`
<commit_msg>#931 We have NotificationAttributes, and identity not domain<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ses"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAwsSESIdentityNotification_basic(t *testing.T) {
	domain := fmt.Sprintf(
		"%s.terraformtesting.com",
		acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))

	resource.Test(t, resource.TestCase{
		PreCheck: func() {
			testAccPreCheck(t)
		},
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsSESIdentityNotificationDestroy,
		Steps: []resource.TestStep{
			{
				Config: fmt.Sprintf(testAccAwsSESIdentityNotificationConfig, domain),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsSESIdentityNotificationExists("aws_ses_identity_notification.test"),
				),
			},
		},
	})
}

func testAccCheckAwsSESIdentityNotificationDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).sesConn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_ses_identity_notification" {
			continue
		}

		identity := rs.Primary.ID
		params := &ses.GetIdentityVerificationAttributesInput{
			Identities: []*string{aws.String(identity)},
		}

		response, err := conn.GetIdentityNotificationAttributes(params)
		if err != nil {
			return err
		}

		if response.NotificationAttributes[identity] != nil {
			return fmt.Errorf("SES Identity Notification %s still exists. Failing!", identity)
		}
	}

	return nil
}

func testAccCheckAwsSESIdentityNotificationExists(n string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("SES Identity Notification not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("SES Identity Notification name not set")
		}

		identity := rs.Primary.ID
		conn := testAccProvider.Meta().(*AWSClient).sesConn

		params := &ses.GetIdentityNotificationAttributesInput{
			Identities: []*string{aws.String(identity)},
		}

		response, err := conn.GetIdentityNotificationAttributes(params)
		if err != nil {
			return err
		}

		if response.NotificationAttributes[identity] == nil {
			return fmt.Errorf("SES Identity Notification %s not found in AWS", identity)
		}

		return nil
	}
}

const testAccAwsSESIdentityNotificationConfig = `
resource "aws_ses_identity_notification" "test" {
	identity = "%s"
	notification_type = "Complaint"
}
`
<|endoftext|>


<commit_before>package myaws

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

// EC2SSHOptions customize the behavior of the SSH command.
type EC2SSHOptions struct {
	InstanceID   string
	LoginName    string
	IdentityFile string
}

// EC2SSH resolves IP address of EC2 instance and connects to it by SSH.
func (client *Client) EC2SSH(options EC2SSHOptions) error {
	hostname, err := client.resolveEC2IPAddress(options.InstanceID)
	if err != nil {
		return errors.Wrap(err, "unable to resolve IP address:")
	}

	config, err := buildSSHConfig(options.LoginName, options.IdentityFile)
	if err != nil {
		return err
	}

	connection, err := ssh.Dial("tcp", hostname+":22", config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // enable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		return errors.Wrap(err, "unable to put terminal in Raw Mode:")
	}
	defer terminal.Restore(fd, oldState)

	width, height, _ := terminal.GetSize(fd)

	if err := session.RequestPty("xterm", height, width, modes); err != nil {
		return errors.Wrap(err, "request for pseudo terminal failed:")
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdin for session:")
	}
	go io.Copy(stdin, os.Stdin)

	stdout, err := session.StdoutPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdout for session:")
	}
	go io.Copy(os.Stdout, stdout)

	stderr, err := session.StderrPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stderr for session:")
	}
	go io.Copy(os.Stderr, stderr)

	if err := session.Shell(); err != nil {
		return errors.Wrap(err, "failed to start shell:")
	}
	session.Wait()

	return nil
}

func (client *Client) resolveEC2IPAddress(instanceID string) (string, error) {
	params := &ec2.DescribeInstancesInput{
		InstanceIds: []*string{&instanceID},
	}

	response, err := client.EC2.DescribeInstances(params)
	if err != nil {
		return "", errors.Wrap(err, "DescribeInstances failed:")
	}

	instance := *response.Reservations[0].Instances[0]
	if instance.PublicIpAddress == nil {
		return "", fmt.Errorf("no public ip address: %s", instanceID)
	}

	return *instance.PublicIpAddress, nil
}

func buildSSHConfig(loginName string, identityFile string) (*ssh.ClientConfig, error) {
	normalizedIdentityFile := strings.Replace(identityFile, "~", os.Getenv("HOME"), 1)
	key, err := ioutil.ReadFile(normalizedIdentityFile)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read private key:")
	}

	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse private key:")
	}

	config := &ssh.ClientConfig{
		User: loginName,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
	}

	return config, nil
}
<commit_msg>Extract method sshWithTerminal<commit_after>package myaws

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

// EC2SSHOptions customize the behavior of the SSH command.
type EC2SSHOptions struct {
	InstanceID   string
	LoginName    string
	IdentityFile string
}

// EC2SSH resolves IP address of EC2 instance and connects to it by SSH.
func (client *Client) EC2SSH(options EC2SSHOptions) error {
	hostname, err := client.resolveEC2IPAddress(options.InstanceID)
	if err != nil {
		return errors.Wrap(err, "unable to resolve IP address:")
	}

	config, err := buildSSHConfig(options.LoginName, options.IdentityFile)
	if err != nil {
		return err
	}

	connection, err := ssh.Dial("tcp", hostname+":22", config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	if err := sshWithTerminal(connection); err != nil {
		return err
	}

	return nil
}

func (client *Client) resolveEC2IPAddress(instanceID string) (string, error) {
	params := &ec2.DescribeInstancesInput{
		InstanceIds: []*string{&instanceID},
	}

	response, err := client.EC2.DescribeInstances(params)
	if err != nil {
		return "", errors.Wrap(err, "DescribeInstances failed:")
	}

	instance := *response.Reservations[0].Instances[0]
	if instance.PublicIpAddress == nil {
		return "", fmt.Errorf("no public ip address: %s", instanceID)
	}

	return *instance.PublicIpAddress, nil
}

func buildSSHConfig(loginName string, identityFile string) (*ssh.ClientConfig, error) {
	normalizedIdentityFile := strings.Replace(identityFile, "~", os.Getenv("HOME"), 1)
	key, err := ioutil.ReadFile(normalizedIdentityFile)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read private key:")
	}

	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse private key:")
	}

	config := &ssh.ClientConfig{
		User: loginName,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
	}

	return config, nil
}

func sshWithTerminal(connection *ssh.Client) error {
	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // enable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		return errors.Wrap(err, "unable to put terminal in Raw Mode:")
	}
	defer terminal.Restore(fd, oldState)

	width, height, _ := terminal.GetSize(fd)

	if err := session.RequestPty("xterm", height, width, modes); err != nil {
		return errors.Wrap(err, "request for pseudo terminal failed:")
	}

	stdin, err := session.StdinPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdin for session:")
	}
	go io.Copy(stdin, os.Stdin)

	stdout, err := session.StdoutPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdout for session:")
	}
	go io.Copy(os.Stdout, stdout)

	stderr, err := session.StderrPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stderr for session:")
	}
	go io.Copy(os.Stderr, stderr)

	if err := session.Shell(); err != nil {
		return errors.Wrap(err, "failed to start shell:")
	}
	session.Wait()

	return nil
}
<|endoftext|>


<commit_before>package myaws

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/sync/errgroup"
)

// EC2SSHOptions customize the behavior of the SSH command.
type EC2SSHOptions struct {
	FilterTag    string
	LoginName    string
	IdentityFile string
	Private      bool
	Command      string
}

// EC2SSH resolves IP address of EC2 instance and connects to it by SSH.
func (client *Client) EC2SSH(options EC2SSHOptions) error {
	config, err := buildSSHConfig(options.LoginName, options.IdentityFile)
	if err != nil {
		return err
	}

	instances, err := client.FindEC2Instances(options.FilterTag, false)
	if err != nil {
		return err
	}

	if len(instances) == 0 {
		return errors.Errorf("no such instance: %s", options.FilterTag)
	}

	if len(instances) >= 2 && options.Command == "" {
		return errors.Errorf("multiple instances found")
	}

	hostnames := []string{}
	for _, instance := range instances {
		hostname, err := client.resolveEC2IPAddress(instance, options.Private)
		if err != nil {
			return err
		}
		hostnames = append(hostnames, hostname)
	}

	// Start single ssh session with terminal
	if options.Command == "" {
		return startSSHSessionWithTerminal(hostnames[0], "22", config)
	}

	// Execute ssh command to multiple hosts
	eg := errgroup.Group{}
	for _, hostname := range hostnames {
		hostname := hostname
		eg.Go(func() error {
			return executeSSHCommand(hostname, "22", config, options.Command)
		})
	}

	if err := eg.Wait(); err != nil {
		return err
	}

	return nil
}

func (client *Client) resolveEC2IPAddress(instance *ec2.Instance, private bool) (string, error) {
	if private {
		return client.resolveEC2PrivateIPAddress(instance)
	}
	return client.resolveEC2PublicIPAddress(instance)
}

func (client *Client) resolveEC2PrivateIPAddress(instance *ec2.Instance) (string, error) {
	if instance.PrivateIpAddress == nil {
		return "", errors.Errorf("no private ip address: %s", instance.InstanceId)
	}
	return *instance.PrivateIpAddress, nil
}

func (client *Client) resolveEC2PublicIPAddress(instance *ec2.Instance) (string, error) {
	if instance.PublicIpAddress == nil {
		return "", errors.Errorf("no public ip address: %s", instance.InstanceId)
	}
	return *instance.PublicIpAddress, nil
}

func buildSSHConfig(loginName string, identityFile string) (*ssh.ClientConfig, error) {
	normalizedIdentityFile := strings.Replace(identityFile, "~", os.Getenv("HOME"), 1)
	key, err := ioutil.ReadFile(normalizedIdentityFile)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read private key:")
	}

	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse private key:")
	}

	config := &ssh.ClientConfig{
		User: loginName,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
	}

	return config, nil
}

func buildSSHSessionPipe(session *ssh.Session) error {
	stdin, err := session.StdinPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdin for session:")
	}
	go io.Copy(stdin, os.Stdin)

	stdout, err := session.StdoutPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdout for session:")
	}
	go io.Copy(os.Stdout, stdout)

	stderr, err := session.StderrPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stderr for session:")
	}
	go io.Copy(os.Stderr, stderr)

	return nil
}

func startSSHSessionWithTerminal(hostname string, port string, config *ssh.ClientConfig) error {
	addr := fmt.Sprintf("%s:%s", hostname, port)
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // enable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		return errors.Wrap(err, "unable to put terminal in Raw Mode:")
	}
	defer terminal.Restore(fd, oldState)

	width, height, _ := terminal.GetSize(fd)

	if err := session.RequestPty("xterm", height, width, modes); err != nil {
		return errors.Wrap(err, "request for pseudo terminal failed:")
	}

	if err := buildSSHSessionPipe(session); err != nil {
		return err
	}

	if err := session.Shell(); err != nil {
		return errors.Wrap(err, "failed to start shell:")
	}
	session.Wait()

	return nil
}

func executeSSHCommand(hostname string, port string, config *ssh.ClientConfig, command string) error {
	addr := fmt.Sprintf("%s:%s", hostname, port)
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	out, err := session.CombinedOutput(command)

	fmt.Printf("========== Start output on host: %s ==========\n", hostname)
	fmt.Println(string(out))
	fmt.Printf("========== End   output on host: %s ==========\n", hostname)

	if err != nil {
		return errors.Wrapf(err, "failed to execute command: %s", command)
	}

	return nil
}
<commit_msg>Execute ssh command to multiple hosts in series<commit_after>package myaws

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/pkg/errors"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/terminal"
)

// EC2SSHOptions customize the behavior of the SSH command.
type EC2SSHOptions struct {
	FilterTag    string
	LoginName    string
	IdentityFile string
	Private      bool
	Command      string
}

// EC2SSH resolves IP address of EC2 instance and connects to it by SSH.
func (client *Client) EC2SSH(options EC2SSHOptions) error {
	config, err := buildSSHConfig(options.LoginName, options.IdentityFile)
	if err != nil {
		return err
	}

	instances, err := client.FindEC2Instances(options.FilterTag, false)
	if err != nil {
		return err
	}

	if len(instances) == 0 {
		return errors.Errorf("no such instance: %s", options.FilterTag)
	}

	if len(instances) >= 2 && options.Command == "" {
		return errors.Errorf("multiple instances found")
	}

	hostnames := []string{}
	for _, instance := range instances {
		hostname, err := client.resolveEC2IPAddress(instance, options.Private)
		if err != nil {
			return err
		}
		hostnames = append(hostnames, hostname)
	}

	// Start single ssh session with terminal
	if options.Command == "" {
		return startSSHSessionWithTerminal(hostnames[0], "22", config)
	}

	// Execute ssh command to multiple hosts in series
	for _, hostname := range hostnames {
		if err := executeSSHCommand(hostname, "22", config, options.Command); err != nil {
			return err
		}
	}

	return nil
}

func (client *Client) resolveEC2IPAddress(instance *ec2.Instance, private bool) (string, error) {
	if private {
		return client.resolveEC2PrivateIPAddress(instance)
	}
	return client.resolveEC2PublicIPAddress(instance)
}

func (client *Client) resolveEC2PrivateIPAddress(instance *ec2.Instance) (string, error) {
	if instance.PrivateIpAddress == nil {
		return "", errors.Errorf("no private ip address: %s", instance.InstanceId)
	}
	return *instance.PrivateIpAddress, nil
}

func (client *Client) resolveEC2PublicIPAddress(instance *ec2.Instance) (string, error) {
	if instance.PublicIpAddress == nil {
		return "", errors.Errorf("no public ip address: %s", instance.InstanceId)
	}
	return *instance.PublicIpAddress, nil
}

func buildSSHConfig(loginName string, identityFile string) (*ssh.ClientConfig, error) {
	normalizedIdentityFile := strings.Replace(identityFile, "~", os.Getenv("HOME"), 1)
	key, err := ioutil.ReadFile(normalizedIdentityFile)
	if err != nil {
		return nil, errors.Wrap(err, "unable to read private key:")
	}

	signer, err := ssh.ParsePrivateKey(key)
	if err != nil {
		return nil, errors.Wrap(err, "unable to parse private key:")
	}

	config := &ssh.ClientConfig{
		User: loginName,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
	}

	return config, nil
}

func buildSSHSessionPipe(session *ssh.Session) error {
	stdin, err := session.StdinPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdin for session:")
	}
	go io.Copy(stdin, os.Stdin)

	stdout, err := session.StdoutPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stdout for session:")
	}
	go io.Copy(os.Stdout, stdout)

	stderr, err := session.StderrPipe()
	if err != nil {
		return errors.Wrap(err, "unable to setup stderr for session:")
	}
	go io.Copy(os.Stderr, stderr)

	return nil
}

func startSSHSessionWithTerminal(hostname string, port string, config *ssh.ClientConfig) error {
	addr := fmt.Sprintf("%s:%s", hostname, port)
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	modes := ssh.TerminalModes{
		ssh.ECHO:          1,     // enable echoing
		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
	}

	fd := int(os.Stdin.Fd())
	oldState, err := terminal.MakeRaw(fd)
	if err != nil {
		return errors.Wrap(err, "unable to put terminal in Raw Mode:")
	}
	defer terminal.Restore(fd, oldState)

	width, height, _ := terminal.GetSize(fd)

	if err := session.RequestPty("xterm", height, width, modes); err != nil {
		return errors.Wrap(err, "request for pseudo terminal failed:")
	}

	if err := buildSSHSessionPipe(session); err != nil {
		return err
	}

	if err := session.Shell(); err != nil {
		return errors.Wrap(err, "failed to start shell:")
	}
	session.Wait()

	return nil
}

func executeSSHCommand(hostname string, port string, config *ssh.ClientConfig, command string) error {
	addr := fmt.Sprintf("%s:%s", hostname, port)
	connection, err := ssh.Dial("tcp", addr, config)
	if err != nil {
		return errors.Wrap(err, "unable to connect:")
	}
	defer connection.Close()

	session, err := connection.NewSession()
	if err != nil {
		return errors.Wrap(err, "unable to new session failed:")
	}
	defer session.Close()

	out, err := session.CombinedOutput(command)

	fmt.Printf("========== Start output on host: %s ==========\n", hostname)
	fmt.Println(string(out))
	fmt.Printf("========== End   output on host: %s ==========\n", hostname)

	if err != nil {
		return errors.Wrapf(err, "failed to execute command: %s", command)
	}

	return nil
}
<|endoftext|>


<commit_before>package core

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

type DbType string

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

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

	DriverName() string
	DataSourceName() string

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

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

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

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

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

	ModifyColumnSql(tableName string, col *Column) string

	ForUpdateSql(query string) string

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

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

	Filters() []Filter
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if rows.Next() {
		return nil
	}

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

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

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

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

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

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

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

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

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

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

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

	return sql
}

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

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

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

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

// QueryDialect query if registed database dialect
func QueryDialect(dbName DbType) Dialect {
	if d, ok := dialects[strings.ToLower(string(dbName))]; ok {
		return d()
	}
	return nil
}
<commit_msg>Add SetArguments(args) method for dialect_xxxxx.go support.<commit_after>package core

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

type DbType string

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

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

	DriverName() string
	DataSourceName() string

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

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

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

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

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

	ModifyColumnSql(tableName string, col *Column) string

	ForUpdateSql(query string) string

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

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

	Filters() []Filter
	SetParams(params map[string]string)
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	if rows.Next() {
		return nil
	}

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

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

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

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

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

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

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

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

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

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

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

	return sql
}

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

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

func (b *Base) SetParams(params map[string]string) {
}

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

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

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


<commit_before>package myqlib

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"time"
)

type MySQLAdminCommand string

const (
	MYSQLADMIN        string            = "mysqladmin"
	STATUS_COMMAND    MySQLAdminCommand = "extended-status"
	VARIABLES_COMMAND MySQLAdminCommand = "variables"
	VAR_PREFIX                          = "V_" // prefix of SHOW VARIABLES keys
)

type Loader interface {
	getStatus() (chan MyqSample, error)
	getVars() (chan MyqSample, error)
	getInterval() time.Duration
}

// MyqSamples are K->V maps
type MyqSample map[string]interface{}

// Number of keys in the sample
func (s MyqSample) Length() int {
	return len(s)
}

// MyqState contains the current and previous SHOW STATUS outputs.  Also SHOW VARIABLES.
// Prev and Vars might be nil
type MyqState struct {
	Cur, Prev   MyqSample
	TimeDiff    float64
	FirstUptime int64
}

// Given a loader, get a channel of myqstates being returned
func GetState(l Loader) (chan MyqState, error) {
	var ch = make(chan MyqState)

	statusch, statuserr := l.getStatus()
	if statuserr != nil {
		return nil, statuserr
	}

	varsch, varserr := l.getVars()
	if varserr != nil {
		return nil, varserr
	}

	// Vars fetching loop
	var latestvars MyqSample
	go func() {
		for vars := range varsch {
			latestvars = vars
		}
	}()

	// Main status loop
	go func() {
		defer close(ch)

		var prev MyqSample
		var firstUptime int64
		for status := range statusch {
			// Add latest vars to status with prefix
			for k, v := range latestvars {
				newkey := fmt.Sprint(VAR_PREFIX, k)
				status[newkey] = v
			}

			// Init new state
			state := new(MyqState)
			state.Cur = status

			// Only needed for File loaders really
			if firstUptime == 0 {
				firstUptime = status["uptime"].(int64)
			}
			state.FirstUptime = firstUptime

			// Assign the prev
			if prev != nil {
				state.Prev = prev

				// Calculate timediff if there is a prev.  Only file loader?
				state.TimeDiff = float64(status["uptime"].(int64) - prev["uptime"].(int64))

				// Skip to the next sample if TimeDiff is < the interval
				if state.TimeDiff < l.getInterval().Seconds() {
					fmt.Println(state.TimeDiff, "<?", l.getInterval().Seconds())
					continue
				}
			}

			// Send the state
			ch <- *state

			// Set the state for the next round
			prev = status
		}
	}()

	return ch, nil
}

type loaderInterval time.Duration

func (l loaderInterval) getInterval() time.Duration {
	return time.Duration(l)
}

// Load mysql status output from a mysqladmin output file
type FileLoader struct {
	loaderInterval
	statusFile    string
	variablesFile string
}

func NewFileLoader(i time.Duration, statusFile, varFile string) *FileLoader {
	return &FileLoader{loaderInterval(i), statusFile, varFile}
}
func (l FileLoader) harvestFile(filename string) (chan MyqSample, error) {
	file, err := os.OpenFile(filename, os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(file)
	var ch = make(chan MyqSample)

	// The file scanning goes into the background
	go func() {
		defer file.Close()
		defer close(ch)
		scanMySQLShowLines(scanner, ch)
	}()

	return ch, nil
}

func (l FileLoader) getStatus() (chan MyqSample, error) { return l.harvestFile(l.statusFile) }

func (l FileLoader) getVars() (chan MyqSample, error) {
	if l.variablesFile != "" {
		return l.harvestFile(l.variablesFile)
	} else {
		// Make a channel and return it, but it will never get anything
		var ch = make(chan MyqSample)
		defer close(ch)
		return ch, nil
	}
}

// SHOW output via mysqladmin on a live server
type LiveLoader struct {
	loaderInterval
	args string // other args for mysqladmin (like -u, -p, -h, etc.)
}

func NewLiveLoader(i time.Duration, args string) *LiveLoader {
	return &LiveLoader{loaderInterval(i), args}
}

// Collect output from mysqladmin and send it back in a sample
func (l LiveLoader) harvestMySQLAdmin(command MySQLAdminCommand) (chan MyqSample, error) {
	// Make sure we have MYSQLADMIN
	path, err := exec.LookPath(MYSQLADMIN)
	if err != nil {
		return nil, err
	}

	// Build the argument list
	args := []string{
		string(command), "-i",
		fmt.Sprintf("%.0f", l.getInterval().Seconds()),
	}
	if l.args != "" {
		args = append(args, l.args)
	}
	// fmt.Println( args )

	// Initialize the command
	cmd := exec.Command(path, args...)
	cleanupSubcmd(cmd)

	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(stdout)
	var ch = make(chan MyqSample)

	// The file scanning goes into the background
	go func() {
		defer close(ch)
		scanMySQLShowLines(scanner, ch)
	}()

	// Handle if the subcommand exits
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println(MYSQLADMIN, "exited: ", err, stderr.String())
		}
	}()

	return ch, nil
}

func (l LiveLoader) getStatus() (chan MyqSample, error) { return l.harvestMySQLAdmin(STATUS_COMMAND) }

func (l LiveLoader) getVars() (chan MyqSample, error) { return l.harvestMySQLAdmin(VARIABLES_COMMAND) }
<commit_msg>Handle no var file less hackily<commit_after>package myqlib

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"time"
)

type MySQLAdminCommand string

const (
	MYSQLADMIN        string            = "mysqladmin"
	STATUS_COMMAND    MySQLAdminCommand = "extended-status"
	VARIABLES_COMMAND MySQLAdminCommand = "variables"
	VAR_PREFIX                          = "V_" // prefix of SHOW VARIABLES keys
)

type Loader interface {
	getStatus() (chan MyqSample, error)
	getVars() (chan MyqSample, error)
	getInterval() time.Duration
}

// MyqSamples are K->V maps
type MyqSample map[string]interface{}

// Number of keys in the sample
func (s MyqSample) Length() int {
	return len(s)
}

// MyqState contains the current and previous SHOW STATUS outputs.  Also SHOW VARIABLES.
// Prev and Vars might be nil
type MyqState struct {
	Cur, Prev   MyqSample
	TimeDiff    float64
	FirstUptime int64
}

// Given a loader, get a channel of myqstates being returned
func GetState(l Loader) (chan MyqState, error) {
	var ch = make(chan MyqState)

	statusch, statuserr := l.getStatus()
	if statuserr != nil {
		return nil, statuserr
	}

	varsch, varserr := l.getVars()
	// return the error if getVars fails, but not if it's just due to a missing file
	if varserr != nil && varserr.Error() != "No file given" {
		return nil, varserr
	}

	// Vars fetching loop
	var latestvars MyqSample // whatever the last vars sample is will be here (may be empty)
	if varserr == nil {
		// Only start up the latestvars loop if there are no errors
		go func() {
			for vars := range varsch {
				latestvars = vars
			}
		}()
	}

	// Main status loop
	go func() {
		defer close(ch)

		var prev MyqSample
		var firstUptime int64
		for status := range statusch {
			// Add latest vars to status with prefix
			for k, v := range latestvars {
				newkey := fmt.Sprint(VAR_PREFIX, k)
				status[newkey] = v
			}

			// Init new state
			state := new(MyqState)
			state.Cur = status

			// Only needed for File loaders really
			if firstUptime == 0 {
				firstUptime = status["uptime"].(int64)
			}
			state.FirstUptime = firstUptime

			// Assign the prev
			if prev != nil {
				state.Prev = prev

				// Calculate timediff if there is a prev.  Only file loader?
				state.TimeDiff = float64(status["uptime"].(int64) - prev["uptime"].(int64))

				// Skip to the next sample if TimeDiff is < the interval
				if state.TimeDiff < l.getInterval().Seconds() {
					fmt.Println(state.TimeDiff, "<?", l.getInterval().Seconds())
					continue
				}
			}

			// Send the state
			ch <- *state

			// Set the state for the next round
			prev = status
		}
	}()

	return ch, nil
}

type loaderInterval time.Duration

func (l loaderInterval) getInterval() time.Duration {
	return time.Duration(l)
}

// Load mysql status output from a mysqladmin output file
type FileLoader struct {
	loaderInterval
	statusFile    string
	variablesFile string
}

func NewFileLoader(i time.Duration, statusFile, varFile string) *FileLoader {
	return &FileLoader{loaderInterval(i), statusFile, varFile}
}
func (l FileLoader) harvestFile(filename string) (chan MyqSample, error) {
	file, err := os.OpenFile(filename, os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(file)
	var ch = make(chan MyqSample)

	// The file scanning goes into the background
	go func() {
		defer file.Close()
		defer close(ch)
		scanMySQLShowLines(scanner, ch)
	}()

	return ch, nil
}

func (l FileLoader) getStatus() (chan MyqSample, error) { return l.harvestFile(l.statusFile) }

func (l FileLoader) getVars() (chan MyqSample, error) {
	if l.variablesFile != "" {
		return l.harvestFile(l.variablesFile)
	} else {
		return nil, errors.New("No file given")
	}
}

// SHOW output via mysqladmin on a live server
type LiveLoader struct {
	loaderInterval
	args string // other args for mysqladmin (like -u, -p, -h, etc.)
}

func NewLiveLoader(i time.Duration, args string) *LiveLoader {
	return &LiveLoader{loaderInterval(i), args}
}

// Collect output from mysqladmin and send it back in a sample
func (l LiveLoader) harvestMySQLAdmin(command MySQLAdminCommand) (chan MyqSample, error) {
	// Make sure we have MYSQLADMIN
	path, err := exec.LookPath(MYSQLADMIN)
	if err != nil {
		return nil, err
	}

	// Build the argument list
	args := []string{
		string(command), "-i",
		fmt.Sprintf("%.0f", l.getInterval().Seconds()),
	}
	if l.args != "" {
		args = append(args, l.args)
	}
	// fmt.Println( args )

	// Initialize the command
	cmd := exec.Command(path, args...)
	cleanupSubcmd(cmd)

	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(stdout)
	var ch = make(chan MyqSample)

	// The file scanning goes into the background
	go func() {
		defer close(ch)
		scanMySQLShowLines(scanner, ch)
	}()

	// Handle if the subcommand exits
	go func() {
		err := cmd.Wait()
		if err != nil {
			fmt.Println(MYSQLADMIN, "exited: ", err, stderr.String())
		}
	}()

	return ch, nil
}

func (l LiveLoader) getStatus() (chan MyqSample, error) { return l.harvestMySQLAdmin(STATUS_COMMAND) }

func (l LiveLoader) getVars() (chan MyqSample, error) { return l.harvestMySQLAdmin(VARIABLES_COMMAND) }
<|endoftext|>


<commit_before>package rel

import (
	"bytes"
	"log"
	"runtime/debug"
)

type MysqlVisitor struct {
	Conn Connector
}

func (v MysqlVisitor) Accept(visitable Visitable) string {
	return v.Visit(visitable)
}

func (v MysqlVisitor) Visit(visitable Visitable) string {
	switch node := visitable.(type) {
	case nil:
		return visitationNil()
	case *SelectStatementNode:
		return visitationSelectStatementNode(v, node)
	case *InNode:
		return visitationInNode(v, node)
	case SqlLiteralNode:
		return visitationSqlLiteralNode(v, node)
	case *SqlLiteralNode:
		return visitationSqlLiteralNode(v, *node)
	case *JoinSource:
		return visitationJoinSourceNode(v, node)
	case *EqualityNode:
		return visitationEqualityNode(v, node)
	case *HavingNode:
		return visitationHavingNode(v, node)
	case *AttributeNode:
		return visitationAttributeNode(v, node)
	case *GroupNode:
		return visitationGroupNode(v, node)
	case *ExistsNode:
		return visitationExistsNode(v, node)
	case *AsNode:
		return visitationAsNode(v, node)
	case *LessThanNode:
		return visitationLessThanNode(v, node)
	case *UnionNode:
		return visitationUnionNode(v, node)
	case *UnionAllNode:
		return visitationUnionAllNode(v, node)
	case *SelectManager:
		return visitationSelectManager(v, node)
	case *GreaterThanNode:
		return visitationGreaterThanNode(v, node)
	case *IntersectNode:
		return visitationIntersectNode(v, node)
	case *ExceptNode:
		return visitationExceptNode(v, node)
	case *OnNode:
		return visitationOnNode(v, node)
	case *AscendingNode:
		return visitationAscendingNode(v, node)
	case *DescendingNode:
		return visitationDescendingNode(v, node)
	case *CountNode:
		return visitationCountNode(v, node)
	case *AndNode:
		return visitationAndNode(v, node)
	case *TableAliasNode:
		return visitationTableAliasNode(v, node)
	case *InnerJoinNode:
		return visitationInnerJoinNode(v, node)
	case *GroupingNode:
		return visitationGroupingNode(v, node)
	case *NamedWindowNode:
		return visitationNamedWindowNode(v, node)
	case *WindowNode:
		return visitationWindowNode(v, node)
	case *RowsNode:
		return visitationRowsNode(v, node)
	case *LockNode:
		return visitationLockNode(v, node)
	case *PrecedingNode:
		return visitationPrecedingNode(v, node)
	case *FollowingNode:
		return visitationFollowingNode(v, node)
	case *CurrentRowNode:
		return visitationCurrentRowNode(v, node)
	case *BetweenNode:
		return visitationBetweenNode(v, node)
	case *RangeNode:
		return visitationRangeNode(v, node)
	case *DistinctNode:
		return visitationDistinctNode(v, node)
	case *WithNode:
		return visitationWithNode(v, node)
	case *WithRecursiveNode:
		return visitationWithRecursiveNode(v, node)
	case *Table:
		if node == nil {
			return visitationNil()
		}
		return visitationTable(v, node)
	case *MultiStatementManager:
		return visitationMultiStatementManager(v, node)
	case *InsertStatementNode:
		return visitationInsertStatementNode(v, node)
	case *SelectCoreNode:
		return visitationSelectCoreNode(v, node)
	case *NotEqualNode:
		return visitationNotEqualNode(v, node)
	case *NotNode:
		return visitationNotNode(v, node)
	case *GreaterThanOrEqualNode:
		return visitationGreaterThanOrEqualNode(v, node)
	case *LessThanOrEqualNode:
		return visitationLessThanOrEqualNode(v, node)
	case *OrNode:
		return visitationOrNode(v, node)
	case *AvgNode:
		return visitationAvgNode(v, node)
	case *NamedFunctionNode:
		return visitationNamedFunctionNode(v, node)
	case *SumNode:
		return visitationSumNode(v, node)
	case *MinNode:
		return visitationMinNode(v, node)
	case *MaxNode:
		return visitationMaxNode(v, node)
	case *MatchesNode:
		return visitationMatchesNode(v, node)
	case *DoesNotMatchNode:
		return visitationDoesNotMatchNode(v, node)
	case *NotInNode:
		return visitationNotInNode(v, node)
	case *BinNode:
		return v.VisitBinNode(node)
	case *ExtractNode:
		return visitationExtractNode(v, node)
	case *InfixOperationNode:
		return visitationInfixOperationNode(v, node)
	case *QuotedNode:
		return visitationQuotedNode(v, node)
	case *OverNode:
		return visitationOverNode(v, node)
	case *AssignmentNode:
		return visitationAssignmentNode(v, node)
	case *UnqualifiedColumnNode:
		return visitationUnqualifiedColumnNode(v, node)
	case *DistinctOnNode:
		return visitationDistinctOnNode(v, node)
	case *OuterJoinNode:
		return visitationOuterJoinNode(v, node)
	case *OffsetNode:
		return visitationOffsetNode(v, node)
	case *LimitNode:
		return visitationLimitNode(v, node)
	case *UpdateStatementNode:
		return visitationUpdateStatementNode(v, node)
	case *DeleteStatementNode:
		return visitationDeleteStatementNode(v, node)
	case *FalseNode:
		return visitationFalseNode(v, node)
	case *TrueNode:
		return visitationTrueNode(v, node)
	case *ValuesNode:
		return visitationValuesNode(v, node)
	case *OrderingNode:
		return visitationOrderingNode(v, node)
	case *TopNode:
		return visitationTopNode(v, node)
	default:
		debug.PrintStack()
		log.Fatalf("MysqlVisitor#Visit unable to handle type %T", visitable)
		return ""
	}
}

func (v MysqlVisitor) QuoteTableName(visitable Visitable) string {
	if alias, ok := visitable.(*TableAliasNode); ok {
		if !alias.Quoted {
			return alias.Name.Raw
		}
	}
	return v.Conn.QuoteTableName(visitable.String())
}

func (v MysqlVisitor) Quote(thing interface{}) string {
	return v.Conn.Quote(thing)
}

func (v MysqlVisitor) QuoteColumnName(literal SqlLiteralNode) string {
	return v.Conn.QuoteColumnName(literal.Raw)
}

func (v MysqlVisitor) VisitBinNode(node *BinNode) string {
	var buf bytes.Buffer
	buf.WriteString("BINARY ")
	buf.WriteString(v.Visit(node.Expr))
	return buf.String()
}
<commit_msg>Remove visibility for VisitBinNode<commit_after>package rel

import (
	"bytes"
	"log"
	"runtime/debug"
)

type MysqlVisitor struct {
	Conn Connector
}

func (v MysqlVisitor) Accept(visitable Visitable) string {
	return v.Visit(visitable)
}

func (v MysqlVisitor) Visit(visitable Visitable) string {
	switch node := visitable.(type) {
	case nil:
		return visitationNil()
	case *SelectStatementNode:
		return visitationSelectStatementNode(v, node)
	case *InNode:
		return visitationInNode(v, node)
	case SqlLiteralNode:
		return visitationSqlLiteralNode(v, node)
	case *SqlLiteralNode:
		return visitationSqlLiteralNode(v, *node)
	case *JoinSource:
		return visitationJoinSourceNode(v, node)
	case *EqualityNode:
		return visitationEqualityNode(v, node)
	case *HavingNode:
		return visitationHavingNode(v, node)
	case *AttributeNode:
		return visitationAttributeNode(v, node)
	case *GroupNode:
		return visitationGroupNode(v, node)
	case *ExistsNode:
		return visitationExistsNode(v, node)
	case *AsNode:
		return visitationAsNode(v, node)
	case *LessThanNode:
		return visitationLessThanNode(v, node)
	case *UnionNode:
		return visitationUnionNode(v, node)
	case *UnionAllNode:
		return visitationUnionAllNode(v, node)
	case *SelectManager:
		return visitationSelectManager(v, node)
	case *GreaterThanNode:
		return visitationGreaterThanNode(v, node)
	case *IntersectNode:
		return visitationIntersectNode(v, node)
	case *ExceptNode:
		return visitationExceptNode(v, node)
	case *OnNode:
		return visitationOnNode(v, node)
	case *AscendingNode:
		return visitationAscendingNode(v, node)
	case *DescendingNode:
		return visitationDescendingNode(v, node)
	case *CountNode:
		return visitationCountNode(v, node)
	case *AndNode:
		return visitationAndNode(v, node)
	case *TableAliasNode:
		return visitationTableAliasNode(v, node)
	case *InnerJoinNode:
		return visitationInnerJoinNode(v, node)
	case *GroupingNode:
		return visitationGroupingNode(v, node)
	case *NamedWindowNode:
		return visitationNamedWindowNode(v, node)
	case *WindowNode:
		return visitationWindowNode(v, node)
	case *RowsNode:
		return visitationRowsNode(v, node)
	case *LockNode:
		return visitationLockNode(v, node)
	case *PrecedingNode:
		return visitationPrecedingNode(v, node)
	case *FollowingNode:
		return visitationFollowingNode(v, node)
	case *CurrentRowNode:
		return visitationCurrentRowNode(v, node)
	case *BetweenNode:
		return visitationBetweenNode(v, node)
	case *RangeNode:
		return visitationRangeNode(v, node)
	case *DistinctNode:
		return visitationDistinctNode(v, node)
	case *WithNode:
		return visitationWithNode(v, node)
	case *WithRecursiveNode:
		return visitationWithRecursiveNode(v, node)
	case *Table:
		if node == nil {
			return visitationNil()
		}
		return visitationTable(v, node)
	case *MultiStatementManager:
		return visitationMultiStatementManager(v, node)
	case *InsertStatementNode:
		return visitationInsertStatementNode(v, node)
	case *SelectCoreNode:
		return visitationSelectCoreNode(v, node)
	case *NotEqualNode:
		return visitationNotEqualNode(v, node)
	case *NotNode:
		return visitationNotNode(v, node)
	case *GreaterThanOrEqualNode:
		return visitationGreaterThanOrEqualNode(v, node)
	case *LessThanOrEqualNode:
		return visitationLessThanOrEqualNode(v, node)
	case *OrNode:
		return visitationOrNode(v, node)
	case *AvgNode:
		return visitationAvgNode(v, node)
	case *NamedFunctionNode:
		return visitationNamedFunctionNode(v, node)
	case *SumNode:
		return visitationSumNode(v, node)
	case *MinNode:
		return visitationMinNode(v, node)
	case *MaxNode:
		return visitationMaxNode(v, node)
	case *MatchesNode:
		return visitationMatchesNode(v, node)
	case *DoesNotMatchNode:
		return visitationDoesNotMatchNode(v, node)
	case *NotInNode:
		return visitationNotInNode(v, node)
	case *BinNode:
		return v.visitBinNode(node)
	case *ExtractNode:
		return visitationExtractNode(v, node)
	case *InfixOperationNode:
		return visitationInfixOperationNode(v, node)
	case *QuotedNode:
		return visitationQuotedNode(v, node)
	case *OverNode:
		return visitationOverNode(v, node)
	case *AssignmentNode:
		return visitationAssignmentNode(v, node)
	case *UnqualifiedColumnNode:
		return visitationUnqualifiedColumnNode(v, node)
	case *DistinctOnNode:
		return visitationDistinctOnNode(v, node)
	case *OuterJoinNode:
		return visitationOuterJoinNode(v, node)
	case *OffsetNode:
		return visitationOffsetNode(v, node)
	case *LimitNode:
		return visitationLimitNode(v, node)
	case *UpdateStatementNode:
		return visitationUpdateStatementNode(v, node)
	case *DeleteStatementNode:
		return visitationDeleteStatementNode(v, node)
	case *FalseNode:
		return visitationFalseNode(v, node)
	case *TrueNode:
		return visitationTrueNode(v, node)
	case *ValuesNode:
		return visitationValuesNode(v, node)
	case *OrderingNode:
		return visitationOrderingNode(v, node)
	case *TopNode:
		return visitationTopNode(v, node)
	default:
		debug.PrintStack()
		log.Fatalf("MysqlVisitor#Visit unable to handle type %T", visitable)
		return ""
	}
}

func (v MysqlVisitor) QuoteTableName(visitable Visitable) string {
	if alias, ok := visitable.(*TableAliasNode); ok {
		if !alias.Quoted {
			return alias.Name.Raw
		}
	}
	return v.Conn.QuoteTableName(visitable.String())
}

func (v MysqlVisitor) Quote(thing interface{}) string {
	return v.Conn.Quote(thing)
}

func (v MysqlVisitor) QuoteColumnName(literal SqlLiteralNode) string {
	return v.Conn.QuoteColumnName(literal.Raw)
}

func (v MysqlVisitor) visitBinNode(node *BinNode) string {
	var buf bytes.Buffer
	buf.WriteString("BINARY ")
	buf.WriteString(v.Visit(node.Expr))
	return buf.String()
}
<|endoftext|>