Skip to content

Commit

Permalink
sql: set "char" type width to 1
Browse files Browse the repository at this point in the history
The `"char"` type is a special single-character type. This commit adds a
`types.QChar` with a width one. It removes the `types.MakeQChar`
function so that it is impossible to create `"char"` types with any
other width.

Release note: None
  • Loading branch information
mgartner committed Oct 12, 2021
1 parent b68b1ba commit d54f3c5
Show file tree
Hide file tree
Showing 8 changed files with 39 additions and 56 deletions.
2 changes: 1 addition & 1 deletion pkg/sql/logictest/testdata/logic_test/information_schema
Original file line number Diff line number Diff line change
Expand Up @@ -2367,7 +2367,7 @@ char_len dc 1 4
char_len ec 12 48
char_len dv NULL NULL
char_len ev 12 48
char_len dq NULL NULL
char_len dq 1 4
char_len f NULL NULL
char_len g 1 NULL
char_len h 12 NULL
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/parser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -10351,7 +10351,7 @@ simple_typename:
// Eventually this clause will be used to parse user-defined types as well,
// since their names can be quoted.
if $1 == "char" {
$$.val = types.MakeQChar(0)
$$.val = types.QChar
} else if $1 == "serial" {
switch sqllex.(*lexer).nakedIntType.Width() {
case 32:
Expand Down Expand Up @@ -11627,7 +11627,7 @@ typed_literal:
// Eventually this clause will be used to parse user-defined types as well,
// since their names can be quoted.
if typName == "char" {
$$.val = &tree.CastExpr{Expr: tree.NewStrVal($2), Type: types.MakeQChar(0), SyntaxMode: tree.CastPrepend}
$$.val = &tree.CastExpr{Expr: tree.NewStrVal($2), Type: types.QChar, SyntaxMode: tree.CastPrepend}
} else if typName == "serial" {
switch sqllex.(*lexer).nakedIntType.Width() {
case 32:
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/sem/tree/col_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestParseColumnType(t *testing.T) {
{"CHAR(11)", types.MakeChar(11)},
{"VARCHAR", types.VarChar},
{"VARCHAR(2)", types.MakeVarChar(2)},
{`"char"`, types.MakeQChar(0)},
{`"char"`, types.QChar},
{"BYTES", types.Bytes},
{"STRING COLLATE da", types.MakeCollatedString(types.String, "da")},
{"CHAR COLLATE de", types.MakeCollatedString(types.MakeChar(1), "de")},
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func TestMakeTableDescColumns(t *testing.T) {
},
{
`"char"`,
types.MakeQChar(0),
types.QChar,
true,
},
{
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/types/oid.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ var OidToType = map[oid.Oid]*T{
oid.T_bool: Bool,
oid.T_bpchar: typeBpChar,
oid.T_bytea: Bytes,
oid.T_char: typeQChar,
oid.T_char: QChar,
oid.T_date: Date,
oid.T_float4: Float4,
oid.T_float8: Float,
Expand Down
53 changes: 21 additions & 32 deletions pkg/sql/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ var (
VarChar = &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_varchar, Locale: &emptyLocale}}

// QChar is the special "char" type that is a single-character column type.
// It's used by system tables. It is reported as "char" (with double quotes
// included) in SHOW CREATE and "char" in introspection for compatibility
// with PostgreSQL.
//
// See https://www.postgresql.org/docs/9.1/static/datatype-character.html
QChar = &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Width: 1, Locale: &emptyLocale}}

// Name is a type-alias for String with a different OID (T_name). It is
// reported as NAME in SHOW CREATE and "name" in introspection for
// compatibility with PostgreSQL.
Expand Down Expand Up @@ -583,24 +592,14 @@ var (

// typeBpChar is the "standard SQL" string type of fixed length, where "bp"
// stands for "blank padded". It is not exported to avoid confusion with
// typeQChar, as well as confusion over its default width.
// QChar, as well as confusion over its default width.
//
// It is reported as CHAR in SHOW CREATE and "character" in introspection for
// compatibility with PostgreSQL.
//
// Its default maximum with is 1. It always has a maximum width.
typeBpChar = &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_bpchar, Locale: &emptyLocale}}

// typeQChar is a special PostgreSQL-only type supported for compatibility.
// It behaves like VARCHAR, its maximum width cannot be modified, and has a
// peculiar name in the syntax and introspection. It is not exported to avoid
// confusion with typeBpChar, as well as confusion over its default width.
//
// It is reported as "char" (with double quotes included) in SHOW CREATE and
// "char" in introspection for compatibility with PostgreSQL.
typeQChar = &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Locale: &emptyLocale}}
)

