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

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package column

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/model"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/util/charset"
	"github.com/pingcap/tidb/util/types"
)

// Col provides meta data describing a table column.
type Col struct {
	model.ColumnInfo
}

// PrimaryKeyName defines primary key name.
const PrimaryKeyName = "PRIMARY"

// IndexedCol defines an index with info.
type IndexedCol struct {
	model.IndexInfo
	X kv.Index
}

func (c *Col) getTypeStr() string {
	ans := []string{types.FieldTypeToStr(c.Tp, c.Charset)}
	if c.Flen != -1 {
		if c.Decimal == -1 {
			ans = append(ans, fmt.Sprintf("(%d)", c.Flen))
		} else {
			ans = append(ans, fmt.Sprintf("(%d, %d)", c.Flen, c.Decimal))
		}
	}
	if mysql.HasUnsignedFlag(c.Flag) {
		ans = append(ans, "UNSIGNED")
	}
	if mysql.HasZerofillFlag(c.Flag) {
		ans = append(ans, "ZEROFILL")
	}
	if mysql.HasBinaryFlag(c.Flag) {
		ans = append(ans, "BINARY")
	}
	if c.Charset != "" && c.Charset != charset.CharsetBin {
		ans = append(ans, fmt.Sprintf("CHARACTER SET %s", c.Charset))
	}
	if c.Collate != "" {
		ans = append(ans, fmt.Sprintf("COLLATE %s", c.Collate))
	}
	return strings.Join(ans, " ")
}

// String implements fmt.Stringer interface.
func (c *Col) String() string {
	ans := []string{c.Name.O, types.FieldTypeToStr(c.Tp, c.Charset)}
	if mysql.HasAutoIncrementFlag(c.Flag) {
		ans = append(ans, "AUTO_INCREMENT")
	}
	if mysql.HasNotNullFlag(c.Flag) {
		ans = append(ans, "NOT NULL")
	}
	return strings.Join(ans, " ")
}

// FindCol finds column in cols by name.
func FindCol(cols []*Col, name string) (c *Col) {
	for _, c = range cols {
		if strings.EqualFold(c.Name.O, name) {
			return
		}
	}
	return nil
}

// FindCols finds columns in cols by names.
func FindCols(cols []*Col, names []string) ([]*Col, error) {
	var rcols []*Col
	for _, name := range names {
		col := FindCol(cols, name)
		if col != nil {
			rcols = append(rcols, col)
		} else {
			return nil, errors.Errorf("unknown column %s", name)
		}
	}

	return rcols, nil
}

// FindOnUpdateCols finds columns have OnUpdateNow flag.
func FindOnUpdateCols(cols []*Col) []*Col {
	var rcols []*Col
	for _, c := range cols {
		if mysql.HasOnUpdateNowFlag(c.Flag) {
			rcols = append(rcols, c)
		}
	}

	return rcols
}

// TypeError returns error for invalid value type.
func (c *Col) TypeError(v interface{}) error {
	return errors.Errorf("cannot use %v (type %T) in assignment to, or comparison with, column %s (type %s)",
		v, v, c.Name, types.FieldTypeToStr(c.Tp, c.Charset))
}

