func checkSQLIdentifier(what string) error {
	if len(what) < MinSQLIdentifier {
		return ErrIdentifierTooShort
	}
	if len(what) > MaxSQLIdentifier {
		return ErrIdentifierTooLong
	}
	if !reSQLIdentifierBegin.MatchString(what) {
		return ErrIdentifierStart
	}
	if !reSQLIdentifier.MatchString(what) {
		return ErrIdentifierInvalid
	}
	return nil
}
Checks an SQL identifier (table name, column name, etc) and returns an
error if it is an invalid identifier.
package sqlgen
import (
	"bytes"
	"database/sql"
	"database/sql/driver"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"github.com/segmentio/ctlstore/pkg/schema"
	"github.com/segmentio/errors-go"
)
type MetaTable struct {
	DriverName string
	FamilyName schema.FamilyName
	TableName  schema.TableName
	Fields     []schema.NamedFieldType
	KeyFields  schema.PrimaryKey
}
var fieldTypeToSQLMap = map[schema.FieldType]map[string]string{
	schema.FTString: {
		"mysql":   "VARCHAR(191)",
		"sqlite3": "VARCHAR(191)",
	},
	schema.FTInteger: {
		"mysql":   "BIGINT",
		"sqlite3": "INTEGER",
	},
	schema.FTDecimal: {
		"mysql":   "DOUBLE",
		"sqlite3": "REAL",
	},
	schema.FTText: {
		"mysql":   "MEDIUMTEXT",
		"sqlite3": "TEXT",
	},
	schema.FTBinary: {
		"mysql":   "MEDIUMBLOB",
		"sqlite3": "BLOB",
	},
	schema.FTByteString: {
		"mysql":   "VARBINARY(255)",
		"sqlite3": "BLOB(255)",
	},
}
func BuildMetaTableFromInput(
	driverName string,
	familyName string,
	tableName string,
	fieldNames []string,
	fieldTypes []schema.FieldType,
	keyFields []string,
) (famName schema.FamilyName, tblName schema.TableName, tbl *MetaTable, err error) {
	famName, err = schema.NewFamilyName(familyName)
	if err != nil {
		return
	}
	tblName, err = schema.NewTableName(tableName)
	if err != nil {
		return
	}
	nameTypeMap := make(map[string]schema.FieldType)
	fts := []schema.NamedFieldType{}
	for i, name := range fieldNames {
		var fn schema.FieldName
		fn, err = schema.NewFieldName(name)
		if err != nil {
			return
		}
		fts = append(fts, schema.NamedFieldType{Name: fn, FieldType: fieldTypes[i]})
		nameTypeMap[name] = fieldTypes[i]
	}
	keyTypes := make([]schema.FieldType, len(keyFields))
	for i, name := range keyFields {
		keyTypes[i] = nameTypeMap[name]
	}
	var pk schema.PrimaryKey
	pk, err = schema.NewPKFromRawNamesAndFieldTypes(keyFields, keyTypes)
	if err != nil {
		return
	}
	tbl = &MetaTable{
		DriverName: driverName,
		FamilyName: famName,
		TableName:  tblName,
		Fields:     fts,
		KeyFields:  pk,
	}
	return
}
var base64EncodedFieldTypes = []schema.FieldType{schema.FTByteString, schema.FTBinary}
func isBase64EncodedFieldType(fieldType schema.FieldType) bool {
	for _, eft := range base64EncodedFieldTypes {
		if fieldType == eft {
			return true
		}
	}
	return false
}
func maybeDecodeBase64(val interface{}, should bool) (interface{}, error) {
	if !should || val == nil {
		return val, nil
	}
	valstr, ok := val.(string)
	if !ok {
		return nil, errors.New("maybeDecodeBase64 input not string")
	}
	decoded, err := base64.StdEncoding.DecodeString(valstr)
	if err != nil {
		return nil, errors.Wrap(err, "maybeDecodeBase64")
	}
	return decoded, nil
}
func (t MetaTable) ForDriver(newDriver string) (MetaTable, error) {
	t.DriverName = newDriver
	return t, nil
}
func (t *MetaTable) AsCreateTableDDL() (string, error) {
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	lines := []string{}
	for _, field := range t.Fields {
		sqlType, ok := fieldTypeToSQLMap[field.FieldType][t.DriverName]
		if !ok {
			return "", fmt.Errorf("Invalid driver+type combo %s:%s", field.FieldType, t.DriverName)
		}
		line := SqlSprintf("$1 $2", dblquote(field.Name.Name), sqlType)
		lines = append(lines, line)
	}
	pkFields := strings.Join(dblquoteStrings(t.KeyFields.Strings()), ",")
	pkDDL := SqlSprintf("PRIMARY KEY($1)", pkFields)
	lines = append(lines, pkDDL)
	tableBody := strings.Join(lines, ", ")
	q := SqlSprintf("CREATE TABLE $1 ($2);", tableName, tableBody)
	return q, nil
}
func (t *MetaTable) AddColumnDDL(fn schema.FieldName, ft schema.FieldType) (string, error) {
	ftString, ok := fieldTypeToSQLMap[ft][t.DriverName]
	if !ok {
		return "", fmt.Errorf("Invalid driver+type combo %s:%s", ft, t.DriverName)
	}
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	ddl := SqlSprintf(
		"ALTER TABLE $1 ADD COLUMN $2 $3",
		tableName,
		dblquote(fn.Name),
		ftString)
	return ddl, nil
}
func (t *MetaTable) FieldNames() []schema.FieldName {
	fns := []schema.FieldName{}
	for _, nft := range t.Fields {
		fns = append(fns, nft.Name)
	}
	return fns
}
func (t *MetaTable) UpsertDML(values []interface{}) (string, error) {
	if len(values) != len(t.Fields) {
		return "", errors.New("assertion failed: len(values) != len(t.Fields)")
	}
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	fieldNames := t.FieldNames()
	fieldNamesSQL := strings.Join(dblquoteStrings(schema.StringifyFieldNames(fieldNames)), ",")
	baseSQL := SqlSprintf("REPLACE INTO $1 ($2) VALUES(", tableName, fieldNamesSQL)
	buf := bytes.NewBuffer([]byte{})
	buf.WriteString(baseSQL)
	for i, val := range values {
		if i > 0 {
			buf.WriteString(",")
		}
		val, err := maybeDecodeBase64(val,
			isBase64EncodedFieldType(t.Fields[i].FieldType))
		if err != nil {
			return "", err
		}
		quoted, err := SQLQuote(val)
		if err != nil {
			return "", err
		}
		buf.WriteString(quoted)
	}
	buf.WriteString(")")
	return buf.String(), nil
}
func (t *MetaTable) DeleteDML(values []interface{}) (string, error) {
	if want, got := len(values), len(t.KeyFields.Strings()); want != got {
		return "", fmt.Errorf("DeleteDML: assertion failed %v != %v", want, got)
	}
	if len(values) != len(t.KeyFields.Fields) {
		return "", errors.New("assertion failed: len(values) != len(t.KeyFields.Fields)")
	}
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	baseSQL := SqlSprintf("DELETE FROM $1 WHERE ", tableName)
	buf := bytes.NewBuffer([]byte{})
	buf.WriteString(baseSQL)
	for i, fn := range t.KeyFields.Fields {
		if i > 0 {
			buf.WriteString(" AND ")
		}
		buf.WriteString(dblquote(fn.String()))
		buf.WriteString(" = ")
		ft, found := t.fieldTypeByName(fn)
		if !found {
			return "", errors.Errorf("DeleteDML couldn't find fieldName %s", fn.String())
		}
		val, err := maybeDecodeBase64(values[i], isBase64EncodedFieldType(ft))
		if err != nil {
			return "", err
		}
		quoted, err := SQLQuote(val)
		if err != nil {
			return "", err
		}
		buf.WriteString(quoted)
	}
	return buf.String(), nil
}
func (t *MetaTable) DropTableDDL() string {
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	ddl := SqlSprintf(
		"DROP TABLE IF EXISTS $1",
		tableName)
	return ddl
}
func (t *MetaTable) ClearTableDDL() string {
	tableName := schema.LDBTableName(t.FamilyName, t.TableName)
	ddl := SqlSprintf(
		"DELETE FROM $1",
		tableName)
	return ddl
}
func (t *MetaTable) fieldTypeByName(fn schema.FieldName) (schema.FieldType, bool) {
	for _, x := range t.Fields {
		if x.Name == fn {
			return x.FieldType, true
		}
	}
	return 0, false
}
func (t *MetaTable) Validate() error {
	if len(t.KeyFields.Fields) < 0 {
		return errors.New("Must have at least one primary key field")
	}
	for _, pkfn := range t.KeyFields.Fields {
		var matchingField schema.NamedFieldType
		found := false
		for _, nft := range t.Fields {
			if nft.Name == pkfn {
				found = true
				matchingField = nft
				break
			}
		}
		if !found {
			return fmt.Errorf("Primary key field '%s' not specified as a field", pkfn.Name)
		}
		if !matchingField.FieldType.CanBeKey() {
			typeName := schema.FieldTypeStringsByFieldType[matchingField.FieldType]
			return fmt.Errorf("Fields of type '%s' cannot be a key field", typeName)
		}
	}
	return nil
}
var sqlDriverNamesByType map[reflect.Type]string
func SqlDriverToDriverName(driver driver.Driver) string {
	if sqlDriverNamesByType == nil {
		sqlDriverNamesByType = map[reflect.Type]string{}
		for _, driverName := range sql.Drivers() {
			db, _ := sql.Open(driverName, "")
			if db != nil {
				driverType := reflect.TypeOf(db.Driver())
				if _, ok := sqlDriverNamesByType[driverType]; !ok {
					sqlDriverNamesByType[driverType] = driverName
				}
			} else {
			}
		}
	}
	driverType := reflect.TypeOf(driver)
	if driverName, found := sqlDriverNamesByType[driverType]; found {
		return driverName
	}
	return ""
}
func SQLPlaceholderSet(count int) string {
	var buffer bytes.Buffer
	for i := 0; i < count; i++ {
		buffer.WriteString("?")
		if i != count-1 {
			buffer.WriteString(",")
		}
	}
	return buffer.String()
}
var reSQLIdentifierBegin = regexp.MustCompile("^[a-zA-Z]")
var reSQLIdentifier = regexp.MustCompile("^[_a-zA-Z0-9]+$")
const MaxSQLIdentifier = 64
const MinSQLIdentifier = 2
var ErrIdentifierTooShort = fmt.Errorf("Identifier must be at least %d characters", MinSQLIdentifier)
var ErrIdentifierTooLong = fmt.Errorf("Identifier must be at most %d characters", MaxSQLIdentifier)
var ErrIdentifierStart = errors.New("Identifier must start with a letter")
var ErrIdentifierInvalid = errors.New("Identifier can only contain letters, numbers, and underscore")