const (
Expand Down Expand Up @@ -798,28 +797,15 @@ func MakeVarChar(width int32) *T {
}

// MakeChar constructs a new instance of the CHAR type (oid = T_bpchar) having
// the given max # characters (0 = unspecified number).
// the given max number of characters.
func MakeChar(width int32) *T {
if width == 0 {
return typeBpChar
}
if width < 0 {
panic(errors.AssertionFailedf("width %d cannot be negative", width))
if width <= 0 {
panic(errors.AssertionFailedf("width for type char must be at least 1"))
}
return &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_bpchar, Width: width, Locale: &emptyLocale}}
}

// MakeQChar constructs a new instance of the "char" type (oid = T_char) having
// the given max # characters (0 = unspecified number).
func MakeQChar(width int32) *T {
if width == 0 {
return typeQChar
}
return &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Width: width, Locale: &emptyLocale}}
}

// MakeCollatedString constructs a new instance of a CollatedStringFamily type
// that is collated according to the given locale. The new type is based upon
// the given string type, having the same oid and width values. For example:
Expand Down Expand Up @@ -1190,27 +1176,30 @@ func (t *T) Precision() int32 {
// Array types have the same type modifier as the contents of the array.
// The value will be -1 for types that do not need atttypmod.
func (t *T) TypeModifier() int32 {
typeModifier := int32(-1)
if t.Family() == ArrayFamily {
return t.ArrayContents().TypeModifier()
}
// The type modifier for "char" is always -1.
if t.Oid() == oid.T_char {
return int32(-1)
}
if width := t.Width(); width != 0 {
switch t.Family() {
case StringFamily, CollatedStringFamily:
// Postgres adds 4 to the attypmod for bounded string types, the
// var header size.
typeModifier = width + 4
return width + 4
case BitFamily:
typeModifier = width
return width
case DecimalFamily:
// attTypMod is calculated by putting the precision in the upper
// bits and the scale in the lower bits of a 32-bit int, and adding
// 4 (the var header size). We mock this for clients' sake. See
// numeric.c.
typeModifier = ((t.Precision() << 16) | width) + 4
return ((t.Precision() << 16) | width) + 4
}
}
return typeModifier
return int32(-1)
}

// Scale is an alias method for Width, used for clarity for types in
Expand Down
28 changes: 11 additions & 17 deletions pkg/sql/types/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,10 @@ func TestTypes(t *testing.T) {
{MakeCollatedString(MakeChar(20), enCollate),
MakeScalar(CollatedStringFamily, oid.T_bpchar, 0, 20, enCollate)},

{MakeCollatedString(typeQChar, enCollate), &T{InternalType: InternalType{
Family: CollatedStringFamily, Oid: oid.T_char, Locale: &enCollate}}},
{MakeCollatedString(MakeQChar(20), enCollate), &T{InternalType: InternalType{
Family: CollatedStringFamily, Oid: oid.T_char, Width: 20, Locale: &enCollate}}},
{MakeCollatedString(MakeQChar(20), enCollate),
MakeScalar(CollatedStringFamily, oid.T_char, 0, 20, enCollate)},
{MakeCollatedString(QChar, enCollate), &T{InternalType: InternalType{
Family: CollatedStringFamily, Oid: oid.T_char, Width: 1, Locale: &enCollate}}},
{MakeCollatedString(QChar, enCollate),
MakeScalar(CollatedStringFamily, oid.T_char, 0, 1, enCollate)},

{MakeCollatedString(Name, enCollate), &T{InternalType: InternalType{
Family: CollatedStringFamily, Oid: oid.T_name, Locale: &enCollate}}},
Expand Down Expand Up @@ -381,19 +379,15 @@ func TestTypes(t *testing.T) {
Family: StringFamily, Oid: oid.T_varchar, Width: 20, Locale: &emptyLocale}}},
{MakeVarChar(20), MakeScalar(StringFamily, oid.T_varchar, 0, 20, emptyLocale)},