// CastValues casts values based on columns type.
func CastValues(ctx context.Context, rec []interface{}, cols []*Col) (err error) {
	for _, c := range cols {
		rec[c.Offset], err = types.Convert(rec[c.Offset], &c.FieldType)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// ColDesc describes column information like MySQL desc and show columns do.
type ColDesc struct {
	Field        string
	Type         string
	Collation    string
	Null         string
	Key          string
	DefaultValue interface{}
	Extra        string
	Privileges   string
	Comment      string
}

const defaultPrivileges string = "select,insert,update,references"

// GetTypeDesc gets the description for column type.
func (c *Col) GetTypeDesc() string {
	var buf bytes.Buffer

	buf.WriteString(types.FieldTypeToStr(c.Tp, c.Charset))
	switch c.Tp {
	case mysql.TypeSet, mysql.TypeEnum:
		// Format is ENUM ('e1', 'e2') or SET ('e1', 'e2')
		buf.WriteString(fmt.Sprintf("('%s')", strings.Join(c.Elems, "','")))
	default:
		if c.Flen != -1 {
			if c.Decimal == -1 {
				buf.WriteString(fmt.Sprintf("(%d)", c.Flen))
			} else {
				buf.WriteString(fmt.Sprintf("(%d,%d)", c.Flen, c.Decimal))
			}
		}
	}<commit_msg>column: handle enum single quote for description<commit_after>// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package column

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/model"
	mysql "github.com/pingcap/tidb/mysqldef"
	"github.com/pingcap/tidb/util/charset"
	"github.com/pingcap/tidb/util/types"
)

// Col provides meta data describing a table column.
type Col struct {
	model.ColumnInfo
}

// PrimaryKeyName defines primary key name.
const PrimaryKeyName = "PRIMARY"

// IndexedCol defines an index with info.
type IndexedCol struct {
	model.IndexInfo
	X kv.Index
}

func (c *Col) getTypeStr() string {
	ans := []string{types.FieldTypeToStr(c.Tp, c.Charset)}
	if c.Flen != -1 {
		if c.Decimal == -1 {
			ans = append(ans, fmt.Sprintf("(%d)", c.Flen))
		} else {
			ans = append(ans, fmt.Sprintf("(%d, %d)", c.Flen, c.Decimal))
		}
	}
	if mysql.HasUnsignedFlag(c.Flag) {
		ans = append(ans, "UNSIGNED")
	}
	if mysql.HasZerofillFlag(c.Flag) {
		ans = append(ans, "ZEROFILL")
	}
	if mysql.HasBinaryFlag(c.Flag) {
		ans = append(ans, "BINARY")
	}
	if c.Charset != "" && c.Charset != charset.CharsetBin {
		ans = append(ans, fmt.Sprintf("CHARACTER SET %s", c.Charset))
	}
	if c.Collate != "" {
		ans = append(ans, fmt.Sprintf("COLLATE %s", c.Collate))
	}
	return strings.Join(ans, " ")
}

// String implements fmt.Stringer interface.
func (c *Col) String() string {
	ans := []string{c.Name.O, types.FieldTypeToStr(c.Tp, c.Charset)}
	if mysql.HasAutoIncrementFlag(c.Flag) {
		ans = append(ans, "AUTO_INCREMENT")
	}
	if mysql.HasNotNullFlag(c.Flag) {
		ans = append(ans, "NOT NULL")
	}
	return strings.Join(ans, " ")
}

// FindCol finds column in cols by name.
func FindCol(cols []*Col, name string) (c *Col) {
	for _, c = range cols {
		if strings.EqualFold(c.Name.O, name) {
			return
		}
	}
	return nil
}

// FindCols finds columns in cols by names.
func FindCols(cols []*Col, names []string) ([]*Col, error) {
	var rcols []*Col
	for _, name := range names {
		col := FindCol(cols, name)
		if col != nil {
			rcols = append(rcols, col)
		} else {
			return nil, errors.Errorf("unknown column %s", name)
		}
	}

	return rcols, nil
}

// FindOnUpdateCols finds columns have OnUpdateNow flag.
func FindOnUpdateCols(cols []*Col) []*Col {
	var rcols []*Col
	for _, c := range cols {
		if mysql.HasOnUpdateNowFlag(c.Flag) {
			rcols = append(rcols, c)
		}
	}

	return rcols
}

// TypeError returns error for invalid value type.
func (c *Col) TypeError(v interface{}) error {
	return errors.Errorf("cannot use %v (type %T) in assignment to, or comparison with, column %s (type %s)",
		v, v, c.Name, types.FieldTypeToStr(c.Tp, c.Charset))
}

// CastValues casts values based on columns type.
func CastValues(ctx context.Context, rec []interface{}, cols []*Col) (err error) {
	for _, c := range cols {
		rec[c.Offset], err = types.Convert(rec[c.Offset], &c.FieldType)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// ColDesc describes column information like MySQL desc and show columns do.
type ColDesc struct {
	Field        string
	Type         string
	Collation    string
	Null         string
	Key          string
	DefaultValue interface{}
	Extra        string
	Privileges   string
	Comment      string
}

const defaultPrivileges string = "select,insert,update,references"

// GetTypeDesc gets the description for column type.
func (c *Col) GetTypeDesc() string {
	var buf bytes.Buffer

	buf.WriteString(types.FieldTypeToStr(c.Tp, c.Charset))
	switch c.Tp {
	case mysql.TypeSet, mysql.TypeEnum:
		// Format is ENUM ('e1', 'e2') or SET ('e1', 'e2')
		// If elem contain ', we will convert ' -> ''
		elems := make([]string, len(c.Elems))
		for i := range elems {
			elems[i] = strings.Replace(c.Elems[i], "'", "''", -1)
		}
		buf.WriteString(fmt.Sprintf("('%s')", strings.Join(elems, "','")))
	default:
		if c.Flen != -1 {
			if c.Decimal == -1 {
				buf.WriteString(fmt.Sprintf("(%d)", c.Flen))
			} else {
				buf.WriteString(fmt.Sprintf("(%d,%d)", c.Flen, c.Decimal))
			}
		}
	}
{'fraction_non_alphanumeric': 0.1457817045576985, 'fraction_numerical': 0.004417627410839349, 'mean_word_length': 3.436902485659656, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6295367', 'n_tokens_mistral': 3451, 'n_tokens_neox': 3288, 'n_words': 952}