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
}
XXX: should we validate schema with SQLite first? (yes!)
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
}