{MakeChar(0), typeBpChar},
{MakeChar(0), &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_bpchar, Locale: &emptyLocale}}},
{MakeChar(1), &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_bpchar, Width: 1, Locale: &emptyLocale}}},
{MakeChar(20), &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_bpchar, Width: 20, Locale: &emptyLocale}}},
{MakeChar(20), MakeScalar(StringFamily, oid.T_bpchar, 0, 20, emptyLocale)},

{MakeQChar(0), typeQChar},
{MakeQChar(0), &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Locale: &emptyLocale}}},
{MakeQChar(20), &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Width: 20, Locale: &emptyLocale}}},
{MakeQChar(20), MakeScalar(StringFamily, oid.T_char, 0, 20, emptyLocale)},
{QChar, &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_char, Width: 1, Locale: &emptyLocale}}},
{QChar, MakeScalar(StringFamily, oid.T_char, 0, 1, emptyLocale)},

{Name, &T{InternalType: InternalType{
Family: StringFamily, Oid: oid.T_name, Locale: &emptyLocale}}},
Expand Down Expand Up @@ -691,7 +685,7 @@ func TestMarshalCompat(t *testing.T) {
{MakeString(10), InternalType{Family: StringFamily, Oid: oid.T_text, Width: 10}},
{VarChar, InternalType{Family: StringFamily, Oid: oid.T_varchar, VisibleType: visibleVARCHAR}},
{MakeChar(10), InternalType{Family: StringFamily, Oid: oid.T_bpchar, Width: 10, VisibleType: visibleCHAR}},
{MakeQChar(1), InternalType{Family: StringFamily, Oid: oid.T_char, Width: 1, VisibleType: visibleQCHAR}},
{QChar, InternalType{Family: StringFamily, Oid: oid.T_char, Width: 1, VisibleType: visibleQCHAR}},
{Name, InternalType{Family: name, Oid: oid.T_name}},
}

Expand Down Expand Up @@ -755,7 +749,7 @@ func TestUnmarshalCompat(t *testing.T) {
{InternalType{Family: StringFamily, VisibleType: visibleVARCHAR}, VarChar},
{InternalType{Family: StringFamily, VisibleType: visibleVARCHAR, Width: 20}, MakeVarChar(20)},
{InternalType{Family: StringFamily, VisibleType: visibleCHAR}, typeBpChar},
{InternalType{Family: StringFamily, VisibleType: visibleQCHAR}, typeQChar},
{InternalType{Family: StringFamily, VisibleType: visibleQCHAR, Width: 1}, QChar},
}

for _, tc := range testCases {
Expand Down
2 changes: 1 addition & 1 deletion pkg/workload/schemachange/type_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ ORDER BY enumsortorder`, name.Object(), name.Schema())
return nil, pgerror.Newf(pgcode.UndefinedObject, "type %s with oid %s does not exist", name.Object(), objectID)
}
// Special case CHAR to have the right width.
if objectID == oid.T_char || objectID == oid.T_bpchar {
if objectID == oid.T_bpchar {
t := *types.OidToType[objectID]
t.InternalType.Width = 1
return &t, nil
Expand Down

0 comments on commit d54f3c5

Please sign in to comment.